Files
parr_api/PARR.Master/Services/OrderMasterService.cs
2023-11-21 14:51:19 +10:00

260 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.BLL.Domain.Mq;
using PARR.BLL.Services.Interfaces;
using PARR.Constants;
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<OrderMasterService> logger;
private readonly IOrderService orderService;
private readonly IAgentHistoryService agentHistoryService;
private readonly OrderManageSettings orderManageSettings;
private readonly MqSettings mqSettings;
private readonly IMqService mqService;
public OrderMasterService(
ILogger<OrderMasterService> 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($"=== Конец работы с нарядами=== ");
}
/// <summary>
/// Находит наряды в статусе 1 и 2, присваивает им историю работы агента, меняет стаус наряда
/// </summary>
/// <param name="order"></param>
/// <returns></returns>
private async Task SetHistoryAndUpdateOrderStatus(Order order)
{
// получаем историю агента, которая не присовена этому наряду
var newAgentHistory = await GetNewAgentHistoryByOrderAsync(order);
logger.LogInformation($"Для наряда {order.Id}, {order.Number} найдено новых историй агентов: {newAgentHistory.Count()} шт.");
// присваиваем наряду
SetOrderToAgentHistory(ref newAgentHistory, ref order);
// совмещенный список истории агента по этому наряду
var allHistory = newAgentHistory;
order.AgentHistories.ToList().ForEach(item => allHistory.Add(item));
allHistory = allHistory.OrderBy(t => t.DateCreated).ToList();
// смотрим историю агента, и тут же меняем статус наряда
// Ищем статус "Агент начал выполнять задание"
var isAgentStarted = allHistory.Any(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.Start);
// Ищем статус "Агент завершил выполнение задания"
var isAgentEnded = allHistory.Any(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.End);
// Закрыть наряд
if (isAgentEnded)
ChangeNextStatus(ref order, OrderStatusEnum.Complete, "");
// Наряд в работу
if (isAgentStarted && !isAgentEnded && order.StatusCode == (int)OrderStatusEnum.New)
ChangeNextStatus(ref order, OrderStatusEnum.InWork, "");
// Закрываем по таймауту. Если есть начало, и нет завершения по АгентТаймаут, то закрываем наряд.
if (isAgentStarted == true && isAgentEnded == false)
{
var lastStrartDate = allHistory.Last(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.Start).DateCreated;
var timeout = (double)order.Template!.ApplicationsInWork!.AgentTimeOutSec!;
if (lastStrartDate.AddSeconds(timeout) <= DateTimeOffset.UtcNow)
{
var logMsg = $"Закрываю наряд по таймауту ожидания агента. Время старта агента: {lastStrartDate}, допустимый timeOut: {timeout} сек.";
ChangeNextStatus(ref order, OrderStatusEnum.Complete, logMsg);
}
else
logger.LogDebug($"Для наряда {order.Id}, {order.Number} оставляю статус прежним, ожидаю завершение работы агента. Время старта агента: {lastStrartDate}, допустимый timeOut: {timeout} сек.");
}
//TODO: тут можно подумать, и закрывать наряды, у которых нет истории агента. сейчас мы такие наряды пропускаем, их будет закрывать руками админ.
if (!allHistory.Any())
{
logger.LogInformation($"Для наряда {order.Id}, {order.Number} нет истории агента. Ничего не делаем.");
}
if (!await orderService.CommitAsync())
{
logger.LogError($"Ошибка при сохранении наряда, id: {order.Id}");
}
logger.LogInformation($"Закончил работу с нарядом {order.Id}, {order.Number}");
}
/// <summary>
/// Присваивает истории агента идентивикатор наряда
/// </summary>
private void SetOrderToAgentHistory(ref List<AgentHistory> newAgentHistory, ref Order order)
{
foreach (var historyItem in newAgentHistory)
{
historyItem.OrderId = order.Id;
logger.LogDebug($"Для наряда {order.Id}, {order.Number} добавил истрию агента {historyItem.Id}, {historyItem.HistoryLevelId}");
// если статус агента = завершил работу. то историю после завешения, не записываем, это возможно относится к другому наряду, или нам уже пофиг на неё
if (historyItem.HistoryLevelId == (int)AgentHistoryLevelEnum.End)
{
logger.LogDebug($"Для наряда {order.Id}, {order.Number} прекращаю добавлять историю агента, так как добавлена запись со статусом Стоп");
continue;
}
}
//отбрасываем все записи которые не добавлены этому наряду
newAgentHistory = newAgentHistory.Where(t => t.OrderId.HasValue).ToList();
}
/// <summary>
/// Получить историю агента не привязанную к наряду, попадающую в дельту существования наряда
/// </summary>
/// <param name="order"></param>
/// <returns></returns>
private async Task<List<AgentHistory>> GetNewAgentHistoryByOrderAsync(Order order)
{
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.DateCreated <= order.GenerateDate!.Value.Add(order.Template!.ApplicationsInWork!.TemplateDurationTimeSpan!.Value)
&& t.OrderId == null
)
.OrderBy(t => t.DateCreated)
.ToListAsync();
return agentHistory;
}
/// <summary>
/// Изменить статус наряда
/// </summary>
/// <param name="order"></param>
/// <param name="nextStatus"></param>
private void ChangeNextStatus(ref Order order, OrderStatusEnum nextStatus, string logCompleteMsg)
{
if (order.NextStatusCode != (int)nextStatus)
{
order.NextStatusCode = (int)nextStatus;
logger.LogInformation($"Для наряда {order.Id}, {order.Number} меняю статус на {nextStatus}. {logCompleteMsg}");
}
else
{
logger.LogDebug($"Для наряда {order.Id}, {order.Number} оставляю статус {nextStatus}, он соответствует дейстивительности.");
}
}
/// <summary>
/// Получить список нарядов, со статусами 1 или 2 и у которых nextStatus<3
/// </summary>
private async Task<List<Order>> GetOrdersNewOrWorkStatusesAsync()
{
return await GetOrdres()
.Where(t =>
(t.StatusCode == (int)OrderStatusEnum.New || t.StatusCode == (int)OrderStatusEnum.InWork)
&& (!t.NextStatusCode.HasValue || t.NextStatusCode < (int)OrderStatusEnum.Stop)
)
.OrderBy(t => t.DateCreated)
.ToListAsync();
}
/// <summary>
/// Получить список нарядов, у которых StatusCode!=NextStatusCode
/// </summary>
private async Task<List<Order>> GetOrdresWithStatusCodeNotEqualAsync()
{
return await GetOrdres()
.Where(t => t.NextStatusCode.HasValue && t.NextStatusCode.Value != t.StatusCode)
.ToListAsync();
}
/// <summary>
/// Основной запрос на получения списка нарядов со связями, по базовому условию
/// </summary>
/// <returns></returns>
private IQueryable<Order> 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
&& string.IsNullOrEmpty(t.Template!.ApplicationsInWork!.TemplateDuration) == false
&& t.ExpirationDate > DateTimeOffset.UtcNow
).OrderBy(t => t.DateCreated);
}
}
}