2025-12-23 18:27:31 +10:00
using Microsoft.EntityFrameworkCore ;
using Microsoft.Extensions.Logging ;
using PARR.BLL.Domain.Mq ;
using PARR.BLL.Services.Interfaces ;
using PARR.Common.Domain ;
using PARR.Constants ;
2026-01-16 19:45:38 +10:00
using PARR.DAL.Cache.Models ;
2026-01-17 09:32:34 +10:00
using PARR.DAL.Contracts ;
2025-12-23 18:27:31 +10:00
using PARR.DAL.DomainServices.Interfaces ;
using PARR.DAL.DomainServices.Shortcodes ;
2026-01-21 18:25:09 +10:00
using PARR.DAL.DomainServices.UnitFilterService ;
2026-01-16 18:26:06 +10:00
using PARR.DAL.Models ;
2025-12-23 18:27:31 +10:00
using PARR.DAL.Models.Job ;
2026-01-22 12:06:57 +10:00
using PARR.DAL.NextRunServices ;
2025-12-23 18:27:31 +10:00
using PARR.DAL.Services.Interfaces ;
using PARR.DAL.Services.Interfaces.Job ;
2025-12-26 21:47:53 +10:00
using PARR.DAL.Services.Interfaces.Unit ;
2025-12-23 18:27:31 +10:00
using PARR.DAL.TransformServices ;
using PARR.TemplateMatcher.Services.Interfaces ;
using PARR.TemplateMatcher.Settings ;
using System.Text.Json ;
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
private readonly Guid targetUnitId = Guid . Parse ( "358437ac-1eeb-4c00-840c-998326f657ac" ) ;
#endif
2025-12-23 18:27:31 +10:00
private const bool DefaultUsedTemplateState = false ;
private const bool DefaultUsedScheduleState = false ;
2026-01-17 09:32:34 +10:00
// Маска для неиспользуемых шаблонов — хардкод, недоступна из GUI
2026-01-20 14:23:02 +10:00
private const string UnusedTemplateNameMask = "%П-1%_Н Е А К Т У А ЛЬ Н ЫЕ _%ЭК%_%ТИКТАК%" ;
2026-01-17 09:32:34 +10:00
2025-12-23 18:27:31 +10:00
private readonly ILogger < SimpleTemplateSynchronizer > logger ;
private readonly IUnitFilterService unitFilterService ;
private readonly MqSettings mqSettings ;
private readonly IMqService mqService ;
private readonly ITemplateService templateService ;
private readonly IJobService jobService ;
private readonly ITemplateReuser templateReuser ;
2026-01-22 12:06:57 +10:00
//private readonly IEsppScheduleTransformService esppScheduleTransformService;
2025-12-26 21:47:53 +10:00
private readonly ITemplateDeactivator templateDeactivator ;
private readonly ITemplateNameNormalizer templateNameNormalizer ;
private readonly ITemplateUpdaterMqSender templateUpdaterMqSender ;
2026-01-16 19:45:38 +10:00
private readonly IMatchingStatusService matchingStatusService ;
2026-01-17 09:32:34 +10:00
private readonly SettingsFromDb settingsFromDb ;
2026-01-22 12:06:57 +10:00
private readonly INextRunService nextRunService ;
2025-12-23 18:27:31 +10:00
public SimpleTemplateSynchronizer (
ILogger < SimpleTemplateSynchronizer > logger ,
IUnitFilterService unitFilterService ,
2025-12-26 21:47:53 +10:00
IUnitInUnitService unitInUnitService ,
IUnitInValueService unitInValueService ,
IUnitService unitService ,
2025-12-23 18:27:31 +10:00
MqSettings mqSettings ,
IMqService mqService ,
ITemplateService templateService ,
IJobService jobService ,
ITemplateReuser templateReuser ,
IShortcodesService shortcodesService ,
2026-01-22 12:06:57 +10:00
//IEsppScheduleTransformService esppScheduleTransformService,
2025-12-26 21:47:53 +10:00
IUnitRegionalEkPtkGroupService regionalEkPtkGroupService ,
IUnitFieldService unitFieldService ,
ITemplateDeactivator templateDeactivator ,
ITemplateNameNormalizer templateNameNormalizer ,
2026-01-16 19:45:38 +10:00
ITemplateUpdaterMqSender templateUpdaterMqSender ,
2026-01-17 09:32:34 +10:00
IMatchingStatusService matchingStatusService ,
2026-01-22 12:06:57 +10:00
SettingsFromDb settingsFromDb ,
INextRunService nextRunService
2025-12-26 21:47:53 +10:00
)
2025-12-23 18:27:31 +10:00
{
this . logger = logger ;
this . unitFilterService = unitFilterService ;
this . mqSettings = mqSettings ;
this . mqService = mqService ;
this . templateService = templateService ;
this . jobService = jobService ;
this . templateReuser = templateReuser ;
2026-01-22 12:06:57 +10:00
//this.esppScheduleTransformService = esppScheduleTransformService;
2025-12-26 21:47:53 +10:00
this . templateDeactivator = templateDeactivator ;
this . templateNameNormalizer = templateNameNormalizer ;
this . templateUpdaterMqSender = templateUpdaterMqSender ;
2026-01-16 19:45:38 +10:00
this . matchingStatusService = matchingStatusService ;
2026-01-17 09:32:34 +10:00
this . settingsFromDb = settingsFromDb ;
2026-01-22 12:06:57 +10:00
this . nextRunService = nextRunService ;
2025-12-23 18:27:31 +10:00
}
public async Task SyncTemplatesForJobAsync ( Guid jobId , HistoryInitiator initiator )
{
2026-01-17 09:32:34 +10:00
// === Специальная обработка для Job неиспользуемых шаблонов ===
if ( jobId = = settingsFromDb . JobIdForUnusedTemplates )
{
logger . LogInformation ( "Обработка синхронизации для Job неиспользуемых шаблонов {JobId}" , jobId ) ;
await SyncUnusedTemplatesAsync ( jobId , initiator ) ;
return ;
}
// === Обычная логика для всех остальных Job ===
2025-12-26 21:47:53 +10:00
logger . LogDebug ( "Начало синхронизации шаблонов для Job {JobId}" , jobId ) ;
2025-12-23 18:27:31 +10:00
2026-01-16 19:45:38 +10:00
// === Проверка: уже запущена? ===
var existingStatus = await matchingStatusService . GetStatusAsync ( jobId , SyncTaskEntityTypeEnum . Job ) ;
if ( existingStatus . DetailsJobs ? . Any ( ) = = true )
2025-12-23 18:27:31 +10:00
{
2026-01-16 19:45:38 +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 = "Начало синхронизации"
} ;
await matchingStatusService . SetMatchingStatusAsync (
jobId ,
SyncTaskEntityTypeEnum . Job ,
new MatchingStatusItem { Data = initialStatus , Timestamp = DateTimeOffset . UtcNow , Source = nameof ( SimpleTemplateSynchronizer ) } ,
TimeSpan . FromMinutes ( 35 )
) ;
try
{
var job = await jobService . Get ( )
2025-12-30 14:27:59 +10:00
. AsNoTracking ( )
2026-01-16 19:45:38 +10:00
. 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 )
2025-12-30 14:27:59 +10:00
{
2026-01-16 19:45:38 +10:00
logger . LogWarning ( "Job {JobId} не найден." , jobId ) ;
await UpdateMatchingStatusAsync ( jobId , "Job не найден" ) ;
return ;
2025-12-30 14:27:59 +10:00
}
2026-01-17 09:32:34 +10:00
2026-01-21 18:25:09 +10:00
// === Получение отфильтрованных юнитов с полной информацией ===
var filteredUnits = await unitFilterService . GetUnitsByJobFilterAsync ( jobId ) ;
if ( filteredUnits = = null | | ! filteredUnits . Any ( ) )
2026-01-16 19:45:38 +10:00
{
2026-01-17 09:32:34 +10:00
logger . LogInformation ( "Для Job {JobId} фильтры не дали Unit'ов." , jobId ) ;
2025-12-23 18:27:31 +10:00
2026-01-17 09:32:34 +10:00
var existingTemplatesForDeactivation = await templateService . Get ( )
. AsNoTracking ( )
. Include ( t = > t . UnitsInTemplate )
. Where ( t = > t . JobId = = jobId & & t . StatusTypeId = = TemplateStatusTypeEnum . Used )
. ToListAsync ( ) ;
2025-12-23 18:27:31 +10:00
2026-01-17 09:32:34 +10:00
await UpdateMatchingStatusAsync ( jobId , $"Нет Unit'ов. Деактивация {existingTemplatesForDeactivation.Count} шаблонов..." ) ;
2026-01-16 19:45:38 +10:00
2026-01-17 09:32:34 +10:00
foreach ( var unusedTemplate in existingTemplatesForDeactivation )
{
logger . LogInformation ( "Деактивация лишнего шаблона {TemplateId} (Job {JobId}, UnitId {UnitId})." , unusedTemplate . Id , jobId , unusedTemplate . UnitId ) ;
await templateDeactivator . DeactivateTemplateAsync ( unusedTemplate , initiator ) ;
2026-01-16 19:45:38 +10:00
}
2026-01-17 09:32:34 +10:00
await UpdateMatchingStatusAsync ( jobId , "Синхронизация завершена: нет Unit'ов" ) ;
await matchingStatusService . DeleteMatchingStatusAsync ( jobId , SyncTaskEntityTypeEnum . Job ) ;
logger . LogInformation ( "Синхронизация шаблонов завершена для Job {JobId}." , jobId ) ;
return ;
2026-01-16 19:45:38 +10:00
}
2026-01-17 09:32:34 +10:00
2026-01-21 18:25:09 +10:00
// Извлекаем ID юнитов для последующих операций
var unitIds = filteredUnits . Select ( u = > u . Id ) . ToList ( ) ;
2026-01-16 19:45:38 +10:00
#if DEBUG
2026-01-17 09:32:34 +10:00
// Отладка: проверить, есть ли юнит в unitIds
2026-01-16 19:45:38 +10:00
if ( unitIds . Contains ( targetUnitId ) )
2025-12-23 18:27:31 +10:00
{
2026-01-16 19:45:38 +10:00
logger . LogDebug ( "Юнит {TargetUnitId} найден в unitIds." , targetUnitId ) ;
2025-12-23 18:27:31 +10:00
}
else
{
2026-01-16 19:45:38 +10:00
logger . LogDebug ( "Юнит {TargetUnitId} Н Е найден в unitIds." , targetUnitId ) ;
2025-12-23 18:27:31 +10:00
}
2026-01-16 19:45:38 +10:00
#endif
2025-12-23 18:27:31 +10:00
2026-01-16 19:45:38 +10:00
var existingTemplates = await templateService . Get ( )
. Include ( t = > t . UnitsInTemplate )
. Include ( t = > t . Job )
. ThenInclude ( t = > t ! . Group )
. ThenInclude ( t = > t . GroupType )
. Include ( t = > t . Job )
. ThenInclude ( t = > t ! . Tnk )
. Include ( t = > t . Unit )
. Where ( t = > t . JobId = = jobId )
. ToListAsync ( ) ;
var existingUsedTemplates = existingTemplates
. Where ( t = > t . StatusTypeId = = TemplateStatusTypeEnum . Used )
. ToList ( ) ;
var existingUnitIds = existingUsedTemplates
. Select ( t = > t . UnitId )
. ToHashSet ( ) ;
var newUnitIds = unitIds . Except ( existingUnitIds ) . ToList ( ) ;
var unusedTemplates = existingUsedTemplates
. Where ( t = > ! unitIds . Contains ( t . UnitId ) )
. ToList ( ) ;
foreach ( var unitId in newUnitIds )
2026-01-14 10:59:17 +10:00
{
2026-01-16 19:45:38 +10:00
var reusableTemplate = await templateReuser . TryReuseOneUnusedTemplateAsync ( jobId , unitId , initiator ) ;
if ( reusableTemplate ! = null )
2026-01-14 10:59:17 +10:00
{
2026-01-16 19:45:38 +10:00
logger . LogInformation ( "Переиспользован шаблон {TemplateId} для Job {JobId}, UnitId {UnitId}." , reusableTemplate . Id , jobId , unitId ) ;
2026-01-14 10:59:17 +10:00
2026-01-17 09:32:34 +10:00
// === Создаём временный Template для нормализации имени ===
2026-01-16 19:45:38 +10:00
var tempTemplateForName = new Template
{
Id = reusableTemplate . Id ,
Name = reusableTemplate . Name ,
JobId = jobId ,
UnitId = unitId ,
Index = reusableTemplate . Index ,
2026-01-17 09:32:34 +10:00
Job = job , // загруженный job
Unit = reusableTemplate . Unit , // может быть null — нормально
UnitsInTemplate = new List < UnitsInTemplate > ( ) // для простого шаблона
2026-01-16 19:45:38 +10:00
} ;
var expectedName = await templateNameNormalizer . GetNormalizedTemplateNameAsync ( tempTemplateForName ) ;
2026-01-22 12:06:57 +10:00
//var nextRun = await GetNextRunAsync(job);
var nextRun = await nextRunService . GetNextRunForTemplateAsync ( reusableTemplate . Id , true ) ;
2026-01-14 10:59:17 +10:00
var updateRequest = new TemplateUpdaterMq
{
2026-01-16 19:45:38 +10:00
TemplateId = reusableTemplate . Id ,
2026-01-14 10:59:17 +10:00
JobId = jobId ,
2026-01-16 19:45:38 +10:00
UnitId = unitId ,
2026-01-14 10:59:17 +10:00
Name = expectedName ,
2026-01-16 19:45:38 +10:00
IsActiveTemplate = job . AutoControl ? . InitUsedTemplateState ? ? DefaultUsedTemplateState ,
IsActiveSchedule = job . AutoControl ? . InitUsedScheduleState ? ? DefaultUsedScheduleState ,
2026-01-14 10:59:17 +10:00
StatusTypeId = TemplateStatusTypeEnum . Used ,
Initiator = initiator ,
2026-01-16 19:45:38 +10:00
NextRun = nextRun ,
2026-01-17 09:32:34 +10:00
UnitsInTemplate = new List < Guid > ( ) // для простого шаблона
2026-01-14 10:59:17 +10:00
} ;
await templateUpdaterMqSender . SendTemplateUpdateMessageAsync ( updateRequest ) ;
}
2026-01-16 19:45:38 +10:00
else
{
logger . LogDebug ( "Создание нового шаблона для Job {JobId}, UnitId {UnitId}." , jobId , unitId ) ;
await CreateSimpleTemplateAsync ( jobId , unitId , initiator ) ;
}
}
// === Обработка существующих шаблонов (проверка имени) ===
foreach ( var template in existingUsedTemplates )
{
if ( unitIds . Contains ( template . UnitId ) )
{
var expectedName = await templateNameNormalizer . GetNormalizedTemplateNameAsync ( template ) ;
if ( ! string . Equals ( template . Name , expectedName , StringComparison . OrdinalIgnoreCase ) )
{
2026-01-17 09:32:34 +10:00
logger . LogDebug ( "Шаблон {TemplateId} требует обновления имени: старое = '{OldName}', новое = '{NewName}'" , template . Id , template . Name , expectedName ) ;
2026-01-16 19:45:38 +10:00
2026-01-22 12:06:57 +10:00
//var nextRun = await GetNextRunAsync(job, template.NextRun);
var nextRun = await nextRunService . GetNextRunForTemplateAsync ( template . Id , false ) ;
2026-01-16 19:45:38 +10:00
var updateRequest = new TemplateUpdaterMq
{
TemplateId = template . Id ,
JobId = jobId ,
UnitId = template . UnitId ,
Name = expectedName ,
IsActiveTemplate = template . IsActiveTemplate ,
IsActiveSchedule = template . IsActiveSchedule ,
LastRun = template . LastRun ,
NextRun = nextRun ,
Index = template . Index ,
StatusTypeId = TemplateStatusTypeEnum . Used ,
Initiator = initiator ,
2026-01-17 09:32:34 +10:00
UnitsInTemplate = new List < Guid > ( ) // для простого шаблона
2026-01-16 19:45:38 +10:00
} ;
await templateUpdaterMqSender . SendTemplateUpdateMessageAsync ( updateRequest ) ;
}
}
}
// === Деактивация лишних шаблонов ===
foreach ( var unusedTemplate in unusedTemplates )
{
logger . LogInformation ( "Деактивация лишнего шаблона {TemplateId} (Job {JobId}, UnitId {UnitId})." , unusedTemplate . Id , jobId , unusedTemplate . UnitId ) ;
await templateDeactivator . DeactivateTemplateAsync ( unusedTemplate , initiator ) ;
2026-01-14 10:59:17 +10:00
}
2026-01-16 19:45:38 +10:00
// === Успешное завершение ===
await UpdateMatchingStatusAsync ( jobId , "Синхронизация завершена успешно" ) ;
await matchingStatusService . DeleteMatchingStatusAsync ( jobId , SyncTaskEntityTypeEnum . Job ) ;
logger . LogInformation ( "Синхронизация шаблонов завершена для Job {JobId}." , jobId ) ;
}
catch ( Exception ex )
2025-12-23 18:27:31 +10:00
{
2026-01-16 19:45:38 +10:00
logger . LogError ( ex , "Ошибка при синхронизации Job {JobId}" , jobId ) ;
await UpdateMatchingStatusAsync ( jobId , $"Ошибка: {ex.Message}" ) ;
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
2025-12-26 21:47:53 +10:00
public async Task SyncTemplatesForJobGroupAsync ( Guid jobGroupId , HistoryInitiator initiator )
{
logger . LogWarning ( "SimpleTemplateSynchronizer: SyncTemplatesForJobGroup вызван для JobGroup {JobGroupId}. Это не поддерживаемая операция." , jobGroupId ) ;
2025-12-23 18:27:31 +10:00
}
public async Task UpdateTemplatesForJobAsync ( Guid jobId , HistoryInitiator initiator )
{
2025-12-26 21:47:53 +10:00
logger . LogDebug ( "Обновление шаблонов для Job {JobId}" , jobId ) ;
2025-12-23 18:27:31 +10:00
2026-01-16 19:45:38 +10:00
// === Проверка: уже запущена? ===
var existingStatus = await matchingStatusService . GetStatusAsync ( jobId , SyncTaskEntityTypeEnum . Job ) ;
if ( existingStatus . DetailsJobs ? . Any ( ) = = true )
2025-12-23 18:27:31 +10:00
{
2026-01-16 19:45:38 +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 = "Начало обновления имён"
} ;
await matchingStatusService . SetMatchingStatusAsync (
jobId ,
SyncTaskEntityTypeEnum . Job ,
new MatchingStatusItem { Data = initialStatus , Timestamp = DateTimeOffset . UtcNow , Source = nameof ( SimpleTemplateSynchronizer ) } ,
TimeSpan . FromMinutes ( 30 )
) ;
try
{
var job = await jobService . Get ( )
. 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 )
{
logger . LogWarning ( "Job {JobId} не найден." , jobId ) ;
await UpdateMatchingStatusAsync ( jobId , "Job не найден" ) ;
return ;
}
2026-01-21 18:25:09 +10:00
// === Получение отфильтрованных юнитов с полной информацией ===
var filteredUnits = await unitFilterService . GetUnitsByJobFilterAsync ( jobId ) ;
if ( filteredUnits = = null | | ! filteredUnits . Any ( ) )
2026-01-16 19:45:38 +10:00
{
logger . LogInformation ( "Для Job {JobId} фильтры не дали Unit'ов." , jobId ) ;
await UpdateMatchingStatusAsync ( jobId , "Нет Unit'ов — обновление не требуется" ) ;
await matchingStatusService . DeleteMatchingStatusAsync ( jobId , SyncTaskEntityTypeEnum . Job ) ;
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-01-16 19:45:38 +10:00
if ( unitIds . Contains ( targetUnitId ) )
{
logger . LogDebug ( "Юнит {TargetUnitId} найден в unitIds." , targetUnitId ) ;
}
else
{
logger . LogDebug ( "Юнит {TargetUnitId} Н Е найден в unitIds." , targetUnitId ) ;
}
2025-12-26 21:47:53 +10:00
#endif
2025-12-25 21:16:20 +10:00
2026-01-16 19:45:38 +10:00
var existingTemplates = await templateService . Get ( )
. 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 ) )
{
var expectedName = await templateNameNormalizer . GetNormalizedTemplateNameAsync ( template ) ;
if ( ! string . Equals ( template . Name , expectedName , StringComparison . OrdinalIgnoreCase ) )
2025-12-26 21:47:53 +10:00
{
2026-01-17 09:32:34 +10:00
logger . LogDebug ( "Шаблон {TemplateId} требует обновления имени: старое = '{OldName}', новое = '{NewName}'" , template . Id , template . Name , expectedName ) ;
2026-01-16 19:45:38 +10:00
2026-01-22 12:06:57 +10:00
//var nextRun = await GetNextRunAsync(job, template.NextRun);
var nextRun = await nextRunService . GetNextRunForTemplateAsync ( template . Id , false ) ;
2026-01-16 19:45:38 +10:00
var updateRequest = new TemplateUpdaterMq
{
TemplateId = template . Id ,
JobId = jobId ,
UnitId = template . UnitId ,
Name = expectedName ,
IsActiveTemplate = template . IsActiveTemplate ,
IsActiveSchedule = template . IsActiveSchedule ,
LastRun = template . LastRun ,
NextRun = nextRun ,
Index = template . Index ,
StatusTypeId = TemplateStatusTypeEnum . Used ,
Initiator = initiator ,
2026-01-17 09:32:34 +10:00
UnitsInTemplate = new List < Guid > ( ) // для простого шаблона
2026-01-16 19:45:38 +10:00
} ;
await templateUpdaterMqSender . SendTemplateUpdateMessageAsync ( updateRequest ) ;
}
2025-12-26 21:47:53 +10:00
}
}
2025-12-23 18:27:31 +10:00
2026-01-16 19:45:38 +10:00
await UpdateMatchingStatusAsync ( jobId , "Обновление завершено" ) ;
await matchingStatusService . DeleteMatchingStatusAsync ( jobId , SyncTaskEntityTypeEnum . Job ) ;
logger . LogInformation ( "Обновление шаблонов завершено для Job {JobId}." , jobId ) ;
}
catch ( Exception ex )
{
logger . LogError ( ex , "Ошибка при обновлении Job {JobId}" , jobId ) ;
await UpdateMatchingStatusAsync ( jobId , $"Ошибка: {ex.Message}" ) ;
throw ;
}
2025-12-23 18:27:31 +10:00
}
2026-01-17 09:32:34 +10:00
private async Task SyncUnusedTemplatesAsync ( Guid unusedJobId , HistoryInitiator initiator )
{
// === Проверка: уже запущена? ===
var existingStatus = await matchingStatusService . GetStatusAsync ( unusedJobId , SyncTaskEntityTypeEnum . Job ) ;
if ( existingStatus . DetailsJobs ? . Any ( ) = = true )
{
logger . LogWarning ( "Синхронизация для Job неиспользуемых шаблонов {JobId} уже запущена. Пропускаем." , unusedJobId ) ;
return ;
}
// === Устанавливаем статус "в процессе" ===
var initialStatus = new MatchingStatusItemDto
{
DateStart = DateTimeOffset . UtcNow ,
Action = TemplateMatcherActionEnum . Sync ,
Comment = "Синхронизация неиспользуемых шаблонов"
} ;
await matchingStatusService . SetMatchingStatusAsync (
unusedJobId ,
SyncTaskEntityTypeEnum . Job ,
new MatchingStatusItem { Data = initialStatus , Timestamp = DateTimeOffset . UtcNow , Source = nameof ( SimpleTemplateSynchronizer ) } ,
TimeSpan . FromMinutes ( 30 )
) ;
try
{
// Загружаем Job неиспользуемых шаблонов с Group
var unusedJob = await jobService . Get ( )
. AsNoTracking ( )
. Include ( j = > j . Group )
. FirstOrDefaultAsync ( j = > j . Id = = unusedJobId ) ;
if ( unusedJob = = null )
{
logger . LogError ( "Job неиспользуемых шаблонов {JobId} не найден." , unusedJobId ) ;
await UpdateMatchingStatusAsync ( unusedJobId , "Job не найден" ) ;
return ;
}
// Находим все шаблоны с о статусом Unused
var unusedTemplates = await templateService . Get ( )
. Include ( t = > t . Unit )
. Where ( t = > t . StatusTypeId = = TemplateStatusTypeEnum . Unused )
. ToListAsync ( ) ;
if ( ! unusedTemplates . Any ( ) )
{
logger . LogInformation ( "Н е найдено шаблонов с о статусом Unused." ) ;
await UpdateMatchingStatusAsync ( unusedJobId , "Нет шаблонов для обработки" ) ;
await matchingStatusService . DeleteMatchingStatusAsync ( unusedJobId , SyncTaskEntityTypeEnum . Job ) ;
return ;
}
await UpdateMatchingStatusAsync ( unusedJobId , $"Найдено {unusedTemplates.Count} шаблонов для обработки" ) ;
int processed = 0 ;
foreach ( var template in unusedTemplates )
{
// Генерируем ожидаемое имя один раз
var expectedName = await GenerateUnusedTemplateNameAsync ( template , unusedJob ) ;
// Проверяем, нужно ли обновление
bool needsUpdate = template . JobId ! = unusedJobId | |
! string . Equals ( template . Name , expectedName , StringComparison . OrdinalIgnoreCase ) ;
if ( ! needsUpdate )
{
logger . LogDebug ( "Шаблон {TemplateId} уже имеет актуальное имя и JobId. Пропускаем." , template . Id ) ;
continue ;
}
2026-01-22 12:06:57 +10:00
//var nextRun = await GetNextRunAsync(unusedJob);
var nextRun = await nextRunService . GetNextRunForTemplateAsync ( template . Id , false ) ;
2026-01-17 09:32:34 +10:00
var updateRequest = new TemplateUpdaterMq
{
TemplateId = template . Id ,
JobId = unusedJobId ,
UnitId = template . UnitId ,
Name = expectedName ,
IsActiveTemplate = false ,
IsActiveSchedule = false ,
LastRun = template . LastRun ,
NextRun = nextRun ,
Index = null ,
StatusTypeId = TemplateStatusTypeEnum . Unused ,
Initiator = initiator ,
UnitsInTemplate = new List < Guid > ( )
} ;
await templateUpdaterMqSender . SendTemplateUpdateMessageAsync ( updateRequest ) ;
processed + + ;
await UpdateMatchingStatusAsync ( unusedJobId , $"Обработано: {processed}/{unusedTemplates.Count}" ) ;
}
await UpdateMatchingStatusAsync ( unusedJobId , "Синхронизация неиспользуемых шаблонов завершена" ) ;
await matchingStatusService . DeleteMatchingStatusAsync ( unusedJobId , SyncTaskEntityTypeEnum . Job ) ;
logger . LogInformation ( "Синхронизация неиспользуемых шаблонов завершена. Обработано {Count} шаблонов." , unusedTemplates . Count ) ;
}
catch ( Exception ex )
{
logger . LogError ( ex , "Ошибка при синхронизации неиспользуемых шаблонов для Job {JobId}" , unusedJobId ) ;
await UpdateMatchingStatusAsync ( unusedJobId , $"Ошибка: {ex.Message}" ) ;
throw ;
}
}
private async Task < string > GenerateUnusedTemplateNameAsync ( Template template , Job unusedJob )
{
var tempJob = new Job
{
Id = unusedJob . Id ,
Name = unusedJob . Name ,
WorkName = unusedJob . WorkName ,
MinValueRelationships = unusedJob . MinValueRelationships ,
MaxValueRelationships = unusedJob . MaxValueRelationships ,
IsParentRelationships = unusedJob . IsParentRelationships ,
TemplateNameMask = UnusedTemplateNameMask ,
WorkGroupMask = unusedJob . WorkGroupMask ,
ResponseAreaMask = unusedJob . ResponseAreaMask ,
TnkId = unusedJob . TnkId ,
GroupId = unusedJob . GroupId ,
Group = unusedJob . Group ,
Tnk = unusedJob . Tnk ,
UnitFilters = unusedJob . UnitFilters ,
Templates = unusedJob . Templates ,
AutoControl = unusedJob . AutoControl
} ;
var tempTemplateForName = new Template
{
Id = template . Id ,
Name = template . Name ,
JobId = unusedJob . Id ,
UnitId = template . UnitId ,
Index = null ,
Job = tempJob ,
Unit = template . Unit ,
UnitsInTemplate = new List < UnitsInTemplate > ( )
} ;
return await templateNameNormalizer . GetNormalizedTemplateNameAsync ( tempTemplateForName ) ;
}
2025-12-26 21:47:53 +10:00
private async Task CreateSimpleTemplateAsync ( Guid jobId , Guid unitId , HistoryInitiator initiator )
2025-12-23 18:27:31 +10:00
{
2025-12-26 21:47:53 +10:00
logger . LogInformation ( "Создание нового простого шаблона для Job {JobId}, UnitId {UnitId}." , jobId , unitId ) ;
2025-12-23 18:27:31 +10:00
var mqRequest = new TemplateGeneratorMq
{
JobId = jobId ,
UnitId = unitId ,
2026-01-17 09:32:34 +10:00
UnitsInTemplate = new List < Guid > ( ) , // для простого шаблона
2025-12-26 21:47:53 +10:00
HistoryInitiator = initiator
2025-12-23 18:27:31 +10:00
} ;
var msg = JsonSerializer . Serialize ( mqRequest ) ;
var result = await mqService . SendAsync ( mqSettings . TemplateGenerator , new [ ] { msg } ) ;
if ( ! result . IsSuccess )
2025-12-26 21:47:53 +10:00
logger . LogError ( "Ошибка отправки команды создания простого шаблона для Job {JobId}, UnitId {UnitId}." , jobId , unitId ) ;
2025-12-23 18:27:31 +10:00
}
2026-01-22 12:06:57 +10:00
//private async Task<DateTimeOffset> GetNextRunAsync(Job job, DateTimeOffset? currentNextRun = null)
//{
// var referenceDate = job.Group?.ReferenceDate ?? DateTimeOffset.UtcNow;
// return await esppScheduleTransformService.GetNextDateAsync(job.GroupId, referenceDate);
//}
2026-01-16 19:45:38 +10:00
private async Task UpdateMatchingStatusAsync ( Guid jobId , string comment )
{
var status = new MatchingStatusItemDto
{
DateStart = DateTimeOffset . UtcNow ,
Action = TemplateMatcherActionEnum . Sync ,
Comment = comment
} ;
await matchingStatusService . SetMatchingStatusAsync (
jobId ,
SyncTaskEntityTypeEnum . Job ,
new MatchingStatusItem { Data = status , Timestamp = DateTimeOffset . UtcNow , Source = nameof ( SimpleTemplateSynchronizer ) } ,
TimeSpan . FromMinutes ( 30 )
) ;
}
2025-12-23 18:27:31 +10:00
}