Files
parr_api/PARR.Master/Services/OrderMasterService.cs
2023-11-16 16:19:47 +10:00

225 lines
12 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.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 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();
logger.LogInformation($"Для наряда {order.Id}, {order.Number} найдено новых историй агентов: {agentHistory.Count()} шт.");
foreach (var historyItem in agentHistory)
{
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;
}
}
//отбрасываем все записи которые не добавлены этому наряду
agentHistory = agentHistory.Where(t => t.OrderId.HasValue).ToList();
// совмещенный список истории агента по этому наряду
var allHistory = agentHistory;
order.AgentHistories.ToList().ForEach(item => allHistory.Add(item));
allHistory = allHistory.OrderBy(t => t.DateCreated).ToList();
// смотрим историю агента, и тут же меняем статус наряда
//1 Ищем статус "Агент начал выполнять задание"
var isAgentStarted = allHistory.Any(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.Start);
//2 Ищем статус "Агент завершил выполнение задания"
var isAgentEnded = allHistory.Any(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.End);
//todo: !!! если nextStatus= желаемому статусу, то не заходить внутрь ИФА!!!
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}");
}
//закрываем по таймауту. Если есть начало, и нет завершения по АгентТаймаут, то закрываем наряд.
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)
{
order.NextStatusCode = (int)OrderStatusEnum.Complete;
logger.LogInformation($"Для наряда {order.Id}, {order.Number} меняю статус на {OrderStatusEnum.Complete}, закрываю наряд по таймауту ожидания агента. Время старта агента: {lastStrartDate}, допустимый timeOut: {timeout} сек.");
}
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>
/// Получить список нарядов, со статусами 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
).OrderBy(t => t.DateCreated);
}
}
}