feat(all): ! Критические изменения логики работы всех сервисорв, База Данных имееет неокончательную версию. Template, Scheduler переведены на работу с Unit+Job+JobGroup вместо Host+ApplicationsInWork

This commit is contained in:
Mikhail Kuznetsov
2025-06-25 14:17:34 +10:00
parent 1b867585d8
commit 03c7852378
54 changed files with 5176 additions and 989 deletions

View File

@@ -21,27 +21,28 @@ namespace PARR.TemplateDistributor.Services
{
using (var scope = serviceProvider.CreateScope())
{
var service = scope.ServiceProvider.GetService<IApplicationsInWorkService>();
//TODO Migratin to Job
//var service = scope.ServiceProvider.GetService<IApplicationsInWorkService>();
if (service == null)
throw new Exception($"Не найден сервис: {nameof(IApplicationsInWorkService)}");
//if (service == null)
// throw new Exception($"Не найден сервис: {nameof(IApplicationsInWorkService)}");
var appInWork = await service.Get()
.Include(aiw => aiw.EsppSchValues)
.FirstOrDefaultAsync(t => t.Id == applicationInWorkId);
//var appInWork = await service.Get()//TODO Migratin to Job
// //.Include(aiw => aiw.EsppSchValues)
// .FirstOrDefaultAsync(t => t.Id == applicationInWorkId);
if (appInWork == null)
{
logger.LogError($"Не найдена регалментная работа {nameof(applicationInWorkId)}: {applicationInWorkId}");
return false;
}
//if (appInWork == null)
//{
// logger.LogError($"Не найдена регалментная работа {nameof(applicationInWorkId)}: {applicationInWorkId}");
// return false;
//}
if (appInWork.IsAutoDistributionEnabled && appInWork.EsppSchValues.Count !=1)
{
logger.LogError($"Регалментная работа {nameof(applicationInWorkId)}: {applicationInWorkId} должна иметь только одно значение EsppSchTypeValue" +
$", соответствующее режиму равномерного распределения по периоду");
return false;
}
//if (appInWork.IsAutoDistributionEnabled && appInWork.EsppSchValues.Count !=1)
//{
// logger.LogError($"Регалментная работа {nameof(applicationInWorkId)}: {applicationInWorkId} должна иметь только одно значение EsppSchTypeValue" +
// $", соответствующее режиму равномерного распределения по периоду");
// return false;
//}
return true;
}

View File

@@ -5,6 +5,7 @@ using PARR.Constants;
using PARR.DAL.Models;
using PARR.DAL.Services.Interfaces;
using PARR.DAL.TransformServices;
using System.Reflection.Metadata.Ecma335;
namespace PARR.TemplateDistributor
{
@@ -38,20 +39,20 @@ namespace PARR.TemplateDistributor
}
public async Task UpdateScheduleAsync(Guid applicationInWorkId)
public async Task UpdateScheduleAsync(Guid applicationInWorkId)//TODO Migratin to Job
{
var appInWork = await applicationsInWorkService.GetAsync(applicationInWorkId);
var templates = await templateService.Get()
.Include(t => t.Host)
.ThenInclude(h => h!.WorkGroup)
.Where(t => t.ApplicationInWorkId == applicationInWorkId && t.Host!.WorkGroupId != null)
.Include(t => t.Unit)//TODO Migratin to Job
//.ThenInclude(h => h!.WorkGroup)
.Where(t => t.JobId == applicationInWorkId && t.Unit!.BaseFields!.WorkGroup != null)//TODO Migratin to Job
.ToListAsync();
//Группируем шаблоны по рабочим группам
var workGroupsWithTemplates = templates.GroupBy(t => t.Host!.WorkGroupId);
var workGroupsWithTemplates = templates.GroupBy(t => t.Unit!.BaseFields!.WorkGroup);
foreach (var workGroupWithTemplates in workGroupsWithTemplates)
/*foreach (var workGroupWithTemplates in workGroupsWithTemplates)
{
var wgId = (Guid)workGroupWithTemplates.Key!;
var values = workGroupWithTemplates.ToList();
@@ -79,163 +80,167 @@ namespace PARR.TemplateDistributor
if (distrTemplates.Any())
if (!await templateService.CommitAsync())
logger.LogError($"Ошибка записи изменений в БД при перераспределении NextRun шаблонов РР({applicationInWorkId})");
}
}*/
// TODO Migratin to Job
}
public async Task<List<Template>> DistributeTemplateAsync(List<Template> templates, Guid applicationInWorkId, Guid workGroupId)
{
var appInWork = await applicationsInWorkService.Get()
.Include(aiw => aiw.EsppSchValues)
.ThenInclude(esv => esv.EsppSchTypeValue)
.ThenInclude(etv => etv!.DistributionPeriod)
.FirstAsync(t => t.Id == applicationInWorkId);
// var appInWork = await applicationsInWorkService.Get()
// .Include(aiw => aiw.EsppSchValues)
// .ThenInclude(esv => esv.EsppSchTypeValue)
// .ThenInclude(etv => etv!.DistributionPeriod)
// .FirstAsync(t => t.Id == applicationInWorkId);
var refDate = appInWork.ReferenceDate;
// var refDate = appInWork.ReferenceDate;
//Проверяем наличие распределения РР на период
if (appInWork.IsAutoDistributionEnabled)
{
var existingTemplates = await templateService.Get()
.Include(t => t.Host)
.Where(t => t.ApplicationInWorkId == applicationInWorkId && t.Host!.WorkGroupId == workGroupId)
.ToListAsync();
// //Проверяем наличие распределения РР на период
// if (appInWork.IsAutoDistributionEnabled)
// {
// var existingTemplates = await templateService.Get()
// .Include(t => t.Unit)
// .Where(t => t.JobId == applicationInWorkId
// //&& t.Host!.WorkGroupId == workGroupId//TODO Migratin to Job
// )
// .ToListAsync();
//Удалим из входных шаблонов уже существующие в БД (по идее, такого никогда не должно случиться)
var duplicates = templates.Select(t => t.Id).Where(t => existingTemplates.Any(et => et.Id == t)).ToList();
if (duplicates.Count > 0)
{
duplicates.ForEach(t =>
{
logger.LogWarning($"Для распределения AiW({applicationInWorkId}),workGroupId({workGroupId}) передан существующий в базе данных шаблон {t}");
});
templates.RemoveAll(t => duplicates.Any(dt => dt == t.Id));
}
// //Удалим из входных шаблонов уже существующие в БД (по идее, такого никогда не должно случиться)
// var duplicates = templates.Select(t => t.Id).Where(t => existingTemplates.Any(et => et.Id == t)).ToList();
// if (duplicates.Count > 0)
// {
// duplicates.ForEach(t =>
// {
// logger.LogWarning($"Для распределения AiW({applicationInWorkId}),workGroupId({workGroupId}) передан существующий в базе данных шаблон {t}");
// });
// templates.RemoveAll(t => duplicates.Any(dt => dt == t.Id));
// }
existingTemplates.AddRange(templates);
// existingTemplates.AddRange(templates);
//Если распределенная РР получаем начало
var period = appInWork.EsppSchValues.First()!.EsppSchTypeValue!.DistributionPeriod;
var periodType = ParseDistributionPeriodType(period!.Type);
// //Если распределенная РР получаем начало
// var period = appInWork.EsppSchValues.First()!.EsppSchTypeValue!.DistributionPeriod;
// var periodType = ParseDistributionPeriodType(period!.Type);
var workDays = await GetWorkDaysAsync(appInWork);
// var workDays = await GetWorkDaysAsync(appInWork);
var result = new List<Template>();
// var result = new List<Template>();
//распределяем РР отдельно активированные
var activatedTemplates = existingTemplates.Where(t => t.IsActiveTemplate && t.IsActiveSchedule).ToList();
if (activatedTemplates.Count > 0)
{
Distribute(ref activatedTemplates, refDate, period, periodType, workDays);
// result.AddRange(activatedTemplates);
}
// //распределяем РР отдельно активированные
// var activatedTemplates = existingTemplates.Where(t => t.IsActiveTemplate && t.IsActiveSchedule).ToList();
// if (activatedTemplates.Count > 0)
// {
// Distribute(ref activatedTemplates, refDate, period, periodType, workDays);
// // result.AddRange(activatedTemplates);
// }
//распределяем РР отдельно деактивированные
var deactivatedTemplates = existingTemplates.Where(t =>
!t.IsActiveTemplate || !t.IsActiveSchedule || (
!t.IsActiveTemplate && !t.IsActiveSchedule
)).ToList();
if (deactivatedTemplates.Count > 0)
{
Distribute(ref deactivatedTemplates, refDate, period, periodType, workDays);
// result.AddRange(deactivatedTemplates);
}
// //распределяем РР отдельно деактивированные
// var deactivatedTemplates = existingTemplates.Where(t =>
// !t.IsActiveTemplate || !t.IsActiveSchedule || (
// !t.IsActiveTemplate && !t.IsActiveSchedule
// )).ToList();
// if (deactivatedTemplates.Count > 0)
// {
// Distribute(ref deactivatedTemplates, refDate, period, periodType, workDays);
// // result.AddRange(deactivatedTemplates);
// }
if (templates.Any())
{
// это была генерация шаблонов, не нужны все шаблоны, нужны только те которые передал для получения NextRun
result.AddRange(templates);
}
else
{
// это было распределение шаблонов, добавляем активированные и деактивированные
result.AddRange(activatedTemplates);
result.AddRange(deactivatedTemplates);
}
// if (templates.Any())
// {
// // это была генерация шаблонов, не нужны все шаблоны, нужны только те которые передал для получения NextRun
// result.AddRange(templates);
// }
// else
// {
// // это было распределение шаблонов, добавляем активированные и деактивированные
// result.AddRange(activatedTemplates);
// result.AddRange(deactivatedTemplates);
// }
return result;
#region comments
// return result;
// #region comments
//var d = new Dictionary<DateOnly, List<Template>>();
//foreach (var wd in workDays)
//{
// 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 d = new Dictionary<DateOnly, List<Template>>();
// //foreach (var wd in workDays)
// //{
// // 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 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;
// //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();
// ////Отталкиваясь от количества
// //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())
// {
// foreach (var wd in workDaysWithMinTemplates)
// {
// //Считаем 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));
// // if (workDaysWithMinTemplates.Length * addingTemplatesCount < templates.Count())
// // {
// // foreach (var wd in workDaysWithMinTemplates)
// // {
// // //Считаем 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;
// // templates.Skip(templateDistributed).Take(addingTemplatesCount).ToList().ForEach(t =>
// // {
// // t.NextRun = currentDay;
// // d[wd.Key].Add(t);
// // });
// // templateDistributed += addingTemplatesCount;
// }
// }
// else
// {
// 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));
// // }
// // }
// // else
// // {
// // 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;
// }
// }
//}
// // templates.Skip(templateDistributed).Take(addingTemplatesCount).ToList().ForEach(t =>
// // {
// // t.NextRun = currentDay;
// // d[wd.Key].Add(t);
// // });
// // templateDistributed += addingTemplatesCount;
// // }
// // }
// //}
#endregion
}
else
// templates.ForEach(t => t.NextRun = appInWork!.ReferenceDate);
// это не автораспределение, по ReferenceDate получаем ближайший рабочий день
templates.ForEach(async t => t.NextRun = await nextRunModifierService.GetWorkDayAsync(appInWork!.ReferenceDate));
// #endregion
// }
// else
// // templates.ForEach(t => t.NextRun = appInWork!.ReferenceDate);
// // это не автораспределение, по ReferenceDate получаем ближайший рабочий день
// templates.ForEach(async t => t.NextRun = await nextRunModifierService.GetWorkDayAsync(appInWork!.ReferenceDate));
return templates;
// return templates;
return new List<Template>();
}
private void Distribute(ref List<Template> templates, DateTimeOffset refDate, DistributionPeriod period, DistributionPeriodTypeEnum periodType, List<DateOnly> workDays)
@@ -246,9 +251,9 @@ namespace PARR.TemplateDistributor
//Проверяем шаблоны уже распределённые, чтобы не перемещать лишний раз
var templateToDistrib = GetTemplatesToDistribute(ref distrPlan, templates);
//logger.LogInformation($"{GetType().Name}(AppInWId:{templates.Select(t => t.ApplicationInWorkId).First()}, WorkGroup:{templates.Select(t => t.Host!.WorkGroup!.Name).First()}) запланировал изменение даты следующего срабатывания у {templateToDistrib.Count()} существующих шаблона(ов), {templates.Count() - templateToDistrib.Count()} остались без изменений");
string? loggerWorkGroup = templates.FirstOrDefault()?.Host?.WorkGroup != null ? templates.FirstOrDefault()?.Host?.WorkGroup?.Name : templates.FirstOrDefault()?.Host?.WorkGroupId?.ToString();
logger.LogInformation($"{GetType().Name}(AppInWId:{templates.Select(t => t.ApplicationInWorkId).First()}, " +
string? loggerWorkGroup = templates.FirstOrDefault()?.Unit?.BaseFields?.WorkGroup != null ? templates.FirstOrDefault()?.Unit.BaseFields.WorkGroup : templates.FirstOrDefault()?.Unit?.BaseFields?.WorkGroup?.ToString();
logger.LogInformation($"{GetType().Name}(AppInWId:{templates.Select(t => t.JobId).First()}, " +
$"WorkGroup:{loggerWorkGroup}) " +
$"запланировал изменение даты следующего срабатывания у {templateToDistrib.Count()} существующих шаблона(ов), {templates.Count() - templateToDistrib.Count()} остались без изменений");
@@ -288,47 +293,49 @@ namespace PARR.TemplateDistributor
/// <returns></returns>
private async Task<List<DateOnly>> GetWorkDaysAsync(ApplicationsInWork appInWork)
{
var period = appInWork.EsppSchValues.FirstOrDefault()?.EsppSchTypeValue?.DistributionPeriod;
var periodType = ParseDistributionPeriodType(period!.Type);
//var period = appInWork.EsppSchValues.FirstOrDefault()?.EsppSchTypeValue?.DistributionPeriod;
//var periodType = ParseDistributionPeriodType(period!.Type);
//старт период, берем сейчас, будем получать рабочие дни с вычетом выходных на весь период сразу.
var startPeriod = DateOnly.FromDateTime(DateTimeOffset.UtcNow.Date);
// старт период считаем относительно refDate
//var startPeriod = await esppScheduleTransformService.GetStartPeriodForDateAsync(appInWork.Id, appInWork.ReferenceDate, appInWork.ReferenceDate, periodType, period.Duration);
////старт период, берем сейчас, будем получать рабочие дни с вычетом выходных на весь период сразу.
//var startPeriod = DateOnly.FromDateTime(DateTimeOffset.UtcNow.Date);
//// старт период считаем относительно refDate
////var startPeriod = await esppScheduleTransformService.GetStartPeriodForDateAsync(appInWork.Id, appInWork.ReferenceDate, appInWork.ReferenceDate, periodType, period.Duration);
var workDays = calendarService.GetWorkDatesForPeriod(startPeriod, TimeOnly.FromDateTime(appInWork.ReferenceDate.DateTime), periodType, period.Duration, weekendDayService.GetWeekends);
//var workDays = calendarService.GetWorkDatesForPeriod(startPeriod, TimeOnly.FromDateTime(appInWork.ReferenceDate.DateTime), periodType, period.Duration, weekendDayService.GetWeekends);
#region старая логика
//var resultWorkDays = new List<DateOnly>();
//#region старая логика
////var resultWorkDays = new List<DateOnly>();
//workDays.ForEach(wd =>
//{
// var wrokDateTimeOffset = new DateTimeOffset(
// wd.Year, wd.Month, wd.Day,
// appInWork.ReferenceDate.Hour, appInWork.ReferenceDate.Minute, appInWork.ReferenceDate.Second,
// new TimeSpan(0, 0, 0));
////workDays.ForEach(wd =>
////{
//// var wrokDateTimeOffset = new DateTimeOffset(
//// wd.Year, wd.Month, wd.Day,
//// appInWork.ReferenceDate.Hour, appInWork.ReferenceDate.Minute, appInWork.ReferenceDate.Second,
//// new TimeSpan(0, 0, 0));
// if (wrokDateTimeOffset < DateTimeOffset.UtcNow)
// {
// wrokDateTimeOffset = esppScheduleTransformService.GetNextDateForDistributionRun(wrokDateTimeOffset, periodType, period.Duration);
// //wd = DateOnly.FromDateTime(wrokDateTimeOffset.DateTime);
// resultWorkDays.Add(DateOnly.FromDateTime(wrokDateTimeOffset.DateTime));
// }
// else
// {
// resultWorkDays.Add(wd);
// }
//});
//// if (wrokDateTimeOffset < DateTimeOffset.UtcNow)
//// {
//// wrokDateTimeOffset = esppScheduleTransformService.GetNextDateForDistributionRun(wrokDateTimeOffset, periodType, period.Duration);
//// //wd = DateOnly.FromDateTime(wrokDateTimeOffset.DateTime);
//// resultWorkDays.Add(DateOnly.FromDateTime(wrokDateTimeOffset.DateTime));
//// }
//// else
//// {
//// resultWorkDays.Add(wd);
//// }
////});
////----- убираем даты, которые могли выйти за конец периода (из-за сдвига NextRun по рабочим дням)
//var endPeriod = calendarService.GetEndPeriodDate(DateOnly.FromDateTime(DateTimeOffset.UtcNow.Date), periodType, period.Duration);
//resultWorkDays = resultWorkDays.Where(t => t <= endPeriod).Distinct().OrderBy(t => t).ToList();
////-----
//////----- убираем даты, которые могли выйти за конец периода (из-за сдвига NextRun по рабочим дням)
////var endPeriod = calendarService.GetEndPeriodDate(DateOnly.FromDateTime(DateTimeOffset.UtcNow.Date), periodType, period.Duration);
////resultWorkDays = resultWorkDays.Where(t => t <= endPeriod).Distinct().OrderBy(t => t).ToList();
//////-----
//return resultWorkDays;
#endregion
////return resultWorkDays;
//#endregion
return workDays;
//return workDays;
return new List<DateOnly>();
}
/// <summary>