2025-12-23 18:27:31 +10:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2026-02-20 15:57:51 +10:00
|
|
|
|
using Microsoft.Extensions.Options;
|
2026-04-30 10:35:31 +10:00
|
|
|
|
using PARR.Core.Repositories.Interfaces;
|
2026-07-03 11:17:48 +10:00
|
|
|
|
using PARR.Core.Repositories.Interfaces.JobRepositories;
|
2026-05-04 11:55:07 +10:00
|
|
|
|
using PARR.Core.Services.MatchingStatusService;
|
|
|
|
|
|
using PARR.Core.Services.UnitFilterService;
|
2026-04-14 16:27:27 +10:00
|
|
|
|
using PARR.Domain.Cache.Models;
|
2026-05-14 16:47:27 +10:00
|
|
|
|
using PARR.Domain.Common.Rabbit.Messages.TemplateMatching;
|
2026-04-13 16:58:30 +10:00
|
|
|
|
using PARR.Domain.Entities.Base.History;
|
2026-04-14 12:01:49 +10:00
|
|
|
|
using PARR.Domain.Enums;
|
2026-05-04 11:55:07 +10:00
|
|
|
|
using PARR.Domain.Settings;
|
2026-09-10 14:12:47 +10:00
|
|
|
|
using PARR.TemplateMatcher.Exceptions;
|
2025-12-23 18:27:31 +10:00
|
|
|
|
using PARR.TemplateMatcher.Services.Interfaces;
|
2026-06-11 15:09:46 +10:00
|
|
|
|
using PARR.TemplateMatcher.Services.SimpleSync;
|
2025-12-23 18:27:31 +10:00
|
|
|
|
using PARR.TemplateMatcher.Settings;
|
2026-06-10 12:08:07 +10:00
|
|
|
|
using System.Diagnostics;
|
2025-12-23 18:27:31 +10:00
|
|
|
|
|
2025-12-26 21:47:53 +10:00
|
|
|
|
namespace PARR.TemplateMatcher.Services.Implementations;
|
2025-12-23 18:27:31 +10:00
|
|
|
|
|
|
|
|
|
|
internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
|
|
|
|
|
{
|
2025-12-26 21:47:53 +10:00
|
|
|
|
#if DEBUG
|
2026-07-03 14:01:56 +10:00
|
|
|
|
private readonly Guid _targetUnitId = Guid.Parse("358437ac-1eeb-4c00-840c-998326f657ac");
|
2025-12-26 21:47:53 +10:00
|
|
|
|
#endif
|
|
|
|
|
|
|
2026-07-03 14:01:56 +10:00
|
|
|
|
private readonly IEnumerable<ISimpleSyncStage> _readStages;
|
|
|
|
|
|
private readonly IEnumerable<ISimpleSyncWriteStage> _writeStages;
|
|
|
|
|
|
private readonly ILogger<SimpleTemplateSynchronizer> _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;
|
2026-09-10 14:12:47 +10:00
|
|
|
|
private readonly IUnusedTemplatesSyncService _unusedTemplatesSyncService;
|
|
|
|
|
|
|
2025-12-23 18:27:31 +10:00
|
|
|
|
|
|
|
|
|
|
public SimpleTemplateSynchronizer(
|
2026-06-11 15:09:46 +10:00
|
|
|
|
IEnumerable<ISimpleSyncStage> readStages,
|
|
|
|
|
|
IEnumerable<ISimpleSyncWriteStage> writeStages,
|
2025-12-23 18:27:31 +10:00
|
|
|
|
ILogger<SimpleTemplateSynchronizer> logger,
|
|
|
|
|
|
IUnitFilterService unitFilterService,
|
2026-04-30 10:35:31 +10:00
|
|
|
|
ITemplateRepository templateService,
|
|
|
|
|
|
IJobRepository jobService,
|
2025-12-26 21:47:53 +10:00
|
|
|
|
ITemplateNameNormalizer templateNameNormalizer,
|
2026-05-20 17:59:06 +10:00
|
|
|
|
ITemplateMqPublisher templateMqPublisher,
|
2026-01-17 09:32:34 +10:00
|
|
|
|
IMatchingStatusService matchingStatusService,
|
2026-01-22 12:06:57 +10:00
|
|
|
|
SettingsFromDb settingsFromDb,
|
2026-05-14 16:47:27 +10:00
|
|
|
|
IOptions<TemplateSettings> templateSettings,
|
2026-09-10 14:12:47 +10:00
|
|
|
|
IUnusedTemplatesSyncService unusedTemplatesSyncService
|
2025-12-26 21:47:53 +10:00
|
|
|
|
)
|
2025-12-23 18:27:31 +10:00
|
|
|
|
{
|
2026-09-10 14:12:47 +10:00
|
|
|
|
_readStages = readStages;
|
|
|
|
|
|
_writeStages = writeStages;
|
|
|
|
|
|
_logger = logger;
|
|
|
|
|
|
_unitFilterService = unitFilterService;
|
|
|
|
|
|
_templateService = templateService;
|
|
|
|
|
|
_jobService = jobService;
|
|
|
|
|
|
_templateNameNormalizer = templateNameNormalizer;
|
|
|
|
|
|
_templateMqPublisher = templateMqPublisher;
|
|
|
|
|
|
_matchingStatusService = matchingStatusService;
|
|
|
|
|
|
_settingsFromDb = settingsFromDb;
|
|
|
|
|
|
_unusedTemplatesSyncService = unusedTemplatesSyncService;
|
2025-12-23 18:27:31 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-10 14:12:47 +10:00
|
|
|
|
|
|
|
|
|
|
public async Task SyncTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator, bool dryRun = false, CancellationToken ct = default)
|
2025-12-23 18:27:31 +10:00
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
if (jobId == _settingsFromDb.JobIdForUnusedTemplates)
|
2026-01-17 09:32:34 +10:00
|
|
|
|
{
|
2026-09-10 14:12:47 +10:00
|
|
|
|
await _unusedTemplatesSyncService.SyncAsync(jobId, initiator, dryRun);
|
2026-01-17 09:32:34 +10:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogInformation("Начало синхронизации шаблонов для Job {JobId}", jobId);
|
2025-12-23 18:27:31 +10:00
|
|
|
|
|
2026-07-03 14:01:56 +10:00
|
|
|
|
var existingStatus = await _matchingStatusService.GetStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
|
|
|
|
|
if (existingStatus.DetailsJobs?.Count > 0)
|
2025-12-23 18:27:31 +10:00
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogWarning("Синхронизация для Job {JobId} уже запущена. Пропускаем.", jobId);
|
2025-12-23 18:27:31 +10:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-16 19:45:38 +10:00
|
|
|
|
var initialStatus = new MatchingStatusItemDto
|
2025-12-23 18:27:31 +10:00
|
|
|
|
{
|
2026-01-16 19:45:38 +10:00
|
|
|
|
DateStart = DateTimeOffset.UtcNow,
|
|
|
|
|
|
Action = TemplateMatcherActionEnum.Sync,
|
|
|
|
|
|
Comment = "Начало синхронизации"
|
|
|
|
|
|
};
|
2026-07-03 14:01:56 +10:00
|
|
|
|
await _matchingStatusService.SetMatchingStatusAsync(
|
2026-06-11 15:09:46 +10:00
|
|
|
|
jobId, SyncTaskEntityTypeEnum.Job,
|
2026-01-16 19:45:38 +10:00
|
|
|
|
new MatchingStatusItem { Data = initialStatus, Timestamp = DateTimeOffset.UtcNow, Source = nameof(SimpleTemplateSynchronizer) },
|
2026-06-11 15:09:46 +10:00
|
|
|
|
TimeSpan.FromMinutes(35));
|
|
|
|
|
|
|
|
|
|
|
|
// Таймер запускается ПОСЛЕ инфраструктурных операций (статус, проверка блокировки)
|
|
|
|
|
|
var totalSw = Stopwatch.StartNew();
|
2026-07-03 14:01:56 +10:00
|
|
|
|
var context = new SimpleSyncContext { JobId = jobId, Initiator = initiator };
|
2026-01-16 19:45:38 +10:00
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
foreach (var stage in _readStages)
|
2026-06-10 12:08:07 +10:00
|
|
|
|
{
|
2026-06-11 15:09:46 +10:00
|
|
|
|
var stageSw = Stopwatch.StartNew();
|
|
|
|
|
|
await stage.ExecuteAsync(context);
|
|
|
|
|
|
stageSw.Stop();
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogDebug("[Perf] Job '{JobName}' ({JobId}) | Этап: {Stage} | Время: {Ms} мс",
|
2026-06-11 15:09:46 +10:00
|
|
|
|
context.JobName, jobId, stage.StageName, stageSw.ElapsedMilliseconds);
|
2026-06-10 12:08:07 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-09 17:09:57 +10:00
|
|
|
|
// Подробный отчёт по результатам read-этапов
|
|
|
|
|
|
PrintDryRunReport(context);
|
|
|
|
|
|
|
|
|
|
|
|
if (!dryRun)
|
2026-01-14 10:59:17 +10:00
|
|
|
|
{
|
2026-09-09 17:09:57 +10:00
|
|
|
|
foreach (var stage in _writeStages)
|
|
|
|
|
|
{
|
|
|
|
|
|
var stageSw = Stopwatch.StartNew();
|
|
|
|
|
|
await stage.ExecuteAsync(context);
|
|
|
|
|
|
stageSw.Stop();
|
|
|
|
|
|
_logger.LogDebug("[Perf] Job '{JobName}' ({JobId}) | Этап: {Stage} | Время: {Ms} мс",
|
|
|
|
|
|
context.JobName, jobId, stage.StageName, stageSw.ElapsedMilliseconds);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-10 14:12:47 +10:00
|
|
|
|
await SetStatusAsync(jobId, "Синхронизация завершена успешно");
|
2026-09-09 17:09:57 +10:00
|
|
|
|
await _matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
|
|
|
|
|
_logger.LogInformation("Синхронизация шаблонов завершена для Job '{JobName}' ({JobId})", context.JobName, jobId);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogInformation("[DryRun] Write-этапы пропущены. Изменения в БД и MQ не выполнены.");
|
2026-09-10 14:12:47 +10:00
|
|
|
|
await SetStatusAsync(jobId, "DryRun: Анализ завершён без изменений");
|
2026-09-09 17:09:57 +10:00
|
|
|
|
await _matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
2026-01-16 19:45:38 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 12:08:07 +10:00
|
|
|
|
totalSw.Stop();
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogInformation("[Perf] Job '{JobName}' ({JobId}) | ИТОГО: {TotalMs} мс",
|
2026-06-11 15:09:46 +10:00
|
|
|
|
context.JobName, jobId, totalSw.ElapsedMilliseconds);
|
2026-01-16 19:45:38 +10:00
|
|
|
|
}
|
2026-09-10 14:12:47 +10:00
|
|
|
|
catch (SyncEarlyExitException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
totalSw.Stop();
|
|
|
|
|
|
_logger.LogInformation("Job {JobId}: {Reason} ({ElapsedMs} мс)",
|
|
|
|
|
|
jobId, ex.Reason, totalSw.ElapsedMilliseconds);
|
|
|
|
|
|
await SetStatusAsync(jobId, ex.Reason);
|
|
|
|
|
|
await _matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
|
|
|
|
|
}
|
2026-01-16 19:45:38 +10:00
|
|
|
|
catch (Exception ex)
|
2025-12-23 18:27:31 +10:00
|
|
|
|
{
|
2026-06-10 12:08:07 +10:00
|
|
|
|
totalSw.Stop();
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogError(ex, "Ошибка при синхронизации Job '{JobName}' ({JobId}) через {ElapsedMs} мс",
|
2026-09-09 17:09:57 +10:00
|
|
|
|
context.JobName ?? string.Empty, jobId, totalSw.ElapsedMilliseconds);
|
2026-09-10 14:12:47 +10:00
|
|
|
|
await SetStatusAsync(jobId, $"Ошибка: {ex.Message}");
|
2026-01-16 19:45:38 +10:00
|
|
|
|
throw;
|
2025-12-23 18:27:31 +10:00
|
|
|
|
}
|
2025-12-26 21:47:53 +10:00
|
|
|
|
}
|
2025-12-23 18:27:31 +10:00
|
|
|
|
|
2026-06-10 12:08:07 +10:00
|
|
|
|
|
2026-09-09 17:09:57 +10:00
|
|
|
|
private void PrintDryRunReport(SimpleSyncContext context)
|
2025-12-26 21:47:53 +10:00
|
|
|
|
{
|
2026-09-09 17:09:57 +10:00
|
|
|
|
_logger.LogInformation("========== [DryRun] ОТЧЁТ по Job '{JobName}' ({JobId}) ==========",
|
|
|
|
|
|
context.JobName, context.JobId);
|
|
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("[DryRun] Отфильтровано юнитов: {Count}", context.FilteredUnitIds.Count);
|
|
|
|
|
|
_logger.LogInformation("[DryRun] Существующих активных шаблонов: {Count}", context.ExistingUsedTemplates.Count);
|
|
|
|
|
|
_logger.LogInformation("[DryRun] Новых юнитов (требуют аллокации): {Count}", context.NewUnitIds.Count);
|
|
|
|
|
|
_logger.LogInformation("[DryRun] Шаблонов для деактивации (Unused): {Count}", context.UnusedTemplates.Count);
|
|
|
|
|
|
_logger.LogInformation("[DryRun] Шаблонов для переименования: {Count}", context.TemplatesToRename.Count);
|
|
|
|
|
|
|
|
|
|
|
|
if (context.NewUnitIds.Any())
|
|
|
|
|
|
{
|
2026-09-10 14:12:47 +10:00
|
|
|
|
_logger.LogDebug("[DryRun] --- Новые юниты (будут созданы шаблоны) ---");
|
2026-09-09 17:09:57 +10:00
|
|
|
|
foreach (var unitId in context.NewUnitIds)
|
|
|
|
|
|
{
|
2026-09-10 14:12:47 +10:00
|
|
|
|
_logger.LogDebug("[DryRun] + {Unit}", context.FormatUnit(unitId));
|
2026-09-09 17:09:57 +10:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (context.UnusedTemplates.Any())
|
|
|
|
|
|
{
|
2026-09-10 14:12:47 +10:00
|
|
|
|
_logger.LogDebug("[DryRun] --- Шаблоны для деактивации ---");
|
2026-09-09 17:09:57 +10:00
|
|
|
|
foreach (var template in context.UnusedTemplates)
|
|
|
|
|
|
{
|
2026-09-10 14:12:47 +10:00
|
|
|
|
_logger.LogDebug("[DryRun] - {TemplateId} (Unit: {Unit}, Name: '{Name}')",
|
2026-09-09 17:09:57 +10:00
|
|
|
|
template.Id, context.FormatUnit(template.UnitId), template.Name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (context.TemplatesToRename.Any())
|
|
|
|
|
|
{
|
2026-09-10 14:12:47 +10:00
|
|
|
|
_logger.LogDebug("[DryRun] --- Шаблоны для переименования ---");
|
2026-09-09 17:09:57 +10:00
|
|
|
|
foreach (var (template, expectedName) in context.TemplatesToRename)
|
|
|
|
|
|
{
|
2026-09-10 14:12:47 +10:00
|
|
|
|
_logger.LogDebug("[DryRun] ~ {TemplateId} (Unit: {Unit}): '{OldName}' -> '{NewName}'",
|
2026-09-09 17:09:57 +10:00
|
|
|
|
template.Id, context.FormatUnit(template.UnitId), template.Name, expectedName);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("========== [DryRun] КОНЕЦ ОТЧЁТА ==========");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-10 14:12:47 +10:00
|
|
|
|
public Task SyncTemplatesForJobGroupAsync(Guid jobGroupId, HistoryInitiator initiator, bool dryRun = false, CancellationToken ct = default)
|
2026-09-09 17:09:57 +10:00
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning(
|
|
|
|
|
|
"SimpleTemplateSynchronizer: SyncTemplatesForJobGroup вызван для JobGroup {JobGroupId} (DryRun={DryRun}). Это не поддерживаемая операция.",
|
|
|
|
|
|
jobGroupId, dryRun);
|
|
|
|
|
|
return Task.CompletedTask;
|
2025-12-23 18:27:31 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-11 15:09:46 +10:00
|
|
|
|
|
2026-09-10 14:12:47 +10:00
|
|
|
|
public async Task UpdateTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator, CancellationToken ct = default)
|
2025-12-23 18:27:31 +10:00
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogDebug("Обновление шаблонов для Job {JobId}", jobId);
|
2025-12-23 18:27:31 +10:00
|
|
|
|
|
2026-01-16 19:45:38 +10:00
|
|
|
|
// === Проверка: уже запущена? ===
|
2026-07-03 14:01:56 +10:00
|
|
|
|
var existingStatus = await _matchingStatusService.GetStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
|
|
|
|
|
if (existingStatus.DetailsJobs?.Count > 0)
|
2025-12-23 18:27:31 +10:00
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogWarning("Обновление для Job {JobId} уже запущено. Пропускаем.", jobId);
|
2025-12-26 21:47:53 +10:00
|
|
|
|
return;
|
2025-12-23 18:27:31 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-16 19:45:38 +10:00
|
|
|
|
var initialStatus = new MatchingStatusItemDto
|
2025-12-25 16:05:55 +10:00
|
|
|
|
{
|
2026-01-16 19:45:38 +10:00
|
|
|
|
DateStart = DateTimeOffset.UtcNow,
|
|
|
|
|
|
Action = TemplateMatcherActionEnum.Update,
|
|
|
|
|
|
Comment = "Начало обновления имён"
|
|
|
|
|
|
};
|
2026-07-03 14:01:56 +10:00
|
|
|
|
await _matchingStatusService.SetMatchingStatusAsync(
|
2026-01-16 19:45:38 +10:00
|
|
|
|
jobId,
|
|
|
|
|
|
SyncTaskEntityTypeEnum.Job,
|
|
|
|
|
|
new MatchingStatusItem { Data = initialStatus, Timestamp = DateTimeOffset.UtcNow, Source = nameof(SimpleTemplateSynchronizer) },
|
|
|
|
|
|
TimeSpan.FromMinutes(30)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
var job = await _jobService.Get()
|
2026-01-16 19:45:38 +10:00
|
|
|
|
.AsNoTracking()
|
|
|
|
|
|
.Include(j => j.AutoControl)
|
|
|
|
|
|
.Include(j => j.Tnk)
|
|
|
|
|
|
.Include(j => j.Group)
|
|
|
|
|
|
.ThenInclude(g => g!.GroupType)
|
|
|
|
|
|
.Include(j => j.UnitFilters)
|
|
|
|
|
|
.ThenInclude(uf => uf.RelationshipFilters)
|
|
|
|
|
|
.FirstOrDefaultAsync(j => j.Id == jobId);
|
|
|
|
|
|
|
|
|
|
|
|
if (job == null)
|
|
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogWarning("Job {JobId} не найден.", jobId);
|
2026-09-10 14:12:47 +10:00
|
|
|
|
await SetStatusAsync(jobId, "Job не найден");
|
2026-01-16 19:45:38 +10:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-21 18:25:09 +10:00
|
|
|
|
// === Получение отфильтрованных юнитов с полной информацией ===
|
2026-07-03 14:01:56 +10:00
|
|
|
|
var filteredUnits = await _unitFilterService.GetUnitsByJobFilterAsync(jobId);
|
2026-01-21 18:25:09 +10:00
|
|
|
|
if (filteredUnits == null || !filteredUnits.Any())
|
2026-01-16 19:45:38 +10:00
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogInformation("Для Job {JobId} фильтры не дали Unit'ов.", jobId);
|
2026-09-10 14:12:47 +10:00
|
|
|
|
await SetStatusAsync(jobId, "Нет Unit'ов — обновление не требуется");
|
2026-07-03 14:01:56 +10:00
|
|
|
|
await _matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
2026-01-16 19:45:38 +10:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-12-25 16:05:55 +10:00
|
|
|
|
|
2026-01-21 18:25:09 +10:00
|
|
|
|
// Извлекаем ID юнитов для последующих операций
|
|
|
|
|
|
var unitIds = filteredUnits.Select(u => u.Id).ToList();
|
|
|
|
|
|
|
2025-12-26 21:47:53 +10:00
|
|
|
|
#if DEBUG
|
2026-01-17 09:32:34 +10:00
|
|
|
|
// Отладка: проверить, есть ли юнит в unitIds
|
2026-07-03 14:01:56 +10:00
|
|
|
|
if (unitIds.Contains(_targetUnitId))
|
2026-01-16 19:45:38 +10:00
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogDebug("Юнит {TargetUnitId} найден в unitIds.", _targetUnitId);
|
2026-01-16 19:45:38 +10:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogDebug("Юнит {TargetUnitId} НЕ найден в unitIds.", _targetUnitId);
|
2026-01-16 19:45:38 +10:00
|
|
|
|
}
|
2025-12-26 21:47:53 +10:00
|
|
|
|
#endif
|
2025-12-25 21:16:20 +10:00
|
|
|
|
|
2026-07-03 14:01:56 +10:00
|
|
|
|
var existingTemplates = await _templateService.Get()
|
2026-01-16 19:45:38 +10:00
|
|
|
|
.AsNoTracking()
|
|
|
|
|
|
.Include(t => t.Unit)
|
|
|
|
|
|
.Include(t => t.UnitsInTemplate)
|
|
|
|
|
|
.Include(t => t.Job)
|
|
|
|
|
|
.ThenInclude(t => t!.Group)
|
|
|
|
|
|
.ThenInclude(t => t!.GroupType)
|
|
|
|
|
|
.Include(t => t.Job)
|
|
|
|
|
|
.ThenInclude(t => t!.Tnk)
|
|
|
|
|
|
.Where(t => t.JobId == jobId && t.StatusTypeId == TemplateStatusTypeEnum.Used)
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var template in existingTemplates)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (unitIds.Contains(template.UnitId))
|
|
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
var expectedName = await _templateNameNormalizer.GetNormalizedTemplateNameAsync(template);
|
2026-01-16 19:45:38 +10:00
|
|
|
|
if (!string.Equals(template.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
2025-12-26 21:47:53 +10:00
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogDebug("Шаблон {TemplateId} требует обновления имени: старое = '{OldName}', новое = '{NewName}'", template.Id, template.Name, expectedName);
|
2026-01-16 19:45:38 +10:00
|
|
|
|
|
2026-05-14 16:47:27 +10:00
|
|
|
|
var updateRequest = new TemplateUpdaterMessage
|
2026-01-16 19:45:38 +10:00
|
|
|
|
{
|
|
|
|
|
|
TemplateId = template.Id,
|
|
|
|
|
|
JobId = jobId,
|
|
|
|
|
|
UnitId = template.UnitId,
|
|
|
|
|
|
Name = expectedName,
|
|
|
|
|
|
IsActiveTemplate = template.IsActiveTemplate,
|
|
|
|
|
|
IsActiveSchedule = template.IsActiveSchedule,
|
2026-02-26 14:42:53 +10:00
|
|
|
|
IsNew = false,
|
2026-01-16 19:45:38 +10:00
|
|
|
|
Index = template.Index,
|
|
|
|
|
|
StatusTypeId = TemplateStatusTypeEnum.Used,
|
|
|
|
|
|
Initiator = initiator,
|
2026-05-14 16:47:27 +10:00
|
|
|
|
UnitsInTemplate = new List<UnitInTemplateMessage>() // для простого шаблона
|
2026-01-16 19:45:38 +10:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-03 14:01:56 +10:00
|
|
|
|
await _templateMqPublisher.PublishUpdateAsync(updateRequest);
|
2026-01-16 19:45:38 +10:00
|
|
|
|
}
|
2025-12-26 21:47:53 +10:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-23 18:27:31 +10:00
|
|
|
|
|
2026-09-10 14:12:47 +10:00
|
|
|
|
await SetStatusAsync(jobId, "Обновление завершено");
|
2026-07-03 14:01:56 +10:00
|
|
|
|
await _matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
|
|
|
|
|
_logger.LogInformation("Обновление шаблонов завершено для Job {JobId}.", jobId);
|
2026-01-16 19:45:38 +10:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-07-03 14:01:56 +10:00
|
|
|
|
_logger.LogError(ex, "Ошибка при обновлении Job {JobId}", jobId);
|
2026-09-10 14:12:47 +10:00
|
|
|
|
await SetStatusAsync(jobId, $"Ошибка: {ex.Message}");
|
2026-01-17 09:32:34 +10:00
|
|
|
|
throw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-18 12:29:55 +10:00
|
|
|
|
|
2026-09-10 14:12:47 +10:00
|
|
|
|
private async Task SetStatusAsync(Guid jobId, string comment)
|
2026-01-16 19:45:38 +10:00
|
|
|
|
{
|
|
|
|
|
|
var status = new MatchingStatusItemDto
|
|
|
|
|
|
{
|
|
|
|
|
|
DateStart = DateTimeOffset.UtcNow,
|
|
|
|
|
|
Action = TemplateMatcherActionEnum.Sync,
|
|
|
|
|
|
Comment = comment
|
|
|
|
|
|
};
|
2026-07-03 14:01:56 +10:00
|
|
|
|
await _matchingStatusService.SetMatchingStatusAsync(
|
2026-01-16 19:45:38 +10:00
|
|
|
|
jobId,
|
|
|
|
|
|
SyncTaskEntityTypeEnum.Job,
|
|
|
|
|
|
new MatchingStatusItem { Data = status, Timestamp = DateTimeOffset.UtcNow, Source = nameof(SimpleTemplateSynchronizer) },
|
|
|
|
|
|
TimeSpan.FromMinutes(30)
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-12-23 18:27:31 +10:00
|
|
|
|
}
|