using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PARR.AIHITRelationshipsSyncer.Models; using PARR.AIHITRelationshipsSyncer.Services.Interfaces; using PARR.AIHITRelationshipsSyncer.Settings; using PARR.DAL.Models.Unit; using PARR.DAL.Services.Interfaces.Unit; using System.Reactive; using Unit = PARR.DAL.Models.Unit.Unit; namespace PARR.AIHITRelationshipsSyncer.Services.Implementations { internal class RelationshipsSyncSrevice : IRelationshipsSyncService { private readonly ILogger logger; private readonly IUnitService unitService; private readonly WorkerSettings workerSettings; public RelationshipsSyncSrevice(ILogger logger, IUnitService unitService, WorkerSettings workerSettings ) { this.logger = logger; this.unitService = unitService; this.workerSettings = workerSettings; } public async Task SyncAsync(List aihitdata) { var groupedData = aihitdata.Where(t => t.EKFindCode != null && t.ChildEk != null).GroupBy(ad => ad.EKFindCode); //var i = 0; //var c = groupedData.Count(); var onlyUpdateTransactionCount = 0; //Перебираем сгруппированные по родительскому ЭК данные foreach (var parrent in groupedData) { //i = i+1; //Получаем имя родителя var parentName = parrent.First().EKFindCode; //По имени получаем экземпляр Unit со всеми дочерними связями var unit = await GetUnitByNameAsync(parentName!); if (unit == null) { logger.LogError($"Не удалось получиться родительский ЭК по имени{parentName}"); return; } //Из данных АИХ ИТ получаем имена всех дочерних ЭК var childsName = parrent.Where(t => t.ChildEk != parentName && t.ChildEk != null).Select(t => t.ChildEk); childsName = childsName.Select(t => t?.Trim()).Distinct(); //Проходим циклом по полученному списку для актуализации связей в полученном нами экземпляре Unit var isChanged = await SyncChildSUnitAsync(unit, childsName); if (!isChanged) onlyUpdateTransactionCount = ++onlyUpdateTransactionCount; //logger.LogInformation($"Обработка № {i.ToString()} из {c}. Выполнено только обновление поля DateSynced - {onlyUpdateTransactionCount}"); if (onlyUpdateTransactionCount == workerSettings.CommitAfter) { //применяем изменения в базе данных if (!await unitService.CommitAsync()) logger.LogError($"Не удалось изменить связи Unit после {workerSettings.CommitAfter} обновлений поля DateSynced"); else logger.LogDebug($"----- Изменены связи Unit после {workerSettings.CommitAfter} обновлений поля DateSynced -----"); onlyUpdateTransactionCount = 0; } //применяем изменения в базе данных if (isChanged && !await unitService.CommitAsync()) logger.LogError($"Не удалось изменить связи Unit {unit.Name}"); else logger.LogDebug($"----- Изменены связи Unit: {unit.Name} -----"); } //Удаляем детей у тех кого детей в выборке АИХ ИТ не оказалось await RemoveWasteRelationships(groupedData); //применяем изменения в базе данных if (!await unitService.CommitAsync()) logger.LogError($"Не удалось удалить лишние связи"); else logger.LogInformation($"----- Иереархические связи актуализированы -----"); } private async Task RemoveWasteRelationships(IEnumerable> groupedData) { var parentNames = groupedData.Select(t => t.Key?.ToLower().Trim()); var units = await unitService.Get() .Include(t => t.ChildUnits).ToListAsync(); var parentsToDeleteChild = units.Where(t => parentNames.All(a => t.Name.ToLower() != a) && t.ChildUnits.Any()); if (parentsToDeleteChild.Any()) { foreach (var unit in parentsToDeleteChild) { DeleteChilds(unit, unit.ChildUnits); } } } private async Task SyncChildSUnitAsync(Unit unit, IEnumerable childsName) { var isChanged = false; //удаляем лишние дочерние связи существующие только в БД var childsToDelete = unit.ChildUnits.Where(t => !childsName.Any(a => t.ChildUnit?.Name.ToLower() == a!.ToLower())); if (childsToDelete.Any()) { DeleteChilds(unit, childsToDelete); if (!isChanged) isChanged = true; } //проверяем отсутсвующие связи var absentChildrens = childsName.Where(c => !unit.ChildUnits.Any(x => x.ChildUnit?.Name.ToLower() == c?.ToLower())); foreach (var absentChildren in absentChildrens) { var childUnit = await GetUnitByNameAsync(absentChildren!); if (childUnit == null) { logger.LogError($"Ошибка получения дочернего ЭК. Из базы пришел NULL для ЭК{absentChildren}"); return false; } if (unit.ChildUnits.Any(x => x.ChildUnitId == childUnit.Id)) logger.LogWarning($"Попытка создать существующую связь для {unit.Name} - {childUnit.Name}"); else { unit.ChildUnits.Add( new UnitInUnit { ParentUnitId = unit.Id, ChildUnitId = childUnit.Id, DateCreated = DateTimeOffset.UtcNow, } ); logger.LogInformation($"----- Создана связь Unit {unit.Name} - {childUnit.Name}-----"); } if (!isChanged) isChanged = true; } foreach (var child in unit.ChildUnits) { child.DateSynced = DateTimeOffset.UtcNow; } return isChanged; } /// /// Удаление дочерних связей у ЭК /// /// /// private void DeleteChilds(Unit unit, IEnumerable unitToDeleteChilds) { foreach (var childUnit in unitToDeleteChilds) { logger.LogInformation($"----- Удаление связи Unit: {unit.Name} -----"); unit.ChildUnits.Remove(childUnit); } } /// /// Создать новый ЭК по имени /// /// /// private async Task CreateUnit(string ekName) { var unit = new Unit { Name = ekName.Trim() }; if (!await unitService.CreateAsync(unit)) { logger.LogError($"Не удалось создать Unit {unit.Name}"); return null; } else { logger.LogInformation($"----- Создан Unit: {unit.Name} -----"); return unit; } } /// /// Получить ЭК с дочерними связями по имени, если такого ещё нет создать /// /// /// private async Task GetUnitByNameAsync(string ekName) { if (!unitService.Get().AsNoTracking().Any(u => u.Name.ToLower() == ekName.ToLower().Trim())) { return await CreateUnit(ekName); } return await unitService.Get() .Include(u => u.ChildUnits) .ThenInclude(t => t.ChildUnit) .FirstAsync(u => u.Name.ToLower() == ekName.ToLower().Trim()); } } }