Files
parr_api/PARR.DAL/NextRunServices/NextRunServiceV2.cs

241 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using NodaTime;
using PARR.Constants;
using PARR.DAL.Contracts;
using PARR.DAL.DomainServices.Shortcodes;
using PARR.DAL.Models;
using PARR.DAL.Models.Job;
using PARR.DAL.NextRunServices.Models;
using PARR.DAL.NextRunServices.Subservices;
using PARR.DAL.Services.Interfaces;
using PARR.DAL.Services.Interfaces.Job;
using PARR.DAL.Services.Interfaces.Schedule;
namespace PARR.DAL.NextRunServices
{
internal class NextRunServiceV2 : INextRunServiceV2
{
private readonly ILogger<NextRunServiceV2> logger;
private readonly ITemplateService templateService;
private readonly IJobGroupService jobGroupService;
private readonly IEsppScheduleTransformService esppScheduleTransformService;
private readonly ITemplateDistributorV2 templateDistributor;
private readonly IShortcodesService shortcodesService;
private readonly IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService;
private readonly SettingsFromDb settingsFromDb;
public NextRunServiceV2(
ILogger<NextRunServiceV2> logger,
ITemplateService templateService,
IJobGroupService jobGroupService,
IEsppScheduleTransformService esppScheduleTransformService,
ITemplateDistributorV2 templateDistributor,
IShortcodesService shortcodesService,
IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService,
SettingsFromDb settingsFromDb
)
{
this.logger = logger;
this.templateService = templateService;
this.jobGroupService = jobGroupService;
this.esppScheduleTransformService = esppScheduleTransformService;
this.templateDistributor = templateDistributor;
this.shortcodesService = shortcodesService;
this.scheduleResponseAreaTimeOffsetService = scheduleResponseAreaTimeOffsetService;
this.settingsFromDb = settingsFromDb;
}
public async Task<List<TemplateNextRunResultDto>?> GetNextRunForJobGroupWithAutoDistributionAsync(Guid jobGroupId)
{
logger.LogInformation("Начинаю распределять шаблоны для группы работ {gobGroupId}", jobGroupId);
var jobGroup = await jobGroupService.Get()
.Include(t => t.DistributionConfig)
.ThenInclude(t => t.DistributionPeriod)
.AsNoTracking()
.FirstOrDefaultAsync(t => t.Id == jobGroupId);
if (jobGroup == null)
{
logger.LogError("Не найдена группа работа с id: {id}", jobGroupId);
return null;
}
if (!jobGroup.IsAutoDistributionEnabled || jobGroup.DistributionConfig == null)
{
logger.LogError("Группа работ id: {id} не подходит для автораспределения, у нее или отсутствуют настройки или не включено автораспределение. IsAutoDistributionEnabled: {IsAutoDistributionEnabled}. Есть конфиг: {DistributionConfig}", jobGroupId, jobGroup.IsAutoDistributionEnabled, jobGroup.DistributionConfig != null);
return null;
}
logger.LogInformation("Параметры распределения. groupId: {groupId}, name: {groupName}, referenceDate: {referenceDate}, " +
"distributionPeriodName: {distributionPeriodName}, distributionPeriodDuration: {distributionPeriodDuration}, " +
"distributionPeriodType: {distributionPeriodType}, IsExcludeWeekends: {IsExcludeWeekends}, IsGroupingByWorkGroup: {IsGroupingByWorkGroup}, IsResponseAreaTimezone: {IsResponseAreaTimezone}",
jobGroupId, jobGroup.GroupName, jobGroup.ReferenceDate, jobGroup.DistributionConfig.DistributionPeriod.Name, jobGroup.DistributionConfig.DistributionPeriod.Duration, jobGroup.DistributionConfig.DistributionPeriod.Type,
jobGroup.DistributionConfig.IsExcludeWeekends, jobGroup.DistributionConfig.IsGroupingByWorkGroup, jobGroup.IsResponseAreaTimezone);
//получаем шаблоны только в статусе Used
var allTemplates = await templateService.Get()
.AsNoTracking()
.Include(t => t.Job)
.Where(t =>
t.Job!.GroupId == jobGroupId
&& t.StatusTypeId == TemplateStatusTypeEnum.Used
).ToListAsync();
if (!allTemplates.Any())
{
logger.LogInformation("В группе c ИД {jobGroupId} отсутствуют шаблоны в статусе Used", jobGroupId);
return null;
}
logger.LogDebug("Всего шаблонов для распределения в статусе Used: {count} шт.", allTemplates.Count);
// продолжительность в днях
var durationDays = GetDurationDays(jobGroup.DistributionConfig);
// группировать по РГ
var isGroupingByWorkGroup = jobGroup.DistributionConfig.IsGroupingByWorkGroup;
// использовать часовой пояс РГ
var isResponseAreaTimeZone = jobGroup.IsResponseAreaTimezone;
var distributedTemplates = new List<TemplateNextRunResultDto>();
if (isGroupingByWorkGroup)
{
// группировать по РГ
if (isResponseAreaTimeZone)
{
// использовать часовой пояс РГ
}
else
{
// не нужно использовать часовой пояс РГ (используем часовой пояс УЗ ЕСПП)
}
}
else
{
//не нужно группировать по РГ
if (isResponseAreaTimeZone)
{
// использовать ЧАСОВОЙ ПОЯС РГ (нужно сгруппировать шаблоны по часовым поясам, затем отдельно распределить каждую группу)
}
else
{
// не нужно использовать часовой пояс РГ
var offset = GetOffsetForDistribution();
var templatesForDistribute = allTemplates.Select(t => new TemplateNextRunDto(t.Id, t.NextRun)).ToList();
var dateStart = GetDateStart(offset);
logger.LogInformation("Буду распределать шаблоны {count} шт, их НЕ НУЖНО группировать по РГ, НЕ НУЖНО использовать часовой пояс РГ. dateStart в целевом часовом поясе: {dateStart}, offset: {offset}",
templatesForDistribute.Count, dateStart, offset);
distributedTemplates = await templateDistributor.DistributeTemplatesAsync(dateStart, durationDays, jobGroup.ReferenceDate, offset, templatesForDistribute, jobGroup.DistributionConfig.IsExcludeWeekends);
}
}
return distributedTemplates;
}
/// <summary>
/// Получить продолжительность в днях
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
private int GetDurationDays(JobGroupDistributionConfig config)
{
var period = config.DistributionPeriod;
var periodType = period!.Type;
int.TryParse(period.Duration, out var duration);
var periodDays = duration;
if (periodType == DistributionPeriodTypeEnum.Day.ToString())
periodDays = duration;
if (periodType == DistributionPeriodTypeEnum.Month.ToString())
// в месяце 30 дней, duration*30
periodDays = duration * 30;
if (periodType == DistributionPeriodTypeEnum.Year.ToString())
// в году 365 дней, duration*365
periodDays = duration * 365;
logger.LogDebug("Период распределения: {name}, duration: {duration}, type: {type}. Итого в днях: {days}", period.Name, period.Duration, period.Type, periodDays);
if (periodDays == 0)
{
periodDays = 1;
logger.LogWarning("Полученный период распределения 0 дней. Неверный конфиг распределения в таблице {table}. {name}, duration: {duration}, type: {type}. Устанавливаем минимальный период распределения {periodDays} дней.", nameof(DistributionPeriod), period.Name, period.Duration, period.Type, periodDays);
}
return periodDays;
}
/// <summary>
/// Получить часовую зону для распределения
/// </summary>
/// <returns></returns>
private TimeSpan GetOffsetForDistribution(string? responseArea = null)
{
// или часова зона робота, или часовая зона РГ
if (responseArea != null)
{
// возвращаем часовой пояс ЗО
var responseAreaOffset = scheduleResponseAreaTimeOffsetService.GetByResponseAreaOrDefault(responseArea).UtcTimeOffset;
logger.LogDebug("Получил часовой пояс для ЗО: {responseArea}, UtcTimeOffset: {responseAreaOffset}", responseArea, responseAreaOffset);
return responseAreaOffset;
}
else
{
// возвращаем часовой пояс робота
var esppOffset = GetEsppAccountOffset();
logger.LogDebug("Не передана ЗО, использую часовой пояс УЗ ЕСПП, UtcTimeOffset: {UtcTimeOffset}", esppOffset);
return esppOffset;
}
}
/// <summary>
/// Получить часовой пояс УЗ ЕСПП
/// </summary>
/// <returns></returns>
private TimeSpan GetEsppAccountOffset()
{
var esppOffset = TimeSpan.FromHours(settingsFromDb.EsppRobotAccountTimeZoneHour);
logger.LogDebug("Оффсет УЗ ЕСПП: {esppOffset}", esppOffset);
return esppOffset;
}
/// <summary>
/// Получить дату начала распределения в целевом часовом поясе
/// </summary>
/// <param name="offset"></param>
/// <returns></returns>
private DateOnly GetDateStart(TimeSpan offset)
{
var dateStart = DateOnly.FromDateTime(DateTimeOffset.UtcNow.ToOffset(offset).Date);
logger.LogDebug("Дата начала распределения (сегодня) в целевом часовом поясе {dateStart}, offset: {offset}", dateStart, offset);
return dateStart;
}
}
}