Files
parr_api/PARR.EsppSync/SyncService.cs

251 lines
13 KiB
C#
Raw Normal View History

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using PARR.Constants;
using PARR.DAL.Contracts;
using PARR.DAL.DomainServices.Shortcodes;
using PARR.DAL.Models;
using PARR.DAL.Services.Interfaces;
using System.Reflection;
namespace PARR.EsppSync
{
internal class SyncService<EsppObject> : ISyncService<EsppObject> where EsppObject : class, IEsppObject
{
private readonly ILogger<SyncService<EsppObject>> logger;
private readonly IServiceProvider serviceProvider;
public SyncService(
ILogger<SyncService<EsppObject>> logger,
IServiceProvider serviceProvider
)
{
this.logger = logger;
this.serviceProvider = serviceProvider;
}
public async Task SyncEsppObjectAsync(
string str,
ParserHandlerDelegate<EsppObject> parser,
ConvertDbObjToComparisonObjHandlerDelegate<EsppObject> converterToEsppObject
)
{
logger.LogDebug($"Получил строку. Начинаю работать. Строка: {str}");
if (string.IsNullOrEmpty(str))
{
logger.LogWarning("Получил пустую строку, ничего не делаю.");
return;
}
var esppObject = parser.Invoke(str);
if (esppObject == null)
{
logger.LogWarning("После парсинга строки, esppObject = null. Дальше ничего не буду делать.");
return;
}
using (var scope = serviceProvider.CreateScope())
{
var templateService = GetServiceInScope<ITemplateService>(scope);
var robotConfigurationService = GetServiceInScope<IRobotConfigurationService>(scope);
var shortcodesService = GetServiceInScope<IShortcodesService>(scope);
try
{
// Загружаем Template и TemplateForShortcodes в одном запросе
var query = templateService.Get()
//.AsNoTracking() <- не надо так, а то потом не сохранится
.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)
.Include(t => t.Job)
.ThenInclude(j => j.Group)
.ThenInclude(g => g.GroupType)
.Include(t => t.Job)
.ThenInclude(t => t.Group)
.ThenInclude(t => t.ScheduleExcludeType)
.Include(t => t.Job)
.ThenInclude(t => t.Group)
.ThenInclude(t => t.ScheduleExcludeTypeCalendar)
.Include(t => t.Job)
.ThenInclude(j => j.Tnk)
.ThenInclude(s => s!.Subprocess)
.ThenInclude(p => p!.Process)
.Include(t => t.UnitsInTemplate);
var template = await query.FirstOrDefaultAsync(t => t.Name == esppObject.TemplateName);
if (template == null)
{
logger.LogWarning($"Найден объект в ЕСПП с именем шаблона {esppObject.TemplateName} незарегистрированный в ПАРР.");
return;
}
var dbObjectInEsppObject = converterToEsppObject.Invoke(template);
// Проверяем наличие Shortcode в полях объекта из БД
var properties = dbObjectInEsppObject.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(string))
{
var value = property.GetValue(dbObjectInEsppObject)?.ToString();
if (!string.IsNullOrEmpty(value))
{
// Передаём исходный template — он уже загружен с Include
var processedValue = await shortcodesService.ApplyShortcodesAsync(value, template);
property.SetValue(dbObjectInEsppObject, processedValue);
}
}
}
bool isChanged = false;
//TODO: FIX ME Please, BRO
//bool isChanged;
// если в БД isActive == false, то синхронизировать только по полям из IEsppObject
if (dbObjectInEsppObject.IsActive == false)
{
logger.LogDebug($"Объект деактивирован в ПАРР. Сравниваем только обязательные поля. {esppObject.TemplateName}");
var lightDbObj = new EsppLightObject(dbObjectInEsppObject);
var lightEsppObject = new EsppLightObject(esppObject);
isChanged = IsChanged(lightEsppObject, lightDbObj, esppObject.TemplateName);
}
else
{
logger.LogDebug($"Объект активирован в ПАРР. Сравниваем все поля. {esppObject.TemplateName}");
isChanged = IsChanged(esppObject, dbObjectInEsppObject, esppObject.TemplateName);
}
if (isChanged)
{
logger.LogDebug($"Есть изменения, требуется обновление. {esppObject.TemplateName}");
var config = robotConfigurationService.GetFromTemplateByRobotCode(esppObject.Robot, template);
// Если предыдущий статус был Create или Ок, то ставим ему Update
// пусть даже Create завершился с ошибками, но раз он уже есть в ЕСПП, то изменим на Update, сбросим все счетчики и пусть попробует обновить и исправить все
if (config.TaskStatusCode != (int)TaskStatusEnum.Updating)
{
SetUpdateStatus(ref template, robotConfigurationService, esppObject.Robot);
if (!await templateService.CommitAsync())
logger.LogError($"Не удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}");
else
logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Updating}, Template {template.Name}, Robot: {esppObject.Robot}");
}
else
{
// Если пред статус был Update, то ничего не делаем, так его и оставляем, не сбрасывам кол-во попыток и ошибок
logger.LogInformation($"Есть изменения в Template {template.Name}, но предыдущий статус TaskStatusCode: {(TaskStatusEnum)config.TaskStatusCode}. Не меняем статус, будем разбираться вручную.");
}
#region Old logic
//SetUpdateStatus(ref template, robotConfigurationService, esppObject.Robot);
//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}");
//если все поля совпали
//проверяем, какой был статус предыдущий статус в БД, если он был не Ок, то ставим ему ОК
var robotConfig = robotConfigurationService.GetFromTemplateByRobotCode(esppObject.Robot, template);
if (robotConfig.TaskStatusCode != (int)TaskStatusEnum.Ok)
{
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}");
}
}
}
catch (Exception ex)
{
logger.LogError(ex, $"Ошибка синхронизации объекта АСУ ЕСПП {esppObject.TemplateName}");
}
}
}
private void SetUpdateStatus(ref Template template, IRobotConfigurationService robotConfigurationService, RobotsEnum robot)
{
var robotConfig = robotConfigurationService.GetFromTemplateByRobotCode(robot, template);
robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, robotConfig);
}
private Service GetServiceInScope<Service>(IServiceScope scope)
{
var service = scope.ServiceProvider.GetService<Service>();
if (service == null)
throw new Exception($"Не найден сервис: {nameof(Service)}");
return service;
}
//private bool IsChanged(EsppObject esppObj, EsppObject dbObj)
private bool IsChanged(object esppObj, object dbObj, string templateName)
{
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 прилетает без переносов. Убираем переносы для стравнения
var dbValueStr = Normalize(dbValue!.ToString()!);
var esppValueStr = Normalize(esppValue!.ToString()!);
if (dbValueStr != esppValueStr)
{
logger.LogInformation($"Не совпадают поля ({prop.Name}). dbValueStr: {dbValueStr}, esppValueStr: {esppValueStr}. Имя шаблона: {templateName}");
return true;
}
}
return false;
}
/// <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);
return str.ToLower();
}
}
}