using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PARR.Core.Common.Interfaces; using PARR.Core.Common.Interfaces.RabbitServices; using PARR.DAL.Models; using PARR.DAL.Services.Interfaces; using PARR.Domain.Common.Rabbit.Messages; using PARR.Domain.Enums; using PARR.EsppOrderManager.Services; using PARR.EsppOrderManager.Settings; namespace PARR.EsppOrderManager { internal class EsppOrderManager : IEsppOrderManager { private readonly MqSettings mqSettings; private readonly IRabbitService mqService; private readonly ILogger logger; private readonly ITransformService transformService; private readonly IEsppOrderService esppOrderService; private readonly IServiceProvider serviceProvider; private readonly ResultTemplateSettings resultTemplateSettings; public EsppOrderManager( MqSettings mqSettings, IRabbitService mqService, ILogger logger, ITransformService transformService, IEsppOrderService esppOrderService, IServiceProvider serviceProvider, ResultTemplateSettings resultTemplateSettings ) { this.mqSettings = mqSettings; this.mqService = mqService; this.logger = logger; this.transformService = transformService; this.esppOrderService = esppOrderService; this.serviceProvider = serviceProvider; this.resultTemplateSettings = resultTemplateSettings; } public async Task StartAsync() { // логика работы описана в документации var isConnected = await mqService.InitConsumerAsync(mqSettings, ManageOrderAsync); if (!isConnected) throw new Exception("Ошибка при подключении к RabbitMq"); //test, and remove async in method name //await ManageOrderAsync("{\"OrderId\":\"49191af5-c313-4388-9720-a61fd13f15a8\"}"); } public async Task StopAsync() { await mqService.DisposeAsync(); } private async Task ManageOrderAsync(string msg) { //logger.LogDebug($"Получили сообщение: {msg}"); var objFromQuery = transformService.GetModelFromJson(msg); if (objFromQuery == null) return; using (var scope = serviceProvider.CreateScope()) { var orderService = scope.ServiceProvider.GetService(); if (orderService == null) throw new Exception("Не смог получить серивс IOrderService, scope.ServiceProvider.GetService()"); await ManageOrderToScopeAsync(orderService, objFromQuery); } logger.LogDebug($"Закончил работу с сообщением: {msg}"); } private async Task ManageOrderToScopeAsync(IOrderService orderService, OrderManageMq objFromQuery) { var order = await orderService.Get() .Include(t => t.AgentHistories).ThenInclude(t => t.AgentHistoryLevel) .Include(t => t.OrderStatus) .Include(t => t.NextStatus) .Include(t => t.Template).ThenInclude(t => t!.Job).ThenInclude(t => t!.Tnk) .FirstOrDefaultAsync(t => t.Id == objFromQuery.OrderId); if (!IsValidOrder(ref order, ref objFromQuery)) return; logger.LogDebug($"Найден наряд в БД: {order!.Number}"); var resultMsg = GenerateResultMsg(order!); var workDuration = CalcWorkDuration(order!); // Изменям статус наряда в ЕСПП var result = await esppOrderService.ChangeOrderToEsppAsync(order!, resultMsg, workDuration); if (result) { logger.LogInformation($"Статусы наряда {order!.Number} в ЕСПП обновлены. Установлен статус: {order.NextStatus!.Description}"); //обновляем статусы в БД order.StatusCode = order.NextStatusCode!.Value; order.DateModified = DateTimeOffset.UtcNow; if (!await orderService.CommitAsync()) { logger.LogError($"Ошибка при обновлении статуса в БД у наряда: {order.Number}. Не установлен статус {order.NextStatus!.Description}"); } } else logger.LogError($"Ошибка при обновлении статуса наряда {order!.Number}. Статус не изменен."); } private bool IsValidOrder(ref Order? order, ref OrderManageMq query) { if (order == null) { logger.LogError($"Не найден наряд с OrderId: {query.OrderId}"); return false; } if (order.StatusCode == order.NextStatusCode) { logger.LogInformation($"У наряда {order.Id}, {order.Number} статусы равны, ничего не делаем. Статус: {order.OrderStatus!.Description}"); return false; } if (!order.NextStatusCode.HasValue) { logger.LogWarning($"У наряда {order.Id}, {order.Number} не установлено поле {nameof(order.NextStatusCode)}"); return false; } return true; } /// /// Генерирует текст поля Решение, в зависимости от статуса наряда и от истории Агента /// /// /// private string GenerateResultMsg(Order order) { var agentHistory = order.AgentHistories .OrderBy(t => t.DateCreated) .Select(t => $"{t.DateCreated} :: {t.AgentHistoryLevel?.Name} :: {t.Message}"); //"Template": "{base}\n{msgAgent}", //"MsgAgent": "Работы выполнены средствами автоматизации.\nЖурнал работы:\n{agentHistory}", //"MsgAgentEmpty": "Работы должны были выполняться средствами автоматизации. От средств автоматизации не поступило сообщений. Работы не выполнены." //Шаблон поля Решение var template = resultTemplateSettings.Template; //Поле решение из ApplicationsInWork var baseMsg = order.Template!.Job!.Group!.Solution; //Формируем сообщения от Агента var msgAgent = agentHistory.Any() ? resultTemplateSettings.MsgAgent.Replace("{agentHistory}", string.Join("\n", agentHistory)) : resultTemplateSettings.MsgAgentEmpty; return template.Replace("{base}", baseMsg).Replace("{msgAgent}", msgAgent); } /// /// Рассчиать трудозатраты /// /// /// private TimeSpan CalcWorkDuration(Order order) { var agentHistory = order.AgentHistories .OrderBy(t => t.DateCreated); if (!agentHistory.Any()) return GetTnkDurationByScriptDuration(TimeSpan.Zero); var dateStart = agentHistory.LastOrDefault(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.Start)?.DateCreated; var dateEnd = agentHistory.LastOrDefault(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.End)?.DateCreated; if (dateStart.HasValue && dateEnd.HasValue) { var duration = dateEnd.Value - dateStart.Value; logger.LogDebug($"Продолжительность работы скрипта: {duration}"); return GetTnkDurationByScriptDuration(duration); } return GetTnkDurationByScriptDuration(TimeSpan.Zero); } /// /// Получить время для списания ТНК по времени работы скрипта /// /// /// private TimeSpan GetTnkDurationByScriptDuration(TimeSpan scriptDuration) { // минимальное значение 1 минута var minDuration = new TimeSpan(0, 1, 0); if (scriptDuration < minDuration) { logger.LogDebug($"Продолжительность меньше минимального, беру минимальное: {minDuration}. {scriptDuration}<{minDuration}"); return minDuration; } else return scriptDuration; } } }