From 5ad26742e69ef73fd4f3b6e3adc48525dbe2cfe4 Mon Sep 17 00:00:00 2001 From: Mikhail Trubnikov Date: Wed, 21 Jan 2026 12:19:27 +1000 Subject: [PATCH] =?UTF-8?q?feat(dal):=20NextRunService,=20GetNextRunForJob?= =?UTF-8?q?GroupWithAutoDistributionAsync=20-=20=D1=80=D0=B5=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F,=20=D1=80=D0=B0=D1=81?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PARR.DAL/NextRunServices/INextRunService.cs | 6 +- .../Models/TemplateWithWorkGroupDto.cs | 5 + PARR.DAL/NextRunServices/NextRunService.cs | 149 +++++++++++++++++- PARR.Test/NextRun/NextRunTest.cs | 12 +- 4 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 PARR.DAL/NextRunServices/Models/TemplateWithWorkGroupDto.cs diff --git a/PARR.DAL/NextRunServices/INextRunService.cs b/PARR.DAL/NextRunServices/INextRunService.cs index 3800f9f9..d784a4a1 100644 --- a/PARR.DAL/NextRunServices/INextRunService.cs +++ b/PARR.DAL/NextRunServices/INextRunService.cs @@ -1,4 +1,6 @@ -namespace PARR.DAL.NextRunServices +using PARR.DAL.NextRunServices.Models; + +namespace PARR.DAL.NextRunServices { public interface INextRunService { @@ -7,7 +9,7 @@ /// /// /// - Task> GetNextRunForJobGroupWithAutoDistributionAsync(Guid jobGroupId); + Task?> GetNextRunForJobGroupWithAutoDistributionAsync(Guid jobGroupId); /// diff --git a/PARR.DAL/NextRunServices/Models/TemplateWithWorkGroupDto.cs b/PARR.DAL/NextRunServices/Models/TemplateWithWorkGroupDto.cs new file mode 100644 index 00000000..cdae20a5 --- /dev/null +++ b/PARR.DAL/NextRunServices/Models/TemplateWithWorkGroupDto.cs @@ -0,0 +1,5 @@ +namespace PARR.DAL.NextRunServices.Models +{ + internal record TemplateWithWorkGroupDto(Guid Id, DateTimeOffset? NextRun, string WorkGroup); + +} diff --git a/PARR.DAL/NextRunServices/NextRunService.cs b/PARR.DAL/NextRunServices/NextRunService.cs index ff5348b7..7860a2ab 100644 --- a/PARR.DAL/NextRunServices/NextRunService.cs +++ b/PARR.DAL/NextRunServices/NextRunService.cs @@ -1,6 +1,11 @@  using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using PARR.Constants; +using PARR.DAL.DomainServices.Shortcodes; +using PARR.DAL.Models; +using PARR.DAL.Models.Job; +using PARR.DAL.NextRunServices.Models; using PARR.DAL.NextRunServices.Subservices; using PARR.DAL.Services.Interfaces; using PARR.DAL.Services.Interfaces.Job; @@ -15,13 +20,15 @@ namespace PARR.DAL.NextRunServices private readonly IJobGroupService jobGroupService; private readonly IEsppScheduleTransformService esppScheduleTransformService; private readonly ITemplateDistributor templateDistributor; + private readonly IShortcodesService shortcodesService; public NextRunService( ILogger logger, ITemplateService templateService, IJobGroupService jobGroupService, IEsppScheduleTransformService esppScheduleTransformService, - ITemplateDistributor templateDistributor + ITemplateDistributor templateDistributor, + IShortcodesService shortcodesService ) { this.logger = logger; @@ -29,14 +36,94 @@ namespace PARR.DAL.NextRunServices this.jobGroupService = jobGroupService; this.esppScheduleTransformService = esppScheduleTransformService; this.templateDistributor = templateDistributor; + this.shortcodesService = shortcodesService; } - public async Task> GetNextRunForJobGroupWithAutoDistributionAsync(Guid jobGroupId) + public async Task?> GetNextRunForJobGroupWithAutoDistributionAsync(Guid jobGroupId) { - //TODO: - throw new NotImplementedException(); - //var ditributedTemplateList = await templateDistributor.DistributeTemplatesAsync(); + logger.LogInformation("Начинаю распределять шаблоны для группы работ {gobGroupId}", jobGroupId); + + var jobGroup = await jobGroupService.Get() + .Include(t => t.DistributionConfig) + .ThenInclude(t => t.DistributionPeriod) + .AsNoTracking() + .FirstOrDefaultAsync(t => t.Id == jobGroupId); + + if (jobGroup == null) + { + logger.LogError("Не найдена группа работа с id: {id}", jobGroupId); + return null; + } + + if (!jobGroup.IsAutoDistributionEnabled || jobGroup.DistributionConfig == null) + { + logger.LogError("Группа работ id: {id} не подходит для автораспределения, у нее или отсутствуют настройки или не включено автораспределение. IsAutoDistributionEnabled: {IsAutoDistributionEnabled}. Есть конфиг: {DistributionConfig}", jobGroupId, jobGroup.IsAutoDistributionEnabled, jobGroup.DistributionConfig != null); + return null; + } + + logger.LogInformation("Параметры распределения. groupId: {groupId}, name: {groupName}, referenceDate: {referenceDate}, " + + "distributionPeriodName: {distributionPeriodName}, distributionPeriodDuration: {distributionPeriodDuration}, " + + "distributionPeriodType: {distributionPeriodType}, IsExcludeWeekends: {IsExcludeWeekends}, IsGroupingByWorkGroup: {IsGroupingByWorkGroup}", + jobGroupId, jobGroup.GroupName, jobGroup.ReferenceDate, jobGroup.DistributionConfig.DistributionPeriod.Name, jobGroup.DistributionConfig.DistributionPeriod.Duration, jobGroup.DistributionConfig.DistributionPeriod.Type, + jobGroup.DistributionConfig.IsExcludeWeekends, jobGroup.DistributionConfig.IsGroupingByWorkGroup + ); + + var duration = GetDurationDays(jobGroup.DistributionConfig); + var dateStart = GetDateStart(jobGroup.ReferenceDate); + + var allTemplates = await templateService.Get() + .Include(t => t.Job) + .Where(t => t.Job!.GroupId == jobGroupId) + .AsNoTracking() + .ToListAsync(); + if (!allTemplates.Any()) + { + logger.LogInformation("В группе c ИД {jobGroupId} отсутствуют шаблоны", jobGroupId); + return null; + } + + logger.LogDebug("Всего шаблонов для распределения: {count} шт.", allTemplates.Count); + + var result = new List(); + + if (jobGroup.DistributionConfig.IsGroupingByWorkGroup) + { + // Нужно группировать по РГ + + // список шаблонов с полученной из шорткода РГ + var templatesWithWorkGroup = new List(); + foreach (var template in allTemplates) + { + var workGroupName = await shortcodesService.ApplyShortcodesAsync(template.Job!.WorkGroupMask, template); + templatesWithWorkGroup.Add(new TemplateWithWorkGroupDto(template.Id, template.NextRun, workGroupName)); + } + + var grouping = templatesWithWorkGroup.GroupBy(t => t.WorkGroup); + + //распределяем + foreach (var workGroupTemplates in grouping) + { + logger.LogInformation("Начинаю распределять шаблоны для рабочей группы: {workGroup}. Всего шаблонов: {countTemplates} шт.", workGroupTemplates.Key, workGroupTemplates.Count()); + var templatesForDistribute = workGroupTemplates.Select(t => new TemplateNextRunDto(t.Id, t.NextRun)).ToList(); + var distributedResult = await templateDistributor.DistributeTemplatesAsync(dateStart, duration, jobGroup.ReferenceDate, templatesForDistribute, jobGroup.DistributionConfig.IsExcludeWeekends); + + result.AddRange(distributedResult); + } + } + else + { + // Не нужна группировка по РГ + + // сразу распределяем все шаблоны + var templatesForDistribute = allTemplates.Select(t => new TemplateNextRunDto(t.Id, t.NextRun)).ToList(); + + result = await templateDistributor.DistributeTemplatesAsync(dateStart, duration, jobGroup.ReferenceDate, templatesForDistribute, jobGroup.DistributionConfig.IsExcludeWeekends); + } + + logger.LogInformation("Завершено распределение шаблонов для группы {jobGroupId}. Всего распределено шаблонов: {count} шт.", jobGroupId, result.Count); + + return result; } @@ -81,5 +168,57 @@ namespace PARR.DAL.NextRunServices return await esppScheduleTransformService.GetNextDateAsync(template.Job.Group.Id, template.Job.Group.ReferenceDate); } } + + + /// + /// Получить продолжительность в днях + /// + /// + /// + private int GetDurationDays(JobGroupDistributionConfig config) + { + var period = config.DistributionPeriod; + var periodType = period!.Type; + int.TryParse(period.Duration, out var duration); + + var periodDays = duration; + + if (periodType == DistributionPeriodTypeEnum.Day.ToString()) + periodDays = duration; + + + if (periodType == DistributionPeriodTypeEnum.Month.ToString()) + // в месяце 30 дней, duration*30 + periodDays = duration * 30; + + + if (periodType == DistributionPeriodTypeEnum.Year.ToString()) + // в году 365 дней, duration*365 + periodDays = duration * 365; + + + logger.LogDebug("Период распределения: {name}, duration: {duration}, type: {type}. Итого в днях: {days}", period.Name, period.Duration, period.Type, periodDays); + + if (periodDays == 0) + logger.LogWarning("Период распределения 0 дней. Неверный конфиг распределения в таблице {table}. {name}, duration: {duration}, type: {type}", nameof(DistributionPeriod), period.Name, period.Duration, period.Type); + + return periodDays; + } + + + /// + /// Получить дату начала распределения + /// + /// + /// + private DateOnly GetDateStart(DateTimeOffset referenceDate) + { + var today = DateTime.UtcNow; + + if (today < referenceDate) + return DateOnly.FromDateTime(referenceDate.Date); + else + return DateOnly.FromDateTime(today.Date); + } } } diff --git a/PARR.Test/NextRun/NextRunTest.cs b/PARR.Test/NextRun/NextRunTest.cs index c815937f..f6c905bf 100644 --- a/PARR.Test/NextRun/NextRunTest.cs +++ b/PARR.Test/NextRun/NextRunTest.cs @@ -1,4 +1,5 @@ -using PARR.DAL.NextRunServices.Models; +using PARR.DAL.NextRunServices; +using PARR.DAL.NextRunServices.Models; using PARR.DAL.NextRunServices.Subservices; namespace PARR.Test.NextRun @@ -6,10 +7,12 @@ namespace PARR.Test.NextRun internal class NextRunTest { private readonly ITemplateDistributor templateDistributor; + private readonly INextRunService nextRunService; - public NextRunTest(ITemplateDistributor templateDistributor) + public NextRunTest(ITemplateDistributor templateDistributor, INextRunService nextRunService) { this.templateDistributor = templateDistributor; + this.nextRunService = nextRunService; } int periodDays = 10; @@ -17,8 +20,11 @@ namespace PARR.Test.NextRun public async Task Test() { - await DistributeTemplatesAsync(); + //await DistributeTemplatesAsync(); //await GetValidNextRunForTemplateAsync(); + + var result = await nextRunService.GetNextRunForJobGroupWithAutoDistributionAsync(Guid.Parse("eadc5498-dba6-4f10-9b4b-a1653e3c3e61")); + } ///