using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PARR.AIHITSyncer.Settings; using PARR.BLL.Domain.Mq; using PARR.BLL.Services.Interfaces; using PARR.DAL.Contracts; using PARR.DAL.Extensions; using PARR.DAL.Models; using PARR.DAL.Services.Interfaces; using System.Text.RegularExpressions; namespace PARR.AIHITSyncer { internal class AihitSyncer : IAihitSyncer { private readonly ILogger logger; private readonly WorkerSettings workerSettings; private readonly IMqService mqService; private readonly MqSettings mqSettings; private readonly ITransformService transformService; private readonly IServiceProvider serviceProvider; public List AppTypes { get; private set; } = new List(); public AihitSyncer( ILogger logger, WorkerSettings workerSettings, IMqService mqService, MqSettings mqSettings, ITransformService transformService, IServiceProvider serviceProvider ) { this.logger = logger; this.workerSettings = workerSettings; this.mqService = mqService; this.mqSettings = mqSettings; this.transformService = transformService; this.serviceProvider = serviceProvider; } private async Task GetAppTypesAsync() { if (AppTypes == null || AppTypes.Count == 0) using (var scope = serviceProvider.CreateScope()) { var appTypesService = scope.ServiceProvider.GetService(); if (appTypesService == null) throw new Exception("Не смог получить серивс IApplicationTypeService, scope.ServiceProvider.GetService()"); AppTypes = await appTypesService.Get().ToListAsync(); } } public void Start() { logger.LogInformation("Запуск сервиса синхронизации данных из АИХИТ и ПАРР."); var isConnected = mqService.InitConsumer(mqSettings, SyncDataAsync); if (!isConnected) throw new Exception("Ошибка при подключении к RabbitMq"); } public void Stop() { mqService.Dispose(); } private async Task SyncDataAsync(string msg) { var objFromQuery = transformService.GetModelFromJson(msg); if (objFromQuery == null) return; using (var scope = serviceProvider.CreateScope()) { var hostService = scope.ServiceProvider.GetService(); if (hostService == null) throw new Exception("Не смог получить серивс IHostService, scope.ServiceProvider.GetService()"); await SyncHostToScopeAsync(hostService, objFromQuery); await GetAppTypesAsync(); } logger.LogDebug($"Закончил работу с сообщением: {msg}"); //await Task.CompletedTask; } private async Task SyncHostToScopeAsync(IHostService hostService, AihitDataMq objFromQuery) { //ищем существующий хост в БД var host = await hostService.GetHostWithAppsAsync(objFromQuery.IP); var isNew = false; //если не нашли - создаём if (host == null) { isNew = true; host = await CreateHostAsync(hostService, objFromQuery); } else { logger.LogDebug($"Найден хост в БД: {host!.Ek}({host.IP})"); //проверям и обновляем поля await SyncHostFieldsAsync(hostService, host, objFromQuery); } //получаем лист ПО из объекта var appsInObj = await GetAppInObjectAsync(host, objFromQuery); await SyncApplicationAndHostAsync(host, appsInObj, !isNew); } private async Task> GetAppInObjectAsync(Host? host, AihitDataMq objFromQuery) { var applications = new List(); if (objFromQuery.APPType != null) { var appList = objFromQuery.APPType.Split(new char[] { ',', ';' }).Distinct().ToList(); foreach (var app in appList) { var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.APP); if (created != null) applications.Add(created); } } if (objFromQuery.DBType != null) { var appList = objFromQuery.DBType.Split(new char[] { ',', ';' }).Distinct().ToList(); foreach (var app in appList) { var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.DB); if (created != null) applications.Add(created); } } if (objFromQuery.OSType != null) { var appList = objFromQuery.OSType.Split(new char[] { ',', ';' }).Distinct().ToList(); foreach (var app in appList) { var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.OS); if (created != null) applications.Add(created); } } return applications; } private async Task SyncHostFieldsAsync(IHostService hostService, Host host, AihitDataMq objFromQuery) { var isChanged = false; if (host.IP != objFromQuery.IP) { host.IP = objFromQuery.IP; isChanged = true; } if (host.EkStatusCode != ResolveCode(objFromQuery.Status)) { host.EkStatusCode = ResolveCode(objFromQuery.Status); isChanged = true; } if (objFromQuery.WorkGroup != null && (host.WorkGroup == null || !host.WorkGroup!.Equals(objFromQuery.WorkGroup))) { host.WorkGroup = objFromQuery.WorkGroup; isChanged = true; } if (host.ResponseAreaCode != ((ResolveCode(objFromQuery.ResponseArea) == 0) ? 99 : ResolveCode(objFromQuery.ResponseArea))) {//TODO Возможно проще выгружать с помощью INCLUDE зону ответственности и сравнивать string host.ResponseAreaCode = ((ResolveCode(objFromQuery.ResponseArea) == 0) ? 99 : ResolveCode(objFromQuery.ResponseArea)); isChanged = true; } if (isChanged) { host.DateModified = DateTimeOffset.UtcNow; if (!await hostService.CommitAsync()) logger.LogError($"Не удалось обновить узел {host.Ek}({host.IP})"); else logger.LogInformation($"Обновлён Host: {host.Ek}({host.Id})"); } } private async Task CreateHostAsync(IHostService hostService, AihitDataMq objFromQuery) { var id = Guid.NewGuid(); var host = new Host { Id = id, Ek = objFromQuery.EK, IP = objFromQuery.IP, EkStatusCode = ResolveCode(objFromQuery.Status), WorkGroup = objFromQuery.WorkGroup, ResponseAreaCode = ResolveCode(objFromQuery.ResponseArea) }; if (!await hostService.CreateAsync(host) || !await hostService.CommitAsync()) logger.LogError($"Не удалось создать узел {host.Ek}({host.IP})"); else logger.LogInformation($"----- Создан узел: {host.Id} -----"); return await hostService.GetHostWithAppsAsync(host.IP); } /// /// Возвращает первую цифру из строки, котроая является кодом статуса или зоны ответственности для ЭК /// /// /// private int ResolveCode(string field) { var pattern = "\\d+"; var regex = new Regex(pattern); int.TryParse(regex.Match(field).Value, out int result); return result; } private async Task CreateAppIfNotExistAsync(string appName, ApplicationTypesEnum type) { if (string.IsNullOrEmpty(appName) || string.IsNullOrWhiteSpace(appName)) return null; using (var scope = serviceProvider.CreateScope()) { var appService = scope.ServiceProvider.GetService(); if (appService == null) throw new Exception("Не смог получить серивс IApplicationService, scope.ServiceProvider.GetService()"); var existApp = await appService.GetByNameAsync(appName); if (existApp != null) return existApp; //Создаем var appType = GetAppTypeByName(type); if (appType == null) { logger.LogError($"Не удалось создать запись в таблице Applications: {appName}, так как не получил AppType: {type.ToString()}"); return null; } var app = new Application { Id = Guid.NewGuid(), Name = appName.Trim(), ApplicationTypeId = appType.Id }; if (!await appService.CreateAsync(app) || !await appService.CommitAsync()) logger.LogError($"Не удалось создать запись в таблице Application: {appName}, {app.ToJson()}"); else logger.LogInformation($"Создана запись а таблице Application: {appName}, {app.ToJson()}"); return await appService.GetAsync(app.Id); } } /// /// Метод синхронизации /// /// Хост /// Список программного обеспечения /// Признак обновления поля DateModified /// private async Task SyncApplicationAndHostAsync(Host? host, List appsInObject, bool isUpdateDateModified = false) { var isUpdated = false; if (host == null) return; logger.LogDebug($"Host: {host.Ek}({host.IP}), начало синхронизации приложений ({appsInObject.Count()}) "); using (var scope = serviceProvider.CreateScope()) { var appInHostService = scope.ServiceProvider.GetService(); if (appInHostService == null) throw new Exception("Не смог получить серивс IApplicationInHostService, scope.ServiceProvider.GetService()"); foreach (var appInObject in appsInObject) { var exist = host.ApplicationsInHosts.FirstOrDefault(a => a.ApplicationId == appInObject.Id); if (exist == null) { //add logger.LogInformation($"Host: {host.Ek}({host.IP}), добавление нового приложения Application {appInObject.Name}"); var newAppHost = new ApplicationInHost { Id = Guid.NewGuid(), ApplicationId = appInObject.Id, HostId = host.Id }; if (!await appInHostService.CreateAsync(newAppHost)) logger.LogError($"Не удалось создать ApplicationInHost {appInObject.Name}, host: {host.Ek}({host.IP})"); isUpdated = true; } } foreach (var appInHost in host.ApplicationsInHosts) { var appToRemove = appsInObject.FirstOrDefault(a => a.Id == appInHost.ApplicationId); if (appToRemove == null) { //todo remove appToRemove var obj = await appInHostService.Get().FirstOrDefaultAsync(t => t.ApplicationId == appInHost.ApplicationId && t.HostId == host.Id); if (obj != null) { logger.LogInformation($"Host: {host.Ek}({host.IP}), удаление неактуального приложения Application {appInHost.Application?.Name}"); appInHostService.Delete(obj); if (!await appInHostService.CommitAsync()) logger.LogError($"Не удалось удалить Application {appInHost.Application?.Name} узла {host.Ek}({host.IP})"); isUpdated = true; } } } if (isUpdated && isUpdateDateModified) host.DateModified = DateTimeOffset.UtcNow; if (!await appInHostService.CommitAsync()) logger.LogError($"Не удалось обновить ApplicationInHost для Host: {host.Ek}({host.IP})"); } logger.LogDebug($"Host: {host.Ek}({host.IP} конец синхронизации приложений"); } private ApplicationType? GetAppTypeByName(ApplicationTypesEnum type) { var existType = AppTypes.FirstOrDefault(t => t.Name == type.ToString()); if (existType == null) logger.LogError($"Не найден AppType: {type}"); return existType; } } }