From 1ce8fcb2845c91f8a47fb653deaf7cabd16a023e Mon Sep 17 00:00:00 2001 From: Mikhail Kuznetsov Date: Mon, 19 May 2025 16:09:55 +1000 Subject: [PATCH] =?UTF-8?q?feat(aihitMainSyncer):=20=D0=9D=D0=B0=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B0=D0=BD=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D1=8B=D1=85=20Unit=20=D0=B8=20Field.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PARR.AIHITMainSyncer/AihitMainSyncer.cs | 55 +++++++ .../AihitMainSyncerInstaller.cs | 43 ++++++ PARR.AIHITMainSyncer/IAihitMainSyncer.cs | 10 ++ .../PARR.AIHITMainSyncer.csproj | 14 ++ .../Services/ISyncerService.cs | 7 + .../Services/SyncerService.cs | 136 ++++++++++++++++++ PARR.AIHITMainSyncer/Settings/MqSettings.cs | 15 ++ .../Settings/WorkerSettings.cs | 7 + PARR.AIHITSyncer/AihitSyncer.cs | 1 - PARR.AIHITSyncer/Services/SyncerService.cs | 2 +- .../PARR.AIHITSyncerWorker.csproj | 2 +- PARR.AIHITSyncerWorker/Program.cs | 10 +- PARR.AIHITSyncerWorker/Worker.cs | 8 +- PARR.API.sln | 6 + .../Implementations/Unit/UnitFieldService.cs | 5 + .../Implementations/Unit/UnitService.cs | 15 ++ .../Interfaces/Unit/IUnitFieldService.cs | 3 +- .../Services/Interfaces/Unit/IUnitService.cs | 1 + 18 files changed, 328 insertions(+), 12 deletions(-) create mode 100644 PARR.AIHITMainSyncer/AihitMainSyncer.cs create mode 100644 PARR.AIHITMainSyncer/AihitMainSyncerInstaller.cs create mode 100644 PARR.AIHITMainSyncer/IAihitMainSyncer.cs create mode 100644 PARR.AIHITMainSyncer/PARR.AIHITMainSyncer.csproj create mode 100644 PARR.AIHITMainSyncer/Services/ISyncerService.cs create mode 100644 PARR.AIHITMainSyncer/Services/SyncerService.cs create mode 100644 PARR.AIHITMainSyncer/Settings/MqSettings.cs create mode 100644 PARR.AIHITMainSyncer/Settings/WorkerSettings.cs diff --git a/PARR.AIHITMainSyncer/AihitMainSyncer.cs b/PARR.AIHITMainSyncer/AihitMainSyncer.cs new file mode 100644 index 00000000..71c163fa --- /dev/null +++ b/PARR.AIHITMainSyncer/AihitMainSyncer.cs @@ -0,0 +1,55 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PARR.AIHITMainSyncer.Services; +using PARR.AIHITMainSyncer.Settings; +using PARR.BLL.Services.Interfaces; + +namespace PARR.AIHITMainSyncer +{ + internal class AihitMainSyncer : IAihitMainSyncer + { + private readonly ILogger logger; + private readonly IMqService mqService; + private readonly MqSettings mqSettings; + private readonly IServiceProvider serviceProvider; + + public AihitMainSyncer( + ILogger logger, + IMqService mqService, + MqSettings mqSettings, + IServiceProvider serviceProvider + ) + { + this.logger = logger; + this.mqService = mqService; + this.mqSettings = mqSettings; + this.serviceProvider = serviceProvider; + } + public void Start() + { + logger.LogInformation("Запуск сервиса синхронизации данных из АИХИТ и ПАРР."); + + var isConnected = mqService.InitConsumer(mqSettings, async (string msg) => + { + using (var scope = serviceProvider.CreateScope()) + { + var syncerService = scope.ServiceProvider.GetService(); + if (syncerService == null) + throw new Exception("Не смог получить серивс ISyncerService, scope.ServiceProvider.GetService()"); + + await syncerService.SyncAsync(msg); + } + + }); + + if (!isConnected) + throw new Exception("Ошибка при подключении к RabbitMq"); + } + + + public void Stop() + { + mqService.Dispose(); + } + } +} diff --git a/PARR.AIHITMainSyncer/AihitMainSyncerInstaller.cs b/PARR.AIHITMainSyncer/AihitMainSyncerInstaller.cs new file mode 100644 index 00000000..5f306a1a --- /dev/null +++ b/PARR.AIHITMainSyncer/AihitMainSyncerInstaller.cs @@ -0,0 +1,43 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using PARR.AIHITMainSyncer.Services; +using PARR.AIHITMainSyncer.Settings; +using PARR.BLL; +using PARR.DAL; + +namespace PARR.AIHITMainSyncer +{ + public static class AihitMainSyncerInstaller + { + public static void InstallAihitMainSyncerServices(this IServiceCollection services, IConfiguration configuration) + { + services.InstallBllServices(configuration); + services.InstallDalServices(configuration); + + var settings = new WorkerSettings(); + configuration.GetSection(nameof(WorkerSettings)).Bind(settings); + services.AddSingleton(settings); + + var mqSettings = new MqSettings(); + configuration.GetSection(nameof(MqSettings)).Bind(mqSettings); + services.AddSingleton(mqSettings); + + services.AddTransient(); + services.AddTransient(); + } + + + public static IConfigurationBuilder AddAihitMainSyncerConfigurations(this IConfigurationBuilder builder, IServiceCollection services) + { + builder.AddDalConfigurations(services); + + return builder; + } + + + public static void AddAihitMainSyncerSettings(this IServiceCollection services, IConfiguration configuration) + { + services.AddDallSettings(configuration); + } + } +} diff --git a/PARR.AIHITMainSyncer/IAihitMainSyncer.cs b/PARR.AIHITMainSyncer/IAihitMainSyncer.cs new file mode 100644 index 00000000..5b975fd9 --- /dev/null +++ b/PARR.AIHITMainSyncer/IAihitMainSyncer.cs @@ -0,0 +1,10 @@ +namespace PARR.AIHITMainSyncer +{ + public interface IAihitMainSyncer + { + void Start(); + + + void Stop(); + } +} diff --git a/PARR.AIHITMainSyncer/PARR.AIHITMainSyncer.csproj b/PARR.AIHITMainSyncer/PARR.AIHITMainSyncer.csproj new file mode 100644 index 00000000..21190861 --- /dev/null +++ b/PARR.AIHITMainSyncer/PARR.AIHITMainSyncer.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + + + + + + + + diff --git a/PARR.AIHITMainSyncer/Services/ISyncerService.cs b/PARR.AIHITMainSyncer/Services/ISyncerService.cs new file mode 100644 index 00000000..31f70655 --- /dev/null +++ b/PARR.AIHITMainSyncer/Services/ISyncerService.cs @@ -0,0 +1,7 @@ +namespace PARR.AIHITMainSyncer.Services +{ + public interface ISyncerService + { + Task SyncAsync(string msg); + } +} diff --git a/PARR.AIHITMainSyncer/Services/SyncerService.cs b/PARR.AIHITMainSyncer/Services/SyncerService.cs new file mode 100644 index 00000000..47a49a89 --- /dev/null +++ b/PARR.AIHITMainSyncer/Services/SyncerService.cs @@ -0,0 +1,136 @@ +using Microsoft.Extensions.Logging; +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; + + public SyncerService( + ILogger logger, + ITransformService transformService, + IUnitService unitService, + IUnitFieldService unitFieldService + ) + { + this.logger = logger; + this.transformService = transformService; + this.unitService = unitService; + this.unitFieldService = unitFieldService; + } + + + 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 fields = await GetFieldsInObjectAsync(objFromQuery); + + var unit = await unitService.GetUnitWithFieldsAsync(objFromQuery.Name); + + if (unit == null) + unit = await CreateUnitAsync(objFromQuery); + else + logger.LogDebug($"Найден Unit в БД: {unit.Name})"); + + await SyncFieldInUnitAsync(unit!, fields); + + + } + + + private async Task SyncFieldInUnitAsync(Unit unit, List fieldsId) + { + var isChanged = false; + + //Смотрим какие поля нам прислали, есть новые? + var newFields = fieldsId.Except(unit.UnitFields.Select(uf => uf.FieldId)).ToList(); + + //добавляем если есть + if (newFields.Any()) + { + if (!isChanged) + isChanged = true; + newFields.ForEach(uf => + { + unit.UnitFields.Add( + new UnitInField { UnitId = unit.Id, FieldId = uf, DateCreated = DateTimeOffset.UtcNow } + ); + }); + } + + //TODO: Удаление неактуальных + + if (isChanged && !await unitFieldService.CommitAsync()) + logger.LogError($"Не удалось изменить набор Fields в Unit {unit.Name}"); + else + logger.LogInformation($"----- Набор Fields в Unit изменён: {unit.Name} -----"); + } + + private async Task> GetFieldsInObjectAsync(AihitMainDataMq objFromQuery) + { + var fields = new List(); + + foreach (var item in objFromQuery.Properties) + { + var field = await CreateUnitFieldIfNotExistAsync(item.Key); + fields.Add(field!.Id); + } + + return fields; + } + + + private async Task CreateUnitAsync(AihitMainDataMq objFromQuery) + { + var unit = new Unit { Name = objFromQuery.Name.Trim() }; + + if (!await unitService.CreateAsync(unit) || !await unitService.CommitAsync()) + logger.LogError($"Не удалось создать Unit {unit.Name}"); + else + logger.LogInformation($"----- Создан Unit: {unit.Name} -----"); + + return await unitService.GetUnitWithFieldsAsync(unit.Name); + } + + + private async Task CreateUnitFieldIfNotExistAsync(string name) + { + var existUnitField = await unitFieldService.GetByAihitNameAsync(name); + + if (existUnitField != null) + return existUnitField; + + var field = new UnitField + { + Id = Guid.NewGuid(), + AihitName = name.Trim(), + EsppName = null + }; + + if (!await unitFieldService.CreateAsync(field) || !await unitFieldService.CommitAsync()) + logger.LogError($"Не удалось создать запись в таблице Fields: {name}, {field.ToJson()}"); + else + logger.LogInformation($"Создана запись а таблице Fields: {name}, {field.ToJson()}"); + + return await unitFieldService.GetAsync(field.Id); + } + } +} diff --git a/PARR.AIHITMainSyncer/Settings/MqSettings.cs b/PARR.AIHITMainSyncer/Settings/MqSettings.cs new file mode 100644 index 00000000..3e0c3ad9 --- /dev/null +++ b/PARR.AIHITMainSyncer/Settings/MqSettings.cs @@ -0,0 +1,15 @@ +using PARR.BLL.Contracts.Interfaces; + +namespace PARR.AIHITMainSyncer.Settings +{ + internal class MqSettings : IMqSettings + { + public string HostName { get; set; } = string.Empty; + + public string QueueName { get; set; } = string.Empty; + + public string User { get; set; } = string.Empty; + + public string Password { get; set; } = string.Empty; + } +} diff --git a/PARR.AIHITMainSyncer/Settings/WorkerSettings.cs b/PARR.AIHITMainSyncer/Settings/WorkerSettings.cs new file mode 100644 index 00000000..c6c51d04 --- /dev/null +++ b/PARR.AIHITMainSyncer/Settings/WorkerSettings.cs @@ -0,0 +1,7 @@ +namespace PARR.AIHITMainSyncer.Settings +{ + internal class WorkerSettings + { + public TimeSpan RepeatEvery { get; set; } + } +} diff --git a/PARR.AIHITSyncer/AihitSyncer.cs b/PARR.AIHITSyncer/AihitSyncer.cs index 1a279a70..8bfe42d2 100644 --- a/PARR.AIHITSyncer/AihitSyncer.cs +++ b/PARR.AIHITSyncer/AihitSyncer.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.Logging; using PARR.AIHITSyncer.Services; using PARR.AIHITSyncer.Settings; using PARR.BLL.Services.Interfaces; -using PARR.DAL.Models; namespace PARR.AIHITSyncer { diff --git a/PARR.AIHITSyncer/Services/SyncerService.cs b/PARR.AIHITSyncer/Services/SyncerService.cs index 82ab4757..af7a7be3 100644 --- a/PARR.AIHITSyncer/Services/SyncerService.cs +++ b/PARR.AIHITSyncer/Services/SyncerService.cs @@ -152,7 +152,7 @@ namespace PARR.AIHITSyncer.Services else logger.LogInformation($"Создана запись а таблице WorkGroups: {workGroupName}, {workGroup.ToJson()}"); - return await workGroupService.GetAsync(workGroup.Id); ; + return await workGroupService.GetAsync(workGroup.Id); } diff --git a/PARR.AIHITSyncerWorker/PARR.AIHITSyncerWorker.csproj b/PARR.AIHITSyncerWorker/PARR.AIHITSyncerWorker.csproj index b662fc6e..f6547c90 100644 --- a/PARR.AIHITSyncerWorker/PARR.AIHITSyncerWorker.csproj +++ b/PARR.AIHITSyncerWorker/PARR.AIHITSyncerWorker.csproj @@ -18,6 +18,6 @@ - + diff --git a/PARR.AIHITSyncerWorker/Program.cs b/PARR.AIHITSyncerWorker/Program.cs index f46ea7e9..8997a21c 100644 --- a/PARR.AIHITSyncerWorker/Program.cs +++ b/PARR.AIHITSyncerWorker/Program.cs @@ -1,7 +1,7 @@ -using PARR.AIHITSyncerWorker; using Elastic.CommonSchema.Serilog; +using PARR.AIHITMainSyncer; +using PARR.AIHITSyncerWorker; using Serilog; -using PARR.AIHITSyncer; var builder = Host.CreateApplicationBuilder(); @@ -22,9 +22,9 @@ builder.Services.AddLogging(config => config.AddSerilog(logger.CreateLogger()); }); -builder.Services.InstallAihitSyncerServices(builder.Configuration); -builder.Configuration.AddAihitSyncerConfigurations(builder.Services); -builder.Services.AddAihitSyncerSettings(builder.Configuration); +builder.Services.InstallAihitMainSyncerServices(builder.Configuration); +builder.Configuration.AddAihitMainSyncerConfigurations(builder.Services); +builder.Services.AddAihitMainSyncerSettings(builder.Configuration); builder.Services.AddHostedService(); diff --git a/PARR.AIHITSyncerWorker/Worker.cs b/PARR.AIHITSyncerWorker/Worker.cs index 69da8a94..ebdb869e 100644 --- a/PARR.AIHITSyncerWorker/Worker.cs +++ b/PARR.AIHITSyncerWorker/Worker.cs @@ -1,23 +1,25 @@ -using PARR.AIHITSyncer; +using PARR.AIHITMainSyncer; namespace PARR.AIHITSyncerWorker { public class Worker : BackgroundService { private readonly ILogger _logger; - private readonly IAihitSyncer aihitSyncer; + private readonly IAihitMainSyncer aihitSyncer; - public Worker(ILogger logger, IAihitSyncer aihitSyncer) + public Worker(ILogger logger, IAihitMainSyncer aihitSyncer) { _logger = logger; this.aihitSyncer = aihitSyncer; } + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { aihitSyncer.Start(); } + public override Task StopAsync(CancellationToken cancellationToken) { aihitSyncer.Stop(); diff --git a/PARR.API.sln b/PARR.API.sln index a3752ec3..af5a834a 100644 --- a/PARR.API.sln +++ b/PARR.API.sln @@ -84,6 +84,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.Common", "PARR.Common\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.AIHITMainLoader", "PARR.AIHITMainLoader\PARR.AIHITMainLoader.csproj", "{1BB7D841-4F6A-43B6-B89A-F0B09CDA40B1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.AIHITMainSyncer", "PARR.AIHITMainSyncer\PARR.AIHITMainSyncer.csproj", "{173E33B2-A10B-4FE9-802C-B22940326DEB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -232,6 +234,10 @@ Global {1BB7D841-4F6A-43B6-B89A-F0B09CDA40B1}.Debug|Any CPU.Build.0 = Debug|Any CPU {1BB7D841-4F6A-43B6-B89A-F0B09CDA40B1}.Release|Any CPU.ActiveCfg = Release|Any CPU {1BB7D841-4F6A-43B6-B89A-F0B09CDA40B1}.Release|Any CPU.Build.0 = Release|Any CPU + {173E33B2-A10B-4FE9-802C-B22940326DEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {173E33B2-A10B-4FE9-802C-B22940326DEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {173E33B2-A10B-4FE9-802C-B22940326DEB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {173E33B2-A10B-4FE9-802C-B22940326DEB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PARR.DAL/Services/Implementations/Unit/UnitFieldService.cs b/PARR.DAL/Services/Implementations/Unit/UnitFieldService.cs index 53144142..a3c2cecc 100644 --- a/PARR.DAL/Services/Implementations/Unit/UnitFieldService.cs +++ b/PARR.DAL/Services/Implementations/Unit/UnitFieldService.cs @@ -19,5 +19,10 @@ namespace PARR.DAL.Services.Implementations.Unit { this.dataContext = dataContext; } + + public async Task GetByAihitNameAsync(string name) + { + return await EntitySet.FirstOrDefaultAsync(uf => uf.AihitName.ToLower() == name.ToLower()); + } } } diff --git a/PARR.DAL/Services/Implementations/Unit/UnitService.cs b/PARR.DAL/Services/Implementations/Unit/UnitService.cs index 6697d539..2b791a38 100644 --- a/PARR.DAL/Services/Implementations/Unit/UnitService.cs +++ b/PARR.DAL/Services/Implementations/Unit/UnitService.cs @@ -18,5 +18,20 @@ namespace PARR.DAL.Services.Implementations.Unit { this.dataContext = dataContext; } + + + public async Task GetUnitWithFieldsAsync(string name) + { + return await GetUnitWithIncludeFields() + .FirstOrDefaultAsync(u => u.Name.ToLower() == name.ToLower()); + } + + + private IQueryable GetUnitWithIncludeFields() + { + return EntitySet + .Include(t => t.UnitFields) + .ThenInclude(f => f.UnitField); + } } } diff --git a/PARR.DAL/Services/Interfaces/Unit/IUnitFieldService.cs b/PARR.DAL/Services/Interfaces/Unit/IUnitFieldService.cs index a0c4dbf0..3b5a48c4 100644 --- a/PARR.DAL/Services/Interfaces/Unit/IUnitFieldService.cs +++ b/PARR.DAL/Services/Interfaces/Unit/IUnitFieldService.cs @@ -3,7 +3,8 @@ using PARR.DAL.Services.Interfaces.Base; namespace PARR.DAL.Services.Interfaces.Unit { - public interface IUnitFieldService: IBaseService + public interface IUnitFieldService : IBaseService { + Task GetByAihitNameAsync(string name); } } diff --git a/PARR.DAL/Services/Interfaces/Unit/IUnitService.cs b/PARR.DAL/Services/Interfaces/Unit/IUnitService.cs index 534cc21c..db142657 100644 --- a/PARR.DAL/Services/Interfaces/Unit/IUnitService.cs +++ b/PARR.DAL/Services/Interfaces/Unit/IUnitService.cs @@ -4,5 +4,6 @@ namespace PARR.DAL.Services.Interfaces.Unit { public interface IUnitService : IBaseService { + Task GetUnitWithFieldsAsync(string name); } }