From 1cc2beb9ca9174f01c3e0fbcfbd5bb8d58131dc3 Mon Sep 17 00:00:00 2001 From: Mikhail Trubnikov Date: Wed, 22 Jul 2026 16:02:04 +1000 Subject: [PATCH] =?UTF-8?q?feat(core):=20=D0=9B=D0=BE=D0=B3=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B2=D1=8B=D0=B4=D0=B0=D1=87=D0=B8=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D1=80=D0=BE=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=83=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B8?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IRobotConfigurationRepository.cs | 6 + .../Implementations/RobotTaskService.cs | 291 ++++++++++++++---- .../RobotTask/Models/RobotTaskDetails.cs | 9 + .../RobotConfigurationRepository.cs | 63 +++- 4 files changed, 297 insertions(+), 72 deletions(-) create mode 100644 PARR.Core/Services/RobotTask/Models/RobotTaskDetails.cs diff --git a/PARR.Core/Repositories/Interfaces/IRobotConfigurationRepository.cs b/PARR.Core/Repositories/Interfaces/IRobotConfigurationRepository.cs index 5bc2d65a..0160ee45 100644 --- a/PARR.Core/Repositories/Interfaces/IRobotConfigurationRepository.cs +++ b/PARR.Core/Repositories/Interfaces/IRobotConfigurationRepository.cs @@ -49,5 +49,11 @@ namespace PARR.Core.Repositories.Interfaces /// /// Task SetInProgressStatusAsync(Guid id); + + /// + /// Установить статус робота - Ошибка, и поставить максимальное значение попыток + /// + /// + void SetErrorRobotStatusAndMaxAttempts(RobotConfiguration configuration); } } diff --git a/PARR.Core/Services/RobotTask/Implementations/RobotTaskService.cs b/PARR.Core/Services/RobotTask/Implementations/RobotTaskService.cs index a10536b5..3757b276 100644 --- a/PARR.Core/Services/RobotTask/Implementations/RobotTaskService.cs +++ b/PARR.Core/Services/RobotTask/Implementations/RobotTaskService.cs @@ -1,11 +1,15 @@ using AutoMapper; +using InfluxDB.Client.Api.Domain; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PARR.BLL.Helpers; using PARR.Core.Repositories.Interfaces; +using PARR.Core.Repositories.Interfaces.TemplateRepositories; using PARR.Core.Services.NextRunServices; using PARR.Core.Services.RobotTask.Interfaces; +using PARR.Core.Services.RobotTask.Models; using PARR.Core.Services.Shortcodes; +using PARR.Domain.Common.Template; using PARR.Domain.DTOs.RobotTask; using PARR.Domain.Entities; using PARR.Domain.Entities.Base.History; @@ -20,16 +24,18 @@ namespace PARR.Core.Services.RobotTask.Implementations { /// /// Количество заданий которые рассматриваем для взятия в работу. + /// Рекомендованное значение, кол-во роботов * 3 /// - private readonly int TakeTasks = 10; + private readonly int TakeTasks = 15 * 3; - private readonly ILogger logger; - private readonly IRobotConfigurationRepository robotConfigurationRepository; - private readonly SettingsFromDb settingsFromDb; - private readonly IRobotHistoryRepository robotHistoryRepository; - private readonly IMapper mapper; - private readonly IShortcodesService shortcodesService; - private readonly INextRunService nextRunService; + private readonly ILogger _logger; + private readonly IRobotConfigurationRepository _robotConfigurationRepository; + private readonly SettingsFromDb _settingsFromDb; + private readonly IRobotHistoryRepository _robotHistoryRepository; + private readonly IMapper _mapper; + private readonly IShortcodesService _shortcodesService; + private readonly INextRunService _nextRunService; + private readonly ITemplateRenamePendingRepository _templateRenamePendingRepository; public RobotTaskService( ILogger logger, @@ -38,16 +44,18 @@ namespace PARR.Core.Services.RobotTask.Implementations IRobotHistoryRepository robotHistoryRepository, IMapper mapper, IShortcodesService shortcodesService, - INextRunService nextRunService + INextRunService nextRunService, + ITemplateRenamePendingRepository templateRenamePendingRepository ) { - this.logger = logger; - this.robotConfigurationRepository = robotConfigurationRepository; - this.settingsFromDb = settingsFromDb; - this.robotHistoryRepository = robotHistoryRepository; - this.mapper = mapper; - this.shortcodesService = shortcodesService; - this.nextRunService = nextRunService; + this._logger = logger; + this._robotConfigurationRepository = robotConfigurationRepository; + this._settingsFromDb = settingsFromDb; + this._robotHistoryRepository = robotHistoryRepository; + this._mapper = mapper; + this._shortcodesService = shortcodesService; + 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 task = mapper.Map(templateTask); + var task = _mapper.Map(templateTask); - 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 { Solution = NormalizeLineEndingsToCrlf(await shortcodesService.ApplyShortcodesAsync(task.Solution, 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 { WorkGroup = await shortcodesService.ApplyShortcodesAsync(task.WorkGroup, templateTask.Template!) }; - task = task with { ResponseArea = await shortcodesService.ApplyShortcodesAsync(task.ResponseArea, 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 { Solution = NormalizeLineEndingsToCrlf(await _shortcodesService.ApplyShortcodesAsync(task.Solution, 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 { WorkGroup = await _shortcodesService.ApplyShortcodesAsync(task.WorkGroup, templateTask.Template!) }; + task = task with { ResponseArea = await _shortcodesService.ApplyShortcodesAsync(task.ResponseArea, templateTask.Template!) }; - task = task with { ClosingCode = settingsFromDb.ClosingCode }; - task = task with { Initiator = settingsFromDb.Initiator }; - task = task with { Category = settingsFromDb.Category }; + task = task with { ClosingCode = _settingsFromDb.ClosingCode }; + task = task with { Initiator = _settingsFromDb.Initiator }; + task = task with { Category = _settingsFromDb.Category }; return task; } @@ -82,22 +90,22 @@ namespace PARR.Core.Services.RobotTask.Implementations var resultUpdateNextRun = await UpdateNextRunAsync(scheduleTask, historyInitiator); if (!resultUpdateNextRun) { - logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}", scheduleTask.TemplateId); + _logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}", scheduleTask.TemplateId); throw new NextRunException($"Ошибка при расчете NextRun для templateId: {scheduleTask.TemplateId}"); } - var task = mapper.Map(scheduleTask); + var task = _mapper.Map(scheduleTask); - task = task with { Timezone = settingsFromDb.EsppScheduleTimezone }; - 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 { Timezone = _settingsFromDb.EsppScheduleTimezone }; + task = task with { WorkGroup = await _shortcodesService.ApplyShortcodesAsync(task.WorkGroup, scheduleTask.Template!) }; + task = task with { ResponseArea = await _shortcodesService.ApplyShortcodesAsync(task.ResponseArea, scheduleTask.Template!) }; //nextRun в часовой зоне УЗ Робота ЕСПП - var nextRunWithRobotTz = scheduleTask.Template!.NextRun.Add(nextRunService.GetEsppAccountOffset()); + var nextRunWithRobotTz = scheduleTask.Template!.NextRun.Add(_nextRunService.GetEsppAccountOffset()); //на всякий случай еще раз проверяем, что дата не устарела и отправляем задание if (nextRunWithRobotTz < DateTimeOffset.UtcNow) { - logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}, итоговое значение для робота, меньше чем сейчас {nextRunWithRobotTz}<{now}", + _logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}, итоговое значение для робота, меньше чем сейчас {nextRunWithRobotTz}<{now}", task.TemplateId, nextRunWithRobotTz, DateTimeOffset.UtcNow); 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 { GenerationTime = EsppScheduleHelpers.GetGenerationTime(nextRunWithRobotTz) }; - task = task with { RepeatRange = settingsFromDb.ScheduleRepeatRange }; + task = task with { RepeatRange = _settingsFromDb.ScheduleRepeatRange }; task = task with { }; return task; @@ -125,7 +133,7 @@ namespace PARR.Core.Services.RobotTask.Implementations private async Task GetTaskAsync(RobotsEnum robotCode, TaskStatusEnum taskStatusCode, bool acquireTask, string? robotIp, string? robotId, TimeSpan scheduleCooldownDuration) { // 1. Ищем все задания с превышенным кол-вом попыток и просроченным временем, ставим им статус ошибки - await robotConfigurationRepository.MarkExpiredTasksAsFailedAsync(settingsFromDb.RobotAttemptsNumber, settingsFromDb.RobotWaitTime); + await _robotConfigurationRepository.MarkExpiredTasksAsFailedAsync(_settingsFromDb.RobotAttemptsNumber, _settingsFromDb.RobotWaitTime); // 2. Ищем доступные задания @@ -148,7 +156,7 @@ namespace PARR.Core.Services.RobotTask.Implementations { // Берем первую задачу из списка доступных acquiredTaskId = availableTasks.First(); - logger.LogDebug("Задача не требует захвата, взята первая из доступных: {TaskId}", acquiredTaskId); + _logger.LogDebug("Задача не требует захвата, взята первая из доступных: {TaskId}", acquiredTaskId); } @@ -168,20 +176,14 @@ namespace PARR.Core.Services.RobotTask.Implementations /// private async Task> GetAvailableTasksAsync(RobotsEnum robotCode, TaskStatusEnum taskStatusCode, TimeSpan scheduleCooldownDuration) { - var query = robotConfigurationRepository.Get() + var query = _robotConfigurationRepository.Get() .AsNoTracking() - .Where(t => t.RobotCode == (int)robotCode/* && t.TaskStatusCode == (int)taskStatusCode*/); + .Where(t => t.RobotCode == (int)robotCode); // Если это задание для робота расписаний if (robotCode == RobotsEnum.ScheduleOrder) { // Выбираем только записи с созданными шаблонами (у которых статус 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)); @@ -200,20 +202,22 @@ namespace PARR.Core.Services.RobotTask.Implementations // Сортируем по nextRun, чтобы те, у кого nextRun ближе к текущей, выполнились скорее query = query.OrderBy(t => t.Template!.NextRun).ThenBy(t => t.Template!.IsActiveSchedule).ThenBy(t => t.Template!.IsActiveTemplate); - // Кандидаты заданий - var tasks = new List(); + // Кандидаты заданий, Id задания и имя шаблона + //var tasks = new List(); + var tasks = new List(); - // Ещем первые 10 заданий в статусе ОЖИДАНИЕ + // Ищем первые TakeTasks заданий в статусе ОЖИДАНИЕ tasks = await query .Where(t => t.RobotStatusCode == (int)RobotStatusEnum.Wait && t.TaskStatusCode == (int)taskStatusCode ).Take(TakeTasks) - .Select(t => t.Id) + //.Select(t => t.Id) + .Select(t => new RobotTaskDetails(t.Id, t.Template!.Name, t.Template.NextRun)) .ToListAsync(); - logger.LogDebug("Найдено заданий в статусе 'Ожидание' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString()); + _logger.LogDebug("Найдено заданий в статусе 'Ожидание' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString()); if (tasks.Count == 0) { @@ -222,20 +226,171 @@ namespace PARR.Core.Services.RobotTask.Implementations // Далее проверяется `LastStatusUpdated`, что время последнего смены статуса не превышает допустимого(берется из настроек, поле `RobotWaitTime`) // и что текущая попытка не больше разрешенной(берется из настроек, поле `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 - && t.TaskStatusCode==(int)taskStatusCode - && t.AttemptsNumber < settingsFromDb.RobotAttemptsNumber + && t.TaskStatusCode == (int)taskStatusCode + && t.AttemptsNumber < _settingsFromDb.RobotAttemptsNumber && t.LastRobotStatusUpdated < endDate) .Take(TakeTasks) - .Select(t => t.Id) + //.Select(t => t.Id) + .Select(t => new RobotTaskDetails(t.Id, t.Template!.Name, t.Template.NextRun)) .ToListAsync(); - logger.LogDebug("Найдено заданий в статусе 'В работе' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString()); + _logger.LogDebug("Найдено заданий в статусе 'В работе' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString()); } - return tasks; + if (robotCode == RobotsEnum.TemplateOrder) + { + // Если запрашиваем шаблоны, смотрим корректируем список заданий в зависимости от статуса переименования. + // Это не относится к расписаниям, потому что у переименованных расписаний статус Updating, а оно не возьмется в работу, пока не обновится шаблон + tasks = await ReplaceTemplateTasksForRenameAsync(tasks, robotCode); + } + + return tasks.Select(t => t.TaskId).ToList(); + } + + + /// + /// Проверяет наличие шаблонов в процессе переименования и заменяет обычные задания на задания по переименованию. + /// Если связанный шаблон не переименован, и у него статус ошибки, целевому шаблону устанавливается статус ошибки. + /// + /// + /// + private async Task> ReplaceTemplateTasksForRenameAsync(List tasks, RobotsEnum robotCode) + { + if (tasks.Count == 0 || robotCode != RobotsEnum.TemplateOrder) + 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(); + 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(); + } + + + /// + /// Установить статус задания - ошибка + /// + /// + /// + private async Task SetErrorStatusAsync(List 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) { - var isChangedStatus = await robotConfigurationRepository.SetInProgressStatusAsync(taskId); + var isChangedStatus = await _robotConfigurationRepository.SetInProgressStatusAsync(taskId); if (isChangedStatus) { - logger.LogDebug("Захвачена задача {TaskId}", taskId); + _logger.LogDebug("Захвачена задача {TaskId}", taskId); - var task = await robotConfigurationRepository.Get() + var task = await _robotConfigurationRepository.Get() .AsNoTracking() .FirstAsync(t => t.Id == taskId); @@ -268,18 +423,18 @@ namespace PARR.Core.Services.RobotTask.Implementations RobotId = robotId }; - if (!await robotHistoryRepository.CreateAsync(history) || !await robotHistoryRepository.CommitAsync()) + if (!await _robotHistoryRepository.CreateAsync(history) || !await _robotHistoryRepository.CommitAsync()) throw new DbErrorException("Ошибка при добавлении истории робота, при взятии задания в работу."); return taskId; } else { - logger.LogDebug("Не удалось захватить задачу {TaskId}", taskId); + _logger.LogDebug("Не удалось захватить задачу {TaskId}", taskId); } } - logger.LogDebug("Не удалось захватить ни одну из доступных задач для робота"); + _logger.LogDebug("Не удалось захватить ни одну из доступных задач для робота"); return null; } @@ -293,7 +448,7 @@ namespace PARR.Core.Services.RobotTask.Implementations /// private async Task GetTaskWithAllDataAsync(Guid taskId, RobotsEnum robotCode) { - IQueryable query = robotConfigurationRepository.Get() + IQueryable query = _robotConfigurationRepository.Get() //.AsNoTracking() // нужно обязательно трекать, так как может измениться nextRun и его нужно будет сохранить .AsSingleQuery() // Общие инклуды для шаблонов и расписаний @@ -372,23 +527,23 @@ namespace PARR.Core.Services.RobotTask.Implementations var template = task.Template!; //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) { - logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun=null, ошибка в расчетах.", template.Id); + _logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun=null, ошибка в расчетах.", template.Id); return false; } if (nextRun.Value < DateTimeOffset.UtcNow) { - logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun + /// Модель задания для робота + /// + /// + /// + internal record RobotTaskDetails(Guid TaskId, string TemplateName, DateTimeOffset NextRun); +} diff --git a/PARR.DAL/Repositories/RobotConfigurationRepository.cs b/PARR.DAL/Repositories/RobotConfigurationRepository.cs index efbb22be..ba4909ed 100644 --- a/PARR.DAL/Repositories/RobotConfigurationRepository.cs +++ b/PARR.DAL/Repositories/RobotConfigurationRepository.cs @@ -1,9 +1,11 @@ -using Microsoft.EntityFrameworkCore; +using InfluxDB.Client.Api.Domain; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PARR.Core.Repositories.Interfaces; using PARR.DAL.Context; using PARR.DAL.Repositories.Base; using PARR.Domain.Entities; +using PARR.Domain.Entities.RobotEntities; using PARR.Domain.Enums; using PARR.Domain.Settings; @@ -94,8 +96,8 @@ namespace PARR.DAL.Repositories configuration.AttemptsNumber++; configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow; break; - //case RobotStatusEnum.Error: - // break; + case RobotStatusEnum.Error: + break; case RobotStatusEnum.Complete: configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow; 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 SetInProgressStatusAsync(Guid id) { @@ -175,10 +185,55 @@ namespace PARR.DAL.Repositories 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(); 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); }