refactor(unitFilterService): Этапы фильтрации вынесены в отдельные классы

This commit is contained in:
Mikhail Kuznetsov
2026-06-09 17:46:43 +10:00
parent 6f8d889680
commit 535e87ec21
15 changed files with 773 additions and 792 deletions

View File

@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using System.Diagnostics;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Repositories.Interfaces;
using PARR.Core.Repositories.Interfaces.Job;
@@ -56,6 +57,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
public async Task SyncTemplatesForJobGroupAsync(Guid jobGroupId, HistoryInitiator initiator)
{
var totalSw = Stopwatch.StartNew();
logger.LogInformation("Начало синхронизации шаблонов для JobGroup {JobGroupId}", jobGroupId);
// === Проверка: уже запущена? ===
@@ -82,7 +84,8 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
try
{
// 1. Загрузка JobGroup и связанных Job'ов
// === ЭТАП 1: Загрузка JobGroup ===
var stageSw = Stopwatch.StartNew();
var jobGroup = await jobGroupService.Get()
.AsNoTracking()
.AsSingleQuery()
@@ -105,8 +108,11 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
}
var jobsInGroup = jobGroup.Jobs.ToList();
stageSw.Stop();
logger.LogDebug("[Perf] JobGroup {JobGroupId} | Этап: Загрузка JobGroup | Время: {Ms} мс | Jobs: {Count}",
jobGroupId, stageSw.ElapsedMilliseconds, jobsInGroup.Count);
// 2. Поиск Job с максимальным MaxValueRelationships
// === Поиск эталонного Job ===
var maxJob = jobsInGroup
.Where(j => j.MaxValueRelationships.HasValue)
.OrderByDescending(j => j.MaxValueRelationships)
@@ -121,9 +127,13 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
logger.LogDebug("Используется Job {JobId} с максимальным MaxValueRelationships ({MaxValue}).", maxJob.Id, maxJob.MaxValueRelationships);
// 3. Получение отфильтрованных юнитов через UnitFilterService
logger.LogDebug("Получение отфильтрованных юнитов через UnitFilterService для Job {JobId}.", maxJob.Id);
// === ЭТАП 2: Фильтрация юнитов ===
stageSw.Restart();
var unitFilterResults = await unitFilterService.GetUnitsByJobFilterAsync(maxJob.Id);
stageSw.Stop();
var filterCount = unitFilterResults?.Count() ?? 0;
logger.LogDebug("[Perf] JobGroup {JobGroupId} | Этап: Фильтрация юнитов | Время: {Ms} мс | Результат: {Count}",
jobGroupId, stageSw.ElapsedMilliseconds, filterCount);
if (unitFilterResults == null || !unitFilterResults.Any())
{
@@ -133,21 +143,26 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
return;
}
// 4. Применение специфичных правил фильтрации для групповых шаблонов
logger.LogDebug("Применение специфичных правил фильтрации для групповых шаблонов.");
// === ЭТАП 3: Групповая фильтрация ===
stageSw.Restart();
var finalFilteredUnits = await groupedTemplateUnitFilter.FilterAsync(unitFilterResults, jobGroup);
stageSw.Stop();
logger.LogDebug("[Perf] JobGroup {JobGroupId} | Этап: Групповая фильтрация | Время: {Ms} мс | Результат: {Count}",
jobGroupId, stageSw.ElapsedMilliseconds, finalFilteredUnits.Count);
if (!finalFilteredUnits.Any())
{
logger.LogInformation("После применения правил фильтрации в JobGroup {JobGroupId} не осталось юнитов.", jobGroupId);
await UpdateMatchingStatusAsync(jobGroupId, "Нет юнитов после фильтрации");
await matchingStatusService.DeleteMatchingStatusAsync(jobGroupId, SyncTaskEntityTypeEnum.JobGroup);
return;
await matchingStatusService.DeleteMatchingStatusAsync(jobGroupId, SyncTaskEntityTypeEnum.JobGroup); return;
}
// 5. Разрешение конфликтов связей и построение первичного маппинга
logger.LogDebug("Разрешение конфликтов связей и построение первичного маппинга.");
// === ЭТАП 4: Разрешение конфликтов ===
stageSw.Restart();
var initialReverseMapping = await unitInTemplateConflictMapper.BuildMappingAsync(finalFilteredUnits, maxJob);
stageSw.Stop();
logger.LogDebug("[Perf] JobGroup {JobGroupId} | Этап: Разрешение конфликтов | Время: {Ms} мс | Связей: {Count}",
jobGroupId, stageSw.ElapsedMilliseconds, initialReverseMapping.Count);
if (!initialReverseMapping.Any())
{
@@ -156,9 +171,12 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
return;
}
// 6. Построение структуры групп (трансформация, внутренняя группировка, разбиение)
logger.LogDebug("Построение структуры групп для шаблонов.");
// === ЭТАП 5: Построение структуры групп ===
stageSw.Restart();
var templateGroups = await groupedTemplateBuilder.BuildAsync(initialReverseMapping, jobGroup, maxJob);
stageSw.Stop();
logger.LogDebug("[Perf] JobGroup {JobGroupId} | Этап: Построение групп | Время: {Ms} мс | Групп: {Count}",
jobGroupId, stageSw.ElapsedMilliseconds, templateGroups.Count);
if (!templateGroups.Any())
{
@@ -167,25 +185,37 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
return;
}
// 7. Обработка групп: сравнение, обновление, создание, отправка MQ
logger.LogDebug("Обработка групп шаблонов: сравнение, обновление и создание.");
// === ЭТАП 6: Обработка групп (сравнение, обновление, MQ) ===
stageSw.Restart();
var expectedTemplateKeys = await groupedTemplateProcessor.ProcessAsync(
templateGroups,
jobsInGroup,
maxJob,
initiator);
stageSw.Stop();
logger.LogDebug("[Perf] JobGroup {JobGroupId} | Этап: Обработка групп | Время: {Ms} мс | Ключей: {Count}",
jobGroupId, stageSw.ElapsedMilliseconds, expectedTemplateKeys.Count);
// 8. Деактивация лишних шаблонов
// === ЭТАП 7: Деактивация лишних шаблонов ===
stageSw.Restart();
await DeactivateUnusedTemplatesAsync(expectedTemplateKeys, jobGroupId, jobsInGroup, initiator);
stageSw.Stop();
logger.LogDebug("[Perf] JobGroup {JobGroupId} | Этап: Деактивация | Время: {Ms} мс",
jobGroupId, stageSw.ElapsedMilliseconds);
// === ИТОГО === totalSw.Stop();
logger.LogInformation(
"[Perf] JobGroup {JobGroupId} | ИТОГО: {TotalMs} мс",
jobGroupId, totalSw.ElapsedMilliseconds);
// 9. Успешное завершение
await UpdateMatchingStatusAsync(jobGroupId, "Синхронизация завершена успешно");
await matchingStatusService.DeleteMatchingStatusAsync(jobGroupId, SyncTaskEntityTypeEnum.JobGroup);
logger.LogInformation("Синхронизация шаблонов завершена для JobGroup {JobGroupId}.", jobGroupId);
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при синхронизации JobGroup {JobGroupId}", jobGroupId);
totalSw.Stop();
logger.LogError(ex, "Ошибка при синхронизации JobGroup {JobGroupId} через {ElapsedMs} мс", jobGroupId, totalSw.ElapsedMilliseconds);
await UpdateMatchingStatusAsync(jobGroupId, $"Ошибка: {ex.Message}");
throw;
}