using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; 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.Services { internal class SyncerService : ISyncerService { private readonly ILogger logger; private readonly IHostService hostService; private readonly IApplicationService appService; private readonly IApplicationInHostService appInHostService; private readonly IApplicationTypeService appTypeService; private readonly ITransformService transformService; public List AppTypes { get; private set; } = new List(); public SyncerService( ILogger logger, IHostService hostService, IApplicationService appService, IApplicationInHostService appInHostService, IApplicationTypeService appTypeService, ITransformService transformService ) { this.logger = logger; this.hostService = hostService; this.appService = appService; this.appInHostService = appInHostService; this.appTypeService = appTypeService; this.transformService = transformService; } public async Task SyncAsync(string msg) { await GetAppTypesAsync(); var objFromQuery = transformService.GetModelFromJson(msg); if (objFromQuery == null) return; await SyncHostAsync(objFromQuery); } private async Task SyncHostAsync(AihitDataMq objFromQuery) { //ищем существующий хост в БД var host = await hostService.GetHostWithAppsAsync(objFromQuery.IP); var isNew = false; //если не нашли - создаём if (host == null) { isNew = true; host = await CreateHostAsync(objFromQuery); } else { logger.LogDebug($"Найден хост в БД: {host!.Ek}({host.IP})"); //проверям и обновляем поля await SyncHostFieldsAsync(host, objFromQuery); } //получаем лист ПО из объекта var appsInObj = await GetAppsInObjectAsync(host, objFromQuery); await SyncApplicationInHostAsync(host, appsInObj, !isNew); } private async Task CreateHostAsync(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 async Task SyncHostFieldsAsync(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> GetAppsInObjectAsync(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 CreateAppIfNotExistAsync(string appName, ApplicationTypesEnum type) { if (string.IsNullOrEmpty(appName) || string.IsNullOrWhiteSpace(appName)) return null; 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 SyncApplicationInHostAsync(Host? host, List appsInObject, bool isUpdateDateModified = false) { var isUpdated = false; if (host == null) return; logger.LogDebug($"Host: {host.Ek}({host.IP}), начало синхронизации приложений ({appsInObject.Count()}) "); 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; } /// /// Возвращает первую цифру из строки, котроая является кодом статуса или зоны ответственности для ЭК /// /// /// 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 GetAppTypesAsync() { if (AppTypes == null || AppTypes.Count == 0) AppTypes = await appTypeService.Get().ToListAsync(); } } }