feat(core): Логика выдачи задания роботу для переименованных шаблонов

This commit is contained in:
Mikhail Trubnikov
2026-07-22 16:02:04 +10:00
parent 0b0c0b04ad
commit 1cc2beb9ca
4 changed files with 297 additions and 72 deletions

View File

@@ -49,5 +49,11 @@ namespace PARR.Core.Repositories.Interfaces
/// <param name="id"></param> /// <param name="id"></param>
/// <returns></returns> /// <returns></returns>
Task<bool> SetInProgressStatusAsync(Guid id); Task<bool> SetInProgressStatusAsync(Guid id);
/// <summary>
/// Установить статус робота - Ошибка, и поставить максимальное значение попыток
/// </summary>
/// <param name="configuration"></param>
void SetErrorRobotStatusAndMaxAttempts(RobotConfiguration configuration);
} }
} }

View File

@@ -1,11 +1,15 @@
using AutoMapper; using AutoMapper;
using InfluxDB.Client.Api.Domain;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using PARR.BLL.Helpers; using PARR.BLL.Helpers;
using PARR.Core.Repositories.Interfaces; using PARR.Core.Repositories.Interfaces;
using PARR.Core.Repositories.Interfaces.TemplateRepositories;
using PARR.Core.Services.NextRunServices; using PARR.Core.Services.NextRunServices;
using PARR.Core.Services.RobotTask.Interfaces; using PARR.Core.Services.RobotTask.Interfaces;
using PARR.Core.Services.RobotTask.Models;
using PARR.Core.Services.Shortcodes; using PARR.Core.Services.Shortcodes;
using PARR.Domain.Common.Template;
using PARR.Domain.DTOs.RobotTask; using PARR.Domain.DTOs.RobotTask;
using PARR.Domain.Entities; using PARR.Domain.Entities;
using PARR.Domain.Entities.Base.History; using PARR.Domain.Entities.Base.History;
@@ -20,16 +24,18 @@ namespace PARR.Core.Services.RobotTask.Implementations
{ {
/// <summary> /// <summary>
/// Количество заданий которые рассматриваем для взятия в работу. /// Количество заданий которые рассматриваем для взятия в работу.
/// Рекомендованное значение, кол-во роботов * 3
/// </summary> /// </summary>
private readonly int TakeTasks = 10; private readonly int TakeTasks = 15 * 3;
private readonly ILogger<RobotTaskService> logger; private readonly ILogger<RobotTaskService> _logger;
private readonly IRobotConfigurationRepository robotConfigurationRepository; private readonly IRobotConfigurationRepository _robotConfigurationRepository;
private readonly SettingsFromDb settingsFromDb; private readonly SettingsFromDb _settingsFromDb;
private readonly IRobotHistoryRepository robotHistoryRepository; private readonly IRobotHistoryRepository _robotHistoryRepository;
private readonly IMapper mapper; private readonly IMapper _mapper;
private readonly IShortcodesService shortcodesService; private readonly IShortcodesService _shortcodesService;
private readonly INextRunService nextRunService; private readonly INextRunService _nextRunService;
private readonly ITemplateRenamePendingRepository _templateRenamePendingRepository;
public RobotTaskService( public RobotTaskService(
ILogger<RobotTaskService> logger, ILogger<RobotTaskService> logger,
@@ -38,16 +44,18 @@ namespace PARR.Core.Services.RobotTask.Implementations
IRobotHistoryRepository robotHistoryRepository, IRobotHistoryRepository robotHistoryRepository,
IMapper mapper, IMapper mapper,
IShortcodesService shortcodesService, IShortcodesService shortcodesService,
INextRunService nextRunService INextRunService nextRunService,
ITemplateRenamePendingRepository templateRenamePendingRepository
) )
{ {
this.logger = logger; this._logger = logger;
this.robotConfigurationRepository = robotConfigurationRepository; this._robotConfigurationRepository = robotConfigurationRepository;
this.settingsFromDb = settingsFromDb; this._settingsFromDb = settingsFromDb;
this.robotHistoryRepository = robotHistoryRepository; this._robotHistoryRepository = robotHistoryRepository;
this.mapper = mapper; this._mapper = mapper;
this.shortcodesService = shortcodesService; this._shortcodesService = shortcodesService;
this.nextRunService = nextRunService; this._nextRunService = nextRunService;
_templateRenamePendingRepository = templateRenamePendingRepository;
} }
@@ -55,19 +63,19 @@ namespace PARR.Core.Services.RobotTask.Implementations
{ {
var templateTask = await GetTaskAsync(RobotsEnum.TemplateOrder, taskStatusCode, acquireTask, robotIp, robotId, TimeSpan.Zero); var templateTask = await GetTaskAsync(RobotsEnum.TemplateOrder, taskStatusCode, acquireTask, robotIp, robotId, TimeSpan.Zero);
var task = mapper.Map<RobotTaskTemplate>(templateTask); var task = _mapper.Map<RobotTaskTemplate>(templateTask);
task = task with { FullDescription = NormalizeLineEndingsToCrlf(await shortcodesService.ApplyShortcodesAsync(task.FullDescription, templateTask.Template!)) }; task = task with { FullDescription = NormalizeLineEndingsToCrlf(await _shortcodesService.ApplyShortcodesAsync(task.FullDescription, templateTask.Template!)) };
task = task with { ShortDescription = await shortcodesService.ApplyShortcodesAsync(task.ShortDescription, templateTask.Template!) }; task = task with { ShortDescription = await _shortcodesService.ApplyShortcodesAsync(task.ShortDescription, templateTask.Template!) };
task = task with { Solution = NormalizeLineEndingsToCrlf(await shortcodesService.ApplyShortcodesAsync(task.Solution, templateTask.Template!)) }; task = task with { Solution = NormalizeLineEndingsToCrlf(await _shortcodesService.ApplyShortcodesAsync(task.Solution, templateTask.Template!)) };
task = task with { TnkName = await shortcodesService.ApplyShortcodesAsync(task.TnkName, templateTask.Template!) }; task = task with { TnkName = await _shortcodesService.ApplyShortcodesAsync(task.TnkName, templateTask.Template!) };
task = task with { WorkName = await shortcodesService.ApplyShortcodesAsync(task.WorkName, templateTask.Template!) }; task = task with { WorkName = await _shortcodesService.ApplyShortcodesAsync(task.WorkName, templateTask.Template!) };
task = task with { WorkGroup = await shortcodesService.ApplyShortcodesAsync(task.WorkGroup, templateTask.Template!) }; task = task with { WorkGroup = await _shortcodesService.ApplyShortcodesAsync(task.WorkGroup, templateTask.Template!) };
task = task with { ResponseArea = await shortcodesService.ApplyShortcodesAsync(task.ResponseArea, templateTask.Template!) }; task = task with { ResponseArea = await _shortcodesService.ApplyShortcodesAsync(task.ResponseArea, templateTask.Template!) };
task = task with { ClosingCode = settingsFromDb.ClosingCode }; task = task with { ClosingCode = _settingsFromDb.ClosingCode };
task = task with { Initiator = settingsFromDb.Initiator }; task = task with { Initiator = _settingsFromDb.Initiator };
task = task with { Category = settingsFromDb.Category }; task = task with { Category = _settingsFromDb.Category };
return task; return task;
} }
@@ -82,22 +90,22 @@ namespace PARR.Core.Services.RobotTask.Implementations
var resultUpdateNextRun = await UpdateNextRunAsync(scheduleTask, historyInitiator); var resultUpdateNextRun = await UpdateNextRunAsync(scheduleTask, historyInitiator);
if (!resultUpdateNextRun) if (!resultUpdateNextRun)
{ {
logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}", scheduleTask.TemplateId); _logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}", scheduleTask.TemplateId);
throw new NextRunException($"Ошибка при расчете NextRun для templateId: {scheduleTask.TemplateId}"); throw new NextRunException($"Ошибка при расчете NextRun для templateId: {scheduleTask.TemplateId}");
} }
var task = mapper.Map<RobotTaskSchedule>(scheduleTask); var task = _mapper.Map<RobotTaskSchedule>(scheduleTask);
task = task with { Timezone = settingsFromDb.EsppScheduleTimezone }; task = task with { Timezone = _settingsFromDb.EsppScheduleTimezone };
task = task with { WorkGroup = await shortcodesService.ApplyShortcodesAsync(task.WorkGroup, scheduleTask.Template!) }; task = task with { WorkGroup = await _shortcodesService.ApplyShortcodesAsync(task.WorkGroup, scheduleTask.Template!) };
task = task with { ResponseArea = await shortcodesService.ApplyShortcodesAsync(task.ResponseArea, scheduleTask.Template!) }; task = task with { ResponseArea = await _shortcodesService.ApplyShortcodesAsync(task.ResponseArea, scheduleTask.Template!) };
//nextRun в часовой зоне УЗ Робота ЕСПП //nextRun в часовой зоне УЗ Робота ЕСПП
var nextRunWithRobotTz = scheduleTask.Template!.NextRun.Add(nextRunService.GetEsppAccountOffset()); var nextRunWithRobotTz = scheduleTask.Template!.NextRun.Add(_nextRunService.GetEsppAccountOffset());
//на всякий случай еще раз проверяем, что дата не устарела и отправляем задание //на всякий случай еще раз проверяем, что дата не устарела и отправляем задание
if (nextRunWithRobotTz < DateTimeOffset.UtcNow) if (nextRunWithRobotTz < DateTimeOffset.UtcNow)
{ {
logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}, итоговое значение для робота, меньше чем сейчас {nextRunWithRobotTz}<{now}", _logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}, итоговое значение для робота, меньше чем сейчас {nextRunWithRobotTz}<{now}",
task.TemplateId, nextRunWithRobotTz, DateTimeOffset.UtcNow); task.TemplateId, nextRunWithRobotTz, DateTimeOffset.UtcNow);
throw new NextRunException($"Ошибка при расчете NextRun для templateId: {scheduleTask.TemplateId}"); throw new NextRunException($"Ошибка при расчете NextRun для templateId: {scheduleTask.TemplateId}");
} }
@@ -105,7 +113,7 @@ namespace PARR.Core.Services.RobotTask.Implementations
task = task with { NextStart = EsppScheduleHelpers.GetNextRun(nextRunWithRobotTz) }; task = task with { NextStart = EsppScheduleHelpers.GetNextRun(nextRunWithRobotTz) };
task = task with { GenerationTime = EsppScheduleHelpers.GetGenerationTime(nextRunWithRobotTz) }; task = task with { GenerationTime = EsppScheduleHelpers.GetGenerationTime(nextRunWithRobotTz) };
task = task with { RepeatRange = settingsFromDb.ScheduleRepeatRange }; task = task with { RepeatRange = _settingsFromDb.ScheduleRepeatRange };
task = task with { }; task = task with { };
return task; return task;
@@ -125,7 +133,7 @@ namespace PARR.Core.Services.RobotTask.Implementations
private async Task<RobotConfiguration> GetTaskAsync(RobotsEnum robotCode, TaskStatusEnum taskStatusCode, bool acquireTask, string? robotIp, string? robotId, TimeSpan scheduleCooldownDuration) private async Task<RobotConfiguration> GetTaskAsync(RobotsEnum robotCode, TaskStatusEnum taskStatusCode, bool acquireTask, string? robotIp, string? robotId, TimeSpan scheduleCooldownDuration)
{ {
// 1. Ищем все задания с превышенным кол-вом попыток и просроченным временем, ставим им статус ошибки // 1. Ищем все задания с превышенным кол-вом попыток и просроченным временем, ставим им статус ошибки
await robotConfigurationRepository.MarkExpiredTasksAsFailedAsync(settingsFromDb.RobotAttemptsNumber, settingsFromDb.RobotWaitTime); await _robotConfigurationRepository.MarkExpiredTasksAsFailedAsync(_settingsFromDb.RobotAttemptsNumber, _settingsFromDb.RobotWaitTime);
// 2. Ищем доступные задания // 2. Ищем доступные задания
@@ -148,7 +156,7 @@ namespace PARR.Core.Services.RobotTask.Implementations
{ {
// Берем первую задачу из списка доступных // Берем первую задачу из списка доступных
acquiredTaskId = availableTasks.First(); acquiredTaskId = availableTasks.First();
logger.LogDebug("Задача не требует захвата, взята первая из доступных: {TaskId}", acquiredTaskId); _logger.LogDebug("Задача не требует захвата, взята первая из доступных: {TaskId}", acquiredTaskId);
} }
@@ -168,20 +176,14 @@ namespace PARR.Core.Services.RobotTask.Implementations
/// <returns></returns> /// <returns></returns>
private async Task<List<Guid>> GetAvailableTasksAsync(RobotsEnum robotCode, TaskStatusEnum taskStatusCode, TimeSpan scheduleCooldownDuration) private async Task<List<Guid>> GetAvailableTasksAsync(RobotsEnum robotCode, TaskStatusEnum taskStatusCode, TimeSpan scheduleCooldownDuration)
{ {
var query = robotConfigurationRepository.Get() var query = _robotConfigurationRepository.Get()
.AsNoTracking() .AsNoTracking()
.Where(t => t.RobotCode == (int)robotCode/* && t.TaskStatusCode == (int)taskStatusCode*/); .Where(t => t.RobotCode == (int)robotCode);
// Если это задание для робота расписаний // Если это задание для робота расписаний
if (robotCode == RobotsEnum.ScheduleOrder) if (robotCode == RobotsEnum.ScheduleOrder)
{ {
// Выбираем только записи с созданными шаблонами (у которых статус 30), а только потом ищем у них расписания // Выбираем только записи с созданными шаблонами (у которых статус 30), а только потом ищем у них расписания
#region Старый не оптимизированный запрос
//var createdTemplates = robotConfigurationRepository.Get()
// .Where(t => t.RobotCode == (int)RobotsEnum.TemplateOrder && t.TaskStatusCode == (int)TaskStatusEnum.Ok)
// .Select(t => t.TemplateId);
//query = query.Where(t => createdTemplates.Contains(t.TemplateId));
#endregion
query = query.Where(t => t.Template!.RobotConfigurations.Any(x => x.RobotCode == (int)RobotsEnum.TemplateOrder && x.TaskStatusCode == (int)TaskStatusEnum.Ok)); query = query.Where(t => t.Template!.RobotConfigurations.Any(x => x.RobotCode == (int)RobotsEnum.TemplateOrder && x.TaskStatusCode == (int)TaskStatusEnum.Ok));
@@ -200,20 +202,22 @@ namespace PARR.Core.Services.RobotTask.Implementations
// Сортируем по nextRun, чтобы те, у кого nextRun ближе к текущей, выполнились скорее // Сортируем по nextRun, чтобы те, у кого nextRun ближе к текущей, выполнились скорее
query = query.OrderBy(t => t.Template!.NextRun).ThenBy(t => t.Template!.IsActiveSchedule).ThenBy(t => t.Template!.IsActiveTemplate); query = query.OrderBy(t => t.Template!.NextRun).ThenBy(t => t.Template!.IsActiveSchedule).ThenBy(t => t.Template!.IsActiveTemplate);
// Кандидаты заданий // Кандидаты заданий, Id задания и имя шаблона
var tasks = new List<Guid>(); //var tasks = new List<Guid>();
var tasks = new List<RobotTaskDetails>();
// Ещем первые 10 заданий в статусе ОЖИДАНИЕ // Ищем первые TakeTasks заданий в статусе ОЖИДАНИЕ
tasks = await query tasks = await query
.Where(t => .Where(t =>
t.RobotStatusCode == (int)RobotStatusEnum.Wait t.RobotStatusCode == (int)RobotStatusEnum.Wait
&& t.TaskStatusCode == (int)taskStatusCode && t.TaskStatusCode == (int)taskStatusCode
).Take(TakeTasks) ).Take(TakeTasks)
.Select(t => t.Id) //.Select(t => t.Id)
.Select(t => new RobotTaskDetails(t.Id, t.Template!.Name, t.Template.NextRun))
.ToListAsync(); .ToListAsync();
logger.LogDebug("Найдено заданий в статусе 'Ожидание' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString()); _logger.LogDebug("Найдено заданий в статусе 'Ожидание' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString());
if (tasks.Count == 0) if (tasks.Count == 0)
{ {
@@ -222,20 +226,171 @@ namespace PARR.Core.Services.RobotTask.Implementations
// Далее проверяется `LastStatusUpdated`, что время последнего смены статуса не превышает допустимого(берется из настроек, поле `RobotWaitTime`) // Далее проверяется `LastStatusUpdated`, что время последнего смены статуса не превышает допустимого(берется из настроек, поле `RobotWaitTime`)
// и что текущая попытка не больше разрешенной(берется из настроек, поле `RobotAttemptsNumber`) - если это так, берется эта запись. // и что текущая попытка не больше разрешенной(берется из настроек, поле `RobotAttemptsNumber`) - если это так, берется эта запись.
var endDate = DateTimeOffset.UtcNow.Add(-settingsFromDb.RobotWaitTime); var endDate = DateTimeOffset.UtcNow.Add(-_settingsFromDb.RobotWaitTime);
tasks = await query.Where(t => t.RobotStatusCode == (int)RobotStatusEnum.InProgress tasks = await query.Where(t => t.RobotStatusCode == (int)RobotStatusEnum.InProgress
&& t.TaskStatusCode==(int)taskStatusCode && t.TaskStatusCode == (int)taskStatusCode
&& t.AttemptsNumber < settingsFromDb.RobotAttemptsNumber && t.AttemptsNumber < _settingsFromDb.RobotAttemptsNumber
&& t.LastRobotStatusUpdated < endDate) && t.LastRobotStatusUpdated < endDate)
.Take(TakeTasks) .Take(TakeTasks)
.Select(t => t.Id) //.Select(t => t.Id)
.Select(t => new RobotTaskDetails(t.Id, t.Template!.Name, t.Template.NextRun))
.ToListAsync(); .ToListAsync();
logger.LogDebug("Найдено заданий в статусе 'В работе' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString()); _logger.LogDebug("Найдено заданий в статусе 'В работе' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString());
} }
if (robotCode == RobotsEnum.TemplateOrder)
{
// Если запрашиваем шаблоны, смотрим корректируем список заданий в зависимости от статуса переименования.
// Это не относится к расписаниям, потому что у переименованных расписаний статус Updating, а оно не возьмется в работу, пока не обновится шаблон
tasks = await ReplaceTemplateTasksForRenameAsync(tasks, robotCode);
}
return tasks.Select(t => t.TaskId).ToList();
}
/// <summary>
/// Проверяет наличие шаблонов в процессе переименования и заменяет обычные задания на задания по переименованию.
/// Если связанный шаблон не переименован, и у него статус ошибки, целевому шаблону устанавливается статус ошибки.
/// </summary>
/// <param name="tasks"></param>
/// <returns></returns>
private async Task<List<RobotTaskDetails>> ReplaceTemplateTasksForRenameAsync(List<RobotTaskDetails> tasks, RobotsEnum robotCode)
{
if (tasks.Count == 0 || robotCode != RobotsEnum.TemplateOrder)
return tasks; return tasks;
// Ищем есть ли связанные шаблоны с таким имененм на переименование
var taskTemplateNames = tasks.Select(t => t.TemplateName);
var templatesToRename = await _templateRenamePendingRepository.Get().AsNoTracking()
.Where(t => taskTemplateNames.Contains(t.OldName))
.ToListAsync();
_logger.LogDebug("Шаблонов для переименования в текущем задании: {Count} шт.", templatesToRename.Count);
if (templatesToRename.Count == 0)
return tasks;
// Ищем связанные переименованные шаблоны по ИД, смотрим можем ли взять их в работу
var renameTemplateIds = templatesToRename.Select(t => t.TemplateId).ToList();
var renameTasks = await _robotConfigurationRepository.Get()
.AsNoTracking()
.Include(t => t.Template)
.Where(t =>
t.RobotCode == (int)robotCode
&& renameTemplateIds.Contains(t.TemplateId)
// Это может быть только обновление. Так как переименования для создаваемого шаблона быть не может
&& t.TaskStatusCode == (int)TaskStatusEnum.Updating
).ToListAsync();
// Ищем связанные задания которые в ошибке, и ставим целевому шаблону задание в ошибку
var errorTasks = renameTasks.Where(t => t.RobotStatusCode == (int)RobotStatusEnum.Error && t.AttemptsNumber >= _settingsFromDb.RobotAttemptsNumber).ToList();
var tasksToSetErrorStatus = new List<Guid>();
if (errorTasks.Count > 0)
{
_logger.LogDebug("Найдено связанных заданий на переименование с ошибками: {ErrorCount}. Будем целевым шаблонам тоже ставить статус ошибки.", errorTasks.Count);
var errorTemplateNames = errorTasks.Select(t => t.Template!.Name).ToList();
// Берем целевые таски, находим в них задания которым надо поставить ошибку
tasksToSetErrorStatus = tasks.Where(t => errorTemplateNames.Contains(t.TemplateName)).Select(t => t.TaskId).ToList();
// Устанавливаем ошибку целевым + пишем комментарий от робота + нажимаем комит
var logMessage = "[RobotTaskService] Установлен статус ошибки, так как не переименован связанный шаблон";
await SetErrorStatusAsync(tasksToSetErrorStatus, logMessage);
}
var endDate = DateTimeOffset.UtcNow.Add(-_settingsFromDb.RobotWaitTime);
// Смотрим статусы роботов, можно взять в работу, только если (RobotStatus == Wait) или (InpRogress но которые еще не просрочены)
var allowedTasks = renameTasks.Where(t =>
t.RobotStatusCode == (int)RobotStatusEnum.Wait
|| (t.RobotStatusCode == (int)RobotStatusEnum.InProgress && t.AttemptsNumber < _settingsFromDb.RobotAttemptsNumber && t.LastRobotStatusUpdated < endDate)
);
// Формируем список заданий
var originalCount = tasks.Count; // Запоминаем сколько было изначально
// Из исходных тасков удалить те которым установлен статус ошибки
var filteredOriginalTasks = tasks.Where(t => !tasksToSetErrorStatus.Contains(t.TaskId)).ToList();
var errorCount = tasksToSetErrorStatus.Count; // Столько ушло в ошибку
// Определяем, какие имена шаблонов мы БУДЕМ подменять на задачи переименования
var allowedRenameTemplateNames = allowedTasks.Select(t => t.Template!.Name).ToList();
// Из исходных удаляем те, которые мы сейчас заменим (подменим)
var resultsTasks = filteredOriginalTasks.Where(t => !allowedRenameTemplateNames.Contains(t.TemplateName)).ToList();
// Считаем сколько именно задач мы ВЫКИНУЛИ из исходного списка ради подмены
var replacedCount = filteredOriginalTasks.Count - resultsTasks.Count;
// В исходные добавляем подменные таски для переименования (мапим их в RobotTaskDetails)
var mappedRenameTasks = allowedTasks.Select(t => new RobotTaskDetails(t.Id, t.Template!.Name, t.Template.NextRun)).ToList();
resultsTasks.AddRange(mappedRenameTasks);
var injectedCount = mappedRenameTasks.Count; // Столько задач переименования добавили взамен
// Логируем итоговую статистику трансформации пула задач
// TODO: Позже этот лог можно понизить до Debug!!!
_logger.LogInformation(
"Трансформация пула задач завершена. Исходных: {OriginalCount} шт. " +
"Отклонено (ошибка): {ErrorCount} шт. Удалено обычных для подмены: {ReplacedCount} шт. " +
"Внедрено задач переименования: {InjectedCount} шт. Итого к выдаче: {FinalCount} шт.",
originalCount, errorCount, replacedCount, injectedCount, resultsTasks.Count);
// Затем отсортируем по NextRun, чтоб ближайшие были выше
return resultsTasks.OrderBy(t => t.NextRun).ToList();
}
/// <summary>
/// Установить статус задания - ошибка
/// </summary>
/// <param name="taskIds"></param>
/// <returns></returns>
private async Task SetErrorStatusAsync(List<Guid> taskIds, string logMessage)
{
var tasks = await _robotConfigurationRepository.Get()
.Include(t => t.Template)
.Where(t => taskIds.Contains(t.Id)).ToListAsync();
if (tasks.Count == 0)
return;
foreach (var task in tasks)
{
// Так как это целевой шаблон, то ставим ему сразу максимальное кол-во попыток и ошибку, чтоб больше он не выдавался в заданиях, пока не исправим связанный
// Устанавливаем статус ошибки
_robotConfigurationRepository.SetErrorRobotStatusAndMaxAttempts(task);
// Пишем в лог роботу
var history = new RobotHistory
{
Id = Guid.NewGuid(),
HistoryLevel = (int)RobotStatusEnum.Error,
TaskStatusCode = task.TaskStatusCode,
RobotConfigurationId = task.Id,
RobotIp = null,
RobotId = ParrComponentsEnum.Api.ToString(),
RobotMessage = logMessage
};
await _robotHistoryRepository.CreateAsync(history);
_logger.LogInformation("Для целевого задания {TaskId}, по переименованию шаблона в {TemplateName}, установлен статус ошибки, так как связанное задание со старым шаблоном со статусом ошибка.", task.Id, task.Template!.Name);
}
if (await _robotHistoryRepository.CommitAsync())
{
_logger.LogDebug("Установлен статус Ошибка, для заданий {TaskCount} шт.", tasks.Count);
}
else
{
_logger.LogError("Ошибка при установке статуса задания Ошибка, для заданий {TaskCount} шт.", tasks.Count);
}
} }
@@ -248,12 +403,12 @@ namespace PARR.Core.Services.RobotTask.Implementations
{ {
foreach (var taskId in tasks) foreach (var taskId in tasks)
{ {
var isChangedStatus = await robotConfigurationRepository.SetInProgressStatusAsync(taskId); var isChangedStatus = await _robotConfigurationRepository.SetInProgressStatusAsync(taskId);
if (isChangedStatus) if (isChangedStatus)
{ {
logger.LogDebug("Захвачена задача {TaskId}", taskId); _logger.LogDebug("Захвачена задача {TaskId}", taskId);
var task = await robotConfigurationRepository.Get() var task = await _robotConfigurationRepository.Get()
.AsNoTracking() .AsNoTracking()
.FirstAsync(t => t.Id == taskId); .FirstAsync(t => t.Id == taskId);
@@ -268,18 +423,18 @@ namespace PARR.Core.Services.RobotTask.Implementations
RobotId = robotId RobotId = robotId
}; };
if (!await robotHistoryRepository.CreateAsync(history) || !await robotHistoryRepository.CommitAsync()) if (!await _robotHistoryRepository.CreateAsync(history) || !await _robotHistoryRepository.CommitAsync())
throw new DbErrorException("Ошибка при добавлении истории робота, при взятии задания в работу."); throw new DbErrorException("Ошибка при добавлении истории робота, при взятии задания в работу.");
return taskId; return taskId;
} }
else else
{ {
logger.LogDebug("Не удалось захватить задачу {TaskId}", taskId); _logger.LogDebug("Не удалось захватить задачу {TaskId}", taskId);
} }
} }
logger.LogDebug("Не удалось захватить ни одну из доступных задач для робота"); _logger.LogDebug("Не удалось захватить ни одну из доступных задач для робота");
return null; return null;
} }
@@ -293,7 +448,7 @@ namespace PARR.Core.Services.RobotTask.Implementations
/// <returns></returns> /// <returns></returns>
private async Task<RobotConfiguration> GetTaskWithAllDataAsync(Guid taskId, RobotsEnum robotCode) private async Task<RobotConfiguration> GetTaskWithAllDataAsync(Guid taskId, RobotsEnum robotCode)
{ {
IQueryable<RobotConfiguration> query = robotConfigurationRepository.Get() IQueryable<RobotConfiguration> query = _robotConfigurationRepository.Get()
//.AsNoTracking() // нужно обязательно трекать, так как может измениться nextRun и его нужно будет сохранить //.AsNoTracking() // нужно обязательно трекать, так как может измениться nextRun и его нужно будет сохранить
.AsSingleQuery() .AsSingleQuery()
// Общие инклуды для шаблонов и расписаний // Общие инклуды для шаблонов и расписаний
@@ -372,23 +527,23 @@ namespace PARR.Core.Services.RobotTask.Implementations
var template = task.Template!; var template = task.Template!;
//var nextRun = await esppScheduleTransformService.GetNextDateAsync(template.Job!.GroupId, template!.Job!.Group!.ReferenceDate); //var nextRun = await esppScheduleTransformService.GetNextDateAsync(template.Job!.GroupId, template!.Job!.Group!.ReferenceDate);
var nextRun = await nextRunService.GetNextRunForTemplateAsync(template.Id, false); var nextRun = await _nextRunService.GetNextRunForTemplateAsync(template.Id, false);
if (!nextRun.HasValue) if (!nextRun.HasValue)
{ {
logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun=null, ошибка в расчетах.", template.Id); _logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun=null, ошибка в расчетах.", template.Id);
return false; return false;
} }
if (nextRun.Value < DateTimeOffset.UtcNow) if (nextRun.Value < DateTimeOffset.UtcNow)
{ {
logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun<Now [{nextRun}<{now}], ошибка в расчетах.", template.Id, nextRun.Value, DateTimeOffset.UtcNow); _logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun<Now [{nextRun}<{now}], ошибка в расчетах.", template.Id, nextRun.Value, DateTimeOffset.UtcNow);
return false; return false;
} }
if (nextRun != template.NextRun) if (nextRun != template.NextRun)
{ {
logger.LogDebug($"Для шаблона id {template.Id} обновляю nextRun, новое значение {nextRun}, старое значение {template.NextRun}"); _logger.LogDebug($"Для шаблона id {template.Id} обновляю nextRun, новое значение {nextRun}, старое значение {template.NextRun}");
template.LastRun = template.NextRun; template.LastRun = template.NextRun;
template.NextRun = nextRun.Value; template.NextRun = nextRun.Value;
@@ -399,7 +554,7 @@ namespace PARR.Core.Services.RobotTask.Implementations
? suffix ? suffix
: $"{historyInitiator.InitiatorComment}. {suffix}"; : $"{historyInitiator.InitiatorComment}. {suffix}";
if (!await robotConfigurationRepository.CommitAsync(historyInitiator)) if (!await _robotConfigurationRepository.CommitAsync(historyInitiator))
throw new DbErrorException("Ошибка при сохранении изменения NextRun"); throw new DbErrorException("Ошибка при сохранении изменения NextRun");
} }

View File

@@ -0,0 +1,9 @@
namespace PARR.Core.Services.RobotTask.Models
{
/// <summary>
/// Модель задания для робота
/// </summary>
/// <param name="TaskId"></param>
/// <param name="TemplateName"></param>
internal record RobotTaskDetails(Guid TaskId, string TemplateName, DateTimeOffset NextRun);
}

View File

@@ -1,9 +1,11 @@
using Microsoft.EntityFrameworkCore; using InfluxDB.Client.Api.Domain;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using PARR.Core.Repositories.Interfaces; using PARR.Core.Repositories.Interfaces;
using PARR.DAL.Context; using PARR.DAL.Context;
using PARR.DAL.Repositories.Base; using PARR.DAL.Repositories.Base;
using PARR.Domain.Entities; using PARR.Domain.Entities;
using PARR.Domain.Entities.RobotEntities;
using PARR.Domain.Enums; using PARR.Domain.Enums;
using PARR.Domain.Settings; using PARR.Domain.Settings;
@@ -94,8 +96,8 @@ namespace PARR.DAL.Repositories
configuration.AttemptsNumber++; configuration.AttemptsNumber++;
configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow; configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
break; break;
//case RobotStatusEnum.Error: case RobotStatusEnum.Error:
// break; break;
case RobotStatusEnum.Complete: case RobotStatusEnum.Complete:
configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow; configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
break; break;
@@ -108,6 +110,14 @@ namespace PARR.DAL.Repositories
} }
} }
public void SetErrorRobotStatusAndMaxAttempts(RobotConfiguration configuration)
{
ChangeRobotStatus(RobotStatusEnum.Error, configuration);
configuration.AttemptsNumber = settingsFromDb.RobotAttemptsNumber;
configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
}
public async Task<bool> SetInProgressStatusAsync(Guid id) public async Task<bool> SetInProgressStatusAsync(Guid id)
{ {
@@ -175,10 +185,55 @@ namespace PARR.DAL.Repositories
logger.LogInformation($"Устанавливаю RobotStatus: {RobotStatusEnum.Error} для RobotConfigurationId {item.Id}"); logger.LogInformation($"Устанавливаю RobotStatus: {RobotStatusEnum.Error} для RobotConfigurationId {item.Id}");
}); });
#region Ищем, есть ли связанные шаблоны, которые должны переименоваться, им тоже нужно установить статус ошибки, но только для Шаблонов
var linksCount = 0;
var templateIds = configObjs.Where(t => t.RobotCode == (int)RobotsEnum.TemplateOrder).Select(t => t.TemplateId).ToList();
if (templateIds.Any())
{
// По этим именам находим целевые шаблоны, берем их задания, и ставим им статус Ошибка + записать в лог робота
var configs = await EntitySet
.Where(t => t.RobotCode == (int)RobotsEnum.TemplateOrder
&& t.RobotStatusCode != (int)RobotStatusEnum.Error
&& EntityContext.Templates.Any(tmpl =>
templateIds.Contains(tmpl.Id)
&& tmpl.TemplateRenamePending != null
&& tmpl.TemplateRenamePending.OldName == t.Template!.Name))
.ToListAsync();
linksCount = configs.Count;
foreach (var item in configs)
{
SetErrorRobotStatusAndMaxAttempts(item);
// Пишем в лог роботу
var history = new RobotHistory
{
Id = Guid.NewGuid(),
HistoryLevel = (int)RobotStatusEnum.Error,
TaskStatusCode = item.TaskStatusCode,
RobotConfigurationId = item.Id,
RobotIp = null,
RobotId = ParrComponentsEnum.Api.ToString(),
RobotMessage = "[RobotConfigurationRepository] Установлен статус ошибки, так как не переименован связанный шаблон"
};
// Синхронный Add работает быстрее и безопаснее внутри цикла
EntityContext.RobotHistories.Add(history);
logger.LogInformation($"Устанавливаю RobotStatus: {RobotStatusEnum.Error} для RobotConfigurationId {item.Id}");
}
}
#endregion
var result = await CommitAsync(); var result = await CommitAsync();
if (!result) if (!result)
logger.LogError($"Ошибка при сохранении изменений RobotStatus для RobotConfigurationId: item.Id, RobotStatus: {RobotStatusEnum.Error}"); logger.LogError("Ошибка при сохранении изменений RobotStatus для просроченных заданий в количестве {Count} шт.", configObjs.Count + linksCount);
//else
// logger.LogInformation("Успешно обработано и переведено в статус Ошибки просроченных заданий: {Count} шт.", configObjs.Count + linksCount);
} }