diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e3757bc7..0544454d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,8 @@ variables: REPO: "harbor.dvgd.rzd" PROD_NAME_API: "parr/parr-api" - PROD_NAME_AIHIT: "parr/parr-aihit-syncher" + PROD_NAME_AIHIT_LOADER: "parr/parr-aihit-loader" + PROD_NAME_AIHIT_SYNCER: "parr/parr-aihit-syncer" PROD_NAME_ESPP_TEMPLATE: "parr/parr-espp-template-sync" PROD_NAME_ESPP_SCHEDULE: "parr/parr-espp-schedule-sync" PROD_NAME_GENERATOR_TEMPLATES: "parr/parr-generator-templates" @@ -65,10 +66,59 @@ prod_deploy: - ${RUNNER} +### AIHIT LOADER PROD ### +prod_aihit_loader_build: + stage: build + only: + - /^al[0-9]+\.[0-9]+\.[0-9]+$/ + except: + - branches + services: + - name: docker:20.10.21-dind + command: [ + "--insecure-registry=10.99.253.167:8090", + "--registry-mirror=http://10.99.253.167:8090", + "--insecure-registry=10.99.253.167:8088", + "--registry-mirror=http://10.99.253.167:8088", + "--insecure-registry=harbor.dvgd.rzd", + "--tls=false" + ] + variables: + DOCKER_HOST: tcp://docker:2375 + DOCKER_DRIVER: overlay2 + DOCKER_TLS_CERTDIR: "" + script: + - APP_VERSION=$(echo $CI_COMMIT_TAG | tr -d al) + - IMAGE_VERSION=$(echo $CI_COMMIT_TAG | sed 's/al/v/g') + - docker build -t $REPO/$PROD_NAME_AIHIT_LOADER:$IMAGE_VERSION -t $REPO/$PROD_NAME_AIHIT_LOADER:latest -t $PROD_NAME_AIHIT_LOADER:$IMAGE_VERSION -t $PROD_NAME_AIHIT_LOADER:latest --build-arg app_version=$APP_VERSION -f PARR.AIHITLoaderWorker/Dockerfile . + - docker login -u $HARBOR_PUSH_USER -p $HARBOR_PUSH_PASS $REPO + - docker push --all-tags $REPO/$PROD_NAME_AIHIT_LOADER + tags: + - docker -### AIHIT PROD ### -prod_aihit_build: +prod_aihit_loader_deploy: + stage: deploy + environment: + name: parr-aihit-loader + only: + - /^al[0-9]+\.[0-9]+\.[0-9]+$/ + except: + - branches + script: + - IMAGE_VERSION=$(echo $CI_COMMIT_TAG | sed 's/al/v/g') + - docker login -u $HARBOR_PULL_USER -p $HARBOR_PULL_PASS $REPO + - tag=$IMAGE_VERSION docker compose -f docker-compose.aihit-loader.yml up -d + parallel: + matrix: + - RUNNER: shell-as-1 + tags: + - ${RUNNER} + + + +### AIHIT SYNCER PROD ### +prod_aihit_syncer_build: stage: build only: - /^as[0-9]+\.[0-9]+\.[0-9]+$/ @@ -91,17 +141,17 @@ prod_aihit_build: script: - APP_VERSION=$(echo $CI_COMMIT_TAG | tr -d as) - IMAGE_VERSION=$(echo $CI_COMMIT_TAG | sed 's/as/v/g') - - docker build -t $REPO/$PROD_NAME_AIHIT:$IMAGE_VERSION -t $REPO/$PROD_NAME_AIHIT:latest -t $PROD_NAME_AIHIT:$IMAGE_VERSION -t $PROD_NAME_AIHIT:latest --build-arg app_version=$APP_VERSION -f PARR.Worker/PARR.Worker/Dockerfile . + - docker build -t $REPO/$PROD_NAME_AIHIT_SYNCER:$IMAGE_VERSION -t $REPO/$PROD_NAME_AIHIT_SYNCER:latest -t $PROD_NAME_AIHIT_SYNCER:$IMAGE_VERSION -t $PROD_NAME_AIHIT_SYNCER:latest --build-arg app_version=$APP_VERSION -f PARR.AIHITSyncerWorker/Dockerfile . - docker login -u $HARBOR_PUSH_USER -p $HARBOR_PUSH_PASS $REPO - - docker push --all-tags $REPO/$PROD_NAME_AIHIT + - docker push --all-tags $REPO/$PROD_NAME_AIHIT_SYNCER tags: - - docker-as + - docker -prod_aihit_deploy: +prod_aihit_syncer_deploy: stage: deploy environment: - name: parr-aihit-syncher + name: parr-aihit-syncer only: - /^as[0-9]+\.[0-9]+\.[0-9]+$/ except: @@ -109,10 +159,10 @@ prod_aihit_deploy: script: - IMAGE_VERSION=$(echo $CI_COMMIT_TAG | sed 's/as/v/g') - docker login -u $HARBOR_PULL_USER -p $HARBOR_PULL_PASS $REPO - - tag=$IMAGE_VERSION docker compose -f docker-compose.aihit-syncher.yml up -d + - tag=$IMAGE_VERSION docker stack deploy -c docker-compose.aihit-syncer.yml parr-aihit-syncer --with-registry-auth parallel: matrix: - - RUNNER: shell-as-1 + - RUNNER: shell-api-swarm-01 tags: - ${RUNNER} diff --git a/PARR.AIHITLoader/AihitLoader.cs b/PARR.AIHITLoader/AihitLoader.cs new file mode 100644 index 00000000..a237579f --- /dev/null +++ b/PARR.AIHITLoader/AihitLoader.cs @@ -0,0 +1,120 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PARR.AIHITLoader.Models; +using PARR.AIHITLoader.Services; +using PARR.AIHITLoader.Settings; +using PARR.BLL.Domain.Mq; +using PARR.BLL.Services.Interfaces; +using System.Globalization; +using System.Text.Json; + +namespace PARR.AIHITLoader +{ + internal class AihitLoader : IAihitLoader + { + private readonly IIntervalService intervalService; + private readonly ILogger logger; + private readonly WorkerSettings workerSettings; + private readonly LoaderSettings loaderSettings; + private readonly IMqService mqService; + private readonly MqSettings mqSettings; + private readonly IServiceProvider serviceProvider; + + public AihitLoader( + IIntervalService intervalService, + ILogger logger, + WorkerSettings workerSettings, + LoaderSettings loaderSettings, + IMqService mqService, + MqSettings mqSettings, + IServiceProvider serviceProvider + ) + { + this.intervalService = intervalService; + this.logger = logger; + this.workerSettings = workerSettings; + this.loaderSettings = loaderSettings; + this.mqService = mqService; + this.mqSettings = mqSettings; + this.serviceProvider = serviceProvider; + } + + public async Task StartAsync() + { + logger.LogInformation("Запуск сервиса загрузки данных из АИХИТ."); + + await intervalService.IntervalInitAsync(UploadDataAsync, workerSettings.RepeatEvery); + } + + private async Task UploadDataAsync() + { + foreach (var respArea in loaderSettings.ResponseAreas) + { + var aihitData = GetDataFromAihit(respArea); + + if (aihitData == null) return; + + var filteredAIhitData = FilterData(aihitData); + + var preparedData = filteredAIhitData.Select(d => JsonSerializer.Serialize(MapAihitToDomain(d))); + + var parts = preparedData.Count() / loaderSettings.PackageSize; + parts++; + + for (var i = 0; i < parts; i++) + { + var batch = preparedData.Skip(i * loaderSettings.PackageSize).Take(loaderSettings.PackageSize).ToList(); + var sendResult = mqService.Send(mqSettings, batch.ToArray()); + if (sendResult.IsSuccess) + { + logger.LogInformation($"Данные переданы в RabbitMQ: {batch.Count()}"); + logger.LogDebug($"Список ЭК переданных в RabbitMQ: {string.Join(", ", batch)}"); + } + else + logger.LogError($"Ошибка при передаче нарядов в Rabbit, не переданные сообщения: {string.Join(", ", sendResult.NotSendMessages)}"); + } + + } + await Task.CompletedTask; + } + + /// + /// Фильтруем входные данные из АИХ ИТ по необходимости. На первом этапе берём только эк по максе ВРТ-* + /// + /// + /// + private List FilterData(List aihitData) + { + var vrt = aihitData.Where(ek => ek.EKFindCode!.StartsWith("ВРТ", true, CultureInfo.GetCultureInfo("ru-RU"))).ToList(); + return vrt.Where(vrt => vrt.IP != null).ToList(); + } + + private List? GetDataFromAihit(string respArea) + { + using (var scope = serviceProvider.CreateScope()) + { + var aihitService = scope.ServiceProvider.GetService(); + if (aihitService == null) + throw new Exception("Не смог получить серивс IAIHITService, scope.ServiceProvider.GetService()"); + + return aihitService.GetEKList(respArea); + } + } + + + private AihitDataMq MapAihitToDomain(EK ek) + { + return new AihitDataMq + { + EK = ek.EKFindCode!, + IP = ek.IP, + APPType = ek.APPType, + DBType = ek.DBType, + OSType = ek.OSType, + WorkGroup = ek.WorkGroup, + Status = (ek.Status == "00-ГВЦ")? "99-ГВЦ": ek.Status!, + ResponseArea = ek.ResponseArea! + }; + } + } +} diff --git a/PARR.AIHITLoader/AihitLoaderInstaller.cs b/PARR.AIHITLoader/AihitLoaderInstaller.cs new file mode 100644 index 00000000..01528531 --- /dev/null +++ b/PARR.AIHITLoader/AihitLoaderInstaller.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using PARR.AIHITLoader.Context; +using PARR.AIHITLoader.Services; +using PARR.AIHITLoader.Settings; +using PARR.BLL; + +namespace PARR.AIHITLoader +{ + public static class AihitLoaderInstaller + { + public static void InstallAihitLoaderServices(this IServiceCollection services, IConfiguration configuration) + { + services.InstallBllServices(configuration); + + var settings = new WorkerSettings(); + configuration.GetSection(nameof(WorkerSettings)).Bind(settings); + services.AddSingleton(settings); + + var loaderSettings = new LoaderSettings(); + configuration.GetSection(nameof(LoaderSettings)).Bind(loaderSettings); + services.AddSingleton(loaderSettings); + + var mqSettings = new MqSettings(); + configuration.GetSection(nameof(MqSettings)).Bind(mqSettings); + services.AddSingleton(mqSettings); + + services.AddDbContext(options => + options.UseSqlServer( + configuration.GetConnectionString("AihitConnection") + , sqlServerOptions => sqlServerOptions.CommandTimeout(1800) + )); + + services.AddTransient(); + services.AddTransient(); + + //todo: config e.t.c + } + } +} diff --git a/PARR.AIHITLoader/Context/AIHITContext.cs b/PARR.AIHITLoader/Context/AIHITContext.cs new file mode 100644 index 00000000..23392fb1 --- /dev/null +++ b/PARR.AIHITLoader/Context/AIHITContext.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; +using PARR.AIHITLoader.Models; + +namespace PARR.AIHITLoader.Context +{ + internal class AIHITContext : DbContext + { + public AIHITContext(DbContextOptions options) : base(options) { } + + + + public DbSet EKs { get; set; } + } +} diff --git a/PARR.AIHITLoader/IAihitLoader.cs b/PARR.AIHITLoader/IAihitLoader.cs new file mode 100644 index 00000000..5424710a --- /dev/null +++ b/PARR.AIHITLoader/IAihitLoader.cs @@ -0,0 +1,7 @@ +namespace PARR.AIHITLoader +{ + public interface IAihitLoader + { + Task StartAsync(); + } +} diff --git a/PARR.AIHITLoader/Models/EK.cs b/PARR.AIHITLoader/Models/EK.cs new file mode 100644 index 00000000..e97044a3 --- /dev/null +++ b/PARR.AIHITLoader/Models/EK.cs @@ -0,0 +1,100 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace PARR.AIHITLoader.Models +{ + public class EK + { + [Column("IP_АДРЕС")] + public string? IP { get; set; } + [Column("МЕТКА")] + public string? Metka { get; set; } + [Column("АКТИВЕН")] + public string? IsActive { get; set; } + [Column("ВАЖНЫЙ_ЭК")] + public string? IsImportant { get; set; } + [Column("ВРЕМЯ_СОЗДАНИЯ")] + public DateTime? CreateTime { get; set; } + [Column("ДОПОЛНИТЕЛЬНАЯ_ИНФОРМАЦИЯ")] + public string? AdditionalInfo { get; set; } + [Column("ЗОНА_ОТВЕТСТВЕННОСТИ")] + public string? ResponseArea { get; set; } + [Column("КАТЕГОРИЯ_ЭК")] + public string? EKCategory { get; set; } + [Column("КОД_ПОИСКА_ЭК")] + public string? EKFindCode { get; set; } + [Column("КОД_ПРОДУКТА")] + public string? ProductCode { get; set; } + [Column("КОД_УСЛУГИ")] + public string? ServiceCode { get; set; } + [Column("КРАТКОЕ_НАИМЕНОВАНИЕ")] + public string? ShortName { get; set; } + [Column("ОТВЕТСТВЕННЫЙ_ЗА_ЭК")] + public string? ResponsibleByEK { get; set; } + [Column("ПЛАНОВОЕ_ВРЕМЯ_ВОССТАНОВЛЕНИЯ")] + public DateTime? PlannedTimeToRepair { get; set; } + [Column("ПОДКАТЕГОРИЯ_ЭК")] + public string? EKSubCategory { get; set; } + [Column("ПОЛНОЕ_НАИМЕНОВАНИЕ")] + public string? FullName { get; set; } + [Column("ПРЕДПИСАНИЕ")] + public string? Prescription { get; set; } + [Column("ПРЕДПРИЯТИЕ")] + public string? Company { get; set; } + [Column("РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК")] + public string? WorkGroup { get; set; } + [Column("РАСПОЛОЖЕНИЕ")] + public string? Location { get; set; } + [Column("РЕВИЗОР_ЭК")] + public string? EKRevizor { get; set; } + [Column("РЕГИСТРАТОР_ЭК")] + public string? EKRegister { get; set; } + [Column("СЕТЕВОЕ_ИМЯ")] + public string? NetworkName { get; set; } + [Column("СТАТУС")] + public string? Status { get; set; } + [Column("ТИП_ЭК")] + public string? EKType { get; set; } + [Column("ФАКТИЧЕСКОЕ_ЗАВЕРШЕНИЕ_ЭКСПЛУАТАЦИИ")] + public DateTime? EndExplotationDate { get; set; } + [Column("ФАКТИЧЕСКОЕ_НАЧАЛО_ЭКСПЛУАТАЦИИ")] + public DateTime? StartExplotationDate { get; set; } + [Column("ЦЕЛЕВОЕ_ВРЕМЯ_ВОССТАНОВЛЕНИЯ")] + public string? TargetRepairTime { get; set; } + [Column("SYSMODTIME")] + public DateTime? SysModTime { get; set; } + [Column("SYSMODUSER")] + public string? SysModUser { get; set; } + [Column("НОВЫЙ_КОД_ПОИСКА")] + public string? NewEKFindCode { get; set; } + [Column("СТАРЫЙ_КОД_ПОИСКА")] + public string? OldEKFindCode { get; set; } + [Column("НАПРАВЛЕНИЕ_ЦТС_ЦК")] + public string? CTSDirection { get; set; } + [Key] + [Column("ID")] + public int? AIHID { get; set; } + [Column("недостоверные_данные")] + public char? IsUnreliableData { get; set; } + [Column("РГ_смены")] + public string? ShiftWorkGroup { get; set; } + [Column("Клиентское_ПО")] + public string? ClientSoftware { get; set; } + [Column("Клиентская_ОС")] + public string? ClientOS { get; set; } + [Column("Тип СУБД")] + public string? DBType { get; set; } + [Column("Тип сервера приложений")] + public string? APPType { get; set; } + [Column("Тип сервера ЦК БС")] + public string? CKBSServerType { get; set; } + [Column("Тип сервера ИБ")] + public string? IBServerType { get; set; } + [Column("Тип сервера инфраструктуры")] + public string? InfrastructureServerType { get; set; } + [Column("Тип сервера мониторинга")] + public string? MonitoringServerType { get; set; } + [Column("ОС")] + public string? OSType { get; set; } + } +} diff --git a/PARR.AIHITLoader/PARR.AIHITLoader.csproj b/PARR.AIHITLoader/PARR.AIHITLoader.csproj new file mode 100644 index 00000000..7fb31cbc --- /dev/null +++ b/PARR.AIHITLoader/PARR.AIHITLoader.csproj @@ -0,0 +1,17 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + diff --git a/PARR.AIHITLoader/Services/AIHITService.cs b/PARR.AIHITLoader/Services/AIHITService.cs new file mode 100644 index 00000000..0e4f92dc --- /dev/null +++ b/PARR.AIHITLoader/Services/AIHITService.cs @@ -0,0 +1,42 @@ +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using PARR.AIHITLoader.Context; +using PARR.AIHITLoader.Models; + +namespace PARR.AIHITLoader.Services +{ + internal class AIHITService : IAIHITService + { + private readonly AIHITContext context; + private readonly ILogger logger; + + public AIHITService(AIHITContext context, ILogger logger) + { + this.context = context; + this.logger = logger; + } + public List? GetEKList(string respArea) + { + var parameters = new List() { new SqlParameter("@зо", respArea) }; + + try + { + var result = context.Set().FromSqlRaw($"EXEC mao2.dbo.sp_IPP_PARR_PTK_get_EK @зо", parameters.ToArray()).ToList(); + + if (result == null || !result.Any()) + { + logger.LogWarning($"Процедура sp_IPP_PARR_PTK_get_EK вернула пустой список ЭК"); + } + + return result; + } + catch (Exception ex) + { + logger.LogError(ex, $"Ошибка при выполнении ХП sp_IPP_PARR_PTK_get_EK({respArea})"); + } + + return null; + } + } +} diff --git a/PARR.AIHITLoader/Services/IAIHITService.cs b/PARR.AIHITLoader/Services/IAIHITService.cs new file mode 100644 index 00000000..398c8486 --- /dev/null +++ b/PARR.AIHITLoader/Services/IAIHITService.cs @@ -0,0 +1,9 @@ +using PARR.AIHITLoader.Models; + +namespace PARR.AIHITLoader.Services +{ + internal interface IAIHITService + { + List? GetEKList(string respArea); + } +} diff --git a/PARR.AIHITLoader/Settings/LoaderSettings.cs b/PARR.AIHITLoader/Settings/LoaderSettings.cs new file mode 100644 index 00000000..ea78c1bf --- /dev/null +++ b/PARR.AIHITLoader/Settings/LoaderSettings.cs @@ -0,0 +1,8 @@ +namespace PARR.AIHITLoader.Settings +{ + internal class LoaderSettings + { + public List ResponseAreas { get; set; } = new List(); + public int PackageSize { get; set; } = 100; + } +} diff --git a/PARR.AIHITLoader/Settings/MqSettings.cs b/PARR.AIHITLoader/Settings/MqSettings.cs new file mode 100644 index 00000000..6f4682b4 --- /dev/null +++ b/PARR.AIHITLoader/Settings/MqSettings.cs @@ -0,0 +1,12 @@ +using PARR.BLL.Contracts.Interfaces; + +namespace PARR.AIHITLoader.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; + } +} diff --git a/PARR.AIHITLoader/Settings/WorkerSettings.cs b/PARR.AIHITLoader/Settings/WorkerSettings.cs new file mode 100644 index 00000000..462e9c89 --- /dev/null +++ b/PARR.AIHITLoader/Settings/WorkerSettings.cs @@ -0,0 +1,7 @@ +namespace PARR.AIHITLoader.Settings +{ + internal class WorkerSettings + { + public TimeSpan RepeatEvery { get; set; } + } +} diff --git a/PARR.AIHITLoaderWorker/Dockerfile b/PARR.AIHITLoaderWorker/Dockerfile new file mode 100644 index 00000000..adbd53da --- /dev/null +++ b/PARR.AIHITLoaderWorker/Dockerfile @@ -0,0 +1,26 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM 10.99.253.167:8090/dotnet/runtime:7.0 AS base +WORKDIR /app + +FROM 10.99.253.167:8090/dotnet/sdk:7.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["NuGet.config", "."] +COPY ["PARR.AIHITLoaderWorker/PARR.AIHITLoaderWorker.csproj", "PARR.AIHITLoaderWorker/"] +COPY ["PARR.AIHITLoader/PARR.AIHITLoader.csproj", "PARR.AIHITLoader/"] +COPY ["PARR.BLL/PARR.BLL.csproj", "PARR.BLL/"] +RUN dotnet restore "./PARR.AIHITLoaderWorker/./PARR.AIHITLoaderWorker.csproj" +COPY . . +WORKDIR "/src/PARR.AIHITLoaderWorker" +RUN dotnet build "./PARR.AIHITLoaderWorker.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +ARG app_version=0.0.0-default +RUN dotnet publish "./PARR.AIHITLoaderWorker.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false /p:Version=$app_version + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "PARR.AIHITLoaderWorker.dll"] \ No newline at end of file diff --git a/PARR.AIHITLoaderWorker/PARR.AIHITLoaderWorker.csproj b/PARR.AIHITLoaderWorker/PARR.AIHITLoaderWorker.csproj new file mode 100644 index 00000000..967301d3 --- /dev/null +++ b/PARR.AIHITLoaderWorker/PARR.AIHITLoaderWorker.csproj @@ -0,0 +1,24 @@ + + + + net7.0 + enable + enable + dotnet-PARR.AIHITLoaderWorker-a6f3114e-7da7-43fd-888c-f45df76de2bd + Linux + + + + + + + + + + + + + + + + diff --git a/PARR.AIHITLoaderWorker/Program.cs b/PARR.AIHITLoaderWorker/Program.cs new file mode 100644 index 00000000..eeec0d37 --- /dev/null +++ b/PARR.AIHITLoaderWorker/Program.cs @@ -0,0 +1,30 @@ +using PARR.AIHITLoader; +using Serilog; +using PARR.AIHITLoaderWorker; +using Elastic.CommonSchema.Serilog; + + +var builder = Host.CreateApplicationBuilder(); + +builder.Services.AddLogging(config => +{ + config.ClearProviders(); + + var logger = new LoggerConfiguration(); + + if (builder.Environment.IsProduction()) + logger.WriteTo.Console(new EcsTextFormatter()); + else + logger.WriteTo.Console(); + + logger.ReadFrom.Configuration(builder.Configuration); + + config.AddSerilog(logger.CreateLogger()); +}); + +builder.Services.InstallAihitLoaderServices(builder.Configuration); + +builder.Services.AddHostedService(); + +var host = builder.Build(); +host.Run(); \ No newline at end of file diff --git a/PARR.AIHITLoaderWorker/Properties/launchSettings.json b/PARR.AIHITLoaderWorker/Properties/launchSettings.json new file mode 100644 index 00000000..9456d7f3 --- /dev/null +++ b/PARR.AIHITLoaderWorker/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "profiles": { + "PARR.AIHITLoaderWorker": { + "commandName": "Project", + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true + }, + "Docker": { + "commandName": "Docker" + } + } +} \ No newline at end of file diff --git a/PARR.AIHITLoaderWorker/Worker.cs b/PARR.AIHITLoaderWorker/Worker.cs new file mode 100644 index 00000000..71cfc89f --- /dev/null +++ b/PARR.AIHITLoaderWorker/Worker.cs @@ -0,0 +1,21 @@ +using PARR.AIHITLoader; + +namespace PARR.AIHITLoaderWorker +{ + public class Worker : BackgroundService + { + private readonly ILogger _logger; + private readonly IAihitLoader aihitLoader; + + public Worker(ILogger logger, IAihitLoader aihitLoader) + { + _logger = logger; + this.aihitLoader = aihitLoader; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await aihitLoader.StartAsync(); + } + } +} diff --git a/PARR.AIHITLoaderWorker/appsettings.Development.json b/PARR.AIHITLoaderWorker/appsettings.Development.json new file mode 100644 index 00000000..b2dcdb67 --- /dev/null +++ b/PARR.AIHITLoaderWorker/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/PARR.AIHITLoaderWorker/appsettings.json b/PARR.AIHITLoaderWorker/appsettings.json new file mode 100644 index 00000000..b978397a --- /dev/null +++ b/PARR.AIHITLoaderWorker/appsettings.json @@ -0,0 +1,43 @@ +{ + "ConnectionStrings": { + "AihitConnection": "Data Source=10.248.19.97; Initial Catalog=mao2;User ID=awhit-ipp-parr;pwd=ET3h$9y1LH#D;TrustServerCertificate=true;" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "WorkerSettings": { + "RepeatEvery": "12:00:00" + }, + "MqSettings": { + "HostName": "10.99.253.216", + "QueueName": "parr-aihit-data", + "User": "aihit_loader", + "Password": "123fsdf&78sd9534dsfD" + }, + "LoaderSettings": { + "ResponseAreas": [ + "96-ДВС", + "10-КЛГ", + "58-ЮВСТ", + "01-ОКТ", + "61-ПРИВ", + "17-МСК", + "28-СЕВ", + "92-ВСИБ", + "00-ГВЦ", + "88-КРАСН", + "24-ГОР", + "80-ЮУР", + "51-СКВ", + "76-СВРД", + "63-КБШ", + "83-ЗСИБ", + "94-ЗАБ" + ], + "PackageSize": 100 + } + +} diff --git a/PARR.AIHITSyncer/AihitSyncer.cs b/PARR.AIHITSyncer/AihitSyncer.cs new file mode 100644 index 00000000..1e914914 --- /dev/null +++ b/PARR.AIHITSyncer/AihitSyncer.cs @@ -0,0 +1,336 @@ +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; + } + } +} diff --git a/PARR.AIHITSyncer/AihitSyncerInstaller.cs b/PARR.AIHITSyncer/AihitSyncerInstaller.cs new file mode 100644 index 00000000..55d2fc24 --- /dev/null +++ b/PARR.AIHITSyncer/AihitSyncerInstaller.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using PARR.AIHITSyncer.Settings; +using PARR.BLL; +using PARR.DAL; + +namespace PARR.AIHITSyncer +{ + public static class AihitSyncerInstaller + { + public static void InstallAihitSyncerServices(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(); + } + } +} diff --git a/PARR.AIHITSyncer/IAihitSyncer.cs b/PARR.AIHITSyncer/IAihitSyncer.cs new file mode 100644 index 00000000..340a8178 --- /dev/null +++ b/PARR.AIHITSyncer/IAihitSyncer.cs @@ -0,0 +1,8 @@ +namespace PARR.AIHITSyncer +{ + public interface IAihitSyncer + { + void Start(); + void Stop(); + } +} diff --git a/PARR.AIHITSyncer/PARR.AIHITSyncer.csproj b/PARR.AIHITSyncer/PARR.AIHITSyncer.csproj new file mode 100644 index 00000000..21190861 --- /dev/null +++ b/PARR.AIHITSyncer/PARR.AIHITSyncer.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + + + + + + + + diff --git a/PARR.AIHITSyncer/Settings/MqSettings.cs b/PARR.AIHITSyncer/Settings/MqSettings.cs new file mode 100644 index 00000000..f0c697cb --- /dev/null +++ b/PARR.AIHITSyncer/Settings/MqSettings.cs @@ -0,0 +1,12 @@ +using PARR.BLL.Contracts.Interfaces; + +namespace PARR.AIHITSyncer.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; + } +} diff --git a/PARR.AIHITSyncer/Settings/WorkerSettings.cs b/PARR.AIHITSyncer/Settings/WorkerSettings.cs new file mode 100644 index 00000000..6258d43a --- /dev/null +++ b/PARR.AIHITSyncer/Settings/WorkerSettings.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PARR.AIHITSyncer.Settings +{ + internal class WorkerSettings + { + public TimeSpan RepeatEvery { get; set; } + } +} diff --git a/PARR.AIHITSyncerWorker/Dockerfile b/PARR.AIHITSyncerWorker/Dockerfile new file mode 100644 index 00000000..e1ef39f2 --- /dev/null +++ b/PARR.AIHITSyncerWorker/Dockerfile @@ -0,0 +1,28 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM 10.99.253.167:8090/dotnet/runtime:7.0 AS base +WORKDIR /app + +FROM 10.99.253.167:8090/dotnet/sdk:7.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["NuGet.config", "."] +COPY ["PARR.AIHITSyncerWorker/PARR.AIHITSyncerWorker.csproj", "PARR.AIHITSyncerWorker/"] +COPY ["PARR.AIHITSyncer/PARR.AIHITSyncer.csproj", "PARR.AIHITSyncer/"] +COPY ["PARR.BLL/PARR.BLL.csproj", "PARR.BLL/"] +COPY ["PARR.DAL/PARR.DAL.csproj", "PARR.DAL/"] +COPY ["PARR.Constants/PARR.Constants.csproj", "PARR.Constants/"] +RUN dotnet restore "./PARR.AIHITSyncerWorker/./PARR.AIHITSyncerWorker.csproj" +COPY . . +WORKDIR "/src/PARR.AIHITSyncerWorker" +RUN dotnet build "./PARR.AIHITSyncerWorker.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +ARG app_version=0.0.0-default +RUN dotnet publish "./PARR.AIHITSyncerWorker.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false /p:Version=$app_version + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "PARR.AIHITSyncerWorker.dll"] \ No newline at end of file diff --git a/PARR.AIHITSyncerWorker/PARR.AIHITSyncerWorker.csproj b/PARR.AIHITSyncerWorker/PARR.AIHITSyncerWorker.csproj new file mode 100644 index 00000000..a89393f1 --- /dev/null +++ b/PARR.AIHITSyncerWorker/PARR.AIHITSyncerWorker.csproj @@ -0,0 +1,24 @@ + + + + net7.0 + enable + enable + dotnet-PARR.AIHITSyncerWorker-cd7db85e-5317-46a2-aa90-44eb3e56f076 + Linux + + + + + + + + + + + + + + + + diff --git a/PARR.AIHITSyncerWorker/Program.cs b/PARR.AIHITSyncerWorker/Program.cs new file mode 100644 index 00000000..e29f03ca --- /dev/null +++ b/PARR.AIHITSyncerWorker/Program.cs @@ -0,0 +1,30 @@ +using PARR.AIHITSyncerWorker; +using Elastic.CommonSchema.Serilog; +using Serilog; +using PARR.AIHITSyncer; + + +var builder = Host.CreateApplicationBuilder(); + +builder.Services.AddLogging(config => +{ + config.ClearProviders(); + + var logger = new LoggerConfiguration(); + + if (builder.Environment.IsProduction()) + logger.WriteTo.Console(new EcsTextFormatter()); + else + logger.WriteTo.Console(); + + logger.ReadFrom.Configuration(builder.Configuration); + + config.AddSerilog(logger.CreateLogger()); +}); + +builder.Services.InstallAihitSyncerServices(builder.Configuration); + +builder.Services.AddHostedService(); + +var host = builder.Build(); +host.Run(); \ No newline at end of file diff --git a/PARR.AIHITSyncerWorker/Properties/launchSettings.json b/PARR.AIHITSyncerWorker/Properties/launchSettings.json new file mode 100644 index 00000000..ef206ea7 --- /dev/null +++ b/PARR.AIHITSyncerWorker/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "profiles": { + "PARR.AIHITSyncerWorker": { + "commandName": "Project", + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true + }, + "Docker": { + "commandName": "Docker" + } + } +} \ No newline at end of file diff --git a/PARR.AIHITSyncerWorker/Worker.cs b/PARR.AIHITSyncerWorker/Worker.cs new file mode 100644 index 00000000..69da8a94 --- /dev/null +++ b/PARR.AIHITSyncerWorker/Worker.cs @@ -0,0 +1,28 @@ +using PARR.AIHITSyncer; + +namespace PARR.AIHITSyncerWorker +{ + public class Worker : BackgroundService + { + private readonly ILogger _logger; + private readonly IAihitSyncer aihitSyncer; + + public Worker(ILogger logger, IAihitSyncer aihitSyncer) + { + _logger = logger; + this.aihitSyncer = aihitSyncer; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + aihitSyncer.Start(); + } + + public override Task StopAsync(CancellationToken cancellationToken) + { + aihitSyncer.Stop(); + + return base.StopAsync(cancellationToken); + } + } +} diff --git a/PARR.AIHITSyncerWorker/appsettings.Development.json b/PARR.AIHITSyncerWorker/appsettings.Development.json new file mode 100644 index 00000000..b2dcdb67 --- /dev/null +++ b/PARR.AIHITSyncerWorker/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/PARR.AIHITSyncerWorker/appsettings.json b/PARR.AIHITSyncerWorker/appsettings.json new file mode 100644 index 00000000..d2adccc6 --- /dev/null +++ b/PARR.AIHITSyncerWorker/appsettings.json @@ -0,0 +1,38 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Server=10.99.253.184;Database=parr;User Id=app_parr; Password=PosdfkhT&)%sdfligL&%5546;" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Debug" + } + }, + "WriteTo": [ + { + "Name": "File", + "Args": { + "path": "log/log-.txt", + "rollingInterval": "Day" + } + } + ] + }, + "WorkerSettings": { + "RepeatEvery": "00:10:00" + }, + "MqSettings": { + "HostName": "10.99.253.216", + "QueueName": "parr-aihit-data", + "User": "aihit_syncer", + "Password": "afsdgDGjhfiU76496as42!@" + } +} diff --git a/PARR.API.sln b/PARR.API.sln index 1fed32c2..8af7aca0 100644 --- a/PARR.API.sln +++ b/PARR.API.sln @@ -60,6 +60,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PARR.EsppScheduleSync", "PA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PARR.EsppScheduleSyncWorker", "PARR.EsppScheduleSyncWorker\PARR.EsppScheduleSyncWorker.csproj", "{C597A1D7-1BC5-493B-BC29-03EC830FB090}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.AIHITLoader", "PARR.AIHITLoader\PARR.AIHITLoader.csproj", "{E7F3C8FB-445B-4711-8F89-0B7DA7DB4537}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.AIHITLoaderWorker", "PARR.AIHITLoaderWorker\PARR.AIHITLoaderWorker.csproj", "{E9434123-E697-42E2-B7C0-FE08766B98D1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.AIHITSyncer", "PARR.AIHITSyncer\PARR.AIHITSyncer.csproj", "{4177B13B-585C-44C7-B3C1-6B6E46D6BABC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.AIHITSyncerWorker", "PARR.AIHITSyncerWorker\PARR.AIHITSyncerWorker.csproj", "{4FA79176-F833-4879-8437-37EA053BAA94}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -160,6 +168,22 @@ Global {C597A1D7-1BC5-493B-BC29-03EC830FB090}.Debug|Any CPU.Build.0 = Debug|Any CPU {C597A1D7-1BC5-493B-BC29-03EC830FB090}.Release|Any CPU.ActiveCfg = Release|Any CPU {C597A1D7-1BC5-493B-BC29-03EC830FB090}.Release|Any CPU.Build.0 = Release|Any CPU + {E7F3C8FB-445B-4711-8F89-0B7DA7DB4537}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7F3C8FB-445B-4711-8F89-0B7DA7DB4537}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7F3C8FB-445B-4711-8F89-0B7DA7DB4537}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7F3C8FB-445B-4711-8F89-0B7DA7DB4537}.Release|Any CPU.Build.0 = Release|Any CPU + {E9434123-E697-42E2-B7C0-FE08766B98D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9434123-E697-42E2-B7C0-FE08766B98D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9434123-E697-42E2-B7C0-FE08766B98D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9434123-E697-42E2-B7C0-FE08766B98D1}.Release|Any CPU.Build.0 = Release|Any CPU + {4177B13B-585C-44C7-B3C1-6B6E46D6BABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4177B13B-585C-44C7-B3C1-6B6E46D6BABC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4177B13B-585C-44C7-B3C1-6B6E46D6BABC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4177B13B-585C-44C7-B3C1-6B6E46D6BABC}.Release|Any CPU.Build.0 = Release|Any CPU + {4FA79176-F833-4879-8437-37EA053BAA94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FA79176-F833-4879-8437-37EA053BAA94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FA79176-F833-4879-8437-37EA053BAA94}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FA79176-F833-4879-8437-37EA053BAA94}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PARR.BLL/Domain/Mq/AihitDataMq.cs b/PARR.BLL/Domain/Mq/AihitDataMq.cs new file mode 100644 index 00000000..b8db206e --- /dev/null +++ b/PARR.BLL/Domain/Mq/AihitDataMq.cs @@ -0,0 +1,17 @@ +namespace PARR.BLL.Domain.Mq +{ + /// + /// Модель в MQ, данные из АИХИТ + /// + public class AihitDataMq + { + public required string EK { get; set; } + public string? IP { get; set; } + public string? DBType { get; set; } + public string? APPType { get; set; } + public string? OSType { get; set; } + public string? WorkGroup { get; set; } + public required string Status { get; set; } + public required string ResponseArea { get; set; } + } +} diff --git a/PARR.DAL/Models/Host.cs b/PARR.DAL/Models/Host.cs index e28639b8..75445a2b 100644 --- a/PARR.DAL/Models/Host.cs +++ b/PARR.DAL/Models/Host.cs @@ -20,7 +20,7 @@ namespace PARR.DAL.Models public int EkStatusCode { get; set; } - public required string IP { get; set; } + public string? IP { get; set; } //public string? RegionalEK { get; set; } //public string? LinkEK { get; set; } diff --git a/PARR.EsppOrderManagerWorker/Program.cs b/PARR.EsppOrderManagerWorker/Program.cs index cd42bd06..271e8644 100644 --- a/PARR.EsppOrderManagerWorker/Program.cs +++ b/PARR.EsppOrderManagerWorker/Program.cs @@ -1,5 +1,4 @@ using Elastic.CommonSchema.Serilog; -using PARR.EsppApi; using PARR.EsppOrderManager; using PARR.EsppOrderManagerWorker; using Serilog; diff --git a/docker-compose.aihit-loader.yml b/docker-compose.aihit-loader.yml new file mode 100644 index 00000000..4b8eba68 --- /dev/null +++ b/docker-compose.aihit-loader.yml @@ -0,0 +1,20 @@ +version: '3.4' + +#AIHIT LOADER +services: + aihit-loader: + image: harbor.dvgd.rzd/parr/parr-aihit-loader:${tag:-latest} + container_name: parr-aihit-loader + restart: unless-stopped + environment: + - ASPNETCORE_ENVIRONMENT=Production + - TZ=Europe/Moscow + volumes: + - /var/log/parr-aihit-loader:/app/log + logging: + driver: fluentd + options: + fluentd-address: dvgd-efk-01.dvgd.oao.rzd:24224 + fluentd-retry-wait: '30s' + fluentd-max-retries: '30' + tag: parr.aihit-loader.serilog diff --git a/docker-compose.aihit-syncer.yml b/docker-compose.aihit-syncer.yml new file mode 100644 index 00000000..8837b9a4 --- /dev/null +++ b/docker-compose.aihit-syncer.yml @@ -0,0 +1,30 @@ +version: '3.4' + +#AIHIT SYNCHER +services: + aihit-syncer: + image: harbor.dvgd.rzd/parr/parr-aihit-syncer:${tag:-latest} + container_name: parr-aihit-syncer + restart: unless-stopped + environment: + - ASPNETCORE_ENVIRONMENT=Production + - TZ=Europe/Moscow + #volumes: + #- /var/log/parr-aihit-syncher:/app/log + logging: + driver: fluentd + options: + fluentd-address: dvgd-efk-01.dvgd.oao.rzd:24224 + fluentd-retry-wait: '30s' + fluentd-max-retries: '30' + tag: parr.aihit-syncer.serilog + deploy: + replicas: 4 + networks: + - parr-network + + +networks: + parr-network: + driver: overlay + external: true \ No newline at end of file diff --git a/docker-compose.aihit-syncher.yml b/docker-compose.aihit-syncher.yml deleted file mode 100644 index 66e38d6c..00000000 --- a/docker-compose.aihit-syncher.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: '3.4' - -#AIHIT SYNCHER -services: - aihit-syncher: - image: harbor.dvgd.rzd/parr/parr-aihit-syncher:${tag:-latest} - container_name: parr-aihit-syncher - restart: unless-stopped - environment: - - ASPNETCORE_ENVIRONMENT=Production - - TZ=Europe/Moscow - volumes: - - /var/log/parr-aihit-syncher:/app/log - logging: - driver: fluentd - options: - fluentd-address: dvgd-efk-01.dvgd.oao.rzd:24224 - fluentd-retry-wait: '30s' - fluentd-max-retries: '30' - tag: parr.aihit-syncher.serilog - - -# последняя версия -#docker compose -f docker-compose.aihit-syncher.yml up -d -# указанная версия -#tag=v1.0.0 docker compose -f docker-compose.aihit-syncher.yml up -d - diff --git a/docker-compose.dcproj b/docker-compose.dcproj index 1c902ded..777b1310 100644 --- a/docker-compose.dcproj +++ b/docker-compose.dcproj @@ -9,7 +9,8 @@ parr.api - + +