diff --git a/PARR.API/appsettings.json b/PARR.API/appsettings.json index e0ebee46..63a9ebff 100644 --- a/PARR.API/appsettings.json +++ b/PARR.API/appsettings.json @@ -113,7 +113,7 @@ } ], "RabbitMq": { - "ThresholdConnections": 33 + "ThresholdConnections": 32 } }, "CommonSettings": { diff --git a/PARR.EsppSync/SyncService.cs b/PARR.EsppSync/SyncService.cs index d2660d87..0376d1f7 100644 --- a/PARR.EsppSync/SyncService.cs +++ b/PARR.EsppSync/SyncService.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PARR.Core.Repositories.Interfaces; +using PARR.Core.Repositories.Interfaces.TemplateRepositories; using PARR.Core.Services.Shortcodes; using PARR.Domain.Entities; using PARR.Domain.Entities.Base.History; @@ -14,16 +15,16 @@ namespace PARR.EsppSync { internal class SyncService : ISyncService where EsppObject : class, IEsppObject { - private readonly ILogger> logger; - private readonly IServiceProvider serviceProvider; + private readonly ILogger> _logger; + private readonly IServiceProvider _serviceProvider; public SyncService( ILogger> logger, IServiceProvider serviceProvider ) { - this.logger = logger; - this.serviceProvider = serviceProvider; + _logger = logger; + _serviceProvider = serviceProvider; } @@ -35,11 +36,11 @@ namespace PARR.EsppSync AfterParseStringToEsppObjectAsync? afterParseStringToEsppObjectAsync = null ) { - logger.LogDebug("Получил строку. Начинаю работать. Строка: {String}", str); + _logger.LogDebug("Получил строку. Начинаю работать. Строка: {String}", str); if (string.IsNullOrEmpty(str)) { - logger.LogWarning("Получил пустую строку, ничего не делаю."); + _logger.LogWarning("Получил пустую строку, ничего не делаю."); return; } @@ -47,7 +48,7 @@ namespace PARR.EsppSync if (esppObject == null) { - logger.LogWarning("После парсинга строки, esppObject = null. Дальше ничего не буду делать."); + _logger.LogWarning("После парсинга строки, esppObject = null. Дальше ничего не буду делать."); return; } @@ -56,47 +57,64 @@ namespace PARR.EsppSync await afterParseStringToEsppObjectAsync.Invoke(esppObject); - using (var scope = serviceProvider.CreateScope()) + using (var scope = _serviceProvider.CreateScope()) { - var templateService = GetServiceInScope(scope); - var robotConfigurationService = GetServiceInScope(scope); - var shortcodesService = GetServiceInScope(scope); + var templateService = scope.ServiceProvider.GetRequiredService(); + var robotConfigurationService = scope.ServiceProvider.GetRequiredService(); + var shortcodesService = scope.ServiceProvider.GetRequiredService(); + var templateRenamePendingRepository = scope.ServiceProvider.GetRequiredService(); try { + #region Проверка, идет ли переименование + + // Если есть у этого шаблона запись в TemplateRenamePending, значит сейчас идет переименование, пропускаем синхронизацию этого шаблона + // Если имя этого шаблона находится в таблице TemplateRenamePending, значит идет переименование связанного шаблона, пропускаем синхронизацию этого шаблона + var isPartOfRename = await templateRenamePendingRepository.Get() + .AnyAsync(t => t.Template.Name == esppObject.TemplateName || t.OldName == esppObject.TemplateName); + + if (isPartOfRename) + { + _logger.LogInformation("Пропускаем синхронизацию '{TemplateName}', так как шаблон ({EsppObject}) участвует в процессе переименования.", esppObject.TemplateName, nameof(EsppObject)); + // Выходим сразу + return; + } + + #endregion + // Загружаем Template и TemplateForShortcodes в одном запросе var query = templateService.Get() - //.AsNoTracking() - .Include(h => h.Unit) - .ThenInclude(t => t!.UnitValues) - .ThenInclude(t => t.Value) - .Include(h => h.Unit) - .ThenInclude(t => t!.UnitValues) - .ThenInclude(t => t.Field) - .Include(t => t.RobotConfigurations) - .Include(t => t.Job) - .ThenInclude(j => j.Group) - .ThenInclude(g => g.GroupType) - .Include(t => t.Job) - .ThenInclude(t => t.Group) - .ThenInclude(t => t.ScheduleExcludeType) - .Include(t => t.Job) - .ThenInclude(t => t.Group) - .ThenInclude(t => t.ScheduleExcludeTypeCalendar) - .Include(t => t.Job) - .ThenInclude(j => j.Tnk) - .ThenInclude(s => s!.Subprocess) - .ThenInclude(p => p!.Process) - .Include(t => t.UnitsInTemplate); + .Include(h => h.Unit) + .ThenInclude(t => t!.UnitValues) + .ThenInclude(t => t.Value) + .Include(h => h.Unit) + .ThenInclude(t => t!.UnitValues) + .ThenInclude(t => t.Field) + .Include(t => t.RobotConfigurations) + .Include(t => t.Job) + .ThenInclude(j => j.Group) + .ThenInclude(g => g.GroupType) + .Include(t => t.Job) + .ThenInclude(t => t.Group) + .ThenInclude(t => t.ScheduleExcludeType) + .Include(t => t.Job) + .ThenInclude(t => t.Group) + .ThenInclude(t => t.ScheduleExcludeTypeCalendar) + .Include(t => t.Job) + .ThenInclude(j => j.Tnk) + .ThenInclude(s => s!.Subprocess) + .ThenInclude(p => p!.Process) + .Include(t => t.UnitsInTemplate); var template = await query.FirstOrDefaultAsync(t => t.Name == esppObject.TemplateName); if (template == null) { - logger.LogWarning("Найден объект в ЕСПП с именем шаблона '{TemplateName}' незарегистрированный в ПАРР. Строка: {String}", esppObject.TemplateName, str); + _logger.LogWarning("Найден объект в ЕСПП с именем шаблона '{TemplateName}' незарегистрированный в ПАРР. Строка: {String}", esppObject.TemplateName, str); return; } + var dbObjectInEsppObject = converterDbToEsppObject.Invoke(template); await ApplyShortcodesAsync(dbObjectInEsppObject, template, shortcodesService); @@ -107,7 +125,7 @@ namespace PARR.EsppSync if (dbObjectInEsppObject.IsActive == false) { - logger.LogDebug("Объект деактивирован в ПАРР. Сравниваем только обязательные поля. {TemplateName}", esppObject.TemplateName); + _logger.LogDebug("Объект деактивирован в ПАРР. Сравниваем только обязательные поля. {TemplateName}", esppObject.TemplateName); var lightDbObj = new EsppLightObject(dbObjectInEsppObject); var lightEsppObject = new EsppLightObject(esppObject); @@ -115,7 +133,7 @@ namespace PARR.EsppSync } else { - logger.LogDebug("Объект активирован в ПАРР. Сравниваем все поля. {TemplateName}", esppObject.TemplateName); + _logger.LogDebug("Объект активирован в ПАРР. Сравниваем все поля. {TemplateName}", esppObject.TemplateName); isChanged = IsChanged(esppObject, dbObjectInEsppObject, esppObject.TemplateName); // выполняем кастомную дополнительную проверку (только если isChanged==false, чтоб лишний раз не гонять) @@ -127,25 +145,25 @@ namespace PARR.EsppSync var isCustomComparision = await customComparisionAsync.Invoke(esppObject, dbObjectInEsppObject, template.Id); if (isCustomComparision) { - logger.LogDebug("Дополнительная проверка прошла."); + _logger.LogDebug("Дополнительная проверка прошла."); } else { // если дополнительная проверка не прошла, то говорим что есть изменения isChanged = true; - logger.LogDebug("Дополнительная проверка не прошла, ставим статус isChanged: {isChanged}", isChanged); + _logger.LogDebug("Дополнительная проверка не прошла, ставим статус isChanged: {isChanged}", isChanged); } } else { - logger.LogDebug("Дополнительная проверка отсутствует"); + _logger.LogDebug("Дополнительная проверка отсутствует"); } } } if (isChanged) { - logger.LogDebug("Есть изменения, требуется обновление. {TemplateName}", esppObject.TemplateName); + _logger.LogDebug("Есть изменения, требуется обновление. {TemplateName}", esppObject.TemplateName); var config = robotConfigurationService.GetFromTemplateByRobotCode(esppObject.Robot, template); @@ -160,25 +178,25 @@ namespace PARR.EsppSync if (isChangedStatus) { if (!await templateService.CommitAsync(GetInitiator())) - logger.LogError("Не удалось изменить запись Template {TemplateName}, Robot: {Robot}", template.Name, esppObject.Robot); + _logger.LogError("Не удалось изменить запись Template {TemplateName}, Robot: {Robot}", template.Name, esppObject.Robot); else - logger.LogInformation("Установлен принудительный статус {TaskStatus}, Template {templateName}, Robot: {Robot}", TaskStatusEnum.Updating, template.Name, esppObject.Robot); + _logger.LogInformation("Установлен принудительный статус {TaskStatus}, Template {templateName}, Robot: {Robot}", TaskStatusEnum.Updating, template.Name, esppObject.Robot); } else { - logger.LogInformation("Нельзя установить статус Updating для шаблона {templateName}, так как текущий статус это запрещает.", template.Name); + _logger.LogInformation("Нельзя установить статус Updating для шаблона {templateName}, так как текущий статус это запрещает.", template.Name); } } else { // Если пред статус был Update, то ничего не делаем, так его и оставляем, не сбрасывам кол-во попыток и ошибок - logger.LogInformation("Есть изменения в Template {TemplateName}, но предыдущий статус TaskStatusCode: {TaskStatusCode}. Не меняем статус, будем разбираться вручную.", template.Name, (TaskStatusEnum)config.TaskStatusCode); + _logger.LogInformation("Есть изменения в Template {TemplateName}, но предыдущий статус TaskStatusCode: {TaskStatusCode}. Не меняем статус, будем разбираться вручную.", template.Name, (TaskStatusEnum)config.TaskStatusCode); } }//надо ли проверять если не изменился, но был статус Updating не понятно. Доверяем роботу пока, что после окончания работ он точно сообщит else { - logger.LogDebug("Нет изменений, обновление не требуется. {TemplateName}", esppObject.TemplateName); + _logger.LogDebug("Нет изменений, обновление не требуется. {TemplateName}", esppObject.TemplateName); //если все поля совпали //проверяем, какой был статус предыдущий статус в БД, если он был не Ок, то ставим ему ОК @@ -187,15 +205,15 @@ namespace PARR.EsppSync { robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Ok, robotConfig); if (!await templateService.CommitAsync(GetInitiator())) - logger.LogError("Не удалось изменить запись Template {TemplateName}, Robot: {Robot}", template.Name, esppObject.Robot); + _logger.LogError("Не удалось изменить запись Template {TemplateName}, Robot: {Robot}", template.Name, esppObject.Robot); else - logger.LogInformation("Установлен принудительный статус {TaskStatus}, Template {TemplateName}, Robot: {Robot}", TaskStatusEnum.Ok, template.Name, esppObject.Robot); + _logger.LogInformation("Установлен принудительный статус {TaskStatus}, Template {TemplateName}, Robot: {Robot}", TaskStatusEnum.Ok, template.Name, esppObject.Robot); } } } catch (Exception ex) { - logger.LogError(ex, "Ошибка синхронизации объекта АСУ ЕСПП {TemplateName}", esppObject.TemplateName); + _logger.LogError(ex, "Ошибка синхронизации объекта АСУ ЕСПП {TemplateName}, {EsppObject}", esppObject.TemplateName, nameof(EsppObject)); } } } @@ -243,36 +261,6 @@ namespace PARR.EsppSync } - ///// - ///// Установить статус - Обновить - ///// - ///// - ///// - ///// - //private void SetUpdateStatus(ref Template template, IRobotConfigurationService robotConfigurationService, RobotsEnum robot) - //{ - // var robotConfig = robotConfigurationService.GetFromTemplateByRobotCode(robot, template); - // robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, robotConfig); - //} - - - /// - /// Получить сервис из scope - /// - /// - /// - /// - /// - private Service GetServiceInScope(IServiceScope scope) - { - var service = scope.ServiceProvider.GetService(); - if (service == null) - throw new Exception($"Не найден сервис: {nameof(Service)}"); - - return service; - } - - /// /// Сравнение объектов /// @@ -282,31 +270,35 @@ namespace PARR.EsppSync /// private bool IsChanged(object esppObj, object dbObj, string templateName) { - foreach (var prop in dbObj.GetType().GetProperties()) + var dbType = dbObj.GetType(); + var esppType = esppObj.GetType(); + + foreach (var dbProp in dbType.GetProperties()) { // Пропускаем свойства, помеченные атрибутом SkipComparison - if (Attribute.IsDefined(prop, typeof(SkipComparisonAttribute))) + if (Attribute.IsDefined(dbProp, typeof(SkipComparisonAttribute))) { - logger.LogDebug("Пропущено сравнение поля {PropertyName} (помечено [SkipComparison]). Шаблон: {TemplateName}", prop.Name, templateName); + _logger.LogDebug("Пропущено сравнение поля {PropertyName} (помечено [SkipComparison]). Шаблон: {TemplateName}", dbProp.Name, templateName); continue; } - if (prop == null) - continue; + // Значение из объекта БД + //var dbValue = dbObj.GetType().GetProperty(prop.Name)?.GetValue(dbObj, null); + var dbValue = dbProp.GetValue(dbObj); - var dbValue = dbObj.GetType().GetProperty(prop.Name)?.GetValue(dbObj, null); - var esppValue = esppObj.GetType().GetProperty(prop.Name)?.GetValue(esppObj, null); + // Ищем аналогичное свойство в объекте из ЕСПП по имени + //var esppValue = esppObj.GetType().GetProperty(dbProp.Name)?.GetValue(esppObj, null); + var esppProp = esppType.GetProperty(dbProp.Name); + var esppValue = esppProp?.GetValue(esppObj); - if (dbValue == null || esppValue == null) - continue; - - //Replace("\r","").Replace("\n","") - в подробном описании могут быть переносы строк, в Rabbit прилетает без переносов. Убираем переносы для стравнения - var dbValueStr = EsppSyncHelpers.Normalize(dbValue!.ToString()); - var esppValueStr = EsppSyncHelpers.Normalize(esppValue!.ToString()); + // Replace("\r","").Replace("\n","") - в подробном описании могут быть переносы строк, в Rabbit прилетает без переносов. Убираем переносы для стравнения + // Если значение null, хелпер Normalize вернет string.Empty, что предотвратит NRE и ложные срабатывания. + var dbValueStr = EsppSyncHelpers.Normalize(dbValue?.ToString()); + var esppValueStr = EsppSyncHelpers.Normalize(esppValue?.ToString()); if (dbValueStr != esppValueStr) { - logger.LogInformation("Не совпадают поля ({PropertyName}). dbValueStr: {DbValueStr}, esppValueStr: {EsppValueStr}. Имя шаблона: {TemplateName}", prop.Name, dbValueStr, esppValueStr, templateName); + _logger.LogInformation("Не совпадают поля ({PropertyName}). dbValueStr: {DbValueStr}, esppValueStr: {EsppValueStr}. Имя шаблона: {TemplateName}", dbProp.Name, dbValueStr, esppValueStr, templateName); return true; }