using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PARR.Core.Repositories.Interfaces; using PARR.Core.Repositories.Interfaces.Job; using PARR.Core.Repositories.Interfaces.Schedule; using PARR.Core.Services.NextRunServices.Models; using PARR.Core.Services.NextRunServices.Subservices; using PARR.Core.Services.Shortcodes; using PARR.Domain.Entities; using PARR.Domain.Entities.JobGroupEntities; using PARR.Domain.Enums; using PARR.Domain.Settings; namespace PARR.Core.Services.NextRunServices { internal class NextRunService : INextRunService { private readonly ILogger logger; private readonly ITemplateRepository templateService; private readonly IJobGroupRepository jobGroupService; private readonly IEsppScheduleTransformService esppScheduleTransformService; private readonly ITemplateDistributor templateDistributor; private readonly IShortcodesService shortcodesService; private readonly IScheduleResponseAreaTimeOffsetRepository scheduleResponseAreaTimeOffsetService; private readonly SettingsFromDb settingsFromDb; public NextRunService( ILogger logger, ITemplateRepository templateService, IJobGroupRepository jobGroupService, IEsppScheduleTransformService esppScheduleTransformService, ITemplateDistributor templateDistributor, IShortcodesService shortcodesService, IScheduleResponseAreaTimeOffsetRepository 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