feat(dal,): NextRunServiceV2 - метод получения nextRun для существующего шаблона

This commit is contained in:
Mikhail Trubnikov
2026-02-16 14:48:43 +10:00
parent 361338f433
commit 62dcf80aa3
3 changed files with 224 additions and 11 deletions

View File

@@ -20,8 +20,8 @@ namespace PARR.DAL.NextRunServices
/// <param name="jobGroupId">ИД группы работ</param>
/// <param name="workGroupName">Рабочая группа, не маска</param>
/// <param name="responseAreaName">Зона ответственности, не маска</param>
/// <returns></returns>
Task<DateTimeOffset> GetNextRunForNewTemplateAsync(Guid jobGroupId, string workGroupName, string responseAreaName);
/// <returns>NextRun или null, если null - то ошибка</returns>
Task<DateTimeOffset?> GetNextRunForNewTemplateAsync(Guid jobGroupId, string workGroupName, string responseAreaName);
/// <summary>
@@ -29,8 +29,8 @@ namespace PARR.DAL.NextRunServices
/// </summary>
/// <param name="templateId">ИД шаблона</param>
/// <param name="isNew">При перемещении шаблона в новую для него работу, ставить isNew=true</param>
/// <returns></returns>
Task<DateTimeOffset> GetNextRunForTemplateAsync(Guid templateId, bool isNew);
/// <returns>NextRun или null, если null - то ошибка</returns>
Task<DateTimeOffset?> GetNextRunForTemplateAsync(Guid templateId, bool isNew);

View File

@@ -260,7 +260,7 @@ namespace PARR.DAL.NextRunServices
}
public async Task<DateTimeOffset> GetNextRunForNewTemplateAsync(Guid jobGroupId, string workGroupName, string responseAreaName)
public async Task<DateTimeOffset?> GetNextRunForNewTemplateAsync(Guid jobGroupId, string workGroupName, string responseAreaName)
{
logger.LogDebug("Получить nextRun для вновь создаваемого шаблона. jobGroupId: {jobGroupId}, workGroupName: {workGroupName}, responseAreaName: {responseAreaName}",
jobGroupId, workGroupName, responseAreaName);
@@ -274,7 +274,8 @@ namespace PARR.DAL.NextRunServices
if (jobGroup == null)
{
logger.LogError("Не найдена группа работ с Id: {jobGroupId}.", jobGroupId);
throw new ArgumentNullException(nameof(jobGroupId), $"Не найдена группа работ с Id: {jobGroupId}");
//throw new ArgumentNullException(nameof(jobGroupId), $"Не найдена группа работ с Id: {jobGroupId}");
return null;
}
if (jobGroup.IsAutoDistributionEnabled)
@@ -285,7 +286,8 @@ namespace PARR.DAL.NextRunServices
if (jobGroup.DistributionConfig == null)
{
logger.LogError("Для группы работ {jobGroupId}, указано автораспределение, но отсутствует конфиг в таблице {GroupDistributionConfigs}", jobGroupId, nameof(JobGroupDistributionConfig));
throw new ArgumentNullException(nameof(JobGroupDistributionConfig), $"Для jobGroupId: {jobGroupId} отсутствует конфигурация автораспределения в таблице {nameof(JobGroupDistributionConfig)}");
//throw new ArgumentNullException(nameof(JobGroupDistributionConfig), $"Для jobGroupId: {jobGroupId} отсутствует конфигурация автораспределения в таблице {nameof(JobGroupDistributionConfig)}");
return null;
}
var startDate = GetDateStart();
@@ -430,7 +432,7 @@ namespace PARR.DAL.NextRunServices
if (jobGroup.IsResponseAreaTimezone)
{
// использовать часовой пояс РГ
// использовать часовой пояс ЗО
var referenceDate = GetReferenceDate(jobGroup, responseAreaName);
var offset = GetOffsetForDistributionByResponseArea(responseAreaName);
@@ -443,7 +445,7 @@ namespace PARR.DAL.NextRunServices
}
else
{
// не использовать часовой пояс РГ
// не использовать часовой пояс ЗО
// так как это классическое расписание еспп, и не используется часовой пояс РГ, то нам нужно считать в часовом поясе УЗ ЕСПП, т.е. МСК
var referenceDate = GetReferenceDate(jobGroup, null);
@@ -459,12 +461,217 @@ namespace PARR.DAL.NextRunServices
}
public async Task<DateTimeOffset> GetNextRunForTemplateAsync(Guid templateId, bool isNew)
public async Task<DateTimeOffset?> GetNextRunForTemplateAsync(Guid templateId, bool isNew)
{
logger.LogDebug("Получить nextRun для существующего шаблона, templateId: {templateId}, isNew: {isNew}", templateId, isNew);
var template = await templateService.Get()
.AsNoTracking()
.Include(t => t.Job)
.ThenInclude(t => t.Group).ThenInclude(t => t.DistributionConfig).ThenInclude(t => t.DistributionPeriod)
.FirstOrDefaultAsync(t => t.Id == templateId);
throw new NotImplementedException();
if (template == null)
{
logger.LogError("Не найден шаблон с Id: {templateId}.", templateId);
//throw new ArgumentNullException(nameof(templateId), $"Не найден шаблон с Id: {templateId}");
return null;
}
if (template.Job!.Group!.IsAutoDistributionEnabled == true)
{
// включено автораспределение
logger.LogDebug("Включено автораспределение");
if (template.Job!.Group.DistributionConfig == null)
{
logger.LogError("Для шаблона {templateId}, jobGroupId {jobGroupId}, указано автораспределение, но отсутствует конфиг в таблице {GroupDistributionConfigs}", template.Id, template.Job.GroupId, nameof(JobGroupDistributionConfig));
// throw new ArgumentNullException(nameof(JobGroupDistributionConfig), $"Для jobGroupId: {template.Job.GroupId} отсутствует конфигурация автораспределения в таблице {nameof(JobGroupDistributionConfig)}");
return null;
}
var startDate = GetDateStart();
var duration = GetDurationDays(template.Job.Group.DistributionConfig);
var excludeWeekends = template.Job.Group.DistributionConfig.IsExcludeWeekends;
var targetTemplate = new TemplateNextRunDto(template.Id, template.NextRun);
var templateStatusType = TemplateStatusTypeEnum.Used;
var jobGroup = template.Job.Group;
// получаем все шаблоны из этой же группы что и template
var allTemplatesQuery = templateService.Get()
.AsNoTracking()
.Include(t => t.Job)
.Where(t => t.StatusTypeId == templateStatusType && t.Job!.GroupId == jobGroup.Id);
// группировать по РГ
var isGroupingByWorkGroup = jobGroup.DistributionConfig.IsGroupingByWorkGroup;
// использовать часовой пояс РГ
var isResponseAreaTimeZone = jobGroup.IsResponseAreaTimezone;
if (isGroupingByWorkGroup)
{
var targetTemplateWorkGroup = await shortcodesService.ApplyShortcodesAsync(template.Job.WorkGroupMask, template);
// группировать по РГ
logger.LogDebug("Нужно группировать по РГ");
// получаем для всех шаблонов РГ и группируем их по РГ
var templatesByWorkGroup = new Dictionary<string?, List<Template>>(StringComparer.OrdinalIgnoreCase);
var allTemplates = await allTemplatesQuery.ToListAsync();
foreach (var templateItem in allTemplates)
{
var templateWorkGroupName = await shortcodesService.ApplyShortcodesAsync(templateItem.Job!.WorkGroupMask, templateItem);
// Если даже workGroupName == null, ну и ладно, сгруппируем по null
if (templateWorkGroupName == null)
logger.LogWarning("Для шаблона {id}, с маской РГ '{workGroupMask}', не смог с помощью шорткода определить рабочую группу, шорткод сервис вернул РГ: '{workGroupName}'",
templateItem.Id, templateItem.Job!.WorkGroupMask, templateWorkGroupName);
// Добавляем шаблон в группу
if (!templatesByWorkGroup.TryGetValue(templateWorkGroupName, out var templatesList))
{
templatesList = new List<Template>();
templatesByWorkGroup[templateWorkGroupName] = templatesList;
}
templatesList.Add(templateItem);
}
//Берем шаблоны только нашей РГ workGroupName
templatesByWorkGroup.TryGetValue(targetTemplateWorkGroup, out var templatesWithWorkGroup);
var templatesByWorkGroupDto = (templatesWithWorkGroup ?? new List<Template>()).Select(t => new TemplateNextRunDto(t.Id, t.NextRun)).ToList();
logger.LogDebug("Шаблонов в РГ {targetTemplateWorkGroup} - {count} шт.", targetTemplateWorkGroup, templatesByWorkGroupDto.Count);
if (isResponseAreaTimeZone)
{
// использовать часовой пояс РГ
var targetTemplateResponseArea = await shortcodesService.ApplyShortcodesAsync(template.Job.ResponseAreaMask, template);
logger.LogDebug("Использовать часовой пояс РГ '{responseAreaName}'", targetTemplateResponseArea);
var offset = GetOffsetForDistributionByResponseArea(targetTemplateResponseArea);
var referenceDate = GetReferenceDate(jobGroup, targetTemplateResponseArea);
var nextRun = await templateDistributor.GetNextRunForTemplateAsync(startDate, duration, referenceDate, offset, targetTemplate, templatesByWorkGroupDto, excludeWeekends, isNew);
return nextRun.NextRun;
}
else
{
// не нужно использовать часовой пояс РГ (используем часовой пояс УЗ ЕСПП)
logger.LogDebug("Не нужно использовать часовой пояс РГ (используем часовой пояс УЗ ЕСПП)");
var offset = GetOffsetForDistributionByResponseArea();
var referenceDate = GetReferenceDate(jobGroup, null);
var nextRun = await templateDistributor.GetNextRunForTemplateAsync(startDate, duration, referenceDate, offset, targetTemplate, templatesByWorkGroupDto, excludeWeekends, isNew);
return nextRun.NextRun;
}
}
else
{
// не нужно группировать по РГ
logger.LogDebug("Не нужно группировать по РГ");
if (isResponseAreaTimeZone)
{
// использовать ЧАСОВОЙ ПОЯС ЗО
var responseAreaName = await shortcodesService.ApplyShortcodesAsync(template.Job.ResponseAreaMask, template);
logger.LogDebug("Нужно использовать часовой пояс ЗО '{responseAreaName}'", responseAreaName);
// получить все шаблоны, сгруппировать их по ЗО, получить nextRun в рамках переданной ЗО
var referenceDate = GetReferenceDate(jobGroup, responseAreaName);
var offset = GetOffsetForDistributionByResponseArea(responseAreaName);
var allTemplates = await allTemplatesQuery.ToListAsync();
// получаем для каждого шаблона ЗО и сразу группируем по ЗО
var templatesByResponseArea = new Dictionary<string?, List<Template>>(StringComparer.OrdinalIgnoreCase);
foreach (var templateItem in allTemplates)
{
var responseArea = await shortcodesService.ApplyShortcodesAsync(templateItem.Job!.ResponseAreaMask, templateItem);
if (responseArea == null)
logger.LogWarning("Для шаблона {id}, с маской ЗО '{responseAreaMask}', не смог с помощью шорткода определить ЗО, шорткод сервис вернул ЗО: '{responseArea}'",
templateItem.Id, templateItem.Job!.ResponseAreaMask, responseArea);
// Добавляем шаблон в группу
if (!templatesByResponseArea.TryGetValue(responseArea, out var templateList))
{
templateList = new List<Template>();
templatesByResponseArea[responseArea] = templateList;
}
templateList.Add(templateItem);
}
// из сгруппированных шаблонов, получаем существующие с ЗО нашего шаблона responseAreaName
templatesByResponseArea.TryGetValue(responseAreaName, out var templatesWithResponseArea);
var templatesWithResponseAreaDto = (templatesWithResponseArea ?? new List<Template>()).Select(t => new TemplateNextRunDto(t.Id, t.NextRun)).ToList();
logger.LogDebug("Существующих шаблонов в БД с responseAreaName: {responseAreaName} - {count} шт.", responseAreaName, templatesWithResponseAreaDto.Count);
var nextRun = await templateDistributor.GetNextRunForTemplateAsync(startDate, duration, referenceDate, offset, targetTemplate, templatesWithResponseAreaDto, excludeWeekends, isNew);
return nextRun.NextRun;
}
else
{
// не нужно использовать часовой пояс ЗО
logger.LogDebug("Не нужно использовать часовой пояс ЗО.");
var referenceDate = GetReferenceDate(jobGroup, null);
var offset = GetOffsetForDistributionByResponseArea();
var allTemplates = await allTemplatesQuery
.Select(t => new TemplateNextRunDto(t.Id, t.NextRun))
.ToListAsync();
var nextRun = await templateDistributor.GetNextRunForTemplateAsync(startDate, duration, referenceDate, offset, targetTemplate, allTemplates, excludeWeekends, isNew);
return nextRun.NextRun;
}
}
}
else
{
// метод расчета ЕСПП
logger.LogDebug("Метод расчета ЕСПП");
if (template.Job.Group.IsResponseAreaTimezone)
{
// использовать часовой пояс ЗО
var responseAreaName = await shortcodesService.ApplyShortcodesAsync(template.Job.ResponseAreaMask, template);
var jobGroup = template.Job.Group;
var referenceDate = GetReferenceDate(jobGroup, responseAreaName);
var offset = GetOffsetForDistributionByResponseArea(responseAreaName);
logger.LogDebug("Расчет nextRun используя метод ЕСПП, использую часовой пояс ЗО '{responseAreaName}, referenceDate: {referenceDate}, offset: {offset}'", responseAreaName, referenceDate, offset);
var nextRun = await esppScheduleTransformService.GetNextDateAsync(jobGroup.Id, referenceDate, offset);
return nextRun;
}
else
{
// не использовать часовой пояс ЗО
// так как это классическое расписание еспп, и не используется часовой пояс РГ, то нам нужно считать в часовом поясе УЗ ЕСПП, т.е. МСК
var referenceDate = GetReferenceDate(template.Job.Group, null);
var offset = GetOffsetForDistributionByResponseArea();
logger.LogDebug("Расчет nextRun используя метод ЕСПП, не использую часовой пояс ЗО, использую часовой пояс УЗ ЕСПП, referenceDate: {referenceDate}, offset: {offset}", referenceDate, offset);
var nextRun = await esppScheduleTransformService.GetNextDateAsync(template.Job.GroupId, referenceDate, offset);
return nextRun;
}
}
}