2026-06-04 14:14:10 +10:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
2025-05-30 09:50:47 +10:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2025-11-27 17:11:14 +10:00
|
|
|
|
using PARR.AIHITMainSyncer.Settings;
|
2026-04-14 16:27:27 +10:00
|
|
|
|
using PARR.Core.Common.Interfaces;
|
2026-04-30 10:35:31 +10:00
|
|
|
|
using PARR.Core.Repositories.Interfaces.Unit;
|
2026-06-04 14:14:10 +10:00
|
|
|
|
using PARR.Core.Services.UnitService.Interfaces;
|
2026-04-14 12:01:49 +10:00
|
|
|
|
using PARR.Domain.Common.Rabbit.Messages;
|
2026-04-30 10:35:31 +10:00
|
|
|
|
using PARR.Domain.Entities.Unit;
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
|
|
|
|
|
namespace PARR.AIHITMainSyncer.Services
|
|
|
|
|
|
{
|
|
|
|
|
|
internal class SyncerService : ISyncerService
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly ILogger<SyncerService> logger;
|
|
|
|
|
|
private readonly ITransformService transformService;
|
2026-06-04 14:14:10 +10:00
|
|
|
|
private readonly IUnitRepository unitRepository;
|
|
|
|
|
|
private readonly IUnitFieldRepository unitFieldRepository;
|
|
|
|
|
|
private readonly IUnitFieldValueRepository unitFieldValueRepository;
|
2025-11-27 17:11:14 +10:00
|
|
|
|
private readonly FieldValueReplacementsSettings fieldValueReplacementsSettings;
|
2026-06-04 14:14:10 +10:00
|
|
|
|
private readonly IUnitService unitService;
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
|
|
|
|
|
public SyncerService(
|
|
|
|
|
|
ILogger<SyncerService> logger,
|
|
|
|
|
|
ITransformService transformService,
|
2026-06-04 14:14:10 +10:00
|
|
|
|
IUnitRepository unitRepository,
|
|
|
|
|
|
IUnitFieldRepository unitFieldRepository,
|
|
|
|
|
|
IUnitFieldValueRepository unitFieldValueRepository,
|
|
|
|
|
|
FieldValueReplacementsSettings fieldValueReplacementsSettings,
|
|
|
|
|
|
IUnitService unitService
|
2025-05-19 16:09:55 +10:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.logger = logger;
|
|
|
|
|
|
this.transformService = transformService;
|
2026-06-04 14:14:10 +10:00
|
|
|
|
this.unitRepository = unitRepository;
|
|
|
|
|
|
this.unitFieldRepository = unitFieldRepository;
|
|
|
|
|
|
this.unitFieldValueRepository = unitFieldValueRepository;
|
2025-11-27 17:11:14 +10:00
|
|
|
|
this.fieldValueReplacementsSettings = fieldValueReplacementsSettings;
|
2026-06-04 14:14:10 +10:00
|
|
|
|
this.unitService = unitService;
|
2025-05-19 16:09:55 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async Task SyncAsync(string msg)
|
|
|
|
|
|
{
|
2026-02-05 14:27:02 +10:00
|
|
|
|
logger.LogInformation("Запуск синхронизации данных из очереди сообщений в ПАРР.");
|
2025-05-19 16:09:55 +10:00
|
|
|
|
var objFromQuery = transformService.GetModelFromJson<AihitMainDataMq>(msg);
|
|
|
|
|
|
if (objFromQuery == null)
|
2026-02-05 14:27:02 +10:00
|
|
|
|
{
|
|
|
|
|
|
logger.LogWarning("Не удалось десериализовать сообщение в объект AihitMainDataMq. Сообщение: {Message}", msg);
|
2025-05-19 16:09:55 +10:00
|
|
|
|
return;
|
2026-02-05 14:27:02 +10:00
|
|
|
|
}
|
2025-05-19 16:09:55 +10:00
|
|
|
|
await SyncUnitAsync(objFromQuery);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private async Task SyncUnitAsync(AihitMainDataMq objFromQuery)
|
|
|
|
|
|
{
|
2026-02-05 14:27:02 +10:00
|
|
|
|
logger.LogDebug("Начинаем синхронизацию Unit для объекта с именем: {UnitName}", objFromQuery.Name);
|
|
|
|
|
|
|
2025-09-25 13:54:33 +10:00
|
|
|
|
//Получаем сразу словарь атрибутов, чтобы не ходить каждый раз в базу
|
2026-06-04 14:14:10 +10:00
|
|
|
|
var fieldsFromDB = await unitFieldRepository.Get().AsNoTracking().ToListAsync();
|
2025-09-25 13:54:33 +10:00
|
|
|
|
|
|
|
|
|
|
if (!objFromQuery.Properties.Any())
|
2026-02-05 14:27:02 +10:00
|
|
|
|
{
|
|
|
|
|
|
logger.LogWarning("Объект {UnitName} не содержит свойств для синхронизации", objFromQuery.Name);
|
2025-09-25 13:54:33 +10:00
|
|
|
|
return;
|
2026-02-05 14:27:02 +10:00
|
|
|
|
}
|
2025-09-25 13:54:33 +10:00
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
// 1. Создаем копию и нормализуем свойства
|
2025-11-27 17:11:14 +10:00
|
|
|
|
var normalizedProperties = new Dictionary<string, string?>();
|
|
|
|
|
|
foreach (var kvp in objFromQuery.Properties)
|
|
|
|
|
|
normalizedProperties[kvp.Key.Trim()] = kvp.Value?.Trim();
|
|
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
// 2. Применяем замены значений
|
2025-11-27 17:11:14 +10:00
|
|
|
|
ApplyFieldValueReplacements(normalizedProperties);
|
2025-11-19 11:52:41 +10:00
|
|
|
|
|
2025-09-25 13:54:33 +10:00
|
|
|
|
var listAihitData = new List<KeyValuePair<string, string?>>();
|
|
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
// 3. Обрабатываем простые значения
|
2025-11-27 17:11:14 +10:00
|
|
|
|
var propertisWithSimpleValues = normalizedProperties
|
2025-09-25 16:28:18 +10:00
|
|
|
|
.Where(t => fieldsFromDB.Any(a => IsStringEqual(t.Key.Trim(), a.AihitName) && a.IsMultipleValue != true))
|
2025-09-25 13:54:33 +10:00
|
|
|
|
.Select(s => new KeyValuePair<string, string?>(s.Key.Trim(), s.Value?.Trim())).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
listAihitData.AddRange(propertisWithSimpleValues);
|
|
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
// 4. Обрабатываем множественные значения
|
2025-09-25 16:28:18 +10:00
|
|
|
|
var propertiesWithMultipleValues = objFromQuery.Properties.Where(t => !propertisWithSimpleValues.Any(a => t.Key.Trim() == a.Key)).ToList();
|
2025-09-25 13:54:33 +10:00
|
|
|
|
|
|
|
|
|
|
propertiesWithMultipleValues.ForEach(p => listAihitData.AddRange(
|
2026-03-25 11:57:09 +10:00
|
|
|
|
SplitValues(p, fieldsFromDB).Distinct().Select(s => new KeyValuePair<string, string?>(s.Key.Trim(), s.Value?.Trim()))
|
2025-09-25 13:54:33 +10:00
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
var unit = await GetUnit(objFromQuery.Name);
|
|
|
|
|
|
|
2025-11-19 11:52:41 +10:00
|
|
|
|
var incomingPairs = listAihitData
|
|
|
|
|
|
.Select(kv => new { kv.Key, kv.Value })
|
|
|
|
|
|
.ToList();
|
2025-09-25 13:54:33 +10:00
|
|
|
|
|
2025-11-27 17:11:14 +10:00
|
|
|
|
// Определяем, какие ПОЛЯ обновляются этим сообщением чтобы случайно не удалить не наши
|
2025-11-19 14:06:41 +10:00
|
|
|
|
var fieldsInMessage = listAihitData
|
2025-12-30 11:57:15 +10:00
|
|
|
|
.Select(kv => kv.Key)
|
|
|
|
|
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
2025-11-19 14:06:41 +10:00
|
|
|
|
.ToHashSet();
|
|
|
|
|
|
|
2025-11-19 11:52:41 +10:00
|
|
|
|
var unitValuesToRemove = unit.UnitValues
|
2025-11-19 14:06:41 +10:00
|
|
|
|
.Where(uv =>
|
|
|
|
|
|
uv.Field != null &&
|
2025-12-30 11:57:15 +10:00
|
|
|
|
fieldsInMessage.Contains(uv.Field.AihitName) &&
|
|
|
|
|
|
!listAihitData.Any(kv =>
|
2025-11-19 14:06:41 +10:00
|
|
|
|
IsStringEqual(uv.Field.AihitName, kv.Key) &&
|
|
|
|
|
|
IsStringEqual(uv.Value?.Value, kv.Value)
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
2025-11-19 11:52:41 +10:00
|
|
|
|
.ToList();
|
|
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
bool hasChanges = false;
|
|
|
|
|
|
|
2026-02-05 14:27:02 +10:00
|
|
|
|
if (unitValuesToRemove.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
logger.LogDebug("Удаляем {Count} устаревших значений для Unit {UnitName}", unitValuesToRemove.Count, unit.Name);
|
|
|
|
|
|
unitValuesToRemove.ForEach(uv => unit.UnitValues.Remove(uv));
|
2026-06-04 14:14:10 +10:00
|
|
|
|
hasChanges = true;
|
2026-02-05 14:27:02 +10:00
|
|
|
|
}
|
2025-09-25 13:54:33 +10:00
|
|
|
|
|
2025-11-19 11:52:41 +10:00
|
|
|
|
var unitValueMissing = listAihitData.Where(t =>
|
2025-09-25 13:54:33 +10:00
|
|
|
|
!unit.UnitValues.Any(a =>
|
|
|
|
|
|
IsStringEqual(t.Key, a.Field!.AihitName) &&
|
|
|
|
|
|
IsStringEqual(t.Value, a.Value!.Value)
|
|
|
|
|
|
)).ToList();
|
|
|
|
|
|
|
2025-11-19 11:52:41 +10:00
|
|
|
|
if (unitValueMissing.Any())
|
2025-09-25 13:54:33 +10:00
|
|
|
|
{
|
2026-02-05 14:27:02 +10:00
|
|
|
|
logger.LogDebug("Найдено {Count} отсутствующих пар значений для Unit {UnitName}", unitValueMissing.Count, unit.Name);
|
|
|
|
|
|
|
2025-11-19 11:52:41 +10:00
|
|
|
|
var fieldsInChanges = unitValueMissing.Select(s => s.Key).Distinct().ToList();
|
|
|
|
|
|
var valuesInChanges = unitValueMissing.Select(s => s.Value).Distinct().ToList();
|
2025-09-25 13:54:33 +10:00
|
|
|
|
|
|
|
|
|
|
await SyncFieldsAsync(fieldsInChanges!, fieldsFromDB);
|
|
|
|
|
|
await SyncValuesAsync(valuesInChanges);
|
|
|
|
|
|
|
2025-11-19 11:52:41 +10:00
|
|
|
|
var normalizedFieldNames = fieldsInChanges
|
|
|
|
|
|
.Select(n => n?.Trim())
|
|
|
|
|
|
.Where(n => n != null)
|
|
|
|
|
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
2025-09-25 13:54:33 +10:00
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
var fieldsNameWithId = await unitFieldRepository.Get().AsNoTracking()
|
2025-11-19 11:52:41 +10:00
|
|
|
|
.Where(f => normalizedFieldNames.Contains(f.AihitName))
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
|
|
var normalizedValues = valuesInChanges
|
|
|
|
|
|
.Where(v => v != null)
|
|
|
|
|
|
.Select(v => v!.Trim())
|
|
|
|
|
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
2025-09-25 13:54:33 +10:00
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
var valuesWithId = await unitFieldValueRepository.Get().AsNoTracking()
|
2025-11-19 11:52:41 +10:00
|
|
|
|
.Where(v =>
|
|
|
|
|
|
(v.Value == null && valuesInChanges.Contains(null)) ||
|
|
|
|
|
|
(v.Value != null && normalizedValues.Contains(v.Value)))
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var change in unitValueMissing)
|
2025-09-25 13:54:33 +10:00
|
|
|
|
ApplyChange(unit, fieldsNameWithId, valuesWithId, change);
|
|
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
hasChanges = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
logger.LogDebug("Нет новых значений для синхронизации для Unit {UnitName}", unit.Name);
|
|
|
|
|
|
|
|
|
|
|
|
// Сохраняем изменения и инвалидируем кэш только если были реальные изменения
|
|
|
|
|
|
if (hasChanges)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!await unitRepository.CommitAsync())
|
2026-02-05 14:27:02 +10:00
|
|
|
|
{
|
2026-06-04 14:14:10 +10:00
|
|
|
|
logger.LogError("Не удалось применить изменения по актуализации атрибутов Unit {UnitName} в базе данных", unit.Name);
|
2026-02-05 14:27:02 +10:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
// Инвалидация кэша после успешного сохранения
|
|
|
|
|
|
await unitService.RemoveFromCacheAsync(unit.Id);
|
|
|
|
|
|
logger.LogInformation("Успешно обновлены атрибуты для Unit {UnitName}. Кэш инвалидирован.", unit.Name);
|
2025-09-25 13:54:33 +10:00
|
|
|
|
}
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
2026-02-05 14:27:02 +10:00
|
|
|
|
logger.LogInformation("Завершена синхронизация Unit {UnitName}", unit.Name);
|
2025-09-25 13:54:33 +10:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
2026-03-25 11:57:09 +10:00
|
|
|
|
private List<KeyValuePair<string, string?>> SplitValues(KeyValuePair<string, string?> multiValueProperty, List<UnitField> fieldsFromDB)
|
2025-09-25 13:54:33 +10:00
|
|
|
|
{
|
2026-03-25 11:57:09 +10:00
|
|
|
|
var result = new List<KeyValuePair<string, string?>>();
|
2025-09-25 13:54:33 +10:00
|
|
|
|
|
2026-03-25 11:57:09 +10:00
|
|
|
|
// Обработка null или пустой строки
|
|
|
|
|
|
if (string.IsNullOrEmpty(multiValueProperty.Value))
|
|
|
|
|
|
{
|
|
|
|
|
|
result.Add(new KeyValuePair<string, string?>(multiValueProperty.Key, multiValueProperty.Value));
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Поиск конфигурации поля для тега
|
|
|
|
|
|
var tagUnitField = fieldsFromDB.FirstOrDefault(f => f.Code == "tag");
|
|
|
|
|
|
bool isTagProperty = tagUnitField != null && multiValueProperty.Key == tagUnitField.AihitName;
|
|
|
|
|
|
|
|
|
|
|
|
string[] splitValues;
|
|
|
|
|
|
if (isTagProperty)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Для тегов нормализуем разделители до пробела
|
|
|
|
|
|
var normalizedValue = multiValueProperty.Value
|
|
|
|
|
|
.Replace(';', ' ')
|
2026-05-05 12:15:34 +10:00
|
|
|
|
.Replace('#', ' ')
|
2026-03-25 11:57:09 +10:00
|
|
|
|
.Replace(',', ' ');
|
|
|
|
|
|
|
|
|
|
|
|
splitValues = normalizedValue.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
}
|
2025-09-25 13:54:33 +10:00
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-25 11:57:09 +10:00
|
|
|
|
// Для остальных полей стандартный разделитель - запятая
|
|
|
|
|
|
splitValues = multiValueProperty.Value.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var value in splitValues)
|
|
|
|
|
|
{
|
|
|
|
|
|
var trimmedValue = value.Trim();
|
2025-09-25 13:54:33 +10:00
|
|
|
|
|
2026-03-25 11:57:09 +10:00
|
|
|
|
// Добавляем только непустые значения после обрезки пробелов
|
|
|
|
|
|
if (!string.IsNullOrEmpty(trimmedValue))
|
|
|
|
|
|
{
|
|
|
|
|
|
result.Add(new KeyValuePair<string, string?>(multiValueProperty.Key, trimmedValue));
|
|
|
|
|
|
}
|
2025-09-25 13:54:33 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-25 11:57:09 +10:00
|
|
|
|
return result;
|
2025-09-25 13:54:33 +10:00
|
|
|
|
}
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
2025-09-25 16:28:18 +10:00
|
|
|
|
|
2025-05-30 09:50:47 +10:00
|
|
|
|
private void ApplyChange(Unit unit, List<UnitField> fieldsNameWithId, List<UnitFieldValue> valuesWithId, KeyValuePair<string, string?> change)
|
2025-05-19 16:09:55 +10:00
|
|
|
|
{
|
2026-02-05 14:27:02 +10:00
|
|
|
|
var field = fieldsNameWithId.FirstOrDefault(t => IsStringEqual(t.AihitName, change.Key));
|
|
|
|
|
|
var value = valuesWithId.FirstOrDefault(t => IsStringEqual(t.Value, change.Value));
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
2026-02-05 14:27:02 +10:00
|
|
|
|
if (field == null || value == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
logger.LogError("Ошибка присвоения значения({Value}) аттрибуту({Key}). FieldId - {FieldId}, ValueId - {ValueId}",
|
|
|
|
|
|
change.Value, change.Key, field?.Id, value?.Id);
|
|
|
|
|
|
}
|
2025-09-25 13:54:33 +10:00
|
|
|
|
else
|
2025-05-30 09:50:47 +10:00
|
|
|
|
{
|
2026-02-05 14:27:02 +10:00
|
|
|
|
var unitInValue = new UnitInValue
|
2025-05-30 09:50:47 +10:00
|
|
|
|
{
|
|
|
|
|
|
UnitId = unit.Id,
|
2026-02-05 14:27:02 +10:00
|
|
|
|
FieldId = field.Id,
|
|
|
|
|
|
ValueId = value.Id,
|
2025-05-30 09:50:47 +10:00
|
|
|
|
DateCreated = DateTimeOffset.UtcNow
|
|
|
|
|
|
};
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
2026-02-05 14:27:02 +10:00
|
|
|
|
unit.UnitValues.Add(unitInValue);
|
2025-05-30 09:50:47 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-25 16:28:18 +10:00
|
|
|
|
}
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
2025-05-21 11:53:07 +10:00
|
|
|
|
|
2026-07-03 11:17:48 +10:00
|
|
|
|
private async Task SyncValuesAsync(List<string?> values)
|
2025-05-19 16:09:55 +10:00
|
|
|
|
{
|
2025-11-19 11:52:41 +10:00
|
|
|
|
bool hasNull = values.Any(v => v is null);
|
|
|
|
|
|
var normalizedNoneNullValues = values
|
|
|
|
|
|
.Where(v => v != null)
|
|
|
|
|
|
.Select(v => v!.Trim())
|
|
|
|
|
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
|
|
|
|
.ToList();
|
2025-05-30 09:50:47 +10:00
|
|
|
|
|
2026-02-05 14:27:02 +10:00
|
|
|
|
logger.LogDebug("Синхронизируем {Count} значений (включая null: {HasNull})", normalizedNoneNullValues.Count, hasNull);
|
|
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
var existingValuesInDb = await unitFieldValueRepository.Get()
|
2025-11-19 11:52:41 +10:00
|
|
|
|
.Where(v => normalizedNoneNullValues.Contains(v.Value!) || v.Value == null)
|
|
|
|
|
|
.Select(v => v.Value)
|
|
|
|
|
|
.ToListAsync();
|
2025-05-30 09:50:47 +10:00
|
|
|
|
|
2025-11-19 11:52:41 +10:00
|
|
|
|
if (!hasNull)
|
|
|
|
|
|
existingValuesInDb.RemoveAll(v => v is null);
|
2025-05-30 09:50:47 +10:00
|
|
|
|
|
2025-11-19 11:52:41 +10:00
|
|
|
|
var newNonNullValues = normalizedNoneNullValues
|
|
|
|
|
|
.Where(v => !existingValuesInDb.Any(ev =>
|
|
|
|
|
|
string.Equals(ev, v, StringComparison.OrdinalIgnoreCase)))
|
|
|
|
|
|
.ToList();
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
2025-11-19 11:52:41 +10:00
|
|
|
|
var newValuesToInsert = new List<string?>();
|
|
|
|
|
|
newValuesToInsert.AddRange(newNonNullValues);
|
2025-05-21 11:53:07 +10:00
|
|
|
|
|
2025-11-19 11:52:41 +10:00
|
|
|
|
if (hasNull && !existingValuesInDb.Contains(null))
|
|
|
|
|
|
newValuesToInsert.Add(null);
|
|
|
|
|
|
|
2026-06-25 12:19:32 +10:00
|
|
|
|
// Если нет новых значений, выходим БЕЗ вызова CommitAsync
|
2025-11-19 11:52:41 +10:00
|
|
|
|
if (newValuesToInsert.Count == 0)
|
2026-02-05 14:27:02 +10:00
|
|
|
|
{
|
|
|
|
|
|
logger.LogDebug("Нет новых значений для добавления в базу данных");
|
2025-11-19 11:52:41 +10:00
|
|
|
|
return;
|
2026-02-05 14:27:02 +10:00
|
|
|
|
}
|
2025-11-19 11:52:41 +10:00
|
|
|
|
|
|
|
|
|
|
var newFieldValues = newValuesToInsert.Select(v =>
|
|
|
|
|
|
new UnitFieldValue
|
2025-05-30 09:50:47 +10:00
|
|
|
|
{
|
2025-11-19 11:52:41 +10:00
|
|
|
|
Id = Guid.NewGuid(),
|
2026-06-04 14:14:10 +10:00
|
|
|
|
Value = v
|
2025-11-19 11:52:41 +10:00
|
|
|
|
})
|
|
|
|
|
|
.ToList();
|
2025-05-30 09:50:47 +10:00
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
if (!await unitFieldValueRepository.AddRangeAsync(newFieldValues))
|
2025-11-19 11:52:41 +10:00
|
|
|
|
{
|
2026-06-04 14:14:10 +10:00
|
|
|
|
logger.LogError("Не удалось добавить {Count} новых значений FieldValues в контекст", newValuesToInsert.Count);
|
2026-06-25 12:19:32 +10:00
|
|
|
|
return;
|
2025-11-19 11:52:41 +10:00
|
|
|
|
}
|
2026-06-25 12:19:32 +10:00
|
|
|
|
// Коммит выполняется ТОЛЬКО если были добавлены новые сущности
|
|
|
|
|
|
if (!await unitFieldValueRepository.CommitAsync())
|
2025-11-19 11:52:41 +10:00
|
|
|
|
{
|
2026-06-25 12:19:32 +10:00
|
|
|
|
logger.LogError("Не удалось сохранить {Count} новых значений FieldValues в базу данных", newValuesToInsert.Count);
|
|
|
|
|
|
return;
|
2025-05-21 11:53:07 +10:00
|
|
|
|
}
|
2026-06-25 12:19:32 +10:00
|
|
|
|
|
|
|
|
|
|
logger.LogDebug("Успешно сохранено {Count} новых значений FieldValues", newValuesToInsert.Count);
|
2025-05-30 09:50:47 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-19 11:52:41 +10:00
|
|
|
|
|
2025-09-25 16:28:18 +10:00
|
|
|
|
private async Task SyncFieldsAsync(List<string> fieldsFromAihit, List<UnitField> fieldsFromDB)
|
2025-05-30 09:50:47 +10:00
|
|
|
|
{
|
2026-02-05 14:27:02 +10:00
|
|
|
|
var newFields = fieldsFromAihit.Where(t => !fieldsFromDB.Any(f => IsStringEqual(f.AihitName, t))).ToList();
|
|
|
|
|
|
|
2026-06-25 12:19:32 +10:00
|
|
|
|
// Если нет новых полей, выходим БЕЗ вызова CommitAsync
|
2026-02-05 14:27:02 +10:00
|
|
|
|
if (!newFields.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
logger.LogDebug("Нет новых полей для добавления в базу данных");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-25 13:54:33 +10:00
|
|
|
|
|
2026-02-05 14:27:02 +10:00
|
|
|
|
logger.LogDebug("Найдено {Count} новых полей для добавления", newFields.Count);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var item in newFields)
|
2025-05-21 11:53:07 +10:00
|
|
|
|
{
|
2026-02-05 14:27:02 +10:00
|
|
|
|
var field = new UnitField
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = Guid.NewGuid(),
|
|
|
|
|
|
AihitName = item,
|
|
|
|
|
|
EsppName = null,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
if (!await unitFieldRepository.CreateAsync(field))
|
2026-02-05 14:27:02 +10:00
|
|
|
|
{
|
2026-06-04 14:14:10 +10:00
|
|
|
|
logger.LogError("Не удалось добавить поле в контекст: {FieldName}", item);
|
2026-06-25 12:19:32 +10:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Коммит выполняется ТОЛЬКО после успешного добавления конкретного поля
|
|
|
|
|
|
if (!await unitFieldRepository.CommitAsync())
|
|
|
|
|
|
{
|
|
|
|
|
|
logger.LogError("Не удалось сохранить поле в базу данных: {FieldName}", item);
|
2026-02-05 14:27:02 +10:00
|
|
|
|
}
|
|
|
|
|
|
else
|
2025-05-21 11:53:07 +10:00
|
|
|
|
{
|
2026-06-25 12:19:32 +10:00
|
|
|
|
logger.LogDebug("Успешно сохранено поле: {FieldName}", item);
|
2025-05-21 11:53:07 +10:00
|
|
|
|
}
|
2026-07-03 11:17:48 +10:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
2025-05-30 09:50:47 +10:00
|
|
|
|
private bool IsStringEqual(string? value1, string? value2)
|
2025-05-19 16:09:55 +10:00
|
|
|
|
{
|
2025-11-19 11:52:41 +10:00
|
|
|
|
return string.Equals(
|
|
|
|
|
|
value1?.Trim(),
|
|
|
|
|
|
value2?.Trim(),
|
|
|
|
|
|
StringComparison.OrdinalIgnoreCase);
|
2025-05-30 09:50:47 +10:00
|
|
|
|
}
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
|
|
|
|
|
|
2025-05-30 09:50:47 +10:00
|
|
|
|
private async Task<Unit> GetUnit(string name)
|
|
|
|
|
|
{
|
|
|
|
|
|
var unit = await GetUnitByName(name.Trim());
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
2025-05-30 09:50:47 +10:00
|
|
|
|
if (unit == null)
|
2026-02-05 14:27:02 +10:00
|
|
|
|
{
|
|
|
|
|
|
logger.LogDebug("Unit с именем {UnitName} не найден, создаем новый", name);
|
2025-05-30 09:50:47 +10:00
|
|
|
|
unit = await CreateUnitAsync(name);
|
2026-02-05 14:27:02 +10:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
logger.LogDebug("Найден существующий Unit с именем {UnitName}", name);
|
|
|
|
|
|
}
|
2025-05-19 16:09:55 +10:00
|
|
|
|
|
2025-05-30 09:50:47 +10:00
|
|
|
|
return unit;
|
2025-05-21 11:53:07 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-25 16:28:18 +10:00
|
|
|
|
|
2025-05-30 09:50:47 +10:00
|
|
|
|
private async Task<Unit> CreateUnitAsync(string name)
|
2025-05-21 11:53:07 +10:00
|
|
|
|
{
|
2025-11-19 11:52:41 +10:00
|
|
|
|
var unit = new Unit { Name = name.Trim().ToUpperInvariant() };
|
2025-05-21 11:53:07 +10:00
|
|
|
|
|
2026-06-04 14:14:10 +10:00
|
|
|
|
if (!await unitRepository.CreateAsync(unit) || !await unitRepository.CommitAsync())
|
2026-02-05 14:27:02 +10:00
|
|
|
|
{
|
|
|
|
|
|
logger.LogError("Не удалось создать Unit {UnitName}", unit.Name);
|
|
|
|
|
|
throw new InvalidOperationException($"Не удалось создать Unit {unit.Name}");
|
|
|
|
|
|
}
|
2025-05-30 09:50:47 +10:00
|
|
|
|
else
|
2026-02-05 14:27:02 +10:00
|
|
|
|
{
|
|
|
|
|
|
logger.LogInformation("Создан Unit: {UnitName}", unit.Name);
|
|
|
|
|
|
}
|
2025-05-30 09:50:47 +10:00
|
|
|
|
return unit;
|
|
|
|
|
|
}
|
2025-05-21 11:53:07 +10:00
|
|
|
|
|
|
|
|
|
|
|
2025-05-30 09:50:47 +10:00
|
|
|
|
public async Task<Unit?> GetUnitByName(string name)
|
|
|
|
|
|
{
|
2025-11-19 11:52:41 +10:00
|
|
|
|
var normalized = name.Trim().ToUpperInvariant();
|
2025-05-30 09:50:47 +10:00
|
|
|
|
return await GetUnitWithFieldsAndValues()
|
2025-11-19 11:52:41 +10:00
|
|
|
|
.FirstOrDefaultAsync(u => u.Name == normalized);
|
2025-05-30 09:50:47 +10:00
|
|
|
|
}
|
2025-05-21 11:53:07 +10:00
|
|
|
|
|
|
|
|
|
|
|
2025-05-30 09:50:47 +10:00
|
|
|
|
private IQueryable<Unit> GetUnitWithFieldsAndValues()
|
|
|
|
|
|
{
|
2026-06-04 14:14:10 +10:00
|
|
|
|
return unitRepository.Get()
|
2025-05-30 09:50:47 +10:00
|
|
|
|
.Include(t => t.UnitValues)
|
|
|
|
|
|
.ThenInclude(uv => uv.Field)
|
|
|
|
|
|
.Include(t => t.UnitValues)
|
|
|
|
|
|
.ThenInclude(uf => uf.Value);
|
2025-05-19 16:09:55 +10:00
|
|
|
|
}
|
2025-11-27 17:11:14 +10:00
|
|
|
|
|
|
|
|
|
|
private void ApplyFieldValueReplacements(Dictionary<string, string?> properties)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var replacement in fieldValueReplacementsSettings.Replacements)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (properties.ContainsKey(replacement.FieldName))
|
|
|
|
|
|
{
|
|
|
|
|
|
var currentValue = properties[replacement.FieldName];
|
2025-12-30 11:57:15 +10:00
|
|
|
|
|
|
|
|
|
|
// Это костыль, List<string?> не может хранить null, вместо null он хранит ""
|
|
|
|
|
|
var stringNull = "null";
|
|
|
|
|
|
|
2025-11-27 17:11:14 +10:00
|
|
|
|
// Проверяем, содержится ли текущее значение в списке OldValues (с учетом регистра)
|
2025-12-30 11:57:15 +10:00
|
|
|
|
if (replacement.OldValues.Contains(currentValue ?? stringNull, StringComparer.OrdinalIgnoreCase))
|
2025-11-27 17:11:14 +10:00
|
|
|
|
{
|
|
|
|
|
|
logger.LogDebug("Заменяем значение поля '{FieldName}' с '{OldValue}' на '{NewValue}'",
|
|
|
|
|
|
replacement.FieldName, currentValue, replacement.NewValue);
|
|
|
|
|
|
properties[replacement.FieldName] = replacement.NewValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-05-19 16:09:55 +10:00
|
|
|
|
}
|
2025-05-30 09:50:47 +10:00
|
|
|
|
}
|