using AutoMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PARR.API.Contracts.V1; using PARR.API.Contracts.V1.Requests.Queries; using PARR.API.Contracts.V1.Responses; using PARR.API.Contracts.V1.Responses.Base; using PARR.API.Controllers.V1.Base; using PARR.API.Services.Interfaces; using PARR.Core.Services.RobotTask.Interfaces; using PARR.Domain.Common.Roles; using PARR.Domain.Entities.Base.History; using PARR.Domain.Enums; using PARR.Domain.Exceptions; namespace PARR.API.Controllers.V1 { [Authorize(Roles = ParrRoles.EsppRobot.RoleOrAdmin)] public class RobotTaskController : BaseApiController { private readonly IRobotTaskService robotTaskService; private readonly IMapper mapper; //private readonly SettingsFromDb settingsFromDb; //private readonly IRobotConfigurationRepository robotConfigurationService; //private readonly ILogger logger; private readonly IClientService clientService; //private readonly IRobotHistoryRepository robotHistoryService; //private readonly IShortcodesService shortcodesService; //private readonly INextRunService nextRunService; public RobotTaskController( IMapper mapper, //SettingsFromDb settingsFromDb, //IRobotConfigurationRepository robotConfigurationService, //ILogger logger, IClientService clientService, //IRobotHistoryRepository robotHistoryService, //IShortcodesService shortcodesService, //INextRunService nextRunService IRobotTaskService robotTaskService ) { this.robotTaskService = robotTaskService; this.mapper = mapper; //this.settingsFromDb = settingsFromDb; //this.robotConfigurationService = robotConfigurationService; //this.logger = logger; this.clientService = clientService; //this.robotHistoryService = robotHistoryService; //this.shortcodesService = shortcodesService; //this.nextRunService = nextRunService; } /// /// Получить задание для робота по коду робота и по статусу задания /// /// /// /// /// [HttpGet(ApiRoutes.RobotTask.GetByRobotAndStatusTask)] public async Task GetByRobotAndStatusTask([FromRoute] RobotsEnum robotCode, [FromRoute] TaskStatusEnum taskStatusCode, [FromQuery] RobotTaskQuery requestQuery) { switch (robotCode) { case RobotsEnum.TemplateOrder: var templateTask = await robotTaskService.GetTemplateTaskAsync( taskStatusCode, requestQuery.SetInProgressStatus ?? false, clientService.GetClientIp()?.ToString(), requestQuery.RobotId ); var templateResponse = mapper.Map(templateTask); return Ok(new Response(templateResponse, true)); case RobotsEnum.ScheduleOrder: var historyIniciator = new HistoryInitiator { InitiatorComment="Задание роботу, расписание.", InitiatorIp=clientService.GetClientIp()?.ToString(), InitiatorParrComponentId= ParrComponentsEnum.Api }; var scheduleTask = await robotTaskService.GetScheduleTaskAsync( taskStatusCode, requestQuery.SetInProgressStatus ?? false, historyIniciator.InitiatorIp, requestQuery.RobotId, historyIniciator ); var scheduleResponse = mapper.Map(scheduleTask); return Ok(new Response(scheduleResponse, true)); default: throw new AppValidationException("Некорректное значение robotCode"); } } #region Old // /// // /// Получить задание для робота по коду робота и по статусу задания // /// // /// // /// // /// // [HttpGet(ApiRoutes.RobotTask.GetByRobotAndStatusTask)] // public async Task 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 { new ErrorModel { Message = "Ошибка при выдаче задания." } })); // } // } // switch (robotCode) // { // case RobotsEnum.TemplateOrder: // { //RobotTaskTemplateResponse // var robotTaskTemplateResponse = mapper.Map(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, 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 { new ErrorModel { Message = "Ошибка при расчете NextRun" } })); // } // //RobotTaskScheduleResponse // var robotTaskScheduleResponse = mapper.Map(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 { new ErrorModel { Message = "Ошибка при расчете NextRun" } })); // } // robotTaskScheduleResponse.NextStart = EsppScheduleHelpers.GetNextRun(nextRunWithRobotTz); // robotTaskScheduleResponse.GenerationTime = EsppScheduleHelpers.GetGenerationTime(nextRunWithRobotTz); // return Ok(new Response(robotTaskScheduleResponse, true)); // } // default: // break; // } // return BadRequest(); // } // /// // /// Обоновить NextRun если он устарел // /// // /// // /// // private async Task 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 // /// Устанавливаем статус "Робот взял в работу", пишем в историю работы роботов инф о начале работ // /// // /// // /// // private async Task 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; // return true; // } // /// // /// Приводит переносы строк в тексте к формату CRLF (\r\n) // /// // /// Исходный текст // /// Текст с унифицированными переносами строк // private string NormalizeLineEndingsToCrlf(string? text) // { // if (string.IsNullOrEmpty(text)) // return string.Empty; // // Заменяем любые варианты переносов (\r\n, \r, \n) на единый \r\n // return System.Text.RegularExpressions.Regex.Replace(text, @"\r\n|\r|\n", "\r\n"); // } #endregion } }