feat(templateDistributor): заготовка
This commit is contained in:
@@ -2,6 +2,6 @@
|
|||||||
{
|
{
|
||||||
public class DistributeRequest
|
public class DistributeRequest
|
||||||
{
|
{
|
||||||
public Guid ApplicationInWorkId { get; set; }
|
public Guid JobGroupId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace PARR.API.Controllers.V1
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Перераспределить шаблоны для регламентной работы
|
/// Перераспределить шаблоны для группы работ
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost(ApiRoutes.Distributor.Distribute)]
|
[HttpPost(ApiRoutes.Distributor.Distribute)]
|
||||||
@@ -48,7 +48,7 @@ namespace PARR.API.Controllers.V1
|
|||||||
|
|
||||||
var requestToMq = new TemplateDistributorMq
|
var requestToMq = new TemplateDistributorMq
|
||||||
{
|
{
|
||||||
ApplicationInWorkId = request.ApplicationInWorkId
|
JobGroupId = request.JobGroupId
|
||||||
};
|
};
|
||||||
|
|
||||||
var msg = JsonSerializer.Serialize(requestToMq);
|
var msg = JsonSerializer.Serialize(requestToMq);
|
||||||
@@ -58,7 +58,7 @@ namespace PARR.API.Controllers.V1
|
|||||||
if (sendResult.IsSuccess)
|
if (sendResult.IsSuccess)
|
||||||
return Created("", new Response<string?>(null, true, new List<ErrorModel>(), "Отправлен запрос на перераспределение регламентных работ."));
|
return Created("", new Response<string?>(null, true, new List<ErrorModel>(), "Отправлен запрос на перераспределение регламентных работ."));
|
||||||
else
|
else
|
||||||
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message="Ошибка при отправке данных."} }));
|
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = "Ошибка при отправке данных." } }));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,27 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using PARR.API.Contracts.V1.Requests;
|
using PARR.API.Contracts.V1.Requests;
|
||||||
using PARR.DAL.Services.Interfaces;
|
using PARR.DAL.Services.Interfaces.Job;
|
||||||
|
|
||||||
namespace PARR.API.Validators
|
namespace PARR.API.Validators
|
||||||
{
|
{
|
||||||
public class DistributeRequestValidator : AbstractValidator<DistributeRequest>
|
public class DistributeRequestValidator : AbstractValidator<DistributeRequest>
|
||||||
{
|
{
|
||||||
private readonly IApplicationsInWorkService applicationsInWorkService;
|
public DistributeRequestValidator(IJobGroupService jobGroupService)
|
||||||
|
|
||||||
public DistributeRequestValidator(IApplicationsInWorkService applicationsInWorkService)
|
|
||||||
{
|
{
|
||||||
this.applicationsInWorkService = applicationsInWorkService;
|
RuleFor(t => t.JobGroupId).NotEmpty().MustAsync(async (entity, value, c) =>
|
||||||
|
|
||||||
|
|
||||||
RuleFor(t => t.ApplicationInWorkId).NotEmpty().MustAsync(async (entity, value, c) =>
|
|
||||||
{
|
{
|
||||||
var appInWork = await applicationsInWorkService.GetAsync(value);
|
return await jobGroupService.Get().FirstOrDefaultAsync(t => t.Id == value) != null;
|
||||||
|
|
||||||
return appInWork != null;
|
|
||||||
}).WithMessage("Недопустимое значение");
|
}).WithMessage("Недопустимое значение");
|
||||||
|
|
||||||
|
RuleFor(t => t.JobGroupId).NotEmpty().MustAsync(async (entity, value, c) =>
|
||||||
|
{
|
||||||
|
var jobGroup = await jobGroupService.Get()
|
||||||
|
.Include(t => t.DistributionConfig)
|
||||||
|
.FirstOrDefaultAsync(t => t.Id == value);
|
||||||
|
|
||||||
|
return jobGroup != null && jobGroup.IsAutoDistributionEnabled && jobGroup.DistributionConfig != null;
|
||||||
|
}).WithMessage("Отсутствуют настройки автораспределения");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
namespace PARR.BLL.Domain.Mq
|
namespace PARR.BLL.Domain.Mq
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Модель в MQ, обновления расписаний шаблонов связанных с РР (для TemplateDistributor)
|
/// Модель в MQ, распределить шаблоны для JobGroupId (для TemplateDistributor)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TemplateDistributorMq
|
public class TemplateDistributorMq
|
||||||
{
|
{
|
||||||
public Guid ApplicationInWorkId { get; set; }
|
public Guid JobGroupId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,12 @@
|
|||||||
using PARR.DAL.Models;
|
namespace PARR.TemplateDistributor
|
||||||
|
|
||||||
namespace PARR.TemplateDistributor
|
|
||||||
{
|
{
|
||||||
public interface ITemplateDistributor
|
public interface ITemplateDistributor
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Формирует расписание запуска для шаблонов относительно одной РР (без сохранения в БД), (реализует оба режима распределения РР)
|
/// Обновляет NextRun (сохраняет в БД)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="templates"></param>
|
/// <param name="jobGroupId"></param>
|
||||||
/// <param name="applicationInWorkId"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<List<Template>> DistributeTemplateAsync(List<Template> templates, Guid applicationInWorkId, Guid workGroupid);
|
Task DistributeAsync(Guid jobGroupId);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Обновляет расписание запуска шаблонов по РР (сохраняет в БД)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="applicationInWorkId"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task UpdateScheduleAsync(Guid applicationInWorkId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace PARR.TemplateDistributor
|
|||||||
|
|
||||||
public async Task StopAsync()
|
public async Task StopAsync()
|
||||||
{
|
{
|
||||||
await mqService.DisposeAsync();
|
await mqService.DisposeAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateScheduleAsync(string msg)
|
private async Task UpdateScheduleAsync(string msg)
|
||||||
@@ -53,9 +53,9 @@ namespace PARR.TemplateDistributor
|
|||||||
if (query == null)
|
if (query == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!await validatorService.IsValidApplicationAndWorksAsync(query.ApplicationInWorkId))
|
if (!await validatorService.IsValidJobGroupAsync(query.JobGroupId))
|
||||||
{
|
{
|
||||||
logger.LogError($"Некорректные параметры регламентной работы {nameof(query.ApplicationInWorkId)}: {query.ApplicationInWorkId}");
|
logger.LogError("Не корректные параметры группы работ {jobGroupId}. Не буду ничего делать.", query.JobGroupId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,9 +63,9 @@ namespace PARR.TemplateDistributor
|
|||||||
{
|
{
|
||||||
var service = scope.ServiceProvider.GetService<ITemplateDistributor>();
|
var service = scope.ServiceProvider.GetService<ITemplateDistributor>();
|
||||||
if (service == null)
|
if (service == null)
|
||||||
throw new Exception($"Не найден сервис: {nameof(service.GetType)}");
|
throw new Exception($"Не найден сервис: {nameof(ITemplateDistributor)}");
|
||||||
|
|
||||||
await service.UpdateScheduleAsync(query.ApplicationInWorkId);
|
await service.DistributeAsync(query.JobGroupId);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
{
|
{
|
||||||
internal interface IValidatorService
|
internal interface IValidatorService
|
||||||
{
|
{
|
||||||
Task<bool> IsValidApplicationAndWorksAsync(Guid applicationInWorkId);
|
Task<bool> IsValidJobGroupAsync(Guid applicationInWorkId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using PARR.DAL.Models.Job;
|
||||||
using PARR.DAL.Services.Interfaces;
|
using PARR.DAL.Services.Interfaces;
|
||||||
|
using PARR.DAL.Services.Interfaces.Job;
|
||||||
|
|
||||||
namespace PARR.TemplateDistributor.Services
|
namespace PARR.TemplateDistributor.Services
|
||||||
{
|
{
|
||||||
@@ -17,32 +19,30 @@ namespace PARR.TemplateDistributor.Services
|
|||||||
this.serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
public async Task<bool> IsValidApplicationAndWorksAsync(Guid applicationInWorkId)
|
public async Task<bool> IsValidJobGroupAsync(Guid jobGroupId)
|
||||||
{
|
{
|
||||||
using (var scope = serviceProvider.CreateScope())
|
using (var scope = serviceProvider.CreateScope())
|
||||||
{
|
{
|
||||||
//TODO Migratin to Job
|
|
||||||
//var service = scope.ServiceProvider.GetService<IApplicationsInWorkService>();
|
|
||||||
|
|
||||||
//if (service == null)
|
var service = scope.ServiceProvider.GetService<IJobGroupService>();
|
||||||
// throw new Exception($"Не найден сервис: {nameof(IApplicationsInWorkService)}");
|
|
||||||
|
|
||||||
//var appInWork = await service.Get()//TODO Migratin to Job
|
if (service == null)
|
||||||
// //.Include(aiw => aiw.EsppSchValues)
|
throw new Exception($"Не найден сервис: {nameof(IJobGroupService)}");
|
||||||
// .FirstOrDefaultAsync(t => t.Id == applicationInWorkId);
|
|
||||||
|
|
||||||
//if (appInWork == null)
|
var jobGroup = await service.Get().Include(t => t.DistributionConfig).FirstOrDefaultAsync(t => t.Id == jobGroupId);
|
||||||
//{
|
|
||||||
// logger.LogError($"Не найдена регалментная работа {nameof(applicationInWorkId)}: {applicationInWorkId}");
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if (appInWork.IsAutoDistributionEnabled && appInWork.EsppSchValues.Count !=1)
|
if (jobGroup == null)
|
||||||
//{
|
{
|
||||||
// logger.LogError($"Регалментная работа {nameof(applicationInWorkId)}: {applicationInWorkId} должна иметь только одно значение EsppSchTypeValue" +
|
logger.LogError($"Не найдена группа работ с Id: {jobGroupId}", jobGroupId);
|
||||||
// $", соответствующее режиму равномерного распределения по периоду");
|
return false;
|
||||||
// return false;
|
}
|
||||||
//}
|
|
||||||
|
if (!jobGroup.IsAutoDistributionEnabled || jobGroup.DistributionConfig == null)
|
||||||
|
{
|
||||||
|
logger.LogError("Для группы работ {jobGroupId} отсутствуют настройки автораспределения. " +
|
||||||
|
"IsAutoDistributionEnabled: {IsAutoDistributionEnabled}, есть настройки в таблице {DistributionConfig}, {existConfig}", jobGroupId, jobGroup.IsAutoDistributionEnabled, nameof(JobGroupDistributionConfig), jobGroup.DistributionConfig != null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using PARR.BLL.Services.Interfaces;
|
using PARR.BLL.Services.Interfaces;
|
||||||
using PARR.Constants;
|
using PARR.Constants;
|
||||||
using PARR.DAL.Models;
|
using PARR.DAL.Models;
|
||||||
|
using PARR.DAL.NextRunServices;
|
||||||
using PARR.DAL.Services.Interfaces;
|
using PARR.DAL.Services.Interfaces;
|
||||||
using PARR.DAL.TransformServices;
|
using PARR.DAL.TransformServices;
|
||||||
using System.Reflection.Metadata.Ecma335;
|
using System.Reflection.Metadata.Ecma335;
|
||||||
@@ -12,427 +13,30 @@ namespace PARR.TemplateDistributor
|
|||||||
internal class TemplateDistributor : ITemplateDistributor
|
internal class TemplateDistributor : ITemplateDistributor
|
||||||
{
|
{
|
||||||
private readonly ILogger<TemplateDistributor> logger;
|
private readonly ILogger<TemplateDistributor> logger;
|
||||||
private readonly ITemplateService templateService;
|
private readonly INextRunService nextRunService;
|
||||||
private readonly IApplicationsInWorkService applicationsInWorkService;
|
|
||||||
private readonly ICalendarService calendarService;
|
|
||||||
private readonly IEsppScheduleTransformService esppScheduleTransformService;
|
|
||||||
private readonly IWeekendDayService weekendDayService;
|
|
||||||
private readonly INextRunModifierService nextRunModifierService;
|
|
||||||
|
|
||||||
public TemplateDistributor(
|
public TemplateDistributor(
|
||||||
ILogger<TemplateDistributor> logger,
|
ILogger<TemplateDistributor> logger,
|
||||||
ITemplateService templateService,
|
INextRunService nextRunService
|
||||||
IApplicationsInWorkService applicationsInWorkService,
|
|
||||||
ICalendarService calendarService,
|
|
||||||
IEsppScheduleTransformService esppScheduleTransformService,
|
|
||||||
IWeekendDayService weekendDayService,
|
|
||||||
INextRunModifierService nextRunModifierService
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.templateService = templateService;
|
this.nextRunService = nextRunService;
|
||||||
this.applicationsInWorkService = applicationsInWorkService;
|
|
||||||
this.calendarService = calendarService;
|
|
||||||
this.esppScheduleTransformService = esppScheduleTransformService;
|
|
||||||
this.weekendDayService = weekendDayService;
|
|
||||||
this.nextRunModifierService = nextRunModifierService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task UpdateScheduleAsync(Guid applicationInWorkId)//TODO Migratin to Job
|
public async Task DistributeAsync(Guid jobGroupId)
|
||||||
{
|
{
|
||||||
var appInWork = await applicationsInWorkService.GetAsync(applicationInWorkId);
|
// вызвать метод распределения, и получить новые даты
|
||||||
|
var distributedTemplates = await nextRunService.GetNextRunForJobGroupWithAutoDistributionAsync(jobGroupId);
|
||||||
|
|
||||||
var templates = await templateService.Get()
|
if (distributedTemplates == 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.Unit!.BaseFields!.WorkGroup);
|
|
||||||
|
|
||||||
/*foreach (var workGroupWithTemplates in workGroupsWithTemplates)
|
|
||||||
{
|
{
|
||||||
var wgId = (Guid)workGroupWithTemplates.Key!;
|
logger.LogWarning("При распределении шаблонов по jobGroupId {jobGroupId} вернулся null. Это ошибка. Прекращаю распределение.", jobGroupId);
|
||||||
var values = workGroupWithTemplates.ToList();
|
|
||||||
|
|
||||||
//Обновляем сразу время при необходимости, так как дальше будем работать только с датой
|
|
||||||
var timesList = values.Select(t => t.NextRun.TimeOfDay).Distinct().ToList();
|
|
||||||
if (timesList.Count() > 1 || (timesList.Count() == 1 && timesList[0] != appInWork!.ReferenceDate.TimeOfDay))
|
|
||||||
{
|
|
||||||
values.ForEach(t =>
|
|
||||||
{
|
|
||||||
if (t.NextRun.TimeOfDay != appInWork!.ReferenceDate.TimeOfDay)
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
if (!await templateService.CommitAsync())
|
|
||||||
{
|
|
||||||
logger.LogError($"Ошибка записи изменений в БД при актуализации времени NextRun шаблонов РР({applicationInWorkId})");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var distrTemplates = await DistributeTemplateAsync(new List<Template>(), applicationInWorkId, wgId);
|
|
||||||
|
|
||||||
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 refDate = appInWork.ReferenceDate;
|
|
||||||
|
|
||||||
// //Проверяем наличие распределения РР на период
|
|
||||||
// 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));
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// existingTemplates.AddRange(templates);
|
|
||||||
|
|
||||||
// //Если распределенная РР получаем начало
|
|
||||||
// var period = appInWork.EsppSchValues.First()!.EsppSchTypeValue!.DistributionPeriod;
|
|
||||||
// var periodType = ParseDistributionPeriodType(period!.Type);
|
|
||||||
|
|
||||||
// var workDays = await GetWorkDaysAsync(appInWork);
|
|
||||||
|
|
||||||
// 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 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);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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 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())
|
|
||||||
// // {
|
|
||||||
// // 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;
|
|
||||||
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
// // 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;
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
// //}
|
|
||||||
|
|
||||||
// #endregion
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// // templates.ForEach(t => t.NextRun = appInWork!.ReferenceDate);
|
|
||||||
// // это не автораспределение, по ReferenceDate получаем ближайший рабочий день
|
|
||||||
// templates.ForEach(async t => t.NextRun = await nextRunModifierService.GetWorkDayAsync(appInWork!.ReferenceDate));
|
|
||||||
|
|
||||||
// return templates;
|
|
||||||
return new List<Template>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Distribute(ref List<Template> templates, DateTimeOffset refDate, DistributionPeriod period, DistributionPeriodTypeEnum periodType, List<DateOnly> workDays)
|
|
||||||
{
|
|
||||||
//Готовим план распределения
|
|
||||||
var distrPlan = GetDistributionPlan(workDays, templates.Count);
|
|
||||||
|
|
||||||
//Проверяем шаблоны уже распределённые, чтобы не перемещать лишний раз
|
|
||||||
var templateToDistrib = GetTemplatesToDistribute(ref distrPlan, templates);
|
|
||||||
|
|
||||||
|
|
||||||
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()} остались без изменений");
|
|
||||||
|
|
||||||
if (!templateToDistrib.Any())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var distributedTemplates = 0;
|
|
||||||
//Распределяем шаблоны в соответствии с планом непосредственно
|
|
||||||
foreach (var workDay in distrPlan)
|
|
||||||
{
|
|
||||||
var templatesCountForCurDay = workDay.Value;
|
|
||||||
if (templatesCountForCurDay < 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
templateToDistrib.Skip(distributedTemplates).Take(templatesCountForCurDay).ToList().ForEach(t =>
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
distrPlan[workDay.Key] -= templatesCountForCurDay;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получить список рабочих дней из календаря и перенести прошедшие даты на следующий период по РР
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="appInWork"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private async Task<List<DateOnly>> GetWorkDaysAsync(ApplicationsInWork appInWork)
|
|
||||||
{
|
|
||||||
//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 workDays = calendarService.GetWorkDatesForPeriod(startPeriod, TimeOnly.FromDateTime(appInWork.ReferenceDate.DateTime), periodType, period.Duration, weekendDayService.GetWeekends);
|
|
||||||
|
|
||||||
//#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));
|
|
||||||
|
|
||||||
//// 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();
|
|
||||||
//////-----
|
|
||||||
|
|
||||||
////return resultWorkDays;
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//return workDays;
|
|
||||||
|
|
||||||
return new List<DateOnly>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Проверить соответствие распределения существующих шаблонов нововму плану.
|
|
||||||
/// На выходе получаем шаблоны, которым требуется изменить дату следующего запуска.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="distributionPlan"></param>
|
|
||||||
/// <param name="templates"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private List<Template> GetTemplatesToDistribute(ref Dictionary<DateOnly, int> distributionPlan, List<Template> templates)
|
|
||||||
{
|
|
||||||
var result = new List<Template>();
|
|
||||||
|
|
||||||
foreach (var wd in distributionPlan)
|
|
||||||
{
|
|
||||||
var templatesInWd = templates.Where(t => DateOnly.FromDateTime(t.NextRun.DateTime) == wd.Key).ToList();
|
|
||||||
|
|
||||||
if (templatesInWd.Any() && templatesInWd.Count > wd.Value)
|
|
||||||
{
|
|
||||||
result.AddRange(templatesInWd.Skip(wd.Value).Take(templatesInWd.Count - wd.Value).ToList());
|
|
||||||
distributionPlan[wd.Key] -= wd.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
distributionPlan[wd.Key] -= templatesInWd.Count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Определим шаблоны за границами рабочих дней. Их тоже необходимо перераспределить
|
// получить список шаблонов, сравнить их с распределенными, обновить даты, сохранить
|
||||||
var workDays = distributionPlan.Keys.ToList();
|
|
||||||
var templateWithNextRunOut = templates.Where(t => workDays.All(w => w != DateOnly.FromDateTime(t.NextRun.DateTime))).ToList();
|
|
||||||
|
|
||||||
if (templateWithNextRunOut.Any())
|
|
||||||
result.AddRange(templateWithNextRunOut);
|
|
||||||
|
|
||||||
if (result.Any())
|
|
||||||
//Обнулим nextRun
|
|
||||||
result.ForEach(t => t.NextRun = DateTimeOffset.MinValue);
|
|
||||||
|
|
||||||
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 result = new Dictionary<DateOnly, int>();
|
|
||||||
//Заполняем будующий план датами из полученных на входе данных
|
|
||||||
foreach (var wd in workDays)
|
|
||||||
{
|
|
||||||
var workDate = new DateOnly(wd.Year, wd.Month, wd.Day);
|
|
||||||
result.Add(workDate, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
var templateDistributedCount = 0;//количество распределённых на текущий момент шаблонов
|
|
||||||
|
|
||||||
//Если количество шаблонов больше количества рабочих дней, то сразу распределяем их равным количеством по всем рабочим дням
|
|
||||||
if (templateCount >= workDays.Count)
|
|
||||||
{
|
|
||||||
//количеством шаблонов на один рабочий день
|
|
||||||
var mainPartTemplateCountToDistribute = templateCount / workDays.Count;
|
|
||||||
|
|
||||||
//перебираем рабочие дня и записываем количество шаблонов
|
|
||||||
foreach (var item in result)
|
|
||||||
{
|
|
||||||
result[item.Key] = mainPartTemplateCountToDistribute;
|
|
||||||
|
|
||||||
templateDistributedCount += mainPartTemplateCountToDistribute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var daysBetweenTemplates = (double)workDays.Count / (templateCount % workDays.Count);//количество дней между шаблонами, если распределить их равномерно
|
|
||||||
var currentStepBetweenTemplates = (double)Math.Floor(daysBetweenTemplates);//инициализация шага перехода между датами запуска двух шаблонов
|
|
||||||
var balance = (double)daysBetweenTemplates - Math.Floor(currentStepBetweenTemplates);//остаток между датами, потому что мы берём целые дни, его нужно учесть в следующей итирации
|
|
||||||
var currentDayIndex = 0;//инициализация индекса даты словаря рабочих дней
|
|
||||||
|
|
||||||
while (templateDistributedCount < templateCount)
|
|
||||||
{
|
|
||||||
//лишняя проверка так как шагом ранее мы распределям шаблоны равным количеством и currentStepBetweenTemplates не может быть меньше одного дня
|
|
||||||
//if (currentStepBetweenTemplates > 0)
|
|
||||||
//{
|
|
||||||
result[result.ElementAt(currentDayIndex).Key] += 1;
|
|
||||||
templateDistributedCount++;
|
|
||||||
currentDayIndex += (int)Math.Floor(currentStepBetweenTemplates);
|
|
||||||
//}
|
|
||||||
|
|
||||||
currentStepBetweenTemplates = daysBetweenTemplates + balance;
|
|
||||||
balance = daysBetweenTemplates - currentStepBetweenTemplates;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,25 +4,23 @@ namespace PARR.TemplateDistributorWorker
|
|||||||
{
|
{
|
||||||
public class Worker : BackgroundService
|
public class Worker : BackgroundService
|
||||||
{
|
{
|
||||||
private readonly ILogger<Worker> _logger;
|
|
||||||
private readonly IMqTemplateDistributor mqTemplateDistributor;
|
private readonly IMqTemplateDistributor mqTemplateDistributor;
|
||||||
|
|
||||||
public Worker(ILogger<Worker> logger, IMqTemplateDistributor mqTemplateDistributor)
|
public Worker(ILogger<Worker> logger, IMqTemplateDistributor mqTemplateDistributor)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
|
||||||
this.mqTemplateDistributor = mqTemplateDistributor;
|
this.mqTemplateDistributor = mqTemplateDistributor;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
await mqTemplateDistributor.StartAsync();
|
await mqTemplateDistributor.StartAsync();
|
||||||
|
await Task.Delay(Timeout.Infinite, stoppingToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task StopAsync(CancellationToken cancellationToken)
|
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
mqTemplateDistributor.StopAsync().Wait();
|
await mqTemplateDistributor.StopAsync();
|
||||||
|
await base.StopAsync(cancellationToken);
|
||||||
return base.StopAsync(cancellationToken);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ services:
|
|||||||
fluentd-buffer-limit: '52428800'
|
fluentd-buffer-limit: '52428800'
|
||||||
tag: parr.template-distributor.serilog
|
tag: parr.template-distributor.serilog
|
||||||
deploy:
|
deploy:
|
||||||
replicas: 2
|
replicas: 1
|
||||||
networks:
|
networks:
|
||||||
parr-network:
|
parr-network:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user