From 6a8f90c860aa3fd3ee97626a6c5c915da097645e Mon Sep 17 00:00:00 2001 From: Mikhail Trubnikov Date: Thu, 25 Dec 2025 12:07:53 +1000 Subject: [PATCH] =?UTF-8?q?feat(distributor):=20=D1=80=D0=B5=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20=D0=BB=D0=BE=D0=B3?= =?UTF-8?q?=D0=B8=D0=BA=D0=B0=20=D1=80=D0=B0=D1=81=D0=BF=D1=80=D0=B5=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B2=D1=81=D0=B5=D1=85?= =?UTF-8?q?=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D0=BE=D0=B2=20=D0=B2=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=B5=20DistributeTemplatesAsync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Models/TemplateNextRunResultDto.cs | 14 + .../Subservices/ITemplateDistributor.cs | 29 +- .../Subservices/TemplateDistributor.cs | 587 +++++++++++------- .../Subservices/TemplateDistributor_old.cs | 345 ++++++++++ PARR.Test/NextRun/NextRunTest.cs | 99 +++ PARR.Test/Program.cs | 15 +- PARR.Test/Worker.cs | 14 + 7 files changed, 860 insertions(+), 243 deletions(-) create mode 100644 PARR.DAL/NextRunServices/Models/TemplateNextRunResultDto.cs create mode 100644 PARR.DAL/NextRunServices/Subservices/TemplateDistributor_old.cs create mode 100644 PARR.Test/NextRun/NextRunTest.cs diff --git a/PARR.DAL/NextRunServices/Models/TemplateNextRunResultDto.cs b/PARR.DAL/NextRunServices/Models/TemplateNextRunResultDto.cs new file mode 100644 index 00000000..3477327c --- /dev/null +++ b/PARR.DAL/NextRunServices/Models/TemplateNextRunResultDto.cs @@ -0,0 +1,14 @@ +namespace PARR.DAL.NextRunServices.Models +{ + /// + /// Модель, результат распределения шаблона + /// + public class TemplateNextRunResultDto + { + public Guid Id { get; set; } + + public DateTimeOffset NextRun { get; set; } + + public DateTimeOffset? NextRunOld { get; set; } + } +} diff --git a/PARR.DAL/NextRunServices/Subservices/ITemplateDistributor.cs b/PARR.DAL/NextRunServices/Subservices/ITemplateDistributor.cs index dd8cccd4..7c04ff58 100644 --- a/PARR.DAL/NextRunServices/Subservices/ITemplateDistributor.cs +++ b/PARR.DAL/NextRunServices/Subservices/ITemplateDistributor.cs @@ -1,18 +1,25 @@ -using PARR.DAL.Models; -using PARR.DAL.NextRunServices.Models; +using PARR.DAL.NextRunServices.Models; namespace PARR.DAL.NextRunServices.Subservices { - internal interface ITemplateDistributor + //todo: public -> internal!!! + public interface ITemplateDistributor { - Task> GetValidNextRunAsync(TemplateNextRunDto template, DateOnly startDate, DateOnly endDate, TimeSpan executionTime); + /// + /// Распределяет все шаблоны на указанный период, учитывая план нагрузки. + /// Возвращает список всех шаблонов с обновлёнными датами выполнения. + /// + /// Дата начала распределения + /// Длительность распределения, дней + /// ReferenceDate + /// Спиоск шаблонов для распределения + /// + Task> DistributeTemplatesAsync(DateOnly startDate, int duration, DateTimeOffset referenceDate, List allTemplates); - Task> GetValidNextRunsAsync(List templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime); - - Task> ReassignOutdatedRunsAsync(List templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime); - - Task> RebalanceTemplatesAsync(List templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime); - - Task> ScheduleTemplatesAsync(List templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime); + /// + /// Возвращает актуальную дату выполнения для одного шаблона, учитывая план нагрузки. + /// Возвращает обновлённый шаблон с новой датой выполнения. + /// + Task GetValidNextRunForTemplateAsync(DateOnly startDate, int duration, DateTimeOffset referenceDate, TemplateNextRunDto targetTemplate, List allTemplates); } } diff --git a/PARR.DAL/NextRunServices/Subservices/TemplateDistributor.cs b/PARR.DAL/NextRunServices/Subservices/TemplateDistributor.cs index 25bf5191..21c431d3 100644 --- a/PARR.DAL/NextRunServices/Subservices/TemplateDistributor.cs +++ b/PARR.DAL/NextRunServices/Subservices/TemplateDistributor.cs @@ -10,8 +10,13 @@ namespace PARR.DAL.NextRunServices.Subservices private readonly ILogger logger; private readonly IWeekendDayService weekendDayService; + /// + /// Смещение nextRun на несколько лет вперед, если не смог его рассчитать + /// + private const int offsetYearsIfErrorCalcNextRun = 5; + public TemplateDistributor( - ILogger logger, + ILogger logger, IWeekendDayService weekendDayService ) { @@ -19,289 +24,417 @@ namespace PARR.DAL.NextRunServices.Subservices this.weekendDayService = weekendDayService; } - /// - /// Распределяет шаблоны по рабочим дням в заданном диапазоне с равномерной загрузкой. - /// Возвращает список с Id и новой датой выполнения для каждого шаблона. - /// - public async Task> ScheduleTemplatesAsync(List templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime) - { - logger.LogInformation("Начинаем распределение {Count} шаблонов с {StartDate} по {EndDate}", templates.Count, startDate, endDate); - // Получаем список выходных/праздничных дней в заданном диапазоне - var weekends = (await weekendDayService.GetWeekends(startDate, endDate).ToListAsync()).ToHashSet(); + public Task GetValidNextRunForTemplateAsync(DateOnly startDate, int duration, DateTimeOffset referenceDate, TemplateNextRunDto targetTemplate, List allTemplates) + { + //TODO: + throw new NotImplementedException(); + } + + + + + + public async Task> DistributeTemplatesAsync(DateOnly startDate, int duration, DateTimeOffset referenceDate, List allTemplates) + { + var _startDate = GetStartPeriodDate(startDate, referenceDate); + var _endDate = GetEndPeriodDate(_startDate, duration); + + logger.LogInformation("Начинаем распределение {Count} шаблонов на {duration} дней, с {_startDate} по {_endDate}. ReferenceDate: {referenceDate}", allTemplates.Count, duration, _startDate, _endDate, referenceDate); + + // Получаем список выходных/праздничных дней + var weekends = await GetWeekendsAsync(_startDate, _endDate); + logger.LogDebug("Найдено не рабочих дней: {Count}", weekends.Count); + + foreach (var weekend in weekends) + logger.LogDebug("Не рабочий день: {weekend}", weekend); // Получаем список рабочих дней (исключая выходные и праздники) - var workDays = GetWorkDays(startDate, endDate, weekends); + var workDays = GetWorkDays(_startDate, _endDate, weekends); - logger.LogDebug("Найдено {Count} рабочих дней", workDays.Count); + logger.LogDebug("Найдено рабочих дней: {Count}", workDays.Count); + + foreach (var workDay in workDays) + logger.LogDebug("Рабочий день: {workDay}", workDay); - // Проверяем, есть ли хотя бы один рабочий день if (workDays.Count == 0) { - logger.LogWarning("Нет доступных рабочих дней в диапазоне {StartDate} - {EndDate}", startDate, endDate); - return new List(); - } + logger.LogWarning("Нет доступных рабочих дней в диапазоне {_startDate} - {_endDate}. Возвращаем список шаблонов со смещением: {offsetYearsIfErrorCalcNextRun} лет.", _startDate, _endDate, offsetYearsIfErrorCalcNextRun); - var totalWorks = templates.Count; // Общее количество шаблонов для распределения - var totalDays = workDays.Count; // Общее количество доступных рабочих дней - - // Сортируем шаблоны: сначала без даты (NextRun == null), затем с датой - var sortedTemplates = templates.OrderBy(t => t.NextRun.HasValue ? 1 : 0).ToList(); - - // Вычисляем интервал между работами: сколько "дней" приходится на одну работу - // Это позволяет равномерно распределить работы по всему периоду - // т е например 2 работы распределятся на 30 дней равномерно, не будут идти подряд - var interval = (double)totalDays / totalWorks; - - // Проходим по всем шаблонам и распределяем их равномерно по дням - for (int i = 0; i < totalWorks; i++) - { - // Вычисляем индекс дня, на который нужно назначить i-ю работу - // Используем округление, чтобы получить ближайший день - var dayIndex = (int)Math.Round(i * interval); - - // Защита от возможного выхода за границы массива из-за округления - if (dayIndex >= totalDays) dayIndex = totalDays - 1; - - // Получаем дату из списка рабочих дней по вычисленному индексу - var date = workDays[dayIndex]; - - // Формируем дату выполнения: дата + заданное время (executionTime) - var nextRun = new DateTimeOffset(date.Year, date.Month, date.Day, executionTime.Hours, executionTime.Minutes, executionTime.Seconds, TimeSpan.Zero); - - // Назначаем вычисленную дату выполнения шаблону - sortedTemplates[i].NextRun = nextRun; - - logger.LogDebug("Шаблон {TemplateId} назначен на {NextRun}", sortedTemplates[i].Id, nextRun); - } - - logger.LogInformation("Распределение завершено. Возвращаем {Count} записей.", templates.Count); - - // Возвращаем список шаблонов с обновлёнными датами выполнения - return templates.Select(t => new TemplateNextRunDto { Id = t.Id, NextRun = t.NextRun!.Value }).ToList(); - } - - - /// - /// Обновляет устаревшие даты выполнения шаблонов. - /// Возвращает список шаблонов, чьи даты были изменены. - /// - public async Task> ReassignOutdatedRunsAsync(List templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime) - { - logger.LogInformation("Начинаем переназначение устаревших дат выполнения"); - - // Получаем список выходных/праздничных дней - var weekends = (await weekendDayService.GetWeekends(startDate, endDate).ToListAsync()).ToHashSet(); - var workDays = GetWorkDays(startDate, endDate, weekends).OrderBy(d => d).ToList(); - - var now = DateTimeOffset.UtcNow; - var result = new List(); - - foreach (var template in templates) - { - if (template.NextRun.HasValue && template.NextRun.Value < now) + // смещаем все nextRun вперед + var result = allTemplates.Select(t => new TemplateNextRunResultDto { - logger.LogDebug("Шаблон {TemplateId} с датой {OldDate} устарел", template.Id, template.NextRun); + Id = t.Id, + NextRun = t.NextRun.HasValue + // если есть nextRun, то прибавляем к нему offsetYearsIfErrorCalcNextRun + ? CreateNextRun(t.NextRun.Value.AddYears(offsetYearsIfErrorCalcNextRun), referenceDate) + // если нет nextRun, берем сегодня и прибавляем offsetYearsIfErrorCalcNextRun + : CreateNextRun(DateTimeOffset.UtcNow.AddYears(offsetYearsIfErrorCalcNextRun), referenceDate), + NextRunOld = t.NextRun + }).OrderBy(t => t.NextRun).ToList(); - var newDate = FindNextAvailableDate(workDays, DateOnly.FromDateTime(now.DateTime), DateOnly.FromDateTime(template.NextRun.Value.Date)); - if (newDate.HasValue) + foreach (var item in result) + logger.LogDebug("Нет рабочих дней, сдвинул nextRun у шаблона {Id}, nextRun: {NextRun}, nextRunOld: {NextRunOld}", item.Id, item.NextRun, item.NextRunOld); + + logger.LogInformation("Распределение завершено. Возвращаем {Count} записей. NextRun не изменился у {oldCount} шт. Обновлен NextRun у {changedCount} шт.", result.Count, result.Count(t => t.NextRun == t.NextRunOld), result.Count(t => t.NextRun != t.NextRunOld)); + + return result; + } + + var totalWorks = allTemplates.Count; + var totalDays = workDays.Count; + + // интервал между шаблонами + var interval = GetInterval(totalDays, totalWorks); + + // строим план, дни на которые должны быть назанчены шаблоны + var plan = GetPlan(totalWorks, totalDays, workDays); + + + // Подсчитываем, сколько работ должно быть на каждый день + var expectedDailyLoad = new Dictionary(); + + foreach (var day in plan) + expectedDailyLoad[day] = expectedDailyLoad.GetValueOrDefault(day, 0) + 1; + + #region Группируем шаблоны по дням, на которые они уже назначены + + var templatesByDay = new Dictionary>(); + + foreach (var template in allTemplates) + { + if (template.NextRun.HasValue) + { + var day = DateOnly.FromDateTime(template.NextRun.Value.Date); + if (!templatesByDay.ContainsKey(day)) { - var nextRun = new DateTimeOffset(newDate.Value.Year, newDate.Value.Month, newDate.Value.Day, executionTime.Hours, executionTime.Minutes, executionTime.Seconds, TimeSpan.Zero); - template.NextRun = nextRun; - - logger.LogDebug("Шаблон {TemplateId} перенесен на {NewDate}", template.Id, nextRun); - - result.Add(new TemplateNextRunDto { Id = template.Id, NextRun = nextRun }); + templatesByDay[day] = new List(); } - else + templatesByDay[day].Add(template); + } + } + + #endregion + + #region Подсчитываем текущую нагрузку на каждый день + + var currentDailyLoad = new Dictionary(); + + foreach (var day in workDays) + { + currentDailyLoad[day] = 0; + } + + foreach (var kvp in templatesByDay) + { + var day = kvp.Key; + var count = kvp.Value.Count; + currentDailyLoad[day] = count; + } + + #endregion + + #region Определяем, какие шаблоны нужно оставить, а какие — переназначить + + var templatesToKeep = new List(); + var templatesToReassign = new List(); + + foreach (var kvp in templatesByDay) + { + var day = kvp.Key; + var templates = kvp.Value; + + // Если день — нерабочий, то все шаблоны нужно переназначить + if (!workDays.Contains(day)) + { + foreach (var template in templates) { - logger.LogWarning("Не удалось найти новую дату для шаблона {TemplateId}", template.Id); + templatesToReassign.Add(template); + logger.LogDebug("Шаблон {TemplateId} назначен на нерабочий день {NextRun} — будет переназначен.", template.Id, template.NextRun); + } + continue; + } + + var expectedCount = expectedDailyLoad.GetValueOrDefault(day, 0); + var currentCount = templates.Count; + + if (currentCount <= expectedCount) + { + // Все шаблоны в этом дне можно оставить + foreach (var template in templates) + { + templatesToKeep.Add(new TemplateNextRunResultDto + { + Id = template.Id, + NextRun = template.NextRun.Value, + NextRunOld = template.NextRun + }); + logger.LogDebug("Шаблон {TemplateId} оставлен на дату {NextRun}, т.к. нагрузка в день не превышена.", template.Id, template.NextRun); + } + } + else + { + // Оставляем только expectedCount штук, остальные — переназначить + for (int i = 0; i < expectedCount; i++) + { + var template = templates[i]; + templatesToKeep.Add(new TemplateNextRunResultDto + { + Id = template.Id, + NextRun = template.NextRun.Value, + NextRunOld = template.NextRun + }); + logger.LogDebug("Шаблон {TemplateId} оставлен на дату {NextRun}, т.к. нагрузка в день не превышена.", template.Id, template.NextRun); + } + + for (int i = expectedCount; i < currentCount; i++) + { + var template = templates[i]; + templatesToReassign.Add(template); + logger.LogDebug("Шаблон {TemplateId} переназначается, т.к. нагрузка в день превышена.", template.Id); } } } - logger.LogInformation("Переназначение завершено. Изменено {Count} записей.", result.Count); + #endregion - return result; - } + #region Добавляем шаблоны, у которых не было даты — тоже нужно распределить - - /// - /// Перераспределяет только те шаблоны, у которых дата недействительна (выходной, вне периода). - /// Возвращает список всех шаблонов с новыми или старыми датами. - /// - public async Task> RebalanceTemplatesAsync(List templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime) - { - logger.LogInformation("Начинаем перераспределение шаблонов"); - - // Получаем список выходных/праздничных дней - var weekends = (await weekendDayService.GetWeekends(startDate, endDate).ToListAsync()).ToHashSet(); - - var workDays = GetWorkDays(startDate, endDate, weekends); - var validDates = new HashSet(workDays); - - // Отфильтруем шаблоны с недействительной датой - var unassigned = templates.Where(t => !t.NextRun.HasValue || !validDates.Contains(DateOnly.FromDateTime(t.NextRun.Value.Date))).ToList(); - - logger.LogDebug("Найдено {Count} шаблонов для переназначения", unassigned.Count); - - // Перераспределяем только невалидные - var reassigned = await ScheduleTemplatesAsync(unassigned, startDate, endDate, executionTime); - - // Возвращаем все шаблоны с их датами - var result = templates.Select(t => new TemplateNextRunDto { Id = t.Id, NextRun = t.NextRun!.Value }).ToList(); - - logger.LogInformation("Перераспределение завершено. Возвращаем {Count} записей.", result.Count); - - return result; - } - - - /// - /// Возвращает актуальную дату выполнения для одного шаблона. - /// Результат — список из одного элемента (если успешно) или пустой. - /// - public async Task> GetValidNextRunAsync(TemplateNextRunDto template, DateOnly startDate, DateOnly endDate, TimeSpan executionTime) - { - logger.LogDebug("Запрос актуальной даты для шаблона {TemplateId}", template.Id); - - // Получаем список выходных/праздничных дней - var weekends = (await weekendDayService.GetWeekends(startDate, endDate).ToListAsync()).ToHashSet(); - - var nextRun = await GetValidNextRunInternalAsync(template, startDate, endDate, weekends, executionTime); - if (nextRun.HasValue) + foreach (var template in allTemplates) { - logger.LogDebug("Шаблон {TemplateId} имеет актуальную дату {NextRun}", template.Id, nextRun); - return new List { new TemplateNextRunDto { Id = template.Id, NextRun = nextRun.Value } }; + if (!template.NextRun.HasValue) + { + templatesToReassign.Add(template); + logger.LogDebug("Шаблон {TemplateId} не имел даты — будет распределён.", template.Id); + } } - logger.LogWarning("Не удалось получить актуальную дату для шаблона {TemplateId}", template.Id); - return new List(); - } + #endregion + #region Распределяем шаблоны, которые нужно переназначить, учитывая нагрузку - /// - /// Возвращает список актуальных дат выполнения для всех переданных шаблонов. - /// - public async Task> GetValidNextRunsAsync(List templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime) - { - logger.LogDebug("Запрос актуальных дат для {Count} шаблонов", templates.Count); + var updatedTemplates = new List(templatesToKeep); - // Получаем список выходных/праздничных дней - var weekends = (await weekendDayService.GetWeekends(startDate, endDate).ToListAsync()).ToHashSet(); - - var result = new List(); - - foreach (var template in templates) + foreach (var template in templatesToReassign) { - var nextRun = await GetValidNextRunInternalAsync(template, startDate, endDate, weekends, executionTime); + var nextRun = FindNextAvailableDateWithLoad(currentDailyLoad, workDays, referenceDate, expectedDailyLoad); if (nextRun.HasValue) { - result.Add(new TemplateNextRunDto { Id = template.Id, NextRun = nextRun.Value }); - } - } + var day = DateOnly.FromDateTime(nextRun.Value.Date); + currentDailyLoad[day]++; - logger.LogInformation("Получено {Count} актуальных дат выполнения", result.Count); - - return result; - } - - - /// - /// Внутренний метод для получения актуальной даты выполнения для одного шаблона. - /// - private async Task GetValidNextRunInternalAsync(TemplateNextRunDto template, DateOnly startDate, DateOnly endDate, HashSet weekends, TimeSpan executionTime) - { - var workDays = GetWorkDays(startDate, endDate, weekends); - var today = DateOnly.FromDateTime(DateTime.UtcNow); - - // Если у шаблона нет даты, назначаем первую доступную - if (!template.NextRun.HasValue) - { - var firstDay = workDays.FirstOrDefault(); - if (firstDay != default) - { - var nextRun = new DateTimeOffset(firstDay.Year, firstDay.Month, firstDay.Day, executionTime.Hours, executionTime.Minutes, executionTime.Seconds, TimeSpan.Zero); - logger.LogDebug("Шаблон {TemplateId} получил первую доступную дату: {NextRun}", template.Id, nextRun); - return nextRun; - } - - logger.LogWarning("Нет доступных рабочих дней для шаблона {TemplateId}", template.Id); - return null; - } - - var scheduledDate = DateOnly.FromDateTime(template.NextRun.Value.Date); - - // Если дата валидна и не устарела — возвращаем как есть - if (workDays.Contains(scheduledDate)) - { - if (template.NextRun.Value < DateTimeOffset.UtcNow) - { - logger.LogDebug("Дата шаблона {TemplateId} ({Date}) устарела", template.Id, template.NextRun); - var newDate = FindNextAvailableDate(workDays, today, scheduledDate); - if (newDate.HasValue) + updatedTemplates.Add(new TemplateNextRunResultDto { - var nextRun = new DateTimeOffset(newDate.Value.Year, newDate.Value.Month, newDate.Value.Day, executionTime.Hours, executionTime.Minutes, executionTime.Seconds, TimeSpan.Zero); - logger.LogDebug("Шаблон {TemplateId} перенесен на {NewDate}", template.Id, nextRun); - return nextRun; - } - logger.LogWarning("Не удалось найти новую дату для устаревшего шаблона {TemplateId}", template.Id); - return null; + Id = template.Id, + NextRun = nextRun.Value, + NextRunOld = template.NextRun + }); + + logger.LogDebug("Шаблону {TemplateId} назначен NextRun: {NextRun}, NextRunOld: {NextRunOld}", template.Id, nextRun, template.NextRun); + } + else + { + logger.LogWarning("Не удалось назначить дату для шаблона {TemplateId}", template.Id); + + // Возвращаем с ошибкой + updatedTemplates.Add(new TemplateNextRunResultDto + { + Id = template.Id, + NextRun = CreateNextRun(DateTimeOffset.UtcNow.AddYears(offsetYearsIfErrorCalcNextRun), referenceDate), + NextRunOld = template.NextRun + }); } - // Дата валидна и не устарела - logger.LogDebug("Шаблон {TemplateId} имеет действительную дату: {NextRun}", template.Id, template.NextRun); - return template.NextRun; } - // Дата недействительна (выходной или вне периода) — ищем ближайшую - var closest = FindClosestValidDate(workDays, scheduledDate); - if (closest.HasValue) + #endregion + + + updatedTemplates = updatedTemplates.OrderBy(t => t.NextRun).ToList(); + + logger.LogDebug("--- Результат распределения: ---"); + foreach (var item in updatedTemplates) + logger.LogDebug("Шаблон {templateId}, NextRun: {NextRun}, NextRunOld: {NextRunOld}", item.Id, item.NextRun, item.NextRunOld); + + logger.LogInformation("Распределение завершено. Возвращаем {Count} записей. NextRun не изменился у {oldCount} шт. Обновлен NextRun у {changedCount} шт.", updatedTemplates.Count, updatedTemplates.Count(t => t.NextRun == t.NextRunOld), updatedTemplates.Count(t => t.NextRun != t.NextRunOld)); + + return updatedTemplates; + } + + + /// + /// Находит следующий день с доступной нагрузкой, учитывая expectedDailyLoad + /// + /// + /// + /// + /// + /// + private DateTimeOffset? FindNextAvailableDateWithLoad(Dictionary currentDailyLoad, List workDays, DateTimeOffset referenceDate, Dictionary expectedDailyLoad) + { + foreach (var day in workDays) { - var nextRun = new DateTimeOffset(closest.Value.Year, closest.Value.Month, closest.Value.Day, executionTime.Hours, executionTime.Minutes, executionTime.Seconds, TimeSpan.Zero); - logger.LogDebug("Шаблон {TemplateId} перемещен на ближайшую дату: {NextRun}", template.Id, nextRun); - return nextRun; + var expected = expectedDailyLoad.GetValueOrDefault(day, 0); + var current = currentDailyLoad.GetValueOrDefault(day, 0); + + if (current < expected) + { + return CreateNextRun(day, referenceDate); + } } - logger.LogWarning("Не удалось найти подходящую дату для шаблона {TemplateId}", template.Id); - return null; + // Если нет дня с доступной нагрузкой — ищем день с минимальной текущей нагрузкой, но не превышающий план + foreach (var day in workDays) + { + var expected = expectedDailyLoad.GetValueOrDefault(day, 0); + var current = currentDailyLoad.GetValueOrDefault(day, 0); + + if (current < expected) + { + return CreateNextRun(day, referenceDate); + } + } + + // Если все дни уже загружены по плану — ищем день с минимальной нагрузкой + var minLoad = currentDailyLoad.Values.Min(); + var minDay = currentDailyLoad.First(x => x.Value == minLoad).Key; + + return CreateNextRun(minDay, referenceDate); + } + + + + /// + /// Получить план: дни, на которые должны быть назначены шаблоны + /// + /// + /// + /// + /// + private List GetPlan(int totalWorks, int totalDays, List workDays) + { + var interval = GetInterval(totalDays, totalWorks); + + var plan = new List(); + + for (int i = 0; i < totalWorks; i++) + { + #region Вынести в метод + // Вычисляем индекс дня, на который нужно назначить i-ю работу + //var dayIndex = (int)Math.Round(i * interval); + var dayIndex = (int)Math.Floor(i * interval); + + // Защита от возможного выхода за границы массива из-за округления + if (dayIndex >= totalDays) + dayIndex = totalDays - 1; + #endregion + + var date = workDays[dayIndex]; + plan.Add(date); + } + + return plan; + // return new HashSet(plan); } /// - /// Ищет следующую доступную дату после указанной, если не находит — ищет назад. + /// Рассчитать интервал между шаблонами /// - private DateOnly? FindNextAvailableDate(List workDays, DateOnly afterDate, DateOnly originalDate) + /// Кол-во рабочих дней + /// Кол-во шаблонов + /// + private double GetInterval(int totalDays, int totalWorks) { - var future = workDays.FirstOrDefault(d => d > afterDate); + var interval = (double)totalDays / totalWorks; - if (future != default) - return future; - - var past = workDays.Where(t => t >= originalDate).OrderByDescending(t => t).FirstOrDefault(); - - return past != default ? (DateOnly?)past : null; + return interval; } /// - /// Ищет ближайшую дату (вперед или назад) к заданной, среди доступных рабочих дней. + /// Создать NextRun (из DateTimeOffset + refDate) /// - private DateOnly? FindClosestValidDate(List workDays, DateOnly targetDate) + /// Откуда взять дату + /// Откуда взять время + /// + private DateTimeOffset CreateNextRun(DateTimeOffset date, DateTimeOffset refDate) { - var future = workDays.FirstOrDefault(t => t >= targetDate); + return CreateNextRun(DateOnly.FromDateTime(date.Date), refDate); + } - if (future != default) - return future; + /// + /// Создать NextRun (из DateOnly + refDate) + /// + /// + /// + /// + private DateTimeOffset CreateNextRun(DateOnly date, DateTimeOffset refDate) + { + return new DateTimeOffset(date.Year, date.Month, date.Day, refDate.Hour, refDate.Minute, refDate.Second, TimeSpan.Zero); + } - var past = workDays.Where(t => t <= targetDate).OrderByDescending(t => t).FirstOrDefault(); - return past != default ? (DateOnly?)past : null; + /// + /// Получить дату начала распределения относительно StartDate, ReferenceDate и текущей даты + /// + /// + /// + /// + private DateOnly GetStartPeriodDate(DateOnly startDate, DateTimeOffset referenceDate) + { + // дата начала не может быть раньше referenceDate + // и не может быть раньше чем сегодня + + var dateStart = startDate; + + // если dateStart < чем refDate, то берем refDate + if (dateStart < DateOnly.FromDateTime(referenceDate.Date)) + dateStart = DateOnly.FromDateTime(referenceDate.Date); + + // если dateStart < чем текущая дата, то берем ьекущую дату + if (dateStart < DateOnly.FromDateTime(DateTime.UtcNow.Date)) + dateStart = DateOnly.FromDateTime(DateTime.UtcNow.Date); + + return dateStart; + } + + + /// + /// Получить дату конца периода распределения + /// + /// + /// + /// + private DateOnly GetEndPeriodDate(DateOnly startDate, int duration) + { + // duration — количество дней, включая startDate + // поэтому вычитаем 1, чтобы получить сдвиг + + return startDate.AddDays(duration - 1); + } + + + /// + /// Получить список выходных дней + /// + /// + /// + /// + private async Task> GetWeekendsAsync(DateOnly startDate, DateOnly endDate) + { + var weekends = await weekendDayService.GetWeekends(startDate, endDate).ToListAsync(); + + return weekends.ToHashSet(); } /// /// Возвращает список рабочих дней в диапазоне, исключая выходные и праздники. /// + /// + /// + /// + /// private List GetWorkDays(DateOnly start, DateOnly end, HashSet weekends) { var result = new List(); @@ -309,6 +442,7 @@ namespace PARR.DAL.NextRunServices.Subservices while (date <= end) { + var dayOfWeek = date.DayOfWeek; if (!weekends.Contains(date)) { result.Add(date); @@ -318,5 +452,6 @@ namespace PARR.DAL.NextRunServices.Subservices return result; } + } } diff --git a/PARR.DAL/NextRunServices/Subservices/TemplateDistributor_old.cs b/PARR.DAL/NextRunServices/Subservices/TemplateDistributor_old.cs new file mode 100644 index 00000000..93c7ba2f --- /dev/null +++ b/PARR.DAL/NextRunServices/Subservices/TemplateDistributor_old.cs @@ -0,0 +1,345 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using PARR.DAL.NextRunServices.Models; +using PARR.DAL.Services.Interfaces; + +namespace PARR.DAL.NextRunServices.Subservices +{ + internal class TemplateDistributor_old //: ITemplateDistributor + { + private readonly ILogger logger; + private readonly IWeekendDayService weekendDayService; + + public TemplateDistributor_old( + ILogger logger, + IWeekendDayService weekendDayService + ) + { + this.logger = logger; + this.weekendDayService = weekendDayService; + } + + /// + /// Распределяет все шаблоны на указанный период, учитывая план нагрузки. + /// Возвращает список всех шаблонов с обновлёнными датами выполнения. + /// + public async Task> DistributeTemplatesAsync( + DateOnly startDate, DateOnly endDate, DateTimeOffset referenceDate, List allTemplates) + { + logger.LogInformation("Начинаем распределение {Count} шаблонов с {StartDate} по {EndDate}", allTemplates.Count, startDate, endDate); + + // Получаем список выходных/праздничных дней + var weekends = await GetWeekendsAsync(startDate, endDate); + + // Получаем список рабочих дней (исключая выходные и праздники) + var workDays = GetWorkDays(startDate, endDate, weekends); + + logger.LogDebug("Найдено {Count} рабочих дней", workDays.Count); + + if (workDays.Count == 0) + { + logger.LogWarning("Нет доступных рабочих дней в диапазоне {StartDate} - {EndDate}", startDate, endDate); + return allTemplates; + } + + var totalWorks = allTemplates.Count; + var totalDays = workDays.Count; + + //------------- + + // Вычисляем интервал между работами: сколько "дней" приходится на одну работу + var interval = (double)totalDays / totalWorks; + + // Создаём новый список шаблонов с обновлёнными датами + var updatedTemplates = new List(); + + for (int i = 0; i < totalWorks; i++) + { + // Вычисляем индекс дня, на который нужно назначить i-ю работу + var dayIndex = (int)Math.Round(i * interval); + + // Защита от возможного выхода за границы массива из-за округления + if (dayIndex >= totalDays) dayIndex = totalDays - 1; + + var date = workDays[dayIndex]; + + // Формируем дату выполнения: дата + время из referenceDate + var nextRun = new DateTimeOffset(date.Year, date.Month, date.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset); + + //------------------ + // Проверяем, не раньше ли дата, чем referenceDate (не включая саму referenceDate) + if (nextRun < referenceDate) + { + logger.LogWarning("Шаблон {TemplateId} имеет дату {NextRun}, которая раньше referenceDate {ReferenceDate}. Переносим на ближайший рабочий день после referenceDate (включая её).", allTemplates[i].Id, nextRun, referenceDate); + + // Ищем ближайший день после referenceDate (включая его) + var newDate = FindNextAvailableDate(workDays, DateOnly.FromDateTime(referenceDate.DateTime), date); + if (newDate.HasValue) + { + nextRun = new DateTimeOffset(newDate.Value.Year, newDate.Value.Month, newDate.Value.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset); + } + else + { + // Если не нашли — используем первый рабочий день + logger.LogWarning("Не удалось найти дату после referenceDate для шаблона {TemplateId}. Используем первый рабочий день.", allTemplates[i].Id); + var firstDay = workDays[0]; + nextRun = new DateTimeOffset(firstDay.Year, firstDay.Month, firstDay.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset); + } + } + //------------------ + + var updatedTemplate = new TemplateNextRunDto + { + Id = allTemplates[i].Id, + NextRun = nextRun + }; + + updatedTemplates.Add(updatedTemplate); + + logger.LogDebug("Шаблон {TemplateId} назначен на {NextRun}", updatedTemplate.Id, updatedTemplate.NextRun); + } + + logger.LogInformation("Распределение завершено. Возвращаем {Count} записей.", updatedTemplates.Count); + + return updatedTemplates; + } + + /// + /// Возвращает актуальную дату выполнения для одного шаблона, учитывая план нагрузки. + /// Возвращает обновлённый шаблон с новой датой выполнения. + /// + public async Task GetValidNextRunForTemplateAsync( + DateOnly startDate, DateOnly endDate, DateTimeOffset referenceDate, TemplateNextRunDto targetTemplate, List allTemplates) + { + logger.LogDebug("Запрос актуальной даты для шаблона {TemplateId}", targetTemplate.Id); + + // Получаем список выходных/праздничных дней + var weekends = await GetWeekendsAsync(startDate, endDate); + + // Получаем список рабочих дней + var workDays = GetWorkDays(startDate, endDate, weekends); + + logger.LogDebug("Найдено {Count} рабочих дней", workDays.Count); + + if (workDays.Count == 0) + { + logger.LogWarning("Нет доступных рабочих дней в диапазоне {StartDate} - {EndDate}", startDate, endDate); + + // Возвращаем дату, равную referenceDate, если не нашли рабочих дней + logger.LogWarning("Используем referenceDate как дату выполнения для шаблона {TemplateId}", targetTemplate.Id); + return new TemplateNextRunDto { Id = targetTemplate.Id, NextRun = referenceDate }; + } + + var now = DateTimeOffset.UtcNow; + + // Проверяем, устарела ли дата + if (targetTemplate.NextRun.HasValue && targetTemplate.NextRun.Value < now) + { + logger.LogDebug("Дата шаблона {TemplateId} ({Date}) устарела", targetTemplate.Id, targetTemplate.NextRun); + + // Строим план на основе оставшихся шаблонов (без устаревшего) + var remainingTemplates = allTemplates.Where(t => t.Id != targetTemplate.Id && t.NextRun.HasValue).ToList(); + + // Вычисляем план для оставшихся + var plan = await BuildPlanAsync(startDate, endDate, referenceDate, remainingTemplates, weekends, workDays); + + // Находим следующую дату в плане, не занятую + var newDate = FindNextAvailableDateNotInPlan(workDays, DateOnly.FromDateTime(now.DateTime), plan); + if (newDate.HasValue) + { + var nextRun = new DateTimeOffset(newDate.Value.Year, newDate.Value.Month, newDate.Value.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset); + logger.LogDebug("Шаблон {TemplateId} перенесен на {NewDate}", targetTemplate.Id, nextRun); + + return new TemplateNextRunDto { Id = targetTemplate.Id, NextRun = nextRun }; + } + else + { + logger.LogWarning("Не удалось найти новую дату для устаревшего шаблона {TemplateId} по плану. Используем ближайшую дату.", targetTemplate.Id); + + // Используем ближайшую дату после now, не в плане + var fallbackDate = FindNextAvailableDate(workDays, DateOnly.FromDateTime(now.DateTime), DateOnly.FromDateTime(now.DateTime)); + if (fallbackDate.HasValue) + { + var nextRun = new DateTimeOffset(fallbackDate.Value.Year, fallbackDate.Value.Month, fallbackDate.Value.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset); + logger.LogDebug("Шаблон {TemplateId} перенесен на резервную дату {NewDate}", targetTemplate.Id, nextRun); + + return new TemplateNextRunDto { Id = targetTemplate.Id, NextRun = nextRun }; + } + else + { + logger.LogWarning("Не удалось найти резервную дату для шаблона {TemplateId}. Используем первую рабочую дату.", targetTemplate.Id); + + var firstDay = workDays[0]; + var nextRun = new DateTimeOffset(firstDay.Year, firstDay.Month, firstDay.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset); + + return new TemplateNextRunDto { Id = targetTemplate.Id, NextRun = nextRun }; + } + } + } + + // Если дата не устарела, но не соответствует плану — переносим + var currentPlan = await BuildPlanAsync(startDate, endDate, referenceDate, allTemplates, weekends, workDays); + var expectedDate = currentPlan.FirstOrDefault(d => d == DateOnly.FromDateTime(targetTemplate.NextRun.Value.DateTime)); + if (expectedDate == default) + { + logger.LogDebug("Дата шаблона {TemplateId} ({Date}) не соответствует плану. Переносим на ближайшую дату в плане.", targetTemplate.Id, targetTemplate.NextRun); + + var newDate = FindNextAvailableDateNotInPlan(workDays, DateOnly.FromDateTime(targetTemplate.NextRun.Value.DateTime), currentPlan); + if (newDate.HasValue) + { + var nextRun = new DateTimeOffset(newDate.Value.Year, newDate.Value.Month, newDate.Value.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset); + logger.LogDebug("Шаблон {TemplateId} перенесен на {NewDate}", targetTemplate.Id, nextRun); + + return new TemplateNextRunDto { Id = targetTemplate.Id, NextRun = nextRun }; + } + else + { + logger.LogWarning("Не удалось найти новую дату для шаблона {TemplateId} по плану. Используем ближайшую дату.", targetTemplate.Id); + + // Используем ближайшую дату, не в плане + var fallbackDate = FindNextAvailableDate(workDays, DateOnly.FromDateTime(targetTemplate.NextRun.Value.DateTime), DateOnly.FromDateTime(targetTemplate.NextRun.Value.DateTime)); + if (fallbackDate.HasValue) + { + var nextRun = new DateTimeOffset(fallbackDate.Value.Year, fallbackDate.Value.Month, fallbackDate.Value.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset); + logger.LogDebug("Шаблон {TemplateId} перенесен на резервную дату {NewDate}", targetTemplate.Id, nextRun); + + return new TemplateNextRunDto { Id = targetTemplate.Id, NextRun = nextRun }; + } + else + { + logger.LogWarning("Не удалось найти резервную дату для шаблона {TemplateId}. Используем первую рабочую дату.", targetTemplate.Id); + + var firstDay = workDays[0]; + var nextRun = new DateTimeOffset(firstDay.Year, firstDay.Month, firstDay.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset); + + return new TemplateNextRunDto { Id = targetTemplate.Id, NextRun = nextRun }; + } + } + } + + logger.LogDebug("Шаблон {TemplateId} имеет актуальную дату: {NextRun}", targetTemplate.Id, targetTemplate.NextRun); + + // Проверяем, не раньше ли дата, чем referenceDate (не включая саму referenceDate) + if (targetTemplate.NextRun < referenceDate) + { + logger.LogWarning("Шаблон {TemplateId} имеет дату {NextRun}, которая раньше referenceDate {ReferenceDate}. Переносим на ближайшую дату.", targetTemplate.Id, targetTemplate.NextRun, referenceDate); + + var newDate = FindNextAvailableDate(workDays, DateOnly.FromDateTime(referenceDate.DateTime), DateOnly.FromDateTime(targetTemplate.NextRun.Value.DateTime)); + if (newDate.HasValue) + { + var nextRun = new DateTimeOffset(newDate.Value.Year, newDate.Value.Month, newDate.Value.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset); + logger.LogDebug("Шаблон {TemplateId} перенесен на {NewDate}", targetTemplate.Id, nextRun); + + return new TemplateNextRunDto { Id = targetTemplate.Id, NextRun = nextRun }; + } + else + { + logger.LogWarning("Не удалось найти дату после referenceDate для шаблона {TemplateId}. Используем первую рабочую дату.", targetTemplate.Id); + + var firstDay = workDays[0]; + var nextRun = new DateTimeOffset(firstDay.Year, firstDay.Month, firstDay.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset); + + return new TemplateNextRunDto { Id = targetTemplate.Id, NextRun = nextRun }; + } + } + + return targetTemplate; + } + + /// + /// Строит план работ на основе списка шаблонов + /// + private async Task> BuildPlanAsync( + DateOnly startDate, DateOnly endDate, DateTimeOffset referenceDate, List templates, HashSet weekends, List workDays) + { + var totalWorks = templates.Count; + var totalDays = workDays.Count; + + if (totalDays == 0) return new List(); + + var interval = (double)totalDays / totalWorks; + var plan = new List(); + + for (int i = 0; i < totalWorks; i++) + { + var dayIndex = (int)Math.Round(i * interval); + if (dayIndex >= totalDays) dayIndex = totalDays - 1; + + var date = workDays[dayIndex]; + plan.Add(date); + } + + return plan.OrderBy(d => d).ToList(); + } + + /// + /// Ищет следующую дату в плане, которая не занята + /// + private DateOnly? FindNextAvailableDateNotInPlan(List workDays, DateOnly afterDate, List plan) + { + var planSet = new HashSet(plan); + + // Ищем вперед + foreach (var day in workDays) + { + if (day > afterDate && !planSet.Contains(day)) + { + return day; + } + } + + // Ищем назад + foreach (var day in workDays.AsEnumerable().Reverse()) + { + if (day <= afterDate && !planSet.Contains(day)) + { + return day; + } + } + + return null; + } + + /// + /// Ищет следующую доступную дату после указанной, если не находит — ищет назад. + /// + private DateOnly? FindNextAvailableDate(List workDays, DateOnly afterDate, DateOnly originalDate) + { + var future = workDays.FirstOrDefault(d => d >= afterDate); + if (future != default) return future; + + var past = workDays.Where(t => t >= originalDate).OrderByDescending(t => t).FirstOrDefault(); + return past != default ? (DateOnly?)past : null; + } + + /// + /// Возвращает список рабочих дней в диапазоне, исключая выходные и праздники. + /// + private List GetWorkDays(DateOnly start, DateOnly end, HashSet weekends) + { + var result = new List(); + var date = start; + + while (date <= end) + { + var dayOfWeek = date.DayOfWeek; + if (!weekends.Contains(date)) + { + result.Add(date); + } + date = date.AddDays(1); + } + + return result; + } + + /// + /// Получить список выходных дней + /// + private async Task> GetWeekendsAsync(DateOnly startDate, DateOnly endDate) + { + var weekends = await weekendDayService.GetWeekends(startDate, endDate).ToListAsync(); + return weekends.ToHashSet(); + } + } +} \ No newline at end of file diff --git a/PARR.Test/NextRun/NextRunTest.cs b/PARR.Test/NextRun/NextRunTest.cs new file mode 100644 index 00000000..4e431db9 --- /dev/null +++ b/PARR.Test/NextRun/NextRunTest.cs @@ -0,0 +1,99 @@ +using PARR.DAL.NextRunServices.Models; +using PARR.DAL.NextRunServices.Subservices; + +namespace PARR.Test.NextRun +{ + internal class NextRunTest + { + private readonly ITemplateDistributor templateDistributor; + + public NextRunTest(ITemplateDistributor templateDistributor) + { + this.templateDistributor = templateDistributor; + } + + int periodDays = 5; + DateTimeOffset referenceDate = new DateTimeOffset(2025, 12, 1, 7, 0, 0, TimeSpan.Zero); + + public async Task Test() + { + await DistributeTemplatesAsync(); + } + + /// + /// распределить шаблоны + /// + private async Task DistributeTemplatesAsync() + { + var templates = await templateDistributor.DistributeTemplatesAsync(GetDateStart(), periodDays, referenceDate, GetTemplates()); + + //await GetValidNextRunForTemplateAsync(templates); + } + + + /// + /// получить nextRun + /// + private async Task GetValidNextRunForTemplateAsync(List? templates = null) + { + //var templateList = templates ?? GetTemplates(); + + //var targetTemplate = new TemplateNextRunDto { Id= Guid.Parse("4C2F61E9-7B47-4049-A881-9200D71303F7"), NextRun= new DateTimeOffset(2025, 12, 1, 3, 0, 0, TimeSpan.Zero) }; + + //var distribTemplates = await templateDistributor.GetValidNextRunForTemplateAsync(GetDateStart(), GetDateEnd(), referenceDate, targetTemplate, templateList); + + //// еще раз + //templateList.Add(targetTemplate); + //var targetTemplate2 = new TemplateNextRunDto { Id = Guid.Parse("4C2F61E9-7B47-4049-A881-9200D71303F7"), NextRun = new DateTimeOffset(2025, 12, 1, 3, 0, 0, TimeSpan.Zero) }; + //var distribTemplates2 = await templateDistributor.GetValidNextRunForTemplateAsync(GetDateStart(), GetDateEnd(), referenceDate, targetTemplate2, templateList); + + } + + + private DateOnly GetDateStart() + { + var today = DateOnly.FromDateTime(DateTime.UtcNow.Date); + + var refDate = DateOnly.FromDateTime(referenceDate.Date); + + return today < refDate ? refDate : today; + } + + //private DateOnly GetDateEnd() + //{ + // return GetDateStart().AddDays(periodDays); + //} + + + private List GetTemplates() + { + var templates = new List(); + + templates.Add(new TemplateNextRunDto { Id = Guid.Parse("55F047E6-3631-4676-8959-FC75FF6BC963"), NextRun = new DateTimeOffset(2027, 1, 15, 3, 0, 0, TimeSpan.Zero) }); + templates.Add(new TemplateNextRunDto { Id = Guid.Parse("DD15E9F6-AAD4-4FE6-9441-7E7FF67781A3"), NextRun = new DateTimeOffset(2025, 12, 24, 3, 0, 0, TimeSpan.Zero) }); + + templates.Add(new TemplateNextRunDto { Id = Guid.Parse("78EE4A94-DBEE-41D1-B51A-CE3BD5AEE4D4"), NextRun = new DateTimeOffset(2025, 12, 29, 3, 0, 0, TimeSpan.Zero) }); + templates.Add(new TemplateNextRunDto { Id = Guid.Parse("B397F1FE-DF2B-4B50-B8D6-2DABECD3D5A1"), NextRun = new DateTimeOffset(2025, 12, 18, 3, 0, 0, TimeSpan.Zero) }); + templates.Add(new TemplateNextRunDto { Id = Guid.Parse("619D9621-2B45-4108-AF54-CF96862A8E9B"), NextRun = new DateTimeOffset(2025, 12, 18, 3, 0, 0, TimeSpan.Zero) }); + templates.Add(new TemplateNextRunDto { Id = Guid.Parse("D74D831C-D435-46BF-9B5F-A24884AAA7DC"), NextRun = new DateTimeOffset(2025, 12, 25, 3, 0, 0, TimeSpan.Zero) }); + templates.Add(new TemplateNextRunDto { Id = Guid.Parse("DE7241F1-5C62-4164-A3DC-99B946350E10"), NextRun = new DateTimeOffset(2025, 12, 18, 3, 0, 0, TimeSpan.Zero) }); + templates.Add(new TemplateNextRunDto { Id = Guid.Parse("94A7375E-D1BD-49B7-9186-D26E85F1CF74"), NextRun = new DateTimeOffset(2025, 12, 18, 3, 0, 0, TimeSpan.Zero) }); + templates.Add(new TemplateNextRunDto { Id = Guid.Parse("B3681F5F-10A5-425C-BBEA-EA2B85FE0967"), NextRun = new DateTimeOffset(2025, 12, 18, 3, 0, 0, TimeSpan.Zero) }); + templates.Add(new TemplateNextRunDto { Id = Guid.Parse("3259B408-48A3-4995-AF9A-097477ECAB81"), NextRun = new DateTimeOffset(2025, 12, 18, 3, 0, 0, TimeSpan.Zero) }); + templates.Add(new TemplateNextRunDto { Id = Guid.Parse("25F53A28-D657-4B9A-82F3-CE9AF4D53385"), NextRun = new DateTimeOffset(2025, 12, 30, 3, 0, 0, TimeSpan.Zero) }); + //templates.Add(new TemplateNextRunDto { Id = Guid.Parse("F75F6B1A-875F-4CB2-98C8-6721FD06C18C"), NextRun = new DateTimeOffset(2025, 12, 18, 3, 0, 0, TimeSpan.Zero) }); + //templates.Add(new TemplateNextRunDto { Id = Guid.Parse("2C06C63D-BE92-4312-98FB-0547E74B8A33"), NextRun = new DateTimeOffset(2025, 12, 18, 3, 0, 0, TimeSpan.Zero) }); + //templates.Add(new TemplateNextRunDto { Id = Guid.Parse("B22D314E-9F86-49BB-826F-20919EC55A7F"), NextRun = new DateTimeOffset(2025, 12, 18, 3, 0, 0, TimeSpan.Zero) }); + //templates.Add(new TemplateNextRunDto { Id = Guid.Parse("061CB58A-B350-456F-8E02-C937D488D6FC"), NextRun = null }); + + //templates.Add(new TemplateNextRunDto { Id = Guid.Parse("C51E2C3F-655D-4395-BF9D-D6D64DA314D9"), NextRun = null }); + //templates.Add(new TemplateNextRunDto { Id = Guid.Parse("C9A77DEF-0FB8-4903-87B7-16B8C3EC97A4"), NextRun = null }); + //templates.Add(new TemplateNextRunDto { Id = Guid.Parse("EC703575-B38F-4A7F-9010-BBB3F92EDEA9"), NextRun = null }); + //templates.Add(new TemplateNextRunDto { Id = Guid.Parse("188181C3-AACD-4205-BCB6-7414966CB33B"), NextRun = null }); + + + return templates; + } + + } +} diff --git a/PARR.Test/Program.cs b/PARR.Test/Program.cs index 8f2fc0c8..58eb52d2 100644 --- a/PARR.Test/Program.cs +++ b/PARR.Test/Program.cs @@ -1,10 +1,11 @@ -using PARR.BLL; -using PARR.EsppApi; -using PARR.DAL; -using PARR.Test; -using Serilog; -using PARR.TemplateDistributor; using Elastic.CommonSchema.Serilog; +using PARR.BLL; +using PARR.DAL; +using PARR.EsppApi; +using PARR.TemplateDistributor; +using PARR.Test; +using PARR.Test.NextRun; +using Serilog; /*IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => @@ -48,6 +49,8 @@ builder.Services.InstallBllServices(builder.Configuration); builder.Configuration.AddDalConfigurations(builder.Services); builder.Services.AddDallSettings(builder.Configuration); +builder.Services.AddTransient(); + builder.Services.AddHostedService(); var host = builder.Build(); diff --git a/PARR.Test/Worker.cs b/PARR.Test/Worker.cs index e8a38173..1a9aa91c 100644 --- a/PARR.Test/Worker.cs +++ b/PARR.Test/Worker.cs @@ -6,6 +6,7 @@ using PARR.DAL.Services.Interfaces; using PARR.EsppApi; using PARR.EsppApi.Constants; using PARR.EsppApi.Models.Query; +using PARR.Test.NextRun; namespace PARR.Test { @@ -34,6 +35,19 @@ namespace PARR.Test protected override async Task ExecuteAsync(CancellationToken stoppingToken) { + + #region NextRunTest + + using (var scope = serviceProvider.CreateScope()) + { + var service = scope.ServiceProvider.GetService(); + + await service!.Test(); + } + + #endregion + + #region test //using (var scope = serviceProvider.CreateScope()) //{