Files
parr_api/PARR.API/Controllers/V1/RobotTaskController.cs

246 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
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.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<RobotTaskController> logger;
private readonly IClientService clientService;
private readonly IRobotHistoryService robotHistoryService;
public RobotTaskController(
IMapper mapper,
SettingsFromDb settingsFromDb,
IRobotConfigurationService robotConfigurationService,
IEsppScheduleTransformService esppScheduleTransformService,
ILogger<RobotTaskController> logger,
IClientService clientService,
IRobotHistoryService robotHistoryService
)
{
this.mapper = mapper;
this.settingsFromDb = settingsFromDb;
this.robotConfigurationService = robotConfigurationService;
this.esppScheduleTransformService = esppScheduleTransformService;
this.logger = logger;
this.clientService = clientService;
this.robotHistoryService = robotHistoryService;
}
/// <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.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.WorkGroup).ThenInclude(t => t!.ResponseArea)
.Include(t => t.Template)
.ThenInclude(a => a!.ApplicationsInWork)
.ThenInclude(w => w!.Work)
.ThenInclude(t => t!.Tnk)
.ThenInclude(s => s!.Subprocess)
.ThenInclude(p => p!.Process);
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.WorkGroup)
.Include(t => t.Template)
.ThenInclude(t => t!.ApplicationsInWork)
.ThenInclude(t => t!.EsppSchValues)
.ThenInclude(t => t!.EsppSchTypeConfig)
.ThenInclude(t => t!.EsppSchTypeSchedule);
//выбираем только записи с созданными шаблонами (у которых статус 20 или 30), а только потом у них ищем расписания
// сначала находим шаблоны со статусом 30
var createdTemplates = robotConfigurationService.Get()
.Where(t => t.RobotCode == (int)RobotsEnum.TemplateOrder && (t.TaskStatusCode == (int)TaskStatusEnum.Updating || 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();
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
return Ok(new Response<RobotTaskTemplateResponse>(mapper.Map<RobotTaskTemplateResponse>(task), true));
case RobotsEnum.ScheduleOrder:
// если был запрос на расписание, проверяем у него nextRun, lastRun, обновляем их
await UpdateLastNextRunDate(task);
//RobotTaskScheduleResponse
var response = mapper.Map<RobotTaskScheduleResponse>(task);
return Ok(new Response<RobotTaskScheduleResponse>(response, true));
default:
break;
}
return BadRequest();
}
private async Task UpdateLastNextRunDate(RobotConfiguration task)
{
var template = task.Template!;
// передаем LastRun, если его нет, то NextRun
//var nextRun = await esppScheduleTransformService.GetNextDateAsync(task.Template!.ApplicationInWorkId, task.Template.ApplicationsInWork!.LastRun ?? task.Template.ApplicationsInWork.NextRun);
// всегда считаем по nextRun
var nextRun = await esppScheduleTransformService.GetNextDateAsync(template.ApplicationInWorkId, template.NextRun);
if (nextRun != template.NextRun)
{
logger.LogDebug($"Для шаблона id {template.Id} обновляю nextRun, новое значение {nextRun}, старое значение {template.NextRun}");
if (template.NextRun < DateTimeOffset.UtcNow)
{
logger.LogDebug($"Для шаблона id {template.Id} обновляю lastRun, новое значение {template.NextRun}, старое значение {template.LastRun}");
template.LastRun = template.NextRun;
}
template.NextRun = nextRun;
await robotConfigurationService.CommitAsync();
}
#region Old, до выноса lastRun, nextRun в templates
//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();
//}
#endregion
}
/// <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, ref 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;
}
}
}