feat(esppScheduleSync, esppSync): вынесена логика сравнения nextRun из EsppSync в EsppScheduleSync. Рефакторинг EsppScheduleSync - логика проверки имени шаблона
This commit is contained in:
@@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using PARR.BLL.Helpers;
|
using PARR.BLL.Helpers;
|
||||||
using PARR.BLL.Services.Interfaces;
|
using PARR.BLL.Services.Interfaces;
|
||||||
|
using PARR.Common.Domain;
|
||||||
using PARR.Constants;
|
using PARR.Constants;
|
||||||
using PARR.DAL.Contracts;
|
using PARR.DAL.Contracts;
|
||||||
using PARR.DAL.Models;
|
using PARR.DAL.Models;
|
||||||
@@ -12,6 +13,7 @@ using PARR.DAL.Services.Interfaces.Schedule;
|
|||||||
using PARR.EsppScheduleSync.Settings;
|
using PARR.EsppScheduleSync.Settings;
|
||||||
using PARR.EsppSync;
|
using PARR.EsppSync;
|
||||||
using PARR.EsppSync.Domain;
|
using PARR.EsppSync.Domain;
|
||||||
|
using PARR.EsppSync.Helpers;
|
||||||
|
|
||||||
namespace PARR.EsppScheduleSync
|
namespace PARR.EsppScheduleSync
|
||||||
{
|
{
|
||||||
@@ -23,7 +25,6 @@ namespace PARR.EsppScheduleSync
|
|||||||
private readonly ISyncService<EsppObjectSchedule> syncService;
|
private readonly ISyncService<EsppObjectSchedule> syncService;
|
||||||
private readonly SettingsFromDb settingsFromDb;
|
private readonly SettingsFromDb settingsFromDb;
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService;
|
|
||||||
private string noneExcludeCalendarEsppValue;
|
private string noneExcludeCalendarEsppValue;
|
||||||
|
|
||||||
|
|
||||||
@@ -33,8 +34,7 @@ namespace PARR.EsppScheduleSync
|
|||||||
IMqService mqService,
|
IMqService mqService,
|
||||||
ISyncService<EsppObjectSchedule> syncService,
|
ISyncService<EsppObjectSchedule> syncService,
|
||||||
SettingsFromDb settingsFromDb,
|
SettingsFromDb settingsFromDb,
|
||||||
IServiceProvider serviceProvider,
|
IServiceProvider serviceProvider
|
||||||
IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
@@ -43,7 +43,6 @@ namespace PARR.EsppScheduleSync
|
|||||||
this.syncService = syncService;
|
this.syncService = syncService;
|
||||||
this.settingsFromDb = settingsFromDb;
|
this.settingsFromDb = settingsFromDb;
|
||||||
this.serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
this.scheduleResponseAreaTimeOffsetService = scheduleResponseAreaTimeOffsetService;
|
|
||||||
this.noneExcludeCalendarEsppValue = string.Empty;
|
this.noneExcludeCalendarEsppValue = string.Empty;
|
||||||
|
|
||||||
if (globalSettings.MqSettings == null)
|
if (globalSettings.MqSettings == null)
|
||||||
@@ -74,7 +73,19 @@ namespace PARR.EsppScheduleSync
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task StopAsync()
|
||||||
|
{
|
||||||
|
await mqService.DisposeAsync();
|
||||||
|
|
||||||
|
logger.LogInformation("=== === === Соединение с очередью {QueueName} закрыто === === ===", globalSettings.MqSettings!.QueueName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение типа исключения
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="InvalidOperationException"></exception>
|
||||||
private async Task GetNoneExcludeCalendarEsppValueAsync()
|
private async Task GetNoneExcludeCalendarEsppValueAsync()
|
||||||
{
|
{
|
||||||
using var scope = serviceProvider.CreateScope();
|
using var scope = serviceProvider.CreateScope();
|
||||||
@@ -88,96 +99,67 @@ namespace PARR.EsppScheduleSync
|
|||||||
noneExcludeCalendarEsppValue = noneExcludeType.EsppValue;
|
noneExcludeCalendarEsppValue = noneExcludeType.EsppValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StopAsync()
|
|
||||||
{
|
|
||||||
await mqService.DisposeAsync();
|
|
||||||
|
|
||||||
logger.LogInformation("=== === === Соединение с очередью {QueueName} закрыто === === ===", globalSettings.MqSettings!.QueueName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Старт синхронизации, получили сообщение
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
private async Task SyncScheduleAsync(string str)
|
private async Task SyncScheduleAsync(string str)
|
||||||
{
|
{
|
||||||
try
|
await syncService.SyncEsppObjectAsync(str, ParseStrToEsppObject, ConvertDbObjToEsppObj, CustomComparisionCheckAsyncHandler, AfterParseStringToEsppObjectAsyncHandler);
|
||||||
{
|
|
||||||
const int expectedParts = 24;
|
|
||||||
var separator = globalSettings.ParsingSeparator;
|
|
||||||
|
|
||||||
var parts = str.Split(separator);
|
|
||||||
if (parts.Length != expectedParts)
|
|
||||||
{
|
|
||||||
logger.LogWarning("Некорректное количество полей в строке расписания: {Actual} (ожидается {Expected})", parts.Length, expectedParts);
|
|
||||||
await syncService.SyncEsppObjectAsync(str, ParseStrToEsppObject, ConvertDbObjToEsppObj);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var esppScheduleId = parts[0]; // ScheduleEsppId из ЕСПП
|
/// <summary>
|
||||||
var templateNameRaw = parts[6];
|
/// Выполняется после парсинга строки в объект ЕСПП.
|
||||||
var templateName = templateNameRaw.ToUpper();
|
/// Проверка, есть ли у расписания шаблон. Если имена шаблона и расписания не совпадают, установить шаблону статус обновления.
|
||||||
|
/// </summary>
|
||||||
// Проверка префикса (как в ParseStrToEsppObject)
|
/// <param name="esppObject"></param>
|
||||||
if (string.IsNullOrEmpty(settingsFromDb.TemplatePrefixWithoutVariable) ||
|
/// <returns></returns>
|
||||||
!templateName.Contains(settingsFromDb.TemplatePrefixWithoutVariable))
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
private async Task AfterParseStringToEsppObjectAsyncHandler(EsppObjectSchedule esppObject)
|
||||||
{
|
{
|
||||||
logger.LogWarning("Имя шаблона '{TemplateName}' не соответствует префиксу '{Prefix}'. Пропущено.", templateName, settingsFromDb.TemplatePrefixWithoutVariable);
|
var esppScheduleId = esppObject.Code;
|
||||||
await syncService.SyncEsppObjectAsync(str, ParseStrToEsppObject, ConvertDbObjToEsppObj);
|
var esppTemplateName = esppObject.TemplateName;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// === ЕДИНЫЙ ЗАПРОС: ищем по имени ИЛИ по ScheduleEsppId ===
|
|
||||||
using var scope = serviceProvider.CreateScope();
|
using var scope = serviceProvider.CreateScope();
|
||||||
var templateService = scope.ServiceProvider.GetRequiredService<ITemplateService>();
|
var templateService = scope.ServiceProvider.GetRequiredService<ITemplateService>();
|
||||||
|
|
||||||
var candidates = await templateService.Get()
|
var candidates = await templateService.Get()
|
||||||
.Include(t => t.RobotConfigurations)
|
.Include(t => t.RobotConfigurations)
|
||||||
.Where(t => t.Name == templateName || t.ScheduleEsppId == esppScheduleId)
|
.Where(t => t.Name == esppTemplateName || t.ScheduleEsppId == esppScheduleId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var templateByName = candidates.FirstOrDefault(t => t.Name == templateName);
|
var templateByName = candidates.FirstOrDefault(t => t.Name == esppTemplateName);
|
||||||
var templateByScheduleId = candidates.FirstOrDefault(t => t.ScheduleEsppId == esppScheduleId);
|
var templateByScheduleId = candidates.FirstOrDefault(t => t.ScheduleEsppId == esppScheduleId);
|
||||||
|
|
||||||
if (templateByName == null && templateByScheduleId == null)
|
if (templateByName == null && templateByScheduleId == null)
|
||||||
{
|
{
|
||||||
// Нет ни по имени, ни по ID
|
// Нет ни по имени, ни по ID
|
||||||
logger.LogWarning(
|
logger.LogWarning("Расписание из ЕСПП не привязано ни к одному шаблону: TemplateName='{TemplateName}', ScheduleEsppId='{EsppId}'", esppTemplateName, esppScheduleId);
|
||||||
"Расписание из ЕСПП не привязано ни к одному шаблону: TemplateName='{TemplateName}', ScheduleEsppId='{EsppId}'",
|
|
||||||
templateName,
|
|
||||||
esppScheduleId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else if (templateByName == null && templateByScheduleId != null)
|
else if (templateByName == null && templateByScheduleId != null)
|
||||||
{
|
{
|
||||||
// Есть только по ID → имя не совпадает
|
// Есть только по ID → имя не совпадает
|
||||||
logger.LogWarning(
|
logger.LogWarning("Расхождение привязки: расписание из ЕСПП с ScheduleEsppId='{EsppId}' и TemplateName='{TemplateName}' соответствует шаблону в БД с именем '{DbTemplateName}', Id='{TemplateId}'.",
|
||||||
"Расхождение привязки: расписание из ЕСПП с ScheduleEsppId='{EsppId}' и TemplateName='{TemplateName}' " +
|
esppScheduleId, esppTemplateName, templateByScheduleId.Name, templateByScheduleId.Id);
|
||||||
"соответствует шаблону в БД с именем '{DbTemplateName}', Id='{TemplateId}'.",
|
|
||||||
esppScheduleId,
|
|
||||||
templateName,
|
|
||||||
templateByScheduleId.Name,
|
|
||||||
templateByScheduleId.Id
|
|
||||||
);
|
|
||||||
|
|
||||||
// Принудительно выставляем статус обновления шаблона, так как имя в ЕСПП изменилось
|
// Принудительно выставляем статус обновления шаблона, так как имя в ЕСПП изменилось
|
||||||
try
|
|
||||||
{
|
|
||||||
var robotConfigurationService = scope.ServiceProvider.GetRequiredService<IRobotConfigurationService>();
|
var robotConfigurationService = scope.ServiceProvider.GetRequiredService<IRobotConfigurationService>();
|
||||||
|
|
||||||
var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.TemplateOrder, templateByScheduleId);
|
var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.TemplateOrder, templateByScheduleId);
|
||||||
|
|
||||||
|
logger.LogDebug("Текущий статус задания {taskStatusCode} шаблона {templateId}", config.TaskStatusCode, templateByScheduleId.Id);
|
||||||
robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, config);
|
robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, config);
|
||||||
|
|
||||||
// Сохраняем изменения в базу данных
|
// Сохраняем изменения в базу данных
|
||||||
if (!await robotConfigurationService.CommitAsync())
|
if (!await templateService.CommitAsync(new HistoryInitiator { InitiatorParrComponentId = ParrComponentsEnum.EsppScheduleSync, InitiatorComment = "Расхождение привязки, расписание из ЕСПП не соответствует имени шаблона в БД" }))
|
||||||
{
|
{
|
||||||
logger.LogError("Не удалось сохранить изменения статуса задачи для шаблона Id='{TemplateId}'", templateByScheduleId.Id);
|
logger.LogError("Не удалось сохранить изменения статуса задачи для шаблона Id='{TemplateId}'", templateByScheduleId.Id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.LogDebug($"Для шаблона {nameof(templateByScheduleId.Id)}:{templateByScheduleId.Id} установлен статус {RobotStatusEnum.Wait.ToString()} при обнаружении расхождения имени с ЕСПП");
|
logger.LogDebug("Для шаблона {templateId} установлен статус задания {taskStatus} при обнаружении расхождения имени с ЕСПП",
|
||||||
}
|
templateByScheduleId.Id, TaskStatusEnum.Updating.ToString());
|
||||||
}
|
|
||||||
catch (Exception updateEx)
|
|
||||||
{
|
|
||||||
logger.LogError(updateEx, "Ошибка при установке статуса обновления для шаблона Id='{TemplateId}'", templateByScheduleId.Id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (templateByName != null)
|
else if (templateByName != null)
|
||||||
@@ -187,53 +169,27 @@ namespace PARR.EsppScheduleSync
|
|||||||
if (string.IsNullOrEmpty(dbScheduleId))
|
if (string.IsNullOrEmpty(dbScheduleId))
|
||||||
{
|
{
|
||||||
// Утерян ID в БД
|
// Утерян ID в БД
|
||||||
logger.LogWarning(
|
logger.LogWarning("У шаблона '{TemplateName}' (Id='{TemplateId}') отсутствует ScheduleEsppId в БД, но в ЕСПП он равен '{EsppId}'", esppTemplateName, templateByName.Id, esppScheduleId);
|
||||||
"У шаблона '{TemplateName}' (Id='{TemplateId}') отсутствует ScheduleEsppId в БД, но в ЕСПП он равен '{EsppId}'",
|
|
||||||
templateName,
|
|
||||||
templateByName.Id,
|
|
||||||
esppScheduleId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else if (dbScheduleId != esppScheduleId)
|
else if (dbScheduleId != esppScheduleId)
|
||||||
{
|
{
|
||||||
// ID не совпадают — проверяем, не занят ли esppScheduleId другим шаблоном
|
// ID не совпадают — проверяем, не занят ли esppScheduleId другим шаблоном
|
||||||
var conflictingTemplate = candidates.FirstOrDefault(t =>
|
var conflictingTemplate = candidates.FirstOrDefault(t => t.Id != templateByName.Id && t.ScheduleEsppId == esppScheduleId);
|
||||||
t.Id != templateByName.Id && t.ScheduleEsppId == esppScheduleId);
|
|
||||||
|
|
||||||
if (conflictingTemplate != null)
|
if (conflictingTemplate != null)
|
||||||
{
|
{
|
||||||
logger.LogWarning(
|
logger.LogWarning("Конфликт ScheduleEsppId: расписание '{EsppId}' из ЕСПП с именем '{TemplateName}' уже привязано к другому шаблону '{OtherTemplateName}' (Id='{OtherTemplateId}') в БД.",
|
||||||
"Конфликт ScheduleEsppId: расписание '{EsppId}' из ЕСПП с именем '{TemplateName}' " +
|
esppScheduleId, esppTemplateName, conflictingTemplate.Name, conflictingTemplate.Id);
|
||||||
"уже привязано к другому шаблону '{OtherTemplateName}' (Id='{OtherTemplateId}') в БД.",
|
|
||||||
esppScheduleId,
|
|
||||||
templateName,
|
|
||||||
conflictingTemplate.Name,
|
|
||||||
conflictingTemplate.Id
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.LogWarning(
|
logger.LogWarning("Несовпадение ScheduleEsppId для шаблона '{TemplateName}' (Id='{TemplateId}'): в БД='{DbId}', в ЕСПП='{EsppId}'",
|
||||||
"Несовпадение ScheduleEsppId для шаблона '{TemplateName}' (Id='{TemplateId}'): в БД='{DbId}', в ЕСПП='{EsppId}'",
|
esppTemplateName, templateByName.Id, dbScheduleId, esppScheduleId);
|
||||||
templateName,
|
|
||||||
templateByName.Id,
|
|
||||||
dbScheduleId,
|
|
||||||
esppScheduleId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Передаём оригинальную строку в стандартный синхронизатор
|
|
||||||
await syncService.SyncEsppObjectAsync(str, ParseStrToEsppObject, ConvertDbObjToEsppObj);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logger.LogError(ex, "Ошибка при синхронизации расписания из строки: {InputString}", str);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Преобразование модели БД в модель для сравнения
|
/// Преобразование модели БД в модель для сравнения
|
||||||
@@ -291,6 +247,85 @@ namespace PARR.EsppScheduleSync
|
|||||||
return ClearOptionalFields(esppObjectFromDb);
|
return ClearOptionalFields(esppObjectFromDb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
|
||||||
|
if (EsppSyncHelpers.Normalize(dbObject.Scheduled) != EsppSyncHelpers.Normalize(esppObject.Scheduled) || EsppSyncHelpers.Normalize(dbObject.Scheduled) != scheduledCalculated || EsppSyncHelpers.Normalize(esppObject.Scheduled) != scheduledCalculated)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Не совпадают поля ({propName}), dbValueStr: {dbValueStr}, esppValueStr: {esppValueStr}, scheduledCalculated: {scheduledCalculated}. Имя шаблона: {templateName}",
|
||||||
|
nameof(dbObject.Scheduled), dbObject.Scheduled, esppObject.Scheduled, scheduledCalculated, dbObject.TemplateName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EsppSyncHelpers.Normalize(dbObject.BasisTime) != EsppSyncHelpers.Normalize(esppObject.BasisTime) || EsppSyncHelpers.Normalize(dbObject.BasisTime) != basisTimeCalculated || EsppSyncHelpers.Normalize(esppObject.BasisTime) != basisTimeCalculated)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Не совпадают поля ({propName}), dbValueStr: {dbValueStr}, esppValueStr: {esppValueStr}, basisTimeCalculated: {basisTimeCalculated}. Имя шаблона: {templateName}",
|
||||||
|
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();
|
||||||
|
var nextRunService = scope.ServiceProvider.GetRequiredService<INextRunServiceV2>();
|
||||||
|
|
||||||
|
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>
|
||||||
private string GetTimezone(/*Template template, string responseArea*/)
|
private string GetTimezone(/*Template template, string responseArea*/)
|
||||||
{
|
{
|
||||||
// договорились, что у роботоа ТЗ МСК
|
// договорились, что у роботоа ТЗ МСК
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ namespace PARR.EsppSync.Domain
|
|||||||
|
|
||||||
public required string TemplateName { get; set; }
|
public required string TemplateName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ИД расписания
|
||||||
|
/// </summary>
|
||||||
public string Code { get; set; } = string.Empty;
|
public string Code { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string ScheduleName { get; set; } = string.Empty;
|
public string ScheduleName { get; set; } = string.Empty;
|
||||||
|
|||||||
25
PARR.EsppSync/Helpers/EsppSyncHelpers.cs
Normal file
25
PARR.EsppSync/Helpers/EsppSyncHelpers.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
namespace PARR.EsppSync.Helpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Хелперы для EsppSync
|
||||||
|
/// </summary>
|
||||||
|
public static class EsppSyncHelpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Првести строку в стандарт сравнения (для сравнения объектов)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string Normalize(string? str)
|
||||||
|
{
|
||||||
|
if (str == null)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
str = str.Replace("\r", string.Empty);
|
||||||
|
str = str.Replace("\n", string.Empty);
|
||||||
|
str = str.Replace(" ", string.Empty);
|
||||||
|
|
||||||
|
return str.ToLower();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ namespace PARR.EsppSync
|
|||||||
/// <typeparam name="EsppObject"></typeparam>
|
/// <typeparam name="EsppObject"></typeparam>
|
||||||
/// <param name="str"></param>
|
/// <param name="str"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public delegate EsppObject? ParserHandlerDelegate<EsppObject>(string str) where EsppObject : class, IEsppObject;
|
public delegate EsppObject? ParseStringToEsppObject<EsppObject>(string str) where EsppObject : class, IEsppObject;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Конвертирует Template в модель для сравнения
|
/// Конвертирует Template в модель для сравнения
|
||||||
@@ -16,17 +16,44 @@ namespace PARR.EsppSync
|
|||||||
/// <typeparam name="EsppObject"></typeparam>
|
/// <typeparam name="EsppObject"></typeparam>
|
||||||
/// <param name="templateName"></param>
|
/// <param name="templateName"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public delegate EsppObject ConvertDbObjToComparisonObjHandlerDelegate<EsppObject>(Template template) where EsppObject : class, IEsppObject;
|
public delegate EsppObject ConvertDbToEsppObject<EsppObject>(Template template) where EsppObject : class, IEsppObject;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дополнительная проверка полей, если проверка прошла, то true
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="EsppObject"></typeparam>
|
||||||
|
/// <param name="esppObject"></param>
|
||||||
|
/// <param name="dbObject"></param>
|
||||||
|
/// <param name="templateId"></param>
|
||||||
|
/// <returns>true - проверка пройдена, false - проверка не пройдена</returns>
|
||||||
|
public delegate Task<bool> CustomComparisionCheckAsync<EsppObject>(EsppObject esppObject, EsppObject dbObject, Guid templateId) where EsppObject : class, IEsppObject;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Выполнить произвольную логику после парсинга строки в объект ЕСПП
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="EsppObject"></typeparam>
|
||||||
|
/// <param name="esppObject"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public delegate Task AfterParseStringToEsppObjectAsync<EsppObject>(EsppObject esppObject) where EsppObject : class, IEsppObject;
|
||||||
|
|
||||||
|
|
||||||
public interface ISyncService<EsppObject> where EsppObject : class, IEsppObject
|
public interface ISyncService<EsppObject> where EsppObject : class, IEsppObject
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Синхронизировать объекты
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="str">Строка из RabbitMQ</param>
|
/// <param name="str">Строка из RabbitMQ</param>
|
||||||
/// <param name="parser">Делегат парсинга из строки в модель EsppObject</param>
|
/// <param name="parseStringToEsppObject">Делегат парсинга из строки в модель EsppObject</param>
|
||||||
/// <param name="converterToEsppObject"> Конвертирует BD Template в модель для сравнения</param>
|
/// <param name="converterDbToEsppObject"> Конвертирует BD Template в модель для сравнения</param>
|
||||||
|
/// <param name="customComparisionAsync">Кастомное сравнение полей</param>
|
||||||
|
/// <param name="afterParseStringToEsppObjectAsync">Выполнить произвольный метод после парсинга строки в модель EsppObject</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task SyncEsppObjectAsync(string str, ParserHandlerDelegate<EsppObject> parser, ConvertDbObjToComparisonObjHandlerDelegate<EsppObject> converterToEsppObject);
|
Task SyncEsppObjectAsync(string str,
|
||||||
|
ParseStringToEsppObject<EsppObject> parseStringToEsppObject,
|
||||||
|
ConvertDbToEsppObject<EsppObject> converterDbToEsppObject,
|
||||||
|
CustomComparisionCheckAsync<EsppObject>? customComparisionAsync = null,
|
||||||
|
AfterParseStringToEsppObjectAsync<EsppObject>? afterParseStringToEsppObjectAsync = null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using PARR.BLL.Helpers;
|
|
||||||
using PARR.Constants;
|
using PARR.Constants;
|
||||||
using PARR.DAL.Contracts;
|
using PARR.DAL.Contracts;
|
||||||
using PARR.DAL.DomainServices.Shortcodes;
|
using PARR.DAL.DomainServices.Shortcodes;
|
||||||
using PARR.DAL.Models;
|
using PARR.DAL.Models;
|
||||||
using PARR.DAL.NextRunServices;
|
|
||||||
using PARR.DAL.Services.Interfaces;
|
using PARR.DAL.Services.Interfaces;
|
||||||
using PARR.EsppSync.Domain;
|
using PARR.EsppSync.Helpers;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace PARR.EsppSync
|
namespace PARR.EsppSync
|
||||||
@@ -30,8 +28,10 @@ namespace PARR.EsppSync
|
|||||||
|
|
||||||
public async Task SyncEsppObjectAsync(
|
public async Task SyncEsppObjectAsync(
|
||||||
string str,
|
string str,
|
||||||
ParserHandlerDelegate<EsppObject> parser,
|
ParseStringToEsppObject<EsppObject> parseStringToEsppObject,
|
||||||
ConvertDbObjToComparisonObjHandlerDelegate<EsppObject> converterToEsppObject
|
ConvertDbToEsppObject<EsppObject> converterDbToEsppObject,
|
||||||
|
CustomComparisionCheckAsync<EsppObject>? customComparisionAsync = null,
|
||||||
|
AfterParseStringToEsppObjectAsync<EsppObject>? afterParseStringToEsppObjectAsync = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
logger.LogDebug("Получил строку. Начинаю работать. Строка: {String}", str);
|
logger.LogDebug("Получил строку. Начинаю работать. Строка: {String}", str);
|
||||||
@@ -42,7 +42,7 @@ namespace PARR.EsppSync
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var esppObject = parser.Invoke(str);
|
var esppObject = parseStringToEsppObject.Invoke(str);
|
||||||
|
|
||||||
if (esppObject == null)
|
if (esppObject == null)
|
||||||
{
|
{
|
||||||
@@ -50,12 +50,16 @@ namespace PARR.EsppSync
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Вызовем кастомный метод, если он есть
|
||||||
|
if (afterParseStringToEsppObjectAsync != null)
|
||||||
|
await afterParseStringToEsppObjectAsync.Invoke(esppObject);
|
||||||
|
|
||||||
|
|
||||||
using (var scope = serviceProvider.CreateScope())
|
using (var scope = serviceProvider.CreateScope())
|
||||||
{
|
{
|
||||||
var templateService = GetServiceInScope<ITemplateService>(scope);
|
var templateService = GetServiceInScope<ITemplateService>(scope);
|
||||||
var robotConfigurationService = GetServiceInScope<IRobotConfigurationService>(scope);
|
var robotConfigurationService = GetServiceInScope<IRobotConfigurationService>(scope);
|
||||||
var shortcodesService = GetServiceInScope<IShortcodesService>(scope);
|
var shortcodesService = GetServiceInScope<IShortcodesService>(scope);
|
||||||
var nextRunService = GetServiceInScope<INextRunServiceV2>(scope);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -92,7 +96,7 @@ namespace PARR.EsppSync
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbObjectInEsppObject = converterToEsppObject.Invoke(template);
|
var dbObjectInEsppObject = converterDbToEsppObject.Invoke(template);
|
||||||
|
|
||||||
await ApplyShortcodesAsync(dbObjectInEsppObject, template, shortcodesService);
|
await ApplyShortcodesAsync(dbObjectInEsppObject, template, shortcodesService);
|
||||||
|
|
||||||
@@ -113,14 +117,28 @@ namespace PARR.EsppSync
|
|||||||
logger.LogDebug("Объект активирован в ПАРР. Сравниваем все поля. {TemplateName}", esppObject.TemplateName);
|
logger.LogDebug("Объект активирован в ПАРР. Сравниваем все поля. {TemplateName}", esppObject.TemplateName);
|
||||||
isChanged = IsChanged(esppObject, dbObjectInEsppObject, esppObject.TemplateName);
|
isChanged = IsChanged(esppObject, dbObjectInEsppObject, esppObject.TemplateName);
|
||||||
|
|
||||||
// если это сравнение расписаний, рассчитать nextRun, и сравнить все три nextRun, БД - ЕСПП - Расчитанное
|
// выполняем кастомную дополнительную проверку (только если isChanged==false, чтоб лишний раз не гонять)
|
||||||
// будем считать, только если НЕТ изменений в остальных полях, если изменения есть, зачем лишний раз напрягать, потом в контроллере все пересчитается
|
if (!isChanged)
|
||||||
if (!isChanged && dbObjectInEsppObject is EsppObjectSchedule dbValues && esppObject is EsppObjectSchedule esppValues)
|
|
||||||
{
|
{
|
||||||
var isEqualNextRun = await IsEqualNextRunAsync(nextRunService, dbValues, esppValues, template.Id);
|
if (customComparisionAsync != null)
|
||||||
|
{
|
||||||
if (isEqualNextRun == false)
|
// добавлена дополнительная проверка
|
||||||
|
var isCustomComparision = await customComparisionAsync.Invoke(esppObject, dbObjectInEsppObject, template.Id);
|
||||||
|
if (isCustomComparision)
|
||||||
|
{
|
||||||
|
logger.LogDebug("Дополнительная проверка прошла.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// если дополнительная проверка не прошла, то говорим что есть изменения
|
||||||
isChanged = true;
|
isChanged = true;
|
||||||
|
logger.LogDebug("Дополнительная проверка не прошла, ставим статус isChanged: {isChanged}", isChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogDebug("Дополнительная проверка отсутствует");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,8 +270,8 @@ namespace PARR.EsppSync
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
//Replace("\r","").Replace("\n","") - в подробном описании могут быть переносы строк, в Rabbit прилетает без переносов. Убираем переносы для стравнения
|
//Replace("\r","").Replace("\n","") - в подробном описании могут быть переносы строк, в Rabbit прилетает без переносов. Убираем переносы для стравнения
|
||||||
var dbValueStr = Normalize(dbValue!.ToString()!);
|
var dbValueStr = EsppSyncHelpers.Normalize(dbValue!.ToString());
|
||||||
var esppValueStr = Normalize(esppValue!.ToString()!);
|
var esppValueStr = EsppSyncHelpers.Normalize(esppValue!.ToString());
|
||||||
|
|
||||||
if (dbValueStr != esppValueStr)
|
if (dbValueStr != esppValueStr)
|
||||||
{
|
{
|
||||||
@@ -266,87 +284,5 @@ namespace PARR.EsppSync
|
|||||||
return false;
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Рассчитать nextRun в часовом поясе УЗ робота
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nextRunService"></param>
|
|
||||||
/// <param name="templateId"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private async Task<DateTimeOffset?> CalcNextRunAsync(INextRunServiceV2 nextRunService, Guid templateId)
|
|
||||||
{
|
|
||||||
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>
|
|
||||||
/// Сравнить NextRun из БД, ЕСПП, рассчитанный
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dbValues"></param>
|
|
||||||
/// <param name="esppValues"></param>
|
|
||||||
/// <param name="templateId"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private async Task<bool> IsEqualNextRunAsync(INextRunServiceV2 nextRunService, EsppObjectSchedule dbValues, EsppObjectSchedule esppValues, Guid templateId)
|
|
||||||
{
|
|
||||||
// в часовой зоне робота
|
|
||||||
var calculatedNextRun = await CalcNextRunAsync(nextRunService, templateId);
|
|
||||||
if (calculatedNextRun == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var scheduledCalculated = EsppScheduleHelpers.GetNextRun(calculatedNextRun.Value);
|
|
||||||
var basisTimeCalculated = EsppScheduleHelpers.GetGenerationTime(calculatedNextRun.Value);
|
|
||||||
|
|
||||||
logger.LogDebug("Рассчитанные значения для шаблона '{templateName}', {templateId}, следующее срабатывание {scheduledCalculated}, время создания наряда: {basisTimeCalculated}",
|
|
||||||
dbValues.TemplateName, templateId, scheduledCalculated, basisTimeCalculated);
|
|
||||||
|
|
||||||
if (Normalize(dbValues.Scheduled) != Normalize(esppValues.Scheduled) || Normalize(dbValues.Scheduled) != scheduledCalculated || Normalize(esppValues.Scheduled) != scheduledCalculated)
|
|
||||||
{
|
|
||||||
logger.LogInformation("Не совпадают поля ({propName}), dbValueStr: {dbValueStr}, esppValueStr: {esppValueStr}, scheduledCalculated: {scheduledCalculated}. Имя шаблона: {templateName}",
|
|
||||||
nameof(dbValues.Scheduled), dbValues.Scheduled, esppValues.Scheduled, scheduledCalculated, dbValues.TemplateName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Normalize(dbValues.BasisTime) != Normalize(esppValues.BasisTime) || Normalize(dbValues.BasisTime) != basisTimeCalculated || Normalize(esppValues.BasisTime) != basisTimeCalculated)
|
|
||||||
{
|
|
||||||
logger.LogInformation("Не совпадают поля ({propName}), dbValueStr: {dbValueStr}, esppValueStr: {esppValueStr}, basisTimeCalculated: {basisTimeCalculated}. Имя шаблона: {templateName}",
|
|
||||||
nameof(dbValues.BasisTime), dbValues.BasisTime, esppValues.BasisTime, basisTimeCalculated, dbValues.TemplateName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.LogDebug("Значения nextRun в БД, ЕСПП, расчитанное, все совпадают. Scheduled: {scheduled}, basisTime: {basisTime}", scheduledCalculated, basisTimeCalculated);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,16 +59,7 @@ namespace PARR.EsppTemplateSync
|
|||||||
|
|
||||||
private async Task SyncTemplateAsync(string str)
|
private async Task SyncTemplateAsync(string str)
|
||||||
{
|
{
|
||||||
//using (var scope = serviceProvider.CreateScope())
|
|
||||||
//{
|
|
||||||
// var syncService = scope.ServiceProvider.GetService<ISyncService<EsppObjectTemplate>>();
|
|
||||||
|
|
||||||
// if (syncService == null)
|
|
||||||
// throw new Exception($"Не смог получить серивс {nameof(ITemplateSyncer)}");
|
|
||||||
|
|
||||||
await syncService.SyncEsppObjectAsync(str, ParseStrToEsppObject, ConvertDbObjToEsppObj);
|
await syncService.SyncEsppObjectAsync(str, ParseStrToEsppObject, ConvertDbObjToEsppObj);
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user