using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PARR.BLL.Domain.Mq; using PARR.Constants; using PARR.DAL.Contracts; using PARR.DAL.Extensions; using PARR.DAL.Models; using PARR.DAL.NextRunServices; using PARR.DAL.Services.Interfaces; using PARR.DAL.Services.Interfaces.Job; using PARR.DAL.Services.Interfaces.Unit; namespace PARR.TemplateUpdater.Services { internal class TemplateUpdaterService : ITemplateUpdaterService { private readonly ILogger logger; private readonly ITemplateService templateService; private readonly IJobService jobService; private readonly IUnitService unitService; private readonly IRobotConfigurationService robotConfigurationService; private readonly INextRunService nextRunService; public TemplateUpdaterService( ILogger logger, ITemplateService templateService, IJobService jobService, IUnitService unitService, IRobotConfigurationService robotConfigurationService, INextRunService nextRunService ) { this.logger = logger; this.templateService = templateService; this.jobService = jobService; this.unitService = unitService; this.robotConfigurationService = robotConfigurationService; this.nextRunService = nextRunService; } public async Task UpdateTemplateAsync(TemplateUpdaterMq query) { var isValid = await IsValidAsync(query); if (!isValid) { logger.LogError("Не валидны входные данные. Конец обработки сообщения {Query}", query.ToJson()); return; } var template = await templateService.Get() .Include(t => t.RobotConfigurations) .Include(t => t.UnitsInTemplate) .AsSplitQuery() .FirstOrDefaultAsync(t => t.Id == query.TemplateId); if (template == null) { logger.LogError("Не найден шаблон с id {TemplateId}", query.TemplateId); return; } var templateIsChanged = false; var scheduleIsChanged = false; if (template.Name != query.Name.Trim()) { template.Name = query.Name.Trim(); templateIsChanged = true; scheduleIsChanged = true; } if (template.JobId != query.JobId) { template.JobId = query.JobId; templateIsChanged = true; scheduleIsChanged = true; } if (template.IsActiveTemplate != query.IsActiveTemplate) { template.IsActiveTemplate = query.IsActiveTemplate; templateIsChanged = true; } if (template.IsActiveSchedule != query.IsActiveSchedule) { template.IsActiveSchedule = query.IsActiveSchedule; scheduleIsChanged = true; } var nextRun = await nextRunService.GetNextRunForTemplateAsync(template.Id, query.IsNew); if (!nextRun.HasValue) { logger.LogError("Ошибка при расчете нового nextRun (вернулся null) для шаблона {templateId}, {templateName}", template.Id, template.Name); return; } //if (template.NextRun != query.NextRun) //{ // template.NextRun = query.NextRun; // scheduleIsChanged = true; //} if (template.NextRun != nextRun.Value) { template.LastRun = template.NextRun; template.NextRun = nextRun.Value; scheduleIsChanged = true; } if (template.UnitId != query.UnitId) { template.UnitId = query.UnitId; templateIsChanged = true; scheduleIsChanged = true; } //просто обнволяем. не влияет ни на шаблон, ни на расписание //template.LastRun = query.LastRun; template.Index = query.Index; template.StatusTypeId = query.StatusTypeId; // === Обработка изменения состава UnitsInTemplate === var currentUnitIds = template.UnitsInTemplate.Select(u => u.UnitId).ToHashSet(); var newUnitIds = query.UnitsInTemplate.ToHashSet(); if (!currentUnitIds.SetEquals(newUnitIds)) { // Удаляем старые связи var toRemove = template.UnitsInTemplate .Where(u => !newUnitIds.Contains(u.UnitId)) .ToList(); foreach (var item in toRemove) template.UnitsInTemplate.Remove(item); // Добавляем новые связи var toAdd = newUnitIds.Except(currentUnitIds); foreach (var unitId in toAdd) { template.UnitsInTemplate.Add(new UnitsInTemplate { TemplateId = template.Id, UnitId = unitId, DateCreated = DateTimeOffset.UtcNow }); } // Поскольку коллекция изменилась — шаблон считается изменённым templateIsChanged = true; } // === конец обработки UnitsInTemplate === if (templateIsChanged) { // ставим задачу на обновление шаблона var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.TemplateOrder, template); robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, config); logger.LogDebug("Для шаблона {TemplateId} устанавливаю статус {Status}", template.Id, RobotStatusEnum.Wait.ToString()); } if (scheduleIsChanged) { // ставим задачу на обновление расписания var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.ScheduleOrder, template); robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, config); logger.LogDebug("Для расписания {TemplateId} устанавливаю статус {Status}", template.Id, RobotStatusEnum.Wait.ToString()); } if (!await templateService.CommitAsync(query.Initiator)) { logger.LogError("Не удалось сохранить изменения в БД. {QueryType}: {Query}", nameof(TemplateUpdaterMq), query.ToJson()); return; } logger.LogInformation("Выполнено изменение шаблона в БД. Отправлен запрос на синхронизацию шаблона: {TemplateIsChanged}, расписания: {ScheduleIsChanged}. Query {Query}", templateIsChanged, scheduleIsChanged, query.ToJson()); } /// /// Валидация входящего сообщения /// /// /// private async Task IsValidAsync(TemplateUpdaterMq query) { var template = await templateService.Get().AsNoTracking() .Include(t => t.RobotConfigurations) .FirstOrDefaultAsync(t => t.Id == query.TemplateId); if (template == null) { logger.LogError("Сообщение не валидно. Не найден шаблон с id: {TemplateId}", query.TemplateId); return false; } if (template.ScheduleEsppId == null) { logger.LogError("Сообщение не валидно. Номер расписания не может быть null, робот не сможет обновить шаблон: {TemplateName}", template.Name); return false; } var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.TemplateOrder, template); if (config == null) { logger.LogError("Сообщение не валидно. Не создана конфигурация роботов: {TemplateName}", template.Name); return false; } if (config.TaskStatusCode != (int)TaskStatusEnum.Ok) { logger.LogError("Сообщение не валидно. Не закончено создание или предыдущее обновление - нельзя начинать новое изменение шаблона: {TemplateName}", template.Name); return false; } if (string.IsNullOrEmpty(query.Name?.Trim())) { logger.LogError("Сообщение не валидно. Имя шаблона не может быть пустым: {TemplateName}", query.Name); return false; } var existTemplateName = await templateService.Get().AsNoTracking().AnyAsync(t => t.Id != query.TemplateId && t.Name!.ToUpper() == query.Name.Trim().ToUpper() && t.Index == query.Index); if (existTemplateName) { logger.LogError("Сообщение не валидно. Имя шаблона не уникально: {TemplateName}", query.Name); return false; } var job = await jobService.Get().AsNoTracking().AnyAsync(t => t.Id == query.JobId); if (!job) { logger.LogError("Сообщение не валидно. Не найдена работа с JobId: {JobId}", query.JobId); return false; } var unit = await unitService.Get().AsNoTracking().AnyAsync(t => t.Id == query.UnitId); if (!unit) { logger.LogError("Сообщение не валидно. Не найден unit с UnitId: {UnitId}", query.UnitId); return false; } return true; } } }