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())
//{