using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PARR.BLL.Domain.Mq; using PARR.BLL.Services.Interfaces; using PARR.Common.Domain; using PARR.Constants; using PARR.DAL.DomainServices.Interfaces; using PARR.DAL.Models; using PARR.DAL.Models.Job; using PARR.DAL.Services.Interfaces; using PARR.DAL.Services.Interfaces.Job; using PARR.TemplateMatcher.Settings; using System.Text.Json; namespace PARR.TemplateMatcher { internal class TemplateMatcher : ITemplateMatcher { private readonly ILogger logger; private readonly IUnitFilterService unitFilterService; private readonly MqSettings mqSettings; private readonly IMqService mqService; private readonly ITemplateService templateService; private readonly IJobService jobService; private readonly IShortcodesService shortcodesService; public TemplateMatcher( ILogger logger, IUnitFilterService unitFilterService, MqSettings mqSettings, IMqService mqService, ITemplateService templateService, IJobService jobService, IShortcodesService shortcodesService ) { this.logger = logger; this.unitFilterService = unitFilterService; this.mqSettings = mqSettings; this.mqService = mqService; this.templateService = templateService; this.jobService = jobService; this.shortcodesService = shortcodesService; } public async Task SyncTemplatesForJob(Guid jobId, HistoryInitiator initiator) { logger.LogDebug("Начало синхронизации шаблонов для JobId {JobId}", jobId); // 1. Получаем UnitId, которые должны иметь шаблоны (по фильтрам Job) var units = await unitFilterService.GetUnitsIdByJobFilterAsync(jobId); if (units == null) { logger.LogWarning("Для JobId {JobId} фильтр вернул null. Пропускаем.", jobId); return; } var expectedUnitIds = units.ToHashSet(); logger.LogDebug("Для JobId {JobId} найдено {Count} Unit'ов, соответствующих фильтрам.", jobId, expectedUnitIds.Count); if (!expectedUnitIds.Any()) { logger.LogInformation("Для JobId {JobId} не найдено Unit'ов, соответствующих фильтрам.", jobId); return; } // 2. Получаем все шаблоны для этого Job var existingTemplates = await templateService.Get() .Where(t => t.JobId == jobId) .ToListAsync(); logger.LogDebug("Для JobId {JobId} найдено {Count} существующих шаблонов.", jobId, existingTemplates.Count()); // 3. Получаем Job, чтобы получить маску имени var job = await jobService.GetAsync(jobId); if (job == null) { logger.LogError("Job с Id {JobId} не найден.", jobId); return; } // 4. Находим UnitId, для которых нет шаблонов (нужно создать) var existingUnitIds = existingTemplates.Select(t => t.UnitId).ToHashSet(); var unitIdsToCreate = expectedUnitIds.Except(existingUnitIds); logger.LogDebug("Для JobId {JobId} найдено {Count} Unit'ов без шаблонов (требуется создание).", jobId, unitIdsToCreate.Count()); foreach (var unitId in unitIdsToCreate) { logger.LogInformation("Для UnitId {UnitId} нет шаблона, запрашиваем создание.", unitId); var mqRequest = new TemplateGeneratorWorkerMq { JobId = jobId, UnitId = unitId, HistoryInitiator = initiator }; var msg = JsonSerializer.Serialize(mqRequest); var result = await mqService.SendAsync(mqSettings.TemplateGenerator, new[] { msg }); if (result.IsSuccess) logger.LogInformation("Отправлен запрос на создание шаблона для UnitId {UnitId} в JobId {JobId}", unitId, jobId); else logger.LogError("Ошибка при отправке запроса на создание шаблона для UnitId {UnitId} в JobId {JobId}: {Message}", unitId, jobId, msg); } // 5. Находим шаблоны, которые больше не соответствуют фильтрам (нужно отключить/обновить) var unitIdsToRemove = existingUnitIds.Except(expectedUnitIds); logger.LogDebug("Для JobId {JobId} найдено {Count} Unit'ов с шаблонами, которые больше не соответствуют фильтрам (требуется обновление/отключение).", jobId, unitIdsToRemove.Count()); foreach (var template in existingTemplates) { var unitId = template.UnitId; if (unitIdsToRemove.Contains(template.UnitId)) { logger.LogInformation("Шаблон {TemplateId} для UnitId {UnitId} больше не соответствует фильтрам Job. Требуется обновление статуса.", template.Id, template.UnitId); // 1. Меняем статус template.StatusTypeId = TemplateStatusTypeEnum.Updating; // 2. Сохраняем изменения в базе через сервис var saved = await templateService.CommitAsync(initiator); if (!saved) { logger.LogError(null, "Не удалось сохранить изменения шаблона {TemplateId} перед отправкой в очередь обновления.", template.Id); continue; // пропускаем этот шаблон } // 3. Подготовим сообщение var updateRequest = new TemplateUpdaterMq { TemplateId = template.Id, JobId = template.JobId, Name = await shortcodesService.ApplyShortcodesAsync(job.TemplateNameMask, template.UnitId, jobId), IsActiveTemplate = template.IsActiveTemplate, IsActiveSchedule = false, LastRun = template.LastRun, NextRun = template.NextRun, UnitId = template.UnitId, Index = template.Index, StatusTypeId = TemplateStatusTypeEnum.Unused, // после обновления статус должен быть Unused Initiator = initiator }; // 4. Отправляем в очередь await SendTemplateUpdateMessage(updateRequest); } else { // Unit всё ещё соответствует фильтрам — проверим, нужно ли обновить имя logger.LogDebug("Шаблон {TemplateId} для UnitId {UnitId} соответствует фильтрам. Проверяем имя.", template.Id, unitId); await CheckAndSendNameUpdateIfRequired(template, job, initiator); } } logger.LogDebug("Окончание синхронизации шаблонов для JobId {JobId}", jobId); } public async Task UpdateTemplatesForJob(Guid jobId, HistoryInitiator initiator) { logger.LogDebug("Начало обновления имён шаблонов для JobId {JobId}", jobId); // 1. Получаем все шаблоны для этого Job var existingTemplates = await templateService.Get() .Where(t => t.JobId == jobId) .ToListAsync(); logger.LogDebug("Для JobId {JobId} найдено {Count} существующих шаблонов.", jobId, existingTemplates.Count()); if (!existingTemplates.Any()) { logger.LogInformation("Для JobId {JobId} не найдено существующих шаблонов для обновления имени.", jobId); return; } // 2. Получаем Job, чтобы получить маску имени var job = await jobService.GetAsync(jobId); if (job == null) { logger.LogError("Job с Id {JobId} не найден.", jobId); return; } foreach (var template in existingTemplates) { logger.LogDebug("Проверяем имя шаблона {TemplateId} для UnitId {UnitId}.", template.Id, template.UnitId); // Проверяем, нужно ли обновить имя await CheckAndSendNameUpdateIfRequired(template, job, initiator); } logger.LogDebug("Окончание обновления имён шаблонов для JobId {JobId}", jobId); } private async Task CheckAndSendNameUpdateIfRequired(Template template, Job job, HistoryInitiator initiator) { var expectedName = await shortcodesService.ApplyShortcodesAsync(job.TemplateNameMask, template.UnitId, job.Id); logger.LogDebug("Шаблон {TemplateId}: текущее имя '{CurrentName}', ожидаемое имя '{ExpectedName}'", template.Id, template.Name, expectedName); if (template.Name != expectedName) { logger.LogInformation("Шаблон {TemplateId} для UnitId {UnitId} имеет устаревшее имя. Требуется обновление.", template.Id, template.UnitId); // 1. Меняем статус в шаблоне template.StatusTypeId = TemplateStatusTypeEnum.Updating; // 2. Сохраняем изменения в базе через сервис var saved = await templateService.CommitAsync(initiator); if (!saved) { logger.LogError("Не удалось сохранить изменения шаблона {TemplateId} перед отправкой в очередь обновления.", template.Id); return; // не отправляем сообщение, если не сохранили } // 3. Подготовим сообщение для отправки в очередь var updateRequest = new TemplateUpdaterMq { TemplateId = template.Id, JobId = template.JobId, Name = expectedName, IsActiveTemplate = template.IsActiveTemplate, IsActiveSchedule = template.IsActiveSchedule, LastRun = template.LastRun, NextRun = template.NextRun, UnitId = template.UnitId, Index = template.Index, StatusTypeId = TemplateStatusTypeEnum.Used, Initiator = initiator }; // 4. Отправляем в очередь await SendTemplateUpdateMessage(updateRequest); } else { logger.LogDebug("Шаблон {TemplateId} для UnitId {UnitId} имеет актуальное имя.", template.Id, template.UnitId); } } private async Task SendTemplateUpdateMessage(TemplateUpdaterMq updateRequest) { logger.LogDebug("Отправка сообщения в очередь '{Queue}' для шаблона {TemplateId}", mqSettings.TemplateUpdater.QueueName, updateRequest.TemplateId); var msg = JsonSerializer.Serialize(updateRequest); var result = await mqService.SendAsync(mqSettings.TemplateUpdater, new[] { msg }); if (result.IsSuccess) { logger.LogInformation("Отправлен запрос на обновление шаблона {TemplateId}", updateRequest.TemplateId); } else { logger.LogError("Ошибка при отправке запроса на обновление шаблона {TemplateId}: {Message}", updateRequest.TemplateId, msg); } } } }