2024-07-02 11:43:59 +10:00
using Microsoft.EntityFrameworkCore ;
2024-07-05 15:17:48 +10:00
using Microsoft.Extensions.Logging ;
using PARR.BLL.Services.Interfaces ;
using PARR.Constants ;
2024-07-02 11:43:59 +10:00
using PARR.DAL.Models ;
using PARR.DAL.Services.Interfaces ;
2024-07-05 15:17:48 +10:00
using PARR.DAL.TransformServices ;
2024-07-02 11:43:59 +10:00
namespace PARR.TemplateDistributor
{
internal class TemplateDistributor : ITemplateDistributor
{
2024-07-05 15:17:48 +10:00
private readonly ILogger < TemplateDistributor > logger ;
private readonly ITemplateService templateService ;
2024-07-02 11:43:59 +10:00
private readonly IApplicationsInWorkService applicationsInWorkService ;
2024-07-05 15:17:48 +10:00
private readonly IEsppSchTypeScheduleService esppSchTypeScheduleService ;
private readonly ICalendarService calendarService ;
private readonly IEsppScheduleTransformService esppScheduleTransformService ;
private readonly IWeekendDayService weekendDayService ;
2024-07-02 11:43:59 +10:00
2024-07-05 15:17:48 +10:00
public TemplateDistributor (
ILogger < TemplateDistributor > logger ,
ITemplateService templateService ,
IApplicationsInWorkService applicationsInWorkService ,
IEsppSchTypeScheduleService esppSchTypeScheduleService ,
ICalendarService calendarService ,
IEsppScheduleTransformService esppScheduleTransformService ,
IWeekendDayService weekendDayService
)
2024-07-02 11:43:59 +10:00
{
2024-07-05 15:17:48 +10:00
this . logger = logger ;
this . templateService = templateService ;
2024-07-02 11:43:59 +10:00
this . applicationsInWorkService = applicationsInWorkService ;
2024-07-05 15:17:48 +10:00
this . esppSchTypeScheduleService = esppSchTypeScheduleService ;
this . calendarService = calendarService ;
this . esppScheduleTransformService = esppScheduleTransformService ;
this . weekendDayService = weekendDayService ;
2024-07-02 11:43:59 +10:00
}
2024-07-02 15:04:23 +10:00
public async Task UpdateScheduleAsync ( Guid applicationInWorkId )
{
// все шаблоны по applicationInWorkId
// группирует по Р Р
// -> DistributeTemplateForPeriodAsync
// сохранить в БД
2024-07-05 15:17:48 +10:00
//applicationInWorkId = Guid.Parse("bdfefd77-bce3-4f62-a484-5042c06f4467");
2024-07-11 17:23:52 +10:00
var appInWork = await applicationsInWorkService . GetAsync ( applicationInWorkId ) ;
2024-07-11 11:32:14 +10:00
// .Include(aiw => aiw.EsppSchValues)
// .ThenInclude(esv => esv.EsppSchTypeValue)
// .ThenInclude(etv => etv!.DistributionPeriod)
// .FirstOrDefaultAsync(t => t.Id == applicationInWorkId);
2024-07-05 15:17:48 +10:00
var templates = await templateService . Get ( )
. Include ( t = > t . Host )
. ThenInclude ( h = > h . WorkGroup )
. Where ( t = > t . ApplicationInWorkId = = applicationInWorkId )
. ToListAsync ( ) ;
var workGroupsWithTemplates = templates . GroupBy ( t = > t . Host ! . WorkGroupId ) ;
2024-07-11 11:32:14 +10:00
foreach ( var workGroupWithTemplates in workGroupsWithTemplates )
2024-07-05 15:17:48 +10:00
{
2024-07-11 11:32:14 +10:00
var wgId = workGroupWithTemplates . Key ? ? Guid . NewGuid ( ) ; //TODO сделать правильно
var values = workGroupWithTemplates . ToList ( ) ;
if ( ! values . Any ( ) )
continue ;
2024-07-05 15:17:48 +10:00
2024-07-11 14:42:55 +10:00
//Обновляем сразу время при необходимости
2024-07-11 17:23:52 +10:00
values . ForEach ( t = >
{
2024-07-11 14:42:55 +10:00
if ( t . NextRun . TimeOfDay ! = appInWork ! . ReferenceDate . TimeOfDay )
2024-07-11 17:23:52 +10:00
//t.NextRun = new DateTimeOffset(t.NextRun.DateTime, new TimeSpan(//(appInWork!.ReferenceDate.Hour, appInWork!.ReferenceDate.Minute, appInWork!.ReferenceDate.Second));
t . NextRun = new DateTimeOffset ( t . NextRun . Year , t . NextRun . Month , t . NextRun . Day ,
appInWork ! . ReferenceDate . Hour , appInWork ! . ReferenceDate . Minute , appInWork ! . ReferenceDate . Second ,
new TimeSpan ( 0 , 0 , 0 ) ) ;
2024-07-11 14:42:55 +10:00
} ) ;
2024-07-11 17:23:52 +10:00
if ( ! await applicationsInWorkService . CommitAsync ( ) )
{
logger . LogError ( $"Ошибка записи изменений в БД при актуализации времени NextRun шаблонов" ) ;
return ;
}
2024-07-11 14:42:55 +10:00
2024-07-05 15:17:48 +10:00
var distrTemplates = await DistributeTemplateAsync ( values , applicationInWorkId , wgId ) ;
2024-07-11 17:23:52 +10:00
if ( distrTemplates . Any ( ) )
if ( ! await applicationsInWorkService . CommitAsync ( ) )
logger . LogError ( $"Ошибка записи изменений в БД при перераспределении NextRun шаблонов" ) ;
}
2024-07-02 15:04:23 +10:00
}
2024-07-02 11:43:59 +10:00
2024-07-11 11:32:14 +10:00
public async Task < List < Template > > DistributeTemplateAsync ( List < Template > templates , Guid applicationInWorkId , Guid workGroupId )
2024-07-02 11:43:59 +10:00
{
2024-07-05 15:17:48 +10:00
var appInWork = await applicationsInWorkService . Get ( )
. Include ( aiw = > aiw . EsppSchValues )
. ThenInclude ( esv = > esv . EsppSchTypeValue )
. ThenInclude ( etv = > etv ! . DistributionPeriod )
. FirstOrDefaultAsync ( t = > t . Id = = applicationInWorkId ) ;
2024-07-11 17:23:52 +10:00
2024-07-11 11:32:14 +10:00
var refDate = appInWork ! . ReferenceDate ;
2024-07-05 15:17:48 +10:00
//Проверяем наличие распределения Р Р на период
if ( appInWork ! . IsAutoDistributionEnabled )
2024-07-02 11:43:59 +10:00
{
2024-07-11 11:32:14 +10:00
var existingTemplates = await templateService . Get ( )
. Include ( t = > t . Host )
. Where ( t = > t . ApplicationInWorkId = = applicationInWorkId & & t . Host ! . WorkGroupId = = workGroupId )
. ToListAsync ( ) ;
2024-07-11 17:23:52 +10:00
//Удалим из входных шаблонов уже существующие в БД
templates . RemoveAll ( t = > existingTemplates . Any ( et = > et . Id = = t . Id ) ) ;
2024-07-05 15:17:48 +10:00
//Если распределенная Р Р получаем начало
var period = appInWork . EsppSchValues . FirstOrDefault ( ) ? . EsppSchTypeValue ? . DistributionPeriod ;
2024-07-11 11:32:14 +10:00
var periodType = ParseDistributionPeriodType ( period ! . Type ) ;
2024-07-02 11:43:59 +10:00
2024-07-11 11:32:14 +10:00
var workDays = await GetWorkDaysAsync ( appInWork ) ;
2024-07-05 15:17:48 +10:00
2024-07-11 11:32:14 +10:00
//Готовим план распределения
var distrPlan = GetDistributionPlan ( workDays , templates . Count + existingTemplates . Count ) ;
2024-07-05 15:17:48 +10:00
2024-07-11 11:32:14 +10:00
var templateToDistrib = new List < Template > ( ) ;
2024-07-05 15:17:48 +10:00
2024-07-11 11:32:14 +10:00
if ( existingTemplates . Count > 0 )
2024-07-11 12:02:50 +10:00
templateToDistrib = GetTemplatesToDistribute ( ref distrPlan , existingTemplates ) ;
2024-07-05 15:17:48 +10:00
2024-07-11 17:23:52 +10:00
templateToDistrib . AddRange ( templates ) ;
2024-07-11 14:42:55 +10:00
if ( ! templateToDistrib . Any ( ) )
return new List < Template > ( ) ;
2024-07-05 15:17:48 +10:00
2024-07-11 11:32:14 +10:00
var distributedTemplates = 0 ;
2024-07-05 15:17:48 +10:00
2024-07-11 11:32:14 +10:00
foreach ( var workDay in distrPlan )
2024-07-05 15:17:48 +10:00
{
2024-07-11 11:32:14 +10:00
var templatesCountForCurDay = workDay . Value ;
2024-07-11 17:23:52 +10:00
if ( templatesCountForCurDay < 1 )
continue ;
2024-07-11 11:32:14 +10:00
templateToDistrib . Skip ( distributedTemplates ) . Take ( templatesCountForCurDay ) . ToList ( ) . ForEach ( t = >
2024-07-05 15:17:48 +10:00
{
2024-07-11 11:32:14 +10:00
var nextRun = new DateTimeOffset (
workDay . Key . Year , workDay . Key . Month , workDay . Key . Day ,
refDate . Hour , refDate . Minute , refDate . Second ,
new TimeSpan ( 0 , 0 , 0 ) ) ;
if ( nextRun < DateTimeOffset . UtcNow )
nextRun = esppScheduleTransformService . GetNextDateForDistributionRun ( nextRun , periodType , period . Duration ) ;
t . NextRun = nextRun ;
} ) ;
distributedTemplates + = templatesCountForCurDay ;
2024-07-11 17:23:52 +10:00
distrPlan [ workDay . Key ] - = templatesCountForCurDay ;
2024-07-05 15:17:48 +10:00
}
2024-07-12 14:51:01 +10:00
return templates ;
2024-07-11 11:32:14 +10:00
//var d = new Dictionary<DateOnly, List<Template>>();
//foreach (var wd in workDays)
2024-07-05 15:17:48 +10:00
//{
2024-07-11 11:32:14 +10:00
// var wrokDateTimeOffset = new DateTimeOffset(
// wd.Year, wd.Month, wd.Day,
// refDate.Hour, refDate.Minute, refDate.Second,
// new TimeSpan(0, 0, 0));
// if (wrokDateTimeOffset < DateTimeOffset.UtcNow)
// wrokDateTimeOffset = esppScheduleTransformService.GetNextDateForDistributionRun(wrokDateTimeOffset, periodType, period.Duration);
// var assignedTemplates = existingTemplates.Where(t => t.NextRun == wrokDateTimeOffset).ToList();
// d.Add(wd, assignedTemplates);
//}
//var templatesPerStep = (double)templates.Count() / workDays.Count();//
//var currentTemplateStep = (int)Math.Ceiling(templatesPerStep);
//var delta = templatesPerStep - currentTemplateStep;
////templates.First().NextRun = currentDay;
//var templateDistributed = 0;
////Отталкиваясь от количества
//while (templateDistributed < templates.Count())
//{
// var wdCounts = d.Select(wd => wd.Value).OrderBy(count => count).ToArray();
// var templateCountDelta = d.Max(t => t.Value.Count()) - d.Min(t => t.Value.Count());
// var addingTemplatesCount = wdCounts.Length > 1 ?
// (templateCountDelta == 0) ? (int)Math.Ceiling(templatesPerStep) : templateCountDelta :
// (int)Math.Ceiling(templatesPerStep);
// var workDaysWithMinTemplates = d.Where(wd => wd.Value == wdCounts[0]).ToArray();
// if (workDaysWithMinTemplates.Length * addingTemplatesCount < templates.Count())
2024-07-05 15:17:48 +10:00
// {
2024-07-11 11:32:14 +10:00
// foreach (var wd in workDaysWithMinTemplates)
2024-07-05 15:17:48 +10:00
// {
2024-07-11 11:32:14 +10:00
// //Считаем DateTimeOffset, потому что у нас только дата пока
// var currentDay = new DateTimeOffset(
// wd.Key.Year, wd.Key.Month, wd.Key.Day,
// refDate.Hour, refDate.Minute, refDate.Second,
// new TimeSpan(0, 0, 0));
// templates.Skip(templateDistributed).Take(addingTemplatesCount).ToList().ForEach(t =>
// {
// t.NextRun = currentDay;
// d[wd.Key].Add(t);
// });
// templateDistributed += addingTemplatesCount;
// }
2024-07-05 15:17:48 +10:00
// }
// else
2024-07-11 11:32:14 +10:00
// {
// while (templateDistributed < templates.Count())
// {
// var wd = d.Where(wd => wd.Value == d.Min(t => t.Value)).First();
// //Считаем DateTimeOffset, потому что у нас только дата пока
// var currentDay = new DateTimeOffset(
// wd.Key.Year, wd.Key.Month, wd.Key.Day,
// refDate.Hour, refDate.Minute, refDate.Second,
// new TimeSpan(0, 0, 0));
// templates.Skip(templateDistributed).Take(addingTemplatesCount).ToList().ForEach(t =>
// {
// t.NextRun = currentDay;
// d[wd.Key].Add(t);
// });
// templateDistributed += addingTemplatesCount;
// }
// }
2024-07-05 15:17:48 +10:00
//}
}
else
templates . ForEach ( t = > t . NextRun = appInWork ! . ReferenceDate ) ;
2024-07-02 11:43:59 +10:00
return templates ;
}
2024-07-05 15:17:48 +10:00
2024-07-11 11:32:14 +10:00
private async Task < List < DateOnly > > GetWorkDaysAsync ( ApplicationsInWork appInWork )
{
var period = appInWork . EsppSchValues . FirstOrDefault ( ) ? . EsppSchTypeValue ? . DistributionPeriod ;
var periodType = ParseDistributionPeriodType ( period ! . Type ) ;
var startPeriod = await esppScheduleTransformService . GetStartPeriodForDateAsync ( appInWork . Id , appInWork . ReferenceDate , appInWork . ReferenceDate , periodType , period . Duration ) ;
var result = calendarService . GetWorkDatesForPeriod ( startPeriod , periodType , period . Duration , weekendDayService . GetWeekends ) ;
result . ForEach ( wd = >
{
var wrokDateTimeOffset = new DateTimeOffset (
wd . Year , wd . Month , wd . Day ,
2024-07-11 14:42:55 +10:00
appInWork . ReferenceDate . Hour , appInWork . ReferenceDate . Minute , appInWork . ReferenceDate . Second ,
2024-07-11 11:32:14 +10:00
new TimeSpan ( 0 , 0 , 0 ) ) ;
if ( wrokDateTimeOffset < DateTimeOffset . UtcNow )
{
wrokDateTimeOffset = esppScheduleTransformService . GetNextDateForDistributionRun ( wrokDateTimeOffset , periodType , period . Duration ) ;
wd = DateOnly . FromDateTime ( wrokDateTimeOffset . DateTime ) ;
}
} ) ;
return result ;
}
private List < Template > GetTemplatesToDistribute ( ref Dictionary < DateOnly , int > distributionPlan , List < Template > templates )
{
var result = new List < Template > ( ) ;
foreach ( var wd in distributionPlan )
{
2024-07-11 17:23:52 +10:00
//if (wd.Value < 1)
// continue;
2024-07-11 11:32:14 +10:00
2024-07-11 17:23:52 +10:00
var templatesInWd = templates . Where ( t = > DateOnly . FromDateTime ( t . NextRun . DateTime ) = = wd . Key ) . ToList ( ) ;
2024-07-11 11:32:14 +10:00
var templatesToDistribute = templatesInWd . Skip ( wd . Value ) . Take ( templatesInWd . Count - wd . Value ) . ToList ( ) ;
distributionPlan [ wd . Key ] - = templatesInWd . Count ;
result . AddRange ( templatesToDistribute ) ;
}
2024-07-11 17:23:52 +10:00
var workDays = distributionPlan . Keys . ToList ( ) ;
var templateWithNextRunOut = templates . Where ( t = > ! workDays . Contains ( DateOnly . FromDateTime ( t . NextRun . DateTime ) ) ) ;
result . AddRange ( templateWithNextRunOut ) ;
2024-07-11 11:32:14 +10:00
2024-07-11 17:23:52 +10:00
//Обнулим nextRun
result . ForEach ( t = > t . NextRun = DateTimeOffset . MinValue ) ;
2024-07-11 11:32:14 +10:00
return result ;
}
private DistributionPeriodTypeEnum ParseDistributionPeriodType ( string value )
{
var result = ( DistributionPeriodTypeEnum ) Enum . Parse ( typeof ( DistributionPeriodTypeEnum ) , value ) ;
return result ;
}
private Dictionary < DateOnly , int > GetDistributionPlan ( List < DateOnly > workDays , int templateCount )
{
var d = new Dictionary < DateOnly , int > ( ) ;
foreach ( var wd in workDays )
{
var workDateTimeOffset = new DateOnly ( wd . Year , wd . Month , wd . Day ) ;
d . Add ( workDateTimeOffset , 0 ) ;
}
var templateDistributed = 0 ;
2024-07-12 14:51:01 +10:00
if ( templateCount > = workDays . Count )
2024-07-11 11:32:14 +10:00
{
var mainPartTemplateCountToDistribute = templateCount / workDays . Count ;
foreach ( var item in d )
{
d [ item . Key ] = mainPartTemplateCountToDistribute ;
templateDistributed + = mainPartTemplateCountToDistribute ;
}
}
2024-07-12 14:51:01 +10:00
2024-07-11 11:32:14 +10:00
var templatesPerWorkDay = ( double ) workDays . Count / ( templateCount % workDays . Count ) ;
var currentStep = ( double ) Math . Floor ( templatesPerWorkDay ) ; //??????? точно double?
var balance = ( double ) templatesPerWorkDay - Math . Floor ( currentStep ) ;
var currentIndex = 0 ;
while ( templateDistributed < templateCount )
{
if ( currentStep > 0 )
{
d [ d . ElementAt ( currentIndex ) . Key ] + = 1 ;
templateDistributed + + ;
currentIndex + = ( int ) Math . Floor ( currentStep ) ;
2024-07-12 14:51:01 +10:00
}
currentStep = templatesPerWorkDay + balance ;
balance = templatesPerWorkDay - currentStep ;
2024-07-11 11:32:14 +10:00
}
return d ;
}
2024-07-02 11:43:59 +10:00
}
2024-07-05 15:17:48 +10:00
2024-07-02 11:43:59 +10:00
}