Merge branch 'next-run-service' into dev
# Conflicts: # PARR.DAL/Contracts/SettingsFromDb.cs
This commit is contained in:
@@ -189,7 +189,7 @@ namespace PARR.DAL.Context
|
||||
new { Name = nameof(SettingsFromDb.TemplatePrefixName), Description = "Префикс имени шаблона в ЕСПП", Value = "%PREFIX%-ЭИТИ-ПТК-ПАРР" },
|
||||
new { Name = nameof(SettingsFromDb.RobotAttemptsNumber), Description = "Количество попыток выполнения задания роботом", Value = "3" },
|
||||
new { Name = nameof(SettingsFromDb.RobotWaitTime), Description = "Время ожидания выполнения роботом задания", Value = "00:15:00" },
|
||||
//new { Name = nameof(SettingsFromDb.ScheduleTimezone), Description = "Расписание регламентной работы - В каком часовом поясе", Value = "MSK" },
|
||||
new { Name = nameof(SettingsFromDb.EsppScheduleTimezone), Description = "Расписание регламентной работы - В каком часовом поясе", Value = "MSK" },
|
||||
//new { Name = nameof(SettingsFromDb.ScheduleExcludeType), Description = "Расписание регламентной работы - Тип исключения", Value = "Выполнить ТОЛЬКО В указанном календаре" },
|
||||
//new { Name = nameof(SettingsFromDb.ScheduleExcludeCalendar), Description = "Расписание регламентной работы - Календарь", Value = "8x5 (8.00-17.00)" },
|
||||
new { Name = nameof(SettingsFromDb.ScheduleRepeatRange), Description = "Расписание регламентной работы - Диапазн повторов", Value = "Отсутствует дата завершения" },
|
||||
|
||||
@@ -76,6 +76,21 @@ namespace PARR.DAL.Contracts
|
||||
/// </summary>
|
||||
public TimeSpan RobotWaitTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Расписание регламентной работы, В каком часовом поясе
|
||||
/// </summary>
|
||||
public string EsppScheduleTimezone { get; set; } = string.Empty;
|
||||
|
||||
///// <summary>
|
||||
///// Расписание регламентной работы - Тип исключения
|
||||
///// </summary>
|
||||
//public string ScheduleExcludeType { get; set; } = string.Empty;
|
||||
|
||||
///// <summary>
|
||||
///// Расписание регламентной работы - Календарь
|
||||
///// </summary>
|
||||
//public string ScheduleExcludeCalendar { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Расписание регламентной работы - Диапазон повторов
|
||||
/// </summary>
|
||||
|
||||
4148
PARR.DAL/Migrations/20260209010657_tblJobGroupAddUserTimeZoneOffsetMinutes.Designer.cs
generated
Normal file
4148
PARR.DAL/Migrations/20260209010657_tblJobGroupAddUserTimeZoneOffsetMinutes.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace PARR.DAL.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class tblJobGroupAddUserTimeZoneOffsetMinutes : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "UserTimeZoneOffsetMinutes",
|
||||
schema: "job",
|
||||
table: "Groups",
|
||||
type: "integer",
|
||||
nullable: true,
|
||||
comment: "Смещение часового пояса пользователя в минутах относительно UTC на момент ReferenceDate");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UserTimeZoneOffsetMinutes",
|
||||
schema: "job",
|
||||
table: "Groups");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1702,6 +1702,10 @@ namespace PARR.DAL.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int?>("UserTimeZoneOffsetMinutes")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("Смещение часового пояса пользователя в минутах относительно UTC на момент ReferenceDate");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GroupTypeId");
|
||||
|
||||
@@ -92,6 +92,21 @@ namespace PARR.DAL.Models.Job
|
||||
public DateTimeOffset ReferenceDate { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Смещение часового пояса пользователя в минутах относительно UTC на момент ReferenceDate
|
||||
/// </summary>
|
||||
[Comment("Смещение часового пояса пользователя в минутах относительно UTC на момент ReferenceDate")]
|
||||
public int? UserTimeZoneOffsetMinutes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Смещение часового пояса пользователя относительно UTC на момент ReferenceDate, рассчитывается из поля UserTimeZoneOffsetMinutes
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public TimeSpan? UserTimeZoneOffset =>
|
||||
UserTimeZoneOffsetMinutes.HasValue
|
||||
? TimeSpan.FromMinutes(UserTimeZoneOffsetMinutes.Value)
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// Использовать таймзону рабочей группы ответственного за ЭК шаблона
|
||||
/// </summary>
|
||||
|
||||
@@ -72,12 +72,12 @@ namespace PARR.DAL.NextRunServices
|
||||
/// <returns></returns>
|
||||
DateTimeOffset GetNextRunWithTimezoneEsppAndResponseArea(DateTimeOffset nextRun, bool? isResponseAreaTimezone = null, string? responseArea = null);
|
||||
|
||||
/// <summary>
|
||||
/// Получить NextRun согласно таймзоны ЗО
|
||||
/// </summary>
|
||||
/// <param name="nextRun"></param>
|
||||
/// <param name="responseArea"></param>
|
||||
/// <returns></returns>
|
||||
DateTimeOffset GetNextRunWithResponseAreaOffset(DateTimeOffset nextRun, string responseArea);
|
||||
///// <summary>
|
||||
///// Получить NextRun согласно таймзоны ЗО
|
||||
///// </summary>
|
||||
///// <param name="nextRun"></param>
|
||||
///// <param name="responseArea"></param>
|
||||
///// <returns></returns>
|
||||
//DateTimeOffset GetNextRunWithResponseAreaOffset(DateTimeOffset nextRun, string responseArea);
|
||||
}
|
||||
}
|
||||
|
||||
66
PARR.DAL/NextRunServices/INextRunServiceV2.cs
Normal file
66
PARR.DAL/NextRunServices/INextRunServiceV2.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using PARR.Constants;
|
||||
using PARR.DAL.Models.Job;
|
||||
using PARR.DAL.NextRunServices.Models;
|
||||
|
||||
namespace PARR.DAL.NextRunServices
|
||||
{
|
||||
public interface INextRunServiceV2
|
||||
{
|
||||
/// <summary>
|
||||
/// Распределить шаблоны в группе работ (получить список распределенных шаблонов с актуальными nextRun)
|
||||
/// </summary>
|
||||
/// <param name="jobGroupId">ИД группы работ</param>
|
||||
/// <param name="templateStatusType">Статус шаблона, если не передать, берутся все шаблоны группы</param>
|
||||
/// <returns>Список TemplateId с NextRun и NextRunOld</returns>
|
||||
Task<List<TemplateNextRunResultDto>?> GetNextRunForJobGroupWithAutoDistributionAsync(Guid jobGroupId, TemplateStatusTypeEnum? templateStatusType);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Получить nextRun для вновь создаваемого шаблона, которого еще нет в БД
|
||||
/// </summary>
|
||||
/// <param name="jobGroupId">ИД группы работ</param>
|
||||
/// <param name="workGroupName">Рабочая группа, не маска</param>
|
||||
/// <param name="responseAreaName">Зона ответственности, не маска</param>
|
||||
/// <returns>NextRun или null, если null - то ошибка</returns>
|
||||
Task<DateTimeOffset?> GetNextRunForNewTemplateAsync(Guid jobGroupId, string workGroupName, string responseAreaName);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Получить nextRun по id существующего в БД шаблона
|
||||
/// </summary>
|
||||
/// <param name="templateId">ИД шаблона</param>
|
||||
/// <param name="isNew">При перемещении шаблона в новую для него работу, ставить isNew=true</param>
|
||||
/// <returns>NextRun или null, если null - то ошибка</returns>
|
||||
Task<DateTimeOffset?> GetNextRunForTemplateAsync(Guid templateId, bool isNew);
|
||||
|
||||
/// <summary>
|
||||
/// Получить часовой пояс УЗ ЕСПП
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
TimeSpan GetEsppAccountOffset();
|
||||
|
||||
/// <summary>
|
||||
/// Получить продолжительность в днях
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
int GetDurationDays(JobGroupDistributionConfig config);
|
||||
|
||||
/// <summary>
|
||||
/// Получить список рабочих дней в диапазоне, исключая выходные и праздники.
|
||||
/// </summary>
|
||||
/// <param name="start"></param>
|
||||
/// <param name="end"></param>
|
||||
/// <param name="excludeWeekends">Исключить выходные и праздники</param>
|
||||
/// <returns></returns>
|
||||
Task<List<DateOnly>> GetWorkDaysAsync(DateOnly start, DateOnly end, bool excludeWeekends);
|
||||
|
||||
/// <summary>
|
||||
/// Получить список выходных дней
|
||||
/// </summary>
|
||||
/// <param name="startDate"></param>
|
||||
/// <param name="endDate"></param>
|
||||
/// <returns></returns>
|
||||
Task<HashSet<DateOnly>> GetWeekendsAsync(DateOnly startDate, DateOnly endDate);
|
||||
}
|
||||
}
|
||||
@@ -153,7 +153,7 @@ namespace PARR.DAL.NextRunServices
|
||||
|
||||
public async Task<DateTimeOffset> GetNextRunForNewTemplateAsync(Guid jobGroupId, string responseArea)
|
||||
{
|
||||
// определяю это автораспределение или нет, вызываю соответствующий рассчет
|
||||
// определяю это автораспределение или нет, вызываю соответствующий расчет
|
||||
|
||||
var jobGroup = await jobGroupService.Get()
|
||||
.Include(t => t.DistributionConfig)
|
||||
@@ -295,6 +295,7 @@ namespace PARR.DAL.NextRunServices
|
||||
}
|
||||
|
||||
|
||||
#region Ok
|
||||
|
||||
/// <summary>
|
||||
/// Получить продолжительность в днях
|
||||
@@ -331,6 +332,8 @@ namespace PARR.DAL.NextRunServices
|
||||
return periodDays;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Получить дату начала распределения
|
||||
@@ -438,14 +441,15 @@ namespace PARR.DAL.NextRunServices
|
||||
//return resultNextRun;
|
||||
}
|
||||
|
||||
public DateTimeOffset GetNextRunWithResponseAreaOffset(DateTimeOffset nextRun, string responseArea)
|
||||
{
|
||||
var responseAreaOffset = scheduleResponseAreaTimeOffsetService.GetByResponseAreaOrDefault(responseArea).UtcTimeOffset;
|
||||
var resulstNextRun = nextRun.Add(responseAreaOffset);
|
||||
logger.LogDebug("Смещение nextRun для ЗО {responseArea}, входящий: {nextRun}, смещение: {offset}, результат: {result}", responseArea, nextRun, responseAreaOffset, resulstNextRun);
|
||||
//// был паблик
|
||||
//private DateTimeOffset GetNextRunWithResponseAreaOffset(DateTimeOffset nextRun, string responseArea)
|
||||
//{
|
||||
// var responseAreaOffset = scheduleResponseAreaTimeOffsetService.GetByResponseAreaOrDefault(responseArea).UtcTimeOffset;
|
||||
// var resulstNextRun = nextRun.Add(responseAreaOffset);
|
||||
// logger.LogDebug("Смещение nextRun для ЗО {responseArea}, входящий: {nextRun}, смещение: {offset}, результат: {result}", responseArea, nextRun, responseAreaOffset, resulstNextRun);
|
||||
|
||||
return resulstNextRun;
|
||||
}
|
||||
// return resulstNextRun;
|
||||
//}
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
||||
833
PARR.DAL/NextRunServices/NextRunServiceV2.cs
Normal file
833
PARR.DAL/NextRunServices/NextRunServiceV2.cs
Normal file
@@ -0,0 +1,833 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
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, TemplateStatusTypeEnum? templateStatusType)
|
||||
{
|
||||
logger.LogInformation("Начинаю распределять шаблоны для группы работ {gobGroupId}, templateStatusType: {templateStatusType}", jobGroupId, templateStatusType);
|
||||
|
||||
var jobGroup = await jobGroupService.Get()
|
||||
.AsNoTracking()
|
||||
.Include(t => t.DistributionConfig)
|
||||
.ThenInclude(t => t.DistributionPeriod)
|
||||
.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 templateQuery = templateService.Get()
|
||||
.AsNoTracking()
|
||||
.Include(t => t.Job)
|
||||
.Where(t =>
|
||||
t.Job!.GroupId == jobGroupId
|
||||
//&& t.StatusTypeId == TemplateStatusTypeEnum.Used
|
||||
);
|
||||
|
||||
if (templateStatusType.HasValue)
|
||||
templateQuery = templateQuery.Where(t => t.StatusTypeId == templateStatusType);
|
||||
|
||||
|
||||
var allTemplates = await templateQuery.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)
|
||||
{
|
||||
// группировать по РГ
|
||||
logger.LogDebug("Нужно группировать по РГ");
|
||||
|
||||
// получаем для каждого шаблона РГ и сразу группируем по РГ
|
||||
var templatesByWorkGroup = new Dictionary<string?, List<Template>>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var template in allTemplates)
|
||||
{
|
||||
var workGroupName = await shortcodesService.ApplyShortcodesAsync(template.Job!.WorkGroupMask, template);
|
||||
// Если даже workGroupName == null, ну и ладно, сгруппируем по null, и распределим шаблоны в рамках этого null
|
||||
if (workGroupName == null)
|
||||
logger.LogWarning("Для шаблона {id}, с маской РГ '{workGroupMask}', не смог с помощью шорткода определить рабочую группу, шорткод сервис вернул РГ: '{workGroupName}'",
|
||||
template.Id, template.Job!.WorkGroupMask, workGroupName);
|
||||
|
||||
// Добавляем шаблон в группу
|
||||
if (!templatesByWorkGroup.TryGetValue(workGroupName, out var templatesList))
|
||||
{
|
||||
templatesList = new List<Template>();
|
||||
templatesByWorkGroup[workGroupName] = templatesList;
|
||||
}
|
||||
templatesList.Add(template);
|
||||
}
|
||||
logger.LogDebug("Групп для распределения: {count}", templatesByWorkGroup.Count);
|
||||
|
||||
var dateStart = GetDateStart();
|
||||
|
||||
// Будем распределять каждую РГ отдельно
|
||||
foreach (var (workGroupName, templates) in templatesByWorkGroup)
|
||||
{
|
||||
logger.LogDebug("Готовлюсь распределать шаблоны {templateCount} шт. в РГ '{workGroup}'", templates.Count, workGroupName);
|
||||
|
||||
if (isResponseAreaTimeZone)
|
||||
{
|
||||
// использовать часовой пояс РГ
|
||||
logger.LogDebug("Использовать часовой пояс РГ");
|
||||
|
||||
// считаем, что у всех шаблонов сгруппированных по РГ, одна ЗО
|
||||
// получаем ЗО для первого шаблона
|
||||
var firstTemplate = templates.First();
|
||||
var responseArea = await shortcodesService.ApplyShortcodesAsync(firstTemplate.Job!.ResponseAreaMask, firstTemplate);
|
||||
if (responseArea == null)
|
||||
logger.LogWarning("Для шаблона {id}, с маской ЗО '{responseAreaMask}', не смог с помощью шорткода определить ЗО, шорткод сервис вернул ЗО: '{responseArea}'",
|
||||
firstTemplate.Id, firstTemplate.Job!.ResponseAreaMask, responseArea);
|
||||
|
||||
var offset = GetOffsetForDistributionByResponseArea(responseArea);
|
||||
var templatesForDistribute = templates.Select(t => new TemplateNextRunDto(t.Id, t.NextRun)).ToList();
|
||||
var referenceDate = GetReferenceDate(jobGroup, responseArea);
|
||||
|
||||
logger.LogInformation("Буду распределать шаблоны {count} шт, сгруппированные по РГ '{workGroup}', использую часовой пояс ЗО. dateStart: {dateStart}, offset: {offset}, referenceDate: {referenceDate}",
|
||||
templatesForDistribute.Count, workGroupName, dateStart, offset, referenceDate);
|
||||
|
||||
var distributionResult = await templateDistributor.DistributeTemplatesAsync(dateStart, durationDays, referenceDate, offset, templatesForDistribute, jobGroup.DistributionConfig.IsExcludeWeekends);
|
||||
|
||||
logger.LogDebug("Закончил распределение для РГ '{workGroupName}', referenceDate: {referenceDate}, часового пояса ЗО {responseArea}, utcOffset: {offset}, распределил шаблонов: {count} шт.",
|
||||
workGroupName, referenceDate, responseArea, offset, distributionResult.Count);
|
||||
|
||||
distributedTemplates.AddRange(distributionResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
// не нужно использовать часовой пояс РГ (используем часовой пояс УЗ ЕСПП)
|
||||
logger.LogDebug("Не нужно использовать часовой пояс РГ (используем часовой пояс УЗ ЕСПП)");
|
||||
|
||||
var offset = GetOffsetForDistributionByResponseArea();
|
||||
var templatesForDistribute = templates.Select(t => new TemplateNextRunDto(t.Id, t.NextRun)).ToList();
|
||||
var referenceDate = GetReferenceDate(jobGroup, null);
|
||||
|
||||
logger.LogInformation("Буду распределать шаблоны {count} шт, сгруппированные по РГ '{workGroup}', НЕ НУЖНО использовать часовой пояс ЗО. dateStart: {dateStart}, offset: {offset}, referenceDate: {referenceDate}",
|
||||
templatesForDistribute.Count, workGroupName, dateStart, offset, referenceDate);
|
||||
|
||||
var distributionResult = await templateDistributor.DistributeTemplatesAsync(dateStart, durationDays, referenceDate, offset, templatesForDistribute, jobGroup.DistributionConfig.IsExcludeWeekends);
|
||||
|
||||
logger.LogDebug("Закончил распределение для РГ '{workGroup}', referenceDate: {referenceDate}, utcOffset: {offset}, распределил шаблонов: {count} шт.", workGroupName, referenceDate, offset, distributionResult.Count);
|
||||
|
||||
distributedTemplates.AddRange(distributionResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//не нужно группировать по РГ
|
||||
logger.LogDebug("Не нужно группировать по РГ");
|
||||
|
||||
if (isResponseAreaTimeZone)
|
||||
{
|
||||
// использовать ЧАСОВОЙ ПОЯС ЗО (нужно сгруппировать шаблоны по часовым поясам, затем отдельно распределить каждую группу)
|
||||
logger.LogDebug("Использовать ЧАСОВОЙ ПОЯС ЗО (нужно сгруппировать шаблоны по часовым поясам, затем отдельно распределить каждую группу)");
|
||||
|
||||
// получаем для каждого шаблона ЗО и сразу группируем по ЗО
|
||||
var templatesByResponseArea = new Dictionary<string?, List<Template>>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var template in allTemplates)
|
||||
{
|
||||
var responseArea = await shortcodesService.ApplyShortcodesAsync(template.Job!.ResponseAreaMask, template);
|
||||
if (responseArea == null)
|
||||
logger.LogWarning("Для шаблона {id}, с маской ЗО '{responseAreaMask}', не смог с помощью шорткода определить ЗО, шорткод сервис вернул ЗО: '{responseArea}'",
|
||||
template.Id, template.Job!.ResponseAreaMask, responseArea);
|
||||
|
||||
// Добавляем шаблон в группу
|
||||
if (!templatesByResponseArea.TryGetValue(responseArea, out var templateList))
|
||||
{
|
||||
templateList = new List<Template>();
|
||||
templatesByResponseArea[responseArea] = templateList;
|
||||
}
|
||||
templateList.Add(template);
|
||||
}
|
||||
logger.LogDebug("Групп для распределения: {count}", templatesByResponseArea.Count);
|
||||
|
||||
var dateStart = GetDateStart();
|
||||
|
||||
// для каждой ЗО получить offset и распределить шаблоны
|
||||
foreach (var (responseArea, templates) in templatesByResponseArea)
|
||||
{
|
||||
var offset = GetOffsetForDistributionByResponseArea(responseArea);
|
||||
var templatesForDistribute = templates.Select(t => new TemplateNextRunDto(t.Id, t.NextRun)).ToList();
|
||||
var referenceDate = GetReferenceDate(jobGroup, responseArea);
|
||||
|
||||
logger.LogInformation("Буду распределять шаблоны {count} шт, их НЕ НУЖНО группировать по РГ, НУЖНО использовать часовой пояс ЗО: {responseArea}, utcOffset: {offset}. dateStart: {dateStart}, referenceDate: {referenceDate}",
|
||||
templatesForDistribute.Count, responseArea, offset, dateStart, referenceDate);
|
||||
|
||||
var distributionResult = await templateDistributor.DistributeTemplatesAsync(dateStart, durationDays, referenceDate, offset, templatesForDistribute, jobGroup.DistributionConfig.IsExcludeWeekends);
|
||||
|
||||
logger.LogDebug("Закончил распределение для часового пояса ЗО {responseArea}, utcOffset: {offset}, referenceDate: {referenceDate}, распределил шаблонов: {count} шт.", responseArea, offset, referenceDate, distributionResult.Count);
|
||||
|
||||
distributedTemplates.AddRange(distributionResult);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// не нужно использовать часовой пояс ЗО
|
||||
logger.LogDebug("Не нужно использовать часовой пояс ЗО. Распределю все шаблоны без группировки.");
|
||||
|
||||
var offset = GetOffsetForDistributionByResponseArea();
|
||||
var templatesForDistribute = allTemplates.Select(t => new TemplateNextRunDto(t.Id, t.NextRun)).ToList();
|
||||
var referenceDate = GetReferenceDate(jobGroup, null);
|
||||
var dateStart = GetDateStart();
|
||||
|
||||
logger.LogInformation("Буду распределать шаблоны {count} шт, их НЕ НУЖНО группировать по РГ, НЕ НУЖНО использовать часовой пояс ЗО. dateStart: {dateStart}, offset: {offset}, referenceDate: {referenceDate}",
|
||||
templatesForDistribute.Count, dateStart, offset, referenceDate);
|
||||
|
||||
distributedTemplates = await templateDistributor.DistributeTemplatesAsync(dateStart, durationDays, referenceDate, offset, templatesForDistribute, jobGroup.DistributionConfig.IsExcludeWeekends);
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogInformation("Закончил распределение шаблонов {count} шт. для jobGroupId: {jobGroupId}. Изменено: {changedCount}, без изменений: {oldCount}",
|
||||
distributedTemplates.Count, jobGroupId, distributedTemplates.Count(t => t.NextRun != t.NextRunOld), distributedTemplates.Count(t => t.NextRun == t.NextRunOld));
|
||||
|
||||
return distributedTemplates;
|
||||
}
|
||||
|
||||
|
||||
public async Task<DateTimeOffset?> GetNextRunForNewTemplateAsync(Guid jobGroupId, string workGroupName, string responseAreaName)
|
||||
{
|
||||
logger.LogDebug("Получить nextRun для вновь создаваемого шаблона. jobGroupId: {jobGroupId}, workGroupName: {workGroupName}, responseAreaName: {responseAreaName}",
|
||||
jobGroupId, workGroupName, responseAreaName);
|
||||
|
||||
var jobGroup = await jobGroupService.Get()
|
||||
.AsNoTracking()
|
||||
.Include(t => t.DistributionConfig)
|
||||
.ThenInclude(t => t.DistributionPeriod)
|
||||
.FirstOrDefaultAsync(t => t.Id == jobGroupId);
|
||||
|
||||
if (jobGroup == null)
|
||||
{
|
||||
logger.LogError("Не найдена группа работ с Id: {jobGroupId}.", jobGroupId);
|
||||
//throw new ArgumentNullException(nameof(jobGroupId), $"Не найдена группа работ с Id: {jobGroupId}");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (jobGroup.IsAutoDistributionEnabled)
|
||||
{
|
||||
// автораспределение
|
||||
logger.LogDebug("Расчет используя метод распределения работ");
|
||||
|
||||
if (jobGroup.DistributionConfig == null)
|
||||
{
|
||||
logger.LogError("Для группы работ {jobGroupId}, указано автораспределение, но отсутствует конфиг в таблице {GroupDistributionConfigs}", jobGroupId, nameof(JobGroupDistributionConfig));
|
||||
//throw new ArgumentNullException(nameof(JobGroupDistributionConfig), $"Для jobGroupId: {jobGroupId} отсутствует конфигурация автораспределения в таблице {nameof(JobGroupDistributionConfig)}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var startDate = GetDateStart();
|
||||
var duration = GetDurationDays(jobGroup.DistributionConfig);
|
||||
var excludeWeekends = jobGroup.DistributionConfig.IsExcludeWeekends;
|
||||
var targetTemplate = new TemplateNextRunDto(Guid.Empty, null);
|
||||
var templateStatusType = TemplateStatusTypeEnum.Used;
|
||||
var allTemplatesQuery = templateService.Get()
|
||||
.AsNoTracking()
|
||||
.Include(t => t.Job)
|
||||
.Where(t => t.StatusTypeId == templateStatusType && t.Job!.GroupId == jobGroupId);
|
||||
|
||||
// группировать по РГ
|
||||
var isGroupingByWorkGroup = jobGroup.DistributionConfig.IsGroupingByWorkGroup;
|
||||
|
||||
// использовать часовой пояс РГ
|
||||
var isResponseAreaTimeZone = jobGroup.IsResponseAreaTimezone;
|
||||
|
||||
if (isGroupingByWorkGroup)
|
||||
{
|
||||
// группировать по РГ
|
||||
logger.LogDebug("Нужно группировать по РГ");
|
||||
|
||||
// получаем для всех шаблонов РГ и группируем их по РГ
|
||||
var templatesByWorkGroup = new Dictionary<string?, List<Template>>(StringComparer.OrdinalIgnoreCase);
|
||||
var allTemplates = await allTemplatesQuery.ToListAsync();
|
||||
foreach (var template in allTemplates)
|
||||
{
|
||||
var templateWorkGroupName = await shortcodesService.ApplyShortcodesAsync(template.Job!.WorkGroupMask, template);
|
||||
|
||||
// Если даже workGroupName == null, ну и ладно, сгруппируем по null
|
||||
if (templateWorkGroupName == null)
|
||||
logger.LogWarning("Для шаблона {id}, с маской РГ '{workGroupMask}', не смог с помощью шорткода определить рабочую группу, шорткод сервис вернул РГ: '{workGroupName}'",
|
||||
template.Id, template.Job!.WorkGroupMask, templateWorkGroupName);
|
||||
|
||||
// Добавляем шаблон в группу
|
||||
if (!templatesByWorkGroup.TryGetValue(templateWorkGroupName, out var templatesList))
|
||||
{
|
||||
templatesList = new List<Template>();
|
||||
templatesByWorkGroup[templateWorkGroupName] = templatesList;
|
||||
}
|
||||
templatesList.Add(template);
|
||||
}
|
||||
|
||||
//Берем шаблоны только нашей РГ workGroupName
|
||||
templatesByWorkGroup.TryGetValue(workGroupName, out var templatesWithWorkGroup);
|
||||
|
||||
var templatesByWorkGroupDto = (templatesWithWorkGroup ?? new List<Template>()).Select(t => new TemplateNextRunDto(t.Id, t.NextRun)).ToList();
|
||||
|
||||
logger.LogDebug("Шаблонов в РГ {workGroupName} - {count} шт.", workGroupName, templatesByWorkGroupDto.Count);
|
||||
|
||||
if (isResponseAreaTimeZone)
|
||||
{
|
||||
// использовать часовой пояс РГ
|
||||
logger.LogDebug("Использовать часовой пояс РГ '{responseAreaName}'", responseAreaName);
|
||||
|
||||
var offset = GetOffsetForDistributionByResponseArea(responseAreaName);
|
||||
var referenceDate = GetReferenceDate(jobGroup, responseAreaName);
|
||||
|
||||
var nextRun = await templateDistributor.GetNextRunForTemplateAsync(startDate, duration, referenceDate, offset, targetTemplate, templatesByWorkGroupDto, excludeWeekends, isNew: true);
|
||||
|
||||
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: true);
|
||||
|
||||
return nextRun.NextRun;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// не нужно группировать по РГ
|
||||
logger.LogDebug("Не нужно группировать по РГ");
|
||||
|
||||
if (isResponseAreaTimeZone)
|
||||
{
|
||||
// использовать ЧАСОВОЙ ПОЯС ЗО
|
||||
|
||||
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 template in allTemplates)
|
||||
{
|
||||
var responseArea = await shortcodesService.ApplyShortcodesAsync(template.Job!.ResponseAreaMask, template);
|
||||
if (responseArea == null)
|
||||
logger.LogWarning("Для шаблона {id}, с маской ЗО '{responseAreaMask}', не смог с помощью шорткода определить ЗО, шорткод сервис вернул ЗО: '{responseArea}'",
|
||||
template.Id, template.Job!.ResponseAreaMask, responseArea);
|
||||
|
||||
// Добавляем шаблон в группу
|
||||
if (!templatesByResponseArea.TryGetValue(responseArea, out var templateList))
|
||||
{
|
||||
templateList = new List<Template>();
|
||||
templatesByResponseArea[responseArea] = templateList;
|
||||
}
|
||||
templateList.Add(template);
|
||||
}
|
||||
|
||||
// из сгруппированных шаблонов, получаем существующие с нашей переданной 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: true);
|
||||
|
||||
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: true);
|
||||
|
||||
return nextRun.NextRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// расписание ЕСПП
|
||||
logger.LogDebug("Расчет используя метод ЕСПП");
|
||||
|
||||
if (jobGroup.IsResponseAreaTimezone)
|
||||
{
|
||||
// использовать часовой пояс ЗО
|
||||
|
||||
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(jobGroupId, referenceDate, offset);
|
||||
|
||||
return nextRun;
|
||||
}
|
||||
else
|
||||
{
|
||||
// не использовать часовой пояс ЗО
|
||||
// так как это классическое расписание еспп, и не используется часовой пояс РГ, то нам нужно считать в часовом поясе УЗ ЕСПП, т.е. МСК
|
||||
|
||||
var referenceDate = GetReferenceDate(jobGroup, null);
|
||||
var offset = GetOffsetForDistributionByResponseArea();
|
||||
|
||||
logger.LogDebug("Расчет nextRun используя метод ЕСПП, не использую часовой пояс ЗО, использую часовой пояс УЗ ЕСПП, referenceDate: {referenceDate}, offset: {offset}", referenceDate, offset);
|
||||
|
||||
var nextRun = await esppScheduleTransformService.GetNextDateAsync(jobGroupId, referenceDate, offset);
|
||||
|
||||
return nextRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
|
||||
public async Task<List<DateOnly>> GetWorkDaysAsync(DateOnly start, DateOnly end, bool excludeWeekends)
|
||||
{
|
||||
return await templateDistributor.GetWorkDaysAsync(start, end, excludeWeekends);
|
||||
}
|
||||
|
||||
|
||||
public async Task<HashSet<DateOnly>> GetWeekendsAsync(DateOnly startDate, DateOnly endDate)
|
||||
{
|
||||
return await templateDistributor.GetWeekendsAsync(startDate, endDate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить часовую зону для распределения
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private TimeSpan GetOffsetForDistributionByResponseArea(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public 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()
|
||||
{
|
||||
//var dateStart = DateOnly.FromDateTime(DateTimeOffset.UtcNow.ToOffset(offset).Date);
|
||||
//logger.LogDebug("Дата начала распределения (сегодня) в целевом часовом поясе {dateStart}, offset: {offset}", dateStart, offset);
|
||||
var dateStart = DateOnly.FromDateTime(DateTime.UtcNow.Date);
|
||||
logger.LogDebug("Дата начала распределения (сегодня): {dateStart}", dateStart);
|
||||
|
||||
return dateStart;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Получить referenceDate для расчета с учетом часового пояса ЗО или без учета
|
||||
/// </summary>
|
||||
/// <param name="jobGroup"></param>
|
||||
/// <param name="responseArea">Не обзяательный параметр, нужен обязательно при расчете с учетом часового пояса ЗО</param>
|
||||
/// <returns></returns>
|
||||
private DateTimeOffset GetReferenceDate(JobGroup jobGroup, string? responseArea)
|
||||
{
|
||||
// для того чтобы посчитать nextRun в часовом поясе ЗО, нужно пересчитать refDate чтобы понять в какое время хотел выполнять заказчик
|
||||
|
||||
logger.LogDebug("Исходный referenceDate UTC: {referenceDate}", jobGroup.ReferenceDate);
|
||||
|
||||
if (jobGroup.IsResponseAreaTimezone)
|
||||
{
|
||||
// нужно вернуть с учетом часового пояса клиента сохранившего refDate (используется для расчета с учетом часового пояса ЗО)
|
||||
|
||||
var clientOffset = jobGroup.UserTimeZoneOffset;
|
||||
|
||||
if (!clientOffset.HasValue)
|
||||
{
|
||||
logger.LogError("При расчете referenceDate для ЗО '{responseArea}', UserTimeZoneOffset=null. Вернул referenceDate {referenceDate} без смещения ЗО",
|
||||
responseArea, jobGroup.ReferenceDate);
|
||||
return jobGroup.ReferenceDate;
|
||||
}
|
||||
|
||||
if (responseArea == null)
|
||||
{
|
||||
logger.LogError("При расчете referenceDate для ЗО '{responseArea}', responseArea=null. Вернул referenceDate {referenceDate} без смещения ЗО",
|
||||
responseArea, jobGroup.ReferenceDate);
|
||||
return jobGroup.ReferenceDate;
|
||||
}
|
||||
|
||||
var responseAreaOffset = scheduleResponseAreaTimeOffsetService.GetByResponseAreaOrDefault(responseArea).UtcTimeOffset;
|
||||
|
||||
// = реф + офКлиента(который нажал в ГУИ сохранить)-офЗО
|
||||
|
||||
var resultOffset = clientOffset.Value - responseAreaOffset;
|
||||
|
||||
var refDateWithResponseAreaOffset = jobGroup.ReferenceDate.Add(resultOffset);
|
||||
|
||||
logger.LogDebug("Клиент ожидает, что будет выполняться задача в каждой ЗО в {time}. referenceDate+clientOffset, {referenceDate}, {clientOffset}",
|
||||
TimeOnly.FromDateTime(jobGroup.ReferenceDate.Add(clientOffset.Value).DateTime), jobGroup.ReferenceDate, clientOffset);
|
||||
logger.LogDebug("Итоговый referenceDate UTC: {resultRefDate}, для использования часовой зоны ЗО: '{responseArea}', добавлено времени {resultOffset} к исходному {refDate}",
|
||||
refDateWithResponseAreaOffset, responseArea, resultOffset, jobGroup.ReferenceDate);
|
||||
logger.LogDebug("Если итоговый referenceDate UTC {refDateWithResponseAreaOffset} перевести в часовой пояс ЗО {responseAreaOffset}, то дата в часовом поясе ЗО будет {refDateTargetTz}",
|
||||
refDateWithResponseAreaOffset, responseAreaOffset, refDateWithResponseAreaOffset.Add(responseAreaOffset));
|
||||
|
||||
return refDateWithResponseAreaOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// если НЕ НУЖНО в часовом поясе ЗО, то просто возвращаем refDate
|
||||
logger.LogDebug("Итоговый referenceDate UTC: {refDate}, не нужно использовать часовой пояс ЗО", jobGroup.ReferenceDate);
|
||||
|
||||
return jobGroup.ReferenceDate;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,13 @@
|
||||
using PARR.DAL.Contracts;
|
||||
using PARR.DAL.DomainModels;
|
||||
using PARR.DAL.Services.Interfaces;
|
||||
using PARR.DAL.TransformServices;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace PARR.DAL.NextRunServices.Subservices
|
||||
{
|
||||
internal class EsppScheduleTransformService : IEsppScheduleTransformService
|
||||
{
|
||||
private readonly IEsppSchTypeConfigService esppSchTypeConfigService;
|
||||
//private readonly INextRunModifierService nextRunModifierService;
|
||||
private readonly ILogger<EsppScheduleTransformService> logger;
|
||||
|
||||
private static readonly Dictionary<string, int> monthDict = new Dictionary<string, int>()
|
||||
@@ -52,11 +51,9 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
public EsppScheduleTransformService(
|
||||
ILogger<EsppScheduleTransformService> logger,
|
||||
IEsppSchTypeConfigService esppSchTypeConfigService
|
||||
//INextRunModifierService nextRunModifierService
|
||||
)
|
||||
{
|
||||
this.esppSchTypeConfigService = esppSchTypeConfigService;
|
||||
//this.nextRunModifierService = nextRunModifierService;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@@ -69,12 +66,19 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
|
||||
private DateTimeOffset GetNextDate(EsppScheduleDto esppSchedule, DateTimeOffset referenceDate, TimeSpan? offset)
|
||||
{
|
||||
//offset - желательно передавать всегда, хотябы МСК, чтобы рассчитывались правильно даты, так как в ЕСПП все в МСК
|
||||
|
||||
logger.LogDebug("Начинаю рассчет nextRun по расписанию ЕСПП. Входные данные: date: {referenceDate}, offset: {offset}", referenceDate, offset);
|
||||
|
||||
var nextRun = referenceDate;
|
||||
|
||||
var finalOffset = new TimeSpan(3, 0, 0) + offset ?? TimeSpan.Zero;
|
||||
//var finalOffset = new TimeSpan(3, 0, 0) + (offset ?? TimeSpan.Zero);
|
||||
var finalOffset = offset ?? TimeSpan.Zero;
|
||||
|
||||
var offsetReferenceDate = referenceDate.ToOffset(finalOffset);
|
||||
|
||||
logger.LogDebug("Дата после добавления offset - {offsetReferenceDate}", offsetReferenceDate);
|
||||
|
||||
////---------
|
||||
//// Для рассчета по МСК времени, потому что в ЮТС может быть еще ВСК, а по МСК это уже ПНД
|
||||
//var offsetReferenceDate = referenceDate.ToOffset(new TimeSpan(3, 0, 0));
|
||||
@@ -108,71 +112,23 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
break;
|
||||
}
|
||||
|
||||
logger.LogDebug("Рассчитанная дата, с учетом offset: {nextRun}", nextRun);
|
||||
|
||||
// Возвращаем обратно в ЮТС
|
||||
nextRun = nextRun.ToOffset(TimeSpan.Zero);
|
||||
|
||||
logger.LogDebug("Рассчитанная дата с нулевым offset: {nextRun}", nextRun);
|
||||
|
||||
return nextRun;
|
||||
}
|
||||
|
||||
|
||||
//private List<DateTimeOffset> GetNextSchedule(EsppScheduleDto esppSchedule, DateTimeOffset referenceDate)
|
||||
//{
|
||||
// // расписание на оставшееся на сегодня время. его так мало...
|
||||
// referenceDate = referenceDate.UtcDateTime;
|
||||
// var schedules = new List<DateTimeOffset>();
|
||||
// var curTime = referenceDate;
|
||||
|
||||
// //Если ещё не произошло то добавляем расписание, возможно это новое AiW и он ещё ниразу не запускался.
|
||||
// //lastRun это будущее
|
||||
// if (referenceDate >= DateTimeOffset.UtcNow && referenceDate <= DateTimeOffset.UtcNow.EndOfDay())
|
||||
// schedules.Add(referenceDate);
|
||||
|
||||
// while (curTime < DateTimeOffset.UtcNow.EndOfDay())
|
||||
// {
|
||||
// switch (esppSchedule.TypeSchedule.Id)
|
||||
// {
|
||||
// case (int)EsppSchTypeScheduleEnum.Regularly:
|
||||
// curTime = GetNextDateRegularly(esppSchedule.Values, curTime);
|
||||
// break;
|
||||
// case (int)EsppSchTypeScheduleEnum.Weekly:
|
||||
// curTime = GetNextDateWeekly(esppSchedule.Values, curTime);
|
||||
// break;
|
||||
// case (int)EsppSchTypeScheduleEnum.Monthly:
|
||||
// curTime = GetNextDateMonthly(esppSchedule.Values, curTime);
|
||||
// break;
|
||||
// case (int)EsppSchTypeScheduleEnum.Monthly2:
|
||||
// curTime = GetNextDateMonthly2(esppSchedule.Values, curTime);
|
||||
// break;
|
||||
// case (int)EsppSchTypeScheduleEnum.Annually:
|
||||
// curTime = GetNextDateAnnually(esppSchedule.Values, curTime);
|
||||
// break;
|
||||
// case (int)EsppSchTypeScheduleEnum.Annually2:
|
||||
// curTime = GetNextDateAnnually2(esppSchedule.Values, curTime);
|
||||
// break;
|
||||
// }
|
||||
|
||||
// schedules.Add(curTime);
|
||||
// }
|
||||
// schedules.RemoveAll(s => s > DateTimeOffset.UtcNow.EndOfDay());
|
||||
|
||||
// return schedules.OrderBy(t => t).ToList();
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//public async Task<List<DateTimeOffset>> GetNextScheduleAsync(Guid jobGroupId, DateTimeOffset referenceDate)
|
||||
//{
|
||||
// //TODO: тут не проверен переход через выходные дни!!! Переход не используется, так как тут не рассчитываем NextRun.
|
||||
// //В общем проверить, когда будем тестировать агента, что с датами все ок
|
||||
// var esppSchedule = await GetEsppScheduleAsync(jobGroupId);
|
||||
// var nextSchedule = GetNextSchedule(esppSchedule, referenceDate);
|
||||
|
||||
// return nextSchedule;
|
||||
//}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Получить расписание из БД
|
||||
/// </summary>
|
||||
/// <param name="jobGroupId"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private async Task<EsppScheduleDto> GetEsppScheduleAsync(Guid jobGroupId)
|
||||
{
|
||||
var esppSchedule = await esppSchTypeConfigService.GetEsppScheduleDtoAsync(jobGroupId);
|
||||
@@ -236,7 +192,22 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
|
||||
var calcDay = referenceDate;
|
||||
if (calcDay < DateTimeOffset.UtcNow)
|
||||
calcDay = new DateTimeOffset(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month, DateTimeOffset.UtcNow.Day, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
{
|
||||
var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
calcDay = new DateTimeOffset(
|
||||
nowInTargetTz.Year,
|
||||
//DateTimeOffset.UtcNow.Year,
|
||||
nowInTargetTz.Month,
|
||||
//DateTimeOffset.UtcNow.Month,
|
||||
nowInTargetTz.Day,
|
||||
//DateTimeOffset.UtcNow.Day,
|
||||
referenceDate.Hour,
|
||||
referenceDate.Minute,
|
||||
referenceDate.Second,
|
||||
referenceDate.Offset
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (((int)calcDay.DayOfWeek) != dwNum)
|
||||
calcDay = OffsetToDayOfWeek(dwNum, calcDay);
|
||||
@@ -260,7 +231,20 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
|
||||
var calcDay = new DateTimeOffset(referenceDate.Year, referenceDate.Month, dayNum, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
if (referenceDate < DateTimeOffset.UtcNow)
|
||||
calcDay = new DateTimeOffset(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month, dayNum, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
{
|
||||
var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
calcDay = new DateTimeOffset(
|
||||
nowInTargetTz.Year,
|
||||
nowInTargetTz.Month,
|
||||
dayNum,
|
||||
referenceDate.Hour,
|
||||
referenceDate.Minute,
|
||||
referenceDate.Second,
|
||||
referenceDate.Offset
|
||||
);
|
||||
//calcDay = new DateTimeOffset(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month, dayNum, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
}
|
||||
|
||||
|
||||
//TODO: вот это повторяется от метода к методу
|
||||
//Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
|
||||
@@ -285,9 +269,29 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
throw new ArgumentException($"Неизвестное значение: {values[1].Value.Value}");
|
||||
}
|
||||
|
||||
//var startDay = referenceDate < DateTimeOffset.UtcNow
|
||||
// ? new DateTimeOffset(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month, 1, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset)
|
||||
// : new DateTimeOffset(referenceDate.Year, referenceDate.Month, 1, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
|
||||
var startDay = referenceDate < DateTimeOffset.UtcNow
|
||||
? new DateTimeOffset(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month, 1, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset)
|
||||
: new DateTimeOffset(referenceDate.Year, referenceDate.Month, 1, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
? new DateTimeOffset(
|
||||
DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset).Year,
|
||||
DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset).Month,
|
||||
1,
|
||||
referenceDate.Hour,
|
||||
referenceDate.Minute,
|
||||
referenceDate.Second,
|
||||
referenceDate.Offset
|
||||
)
|
||||
: new DateTimeOffset(
|
||||
referenceDate.Year,
|
||||
referenceDate.Month,
|
||||
1,
|
||||
referenceDate.Hour,
|
||||
referenceDate.Minute,
|
||||
referenceDate.Second,
|
||||
referenceDate.Offset
|
||||
);
|
||||
|
||||
var daysList = FindAllDaysInMonth(dwNum, startDay);
|
||||
|
||||
@@ -330,7 +334,12 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
throw new ArgumentException($"Неизвестное значение: {values[0].Value.Value}");
|
||||
}
|
||||
|
||||
var year = (referenceDate.Year < DateTimeOffset.UtcNow.Year) ? DateTimeOffset.UtcNow.Year : referenceDate.Year;
|
||||
//var year = (referenceDate.Year < DateTimeOffset.UtcNow.Year) ? DateTimeOffset.UtcNow.Year : referenceDate.Year;
|
||||
//сравниваем года в одном часовом поясе
|
||||
var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
var year = (referenceDate.Year < nowInTargetTz.Year)
|
||||
? nowInTargetTz.Year
|
||||
: referenceDate.Year;
|
||||
|
||||
var calcDay = new DateTimeOffset(
|
||||
year, monthNum, dayNum,
|
||||
@@ -379,9 +388,15 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
throw new ArgumentException($"Неизвестное значение: {values[1].Value.Value}");
|
||||
}
|
||||
|
||||
var year = (referenceDate.Year < DateTimeOffset.UtcNow.Year) ? DateTimeOffset.UtcNow.Year : referenceDate.Year;
|
||||
//var year = (referenceDate.Year < DateTimeOffset.UtcNow.Year) ? DateTimeOffset.UtcNow.Year : referenceDate.Year;
|
||||
// сравниваем года в одном часовом поясе
|
||||
var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
var year = (referenceDate.Year < nowInTargetTz.Year)
|
||||
? nowInTargetTz.Year
|
||||
: referenceDate.Year;
|
||||
|
||||
if (year == DateTimeOffset.UtcNow.Year && referenceDate.Month < DateTimeOffset.UtcNow.Month)
|
||||
//if (year == DateTimeOffset.UtcNow.Year && referenceDate.Month < DateTimeOffset.UtcNow.Month)
|
||||
if (year == nowInTargetTz.Year && referenceDate.Month < nowInTargetTz.Month)
|
||||
year += 1;
|
||||
|
||||
var calcDay = new DateTimeOffset(
|
||||
@@ -432,91 +447,5 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
return calcDay.AddDays(daysOffset);
|
||||
}
|
||||
|
||||
|
||||
//public DateTimeOffset GetNextDateForDistributionRun(DateTimeOffset lastRun, DistributionPeriodTypeEnum periodType, string distributionPeriod)
|
||||
//{
|
||||
// var nextRun = lastRun;
|
||||
|
||||
// switch (periodType)
|
||||
// {
|
||||
// case (DistributionPeriodTypeEnum.Day):
|
||||
// nextRun = lastRun.AddDays(ParseInt(distributionPeriod));
|
||||
// break;
|
||||
// case (DistributionPeriodTypeEnum.Month):
|
||||
// nextRun = lastRun.AddMonths(ParseInt(distributionPeriod));
|
||||
// break;
|
||||
// case (DistributionPeriodTypeEnum.Year):
|
||||
// nextRun = lastRun.AddYears(ParseInt(distributionPeriod));
|
||||
// break;
|
||||
// }
|
||||
|
||||
// return nextRunModifierService.GetWorkDayAsync(nextRun).GetAwaiter().GetResult();
|
||||
//}
|
||||
|
||||
|
||||
//private DateTimeOffset GetPrevDateForDistributionRun(DateTimeOffset lastRun, DistributionPeriodTypeEnum periodType, string distributionPeriod)
|
||||
//{
|
||||
// switch (periodType)
|
||||
// {
|
||||
// case (DistributionPeriodTypeEnum.Day):
|
||||
// return lastRun.AddDays(-ParseInt(distributionPeriod));
|
||||
|
||||
// case (DistributionPeriodTypeEnum.Month):
|
||||
// return lastRun.AddMonths(-ParseInt(distributionPeriod));
|
||||
|
||||
// case (DistributionPeriodTypeEnum.Year):
|
||||
// return lastRun.AddYears(-ParseInt(distributionPeriod));
|
||||
// }
|
||||
|
||||
// return lastRun;
|
||||
//}
|
||||
|
||||
|
||||
//public async Task<DateOnly> GetStartPeriodForDateAsync(Guid jobGroupId, DateTimeOffset date, DateTimeOffset referenceDate, DistributionPeriodTypeEnum periodType, string distributionPeriod)
|
||||
//{
|
||||
// var esppSchedule = await GetEsppScheduleAsync(jobGroupId);
|
||||
|
||||
// var currentStartPeriod = referenceDate;
|
||||
// var currentEndPeriod = GetNextDateForDistributionRun(currentStartPeriod, periodType, distributionPeriod);
|
||||
|
||||
// while (!(date >= currentStartPeriod && date < currentEndPeriod))
|
||||
// {
|
||||
// if (date > currentEndPeriod)
|
||||
// {
|
||||
// currentStartPeriod = currentEndPeriod;
|
||||
// currentEndPeriod = GetNextDateForDistributionRun(currentStartPeriod, periodType, distributionPeriod);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// currentEndPeriod = currentStartPeriod;
|
||||
// currentStartPeriod = GetPrevDateForDistributionRun(currentStartPeriod, periodType, distributionPeriod);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return DateOnly.FromDateTime(currentStartPeriod.DateTime);
|
||||
//}
|
||||
|
||||
|
||||
//private int ParseInt(string value)
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// return int.Parse(value);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// logger.LogError(ex, $"{nameof(this.GetType)}, получение конца периода. Не смог распарсить string в int для значения {value}.");
|
||||
// return 0;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
//private DistributionPeriodTypeEnum ParseDistributionPeriodType(string value)
|
||||
//{
|
||||
// var result = (DistributionPeriodTypeEnum)Enum.Parse(typeof(DistributionPeriodTypeEnum), value);
|
||||
|
||||
// return result;
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,55 +5,13 @@
|
||||
/// </summary>
|
||||
internal interface IEsppScheduleTransformService
|
||||
{
|
||||
///// <summary>
|
||||
///// Получить следующую дату согласно расписания
|
||||
///// </summary>
|
||||
///// <param name="esppSchedule"></param>
|
||||
///// <param name="lastRun"></param>
|
||||
///// <returns></returns>
|
||||
//DateTimeOffset GetNextDate(EsppScheduleDto esppSchedule, DateTimeOffset lastRun);
|
||||
|
||||
///// <summary>
|
||||
///// Получить расписание
|
||||
///// </summary>
|
||||
///// <param name="esppSchedule"></param>
|
||||
///// <param name="lastRun"></param>
|
||||
///// <returns></returns>
|
||||
//List<DateTimeOffset> GetNextSchedule(EsppScheduleDto esppSchedule, DateTimeOffset lastRun);
|
||||
|
||||
/// <summary>
|
||||
/// Получить следующую дату по jobGroupId
|
||||
/// </summary>
|
||||
/// <param name="jobGroupId"></param>
|
||||
/// <param name="lastRun"></param>
|
||||
/// <param name="referenceDate">referenceDate или nextRun</param>
|
||||
/// <param name="offset">offset - желательно передавать всегда, хотябы МСК, чтобы рассчитывались правильно даты, так как в ЕСПП все в МСК</param>
|
||||
/// <returns></returns>
|
||||
Task<DateTimeOffset> GetNextDateAsync(Guid jobGroupId, DateTimeOffset lastRun, TimeSpan? offset = null);
|
||||
|
||||
///// <summary>
|
||||
///// Получить расписание по jobGroupId
|
||||
///// </summary>
|
||||
///// <param name="jobGroupId"></param>
|
||||
///// <param name="lastRun"></param>
|
||||
///// <returns></returns>
|
||||
//Task<List<DateTimeOffset>> GetNextScheduleAsync(Guid jobGroupId, DateTimeOffset lastRun);
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// Получить следующую дату срабатывания в указанном периоде
|
||||
///// </summary>
|
||||
///// <param name="lastRun"></param>
|
||||
///// <param name="periodType"></param>
|
||||
///// <param name="distributionPeriod"></param>
|
||||
///// <returns></returns>
|
||||
//DateTimeOffset GetNextDateForDistributionRun(DateTimeOffset lastRun, DistributionPeriodTypeEnum periodType, string distributionPeriod);
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// Получить дату начала периода распределения относительно опорной даты (Reference Date)
|
||||
///// </summary>
|
||||
///// <param name="jobGroupId"></param>
|
||||
///// <param name="date"></param>
|
||||
///// <returns></returns>
|
||||
//Task<DateOnly> GetStartPeriodForDateAsync(Guid jobGroupId, DateTimeOffset date, DateTimeOffset refrenceDate, DistributionPeriodTypeEnum periodType, string distributionPeriod);
|
||||
Task<DateTimeOffset> GetNextDateAsync(Guid jobGroupId, DateTimeOffset referenceDate, TimeSpan? offset = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using PARR.DAL.NextRunServices.Models;
|
||||
|
||||
namespace PARR.DAL.NextRunServices.Subservices
|
||||
{
|
||||
internal interface ITemplateDistributorV2
|
||||
{
|
||||
/// <summary>
|
||||
/// Распределить шаблоны
|
||||
/// </summary>
|
||||
/// <param name="startDate">Дата начала, в любом часовом поясе</param>
|
||||
/// <param name="duration"></param>
|
||||
/// <param name="referenceDate"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <param name="allTemplates"></param>
|
||||
/// <param name="excludeWeekends"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<TemplateNextRunResultDto>> DistributeTemplatesAsync(DateOnly startDate, int duration, DateTimeOffset referenceDate, TimeSpan? offset, List<TemplateNextRunDto> allTemplates, bool excludeWeekends);
|
||||
|
||||
/// <summary>
|
||||
/// Метод проверяет, актуален ли текущий NextRun шаблона, если не актуален, возвращает актуальное значение
|
||||
/// При проверке загружаются шаблоны из БД! Если хотим рассчитывать nextRun для НЕСКОЛЬКИХ шаблонов, то следует рассчитать nextRun для одного шаблона, сохранить в БД, затем рассчитать для следующего
|
||||
/// </summary>
|
||||
/// <param name="startDate">Дата начала, в любом часовом поясе</param>
|
||||
/// <param name="duration"></param>
|
||||
/// <param name="referenceDate"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <param name="targetTemplate"></param>
|
||||
/// <param name="allTemplates"></param>
|
||||
/// <param name="excludeWeekends"></param>
|
||||
/// <param name="isNew"></param>
|
||||
/// <returns></returns>
|
||||
Task<TemplateNextRunResultDto> GetNextRunForTemplateAsync(DateOnly startDate, int duration, DateTimeOffset referenceDate, TimeSpan? offset, TemplateNextRunDto targetTemplate, List<TemplateNextRunDto> allTemplates, bool excludeWeekends, bool isNew);
|
||||
|
||||
/// <summary>
|
||||
/// Получить список выходных дней
|
||||
/// </summary>
|
||||
/// <param name="startDate"></param>
|
||||
/// <param name="endDate"></param>
|
||||
/// <returns></returns>
|
||||
Task<HashSet<DateOnly>> GetWeekendsAsync(DateOnly startDate, DateOnly endDate);
|
||||
|
||||
/// <summary>
|
||||
/// Получить список рабочих дней в диапазоне, исключая выходные и праздники.
|
||||
/// </summary>
|
||||
/// <param name="start"></param>
|
||||
/// <param name="end"></param>
|
||||
/// <param name="excludeWeekends">Исключить выходные и праздники</param>
|
||||
/// <returns></returns>
|
||||
Task<List<DateOnly>> GetWorkDaysAsync(DateOnly start, DateOnly end, bool excludeWeekends);
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,9 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
|
||||
public async Task<TemplateNextRunResultDto> GetValidNextRunForTemplateAsync(DateOnly startDate, int duration, DateTimeOffset referenceDate, TemplateNextRunDto targetTemplate, List<TemplateNextRunDto> allTemplates, bool excludeWeekends, bool isNew)
|
||||
{
|
||||
//if (offset.HasValue)
|
||||
// referenceDate = referenceDate.ToOffset(offset.Value);
|
||||
|
||||
//todo: allTemplates - пока не используется. Будет использоваться когда каждый раз будем строить план
|
||||
|
||||
// Пока работает так:
|
||||
@@ -104,6 +107,10 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
|
||||
public async Task<List<TemplateNextRunResultDto>> DistributeTemplatesAsync(DateOnly startDate, int duration, DateTimeOffset referenceDate, List<TemplateNextRunDto> allTemplates, bool excludeWeekends)
|
||||
{
|
||||
//if (offset.HasValue)
|
||||
//referenceDate = referenceDate.ToOffset(offset.Value);
|
||||
|
||||
|
||||
var _startDate = GetStartPeriodDate(startDate, referenceDate);
|
||||
var _endDate = GetEndPeriodDate(_startDate, duration);
|
||||
|
||||
@@ -417,7 +424,7 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
return CreateDateTimeOffset(minDay, referenceDate);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Получить план: дни, на которые должны быть назначены шаблоны
|
||||
@@ -492,7 +499,7 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
/// <returns></returns>
|
||||
private DateTimeOffset CreateDateTimeOffset(DateOnly date, DateTimeOffset refDate)
|
||||
{
|
||||
return new DateTimeOffset(date.Year, date.Month, date.Day, refDate.Hour, refDate.Minute, refDate.Second, TimeSpan.Zero);
|
||||
return new DateTimeOffset(date.Year, date.Month, date.Day, refDate.Hour, refDate.Minute, refDate.Second, refDate.Offset);
|
||||
}
|
||||
|
||||
|
||||
@@ -507,20 +514,61 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
// дата начала не может быть раньше referenceDate
|
||||
// и не может быть раньше чем сегодня
|
||||
|
||||
var dateStart = startDate;
|
||||
#region old
|
||||
//var dateStart = startDate;
|
||||
|
||||
// если dateStart < чем refDate, то берем refDate
|
||||
if (dateStart < DateOnly.FromDateTime(referenceDate.Date))
|
||||
dateStart = DateOnly.FromDateTime(referenceDate.Date);
|
||||
//// если 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);
|
||||
//// если dateStart < чем текущая дата, то берем текущую дату
|
||||
//if (dateStart < DateOnly.FromDateTime(DateTime.UtcNow.Date))
|
||||
// dateStart = DateOnly.FromDateTime(DateTime.UtcNow.Date);
|
||||
|
||||
// если текущее время > referenceDate, то прибавляем 1 день
|
||||
//// если текущее время > referenceDate, то прибавляем 1 день
|
||||
//var dateTimeStart = CreateDateTimeOffset(dateStart, referenceDate);
|
||||
//if (dateTimeStart < DateTimeOffset.UtcNow)
|
||||
// dateStart = dateStart.AddDays(1);
|
||||
|
||||
//return dateStart;
|
||||
#endregion
|
||||
|
||||
// 1. Конвертируем startDate (UTC) в целевой часовой пояс
|
||||
var startDateUtc = new DateTimeOffset(
|
||||
startDate.Year, startDate.Month, startDate.Day,
|
||||
0, 0, 0,
|
||||
TimeSpan.Zero);
|
||||
|
||||
var startDateInTargetTz = startDateUtc.ToOffset(referenceDate.Offset);
|
||||
var dateStart = DateOnly.FromDateTime(startDateInTargetTz.DateTime);
|
||||
|
||||
logger.LogDebug("startDate UTC ({startDate} 00:00 UTC) конвертирована в целевой часовой пояс {offset}: {dateStart}", startDate, referenceDate.Offset, dateStart);
|
||||
|
||||
// 2. Дата начала не может быть раньше даты из referenceDate (в его часовом поясе)
|
||||
var refDateInTargetTz = DateOnly.FromDateTime(referenceDate.Date);
|
||||
if (dateStart < refDateInTargetTz)
|
||||
{
|
||||
logger.LogDebug("Дата начала {dateStart} раньше даты из referenceDate {refDate}. Используем дату из referenceDate.", dateStart, refDateInTargetTz);
|
||||
dateStart = refDateInTargetTz;
|
||||
}
|
||||
|
||||
// 3. Дата начала не может быть раньше "сегодня" в целевом часовом поясе
|
||||
var todayInTargetTz = DateOnly.FromDateTime(DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset).Date);
|
||||
if (dateStart < todayInTargetTz)
|
||||
{
|
||||
logger.LogDebug("Дата начала {dateStart} раньше 'сегодня' в целевом поясе {today}. Используем 'сегодня'.", dateStart, todayInTargetTz);
|
||||
dateStart = todayInTargetTz;
|
||||
}
|
||||
|
||||
// 4. Если момент времени "начало дня в целевом поясе" уже прошёл — добавляем 1 день
|
||||
var dateTimeStart = CreateDateTimeOffset(dateStart, referenceDate);
|
||||
if (dateTimeStart < DateTimeOffset.UtcNow)
|
||||
{
|
||||
logger.LogDebug("Момент времени {dateTimeStart} уже прошёл (сейчас {now}). Добавляем 1 день.", dateTimeStart, DateTimeOffset.UtcNow);
|
||||
dateStart = dateStart.AddDays(1);
|
||||
}
|
||||
|
||||
logger.LogDebug("Итоговая дата начала распределения: {dateStart} (в часовом поясе {offset})", dateStart, referenceDate.Offset);
|
||||
|
||||
return dateStart;
|
||||
}
|
||||
@@ -565,41 +613,44 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
/// <param name="startDate"></param>
|
||||
/// <param name="endDate"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<HashSet<DateOnly>> GetWeekendsV2Async(DateOnly startDate, DateOnly endDate, DateTimeOffset referenceDate)
|
||||
public async Task<HashSet<DateOnly>> GetWeekendsV2Async(DateOnly startDate, DateOnly endDate, DateTimeOffset referenceDate, TimeSpan? offset = null)
|
||||
{
|
||||
if (offset.HasValue)
|
||||
referenceDate = referenceDate.ToOffset(offset.Value);
|
||||
|
||||
// проверяем это текущие или прошлые сутки относительно refDate, если надо, корректируем dateStart, dateEnd
|
||||
bool isPreviousDays = IsPreviousDays(referenceDate);
|
||||
//bool isPreviousDays = IsPreviousDays(referenceDate);
|
||||
|
||||
if (isPreviousDays)
|
||||
{
|
||||
// Это предыдущие сутки, корректируем
|
||||
startDate = startDate.AddDays(-1);
|
||||
// так как мы потом будем сдвигать выходные дни влево, то надо взять на один день дальше, чтоб знать какой он
|
||||
// вроде бы это не костыль
|
||||
endDate = endDate.AddDays(1);
|
||||
//endDate = endDate.AddDays(-1);
|
||||
//if (isPreviousDays)
|
||||
//{
|
||||
// // Это предыдущие сутки, корректируем
|
||||
// startDate = startDate.AddDays(-1);
|
||||
// // так как мы потом будем сдвигать выходные дни влево, то надо взять на один день дальше, чтоб знать какой он
|
||||
// // вроде бы это не костыль
|
||||
// endDate = endDate.AddDays(1);
|
||||
// //endDate = endDate.AddDays(-1);
|
||||
|
||||
logger.LogDebug("Это предыдущие сутки относительно referenceDate: {referenceDate}, корректируем даты для получения выходных дней, новые значения dateStart: {dateStart}, dateEnd: {dateEnd}", referenceDate, startDate, endDate);
|
||||
}
|
||||
// logger.LogDebug("Это предыдущие сутки относительно referenceDate: {referenceDate}, корректируем даты для получения выходных дней, новые значения dateStart: {dateStart}, dateEnd: {dateEnd}", referenceDate, startDate, endDate);
|
||||
//}
|
||||
|
||||
var weekends = await weekendDayService.GetWeekends(startDate, endDate).ToListAsync();
|
||||
|
||||
var weekendsResult = new List<DateOnly>();
|
||||
|
||||
if (isPreviousDays)
|
||||
{
|
||||
// так как должны быть предыдущие дни, отнимаем 1 день
|
||||
foreach (var weekend in weekends)
|
||||
{
|
||||
var previous = weekend.AddDays(-1);
|
||||
logger.LogDebug("Выходной день, -1, так как день должен быть предыдущий, старое значение {weekend}, новое {previous}", weekend, previous);
|
||||
weekendsResult.Add(previous);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
weekendsResult = weekends;
|
||||
}
|
||||
//if (isPreviousDays)
|
||||
//{
|
||||
// // так как должны быть предыдущие дни, отнимаем 1 день
|
||||
// foreach (var weekend in weekends)
|
||||
// {
|
||||
// var previous = weekend.AddDays(-1);
|
||||
// logger.LogDebug("Выходной день, -1, так как день должен быть предыдущий, старое значение {weekend}, новое {previous}", weekend, previous);
|
||||
// weekendsResult.Add(previous);
|
||||
// }
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// weekendsResult = weekends;
|
||||
//}
|
||||
|
||||
logger.LogDebug("Найдено не рабочих дней: {Count}", weekendsResult.Count);
|
||||
foreach (var weekend in weekendsResult)
|
||||
@@ -644,19 +695,22 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
//}
|
||||
|
||||
|
||||
public async Task<List<DateOnly>> GetWorkDaysV2Async(DateOnly start, DateOnly end, bool excludeWeekends, DateTimeOffset referenceDate)
|
||||
public async Task<List<DateOnly>> GetWorkDaysV2Async(DateOnly start, DateOnly end, bool excludeWeekends, DateTimeOffset referenceDate, TimeSpan? offset = null)
|
||||
{
|
||||
if (offset.HasValue)
|
||||
referenceDate = referenceDate.ToOffset(offset.Value);
|
||||
|
||||
logger.LogDebug("Формирование списка рабочих дней, start: {start}, end: {end}, excludeWeekends: {excludeWeekends}, referenceDate: {referenceDate}", start, end, excludeWeekends, referenceDate);
|
||||
|
||||
// проверяем это текущие или прошлые сутки относительно refDate, если надо, корректируем dateStart, dateEnd
|
||||
if (IsPreviousDays(referenceDate))
|
||||
{
|
||||
// Это предыдущие сутки, корректируем
|
||||
start = start.AddDays(-1);
|
||||
end = end.AddDays(-1);
|
||||
//// проверяем это текущие или прошлые сутки относительно refDate, если надо, корректируем dateStart, dateEnd
|
||||
//if (IsPreviousDays(referenceDate))
|
||||
//{
|
||||
// // Это предыдущие сутки, корректируем
|
||||
// start = start.AddDays(-1);
|
||||
// end = end.AddDays(-1);
|
||||
|
||||
logger.LogDebug("Это предыдущие сутки относительно referenceDate: {referenceDate}, корректируем даты, новые значения dateStart: {dateStart}, dateEnd: {dateEnd}", referenceDate, start, end);
|
||||
}
|
||||
// logger.LogDebug("Это предыдущие сутки относительно referenceDate: {referenceDate}, корректируем даты, новые значения dateStart: {dateStart}, dateEnd: {dateEnd}", referenceDate, start, end);
|
||||
//}
|
||||
|
||||
// Получаем список выходных/праздничных дней
|
||||
var weekends = new HashSet<DateOnly>();
|
||||
@@ -683,33 +737,45 @@ namespace PARR.DAL.NextRunServices.Subservices
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Это предыдущие сутки? Относительно referenceDate и МСК времени
|
||||
/// </summary>
|
||||
/// <param name="referenceDate"></param>
|
||||
/// <returns></returns>
|
||||
private bool IsPreviousDays(DateTimeOffset referenceDate)
|
||||
|
||||
|
||||
public Task<HashSet<DateOnly>> GetWeekendsV2Async(DateOnly startDate, DateOnly endDate, DateTimeOffset referenceDate)
|
||||
{
|
||||
// смещение часов относительно UTC - MSK
|
||||
int offsetMskHour = 3;
|
||||
|
||||
// если время больше 21 часа, то это текущие сутки, если меньше, то прошлые
|
||||
int maxHour = 24 - offsetMskHour;
|
||||
|
||||
if (referenceDate.Hour >= maxHour && referenceDate.Hour <= 23)
|
||||
{
|
||||
// это прошлые сутки
|
||||
logger.LogDebug("ReferenceDate {refDate} - прошлые стуки", referenceDate);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// это текущие сутки
|
||||
logger.LogDebug("ReferenceDate {refDate} - текущие стуки", referenceDate);
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<List<DateOnly>> GetWorkDaysV2Async(DateOnly start, DateOnly end, bool excludeWeekends, DateTimeOffset referenceDate)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Это предыдущие сутки? Относительно referenceDate и МСК времени
|
||||
///// </summary>
|
||||
///// <param name="referenceDate"></param>
|
||||
///// <returns></returns>
|
||||
//private bool IsPreviousDays(DateTimeOffset referenceDate)
|
||||
//{
|
||||
// // смещение часов относительно UTC - MSK
|
||||
// int offsetMskHour = 3;
|
||||
|
||||
// // если время больше 21 часа, то это текущие сутки, если меньше, то прошлые
|
||||
// int maxHour = 24 - offsetMskHour;
|
||||
|
||||
// if (referenceDate.Hour >= maxHour && referenceDate.Hour <= 23)
|
||||
// {
|
||||
// // это прошлые сутки
|
||||
// logger.LogDebug("ReferenceDate {refDate} - прошлые стуки", referenceDate);
|
||||
// return true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // это текущие сутки
|
||||
// logger.LogDebug("ReferenceDate {refDate} - текущие стуки", referenceDate);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
1259
PARR.DAL/NextRunServices/Subservices/TemplateDistributorV2.cs
Normal file
1259
PARR.DAL/NextRunServices/Subservices/TemplateDistributorV2.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PARR.DAL.Cache.Services.Base;
|
||||
using PARR.DAL.Configurations.DbSettings;
|
||||
using PARR.DAL.Context;
|
||||
@@ -141,6 +142,8 @@ namespace PARR.DAL
|
||||
services.AddTransient<IEsppScheduleTransformService, EsppScheduleTransformService>();
|
||||
services.AddTransient<INextRunService, NextRunService>();
|
||||
services.AddTransient<ITemplateDistributor, TemplateDistributor>();
|
||||
services.AddTransient<ITemplateDistributorV2, TemplateDistributorV2>();
|
||||
services.AddTransient<INextRunServiceV2, NextRunServiceV2>();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -189,8 +192,9 @@ namespace PARR.DAL
|
||||
using var scope = provider.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<DataContext>();
|
||||
var settingsFromDb = scope.ServiceProvider.GetRequiredService<SettingsFromDb>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<ScheduleResponseAreaTimeOffsetService>>();
|
||||
|
||||
return new ScheduleResponseAreaTimeOffsetService(dbContext, settingsFromDb);
|
||||
return new ScheduleResponseAreaTimeOffsetService(dbContext, settingsFromDb, logger);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PARR.DAL.Context;
|
||||
using PARR.DAL.Contracts;
|
||||
using PARR.DAL.Models.Schedule;
|
||||
@@ -17,8 +18,9 @@ namespace PARR.DAL.Services.Implementations.Schedule
|
||||
/// Настройки, если не нашли в offsetList
|
||||
/// </summary>
|
||||
private readonly ScheduleResponseAreaTimeOffset defaultOffset;
|
||||
private readonly ILogger<ScheduleResponseAreaTimeOffsetService> logger;
|
||||
|
||||
public ScheduleResponseAreaTimeOffsetService(DataContext dataContext, SettingsFromDb settingsFromDb)
|
||||
public ScheduleResponseAreaTimeOffsetService(DataContext dataContext, SettingsFromDb settingsFromDb, ILogger<ScheduleResponseAreaTimeOffsetService> logger)
|
||||
{
|
||||
offsetList = dataContext.ScheduleResponseAreaTimeOffsets
|
||||
.AsNoTracking()
|
||||
@@ -36,6 +38,7 @@ namespace PARR.DAL.Services.Implementations.Schedule
|
||||
EsppValue = "",
|
||||
UtcTimeOffset = new TimeSpan(3, 0, 0)
|
||||
};
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +48,20 @@ namespace PARR.DAL.Services.Implementations.Schedule
|
||||
|
||||
public ScheduleResponseAreaTimeOffset GetByResponseAreaOrDefault(string responseArea)
|
||||
{
|
||||
return offsetList.TryGetValue(responseArea, out var value) ? value : defaultOffset;
|
||||
offsetList.TryGetValue(responseArea, out var value);
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
logger.LogDebug("Получил часовой пояс по ЗО '{responseArea}', esppValue: {EsppValue}, utcTimeOffset: {utcTimeOffset}", responseArea, value.EsppValue, value.UtcTimeOffset);
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning("Не смог получить часовой пояс по ЗО '{responseArea}', вернул часово пояс по умолчанию: name: {name}, esppValue: {EsppValue}, utcTimeOffset: {utcTimeOffset}",
|
||||
responseArea, defaultOffset.ResponseArea, defaultOffset.EsppValue, defaultOffset.UtcTimeOffset);
|
||||
return defaultOffset;
|
||||
}
|
||||
//return offsetList.TryGetValue(responseArea, out var value) ? value : defaultOffset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user