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

386 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

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

using Microsoft.Extensions.Logging;
using PARR.Constants;
using PARR.DAL.Contracts;
using PARR.DAL.DomainModels;
using PARR.DAL.Extensions;
using PARR.DAL.Models;
using PARR.DAL.Services.Interfaces;
namespace PARR.DAL.TransformServices
{
internal class EsppScheduleTransformService : IEsppScheduleTransformService
{
private readonly IEsppSchTypeConfigService esppSchTypeConfigService;
private readonly IApplicationsInWorkService applicationsInWorkService;
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,
IApplicationsInWorkService applicationsInWorkService
)
{
this.esppSchTypeConfigService = esppSchTypeConfigService;
this.applicationsInWorkService = applicationsInWorkService;
this.logger = logger;
}
public DateTimeOffset GetNextDate(EsppScheduleDto esppSchedule, DateTimeOffset lastRun)
{
//todo:
//если больше текущего вернуть lastRun. Это и есть следующая дата
if (lastRun > DateTimeOffset.UtcNow)
return lastRun;
switch (esppSchedule.TypeSchedule.Id)
{
case (int)EsppSchTypeScheduleEnum.Regularly:
return GetNextDateRegularly(esppSchedule.Values, lastRun);
case (int)EsppSchTypeScheduleEnum.Weekly:
return GetNextDateWeekly(esppSchedule.Values, lastRun);
case (int)EsppSchTypeScheduleEnum.Monthly:
return GetNextDateMonthly(esppSchedule.Values, lastRun);
case (int)EsppSchTypeScheduleEnum.Monthly2:
return GetNextDateMonthly2(esppSchedule.Values, lastRun);
case (int)EsppSchTypeScheduleEnum.Annually:
return GetNextDateAnnually(esppSchedule.Values, lastRun);
case (int)EsppSchTypeScheduleEnum.Annually2:
return GetNextDateAnnually2(esppSchedule.Values, lastRun);
}
return lastRun;//todo убрать lastrun должен возвращать реальный результат
}
public 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 applicationInWorkId, DateTimeOffset lastRun)
{
var esppSchedule = await GetEsppScheduleAsync(applicationInWorkId);
return GetNextDate(esppSchedule, lastRun);
}
public async Task<List<DateTimeOffset>> GetNextScheduleAsync(Guid applicationInWorkId, DateTimeOffset lastRun)
{
var esppSchedule = await GetEsppScheduleAsync(applicationInWorkId);
var nextSchedule = GetNextSchedule(esppSchedule, lastRun);
return nextSchedule;
}
private async Task<EsppScheduleDto> GetEsppScheduleAsync(Guid applicationInWorkId)
{
var esppSchedule = await esppSchTypeConfigService.GetEsppScheduleDtoAsync(applicationInWorkId);
if (esppSchedule == null)
{
logger.LogError($"Не найдено расписание в БД, applicationInWorkId: {applicationInWorkId}");
throw new Exception($"Не найдено расписание в БД, applicationInWorkId: {applicationInWorkId}");
}
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(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 = 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(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month, dayNum, lastRun.Hour, lastRun.Minute, lastRun.Second, lastRun.Offset);
//TODO: вот это повторяется от метода к методу
//Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
if (calcDay < DateTimeOffset.UtcNow)
calcDay = 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(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 = GetNextDateAnnually2(values, calcDay);
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);
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);
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)
{
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 startPeriod.AddDays(duration.Days).AddDays(-1);
return lastRun;
}
public async Task<DateOnly> GetStartPeriodForDateAsync(Guid applicationInWorkId, DateTimeOffset date, DateTimeOffset refrenceDate, DistributionPeriodTypeEnum periodType, string distributionPeriod)
{
var esppSchedule = await GetEsppScheduleAsync(applicationInWorkId);
var currentStartPeriod = refrenceDate;
var currentEndPeriod = GetNextDateForDistributionRun(currentStartPeriod,periodType,distributionPeriod);
while (!(date>=currentStartPeriod && date< currentEndPeriod))
{
currentStartPeriod = currentEndPeriod;
currentEndPeriod = GetNextDateForDistributionRun(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;
}
}
}
}