using AutoMapper; using MailKit.Search; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using MimeKit; using PARR.AIHIT.Context; using PARR.AIHIT.Models; using PARR.AIHIT.Settings; using PARR.DAL.Contracts; using PARR.DAL.Extensions; using PARR.DAL.Models; using PARR.DAL.Models.AIHIT; using PARR.DAL.Models.V1; using PARR.DAL.Services.Interfaces; using PARR.DAL.Services.Interfaces.AIHIT; using PARR.DAL.Services.Interfaces.V1; using PARR.Mail.Services; using System.Text; using System.Xml; namespace PARR.AIHIT { internal class Syncher : ISyncher { private readonly ILogger logger; private readonly IMailService mailService; private readonly SyncherSettings syncherSettings; private readonly V1_IHostService V1_hostService; private readonly V1_IApplicationTypeService V1_appTypeService; private readonly V1_IApplicationInHostService V1_appInHostService; private readonly V1_IApplicationService V1_appService; private readonly IHostService hostService; private readonly IMapper mapper; private readonly IApplicationService appService; private readonly IApplicationTypeService appTypeService; private readonly IApplicationInHostService appInHostService; private readonly IRawDataEKService rawDataEKService; private readonly ISettingService settingService; public List AppTypes { get; private set; } = new List(); public Syncher( ILogger logger, IMailService mailService, SyncherSettings syncherSettings, V1_IHostService V1_hostService, V1_IApplicationService V1_appService, V1_IApplicationTypeService V1_appTypeService, V1_IApplicationInHostService V1_appInHostService, IHostService hostService, IMapper mapper, IApplicationService appService, IApplicationTypeService appTypeService, IApplicationInHostService appInHostService, IRawDataEKService rawDataEKService, ISettingService settingService ) { this.hostService = hostService; this.mapper = mapper; this.appService = appService; this.appTypeService = appTypeService; this.appInHostService = appInHostService; this.rawDataEKService = rawDataEKService; this.settingService = settingService; this.logger = logger; this.mailService = mailService; this.syncherSettings = syncherSettings; this.V1_hostService = V1_hostService; this.V1_appService = V1_appService; this.V1_appTypeService = V1_appTypeService; this.V1_appInHostService = V1_appInHostService; } private async Task GetAppTypesAsync() { AppTypes = await appTypeService.Get().ToListAsync(); } public async Task InvokeAsync() { await GetAppTypesAsync(); var messages = await mailService.CheckMailAsync(SearchQuery.NotSeen); if (!messages.Any()) { return; } var filtered = messages.Where(m => syncherSettings.IncludeAddressesArray.Any(i => m.Envelope.From.Mailboxes.FirstOrDefault()?.Address == i)).ToList(); var lastMessage = filtered.OrderByDescending(f => f.Date).First(); var attachments = await mailService.GetAttachmentsAsync(lastMessage); foreach (var attachment in attachments) { if (attachment is MessagePart) { continue; } else { using var stream = new MemoryStream(); ((MimePart)attachment).Content.DecodeTo(stream); if (stream.Position > 0) stream.Position = 0; var fileName = EncodeString(((MimePart)attachment).FileName); logger.LogInformation($" Получен файл {fileName}"); if (fileName.Equals("Компонентный состав регионального ЭК.xml")) { XmlDocument doc = new XmlDocument(); doc.Load(stream); var vms = doc.GetElementsByTagName("Сведения"); //var hosts = await hostService.Get().ToListAsync(); foreach (XmlNode vm in vms) { var ip = vm?.Attributes?["IP_АДРЕС"]?.Value.Trim(); if (ip == null) continue; var regionalEK = vm!.Attributes!["РЕГИОНАЛЬНЫЙ_ЭК"]!.Value.Trim(); var linkEK = vm!.Attributes!["СВЯЗАННЫЙ_ЭК"]!.Value.Trim(); if (!linkEK.StartsWith("ВРТ")) continue; // var foundHost = await hostService.GetHostAsync(ip, linkEK, regionalEK); var foundHost = await V1_hostService.GetHostWithAppsAsync(ip, linkEK, regionalEK); var mappedHost = mapper.Map(vm); var hostApplications = await V1_GetAndFillApplicationsAsync(vm); if (foundHost == null) { // создаем if (!await V1_hostService.CreateAsync(mappedHost) || !await V1_hostService.CommitAsync()) { logger.LogError($"Не удалось создать узел {mappedHost.IP} {mappedHost.RegionalEK}"); continue; } else logger.LogInformation($"----- Создан Host: {mappedHost.Id}, {mappedHost.LinkEK} -----"); // Синхронизация Applications await V1_SyncApplicationAndHostAsync(foundHost, hostApplications); } else { // обновляем var isChanged = false; if (!foundHost.RegionalEK!.Equals(mappedHost.RegionalEK)) { foundHost.RegionalEK = mappedHost.RegionalEK; isChanged = true; } else if (!foundHost.LinkEK!.Equals(mappedHost.LinkEK)) { foundHost.LinkEK = mappedHost.LinkEK; isChanged = true; } else if (!foundHost.Status!.Equals(mappedHost.Status)) { foundHost.Status = mappedHost.Status; isChanged = true; } else if (!foundHost.WorkGroup!.Equals(mappedHost.WorkGroup)) { foundHost.WorkGroup = mappedHost.WorkGroup; isChanged = true; } else if (!foundHost.Responsible!.Equals(mappedHost.Responsible)) { foundHost.Responsible = mappedHost.Responsible; isChanged = true; } //else if (!foundHost.OS!.Equals(mappedHost.OS)) //{ // foundHost.OS = mappedHost.OS; // isChanged = true; //} //else if (!foundHost.DB!.Equals(mappedHost.DB)) //{ // foundHost.DB = mappedHost.DB; // isChanged = true; //} //else if (!foundHost.APP!.Equals(mappedHost.APP)) //{ // foundHost.APP = mappedHost.APP; // isChanged = true; //} if (isChanged) { foundHost.DateModified = DateTimeOffset.UtcNow; if (!await V1_hostService.CommitAsync()) logger.LogError($"Не удалось обновить узел {mappedHost.IP} {mappedHost.RegionalEK}"); else logger.LogInformation($"----- Обновлён Host: {mappedHost.Id}, {mappedHost.LinkEK} -----"); } // Синхронизация Applications await V1_SyncApplicationAndHostAsync(foundHost, hostApplications, true); } } } break; } } //прочитано для всех foreach (var message in filtered) { await mailService.SetAsSeenAsync(message); } } public async Task InvokeFromDatabase() { await GetAppTypesAsync(); //Получаем зоны ответственности из БД var respAreas = settingService.Get().Where(ra => ra.Group == AIHTITSettings.RespAreaGroupName).ToList(); //Запускаем хранимую процедуру в АИХ ИТ и складываем в таблицу RawDataEKs, предварительно почистив старые данные foreach (var respArea in respAreas) { var eksFromAIHIT = new List(); using (AIHContext db = new AIHContext()) { var zo = new Microsoft.Data.SqlClient.SqlParameter { ParameterName = "@зо", SqlDbType = System.Data.SqlDbType.NVarChar, Direction = System.Data.ParameterDirection.Input, Size = 60, Value = respArea.Value }; eksFromAIHIT = db.EKs.FromSqlRaw($"EXEC mao2.dbo.sp_IPP_PARR_PTK_get_EK @зо", zo).ToList(); if (eksFromAIHIT == null || !eksFromAIHIT.Any()) { logger.LogWarning($"Процедура sp_IPP_PARR_PTK_get_EK вурнула пустой список ЭК"); return; } } //Удаляем текущие данные по зоне ответственности respArea var toDelete = rawDataEKService.Get().Where(rd => rd.ResponseArea == respArea.Value).ToList(); toDelete.ForEach(d => rawDataEKService.Delete(d)); foreach (var ek in eksFromAIHIT) { // создаем var mappedEK = new RawDataEK() { Id = Guid.NewGuid(), DateCreated = DateTimeOffset.UtcNow, IP = ek.IP, Metka = ek.Metka, IsActive = ek.IsActive, IsImportant = ek.IsImportant, CreateTime = ek.CreateTime.ToString(), AdditionalInfo = ek.AdditionalInfo, ResponseArea = ek.ResponseArea, EKCategory = ek.EKCategory, EKFindCode = ek.EKFindCode, ProductCode = ek.ProductCode, ServiceCode = ek.ServiceCode, ShortName = ek.ShortName, ResponsibleByEK = ek.ResponsibleByEK, PlannedTimeToRepair = ek.PlannedTimeToRepair.ToString(), EKSubCategory = ek.EKSubCategory, FullName = ek.FullName, Prescription = ek.Prescription, Company = ek.Company, WorkGroup = ek.WorkGroup, Location = ek.Location, EKRevizor = ek.EKRevizor, EKRegister = ek.EKRegister, NetworkName = ek.NetworkName, Status = ek.Status, EKType = ek.EKType, EndExplotationDate = ek.EndExplotationDate.ToString(), StartExplotationDate = ek.StartExplotationDate.ToString(), TargetRepairTime = ek.TargetRepairTime, SysModTime = ek.SysModTime.ToString(), SysModUser = ek.SysModUser, NewEKFindCode = ek.NewEKFindCode, OldEKFindCode = ek.OldEKFindCode, CTSDirection = ek.CTSDirection, AIHID = ek.ID, IsUnreliableData = ek.IsUnreliableData, ShiftWorkGroup = ek.ShiftWorkGroup, ClientSoftware = ek.ClientSoftware, ClientOS = ek.ClientOS, DBType = ek.DBType, APPType = ek.APPType, CKBSServerType = ek.CKBSServerType, IBServerType = ek.IBServerType, InfrastructureServerType = ek.InfrastructureServerType, MonitoringServerType = ek.MonitoringServerType }; if (!await rawDataEKService.CreateAsync(mappedEK) || !await rawDataEKService.CommitAsync()) { logger.LogError($"Не удалось создать запись rawDataEK {mappedEK.ShortName}({mappedEK.IP})"); continue; } else logger.LogInformation($"----- Создана запись rawDataEK {mappedEK.ShortName}({mappedEK.IP}) -----"); } } //Разбираем сырые данные //Получаем интересующие статусы ЭК var statusList = settingService.Get().Where(s => s.Group == AIHTITSettings.StatusGroupName).Select(s => s.Value.Trim()).ToList(); if (statusList == null || statusList.Count == 0) return; var eks = rawDataEKService.Get().Where(e => e.IsActive == "t" && (e.Status != null && statusList.Contains(e.Status.Trim())) ).ToList(); foreach (var ek in eks) { if (ek.IP == null) continue; var foundHost = await hostService.GetHostWithAppsAsync(ek.IP); var mappedHost = mapper.Map(ek); var hostApplications = await GetAndFillApplicationsAsync(ek); if (foundHost == null) { // создаем if (!await hostService.CreateAsync(mappedHost) || !await hostService.CommitAsync()) { logger.LogError($"Не удалось создать узел {mappedHost.IP}"); continue; } else logger.LogInformation($"----- Создан Host: {mappedHost.Id} -----"); // Синхронизация Applications await SyncApplicationAndHostAsync(foundHost, hostApplications); } else { // обновляем var isChanged = false; if (!foundHost.Status!.Equals(mappedHost.Status)) { foundHost.Status = mappedHost.Status; isChanged = true; } else if (!foundHost.WorkGroup!.Equals(mappedHost.WorkGroup)) { foundHost.WorkGroup = mappedHost.WorkGroup; isChanged = true; } if (isChanged) { foundHost.DateModified = DateTimeOffset.UtcNow; if (!await hostService.CommitAsync()) logger.LogError($"Не удалось обновить узел {mappedHost.IP}"); else logger.LogInformation($"----- Обновлён Host: {mappedHost.Id} -----"); } // Синхронизация Applications await SyncApplicationAndHostAsync(foundHost, hostApplications, true); } } } private async Task V1_SyncApplicationAndHostAsync(V1_Host? host, List hostApplications, bool isUpdateDateModified = false) { var isUpdated = false; if (host == null) return; logger.LogInformation($"--- Host: {host.Id}, {host.LinkEK} начало синхронизации приложений ({hostApplications.Count()}) ---"); foreach (var changedHostApp in hostApplications) { var exist = host.ApplicationsInHosts.FirstOrDefault(a => a.ApplicationId == changedHostApp.Id); if (exist == null) { //add logger.LogInformation($"Host: {host.Id}, {host.LinkEK} добавление нового приложения Application {changedHostApp.Name}"); var newAppHost = new V1_ApplicationInHost { Id = Guid.NewGuid(), ApplicationId = changedHostApp.Id, HostId = host.Id }; if (!await V1_appInHostService.CreateAsync(newAppHost)) logger.LogError($"Не удалось создать ApplicationInHost {changedHostApp.Name}, host: {host.Id}, {host.LinkEK}"); isUpdated = true; } } foreach (var app in host.ApplicationsInHosts) { var appToRemove = hostApplications.FirstOrDefault(a => a.Id == app.Id); if (appToRemove == null) { //todo remove appToRemove var obj = await V1_appInHostService.Get().FirstOrDefaultAsync(t => t.ApplicationId == app.Id && t.HostId == host.Id); if (obj != null) { logger.LogInformation($"Host: {host.Id}, {host.LinkEK} удаление неактуального приложения Application {app.Application?.Name}"); V1_appInHostService.Delete(obj); isUpdated = true; } } } if (isUpdated && isUpdateDateModified) host.DateModified = DateTimeOffset.UtcNow; if (!await appInHostService.CommitAsync()) logger.LogError($"Не удалось обновить ApplicationInHost для Host: {host.LinkEK}, {host.Id}"); logger.LogInformation($"=== Host: {host.Id}, {host.LinkEK} конец синхронизации приложений ==="); } private async Task> V1_GetAndFillApplicationsAsync(XmlNode vm) { logger.LogInformation($"--- Начало парсинга данных о программном обеспечении(СП, БД, ОС) из Xml документа от АИХ ИТ ---"); var applications = new List(); //TODO GetAppFromXML(vm?.Attributes?["СП"]?.Value).ForEach(async item => await CreateAppIfNotExistAsync(item, ApplicationTypesEnum.APP)); //GetDBFromXML(vm?.Attributes?["СУБД"]?.Value).ForEach(async item => await CreateAppIfNotExistAsync(item, ApplicationTypesEnum.DB)); //GetOSFromXML(vm?.Attributes?["ОС"]?.Value).ForEach(async item => await CreateAppIfNotExistAsync(item, ApplicationTypesEnum.OS)); var appList = GetAppFromXML(vm?.Attributes?["СП"]?.Value); foreach (var app in appList) { var created = await V1_CreateAppIfNotExistAsync(app, ApplicationTypesEnum.APP); if (created != null) applications.Add(created); } var dbList = GetAppFromXML(vm?.Attributes?["СУБД"]?.Value); foreach (var db in dbList) { var created = await V1_CreateAppIfNotExistAsync(db, ApplicationTypesEnum.DB); if (created != null) applications.Add(created); } var osList = GetAppFromXML(vm?.Attributes?["ОС"]?.Value); foreach (var os in appList) { var created = await V1_CreateAppIfNotExistAsync(os, ApplicationTypesEnum.OS); if (created != null) applications.Add(created); } logger.LogInformation($"=== Конец парсинга данных о программном обеспечении(СП, БД, ОС) из Xml документа от АИХ ИТ ==="); return applications; } private async Task SyncApplicationAndHostAsync(Host? host, List hostApplications, bool isUpdateDateModified = false) { var isUpdated = false; if (host == null) return; logger.LogInformation($"--- Host: {host.Id}, начало синхронизации приложений ({hostApplications.Count()}) ---"); foreach (var changedHostApp in hostApplications) { var exist = host.ApplicationsInHosts.FirstOrDefault(a => a.ApplicationId == changedHostApp.Id); if (exist == null) { //add logger.LogInformation($"Host: {host.Id}, добавление нового приложения Application {changedHostApp.Name}"); var newAppHost = new ApplicationInHost { Id = Guid.NewGuid(), ApplicationId = changedHostApp.Id, HostId = host.Id }; if (!await appInHostService.CreateAsync(newAppHost)) logger.LogError($"Не удалось создать ApplicationInHost {changedHostApp.Name}, host: {host.Id}"); isUpdated = true; } } foreach (var app in host.ApplicationsInHosts) { var appToRemove = hostApplications.FirstOrDefault(a => a.Id == app.Id); if (appToRemove == null) { //todo remove appToRemove var obj = await appInHostService.Get().FirstOrDefaultAsync(t => t.ApplicationId == app.Id && t.HostId == host.Id); if (obj != null) { logger.LogInformation($"Host: {host.Id}, удаление неактуального приложения Application {app.Application?.Name}"); appInHostService.Delete(obj); isUpdated = true; } } } if (isUpdated && isUpdateDateModified) host.DateModified = DateTimeOffset.UtcNow; if (!await appInHostService.CommitAsync()) logger.LogError($"Не удалось обновить ApplicationInHost для Host: {host.Id}"); logger.LogInformation($"=== Host: {host.Id} конец синхронизации приложений ==="); } private async Task> GetAndFillApplicationsAsync(RawDataEK vm) { logger.LogInformation($"--- Начало парсинга данных о программном обеспечении(СП, БД, ОС) из АИХ ИТ ---"); var applications = new List(); if (vm.APPType != null) { var appList = vm.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 (vm.DBType != null) { var appList = vm.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 (vm.ClientOS != null) { var appList = vm.ClientOS.Split(new char[] { ',', ';' }).Distinct().ToList(); foreach (var app in appList) { var created = await CreateAppIfNotExistAsync(app, ApplicationTypesEnum.OS); if (created != null) applications.Add(created); } } logger.LogInformation($"=== Конец парсинга данных о программном обеспечении(СП, БД, ОС) из АИХ ИТ ==="); return applications; } private List GetAppFromXML(string? appField) { if (string.IsNullOrEmpty(appField)) return new List(); var appList = appField.Split(";").Distinct().ToList(); //appList.ForEach(item => item.Trim()); return appList; } private List GetDBFromXML(string? dbField) { if (string.IsNullOrEmpty(dbField)) return new List(); var dbList = dbField.Split(";").Distinct().ToList(); //dbList.ForEach(item => item.Trim()); return dbList; } private List GetOSFromXML(string? osField) { if (string.IsNullOrEmpty(osField)) return new List(); var osList = osField.Split(";").Distinct().ToList(); //osList.ForEach(item => item.Trim()); return osList; } private async Task V1_CreateAppIfNotExistAsync(string appName, ApplicationTypesEnum type)//Application application) { if (string.IsNullOrEmpty(appName) || string.IsNullOrWhiteSpace(appName)) return null; var existApp = await V1_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 V1_Application { Id = Guid.NewGuid(), Name = appName.Trim(), ApplicationTypeId = appType.Id }; if (!await V1_appService.CreateAsync(app) || !await V1_appService.CommitAsync()) logger.LogError($"Не удалось создать запись в таблице Application: {appName}, {app.ToJson()}"); else logger.LogInformation($"Создана запись а таблице Application: {appName}, {app.ToJson()}"); return await V1_appService.GetAsync(app.Id); } private async Task CreateAppIfNotExistAsync(string appName, ApplicationTypesEnum type)//Application application) { 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); } private ApplicationType? GetAppTypeByName(ApplicationTypesEnum type) { var existType = AppTypes.FirstOrDefault(t => t.Name == type.ToString()); if (existType == null) logger.LogError($"Не найден AppType: {type}"); return existType; } private static string EncodeString(string str) { byte[] bStr = Encoding.GetEncoding("koi8r").GetBytes(str); Encoding encoding = Encoding.GetEncoding("koi8r"); str = encoding.GetString(bStr, 0, bStr.Length); return str; } } }