From fb9578fec0493247ced68fb243c4b51d7f6e73a0 Mon Sep 17 00:00:00 2001 From: Mikhail Kuznetsov Date: Fri, 3 Jul 2026 14:01:56 +1000 Subject: [PATCH] =?UTF-8?q?feat(templateMatcher):=20=D0=92=20=D1=81=D0=B8?= =?UTF-8?q?=D0=BD=D1=85=D1=80=D0=BE=D0=BD=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8E=20=D0=BD=D0=B5=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D1=83=D0=B5=D0=BC=D1=8B=D1=85=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BC=D0=B5=D1=82=D1=80=D0=B8=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B2=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=B8=20=D0=B2=D1=8B=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GroupedTemplateSynchronizer.cs | 8 +- .../SimpleTemplateSynchronizer.cs | 251 ++++++++---------- .../Implementations/TemplateMatcher.cs | 1 - 3 files changed, 122 insertions(+), 138 deletions(-) diff --git a/PARR.TemplateMatcher/Services/Implementations/GroupedTemplateSynchronizer.cs b/PARR.TemplateMatcher/Services/Implementations/GroupedTemplateSynchronizer.cs index 7b2eaeef..f902253a 100644 --- a/PARR.TemplateMatcher/Services/Implementations/GroupedTemplateSynchronizer.cs +++ b/PARR.TemplateMatcher/Services/Implementations/GroupedTemplateSynchronizer.cs @@ -3,11 +3,12 @@ using PARR.Core.Services.MatchingStatusService; using PARR.Domain.Cache.Models; using PARR.Domain.Entities.Base.History; using PARR.Domain.Enums; +using PARR.TemplateMatcher.Services.GroupedSync; using PARR.TemplateMatcher.Services.Implementations.GroupedSync; using PARR.TemplateMatcher.Services.Interfaces; using System.Diagnostics; -namespace PARR.TemplateMatcher.Services.GroupedSync; +namespace PARR.TemplateMatcher.Services.Implementations; internal class GroupedTemplateSynchronizer : ITemplateSynchronizer { @@ -20,7 +21,8 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer IEnumerable readStages, IEnumerable writeStages, IMatchingStatusService matchingStatusService, - ILogger logger) + ILogger logger + ) { _readStages = readStages; _writeStages = writeStages; @@ -33,7 +35,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer _logger.LogInformation("Начало синхронизации шаблонов для JobGroup {JobGroupId}", jobGroupId); var existingStatus = await _matchingStatusService.GetStatusAsync(jobGroupId, SyncTaskEntityTypeEnum.JobGroup); - if (existingStatus.DetailsJobGroups?.Any() == true) + if (existingStatus.DetailsJobGroups?.Count > 0) { _logger.LogWarning("Синхронизация для JobGroup {JobGroupId} уже запущена. Пропускаем.", jobGroupId); return; diff --git a/PARR.TemplateMatcher/Services/Implementations/SimpleTemplateSynchronizer.cs b/PARR.TemplateMatcher/Services/Implementations/SimpleTemplateSynchronizer.cs index 27db4b42..724299f3 100644 --- a/PARR.TemplateMatcher/Services/Implementations/SimpleTemplateSynchronizer.cs +++ b/PARR.TemplateMatcher/Services/Implementations/SimpleTemplateSynchronizer.cs @@ -1,7 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using PARR.Core.Common.Interfaces.RabbitServices; using PARR.Core.Repositories.Interfaces; using PARR.Core.Repositories.Interfaces.JobRepositories; using PARR.Core.Repositories.Interfaces.Unit; @@ -26,41 +25,32 @@ namespace PARR.TemplateMatcher.Services.Implementations; internal class SimpleTemplateSynchronizer : ITemplateSynchronizer { #if DEBUG - private readonly Guid targetUnitId = Guid.Parse("358437ac-1eeb-4c00-840c-998326f657ac"); + private readonly Guid _targetUnitId = Guid.Parse("358437ac-1eeb-4c00-840c-998326f657ac"); #endif - private const bool DefaultUsedTemplateState = false; - private const bool DefaultUsedScheduleState = false; - - private readonly IEnumerable readStages; - private readonly IEnumerable writeStages; - private readonly ILogger logger; - private readonly IUnitFilterService unitFilterService; - private readonly MqSettings mqSettings; - private readonly IRabbitService mqService; - private readonly ITemplateRepository templateService; - private readonly IJobRepository jobService; - private readonly ITemplateNameNormalizer templateNameNormalizer; - private readonly ITemplateAllocationService templateAllocationService; - private readonly ITemplateMqPublisher templateMqPublisher; - private readonly IMatchingStatusService matchingStatusService; - private readonly SettingsFromDb settingsFromDb; - private readonly IOptions templateSettings; - private readonly IUnitFieldRepository unitFieldService; - private readonly IUnitInValueRepository unitInValueService; - private readonly IUnitRepository unitRepository; + private readonly IEnumerable _readStages; + private readonly IEnumerable _writeStages; + private readonly ILogger _logger; + private readonly IUnitFilterService _unitFilterService; + private readonly ITemplateRepository _templateService; + private readonly IJobRepository _jobService; + private readonly ITemplateNameNormalizer _templateNameNormalizer; + private readonly ITemplateMqPublisher _templateMqPublisher; + private readonly IMatchingStatusService _matchingStatusService; + private readonly SettingsFromDb _settingsFromDb; + private readonly IOptions _templateSettings; + private readonly IUnitFieldRepository _unitFieldService; + private readonly IUnitInValueRepository _unitInValueService; + private readonly IUnitRepository _unitRepository; public SimpleTemplateSynchronizer( IEnumerable readStages, IEnumerable writeStages, ILogger logger, IUnitFilterService unitFilterService, - MqSettings mqSettings, - IRabbitService mqService, ITemplateRepository templateService, IJobRepository jobService, ITemplateNameNormalizer templateNameNormalizer, - ITemplateAllocationService templateAllocationService, ITemplateMqPublisher templateMqPublisher, IMatchingStatusService matchingStatusService, SettingsFromDb settingsFromDb, @@ -70,40 +60,37 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer IUnitRepository unitRepository ) { - this.readStages = readStages; - this.writeStages = writeStages; - this.logger = logger; - this.unitFilterService = unitFilterService; - this.mqSettings = mqSettings; - this.mqService = mqService; - this.templateService = templateService; - this.jobService = jobService; - this.templateNameNormalizer = templateNameNormalizer; - this.templateAllocationService = templateAllocationService; - this.templateMqPublisher = templateMqPublisher; - this.matchingStatusService = matchingStatusService; - this.settingsFromDb = settingsFromDb; - this.templateSettings = templateSettings; - this.unitFieldService = unitFieldService; - this.unitInValueService = unitInValueService; - this.unitRepository = unitRepository; + this._readStages = readStages; + this._writeStages = writeStages; + this._logger = logger; + this._unitFilterService = unitFilterService; + this._templateService = templateService; + this._jobService = jobService; + this._templateNameNormalizer = templateNameNormalizer; + this._templateMqPublisher = templateMqPublisher; + this._matchingStatusService = matchingStatusService; + this._settingsFromDb = settingsFromDb; + this._templateSettings = templateSettings; + this._unitFieldService = unitFieldService; + this._unitInValueService = unitInValueService; + this._unitRepository = unitRepository; } public async Task SyncTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator) { - if (jobId == settingsFromDb.JobIdForUnusedTemplates) + if (jobId == _settingsFromDb.JobIdForUnusedTemplates) { - logger.LogInformation("Обработка синхронизации для Job неиспользуемых шаблонов '{JobId}'", jobId); + _logger.LogInformation("Обработка синхронизации для Job неиспользуемых шаблонов '{JobId}'", jobId); await SyncUnusedTemplatesAsync(jobId, initiator); return; } - logger.LogInformation("Начало синхронизации шаблонов для Job {JobId}", jobId); + _logger.LogInformation("Начало синхронизации шаблонов для Job {JobId}", jobId); - var existingStatus = await matchingStatusService.GetStatusAsync(jobId, SyncTaskEntityTypeEnum.Job); - if (existingStatus.DetailsJobs?.Any() == true) + var existingStatus = await _matchingStatusService.GetStatusAsync(jobId, SyncTaskEntityTypeEnum.Job); + if (existingStatus.DetailsJobs?.Count > 0) { - logger.LogWarning("Синхронизация для Job {JobId} уже запущена. Пропускаем.", jobId); + _logger.LogWarning("Синхронизация для Job {JobId} уже запущена. Пропускаем.", jobId); return; } @@ -113,49 +100,48 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer Action = TemplateMatcherActionEnum.Sync, Comment = "Начало синхронизации" }; - await matchingStatusService.SetMatchingStatusAsync( + await _matchingStatusService.SetMatchingStatusAsync( jobId, SyncTaskEntityTypeEnum.Job, new MatchingStatusItem { Data = initialStatus, Timestamp = DateTimeOffset.UtcNow, Source = nameof(SimpleTemplateSynchronizer) }, TimeSpan.FromMinutes(35)); // Таймер запускается ПОСЛЕ инфраструктурных операций (статус, проверка блокировки) var totalSw = Stopwatch.StartNew(); + var context = new SimpleSyncContext { JobId = jobId, Initiator = initiator }; try { - var context = new SimpleSyncContext { JobId = jobId, Initiator = initiator }; - - foreach (var stage in readStages) + foreach (var stage in _readStages) { var stageSw = Stopwatch.StartNew(); await stage.ExecuteAsync(context); stageSw.Stop(); - logger.LogDebug("[Perf] Job '{JobName}' ({JobId}) | Этап: {Stage} | Время: {Ms} мс", + _logger.LogDebug("[Perf] Job '{JobName}' ({JobId}) | Этап: {Stage} | Время: {Ms} мс", context.JobName, jobId, stage.StageName, stageSw.ElapsedMilliseconds); } - foreach (var stage in writeStages) + foreach (var stage in _writeStages) { var stageSw = Stopwatch.StartNew(); await stage.ExecuteAsync(context); stageSw.Stop(); - logger.LogDebug("[Perf] Job '{JobName}' ({JobId}) | Этап: {Stage} | Время: {Ms} мс", + _logger.LogDebug("[Perf] Job '{JobName}' ({JobId}) | Этап: {Stage} | Время: {Ms} мс", context.JobName, jobId, stage.StageName, stageSw.ElapsedMilliseconds); } totalSw.Stop(); - logger.LogInformation("[Perf] Job '{JobName}' ({JobId}) | ИТОГО: {TotalMs} мс", + _logger.LogInformation("[Perf] Job '{JobName}' ({JobId}) | ИТОГО: {TotalMs} мс", context.JobName, jobId, totalSw.ElapsedMilliseconds); await UpdateMatchingStatusAsync(jobId, "Синхронизация завершена успешно"); - await matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job); - logger.LogInformation("Синхронизация шаблонов завершена для Job '{JobName}' ({JobId})", + await _matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job); + _logger.LogInformation("Синхронизация шаблонов завершена для Job '{JobName}' ({JobId})", context.JobName, jobId); } catch (Exception ex) { totalSw.Stop(); - logger.LogError(ex, "Ошибка при синхронизации Job '{JobName}' ({JobId}) через {ElapsedMs} мс", + _logger.LogError(ex, "Ошибка при синхронизации Job '{JobName}' ({JobId}) через {ElapsedMs} мс", string.Empty, jobId, totalSw.ElapsedMilliseconds); await UpdateMatchingStatusAsync(jobId, $"Ошибка: {ex.Message}"); throw; @@ -165,19 +151,19 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer public async Task SyncTemplatesForJobGroupAsync(Guid jobGroupId, HistoryInitiator initiator) { - logger.LogWarning("SimpleTemplateSynchronizer: SyncTemplatesForJobGroup вызван для JobGroup {JobGroupId}. Это не поддерживаемая операция.", jobGroupId); + _logger.LogWarning("SimpleTemplateSynchronizer: SyncTemplatesForJobGroup вызван для JobGroup {JobGroupId}. Это не поддерживаемая операция.", jobGroupId); } public async Task UpdateTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator) { - logger.LogDebug("Обновление шаблонов для Job {JobId}", jobId); + _logger.LogDebug("Обновление шаблонов для Job {JobId}", jobId); // === Проверка: уже запущена? === - var existingStatus = await matchingStatusService.GetStatusAsync(jobId, SyncTaskEntityTypeEnum.Job); - if (existingStatus.DetailsJobs?.Any() == true) + var existingStatus = await _matchingStatusService.GetStatusAsync(jobId, SyncTaskEntityTypeEnum.Job); + if (existingStatus.DetailsJobs?.Count > 0) { - logger.LogWarning("Обновление для Job {JobId} уже запущено. Пропускаем.", jobId); + _logger.LogWarning("Обновление для Job {JobId} уже запущено. Пропускаем.", jobId); return; } @@ -187,7 +173,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer Action = TemplateMatcherActionEnum.Update, Comment = "Начало обновления имён" }; - await matchingStatusService.SetMatchingStatusAsync( + await _matchingStatusService.SetMatchingStatusAsync( jobId, SyncTaskEntityTypeEnum.Job, new MatchingStatusItem { Data = initialStatus, Timestamp = DateTimeOffset.UtcNow, Source = nameof(SimpleTemplateSynchronizer) }, @@ -196,7 +182,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer try { - var job = await jobService.Get() + var job = await _jobService.Get() .AsNoTracking() .Include(j => j.AutoControl) .Include(j => j.Tnk) @@ -208,18 +194,18 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer if (job == null) { - logger.LogWarning("Job {JobId} не найден.", jobId); + _logger.LogWarning("Job {JobId} не найден.", jobId); await UpdateMatchingStatusAsync(jobId, "Job не найден"); return; } // === Получение отфильтрованных юнитов с полной информацией === - var filteredUnits = await unitFilterService.GetUnitsByJobFilterAsync(jobId); + var filteredUnits = await _unitFilterService.GetUnitsByJobFilterAsync(jobId); if (filteredUnits == null || !filteredUnits.Any()) { - logger.LogInformation("Для Job {JobId} фильтры не дали Unit'ов.", jobId); + _logger.LogInformation("Для Job {JobId} фильтры не дали Unit'ов.", jobId); await UpdateMatchingStatusAsync(jobId, "Нет Unit'ов — обновление не требуется"); - await matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job); + await _matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job); return; } @@ -228,17 +214,17 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer #if DEBUG // Отладка: проверить, есть ли юнит в unitIds - if (unitIds.Contains(targetUnitId)) + if (unitIds.Contains(_targetUnitId)) { - logger.LogDebug("Юнит {TargetUnitId} найден в unitIds.", targetUnitId); + _logger.LogDebug("Юнит {TargetUnitId} найден в unitIds.", _targetUnitId); } else { - logger.LogDebug("Юнит {TargetUnitId} НЕ найден в unitIds.", targetUnitId); + _logger.LogDebug("Юнит {TargetUnitId} НЕ найден в unitIds.", _targetUnitId); } #endif - var existingTemplates = await templateService.Get() + var existingTemplates = await _templateService.Get() .AsNoTracking() .Include(t => t.Unit) .Include(t => t.UnitsInTemplate) @@ -254,10 +240,10 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer { if (unitIds.Contains(template.UnitId)) { - var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(template); + var expectedName = await _templateNameNormalizer.GetNormalizedTemplateNameAsync(template); if (!string.Equals(template.Name, expectedName, StringComparison.OrdinalIgnoreCase)) { - logger.LogDebug("Шаблон {TemplateId} требует обновления имени: старое = '{OldName}', новое = '{NewName}'", template.Id, template.Name, expectedName); + _logger.LogDebug("Шаблон {TemplateId} требует обновления имени: старое = '{OldName}', новое = '{NewName}'", template.Id, template.Name, expectedName); var updateRequest = new TemplateUpdaterMessage { @@ -274,18 +260,18 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer UnitsInTemplate = new List() // для простого шаблона }; - await templateMqPublisher.PublishUpdateAsync(updateRequest); + await _templateMqPublisher.PublishUpdateAsync(updateRequest); } } } await UpdateMatchingStatusAsync(jobId, "Обновление завершено"); - await matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job); - logger.LogInformation("Обновление шаблонов завершено для Job {JobId}.", jobId); + await _matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job); + _logger.LogInformation("Обновление шаблонов завершено для Job {JobId}.", jobId); } catch (Exception ex) { - logger.LogError(ex, "Ошибка при обновлении Job {JobId}", jobId); + _logger.LogError(ex, "Ошибка при обновлении Job {JobId}", jobId); await UpdateMatchingStatusAsync(jobId, $"Ошибка: {ex.Message}"); throw; } @@ -297,10 +283,10 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer // Проверка отмены в самом начале ct.ThrowIfCancellationRequested(); - var existingStatus = await matchingStatusService.GetStatusAsync(unusedJobId, SyncTaskEntityTypeEnum.Job); - if (existingStatus.DetailsJobs?.Any() == true) + var existingStatus = await _matchingStatusService.GetStatusAsync(unusedJobId, SyncTaskEntityTypeEnum.Job); + if (existingStatus.DetailsJobs?.Count > 0) { - logger.LogWarning("Синхронизация для Job неиспользуемых шаблонов {JobId} уже запущена. Пропускаем.", unusedJobId); + _logger.LogWarning("Синхронизация для Job неиспользуемых шаблонов {JobId} уже запущена. Пропускаем.", unusedJobId); return; } @@ -310,22 +296,24 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer Action = TemplateMatcherActionEnum.Sync, Comment = "Синхронизация неиспользуемых шаблонов" }; - await matchingStatusService.SetMatchingStatusAsync( + await _matchingStatusService.SetMatchingStatusAsync( unusedJobId, SyncTaskEntityTypeEnum.Job, new MatchingStatusItem { Data = initialStatus, Timestamp = DateTimeOffset.UtcNow, Source = nameof(SimpleTemplateSynchronizer) }, TimeSpan.FromMinutes(30) ); + var totalSw = Stopwatch.StartNew(); + try { // 1. Находим ID нужных полей - var responsableAreaField = await unitFieldService.GetByAihitNameAsync(UnusedTemplateConstants.ResponsibilityAreaFieldName); - var tagField = await unitFieldService.GetByAihitNameAsync(UnusedTemplateConstants.ParrTagFieldName); + var responsableAreaField = await _unitFieldService.GetByAihitNameAsync(UnusedTemplateConstants.ResponsibilityAreaFieldName, ct); + var tagField = await _unitFieldService.GetByAihitNameAsync(UnusedTemplateConstants.ParrTagFieldName, ct); if (responsableAreaField == null || tagField == null) { - logger.LogError("Не найдены поля '{Field1}' или '{Field2}'. Синхронизация прервана.", UnusedTemplateConstants.ResponsibilityAreaFieldName, UnusedTemplateConstants.NotUsedTagValue); + _logger.LogError("Не найдены поля '{Field1}' или '{Field2}'. Синхронизация прервана.", UnusedTemplateConstants.ResponsibilityAreaFieldName, UnusedTemplateConstants.NotUsedTagValue); await UpdateMatchingStatusAsync(unusedJobId, "Ошибка конфигурации полей"); return; } @@ -334,7 +322,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer var tagFieldId = tagField.Id; // 2. Находим ValueId для тега "ПАРР-НЕИСП" - var targetTagValueId = await unitInValueService.Get() + var targetTagValueId = await _unitInValueService.Get() .AsNoTracking() .Where(uiv => uiv.FieldId == tagFieldId && uiv.Value != null && uiv.Value.Value == UnusedTemplateConstants.NotUsedTagValue) .Select(uiv => uiv.ValueId) @@ -342,31 +330,31 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer if (targetTagValueId == Guid.Empty) { - logger.LogWarning("Значение '{TagValue}' для поля '{FieldName}' не найдено в справочнике UnitFieldValue.", UnusedTemplateConstants.NotUsedTagValue, UnusedTemplateConstants.ParrTagFieldName); + _logger.LogWarning("Значение '{TagValue}' для поля '{FieldName}' не найдено в справочнике UnitFieldValue.", UnusedTemplateConstants.NotUsedTagValue, UnusedTemplateConstants.ParrTagFieldName); } - var unusedJob = await jobService.Get().AsNoTracking() + var unusedJob = await _jobService.Get().AsNoTracking() .Include(t => t!.Group).ThenInclude(t => t!.GroupType) .Include(t => t!.Tnk) .FirstOrDefaultAsync(j => j.Id == unusedJobId, ct); if (unusedJob == null) { - logger.LogError("Job неиспользуемых шаблонов {JobId} не найден.", unusedJobId); + _logger.LogError("Job неиспользуемых шаблонов {JobId} не найден.", unusedJobId); await UpdateMatchingStatusAsync(unusedJobId, "Job не найден"); return; } - var unusedTemplates = await templateService.Get() + var unusedTemplates = await _templateService.Get() .Include(t => t.Unit) .Where(t => t.StatusTypeId == TemplateStatusTypeEnum.Unused) .ToListAsync(ct); if (!unusedTemplates.Any()) { - logger.LogInformation("Не найдено шаблонов со статусом Unused."); + _logger.LogInformation("Не найдено шаблонов со статусом Unused."); await UpdateMatchingStatusAsync(unusedJobId, "Нет шаблонов для обработки"); - await matchingStatusService.DeleteMatchingStatusAsync(unusedJobId, SyncTaskEntityTypeEnum.Job); + await _matchingStatusService.DeleteMatchingStatusAsync(unusedJobId, SyncTaskEntityTypeEnum.Job); return; } @@ -376,7 +364,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer var allTemplateUnitIds = unusedTemplates.Select(t => t.UnitId).Distinct().ToList(); // Получаем значения ЗОНА_ОТВЕТСТВЕННОСТИ для всех юнитов шаблонов - var unitResponsableAreaValues = await unitInValueService.GetByUnitIdsAndFieldIdsAsync(allTemplateUnitIds, new List { responsableAreaFieldId }); + var unitResponsableAreaValues = await _unitInValueService.GetByUnitIdsAndFieldIdsAsync(allTemplateUnitIds, new List { responsableAreaFieldId }, ct); var unitToResponsableAreaValueMap = unitResponsableAreaValues .Where(uiv => uiv.ValueId != Guid.Empty) .ToDictionary(uiv => uiv.UnitId, uiv => uiv.ValueId); @@ -385,15 +373,16 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer var responsableAreaToTargetUnitMap = new Dictionary(); var distinctResponsableAreaValues = unitToResponsableAreaValueMap.Values.Distinct().ToList(); - if (targetTagValueId != Guid.Empty && distinctResponsableAreaValues.Any()) + if (targetTagValueId != Guid.Empty && + distinctResponsableAreaValues.Any()) { - logger.LogDebug("Пакетный поиск целевых юнитов с тегом 'ПАРР-НЕИСП' для {Count} уникальных значений ЗОНА_ОТВЕТСТВЕННОСТИ.", distinctResponsableAreaValues.Count); + _logger.LogDebug("Пакетный поиск целевых юнитов с тегом 'ПАРР-НЕИСП' для {Count} уникальных значений ЗОНА_ОТВЕТСТВЕННОСТИ.", distinctResponsableAreaValues.Count); // Передаем ct в ToListAsync - var matches = await unitInValueService.Get().AsNoTracking() + var matches = await _unitInValueService.Get().AsNoTracking() .Where(uiv => uiv.FieldId == responsableAreaFieldId && distinctResponsableAreaValues.Contains(uiv.ValueId)) .Join( - unitInValueService.Get().AsNoTracking().Where(t => t.FieldId == tagFieldId && t.ValueId == targetTagValueId), + _unitInValueService.Get().AsNoTracking().Where(t => t.FieldId == tagFieldId && t.ValueId == targetTagValueId), responsableArea => responsableArea.UnitId, tag => tag.UnitId, (responsableArea, tag) => new { responsableArea.ValueId, responsableArea.UnitId } @@ -404,7 +393,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer .GroupBy(x => x.ValueId) .ToDictionary(g => g.Key, g => g.First().UnitId); - logger.LogDebug("Сформирован кэш соответствий: найдено {Count} целевых юнитов.", responsableAreaToTargetUnitMap.Count); + _logger.LogDebug("Сформирован кэш соответствий: найдено {Count} целевых юнитов.", responsableAreaToTargetUnitMap.Count); } foreach (var template in unusedTemplates) @@ -416,7 +405,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer if (template.Unit == null) { - logger.LogWarning("У шаблона {TemplateId} отсутствует Unit. Пропускаем.", template.Id); + _logger.LogWarning("У шаблона {TemplateId} отсутствует Unit. Пропускаем.", template.Id); processed++; continue; } @@ -441,11 +430,11 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer if (targetUnitId.HasValue && targetUnitId.Value != template.UnitId) { - logger.LogInformation("Для шаблона {TemplateId} найден новый UnitId {NewUnitId} (был {OldUnitId}).", + _logger.LogInformation("Для шаблона {TemplateId} найден новый UnitId {NewUnitId} (был {OldUnitId}).", template.Id, targetUnitId.Value, template.UnitId); // Передаем ct в запрос - var newUnit = await unitRepository.Get().AsNoTracking() + var newUnit = await _unitRepository.Get().AsNoTracking() .FirstOrDefaultAsync(u => u.Id == targetUnitId.Value, ct); if (newUnit != null) @@ -454,13 +443,13 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer } else { - logger.LogWarning("Не удалось загрузить новый юнит {UnitId}. Используем старый.", targetUnitId.Value); + _logger.LogWarning("Не удалось загрузить новый юнит {UnitId}. Используем старый.", targetUnitId.Value); finalUnitId = template.UnitId; } } else if (!hasResponsableArea) { - logger.LogDebug("У юнита {UnitId} шаблона {TemplateId} нет значения поля ЗОНА_ОТВЕТСТВЕННОСТИ. Оставляем текущий UnitId.", template.UnitId, template.Id); + _logger.LogDebug("У юнита {UnitId} шаблона {TemplateId} нет значения поля ЗОНА_ОТВЕТСТВЕННОСТИ. Оставляем текущий UnitId.", template.UnitId, template.Id); } // 4. Генерация целевого имени @@ -473,30 +462,43 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer if (!unitChanged && !jobChanged && !nameChanged) { - logger.LogDebug("Шаблон {TemplateId} уже актуален. Пропуск отправки в MQ.", template.Id); + _logger.LogDebug("Шаблон {TemplateId} уже актуален. Пропуск отправки в MQ.", template.Id); processed++; continue; } await SendUpdateRequest(template, unusedJobId, expectedName, initiator, finalUnitId); - logger.LogDebug("Отправлен запрос на обновление шаблона {TemplateId}. Изменения: Unit={U}, Job={J}, Name={N}", + _logger.LogDebug("Отправлен запрос на обновление шаблона {TemplateId}. Изменения: Unit={U}, Job={J}, Name={N}", template.Id, unitChanged, jobChanged, nameChanged); processed++; } catch (Exception ex) { - logger.LogError(ex, "Ошибка при обработке шаблона {TemplateId}", template.Id); + _logger.LogError(ex, "Ошибка при обработке шаблона {TemplateId}", template.Id); } } + + totalSw.Stop(); + + totalSw.Stop(); + _logger.LogInformation("[Perf] Job '{JobName}' ({JobId}) | ИТОГО: {TotalMs} мс", + unusedJob.Name, unusedJob.Id, totalSw.ElapsedMilliseconds); + await UpdateMatchingStatusAsync(unusedJobId, "Синхронизация неиспользуемых шаблонов завершена"); - await matchingStatusService.DeleteMatchingStatusAsync(unusedJobId, SyncTaskEntityTypeEnum.Job); - logger.LogInformation("Синхронизация неиспользуемых шаблонов завершена. Обработано {Count} шаблонов.", unusedTemplates.Count); + await _matchingStatusService.DeleteMatchingStatusAsync(unusedJobId, SyncTaskEntityTypeEnum.Job); + totalSw.Stop(); + _logger.LogInformation( + "Синхронизация неиспользуемых шаблонов завершена. Обработано {Count} шаблонов", + unusedTemplates.Count); } catch (Exception ex) { - logger.LogError(ex, "Ошибка при синхронизации неиспользуемых шаблонов для Job {JobId}", unusedJobId); + totalSw.Stop(); + _logger.LogError(ex, + "Ошибка при синхронизации неиспользуемых шаблонов для Job {JobId} через {ElapsedMs} мс", + unusedJobId, totalSw.ElapsedMilliseconds); await UpdateMatchingStatusAsync(unusedJobId, $"Ошибка: {ex.Message}"); throw; } @@ -520,7 +522,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer UnitsInTemplate = new List() }; - await templateMqPublisher.PublishUpdateAsync(updateRequest); + await _templateMqPublisher.PublishUpdateAsync(updateRequest); } @@ -534,7 +536,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer MinValueRelationships = unusedJob.MinValueRelationships, MaxValueRelationships = unusedJob.MaxValueRelationships, IsParentRelationships = unusedJob.IsParentRelationships, - TemplateNameMask = templateSettings.Value.UnusedTemplateNameMask, + TemplateNameMask = _templateSettings.Value.UnusedTemplateNameMask, WorkGroupMask = unusedJob.WorkGroupMask, ResponseAreaMask = unusedJob.ResponseAreaMask, TnkId = unusedJob.TnkId, @@ -558,26 +560,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer UnitsInTemplate = new List() }; - return await templateNameNormalizer.GetNormalizedTemplateNameAsync(tempTemplateForName); - } - - - private async Task CreateSimpleTemplateAsync(Guid jobId, Guid unitId, HistoryInitiator initiator) - { - logger.LogInformation("Создание нового простого шаблона для Job {JobId}, UnitId {UnitId}.", jobId, unitId); - - var mqRequest = new TemplateGeneratorMessage - { - JobId = jobId, - UnitId = unitId, - UnitsInTemplate = new List(), // для простого шаблона - HistoryInitiator = initiator - }; - - var result = await mqService.SendAsync(mqSettings.TemplateGenerator, new List { mqRequest }); - - if (!result.IsSuccess) - logger.LogError("Ошибка отправки команды создания простого шаблона для Job {JobId}, UnitId {UnitId}.", jobId, unitId); + return await _templateNameNormalizer.GetNormalizedTemplateNameAsync(tempTemplateForName); } @@ -589,7 +572,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer Action = TemplateMatcherActionEnum.Sync, Comment = comment }; - await matchingStatusService.SetMatchingStatusAsync( + await _matchingStatusService.SetMatchingStatusAsync( jobId, SyncTaskEntityTypeEnum.Job, new MatchingStatusItem { Data = status, Timestamp = DateTimeOffset.UtcNow, Source = nameof(SimpleTemplateSynchronizer) }, diff --git a/PARR.TemplateMatcher/Services/Implementations/TemplateMatcher.cs b/PARR.TemplateMatcher/Services/Implementations/TemplateMatcher.cs index 15da9ca3..eccc77ee 100644 --- a/PARR.TemplateMatcher/Services/Implementations/TemplateMatcher.cs +++ b/PARR.TemplateMatcher/Services/Implementations/TemplateMatcher.cs @@ -5,7 +5,6 @@ using PARR.Core.Repositories.Interfaces.JobRepositories; using PARR.Domain.Entities.Base.History; using PARR.Domain.Entities.JobEntities; using PARR.Domain.Enums; -using PARR.TemplateMatcher.Services.GroupedSync; using PARR.TemplateMatcher.Services.Interfaces; namespace PARR.TemplateMatcher.Services.Implementations