From 76bd6aa11183929a8ab44aff2952f868d04babbe Mon Sep 17 00:00:00 2001 From: Mikhail Trubnikov Date: Wed, 28 Jan 2026 14:40:40 +1000 Subject: [PATCH] =?UTF-8?q?feat(api,dal):=20=D1=81=D1=82=D0=B0=D1=82=D0=B8?= =?UTF-8?q?=D1=81=D1=82=D0=B8=D0=BA=D0=B0=20=D1=80=D0=B0=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B5=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F.=20=D0=A1?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=BE=D0=BA=20=D1=80=D0=B0=D0=B1=D0=BE=D1=87?= =?UTF-8?q?=D0=B8=D1=85=20=D0=B8=20=D0=B2=D1=8B=D1=85=D0=BE=D0=B4=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D1=84=D0=BE=D1=80=D0=BC=D0=B8=D1=80=D1=83=D0=B5?= =?UTF-8?q?=D1=82=D1=81=D1=8F=20=D0=B2=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81?= =?UTF-8?q?=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B8=20=D0=BE=D1=82=20=D0=B2?= =?UTF-8?q?=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=B8=20=D0=9C=D0=A1=D0=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StatTemplateDistributorResponse.cs | 10 +- .../StatTemplateDistributionController.cs | 49 ++++++-- PARR.DAL/NextRunServices/INextRunService.cs | 11 +- PARR.DAL/NextRunServices/NextRunService.cs | 11 +- .../EsppScheduleTransformService.cs | 5 - .../Subservices/ITemplateDistributor.cs | 17 ++- .../Subservices/TemplateDistributor.cs | 118 ++++++++++++++++++ PARR.Test/NextRun/NextRunTest.cs | 11 +- PARR.Test/Worker.cs | 3 + 9 files changed, 209 insertions(+), 26 deletions(-) diff --git a/PARR.API/Contracts/V1/Responses/Statistics/StatTemplateDistributorResponse.cs b/PARR.API/Contracts/V1/Responses/Statistics/StatTemplateDistributorResponse.cs index 0c55fbd7..d1e87774 100644 --- a/PARR.API/Contracts/V1/Responses/Statistics/StatTemplateDistributorResponse.cs +++ b/PARR.API/Contracts/V1/Responses/Statistics/StatTemplateDistributorResponse.cs @@ -19,6 +19,11 @@ /// public int IsDeactivatedAllCount => ItemsList?.Sum(t => t.IsDeactivatedCount) ?? 0; + /// + /// Шаблонов назначенных на выходной день + /// + public int TemplatesInWeekendAllCount => ItemsList?.Sum(t => t.TemplatesInWeekendCount) ?? 0; + public List? ItemsList { get; set; } } @@ -29,16 +34,19 @@ public int AllCount => Statistics?.Sum(t => t.AllCount) ?? 0; public int IsActivatedCount => Statistics?.Sum(t => t.IsActivatedCount) ?? 0; public int IsDeactivatedCount => Statistics?.Sum(t => t.IsDeactivatedCount) ?? 0; + public int TemplatesInWeekendCount => Statistics?.Sum(t => t.TemplatesInWeekendCount) ?? 0; public List? Statistics { get; set; } } public class StatTemplateDistributorDateStat { - public DateOnly Date { get; set; } + public DateTimeOffset Date { get; set; } + public bool IsWorkDay { get; set; } public int AllCount { get; set; } public int IsActivatedCount { get; set; } public int IsDeactivatedCount { get; set; } + public int TemplatesInWeekendCount => !IsWorkDay && Templates != null ? Templates.Count : 0; public List? Templates { get; set; } } diff --git a/PARR.API/Controllers/V1/Statistics/StatTemplateDistributionController.cs b/PARR.API/Controllers/V1/Statistics/StatTemplateDistributionController.cs index d724c613..d5a224d5 100644 --- a/PARR.API/Controllers/V1/Statistics/StatTemplateDistributionController.cs +++ b/PARR.API/Controllers/V1/Statistics/StatTemplateDistributionController.cs @@ -59,6 +59,8 @@ namespace PARR.API.Controllers.V1.Statistics { // eadc5498-dba6-4f10-9b4b-a1653e3c3e61 + //TODO: похоже что timeZoneQuery лишняя + var jobGroup = await jobGroupService.Get() .Include(t => t.DistributionConfig) .ThenInclude(t => t.DistributionPeriod) @@ -88,8 +90,16 @@ namespace PARR.API.Controllers.V1.Statistics ItemsList = null }, true)); + var (dateStart, dateEnd) = GetStartEndPeriod(jobGroup, items.Max(t => t.Template.NextRun), timeZoneQuery.TimeZoneOffsetHours); + // получаем список рабочих дней - var workDays = await GetWorkDaysAsync(jobGroup, items.Max(t => t.Template.NextRun)); + // получет все дни, даже с выходными, чтоб видеть реальную картину + var workDays = await nextRunService.GetWorkDaysAsync(dateStart, dateEnd, false, jobGroup.ReferenceDate); + + // получить список выходных дней (показываем только если IsExcludeWeekends = true) + var weekends = jobGroup.DistributionConfig.IsExcludeWeekends + ? await nextRunService.GetWeekendsAsync(dateStart, dateEnd, jobGroup.ReferenceDate) + : new HashSet(); var responseItemsList = new List(); @@ -114,7 +124,7 @@ namespace PARR.API.Controllers.V1.Statistics foreach (var groupedItem in grouped) { - var statResult = GetResponseByWorkGroup(groupedItem.Key, groupedItem.Value, workDays, timeZoneQuery.TimeZoneOffsetHours); + var statResult = GetResponseByWorkGroup(groupedItem.Key, groupedItem.Value, workDays, weekends, timeZoneQuery.TimeZoneOffsetHours, jobGroup.ReferenceDate); responseItemsList.Add(statResult); } } @@ -123,7 +133,7 @@ namespace PARR.API.Controllers.V1.Statistics // не нужно группировать по рабочим группам // сразу формируем response - responseItemsList.Add(GetResponseByWorkGroup(null, items.Select(t => t.Template).ToList(), workDays, timeZoneQuery.TimeZoneOffsetHours)); + responseItemsList.Add(GetResponseByWorkGroup(null, items.Select(t => t.Template).ToList(), workDays, weekends, timeZoneQuery.TimeZoneOffsetHours, jobGroup.ReferenceDate)); } var response = new StatTemplateDistributorResponse @@ -135,32 +145,46 @@ namespace PARR.API.Controllers.V1.Statistics return Ok(new Response(response, true)); } - - private async Task> GetWorkDaysAsync(JobGroup jobGroup, DateTimeOffset maxNextRunTemplate) + /// + /// Получить дату начала и конца периода + /// + /// + /// + /// + /// + private (DateOnly dateStart, DateOnly dateEnd) GetStartEndPeriod(JobGroup jobGroup, DateTimeOffset maxNextRunTemplate, int timeZoneOffsetHours) { var durationDays = nextRunService.GetDurationDays(jobGroup.DistributionConfig!); - var dateStart = DateOnly.FromDateTime(DateTime.UtcNow); + var dateStart = DateOnly.FromDateTime(DateTime.UtcNow.AddHours(timeZoneOffsetHours)); // последний день, не может быть меньше чем durationDays var dateEnd = dateStart.AddDays(durationDays); // последний день, не может быть меньше чем refDate+durationDays - var sumRefDuration = DateOnly.FromDateTime(jobGroup.ReferenceDate.AddDays(durationDays).Date); + var sumRefDuration = DateOnly.FromDateTime(jobGroup.ReferenceDate.AddHours(timeZoneOffsetHours).AddDays(durationDays).Date); if (dateEnd < sumRefDuration) dateEnd = sumRefDuration; // с DateEnd вообще какая-то шурпатня :( // как наглядно понять ок не ок распределяется, если на каком-то графике dateEnd может ухеать за период распределения // последний день, не может быть меньше чем дата последнего шаблона maxNextRunTemplate - var lastTempalteDate = DateOnly.FromDateTime(maxNextRunTemplate.Date); + var lastTempalteDate = DateOnly.FromDateTime(maxNextRunTemplate.AddHours(timeZoneOffsetHours).Date); if (dateEnd < lastTempalteDate) dateEnd = lastTempalteDate; - return await nextRunService.GetWorkDaysAsync(dateStart, dateEnd, jobGroup.DistributionConfig!.IsExcludeWeekends); + return (dateStart, dateEnd); } - private StatDistributorItemResponse GetResponseByWorkGroup(string? workGroupName, List