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; private readonly IWorkGroupService workGroupService; public List AppTypes { get; private set; } = new List(); public SyncerService( ILogger logger, IHostService hostService, IApplicationService appService, IApplicationInHostService appInHostService, IApplicationTypeService appTypeService, ITransformService transformService, IWorkGroupService workGroupService ) { this.logger = logger; this.hostService = hostService; this.appService = appService; this.appInHostService = appInHostService; this.appTypeService = appTypeService; this.transformService = transformService; this.workGroupService = workGroupService; } 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.GetHostWithAppsByIpAsync(objFromQuery.IP); var host = await hostService.GetHostWithAppsByEkAsync(objFromQuery.EK); 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(); int responseAreaCode = GetResponseAreaCodeFromStr(objFromQuery.ResponseArea) ?? 0; if (responseAreaCode == 0) return null; var host = new Host { Id = id, Ek = objFromQuery.EK, IP = objFromQuery.IP, EkStatusCode = ResolveCode(objFromQuery.Status), //WorkGroup = objFromQuery.WorkGroup, WorkGroup = await CreateWorkGroupIfNotExistAsync(objFromQuery.WorkGroup, objFromQuery.WorkGroupResponseArea), // ResponseAreaCode = ((ResolveCode(objFromQuery.ResponseArea) == 0) ? 99 : ResolveCode(objFromQuery.ResponseArea)) ResponseAreaCode = responseAreaCode }; if (!await hostService.CreateAsync(host) || !await hostService.CommitAsync()) logger.LogError($"Не удалось создать узел {host.Ek}({host.IP})"); else logger.LogInformation($"----- Создан узел: {host.Id} -----"); //return await hostService.GetHostWithAppsByIpAsync(host.IP); return await hostService.GetHostWithAppsByEkAsync(host.Ek); } private async Task CreateWorkGroupIfNotExistAsync(string? workGroupName, string wgResponseArea) { if (string.IsNullOrEmpty(workGroupName) || string.IsNullOrWhiteSpace(workGroupName)) return null; if (string.IsNullOrEmpty(wgResponseArea) || string.IsNullOrWhiteSpace(wgResponseArea)) return null; int wgCode = GetResponseAreaCodeFromStr(wgResponseArea) ?? 0; if (wgCode == 0) return null; var existWorkGroup = await workGroupService.GetByNameAsync(workGroupName); if (existWorkGroup != null) { //проверяем, соответствует ли рабочая группа if (existWorkGroup.ResponseAreaCode != wgCode) { logger.LogDebug($"У рабочей группы {existWorkGroup.Name} не совпадают ЗО, обновляем, текущее значение: {existWorkGroup.ResponseAreaCode}, новое: {wgCode}"); existWorkGroup.ResponseAreaCode = wgCode; if (!await workGroupService.CommitAsync()) { logger.LogError($"Ошибка при обновлении ЗО рабочей группы {existWorkGroup.Name}"); } } return existWorkGroup; } var workGroup = new WorkGroup { Id = Guid.NewGuid(), Name = workGroupName.Trim(), ResponseAreaCode = wgCode }; if (!await workGroupService.CreateAsync(workGroup) || !await workGroupService.CommitAsync()) logger.LogError($"Не удалось создать запись в таблице WorkGroups: {workGroupName}, {workGroup.ToJson()}"); else logger.LogInformation($"Создана запись а таблице WorkGroups: {workGroupName}, {workGroup.ToJson()}"); return await workGroupService.GetAsync(workGroup.Id); } 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; } //TODO Удалить после перехода на самостоятельный объект WorkGroup //if (objFromQuery.WorkGroup != null && (host.WorkGroup == null || !host.WorkGroup!.Equals(objFromQuery.WorkGroup))) //{ // host.WorkGroup = objFromQuery.WorkGroup; // isChanged = true; //} if (objFromQuery.WorkGroup != null && (host.WorkGroup == null || !host.WorkGroup!.Name.Equals(objFromQuery.WorkGroup))) { host.WorkGroup = await CreateWorkGroupIfNotExistAsync(objFromQuery.WorkGroup, objFromQuery.WorkGroupResponseArea); isChanged = true; } int responseAreaCode = GetResponseAreaCodeFromStr(objFromQuery.ResponseArea) ?? 0; if (responseAreaCode == 0) { logger.LogWarning($"Ошибка при получении ЗО хоста: {objFromQuery.EK}, ЗО: {objFromQuery.ResponseArea}"); return; } //if (host.ResponseAreaCode != ((ResolveCode(objFromQuery.ResponseArea) == 0) ? 99 : ResolveCode(objFromQuery.ResponseArea))) if (host.ResponseAreaCode != responseAreaCode) {//TODO Возможно проще выгружать с помощью INCLUDE зону ответственности и сравнивать string //host.ResponseAreaCode = ((ResolveCode(objFromQuery.ResponseArea) == 0) ? 99 : ResolveCode(objFromQuery.ResponseArea)); host.ResponseAreaCode = responseAreaCode; 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.APP != null) { var appList = ParseAppsList(objFromQuery.APP); foreach (var app in appList) { var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.APP); if (created != null) applications.Add(created); } } if (objFromQuery.DB != null) { var appList = ParseAppsList(objFromQuery.DB); foreach (var app in appList) { var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.DB); if (created != null) applications.Add(created); } } if (objFromQuery.CKBS != null) { var appList = ParseAppsList(objFromQuery.CKBS); foreach (var app in appList) { var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.CKBS); if (created != null) applications.Add(created); } } if (objFromQuery.IB != null) { var appList = ParseAppsList(objFromQuery.IB); foreach (var app in appList) { var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.IB); if (created != null) applications.Add(created); } } if (objFromQuery.SI != null) { var appList = ParseAppsList(objFromQuery.SI); foreach (var app in appList) { var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.SI); if (created != null) applications.Add(created); } } if (objFromQuery.SM != null) { var appList = ParseAppsList(objFromQuery.SM); foreach (var app in appList) { var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.SM); if (created != null) applications.Add(created); } } if (objFromQuery.OS != null) { var appList = ParseAppsList(objFromQuery.OS); foreach (var app in appList) { var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.OS); if (created != null) applications.Add(created); } } return applications; } private List ParseAppsList(string appStr) { // в АИХИТ стали появляться разделители \r \n находим их и заменяем на , if (appStr.Contains("\r") || appStr.Contains("\n")) { string orig = appStr; appStr = appStr.Replace("\r", ",").Replace("\n", ","); logger.LogDebug($"В строке найдены переносы, заменены на разделители. Исходная строка: {orig}. Результат: {appStr}"); } var regex = new Regex("\\([^)]*\\)"); if (regex.IsMatch(appStr)) foreach (var parenthesis in regex.Matches(appStr)) { if (!string.IsNullOrEmpty(parenthesis.ToString())) { var newParenthesis = parenthesis.ToString()!.Replace(",", "<|>").Replace(";", "<|>"); appStr = appStr.Replace(parenthesis!.ToString()!, newParenthesis); } } return appStr.Split(new char[] { ',', ';' }).Select(a => a.Replace("<|>", ",")).Distinct().ToList(); } private async Task CreateAppIfNotExistAsync(string appName, ApplicationTypesEnum type) { if (string.IsNullOrEmpty(appName) || string.IsNullOrWhiteSpace(appName)) return null; var appType = GetAppTypeByName(type); if (appType == null) { logger.LogError($"Не удалось создать запись в таблице Applications: {appName}, так как не получил AppType: {type.ToString()}"); return null; } var existApp = await appService.GetByNameAsync(appName, appType.Id); if (existApp != null) return existApp; //Создаем 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 int? GetResponseAreaCodeFromStr(string responseAreaStr) { if (string.IsNullOrEmpty(responseAreaStr) || string.IsNullOrWhiteSpace(responseAreaStr)) return null; if (responseAreaStr.ToUpper().Trim() == "ОБЩЕЕ") return (int)ResponseAreaEnum.general; if (responseAreaStr.ToUpper().Trim() == "00-ГВЦ") return (int)ResponseAreaEnum.gvc; return ResolveCode(responseAreaStr); } private async Task GetAppTypesAsync() { if (AppTypes == null || AppTypes.Count == 0) AppTypes = await appTypeService.Get().ToListAsync(); } } }