2023-10-05 16:56:02 +10:00
using AutoMapper ;
2023-12-04 10:16:00 +10:00
using Microsoft.AspNetCore.Authorization ;
2023-10-05 16:56:02 +10:00
using Microsoft.AspNetCore.Mvc ;
using PARR.API.Contracts.V1 ;
2024-08-01 14:21:28 +10:00
using PARR.API.Contracts.V1.Requests.Queries ;
2026-05-26 11:26:52 +10:00
using PARR.API.Contracts.V1.Responses ;
using PARR.API.Contracts.V1.Responses.Base ;
2023-10-05 16:56:02 +10:00
using PARR.API.Controllers.V1.Base ;
2024-08-01 14:21:28 +10:00
using PARR.API.Services.Interfaces ;
2026-05-25 16:59:42 +10:00
using PARR.Core.Services.RobotTask.Interfaces ;
2026-04-14 12:01:49 +10:00
using PARR.Domain.Common.Roles ;
2026-05-26 11:26:52 +10:00
using PARR.Domain.Entities.Base.History ;
2026-04-13 16:58:30 +10:00
using PARR.Domain.Enums ;
2026-05-26 11:26:52 +10:00
using PARR.Domain.Exceptions ;
2023-10-05 16:56:02 +10:00
namespace PARR.API.Controllers.V1
{
2023-12-04 10:16:00 +10:00
[Authorize(Roles = ParrRoles.EsppRobot.RoleOrAdmin)]
2023-10-05 16:56:02 +10:00
public class RobotTaskController : BaseApiController
{
2026-05-26 11:26:52 +10:00
private readonly IRobotTaskService robotTaskService ;
2023-10-05 16:56:02 +10:00
private readonly IMapper mapper ;
2026-05-26 11:26:52 +10:00
//private readonly SettingsFromDb settingsFromDb;
//private readonly IRobotConfigurationRepository robotConfigurationService;
//private readonly ILogger<RobotTaskController> logger;
2024-08-01 14:21:28 +10:00
private readonly IClientService clientService ;
2026-05-26 11:26:52 +10:00
//private readonly IRobotHistoryRepository robotHistoryService;
//private readonly IShortcodesService shortcodesService;
//private readonly INextRunService nextRunService;
2023-10-05 16:56:02 +10:00
public RobotTaskController (
IMapper mapper ,
2026-05-26 11:26:52 +10:00
//SettingsFromDb settingsFromDb,
//IRobotConfigurationRepository robotConfigurationService,
//ILogger<RobotTaskController> logger,
2024-08-01 14:21:28 +10:00
IClientService clientService ,
2026-05-26 11:26:52 +10:00
//IRobotHistoryRepository robotHistoryService,
//IShortcodesService shortcodesService,
//INextRunService nextRunService
2026-05-25 16:59:42 +10:00
IRobotTaskService robotTaskService
2023-10-05 16:56:02 +10:00
)
{
2026-05-26 11:26:52 +10:00
this . robotTaskService = robotTaskService ;
2023-10-05 16:56:02 +10:00
this . mapper = mapper ;
2026-05-26 11:26:52 +10:00
//this.settingsFromDb = settingsFromDb;
//this.robotConfigurationService = robotConfigurationService;
//this.logger = logger;
2024-08-01 14:21:28 +10:00
this . clientService = clientService ;
2026-05-26 11:26:52 +10:00
//this.robotHistoryService = robotHistoryService;
//this.shortcodesService = shortcodesService;
//this.nextRunService = nextRunService;
2023-10-05 16:56:02 +10:00
}
/// <summary>
2026-05-26 11:26:52 +10:00
/// Получить задание для робота по коду робота и по статусу задания
2023-10-05 16:56:02 +10:00
/// </summary>
2026-05-26 11:26:52 +10:00
/// <param name="robotCode"></param>
/// <param name="taskStatusCode"></param>
/// <param name="requestQuery"></param>
2023-10-05 16:56:02 +10:00
/// <returns></returns>
[HttpGet(ApiRoutes.RobotTask.GetByRobotAndStatusTask)]
2024-08-01 14:21:28 +10:00
public async Task < IActionResult > GetByRobotAndStatusTask ( [ FromRoute ] RobotsEnum robotCode , [ FromRoute ] TaskStatusEnum taskStatusCode , [ FromQuery ] RobotTaskQuery requestQuery )
2023-10-05 16:56:02 +10:00
{
2026-05-26 11:26:52 +10:00
switch ( robotCode )
{
case RobotsEnum . TemplateOrder :
var templateTask = await robotTaskService . GetTemplateTaskAsync (
taskStatusCode ,
requestQuery . SetInProgressStatus ? ? false ,
clientService . GetClientIp ( ) ? . ToString ( ) ,
requestQuery . RobotId
) ;
var templateResponse = mapper . Map < RobotTaskTemplateResponse > ( templateTask ) ;
2026-02-16 16:54:06 +10:00
2026-05-26 11:26:52 +10:00
return Ok ( new Response < RobotTaskTemplateResponse > ( templateResponse , true ) ) ;
case RobotsEnum . ScheduleOrder :
var historyIniciator = new HistoryInitiator
{
2026-02-16 16:54:06 +10:00
2026-05-26 11:26:52 +10:00
} ;
var scheduleTask = await robotTaskService . GetScheduleTaskAsync (
taskStatusCode ,
requestQuery . SetInProgressStatus ? ? false ,
historyIniciator . InitiatorIp ,
requestQuery . RobotId ,
historyIniciator
) ;
2024-02-27 16:01:08 +10:00
2026-05-26 11:26:52 +10:00
var scheduleResponse = mapper . Map < RobotTaskScheduleResponse > ( scheduleTask ) ;
2024-02-27 16:01:08 +10:00
2026-05-26 11:26:52 +10:00
return Ok ( new Response < RobotTaskScheduleResponse > ( scheduleResponse , true ) ) ;
default :
throw new AppValidationException ( "Некорректное значение robotCode" ) ;
}
2023-10-17 10:53:02 +10:00
}
2024-08-01 14:21:28 +10:00
2026-05-26 11:26:52 +10:00
#region Old
// /// <summary>
// /// Получить задание для робота по коду робота и по статусу задания
// /// </summary>
// /// <param name="robotCode"></param>
// /// <param name="taskStatusCode"></param>
// /// <returns></returns>
// [HttpGet(ApiRoutes.RobotTask.GetByRobotAndStatusTask)]
// public async Task<IActionResult> GetByRobotAndStatusTask([FromRoute] RobotsEnum robotCode, [FromRoute] TaskStatusEnum taskStatusCode, [FromQuery] RobotTaskQuery requestQuery)
// {
// //Ищем все задания с превышенным кол-вом попыток и с просроченным временем и ставим им статус ошибки
// await robotConfigurationService.MarkExpiredTasksAsFailedAsync(settingsFromDb.RobotAttemptsNumber, settingsFromDb.RobotWaitTime);
// var query = robotConfigurationService.Get()
// .AsSingleQuery()
// .Where(t => t.RobotCode == (int)robotCode && t.TaskStatusCode == (int)taskStatusCode);
// switch (robotCode)
// {
// case RobotsEnum.TemplateOrder:
// // шаблоны
// query = query
// .Include(t => t.Template)
// .ThenInclude(t => t!.Unit)
// .ThenInclude(t => t!.UnitValues)
// .ThenInclude(t => t.Field)
// .Include(t => t.Template)
// .ThenInclude(t => t!.Unit)
// .ThenInclude(t => t!.UnitValues)
// .ThenInclude(t => t.Value)
// .Include(t => t.Template)
// .ThenInclude(a => a!.Job)
// .ThenInclude(t => t!.Group)
// .ThenInclude(g => g.GroupType)
// .Include(t => t.Template)
// .ThenInclude(w => w!.Job)
// .ThenInclude(t => t!.Tnk)
// .ThenInclude(s => s!.Subprocess)
// .ThenInclude(p => p!.Process);
// query = query
// .Include(t => t.Template)
// .ThenInclude(t => t!.UnitsInTemplate);
// break;
// case RobotsEnum.ScheduleOrder:
// //расписание
// query = query
// .Include(t => t.Template)
// .ThenInclude(t => t!.Unit)
// .ThenInclude(t => t!.UnitValues)
// .ThenInclude(t => t.Field)
// .Include(t => t.Template)
// .ThenInclude(t => t!.Unit)
// .ThenInclude(t => t!.UnitValues)
// .ThenInclude(t => t.Value)
// .Include(t => t.Template)
// .ThenInclude(a => a!.Job)
// .ThenInclude(t => t!.Group)
// .ThenInclude(g => g.GroupType)
// .Include(t => t.Template)
// .ThenInclude(a => a!.Job)
// .ThenInclude(t => t!.Group)
// .ThenInclude(t => t!.EsppSchValues)
// .ThenInclude(t => t!.EsppSchTypeConfig)
// .ThenInclude(t => t!.EsppSchTypeSchedule)
// .Include(t => t.Template)
// .ThenInclude(t => t!.Job)
// .ThenInclude(t => t!.Group)
// .ThenInclude(t => t!.ScheduleExcludeType)
// .Include(t => t.Template)
// .ThenInclude(t => t!.Job)
// .ThenInclude(t => t!.Group)
// .ThenInclude(t => t.ScheduleExcludeTypeCalendar)
// .Include(t => t.Template)
// .ThenInclude(t => t!.Job)
// .ThenInclude(t => t!.Tnk)
// .Include(t => t.Template)
// .ThenInclude(t => t!.UnitsInTemplate);
// //выбираем только записи с созданными шаблонами (у которых статус 20 или 30), а только потом у них ищем расписания
// var createdTemplates = robotConfigurationService.Get()
// .Where(t => t.RobotCode == (int)RobotsEnum.TemplateOrder && (t.TaskStatusCode == (int)TaskStatusEnum.Ok))
// .Select(t => t.TemplateId);
// query = query.Where(t => t.RobotCode == (int)RobotsEnum.ScheduleOrder && createdTemplates.Contains(t.TemplateId));
// // query = query.Where(t => t.RobotCode == (int)RobotsEnum.ScheduleOrder && t.TemplateId==Guid.Parse("7cb7c3be-506d-40e0-a63f-4554edb52459"));
// break;
// default:
// break;
// }
// // сортируем по NextRun, чтобы те у которых дата след срабатывания ближе к текущей, выполнились скорее
// query = query.OrderBy(t => t.Template!.NextRun).ThenBy(t => t.Template!.IsActiveSchedule).ThenBy(t => t.Template.IsActiveTemplate);
// RobotConfiguration? task = null;
// //ищем задание в ожидании, если нашли, выбираем е г о
// task = await query.FirstOrDefaultAsync(t => t.RobotStatusCode == (int)RobotStatusEnum.Wait);
// if (task == null)
// {
// //ищем задания в работе, которые можно перезапустить
// //Поиск по `RobotStatusCode` = 22.
// //Далее проверяется `LastStatusUpdated`, что время последнего смены статуса не превышает допустимого(берется из настроек, поле `RobotWaitTime`)
// //и что текущая попытка не больше разрешенной(берется из настроек, поле `RobotAttemptsNumber`) - если это так, берется эта запись.
// var endDate = DateTimeOffset.UtcNow.Add(-settingsFromDb.RobotWaitTime);
// task = await query
// .FirstOrDefaultAsync(t =>
// t.RobotStatusCode == (int)RobotStatusEnum.InProgress
// && t.AttemptsNumber < settingsFromDb.RobotAttemptsNumber
// && t.LastRobotStatusUpdated < endDate
// );
// }
// if (task == null)
// return NotFound();
// if (requestQuery?.SetInProgressStatus == true)
// {
// var resultSetStatus = await SetInProgressStatusAsync(task.Id);
// if (resultSetStatus == false)
// {
// logger.LogError($"Ошибка при установке статуса {RobotStatusEnum.InProgress.ToString()} для задания RobotConfigutationId {task.Id} (при выдаче задания роботу)");
// return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = "Ошибка при выдаче задания." } }));
// }
// }
// switch (robotCode)
// {
// case RobotsEnum.TemplateOrder:
// { //RobotTaskTemplateResponse
// var robotTaskTemplateResponse = mapper.Map<RobotTaskTemplateResponse>(task);
// robotTaskTemplateResponse.FullDescription = NormalizeLineEndingsToCrlf(await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.FullDescription, task.Template!));
// robotTaskTemplateResponse.ShortDescription = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.ShortDescription, task.Template!);
// robotTaskTemplateResponse.Solution = NormalizeLineEndingsToCrlf(await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.Solution, task.Template!));
// robotTaskTemplateResponse.TnkName = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.TnkName, task.Template!);
// robotTaskTemplateResponse.WorkName = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.WorkName, task.Template!);
// robotTaskTemplateResponse.WorkGroup = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.WorkGroup, task.Template!);
// robotTaskTemplateResponse.ResponseArea = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.ResponseArea, task.Template!);
// return Ok(new Response<RobotTaskTemplateResponse>(robotTaskTemplateResponse, true));
// }
// case RobotsEnum.ScheduleOrder:
// { // если был запрос на расписание, проверяем у него nextRun, lastRun, обновляем их
// var resultUpdateNextRun = await UpdateNextRunAsync(task);
// if (!resultUpdateNextRun)
// {
// logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}", task.TemplateId);
// return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = "Ошибка при расчете NextRun" } }));
// }
// //RobotTaskScheduleResponse
// var robotTaskScheduleResponse = mapper.Map<RobotTaskScheduleResponse>(task);
// robotTaskScheduleResponse.Timezone = settingsFromDb.EsppScheduleTimezone;
// robotTaskScheduleResponse.WorkGroup = await shortcodesService.ApplyShortcodesAsync(robotTaskScheduleResponse.WorkGroup, task.Template!);
// robotTaskScheduleResponse.ResponseArea = await shortcodesService.ApplyShortcodesAsync(robotTaskScheduleResponse.ResponseArea, task.Template!);
// //var nextRunWithRobotTz = nextRunService.GetNextRunWithTimezoneEsppAndResponseArea(task.Template!.NextRun, task.Template!.Job?.Group?.IsResponseAreaTimezone, robotTaskScheduleResponse.ResponseArea);
// //nextRun в часовой зоне У З Робота ЕСПП
// var nextRunWithRobotTz = task.Template!.NextRun.Add(nextRunService.GetEsppAccountOffset());
// //на всякий случай еще раз проверяем, что дата не устарела и отправляем задание
// if (nextRunWithRobotTz < DateTimeOffset.UtcNow)
// {
// logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}, итоговое значение для робота, меньше чем сейчас {nextRunWithRobotTz}<{now}",
// task.TemplateId, nextRunWithRobotTz, DateTimeOffset.UtcNow);
// return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = "Ошибка при расчете NextRun" } }));
// }
// robotTaskScheduleResponse.NextStart = EsppScheduleHelpers.GetNextRun(nextRunWithRobotTz);
// robotTaskScheduleResponse.GenerationTime = EsppScheduleHelpers.GetGenerationTime(nextRunWithRobotTz);
// return Ok(new Response<RobotTaskScheduleResponse>(robotTaskScheduleResponse, true));
// }
// default:
// break;
// }
// return BadRequest();
// }
// /// <summary>
// /// Обоновить NextRun если он устарел
// /// </summary>
// /// <param name="task"></param>
// /// <returns></returns>
// private async Task<bool> UpdateNextRunAsync(RobotConfiguration task)
// {
// var template = task.Template!;
// //var nextRun = await esppScheduleTransformService.GetNextDateAsync(template.Job!.GroupId, template!.Job!.Group!.ReferenceDate);
// var nextRun = await nextRunService.GetNextRunForTemplateAsync(template.Id, false);
// if (!nextRun.HasValue)
// {
// logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun=null, ошибка в расчетах.", template.Id);
// return false;
// }
// if (nextRun.Value < DateTimeOffset.UtcNow)
// {
// logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun<Now [{nextRun}<{now}], ошибка в расчетах.", template.Id, nextRun.Value, DateTimeOffset.UtcNow);
// return false;
// }
// if (nextRun != template.NextRun)
// {
// logger.LogDebug($"Для шаблона id {template.Id} обновляю nextRun, новое значение {nextRun}, старое значение {template.NextRun}");
// template.LastRun = template.NextRun;
// template.NextRun = nextRun.Value;
// await robotConfigurationService.CommitAsync(new HistoryInitiator { InitiatorComment = "При получении задания роботом, обновил NextRun", InitiatorIp = clientService.GetClientIp()?.ToString(), InitiatorParrComponentId = ParrComponentsEnum.Api });
// }
// return true;
// }
// /// <summary>
// /// Устанавливаем статус "Робот взял в работу", пишем в историю работы роботов инф о начале работ
// /// </summary>
// /// <param name="taskId"></param>
// /// <returns></returns>
// private async Task<bool> SetInProgressStatusAsync(Guid taskId)
// {
// var config = await robotConfigurationService.GetAsync(taskId);
// //изменение статуса робота
// robotConfigurationService.ChangeRobotStatus(RobotStatusEnum.InProgress, config!);
// if (!await robotConfigurationService.CommitAsync())
// return false;
// //записываем в лог робота
// var history = new RobotHistory
// {
// Id = Guid.NewGuid(),
// HistoryLevel = (int)RobotHistoryLevelEnum.Start,
// TaskStatusCode = config.TaskStatusCode,
// RobotConfigurationId = config.Id,
// RobotIp = clientService.GetClientIp()?.ToString()
// };
// if (!await robotHistoryService.CreateAsync(history) || !await robotHistoryService.CommitAsync())
// return false;
2024-08-01 14:21:28 +10:00
2026-05-26 11:26:52 +10:00
// return true;
// }
2024-08-01 14:21:28 +10:00
2026-05-26 11:26:52 +10:00
// /// <summary>
// /// Приводит переносы строк в тексте к формату CRLF (\r\n)
// /// </summary>
// /// <param name="text">Исходный текст</param>
// /// <returns>Текст с унифицированными переносами строк</returns>
// private string NormalizeLineEndingsToCrlf(string? text)
// {
// if (string.IsNullOrEmpty(text))
// return string.Empty;
2026-04-27 14:03:10 +10:00
2026-05-26 11:26:52 +10:00
// // Заменяем любые варианты переносов (\r\n, \r, \n) на единый \r\n
// return System.Text.RegularExpressions.Regex.Replace(text, @"\r\n|\r|\n", "\r\n");
// }
2026-04-27 14:03:10 +10:00
2026-05-26 11:26:52 +10:00
#endregion
2023-10-05 16:56:02 +10:00
}
}