feat(tempateMatcher,aihitLoader,dal): В UnitFilter добавлен Cache; в данные по ПТК добавлены аттрибуты "Холодный резерв" и "ДОПОЛНИТЕЛЬНАЯ_ИНФОРМАЦИЯ"; в синхронизаторах устанавливается неактуальная работа при деактивации

This commit is contained in:
Mikhail Kuznetsov
2025-12-25 21:16:20 +10:00
parent dbb7dee4b4
commit 2af8ed26d8
6 changed files with 154 additions and 32 deletions

View File

@@ -41,6 +41,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
private readonly IEsppScheduleTransformService esppScheduleTransformService;
private readonly IUnitRegionalEkPtkGroupService regionalEkPtkGroupService;
private readonly IUnitFieldService unitFieldService;
private readonly IJobService jobService;
public GroupedTemplateSynchronizer(
ILogger<GroupedTemplateSynchronizer> logger,
@@ -56,7 +57,8 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
IShortcodesService shortcodesService,
IEsppScheduleTransformService esppScheduleTransformService,
IUnitRegionalEkPtkGroupService regionalEkPtkGroupService,
IUnitFieldService unitFieldService
IUnitFieldService unitFieldService,
IJobService jobService
)
{
this.logger = logger;
@@ -73,6 +75,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
this.esppScheduleTransformService = esppScheduleTransformService;
this.regionalEkPtkGroupService = regionalEkPtkGroupService;
this.unitFieldService = unitFieldService;
this.jobService = jobService;
}
public async Task SyncTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator)
@@ -105,7 +108,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
var jobsInGroup = jobGroup.Jobs.ToList();
// --- НОВАЯ ЛОГИКА: Получение FieldId и разрешённых значений для "РАБОЧАЯ_ГР_ОТВ_ЗАК" ---
// --- Получение FieldId и разрешённых значений для "РАБОЧАЯ_ГР_ОТВ_ЗАК" ---
var workGroupField = await unitFieldService.GetByAihitNameAsync("РАБОЧАЯ_ГР_ОТВ_ЗАК");
if (workGroupField == null)
{
@@ -120,6 +123,17 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
logger.LogDebug("Найдено {Count} значений из UnitRegionalEkPtkGroup для проверки поля 'РАБОЧАЯ_ГР_ОТВ_ЗАК'.", regionalGroupValueIds.Count);
// Загружаем заранее подготовленный Job (например, для неиспользуемых шаблонов)
var unusedJobId = Guid.Parse("8f85a91c-a223-4686-bb69-1f0ee73624f2");
var unusedJob = await jobService.Get()
.AsNoTracking()
.Include(j => j.Tnk)
.Include(j => j.Group)
.ThenInclude(g => g.GroupType)
.FirstOrDefaultAsync(j => j.Id == unusedJobId);
// 2. Найти Job с максимальным MaxValueRelationships
var maxJob = jobsInGroup
.Where(j => j.MaxValueRelationships.HasValue)
@@ -387,7 +401,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
logger.LogDebug("Связанный юнит {RegionalUnitId}: разбит на {GroupCount} подгрупп.", relationshipUnitId, childUnitGroups.Count);
// Для каждой подгруппы:
for (int i = 1; i <= childUnitGroups.Count; i++)
for (int i = 0; i < childUnitGroups.Count; i++)
{
var subGroup = childUnitGroups[i];
var subGroupSize = subGroup.Count;
@@ -401,7 +415,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
var existingTemplatesForRelationship = await templateService.Get()
.AsNoTracking()
.Include(t => t.UnitsInTemplate)
.Where(t => t.JobId == targetJob.Id && t.UnitId == relationshipUnitId && t.Index == i)
.Where(t => t.JobId == targetJob.Id && t.UnitId == relationshipUnitId && t.Index == i && t.StatusTypeId == TemplateStatusTypeEnum.Used)
.ToListAsync();
var existingTemplateForSubGroup = existingTemplatesForRelationship.FirstOrDefault();
@@ -416,7 +430,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
{
logger.LogDebug("Шаблон {TemplateId} (Job {JobId}, Regional {RegionalId}, Index {Index}) актуален по юнитам.", existingTemplateForSubGroup.Id, targetJob.Id, relationshipUnitId, i);
// Проверить, изменилось ли имя шаблона (например, из-за %МАКС:...% или %ТНК-КРАТКО%)
// Проверить, изменилось ли имя шаблона (например, из-за %МАКС:...% или %ТНК-КРАТКО%)
var expectedName = await GetNormalizedTemplateNameAsync(targetJob, relationshipUnitId, i, subGroup);
if (!string.Equals(existingTemplateForSubGroup.Name, expectedName, StringComparison.OrdinalIgnoreCase))
{
@@ -434,7 +448,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
IsActiveSchedule = existingTemplateForSubGroup.IsActiveSchedule,
LastRun = existingTemplateForSubGroup.LastRun,
NextRun = nextRun,
Index = i,
Index = i ,
StatusTypeId = TemplateStatusTypeEnum.Used,
Initiator = initiator,
UnitsInTemplate = subGroup
@@ -539,7 +553,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
if (!expectedTemplateKeys.Contains(key))
{
logger.LogInformation("Деактивация лишнего шаблона {TemplateId} (Job {JobId}, Regional {UnitId}, Index {Index}).", existingTemplate.Id, existingTemplate.JobId, existingTemplate.UnitId, existingTemplate.Index);
await DeactivateTemplateAsync(existingTemplate, existingTemplate.JobId, initiator);
await DeactivateTemplateAsync(existingTemplate, existingTemplate.JobId, unusedJob, initiator);
}
}
@@ -644,10 +658,11 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
private async Task<bool> DeactivateTemplateAsync(
Template template,
Guid jobId,
Job unusedJob,
HistoryInitiator initiator)
{
if (template.StatusTypeId == TemplateStatusTypeEnum.Updating)
return true; // уже в обработке
return true;
if (template.StatusTypeId == TemplateStatusTypeEnum.Unused)
{
@@ -667,17 +682,26 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
return false;
}
if (unusedJob == null)
{
logger.LogError("Job для деактивированных шаблонов не найден.");
return false;
}
// Вычисляем имя шаблона с новым Job
var expectedName = await GetNormalizedTemplateNameAsync(unusedJob, template.UnitId);
var updateRequest = new TemplateUpdaterMq
{
TemplateId = template.Id,
JobId = jobId,
JobId = unusedJob.Id,
UnitId = template.UnitId,
Name = GetTemplateNameForUnused(template.Name),
Name = GetTemplateNameForUnused(expectedName),
IsActiveTemplate = DefaultUnusedTemplateState,
IsActiveSchedule = DefaultUnusedScheduleState,
LastRun = template.LastRun,
NextRun = template.NextRun,
Index = template.Index,
Index = null,
StatusTypeId = TemplateStatusTypeEnum.Unused,
Initiator = initiator,
UnitsInTemplate = new List<Guid>()
@@ -717,7 +741,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
var templateForShortcodes = new TemplateForShortcodes
{
Id = Guid.Empty, // Не используется в подстановке, но нужен для структуры
Index = index,
Index = index+1,
JobId = targetJob.Id,
UnitId = unitId,
Job = new JobForShortcodes

View File

@@ -69,6 +69,16 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
{
logger.LogDebug("Начало синхронизации шаблонов для JobId {JobId}", jobId);
// Загружаем заранее подготовленный Job (например, для неиспользуемых шаблонов)
var unusedJobId = Guid.Parse("8f85a91c-a223-4686-bb69-1f0ee73624f2");
var unusedJob = await jobService.Get()
.AsNoTracking()
.Include(j => j.Tnk)
.Include(j => j.Group)
.ThenInclude(g => g.GroupType)
.FirstOrDefaultAsync(j => j.Id == unusedJobId);
var expectedUnitIds = await GetExpectedUnitIdsAsync(jobId) ?? new HashSet<Guid>();
logger.LogDebug("JobId {JobId}: найдено {Count} UnitId по фильтрам.", jobId, expectedUnitIds.Count);
@@ -106,7 +116,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
foreach (var template in existingTemplates)
{
await DeactivateTemplateAsync(template, jobId, initiator);
await DeactivateTemplateAsync(template, jobId, unusedJob, initiator);
}
}
else
@@ -125,14 +135,14 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
foreach (var template in templatesToDeactivate)
{
await DeactivateTemplateAsync(template, jobId, initiator);
await DeactivateTemplateAsync(template, jobId, unusedJob, initiator);
}
// Перечитываем шаблоны после деактивации
existingTemplates = await templateService.Get()
.AsNoTracking()
.Include(t=>t.Job)
.ThenInclude(t=>t.Tnk)
.Include(t => t.Job)
.ThenInclude(t => t.Tnk)
.Where(t => t.JobId == jobId)
.ToListAsync();
@@ -202,6 +212,16 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
{
logger.LogDebug("Начало обновления шаблонов для JobId {JobId}", jobId);
// Загружаем заранее подготовленный Job (например, для неиспользуемых шаблонов)
var unusedJobId = Guid.Parse("8f85a91c-a223-4686-bb69-1f0ee73624f2");
var unusedJob = await jobService.Get()
.AsNoTracking()
.Include(j => j.Tnk)
.Include(j => j.Group)
.ThenInclude(g => g.GroupType)
.FirstOrDefaultAsync(j => j.Id == unusedJobId);
var existingTemplates = await templateService.Get()
.AsNoTracking()
.Where(t => t.JobId == jobId)
@@ -237,7 +257,18 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
targetStatus = TemplateStatusTypeEnum.Unused;
targetIsActiveTemplate = DefaultUnusedTemplateState;
targetIsActiveSchedule = DefaultUnusedScheduleState;
expectedName = GetTemplateNameForUnused(expectedName);
// ✅ Вычисляем имя шаблона с новым unusedJob
if (unusedJob != null)
{
expectedName = await GetNormalizedTemplateNameAsync(unusedJob, template.UnitId);
}
else
{
logger.LogError("Job для деактивированных шаблонов не найден.");
expectedName = GetTemplateNameForUnused(template.Name);
}
logger.LogInformation("Шаблон {TemplateId} (UnitId {UnitId}) → деактивация.", template.Id, template.UnitId);
}
@@ -270,7 +301,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
var updateRequest = new TemplateUpdaterMq
{
TemplateId = template.Id,
JobId = jobId,
JobId = unitStillInFilter ? jobId : unusedJob?.Id ?? jobId, // ✅ Используем unusedJob.Id, если деактивация
UnitId = template.UnitId,
Name = expectedName,
IsActiveTemplate = targetIsActiveTemplate,
@@ -293,6 +324,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
private async Task<bool> DeactivateTemplateAsync(
Template template,
Guid jobId,
Job unusedJob,
HistoryInitiator initiator)
{
if (template.StatusTypeId == TemplateStatusTypeEnum.Updating)
@@ -316,12 +348,21 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
return false;
}
if (unusedJob == null)
{
logger.LogError("Job для деактивированных шаблонов не найден.");
return false;
}
// ✅ Вычисляем имя шаблона с новым Job
var expectedName = await GetNormalizedTemplateNameAsync(unusedJob, template.UnitId);
var updateRequest = new TemplateUpdaterMq
{
TemplateId = template.Id,
JobId = jobId,
JobId = unusedJob.Id, // ✅ Используем unusedJob.Id
UnitId = template.UnitId,
Name = GetTemplateNameForUnused(template.Name),
Name = GetTemplateNameForUnused(expectedName), // ✅ Используем новое имя
IsActiveTemplate = DefaultUnusedTemplateState,
IsActiveSchedule = DefaultUnusedScheduleState,
LastRun = template.LastRun,
@@ -434,7 +475,7 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
{
return await jobService.Get()
.AsNoTracking()
.Include(j=>j.Tnk)
.Include(j => j.Tnk)
.Include(j => j.Group)
.ThenInclude(j => j.GroupType)
.Include(j => j.AutoControl)
@@ -453,8 +494,8 @@ internal class SimpleTemplateSynchronizer : ITemplateSynchronizer
var templateForShortcodes = new TemplateForShortcodes
{
Id = Guid.Empty, // шаблон ещё не создан
Index = null,
JobId = job.Id,
Index = null,
JobId = job.Id,
UnitId = unitId,
Job = new JobForShortcodes
{

View File

@@ -34,7 +34,6 @@ internal class TemplateReuser : ITemplateReuser
try
{
var unusedCandidates = await templateService.Get()
.AsNoTracking()
.Where(t => t.StatusTypeId == TemplateStatusTypeEnum.Unused)
.OrderBy(t => t.DateModified ?? t.DateCreated)
.Take(UnusedCandidateBatchSize)