From 62dcf80aa33be08c1dc86d543849f7ae1b7d3a2b Mon Sep 17 00:00:00 2001 From: Mikhail Trubnikov Date: Mon, 16 Feb 2026 14:48:43 +1000 Subject: [PATCH] =?UTF-8?q?feat(dal,):=20NextRunServiceV2=20-=20=D0=BC?= =?UTF-8?q?=D0=B5=D1=82=D0=BE=D0=B4=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20nextRun=20=D0=B4=D0=BB=D1=8F=20=D1=81?= =?UTF-8?q?=D1=83=D1=89=D0=B5=D1=81=D1=82=D0=B2=D1=83=D1=8E=D1=89=D0=B5?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PARR.DAL/NextRunServices/INextRunServiceV2.cs | 8 +- PARR.DAL/NextRunServices/NextRunServiceV2.cs | 221 +++++++++++++++++- PARR.Test/NextRun/NextRunTest.cs | 6 + 3 files changed, 224 insertions(+), 11 deletions(-) diff --git a/PARR.DAL/NextRunServices/INextRunServiceV2.cs b/PARR.DAL/NextRunServices/INextRunServiceV2.cs index f59081b9..c24af80f 100644 --- a/PARR.DAL/NextRunServices/INextRunServiceV2.cs +++ b/PARR.DAL/NextRunServices/INextRunServiceV2.cs @@ -20,8 +20,8 @@ namespace PARR.DAL.NextRunServices /// ИД группы работ /// Рабочая группа, не маска /// Зона ответственности, не маска - /// - Task GetNextRunForNewTemplateAsync(Guid jobGroupId, string workGroupName, string responseAreaName); + /// NextRun или null, если null - то ошибка + Task GetNextRunForNewTemplateAsync(Guid jobGroupId, string workGroupName, string responseAreaName); /// @@ -29,8 +29,8 @@ namespace PARR.DAL.NextRunServices /// /// ИД шаблона /// При перемещении шаблона в новую для него работу, ставить isNew=true - /// - Task GetNextRunForTemplateAsync(Guid templateId, bool isNew); + /// NextRun или null, если null - то ошибка + Task GetNextRunForTemplateAsync(Guid templateId, bool isNew); diff --git a/PARR.DAL/NextRunServices/NextRunServiceV2.cs b/PARR.DAL/NextRunServices/NextRunServiceV2.cs index 9c74bfce..655c2ab3 100644 --- a/PARR.DAL/NextRunServices/NextRunServiceV2.cs +++ b/PARR.DAL/NextRunServices/NextRunServiceV2.cs @@ -260,7 +260,7 @@ namespace PARR.DAL.NextRunServices } - public async Task GetNextRunForNewTemplateAsync(Guid jobGroupId, string workGroupName, string responseAreaName) + public async Task GetNextRunForNewTemplateAsync(Guid jobGroupId, string workGroupName, string responseAreaName) { logger.LogDebug("Получить nextRun для вновь создаваемого шаблона. jobGroupId: {jobGroupId}, workGroupName: {workGroupName}, responseAreaName: {responseAreaName}", jobGroupId, workGroupName, responseAreaName); @@ -274,7 +274,8 @@ namespace PARR.DAL.NextRunServices if (jobGroup == null) { logger.LogError("Не найдена группа работ с Id: {jobGroupId}.", jobGroupId); - throw new ArgumentNullException(nameof(jobGroupId), $"Не найдена группа работ с Id: {jobGroupId}"); + //throw new ArgumentNullException(nameof(jobGroupId), $"Не найдена группа работ с Id: {jobGroupId}"); + return null; } if (jobGroup.IsAutoDistributionEnabled) @@ -285,7 +286,8 @@ namespace PARR.DAL.NextRunServices if (jobGroup.DistributionConfig == null) { logger.LogError("Для группы работ {jobGroupId}, указано автораспределение, но отсутствует конфиг в таблице {GroupDistributionConfigs}", jobGroupId, nameof(JobGroupDistributionConfig)); - throw new ArgumentNullException(nameof(JobGroupDistributionConfig), $"Для jobGroupId: {jobGroupId} отсутствует конфигурация автораспределения в таблице {nameof(JobGroupDistributionConfig)}"); + //throw new ArgumentNullException(nameof(JobGroupDistributionConfig), $"Для jobGroupId: {jobGroupId} отсутствует конфигурация автораспределения в таблице {nameof(JobGroupDistributionConfig)}"); + return null; } var startDate = GetDateStart(); @@ -430,7 +432,7 @@ namespace PARR.DAL.NextRunServices if (jobGroup.IsResponseAreaTimezone) { - // использовать часовой пояс РГ + // использовать часовой пояс ЗО var referenceDate = GetReferenceDate(jobGroup, responseAreaName); var offset = GetOffsetForDistributionByResponseArea(responseAreaName); @@ -443,7 +445,7 @@ namespace PARR.DAL.NextRunServices } else { - // не использовать часовой пояс РГ + // не использовать часовой пояс ЗО // так как это классическое расписание еспп, и не используется часовой пояс РГ, то нам нужно считать в часовом поясе УЗ ЕСПП, т.е. МСК var referenceDate = GetReferenceDate(jobGroup, null); @@ -459,12 +461,217 @@ namespace PARR.DAL.NextRunServices } - public async Task GetNextRunForTemplateAsync(Guid templateId, bool isNew) + public async Task GetNextRunForTemplateAsync(Guid templateId, bool isNew) { logger.LogDebug("Получить nextRun для существующего шаблона, templateId: {templateId}, isNew: {isNew}", templateId, isNew); + var template = await templateService.Get() + .AsNoTracking() + .Include(t => t.Job) + .ThenInclude(t => t.Group).ThenInclude(t => t.DistributionConfig).ThenInclude(t => t.DistributionPeriod) + .FirstOrDefaultAsync(t => t.Id == templateId); - throw new NotImplementedException(); + if (template == null) + { + logger.LogError("Не найден шаблон с Id: {templateId}.", templateId); + //throw new ArgumentNullException(nameof(templateId), $"Не найден шаблон с Id: {templateId}"); + return null; + } + + if (template.Job!.Group!.IsAutoDistributionEnabled == true) + { + // включено автораспределение + logger.LogDebug("Включено автораспределение"); + + if (template.Job!.Group.DistributionConfig == null) + { + logger.LogError("Для шаблона {templateId}, jobGroupId {jobGroupId}, указано автораспределение, но отсутствует конфиг в таблице {GroupDistributionConfigs}", template.Id, template.Job.GroupId, nameof(JobGroupDistributionConfig)); + // throw new ArgumentNullException(nameof(JobGroupDistributionConfig), $"Для jobGroupId: {template.Job.GroupId} отсутствует конфигурация автораспределения в таблице {nameof(JobGroupDistributionConfig)}"); + return null; + } + + var startDate = GetDateStart(); + var duration = GetDurationDays(template.Job.Group.DistributionConfig); + var excludeWeekends = template.Job.Group.DistributionConfig.IsExcludeWeekends; + var targetTemplate = new TemplateNextRunDto(template.Id, template.NextRun); + var templateStatusType = TemplateStatusTypeEnum.Used; + + var jobGroup = template.Job.Group; + + // получаем все шаблоны из этой же группы что и template + var allTemplatesQuery = templateService.Get() + .AsNoTracking() + .Include(t => t.Job) + .Where(t => t.StatusTypeId == templateStatusType && t.Job!.GroupId == jobGroup.Id); + + // группировать по РГ + var isGroupingByWorkGroup = jobGroup.DistributionConfig.IsGroupingByWorkGroup; + + // использовать часовой пояс РГ + var isResponseAreaTimeZone = jobGroup.IsResponseAreaTimezone; + + if (isGroupingByWorkGroup) + { + var targetTemplateWorkGroup = await shortcodesService.ApplyShortcodesAsync(template.Job.WorkGroupMask, template); + + // группировать по РГ + logger.LogDebug("Нужно группировать по РГ"); + + // получаем для всех шаблонов РГ и группируем их по РГ + var templatesByWorkGroup = new Dictionary>(StringComparer.OrdinalIgnoreCase); + var allTemplates = await allTemplatesQuery.ToListAsync(); + foreach (var templateItem in allTemplates) + { + var templateWorkGroupName = await shortcodesService.ApplyShortcodesAsync(templateItem.Job!.WorkGroupMask, templateItem); + + // Если даже workGroupName == null, ну и ладно, сгруппируем по null + if (templateWorkGroupName == null) + logger.LogWarning("Для шаблона {id}, с маской РГ '{workGroupMask}', не смог с помощью шорткода определить рабочую группу, шорткод сервис вернул РГ: '{workGroupName}'", + templateItem.Id, templateItem.Job!.WorkGroupMask, templateWorkGroupName); + + // Добавляем шаблон в группу + if (!templatesByWorkGroup.TryGetValue(templateWorkGroupName, out var templatesList)) + { + templatesList = new List