2023-11-15 16:34:28 +10:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2026-04-14 16:27:27 +10:00
|
|
|
|
using PARR.Core.Common.Interfaces.RabbitServices;
|
2026-04-30 10:35:31 +10:00
|
|
|
|
using PARR.Core.Repositories.Interfaces;
|
2026-04-14 12:01:49 +10:00
|
|
|
|
using PARR.Domain.Common.Rabbit.Messages;
|
2026-04-30 10:35:31 +10:00
|
|
|
|
using PARR.Domain.Entities;
|
2026-04-13 16:58:30 +10:00
|
|
|
|
using PARR.Domain.Enums;
|
2023-11-15 16:34:28 +10:00
|
|
|
|
using PARR.Master.Settings;
|
2026-01-22 10:56:02 +10:00
|
|
|
|
using System.Text.Encodings.Web;
|
2023-11-15 16:34:28 +10:00
|
|
|
|
using System.Text.Json;
|
|
|
|
|
|
|
|
|
|
|
|
namespace PARR.Master.Services
|
|
|
|
|
|
{
|
|
|
|
|
|
internal class OrderMasterService : IOrderMasterService
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly ILogger<OrderMasterService> logger;
|
2026-04-30 10:35:31 +10:00
|
|
|
|
private readonly IOrderRepository orderService;
|
|
|
|
|
|
private readonly IAgentHistoryRepository agentHistoryService;
|
2023-11-15 16:34:28 +10:00
|
|
|
|
private readonly OrderManageSettings orderManageSettings;
|
|
|
|
|
|
private readonly MqSettings mqSettings;
|
2026-04-14 12:01:49 +10:00
|
|
|
|
private readonly IRabbitService mqService;
|
2023-11-15 16:34:28 +10:00
|
|
|
|
|
2026-01-22 10:56:02 +10:00
|
|
|
|
private static readonly JsonSerializerOptions jsonOptions = new JsonSerializerOptions
|
|
|
|
|
|
{
|
|
|
|
|
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-15 16:34:28 +10:00
|
|
|
|
public OrderMasterService(
|
|
|
|
|
|
ILogger<OrderMasterService> logger,
|
2026-04-30 10:35:31 +10:00
|
|
|
|
IOrderRepository orderService,
|
|
|
|
|
|
IAgentHistoryRepository agentHistoryService,
|
2023-11-15 16:34:28 +10:00
|
|
|
|
OrderManageSettings orderManageSettings,
|
|
|
|
|
|
MqSettings mqSettings,
|
2026-04-14 12:01:49 +10:00
|
|
|
|
IRabbitService mqService
|
2023-11-15 16:34:28 +10:00
|
|
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.logger = logger;
|
|
|
|
|
|
this.orderService = orderService;
|
|
|
|
|
|
this.agentHistoryService = agentHistoryService;
|
|
|
|
|
|
this.orderManageSettings = orderManageSettings;
|
|
|
|
|
|
this.mqSettings = mqSettings;
|
|
|
|
|
|
this.mqService = mqService;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async Task ManageOrdersAsync()
|
|
|
|
|
|
{
|
2023-11-16 14:24:22 +10:00
|
|
|
|
// логика работы описана в документации проекта
|
|
|
|
|
|
|
2023-11-16 16:19:47 +10:00
|
|
|
|
logger.LogInformation($"--- --- Начало поиска нарядов --- ---");
|
2023-11-15 16:34:28 +10:00
|
|
|
|
|
2023-11-16 14:24:22 +10:00
|
|
|
|
//ищем наряды в статусе 1 и 2
|
2023-11-15 16:34:28 +10:00
|
|
|
|
var ordersToManage = await GetOrdersNewOrWorkStatusesAsync();
|
|
|
|
|
|
|
|
|
|
|
|
logger.LogInformation($"Ищу наряды со статусом 1 и 2. Найдено нарядов: {ordersToManage.Count()} шт.");
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var order in ordersToManage)
|
|
|
|
|
|
{
|
2023-11-16 14:24:22 +10:00
|
|
|
|
//присваиваем им историю работы агента, меняем стаус наряда
|
2023-11-16 16:19:47 +10:00
|
|
|
|
logger.LogInformation($"--- Найден наряд id: {order.Id}, {order.Number} ---");
|
2023-11-15 16:34:28 +10:00
|
|
|
|
await SetHistoryAndUpdateOrderStatus(order);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//находим наряды где статусы не равны, добавляем их в Rabbit
|
|
|
|
|
|
var ordersWithStatusNotEqual = await GetOrdresWithStatusCodeNotEqualAsync();
|
2026-01-22 10:56:02 +10:00
|
|
|
|
var objs = ordersWithStatusNotEqual.Select(t => JsonSerializer.Serialize(new OrderManageMq { OrderId = t.Id }, jsonOptions));
|
2023-11-15 16:34:28 +10:00
|
|
|
|
|
|
|
|
|
|
logger.LogInformation($"Ищу наряды где статусы не равны. Найдено нарядов: {ordersWithStatusNotEqual.Count()}. Добавляю их в RabbitMQ.");
|
|
|
|
|
|
|
|
|
|
|
|
if (objs.Any())
|
|
|
|
|
|
{
|
2025-08-20 14:10:22 +10:00
|
|
|
|
var sendResult = await mqService.SendAsync(mqSettings, objs.ToArray());
|
2023-11-15 16:34:28 +10:00
|
|
|
|
if (sendResult.IsSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
logger.LogInformation($"Наряды переданы в RabbitMQ: {objs.Count()}");
|
|
|
|
|
|
logger.LogDebug($"Список нардов переданных в RabbitMQ: {string.Join(", ", objs)}");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2023-11-16 16:19:47 +10:00
|
|
|
|
logger.LogError($"Ошибка при передаче нарядов в Rabbit, не переданные сообщения: {string.Join(", ", sendResult!.NotSendMessages)}");
|
2023-11-15 16:34:28 +10:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logger.LogInformation($"=== Конец работы с нарядами=== ");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Находит наряды в статусе 1 и 2, присваивает им историю работы агента, меняет стаус наряда
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="order"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private async Task SetHistoryAndUpdateOrderStatus(Order order)
|
|
|
|
|
|
{
|
2023-11-17 14:11:38 +10:00
|
|
|
|
// получаем историю агента, которая не присовена этому наряду
|
|
|
|
|
|
var newAgentHistory = await GetNewAgentHistoryByOrderAsync(order);
|
2023-11-16 14:24:22 +10:00
|
|
|
|
|
2023-11-17 14:11:38 +10:00
|
|
|
|
logger.LogInformation($"Для наряда {order.Id}, {order.Number} найдено новых историй агентов: {newAgentHistory.Count()} шт.");
|
2023-11-16 14:24:22 +10:00
|
|
|
|
|
2023-11-17 14:11:38 +10:00
|
|
|
|
// присваиваем наряду
|
|
|
|
|
|
SetOrderToAgentHistory(ref newAgentHistory, ref order);
|
2023-11-16 14:24:22 +10:00
|
|
|
|
|
|
|
|
|
|
// совмещенный список истории агента по этому наряду
|
2023-11-17 14:11:38 +10:00
|
|
|
|
var allHistory = newAgentHistory;
|
2023-11-16 14:24:22 +10:00
|
|
|
|
order.AgentHistories.ToList().ForEach(item => allHistory.Add(item));
|
|
|
|
|
|
allHistory = allHistory.OrderBy(t => t.DateCreated).ToList();
|
|
|
|
|
|
|
2023-11-15 16:34:28 +10:00
|
|
|
|
// смотрим историю агента, и тут же меняем статус наряда
|
|
|
|
|
|
|
2023-11-17 14:11:38 +10:00
|
|
|
|
// Ищем статус "Агент начал выполнять задание"
|
2023-11-16 14:24:22 +10:00
|
|
|
|
var isAgentStarted = allHistory.Any(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.Start);
|
2023-11-15 16:34:28 +10:00
|
|
|
|
|
2023-11-17 14:11:38 +10:00
|
|
|
|
// Ищем статус "Агент завершил выполнение задания"
|
2023-11-16 14:24:22 +10:00
|
|
|
|
var isAgentEnded = allHistory.Any(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.End);
|
2023-11-15 16:34:28 +10:00
|
|
|
|
|
2023-11-17 14:11:38 +10:00
|
|
|
|
// Закрыть наряд
|
2023-11-15 16:34:28 +10:00
|
|
|
|
if (isAgentEnded)
|
2023-11-17 14:11:38 +10:00
|
|
|
|
ChangeNextStatus(ref order, OrderStatusEnum.Complete, "");
|
2023-11-15 16:34:28 +10:00
|
|
|
|
|
2023-11-17 14:11:38 +10:00
|
|
|
|
// Наряд в работу
|
2023-11-15 16:34:28 +10:00
|
|
|
|
if (isAgentStarted && !isAgentEnded && order.StatusCode == (int)OrderStatusEnum.New)
|
2023-11-17 14:11:38 +10:00
|
|
|
|
ChangeNextStatus(ref order, OrderStatusEnum.InWork, "");
|
2023-11-16 14:24:22 +10:00
|
|
|
|
|
2023-11-17 14:11:38 +10:00
|
|
|
|
// Закрываем по таймауту. Если есть начало, и нет завершения по АгентТаймаут, то закрываем наряд.
|
2023-11-16 14:24:22 +10:00
|
|
|
|
if (isAgentStarted == true && isAgentEnded == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
var lastStrartDate = allHistory.Last(t => t.HistoryLevelId == (int)AgentHistoryLevelEnum.Start).DateCreated;
|
2025-06-25 14:17:34 +10:00
|
|
|
|
var timeout = (double)order.Template!.Job!.Group!.AgentTimeOutSec!;
|
2023-11-16 14:24:22 +10:00
|
|
|
|
|
|
|
|
|
|
if (lastStrartDate.AddSeconds(timeout) <= DateTimeOffset.UtcNow)
|
|
|
|
|
|
{
|
2023-11-17 14:11:38 +10:00
|
|
|
|
var logMsg = $"Закрываю наряд по таймауту ожидания агента. Время старта агента: {lastStrartDate}, допустимый timeOut: {timeout} сек.";
|
|
|
|
|
|
ChangeNextStatus(ref order, OrderStatusEnum.Complete, logMsg);
|
2023-11-16 14:24:22 +10:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
logger.LogDebug($"Для наряда {order.Id}, {order.Number} оставляю статус прежним, ожидаю завершение работы агента. Время старта агента: {lastStrartDate}, допустимый timeOut: {timeout} сек.");
|
2023-11-15 16:34:28 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-16 14:24:22 +10:00
|
|
|
|
//TODO: тут можно подумать, и закрывать наряды, у которых нет истории агента. сейчас мы такие наряды пропускаем, их будет закрывать руками админ.
|
2023-11-16 16:19:47 +10:00
|
|
|
|
if (!allHistory.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
logger.LogInformation($"Для наряда {order.Id}, {order.Number} нет истории агента. Ничего не делаем.");
|
|
|
|
|
|
}
|
2023-11-15 16:34:28 +10:00
|
|
|
|
|
|
|
|
|
|
if (!await orderService.CommitAsync())
|
|
|
|
|
|
{
|
2023-11-17 14:11:38 +10:00
|
|
|
|
logger.LogError($"Ошибка при сохранении наряда, id: {order.Id}");
|
2023-11-15 16:34:28 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-16 14:24:22 +10:00
|
|
|
|
logger.LogInformation($"Закончил работу с нарядом {order.Id}, {order.Number}");
|
2023-11-15 16:34:28 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-17 14:11:38 +10:00
|
|
|
|
/// <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)
|
2025-06-25 14:17:34 +10:00
|
|
|
|
&& t.DateCreated <= order.GenerateDate!.Value.Add(order.Template!.Job!.Group!.TemplateDurationTimeSpan!.Value)
|
2023-11-17 14:11:38 +10:00
|
|
|
|
&& 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}, он соответствует дейстивительности.");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-15 16:34:28 +10:00
|
|
|
|
/// <summary>
|
2023-11-16 16:19:47 +10:00
|
|
|
|
/// Получить список нарядов, со статусами 1 или 2 и у которых nextStatus<3
|
2023-11-15 16:34:28 +10:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async Task<List<Order>> GetOrdersNewOrWorkStatusesAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
return await GetOrdres()
|
2023-11-16 16:19:47 +10:00
|
|
|
|
.Where(t =>
|
|
|
|
|
|
(t.StatusCode == (int)OrderStatusEnum.New || t.StatusCode == (int)OrderStatusEnum.InWork)
|
|
|
|
|
|
&& (!t.NextStatusCode.HasValue || t.NextStatusCode < (int)OrderStatusEnum.Stop)
|
|
|
|
|
|
)
|
|
|
|
|
|
.OrderBy(t => t.DateCreated)
|
2023-11-15 16:34:28 +10:00
|
|
|
|
.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()
|
2025-06-25 14:17:34 +10:00
|
|
|
|
.Include(t => t.Template).ThenInclude(t => t!.Job)
|
2023-11-15 16:34:28 +10:00
|
|
|
|
.Include(t => t.AgentHistories)
|
|
|
|
|
|
.Where(t =>
|
|
|
|
|
|
t.TemplateId.HasValue
|
2025-06-25 14:17:34 +10:00
|
|
|
|
&& t.Template!.Job!.Group!.IsAgent == true
|
2023-11-15 16:34:28 +10:00
|
|
|
|
&& t.GenerateDate.HasValue
|
2025-06-25 14:17:34 +10:00
|
|
|
|
&& t.Template!.Job!.Group!.AgentTimeOutSec.HasValue
|
|
|
|
|
|
&& string.IsNullOrEmpty(t.Template!.Job!.Group!.TemplateDuration) == false
|
2023-11-17 16:03:36 +10:00
|
|
|
|
&& t.ExpirationDate > DateTimeOffset.UtcNow
|
2023-11-16 14:24:22 +10:00
|
|
|
|
).OrderBy(t => t.DateCreated);
|
2023-11-15 16:34:28 +10:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|