using AutoMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using PARR.API.Contracts.V1; using PARR.API.Contracts.V1.Responses; using PARR.API.Contracts.V1.Responses.Base; using PARR.API.Controllers.V1.Base; using PARR.Constants; using PARR.DAL.Contracts; using PARR.DAL.Models; using PARR.DAL.Services.Interfaces; using PARR.DAL.TransformServices; namespace PARR.API.Controllers.V1 { [Authorize(Roles = ParrRoles.EsppRobot.RoleOrAdmin)] public class RobotTaskController : BaseApiController { private readonly IMapper mapper; private readonly SettingsFromDb settingsFromDb; private readonly IRobotConfigurationService robotConfigurationService; private readonly IEsppScheduleTransformService esppScheduleTransformService; private readonly ILogger logger; public RobotTaskController( IMapper mapper, SettingsFromDb settingsFromDb, IRobotConfigurationService robotConfigurationService, IEsppScheduleTransformService esppScheduleTransformService, ILogger logger ) { this.mapper = mapper; this.settingsFromDb = settingsFromDb; this.robotConfigurationService = robotConfigurationService; this.esppScheduleTransformService = esppScheduleTransformService; this.logger = logger; } /// /// Получить задание для робота по коду робота и по статусу задания /// /// /// /// [HttpGet(ApiRoutes.RobotTask.GetByRobotAndStatusTask)] public async Task GetByRobotAndStatusTask([FromRoute] RobotsEnum robotCode, [FromRoute] TaskStatusEnum taskStatusCode) { //Ищем все задания с превышенным кол-вом попыток и с просроченным временем и ставим им статус ошибки await robotConfigurationService.FindUnfulfilledTaskAndSetRobotErrorStatusAsync(settingsFromDb.RobotAttemptsNumber, settingsFromDb.RobotWaitTime); var query = robotConfigurationService.Get() .Where(t => t.RobotCode == (int)robotCode && t.TaskStatusCode == (int)taskStatusCode); //var query = robotConfigurationService.GetAsync(); switch (robotCode) { case RobotsEnum.TemplateOrder: // шаблоны query = query.Include(t => t.Template) .ThenInclude(t => t!.Host).ThenInclude(t => t!.ResponseArea) .ThenInclude(t => t!.Hosts).ThenInclude(t => t.WorkGroupItem) .Include(t => t.Template) .ThenInclude(a => a!.ApplicationsInWork) .ThenInclude(w => w!.Work) .ThenInclude(t => t!.Tnk) .ThenInclude(s => s!.Subprocess) .ThenInclude(p => p!.Process); //.AsSplitQuery(); break; case RobotsEnum.ScheduleOrder: //расписание // тут не делаем AsSplitQuery, не может подтянуть все таблицы query = query.Include(t => t.Template) .ThenInclude(t => t!.Host).ThenInclude(t => t!.ResponseArea) .ThenInclude(t => t!.Hosts).ThenInclude(t => t.WorkGroupItem) .Include(t => t.Template) .ThenInclude(t => t!.ApplicationsInWork) .ThenInclude(t => t!.EsppSchValues) .ThenInclude(t => t!.EsppSchTypeConfig) .ThenInclude(t => t!.EsppSchTypeSchedule); //выбираем только записи с созданными шаблонами (у которых статус 30), а только потом у них ищем расписания // сначала находим шаблоны со статусом 30 var createdTemplates = robotConfigurationService.Get() .Where(t => t.RobotCode == (int)RobotsEnum.TemplateOrder && t.TaskStatusCode == (int)TaskStatusEnum.Ok) .Select(t => t.TemplateId); //находим задания с расписанием по списку шаблонов createdTemplates query = query.Where(t => t.RobotCode == (int)RobotsEnum.ScheduleOrder && createdTemplates.Contains(t.TemplateId)); break; default: break; } 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(); switch (robotCode) { case RobotsEnum.TemplateOrder: //RobotTaskTemplateResponse return Ok(new Response(mapper.Map(task), true)); case RobotsEnum.ScheduleOrder: // если был запрос на расписание, проверяем у него nextRun, lastRun, обновляем их await UpdateLastNextRunDate(task); //RobotTaskScheduleResponse var response = mapper.Map(task); return Ok(new Response(response, true)); default: break; } return BadRequest(); } private async Task UpdateLastNextRunDate(RobotConfiguration task) { var appInWorks = task.Template!.ApplicationsInWork!; // передаем LastRun, если его нет, то NextRun //var nextRun = await esppScheduleTransformService.GetNextDateAsync(task.Template!.ApplicationInWorkId, task.Template.ApplicationsInWork!.LastRun ?? task.Template.ApplicationsInWork.NextRun); // всегда считаем по nextRun var nextRun = await esppScheduleTransformService.GetNextDateAsync(task.Template!.ApplicationInWorkId, task.Template.ApplicationsInWork!.NextRun); if (nextRun != appInWorks.NextRun) { logger.LogDebug($"Для appilcationInWork id {task.Template.ApplicationInWorkId}, шаблона id {task.TemplateId} обновляю nextRun, новое значение {nextRun}, старое значение {appInWorks.NextRun}"); if (appInWorks.NextRun < DateTimeOffset.UtcNow) { logger.LogDebug($"Для appilcationInWork id {task.Template.ApplicationInWorkId}, шаблона id {task.TemplateId} обновляю lastRun, новое значение {appInWorks.NextRun}, старое значение {appInWorks.LastRun}"); appInWorks.LastRun = appInWorks.NextRun; } appInWorks.NextRun = nextRun; await robotConfigurationService.CommitAsync(); } //if (appInWorks.LastRun != null) // if - ерунда какая-то. ошибка видимо. //if (nextRun > DateTimeOffset.UtcNow) // appInWorks.LastRun = appInWorks.NextRun; //appInWorks.NextRun = nextRun; //await robotConfigurationService.CommitAsync(); } } }