feat(aihitMainSyncer): Написана логика создания новых Unit и Field.

This commit is contained in:
Mikhail Kuznetsov
2025-05-19 16:09:55 +10:00
parent 082bc9bf15
commit 1ce8fcb284
18 changed files with 328 additions and 12 deletions

View File

@@ -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<AihitMainSyncer> logger;
private readonly IMqService mqService;
private readonly MqSettings mqSettings;
private readonly IServiceProvider serviceProvider;
public AihitMainSyncer(
ILogger<AihitMainSyncer> 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<ISyncerService>();
if (syncerService == null)
throw new Exception("Не смог получить серивс ISyncerService, scope.ServiceProvider.GetService<ISyncerService>()");
await syncerService.SyncAsync(msg);
}
});
if (!isConnected)
throw new Exception("Ошибка при подключении к RabbitMq");
}
public void Stop()
{
mqService.Dispose();
}
}
}

View File

@@ -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<IAihitMainSyncer, AihitMainSyncer>();
services.AddTransient<ISyncerService, SyncerService>();
}
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);
}
}
}

View File

@@ -0,0 +1,10 @@
namespace PARR.AIHITMainSyncer
{
public interface IAihitMainSyncer
{
void Start();
void Stop();
}
}

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PARR.BLL\PARR.BLL.csproj" />
<ProjectReference Include="..\PARR.DAL\PARR.DAL.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,7 @@
namespace PARR.AIHITMainSyncer.Services
{
public interface ISyncerService
{
Task SyncAsync(string msg);
}
}

View File

@@ -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<SyncerService> logger;
private readonly ITransformService transformService;
private readonly IUnitService unitService;
private readonly IUnitFieldService unitFieldService;
public SyncerService(
ILogger<SyncerService> 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<AihitMainDataMq>(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<Guid> 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<List<Guid>> GetFieldsInObjectAsync(AihitMainDataMq objFromQuery)
{
var fields = new List<Guid>();
foreach (var item in objFromQuery.Properties)
{
var field = await CreateUnitFieldIfNotExistAsync(item.Key);
fields.Add(field!.Id);
}
return fields;
}
private async Task<Unit?> 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<UnitField?> 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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,7 @@
namespace PARR.AIHITMainSyncer.Settings
{
internal class WorkerSettings
{
public TimeSpan RepeatEvery { get; set; }
}
}

View File

@@ -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
{

View File

@@ -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);
}

View File

@@ -18,6 +18,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PARR.AIHITSyncer\PARR.AIHITSyncer.csproj" />
<ProjectReference Include="..\PARR.AIHITMainSyncer\PARR.AIHITMainSyncer.csproj" />
</ItemGroup>
</Project>

View File

@@ -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<Worker>();

View File

@@ -1,23 +1,25 @@
using PARR.AIHITSyncer;
using PARR.AIHITMainSyncer;
namespace PARR.AIHITSyncerWorker
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly IAihitSyncer aihitSyncer;
private readonly IAihitMainSyncer aihitSyncer;
public Worker(ILogger<Worker> logger, IAihitSyncer aihitSyncer)
public Worker(ILogger<Worker> 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();

View File

@@ -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

View File

@@ -19,5 +19,10 @@ namespace PARR.DAL.Services.Implementations.Unit
{
this.dataContext = dataContext;
}
public async Task<UnitField?> GetByAihitNameAsync(string name)
{
return await EntitySet.FirstOrDefaultAsync(uf => uf.AihitName.ToLower() == name.ToLower());
}
}
}

View File

@@ -18,5 +18,20 @@ namespace PARR.DAL.Services.Implementations.Unit
{
this.dataContext = dataContext;
}
public async Task<Models.Unit.Unit?> GetUnitWithFieldsAsync(string name)
{
return await GetUnitWithIncludeFields()
.FirstOrDefaultAsync(u => u.Name.ToLower() == name.ToLower());
}
private IQueryable<Models.Unit.Unit> GetUnitWithIncludeFields()
{
return EntitySet
.Include(t => t.UnitFields)
.ThenInclude(f => f.UnitField);
}
}
}

View File

@@ -5,5 +5,6 @@ namespace PARR.DAL.Services.Interfaces.Unit
{
public interface IUnitFieldService : IBaseService<UnitField>
{
Task<UnitField?> GetByAihitNameAsync(string name);
}
}

View File

@@ -4,5 +4,6 @@ namespace PARR.DAL.Services.Interfaces.Unit
{
public interface IUnitService : IBaseService<Models.Unit.Unit>
{
Task<Models.Unit.Unit?> GetUnitWithFieldsAsync(string name);
}
}