Files
parr_api/PARR.AIHITMainSyncer/Services/SyncerService.cs

315 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using PARR.BLL.Domain.Mq;
using PARR.BLL.Services.Interfaces;
using PARR.DAL.Models.Unit;
using PARR.DAL.Services.Interfaces.Unit;
namespace PARR.AIHITMainSyncer.Services
{
internal class SyncerService : ISyncerService
{
private readonly ILogger<SyncerService> logger;
private readonly ITransformService transformService;
private readonly IUnitService unitService;
private readonly IUnitFieldService unitFieldService;
private readonly IUnitFieldValueService unitFieldValueService;
public SyncerService(
ILogger<SyncerService> logger,
ITransformService transformService,
IUnitService unitService,
IUnitFieldService unitFieldService,
IUnitFieldValueService unitFieldValueService
)
{
this.logger = logger;
this.transformService = transformService;
this.unitService = unitService;
this.unitFieldService = unitFieldService;
this.unitFieldValueService = unitFieldValueService;
}
public async Task SyncAsync(string msg)
{
//logger.LogInformation("Запуск синхронизации данных из очереди сообщений в ПАРР.");
var objFromQuery = transformService.GetModelFromJson<AihitMainDataMq>(msg);
if (objFromQuery == null)
return;
await SyncUnitAsync(objFromQuery);
}
private async Task SyncUnitAsync(AihitMainDataMq objFromQuery)
{
//Получаем сразу словарь атрибутов, чтобы не ходить каждый раз в базу
var fieldsFromDB = await unitFieldService.Get().AsNoTracking()/*.Select(t => new FieldDto { Id = t.Id, Name = t.AihitName, IsMultipleValue = t.IsMultipleValue })*/.ToListAsync();
if (!objFromQuery.Properties.Any())
return;
foreach (var kvp in objFromQuery.Properties.ToList())
objFromQuery.Properties[kvp.Key.Trim()] = kvp.Value?.Trim();
var listAihitData = new List<KeyValuePair<string, string?>>();
var propertisWithSimpleValues = objFromQuery.Properties
//.Where(t => fieldsFromDB.Any(a => IsStringEqual(t.Key.Trim(), a.Name) && a.IsMultipleValue != true))
.Where(t => fieldsFromDB.Any(a => IsStringEqual(t.Key.Trim(), a.AihitName) && a.IsMultipleValue != true))
.Select(s => new KeyValuePair<string, string?>(s.Key.Trim(), s.Value?.Trim())).ToList();
listAihitData.AddRange(propertisWithSimpleValues);
var propertiesWithMultipleValues = objFromQuery.Properties.Where(t => !propertisWithSimpleValues.Any(a => t.Key.Trim() == a.Key)).ToList();
propertiesWithMultipleValues.ForEach(p => listAihitData.AddRange(
SplitValues(p).Distinct().Select(s => new KeyValuePair<string, string?>(s.Key.Trim(), s.Value?.Trim()))
));
var unit = await GetUnit(objFromQuery.Name);
var incomingPairs = listAihitData
.Select(kv => new { kv.Key, kv.Value })
.ToList();
var unitValuesToRemove = unit.UnitValues
.Where(uv => !incomingPairs.Any(ip =>
IsStringEqual(uv.Field!.AihitName, ip.Key) &&
IsStringEqual(uv.Value!.Value, ip.Value)))
.ToList();
unitValuesToRemove.ForEach(uv => unit.UnitValues.Remove(uv));
var unitValueMissing = listAihitData.Where(t =>
!unit.UnitValues.Any(a =>
IsStringEqual(t.Key, a.Field!.AihitName) &&
IsStringEqual(t.Value, a.Value!.Value)
)).ToList();
if (unitValueMissing.Any())
{
var fieldsInChanges = unitValueMissing.Select(s => s.Key).Distinct().ToList();
var valuesInChanges = unitValueMissing.Select(s => s.Value).Distinct().ToList();
await SyncFieldsAsync(fieldsInChanges!, fieldsFromDB);
await SyncValuesAsync(valuesInChanges);
var normalizedFieldNames = fieldsInChanges
.Select(n => n?.Trim())
.Where(n => n != null)
.ToHashSet(StringComparer.OrdinalIgnoreCase);
var fieldsNameWithId = await unitFieldService.Get().AsNoTracking()
.Where(f => normalizedFieldNames.Contains(f.AihitName))
.ToListAsync();
var normalizedValues = valuesInChanges
.Where(v => v != null)
.Select(v => v!.Trim())
.ToHashSet(StringComparer.OrdinalIgnoreCase);
var valuesWithId = await unitFieldValueService.Get().AsNoTracking()
.Where(v =>
(v.Value == null && valuesInChanges.Contains(null)) ||
(v.Value != null && normalizedValues.Contains(v.Value)))
.ToListAsync();
foreach (var change in unitValueMissing)
ApplyChange(unit, fieldsNameWithId, valuesWithId, change);
if (!await unitService.CommitAsync())
logger.LogError($"Не удалось применить изменения по актуализиции аттрибуто Unit {unit.Name} в базе данных");
}
return;
}
private static List<KeyValuePair<string, string?>> SplitValues(KeyValuePair<string, string?> multiValueProperty)
{
var splitedProperties = new List<KeyValuePair<string, string?>>();
if (multiValueProperty.Value == null)
splitedProperties.Add(new KeyValuePair<string, string?>(multiValueProperty.Key, multiValueProperty.Value));
else
{
var values = multiValueProperty.Value.Split(',').Select(s => s.Trim());
foreach (var value in values)
splitedProperties.Add(new KeyValuePair<string, string?>(multiValueProperty.Key, value));
}
return splitedProperties;
}
private void ApplyChange(Unit unit, List<UnitField> fieldsNameWithId, List<UnitFieldValue> valuesWithId, KeyValuePair<string, string?> change)
{
var Field = fieldsNameWithId.FirstOrDefault(t => IsStringEqual(t.AihitName, change.Key));
var Value = valuesWithId.FirstOrDefault(t => IsStringEqual(t.Value, change.Value));
if (Field == null || Value == null)
logger.LogError($"Ошибка присвоения значения({change.Value}) аттрибуту({change.Key}). FieldId - {Field}, ValueId - {Value}");
else
{
var f = new UnitInValue
{
UnitId = unit.Id,
FieldId = Field.Id,
ValueId = Value.Id,
DateCreated = DateTimeOffset.UtcNow
};
unit.UnitValues.Add(f);
}
}
private async Task SyncValuesAsync(List<string?> values)
{
bool hasNull = values.Any(v => v is null);
// все поля из АИХИТ
var normalizedNoneNullValues = values
.Where(v => v != null)
.Select(v => v!.Trim())
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
// Получаем существующие значения из БД
var existingValuesInDb = await unitFieldValueService.Get()
.Where(v => normalizedNoneNullValues.Contains(v.Value!) || v.Value == null)
.Select(v => v.Value)
.ToListAsync();
if (!hasNull)
existingValuesInDb.RemoveAll(v => v is null);
var newNonNullValues = normalizedNoneNullValues
.Where(v => !existingValuesInDb.Any(ev =>
string.Equals(ev, v, StringComparison.OrdinalIgnoreCase)))
.ToList();
var newValuesToInsert = new List<string?>();
newValuesToInsert.AddRange(newNonNullValues);
if (hasNull && !existingValuesInDb.Contains(null))
newValuesToInsert.Add(null);
if (newValuesToInsert.Count == 0)
return;
// Создаём сущности — сохраняем оригинальный регистр из nonNullValues!
// (для null — просто null)
var newFieldValues = newValuesToInsert.Select(v =>
new UnitFieldValue
{
Id = Guid.NewGuid(),
Value = v // ← v — уже trim()-нутый non-null, или null
})
.ToList();
if (!await unitFieldValueService.AddRangeAsync(newFieldValues) || !await unitFieldValueService.CommitAsync())
{
logger.LogError(
"Не удалось сохранить {Count} новых значений FieldValues",
newValuesToInsert.Count);
}
else
{
if (newValuesToInsert.Count <= 10)
{
var preview = string.Join(", ", newValuesToInsert.Select(v => v ?? "<null>"));
logger.LogInformation("Добавлено {Count} значений FieldValues: [{Values}]", newValuesToInsert.Count, preview);
}
else
{
logger.LogInformation("Добавлено {Count} значений FieldValues (первые 5: {Preview})",
newValuesToInsert.Count,
string.Join(", ", newValuesToInsert.Take(5).Select(v => v ?? "<null>")));
}
}
}
private async Task SyncFieldsAsync(List<string> fieldsFromAihit, List<UnitField> fieldsFromDB)
{
var newFields = fieldsFromAihit.Where(t => !fieldsFromDB.Any(f => f.AihitName == t));
if (newFields.Any())
{
foreach (var item in newFields)
{
var field = new UnitField
{
Id = Guid.NewGuid(),
AihitName = item,
EsppName = null,
};
if (!await unitFieldService.CreateAsync(field))
logger.LogError("Не удалось создать запись в таблице Fields:{item},{field}", item, field);
else
logger.LogInformation("Создана запись а таблице Fields: Fields:{item},{field}", item, field);
}
if (!await unitFieldService.CommitAsync())
logger.LogError($"Не удалось применить изменения по добавлению новых Fields в базе данных");
}
}
private bool IsStringEqual(string? value1, string? value2)
{
return string.Equals(
value1?.Trim(),
value2?.Trim(),
StringComparison.OrdinalIgnoreCase);
}
private async Task<Unit> GetUnit(string name)
{
var unit = await GetUnitByName(name.Trim());
if (unit == null)
unit = await CreateUnitAsync(name);
return unit;
}
private async Task<Unit> CreateUnitAsync(string name)
{
var unit = new Unit { Name = name.Trim().ToUpperInvariant() };
if (!await unitService.CreateAsync(unit) || !await unitService.CommitAsync())
logger.LogError($"Не удалось создать Unit {unit.Name}");
else
logger.LogInformation($"----- Создан Unit: {unit.Name} -----");
return unit;
}
public async Task<Unit?> GetUnitByName(string name)
{
var normalized = name.Trim().ToUpperInvariant();
return await GetUnitWithFieldsAndValues()
.FirstOrDefaultAsync(u => u.Name == normalized);
}
private IQueryable<Unit> GetUnitWithFieldsAndValues()
{
return unitService.Get()
.Include(t => t.UnitValues)
.ThenInclude(uv => uv.Field)
.Include(t => t.UnitValues)
.ThenInclude(uf => uf.Value);
}
}
}