fix(templateMatcher, templateUpdater): исправлена валидация и добавлена проверка имени шаблона
This commit is contained in:
@@ -93,6 +93,8 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
.Include(jg => jg.Jobs)
|
.Include(jg => jg.Jobs)
|
||||||
.ThenInclude(j => j.UnitFilters)
|
.ThenInclude(j => j.UnitFilters)
|
||||||
.ThenInclude(uf => uf.RelationshipFilters)
|
.ThenInclude(uf => uf.RelationshipFilters)
|
||||||
|
.Include(jg => jg.Jobs)
|
||||||
|
.ThenInclude(jg => jg.Tnk)
|
||||||
.FirstOrDefaultAsync(jg => jg.Id == jobGroupId);
|
.FirstOrDefaultAsync(jg => jg.Id == jobGroupId);
|
||||||
|
|
||||||
if (jobGroup == null || jobGroup.Jobs == null || !jobGroup.Jobs.Any())
|
if (jobGroup == null || jobGroup.Jobs == null || !jobGroup.Jobs.Any())
|
||||||
@@ -177,7 +179,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- НОВАЯ ЛОГИКА: Дополнительная фильтрация по "РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК" ---
|
// --- Дополнительная фильтрация по "РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК" ---
|
||||||
var unitIdsWithValidWorkGroupFieldSet = expectedUnitsWithGroupingField
|
var unitIdsWithValidWorkGroupFieldSet = expectedUnitsWithGroupingField
|
||||||
.Where(u => unitIdsWithValidGroupingFieldSet.Contains(u.Id) && // Убедимся, что юнит уже прошёл фильтр по GroupingFieldId
|
.Where(u => unitIdsWithValidGroupingFieldSet.Contains(u.Id) && // Убедимся, что юнит уже прошёл фильтр по GroupingFieldId
|
||||||
u.UnitValues.Any(uv =>
|
u.UnitValues.Any(uv =>
|
||||||
@@ -274,7 +276,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
|
|
||||||
logger.LogDebug("После применения RelationshipFilters осталось {Count} связей UnitInUnit.", filteredUnitInUnitLinks.Count);
|
logger.LogDebug("После применения RelationshipFilters осталось {Count} связей UnitInUnit.", filteredUnitInUnitLinks.Count);
|
||||||
|
|
||||||
// --- НОВАЯ ЛОГИКА: Сгруппировать юниты из unitIdsWithValidGroupingFieldSet по связанному юниту ---
|
// --- Сгруппировать юниты из unitIdsWithValidGroupingFieldSet по связанному юниту ---
|
||||||
var groupedRelationships = new Dictionary<Guid, List<Guid>>();
|
var groupedRelationships = new Dictionary<Guid, List<Guid>>();
|
||||||
foreach (var link in filteredUnitInUnitLinks)
|
foreach (var link in filteredUnitInUnitLinks)
|
||||||
{
|
{
|
||||||
@@ -301,7 +303,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
|
|
||||||
logger.LogDebug("Сформировано {Count} групп по связанным юнитам до разрешения конфликтов.", groupedRelationships.Count);
|
logger.LogDebug("Сформировано {Count} групп по связанным юнитам до разрешения конфликтов.", groupedRelationships.Count);
|
||||||
|
|
||||||
// --- НОВАЯ ЛОГИКА: Разрешение конфликта - один юнит из unitIdsWithValidGroupingFieldSet только в одном списке значений ---
|
// --- Разрешение конфликта - один юнит из unitIdsWithValidGroupingFieldSet только в одном списке значений ---
|
||||||
var unitToKeys = new Dictionary<Guid, List<Guid>>(); // Карта: юнит из списка -> список ключей, где он встречается
|
var unitToKeys = new Dictionary<Guid, List<Guid>>(); // Карта: юнит из списка -> список ключей, где он встречается
|
||||||
|
|
||||||
foreach (var kvp in groupedRelationships)
|
foreach (var kvp in groupedRelationships)
|
||||||
@@ -392,30 +394,10 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
|
|
||||||
logger.LogDebug("Обработка подгруппы {Index} связанного юнита {RegionalUnitId}, размер {Size}.", i, relationshipUnitId, subGroupSize);
|
logger.LogDebug("Обработка подгруппы {Index} связанного юнита {RegionalUnitId}, размер {Size}.", i, relationshipUnitId, subGroupSize);
|
||||||
|
|
||||||
// 8. Найти подходящий Job для подгруппы (логика без изменений)
|
// Выбираем targetJob для подгруппы
|
||||||
Job? targetJob = jobsInGroup
|
Job? targetJob = SelectTargetJob(jobsInGroup, subGroupSize, maxJob);
|
||||||
.Where(j => j.MaxValueRelationships.HasValue && j.MaxValueRelationships.Value == subGroupSize)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (targetJob == null)
|
// 9. Загрузить существующие шаблоны для targetJob, связанные с relationshipUnitId
|
||||||
{
|
|
||||||
targetJob = jobsInGroup
|
|
||||||
.Where(j => j.MaxValueRelationships.HasValue && j.MaxValueRelationships.Value >= subGroupSize)
|
|
||||||
.OrderBy(j => j.MaxValueRelationships!.Value)
|
|
||||||
.FirstOrDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetJob == null)
|
|
||||||
{
|
|
||||||
targetJob = maxJob; // maxJob уже проверен на null ранее
|
|
||||||
logger.LogDebug("Для подгруппы {Index} связанного юнита {RegionalUnitId} не найден подходящий Job, используем maxJob {MaxJobId}.", i, relationshipUnitId, maxJob.Id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.LogDebug("Для подгруппы {Index} связанного юнита {RegionalUnitId} выбран Job {TargetJobId} с MaxValueRelationships {MaxValue}.", i, relationshipUnitId, targetJob.Id, targetJob.MaxValueRelationships);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 9. Загрузить существующие шаблоны для targetJob, связанные с regionalUnitId
|
|
||||||
var existingTemplatesForRelationship = await templateService.Get()
|
var existingTemplatesForRelationship = await templateService.Get()
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Include(t => t.UnitsInTemplate)
|
.Include(t => t.UnitsInTemplate)
|
||||||
@@ -432,12 +414,53 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
|
|
||||||
if (existingUnitIds.SetEquals(newUnitIds))
|
if (existingUnitIds.SetEquals(newUnitIds))
|
||||||
{
|
{
|
||||||
logger.LogDebug("Шаблон {TemplateId} (Job {JobId}, Regional {RegionalId}, Index {Index}) актуален.", existingTemplateForSubGroup.Id, targetJob.Id, relationshipUnitId, i);
|
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))
|
||||||
|
{
|
||||||
|
logger.LogDebug("Шаблон {TemplateId} требует обновления имени: старое = '{OldName}', новое = '{NewName}'", existingTemplateForSubGroup.Id, existingTemplateForSubGroup.Name, expectedName);
|
||||||
|
|
||||||
|
var nextRun = await GetNextRunAsync(targetJob, existingTemplateForSubGroup.NextRun);
|
||||||
|
|
||||||
|
var updateRequest = new TemplateUpdaterMq
|
||||||
|
{
|
||||||
|
TemplateId = existingTemplateForSubGroup.Id,
|
||||||
|
JobId = targetJob.Id,
|
||||||
|
UnitId = relationshipUnitId,
|
||||||
|
Name = expectedName,
|
||||||
|
IsActiveTemplate = existingTemplateForSubGroup.IsActiveTemplate,
|
||||||
|
IsActiveSchedule = existingTemplateForSubGroup.IsActiveSchedule,
|
||||||
|
LastRun = existingTemplateForSubGroup.LastRun,
|
||||||
|
NextRun = nextRun,
|
||||||
|
Index = i,
|
||||||
|
StatusTypeId = TemplateStatusTypeEnum.Used,
|
||||||
|
Initiator = initiator,
|
||||||
|
UnitsInTemplate = subGroup
|
||||||
|
};
|
||||||
|
|
||||||
|
await SendTemplateUpdateMessage(updateRequest);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogDebug("Шаблон {TemplateId} полностью актуален (юниты и имя).", existingTemplateForSubGroup.Id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.LogDebug("Шаблон {TemplateId} (Job {JobId}, Regional {RegionalId}, Index {Index}) требует обновления юнитов.", existingTemplateForSubGroup.Id, targetJob.Id, relationshipUnitId, i);
|
logger.LogDebug("Шаблон {TemplateId} (Job {JobId}, Regional {RegionalId}, Index {Index}) требует обновления юнитов.", existingTemplateForSubGroup.Id, targetJob.Id, relationshipUnitId, i);
|
||||||
await UpdateTemplateUnitsAsync(existingTemplateForSubGroup, subGroup, targetJob, initiator);
|
|
||||||
|
// Пересчитать targetJob для нового размера subGroup
|
||||||
|
var newTargetJob = SelectTargetJob(jobsInGroup, subGroupSize, maxJob);
|
||||||
|
|
||||||
|
// Если Job изменился — нужно обновить шаблон с новым JobId
|
||||||
|
if (newTargetJob.Id != existingTemplateForSubGroup.JobId)
|
||||||
|
{
|
||||||
|
logger.LogDebug("Job для шаблона {TemplateId} изменился: старый {OldJobId}, новый {NewJobId}.", existingTemplateForSubGroup.Id, existingTemplateForSubGroup.JobId, newTargetJob.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
await UpdateTemplateUnitsAsync(existingTemplateForSubGroup, subGroup, newTargetJob, initiator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -495,19 +518,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
var subGroup = childUnitGroups[i];
|
var subGroup = childUnitGroups[i];
|
||||||
var subGroupSize = subGroup.Count;
|
var subGroupSize = subGroup.Count;
|
||||||
|
|
||||||
Job? targetJobForExpectedKey = jobsInGroup
|
Job? targetJobForExpectedKey = SelectTargetJob(jobsInGroup, subGroupSize, maxJob);
|
||||||
.Where(j => j.MaxValueRelationships.HasValue && j.MaxValueRelationships.Value == subGroupSize)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (targetJobForExpectedKey == null)
|
|
||||||
{
|
|
||||||
targetJobForExpectedKey = jobsInGroup
|
|
||||||
.Where(j => j.MaxValueRelationships.HasValue && j.MaxValueRelationships.Value >= subGroupSize)
|
|
||||||
.OrderBy(j => j.MaxValueRelationships!.Value)
|
|
||||||
.FirstOrDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetJobForExpectedKey == null) targetJobForExpectedKey = maxJob; // maxJob уже проверен на null
|
|
||||||
|
|
||||||
expectedTemplateKeys.Add((targetJobForExpectedKey.Id, regionalUnitId, i));
|
expectedTemplateKeys.Add((targetJobForExpectedKey.Id, regionalUnitId, i));
|
||||||
}
|
}
|
||||||
@@ -542,6 +553,41 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Вспомогательные методы ---
|
// --- Вспомогательные методы ---
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Выбирает Job, соответствующий размеру подгруппы
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jobsInGroup">Список Job'ов в группе</param>
|
||||||
|
/// <param name="subGroupSize">Размер подгруппы</param>
|
||||||
|
/// <param name="maxJob">Job с максимальным MaxValueRelationships</param>
|
||||||
|
/// <returns>Найденный Job или maxJob, если не найден подходящий</returns>
|
||||||
|
private Job SelectTargetJob(List<Job> jobsInGroup, int subGroupSize, Job maxJob)
|
||||||
|
{
|
||||||
|
Job? targetJob = jobsInGroup
|
||||||
|
.Where(j => j.MaxValueRelationships.HasValue && j.MaxValueRelationships.Value == subGroupSize)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (targetJob == null)
|
||||||
|
{
|
||||||
|
targetJob = jobsInGroup
|
||||||
|
.Where(j => j.MaxValueRelationships.HasValue && j.MaxValueRelationships.Value >= subGroupSize)
|
||||||
|
.OrderBy(j => j.MaxValueRelationships!.Value)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetJob == null)
|
||||||
|
{
|
||||||
|
targetJob = maxJob; // maxJob уже проверен на null ранее
|
||||||
|
logger.LogDebug("Для подгруппы размером {Size} не найден подходящий Job, используем maxJob {MaxJobId}.", subGroupSize, maxJob.Id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogDebug("Для подгруппы размером {Size} выбран Job {TargetJobId} с MaxValueRelationships {MaxValue}.", subGroupSize, targetJob.Id, targetJob.MaxValueRelationships);
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetJob;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UpdateTemplateUnitsAsync(Template template, List<Guid> newUnitIds, Job targetJob, HistoryInitiator initiator)
|
private async Task UpdateTemplateUnitsAsync(Template template, List<Guid> newUnitIds, Job targetJob, HistoryInitiator initiator)
|
||||||
{
|
{
|
||||||
template.StatusTypeId = TemplateStatusTypeEnum.Updating;
|
template.StatusTypeId = TemplateStatusTypeEnum.Updating;
|
||||||
@@ -659,7 +705,6 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
return templateName + "_" + DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
return templateName + "_" + DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Изменённая сигнатура: добавлен templateUnitIds
|
|
||||||
private async Task<string> GetNormalizedTemplateNameAsync(Job targetJob, Guid unitId, int? index = null, List<Guid>? templateUnitIds = null)
|
private async Task<string> GetNormalizedTemplateNameAsync(Job targetJob, Guid unitId, int? index = null, List<Guid>? templateUnitIds = null)
|
||||||
{
|
{
|
||||||
// Подготовка объекта TemplateForShortcodes для передачи в ShortcodesService
|
// Подготовка объекта TemplateForShortcodes для передачи в ShortcodesService
|
||||||
|
|||||||
@@ -180,7 +180,10 @@ namespace PARR.TemplateUpdater.Services
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var existTemplateName = await templateService.Get().AsNoTracking().AnyAsync(t => t.Id != query.TemplateId && t.Name.ToUpper() == query.Name.Trim().ToUpper());
|
var existTemplateName = await templateService.Get().AsNoTracking().AnyAsync(t =>
|
||||||
|
t.Id != query.TemplateId
|
||||||
|
&& t.Name.ToUpper() == query.Name.Trim().ToUpper()
|
||||||
|
&& t.Index == query.Index);
|
||||||
if (existTemplateName)
|
if (existTemplateName)
|
||||||
{
|
{
|
||||||
logger.LogError($"Сообщение не валидно. Имя шаблона не уникально: {query.Name}");
|
logger.LogError($"Сообщение не валидно. Имя шаблона не уникально: {query.Name}");
|
||||||
|
|||||||
Reference in New Issue
Block a user