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) { await SyncFieldsAsync(objFromQuery.Properties); await SyncValuesAsync(objFromQuery.Properties); Unit unit = await GetUnit(objFromQuery.Name); var changes = GetChangesInUnit(objFromQuery.Properties, unit); if (changes.Any()) { 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(); foreach (var change in changes) { ApplyChange(unit, fieldsNameWithId, valuesWithId, change); } if (!await unitService.CommitAsync()) logger.LogError($"Не удалось применить изменения по актуализиции аттрибуто Unit {unit.Name} в базе данных"); } } 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 f = new UnitInValue { UnitId = unit.Id, FieldId = fieldsNameWithId.First(t => IsStringEqual(t.AihitName, change.Key)).Id, ValueId = valuesWithId.First(t => IsStringEqual(t.Value, change.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(Dictionary properties, Unit unit) { return 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(); } private async Task SyncValuesAsync(Dictionary properties) { // все поля из АИХИТ var valuesFromAihit = properties.Select(t => t.Value?.Trim()).Distinct(); // список values для поиска в БД без null var valuesFromAihitWithoutNull = valuesFromAihit.Where(x => x != null).Select(t => t!.ToLower()).ToList(); var valuesInDb = await unitFieldValueService.Get().Where(t => (t.Value != null && valuesFromAihitWithoutNull.Any(x => x == t.Value.ToLower()) ) || (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 = Sanitize(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(Dictionary properties) { var fields = properties.Select(t => t.Key.Trim()); var newFields = fields.Where(t => !unitFieldService.Get().Any(f => f.AihitName.ToLower() == t.ToLower())); if (newFields.Any()) { foreach (var item in newFields) { var field = new UnitField { Id = Guid.NewGuid(), AihitName = Sanitize(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 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(); 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); } } }