feat(tempateMatcher): в матчеры добавлено взаимодействие с MatchingStatusService, для отображения статуса операции
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ using PARR.BLL.Domain.Mq;
|
|||||||
using PARR.BLL.Services.Interfaces;
|
using PARR.BLL.Services.Interfaces;
|
||||||
using PARR.Common.Domain;
|
using PARR.Common.Domain;
|
||||||
using PARR.Constants;
|
using PARR.Constants;
|
||||||
|
using PARR.DAL.Cache.Models;
|
||||||
using PARR.DAL.DomainServices.Interfaces;
|
using PARR.DAL.DomainServices.Interfaces;
|
||||||
using PARR.DAL.DomainServices.Shortcodes;
|
using PARR.DAL.DomainServices.Shortcodes;
|
||||||
using PARR.DAL.Models;
|
using PARR.DAL.Models;
|
||||||
@@ -38,6 +39,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
private readonly ITemplateDeactivator templateDeactivator;
|
private readonly ITemplateDeactivator templateDeactivator;
|
||||||
private readonly ITemplateNameNormalizer templateNameNormalizer;
|
private readonly ITemplateNameNormalizer templateNameNormalizer;
|
||||||
private readonly ITemplateUpdaterMqSender templateUpdaterMqSender;
|
private readonly ITemplateUpdaterMqSender templateUpdaterMqSender;
|
||||||
|
private readonly IMatchingStatusService matchingStatusService;
|
||||||
|
|
||||||
public SimpleTemplateSynchronizer(
|
public SimpleTemplateSynchronizer(
|
||||||
ILogger<SimpleTemplateSynchronizer> logger,
|
ILogger<SimpleTemplateSynchronizer> logger,
|
||||||
@@ -56,7 +58,8 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
IUnitFieldService unitFieldService,
|
IUnitFieldService unitFieldService,
|
||||||
ITemplateDeactivator templateDeactivator,
|
ITemplateDeactivator templateDeactivator,
|
||||||
ITemplateNameNormalizer templateNameNormalizer,
|
ITemplateNameNormalizer templateNameNormalizer,
|
||||||
ITemplateUpdaterMqSender templateUpdaterMqSender
|
ITemplateUpdaterMqSender templateUpdaterMqSender,
|
||||||
|
IMatchingStatusService matchingStatusService
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
@@ -70,174 +73,237 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
this.templateDeactivator = templateDeactivator;
|
this.templateDeactivator = templateDeactivator;
|
||||||
this.templateNameNormalizer = templateNameNormalizer;
|
this.templateNameNormalizer = templateNameNormalizer;
|
||||||
this.templateUpdaterMqSender = templateUpdaterMqSender;
|
this.templateUpdaterMqSender = templateUpdaterMqSender;
|
||||||
|
this.matchingStatusService = matchingStatusService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SyncTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator)
|
public async Task SyncTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator)
|
||||||
{
|
{
|
||||||
logger.LogDebug("Начало синхронизации шаблонов для Job {JobId}", jobId);
|
logger.LogDebug("Начало синхронизации шаблонов для Job {JobId}", jobId);
|
||||||
|
|
||||||
var job = await jobService.Get()
|
// === Проверка: уже запущена? ===
|
||||||
.AsNoTracking()
|
var existingStatus = await matchingStatusService.GetStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
||||||
.Include(j => j.AutoControl)
|
if (existingStatus.DetailsJobs?.Any() == true)
|
||||||
.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);
|
logger.LogWarning("Синхронизация для Job {JobId} уже запущена. Пропускаем.", jobId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var unitIds = await unitFilterService.GetUnitsIdByJobFilterAsync(jobId);
|
// === Устанавливаем статус "в процессе" ===
|
||||||
if (unitIds == null || !unitIds.Any())
|
var initialStatus = new MatchingStatusItemDto
|
||||||
{
|
{
|
||||||
logger.LogInformation("Для Job {JobId} фильтры не дали Unit'ов.", jobId);
|
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)
|
||||||
|
);
|
||||||
|
|
||||||
// Деактивировать все существующие шаблоны для этого Job
|
try
|
||||||
var existingTemplatesForDeactivation = await templateService.Get()
|
{
|
||||||
|
var job = await jobService.Get()
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Include(t => t.UnitsInTemplate)
|
.Include(j => j.AutoControl)
|
||||||
.Where(t => t.JobId == jobId && t.StatusTypeId == TemplateStatusTypeEnum.Used)
|
.Include(j => j.Tnk)
|
||||||
.ToListAsync();
|
.Include(j => j.Group)
|
||||||
|
.ThenInclude(g => g!.GroupType)
|
||||||
|
.Include(j => j.UnitFilters)
|
||||||
|
.ThenInclude(uf => uf.RelationshipFilters)
|
||||||
|
.FirstOrDefaultAsync(j => j.Id == jobId);
|
||||||
|
|
||||||
foreach (var unusedTemplate in existingTemplatesForDeactivation)
|
if (job == null)
|
||||||
{
|
{
|
||||||
logger.LogInformation("Деактивация лишнего шаблона {TemplateId} (Job {JobId}, UnitId {UnitId}).", unusedTemplate.Id, jobId, unusedTemplate.UnitId);
|
logger.LogWarning("Job {JobId} не найден.", jobId);
|
||||||
await templateDeactivator.DeactivateTemplateAsync(unusedTemplate, initiator);
|
await UpdateMatchingStatusAsync(jobId, "Job не найден");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.LogInformation("Синхронизация шаблонов завершена для Job {JobId}.", jobId);
|
var unitIds = await unitFilterService.GetUnitsIdByJobFilterAsync(jobId);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
// Отладка: проверить, есть ли юнит в unitIds
|
|
||||||
if (unitIds.Contains(targetUnitId))
|
|
||||||
{
|
|
||||||
logger.LogDebug("Юнит {TargetUnitId} найден в unitIds.", targetUnitId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.LogDebug("Юнит {TargetUnitId} НЕ найден в unitIds.", targetUnitId);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
var reusableTemplate = await templateReuser.TryReuseOneUnusedTemplateAsync(jobId, unitId, initiator);
|
|
||||||
if (reusableTemplate != null)
|
|
||||||
{
|
{
|
||||||
logger.LogInformation("Переиспользован шаблон {TemplateId} для Job {JobId}, UnitId {UnitId}.", reusableTemplate.Id, jobId, unitId);
|
if (unitIds == null || !unitIds.Any())
|
||||||
|
|
||||||
// === Создаём временный Template для нормализации имени ===
|
|
||||||
var tempTemplateForName = new Template
|
|
||||||
{
|
{
|
||||||
Id = reusableTemplate.Id,
|
logger.LogInformation("Для Job {JobId} фильтры не дали Unit'ов.", jobId);
|
||||||
Name = reusableTemplate.Name,
|
|
||||||
JobId = jobId,
|
|
||||||
UnitId = unitId,
|
|
||||||
Index = reusableTemplate.Index,
|
|
||||||
Job = job, // загруженный job
|
|
||||||
Unit = reusableTemplate.Unit, // может быть null — нормально
|
|
||||||
UnitsInTemplate = new List<UnitsInTemplate>() // для простого шаблона
|
|
||||||
};
|
|
||||||
|
|
||||||
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(tempTemplateForName);
|
var existingTemplatesForDeactivation = await templateService.Get()
|
||||||
var nextRun = await GetNextRunAsync(job);
|
.AsNoTracking()
|
||||||
|
.Include(t => t.UnitsInTemplate)
|
||||||
|
.Where(t => t.JobId == jobId && t.StatusTypeId == TemplateStatusTypeEnum.Used)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
var updateRequest = new TemplateUpdaterMq
|
await UpdateMatchingStatusAsync(jobId, $"Нет Unit'ов. Деактивация {existingTemplatesForDeactivation.Count} шаблонов...");
|
||||||
{
|
|
||||||
TemplateId = reusableTemplate.Id,
|
|
||||||
JobId = jobId,
|
|
||||||
UnitId = unitId,
|
|
||||||
Name = expectedName,
|
|
||||||
IsActiveTemplate = job.AutoControl?.InitUsedTemplateState ?? DefaultUsedTemplateState,
|
|
||||||
IsActiveSchedule = job.AutoControl?.InitUsedScheduleState ?? DefaultUsedScheduleState,
|
|
||||||
StatusTypeId = TemplateStatusTypeEnum.Used,
|
|
||||||
Initiator = initiator,
|
|
||||||
NextRun = nextRun,
|
|
||||||
UnitsInTemplate = new List<Guid>() // для простого шаблона
|
|
||||||
};
|
|
||||||
|
|
||||||
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
foreach (var unusedTemplate in existingTemplatesForDeactivation)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Деактивация лишнего шаблона {TemplateId} (Job {JobId}, UnitId {UnitId}).", unusedTemplate.Id, jobId, unusedTemplate.UnitId);
|
||||||
|
await templateDeactivator.DeactivateTemplateAsync(unusedTemplate, initiator);
|
||||||
|
}
|
||||||
|
|
||||||
|
await UpdateMatchingStatusAsync(jobId, "Синхронизация завершена: нет Unit'ов");
|
||||||
|
await matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
||||||
|
logger.LogInformation("Синхронизация шаблонов завершена для Job {JobId}.", jobId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
if (unitIds.Contains(targetUnitId))
|
||||||
|
{
|
||||||
|
logger.LogDebug("Юнит {TargetUnitId} найден в unitIds.", targetUnitId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.LogDebug("Создание нового шаблона для Job {JobId}, UnitId {UnitId}.", jobId, unitId);
|
logger.LogDebug("Юнит {TargetUnitId} НЕ найден в unitIds.", targetUnitId);
|
||||||
await CreateSimpleTemplateAsync(jobId, unitId, initiator);
|
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
|
|
||||||
// Обработка существующих шаблонов (проверка имени)
|
var existingTemplates = await templateService.Get()
|
||||||
foreach (var template in existingUsedTemplates)
|
.Include(t => t.UnitsInTemplate)
|
||||||
{
|
.Include(t => t.Job)
|
||||||
if (unitIds.Contains(template.UnitId))
|
.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();
|
||||||
|
|
||||||
|
// === Подсчёт операций ===
|
||||||
|
int toCreate = newUnitIds.Count;
|
||||||
|
int toUpdate = 0;
|
||||||
|
int toDeactivate = unusedTemplates.Count;
|
||||||
|
|
||||||
|
// Подсчёт обновлений по имени
|
||||||
|
foreach (var template in existingUsedTemplates)
|
||||||
{
|
{
|
||||||
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(template);
|
if (unitIds.Contains(template.UnitId))
|
||||||
if (!string.Equals(template.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
logger.LogDebug("Шаблон {TemplateId} требует обновления имени: старое = '{OldName}', новое = '{NewName}'", template.Id, template.Name, expectedName);
|
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(template);
|
||||||
|
if (!string.Equals(template.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
toUpdate++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var nextRun = await GetNextRunAsync(job, template.NextRun);
|
await UpdateMatchingStatusAsync(jobId, $"Осталось: создать={toCreate}, обновить={toUpdate}, деактивировать={toDeactivate}");
|
||||||
|
|
||||||
|
// === Обработка новых юнитов ===
|
||||||
|
int created = 0, updated = 0, deactivated = 0;
|
||||||
|
foreach (var unitId in newUnitIds)
|
||||||
|
{
|
||||||
|
var reusableTemplate = await templateReuser.TryReuseOneUnusedTemplateAsync(jobId, unitId, initiator);
|
||||||
|
if (reusableTemplate != null)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Переиспользован шаблон {TemplateId} для Job {JobId}, UnitId {UnitId}.", reusableTemplate.Id, jobId, unitId);
|
||||||
|
|
||||||
|
var tempTemplateForName = new Template
|
||||||
|
{
|
||||||
|
Id = reusableTemplate.Id,
|
||||||
|
Name = reusableTemplate.Name,
|
||||||
|
JobId = jobId,
|
||||||
|
UnitId = unitId,
|
||||||
|
Index = reusableTemplate.Index,
|
||||||
|
Job = job,
|
||||||
|
Unit = reusableTemplate.Unit,
|
||||||
|
UnitsInTemplate = new List<UnitsInTemplate>()
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(tempTemplateForName);
|
||||||
|
var nextRun = await GetNextRunAsync(job);
|
||||||
|
|
||||||
var updateRequest = new TemplateUpdaterMq
|
var updateRequest = new TemplateUpdaterMq
|
||||||
{
|
{
|
||||||
TemplateId = template.Id,
|
TemplateId = reusableTemplate.Id,
|
||||||
JobId = jobId,
|
JobId = jobId,
|
||||||
UnitId = template.UnitId,
|
UnitId = unitId,
|
||||||
Name = expectedName,
|
Name = expectedName,
|
||||||
IsActiveTemplate = template.IsActiveTemplate,
|
IsActiveTemplate = job.AutoControl?.InitUsedTemplateState ?? DefaultUsedTemplateState,
|
||||||
IsActiveSchedule = template.IsActiveSchedule,
|
IsActiveSchedule = job.AutoControl?.InitUsedScheduleState ?? DefaultUsedScheduleState,
|
||||||
LastRun = template.LastRun,
|
|
||||||
NextRun = nextRun,
|
|
||||||
Index = template.Index,
|
|
||||||
StatusTypeId = TemplateStatusTypeEnum.Used,
|
StatusTypeId = TemplateStatusTypeEnum.Used,
|
||||||
Initiator = initiator,
|
Initiator = initiator,
|
||||||
UnitsInTemplate = new List<Guid>() // для простого шаблона
|
NextRun = nextRun,
|
||||||
|
UnitsInTemplate = new List<Guid>()
|
||||||
};
|
};
|
||||||
|
|
||||||
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogDebug("Создание нового шаблона для Job {JobId}, UnitId {UnitId}.", jobId, unitId);
|
||||||
|
await CreateSimpleTemplateAsync(jobId, unitId, initiator);
|
||||||
|
}
|
||||||
|
created++;
|
||||||
|
await UpdateMatchingStatusAsync(jobId, $"Прогресс: создано={created}, обновлено={updated}, деактивировано={deactivated}. Осталось: создать={toCreate - created}, обновить={toUpdate - updated}, деактивировать={toDeactivate - deactivated}");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Деактивация лишних шаблонов
|
// === Обработка существующих шаблонов (проверка имени) ===
|
||||||
foreach (var unusedTemplate in unusedTemplates)
|
foreach (var template in existingUsedTemplates)
|
||||||
|
{
|
||||||
|
if (unitIds.Contains(template.UnitId))
|
||||||
|
{
|
||||||
|
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(template);
|
||||||
|
if (!string.Equals(template.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
logger.LogDebug("Шаблон {TemplateId} требует обновления имени.", template.Id);
|
||||||
|
|
||||||
|
var nextRun = await GetNextRunAsync(job, template.NextRun);
|
||||||
|
|
||||||
|
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,
|
||||||
|
UnitsInTemplate = new List<Guid>()
|
||||||
|
};
|
||||||
|
|
||||||
|
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
||||||
|
updated++;
|
||||||
|
await UpdateMatchingStatusAsync(jobId, $"Прогресс: создано={created}, обновлено={updated}, деактивировано={deactivated}. Осталось: создать={toCreate - created}, обновить={toUpdate - updated}, деактивировать={toDeactivate - deactivated}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Деактивация лишних шаблонов ===
|
||||||
|
foreach (var unusedTemplate in unusedTemplates)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Деактивация лишнего шаблона {TemplateId} (Job {JobId}, UnitId {UnitId}).", unusedTemplate.Id, jobId, unusedTemplate.UnitId);
|
||||||
|
await templateDeactivator.DeactivateTemplateAsync(unusedTemplate, initiator);
|
||||||
|
deactivated++;
|
||||||
|
await UpdateMatchingStatusAsync(jobId, $"Прогресс: создано={created}, обновлено={updated}, деактивировано={deactivated}. Осталось: создать={toCreate - created}, обновить={toUpdate - updated}, деактивировать={toDeactivate - deactivated}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Успешное завершение ===
|
||||||
|
await UpdateMatchingStatusAsync(jobId, "Синхронизация завершена успешно");
|
||||||
|
await matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
||||||
|
logger.LogInformation("Синхронизация шаблонов завершена для Job {JobId}.", jobId);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogInformation("Деактивация лишнего шаблона {TemplateId} (Job {JobId}, UnitId {UnitId}).", unusedTemplate.Id, jobId, unusedTemplate.UnitId);
|
logger.LogError(ex, "Ошибка при синхронизации Job {JobId}", jobId);
|
||||||
await templateDeactivator.DeactivateTemplateAsync(unusedTemplate, initiator);
|
await UpdateMatchingStatusAsync(jobId, $"Ошибка: {ex.Message}");
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.LogInformation("Синхронизация шаблонов завершена для Job {JobId}.", jobId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SyncTemplatesForJobGroupAsync(Guid jobGroupId, HistoryInitiator initiator)
|
public async Task SyncTemplatesForJobGroupAsync(Guid jobGroupId, HistoryInitiator initiator)
|
||||||
@@ -249,86 +315,136 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
{
|
{
|
||||||
logger.LogDebug("Обновление шаблонов для Job {JobId}", jobId);
|
logger.LogDebug("Обновление шаблонов для Job {JobId}", jobId);
|
||||||
|
|
||||||
var job = await jobService.Get()
|
// === Проверка: уже запущена? ===
|
||||||
.AsNoTracking()
|
var existingStatus = await matchingStatusService.GetStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
||||||
.Include(j => j.AutoControl)
|
if (existingStatus.DetailsJobs?.Any() == true)
|
||||||
.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);
|
logger.LogWarning("Обновление для Job {JobId} уже запущено. Пропускаем.", jobId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var unitIds = await unitFilterService.GetUnitsIdByJobFilterAsync(jobId);
|
var initialStatus = new MatchingStatusItemDto
|
||||||
if (unitIds == null || !unitIds.Any())
|
|
||||||
{
|
{
|
||||||
logger.LogInformation("Для Job {JobId} фильтры не дали Unit'ов.", jobId);
|
DateStart = DateTimeOffset.UtcNow,
|
||||||
return;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
var unitIds = await unitFilterService.GetUnitsIdByJobFilterAsync(jobId);
|
||||||
|
if (unitIds == null || !unitIds.Any())
|
||||||
|
{
|
||||||
|
logger.LogInformation("Для Job {JobId} фильтры не дали Unit'ов.", jobId);
|
||||||
|
await UpdateMatchingStatusAsync(jobId, "Нет Unit'ов — обновление не требуется");
|
||||||
|
await matchingStatusService.DeleteMatchingStatusAsync(jobId, SyncTaskEntityTypeEnum.Job);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// Отладка: проверить, есть ли юнит в unitIds
|
if (unitIds.Contains(targetUnitId))
|
||||||
if (unitIds.Contains(targetUnitId))
|
{
|
||||||
{
|
logger.LogDebug("Юнит {TargetUnitId} найден в unitIds.", targetUnitId);
|
||||||
logger.LogDebug("Юнит {TargetUnitId} найден в unitIds.", targetUnitId);
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
logger.LogDebug("Юнит {TargetUnitId} НЕ найден в unitIds.", targetUnitId);
|
||||||
logger.LogDebug("Юнит {TargetUnitId} НЕ найден в unitIds.", targetUnitId);
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var existingTemplates = await templateService.Get()
|
var existingTemplates = await templateService.Get()
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Include(t => t.Unit)
|
.Include(t => t.Unit)
|
||||||
.Include(t => t.UnitsInTemplate)
|
.Include(t => t.UnitsInTemplate)
|
||||||
.Include(t => t.Job)
|
.Include(t => t.Job)
|
||||||
.ThenInclude(t => t!.Group)
|
.ThenInclude(t => t!.Group)
|
||||||
.ThenInclude(t => t!.GroupType)
|
.ThenInclude(t => t!.GroupType)
|
||||||
.Include(t => t.Job)
|
.Include(t => t.Job)
|
||||||
.ThenInclude(t => t!.Tnk)
|
.ThenInclude(t => t!.Tnk)
|
||||||
.Where(t => t.JobId == jobId && t.StatusTypeId == TemplateStatusTypeEnum.Used)
|
.Where(t => t.JobId == jobId && t.StatusTypeId == TemplateStatusTypeEnum.Used)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
foreach (var template in existingTemplates)
|
int toUpdate = 0;
|
||||||
{
|
foreach (var template in existingTemplates)
|
||||||
if (unitIds.Contains(template.UnitId))
|
|
||||||
{
|
{
|
||||||
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(template);
|
if (unitIds.Contains(template.UnitId))
|
||||||
if (!string.Equals(template.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
logger.LogDebug("Шаблон {TemplateId} требует обновления имени: старое = '{OldName}', новое = '{NewName}'", template.Id, template.Name, expectedName);
|
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(template);
|
||||||
|
if (!string.Equals(template.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
||||||
var nextRun = await GetNextRunAsync(job, template.NextRun);
|
toUpdate++;
|
||||||
|
|
||||||
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,
|
|
||||||
UnitsInTemplate = new List<Guid>() // для простого шаблона
|
|
||||||
};
|
|
||||||
|
|
||||||
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
logger.LogInformation("Обновление шаблонов завершено для Job {JobId}.", jobId);
|
await UpdateMatchingStatusAsync(jobId, $"Обновление имён: {toUpdate} шаблонов");
|
||||||
|
|
||||||
|
int updated = 0;
|
||||||
|
foreach (var template in existingTemplates)
|
||||||
|
{
|
||||||
|
if (unitIds.Contains(template.UnitId))
|
||||||
|
{
|
||||||
|
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(template);
|
||||||
|
if (!string.Equals(template.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
logger.LogDebug("Шаблон {TemplateId} требует обновления имени.", template.Id);
|
||||||
|
|
||||||
|
var nextRun = await GetNextRunAsync(job, template.NextRun);
|
||||||
|
|
||||||
|
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,
|
||||||
|
UnitsInTemplate = new List<Guid>()
|
||||||
|
};
|
||||||
|
|
||||||
|
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
||||||
|
updated++;
|
||||||
|
await UpdateMatchingStatusAsync(jobId, $"Обновлено: {updated}/{toUpdate}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CreateSimpleTemplateAsync(Guid jobId, Guid unitId, HistoryInitiator initiator)
|
private async Task CreateSimpleTemplateAsync(Guid jobId, Guid unitId, HistoryInitiator initiator)
|
||||||
@@ -339,7 +455,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
{
|
{
|
||||||
JobId = jobId,
|
JobId = jobId,
|
||||||
UnitId = unitId,
|
UnitId = unitId,
|
||||||
UnitsInTemplate = new List<Guid>(), // для простого шаблона
|
UnitsInTemplate = new List<Guid>(),
|
||||||
HistoryInitiator = initiator
|
HistoryInitiator = initiator
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -355,4 +471,20 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
var referenceDate = job.Group?.ReferenceDate ?? DateTimeOffset.UtcNow;
|
var referenceDate = job.Group?.ReferenceDate ?? DateTimeOffset.UtcNow;
|
||||||
return await esppScheduleTransformService.GetNextDateAsync(job.GroupId, referenceDate);
|
return await esppScheduleTransformService.GetNextDateAsync(job.GroupId, referenceDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user