Files
parr_api/PARR.DAL/TransformServices/EsppScheduleTransformService.cs

492 lines
21 KiB
C#
Raw Normal View History

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Common;
using PARR.Constants;
using PARR.DAL.Contracts;
using PARR.DAL.DomainModels;
using PARR.DAL.Extensions;
using PARR.DAL.Services.Interfaces;
using PARR.DAL.Services.Interfaces.Job;
namespace PARR.DAL.TransformServices
{
internal class EsppScheduleTransformService : IEsppScheduleTransformService
{
private readonly IEsppSchTypeConfigService esppSchTypeConfigService;
private readonly INextRunModifierService nextRunModifierService;
private readonly IJobGroupService jobGroupService;
private readonly ILogger<EsppScheduleTransformService> logger;
private readonly Dictionary<string, int> monthDict = new Dictionary<string, int>()
{
{ "Январь", 1 },
{ "Февраль", 2 },
{ "Март", 3 },
{ "Апрель", 4 },
{ "Май", 5 },
{ "Июнь", 6 },
{ "Июль", 7 },
{ "Август", 8 },
{ "Сентябрь", 9 },
{ "Октябрь", 10 },
{ "Ноябрь", 11 },
{ "Декабрь", 12 }
};
private readonly Dictionary<string, int> orderDict = new Dictionary<string, int>()
{
{ "Первый", 1 },
{ "Второй", 2 },
{ "Третий", 3 },
{ "Четвертый", 4 },
{ "Последний", 5 }
};
private readonly Dictionary<string, int> dwDict = new Dictionary<string, int>()
{
{"Понедельник", 1 },
{"Вторник", 2 },
{"Среда", 3 },
{"Четверг", 4 },
{"Пятница", 5 },
{"Суббота", 6 },
{"Воскресенье", 0 }
};
public EsppScheduleTransformService(
ILogger<EsppScheduleTransformService> logger,
IEsppSchTypeConfigService esppSchTypeConfigService,
IWeekendDayService weekendDayService,
INextRunModifierService nextRunModifierService,
IJobGroupService jobGroupService
)
{
this.esppSchTypeConfigService = esppSchTypeConfigService;
this.nextRunModifierService = nextRunModifierService;
this.jobGroupService = jobGroupService;
this.logger = logger;
}
public DateTimeOffset GetNextDate(EsppScheduleDto esppSchedule, DateTimeOffset lastRun)
{
var nextRun = lastRun;
//если больше текущего вернуть lastRun. Это и есть следующая дата
if (lastRun > DateTimeOffset.UtcNow)
nextRun = lastRun;
//return lastRun;
switch (esppSchedule.TypeSchedule.Id)
{
case (int)EsppSchTypeScheduleEnum.Regularly:
//return GetNextDateRegularly(esppSchedule.Values, lastRun);
nextRun = GetNextDateRegularly(esppSchedule.Values, lastRun);
break;
case (int)EsppSchTypeScheduleEnum.Weekly:
//return GetNextDateWeekly(esppSchedule.Values, lastRun);
nextRun = GetNextDateWeekly(esppSchedule.Values, lastRun);
break;
case (int)EsppSchTypeScheduleEnum.Monthly:
//return GetNextDateMonthly(esppSchedule.Values, lastRun);
nextRun = GetNextDateMonthly(esppSchedule.Values, lastRun);
break;
case (int)EsppSchTypeScheduleEnum.Monthly2:
//return GetNextDateMonthly2(esppSchedule.Values, lastRun);
nextRun = GetNextDateMonthly2(esppSchedule.Values, lastRun);
break;
case (int)EsppSchTypeScheduleEnum.Annually:
//return GetNextDateAnnually(esppSchedule.Values, lastRun);
nextRun = GetNextDateAnnually(esppSchedule.Values, lastRun);
break;
case (int)EsppSchTypeScheduleEnum.Annually2:
//return GetNextDateAnnually2(esppSchedule.Values, lastRun);
nextRun = GetNextDateAnnually2(esppSchedule.Values, lastRun);
break;
}
nextRun = nextRunModifierService.GetWorkDayAsync(nextRun).GetAwaiter().GetResult();
// тут может быть 21 час по МСК, то это предыдущие сутки, нужно отнять 1 день
if (!DateResolver.IsCurrentDayRelativeMskTime(nextRun.Hour))
nextRun = nextRun.AddDays(-1);
return nextRun;
}
private List<DateTimeOffset> GetNextSchedule(EsppScheduleDto esppSchedule, DateTimeOffset lastRun)
{
// расписание на оставшееся на сегодня время. его так мало...
lastRun = lastRun.UtcDateTime;
var schedules = new List<DateTimeOffset>();
var curTime = lastRun;
//Если ещё не произошло то добавляем расписание, возможно это новое AiW и он ещё ниразу не запускался.
//lastRun это будущее
if (lastRun >= DateTimeOffset.UtcNow && lastRun <= DateTimeOffset.UtcNow.EndOfDay())
schedules.Add(lastRun);
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<DateTimeOffset> GetNextDateAsync(Guid jobGroupId, DateTimeOffset lastRun)
{
//если больше текущего вернуть lastRun. Это и есть следующая дата
if (lastRun > DateTimeOffset.UtcNow)
return await nextRunModifierService.GetWorkDayAsync(lastRun);
var jobGroup = await jobGroupService.Get().AsTracking()
.Include(t => t.EsppSchValues)
.ThenInclude(esv => esv.EsppSchTypeValue)
.ThenInclude(etv => etv!.DistributionPeriod)
.FirstOrDefaultAsync(t => t.Id == jobGroupId);
var esppSchedule = await GetEsppScheduleAsync(jobGroupId);
//if (jobGroup!.IsAutoDistributionEnabled)
//{
// var period = jobGroup.EsppSchValues.FirstOrDefault()?.EsppSchTypeValue?.DistributionPeriod;
// var periodType = ParseDistributionPeriodType(period!.Type);
// return GetNextDateForDistributionRun(lastRun, periodType, period.Duration);
//}
return GetNextDate(esppSchedule, lastRun);
}
public async Task<List<DateTimeOffset>> GetNextScheduleAsync(Guid jobGroupId, DateTimeOffset lastRun)
{
//TODO: тут не проверен переход через выходные дни!!! Переход не используется, так как тут не рассчитываем NextRun.
//В общем проверить, когда будем тестировать агента, что с датами все ок
var esppSchedule = await GetEsppScheduleAsync(jobGroupId);
var nextSchedule = GetNextSchedule(esppSchedule, lastRun);
return nextSchedule;
}
private async Task<EsppScheduleDto> GetEsppScheduleAsync(Guid jobGroupId)
{
var esppSchedule = await esppSchTypeConfigService.GetEsppScheduleDtoAsync(jobGroupId);
if (esppSchedule == null)
{
logger.LogError($"Не найдено расписание в БД, jobId: {jobGroupId}");
throw new Exception($"Не найдено расписание в БД, jobId: {jobGroupId}");
}
return esppSchedule;
}
private DateTimeOffset GetNextDateRegularly(List<EsppScheduleValDto> values, DateTimeOffset lastRun)
{
var regDict = new Dictionary<string, int>
{
{"Каждый час",1},
{"Каждые 2 часа",2},
{"Каждые 3 часа", 3},
{"Каждые 4 часа", 4},
{"Каждые 6 часов", 6},
{"Каждые 12 часов", 12},
{"Ежедневно", 24},
{"Каждые 72 часа", 72},
{"Каждые 96 часов", 96},
{"Каждые 2 недели", 336},
{"Каждые 60 дней", 1440},
{"Каждые 80 дней", 1920},
{"Каждые 90 дней", 2160},
{"Каждые полгода", 4380},
{"Каждые 1,5 года", 13140},
{"Каждые 3 года", 26280},
};
var regNum = regDict[values[0].Value.Value];
var calcDay = lastRun.AddHours(regNum);
//TODO: вот это повторяется от метода к методу
//Если итоговая дата указывает на прошлое, то повторяем расчёт и уходим в рекурсию
if (calcDay < DateTimeOffset.UtcNow)
calcDay = GetNextDateRegularly(values, calcDay);
return calcDay;
}
private DateTimeOffset GetNextDateWeekly(List<EsppScheduleValDto> values, DateTimeOffset lastRun)
{
var dwNum = dwDict[values[0].Value.Value];
var calcDay = new DateTimeOffset(lastRun.Year, lastRun.Month, lastRun.Day, lastRun.Hour, lastRun.Minute, lastRun.Second, lastRun.Offset);
if (lastRun < DateTimeOffset.UtcNow)
calcDay = new DateTimeOffset(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month, DateTimeOffset.UtcNow.Day, lastRun.Hour, lastRun.Minute, lastRun.Second, lastRun.Offset);
if (((int)calcDay.DayOfWeek) != dwNum)
OffsetToDayOfWeek(dwNum, ref calcDay);
//TODO: вот это повторяется от метода к методу
//Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
if (calcDay < DateTimeOffset.UtcNow)
calcDay = GetNextDateWeekly(values, calcDay.AddDays(7));//а вот тут что-то новенькое
return calcDay;
}
private DateTimeOffset GetNextDateMonthly(List<EsppScheduleValDto> values, DateTimeOffset lastRun)
{
int.TryParse(values[0].Value.Value, out int dayNum);
var calcDay = new DateTimeOffset(lastRun.Year, lastRun.Month, dayNum, lastRun.Hour, lastRun.Minute, lastRun.Second, lastRun.Offset);
if (lastRun < DateTimeOffset.UtcNow)
calcDay = new DateTimeOffset(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month, dayNum, lastRun.Hour, lastRun.Minute, lastRun.Second, lastRun.Offset);
//TODO: вот это повторяется от метода к методу
//Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
if (calcDay < DateTimeOffset.UtcNow)
//todo: fixme
calcDay = GetNextDateMonthly(values, calcDay.AddMonths(1));//и вот тут что-то новенькое
return calcDay;
}
private DateTimeOffset GetNextDateMonthly2(List<EsppScheduleValDto> values, DateTimeOffset lastRun)
{
var orderNum = orderDict[values[0].Value.Value];
var dwNum = dwDict[values[1].Value.Value];
var calcDay = new DateTimeOffset(lastRun.Year, lastRun.Month, 1, lastRun.Hour, lastRun.Minute, lastRun.Second, lastRun.Offset);
if (lastRun < DateTimeOffset.UtcNow)
calcDay = new DateTimeOffset(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month, 1, lastRun.Hour, lastRun.Minute, lastRun.Second, lastRun.Offset);
List<DateTimeOffset> daysList = FindAllDaysInMonth(dwNum, ref calcDay);
if (orderNum == 5)//5-последний
calcDay = daysList.Last();
else
calcDay = daysList[orderNum - 1];
//TODO: вот это повторяется от метода к методу
//Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
if (calcDay < DateTimeOffset.UtcNow)
calcDay = GetNextDateMonthly2(values, calcDay.AddMonths(1));
return calcDay;
}
private DateTimeOffset GetNextDateAnnually(List<EsppScheduleValDto> values, DateTimeOffset lastRun)
{
var monthNum = monthDict[values[0].Value.Value];
int.TryParse(values[1].Value.Value, out int dayNum);
var calcDay = new DateTimeOffset(lastRun.Year + 1, monthNum, dayNum, lastRun.Hour, lastRun.Minute, lastRun.Second, lastRun.Offset);
//TODO: вот это повторяется от метода к методу
//Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
if (calcDay < DateTimeOffset.UtcNow)
calcDay = GetNextDateAnnually(values, calcDay.AddYears(1));
return calcDay;
}
private DateTimeOffset GetNextDateAnnually2(List<EsppScheduleValDto> values, DateTimeOffset lastRun)
{
var monthADict = new Dictionary<string, int>
{
{ "Января", 1 },
{ "Февраля", 2 },
{ "Марта", 3 },
{ "Апреля", 4 },
{ "Мая", 5 },
{ "Июня", 6 },
{ "Июля", 7 },
{ "Августа", 8 },
{ "Сентября", 9 },
{ "Октября", 10 },
{ "Ноября", 11 },
{ "Декабря", 12 }
};
var monthNum = monthADict[values[2].Value.Value];
var orderNum = orderDict[values[0].Value.Value];
var dwNum = dwDict[values[1].Value.Value];
var calcDay = new DateTimeOffset(lastRun.Year + 1, monthNum, 1, lastRun.Hour, lastRun.Minute, lastRun.Second, lastRun.Offset);
List<DateTimeOffset> daysList = FindAllDaysInMonth(dwNum, ref calcDay);
if (orderNum == 5)//5-последний
calcDay = daysList.Last();
else
calcDay = daysList[orderNum];
//TODO: вот это повторяется от метода к методу
//Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
if (calcDay < DateTimeOffset.UtcNow)
//calcDay = GetNextDateAnnually2(values, calcDay);
calcDay = GetNextDateAnnually2(values, calcDay.AddYears(1));
return calcDay;
}
private List<DateTimeOffset> FindAllDaysInMonth(int dwNum, ref DateTimeOffset calcDay)
{
if (((int)calcDay.DayOfWeek) != dwNum)
OffsetToDayOfWeek(dwNum, ref calcDay);
var daysList = new List<DateTimeOffset>();
var monthNum = calcDay.Month;
while (calcDay.Month == monthNum)
{
daysList.Add(calcDay);
calcDay = calcDay.AddDays(7);
}
return daysList;
}
private void OffsetToDayOfWeek(int dwNum, ref DateTimeOffset calcDay)
{
var daysOffset = dwNum - ((int)calcDay.DayOfWeek);
if (daysOffset < 0)
daysOffset = 7 + daysOffset;
calcDay = calcDay.AddDays(daysOffset);
}
public DateTimeOffset GetNextDateForDistributionRun(DateTimeOffset lastRun, DistributionPeriodTypeEnum periodType, string distributionPeriod)
{
var nextRun = lastRun;
switch (periodType)
{
case (DistributionPeriodTypeEnum.Day):
//return lastRun.AddDays(ParseInt(distributionPeriod));
nextRun = lastRun.AddDays(ParseInt(distributionPeriod));
break;
case (DistributionPeriodTypeEnum.Month):
//return lastRun.AddMonths(ParseInt(distributionPeriod));
nextRun = lastRun.AddMonths(ParseInt(distributionPeriod));
break;
case (DistributionPeriodTypeEnum.Year):
//return lastRun.AddYears(ParseInt(distributionPeriod));
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;
}
}
}