feat(templateMatcher): добавлена обработка работы с неимпользуемыми шаблонами.
This commit is contained in:
@@ -5,6 +5,7 @@ using PARR.BLL.Services.Interfaces;
|
||||
using PARR.Common.Domain;
|
||||
using PARR.Constants;
|
||||
using PARR.DAL.Cache.Models;
|
||||
using PARR.DAL.Contracts;
|
||||
using PARR.DAL.DomainServices.Interfaces;
|
||||
using PARR.DAL.DomainServices.Shortcodes;
|
||||
using PARR.DAL.Models;
|
||||
@@ -28,6 +29,9 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
private const bool DefaultUsedTemplateState = false;
|
||||
private const bool DefaultUsedScheduleState = false;
|
||||
|
||||
// Маска для неиспользуемых шаблонов — хардкод, недоступна из GUI
|
||||
private const string UnusedTemplateNameMask = "%П-1%_НЕАКТУАЛЬНЫЕ РАБОТЫ_%ЭК%_%ТИКТАК%";
|
||||
|
||||
private readonly ILogger<SimpleTemplateSynchronizer> logger;
|
||||
private readonly IUnitFilterService unitFilterService;
|
||||
private readonly MqSettings mqSettings;
|
||||
@@ -40,6 +44,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
private readonly ITemplateNameNormalizer templateNameNormalizer;
|
||||
private readonly ITemplateUpdaterMqSender templateUpdaterMqSender;
|
||||
private readonly IMatchingStatusService matchingStatusService;
|
||||
private readonly SettingsFromDb settingsFromDb;
|
||||
|
||||
public SimpleTemplateSynchronizer(
|
||||
ILogger<SimpleTemplateSynchronizer> logger,
|
||||
@@ -59,7 +64,8 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
ITemplateDeactivator templateDeactivator,
|
||||
ITemplateNameNormalizer templateNameNormalizer,
|
||||
ITemplateUpdaterMqSender templateUpdaterMqSender,
|
||||
IMatchingStatusService matchingStatusService
|
||||
IMatchingStatusService matchingStatusService,
|
||||
SettingsFromDb settingsFromDb
|
||||
)
|
||||
{
|
||||
this.logger = logger;
|
||||
@@ -74,10 +80,20 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
this.templateNameNormalizer = templateNameNormalizer;
|
||||
this.templateUpdaterMqSender = templateUpdaterMqSender;
|
||||
this.matchingStatusService = matchingStatusService;
|
||||
this.settingsFromDb = settingsFromDb;
|
||||
}
|
||||
|
||||
public async Task SyncTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator)
|
||||
{
|
||||
// === Специальная обработка для Job неиспользуемых шаблонов ===
|
||||
if (jobId == settingsFromDb.JobIdForUnusedTemplates)
|
||||
{
|
||||
logger.LogInformation("Обработка синхронизации для Job неиспользуемых шаблонов {JobId}", jobId);
|
||||
await SyncUnusedTemplatesAsync(jobId, initiator);
|
||||
return;
|
||||
}
|
||||
|
||||
// === Обычная логика для всех остальных Job ===
|
||||
logger.LogDebug("Начало синхронизации шаблонов для Job {JobId}", jobId);
|
||||
|
||||
// === Проверка: уже запущена? ===
|
||||
@@ -122,32 +138,32 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
}
|
||||
|
||||
var unitIds = await unitFilterService.GetUnitsIdByJobFilterAsync(jobId);
|
||||
if (unitIds == null || !unitIds.Any())
|
||||
{
|
||||
if (unitIds == null || !unitIds.Any())
|
||||
logger.LogInformation("Для Job {JobId} фильтры не дали Unit'ов.", jobId);
|
||||
|
||||
var existingTemplatesForDeactivation = await templateService.Get()
|
||||
.AsNoTracking()
|
||||
.Include(t => t.UnitsInTemplate)
|
||||
.Where(t => t.JobId == jobId && t.StatusTypeId == TemplateStatusTypeEnum.Used)
|
||||
.ToListAsync();
|
||||
|
||||
await UpdateMatchingStatusAsync(jobId, $"Нет Unit'ов. Деактивация {existingTemplatesForDeactivation.Count} шаблонов...");
|
||||
|
||||
foreach (var unusedTemplate in existingTemplatesForDeactivation)
|
||||
{
|
||||
logger.LogInformation("Для Job {JobId} фильтры не дали Unit'ов.", jobId);
|
||||
|
||||
var existingTemplatesForDeactivation = await templateService.Get()
|
||||
.AsNoTracking()
|
||||
.Include(t => t.UnitsInTemplate)
|
||||
.Where(t => t.JobId == jobId && t.StatusTypeId == TemplateStatusTypeEnum.Used)
|
||||
.ToListAsync();
|
||||
|
||||
await UpdateMatchingStatusAsync(jobId, $"Нет Unit'ов. Деактивация {existingTemplatesForDeactivation.Count} шаблонов...");
|
||||
|
||||
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;
|
||||
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
|
||||
// Отладка: проверить, есть ли юнит в unitIds
|
||||
if (unitIds.Contains(targetUnitId))
|
||||
{
|
||||
logger.LogDebug("Юнит {TargetUnitId} найден в unitIds.", targetUnitId);
|
||||
@@ -209,6 +225,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
{
|
||||
logger.LogInformation("Переиспользован шаблон {TemplateId} для Job {JobId}, UnitId {UnitId}.", reusableTemplate.Id, jobId, unitId);
|
||||
|
||||
// === Создаём временный Template для нормализации имени ===
|
||||
var tempTemplateForName = new Template
|
||||
{
|
||||
Id = reusableTemplate.Id,
|
||||
@@ -216,9 +233,9 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
JobId = jobId,
|
||||
UnitId = unitId,
|
||||
Index = reusableTemplate.Index,
|
||||
Job = job,
|
||||
Unit = reusableTemplate.Unit,
|
||||
UnitsInTemplate = new List<UnitsInTemplate>()
|
||||
Job = job, // загруженный job
|
||||
Unit = reusableTemplate.Unit, // может быть null — нормально
|
||||
UnitsInTemplate = new List<UnitsInTemplate>() // для простого шаблона
|
||||
};
|
||||
|
||||
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(tempTemplateForName);
|
||||
@@ -235,7 +252,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
StatusTypeId = TemplateStatusTypeEnum.Used,
|
||||
Initiator = initiator,
|
||||
NextRun = nextRun,
|
||||
UnitsInTemplate = new List<Guid>()
|
||||
UnitsInTemplate = new List<Guid>() // для простого шаблона
|
||||
};
|
||||
|
||||
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
||||
@@ -257,7 +274,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(template);
|
||||
if (!string.Equals(template.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
logger.LogDebug("Шаблон {TemplateId} требует обновления имени.", template.Id);
|
||||
logger.LogDebug("Шаблон {TemplateId} требует обновления имени: старое = '{OldName}', новое = '{NewName}'", template.Id, template.Name, expectedName);
|
||||
|
||||
var nextRun = await GetNextRunAsync(job, template.NextRun);
|
||||
|
||||
@@ -274,7 +291,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
Index = template.Index,
|
||||
StatusTypeId = TemplateStatusTypeEnum.Used,
|
||||
Initiator = initiator,
|
||||
UnitsInTemplate = new List<Guid>()
|
||||
UnitsInTemplate = new List<Guid>() // для простого шаблона
|
||||
};
|
||||
|
||||
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
||||
@@ -365,6 +382,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// Отладка: проверить, есть ли юнит в unitIds
|
||||
if (unitIds.Contains(targetUnitId))
|
||||
{
|
||||
logger.LogDebug("Юнит {TargetUnitId} найден в unitIds.", targetUnitId);
|
||||
@@ -387,20 +405,6 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
.Where(t => t.JobId == jobId && t.StatusTypeId == TemplateStatusTypeEnum.Used)
|
||||
.ToListAsync();
|
||||
|
||||
int toUpdate = 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))
|
||||
toUpdate++;
|
||||
}
|
||||
}
|
||||
|
||||
await UpdateMatchingStatusAsync(jobId, $"Обновление имён: {toUpdate} шаблонов");
|
||||
|
||||
int updated = 0;
|
||||
foreach (var template in existingTemplates)
|
||||
{
|
||||
if (unitIds.Contains(template.UnitId))
|
||||
@@ -408,7 +412,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(template);
|
||||
if (!string.Equals(template.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
logger.LogDebug("Шаблон {TemplateId} требует обновления имени.", template.Id);
|
||||
logger.LogDebug("Шаблон {TemplateId} требует обновления имени: старое = '{OldName}', новое = '{NewName}'", template.Id, template.Name, expectedName);
|
||||
|
||||
var nextRun = await GetNextRunAsync(job, template.NextRun);
|
||||
|
||||
@@ -425,12 +429,10 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
Index = template.Index,
|
||||
StatusTypeId = TemplateStatusTypeEnum.Used,
|
||||
Initiator = initiator,
|
||||
UnitsInTemplate = new List<Guid>()
|
||||
UnitsInTemplate = new List<Guid>() // для простого шаблона
|
||||
};
|
||||
|
||||
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
||||
updated++;
|
||||
await UpdateMatchingStatusAsync(jobId, $"Обновлено: {updated}/{toUpdate}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -447,6 +449,149 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
var nextRun = await GetNextRunAsync(unusedJob);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private async Task CreateSimpleTemplateAsync(Guid jobId, Guid unitId, HistoryInitiator initiator)
|
||||
{
|
||||
logger.LogInformation("Создание нового простого шаблона для Job {JobId}, UnitId {UnitId}.", jobId, unitId);
|
||||
@@ -455,7 +600,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
|
||||
{
|
||||
JobId = jobId,
|
||||
UnitId = unitId,
|
||||
UnitsInTemplate = new List<Guid>(),
|
||||
UnitsInTemplate = new List<Guid>(), // для простого шаблона
|
||||
HistoryInitiator = initiator
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user