diff --git a/PARR.EsppScheduleSync/EsppScheduleSyncInstaller.cs b/PARR.EsppScheduleSync/EsppScheduleSyncInstaller.cs index 4cbebe01..4dfc8041 100644 --- a/PARR.EsppScheduleSync/EsppScheduleSyncInstaller.cs +++ b/PARR.EsppScheduleSync/EsppScheduleSyncInstaller.cs @@ -2,9 +2,9 @@ using Microsoft.Extensions.DependencyInjection; using PARR.BLL; using PARR.DAL; -using PARR.EsppSync; -using PARR.EsppScheduleSync.Domain; using PARR.EsppScheduleSync.Settings; +using PARR.EsppSync; +using PARR.EsppSync.Domain; namespace PARR.EsppScheduleSync { diff --git a/PARR.EsppScheduleSync/ScheduleSyncher.cs b/PARR.EsppScheduleSync/ScheduleSyncher.cs index 2f4fec96..50ba49ff 100644 --- a/PARR.EsppScheduleSync/ScheduleSyncher.cs +++ b/PARR.EsppScheduleSync/ScheduleSyncher.cs @@ -5,14 +5,13 @@ using PARR.BLL.Helpers; using PARR.BLL.Services.Interfaces; using PARR.Constants; using PARR.DAL.Contracts; -using PARR.DAL.DomainServices.Shortcodes; using PARR.DAL.Models; using PARR.DAL.NextRunServices; using PARR.DAL.Services.Interfaces; using PARR.DAL.Services.Interfaces.Schedule; -using PARR.EsppScheduleSync.Domain; using PARR.EsppScheduleSync.Settings; using PARR.EsppSync; +using PARR.EsppSync.Domain; namespace PARR.EsppScheduleSync { @@ -26,7 +25,6 @@ namespace PARR.EsppScheduleSync private readonly IServiceProvider serviceProvider; private readonly IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService; private string noneExcludeCalendarEsppValue; - //private Dictionary responseAreaTimeOffsetDict; public ScheduleSyncher( @@ -46,7 +44,6 @@ namespace PARR.EsppScheduleSync this.settingsFromDb = settingsFromDb; this.serviceProvider = serviceProvider; this.scheduleResponseAreaTimeOffsetService = scheduleResponseAreaTimeOffsetService; - //this.responseAreaTimeOffsetDict = new Dictionary(); this.noneExcludeCalendarEsppValue = string.Empty; if (globalSettings.MqSettings == null) @@ -66,8 +63,7 @@ namespace PARR.EsppScheduleSync if (string.IsNullOrEmpty(noneExcludeCalendarEsppValue)) await GetNoneExcludeCalendarEsppValueAsync(); - //if (responseAreaTimeOffsetDict.Count == 0) - // await GetResponseAreaTimeOffsetsAsync(); + var isConnected = await mqService.InitConsumerAsync(globalSettings!.MqSettings!, SyncScheduleAsync); @@ -77,13 +73,7 @@ namespace PARR.EsppScheduleSync logger.LogInformation("Запущена проверка очереди {QueueName}.", globalSettings.MqSettings!.QueueName); } - //private async Task GetResponseAreaTimeOffsetsAsync() - //{ - // using var scope = serviceProvider.CreateScope(); - // var service = scope.ServiceProvider.GetRequiredService(); - // responseAreaTimeOffsetDict = await service.Get().ToDictionaryAsync(t => t.ResponseArea, t => t.TimeOffset); - //} private async Task GetNoneExcludeCalendarEsppValueAsync() { @@ -253,10 +243,10 @@ namespace PARR.EsppScheduleSync /// private EsppObjectSchedule ConvertDbObjToEsppObj(Template template) { - //дефолтное значение, изменится в сервисе nextRunModifierService - var nextRunWithTimeZone = DateTimeOffset.MinValue; - // дефолтное значение, изменится в ApplyShortcodesAsync - var responseArea = "%ЗО_РГ%"; + //дефолтное значение, nextRun в часовом поясе робота ЕСПП + var nextRunWithEsppTz = DateTimeOffset.MinValue; + //// дефолтное значение, изменится в ApplyShortcodesAsync + //var responseArea = "%ЗО_РГ%"; using (var scope = serviceProvider.CreateScope()) { @@ -266,11 +256,11 @@ namespace PARR.EsppScheduleSync //nextRunWithTimeZone = nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun); - var nextRunService = scope.ServiceProvider.GetRequiredService(); - var shortcodeService = scope.ServiceProvider.GetRequiredService(); + var nextRunService = scope.ServiceProvider.GetRequiredService(); + //var shortcodeService = scope.ServiceProvider.GetRequiredService(); - responseArea = shortcodeService.ApplyShortcodesAsync(template.Job!.ResponseAreaMask, template).GetAwaiter().GetResult(); - nextRunWithTimeZone = nextRunService.GetNextRunWithTimezoneEsppAndResponseArea(template.NextRun, template.Job?.Group?.IsResponseAreaTimezone, responseArea); + //responseArea = shortcodeService.ApplyShortcodesAsync(template.Job!.ResponseAreaMask, template).GetAwaiter().GetResult(); + nextRunWithEsppTz = template.NextRun.Add(nextRunService.GetEsppAccountOffset()); } var esppObjectFromDb = new EsppObjectSchedule @@ -289,9 +279,9 @@ namespace PARR.EsppScheduleSync //Scheduled = EsppScheduleHelpers.GetNextRun(template.NextRun), //Scheduled = EsppScheduleHelpers.GetNextRun(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)), //BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)), - Scheduled = EsppScheduleHelpers.GetNextRun(nextRunWithTimeZone), - BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunWithTimeZone), - Timezone = GetTimezone(template, responseArea), + Scheduled = EsppScheduleHelpers.GetNextRun(nextRunWithEsppTz), + BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunWithEsppTz), + Timezone = GetTimezone(/*template, responseArea*/), //Мы решили, что для всех расписаний "Отсутствует дата завершения", если что-то поменяется, тут нужно переделать TerminationType = settingsFromDb.ScheduleRepeatRange == "Отсутствует дата завершения" ? "forever" : "", CompleteAfter = "" @@ -301,34 +291,38 @@ namespace PARR.EsppScheduleSync return ClearOptionalFields(esppObjectFromDb); } - private string GetTimezone(Template template, string responseArea) + private string GetTimezone(/*Template template, string responseArea*/) { + // договорились, что у роботоа ТЗ МСК + return settingsFromDb.EsppScheduleTimezone; - //if (template.Job?.Group?.IsWorkGroupTimezone != true) - // return settingsFromDb.ScheduleTimezone; + #region old + ////if (template.Job?.Group?.IsWorkGroupTimezone != true) + //// return settingsFromDb.ScheduleTimezone; - if (template.Job?.Group?.IsResponseAreaTimezone != true) - return scheduleResponseAreaTimeOffsetService.GetDefault.EsppValue; + //if (template.Job?.Group?.IsResponseAreaTimezone != true) + // return scheduleResponseAreaTimeOffsetService.GetDefault.EsppValue; - //var responseArea = template.Unit?.BaseFields?.ResponseArea; + ////var responseArea = template.Unit?.BaseFields?.ResponseArea; - //if (string.IsNullOrEmpty(responseArea)) - //{ - // throw new InvalidOperationException( - // $"У шаблона Id={template.Id}, Name='{template.Name}' не задана ResponseArea в Unit.BaseFields, " + - // "но включена настройка 'использовать часовой пояс рабочей группы'."); - //} + ////if (string.IsNullOrEmpty(responseArea)) + ////{ + //// throw new InvalidOperationException( + //// $"У шаблона Id={template.Id}, Name='{template.Name}' не задана ResponseArea в Unit.BaseFields, " + + //// "но включена настройка 'использовать часовой пояс рабочей группы'."); + ////} - //if (!responseAreaTimeOffsetDict.TryGetValue(responseArea, out var offset)) - //{ - // throw new InvalidOperationException( - // $"Не найдено временное смещение для ResponseArea '{responseArea}' у шаблона Id={template.Id}, Name='{template.Name}'. " + - // "Проверьте наличие записи в таблице ScheduleResponseAreaTimeOffset."); - //} + ////if (!responseAreaTimeOffsetDict.TryGetValue(responseArea, out var offset)) + ////{ + //// throw new InvalidOperationException( + //// $"Не найдено временное смещение для ResponseArea '{responseArea}' у шаблона Id={template.Id}, Name='{template.Name}'. " + + //// "Проверьте наличие записи в таблице ScheduleResponseAreaTimeOffset."); + ////} - //return offset; + ////return offset; - return scheduleResponseAreaTimeOffsetService.GetByResponseAreaOrDefault(responseArea).EsppValue; + //return scheduleResponseAreaTimeOffsetService.GetByResponseAreaOrDefault(responseArea).EsppValue; + #endregion } @@ -373,20 +367,6 @@ namespace PARR.EsppScheduleSync logger.LogDebug("Для шаблона шаблона {TemplateName}, {TemplateId} тип исключения \"{Name}\", EsppValue: {EsppValue}", template.Name, template.Id, template.Job.Group.ScheduleExcludeType.Title, template.Job.Group.ScheduleExcludeType.EsppValue); return template.Job.Group.ScheduleExcludeType.EsppValue; - - - #region old logic - //// Это костыль, нужно придумать как это хранить в БД. - //switch (settingsFromDb.ScheduleExcludeType.ToLower()) - //{ - // case ("нет исключений"): - // return "NONE"; - // case ("выполнить только в указанном календаре"): - // return "ONLY"; - // default: - // return ""; - //} - #endregion } @@ -426,17 +406,7 @@ namespace PARR.EsppScheduleSync esppObject.Dayofweek = esppSchedule.Values.First(t => t.Order == 0).Value.EsppExportValue; break; case EsppSchTypeScheduleEnum.Monthly: - //если включено автораспределение, подставляем дату месяца из NextRun - //if (template.ApplicationsInWork?.IsAutoDistributionEnabled == true)//TODO Migration to job - //{ - // var nextRunByRobotTimeZone = nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun); - // esppObject.Dayofmonth = nextRunByRobotTimeZone.Day.ToString(); - // // esppObject.Dayofmonth = template.NextRun.Day.ToString(); - //} - //else - //{ esppObject.Dayofmonth = esppSchedule.Values.First(t => t.Order == 0).Value.EsppExportValue; - //} break; case EsppSchTypeScheduleEnum.Monthly2: esppObject.Md1 = esppSchedule.Values.First(t => t.Order == 0).Value.EsppExportValue; @@ -452,8 +422,6 @@ namespace PARR.EsppScheduleSync esppObject.An3 = esppSchedule.Values.First(t => t.Order == 2).Value.EsppExportValue; break; } - - } } diff --git a/PARR.EsppScheduleSync/Domain/EsppObjectSchedule.cs b/PARR.EsppSync/Domain/EsppObjectSchedule.cs similarity index 97% rename from PARR.EsppScheduleSync/Domain/EsppObjectSchedule.cs rename to PARR.EsppSync/Domain/EsppObjectSchedule.cs index 419906a6..37675d10 100644 --- a/PARR.EsppScheduleSync/Domain/EsppObjectSchedule.cs +++ b/PARR.EsppSync/Domain/EsppObjectSchedule.cs @@ -1,10 +1,9 @@ using PARR.Constants; using PARR.DAL.Contracts; -using PARR.EsppSync; -namespace PARR.EsppScheduleSync.Domain +namespace PARR.EsppSync.Domain { - internal class EsppObjectSchedule : IEsppObject + public class EsppObjectSchedule : IEsppObject { //Все поля описаны в документации по роботам: http://gitlab.dvgd.oao.rzd/devptk/parr/parr_api/-/wikis/EsppRobots diff --git a/PARR.EsppSync/SyncService.cs b/PARR.EsppSync/SyncService.cs index 7343d5b9..9e3c45da 100644 --- a/PARR.EsppSync/SyncService.cs +++ b/PARR.EsppSync/SyncService.cs @@ -1,11 +1,14 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using PARR.BLL.Helpers; using PARR.Constants; using PARR.DAL.Contracts; using PARR.DAL.DomainServices.Shortcodes; using PARR.DAL.Models; +using PARR.DAL.NextRunServices; using PARR.DAL.Services.Interfaces; +using PARR.EsppSync.Domain; using System.Reflection; namespace PARR.EsppSync @@ -52,7 +55,7 @@ namespace PARR.EsppSync var templateService = GetServiceInScope(scope); var robotConfigurationService = GetServiceInScope(scope); var shortcodesService = GetServiceInScope(scope); - + var nextRunService = GetServiceInScope(scope); try { @@ -85,7 +88,7 @@ namespace PARR.EsppSync if (template == null) { - logger.LogWarning("Найден объект в ЕСПП с именем шаблона {TemplateName} незарегистрированный в ПАРР. Строка: {String}", esppObject.TemplateName, str); + logger.LogWarning("Найден объект в ЕСПП с именем шаблона '{TemplateName}' незарегистрированный в ПАРР. Строка: {String}", esppObject.TemplateName, str); return; } @@ -93,28 +96,7 @@ namespace PARR.EsppSync await ApplyShortcodesAsync(dbObjectInEsppObject, template, shortcodesService); - #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 - //bool isChanged; // если в БД isActive == false, то синхронизировать только по полям из IEsppObject @@ -130,9 +112,17 @@ namespace PARR.EsppSync { logger.LogDebug("Объект активирован в ПАРР. Сравниваем все поля. {TemplateName}", esppObject.TemplateName); isChanged = IsChanged(esppObject, dbObjectInEsppObject, esppObject.TemplateName); - } - //todo: если это сравнение расписаний, рассчитать nextRun, и сравнить все три nextRun, БД - ЕСПП - Расчитанное + // если это сравнение расписаний, рассчитать nextRun, и сравнить все три nextRun, БД - ЕСПП - Расчитанное + // будем считать, только если НЕТ изменений в остальных полях, если изменения есть, зачем лишний раз напрягать, потом в контроллере все пересчитается + if (!isChanged && dbObjectInEsppObject is EsppObjectSchedule dbValues && esppObject is EsppObjectSchedule esppValues) + { + var isEqualNextRun = await IsEqualNextRunAsync(nextRunService, dbValues, esppValues, template.Id); + + if (isEqualNextRun == false) + isChanged = true; + } + } if (isChanged) { @@ -157,15 +147,6 @@ namespace PARR.EsppSync logger.LogInformation("Есть изменения в Template {TemplateName}, но предыдущий статус TaskStatusCode: {TaskStatusCode}. Не меняем статус, будем разбираться вручную.", template.Name, (TaskStatusEnum)config.TaskStatusCode); } - #region Old logic - - //SetUpdateStatus(ref template, robotConfigurationService, esppObject.Robot); - - //if (!await templateService.CommitAsync()) - // logger.LogError($"Не удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}"); - //else - // logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Updating}, Template {template.Name}, Robot: {esppObject.Robot}"); - #endregion }//надо ли проверять если не изменился, но был статус Updating не понятно. Доверяем роботу пока, что после окончания работ он точно сообщит else { @@ -191,6 +172,7 @@ namespace PARR.EsppSync } } + /// /// Применяем шорткоды /// @@ -218,6 +200,13 @@ namespace PARR.EsppSync } } + + /// + /// Установить статус - Обновить + /// + /// + /// + /// private void SetUpdateStatus(ref Template template, IRobotConfigurationService robotConfigurationService, RobotsEnum robot) { var robotConfig = robotConfigurationService.GetFromTemplateByRobotCode(robot, template); @@ -225,6 +214,13 @@ namespace PARR.EsppSync } + /// + /// Получить сервис из scope + /// + /// + /// + /// + /// private Service GetServiceInScope(IServiceScope scope) { var service = scope.ServiceProvider.GetService(); @@ -235,7 +231,13 @@ namespace PARR.EsppSync } - //private bool IsChanged(EsppObject esppObj, EsppObject dbObj) + /// + /// Сравнение объектов + /// + /// + /// + /// + /// private bool IsChanged(object esppObj, object dbObj, string templateName) { foreach (var prop in dbObj.GetType().GetProperties()) @@ -278,5 +280,73 @@ namespace PARR.EsppSync return str.ToLower(); } + + + /// + /// Рассчитать nextRun в часовом поясе УЗ робота + /// + /// + /// + /// + private async Task CalcNextRunAsync(INextRunServiceV2 nextRunService, Guid templateId) + { + var nextRun = await nextRunService.GetNextRunForTemplateAsync(templateId, isNew: false); + + if (nextRun.HasValue) + { + var nextRunWithEsppAccountTz = nextRun.Value.Add(nextRunService.GetEsppAccountOffset()); + logger.LogDebug("Расчитанный nextRun для шаблона {templateId}, UTC: {nextRun}, EsppAccountTz: {nextRunWithEsppAccountTz}", templateId, nextRun, nextRunWithEsppAccountTz); + + return nextRunWithEsppAccountTz; + } + else + { + logger.LogError("При расчете nextRun для templateId: {templateId} верунлся null", templateId); + return null; + } + } + + + /// + /// Сравнить NextRun из БД, ЕСПП, рассчитанный + /// + /// + /// + /// + /// + private async Task IsEqualNextRunAsync(INextRunServiceV2 nextRunService, EsppObjectSchedule dbValues, EsppObjectSchedule esppValues, Guid templateId) + { + // в часовой зоне робота + var calculatedNextRun = await CalcNextRunAsync(nextRunService, templateId); + if (calculatedNextRun == null) + { + return false; + } + + var scheduledCalculated = EsppScheduleHelpers.GetNextRun(calculatedNextRun.Value); + var basisTimeCalculated = EsppScheduleHelpers.GetGenerationTime(calculatedNextRun.Value); + + logger.LogDebug("Рассчитанные значения для шаблона '{templateName}', {templateId}, следующее срабатывание {scheduledCalculated}, время создания наряда: {basisTimeCalculated}", + dbValues.TemplateName, templateId, scheduledCalculated, basisTimeCalculated); + + if (Normalize(dbValues.Scheduled) != Normalize(esppValues.Scheduled) || Normalize(dbValues.Scheduled) != scheduledCalculated || Normalize(esppValues.Scheduled) != scheduledCalculated) + { + logger.LogInformation("Не совпадают поля ({propName}), dbValueStr: {dbValueStr}, esppValueStr: {esppValueStr}, scheduledCalculated: {scheduledCalculated}. Имя шаблона: {templateName}", + nameof(dbValues.Scheduled), dbValues.Scheduled, esppValues.Scheduled, scheduledCalculated, dbValues.TemplateName); + return false; + } + + if (Normalize(dbValues.BasisTime) != Normalize(esppValues.BasisTime) || Normalize(dbValues.BasisTime) != basisTimeCalculated || Normalize(esppValues.BasisTime) != basisTimeCalculated) + { + logger.LogInformation("Не совпадают поля ({propName}), dbValueStr: {dbValueStr}, esppValueStr: {esppValueStr}, basisTimeCalculated: {basisTimeCalculated}. Имя шаблона: {templateName}", + nameof(dbValues.BasisTime), dbValues.BasisTime, esppValues.BasisTime, basisTimeCalculated, dbValues.TemplateName); + return false; + } + + logger.LogDebug("Значения nextRun в БД, ЕСПП, расчитанное, все совпадают. Scheduled: {scheduled}, basisTime: {basisTime}", scheduledCalculated, basisTimeCalculated); + + return true; + } + } } \ No newline at end of file