feat(aihitRelationshipsSyncer): полностью переписан класс+добавлены сообщения дл\ отладки
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -364,3 +364,4 @@ MigrationBackup/
|
|||||||
|
|
||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
/PARR.AIHITRelationshipsSyncerWorker/MockData.json
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace PARR.AIHITRelationshipsSyncer
|
|||||||
|
|
||||||
services.AddTransient<IRelationshipsSyncer, RelationshipsSyncer>();
|
services.AddTransient<IRelationshipsSyncer, RelationshipsSyncer>();
|
||||||
services.AddTransient<IAihitService, AihitService>();
|
services.AddTransient<IAihitService, AihitService>();
|
||||||
services.AddTransient<IRelationshipsSyncService, RelationshipsSyncSrevice>();
|
services.AddTransient<IRelationshipsSyncService, RelationshipsSyncService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,17 @@ namespace PARR.AIHITRelationshipsSyncer.Context
|
|||||||
|
|
||||||
|
|
||||||
public DbSet<AihitData> AihitDatas { get; set; }
|
public DbSet<AihitData> AihitDatas { get; set; }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
//Делаем это чтобы EF не пытался трекать объкты полученые из хранимой процедуры
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity<AihitData>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasNoKey();
|
||||||
|
entity.ToView(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ using System.ComponentModel.DataAnnotations.Schema;
|
|||||||
|
|
||||||
namespace PARR.AIHITRelationshipsSyncer.Models
|
namespace PARR.AIHITRelationshipsSyncer.Models
|
||||||
{
|
{
|
||||||
[PrimaryKey(nameof(EKFindCode), nameof(ChildEk))]
|
[PrimaryKey(nameof(ParentName), nameof(ChildName))]
|
||||||
public class AihitData
|
public record AihitData
|
||||||
{
|
{
|
||||||
[Column("КОД_ПОИСКА_ЭК")]
|
[Column("КОД_ПОИСКА_ЭК")]
|
||||||
public string? EKFindCode { get; set; }
|
public string? ParentName { get; set; }
|
||||||
|
|
||||||
//[Column("СТАТУС")]
|
//[Column("СТАТУС")]
|
||||||
//public string? Status { get; set; }
|
//public string? Status { get; set; }
|
||||||
@@ -21,6 +21,6 @@ namespace PARR.AIHITRelationshipsSyncer.Models
|
|||||||
//[Column("ТИП_ЭК")]
|
//[Column("ТИП_ЭК")]
|
||||||
//public string? EKType { get; set; }
|
//public string? EKType { get; set; }
|
||||||
[Column("Дочерний ЭК")]
|
[Column("Дочерний ЭК")]
|
||||||
public string? ChildEk { get; set; }
|
public string? ChildName { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using PARR.AIHITRelationshipsSyncer.Models;
|
||||||
using PARR.AIHITRelationshipsSyncer.Services.Interfaces;
|
using PARR.AIHITRelationshipsSyncer.Services.Interfaces;
|
||||||
using PARR.AIHITRelationshipsSyncer.Settings;
|
using PARR.AIHITRelationshipsSyncer.Settings;
|
||||||
using PARR.BLL.Services.Interfaces;
|
using PARR.BLL.Services.Interfaces;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace PARR.AIHITRelationshipsSyncer
|
namespace PARR.AIHITRelationshipsSyncer
|
||||||
{
|
{
|
||||||
@@ -39,21 +41,62 @@ namespace PARR.AIHITRelationshipsSyncer
|
|||||||
{
|
{
|
||||||
logger.LogInformation($"Начата загрузка иерархических связей между ЭК из АИХ ИТ");
|
logger.LogInformation($"Начата загрузка иерархических связей между ЭК из АИХ ИТ");
|
||||||
|
|
||||||
var aihitdata = aihitService.GetData();
|
List<AihitData>? aihitDataList = null;
|
||||||
|
|
||||||
var aihitDataCount = aihitdata?.Count();
|
#if DEBUG
|
||||||
|
const string mockFilePath = "MockData.json";
|
||||||
|
|
||||||
if (aihitdata == null || aihitDataCount == 0)
|
//Пытаемся загрузить mock-данные
|
||||||
|
if (File.Exists(mockFilePath) && new FileInfo(mockFilePath).Length > 0)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Загрузка данных из mock-файла: {MockFile}", mockFilePath);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = await File.ReadAllTextAsync(mockFilePath);
|
||||||
|
aihitDataList = JsonSerializer.Deserialize<List<AihitData>>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||||
|
logger.LogInformation("Mock-данные успешно загружены");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogWarning(ex, "Не уадлось десериализовать {MockFile}. Будет выполнен запрос в базу АИХ ИТ", mockFilePath);
|
||||||
|
aihitDataList = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aihitDataList == null)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Выполнение запроса в АИХ ИТ в режиме отладки");
|
||||||
|
var rawData = aihitService.GetData();
|
||||||
|
aihitDataList = rawData?.ToList();
|
||||||
|
|
||||||
|
if (aihitDataList != null && aihitDataList.Count > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = JsonSerializer.Serialize(aihitDataList, new JsonSerializerOptions { WriteIndented = true });
|
||||||
|
await File.WriteAllTextAsync(mockFilePath, json);
|
||||||
|
logger.LogInformation("Данные сохранены в {MockFile} для будущей отладки", mockFilePath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogWarning(ex, "Не удалось сохранить данные в {MockFile}", mockFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
//Release
|
||||||
|
var rawData = aihitService.GetData();
|
||||||
|
aihitDataList = rawData?.ToList();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (aihitDataList == null || aihitDataList.Count == 0)
|
||||||
{
|
{
|
||||||
logger.LogInformation("АИХ ИТ вернул пустые данные");
|
logger.LogInformation("АИХ ИТ вернул пустые данные");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//File.WriteAllText("MockData.json", aihitdata.ToJson());
|
logger.LogInformation("Получено {EkCount} записей для синхронизации", aihitDataList.Count);
|
||||||
//string text = File.OpenText("MockData.json").ReadToEnd();
|
await relationshipsSyncService.SyncAsync(aihitDataList);
|
||||||
//var aihitdata = JsonSerializer.Deserialize<List<AihitData>>(text);
|
|
||||||
|
|
||||||
await relationshipsSyncService.SyncAsync(aihitdata.ToList());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -0,0 +1,236 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
namespace PARR.AIHITRelationshipsSyncer.Services.Implementations
|
||||||
|
{
|
||||||
|
internal class RelationshipsSyncService : IRelationshipsSyncService
|
||||||
|
{
|
||||||
|
private readonly ILogger<RelationshipsSyncService> logger;
|
||||||
|
private readonly IUnitService unitService;
|
||||||
|
private readonly WorkerSettings workerSettings;
|
||||||
|
|
||||||
|
public RelationshipsSyncService(
|
||||||
|
ILogger<RelationshipsSyncService> logger,
|
||||||
|
IUnitService unitService,
|
||||||
|
WorkerSettings workerSettings
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
this.unitService = unitService;
|
||||||
|
this.workerSettings = workerSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task SyncAsync(List<AihitData> aihitdata)
|
||||||
|
{
|
||||||
|
var validPairs = ValidateInput(aihitdata);
|
||||||
|
if (!validPairs.Any()) return;
|
||||||
|
|
||||||
|
var sourceUnitNames = ExtractUnitNames(validPairs);
|
||||||
|
var existingUnits = await GetOrCreateUnitsAsync(sourceUnitNames);
|
||||||
|
|
||||||
|
var (toRemove, toAdd) = await GetDeltaAsync(validPairs);
|
||||||
|
|
||||||
|
await RemoveRelationshipsAsync(toRemove);
|
||||||
|
await AddRelationshipsAsync(toAdd, existingUnits);
|
||||||
|
|
||||||
|
await FinalCommitAsync(toRemove.Count, toAdd.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<(string Parent, string Child)> ValidateInput(List<AihitData> data)
|
||||||
|
{
|
||||||
|
if (data == null || data.Count == 0)
|
||||||
|
{
|
||||||
|
logger.LogWarning("Получены пустые или null данные из АИХ ИТ. Пропускаю синхронизацию");
|
||||||
|
return null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
var validPairs = data
|
||||||
|
.Where(t => !string.IsNullOrWhiteSpace(t.ParentName) && !string.IsNullOrWhiteSpace(t.ChildName))
|
||||||
|
.Where(t => t.ParentName != t.ChildName)
|
||||||
|
.Select(t => (
|
||||||
|
Parent: t.ParentName!.Trim().ToUpperInvariant(),
|
||||||
|
Child: t.ChildName!.Trim().ToUpperInvariant()
|
||||||
|
))
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
if (validPairs.Count == 0)
|
||||||
|
{
|
||||||
|
logger.LogWarning("После нормализации не осталось валидных связей. Пропускаю синхронизацию");
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogInformation("Получено {Count} валидных связей", validPairs.Count);
|
||||||
|
return validPairs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static List<string> ExtractUnitNames(List<(string Parent, string Child)> pairs)
|
||||||
|
{
|
||||||
|
return pairs
|
||||||
|
.SelectMany(x => new[] { x.Parent, x.Child })
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task<Dictionary<string, Unit>> GetOrCreateUnitsAsync(List<string> names)
|
||||||
|
{
|
||||||
|
var existing = await unitService.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.ToDictionaryAsync(u => u.Name, StringComparer.Ordinal);
|
||||||
|
|
||||||
|
var missing = names.Except(existing.Keys).ToList();
|
||||||
|
if (!missing.Any()) return existing;
|
||||||
|
|
||||||
|
logger.LogInformation("Создание отсутствующих Unit:{Count}", missing.Count);
|
||||||
|
foreach (var name in missing)
|
||||||
|
{
|
||||||
|
var unit = new Unit
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Name = name,
|
||||||
|
DateCreated = DateTimeOffset.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
if (await unitService.CreateAsync(unit))
|
||||||
|
{
|
||||||
|
logger.LogInformation("Создан Unit: {Name}", name);
|
||||||
|
existing[name] = unit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
logger.LogError("Не удалось создать Unit: {Name}", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
await unitService.CommitAsync();
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task<(List<ExistingRelationship> ToRemove, List<(string, string)> ToAdd)> GetDeltaAsync(List<(string Parent, string Child)> sourcePairs)
|
||||||
|
{
|
||||||
|
var allExistings = await unitService.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Include(u => u.ChildUnits).ThenInclude(r => r.ChildUnit)
|
||||||
|
.Where(u => u.ChildUnits.Any())
|
||||||
|
.SelectMany(u => u.ChildUnits, (parent, relationship) => new ExistingRelationship
|
||||||
|
{
|
||||||
|
ParentName = parent.Name,
|
||||||
|
ChildName = relationship.ChildUnit!.Name,
|
||||||
|
Relationship = relationship
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var sourceSet = new HashSet<(string, string)>(sourcePairs);
|
||||||
|
var existingSet = new HashSet<(string, string)>(allExistings.Select(x => (x.ParentName, x.ChildName)));
|
||||||
|
|
||||||
|
var toRemove = allExistings.Where(r => !sourceSet.Contains((r.ParentName, r.ChildName))).ToList();
|
||||||
|
var toAdd = sourcePairs.Where(p => !existingSet.Contains(p)).ToList();
|
||||||
|
|
||||||
|
logger.LogInformation("Связей для удаления: {ToRemove}, для добавления: {ToAdd}", toRemove.Count, toAdd.Count);
|
||||||
|
return (toRemove, toAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task RemoveRelationshipsAsync(List<ExistingRelationship> toRemove)
|
||||||
|
{
|
||||||
|
if (!toRemove.Any()) return;
|
||||||
|
|
||||||
|
var parentNames = toRemove.Select(r => r.ParentName).Distinct().ToList();
|
||||||
|
|
||||||
|
var parents = await unitService.Get()
|
||||||
|
.Include(u => u.ChildUnits)
|
||||||
|
.Where(u => parentNames.Contains(u.Name))
|
||||||
|
.ToDictionaryAsync(u => u.Name, StringComparer.Ordinal);
|
||||||
|
|
||||||
|
foreach (var rel in toRemove)
|
||||||
|
{
|
||||||
|
if (!parents.TryGetValue(rel.ParentName, out var parent))
|
||||||
|
{
|
||||||
|
logger.LogWarning("Родитель '{Parent}' не найден при попытке удаления связи - '{Child}'", rel.ParentName, rel.ChildName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var relationshipToRemove = parent.ChildUnits
|
||||||
|
.FirstOrDefault(r => r.ChildUnitId == rel.Relationship.ChildUnitId);
|
||||||
|
|
||||||
|
if (relationshipToRemove != null)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Удаление связи: '{Parent}' - '{Child}'", rel.ParentName, rel.ChildName);
|
||||||
|
parent.ChildUnits.Remove(relationshipToRemove);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogWarning("Связь '{Parent}'-'{Child}' не найдена в коллекции ChildUnits для удаления", rel.ParentName, rel.ChildName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddRelationshipsAsync(List<(string Parent, string Child)> toAdd, Dictionary<string, Unit> allUnits)
|
||||||
|
{
|
||||||
|
if (!toAdd.Any()) return;
|
||||||
|
|
||||||
|
var parentNames = toAdd.Select(x => x.Parent).Distinct().ToList();
|
||||||
|
var parents = await unitService.Get()
|
||||||
|
.Include(u => u.ChildUnits)
|
||||||
|
.Where(u => parentNames.Contains(u.Name))
|
||||||
|
.ToDictionaryAsync(u => u.Name, StringComparer.Ordinal);
|
||||||
|
|
||||||
|
foreach (var (Parent, Child) in toAdd)
|
||||||
|
{
|
||||||
|
if (!parents.TryGetValue(Parent, out var parent) ||
|
||||||
|
!allUnits.TryGetValue(Child, out var child))
|
||||||
|
{
|
||||||
|
logger.LogWarning("Пропущена связь '{Parent}' - '{Child}': Unit не найден", Parent, Child);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent!.ChildUnits.Any(r => r.ChildUnitId == child.Id))
|
||||||
|
{
|
||||||
|
logger.LogWarning("Связь уже существует: '{Parent}' - '{Child}'", Parent, Child);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.ChildUnits.Add(new UnitInUnit
|
||||||
|
{
|
||||||
|
ParentUnitId = parent.Id,
|
||||||
|
ChildUnitId = child.Id,
|
||||||
|
DateCreated = DateTimeOffset.UtcNow
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.LogInformation("Добавлена связь: '{Parent}' - '{Child}'", Parent, Child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task FinalCommitAsync(int removedCount, int addedCount)
|
||||||
|
{
|
||||||
|
if (removedCount > 0 || addedCount > 0)
|
||||||
|
{
|
||||||
|
if (await unitService.CommitAsync())
|
||||||
|
{
|
||||||
|
logger.LogInformation("Синхронизация завершена. Удалено: {Removed}, добавлено: {Added}", removedCount, addedCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
logger.LogError("Не удалось сохранить изменения в БД");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
logger.LogInformation("Изменений не обнаружено");
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ExistingRelationship
|
||||||
|
{
|
||||||
|
public string ParentName { get; init; }
|
||||||
|
public string ChildName { get; init; }
|
||||||
|
public UnitInUnit Relationship { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
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<RelationshipsSyncSrevice> logger;
|
|
||||||
private readonly IUnitService unitService;
|
|
||||||
private readonly WorkerSettings workerSettings;
|
|
||||||
|
|
||||||
public RelationshipsSyncSrevice(ILogger<RelationshipsSyncSrevice> logger,
|
|
||||||
IUnitService unitService,
|
|
||||||
WorkerSettings workerSettings
|
|
||||||
)
|
|
||||||
{
|
|
||||||
this.logger = logger;
|
|
||||||
this.unitService = unitService;
|
|
||||||
this.workerSettings = workerSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task SyncAsync(List<AihitData> 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<IGrouping<string?, AihitData>> 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<bool> SyncChildSUnitAsync(Unit unit, IEnumerable<string?> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Удаление дочерних связей у ЭК
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="unit"></param>
|
|
||||||
/// <param name="unitToDeleteChilds"></param>
|
|
||||||
private void DeleteChilds(Unit unit, IEnumerable<UnitInUnit> unitToDeleteChilds)
|
|
||||||
{
|
|
||||||
foreach (var childUnit in unitToDeleteChilds)
|
|
||||||
{
|
|
||||||
logger.LogInformation($"----- Удаление связи Unit: {unit.Name} -----");
|
|
||||||
unit.ChildUnits.Remove(childUnit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Создать новый ЭК по имени
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ekName"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private async Task<Unit?> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получить ЭК с дочерними связями по имени, если такого ещё нет создать
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ekName"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private async Task<Unit?> 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
{
|
{
|
||||||
"Logging": {
|
"Serilog": {
|
||||||
"LogLevel": {
|
"MinimumLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
"Override": {
|
||||||
|
"Microsoft": "Debug",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,17 @@ namespace PARR.DAL.Context
|
|||||||
|
|
||||||
var dateCreated = new DateTimeOffset(2023, 05, 01, 0, 0, 0, new TimeSpan(0));
|
var dateCreated = new DateTimeOffset(2023, 05, 01, 0, 0, 0, new TimeSpan(0));
|
||||||
|
|
||||||
|
#region Unit
|
||||||
|
//Добавляем ограничение по имени ЭК. нельзя писать прописными!!!
|
||||||
|
modelBuilder.Entity<Unit>(f =>
|
||||||
|
{
|
||||||
|
f.ToTable(t => t.HasCheckConstraint(
|
||||||
|
"CK_Units_Name_Upper",
|
||||||
|
"\"Name\" = UPPER(\"Name\") AND \"Name\" IS NOT NULL AND LENGTH(\"Name\")>0"
|
||||||
|
));
|
||||||
|
});
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region ApplicationType
|
#region ApplicationType
|
||||||
modelBuilder.Entity<ApplicationType>(f =>
|
modelBuilder.Entity<ApplicationType>(f =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PARR.DAL.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddUnitNameUppercaseCheck : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddCheckConstraint(
|
||||||
|
name: "CK_Units_Name_Upper",
|
||||||
|
schema: "unit",
|
||||||
|
table: "Units",
|
||||||
|
sql: "\"Name\" = UPPER(\"Name\") AND \"Name\" IS NOT NULL AND LENGTH(\"Name\")>0");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropCheckConstraint(
|
||||||
|
name: "CK_Units_Name_Upper",
|
||||||
|
schema: "unit",
|
||||||
|
table: "Units");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2768,6 +2768,8 @@ namespace PARR.DAL.Migrations
|
|||||||
b.ToTable("Units", "unit", t =>
|
b.ToTable("Units", "unit", t =>
|
||||||
{
|
{
|
||||||
t.HasComment("Таблица с ЭК");
|
t.HasComment("Таблица с ЭК");
|
||||||
|
|
||||||
|
t.HasCheckConstraint("CK_Units_Name_Upper", "\"Name\" = UPPER(\"Name\") AND \"Name\" IS NOT NULL AND LENGTH(\"Name\")>0");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user