feat(distributor): первая версия распределителя
This commit is contained in:
12
PARR.API.sln
12
PARR.API.sln
@@ -92,10 +92,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.AIHITRelationshipsSync
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.TemplateGeneratorWorker", "PARR.TemplateGeneratorWorker\PARR.TemplateGeneratorWorker.csproj", "{17245D0B-3290-4C96-B5B1-4B0711BABAF2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.TemplateTaskGenerator", "PARR.TemplateTaskGenerator\PARR.TemplateTaskGenerator.csproj", "{D4C51090-CE06-4158-9A0E-961744872411}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.TemplateTaskGeneratorWorker", "PARR.TemplateTaskGeneratorWorker\PARR.TemplateTaskGeneratorWorker.csproj", "{93249642-DF7D-41A1-87C0-6E3BAECB43EC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.TemplateMatcher", "PARR.TemplateMatcher\PARR.TemplateMatcher.csproj", "{EADC9A11-426A-41E9-98B4-010295412FF5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.TemplateMatcherWorker", "PARR.TemplateMatcherWorker\PARR.TemplateMatcherWorker.csproj", "{DA6B2A17-6E64-4E2C-8D4C-E278BEE94E03}"
|
||||
@@ -272,14 +268,6 @@ Global
|
||||
{17245D0B-3290-4C96-B5B1-4B0711BABAF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{17245D0B-3290-4C96-B5B1-4B0711BABAF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{17245D0B-3290-4C96-B5B1-4B0711BABAF2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D4C51090-CE06-4158-9A0E-961744872411}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D4C51090-CE06-4158-9A0E-961744872411}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D4C51090-CE06-4158-9A0E-961744872411}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D4C51090-CE06-4158-9A0E-961744872411}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{93249642-DF7D-41A1-87C0-6E3BAECB43EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{93249642-DF7D-41A1-87C0-6E3BAECB43EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{93249642-DF7D-41A1-87C0-6E3BAECB43EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{93249642-DF7D-41A1-87C0-6E3BAECB43EC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EADC9A11-426A-41E9-98B4-010295412FF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EADC9A11-426A-41E9-98B4-010295412FF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EADC9A11-426A-41E9-98B4-010295412FF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
29
PARR.DAL/NextRunServices/INextRunService.cs
Normal file
29
PARR.DAL/NextRunServices/INextRunService.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
namespace PARR.DAL.NextRunServices
|
||||
{
|
||||
public interface INextRunService
|
||||
{
|
||||
/// <summary>
|
||||
/// Получить список TemplateId, NextRun по jobGroupId с автораспределением
|
||||
/// </summary>
|
||||
/// <param name="jobGroupId"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<(Guid TemplateId, DateTimeOffset NextRun)>> GetNextRunForJobGroupWithAutoDistributionAsync(Guid jobGroupId);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Получить NextRun по jobGroupId с расписанием ЕСПП
|
||||
/// </summary>
|
||||
/// <param name="jobGroupId"></param>
|
||||
/// <returns></returns>
|
||||
Task<DateTimeOffset> GetNextRunForJobGroupWithEsppSchedulleAsync(Guid jobGroupId);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Получить NextRun по id шаблона
|
||||
/// </summary>
|
||||
/// <param name="templateId"></param>
|
||||
/// <returns></returns>
|
||||
Task<DateTimeOffset> GetNextRunForTemplate(Guid templateId);
|
||||
|
||||
}
|
||||
}
|
||||
12
PARR.DAL/NextRunServices/Models/TemplateNextRunDto.cs
Normal file
12
PARR.DAL/NextRunServices/Models/TemplateNextRunDto.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace PARR.DAL.NextRunServices.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Модель шаблона, для расчета NextRun
|
||||
/// </summary>
|
||||
public class TemplateNextRunDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTimeOffset? NextRun { get; set; }
|
||||
}
|
||||
}
|
||||
79
PARR.DAL/NextRunServices/NextRunService.cs
Normal file
79
PARR.DAL/NextRunServices/NextRunService.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PARR.DAL.Services.Interfaces;
|
||||
using PARR.DAL.Services.Interfaces.Job;
|
||||
using PARR.DAL.TransformServices;
|
||||
|
||||
namespace PARR.DAL.NextRunServices
|
||||
{
|
||||
internal class NextRunService : INextRunService
|
||||
{
|
||||
private readonly ILogger<NextRunService> logger;
|
||||
private readonly ITemplateService templateService;
|
||||
private readonly IJobGroupService jobGroupService;
|
||||
private readonly IEsppScheduleTransformService esppScheduleTransformService;
|
||||
|
||||
public NextRunService(
|
||||
ILogger<NextRunService> logger,
|
||||
ITemplateService templateService,
|
||||
IJobGroupService jobGroupService,
|
||||
IEsppScheduleTransformService esppScheduleTransformService
|
||||
)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.templateService = templateService;
|
||||
this.jobGroupService = jobGroupService;
|
||||
this.esppScheduleTransformService = esppScheduleTransformService;
|
||||
}
|
||||
|
||||
|
||||
public Task<List<(Guid TemplateId, DateTimeOffset NextRun)>> GetNextRunForJobGroupWithAutoDistributionAsync(Guid jobGroupId)
|
||||
{
|
||||
//TODO:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public async Task<DateTimeOffset> GetNextRunForJobGroupWithEsppSchedulleAsync(Guid jobGroupId)
|
||||
{
|
||||
var jobGroup = await jobGroupService.Get().AsNoTracking().FirstOrDefaultAsync(t => t.Id == jobGroupId);
|
||||
|
||||
if (jobGroup == null)
|
||||
{
|
||||
logger.LogError("Не найдена группа работ с Id: {jobGroupId}.", jobGroupId);
|
||||
throw new ArgumentNullException(nameof(jobGroupId), $"Не найдена группа работ с Id: {jobGroupId}");
|
||||
}
|
||||
|
||||
// Берем из обычного расписания ЕСПП
|
||||
return await esppScheduleTransformService.GetNextDateAsync(jobGroupId, jobGroup.ReferenceDate);
|
||||
}
|
||||
|
||||
|
||||
public async Task<DateTimeOffset> GetNextRunForTemplate(Guid templateId)
|
||||
{
|
||||
var template = await templateService.Get()
|
||||
.Include(t => t.Job).ThenInclude(t => t.Group)
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(t => t.Id == templateId);
|
||||
|
||||
if (template == null)
|
||||
{
|
||||
logger.LogError("Не найдена шаблон с Id: {templateId}.", templateId);
|
||||
throw new ArgumentNullException(nameof(templateId), $"Не найдена шаблон с Id: {templateId}");
|
||||
}
|
||||
|
||||
if (template.Job!.Group!.IsAutoDistributionEnabled == true)
|
||||
{
|
||||
// включено автораспределение
|
||||
//TODO: !!!!!!!!!!!!!!!! добавить метод рассчета с учетом распределения
|
||||
throw new NotImplementedException("добавить метод рассчета с учетом распределения");
|
||||
}
|
||||
else
|
||||
{
|
||||
// считаем как ЕСПП
|
||||
return await esppScheduleTransformService.GetNextDateAsync(template.Job.Group.Id, template.Job.Group.ReferenceDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
PARR.DAL/NextRunServices/Subservices/ITemplateDistributor.cs
Normal file
18
PARR.DAL/NextRunServices/Subservices/ITemplateDistributor.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using PARR.DAL.Models;
|
||||
using PARR.DAL.NextRunServices.Models;
|
||||
|
||||
namespace PARR.DAL.NextRunServices.Subservices
|
||||
{
|
||||
internal interface ITemplateDistributor
|
||||
{
|
||||
Task<List<TemplateNextRunDto>> GetValidNextRunAsync(TemplateNextRunDto template, DateOnly startDate, DateOnly endDate, TimeSpan executionTime);
|
||||
|
||||
Task<List<TemplateNextRunDto>> GetValidNextRunsAsync(List<TemplateNextRunDto> templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime);
|
||||
|
||||
Task<List<TemplateNextRunDto>> ReassignOutdatedRunsAsync(List<TemplateNextRunDto> templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime);
|
||||
|
||||
Task<List<TemplateNextRunDto>> RebalanceTemplatesAsync(List<TemplateNextRunDto> templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime);
|
||||
|
||||
Task<List<TemplateNextRunDto>> ScheduleTemplatesAsync(List<TemplateNextRunDto> templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime);
|
||||
}
|
||||
}
|
||||
322
PARR.DAL/NextRunServices/Subservices/TemplateDistributor.cs
Normal file
322
PARR.DAL/NextRunServices/Subservices/TemplateDistributor.cs
Normal file
@@ -0,0 +1,322 @@
|
||||
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 : ITemplateDistributor
|
||||
{
|
||||
private readonly ILogger<TemplateDistributor> logger;
|
||||
private readonly IWeekendDayService weekendDayService;
|
||||
|
||||
public TemplateDistributor(
|
||||
ILogger<TemplateDistributor> logger,
|
||||
IWeekendDayService weekendDayService
|
||||
)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.weekendDayService = weekendDayService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Распределяет шаблоны по рабочим дням в заданном диапазоне с равномерной загрузкой.
|
||||
/// Возвращает список с Id и новой датой выполнения для каждого шаблона.
|
||||
/// </summary>
|
||||
public async Task<List<TemplateNextRunDto>> ScheduleTemplatesAsync(List<TemplateNextRunDto> 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();
|
||||
|
||||
// Получаем список рабочих дней (исключая выходные и праздники)
|
||||
var workDays = GetWorkDays(startDate, endDate, weekends);
|
||||
|
||||
logger.LogDebug("Найдено {Count} рабочих дней", workDays.Count);
|
||||
|
||||
// Проверяем, есть ли хотя бы один рабочий день
|
||||
if (workDays.Count == 0)
|
||||
{
|
||||
logger.LogWarning("Нет доступных рабочих дней в диапазоне {StartDate} - {EndDate}", startDate, endDate);
|
||||
return new List<TemplateNextRunDto>();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Обновляет устаревшие даты выполнения шаблонов.
|
||||
/// Возвращает список шаблонов, чьи даты были изменены.
|
||||
/// </summary>
|
||||
public async Task<List<TemplateNextRunDto>> ReassignOutdatedRunsAsync(List<TemplateNextRunDto> 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<TemplateNextRunDto>();
|
||||
|
||||
foreach (var template in templates)
|
||||
{
|
||||
if (template.NextRun.HasValue && template.NextRun.Value < now)
|
||||
{
|
||||
logger.LogDebug("Шаблон {TemplateId} с датой {OldDate} устарел", template.Id, template.NextRun);
|
||||
|
||||
var newDate = FindNextAvailableDate(workDays, DateOnly.FromDateTime(now.DateTime), DateOnly.FromDateTime(template.NextRun.Value.Date));
|
||||
if (newDate.HasValue)
|
||||
{
|
||||
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 });
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning("Не удалось найти новую дату для шаблона {TemplateId}", template.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogInformation("Переназначение завершено. Изменено {Count} записей.", result.Count);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Перераспределяет только те шаблоны, у которых дата недействительна (выходной, вне периода).
|
||||
/// Возвращает список всех шаблонов с новыми или старыми датами.
|
||||
/// </summary>
|
||||
public async Task<List<TemplateNextRunDto>> RebalanceTemplatesAsync(List<TemplateNextRunDto> 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<DateOnly>(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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает актуальную дату выполнения для одного шаблона.
|
||||
/// Результат — список из одного элемента (если успешно) или пустой.
|
||||
/// </summary>
|
||||
public async Task<List<TemplateNextRunDto>> 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)
|
||||
{
|
||||
logger.LogDebug("Шаблон {TemplateId} имеет актуальную дату {NextRun}", template.Id, nextRun);
|
||||
return new List<TemplateNextRunDto> { new TemplateNextRunDto { Id = template.Id, NextRun = nextRun.Value } };
|
||||
}
|
||||
|
||||
logger.LogWarning("Не удалось получить актуальную дату для шаблона {TemplateId}", template.Id);
|
||||
return new List<TemplateNextRunDto>();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает список актуальных дат выполнения для всех переданных шаблонов.
|
||||
/// </summary>
|
||||
public async Task<List<TemplateNextRunDto>> GetValidNextRunsAsync(List<TemplateNextRunDto> templates, DateOnly startDate, DateOnly endDate, TimeSpan executionTime)
|
||||
{
|
||||
logger.LogDebug("Запрос актуальных дат для {Count} шаблонов", templates.Count);
|
||||
|
||||
// Получаем список выходных/праздничных дней
|
||||
var weekends = (await weekendDayService.GetWeekends(startDate, endDate).ToListAsync()).ToHashSet();
|
||||
|
||||
var result = new List<TemplateNextRunDto>();
|
||||
|
||||
foreach (var template in templates)
|
||||
{
|
||||
var nextRun = await GetValidNextRunInternalAsync(template, startDate, endDate, weekends, executionTime);
|
||||
if (nextRun.HasValue)
|
||||
{
|
||||
result.Add(new TemplateNextRunDto { Id = template.Id, NextRun = nextRun.Value });
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogInformation("Получено {Count} актуальных дат выполнения", result.Count);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Внутренний метод для получения актуальной даты выполнения для одного шаблона.
|
||||
/// </summary>
|
||||
private async Task<DateTimeOffset?> GetValidNextRunInternalAsync(TemplateNextRunDto template, DateOnly startDate, DateOnly endDate, HashSet<DateOnly> 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
// Дата валидна и не устарела
|
||||
logger.LogDebug("Шаблон {TemplateId} имеет действительную дату: {NextRun}", template.Id, template.NextRun);
|
||||
return template.NextRun;
|
||||
}
|
||||
|
||||
// Дата недействительна (выходной или вне периода) — ищем ближайшую
|
||||
var closest = FindClosestValidDate(workDays, scheduledDate);
|
||||
if (closest.HasValue)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
logger.LogWarning("Не удалось найти подходящую дату для шаблона {TemplateId}", template.Id);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ищет следующую доступную дату после указанной, если не находит — ищет назад.
|
||||
/// </summary>
|
||||
private DateOnly? FindNextAvailableDate(List<DateOnly> 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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ищет ближайшую дату (вперед или назад) к заданной, среди доступных рабочих дней.
|
||||
/// </summary>
|
||||
private DateOnly? FindClosestValidDate(List<DateOnly> workDays, DateOnly targetDate)
|
||||
{
|
||||
var future = workDays.FirstOrDefault(t => t >= targetDate);
|
||||
|
||||
if (future != default)
|
||||
return future;
|
||||
|
||||
var past = workDays.Where(t => t <= targetDate).OrderByDescending(t => t).FirstOrDefault();
|
||||
|
||||
return past != default ? (DateOnly?)past : null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает список рабочих дней в диапазоне, исключая выходные и праздники.
|
||||
/// </summary>
|
||||
private List<DateOnly> GetWorkDays(DateOnly start, DateOnly end, HashSet<DateOnly> weekends)
|
||||
{
|
||||
var result = new List<DateOnly>();
|
||||
var date = start;
|
||||
|
||||
while (date <= end)
|
||||
{
|
||||
if (!weekends.Contains(date))
|
||||
{
|
||||
result.Add(date);
|
||||
}
|
||||
date = date.AddDays(1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ using PARR.DAL.Contracts;
|
||||
using PARR.DAL.DomainServices.Implementations;
|
||||
using PARR.DAL.DomainServices.Interfaces;
|
||||
using PARR.DAL.InfluxDbServices;
|
||||
using PARR.DAL.NextRunServices;
|
||||
using PARR.DAL.NextRunServices.Subservices;
|
||||
using PARR.DAL.Services.Implementation;
|
||||
using PARR.DAL.Services.Implementations;
|
||||
using PARR.DAL.Services.Implementations.Job;
|
||||
@@ -17,6 +19,7 @@ using PARR.DAL.Services.Interfaces.Job;
|
||||
using PARR.DAL.Services.Interfaces.Unit;
|
||||
using PARR.DAL.Settings;
|
||||
using PARR.DAL.TransformServices;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace PARR.DAL
|
||||
{
|
||||
@@ -121,6 +124,14 @@ namespace PARR.DAL
|
||||
services.AddTransient<IEsppScheduleTransformService, EsppScheduleTransformService>();
|
||||
services.AddTransient<INextRunModifierService, NextRunModifierService>();
|
||||
|
||||
#region NextRun Services
|
||||
|
||||
services.AddTransient<INextRunService, NextRunService>();
|
||||
services.AddTransient<ITemplateDistributor, TemplateDistributor>();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region DomainServces
|
||||
services.AddTransient<IShortcodesService, ShortcodesService>();
|
||||
services.AddTransient<IUnitFilterService, UnitFilterService>();
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace PARR.DAL.Services.Implementations
|
||||
|
||||
public IQueryable<DateOnly> GetWeekends(DateOnly start, DateOnly end)
|
||||
{
|
||||
return EntitySet.Where(t => t.Date >= start && t.Date <= end).Select(t => t.Date);
|
||||
return EntitySet.Where(t => t.Date >= start && t.Date <= end).AsNoTracking().Select(t => t.Date);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace PARR.TemplateTaskGenerator
|
||||
{
|
||||
var mqRequest = new TemplateGeneratorMq
|
||||
{
|
||||
UnitsInTemplate=new List<Guid>(),
|
||||
JobId = jobId,
|
||||
UnitId = unitId,
|
||||
HistoryInitiator = new HistoryInitiator { InitiatorComment = "Запрос на создание шаблона", InitiatorParrComponentId = ParrComponentsEnum.TemplateTaskGenerator }
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="docker-compose.template-matcher.yml" />
|
||||
<None Include="docker-compose.template-task-generator.yml" />
|
||||
<None Include="docker-compose.template-generator.yml" />
|
||||
<None Include="docker-compose.aihit-loader.yml" />
|
||||
<None Include="docker-compose.aihit-relationships-syncer.yml" />
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
version: '3.4'
|
||||
|
||||
#PARR TEMPLATE TASK GENERATOR
|
||||
services:
|
||||
parr-template-task-generator:
|
||||
image: harbor.dvgd.rzd/parr/parr-template-task-generator:${tag-latest}
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- TZ=Europe/Moscow
|
||||
logging:
|
||||
driver: fluentd
|
||||
options:
|
||||
fluentd-address: dvgd-efk-01.dvgd.oao.rzd:24224
|
||||
fluentd-retry-wait: '10s'
|
||||
fluentd-max-retries: '30'
|
||||
fluentd-async: 'true'
|
||||
fluentd-buffer-limit: '52428800'
|
||||
tag: parr.template-task-generator.serilog
|
||||
deploy:
|
||||
replicas: 1
|
||||
networks:
|
||||
parr-network:
|
||||
|
||||
networks:
|
||||
parr-network:
|
||||
driver: overlay
|
||||
external: true
|
||||
Reference in New Issue
Block a user