Files
parr_api/PARR.Test/Worker.cs
Mikhail Kuznetsov a438f18480 refactor(templateMatcher): Переход на Pipeline-архитектуру для SimpleSync и GroupedSync.
- SimpleTemplateSynchronizer и GroupedTemplateSynchronizer переведены на паттерн Pipeline с разделением на Read/Write этапы
- Выделены контракты этапов (ISimpleSyncStage, IGroupedSyncStage) и контексты (SimpleSyncContext, GroupedSyncContext)
- Read-этапы безопасны для тестов (не пишут в БД/MQ), Write-этапы изолированы через отдельные интерфейсы
- Добавлено [Perf]-логирование каждого этапа с метриками времени выполнения
- Логи приведены к человекочитаемому формату 'Имя' (ID) для Job, JobGroup и Unit
- Устранено дублирование данных в контекстах (FilteredUnits перезаписывается, TemplateGroups строго типизирован)
- Константы неиспользуемых шаблонов вынесены в UnusedTemplateConstants
- Структура проекта реорганизована: SimpleSync, GroupedSync, Implementations, Interfaces
2026-06-11 15:09:46 +10:00

375 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.EntityFrameworkCore;
using PARR.Core.Common.Interfaces;
using PARR.Core.Repositories.Interfaces.Job;
using PARR.Core.Services.UnitFilterService;
using PARR.Domain.Cache;
using PARR.Domain.Entities.Base.History;
using PARR.Domain.Enums;
using PARR.EsppApi;
using PARR.EsppApi.Constants;
using PARR.EsppApi.Models.Query;
using PARR.TemplateMatcher;
using PARR.TemplateMatcher.Services.GroupedSync;
using PARR.TemplateMatcher.Services.Implementations;
using PARR.TemplateMatcher.Services.Interfaces;
using PARR.Test.NextRun;
using System.Diagnostics;
namespace PARR.Test
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly IEsppApiService esppApiService;
private readonly IIntervalService intervalService;
//private readonly ICalendarService calendarService;
private readonly IServiceProvider serviceProvider;
public Worker(
ILogger<Worker> logger,
IEsppApiService esppApiService,
IIntervalService intervalService,
//ICalendarService calendarService,
IServiceProvider serviceProvider
)
{
_logger = logger;
this.esppApiService = esppApiService;
this.intervalService = intervalService;
//this.calendarService = calendarService;
this.serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//var aaa = DateTimeOffset.UtcNow;
//var bbb = aaa.ToOffset(new TimeSpan(3, 0, 0));
//await TemplateMatcherTest();
await PreCommitValidationTest();
//await NextRunTest();
#region test
//using (var scope = serviceProvider.CreateScope())
//{
// var templateService = scope.ServiceProvider.GetService<ITemplateService>();
// var shortcodesService = scope.ServiceProvider.GetService<IShortcodesService>();
// var robotConfigurationService = scope.ServiceProvider.GetService<IRobotConfigurationService>();
// var ts = await templateService.Get().Include(t => t.Job).Where(t => t.ScheduleEsppId != null && t.Name.Contains("ЭИТИ-ПТК-ПАРР") && !t.Name.Contains("!!!")).ToListAsync();
// if (!ts.Any())
// return;
// var jobIds = ts.Select(t => t.JobId).Distinct();
// foreach (var template in ts)
// {
// var isExistSameKeyValueTemplate = await templateService.Get().AsNoTracking().AnyAsync(t => t.JobId == template.JobId && t.UnitId == template.UnitId && t.Name != template.Name);
// var templateName = await shortcodesService.ApplyShortcodesAsync(template.Job.TemplateNameMask, template.UnitId, template.JobId);
// if (isExistSameKeyValueTemplate)
// {
// template.IsActiveTemplate = false;
// template.IsActiveSchedule = false;
// template.Name = string.Concat("!!!", templateName.ToUpper());
// template.DateModified = DateTimeOffset.UtcNow;
// if (!(await templateService.CommitAsync(template)))
// _logger.LogInformation($"Не удалось сохнаить изменения {template.ToString()}");
// continue;
// }
// var oldName = template.Name;
// var rt = await robotConfigurationService.Get().FirstOrDefaultAsync(t => t.TemplateId == template.Id);
// if (rt != null)
// {
// template.IsActiveTemplate = true;
// template.IsActiveSchedule = false;
// template.Name = templateName.ToUpper();
// template.DateModified = DateTimeOffset.UtcNow;
// robotConfigurationService.ChangeTaskStatus(DAL.Contracts.TaskStatusEnum.Updating, ref rt);
// //_logger.LogInformation(templateName);
// }
// if (!(await templateService.CommitAsync(template)))
// _logger.LogInformation($"Не удалось сохранить изменения {template.ToString()}");
// }
// _logger.LogInformation($"Опа опа опа...");
//}
#endregion
#region unit
//using (var scope = serviceProvider.CreateScope())
//{
// var service = scope.ServiceProvider.GetService<IUnitService>();
// var unit = await service.Get().Include(t => t.Parents).Include(t => t.Childs).FirstOrDefaultAsync(t => t.Id == Guid.Parse("0f89e2ec-0e4d-4de6-b990-c55e8308accf"));
// var parent = unit?.Parents;
//}
#endregion
#region Calendar
//var testCalendar = new BLL.Services.Implementations.Test(calendarService);
//testCalendar.Ttt() ;
//using (var scope = serviceProvider.CreateScope())
//{
// var weekendDayService = scope.ServiceProvider.GetService<IWeekendDayService>();
// var workDays = calendarService.GetWorkDatesForPeriod(new DateOnly(2024, 07, 01), TimeSpan.Parse("30:00:00:00"), weekendDayService!.GetWeekends);
//}
//IDistributionPeriodService
//using (var scope = serviceProvider.CreateScope())
//{
// var service = scope.ServiceProvider.GetService<ICalendarService>();
// var weekEndService = scope.ServiceProvider.GetService<IWeekendDayService>();
// var aa = service!.GetWorkDatesForPeriod(new DateOnly(2024, 07, 01), DistributionPeriodTypeEnum.Year, "1", weekEndService!.GetWeekends);
// //var aaa = await service!.Get().ToListAsync();
// //foreach (var aaaItem in aaa)
// //{
// // var bbbb = aaaItem.AddDate;
// // var now = DateTimeOffset.UtcNow;
// // var newwww = now.AddMicroseconds(bbbb.);
// //}
//}
#endregion
}
#region PreCommitValidation
private async Task PreCommitValidationTest()
{
_logger.LogInformation("=== НАЧАЛО PRE-COMMIT ВАЛИДАЦИИ ===");
await using var scope = serviceProvider.CreateAsyncScope();
// === 1. Проверка Simple Pipeline ===
_logger.LogInformation("--- Simple Pipeline ---");
var simpleSync = scope.ServiceProvider
.GetRequiredService<IEnumerable<ITemplateSynchronizer>>()
.FirstOrDefault(s => s is SimpleTemplateSynchronizer);
if (simpleSync == null)
{
_logger.LogError("[FAIL] SimpleTemplateSynchronizer не найден в DI");
return;
}
// Подставьте реальный JobId для Simple
var simpleJobId = Guid.Parse("6ff1de05-80c3-4b38-846b-0c793fd7fc8c");
try
{
var sw = Stopwatch.StartNew();
await simpleSync.SyncTemplatesForJobAsync(simpleJobId, new HistoryInitiator
{
InitiatorIp = "127.0.0.1",
InitiatorParrComponentId = ParrComponentsEnum.Master,
InitiatorComment = "Pre-commit validation: Simple Pipeline"
});
sw.Stop();
_logger.LogInformation("[OK] Simple Pipeline: завершено за {Ms} мс", sw.ElapsedMilliseconds);
}
catch (Exception ex)
{
_logger.LogError(ex, "[FAIL] Simple Pipeline: исключение");
}
// === 2. Проверка Grouped Pipeline ===
_logger.LogInformation("--- Grouped Pipeline ---");
var groupedSync = scope.ServiceProvider
.GetRequiredService<IEnumerable<ITemplateSynchronizer>>()
.FirstOrDefault(s => s is GroupedTemplateSynchronizer);
if (groupedSync == null)
{
_logger.LogError("[FAIL] GroupedTemplateSynchronizer не найден в DI");
return;
}
// Подставьте реальный JobGroupId для Grouped
var groupedJobGroupId = Guid.Parse("51acaa95-08bf-425f-9a09-87b6a4cbc77e");
try
{
var sw = Stopwatch.StartNew();
await groupedSync.SyncTemplatesForJobGroupAsync(groupedJobGroupId, new HistoryInitiator
{
InitiatorIp = "127.0.0.1",
InitiatorParrComponentId = ParrComponentsEnum.Master,
InitiatorComment = "Pre-commit validation: Grouped Pipeline"
});
sw.Stop();
_logger.LogInformation("[OK] Grouped Pipeline: завершено за {Ms} мс", sw.ElapsedMilliseconds);
}
catch (Exception ex)
{
_logger.LogError(ex, "[FAIL] Grouped Pipeline: исключение");
}
// === 3. Проверка контрактных констант ===
_logger.LogInformation("--- Контрактные константы ---");
var constantsType = typeof(PARR.TemplateMatcher.Constants.UnusedTemplateConstants);
var fields = constantsType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
foreach (var field in fields)
{
var value = field.GetValue(null);
_logger.LogInformation("[OK] Константа {Name} = '{Value}'", field.Name, value);
}
}
#endregion
#region TemplateMatcher
private async Task TemplateMatcherTest()
{
await using var scope = serviceProvider.CreateAsyncScope();
var templateMatcher = scope.ServiceProvider.GetRequiredService<ITemplateMatcher>();
var groupRepository = scope.ServiceProvider.GetRequiredService<IJobGroupRepository>();
var groups = await groupRepository.Get().AsNoTracking()
.Where(t => t.GroupType != null && t.GroupType.Code == JobGroupTypesEnum.Group)
.Select(t => t.Id).ToHashSetAsync();
foreach (var groupId in groups)
{
await templateMatcher.SyncTemplatesForJobGroup(groupId,
new HistoryInitiator
{
InitiatorIp = "10.99.246.156",
InitiatorParrComponentId = ParrComponentsEnum.Master,
InitiatorComment = "Тестовый проект. Синхронизация Группированых работ"
}
);
}
return;
}
#endregion
private async Task NextRunTest()
{
using (var scope = serviceProvider.CreateScope())
{
var service = scope.ServiceProvider.GetService<NextRunTest>();
await service!.Test();
}
}
private async Task TestEsppApi()
{
//var result = await esppApiService.FindOrdersAsync(new FindOrdersQuery { DescriptionContains = "Полное", GenerateDateStart = new DateTime(2023, 10, 10) , GenerateDateEnd = new DateTime(2023, 10, 24) });
var recordId = "НАР24-00009262";
var result = await esppApiService.FindOrderByRecordIdAsync(recordId);
if (result.IsSuccess && (result.Data != null && result.Data.Number.Any()))
{
//var orders = result.Data;
//в работу
var inWorkResult = await esppApiService.SetStatusInWorkAsync(recordId, "Регламентные работы в автоматическом режиме созданы и выполнены;"); //"Решение...\nС новой строки"
if (!inWorkResult.IsSuccess)
{
_logger.LogError(inWorkResult.Exception, $"Не удалось взять в работу наряд {recordId}");
return;
}
var tnk = new AddMtnkQuery { RecordId = recordId, JobOperation = "Не учтена в перечне ТНК", Time = "00:05" };
var addMtnkResult = await esppApiService.AddMTnkAsync(tnk);
if (!addMtnkResult.IsSuccess)
{
_logger.LogError(addMtnkResult.Exception, $"Не удалось списать трудозатраты наряда {recordId}");
return;
}
//выполнить
var resFinish = await esppApiService.SetStatusIsDoneAsync(recordId, "Регламентные работы в автоматическом режиме созданы и выполнены;", EsppOrderClosingCodeEnum.Auto);
if (!resFinish.IsSuccess)
{
_logger.LogError(resFinish.Exception, $"Не удалось обновить статус наряда {recordId}");
return;
}
}
else
{
_logger.LogWarning($"не найден наряд {recordId}");
}
}
private async Task StopDiscontinuedOrdersInEspp()
{
const string ResultMsg = "Сформирован ошибочно";
var searchQuery = new FindOrdersQuery
{
DescriptionContains = "ЭИТИ-ПТК-ПАРР",
StatusIs = "1-Направлен в группу",
GenerateDateStart = new DateTime(2024, 02, 26, 0, 0, 0),
GenerateDateEnd = new DateTime(2024, 02, 29, 0, 0, 0)
};
var result = await esppApiService.FindOrdersAsync(searchQuery);
if (result.IsSuccess && result.Data != null)
{
foreach (var item in result.Data)
{
if (item.StatusEnum == OrderStatusEnum.New)
{
await esppApiService.SetStatusInWorkAsync(item.Number, ResultMsg);
}
var newItem = await esppApiService.FindOrderByRecordIdAsync(item.Number);
if (newItem.IsSuccess && newItem.Data != null && newItem.Data.StatusEnum == OrderStatusEnum.InWork)
{
var tnk = new AddMtnkQuery { RecordId = newItem.Data.Number, JobOperation = "Прочее(наряды)", Time = "00:01" };
await esppApiService.AddMTnkAsync(tnk);
await esppApiService.SetStatusIsDoneAsync(newItem.Data.Number, ResultMsg, EsppOrderClosingCodeEnum.Breake);
}
}
}
}
}
}