2025-12-23 18:27:31 +10:00
using Microsoft.EntityFrameworkCore ;
using Microsoft.Extensions.DependencyInjection ;
2023-11-22 11:37:22 +10:00
using Microsoft.Extensions.Logging ;
2023-11-22 12:04:33 +10:00
using PARR.Constants ;
2023-11-22 11:37:22 +10:00
using PARR.DAL.Contracts ;
2025-12-23 18:27:31 +10:00
using PARR.DAL.DomainServices.Shortcodes ;
using PARR.DAL.DomainServices.Shortcodes.Models ;
2023-11-22 11:37:22 +10:00
using PARR.DAL.Models ;
using PARR.DAL.Services.Interfaces ;
2025-08-06 10:33:55 +10:00
using System.Reflection ;
2023-11-22 11:37:22 +10:00
namespace PARR.EsppSync
{
internal class SyncService < EsppObject > : ISyncService < EsppObject > where EsppObject : class , IEsppObject
{
private readonly ILogger < SyncService < EsppObject > > logger ;
2025-08-20 14:10:22 +10:00
private readonly IServiceProvider serviceProvider ;
2023-11-22 11:37:22 +10:00
public SyncService (
ILogger < SyncService < EsppObject > > logger ,
2025-08-20 14:10:22 +10:00
IServiceProvider serviceProvider
2023-11-22 11:37:22 +10:00
)
{
this . logger = logger ;
2025-08-20 14:10:22 +10:00
this . serviceProvider = serviceProvider ;
2023-11-22 11:37:22 +10:00
}
public async Task SyncEsppObjectAsync (
string str ,
ParserHandlerDelegate < EsppObject > parser ,
2023-11-22 12:04:33 +10:00
ConvertDbObjToComparisonObjHandlerDelegate < EsppObject > converterToEsppObject
2023-11-22 11:37:22 +10:00
)
{
logger . LogDebug ( $"Получил строку. Начинаю работать. Строка: {str}" ) ;
if ( string . IsNullOrEmpty ( str ) )
{
logger . LogWarning ( "Получил пустую строку, ничего не делаю." ) ;
return ;
}
var esppObject = parser . Invoke ( str ) ;
if ( esppObject = = null )
{
logger . LogWarning ( "После парсинга строки, esppObject = null. Дальше ничего не буду делать." ) ;
return ;
}
2025-08-20 14:10:22 +10:00
using ( var scope = serviceProvider . CreateScope ( ) )
2023-11-22 11:37:22 +10:00
{
2025-08-20 14:10:22 +10:00
var templateService = GetServiceInScope < ITemplateService > ( scope ) ;
var robotConfigurationService = GetServiceInScope < IRobotConfigurationService > ( scope ) ;
var shortcodesService = GetServiceInScope < IShortcodesService > ( scope ) ;
2023-11-22 11:37:22 +10:00
2025-08-20 14:10:22 +10:00
try
2023-11-22 11:37:22 +10:00
{
2025-12-23 18:27:31 +10:00
// Загружаем Template и TemplateForShortcodes в одном запросе
var query = templateService . Get ( )
. AsNoTracking ( )
2025-12-29 19:21:51 +10:00
. Include ( h = > h . Unit )
. ThenInclude ( t = > t ! . UnitValues )
. ThenInclude ( t = > t . Value )
. Include ( h = > h . Unit )
. ThenInclude ( t = > t ! . UnitValues )
. ThenInclude ( t = > t . Field )
. Include ( t = > t . RobotConfigurations )
2025-12-23 18:27:31 +10:00
. Include ( t = > t . Job )
. ThenInclude ( j = > j . Group )
. ThenInclude ( g = > g . GroupType )
2025-12-25 17:01:42 +10:00
. Include ( t = > t . Job )
. ThenInclude ( t = > t . Group )
. ThenInclude ( t = > t . ScheduleExcludeType )
. Include ( t = > t . Job )
. ThenInclude ( t = > t . Group )
. ThenInclude ( t = > t . ScheduleExcludeTypeCalendar )
2025-12-23 18:27:31 +10:00
. Include ( t = > t . Job )
. ThenInclude ( j = > j . Tnk )
2025-12-29 19:21:51 +10:00
. ThenInclude ( s = > s ! . Subprocess )
. ThenInclude ( p = > p ! . Process )
2025-12-23 18:27:31 +10:00
. Include ( t = > t . UnitsInTemplate ) ;
var template = await query . FirstOrDefaultAsync ( t = > t . Name = = esppObject . TemplateName ) ;
2025-08-06 10:33:55 +10:00
2025-08-20 14:10:22 +10:00
if ( template = = null )
2025-08-06 10:33:55 +10:00
{
2025-08-20 14:10:22 +10:00
logger . LogWarning ( $"Найден объект в ЕСПП с именем шаблона {esppObject.TemplateName} незарегистрированный в ПАРР." ) ;
return ;
2025-08-06 10:33:55 +10:00
}
2025-12-23 18:27:31 +10:00
// ✅ Построим TemplateForShortcodes из уже загруженного template
var templateForShortcodes = new TemplateForShortcodes
2025-08-20 14:10:22 +10:00
{
2025-12-23 18:27:31 +10:00
Id = template . Id ,
Index = template . Index ,
JobId = template . JobId ,
UnitId = template . UnitId ,
Job = template . Job = = null ? null : new JobForShortcodes
{
Group = template . Job . Group = = null ? null : new JobGroupForShortcodes
{
GroupingUnitFieldId = template . Job . Group . GroupingUnitFieldId ,
GroupType = template . Job . Group . GroupType = = null ? null : new JobGroupTypeForShortcodes
{
Code = template . Job . Group . GroupType . Code
} ,
GroupName = template . Job . Group . GroupName
} ,
Tnk = template . Job . Tnk = = null ? null : new TnkForShortcodes
{
Name = template . Job . Tnk . Name ,
ShortName = template . Job . Tnk . ShortName
} ,
WorkName = template . Job . WorkName ,
Name = template . Job . Name
} ,
UnitsInTemplate = template . UnitsInTemplate ? . Select ( uit = > new UnitInTemplateForShortcodes { UnitId = uit . UnitId } ) . ToList ( ) ? ? new List < UnitInTemplateForShortcodes > ( )
} ;
2025-08-06 10:33:55 +10:00
2025-12-23 18:27:31 +10:00
var dbObjectInEsppObject = converterToEsppObject . Invoke ( template ) ;
2024-02-28 10:17:01 +10:00
2025-12-23 18:27:31 +10:00
//Проверяем наличие Shortcode в полях объекта из БД
var properties = dbObjectInEsppObject . GetType ( ) . GetProperties ( ) ;
foreach ( PropertyInfo property in properties )
{
var value = property . GetValue ( dbObjectInEsppObject ) ? . ToString ( ) ;
2024-02-28 10:17:01 +10:00
2025-12-23 18:27:31 +10:00
if ( value ! = null & & shortcodesService . IsAnyShortcodes ( value ) )
{
var processedValue = await shortcodesService . ApplyShortcodesAsync ( value , templateForShortcodes ) ;
property . SetValue ( dbObjectInEsppObject , processedValue ) ;
2025-08-20 14:10:22 +10:00
}
2025-12-23 18:27:31 +10:00
}
bool isChanged = false ;
//TODO: FIX ME Please, BRO
//bool isChanged;
2023-11-22 11:37:22 +10:00
2025-12-23 18:27:31 +10:00
// если в БД isActive == false, то синхронизировать только по полям из IEsppObject
if ( dbObjectInEsppObject . IsActive = = false )
{
logger . LogDebug ( $"Объект деактивирован в ПАРР. Сравниваем только обязательные поля. {esppObject.TemplateName}" ) ;
var lightDbObj = new EsppLightObject ( dbObjectInEsppObject ) ;
var lightEsppObject = new EsppLightObject ( esppObject ) ;
2023-11-24 16:52:56 +10:00
2025-12-23 18:27:31 +10:00
isChanged = IsChanged ( lightEsppObject , lightDbObj , esppObject . TemplateName ) ;
}
else
{
logger . LogDebug ( $"Объект активирован в ПАРР. Сравниваем все поля. {esppObject.TemplateName}" ) ;
isChanged = IsChanged ( esppObject , dbObjectInEsppObject , esppObject . TemplateName ) ;
}
2024-08-02 09:38:05 +10:00
2025-12-23 18:27:31 +10:00
if ( isChanged )
{
logger . LogDebug ( $"Есть изменения, требуется обновление. {esppObject.TemplateName}" ) ;
var config = robotConfigurationService . GetFromTemplateByRobotCode ( esppObject . Robot , template ) ;
// Если предыдущий статус был Create или Ок, то ставим ему Update
// пусть даже Create завершился с ошибками, но раз он уже есть в ЕСПП, то изменим на Update, сбросим все счетчики и пусть попробует обновить и исправить все
if ( config . TaskStatusCode ! = ( int ) TaskStatusEnum . Updating )
2024-08-02 09:38:05 +10:00
{
2025-12-23 18:27:31 +10:00
SetUpdateStatus ( ref template , robotConfigurationService , esppObject . Robot ) ;
2024-08-02 09:38:05 +10:00
2025-12-23 18:27:31 +10:00
if ( ! await templateService . CommitAsync ( ) )
logger . LogError ( $"Н е удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}" ) ;
else
logger . LogInformation ( $"Установлен принудительный статус {TaskStatusEnum.Updating}, Template {template.Name}, Robot: {esppObject.Robot}" ) ;
2024-08-02 09:38:05 +10:00
}
2023-11-22 11:37:22 +10:00
else
2024-08-02 09:38:05 +10:00
{
2025-12-23 18:27:31 +10:00
// Если пред статус был Update, то ничего не делаем, так е г о и оставляем, не сбрасывам кол-во попыток и ошибок
logger . LogInformation ( $"Есть изменения в Template {template.Name}, но предыдущий статус TaskStatusCode: {(TaskStatusEnum)config.TaskStatusCode}. Н е меняем статус, будем разбираться вручную." ) ;
2024-08-02 09:38:05 +10:00
}
2025-12-23 18:27:31 +10:00
#region Old logic
2025-08-20 14:10:22 +10:00
2025-12-23 18:27:31 +10:00
//SetUpdateStatus(ref template, robotConfigurationService, esppObject.Robot);
2025-08-20 14:10:22 +10:00
2025-12-23 18:27:31 +10:00
//if (!await templateService.CommitAsync())
// logger.LogError($"Н е удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}");
//else
// logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Updating}, Template {template.Name}, Robot: {esppObject.Robot}");
#endregion
} //надо ли проверять если не изменился, но был статус Updating не понятно. Доверяем роботу пока, что после окончания работ он точно сообщит
else
{
logger . LogDebug ( $"Нет изменений, обновление не требуется. {esppObject.TemplateName}" ) ;
2025-08-20 14:10:22 +10:00
2025-12-23 18:27:31 +10:00
//если все поля совпали
//проверяем, какой был статус предыдущий статус в БД, если он был не Ок, то ставим ему О К
var robotConfig = robotConfigurationService . GetFromTemplateByRobotCode ( esppObject . Robot , template ) ;
if ( robotConfig . TaskStatusCode ! = ( int ) TaskStatusEnum . Ok )
2025-08-20 14:10:22 +10:00
{
2025-12-23 18:27:31 +10:00
robotConfigurationService . ChangeTaskStatus ( TaskStatusEnum . Ok , robotConfig ) ;
if ( ! await templateService . CommitAsync ( ) )
logger . LogError ( $"Н е удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}" ) ;
else
logger . LogInformation ( $"Установлен принудительный статус {TaskStatusEnum.Ok}, Template {template.Name}, Robot: {esppObject.Robot}" ) ;
2023-11-22 11:37:22 +10:00
}
}
}
2025-08-20 14:10:22 +10:00
catch ( Exception ex )
{
logger . LogError ( ex , $"Ошибка синхронизации объекта А С У ЕСПП {esppObject.TemplateName}" ) ;
}
2025-08-06 10:33:55 +10:00
}
2023-11-22 11:37:22 +10:00
}
private void SetUpdateStatus ( ref Template template , IRobotConfigurationService robotConfigurationService , RobotsEnum robot )
{
2025-11-28 09:12:26 +10:00
var robotConfig = robotConfigurationService . GetFromTemplateByRobotCode ( robot , template ) ;
robotConfigurationService . ChangeTaskStatus ( TaskStatusEnum . Updating , robotConfig ) ;
2023-11-22 11:37:22 +10:00
}
private Service GetServiceInScope < Service > ( IServiceScope scope )
{
var service = scope . ServiceProvider . GetService < Service > ( ) ;
if ( service = = null )
throw new Exception ( $"Н е найден сервис: {nameof(Service)}" ) ;
return service ;
}
2024-02-28 10:17:01 +10:00
//private bool IsChanged(EsppObject esppObj, EsppObject dbObj)
2025-12-04 09:26:17 +10:00
private bool IsChanged ( object esppObj , object dbObj , string templateName )
2023-11-22 11:37:22 +10:00
{
foreach ( var prop in dbObj . GetType ( ) . GetProperties ( ) )
{
if ( prop = = null )
continue ;
var dbValue = dbObj . GetType ( ) . GetProperty ( prop . Name ) ? . GetValue ( dbObj , null ) ;
var esppValue = esppObj . GetType ( ) . GetProperty ( prop . Name ) ? . GetValue ( esppObj , null ) ;
if ( dbValue = = null | | esppValue = = null )
continue ;
//Replace("\r","").Replace("\n","") - в подробном описании могут быть переносы строк, в Rabbit прилетает без переносов. Убираем переносы для стравнения
2025-08-21 14:51:40 +10:00
var dbValueStr = Normalize ( dbValue ! . ToString ( ) ! ) ;
var esppValueStr = Normalize ( esppValue ! . ToString ( ) ! ) ;
2023-11-22 11:37:22 +10:00
if ( dbValueStr ! = esppValueStr )
{
2025-12-04 12:10:10 +10:00
logger . LogInformation ( $"Н е совпадают поля ({prop.Name}). dbValueStr: {dbValueStr}, esppValueStr: {esppValueStr}. Имя шаблона: {templateName}" ) ;
2023-11-22 11:37:22 +10:00
return true ;
}
}
return false ;
}
2025-11-28 09:12:26 +10:00
2025-08-21 14:51:40 +10:00
/// <summary>
/// Удаляет ненужные символы из строки
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private string Normalize ( string str )
{
str = str . Replace ( "\r" , string . Empty ) ;
str = str . Replace ( "\n" , string . Empty ) ;
str = str . Replace ( " " , string . Empty ) ;
2023-11-22 11:37:22 +10:00
2025-08-21 14:51:40 +10:00
return str . ToLower ( ) ;
}
2023-11-22 11:37:22 +10:00
}
}