using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using PARR.BLL.Domain.Mq; using PARR.BLL.Services.Interfaces; using PARR.DAL.Extensions; using PARR.DAL.Models.Unit; using PARR.DAL.Services.Interfaces.Unit; namespace PARR.AIHITMainSyncer.Services { internal class SyncerService : ISyncerService { private readonly ILogger logger; private readonly ITransformService transformService; private readonly IUnitService unitService; private readonly IUnitFieldService unitFieldService; private readonly IUnitFieldValueService unitFieldValueService; public SyncerService( ILogger 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(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; var listAihitData = new List>(); var propertisWithSimpleValues = objFromQuery.Properties .Where(t => fieldsFromDB.Any(a => IsStringEqual(t.Key, a.Name) && a.IsMultipleValue != true)) .Select(s => new KeyValuePair(s.Key.Trim(), s.Value?.Trim())).ToList(); listAihitData.AddRange(propertisWithSimpleValues); var propertiesWithMultipleValues = objFromQuery.Properties.Except(propertisWithSimpleValues).ToList(); propertiesWithMultipleValues.ForEach(p => listAihitData.AddRange( SplitValues(p).Distinct().Select(s => new KeyValuePair(s.Key.Trim(), s.Value?.Trim())) )); var unit = await GetUnit(objFromQuery.Name); var unitValuesToRemove = unit.UnitValues.Where(t => !listAihitData.Any(a => IsStringEqual(t.Field!.AihitName, a.Key) ) || !listAihitData.Any(a => IsStringEqual(t.Field!.AihitName, a.Key) && IsStringEqual(t.Value!.Value, a.Value) )).ToList(); unitValuesToRemove.ForEach(uv => unit.UnitValues.Remove(uv)); var unitValueExisting = unit.UnitValues.Where(t => listAihitData.Any(a => IsStringEqual(t.Field!.AihitName, a.Key) && IsStringEqual(t.Value!.Value, a.Value) )).ToList(); var unitValueMising = listAihitData.Where(t => !unit.UnitValues.Any(a => IsStringEqual(t.Key, a.Field!.AihitName) && IsStringEqual(t.Value, a.Value!.Value) )).ToList(); if (unitValueMising.Any()) { var fieldsInChanges = unitValueMising.Select(s => s.Key).Distinct().ToList(); var valuesInChanges = unitValueMising.Select(s => s.Value).Distinct().ToList(); await SyncFieldsAsync(fieldsInChanges!, fieldsFromDB); await SyncValuesAsync(valuesInChanges); //fieldsInChanges = fieldsInChanges.Select(s => s.ToLower()).ToList()!; //valuesInChanges = valuesInChanges.Select(s => (s == null) ? null : Sanitize(s.ToLower())).ToList()!; var fieldsNameWithId = await unitFieldService.Get().AsNoTracking().Where(t => fieldsInChanges.Any(c => c == t.AihitName//.AihitName.ToLower() )).ToListAsync(); var valuesWithId = await unitFieldValueService.Get().AsNoTracking().Where(t => valuesInChanges.Any(c => c == t.Value//((t.Value == null) ? null : t.Value.ToLower()) )).ToListAsync(); foreach (var change in unitValueMising) ApplyChange(unit, fieldsNameWithId, valuesWithId, change); if (!await unitService.CommitAsync()) logger.LogError($"Не удалось применить изменения по актуализиции аттрибуто Unit {unit.Name} в базе данных"); } //Проверяем наличие всех полей и их значений в справочниках //var fieldNamesToCreate = objFromQuery.Properties.Keys.Where(t => !fieldsFromDB.Any(a => t == a.Name)); //foreach (var item in fieldsToCreate) //{ //} //Превращаем словарь в лист так как могут содержаться аттрибуты с несколькими значениями /*var list = new List(); foreach (var property in objFromQuery.Properties) { var key = property.Key; var existingField = fieldsFromDB.FirstOrDefault(t => IsStringEqual(t.Name, key)); if (existingField == null) { var newField = new UnitField { Id = Guid.NewGuid(), AihitName = key, DateCreated = DateTimeOffset.UtcNow }; if (await unitFieldService.CreateAsync(newField)) existingField = new FieldDto { Id = newField.Id, Name = newField.AihitName }; else { logger.LogError($"Ошибка создания атрибута с именем {key}"); return; } } if(existingField.IsMultipleValue == true) { } else list.Add(new ValueWithId { FieldId, }) } /* Unit unit = await GetUnit(objFromQuery.Name); var fieldsFromDB = await unitFieldService.Get().Select(t => new FieldRecord { AihitName = t.AihitName, IsMultipleValue = t.IsMultipleValue }).ToListAsync(); var multiValueProperties = GetPropertiesWithMultiValueFields(objFromQuery.Properties, fieldsFromDB); var simplifiedProperties = objFromQuery.Properties.Except(multiValueProperties).ToDictionary(t => t.Key, t => t.Value).ToList(); var changes = GetChangesInUnit(simplifiedProperties, unit); if (multiValueProperties.Any()) { var splitedProperties = SplitValue(multiValueProperties); changes.AddRange(GetChangesInUnit(splitedProperties, unit)); } if (changes.Any()) { await SyncFieldsAsync(changes.Select(s => s.Key).ToList()!, fieldsFromDB); await SyncValuesAsync(changes.Select(s => s.Value).ToList()!); var fieldsNameWithId = await unitFieldService.Get().AsNoTracking().Where(t => changes.Select(c => Sanitize(c.Key.ToLower())).Any(c => c == t.AihitName.ToLower() )).ToListAsync(); var valuesWithId = await unitFieldValueService.Get().AsNoTracking().Where(t => changes.Select(c => (c.Value == null) ? null : Sanitize(c.Value.ToLower()) ).Any(c => c == ((t.Value == null) ? null : t.Value!.ToLower()) )).ToListAsync(); //Удаляем значения атрибутов, так как это часть индекса и EF не даст просто присвоить значение var unitValueToRemove = unit.UnitValues.Where(t => fieldsNameWithId.Any(a => a.Id == t.FieldId)); foreach (var unitValue in unitValueToRemove) unit.UnitValues.Remove(unitValue); foreach (var change in changes) { ApplyChange(unit, fieldsNameWithId, valuesWithId, change); } if (!await unitService.CommitAsync()) logger.LogError($"Не удалось применить изменения по актуализиции аттрибуто Unit {unit.Name} в базе данных"); }*/ return; } private static List> SplitValues(Dictionary multiValueProperties) { var splitedProperties = new List>(); foreach (var multiValueProperty in multiValueProperties) { if (multiValueProperty.Value == null) splitedProperties.Add(new KeyValuePair(multiValueProperty.Key, multiValueProperty.Value)); else { var values = multiValueProperty.Value.Split(',').Select(s => s.Trim()); foreach (var value in values) splitedProperties.Add(new KeyValuePair(multiValueProperty.Key, value)); } } return splitedProperties; } private static List> SplitValues(KeyValuePair multiValueProperty) { var splitedProperties = new List>(); if (multiValueProperty.Value == null) splitedProperties.Add(new KeyValuePair(multiValueProperty.Key, multiValueProperty.Value)); else { var values = multiValueProperty.Value.Split(',').Select(s => s.Trim()); foreach (var value in values) splitedProperties.Add(new KeyValuePair(multiValueProperty.Key, value)); } return splitedProperties; } private void ApplyChange(Unit unit, List fieldsNameWithId, List valuesWithId, KeyValuePair change) { // Теперь они все новые //var uValue = unit.UnitValues.FirstOrDefault(t => IsStringEqual(t.Field?.AihitName, change.Key)); //if (uValue == null) //{ 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); } //} //else //{ // uValue.ValueId = valuesWithId.First(t => IsStringEqual(t.Value, change.Value)).Id; // uValue.DateModified = DateTimeOffset.UtcNow; //} } //private List> GetChangesInUnit(List> properties, Unit unit) //{ // var changes = properties.Where(t => // !unit.UnitValues.Any( // uv => uv.Field!.AihitName.ToLower() == Sanitize(t.Key.ToLower()) && ( // (t.Value == null) ? // uv.Value?.Value == null : // ((uv.Value?.Value == null) ? null : uv.Value!.Value!.ToLower()) == Sanitize(t.Value.ToLower()) // ) // ) // ).ToList(); // return changes; //} private async Task SyncValuesAsync(List values) { // все поля из АИХИТ var valuesFromAihit = values.Distinct(); // список values для поиска в БД без null var valuesFromAihitWithoutNull = valuesFromAihit.Where(x => x != null).ToList(); var valuesInDb = await unitFieldValueService.Get().Where(t => (t.Value != null && valuesFromAihitWithoutNull.Any(x => x == t.Value) ) || (t.Value == null) ).Select(t => t.Value) .ToListAsync(); if (!valuesFromAihit.Any(x => x == null)) valuesInDb.Remove(null); var newValues = valuesFromAihit.Where(t => !valuesInDb.Any(x => IsStringEqual(x, t))).ToList(); if (newValues.Any()) { foreach (var item in newValues) { var value = new UnitFieldValue { Id = Guid.NewGuid(), DateCreated = DateTimeOffset.UtcNow, Value = item }; if (!await unitFieldValueService.CreateAsync(value)) logger.LogError($"Не удалось создать запись в таблице FieldValues: {item}, {value.ToJson()}"); else logger.LogInformation($"Создана запись а таблице FieldValues: {item}, {value.ToJson()}"); } if (!await unitFieldValueService.CommitAsync()) logger.LogError($"Не удалось применить изменения по добавлению новых Fields в базе данных"); } } private async Task SyncFieldsAsync(List fieldsFromAihit, List fieldsFromDB) { var newFields = fieldsFromAihit.Where(t => !fieldsFromDB.Any(f => f.Name == 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.ToJson()}"); else logger.LogInformation($"Создана запись а таблице Fields: {item}, {field.ToJson()}"); } if (!await unitFieldService.CommitAsync()) logger.LogError($"Не удалось применить изменения по добавлению новых Fields в базе данных"); } } //private Dictionary GetPropertiesWithMultiValueFields(Dictionary properties, List fieldsFromDB) //{ // var fieldsName = properties.Select(s => Sanitize(s.Key.ToLower())); // //var multiValueField = await unitFieldService.Get().Where(t => // // t.IsMultipleValue == true && // // fieldsName.Any(p => p == t.AihitName.ToLower()) // // ).Select(s => s.AihitName.ToLower()).ToListAsync(); // var multiValueField = fieldsFromDB.Where(t => t.IsMultipleValue == true // && fieldsName.Any(p => p == t.AihitName.ToLower())) // .Select(s => s.AihitName.ToLower()).ToList(); // return properties.Where(p => multiValueField.Any(a => Sanitize(p.Key.ToLower()) == a)).ToDictionary(t => t.Key, t => t.Value); //} //private string? Sanitize(string? value) //{ // var _value = value?.Trim(); // return _value; //} private bool IsStringEqual(string? value1, string? value2) { //var _value1 = value1?.ToLower().Trim(); //var _value2 = value2?.ToLower().Trim(); var _value1 = value1; var _value2 = value2; return _value1 == _value2; } private async Task GetUnit(string name) { var unit = await GetUnitByName(name.Trim()); if (unit == null) unit = await CreateUnitAsync(name); return unit; } private async Task CreateUnitAsync(string name) { var unit = new Unit { Name = name.Trim() }; if (!await unitService.CreateAsync(unit) || !await unitService.CommitAsync()) logger.LogError($"Не удалось создать Unit {unit.Name}"); else logger.LogInformation($"----- Создан Unit: {unit.Name} -----"); return unit; } public async Task GetUnitByName(string name) { return await GetUnitWithFieldsAndValues() .FirstOrDefaultAsync(u => u.Name.ToLower() == name.ToLower()); } private IQueryable GetUnitWithFieldsAndValues() { return unitService.Get() .Include(t => t.UnitValues) .ThenInclude(uv => uv.Field) .Include(t => t.UnitValues) .ThenInclude(uf => uf.Value); } } //internal record FieldRecord //{ // public required string AihitName; // public bool? IsMultipleValue; //} //internal class ValueWithId //{ // public required Guid FieldId; // public required string FieldName; // public required Guid ValueId; // public required string? ValueValue; //} internal record FieldDto { public required Guid Id; public required string Name; public bool? IsMultipleValue; } }