fix(templateMatcher): добавлена деактивация шаблонов если после устранения конфликтов связанный эк остался без ВРТ.
This commit is contained in:
@@ -22,7 +22,8 @@ namespace PARR.TemplateMatcher.Services.Implementations;
|
|||||||
internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
private readonly Guid targetUnitId = Guid.Parse("87fc4c36-1ea8-4163-983f-1605fee1de99");
|
private readonly string[] debugWatchUnitNames = { "МОНИТОР-АСОУП-3-ВСИБ", "ЦЕКОН-ВСИБ", "ВРТ-ASOUP2-ВСИБ" };
|
||||||
|
private Dictionary<string, Guid> debugWatchUnitIds = new Dictionary<string, Guid>();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private const bool DefaultUsedTemplateState = false;
|
private const bool DefaultUsedTemplateState = false;
|
||||||
@@ -64,7 +65,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
ITemplateNameNormalizer templateNameNormalizer,
|
ITemplateNameNormalizer templateNameNormalizer,
|
||||||
ITemplateUpdaterMqSender templateUpdaterMqSender,
|
ITemplateUpdaterMqSender templateUpdaterMqSender,
|
||||||
IMatchingStatusService matchingStatusService
|
IMatchingStatusService matchingStatusService
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.unitFilterService = unitFilterService;
|
this.unitFilterService = unitFilterService;
|
||||||
@@ -85,6 +86,36 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
this.matchingStatusService = matchingStatusService;
|
this.matchingStatusService = matchingStatusService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
private async Task InitializeDebugUnitsAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var watchUnits = await unitService.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(u => debugWatchUnitNames.Contains(u.Name))
|
||||||
|
.ToDictionaryAsync(u => u.Name, u => u.Id);
|
||||||
|
|
||||||
|
foreach (var name in debugWatchUnitNames)
|
||||||
|
{
|
||||||
|
if (watchUnits.TryGetValue(name, out var id))
|
||||||
|
{
|
||||||
|
debugWatchUnitIds[name] = id;
|
||||||
|
logger.LogWarning("[DEBUG] Найден юнит для отслеживания '{UnitName}' с ID: {UnitId}", name, id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogWarning("[DEBUG] Юнит для отслеживания '{UnitName}' не найден в БД", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "[DEBUG] Ошибка при поиске юнитов для отслеживания");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public async Task SyncTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator)
|
public async Task SyncTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator)
|
||||||
{
|
{
|
||||||
logger.LogWarning("GroupedTemplateSynchronizer: SyncTemplatesForJob вызван для JobId {JobId}. Это не поддерживаемая операция.", jobId);
|
logger.LogWarning("GroupedTemplateSynchronizer: SyncTemplatesForJob вызван для JobId {JobId}. Это не поддерживаемая операция.", jobId);
|
||||||
@@ -92,6 +123,10 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
|
|
||||||
public async Task SyncTemplatesForJobGroupAsync(Guid jobGroupId, HistoryInitiator initiator)
|
public async Task SyncTemplatesForJobGroupAsync(Guid jobGroupId, HistoryInitiator initiator)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
await InitializeDebugUnitsAsync();
|
||||||
|
#endif
|
||||||
|
|
||||||
logger.LogDebug("Начало синхронизации шаблонов для JobGroup {JobGroupId}", jobGroupId);
|
logger.LogDebug("Начало синхронизации шаблонов для JobGroup {JobGroupId}", jobGroupId);
|
||||||
|
|
||||||
// === Проверка: уже запущена? ===
|
// === Проверка: уже запущена? ===
|
||||||
@@ -149,11 +184,9 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var workGroupFieldId = workGroupField.Id;
|
var workGroupFieldId = workGroupField.Id;
|
||||||
|
|
||||||
var regionalGroupValueIds = regionalEkPtkGroupService.Get()
|
var regionalGroupValueIds = regionalEkPtkGroupService.Get()
|
||||||
.Select(g => g.FieldValueId)
|
.Select(g => g.FieldValueId)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
logger.LogDebug("Найдено {Count} значений из UnitRegionalEkPtkGroup для проверки поля 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК'.", regionalGroupValueIds.Count);
|
logger.LogDebug("Найдено {Count} значений из UnitRegionalEkPtkGroup для проверки поля 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК'.", regionalGroupValueIds.Count);
|
||||||
|
|
||||||
// 2. Найти Job с максимальным MaxValueRelationships
|
// 2. Найти Job с максимальным MaxValueRelationships
|
||||||
@@ -186,13 +219,21 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if (expectedUnitIds.Contains(targetUnitId))
|
var debugMonitorUnitId = debugWatchUnitIds.GetValueOrDefault("МОНИТОР-АСОУП-3-ВСИБ", Guid.Empty);
|
||||||
|
var debugCekonUnitId = debugWatchUnitIds.GetValueOrDefault("ЦЕКОН-ВСИБ", Guid.Empty);
|
||||||
|
var debugVrtUnitId = debugWatchUnitIds.GetValueOrDefault("ВРТ-ASOUP2-ВСИБ", Guid.Empty);
|
||||||
|
|
||||||
|
if (debugMonitorUnitId != Guid.Empty && expectedUnitIds.Contains(debugMonitorUnitId))
|
||||||
{
|
{
|
||||||
logger.LogDebug("Юнит {TargetUnitId} найден в expectedUnitIds.", targetUnitId);
|
logger.LogDebug("Юнит МОНИТОР-АСОУП-3-ВСИБ найден в expectedUnitIds.");
|
||||||
}
|
}
|
||||||
else
|
if (debugCekonUnitId != Guid.Empty && expectedUnitIds.Contains(debugCekonUnitId))
|
||||||
{
|
{
|
||||||
logger.LogDebug("Юнит {TargetUnitId} НЕ найден в expectedUnitIds.", targetUnitId);
|
logger.LogDebug("Юнит ЦЕКОН-ВСИБ найден в expectedUnitIds.");
|
||||||
|
}
|
||||||
|
if (debugVrtUnitId != Guid.Empty && expectedUnitIds.Contains(debugVrtUnitId))
|
||||||
|
{
|
||||||
|
logger.LogDebug("Юнит ВРТ-ASOUP2-ВСИБ найден в expectedUnitIds.");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -205,7 +246,6 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
}
|
}
|
||||||
|
|
||||||
var groupingFieldId = jobGroup.GroupingUnitFieldId.Value;
|
var groupingFieldId = jobGroup.GroupingUnitFieldId.Value;
|
||||||
|
|
||||||
var expectedUnitsWithGroupingField = await unitService.Get()
|
var expectedUnitsWithGroupingField = await unitService.Get()
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Include(u => u.UnitValues)
|
.Include(u => u.UnitValues)
|
||||||
@@ -218,17 +258,6 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
.Select(u => u.Id)
|
.Select(u => u.Id)
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
if (unitIdsWithValidGroupingFieldSet.Contains(targetUnitId))
|
|
||||||
{
|
|
||||||
logger.LogDebug("Юнит {TargetUnitId} найден в unitIdsWithValidGroupingFieldSet.", targetUnitId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.LogDebug("Юнит {TargetUnitId} НЕ найден в unitIdsWithValidGroupingFieldSet.", targetUnitId);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
logger.LogDebug("После фильтрации по GroupingUnitFieldId осталось {Count} юнитов.", unitIdsWithValidGroupingFieldSet.Count);
|
logger.LogDebug("После фильтрации по GroupingUnitFieldId осталось {Count} юнитов.", unitIdsWithValidGroupingFieldSet.Count);
|
||||||
|
|
||||||
if (!unitIdsWithValidGroupingFieldSet.Any())
|
if (!unitIdsWithValidGroupingFieldSet.Any())
|
||||||
@@ -246,17 +275,6 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
regionalGroupValueIds
|
regionalGroupValueIds
|
||||||
);
|
);
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
if (unitIdsWithValidWorkGroupFieldSet.Contains(targetUnitId))
|
|
||||||
{
|
|
||||||
logger.LogDebug("Юнит {TargetUnitId} найден в unitIdsWithValidWorkGroupFieldSet.", targetUnitId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.LogDebug("Юнит {TargetUnitId} НЕ найден в unitIdsWithValidWorkGroupFieldSet.", targetUnitId);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
logger.LogDebug("После фильтрации по 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК' осталось {Count} юнитов.", unitIdsWithValidWorkGroupFieldSet.Count);
|
logger.LogDebug("После фильтрации по 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК' осталось {Count} юнитов.", unitIdsWithValidWorkGroupFieldSet.Count);
|
||||||
|
|
||||||
if (!unitIdsWithValidWorkGroupFieldSet.Any())
|
if (!unitIdsWithValidWorkGroupFieldSet.Any())
|
||||||
@@ -305,11 +323,11 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
.ToDictionary(g => g.Key, g => g.ToList());
|
.ToDictionary(g => g.Key, g => g.ToList());
|
||||||
|
|
||||||
logger.LogDebug("Применение {Count} RelationshipFilters к найденным связям.", relationshipFilters.Count);
|
logger.LogDebug("Применение {Count} RelationshipFilters к найденным связям.", relationshipFilters.Count);
|
||||||
|
|
||||||
var filteredUnitInUnitLinks = new List<UnitInUnit>();
|
var filteredUnitInUnitLinks = new List<UnitInUnit>();
|
||||||
foreach (var link in potentialUnitInUnitLinks)
|
foreach (var link in potentialUnitInUnitLinks)
|
||||||
{
|
{
|
||||||
bool linkMatchesAllFilters = true;
|
bool linkMatchesAllFilters = true;
|
||||||
|
|
||||||
foreach (var rf in relationshipFilters)
|
foreach (var rf in relationshipFilters)
|
||||||
{
|
{
|
||||||
var valuesToCheck = rf.IsParent ? parentValuesMap.GetValueOrDefault(link.ParentUnitId, new List<UnitInValue>()) : childValuesMap.GetValueOrDefault(link.ChildUnitId, new List<UnitInValue>());
|
var valuesToCheck = rf.IsParent ? parentValuesMap.GetValueOrDefault(link.ParentUnitId, new List<UnitInValue>()) : childValuesMap.GetValueOrDefault(link.ChildUnitId, new List<UnitInValue>());
|
||||||
@@ -366,7 +384,13 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
|
|
||||||
logger.LogDebug("Сформировано {Count} групп по связанным юнитам до разрешения конфликтов.", groupedRelationships.Count);
|
logger.LogDebug("Сформировано {Count} групп по связанным юнитам до разрешения конфликтов.", groupedRelationships.Count);
|
||||||
|
|
||||||
// --- Разрешение конфликта ---
|
// === DEBUG: СПЕЦИАЛЬНАЯ ПРОВЕРКА ДУБЛИРОВАНИЯ ===
|
||||||
|
#if DEBUG
|
||||||
|
await LogGroupingDebugInfo(jobGroup, groupedRelationships);
|
||||||
|
#endif
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// --- Разрешение конфликта с детерминированной сортировкой ---
|
||||||
var unitToKeys = new Dictionary<Guid, List<Guid>>();
|
var unitToKeys = new Dictionary<Guid, List<Guid>>();
|
||||||
foreach (var kvp in groupedRelationships)
|
foreach (var kvp in groupedRelationships)
|
||||||
{
|
{
|
||||||
@@ -382,163 +406,66 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var conflictedUnits = unitToKeys.Where(kvp => kvp.Value.Count > 1).ToList();
|
// Получаем имена всех конфликтующих ключей
|
||||||
foreach (var conflictedUnitEntry in conflictedUnits)
|
var conflictKeys = unitToKeys
|
||||||
|
.Where(kvp => kvp.Value.Count > 1)
|
||||||
|
.SelectMany(kvp => kvp.Value)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var unitNamesMap = await unitService.Get()
|
||||||
|
.Where(u => conflictKeys.Contains(u.Id))
|
||||||
|
.ToDictionaryAsync(u => u.Id, u => u.Name ?? string.Empty);
|
||||||
|
|
||||||
|
foreach (var conflictedUnitEntry in unitToKeys.Where(kvp => kvp.Value.Count > 1))
|
||||||
{
|
{
|
||||||
var unitId = conflictedUnitEntry.Key;
|
var unitId = conflictedUnitEntry.Key;
|
||||||
var keysForUnit = conflictedUnitEntry.Value;
|
var keysForUnit = conflictedUnitEntry.Value;
|
||||||
|
|
||||||
Guid bestKey = keysForUnit[0];
|
// Сортируем по: 1) кол-во юнитов (убывание), 2) имя ключа (возрастание)
|
||||||
int maxCount = groupedRelationships[bestKey].Count;
|
var sortedKeys = keysForUnit
|
||||||
|
.Select(key => (
|
||||||
|
key,
|
||||||
|
count: groupedRelationships[key].Count,
|
||||||
|
name: unitNamesMap.GetValueOrDefault(key, "")
|
||||||
|
))
|
||||||
|
.OrderByDescending(x => x.count)
|
||||||
|
.ThenBy(x => x.name)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
for (int i = 1; i < keysForUnit.Count; i++)
|
var bestKey = sortedKeys.First().key;
|
||||||
{
|
|
||||||
var currentKey = keysForUnit[i];
|
|
||||||
var currentCount = groupedRelationships[currentKey].Count;
|
|
||||||
if (currentCount > maxCount)
|
|
||||||
{
|
|
||||||
bestKey = currentKey;
|
|
||||||
maxCount = currentCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Удаляем юнит из ВСЕХ групп, кроме лучшей
|
||||||
foreach (var key in keysForUnit)
|
foreach (var key in keysForUnit)
|
||||||
{
|
{
|
||||||
if (key != bestKey)
|
if (key != bestKey && groupedRelationships.ContainsKey(key))
|
||||||
{
|
{
|
||||||
groupedRelationships[key].Remove(unitId);
|
groupedRelationships[key].Remove(unitId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var keysToRemove = groupedRelationships.Where(kvp => kvp.Value.Count == 0).Select(kvp => kvp.Key).ToList();
|
// Удаляем пустые группы
|
||||||
foreach (var key in keysToRemove)
|
var emptyKeys = groupedRelationships
|
||||||
|
.Where(kvp => !kvp.Value.Any())
|
||||||
|
.Select(kvp => kvp.Key)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var key in emptyKeys)
|
||||||
{
|
{
|
||||||
groupedRelationships.Remove(key);
|
groupedRelationships.Remove(key);
|
||||||
}
|
}
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
logger.LogDebug("Сформировано {Count} групп по связанным юнитам после разрешения конфликтов.", groupedRelationships.Count);
|
logger.LogDebug("Сформировано {Count} групп по связанным юнитам после разрешения конфликтов.", groupedRelationships.Count);
|
||||||
|
|
||||||
// === Подсчёт операций ===
|
// === DEBUG: ЛОГИРОВАНИЕ ПОСЛЕ РАЗРЕШЕНИЯ КОНФЛИКТОВ ===
|
||||||
int toCreate = 0;
|
#if DEBUG
|
||||||
int toUpdate = 0;
|
await LogGroupingDebugInfo(jobGroup, groupedRelationships, true);
|
||||||
int toDeactivate = 0;
|
#endif
|
||||||
|
// ======================================================
|
||||||
foreach (var kvp in groupedRelationships)
|
|
||||||
{
|
|
||||||
var childUnitIds = kvp.Value;
|
|
||||||
if (childUnitIds.Count == 0) continue;
|
|
||||||
|
|
||||||
var childUnitNameMap = await unitService.Get()
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(u => childUnitIds.Contains(u.Id))
|
|
||||||
.ToDictionaryAsync(u => u.Id, u => u.Name);
|
|
||||||
|
|
||||||
var sortedChildUnitIds = childUnitIds
|
|
||||||
.OrderBy(id => childUnitNameMap.GetValueOrDefault(id, id.ToString()))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
int maxValueForSplitting = maxJob.MaxValueRelationships!.Value;
|
|
||||||
var childUnitGroups = sortedChildUnitIds
|
|
||||||
.Select((id, index) => new { id, groupIndex = index / maxValueForSplitting })
|
|
||||||
.GroupBy(x => x.groupIndex)
|
|
||||||
.Select(g => g.Select(x => x.id).ToList())
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
for (int i = 0; i < childUnitGroups.Count; i++)
|
|
||||||
{
|
|
||||||
var subGroup = childUnitGroups[i];
|
|
||||||
var subGroupSize = subGroup.Count;
|
|
||||||
Job? targetJob = SelectTargetJob(jobsInGroup, subGroupSize, maxJob);
|
|
||||||
|
|
||||||
var existingTemplatesForRelationship = await templateService.Get()
|
|
||||||
.AsNoTracking()
|
|
||||||
.Include(t=>t.Unit)
|
|
||||||
.Include(t=>t.Job)
|
|
||||||
.ThenInclude(t=>t.Group)
|
|
||||||
.ThenInclude(t=>t.GroupType)
|
|
||||||
.Include(t => t.Job)
|
|
||||||
.ThenInclude(t => t.Tnk)
|
|
||||||
.Include(t => t.UnitsInTemplate)
|
|
||||||
.Where(t => t.JobId == targetJob.Id && t.UnitId == kvp.Key && t.Index == i && t.StatusTypeId == TemplateStatusTypeEnum.Used)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
if (existingTemplatesForRelationship.Any())
|
|
||||||
{
|
|
||||||
var existingTemplate = existingTemplatesForRelationship.First();
|
|
||||||
var existingUnitIds = existingTemplate.UnitsInTemplate.Select(uit => uit.UnitId).ToHashSet();
|
|
||||||
var newUnitIds = subGroup.ToHashSet();
|
|
||||||
|
|
||||||
if (!existingUnitIds.SetEquals(newUnitIds))
|
|
||||||
toUpdate++;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Обновляем UnitsInTemplate для корректной генерации имени
|
|
||||||
existingTemplate.UnitsInTemplate = subGroup.Select(id => new UnitsInTemplate { UnitId = id }).ToList();
|
|
||||||
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(existingTemplate);
|
|
||||||
if (!string.Equals(existingTemplate.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
|
||||||
toUpdate++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
toCreate++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Подсчёт деактивации
|
|
||||||
var expectedTemplateKeys = new HashSet<(Guid JobId, Guid UnitId, int Index)>();
|
|
||||||
foreach (var kvp in groupedRelationships)
|
|
||||||
{
|
|
||||||
var relationshipUnitId = kvp.Key;
|
|
||||||
var childUnitIds = kvp.Value;
|
|
||||||
|
|
||||||
var childUnitNameMapForDeactivate = await unitService.Get()
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(u => childUnitIds.Contains(u.Id))
|
|
||||||
.ToDictionaryAsync(u => u.Id, u => u.Name);
|
|
||||||
|
|
||||||
var sortedChildUnitIdsForDeactivate = childUnitIds
|
|
||||||
.OrderBy(id => childUnitNameMapForDeactivate.GetValueOrDefault(id, id.ToString()))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
int maxValueForSplitting = maxJob.MaxValueRelationships!.Value;
|
|
||||||
var childUnitGroups = sortedChildUnitIdsForDeactivate
|
|
||||||
.Select((id, index) => new { id, groupIndex = index / maxValueForSplitting })
|
|
||||||
.GroupBy(x => x.groupIndex)
|
|
||||||
.Select(g => g.Select(x => x.id).ToList())
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
for (int i = 0; i < childUnitGroups.Count; i++)
|
|
||||||
{
|
|
||||||
var subGroup = childUnitGroups[i];
|
|
||||||
var subGroupSize = subGroup.Count;
|
|
||||||
Job? targetJobForExpectedKey = SelectTargetJob(jobsInGroup, subGroupSize, maxJob);
|
|
||||||
expectedTemplateKeys.Add((targetJobForExpectedKey.Id, relationshipUnitId, i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var allRelationshipUnitIds = groupedRelationships.Keys.ToHashSet();
|
|
||||||
var allJobIdsInGroup = jobsInGroup.Select(j => j.Id).ToHashSet();
|
|
||||||
|
|
||||||
var allExistingTemplatesInGroup = await templateService.Get()
|
|
||||||
.AsNoTracking()
|
|
||||||
.Include(t => t.UnitsInTemplate)
|
|
||||||
.Where(t => allJobIdsInGroup.Contains(t.JobId) && allRelationshipUnitIds.Contains(t.UnitId))
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
foreach (var existingTemplate in allExistingTemplatesInGroup)
|
|
||||||
{
|
|
||||||
var key = (existingTemplate.JobId, existingTemplate.UnitId, existingTemplate.Index ?? -1);
|
|
||||||
if (!expectedTemplateKeys.Contains(key))
|
|
||||||
toDeactivate++;
|
|
||||||
}
|
|
||||||
|
|
||||||
await UpdateMatchingStatusAsync(jobGroupId, $"Осталось: создать={toCreate}, обновить={toUpdate}, деактивировать={toDeactivate}");
|
|
||||||
|
|
||||||
// === Основной цикл обработки ===
|
// === Основной цикл обработки ===
|
||||||
int created = 0, updated = 0, deactivated = 0;
|
|
||||||
foreach (var kvp in groupedRelationships)
|
foreach (var kvp in groupedRelationships)
|
||||||
{
|
{
|
||||||
var relationshipUnitId = kvp.Key;
|
var relationshipUnitId = kvp.Key;
|
||||||
@@ -551,13 +478,11 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(u => debugUnitIds.Contains(u.Id))
|
.Where(u => debugUnitIds.Contains(u.Id))
|
||||||
.ToDictionaryAsync(u => u.Id, u => u.Name);
|
.ToDictionaryAsync(u => u.Id, u => u.Name);
|
||||||
|
|
||||||
var childUnitNamesForDebug = childUnitIds.Select(id => debugUnits.GetValueOrDefault(id, id.ToString())).ToList();
|
var childUnitNamesForDebug = childUnitIds.Select(id => debugUnits.GetValueOrDefault(id, id.ToString())).ToList();
|
||||||
var relationshipUnitName = debugUnits.GetValueOrDefault(relationshipUnitId, relationshipUnitId.ToString());
|
var relationshipUnitName = debugUnits.GetValueOrDefault(relationshipUnitId, relationshipUnitId.ToString());
|
||||||
|
if (debugWatchUnitIds.Values.Contains(relationshipUnitId) || childUnitNamesForDebug.Contains("ВРТ-ASOUP2-ВСИБ"))
|
||||||
if (childUnitNamesForDebug.Contains("ВРТ-AOS-05-ДВС"))
|
|
||||||
{
|
{
|
||||||
logger.LogDebug("Группа с ключом {Key} (название: {Name}) содержит юнит 'ВРТ-AOS-05-ДВС' в childUnitIds: [{ChildUnitNames}]", relationshipUnitId, relationshipUnitName, string.Join(", ", childUnitNamesForDebug));
|
logger.LogDebug("Группа с ключом {Key} (название: {Name}) содержит юниты: [{ChildUnitNames}]", relationshipUnitId, relationshipUnitName, string.Join(", ", childUnitNamesForDebug));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -585,7 +510,6 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
{
|
{
|
||||||
var subGroup = childUnitGroups[i];
|
var subGroup = childUnitGroups[i];
|
||||||
var subGroupSize = subGroup.Count;
|
var subGroupSize = subGroup.Count;
|
||||||
|
|
||||||
logger.LogDebug("Обработка подгруппы {Index} связанного юнита {RelationshipUnitId}, размер {Size}.", i, relationshipUnitId, subGroupSize);
|
logger.LogDebug("Обработка подгруппы {Index} связанного юнита {RelationshipUnitId}, размер {Size}.", i, relationshipUnitId, subGroupSize);
|
||||||
|
|
||||||
Job? targetJob = SelectTargetJob(jobsInGroup, subGroupSize, maxJob);
|
Job? targetJob = SelectTargetJob(jobsInGroup, subGroupSize, maxJob);
|
||||||
@@ -598,29 +522,47 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
.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.UnitsInTemplate)
|
||||||
|
.ThenInclude(uit => uit.Unit)
|
||||||
.Where(t => t.JobId == targetJob.Id && t.UnitId == relationshipUnitId && t.Index == i && t.StatusTypeId == TemplateStatusTypeEnum.Used)
|
.Where(t => t.JobId == targetJob.Id && t.UnitId == relationshipUnitId && t.Index == i && t.StatusTypeId == TemplateStatusTypeEnum.Used)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var existingTemplateForSubGroup = existingTemplatesForRelationship.FirstOrDefault();
|
var existingTemplateForSubGroup = existingTemplatesForRelationship.FirstOrDefault();
|
||||||
|
|
||||||
|
// === DEBUG: ДЕТАЛЬНОЕ СРАВНЕНИЕ UnitsInTemplate ===
|
||||||
|
#if DEBUG
|
||||||
if (existingTemplateForSubGroup != null)
|
if (existingTemplateForSubGroup != null)
|
||||||
{
|
{
|
||||||
var existingUnitIds = existingTemplateForSubGroup.UnitsInTemplate.Select(uit => uit.UnitId).ToHashSet();
|
var relationshipUnit = await unitService.Get().AsNoTracking().FirstOrDefaultAsync(u => u.Id == relationshipUnitId);
|
||||||
var newUnitIds = subGroup.ToHashSet();
|
if (relationshipUnit != null &&
|
||||||
|
(relationshipUnit.Name == "МОНИТОР-АСОУП-3-ВСИБ" || relationshipUnit.Name == "ЦЕКОН-ВСИБ") &&
|
||||||
if (existingUnitIds.SetEquals(newUnitIds))
|
subGroup.Contains(debugWatchUnitIds.GetValueOrDefault("ВРТ-ASOUP2-ВСИБ", Guid.Empty)))
|
||||||
{
|
{
|
||||||
logger.LogDebug("Шаблон {TemplateId} актуален по юнитам.", existingTemplateForSubGroup.Id);
|
await LogUnitComparisonDebugAsync(existingTemplateForSubGroup, subGroup, relationshipUnit.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// ===========================================
|
||||||
|
|
||||||
|
if (existingTemplateForSubGroup != null)
|
||||||
|
{
|
||||||
|
var existingUnitIds = existingTemplateForSubGroup.UnitsInTemplate.Select(uit => uit.UnitId).ToList();
|
||||||
|
var newUnitIds = subGroup.ToList();
|
||||||
|
|
||||||
|
// Сравниваем не только наборы, но и порядок
|
||||||
|
bool areUnitsEqual = existingUnitIds.SequenceEqual(newUnitIds);
|
||||||
|
|
||||||
|
if (areUnitsEqual)
|
||||||
|
{
|
||||||
|
logger.LogDebug("Шаблон {TemplateId} актуален по юнитам и их порядку.", existingTemplateForSubGroup.Id);
|
||||||
// Обновляем UnitsInTemplate для генерации имени
|
// Обновляем UnitsInTemplate для генерации имени
|
||||||
existingTemplateForSubGroup.UnitsInTemplate = subGroup.Select(id => new UnitsInTemplate { UnitId = id }).ToList();
|
existingTemplateForSubGroup.UnitsInTemplate = subGroup.Select(id => new UnitsInTemplate { UnitId = id }).ToList();
|
||||||
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(existingTemplateForSubGroup);
|
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(existingTemplateForSubGroup);
|
||||||
|
|
||||||
if (!string.Equals(existingTemplateForSubGroup.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
if (!string.Equals(existingTemplateForSubGroup.Name, expectedName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
logger.LogDebug("Шаблон {TemplateId} требует обновления имени.", existingTemplateForSubGroup.Id);
|
logger.LogDebug("Шаблон {TemplateId} требует обновления имени.", existingTemplateForSubGroup.Id);
|
||||||
|
|
||||||
var nextRun = await GetNextRunAsync(targetJob, existingTemplateForSubGroup.NextRun);
|
var nextRun = await GetNextRunAsync(targetJob, existingTemplateForSubGroup.NextRun);
|
||||||
|
|
||||||
var updateRequest = new TemplateUpdaterMq
|
var updateRequest = new TemplateUpdaterMq
|
||||||
{
|
{
|
||||||
TemplateId = existingTemplateForSubGroup.Id,
|
TemplateId = existingTemplateForSubGroup.Id,
|
||||||
@@ -636,10 +578,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
Initiator = initiator,
|
Initiator = initiator,
|
||||||
UnitsInTemplate = subGroup
|
UnitsInTemplate = subGroup
|
||||||
};
|
};
|
||||||
|
|
||||||
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
||||||
updated++;
|
|
||||||
await UpdateMatchingStatusAsync(jobGroupId, $"Прогресс: создано={created}, обновлено={updated}, деактивировано={deactivated}. Осталось: создать={toCreate - created}, обновить={toUpdate - updated}, деактивировать={toDeactivate - deactivated}");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -648,23 +587,18 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.LogDebug("Шаблон {TemplateId} требует обновления юнитов.", existingTemplateForSubGroup.Id);
|
logger.LogDebug("Шаблон {TemplateId} требует обновления юнитов или их порядка.", existingTemplateForSubGroup.Id);
|
||||||
|
|
||||||
var newTargetJob = SelectTargetJob(jobsInGroup, subGroupSize, maxJob);
|
var newTargetJob = SelectTargetJob(jobsInGroup, subGroupSize, maxJob);
|
||||||
if (newTargetJob.Id != existingTemplateForSubGroup.JobId)
|
if (newTargetJob.Id != existingTemplateForSubGroup.JobId)
|
||||||
{
|
{
|
||||||
logger.LogDebug("Job для шаблона {TemplateId} изменился.", existingTemplateForSubGroup.Id);
|
logger.LogDebug("Job для шаблона {TemplateId} изменился.", existingTemplateForSubGroup.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
await UpdateTemplateUnitsAsync(existingTemplateForSubGroup, subGroup, newTargetJob, initiator);
|
await UpdateTemplateUnitsAsync(existingTemplateForSubGroup, subGroup, newTargetJob, initiator);
|
||||||
updated++;
|
|
||||||
await UpdateMatchingStatusAsync(jobGroupId, $"Прогресс: создано={created}, обновлено={updated}, деактивировано={deactivated}. Осталось: создать={toCreate - created}, обновить={toUpdate - updated}, деактивировать={toDeactivate - deactivated}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var reusableTemplate = await templateReuser.TryReuseOneUnusedTemplateAsync(targetJob.Id, relationshipUnitId, initiator);
|
var reusableTemplate = await templateReuser.TryReuseOneUnusedTemplateAsync(targetJob.Id, relationshipUnitId, initiator);
|
||||||
|
|
||||||
if (reusableTemplate != null)
|
if (reusableTemplate != null)
|
||||||
{
|
{
|
||||||
logger.LogInformation("Переиспользован шаблон {TemplateId} для Job {JobId}, связанного юнита {RelationshipId}, Index {Index}.", reusableTemplate.Id, targetJob.Id, relationshipUnitId, i);
|
logger.LogInformation("Переиспользован шаблон {TemplateId} для Job {JobId}, связанного юнита {RelationshipId}, Index {Index}.", reusableTemplate.Id, targetJob.Id, relationshipUnitId, i);
|
||||||
@@ -700,30 +634,63 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
};
|
};
|
||||||
|
|
||||||
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
|
||||||
created++;
|
|
||||||
await UpdateMatchingStatusAsync(jobGroupId, $"Прогресс: создано={created}, обновлено={updated}, деактивировано={deactivated}. Осталось: создать={toCreate - created}, обновить={toUpdate - updated}, деактивировать={toDeactivate - deactivated}");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.LogDebug("Создание нового шаблона для Job {JobId}, связанного юнита {RelationshipUnitId}, Index {Index}, с {Count} юнитами.", targetJob.Id, relationshipUnitId, i, subGroup.Count);
|
logger.LogDebug("Создание нового шаблона для Job {JobId}, связанного юнита {RelationshipUnitId}, Index {Index}, с {Count} юнитами.", targetJob.Id, relationshipUnitId, i, subGroup.Count);
|
||||||
await CreateGroupedTemplateAsync(targetJob.Id, relationshipUnitId, subGroup, i, initiator);
|
await CreateGroupedTemplateAsync(targetJob.Id, relationshipUnitId, subGroup, i, initiator);
|
||||||
created++;
|
|
||||||
await UpdateMatchingStatusAsync(jobGroupId, $"Прогресс: создано={created}, обновлено={updated}, деактивировано={deactivated}. Осталось: создать={toCreate - created}, обновить={toUpdate - updated}, деактивировать={toDeactivate - deactivated}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Деактивация ===
|
// === Деактивация ===
|
||||||
|
var expectedTemplateKeys = new HashSet<(Guid JobId, Guid UnitId, int Index)>();
|
||||||
|
foreach (var kvp in groupedRelationships)
|
||||||
|
{
|
||||||
|
var relationshipUnitId = kvp.Key;
|
||||||
|
var childUnitIds = kvp.Value;
|
||||||
|
var childUnitNameMapForDeactivate = await unitService.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(u => childUnitIds.Contains(u.Id))
|
||||||
|
.ToDictionaryAsync(u => u.Id, u => u.Name);
|
||||||
|
var sortedChildUnitIdsForDeactivate = childUnitIds
|
||||||
|
.OrderBy(id => childUnitNameMapForDeactivate.GetValueOrDefault(id, id.ToString()))
|
||||||
|
.ToList();
|
||||||
|
int maxValueForSplitting = maxJob.MaxValueRelationships!.Value;
|
||||||
|
var childUnitGroups = sortedChildUnitIdsForDeactivate
|
||||||
|
.Select((id, index) => new { id, groupIndex = index / maxValueForSplitting })
|
||||||
|
.GroupBy(x => x.groupIndex)
|
||||||
|
.Select(g => g.Select(x => x.id).ToList())
|
||||||
|
.ToList();
|
||||||
|
for (int i = 0; i < childUnitGroups.Count; i++)
|
||||||
|
{
|
||||||
|
var subGroup = childUnitGroups[i];
|
||||||
|
var subGroupSize = subGroup.Count;
|
||||||
|
Job? targetJobForExpectedKey = SelectTargetJob(jobsInGroup, subGroupSize, maxJob);
|
||||||
|
expectedTemplateKeys.Add((targetJobForExpectedKey.Id, relationshipUnitId, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем ВСЕ шаблоны для JobGroup (не только для текущих relationshipUnitIds)
|
||||||
|
var allJobIdsInGroup = jobsInGroup.Select(j => j.Id).ToHashSet();
|
||||||
|
var allExistingTemplatesInGroup = await templateService.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Include(t=>t.Unit)
|
||||||
|
.Include(t => t.UnitsInTemplate)
|
||||||
|
.Where(t => allJobIdsInGroup.Contains(t.JobId) &&
|
||||||
|
t.StatusTypeId == TemplateStatusTypeEnum.Used &&
|
||||||
|
t.Job!.GroupId == jobGroupId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
foreach (var existingTemplate in allExistingTemplatesInGroup)
|
foreach (var existingTemplate in allExistingTemplatesInGroup)
|
||||||
{
|
{
|
||||||
var key = (existingTemplate.JobId, existingTemplate.UnitId, existingTemplate.Index ?? -1);
|
var key = (existingTemplate.JobId, existingTemplate.UnitId, existingTemplate.Index ?? -1);
|
||||||
if (!expectedTemplateKeys.Contains(key))
|
if (!expectedTemplateKeys.Contains(key))
|
||||||
{
|
{
|
||||||
logger.LogInformation("Деактивация лишнего шаблона {TemplateId} (Job {JobId}, Relationship {UnitId}, Index {Index}).", existingTemplate.Id, existingTemplate.JobId, existingTemplate.UnitId, existingTemplate.Index);
|
logger.LogInformation("Деактивация лишнего шаблона {TemplateId} (Job {JobId}, Relationship {UnitId}, Index {Index}).",
|
||||||
|
existingTemplate.Id, existingTemplate.JobId, existingTemplate.UnitId, existingTemplate.Index);
|
||||||
await templateDeactivator.DeactivateTemplateAsync(existingTemplate, initiator);
|
await templateDeactivator.DeactivateTemplateAsync(existingTemplate, initiator);
|
||||||
deactivated++;
|
|
||||||
await UpdateMatchingStatusAsync(jobGroupId, $"Прогресс: создано={created}, обновлено={updated}, деактивировано={deactivated}. Осталось: создать={toCreate - created}, обновить={toUpdate - updated}, деактивировать={toDeactivate - deactivated}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -740,6 +707,160 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
private async Task LogGroupingDebugInfo(JobGroup jobGroup, Dictionary<Guid, List<Guid>> groupedRelationships, bool isAfterConflictResolution = false)
|
||||||
|
{
|
||||||
|
logger.LogDebug("===[ DEBUG: {Stage} ГРУППИРОВКА ДЛЯ ОТСЛЕЖИВАНИЯ ДУБЛИРОВАНИЯ ВРТ-ASOUP2-ВСИБ ]===",
|
||||||
|
isAfterConflictResolution ? "ПОСЛЕ РАЗРЕШЕНИЯ КОНФЛИКТОВ" : "ДО РАЗРЕШЕНИЯ КОНФЛИКТОВ");
|
||||||
|
|
||||||
|
// Проверяем, есть ли в группах наши целевые юниты
|
||||||
|
var watchUnitsInGroups = groupedRelationships
|
||||||
|
.Where(g => debugWatchUnitIds.Values.Contains(g.Key) || g.Value.Any(id => debugWatchUnitIds.Values.Contains(id)))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (!watchUnitsInGroups.Any())
|
||||||
|
{
|
||||||
|
logger.LogDebug("Целевые юниты не найдены в группах");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загружаем имена для всех юнитов в группах
|
||||||
|
var allUnitIds = watchUnitsInGroups.SelectMany(g => new[] { g.Key }.Concat(g.Value)).Distinct().ToList();
|
||||||
|
var unitNames = await unitService.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(u => allUnitIds.Contains(u.Id))
|
||||||
|
.ToDictionaryAsync(u => u.Id, u => u.Name ?? $"(ID={u.Id})");
|
||||||
|
|
||||||
|
// Ищем все группы, содержащие ВРТ-ASOUP2-ВСИБ
|
||||||
|
var vrtUnitId = debugWatchUnitIds.GetValueOrDefault("ВРТ-ASOUP2-ВСИБ", Guid.Empty);
|
||||||
|
if (vrtUnitId != Guid.Empty)
|
||||||
|
{
|
||||||
|
var groupsWithVrt = groupedRelationships
|
||||||
|
.Where(g => g.Key == vrtUnitId || g.Value.Contains(vrtUnitId))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (groupsWithVrt.Any())
|
||||||
|
{
|
||||||
|
logger.LogWarning("===[ DEBUG: ВРТ-ASOUP2-ВСИБ НАЙДЕН В СЛЕДУЮЩИХ ГРУППАХ ]===");
|
||||||
|
foreach (var group in groupsWithVrt)
|
||||||
|
{
|
||||||
|
var groupName = unitNames.GetValueOrDefault(group.Key, $"(ID={group.Key})");
|
||||||
|
var members = string.Join(", ", group.Value.Select(id => unitNames.GetValueOrDefault(id, $"(ID={id})")));
|
||||||
|
logger.LogWarning("Группа [Ключ: {GroupName}]: Участники [{Members}]", groupName, members);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogDebug("ВРТ-ASOUP2-ВСИБ не найден в группах");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Дополнительно: проверяем принадлежность ВРТ-ASOUP2-ВСИБ к целевым группам
|
||||||
|
var monitorGroupId = debugWatchUnitIds.GetValueOrDefault("МОНИТОР-АСОУП-3-ВСИБ", Guid.Empty);
|
||||||
|
var cekonGroupId = debugWatchUnitIds.GetValueOrDefault("ЦЕКОН-ВСИБ", Guid.Empty);
|
||||||
|
|
||||||
|
if (vrtUnitId != Guid.Empty && (monitorGroupId != Guid.Empty || cekonGroupId != Guid.Empty))
|
||||||
|
{
|
||||||
|
bool vrtInMonitorGroup = false;
|
||||||
|
if (monitorGroupId != Guid.Empty && groupedRelationships.TryGetValue(monitorGroupId, out var monitorGroup))
|
||||||
|
{
|
||||||
|
vrtInMonitorGroup = monitorGroup.Contains(vrtUnitId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vrtInCekonGroup = false;
|
||||||
|
if (cekonGroupId != Guid.Empty && groupedRelationships.TryGetValue(cekonGroupId, out var cekonGroup))
|
||||||
|
{
|
||||||
|
vrtInCekonGroup = cekonGroup.Contains(vrtUnitId);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogWarning("=== АНАЛИЗ ПРИНАДЛЕЖНОСТИ ВРТ-ASOUP2-ВСИБ ===");
|
||||||
|
logger.LogWarning("ВРТ-ASOUP2-ВСИБ в группе МОНИТОР-АСОУП-3-ВСИБ: {InGroup}", vrtInMonitorGroup);
|
||||||
|
logger.LogWarning("ВРТ-ASOUP2-ВСИБ в группе ЦЕКОН-ВСИБ: {InGroup}", vrtInCekonGroup);
|
||||||
|
|
||||||
|
if (vrtInMonitorGroup && vrtInCekonGroup)
|
||||||
|
{
|
||||||
|
logger.LogError("!!! КРИТИЧНО: ВРТ-ASOUP2-ВСИБ ПРИНАДЛЕЖИТ ОБЕИМ ГРУППАМ !!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Дополнительная информация о составе целевых групп
|
||||||
|
if (monitorGroupId != Guid.Empty || cekonGroupId != Guid.Empty)
|
||||||
|
{
|
||||||
|
logger.LogWarning("=== СОСТАВ ЦЕЛЕВЫХ ГРУПП ===");
|
||||||
|
|
||||||
|
if (monitorGroupId != Guid.Empty && groupedRelationships.TryGetValue(monitorGroupId, out var monitorGroup))
|
||||||
|
{
|
||||||
|
var members = string.Join(", ", monitorGroup.Select(id => unitNames.GetValueOrDefault(id, $"(ID={id})")));
|
||||||
|
logger.LogWarning("Группа МОНИТОР-АСОУП-3-ВСИБ ({Count} участников): [{Members}]",
|
||||||
|
monitorGroup.Count, members);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cekonGroupId != Guid.Empty && groupedRelationships.TryGetValue(cekonGroupId, out var cekonGroup))
|
||||||
|
{
|
||||||
|
var members = string.Join(", ", cekonGroup.Select(id => unitNames.GetValueOrDefault(id, $"(ID={id})")));
|
||||||
|
logger.LogWarning("Группа ЦЕКОН-ВСИБ ({Count} участников): [{Members}]",
|
||||||
|
cekonGroup.Count, members);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LogUnitComparisonDebugAsync(Template template, List<Guid> newUnitIds, string groupName)
|
||||||
|
{
|
||||||
|
logger.LogWarning("===[ DEBUG: СРАВНЕНИЕ UnitsInTemplate ДЛЯ {GroupName} ]===", groupName);
|
||||||
|
|
||||||
|
// Загружаем ИМЕНА для юнитов из базы
|
||||||
|
var dbUnitIds = template.UnitsInTemplate.Select(uit => uit.UnitId).ToList();
|
||||||
|
var dbUnitsMap = await unitService.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(u => dbUnitIds.Contains(u.Id))
|
||||||
|
.ToDictionaryAsync(u => u.Id, u => u.Name);
|
||||||
|
|
||||||
|
var dbUnitNames = template.UnitsInTemplate
|
||||||
|
.Select(uit => dbUnitsMap.GetValueOrDefault(uit.UnitId, $"(ID={uit.UnitId})"))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Загружаем ИМЕНА для новых юнитов
|
||||||
|
var newUnitsMap = await unitService.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(u => newUnitIds.Contains(u.Id))
|
||||||
|
.ToDictionaryAsync(u => u.Id, u => u.Name);
|
||||||
|
|
||||||
|
var newUnitNames = newUnitIds
|
||||||
|
.Select(id => newUnitsMap.GetValueOrDefault(id, $"(ID={id})"))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
logger.LogWarning("Шаблон ID={TemplateId}, Имя='{TemplateName}'",
|
||||||
|
template.Id, template.Name);
|
||||||
|
|
||||||
|
logger.LogWarning("Юниты в базе ({Count}): [{Units}]",
|
||||||
|
dbUnitNames.Count, string.Join(", ", dbUnitNames));
|
||||||
|
|
||||||
|
logger.LogWarning("Новые юниты ({Count}): [{Units}]",
|
||||||
|
newUnitNames.Count, string.Join(", ", newUnitNames));
|
||||||
|
|
||||||
|
// Сравниваем ID
|
||||||
|
var existingUnitIds = template.UnitsInTemplate.Select(uit => uit.UnitId).ToList();
|
||||||
|
var newUnitIdsList = newUnitIds.ToList();
|
||||||
|
|
||||||
|
bool idSetsEqual = existingUnitIds.ToHashSet().SetEquals(newUnitIdsList.ToHashSet());
|
||||||
|
bool orderEqual = existingUnitIds.SequenceEqual(newUnitIdsList);
|
||||||
|
|
||||||
|
logger.LogWarning("Сравнение: ID-наборы совпадают={IdMatch}, Порядок совпадает={OrderMatch}",
|
||||||
|
idSetsEqual, orderEqual);
|
||||||
|
|
||||||
|
if (!orderEqual)
|
||||||
|
{
|
||||||
|
logger.LogWarning("ПОРЯДОК ОТЛИЧАЕТСЯ:");
|
||||||
|
logger.LogWarning(" База: [{Order}]", string.Join(", ", dbUnitNames));
|
||||||
|
logger.LogWarning(" Новое: [{Order}]", string.Join(", ", newUnitNames));
|
||||||
|
|
||||||
|
// Дополнительно: показываем ID для точного сравнения
|
||||||
|
logger.LogWarning(" ID базы: [{Ids}]", string.Join(", ", existingUnitIds));
|
||||||
|
logger.LogWarning(" ID новых: [{Ids}]", string.Join(", ", newUnitIdsList));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public async Task UpdateTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator)
|
public async Task UpdateTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator)
|
||||||
{
|
{
|
||||||
logger.LogWarning("GroupedTemplateSynchronizer: UpdateTemplatesForJob вызван для JobId {JobId}. Это не поддерживаемая операция. Используйте SyncTemplatesForJobGroup для обновления.", jobId);
|
logger.LogWarning("GroupedTemplateSynchronizer: UpdateTemplatesForJob вызван для JobId {JobId}. Это не поддерживаемая операция. Используйте SyncTemplatesForJobGroup для обновления.", jobId);
|
||||||
@@ -747,7 +868,6 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Вспомогательные методы ---
|
// --- Вспомогательные методы ---
|
||||||
|
|
||||||
private async Task<HashSet<Guid>> FilterByWorkGroupFieldAsync(
|
private async Task<HashSet<Guid>> FilterByWorkGroupFieldAsync(
|
||||||
List<Unit> units,
|
List<Unit> units,
|
||||||
HashSet<Guid> candidateUnitIds,
|
HashSet<Guid> candidateUnitIds,
|
||||||
@@ -756,10 +876,10 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
{
|
{
|
||||||
return units
|
return units
|
||||||
.Where(u => candidateUnitIds.Contains(u.Id) &&
|
.Where(u => candidateUnitIds.Contains(u.Id) &&
|
||||||
u.UnitValues.Any(uv =>
|
u.UnitValues.Any(uv =>
|
||||||
uv.FieldId == workGroupFieldId &&
|
uv.FieldId == workGroupFieldId &&
|
||||||
uv.Value != null &&
|
uv.Value != null &&
|
||||||
regionalGroupValueIds.Contains(uv.Value.Id)))
|
regionalGroupValueIds.Contains(uv.Value.Id)))
|
||||||
.Select(u => u.Id)
|
.Select(u => u.Id)
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
}
|
}
|
||||||
@@ -795,7 +915,6 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
{
|
{
|
||||||
template.StatusTypeId = TemplateStatusTypeEnum.Updating;
|
template.StatusTypeId = TemplateStatusTypeEnum.Updating;
|
||||||
template.DateModified = DateTimeOffset.UtcNow;
|
template.DateModified = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
if (!await templateService.CommitAsync(initiator))
|
if (!await templateService.CommitAsync(initiator))
|
||||||
{
|
{
|
||||||
logger.LogError("Не удалось перевести шаблон {TemplateId} в Updating для обновления юнитов.", template.Id);
|
logger.LogError("Не удалось перевести шаблон {TemplateId} в Updating для обновления юнитов.", template.Id);
|
||||||
@@ -816,7 +935,6 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
|
|
||||||
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(tempTemplateForName);
|
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(tempTemplateForName);
|
||||||
var nextRun = await GetNextRunAsync(targetJob, template.NextRun);
|
var nextRun = await GetNextRunAsync(targetJob, template.NextRun);
|
||||||
|
|
||||||
var updateRequest = new TemplateUpdaterMq
|
var updateRequest = new TemplateUpdaterMq
|
||||||
{
|
{
|
||||||
TemplateId = template.Id,
|
TemplateId = template.Id,
|
||||||
@@ -870,6 +988,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
|
|||||||
Action = TemplateMatcherActionEnum.Sync,
|
Action = TemplateMatcherActionEnum.Sync,
|
||||||
Comment = comment
|
Comment = comment
|
||||||
};
|
};
|
||||||
|
|
||||||
await matchingStatusService.SetMatchingStatusAsync(
|
await matchingStatusService.SetMatchingStatusAsync(
|
||||||
jobGroupId,
|
jobGroupId,
|
||||||
SyncTaskEntityTypeEnum.JobGroup,
|
SyncTaskEntityTypeEnum.JobGroup,
|
||||||
|
|||||||
Reference in New Issue
Block a user