From 6e4035cde126f272249fa9726378fd3675621e23 Mon Sep 17 00:00:00 2001 From: Mikhail Trubnikov Date: Wed, 15 Nov 2023 16:34:28 +1000 Subject: [PATCH] =?UTF-8?q?feat(master):=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BF=D1=80=D0=B8=D1=81=D0=B2=D0=BE=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B8=D1=81=D1=82=D0=BE=D1=80=D0=B8=D0=B8=20=D0=B0?= =?UTF-8?q?=D0=B3=D0=B5=D0=BD=D1=82=D0=B0=20=D0=BD=D0=B0=D1=80=D1=8F=D0=B4?= =?UTF-8?q?=D0=B0=D0=BC.=20=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=81=D1=82=D0=B0=D1=82=D1=83=D1=81=D0=B0=20?= =?UTF-8?q?=D0=BD=D0=B0=D1=80=D1=8F=D0=B4=D0=B0.=20=D0=9E=D1=82=D0=BF?= =?UTF-8?q?=D1=80=D0=B0=D0=B2=D0=BA=D0=B0=20=D0=B2=20MQ.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PARR.BLL/Domain/Mq/OrderManageMq.cs | 10 ++ PARR.Master/Master.cs | 26 ++- PARR.Master/PARR.Master.csproj | 4 - PARR.Master/ParrMasterInstaller.cs | 6 + PARR.Master/Services/IOrderMasterService.cs | 7 + PARR.Master/Services/OrderMasterService.cs | 179 ++++++++++++++++++++ PARR.Master/Settings/OrderManageSettings.cs | 10 ++ PARR.MasterWorker/appsettings.json | 3 + 8 files changed, 237 insertions(+), 8 deletions(-) create mode 100644 PARR.BLL/Domain/Mq/OrderManageMq.cs create mode 100644 PARR.Master/Services/IOrderMasterService.cs create mode 100644 PARR.Master/Services/OrderMasterService.cs create mode 100644 PARR.Master/Settings/OrderManageSettings.cs diff --git a/PARR.BLL/Domain/Mq/OrderManageMq.cs b/PARR.BLL/Domain/Mq/OrderManageMq.cs new file mode 100644 index 00000000..06c5e731 --- /dev/null +++ b/PARR.BLL/Domain/Mq/OrderManageMq.cs @@ -0,0 +1,10 @@ +namespace PARR.BLL.Domain.Mq +{ + /// + /// Модель в MQ, запрос на изменение Наряда в ЕСПП + /// + public class OrderManageMq + { + public Guid OrderId { get; set; } + } +} diff --git a/PARR.Master/Master.cs b/PARR.Master/Master.cs index dd1b3484..cc15614f 100644 --- a/PARR.Master/Master.cs +++ b/PARR.Master/Master.cs @@ -1,4 +1,6 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PARR.Master.Services; using PARR.Master.Settings; namespace PARR.Master @@ -7,14 +9,17 @@ namespace PARR.Master { private readonly WorkerSettings settings; private readonly ILogger logger; + private readonly IServiceProvider serviceProvider; public Master( WorkerSettings settings, - ILogger logger + ILogger logger, + IServiceProvider serviceProvider ) { this.settings = settings; this.logger = logger; + this.serviceProvider = serviceProvider; } @@ -30,8 +35,7 @@ namespace PARR.Master { logger.LogInformation("Запуск по расписанию."); - //todo: - // await Tratata(); + await ManageOrdersAsync(); nextStart = DateTime.Now.Add(settings.RepeatEvery); logger.LogInformation($"Следующая дата запуска: {nextStart}"); @@ -41,5 +45,19 @@ namespace PARR.Master await Task.Delay(60 * 1000); } } + + + private async Task ManageOrdersAsync() + { + using (var scope = serviceProvider.CreateScope()) + { + var orderMasterService = scope.ServiceProvider.GetService(); + if (orderMasterService == null) + throw new Exception("Не смог получить серивс IOrderMasterService, scope.ServiceProvider.GetService()"); + + await orderMasterService.ManageOrdersAsync(); + } + } + } } diff --git a/PARR.Master/PARR.Master.csproj b/PARR.Master/PARR.Master.csproj index 57e11ce6..21190861 100644 --- a/PARR.Master/PARR.Master.csproj +++ b/PARR.Master/PARR.Master.csproj @@ -11,8 +11,4 @@ - - - - diff --git a/PARR.Master/ParrMasterInstaller.cs b/PARR.Master/ParrMasterInstaller.cs index 486ae39c..a6c150c1 100644 --- a/PARR.Master/ParrMasterInstaller.cs +++ b/PARR.Master/ParrMasterInstaller.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using PARR.BLL; using PARR.DAL; +using PARR.Master.Services; using PARR.Master.Settings; namespace PARR.Master @@ -21,8 +22,13 @@ namespace PARR.Master configuration.GetSection(nameof(MqSettings)).Bind(mqSettings); services.AddSingleton(mqSettings); + var orderManageSettings = new OrderManageSettings(); + configuration.GetSection(nameof(OrderManageSettings)).Bind(orderManageSettings); + services.AddSingleton(orderManageSettings); + //add other services services.AddTransient(); + services.AddTransient(); } public static IConfigurationBuilder AddMasterConfigurations(this IConfigurationBuilder builder, IServiceCollection services) diff --git a/PARR.Master/Services/IOrderMasterService.cs b/PARR.Master/Services/IOrderMasterService.cs new file mode 100644 index 00000000..3fc2439a --- /dev/null +++ b/PARR.Master/Services/IOrderMasterService.cs @@ -0,0 +1,7 @@ +namespace PARR.Master.Services +{ + internal interface IOrderMasterService + { + Task ManageOrdersAsync(); + } +} diff --git a/PARR.Master/Services/OrderMasterService.cs b/PARR.Master/Services/OrderMasterService.cs new file mode 100644 index 00000000..db6991bf --- /dev/null +++ b/PARR.Master/Services/OrderMasterService.cs @@ -0,0 +1,179 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using PARR.BLL.Domain.Mq; +using PARR.BLL.Services.Interfaces; +using PARR.DAL.Contracts; +using PARR.DAL.Models; +using PARR.DAL.Services.Interfaces; +using PARR.Master.Settings; +using System.Text.Json; + +namespace PARR.Master.Services +{ + internal class OrderMasterService : IOrderMasterService + { + private readonly ILogger logger; + private readonly IOrderService orderService; + private readonly IAgentHistoryService agentHistoryService; + private readonly OrderManageSettings orderManageSettings; + private readonly MqSettings mqSettings; + private readonly IMqService mqService; + + public OrderMasterService( + ILogger logger, + IOrderService orderService, + IAgentHistoryService agentHistoryService, + OrderManageSettings orderManageSettings, + MqSettings mqSettings, + IMqService mqService + + ) + { + this.logger = logger; + this.orderService = orderService; + this.agentHistoryService = agentHistoryService; + this.orderManageSettings = orderManageSettings; + this.mqSettings = mqSettings; + this.mqService = mqService; + } + + + public async Task ManageOrdersAsync() + { + logger.LogInformation($"--- Начало поиска нарядов ---"); + + //ищем наряды в статусе 1 и 2, присваиваем им историю работы агента, меняем стаус наряда + var ordersToManage = await GetOrdersNewOrWorkStatusesAsync(); + + logger.LogInformation($"Ищу наряды со статусом 1 и 2. Найдено нарядов: {ordersToManage.Count()} шт."); + + foreach (var order in ordersToManage) + { + logger.LogInformation($"Найден наряд id: {order.Id}, {order.Number}"); + await SetHistoryAndUpdateOrderStatus(order); + } + + + //находим наряды где статусы не равны, добавляем их в Rabbit + var ordersWithStatusNotEqual = await GetOrdresWithStatusCodeNotEqualAsync(); + var objs = ordersWithStatusNotEqual.Select(t => JsonSerializer.Serialize(new OrderManageMq { OrderId = t.Id })); + + logger.LogInformation($"Ищу наряды где статусы не равны. Найдено нарядов: {ordersWithStatusNotEqual.Count()}. Добавляю их в RabbitMQ."); + + if (objs.Any()) + { + var sendResult = mqService.Send(mqSettings, objs.ToArray()); + if (sendResult.IsSuccess) + { + logger.LogInformation($"Наряды переданы в RabbitMQ: {objs.Count()}"); + logger.LogDebug($"Список нардов переданных в RabbitMQ: {string.Join(", ", objs)}"); + } + else + logger.LogError($"Ошибка при передаче нарядов в Rabbit, неперадынные сообщения: {string.Join(", ", sendResult!.NotSendMessages)}"); + + } + + logger.LogInformation($"=== Конец работы с нарядами=== "); + } + + + /// + /// Находит наряды в статусе 1 и 2, присваивает им историю работы агента, меняет стаус наряда + /// + /// + /// + private async Task SetHistoryAndUpdateOrderStatus(Order order) + { + //TODO: тут тот таймаут? или надо TemplateDuration + var agentHistory = await agentHistoryService.Get() + .Where(t => + t.TemplateId == order.TemplateId + && t.DateCreated > order.GenerateDate!.Value.Add(-orderManageSettings.FindAgentHistoryStartOffset) + && t.DateCreated <= order.GenerateDate!.Value.AddSeconds(order.Template!.ApplicationsInWork!.AgentTimeOutSec!.Value) + && t.OrderId == null + ) + .ToListAsync(); + + logger.LogInformation($"Для наряда {order.Id}, {order.Number} надено новых историй агентов: {agentHistory.Count()} шт. Присваиваю истории ИД наряда."); + + foreach (var historyItem in agentHistory) + historyItem.OrderId = order.Id; + + // смотрим историю агента, и тут же меняем статус наряда + + //1 Ищем статус "Агент начал выполнять задание" + var isAgentStarted = agentHistory.Any(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.Start) || order.AgentHistories.Any(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.Start); + + //2 Ищем статус "Агент завершил выполнение задания" + var isAgentEnded = agentHistory.Any(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.End) || order.AgentHistories.Any(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.End); + + if (isAgentEnded) + { + order.NextStatusCode = (int)OrderStatusEnum.Complete; + logger.LogInformation($"Для наряда {order.Id}, {order.Number} меню статус на {OrderStatusEnum.Complete}"); + } + + if (isAgentStarted && !isAgentEnded && order.StatusCode == (int)OrderStatusEnum.New) + { + order.NextStatusCode = (int)OrderStatusEnum.InWork; + logger.LogInformation($"Для наряда {order.Id}, {order.Number} меню статус на {OrderStatusEnum.InWork}"); + } + + //TODO: тут подумать про таймаут, по какому таймауту закрывтаь наряд? по таймауту агента, или по TemplateDuration? + //timeout + //if(isAgentStarted==false && isAgentEnded == false && ) + //{ + // // ставим Выполнен, так как превысил таймаут + // order.NextStatusCode = (int)OrderStatusEnum.Complete; + // logger.LogInformation($"Для наряда {order.Id}, {order.Number} меню статус на {OrderStatusEnum.Complete}"); + //} + + if (!await orderService.CommitAsync()) + { + logger.LogError($"Ошибка при сохранении НАРЯДА, id: {order.Id}"); + } + + logger.LogInformation($"Закончил работу с нарядом {order.Id}, {order.Number}"); + } + + + /// + /// Получить список нарядов, со статусами 1 или 2 + /// + private async Task> GetOrdersNewOrWorkStatusesAsync() + { + return await GetOrdres() + .Where(t => t.StatusCode == (int)OrderStatusEnum.New || t.StatusCode == (int)OrderStatusEnum.InWork) + .ToListAsync(); + } + + + /// + /// Получить список нарядов, у которых StatusCode!=NextStatusCode + /// + private async Task> GetOrdresWithStatusCodeNotEqualAsync() + { + return await GetOrdres() + .Where(t => t.NextStatusCode.HasValue && t.NextStatusCode.Value != t.StatusCode) + .ToListAsync(); + } + + + /// + /// Основной запрос на получения списка нарядов со связями, по базовому условию + /// + /// + private IQueryable GetOrdres() + { + return orderService.Get() + .Include(t => t.Template).ThenInclude(t => t!.ApplicationsInWork) + .Include(t => t.AgentHistories) + .Where(t => + t.TemplateId.HasValue + && t.Template!.ApplicationsInWork!.IsAgent == true + && t.GenerateDate.HasValue + && t.Template!.ApplicationsInWork!.AgentTimeOutSec.HasValue + ); + } + } +} diff --git a/PARR.Master/Settings/OrderManageSettings.cs b/PARR.Master/Settings/OrderManageSettings.cs new file mode 100644 index 00000000..c32cc5ba --- /dev/null +++ b/PARR.Master/Settings/OrderManageSettings.cs @@ -0,0 +1,10 @@ +namespace PARR.Master.Settings +{ + internal class OrderManageSettings + { + /// + /// Смещение, относительно которого начинается поиск истории агента для привязки к времени генерации наряда + /// + public TimeSpan FindAgentHistoryStartOffset { get; set; } + } +} diff --git a/PARR.MasterWorker/appsettings.json b/PARR.MasterWorker/appsettings.json index b70e9302..76e7193a 100644 --- a/PARR.MasterWorker/appsettings.json +++ b/PARR.MasterWorker/appsettings.json @@ -34,5 +34,8 @@ }, "WorkerSettings": { "RepeatEvery": "0:30:00" + }, + "OrderManageSettings": { + "FindAgentHistoryStartOffset": "0:05:00" } }