feat(aihitRelationshipsSyncer): Реализована логика синхронизации связей между Unit+CI/CD
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using PARR.AIHITRelationshipsSyncer.Context;
|
||||
using PARR.AIHITRelationshipsSyncer.Services.Implementations;
|
||||
using PARR.AIHITRelationshipsSyncer.Services.Interfaces;
|
||||
using PARR.AIHITRelationshipsSyncer.Settings;
|
||||
using PARR.BLL;
|
||||
using PARR.DAL;
|
||||
|
||||
namespace PARR.AIHITRelationshipsSyncer
|
||||
{
|
||||
public static class AihitRelationshpsSyncerInstaller
|
||||
{
|
||||
public static void InstallAihitRelationshpsSyncerServices(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.InstallBllServices(configuration);
|
||||
services.InstallDalServices(configuration);
|
||||
|
||||
var settings = new WorkerSettings();
|
||||
configuration.GetSection(nameof(WorkerSettings)).Bind(settings);
|
||||
services.AddSingleton(settings);
|
||||
|
||||
services.AddDbContext<AIHITContext>(options =>
|
||||
options.UseSqlServer(
|
||||
configuration.GetConnectionString("AihitConnection"),
|
||||
sqlServerOptions => sqlServerOptions.CommandTimeout(1800)
|
||||
));
|
||||
|
||||
services.AddTransient<IRelationshipsSyncer, RelationshipsSyncer>();
|
||||
services.AddTransient<IAihitService, AihitService>();
|
||||
services.AddTransient<IRelationshipsSyncService, RelationshipsSyncSrevice>();
|
||||
}
|
||||
|
||||
|
||||
public static IConfigurationBuilder AddAihitRelationshpsSyncerConfigurations(this IConfigurationBuilder builder, IServiceCollection services)
|
||||
{
|
||||
builder.AddDalConfigurations(services);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
public static void AddAihitRelationshpsSyncerSettings(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddDallSettings(configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
PARR.AIHITRelationshipsSyncer/Context/AIHITContext.cs
Normal file
13
PARR.AIHITRelationshipsSyncer/Context/AIHITContext.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PARR.AIHITRelationshipsSyncer.Models;
|
||||
|
||||
namespace PARR.AIHITRelationshipsSyncer.Context
|
||||
{
|
||||
internal class AIHITContext : DbContext
|
||||
{
|
||||
public AIHITContext(DbContextOptions<AIHITContext> options) : base(options) { }
|
||||
|
||||
|
||||
public DbSet<AihitData> AihitDatas { get; set; }
|
||||
}
|
||||
}
|
||||
7
PARR.AIHITRelationshipsSyncer/IRelationshipsSyncer.cs
Normal file
7
PARR.AIHITRelationshipsSyncer/IRelationshipsSyncer.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace PARR.AIHITRelationshipsSyncer
|
||||
{
|
||||
public interface IRelationshipsSyncer
|
||||
{
|
||||
Task StartAsync();
|
||||
}
|
||||
}
|
||||
26
PARR.AIHITRelationshipsSyncer/Models/AihitData.cs
Normal file
26
PARR.AIHITRelationshipsSyncer/Models/AihitData.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace PARR.AIHITRelationshipsSyncer.Models
|
||||
{
|
||||
[PrimaryKey(nameof(EKFindCode), nameof(ChildEk))]
|
||||
public class AihitData
|
||||
{
|
||||
[Column("КОД_ПОИСКА_ЭК")]
|
||||
public string? EKFindCode { get; set; }
|
||||
|
||||
//[Column("СТАТУС")]
|
||||
//public string? Status { get; set; }
|
||||
|
||||
//[Column("ЗОНА_ОТВЕТСТВЕННОСТИ")]
|
||||
//public string? ResponseArea { get; set; }
|
||||
|
||||
//[Column("ПОДКАТЕГОРИЯ_ЭК")]
|
||||
//public string? EKSubCategory { get; set; }
|
||||
|
||||
//[Column("ТИП_ЭК")]
|
||||
//public string? EKType { get; set; }
|
||||
[Column("Дочерний ЭК")]
|
||||
public string? ChildEk { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.20" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PARR.BLL\PARR.BLL.csproj" />
|
||||
<ProjectReference Include="..\PARR.DAL\PARR.DAL.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
71
PARR.AIHITRelationshipsSyncer/RelationshipsSyncer.cs
Normal file
71
PARR.AIHITRelationshipsSyncer/RelationshipsSyncer.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PARR.AIHITRelationshipsSyncer.Services.Interfaces;
|
||||
using PARR.AIHITRelationshipsSyncer.Settings;
|
||||
using PARR.BLL.Services.Interfaces;
|
||||
|
||||
namespace PARR.AIHITRelationshipsSyncer
|
||||
{
|
||||
internal class RelationshipsSyncer : IRelationshipsSyncer
|
||||
{
|
||||
private readonly ILogger<RelationshipsSyncer> logger;
|
||||
private readonly IIntervalService intervalService;
|
||||
private readonly IAihitService aihitService;
|
||||
private readonly IRelationshipsSyncService relationshipsSyncService;
|
||||
private readonly WorkerSettings workerSettings;
|
||||
|
||||
public RelationshipsSyncer(
|
||||
ILogger<RelationshipsSyncer> logger,
|
||||
IIntervalService intervalService,
|
||||
IAihitService aihitService,
|
||||
IRelationshipsSyncService relationshipsSyncService,
|
||||
WorkerSettings workerSettings
|
||||
)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.intervalService = intervalService;
|
||||
this.aihitService = aihitService;
|
||||
this.relationshipsSyncService = relationshipsSyncService;
|
||||
this.workerSettings = workerSettings;
|
||||
}
|
||||
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
logger.LogInformation("Запуск сервиса синхронизации связей иерархии ЭК из АИХ ИТ в ПАРР");
|
||||
|
||||
await intervalService.IntervalInitAsync(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.LogInformation($"Начата загрузка иерархических связей между ЭК из АИХ ИТ");
|
||||
|
||||
var aihitdata = aihitService.GetData();
|
||||
|
||||
var aihitDataCount = aihitdata?.Count();
|
||||
|
||||
if (aihitdata == null || aihitDataCount == 0)
|
||||
{
|
||||
logger.LogInformation("АИХ ИТ вернул пустые данные");
|
||||
return;
|
||||
}
|
||||
|
||||
//File.WriteAllText("MockData.json", aihitdata.ToJson());
|
||||
//string text = File.OpenText("MockData.json").ReadToEnd();
|
||||
//var aihitdata = JsonSerializer.Deserialize<List<AihitData>>(text);
|
||||
|
||||
await relationshipsSyncService.SyncAsync(aihitdata.ToList());
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Ошибка синхронизации иерархических данных Unit из АИХ ИТ в ПАРР");
|
||||
}
|
||||
finally
|
||||
{
|
||||
logger.LogInformation($"Завершена загрузка иерархических связей между ЭК из АИХ ИТ");
|
||||
}
|
||||
|
||||
}, workerSettings.RepeatEvery);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PARR.AIHITRelationshipsSyncer.Context;
|
||||
using PARR.AIHITRelationshipsSyncer.Models;
|
||||
using PARR.AIHITRelationshipsSyncer.Services.Interfaces;
|
||||
|
||||
namespace PARR.AIHITRelationshipsSyncer.Services.Implementations
|
||||
{
|
||||
internal class AihitService : IAihitService
|
||||
{
|
||||
private readonly ILogger<AihitService> logger;
|
||||
private readonly AIHITContext context;
|
||||
|
||||
public AihitService(
|
||||
ILogger<AihitService> logger,
|
||||
AIHITContext context
|
||||
)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных из АИХ ИТ вызовом хранимой процедуры в базе данных
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<AihitData>? GetData()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = context.AihitDatas.FromSqlRaw($"EXEC mao2.dbo.sp_IPP_PARR_PTK_get_Relationships").ToList();
|
||||
|
||||
if (result == null || !result.Any())
|
||||
{
|
||||
logger.LogWarning($"Процедура sp_IPP_PARR_PTK_get_Relationships вернула пустой список ЭК");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, $"Ошибка при выполнении ХП sp_IPP_PARR_PTK_get_Relationships");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PARR.AIHITRelationshipsSyncer.Models;
|
||||
using PARR.AIHITRelationshipsSyncer.Services.Interfaces;
|
||||
using PARR.DAL.Models.Unit;
|
||||
using PARR.DAL.Services.Interfaces.Unit;
|
||||
|
||||
namespace PARR.AIHITRelationshipsSyncer.Services.Implementations
|
||||
{
|
||||
internal class RelationshipsSyncSrevice : IRelationshipsSyncService
|
||||
{
|
||||
private readonly ILogger<RelationshipsSyncSrevice> logger;
|
||||
private readonly IUnitService unitService;
|
||||
|
||||
public RelationshipsSyncSrevice(ILogger<RelationshipsSyncSrevice> logger,
|
||||
IUnitService unitService
|
||||
)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.unitService = unitService;
|
||||
}
|
||||
|
||||
|
||||
public async Task SyncAsync(List<AihitData> aihitdata)
|
||||
{
|
||||
var groupedData = aihitdata.GroupBy(ad => ad.EKFindCode);
|
||||
|
||||
var units = await GetUnitsWithChildrens();
|
||||
|
||||
//Перебираем сгруппированные по родительскому ЭК данные
|
||||
foreach (var item in groupedData)
|
||||
{
|
||||
//Получаем имя родителя
|
||||
var parentName = item.Select(t => t.EKFindCode).First();
|
||||
if (parentName == null)
|
||||
continue;
|
||||
|
||||
//if (parentName.ToLower() != "ас-рп-рцку-гор")
|
||||
// continue;
|
||||
|
||||
//По имени получаем экземпляр Unit со всеми дочерними связями
|
||||
var unit = await GetUnitByNameAsync(parentName);
|
||||
|
||||
//Из данных АИХ ИТ получаем имена всех дочерних ЭК
|
||||
var childsName = item.Select(t => t.ChildEk).ToList();
|
||||
if (childsName == null || childsName.Count == 0)
|
||||
continue;
|
||||
|
||||
//Проходим циклом по полученному списку для актуализации связей в полученном нами экземпляре Unit
|
||||
foreach (var childName in childsName)
|
||||
{
|
||||
if (childName == null)
|
||||
continue;
|
||||
|
||||
//if (childName.ToLower() != "рпа-робин-рцку-спиуи-прочее-26-гор")
|
||||
// continue;
|
||||
//Ищем детей по имени
|
||||
var child = unit.ChildUnits.FirstOrDefault(t => t.ChildUnit?.Name.ToLower() == childName.ToLower().Trim());
|
||||
|
||||
//не нашли создаём и сразу пишем в базу, чтобы если попадуться дубликаты в списке они были созданы
|
||||
if (child == null)
|
||||
{
|
||||
var childUnit = await GetUnitByNameAsync(childName!);
|
||||
unit.ChildUnits.Add(
|
||||
new UnitInUnit
|
||||
{
|
||||
ParentUnitId = unit.Id,
|
||||
ChildUnitId = childUnit.Id,
|
||||
DateCreated = DateTimeOffset.UtcNow,
|
||||
DateSynced = DateTimeOffset.UtcNow
|
||||
}
|
||||
);
|
||||
|
||||
if (!await unitService.CommitAsync())
|
||||
logger.LogError($"Не удалось создать связь Unit {unit.Name} с {childUnit.Name}");
|
||||
else
|
||||
logger.LogDebug($"----- Изменене связи Unit: {unit.Name} -----");
|
||||
}
|
||||
//Для всех обновляем дату синхронизации
|
||||
else
|
||||
child.DateSynced = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
//После проверки пришедших к нам из АИХ ИТ дочерних связей удаляем лишние/уже не существующие в базе
|
||||
var childsToDelete = unit.ChildUnits.Where(t => childsName.All(a => t.ChildUnit?.Name != a)).ToList();
|
||||
if (childsToDelete != null && childsToDelete.Count() > 0)
|
||||
DeleteChilds(unit, childsToDelete);
|
||||
|
||||
//применяем изменения в базе данных
|
||||
if (!await unitService.CommitAsync())
|
||||
logger.LogError($"Не удалось изменить связи Unit {unit.Name}");
|
||||
else
|
||||
logger.LogDebug($"----- Изменены связи Unit: {unit.Name} -----");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Удаление дочерних связей у ЭК
|
||||
/// </summary>
|
||||
/// <param name="unit"></param>
|
||||
/// <param name="unitToDeleteChilds"></param>
|
||||
private void DeleteChilds(Unit unit, List<UnitInUnit> unitToDeleteChilds)
|
||||
{
|
||||
foreach (var childUnit in unitToDeleteChilds)
|
||||
{
|
||||
logger.LogInformation($"----- Удаление связи Unit: {childUnit.ParentUnit!.Name} - {childUnit.ChildUnit!.Name} -----");
|
||||
unit.ChildUnits.Remove(childUnit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Получение списка ЭК с дочерними связями
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task<List<Unit>> GetUnitsWithChildrens()
|
||||
{
|
||||
return await unitService.Get()
|
||||
.Include(t => t.ChildUnits).ToListAsync();
|
||||
}
|
||||
|
||||
|
||||
/// <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) || !await unitService.CommitAsync())
|
||||
logger.LogError($"Не удалось создать Unit {unit.Name}");
|
||||
else
|
||||
logger.LogDebug($"----- Создан 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)
|
||||
.FirstAsync(u => u.Name.ToLower() == ekName.ToLower().Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using PARR.AIHITRelationshipsSyncer.Models;
|
||||
|
||||
namespace PARR.AIHITRelationshipsSyncer.Services.Interfaces
|
||||
{
|
||||
public interface IAihitService
|
||||
{
|
||||
IEnumerable<AihitData>? GetData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using PARR.AIHITRelationshipsSyncer.Models;
|
||||
|
||||
namespace PARR.AIHITRelationshipsSyncer.Services.Interfaces
|
||||
{
|
||||
public interface IRelationshipsSyncService
|
||||
{
|
||||
Task SyncAsync(List<AihitData> aihitdata);
|
||||
}
|
||||
}
|
||||
7
PARR.AIHITRelationshipsSyncer/Settings/WorkerSettings.cs
Normal file
7
PARR.AIHITRelationshipsSyncer/Settings/WorkerSettings.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace PARR.AIHITRelationshipsSyncer.Settings
|
||||
{
|
||||
internal class WorkerSettings
|
||||
{
|
||||
public TimeSpan RepeatEvery { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user