using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PARR.BLL.Helpers; using PARR.BLL.Services.Interfaces; using PARR.DAL.Contracts; using PARR.DAL.Models; using PARR.DAL.Services.Interfaces; using PARR.DAL.TransformServices; using PARR.EsppScheduleSync.Domain; using PARR.EsppScheduleSync.Settings; using PARR.EsppSync; namespace PARR.EsppScheduleSync { internal class ScheduleSyncher : IScheduleSyncher { private readonly ILogger logger; private readonly GlobalSettings globalSettings; private readonly IMqService mqService; private readonly ISyncService syncService; private readonly SettingsFromDb settingsFromDb; private readonly IServiceProvider serviceProvider; //private readonly INextRunModifierService nextRunModifierService; public ScheduleSyncher( ILogger logger, GlobalSettings globalSettings, IMqService mqService, ISyncService syncService, SettingsFromDb settingsFromDb, IServiceProvider serviceProvider //INextRunModifierService nextRunModifierService ) { this.logger = logger; this.globalSettings = globalSettings; this.mqService = mqService; this.syncService = syncService; this.settingsFromDb = settingsFromDb; this.serviceProvider = serviceProvider; //this.nextRunModifierService = nextRunModifierService; if (globalSettings.MqSettings == null) { logger.LogError("Нет секции настроек хранилища. MqSettings, EsppTemplates"); throw new Exception("Нет секции настроек хранилища. MqSettings, EsppTemplates"); } } public async Task StartAsync() { var isConnected = await mqService.InitConsumerAsync(globalSettings!.MqSettings!, SyncScheduleAsync); if (!isConnected) throw new Exception("Ошибка при подключении к RabbitMq"); logger.LogInformation($"Запущена проверка очереди {globalSettings.MqSettings!.QueueName}."); } public async Task StopAsync() { await mqService.DisposeAsync(); logger.LogInformation($"=== === === Соединение с очередью {globalSettings.MqSettings!.QueueName} закрыто === === ==="); } private async Task SyncScheduleAsync(string str) { await syncService.SyncEsppObjectAsync(str, ParseStrToEsppObject, ConvertDbObjToEsppObj); } /// /// Преобразование модели БД в модель для сравнения /// /// /// /// private EsppObjectSchedule ConvertDbObjToEsppObj(Template template) { //дефолтное значение, изменится в сервисе nextRunModifierService var nextRunByAccountRobotTimeZone = DateTimeOffset.MinValue; using (var scope = serviceProvider.CreateScope()) { var nextRunModifierService = scope.ServiceProvider.GetService(); if (nextRunModifierService == null) throw new Exception($"Не найден сервис: {nameof(INextRunModifierService)}"); nextRunByAccountRobotTimeZone = nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun); } var esppObjectFromDb = new EsppObjectSchedule { TemplateName = template.Name.ToUpper(), Code = template.ScheduleEsppId ?? "", ScheduleName = template.Name, IsActive = template.IsActiveSchedule, //ResponseArea = template.Host!.ResponseArea!.Name,//TODO Migration to job //WorkGroup = template.Host!.WorkGroup!, //WorkGroup = template.Host!.WorkGroup!.Name,//TODO Migration to job //Мы решили, что для всех расписаний "Нет исключений", если что-то поменяется, тут нужно переделать //TypeV60calendar = settingsFromDb.ScheduleExcludeType == "Нет исключений" ? "NONE" : "", TypeV60calendar = GetTypeV60calendar(template), V60calendar = GetV60calendar(template), //Scheduled = EsppScheduleHelpers.GetNextRun(template.NextRun), //Scheduled = EsppScheduleHelpers.GetNextRun(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)), //BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)), Scheduled = EsppScheduleHelpers.GetNextRun(nextRunByAccountRobotTimeZone), BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunByAccountRobotTimeZone), Timezone = settingsFromDb.ScheduleTimezone, //Мы решили, что для всех расписаний "Отсутствует дата завершения", если что-то поменяется, тут нужно переделать TerminationType = settingsFromDb.ScheduleRepeatRange == "Отсутствует дата завершения" ? "forever" : "", CompleteAfter = "" }; FillScheduleFromDb(template, ref esppObjectFromDb); return ClearOptionalFields(esppObjectFromDb); } /// /// Получить исключение - Календарь /// /// /// private string GetV60calendar(Template template) { // мы знаем, что у нас точно в шаблоне есть инклуды до ScheduleExcludeType и ScheduleExcludeTypeCalendar if (template.Job?.Group?.ScheduleExcludeTypeCalendar == null) { logger.LogDebug("Для шаблона шаблона {templte}, {templateId} нет исключений календаря", template.Name, template.Id); //TODO:!!!!!!!!!!! Вот тут null или string.Empty??? Спросить у Андрея что он нам вернет! return string.Empty; } logger.LogDebug("Для шаблона шаблона {templte}, {templateId} установлено исключений календаря \"{name}\", EsppValue: {esppValue}", template.Name, template.Id, template.Job.Group.ScheduleExcludeTypeCalendar.Title, template.Job.Group.ScheduleExcludeTypeCalendar.EsppValue); return template.Job.Group.ScheduleExcludeTypeCalendar.EsppValue; } /// /// Получить тип календаря /// /// private string GetTypeV60calendar(Template template) { // мы знаем, что у нас точно в шаблоне есть инклуды до ScheduleExcludeType и ScheduleExcludeTypeCalendar // на всякий конечно же проверим if (template.Job?.Group?.ScheduleExcludeType == null) { logger.LogError("Для шаблона {templte}, {templateId} не смог получить тип исключения, установил значение по умолчанию \"Без исключения\"", template.Name, template.Id); return "NONE"; } logger.LogDebug("Для шаблона шаблона {templte}, {templateId} тип исключения \"{name}\", EsppValue: {esppValue}", template.Name, template.Id, template.Job.Group.ScheduleExcludeType.Title, template.Job.Group.ScheduleExcludeType.EsppValue); return template.Job.Group.ScheduleExcludeType.EsppValue; #region old logic //// Это костыль, нужно придумать как это хранить в БД. //switch (settingsFromDb.ScheduleExcludeType.ToLower()) //{ // case ("нет исключений"): // return "NONE"; // case ("выполнить только в указанном календаре"): // return "ONLY"; // default: // return ""; //} #endregion } /// /// Заполнить расписание из БД /// /// private void FillScheduleFromDb(Template template, ref EsppObjectSchedule esppObject) { using (var scope = serviceProvider.CreateScope()) { var esppSchTypeConfigService = scope.ServiceProvider.GetService(); if (esppSchTypeConfigService == null) throw new Exception($"Не найден сервис: {nameof(IEsppSchTypeConfigService)}"); var esppSchedule = esppSchTypeConfigService.GetEsppScheduleDto(template.Job!.GroupId); if (esppSchedule == null) { logger.LogError($"Не смог получить расписание из БД для шаблона templateId: {template.Id}, {template.Name}"); return; } var typeSchedule = GetTypeScheduleByString(esppSchedule.TypeSchedule.Name); if (!typeSchedule.HasValue) return; //тип повторения esppObject.TypeSchedule = typeSchedule.Value; //в зависимости от типа, присваиваем значения switch (esppObject.TypeSchedule) { case EsppSchTypeScheduleEnum.Regularly: esppObject.Interval = esppSchedule.Values.First(t => t.Order == 0).Value.EsppExportValue; break; case EsppSchTypeScheduleEnum.Weekly: esppObject.Dayofweek = esppSchedule.Values.First(t => t.Order == 0).Value.EsppExportValue; break; case EsppSchTypeScheduleEnum.Monthly: //если включено автораспределение, подставляем дату месяца из NextRun //if (template.ApplicationsInWork?.IsAutoDistributionEnabled == true)//TODO Migration to job //{ // var nextRunByRobotTimeZone = nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun); // esppObject.Dayofmonth = nextRunByRobotTimeZone.Day.ToString(); // // esppObject.Dayofmonth = template.NextRun.Day.ToString(); //} //else //{ esppObject.Dayofmonth = esppSchedule.Values.First(t => t.Order == 0).Value.EsppExportValue; //} break; case EsppSchTypeScheduleEnum.Monthly2: esppObject.Md1 = esppSchedule.Values.First(t => t.Order == 0).Value.EsppExportValue; esppObject.Md2 = esppSchedule.Values.First(t => t.Order == 1).Value.EsppExportValue; break; case EsppSchTypeScheduleEnum.Annually: esppObject.Annualm = esppSchedule.Values.First(t => t.Order == 0).Value.EsppExportValue; esppObject.Annualday = esppSchedule.Values.First(t => t.Order == 1).Value.EsppExportValue; break; case EsppSchTypeScheduleEnum.Annually2: esppObject.An1 = esppSchedule.Values.First(t => t.Order == 0).Value.EsppExportValue; esppObject.An2 = esppSchedule.Values.First(t => t.Order == 1).Value.EsppExportValue; esppObject.An3 = esppSchedule.Values.First(t => t.Order == 2).Value.EsppExportValue; break; } } } /// /// Парсинг из строки в модель для сравнения /// /// /// /// private EsppObjectSchedule? ParseStrToEsppObject(string str) { var splittedContent = str.Split(globalSettings.ParsingSeparator); if (splittedContent.Length != 24) { logger.LogError($"Входная строка после сплита не содержит 24 объекта (факт: {splittedContent.Length})."); return null; } var templateName = splittedContent[6].ToUpper(); var scheduleName = splittedContent[2]; if (!IsValidName(templateName) || !IsValidName(scheduleName)) return null; bool.TryParse(splittedContent[1].Trim(), out var isActive); var typeSchedule = GetTypeScheduleByString(splittedContent[7]); if (!typeSchedule.HasValue) return null; var esppObject = new EsppObjectSchedule { TemplateName = templateName, Code = splittedContent[0], ScheduleName = scheduleName, IsActive = isActive, ResponseArea = splittedContent[5], WorkGroup = splittedContent[3], TypeSchedule = typeSchedule.Value, Interval = splittedContent[8], Dayofweek = splittedContent[15], Dayofmonth = splittedContent[14], Md1 = splittedContent[16], Md2 = splittedContent[17], Annualm = splittedContent[13], Annualday = splittedContent[12], An1 = splittedContent[9], An2 = splittedContent[10], An3 = splittedContent[11], TypeV60calendar = splittedContent[21], Scheduled = splittedContent[4], Timezone = splittedContent[18], TerminationType = splittedContent[19], CompleteAfter = splittedContent[20], V60calendar = splittedContent[22], BasisTime = splittedContent[23] }; return ClearOptionalFields(esppObject); } /// /// Преобразовать из строки в Тип Повторения /// /// /// private EsppSchTypeScheduleEnum? GetTypeScheduleByString(string typeSchedule) { typeSchedule = typeSchedule.ToLower(); //Regularly, Регулярно (значение в ЕСПП и в БД не совпадают, в бд Regularly, в ЕСПП simple) if (typeSchedule == "simple" || typeSchedule == EsppSchTypeScheduleEnum.Regularly.ToString().ToLower()) return EsppSchTypeScheduleEnum.Regularly; if (typeSchedule == EsppSchTypeScheduleEnum.Weekly.ToString().ToLower()) return EsppSchTypeScheduleEnum.Weekly; if (typeSchedule == EsppSchTypeScheduleEnum.Monthly.ToString().ToLower()) return EsppSchTypeScheduleEnum.Monthly; if (typeSchedule == EsppSchTypeScheduleEnum.Monthly2.ToString().ToLower()) return EsppSchTypeScheduleEnum.Monthly2; if (typeSchedule == EsppSchTypeScheduleEnum.Annually.ToString().ToLower()) return EsppSchTypeScheduleEnum.Annually; if (typeSchedule == EsppSchTypeScheduleEnum.Annually2.ToString().ToLower()) return EsppSchTypeScheduleEnum.Annually2; logger.LogError($"Не смог преобразовать Тип повторения из ЕСПП в EsppSchTypeScheduleEnum. Получено значение {typeSchedule}"); return null; } /// /// Проверка имени шаблона и расписание на соответствие префиксу из настроек /// /// /// private bool IsValidName(string name) { var currentCulture = Thread.CurrentThread.CurrentCulture; //if (!string.IsNullOrEmpty(settingsFromDb.TemplatePrefixName) && name.StartsWith(settingsFromDb.TemplatePrefixName, true, currentCulture)) // return true; if (!string.IsNullOrEmpty(settingsFromDb.TemplatePrefixWithoutVariable) && name.Contains(settingsFromDb.TemplatePrefixWithoutVariable)) // , StringComparison.CurrentCultureIgnoreCase return true; logger.LogWarning($"Имя шаблона или расписания не соответствует обязательному префиксу({settingsFromDb.TemplatePrefixWithoutVariable}). {name} игнорирован"); return false; } /// /// Очистка полей которые не нуждаются в синхронизации /// /// /// private EsppObjectSchedule ClearOptionalFields(EsppObjectSchedule esppObject) { esppObject.Code = string.Empty; esppObject.ScheduleName = string.Empty; esppObject.ResponseArea = string.Empty; esppObject.WorkGroup = string.Empty; esppObject.CompleteAfter = string.Empty; //esppObject.V60calendar = string.Empty; // В ЕСПП, при изменении "Повторять задачу", остаются предыдущие значения, их не нужно синхронизировать (касается только данных полученных из ЕСПП, в БД все ок) // т.е. если стояло Ежедненвно:понедельник, а изменили например на Еженедельно..., то в ежедневно значения останутся, но будут отрабатывать значения из Еженедельно. // Т е значения из Ежедненвно проверять не нужно, вот их и будем очищать switch (esppObject.TypeSchedule) { case EsppSchTypeScheduleEnum.Regularly: //esppObject.Interval = string.Empty; esppObject.Dayofweek = string.Empty; esppObject.Dayofmonth = string.Empty; esppObject.Md1 = string.Empty; esppObject.Md2 = string.Empty; esppObject.Annualm = string.Empty; esppObject.Annualday = string.Empty; esppObject.An1 = string.Empty; esppObject.An2 = string.Empty; esppObject.An3 = string.Empty; break; case EsppSchTypeScheduleEnum.Weekly: esppObject.Interval = string.Empty; //esppObject.Dayofweek = string.Empty; esppObject.Dayofmonth = string.Empty; esppObject.Md1 = string.Empty; esppObject.Md2 = string.Empty; esppObject.Annualm = string.Empty; esppObject.Annualday = string.Empty; esppObject.An1 = string.Empty; esppObject.An2 = string.Empty; esppObject.An3 = string.Empty; break; case EsppSchTypeScheduleEnum.Monthly: esppObject.Interval = string.Empty; esppObject.Dayofweek = string.Empty; //esppObject.Dayofmonth = string.Empty; esppObject.Md1 = string.Empty; esppObject.Md2 = string.Empty; esppObject.Annualm = string.Empty; esppObject.Annualday = string.Empty; esppObject.An1 = string.Empty; esppObject.An2 = string.Empty; esppObject.An3 = string.Empty; break; case EsppSchTypeScheduleEnum.Monthly2: esppObject.Interval = string.Empty; esppObject.Dayofweek = string.Empty; esppObject.Dayofmonth = string.Empty; //esppObject.Md1 = string.Empty; //esppObject.Md2 = string.Empty; esppObject.Annualm = string.Empty; esppObject.Annualday = string.Empty; esppObject.An1 = string.Empty; esppObject.An2 = string.Empty; esppObject.An3 = string.Empty; break; case EsppSchTypeScheduleEnum.Annually: esppObject.Interval = string.Empty; esppObject.Dayofweek = string.Empty; esppObject.Dayofmonth = string.Empty; esppObject.Md1 = string.Empty; esppObject.Md2 = string.Empty; //esppObject.Annualm = string.Empty; //esppObject.Annualday = string.Empty; esppObject.An1 = string.Empty; esppObject.An2 = string.Empty; esppObject.An3 = string.Empty; break; case EsppSchTypeScheduleEnum.Annually2: esppObject.Interval = string.Empty; esppObject.Dayofweek = string.Empty; esppObject.Dayofmonth = string.Empty; esppObject.Md1 = string.Empty; esppObject.Md2 = string.Empty; esppObject.Annualm = string.Empty; esppObject.Annualday = string.Empty; //esppObject.An1 = string.Empty; //esppObject.An2 = string.Empty; //esppObject.An3 = string.Empty; break; } return esppObject; } } }