From 35f64c051c0d0ad4a57e479b1bc58ae7f2ab214e Mon Sep 17 00:00:00 2001 From: Mikhail Trubnikov Date: Mon, 16 Feb 2026 16:54:06 +1000 Subject: [PATCH] =?UTF-8?q?feat(dal,api,nextRunWorker):=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B4=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B0=20=D0=BB=D0=BE?= =?UTF-8?q?=D0=B3=D0=B8=D0=BA=D0=B0=20NextRunServiceV2=20=D0=B2=20nextRunW?= =?UTF-8?q?orker,=20=D0=B2=20api=20RobotTaskController?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/V1/RobotTaskController.cs | 107 ++++++------------ PARR.DAL/Context/DataContext.cs | 2 +- PARR.DAL/Contracts/SettingsFromDb.cs | 8 +- PARR.DAL/NextRunServices/INextRunServiceV2.cs | 5 + PARR.DAL/NextRunServices/NextRunServiceV2.cs | 6 +- .../IEsppScheduleTransformService.cs | 3 +- .../Subservices/ITemplateDistributorV2.cs | 3 +- PARR.EsppSync/SyncService.cs | 64 ++++++++--- PARR.NextRun/NextRunManager.cs | 23 ++-- PARR.Test/NextRun/NextRunTest.cs | 8 +- 10 files changed, 117 insertions(+), 112 deletions(-) diff --git a/PARR.API/Controllers/V1/RobotTaskController.cs b/PARR.API/Controllers/V1/RobotTaskController.cs index b86a4e33..a2c54b19 100644 --- a/PARR.API/Controllers/V1/RobotTaskController.cs +++ b/PARR.API/Controllers/V1/RobotTaskController.cs @@ -31,7 +31,7 @@ namespace PARR.API.Controllers.V1 private readonly IClientService clientService; private readonly IRobotHistoryService robotHistoryService; private readonly IShortcodesService shortcodesService; - private readonly INextRunService nextRunService; + private readonly INextRunServiceV2 nextRunService; private readonly IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService; public RobotTaskController( @@ -43,7 +43,7 @@ namespace PARR.API.Controllers.V1 IClientService clientService, IRobotHistoryService robotHistoryService, IShortcodesService shortcodesService, - INextRunService nextRunService, + INextRunServiceV2 nextRunService, IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService ) { @@ -156,11 +156,6 @@ namespace PARR.API.Controllers.V1 // сортируем по NextRun, чтобы те у которых дата след срабатывания ближе к текущей, выполнились скорее query = query.OrderBy(t => t.Template!.NextRun).ThenBy(t => t.Template!.IsActiveSchedule).ThenBy(t => t.Template.IsActiveTemplate); - #region Так нельзя делать!!! Должно всегда сортироваться по NextRun и не важно какой тип у шаблона!!! - // Сортируем сначала по StatusTypeId, т е те что used, будут первыми, потом сортируем по NextRun, чтобы ближайшие даты выполнились скорее - //query = query.OrderBy(t => t.Template!.StatusTypeId).ThenBy(t => t.Template!.NextRun); - #endregion - RobotConfiguration? task = null; //ищем задание в ожидании, если нашли, выбираем его @@ -213,20 +208,35 @@ namespace PARR.API.Controllers.V1 } case RobotsEnum.ScheduleOrder: { // если был запрос на расписание, проверяем у него nextRun, lastRun, обновляем их - await UpdateLastNextRunDate(task); + 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 = GetTimezoneForTemplate(task.Template!); + robotTaskScheduleResponse.Timezone = settingsFromDb.EsppScheduleTimezone; robotTaskScheduleResponse.WorkGroup = await shortcodesService.ApplyShortcodesAsync(robotTaskScheduleResponse.WorkGroup, task.Template!); robotTaskScheduleResponse.ResponseArea = await shortcodesService.ApplyShortcodesAsync(robotTaskScheduleResponse.ResponseArea, task.Template!); - var nextRunWithTimezone = nextRunService.GetNextRunWithTimezoneEsppAndResponseArea(task.Template!.NextRun, task.Template!.Job?.Group?.IsResponseAreaTimezone, robotTaskScheduleResponse.ResponseArea); + //var nextRunWithRobotTz = nextRunService.GetNextRunWithTimezoneEsppAndResponseArea(task.Template!.NextRun, task.Template!.Job?.Group?.IsResponseAreaTimezone, robotTaskScheduleResponse.ResponseArea); + //nextRun в часовой зоне УЗ Робота ЕСПП + var nextRunWithRobotTz = task.Template!.NextRun.Add(nextRunService.GetEsppAccountOffset()); - robotTaskScheduleResponse.NextStart = EsppScheduleHelpers.GetNextRun(nextRunWithTimezone); - robotTaskScheduleResponse.GenerationTime = EsppScheduleHelpers.GetGenerationTime(nextRunWithTimezone); + //на всякий случай еще раз проверяем, что дата не устарела и отправляем задание + 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)); } @@ -243,54 +253,36 @@ namespace PARR.API.Controllers.V1 /// /// /// - private async Task UpdateLastNextRunDate(RobotConfiguration task) + private async Task UpdateNextRunAsync(RobotConfiguration task) { var template = task.Template!; - #region old - // передаем 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.Job!.GroupId, template.NextRun); - #endregion - //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 string GetTimezoneForTemplate(Template template) - { - if (template.Job?.Group?.IsResponseAreaTimezone != true) - return scheduleResponseAreaTimeOffsetService.GetDefault.EsppValue; - - var responseArea = template.Unit?.BaseFields?.ResponseArea; - - if (string.IsNullOrEmpty(responseArea)) - { - throw new InvalidOperationException( - $"У шаблона Id={template.Id}, Name='{template.Name}' не задана ResponseArea в Unit.BaseFields, " + - "но включена настройка 'использовать часовой пояс рабочей группы'."); - } - - return scheduleResponseAreaTimeOffsetService.GetByResponseAreaOrDefault(responseArea).EsppValue; - } - } } diff --git a/PARR.DAL/Context/DataContext.cs b/PARR.DAL/Context/DataContext.cs index 836b116f..3a90aeaf 100644 --- a/PARR.DAL/Context/DataContext.cs +++ b/PARR.DAL/Context/DataContext.cs @@ -189,7 +189,7 @@ namespace PARR.DAL.Context new { Name = nameof(SettingsFromDb.TemplatePrefixName), Description = "Префикс имени шаблона в ЕСПП", Value = "%PREFIX%-ЭИТИ-ПТК-ПАРР" }, new { Name = nameof(SettingsFromDb.RobotAttemptsNumber), Description = "Количество попыток выполнения задания роботом", Value = "3" }, new { Name = nameof(SettingsFromDb.RobotWaitTime), Description = "Время ожидания выполнения роботом задания", Value = "00:15:00" }, - //new { Name = nameof(SettingsFromDb.ScheduleTimezone), Description = "Расписание регламентной работы - В каком часовом поясе", Value = "MSK" }, + new { Name = nameof(SettingsFromDb.EsppScheduleTimezone), Description = "Расписание регламентной работы - В каком часовом поясе", Value = "MSK" }, //new { Name = nameof(SettingsFromDb.ScheduleExcludeType), Description = "Расписание регламентной работы - Тип исключения", Value = "Выполнить ТОЛЬКО В указанном календаре" }, //new { Name = nameof(SettingsFromDb.ScheduleExcludeCalendar), Description = "Расписание регламентной работы - Календарь", Value = "8x5 (8.00-17.00)" }, new { Name = nameof(SettingsFromDb.ScheduleRepeatRange), Description = "Расписание регламентной работы - Диапазн повторов", Value = "Отсутствует дата завершения" }, diff --git a/PARR.DAL/Contracts/SettingsFromDb.cs b/PARR.DAL/Contracts/SettingsFromDb.cs index 3e12a1eb..76b15c4c 100644 --- a/PARR.DAL/Contracts/SettingsFromDb.cs +++ b/PARR.DAL/Contracts/SettingsFromDb.cs @@ -77,10 +77,10 @@ namespace PARR.DAL.Contracts /// public TimeSpan RobotWaitTime { get; set; } - ///// - ///// Расписание регламентной работы - В каком часовом поясе - ///// - //public string ScheduleTimezone { get; set; } = string.Empty; + /// + /// Расписание регламентной работы, В каком часовом поясе + /// + public string EsppScheduleTimezone { get; set; } = string.Empty; ///// ///// Расписание регламентной работы - Тип исключения diff --git a/PARR.DAL/NextRunServices/INextRunServiceV2.cs b/PARR.DAL/NextRunServices/INextRunServiceV2.cs index c24af80f..750afd1b 100644 --- a/PARR.DAL/NextRunServices/INextRunServiceV2.cs +++ b/PARR.DAL/NextRunServices/INextRunServiceV2.cs @@ -32,6 +32,11 @@ namespace PARR.DAL.NextRunServices /// NextRun или null, если null - то ошибка Task GetNextRunForTemplateAsync(Guid templateId, bool isNew); + /// + /// Получить часовой пояс УЗ ЕСПП + /// + /// + TimeSpan GetEsppAccountOffset(); } diff --git a/PARR.DAL/NextRunServices/NextRunServiceV2.cs b/PARR.DAL/NextRunServices/NextRunServiceV2.cs index 655c2ab3..3d8c7c9e 100644 --- a/PARR.DAL/NextRunServices/NextRunServiceV2.cs +++ b/PARR.DAL/NextRunServices/NextRunServiceV2.cs @@ -740,11 +740,7 @@ namespace PARR.DAL.NextRunServices } - /// - /// Получить часовой пояс УЗ ЕСПП - /// - /// - private TimeSpan GetEsppAccountOffset() + public TimeSpan GetEsppAccountOffset() { var esppOffset = TimeSpan.FromHours(settingsFromDb.EsppRobotAccountTimeZoneHour); logger.LogDebug("Оффсет УЗ ЕСПП: {esppOffset}", esppOffset); diff --git a/PARR.DAL/NextRunServices/Subservices/IEsppScheduleTransformService.cs b/PARR.DAL/NextRunServices/Subservices/IEsppScheduleTransformService.cs index b7b5f1fd..fc4e7d75 100644 --- a/PARR.DAL/NextRunServices/Subservices/IEsppScheduleTransformService.cs +++ b/PARR.DAL/NextRunServices/Subservices/IEsppScheduleTransformService.cs @@ -1,10 +1,9 @@ namespace PARR.DAL.NextRunServices.Subservices { - //todo: public->internal /// /// Сервис трансформации расписания ЕСПП в дату/расписание /// - public interface IEsppScheduleTransformService + internal interface IEsppScheduleTransformService { /// /// Получить следующую дату по jobGroupId diff --git a/PARR.DAL/NextRunServices/Subservices/ITemplateDistributorV2.cs b/PARR.DAL/NextRunServices/Subservices/ITemplateDistributorV2.cs index 81eb29d3..4bdb0ad2 100644 --- a/PARR.DAL/NextRunServices/Subservices/ITemplateDistributorV2.cs +++ b/PARR.DAL/NextRunServices/Subservices/ITemplateDistributorV2.cs @@ -2,8 +2,7 @@ namespace PARR.DAL.NextRunServices.Subservices { - //todo: -> internal - public interface ITemplateDistributorV2 + internal interface ITemplateDistributorV2 { /// /// Распределить шаблоны diff --git a/PARR.EsppSync/SyncService.cs b/PARR.EsppSync/SyncService.cs index 6e41fcf2..7343d5b9 100644 --- a/PARR.EsppSync/SyncService.cs +++ b/PARR.EsppSync/SyncService.cs @@ -58,7 +58,7 @@ namespace PARR.EsppSync { // Загружаем Template и TemplateForShortcodes в одном запросе var query = templateService.Get() - //.AsNoTracking() <- не надо так, а то потом не сохранится + //.AsNoTracking() .Include(h => h.Unit) .ThenInclude(t => t!.UnitValues) .ThenInclude(t => t.Value) @@ -91,22 +91,26 @@ namespace PARR.EsppSync var dbObjectInEsppObject = converterToEsppObject.Invoke(template); - // Проверяем наличие Shortcode в полях объекта из БД - var properties = dbObjectInEsppObject.GetType().GetProperties(); + await ApplyShortcodesAsync(dbObjectInEsppObject, template, shortcodesService); - foreach (PropertyInfo property in properties) - { - if (property.PropertyType == typeof(string)) - { - var value = property.GetValue(dbObjectInEsppObject)?.ToString(); - if (!string.IsNullOrEmpty(value)) - { - // Передаём исходный template — он уже загружен с Include - var processedValue = await shortcodesService.ApplyShortcodesAsync(value, template); - property.SetValue(dbObjectInEsppObject, processedValue); - } - } - } + #region old + //// Проверяем наличие Shortcode в полях объекта из БД + //var properties = dbObjectInEsppObject.GetType().GetProperties(); + + //foreach (PropertyInfo property in properties) + //{ + // if (property.PropertyType == typeof(string)) + // { + // var value = property.GetValue(dbObjectInEsppObject)?.ToString(); + // if (!string.IsNullOrEmpty(value)) + // { + // // Передаём исходный template — он уже загружен с Include + // var processedValue = await shortcodesService.ApplyShortcodesAsync(value, template); + // property.SetValue(dbObjectInEsppObject, processedValue); + // } + // } + //} + #endregion bool isChanged = false; //TODO: FIX ME Please, BRO @@ -128,6 +132,8 @@ namespace PARR.EsppSync isChanged = IsChanged(esppObject, dbObjectInEsppObject, esppObject.TemplateName); } + //todo: если это сравнение расписаний, рассчитать nextRun, и сравнить все три nextRun, БД - ЕСПП - Расчитанное + if (isChanged) { logger.LogDebug("Есть изменения, требуется обновление. {TemplateName}", esppObject.TemplateName); @@ -185,6 +191,32 @@ namespace PARR.EsppSync } } + /// + /// Применяем шорткоды + /// + /// + /// + /// + /// + private async Task ApplyShortcodesAsync(EsppObject dbObjectInEsppObject, Template template, IShortcodesService shortcodesService) + { + // Проверяем наличие Shortcode в полях объекта из БД + var properties = dbObjectInEsppObject.GetType().GetProperties(); + + foreach (PropertyInfo property in properties) + { + if (property.PropertyType == typeof(string)) + { + var value = property.GetValue(dbObjectInEsppObject)?.ToString(); + if (!string.IsNullOrEmpty(value)) + { + // Передаём исходный template — он уже загружен с Include + var processedValue = await shortcodesService.ApplyShortcodesAsync(value, template); + property.SetValue(dbObjectInEsppObject, processedValue); + } + } + } + } private void SetUpdateStatus(ref Template template, IRobotConfigurationService robotConfigurationService, RobotsEnum robot) { diff --git a/PARR.NextRun/NextRunManager.cs b/PARR.NextRun/NextRunManager.cs index f075aa8a..38ac112a 100644 --- a/PARR.NextRun/NextRunManager.cs +++ b/PARR.NextRun/NextRunManager.cs @@ -43,10 +43,10 @@ namespace PARR.NextRun //var esppSchService = scope.ServiceProvider.GetService(); var templateService = scope.ServiceProvider.GetService(); var jobGroupService = scope.ServiceProvider.GetService(); - var nextRunService = scope.ServiceProvider.GetService(); + var nextRunService = scope.ServiceProvider.GetService(); if (templateService == null || nextRunService == null || jobGroupService == null) - throw new Exception($"Не смог получить серивс {nameof(ITemplateService)} или {nameof(INextRunService)} или {nameof(IJobGroupService)}"); + throw new Exception($"Не смог получить серивс {nameof(ITemplateService)} или {nameof(INextRunServiceV2)} или {nameof(IJobGroupService)}"); await HandlerAsync(templateService, nextRunService, jobGroupService); } @@ -58,7 +58,7 @@ namespace PARR.NextRun /// Рассчет следующей даты срабатываения /// /// - private async Task HandlerAsync(ITemplateService templateService, INextRunService nextRunService, IJobGroupService jobGroupService) + private async Task HandlerAsync(ITemplateService templateService, INextRunServiceV2 nextRunService, IJobGroupService jobGroupService) { logger.LogInformation("Начинаю обновлять NextRun по расписанию"); @@ -74,7 +74,7 @@ namespace PARR.NextRun } - private async Task UpdateNextRunAsync(ITemplateService templateService, INextRunService nextRunService, IJobGroupService jobGroupService) + private async Task UpdateNextRunAsync(ITemplateService templateService, INextRunServiceV2 nextRunService, IJobGroupService jobGroupService) { // выбираем все шаблоны с просроченным nextRun в статусе Used @@ -94,8 +94,15 @@ namespace PARR.NextRun { var newNextRun = await nextRunService.GetNextRunForTemplateAsync(template.Id, false); - template.LastRun = template.NextRun; - template.NextRun = newNextRun; + if (newNextRun.HasValue) + { + template.LastRun = template.NextRun; + template.NextRun = newNextRun.Value; + } + else + { + logger.LogError("При расчете nextRun для шаблона {templateId}, '{templateName}', nextRun=null. Это значит что при расчете возникла ошибка.", template.Id, template.Name); + } } if (await templateService.CommitAsync(new HistoryInitiator { InitiatorComment = "Обновлён NextRun", InitiatorParrComponentId = ParrComponentsEnum.NextRun })) @@ -153,7 +160,7 @@ namespace PARR.NextRun } - + //// выбираем шаблоны которые не учавствуют в автораспределении //// и у которых Used //// затем группируем по GroupId, так как nextRun и расписание настраивается для группы, то для всех дочерних шаблонов, nextRun будет одинаковым @@ -200,7 +207,7 @@ namespace PARR.NextRun // logger.LogError($"Ошибка при обновлении значений полей NextRun: {nextRun}, для шаблонов {templatesToUpdate.Count} шт., группы {group.Id} с выключенным автораспределением, refDate: {group.ReferenceDate} "); // } //} - + } diff --git a/PARR.Test/NextRun/NextRunTest.cs b/PARR.Test/NextRun/NextRunTest.cs index be779bea..3913f01b 100644 --- a/PARR.Test/NextRun/NextRunTest.cs +++ b/PARR.Test/NextRun/NextRunTest.cs @@ -13,7 +13,7 @@ namespace PARR.Test.NextRun private readonly INextRunService nextRunService; private readonly ITemplateService templateService; private readonly IShortcodesService shortcodesService; - private readonly ITemplateDistributorV2 templateDistributorV2; + //private readonly ITemplateDistributorV2 templateDistributorV2; private readonly INextRunServiceV2 nextRunServiceV2; public NextRunTest( @@ -21,7 +21,7 @@ namespace PARR.Test.NextRun INextRunService nextRunService, ITemplateService templateService, IShortcodesService shortcodesService, - ITemplateDistributorV2 templateDistributorV2, + //ITemplateDistributorV2 templateDistributorV2, INextRunServiceV2 nextRunServiceV2 ) { @@ -29,7 +29,7 @@ namespace PARR.Test.NextRun this.nextRunService = nextRunService; this.templateService = templateService; this.shortcodesService = shortcodesService; - this.templateDistributorV2 = templateDistributorV2; + //this.templateDistributorV2 = templateDistributorV2; this.nextRunServiceV2 = nextRunServiceV2; } @@ -52,7 +52,7 @@ namespace PARR.Test.NextRun //var nextRunForExistTemplateDistib = await nextRunServiceV2.GetNextRunForTemplateAsync(Guid.Parse("05769f8d-9630-4683-b07a-070d69e1dc12"), false); // существующий шаблон - еспп - var nextRunForExistTemplateEspp = await nextRunServiceV2.GetNextRunForTemplateAsync(Guid.Parse("1329dfca-5e09-4b19-a2e6-560a4896080c"), false); + //var nextRunForExistTemplateEspp = await nextRunServiceV2.GetNextRunForTemplateAsync(Guid.Parse("1329dfca-5e09-4b19-a2e6-560a4896080c"), false); #endregion