2026-01-19 11:46:25 +10:00
using Microsoft.EntityFrameworkCore ;
using Microsoft.Extensions.DependencyInjection ;
2023-11-27 12:06:16 +10:00
using Microsoft.Extensions.Logging ;
using PARR.BLL.Helpers ;
2023-11-22 16:34:16 +10:00
using PARR.BLL.Services.Interfaces ;
2026-02-18 11:40:25 +10:00
using PARR.Common.Domain ;
2026-02-03 12:08:26 +10:00
using PARR.Constants ;
2023-11-22 16:34:16 +10:00
using PARR.DAL.Contracts ;
using PARR.DAL.Models ;
2026-02-03 16:15:19 +10:00
using PARR.DAL.NextRunServices ;
2023-11-27 12:06:16 +10:00
using PARR.DAL.Services.Interfaces ;
2026-01-30 12:29:37 +10:00
using PARR.DAL.Services.Interfaces.Schedule ;
2026-03-03 10:19:59 +10:00
using PARR.EsppScheduleSync.Domain ;
2023-11-22 16:34:16 +10:00
using PARR.EsppScheduleSync.Settings ;
using PARR.EsppSync ;
2026-02-18 11:40:25 +10:00
using PARR.EsppSync.Helpers ;
2023-11-22 16:34:16 +10:00
namespace PARR.EsppScheduleSync
{
internal class ScheduleSyncher : IScheduleSyncher
{
private readonly ILogger < ScheduleSyncher > logger ;
private readonly GlobalSettings globalSettings ;
private readonly IMqService mqService ;
private readonly ISyncService < EsppObjectSchedule > syncService ;
private readonly SettingsFromDb settingsFromDb ;
2023-11-27 12:06:16 +10:00
private readonly IServiceProvider serviceProvider ;
2026-01-30 12:29:37 +10:00
private string noneExcludeCalendarEsppValue ;
2023-11-22 16:34:16 +10:00
public ScheduleSyncher (
ILogger < ScheduleSyncher > logger ,
GlobalSettings globalSettings ,
IMqService mqService ,
ISyncService < EsppObjectSchedule > syncService ,
2023-11-27 12:06:16 +10:00
SettingsFromDb settingsFromDb ,
2026-02-18 11:40:25 +10:00
IServiceProvider serviceProvider
2023-11-22 16:34:16 +10:00
)
{
this . logger = logger ;
this . globalSettings = globalSettings ;
this . mqService = mqService ;
this . syncService = syncService ;
this . settingsFromDb = settingsFromDb ;
2023-11-27 12:06:16 +10:00
this . serviceProvider = serviceProvider ;
2026-01-30 12:29:37 +10:00
this . noneExcludeCalendarEsppValue = string . Empty ;
2023-11-22 16:34:16 +10:00
if ( globalSettings . MqSettings = = null )
{
logger . LogError ( "Нет секции настроек хранилища. MqSettings, EsppTemplates" ) ;
throw new Exception ( "Нет секции настроек хранилища. MqSettings, EsppTemplates" ) ;
}
2026-01-30 12:29:37 +10:00
if ( string . IsNullOrEmpty ( globalSettings . ParsingSeparator ) )
throw new ArgumentException ( "ParsingSeparator не задан в GlobalSettings." ) ;
2023-11-22 16:34:16 +10:00
}
2025-08-20 14:10:22 +10:00
public async Task StartAsync ( )
2023-11-22 16:34:16 +10:00
{
2026-01-30 12:29:37 +10:00
if ( string . IsNullOrEmpty ( noneExcludeCalendarEsppValue ) )
await GetNoneExcludeCalendarEsppValueAsync ( ) ;
2026-02-17 16:27:10 +10:00
2026-01-30 12:29:37 +10:00
2025-08-20 14:10:22 +10:00
var isConnected = await mqService . InitConsumerAsync ( globalSettings ! . MqSettings ! , SyncScheduleAsync ) ;
2023-11-22 16:34:16 +10:00
if ( ! isConnected )
throw new Exception ( "Ошибка при подключении к RabbitMq" ) ;
2026-01-26 17:16:56 +10:00
logger . LogInformation ( "Запущена проверка очереди {QueueName}." , globalSettings . MqSettings ! . QueueName ) ;
2023-11-22 16:34:16 +10:00
}
2026-01-30 12:29:37 +10:00
2026-02-18 11:40:25 +10:00
public async Task StopAsync ( )
{
await mqService . DisposeAsync ( ) ;
logger . LogInformation ( "=== === === Соединение с очередью {QueueName} закрыто === === ===" , globalSettings . MqSettings ! . QueueName ) ;
}
2026-01-30 12:29:37 +10:00
2026-02-18 11:40:25 +10:00
/// <summary>
/// Получение типа исключения
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
2026-01-30 12:29:37 +10:00
private async Task GetNoneExcludeCalendarEsppValueAsync ( )
{
using var scope = serviceProvider . CreateScope ( ) ;
var service = scope . ServiceProvider . GetRequiredService < IScheduleExcludeTypeService > ( ) ;
var noneExcludeType = await service . Get ( ) . AsNoTracking ( ) . FirstOrDefaultAsync ( t = > t . Code = = nameof ( ScheduleExcludeTypeEnum . None ) ) ;
if ( noneExcludeType = = null )
throw new InvalidOperationException ( $"Н е найден тип исключения с кодом '{nameof(ScheduleExcludeTypeEnum.None)}'" ) ;
noneExcludeCalendarEsppValue = noneExcludeType . EsppValue ;
}
2023-11-22 16:34:16 +10:00
2026-02-18 11:40:25 +10:00
/// <summary>
/// Старт синхронизации, получили сообщение
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private async Task SyncScheduleAsync ( string str )
{
await syncService . SyncEsppObjectAsync ( str , ParseStrToEsppObject , ConvertDbObjToEsppObj , CustomComparisionCheckAsyncHandler , AfterParseStringToEsppObjectAsyncHandler ) ;
2023-11-22 16:34:16 +10:00
}
2026-02-18 11:40:25 +10:00
/// <summary>
/// Выполняется после парсинга строки в объект ЕСПП.
/// Проверка, есть ли у расписания шаблон. Если имена шаблона и расписания не совпадают, установить шаблону статус обновления.
/// </summary>
/// <param name="esppObject"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
private async Task AfterParseStringToEsppObjectAsyncHandler ( EsppObjectSchedule esppObject )
2023-11-22 16:34:16 +10:00
{
2026-02-18 11:40:25 +10:00
var esppScheduleId = esppObject . Code ;
var esppTemplateName = esppObject . TemplateName ;
2026-01-19 11:46:25 +10:00
2026-02-18 11:40:25 +10:00
using var scope = serviceProvider . CreateScope ( ) ;
var templateService = scope . ServiceProvider . GetRequiredService < ITemplateService > ( ) ;
2026-01-19 11:46:25 +10:00
2026-03-03 10:19:59 +10:00
// это не очень, так как esppScheduleId может быть null или empty, а в БД таких значений может быть много
//var candidates = await templateService.Get()
// .Include(t => t.RobotConfigurations)
// .Where(t => t.Name == esppTemplateName || t.ScheduleEsppId == esppScheduleId)
// .ToListAsync();
IQueryable < Template > query = templateService . Get ( ) . Include ( t = > t . RobotConfigurations ) ;
if ( string . IsNullOrEmpty ( esppScheduleId ) )
{
query = query . Where ( t = > t . Name = = esppTemplateName ) ;
}
else
{
//ScheduleEsppId не нулл и не пустой, поизем по нему тоже
query = query . Where ( t = > t . Name = = esppTemplateName | | t . ScheduleEsppId = = esppScheduleId ) ;
}
var candidates = await query . ToListAsync ( ) ;
2026-01-19 11:46:25 +10:00
2026-02-18 11:40:25 +10:00
var templateByName = candidates . FirstOrDefault ( t = > t . Name = = esppTemplateName ) ;
var templateByScheduleId = candidates . FirstOrDefault ( t = > t . ScheduleEsppId = = esppScheduleId ) ;
2026-01-19 11:46:25 +10:00
2026-02-18 11:40:25 +10:00
if ( templateByName = = null & & templateByScheduleId = = null )
{
// Нет ни по имени, ни по ID
logger . LogWarning ( "Расписание из ЕСПП не привязано ни к одному шаблону: TemplateName='{TemplateName}', ScheduleEsppId='{EsppId}'" , esppTemplateName , esppScheduleId ) ;
}
else if ( templateByName = = null & & templateByScheduleId ! = null )
{
// Есть только по ID → имя не совпадает
logger . LogWarning ( "Расхождение привязки: расписание из ЕСПП с ScheduleEsppId='{EsppId}' и TemplateName='{TemplateName}' соответствует шаблону в БД с именем '{DbTemplateName}', Id='{TemplateId}'." ,
esppScheduleId , esppTemplateName , templateByScheduleId . Name , templateByScheduleId . Id ) ;
2026-01-19 11:46:25 +10:00
2026-02-18 11:40:25 +10:00
// Принудительно выставляем статус обновления шаблона, так как имя в ЕСПП изменилось
var robotConfigurationService = scope . ServiceProvider . GetRequiredService < IRobotConfigurationService > ( ) ;
var config = robotConfigurationService . GetFromTemplateByRobotCode ( RobotsEnum . TemplateOrder , templateByScheduleId ) ;
2026-01-19 11:46:25 +10:00
2026-02-18 11:40:25 +10:00
logger . LogDebug ( "Текущий статус задания {taskStatusCode} шаблона {templateId}" , config . TaskStatusCode , templateByScheduleId . Id ) ;
robotConfigurationService . ChangeTaskStatus ( TaskStatusEnum . Updating , config ) ;
2026-01-19 11:46:25 +10:00
2026-02-18 11:40:25 +10:00
// Сохраняем изменения в базу данных
if ( ! await templateService . CommitAsync ( new HistoryInitiator { InitiatorParrComponentId = ParrComponentsEnum . EsppScheduleSync , InitiatorComment = "Расхождение привязки, расписание из ЕСПП не соответствует имени шаблона в БД" } ) )
2026-01-19 11:46:25 +10:00
{
2026-02-18 11:40:25 +10:00
logger . LogError ( "Н е удалось сохранить изменения статуса задачи для шаблона Id='{TemplateId}'" , templateByScheduleId . Id ) ;
2026-01-19 11:46:25 +10:00
}
2026-02-18 11:40:25 +10:00
else
2026-01-19 11:46:25 +10:00
{
2026-02-18 11:40:25 +10:00
logger . LogDebug ( "Для шаблона {templateId} установлен статус задания {taskStatus} при обнаружении расхождения имени с ЕСПП" ,
templateByScheduleId . Id , TaskStatusEnum . Updating . ToString ( ) ) ;
2026-01-30 12:29:37 +10:00
}
2026-02-18 11:40:25 +10:00
}
else if ( templateByName ! = null )
{
var dbScheduleId = templateByName . ScheduleEsppId ? ? string . Empty ;
if ( string . IsNullOrEmpty ( dbScheduleId ) )
{
// Утерян ID в БД
logger . LogWarning ( "У шаблона '{TemplateName}' (Id='{TemplateId}') отсутствует ScheduleEsppId в БД, но в ЕСПП он равен '{EsppId}'" , esppTemplateName , templateByName . Id , esppScheduleId ) ;
}
else if ( dbScheduleId ! = esppScheduleId )
2026-01-30 12:29:37 +10:00
{
2026-02-18 11:40:25 +10:00
// ID не совпадают — проверяем, не занят ли esppScheduleId другим шаблоном
var conflictingTemplate = candidates . FirstOrDefault ( t = > t . Id ! = templateByName . Id & & t . ScheduleEsppId = = esppScheduleId ) ;
2026-01-19 11:46:25 +10:00
2026-02-18 11:40:25 +10:00
if ( conflictingTemplate ! = null )
2026-01-19 11:46:25 +10:00
{
2026-02-18 11:40:25 +10:00
logger . LogWarning ( "Конфликт ScheduleEsppId: расписание '{EsppId}' из ЕСПП с именем '{TemplateName}' уже привязано к другому шаблону '{OtherTemplateName}' (Id='{OtherTemplateId}') в БД." ,
esppScheduleId , esppTemplateName , conflictingTemplate . Name , conflictingTemplate . Id ) ;
2026-01-19 11:46:25 +10:00
}
2026-02-18 11:40:25 +10:00
else
2026-01-30 12:29:37 +10:00
{
2026-02-18 11:40:25 +10:00
logger . LogWarning ( "Несовпадение ScheduleEsppId для шаблона '{TemplateName}' (Id='{TemplateId}'): в БД='{DbId}', в ЕСПП='{EsppId}'" ,
esppTemplateName , templateByName . Id , dbScheduleId , esppScheduleId ) ;
2026-01-30 12:29:37 +10:00
}
2026-01-19 11:46:25 +10:00
}
}
2023-11-22 16:34:16 +10:00
}
/// <summary>
/// Преобразование модели БД в модель для сравнения
/// </summary>
/// <param name="template"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
2025-08-06 10:33:55 +10:00
private EsppObjectSchedule ConvertDbObjToEsppObj ( Template template )
2023-11-22 16:34:16 +10:00
{
2026-02-17 16:27:10 +10:00
//дефолтное значение, nextRun в часовом поясе робота ЕСПП
var nextRunWithEsppTz = DateTimeOffset . MinValue ;
//// дефолтное значение, изменится в ApplyShortcodesAsync
//var responseArea = "%З О _Р Г%";
2025-08-20 14:10:22 +10:00
using ( var scope = serviceProvider . CreateScope ( ) )
{
2026-02-03 16:15:19 +10:00
//var nextRunModifierService = scope.ServiceProvider.GetService<INextRunModifierService>();
//if (nextRunModifierService == null)
// throw new Exception($"Н е найден сервис: {nameof(INextRunModifierService)}");
//nextRunWithTimeZone = nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun);
2026-03-05 10:19:54 +10:00
var nextRunService = scope . ServiceProvider . GetRequiredService < INextRunService > ( ) ;
2026-02-17 16:27:10 +10:00
//var shortcodeService = scope.ServiceProvider.GetRequiredService<IShortcodesService>();
2025-08-20 14:10:22 +10:00
2026-02-17 16:27:10 +10:00
//responseArea = shortcodeService.ApplyShortcodesAsync(template.Job!.ResponseAreaMask, template).GetAwaiter().GetResult();
nextRunWithEsppTz = template . NextRun . Add ( nextRunService . GetEsppAccountOffset ( ) ) ;
2025-08-20 14:10:22 +10:00
}
2023-11-27 12:06:16 +10:00
var esppObjectFromDb = new EsppObjectSchedule
{
2025-11-28 15:32:06 +10:00
TemplateName = template . Name . ToUpper ( ) ,
2023-11-27 12:06:16 +10:00
Code = template . ScheduleEsppId ? ? "" ,
ScheduleName = template . Name ,
IsActive = template . IsActiveSchedule ,
2025-06-25 14:17:34 +10:00
//ResponseArea = template.Host!.ResponseArea!.Name,//TODO Migration to job
2026-01-26 17:16:56 +10:00
//WorkGroup = template.Host!.WorkGroup!,//TODO Migration to job
2025-06-25 14:17:34 +10:00
//WorkGroup = template.Host!.WorkGroup!.Name,//TODO Migration to job
2023-11-27 12:06:16 +10:00
//Мы решили, что для всех расписаний "Нет исключений", если что-то поменяется, тут нужно переделать
2025-12-04 13:55:03 +10:00
//TypeV60calendar = settingsFromDb.ScheduleExcludeType == "Нет исключений" ? "NONE" : "",
2025-12-25 17:01:42 +10:00
TypeV60calendar = GetTypeV60calendar ( template ) ,
V60calendar = GetV60calendar ( template ) ,
2024-08-06 11:06:49 +10:00
//Scheduled = EsppScheduleHelpers.GetNextRun(template.NextRun),
2025-08-20 14:10:22 +10:00
//Scheduled = EsppScheduleHelpers.GetNextRun(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)),
//BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)),
2026-02-17 16:27:10 +10:00
Scheduled = EsppScheduleHelpers . GetNextRun ( nextRunWithEsppTz ) ,
BasisTime = EsppScheduleHelpers . GetGenerationTime ( nextRunWithEsppTz ) ,
Timezone = GetTimezone ( /*template, responseArea*/ ) ,
2023-11-27 12:06:16 +10:00
//Мы решили, что для всех расписаний "Отсутствует дата завершения", если что-то поменяется, тут нужно переделать
TerminationType = settingsFromDb . ScheduleRepeatRange = = "Отсутствует дата завершения" ? "forever" : "" ,
2025-12-04 13:55:03 +10:00
CompleteAfter = ""
2023-11-27 12:06:16 +10:00
} ;
FillScheduleFromDb ( template , ref esppObjectFromDb ) ;
return ClearOptionalFields ( esppObjectFromDb ) ;
}
2026-02-18 11:40:25 +10:00
/// <summary>
/// Кастомная дополнительная проверка полей
/// </summary>
/// <param name="esppObject"></param>
/// <param name="dbObject"></param>
/// <param name="templateId"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
private async Task < bool > CustomComparisionCheckAsyncHandler ( EsppObjectSchedule esppObject , EsppObjectSchedule dbObject , Guid templateId )
{
// рассчитать nextRun, и сравнить все три nextRun, БД - ЕСПП - Расчитанное
// в часовой зоне робота
var calculatedNextRun = await CalcNextRunWithRobotTzAsync ( templateId ) ;
if ( calculatedNextRun = = null )
{
return false ;
}
var scheduledCalculated = EsppScheduleHelpers . GetNextRun ( calculatedNextRun . Value ) ;
var basisTimeCalculated = EsppScheduleHelpers . GetGenerationTime ( calculatedNextRun . Value ) ;
logger . LogDebug ( "Рассчитанные значения для шаблона '{templateName}', {templateId}, следующее срабатывание {scheduledCalculated}, время создания наряда: {basisTimeCalculated}" ,
dbObject . TemplateName , templateId , scheduledCalculated , basisTimeCalculated ) ;
2026-02-27 09:33:27 +10:00
var dbScheduledNormalized = EsppSyncHelpers . Normalize ( dbObject . Scheduled ) ;
var esppScheduledNormalized = EsppSyncHelpers . Normalize ( esppObject . Scheduled ) ;
var scheduleCalculatedNormalized = EsppSyncHelpers . Normalize ( scheduledCalculated ) ;
//if (EsppSyncHelpers.Normalize(dbObject.Scheduled) != EsppSyncHelpers.Normalize(esppObject.Scheduled) || EsppSyncHelpers.Normalize(dbObject.Scheduled) != scheduledCalculated || EsppSyncHelpers.Normalize(esppObject.Scheduled) != scheduledCalculated)
if ( dbScheduledNormalized ! = esppScheduledNormalized | | dbScheduledNormalized ! = scheduleCalculatedNormalized | | esppScheduledNormalized ! = scheduleCalculatedNormalized )
2026-02-18 11:40:25 +10:00
{
2026-02-27 09:33:27 +10:00
logger . LogInformation ( "Н е совпадают поля ({PropertyName}), dbValueStr: {dbValueStr}, esppValueStr: {esppValueStr}, scheduledCalculated: {scheduledCalculated}. Имя шаблона: {templateName}" ,
2026-02-18 11:40:25 +10:00
nameof ( dbObject . Scheduled ) , dbObject . Scheduled , esppObject . Scheduled , scheduledCalculated , dbObject . TemplateName ) ;
return false ;
}
2026-02-27 09:33:27 +10:00
var dbBasisTimeNormalized = EsppSyncHelpers . Normalize ( dbObject . BasisTime ) ;
var esppBasisTimeNormalized = EsppSyncHelpers . Normalize ( esppObject . BasisTime ) ;
var calculatedBasisTimeNormalized = EsppSyncHelpers . Normalize ( basisTimeCalculated ) ;
//if (EsppSyncHelpers.Normalize(dbObject.BasisTime) != EsppSyncHelpers.Normalize(esppObject.BasisTime) || EsppSyncHelpers.Normalize(dbObject.BasisTime) != basisTimeCalculated || EsppSyncHelpers.Normalize(esppObject.BasisTime) != basisTimeCalculated)
if ( dbBasisTimeNormalized ! = esppBasisTimeNormalized | | dbBasisTimeNormalized ! = calculatedBasisTimeNormalized | | esppBasisTimeNormalized ! = calculatedBasisTimeNormalized )
2026-02-18 11:40:25 +10:00
{
2026-02-27 09:33:27 +10:00
logger . LogInformation ( "Н е совпадают поля ({PropertyName}), dbValueStr: {dbValueStr}, esppValueStr: {esppValueStr}, basisTimeCalculated: {basisTimeCalculated}. Имя шаблона: {templateName}" ,
2026-02-18 11:40:25 +10:00
nameof ( dbObject . BasisTime ) , dbObject . BasisTime , esppObject . BasisTime , basisTimeCalculated , dbObject . TemplateName ) ;
return false ;
}
logger . LogDebug ( "Значения nextRun в БД, ЕСПП, расчитанное, все совпадают. Scheduled: {scheduled}, basisTime: {basisTime}" , scheduledCalculated , basisTimeCalculated ) ;
return true ;
}
/// <summary>
/// Рассчитать nextRun в часовом поясе У З робота
/// </summary>
/// <param name="templateId"></param>
/// <returns></returns>
private async Task < DateTimeOffset ? > CalcNextRunWithRobotTzAsync ( Guid templateId )
{
using var scope = serviceProvider . CreateScope ( ) ;
2026-03-05 10:19:54 +10:00
var nextRunService = scope . ServiceProvider . GetRequiredService < INextRunService > ( ) ;
2026-02-18 11:40:25 +10:00
var nextRun = await nextRunService . GetNextRunForTemplateAsync ( templateId , isNew : false ) ;
if ( nextRun . HasValue )
{
var nextRunWithEsppAccountTz = nextRun . Value . Add ( nextRunService . GetEsppAccountOffset ( ) ) ;
logger . LogDebug ( "Расчитанный nextRun для шаблона {templateId}, UTC: {nextRun}, EsppAccountTz: {nextRunWithEsppAccountTz}" , templateId , nextRun , nextRunWithEsppAccountTz ) ;
return nextRunWithEsppAccountTz ;
}
else
{
logger . LogError ( "При расчете nextRun для templateId: {templateId} верунлся null" , templateId ) ;
return null ;
}
}
/// <summary>
/// Получить "В каком часовом поясе"
/// </summary>
/// <returns></returns>
2026-02-17 16:27:10 +10:00
private string GetTimezone ( /*Template template, string responseArea*/ )
2026-01-30 12:29:37 +10:00
{
2026-02-17 16:27:10 +10:00
// договорились, что у роботоа Т З М С К
return settingsFromDb . EsppScheduleTimezone ;
2026-01-30 12:29:37 +10:00
2026-02-17 16:27:10 +10:00
#region old
////if (template.Job?.Group?.IsWorkGroupTimezone != true)
//// return settingsFromDb.ScheduleTimezone;
2026-01-30 12:29:37 +10:00
2026-02-17 16:27:10 +10:00
//if (template.Job?.Group?.IsResponseAreaTimezone != true)
// return scheduleResponseAreaTimeOffsetService.GetDefault.EsppValue;
2026-01-30 12:29:37 +10:00
2026-02-17 16:27:10 +10:00
////var responseArea = template.Unit?.BaseFields?.ResponseArea;
2026-01-30 12:29:37 +10:00
2026-02-17 16:27:10 +10:00
////if (string.IsNullOrEmpty(responseArea))
////{
//// throw new InvalidOperationException(
//// $"У шаблона Id={template.Id}, Name='{template.Name}' не задана ResponseArea в Unit.BaseFields, " +
//// "но включена настройка 'использовать часовой пояс рабочей группы'.");
////}
2026-02-03 16:15:19 +10:00
2026-02-17 16:27:10 +10:00
////if (!responseAreaTimeOffsetDict.TryGetValue(responseArea, out var offset))
////{
//// throw new InvalidOperationException(
//// $"Н е найдено временное смещение для ResponseArea '{responseArea}' у шаблона Id={template.Id}, Name='{template.Name}'. " +
//// "Проверьте наличие записи в таблице ScheduleResponseAreaTimeOffset.");
////}
2026-02-03 16:15:19 +10:00
2026-02-17 16:27:10 +10:00
////return offset;
2026-01-30 12:29:37 +10:00
2026-02-17 16:27:10 +10:00
//return scheduleResponseAreaTimeOffsetService.GetByResponseAreaOrDefault(responseArea).EsppValue;
#endregion
2026-01-30 12:29:37 +10:00
}
2023-11-27 12:06:16 +10:00
2025-12-25 17:01:42 +10:00
/// <summary>
/// Получить исключение - Календарь
/// </summary>
/// <param name="template"></param>
/// <returns></returns>
private string GetV60calendar ( Template template )
{
// мы знаем, что у нас точно в шаблоне есть инклуды до ScheduleExcludeType и ScheduleExcludeTypeCalendar
if ( template . Job ? . Group ? . ScheduleExcludeTypeCalendar = = null )
{
2026-01-26 17:16:56 +10:00
logger . LogDebug ( "Для шаблона шаблона {TemplateName}, {TemplateId} нет исключений календаря" , template . Name , template . Id ) ;
2025-12-25 17:01:42 +10:00
//TODO:!!!!!!!!!!! Вот тут null или string.Empty??? Спросить у Андрея что он нам вернет!
return string . Empty ;
}
2026-01-26 17:16:56 +10:00
logger . LogDebug ( "Для шаблона шаблона {TemplateName}, {TemplateId} установлено исключений календаря \"{Name}\", EsppValue: {EsppValue}" , template . Name , template . Id , template . Job . Group . ScheduleExcludeTypeCalendar . Title , template . Job . Group . ScheduleExcludeTypeCalendar . EsppValue ) ;
2025-12-25 17:01:42 +10:00
return template . Job . Group . ScheduleExcludeTypeCalendar . EsppValue ;
}
2025-12-04 13:55:03 +10:00
/// <summary>
/// Получить тип календаря
/// </summary>
/// <returns></returns>
2025-12-25 17:01:42 +10:00
private string GetTypeV60calendar ( Template template )
2025-12-04 13:55:03 +10:00
{
2025-12-25 17:01:42 +10:00
// мы знаем, что у нас точно в шаблоне есть инклуды до ScheduleExcludeType и ScheduleExcludeTypeCalendar
// на всякий конечно же проверим
if ( template . Job ? . Group ? . ScheduleExcludeType = = null )
2025-12-04 13:55:03 +10:00
{
2026-01-26 17:16:56 +10:00
logger . LogError ( "Для шаблона {TemplateName}, {TemplateId} не смог получить тип исключения, установил значение по умолчанию \"Без исключения\"" , template . Name , template . Id ) ;
2025-12-25 17:01:42 +10:00
return "NONE" ;
2025-12-04 13:55:03 +10:00
}
2025-12-25 17:01:42 +10:00
2026-01-26 17:16:56 +10:00
logger . LogDebug ( "Для шаблона шаблона {TemplateName}, {TemplateId} тип исключения \"{Name}\", EsppValue: {EsppValue}" , template . Name , template . Id , template . Job . Group . ScheduleExcludeType . Title , template . Job . Group . ScheduleExcludeType . EsppValue ) ;
2025-12-25 17:01:42 +10:00
return template . Job . Group . ScheduleExcludeType . EsppValue ;
2025-12-04 13:55:03 +10:00
}
2023-11-27 12:06:16 +10:00
/// <summary>
/// Заполнить расписание из БД
/// </summary>
/// <returns></returns>
private void FillScheduleFromDb ( Template template , ref EsppObjectSchedule esppObject )
{
using ( var scope = serviceProvider . CreateScope ( ) )
{
var esppSchTypeConfigService = scope . ServiceProvider . GetService < IEsppSchTypeConfigService > ( ) ;
if ( esppSchTypeConfigService = = null )
throw new Exception ( $"Н е найден сервис: {nameof(IEsppSchTypeConfigService)}" ) ;
2025-08-21 15:23:46 +10:00
var esppSchedule = esppSchTypeConfigService . GetEsppScheduleDto ( template . Job ! . GroupId ) ;
2023-11-27 12:06:16 +10:00
if ( esppSchedule = = null )
{
2026-01-26 17:16:56 +10:00
logger . LogError ( "Н е смог получить расписание из БД для шаблона templateId: {TemplateId}, {TemplateName}" , template . Id , template . Name ) ;
2023-11-27 12:06:16 +10:00
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 :
2025-08-20 14:10:22 +10:00
esppObject . Dayofmonth = esppSchedule . Values . First ( t = > t . Order = = 0 ) . Value . EsppExportValue ;
2023-11-27 12:06:16 +10:00
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 ;
}
}
2023-11-22 16:34:16 +10:00
}
/// <summary>
/// Парсинг из строки в модель для сравнения
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
private EsppObjectSchedule ? ParseStrToEsppObject ( string str )
{
2023-11-24 16:52:56 +10:00
var splittedContent = str . Split ( globalSettings . ParsingSeparator ) ;
2024-08-08 11:25:35 +10:00
if ( splittedContent . Length ! = 24 )
2023-11-24 16:52:56 +10:00
{
2026-01-26 17:16:56 +10:00
logger . LogError ( "Входная строка после сплита не содержит 24 объекта (факт: {Length})." , splittedContent . Length ) ;
2023-11-24 16:52:56 +10:00
return null ;
}
2025-11-28 15:32:06 +10:00
var templateName = splittedContent [ 6 ] . ToUpper ( ) ;
2023-11-24 16:52:56 +10:00
var scheduleName = splittedContent [ 2 ] ;
if ( ! IsValidName ( templateName ) | | ! IsValidName ( scheduleName ) )
return null ;
bool . TryParse ( splittedContent [ 1 ] . Trim ( ) , out var isActive ) ;
2023-11-27 09:27:20 +10:00
var typeSchedule = GetTypeScheduleByString ( splittedContent [ 7 ] ) ;
if ( ! typeSchedule . HasValue )
return null ;
2023-11-24 16:52:56 +10:00
var esppObject = new EsppObjectSchedule
{
TemplateName = templateName ,
Code = splittedContent [ 0 ] ,
ScheduleName = scheduleName ,
IsActive = isActive ,
ResponseArea = splittedContent [ 5 ] ,
WorkGroup = splittedContent [ 3 ] ,
2023-11-27 09:27:20 +10:00
TypeSchedule = typeSchedule . Value ,
2023-11-24 16:52:56 +10:00
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 ] ,
2024-08-08 11:25:35 +10:00
V60calendar = splittedContent [ 22 ] ,
BasisTime = splittedContent [ 23 ]
2023-11-24 16:52:56 +10:00
} ;
return ClearOptionalFields ( esppObject ) ;
2023-11-22 16:34:16 +10:00
}
2023-11-24 16:52:56 +10:00
2023-11-27 09:27:20 +10:00
/// <summary>
/// Преобразовать из строки в Тип Повторения
/// </summary>
/// <param name="typeSchedule"></param>
/// <returns></returns>
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 ;
2026-01-26 17:16:56 +10:00
logger . LogError ( "Н е смог преобразовать Тип повторения из ЕСПП в EsppSchTypeScheduleEnum. Получено значение {TypeSchedule}" , typeSchedule ) ;
2023-11-27 09:27:20 +10:00
return null ;
}
2023-11-24 16:52:56 +10:00
/// <summary>
/// Проверка имени шаблона и расписание на соответствие префиксу из настроек
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
private bool IsValidName ( string name )
{
var currentCulture = Thread . CurrentThread . CurrentCulture ;
2024-02-15 09:21:01 +10:00
//if (!string.IsNullOrEmpty(settingsFromDb.TemplatePrefixName) && name.StartsWith(settingsFromDb.TemplatePrefixName, true, currentCulture))
// return true;
2024-02-15 13:47:34 +10:00
if ( ! string . IsNullOrEmpty ( settingsFromDb . TemplatePrefixWithoutVariable ) & & name . Contains ( settingsFromDb . TemplatePrefixWithoutVariable ) ) // , StringComparison.CurrentCultureIgnoreCase
2023-11-24 16:52:56 +10:00
return true ;
2026-01-26 17:16:56 +10:00
logger . LogWarning ( "Имя шаблона или расписания не соответствует обязательному префиксу({Prefix}). {Name} игнорирован" , settingsFromDb . TemplatePrefixWithoutVariable , name ) ;
2023-11-24 16:52:56 +10:00
return false ;
}
/// <summary>
/// Очистка полей которые не нуждаются в синхронизации
/// </summary>
/// <param name="esppObject"></param>
/// <returns></returns>
private EsppObjectSchedule ClearOptionalFields ( EsppObjectSchedule esppObject )
{
2026-03-03 10:19:59 +10:00
#region О т к л ю ч е н и е с и н х р о н и з а ц и и н е н у ж н ы х п о л е й н а с т р а и в а е т с я ч е р е з а т р и б у т SkipComparison в м о д е л е
// Обнуляем ЕСПП ИД, потому что У Н А С РЕАЛЬНЫЙ ЕСПП ИД И ЕСПП ИД В БД МОГУ РАСХОДИТЬСЯ!!! УЖАССС!!!
//esppObject.Code = string.Empty;
//esppObject.ScheduleName = string.Empty;
//esppObject.ResponseArea = string.Empty;
//esppObject.WorkGroup = string.Empty;
//esppObject.CompleteAfter = string.Empty;
#endregion
2026-01-30 12:29:37 +10:00
if ( esppObject . TypeV60calendar = = noneExcludeCalendarEsppValue )
esppObject . V60calendar = string . Empty ;
2023-11-24 16:52:56 +10:00
// В ЕСПП, при изменении "Повторять задачу", остаются предыдущие значения, их не нужно синхронизировать (касается только данных полученных из ЕСПП, в БД все ок)
// т.е . если стояло Ежедненвно:понедельник, а изменили например на Еженедельно..., то в ежедневно значения останутся, но будут отрабатывать значения из Еженедельно.
// Т е значения из Ежедненвно проверять не нужно, вот их и будем очищать
2023-11-27 09:27:20 +10:00
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 ;
}
2023-11-24 16:52:56 +10:00
return esppObject ;
}
2023-11-22 16:34:16 +10:00
}
2026-01-26 17:16:56 +10:00
}