using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PARR.Constants; using PARR.DAL.Contracts; using PARR.DAL.DomainServices.Shortcodes; using PARR.DAL.Models; using PARR.DAL.Models.Job; using PARR.DAL.NextRunServices.Models; using PARR.DAL.NextRunServices.Subservices; using PARR.DAL.Services.Interfaces; using PARR.DAL.Services.Interfaces.Job; using PARR.DAL.Services.Interfaces.Schedule; namespace PARR.DAL.NextRunServices { internal class NextRunServiceV2 : INextRunServiceV2 { private readonly ILogger logger; private readonly ITemplateService templateService; private readonly IJobGroupService jobGroupService; private readonly IEsppScheduleTransformService esppScheduleTransformService; private readonly ITemplateDistributorV2 templateDistributor; private readonly IShortcodesService shortcodesService; private readonly IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService; private readonly SettingsFromDb settingsFromDb; public NextRunServiceV2( ILogger logger, ITemplateService templateService, IJobGroupService jobGroupService, IEsppScheduleTransformService esppScheduleTransformService, ITemplateDistributorV2 templateDistributor, IShortcodesService shortcodesService, IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService, SettingsFromDb settingsFromDb ) { this.logger = logger; this.templateService = templateService; this.jobGroupService = jobGroupService; this.esppScheduleTransformService = esppScheduleTransformService; this.templateDistributor = templateDistributor; this.shortcodesService = shortcodesService; this.scheduleResponseAreaTimeOffsetService = scheduleResponseAreaTimeOffsetService; this.settingsFromDb = settingsFromDb; } public async Task?> GetNextRunForJobGroupWithAutoDistributionAsync(Guid jobGroupId, TemplateStatusTypeEnum? templateStatusType) { logger.LogInformation("Начинаю распределять шаблоны для группы работ {gobGroupId}, templateStatusType: {templateStatusType}", jobGroupId, templateStatusType); var jobGroup = await jobGroupService.Get() .AsNoTracking() .Include(t => t.DistributionConfig) .ThenInclude(t => t.DistributionPeriod) .FirstOrDefaultAsync(t => t.Id == jobGroupId); if (jobGroup == null) { logger.LogError("Не найдена группа работа с id: {id}", jobGroupId); return null; } if (!jobGroup.IsAutoDistributionEnabled || jobGroup.DistributionConfig == null) { logger.LogError("Группа работ id: {id} не подходит для автораспределения, у нее или отсутствуют настройки или не включено автораспределение. IsAutoDistributionEnabled: {IsAutoDistributionEnabled}. Есть конфиг: {DistributionConfig}", jobGroupId, jobGroup.IsAutoDistributionEnabled, jobGroup.DistributionConfig != null); return null; } logger.LogInformation("Параметры распределения. groupId: {groupId}, name: {groupName}, referenceDate: {referenceDate}, " + "distributionPeriodName: {distributionPeriodName}, distributionPeriodDuration: {distributionPeriodDuration}, " + "distributionPeriodType: {distributionPeriodType}, IsExcludeWeekends: {IsExcludeWeekends}, IsGroupingByWorkGroup: {IsGroupingByWorkGroup}, IsResponseAreaTimezone: {IsResponseAreaTimezone}", jobGroupId, jobGroup.GroupName, jobGroup.ReferenceDate, jobGroup.DistributionConfig.DistributionPeriod.Name, jobGroup.DistributionConfig.DistributionPeriod.Duration, jobGroup.DistributionConfig.DistributionPeriod.Type, jobGroup.DistributionConfig.IsExcludeWeekends, jobGroup.DistributionConfig.IsGroupingByWorkGroup, jobGroup.IsResponseAreaTimezone); //получаем шаблоны только в статусе Used var templateQuery = templateService.Get() .AsNoTracking() .Include(t => t.Job) .Where(t => t.Job!.GroupId == jobGroupId //&& t.StatusTypeId == TemplateStatusTypeEnum.Used ); if (templateStatusType.HasValue) templateQuery = templateQuery.Where(t => t.StatusTypeId == templateStatusType); var allTemplates = await templateQuery.ToListAsync(); if (!allTemplates.Any()) { logger.LogInformation("В группе c ИД {jobGroupId} отсутствуют шаблоны в статусе Used", jobGroupId); return null; } logger.LogDebug("Всего шаблонов для распределения в статусе Used: {count} шт.", allTemplates.Count); // продолжительность в днях var durationDays = GetDurationDays(jobGroup.DistributionConfig); // группировать по РГ var isGroupingByWorkGroup = jobGroup.DistributionConfig.IsGroupingByWorkGroup; // использовать часовой пояс РГ var isResponseAreaTimeZone = jobGroup.IsResponseAreaTimezone; var distributedTemplates = new List(); if (isGroupingByWorkGroup) { // группировать по РГ logger.LogDebug("Нужно группировать по РГ"); // получаем для каждого шаблона РГ и сразу группируем по РГ var templatesByWorkGroup = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (var template in allTemplates) { var workGroupName = await shortcodesService.ApplyShortcodesAsync(template.Job!.WorkGroupMask, template); // Если даже workGroupName == null, ну и ладно, сгруппируем по null, и распределим шаблоны в рамках этого null if (workGroupName == null) logger.LogWarning("Для шаблона {id}, с маской РГ '{workGroupMask}', не смог с помощью шорткода определить рабочую группу, шорткод сервис вернул РГ: '{workGroupName}'", template.Id, template.Job!.WorkGroupMask, workGroupName); // Добавляем шаблон в группу if (!templatesByWorkGroup.TryGetValue(workGroupName, out var templatesList)) { templatesList = new List