1298 lines
59 KiB
C#
1298 lines
59 KiB
C#
using SJZY.Track.Application.Services.Warning.Model;
|
||
using SJZY.Track.Domain.Aggregates.KPIAreaAggregate;
|
||
using SJZY.Track.Domain.Aggregates.OrderAggregate;
|
||
using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate;
|
||
using SJZY.Track.Domain.Aggregates.TrackStatusAggregate;
|
||
using SJZY.Track.Domain.Aggregates.WarningAggregate;
|
||
using SJZY.Track.Domain.Aggregates.WarningAggregate.Signals;
|
||
using SJZY.Track.Domain.Aggregates.WarningAggregate.Transport;
|
||
using SJZY.Track.Domain.Dtos.Forewarning;
|
||
using SJZY.Track.Domain.Dtos.TrackDetails;
|
||
using SJZY.Track.Domain.Dtos.Warning;
|
||
using SJZY.Track.Domain.Enums;
|
||
using Volo.Abp.DependencyInjection;
|
||
|
||
namespace SJZY.Track.Application.Services.Warning.Handlers;
|
||
|
||
public class TransportWarningHandler : ITransportWarningHandler, ITransientDependency
|
||
{
|
||
private readonly IKpiAreaRepository _kpiAreaRepository;
|
||
private readonly IOrderRepository _orderRepository;
|
||
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
|
||
private readonly IWarningRuleRepository _warningRuleRepository;
|
||
private readonly IWarningTaskRepository _warningTaskRepository;
|
||
private readonly IWarningDetailsEsRepository _warningDetailsEsRepository;
|
||
private readonly ITrackStatusRepository _trackStatusRepository;
|
||
|
||
public TransportWarningHandler(IWarningTaskRepository warningTaskRepository, IWarningDetailsEsRepository warningDetailsEsRepository, IWarningRuleRepository warningRuleRepository, IOrderRepository orderRepository, ITrackDetailsEsRepository trackDetailsEsRepository, IKpiAreaRepository kpiAreaRepository, ITrackStatusRepository trackStatusRepository)
|
||
{
|
||
_warningTaskRepository = warningTaskRepository;
|
||
_warningDetailsEsRepository = warningDetailsEsRepository;
|
||
_warningRuleRepository = warningRuleRepository;
|
||
_orderRepository = orderRepository;
|
||
_trackDetailsEsRepository = trackDetailsEsRepository;
|
||
_kpiAreaRepository = kpiAreaRepository;
|
||
_trackStatusRepository = trackStatusRepository;
|
||
}
|
||
|
||
#region todo-test: 预警信号批处理-删除轨迹
|
||
|
||
// tips:删除轨迹信号在运输预警处理只关注被删除的轨迹的状态
|
||
public async Task TrackDeletedHandleAsync(List<TrackDeletedSignal> deletedSignals)
|
||
{
|
||
// 1.查询订单
|
||
var orderIdSet = deletedSignals.Select(x => x.OrderId).ToHashSet();
|
||
// tips:只处理未完结并且未删除的订单
|
||
var orderIds = (await _orderRepository.GetListAsync(
|
||
x => orderIdSet.Contains(x.Id) && x.IsCompleted == false,
|
||
y => y.Id
|
||
)).ToHashSet();
|
||
if (orderIds.Count == 0) return;
|
||
|
||
// 1.1 构建订单和轨迹状态ids字典
|
||
var orderIdTrackStatusIdsDict = deletedSignals
|
||
.Where(s => orderIds.Contains(s.OrderId)) // tips:信号过滤
|
||
.GroupBy(x => x.OrderId)
|
||
.ToDictionary(x => x.Key, y => y.Select(z => z.DeletedTrack.TrackStatusId).ToHashSet());
|
||
if (orderIdTrackStatusIdsDict.Count == 0) return;
|
||
|
||
// 2.查询订单包含的轨迹,并筛选和删除的轨迹状态一致(tips:由于已经是删除,查询出的轨迹不会包含当前已经删除的轨迹)
|
||
var orderTracksDict = await _trackDetailsEsRepository.GetOrderTracksDictAsync(new GetOrderTracksDictVO
|
||
{
|
||
// tips:要求订单id+轨迹状态id同时满足的轨迹
|
||
OrderIdTracksStatusIdsDict = orderIdTrackStatusIdsDict
|
||
});
|
||
|
||
// 3.查订单的运输时效预警明细,命中起始轨迹状态的(即将超时/已超时)
|
||
var warningDetails = await _warningDetailsEsRepository.GetTransportWarningDetailsAsync(new GetTransportWarningDetailsVO
|
||
{
|
||
// tips:要求订单id+轨迹状态id同时满足的预警明细
|
||
OrderIdStartTracksStatusIdsDict = orderIdTrackStatusIdsDict
|
||
});
|
||
|
||
// 订单和它的预警明细字典
|
||
var orderDetailsDict = warningDetails
|
||
.GroupBy(x => x.OrderId)
|
||
.ToDictionary(x => x.Key, y => y.ToList());
|
||
|
||
// 4.查询预警明细生成任务,命中起始轨迹状态的(即将超时/已超时)
|
||
var generateTasks = await _warningTaskRepository.GetTransportWarningDetailsGenerateTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& orderIds.Contains(x.OrderId)
|
||
// && orderIdTrackStatusIdsDict[x.OrderId].Contains(x.StartTrackStatusId)
|
||
);
|
||
generateTasks = generateTasks.Where(x =>
|
||
{
|
||
var statusIds = orderIdTrackStatusIdsDict[x.OrderId];
|
||
return statusIds.Contains(x.StartTrackStatusId);
|
||
}).ToList();
|
||
|
||
// 订单和它的预警生成任务字典
|
||
var oderGenerateTaskDict = generateTasks
|
||
.GroupBy(x => x.OrderId)
|
||
.ToDictionary(x => x.Key, y => y.ToList());
|
||
|
||
var needUpdateDetails = new List<TransportWarningDetailsIndex>();
|
||
var needUpdateGenerateTask = new List<TransportWarningDetailsGenerateTaskEntity>();
|
||
var needCloseNotifyWarningDetailsIds = new HashSet<string>();
|
||
// 以订单的维度处理预警明细
|
||
foreach (var orderId in orderIds)
|
||
{
|
||
var orderDetails = orderDetailsDict.GetValueOrDefault(orderId, new List<TransportWarningDetailsIndex>(0));
|
||
var orderTracks = orderTracksDict.GetValueOrDefault(orderId, new List<TrackDetailsIndex>(0));
|
||
var orderTasks = oderGenerateTaskDict.GetValueOrDefault(orderId, new List<TransportWarningDetailsGenerateTaskEntity>(0));
|
||
|
||
// 明细和生成任务都不存在跳过
|
||
if (orderDetails.Count == 0 && orderTasks.Count == 0)
|
||
continue;
|
||
|
||
// 起始轨迹状态全部被删除
|
||
if (orderTracks.Count == 0)
|
||
{
|
||
orderDetails.ForEach(x =>
|
||
{
|
||
x.Status = TransportWarningStatus.已关闭.GetHashCode();
|
||
x.CumulativeTimeSpan = x.CalcCumulativeTimeSpan(); //tips:完成态缓存累计运输时间
|
||
});
|
||
orderTasks.ForEach(x => x.IsClosed = true);
|
||
needUpdateDetails.AddRange(orderDetails);
|
||
needUpdateGenerateTask.AddRange(orderTasks);
|
||
continue;
|
||
}
|
||
|
||
// 构建轨迹状态id+最早轨迹字典
|
||
var earliestTrackStatusTrackDict = orderTracks
|
||
.GroupBy(x => x.TrackStatusId)
|
||
.ToDictionary(x => x.Key, y => y.OrderBy(x => x.TrackTime).First());
|
||
|
||
// 以起始轨迹状态id分组处理预警生成任务
|
||
foreach (var group in orderTasks.GroupBy(x => x.StartTrackStatusId))
|
||
{
|
||
var tasks = group.ToList();
|
||
|
||
// 尝试寻找该轨迹状态的最早轨迹
|
||
var earliestTrack = earliestTrackStatusTrackDict.GetValueOrDefault(group.Key);
|
||
|
||
// 不存在起始轨迹状态轨迹?
|
||
// 关闭预警生成任务
|
||
if (earliestTrack == null)
|
||
{
|
||
tasks.ForEach(x => x.IsClosed = true);
|
||
needUpdateGenerateTask.AddRange(tasks);
|
||
continue;
|
||
}
|
||
|
||
// 存在起始状态最早轨迹?
|
||
// 如果任务上的起始轨迹发生时间有变动则修改为最新的
|
||
// todo:需要补充转化未utc0时区
|
||
foreach (var task in tasks.Where(x => x.StartTrackTime != earliestTrack.TrackTime))
|
||
{
|
||
task.StartTrackTime = earliestTrack.TrackTime;
|
||
task.StartTrackTimeZone = earliestTrack.TrackTimeZone;
|
||
task.PlanExecutionTime = earliestTrack.TrackTime.AddHours(task.StartTrackWarningTimeSpan);
|
||
needUpdateGenerateTask.Add(task);
|
||
}
|
||
}
|
||
|
||
// 以起始轨迹状态id分组处理预警明细
|
||
foreach (var group in orderDetails.GroupBy(x => x.StartTrackStatusId))
|
||
{
|
||
var details = group.ToList();
|
||
|
||
// 尝试寻找该轨迹状态的最早轨迹
|
||
var earliestTrack = earliestTrackStatusTrackDict.GetValueOrDefault(group.Key);
|
||
|
||
// 不存在起始轨迹状态轨迹?
|
||
// 1.关闭预警通知任务
|
||
// 2.预警明细状态设置为【已关闭】并重新设置累计【运输时间】
|
||
if (earliestTrack == null)
|
||
{
|
||
// 暂存要关闭预警通知任务的明细id
|
||
needCloseNotifyWarningDetailsIds.UnionWith(details.Select(x => x.Id));
|
||
|
||
// 修改预警明细
|
||
details.ForEach(x =>
|
||
{
|
||
x.Status = TransportWarningStatus.已关闭.GetHashCode();
|
||
x.CumulativeTimeSpan = x.CalcCumulativeTimeSpan(); //tips:完成态缓存累计运输时间
|
||
});
|
||
needUpdateDetails.AddRange(details);
|
||
continue;
|
||
}
|
||
|
||
// 存在起始状态最早轨迹?
|
||
// 最早轨迹如果早于预警明细中的起始轨迹时间,则修改预警明细
|
||
// todo:时区信息如果未提供则比较时间无意义!!,出现一个有时区一个无时区(后续可能强约束时区必填)?
|
||
foreach (var detail in details)
|
||
{
|
||
//保持预警明细中始终为起始状态的最早轨迹
|
||
detail.StartTrackContentId = earliestTrack.TrackContentId;
|
||
detail.StartTrackContent = earliestTrack.TrackContent;
|
||
detail.StartTrackTime = earliestTrack.TrackTime;
|
||
detail.StartTrackTimeZone = earliestTrack.TrackTimeZone;
|
||
}
|
||
|
||
needUpdateDetails.AddRange(details);
|
||
}
|
||
}
|
||
|
||
// 批量处理
|
||
if (needUpdateGenerateTask.Count > 0)
|
||
await _warningTaskRepository.BulkUpdateTransportWarningDetailsGenerateTaskAsync(needUpdateGenerateTask);
|
||
if (needUpdateDetails.Count > 0)
|
||
await _warningDetailsEsRepository.BulkUpdateTransportWarningDetailsAsync(needUpdateDetails);
|
||
if (needCloseNotifyWarningDetailsIds.Count > 0)
|
||
await _warningTaskRepository.CloseWarningNotifyTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningType == WarningRuleType.运输时效预警.GetHashCode()
|
||
&& needCloseNotifyWarningDetailsIds.Contains(x.WarningDetailsId)
|
||
);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region todo-test: 预警信号批处理-新增修改轨迹
|
||
|
||
// tips:新增修改轨迹信号,在运输处理关注最新的轨迹
|
||
public async Task TrackAddedOrUpdatedHandleAsync(List<IWarningSignal> addOrUpdateSignals)
|
||
{
|
||
// 1.查询订单
|
||
var orderIdSet = addOrUpdateSignals.Select(x => x.OrderId).ToHashSet();
|
||
// 只处理未完结并且未删除的订单
|
||
var orders = await _orderRepository.GetListAsync(x => orderIdSet.Contains(x.Id) && x.IsCompleted == false);
|
||
if (orders.Count == 0) return; //无任何订单需要处理,直接提交信号丢弃
|
||
|
||
var orderIds = orders.Select(x => x.Id).ToHashSet();
|
||
// 1.1 构建订单和轨迹状态ids字典
|
||
var orderIdTrackStatusIdsDict = addOrUpdateSignals
|
||
.Where(x => orderIds.Contains(x.OrderId)) // tips:信号过滤
|
||
.GroupBy(x => x.OrderId)
|
||
.ToDictionary(x => x.Key, y =>
|
||
y.Select(x => x.Type == WarningSignalType.轨迹新增
|
||
? ((TrackAddedSignal) x).TrackStatusId
|
||
: ((TrackUpdatedSignal) x).BeforeChangeTrack.TrackStatusId
|
||
).ToHashSet()
|
||
);
|
||
if (orderIdTrackStatusIdsDict.Count == 0) return;
|
||
|
||
// 2.查询订单所有轨迹(tips:查最新轨迹) k:订单id v:[k:轨迹状态id v:轨迹状态下最早轨迹]
|
||
var earliestTrackStatusTrackDict = await _trackDetailsEsRepository.GetTrackStatusEarliestDictAsync(orderIds);
|
||
|
||
// 3.以订单维度处理
|
||
var updateDetails = new List<TransportWarningDetailsIndex>();
|
||
var addDetailGenerateTasks = new List<TransportWarningDetailsGenerateTaskEntity>();
|
||
var updateDetailGenerateTasks = new List<TransportWarningDetailsGenerateTaskEntity>();
|
||
var closeNotifyDetailsIds = new HashSet<string>();
|
||
// todo:优化流程
|
||
foreach (var order in orders)
|
||
{
|
||
var trackStatusTrackDict = earliestTrackStatusTrackDict.GetValueOrDefault(order.Id);
|
||
if (trackStatusTrackDict == null) continue;
|
||
|
||
// 匹配规则主体,要求已经启用
|
||
var rule = await _warningRuleRepository.GetWarningRuleForEnabledAsync(order.SubSystemId, order.CustomId, WarningRuleType.运输时效预警);
|
||
if (rule == null) continue;
|
||
|
||
// 查找主体下所有场景
|
||
var scenes = await _warningRuleRepository.GetTransportSceneAsync(rule.Id);
|
||
if (scenes.Count == 0) continue;
|
||
|
||
// 查找场景关联的区域信息
|
||
var areaIds = scenes.Select(x => x.AreaId ?? 0).ToHashSet();
|
||
var ruleAreaInfosDict = await _kpiAreaRepository.GetAreaInfosDictByAreaId(areaIds);
|
||
|
||
// 选出订单命中的场景ids
|
||
var sceneBos = TransportWarningSceneBO.From(scenes);
|
||
var hitSceneIds = sceneBos
|
||
.Where(x => x.CanHitScene(order, ruleAreaInfosDict.GetValueOrDefault(x.AreaId ?? 0)))
|
||
.Select(x => x.Id)
|
||
.ToHashSet();
|
||
|
||
// 查询命中场景下的所有规则项目
|
||
var ruleItems = await _warningRuleRepository.GetTransportSceneRuleItemsAsync(x => hitSceneIds.Contains(x.TransportWarningSceneId), y => y);
|
||
if (ruleItems.Count == 0) continue;
|
||
|
||
var ruleItemIds = ruleItems.Select(x => x.Id).ToHashSet();
|
||
var ruleItemBoDict = TransportWarningSceneRuleItemBO
|
||
.From(ruleItems)
|
||
.ToDictionary(x => x.Id);
|
||
|
||
// 查订单所有跟规则相关的明细,要求(即将超时/已超时)
|
||
var warningDetails = await _warningDetailsEsRepository.GetTransportWarningDetailsAsync(new GetTransportWarningDetailsVO
|
||
{
|
||
RuleItemIds = ruleItemIds,
|
||
OrderIds = new HashSet<int>(1)
|
||
{
|
||
order.Id
|
||
},
|
||
});
|
||
|
||
// 处理【通知阶段】的预警明细
|
||
if (warningDetails.Count > 0)
|
||
{
|
||
var (needUpdateDetails, needCloseNotifyDetailsIds) = HandleExistsDetails(warningDetails, ruleItemBoDict, trackStatusTrackDict);
|
||
updateDetails.AddRange(needUpdateDetails);
|
||
closeNotifyDetailsIds.UnionWith(needCloseNotifyDetailsIds);
|
||
}
|
||
|
||
// 查订单所有跟规则相关的明细生成任务(未来可能会执行),要求(未关闭)
|
||
var orderTasks = await _warningTaskRepository.GetTransportWarningDetailsGenerateTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.OrderId == order.Id
|
||
&& ruleItemIds.Contains(x.WarningSceneRuleItemId)
|
||
);
|
||
|
||
// 处理【预警明细生成就绪阶段】预警明细生成任务
|
||
var (updateTasks, addTasks) = HandleDetailGenerateTask(order, orderTasks, warningDetails, ruleItemBoDict, trackStatusTrackDict);
|
||
addDetailGenerateTasks.AddRange(addTasks);
|
||
updateDetailGenerateTasks.AddRange(updateTasks);
|
||
}
|
||
|
||
// 4.批量操作
|
||
if (closeNotifyDetailsIds.Count > 0)
|
||
{
|
||
await _warningTaskRepository.CloseWarningNotifyTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningType == WarningRuleType.运输时效预警.GetHashCode()
|
||
&& closeNotifyDetailsIds.Contains(x.WarningDetailsId)
|
||
);
|
||
}
|
||
|
||
if (updateDetails.Count > 0)
|
||
await _warningDetailsEsRepository.BulkUpdateTransportWarningDetailsAsync(updateDetails);
|
||
if (addDetailGenerateTasks.Count > 0)
|
||
await _warningTaskRepository.BulkInsertTransportWarningDetailsGenerateTaskAsync(addDetailGenerateTasks);
|
||
if (updateDetailGenerateTasks.Count > 0)
|
||
await _warningTaskRepository.BulkUpdateTransportWarningDetailsGenerateTaskAsync(updateDetailGenerateTasks);
|
||
}
|
||
|
||
// 处理【通知阶段】的预警明细
|
||
// 起始轨迹状态轨迹变更(存在起始,不存在截止)
|
||
// 收到了截止轨迹(存在起始,存在截止)
|
||
private (List<TransportWarningDetailsIndex> UpdateDeatils, HashSet<string> CloseNotifyDetailIds) HandleExistsDetails(
|
||
List<TransportWarningDetailsIndex> warningDetails,
|
||
Dictionary<int, TransportWarningSceneRuleItemBO> ruleItemBoDict,
|
||
Dictionary<int, TrackDetailsIndex> trackStatusIdEarliestTrackDict
|
||
)
|
||
{
|
||
var updateDetails = new List<TransportWarningDetailsIndex>();
|
||
var closeNotifyDetailIds = new HashSet<string>();
|
||
|
||
var trackStatusIds = trackStatusIdEarliestTrackDict.Keys.ToHashSet();
|
||
foreach (var detail in warningDetails)
|
||
{
|
||
var ruleItem = ruleItemBoDict.GetValueOrDefault(detail.WarningSceneRuleItemId);
|
||
|
||
// 明细相关的规则的都不存在了,直接关闭
|
||
if (ruleItem == null)
|
||
{
|
||
detail.Status = TransportWarningStatus.已关闭.GetHashCode();
|
||
updateDetails.Add(detail);
|
||
closeNotifyDetailIds.Add(detail.Id);
|
||
continue;
|
||
}
|
||
|
||
// 明细上的规则校验为命中,明细的起始轨迹信息修改为起始状态最早轨迹
|
||
if (ruleItem.CanHitRule(trackStatusIds))
|
||
{
|
||
var earliestTrack = trackStatusIdEarliestTrackDict[detail.StartTrackStatusId];
|
||
// 当现有轨迹的【轨迹状态最早发生时间】早于预警明细上的,则进行修改
|
||
if (earliestTrack.GetUtc0TrackTime() < detail.GetUtc0StartTrackTime())
|
||
{
|
||
detail.StartTrackTime = earliestTrack.TrackTime;
|
||
detail.StartTrackTimeZone = earliestTrack.TrackTimeZone;
|
||
detail.StartTrackContentId = earliestTrack.TrackContentId;
|
||
detail.StartTrackContent = earliestTrack.TrackContent;
|
||
updateDetails.Add(detail);
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
// 明细上的规则校验为完成,修改为完成态(使用终止轨迹状态最早的轨迹填充截止轨迹信息)
|
||
if (ruleItem.CanCompleted(trackStatusIds))
|
||
{
|
||
var startTrackStatusEarliestTrack = trackStatusIdEarliestTrackDict[detail.StartTrackStatusId];
|
||
var endTrackStatusEarliestTrack = trackStatusIdEarliestTrackDict[detail.EndTrackStatusId];
|
||
|
||
// 填充终止状态轨迹信息
|
||
detail.EndTrackContentId = endTrackStatusEarliestTrack.TrackContentId;
|
||
detail.EndTrackContent = endTrackStatusEarliestTrack.TrackContent;
|
||
detail.EndTrackTime = endTrackStatusEarliestTrack.TrackTime;
|
||
detail.EndTrackTimeZone = endTrackStatusEarliestTrack.TrackTimeZone;
|
||
|
||
// 维护状态(已完成/超时完成)
|
||
detail.Status = detail.CalcStatus(startTrackStatusEarliestTrack, endTrackStatusEarliestTrack);
|
||
|
||
// 维护累计运输时间
|
||
detail.CumulativeTimeSpan = detail.CalcCumulativeTimeSpan();
|
||
|
||
updateDetails.Add(detail);
|
||
closeNotifyDetailIds.Add(detail.Id);
|
||
continue;
|
||
}
|
||
|
||
// 规则没命中也没完成,直接关闭处理
|
||
detail.Status = TransportWarningStatus.已关闭.GetHashCode();
|
||
updateDetails.Add(detail);
|
||
closeNotifyDetailIds.Add(detail.Id);
|
||
}
|
||
|
||
return (updateDetails, closeNotifyDetailIds);
|
||
}
|
||
|
||
|
||
// 处理【预警明细生成就绪阶段】预警明细生成任务
|
||
// 规则命中(存在起始,不存在截止)
|
||
// 规则未命中(起始不存在/起始和截止都存在)
|
||
// 命中:存在任务,起始轨迹状态轨迹变更则更新任务信息,
|
||
// 命中:不存在任务,添加生成任务
|
||
// 未命中,已有的任务关闭,没有的忽略生成
|
||
private (List<TransportWarningDetailsGenerateTaskEntity> UpdateTasks, List<TransportWarningDetailsGenerateTaskEntity> AddTasks) HandleDetailGenerateTask(OrderEntity order,
|
||
List<TransportWarningDetailsGenerateTaskEntity> tasks,
|
||
List<TransportWarningDetailsIndex> warningDetails,
|
||
Dictionary<int, TransportWarningSceneRuleItemBO> ruleItemBoDict,
|
||
Dictionary<int, TrackDetailsIndex> trackStatusIdEarliestTrackDict)
|
||
{
|
||
var insertTasks = new List<TransportWarningDetailsGenerateTaskEntity>();
|
||
var updateTasks = new List<TransportWarningDetailsGenerateTaskEntity>();
|
||
var trackStatusIds = trackStatusIdEarliestTrackDict.Keys.ToHashSet();
|
||
var ruleIdTasksDict = tasks
|
||
.GroupBy(x => x.WarningSceneRuleItemId)
|
||
.ToDictionary(x => x.Key, y => y.ToList());
|
||
|
||
foreach (var (ruleId, ruleItem) in ruleItemBoDict)
|
||
{
|
||
// 命中规则
|
||
if (ruleItem.CanHitRule(trackStatusIds))
|
||
{
|
||
var task = ruleIdTasksDict.GetValueOrDefault(ruleId)?.FirstOrDefault();
|
||
var earliestTrack = trackStatusIdEarliestTrackDict[ruleItem.StartTrackStatusId];
|
||
|
||
// 不存在任务,生成任务根据最早轨迹
|
||
if (task == null)
|
||
{
|
||
// 不存在开启的预警明细,则添加一个生成任务
|
||
if (warningDetails.All(x => x.WarningSceneRuleItemId != ruleId))
|
||
insertTasks.Add(BuildDetailsGenerateTask(order, ruleItem, earliestTrack));
|
||
continue;
|
||
}
|
||
|
||
// 存在任务,根据条件判断,更新任务根据最早轨迹
|
||
// 任务起始轨迹时间变更,起始轨迹生成时间变更
|
||
if (task.StartTrackTime != earliestTrack.TrackTime
|
||
|| (Math.Abs(task.StartTrackWarningTimeSpan - ruleItem.StartTrackWarningTimeSpan) > 0.01f))
|
||
{
|
||
task.StartTrackTime = earliestTrack.TrackTime;
|
||
task.StartTrackWarningTimeSpan = ruleItem.StartTrackWarningTimeSpan;
|
||
task.PlanExecutionTime = earliestTrack.TrackTime.AddHours(ruleItem.StartTrackWarningTimeSpan);
|
||
updateTasks.Add(task);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// 未命中规则
|
||
if (ruleItem.CanNotHit(trackStatusIds))
|
||
{
|
||
var task = ruleIdTasksDict.GetValueOrDefault(ruleId)?.FirstOrDefault();
|
||
|
||
// 不存在任务忽略
|
||
if (task == null) continue;
|
||
|
||
// 存在任务需要关闭处理
|
||
task.IsClosed = true;
|
||
updateTasks.Add(task);
|
||
}
|
||
}
|
||
|
||
return (updateTasks, insertTasks);
|
||
}
|
||
|
||
private TransportWarningDetailsGenerateTaskEntity BuildDetailsGenerateTask(
|
||
OrderEntity order,
|
||
TransportWarningSceneRuleItemBO ruleItem,
|
||
TrackDetailsIndex earliestTrack
|
||
)
|
||
{
|
||
return new TransportWarningDetailsGenerateTaskEntity
|
||
{
|
||
Id = 0,
|
||
OrderId = order.Id,
|
||
WarningId = ruleItem.WarningId,
|
||
WarningSceneId = ruleItem.SceneId,
|
||
WarningSceneRuleItemId = ruleItem.Id,
|
||
StartTrackStatusId = ruleItem.StartTrackStatusId,
|
||
EndTrackStatusId = ruleItem.EndTrackStatusId,
|
||
StartTrackTime = earliestTrack.TrackTime,
|
||
StartTrackWarningTimeSpan = ruleItem.StartTrackWarningTimeSpan,
|
||
PlanExecutionTime = earliestTrack.TrackTime.AddHours(ruleItem.StartTrackWarningTimeSpan),
|
||
IsClosed = false,
|
||
CreateTime = DateTime.Now
|
||
};
|
||
}
|
||
|
||
#endregion 预警信号处理
|
||
|
||
#region todo-test: 运输预警主体关闭
|
||
|
||
public async Task WarningRuleToDisabledHandleAsync(WarningRuleEntity rule)
|
||
{
|
||
if (rule.IsEnabled) return;
|
||
if (rule.WarningType != WarningRuleType.运输时效预警.GetHashCode()) return;
|
||
|
||
// 1.预警信息状态修改为已关闭(即将超时/已超时)
|
||
await _warningDetailsEsRepository.BulkCloseTransportWarningDetailsAsync(new BulkCloseTransportWarningDetailsVO
|
||
{
|
||
WarningIds = new HashSet<int>(1)
|
||
{
|
||
rule.Id
|
||
}
|
||
});
|
||
|
||
// 2.关闭相关的预警生成任务(未关闭的)
|
||
await _warningTaskRepository.CloseTransportWarningDetailsGenerateTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningId == rule.Id
|
||
);
|
||
|
||
// 3.关闭关联的预警通知任务(未关闭的)
|
||
await _warningTaskRepository.CloseWarningNotifyTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningId == rule.Id
|
||
&& x.WarningType == WarningRuleType.运输时效预警.GetHashCode()
|
||
);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region todo-test: 规则场景变更
|
||
|
||
// tips:由于只是改动了场景,只要订单仍然命中场景,那么订单相关的预警生成任务/预警通知任务无需处理,预警明细更新部分场景信息。未命中则相关联条目全部删除
|
||
// 更新:删除不符合【场景】条件的【预警生成任务】【预警明细】【预警通知任务】,符合场景则修改预警明细的【预警规则描述】
|
||
public async Task HandleSceneChangedAsync(WarningRuleEntity rule, TransportWarningSceneBO oldScene, TransportWarningSceneBO postScene)
|
||
{
|
||
if (!CanSceneChange(oldScene, postScene))
|
||
return;
|
||
|
||
// 1.1 找到该场景下所有的预警生成任务(未关闭的)
|
||
var generateTasks = await _warningTaskRepository.GetTransportWarningDetailsGenerateTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningId == postScene.Id
|
||
&& x.WarningSceneId == postScene.Id
|
||
);
|
||
|
||
// 1.1 预警生成任务反找订单id
|
||
var orderIds = generateTasks.Select(x => x.OrderId).ToHashSet();
|
||
|
||
// 2.1 找到该场景下所有的预警明细(即将超时/已超时)
|
||
var warningDetails = await _warningDetailsEsRepository.GetTransportWarningDetailsAsync(new GetTransportWarningDetailsVO()
|
||
{
|
||
SceneIds = new HashSet<int>(1)
|
||
{
|
||
postScene.Id
|
||
},
|
||
});
|
||
|
||
// 2.2 预警明细反找订单id并合并
|
||
orderIds.UnionWith(warningDetails.Select(x => x.OrderId));
|
||
|
||
// tips:无需在通过预警通知任务反找订单,因为产生预警通知任务时预警明细已经存在!
|
||
var orders = await _orderRepository.GetListAsync(x => orderIds.Contains(x.Id) && x.IsCompleted == false);
|
||
if (orders.Count == 0)
|
||
return;
|
||
|
||
// 3.重新验证订单是否命中新场景
|
||
var areas = postScene.AreaId.HasValue
|
||
? _kpiAreaRepository.GetAreaInfosByAreaId(postScene.AreaId.Value)
|
||
: new List<KpiAreaInfo>();
|
||
var noHitSceneOrderIds = new HashSet<int>();
|
||
var reHitSceneOrderIds = new HashSet<int>();
|
||
foreach (var order in orders)
|
||
{
|
||
if (postScene.CanHitScene(order, areas))
|
||
reHitSceneOrderIds.Add(order.Id);
|
||
else
|
||
noHitSceneOrderIds.Add(order.Id);
|
||
}
|
||
|
||
// 4.仍然命中场景的订单需要修改明细中的内容(预警名称,预警规则描述)
|
||
var needUpdateDetails = new List<TransportWarningDetailsIndex>();
|
||
if (reHitSceneOrderIds.Count > 0)
|
||
{
|
||
// 明细->规则
|
||
var updateDetails = warningDetails.Where(x => reHitSceneOrderIds.Contains(x.OrderId)).ToList();
|
||
var updateRuleItemIds = updateDetails.Select(x => x.WarningSceneRuleItemId).ToHashSet();
|
||
var updateRuleItems = await _warningRuleRepository.GetTransportSceneRuleItemsAsync(
|
||
x => updateRuleItemIds.Contains(x.Id),
|
||
y => y);
|
||
var ruleItemDict = updateRuleItems.ToDictionary(x => x.Id);
|
||
|
||
// 场景->区域
|
||
var ruleArea = postScene.AreaId.HasValue
|
||
? await _kpiAreaRepository.GetFirstAsync(x => x.Id == postScene.AreaId.Value)
|
||
: null;
|
||
foreach (var detail in updateDetails)
|
||
{
|
||
var ruleItem = ruleItemDict[detail.WarningSceneRuleItemId];
|
||
detail.WarningName = rule.Name;
|
||
detail.WarningRuleDescription = detail.GetWarningRuleDescription(postScene.ToEntity(), ruleItem, ruleArea);
|
||
needUpdateDetails.Add(detail);
|
||
}
|
||
}
|
||
|
||
// 更新预警明细
|
||
if (needUpdateDetails.Count > 0)
|
||
await _warningDetailsEsRepository.BulkUpdateTransportWarningDetailsAsync(needUpdateDetails);
|
||
|
||
if (noHitSceneOrderIds.Count == 0)
|
||
return;
|
||
|
||
// 5.关闭不符合场景的【预警明细】【预警生成任务】【预警通知任务】
|
||
// 预警明细状态修改为已关闭(即将超时/已超时)
|
||
await _warningDetailsEsRepository.BulkCloseTransportWarningDetailsAsync(new BulkCloseTransportWarningDetailsVO
|
||
{
|
||
SceneIds = new HashSet<int>(1)
|
||
{
|
||
postScene.Id
|
||
},
|
||
OrderIds = noHitSceneOrderIds
|
||
});
|
||
|
||
// 关闭运输预警生成任务(未关闭的)
|
||
await _warningTaskRepository.CloseTransportWarningDetailsGenerateTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningSceneId == postScene.Id
|
||
&& noHitSceneOrderIds.Contains(x.OrderId)
|
||
);
|
||
|
||
// 关闭预警通知任务(未关闭的)
|
||
await _warningTaskRepository.CloseWarningNotifyTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningId == postScene.WarningId
|
||
&& x.WarningType == WarningRuleType.运输时效预警.GetHashCode()
|
||
&& x.AuxiliaryId == postScene.Id.ToString() //tips:通过一个辅助字段才增加对场景的匹配
|
||
&& noHitSceneOrderIds.Contains(x.OrderId)
|
||
);
|
||
}
|
||
|
||
public async Task HandleSceneDeletedAsync(WarningRuleEntity rule, TransportWarningSceneBO scene)
|
||
{
|
||
// 1.预警信息状态修改为已关闭(即将超时/已超时)
|
||
await _warningDetailsEsRepository.BulkCloseTransportWarningDetailsAsync(new BulkCloseTransportWarningDetailsVO
|
||
{
|
||
SceneIds = new HashSet<int>(1)
|
||
{
|
||
scene.Id
|
||
},
|
||
});
|
||
|
||
// 2.关闭相关的预警生成任务(未关闭的)
|
||
await _warningTaskRepository.CloseTransportWarningDetailsGenerateTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningId == rule.Id
|
||
&& x.WarningSceneId == scene.Id
|
||
);
|
||
|
||
// 3.关闭关联的预警通知任务(未关闭的)
|
||
await _warningTaskRepository.CloseWarningNotifyTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningId == rule.Id
|
||
&& x.AuxiliaryId == scene.Id.ToString()
|
||
&& x.WarningType == WarningRuleType.运输时效预警.GetHashCode()
|
||
);
|
||
}
|
||
|
||
private bool CanSceneChange(TransportWarningSceneBO oldScene, TransportWarningSceneBO postScene)
|
||
{
|
||
return oldScene.CargoType != postScene.CargoType || //多个 普货,危险品
|
||
oldScene.Months != postScene.Months || //多个 约束提交的月份组从小到大 1/3/4,不能出现 4/1/3这种 (如果出现则SequenceEqual)
|
||
oldScene.AreaId != postScene.AreaId || // 单一,area1
|
||
oldScene.IsTransit != postScene.IsTransit || //单一,true/false
|
||
oldScene.LeavePort != postScene.LeavePort || //多个
|
||
oldScene.ProductCode != postScene.ProductCode; //多个
|
||
}
|
||
|
||
#endregion todo: 规则场景变更
|
||
|
||
#region todo-test: 规则项目变更
|
||
|
||
public async Task HandleEditAsync(WarningRuleEntity rule, TransportWarningSceneBO scene, List<TransportWarningSceneRuleItemBO> postRuleItems)
|
||
{
|
||
var addRuleItems = postRuleItems.Where(x => x.Id == 0).ToList();
|
||
|
||
// 1.找到之前的配置项目
|
||
var entities = await _warningRuleRepository.GetTransportSceneRuleItemsAsync(rule.Id, scene.Id);
|
||
var oldRuleItems = TransportWarningSceneRuleItemBO.From(entities);
|
||
// 1.1之前的配置数量为0,做只新增处理
|
||
if (oldRuleItems.Count == 0)
|
||
{
|
||
if (addRuleItems.Count > 0)
|
||
await OnlyAddHandleAsync(rule, scene, addRuleItems);
|
||
return;
|
||
}
|
||
|
||
// 1.2之前的配置数量不为0,提交的数量为0,做只删除处理
|
||
if (oldRuleItems.Count > 0 && postRuleItems.Count == 0)
|
||
{
|
||
await OnlyDeleteHandleAsync(rule, scene, oldRuleItems);
|
||
return;
|
||
}
|
||
|
||
// 可能同时存在 新增/修改/修改中需要关注的/删除
|
||
var updateRuleItems = new List<TransportWarningSceneRuleItemBO>();
|
||
var updateFollowRuleItems = new List<TransportWarningSceneRuleItemBO>();
|
||
var deleteRuleItems = new List<TransportWarningSceneRuleItemBO>();
|
||
|
||
// 2.差异化比较
|
||
// 2.1构建配置id字段
|
||
var oldRuleIdDict = oldRuleItems.ToDictionary(x => x.Id);
|
||
var postRuleIdDict = postRuleItems
|
||
.Except(addRuleItems) //tips:忽略新增的规则条目
|
||
.ToDictionary(x => x.Id);
|
||
var ruleItemIds = oldRuleIdDict.Keys.Concat(postRuleIdDict.Keys).ToHashSet();
|
||
// 2.2比较
|
||
foreach (var ruleItemId in ruleItemIds)
|
||
{
|
||
var oldRule = oldRuleIdDict.GetValueOrDefault(ruleItemId);
|
||
var postRule = postRuleIdDict.GetValueOrDefault(ruleItemId);
|
||
if (oldRule != null && postRule != null)
|
||
{
|
||
//规则属性变更,(起/止状态,时效变更)
|
||
var (follow, change) = CanNeedFollowAndChange(oldRule, postRule);
|
||
if (follow) updateFollowRuleItems.Add(postRule); //需要关注的配置
|
||
if (change) updateRuleItems.Add(postRule); //更新
|
||
continue;
|
||
}
|
||
|
||
//规则条目被删除
|
||
if (oldRule != null && postRule == null)
|
||
deleteRuleItems.Add(oldRule); //删除
|
||
}
|
||
|
||
// 3.处理变更
|
||
await HandleRuleItemsChangeAsync(rule, scene, addRuleItems, updateRuleItems, updateFollowRuleItems, deleteRuleItems);
|
||
}
|
||
|
||
private async Task OnlyAddHandleAsync(WarningRuleEntity rule, TransportWarningSceneBO scene, List<TransportWarningSceneRuleItemBO> bos)
|
||
{
|
||
if (bos.Count == 0) return;
|
||
var entities = TransportWarningSceneRuleItemBO.ToEntities(bos);
|
||
await _warningRuleRepository.BulkInsertTransportSceneRuleItemsAsync(entities);
|
||
// 更新规则主体版本号
|
||
await _warningRuleRepository.UpdateVersionAsync(rule);
|
||
}
|
||
|
||
private async Task OnlyDeleteHandleAsync(WarningRuleEntity rule, TransportWarningSceneBO scene, List<TransportWarningSceneRuleItemBO> bos)
|
||
{
|
||
var ruleItemIds = bos.Select(x => x.Id).ToHashSet();
|
||
await _warningRuleRepository.DeleteTransportSceneRuleItemsAsync(ruleItemIds);
|
||
|
||
// 更新规则主体版本号
|
||
await _warningRuleRepository.UpdateVersionAsync(rule);
|
||
|
||
// 处理规则删除
|
||
await HandleDeleteAsync(ruleItemIds);
|
||
}
|
||
|
||
// todo:需要优化
|
||
private async Task HandleRuleItemsChangeAsync(
|
||
WarningRuleEntity rule,
|
||
TransportWarningSceneBO scene,
|
||
List<TransportWarningSceneRuleItemBO> addRuleItems,
|
||
List<TransportWarningSceneRuleItemBO> updateRuleItems,
|
||
List<TransportWarningSceneRuleItemBO> updateFollowRuleItems,
|
||
List<TransportWarningSceneRuleItemBO> deleteRuleItems
|
||
)
|
||
{
|
||
// 新增/修改/删除 实体,稍后做处理
|
||
if (addRuleItems.Count > 0)
|
||
{
|
||
var addEntities = TransportWarningSceneRuleItemBO.ToEntities(addRuleItems);
|
||
await _warningRuleRepository.BulkInsertTransportSceneRuleItemsAsync(addEntities);
|
||
}
|
||
|
||
if (updateRuleItems.Count > 0)
|
||
{
|
||
var updateEntities = TransportWarningSceneRuleItemBO.ToEntities(updateRuleItems);
|
||
await _warningRuleRepository.BulkUpdateTransportSceneRuleItemsAsync(updateEntities);
|
||
}
|
||
|
||
var deleteRuleItemIds = deleteRuleItems.Select(x => x.Id).ToHashSet();
|
||
if (deleteRuleItemIds.Count > 0)
|
||
await _warningRuleRepository.DeleteTransportSceneRuleItemsAsync(deleteRuleItemIds);
|
||
|
||
// 不存在更新需要关注的,也不存在删除的
|
||
if (updateFollowRuleItems.Count == 0 && deleteRuleItemIds.Count == 0) return;
|
||
|
||
// 只存在删除的规则项目,直接关闭处理返回即可
|
||
if (updateFollowRuleItems.Count == 0)
|
||
{
|
||
// 关闭相关的预警生成任务(未关闭的)
|
||
await _warningTaskRepository.CloseTransportWarningDetailsGenerateTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningId == rule.Id
|
||
&& deleteRuleItemIds.Contains(x.WarningSceneRuleItemId)
|
||
);
|
||
|
||
// 关闭关联的预警通知任务(未关闭的)
|
||
await _warningTaskRepository.CloseWarningNotifyTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningId == rule.Id
|
||
&& x.WarningType == WarningRuleType.运输时效预警.GetHashCode()
|
||
&& deleteRuleItemIds.Contains(x.WarningRuleItemId)
|
||
);
|
||
return;
|
||
}
|
||
|
||
// 1.查询变更配置相关的预警生成任务(未关闭的)
|
||
var updateFollowRuleItemIds = updateFollowRuleItems.Select(x => x.Id).ToHashSet();
|
||
var generateTasks = await _warningTaskRepository.GetTransportWarningDetailsGenerateTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningId == scene.WarningId
|
||
&& updateFollowRuleItemIds.Contains(x.WarningSceneRuleItemId)
|
||
);
|
||
|
||
// 2.查询变更配置相关的预警通知任务(未关闭的)
|
||
var warningNotifyTasks = await _warningTaskRepository.GetWarningNotifyTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningId == rule.Id
|
||
&& x.WarningType == WarningRuleType.运输时效预警.GetHashCode()
|
||
&& updateFollowRuleItemIds.Contains(x.WarningRuleItemId)
|
||
);
|
||
|
||
// 3.查询变更配置相关的预警明细(即将超时/已超时)
|
||
var warningDetails = await _warningDetailsEsRepository.GetTransportWarningDetailsAsync(new GetTransportWarningDetailsVO
|
||
{
|
||
RuleItemIds = updateFollowRuleItemIds,
|
||
});
|
||
|
||
// 构建处理快
|
||
var handleBlocks = new List<HandleBlock>(generateTasks.Count + warningNotifyTasks.Count + warningDetails.Count);
|
||
// 使用【预警明细】【预警生成任务】【预警通知任务】填充处理块
|
||
handleBlocks.AddRange(generateTasks.Select(x => new HandleBlock(x.OrderId, x.WarningSceneRuleItemId, x)));
|
||
handleBlocks.AddRange(warningNotifyTasks.Select(x => new HandleBlock(x.OrderId, x.WarningRuleItemId, x)));
|
||
handleBlocks.AddRange(warningDetails.Select(x => new HandleBlock(x.OrderId, x.WarningSceneRuleItemId, x)));
|
||
|
||
// tips:无需在通过预警通知任务反找订单,因为产生预警通知任务时预警明细已经存在!
|
||
// 查询订单
|
||
var orderIds = handleBlocks.Select(x => x.OrderId).ToHashSet();
|
||
var orders = await _orderRepository.GetListAsync(x => orderIds.Contains(x.Id) && x.IsCompleted == false);
|
||
orderIds = orders.Select(x => x.Id).ToHashSet();
|
||
if (orderIds.Count == 0) return;
|
||
|
||
// 查询这些订单相关的轨迹
|
||
var allOrderTracks = await _trackDetailsEsRepository.GetListAsync(orderIds.ToArray());
|
||
var orderTrackStatusIdsDict = allOrderTracks.GroupBy(x => x.OrderId)
|
||
.ToDictionary(x => x.Key, y => y.ToList());
|
||
|
||
// 3.检查是否重新命中规则
|
||
var ruleArea = scene.AreaId.HasValue
|
||
? await _kpiAreaRepository.GetFirstAsync(x => x.Id == scene.AreaId.Value)
|
||
: null;
|
||
var updateFollowRuleItemIdDict = updateFollowRuleItems.ToDictionary(x => x.Id);
|
||
foreach (var group in handleBlocks.GroupBy(x => (x.OrderId, x.RuleItemId)))
|
||
{
|
||
var key = group.Key;
|
||
var ruleItem = updateFollowRuleItemIdDict[key.RuleItemId];
|
||
var orderTracks = orderTrackStatusIdsDict.GetValueOrDefault(key.OrderId);
|
||
if (orderTracks == null) continue; // tips:该订单不存在任何轨迹
|
||
var orderStatusIds = orderTracks
|
||
.Select(x => x.TrackStatusId)
|
||
.ToHashSet();
|
||
|
||
//存在起始,不存在截止,视为命中
|
||
var reHit = ruleItem.CanHitRule(orderStatusIds);
|
||
|
||
// 命中规则的一定存在起始轨迹,选出最早轨迹
|
||
var earliestTrack = orderTracks
|
||
.Where(x => x.TrackStatusId == ruleItem.StartTrackStatusId)
|
||
.OrderBy(x => x.TrackTime)
|
||
.FirstOrDefault();
|
||
|
||
foreach (var block in group)
|
||
{
|
||
block.ReHitRule = reHit;
|
||
if (reHit && block.Type == HandleType.Detail) //重新命中的轨迹,需要更新预警信息中的规则描述,以及轨迹信息(起始)
|
||
{
|
||
// 配置相关信息
|
||
block.Detail.WarningName = rule.Name;
|
||
block.Detail.WarningRuleDescription = block.Detail.GetWarningRuleDescription(scene.ToEntity(), ruleItem.ToEntity(), ruleArea);
|
||
block.Detail.TransportTimeSpan = ruleItem.TransportTimeSpan;
|
||
block.Detail.StartTrackWarningTimeSpan = ruleItem.StartTrackWarningTimeSpan;
|
||
// 起始轨迹相关信息
|
||
block.Detail.StartTrackStatusId = earliestTrack!.TrackStatusId;
|
||
block.Detail.StartTrackStatus = earliestTrack.TrackStatus;
|
||
block.Detail.StartTrackContentId = earliestTrack.TrackContentId;
|
||
block.Detail.StartTrackContent = earliestTrack.TrackContent;
|
||
block.Detail.StartTrackTime = earliestTrack.TrackTime; //用于更新累计运输时间
|
||
block.Detail.StartTrackTimeZone = earliestTrack.TrackTimeZone;
|
||
continue;
|
||
}
|
||
|
||
// tips:更改后直接引发任务进行,等待下一轮任务扫描就会注意到
|
||
if (reHit && block.Type == HandleType.GenerateTask) //重新命中的轨迹,需要更新预警生成任务
|
||
{
|
||
// 更新生成任务当中的相关信息
|
||
block.GenerateTask.StartTrackStatusId = earliestTrack!.TrackStatusId;
|
||
block.GenerateTask.EndTrackStatusId = ruleItem.EndTrackStatusId;
|
||
block.GenerateTask.StartTrackTime = earliestTrack.TrackTime;
|
||
block.GenerateTask.StartTrackTimeZone = earliestTrack.TrackTimeZone;
|
||
block.GenerateTask.StartTrackWarningTimeSpan = ruleItem.StartTrackWarningTimeSpan;
|
||
block.GenerateTask.PlanExecutionTime = earliestTrack.TrackTime.AddHours(ruleItem.StartTrackWarningTimeSpan);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 重新命中规则的处理块
|
||
var reHitRuleBlocks = handleBlocks.Where(x => x.ReHitRule).ToList();
|
||
|
||
// 3.1 更新预警明细(重新命中规则)
|
||
var updateWarningDetails = reHitRuleBlocks
|
||
.Where(x => x.Type == HandleType.Detail)
|
||
.Select(x => x.Detail)
|
||
.ToList();
|
||
if (reHitRuleBlocks.Count > 0)
|
||
await _warningDetailsEsRepository.BulkUpdateTransportWarningDetailsAsync(updateWarningDetails);
|
||
|
||
// 3.2 更新预警生成任务(重新命中规则)
|
||
var updateGenerateTasks = reHitRuleBlocks
|
||
.Where(x => x.Type == HandleType.GenerateTask)
|
||
.Select(x => x.GenerateTask)
|
||
.ToList();
|
||
if (updateGenerateTasks.Count > 0)
|
||
await _warningTaskRepository.BulkUpdateTransportWarningDetailsGenerateTaskAsync(updateGenerateTasks);
|
||
|
||
// 3.3 处理需要关闭的【预警明细】【预警生成任务】【预警通知任务】(未命中规则)
|
||
var typeIdsDict = handleBlocks
|
||
.Except(reHitRuleBlocks)
|
||
.GroupBy(x => x.Type)
|
||
.ToDictionary(x => x.Key, y =>
|
||
{
|
||
return y.Key switch
|
||
{
|
||
HandleType.Detail => y.Select(x => x.Detail.Id).ToHashSet(),
|
||
HandleType.GenerateTask => y.Select(x => x.GenerateTask.Id.ToString()).ToHashSet(),
|
||
HandleType.NotifyTask => y.Select(x => x.NotifyTask.Id.ToString()).ToHashSet(),
|
||
_ => throw new ArgumentOutOfRangeException()
|
||
};
|
||
});
|
||
|
||
// 4.处理删除规则的相关联的【预警明细】【预警生成任务】【预警通知任务】
|
||
// 预警信息状态修改为已关闭(即将超时/已超时)
|
||
var closeDetailIds = typeIdsDict.GetValueOrDefault(HandleType.Detail, new HashSet<string>());
|
||
if (closeDetailIds.Count > 0)
|
||
{
|
||
await _warningDetailsEsRepository.BulkCloseTransportWarningDetailsAsync(new BulkCloseTransportWarningDetailsVO
|
||
{
|
||
DetailsIds = closeDetailIds,
|
||
});
|
||
}
|
||
|
||
// 关闭相关的预警生成任务(未关闭的)
|
||
var closeGenerateTaskIds = typeIdsDict.GetValueOrDefault(HandleType.GenerateTask, new HashSet<string>());
|
||
await _warningTaskRepository.CloseTransportWarningDetailsGenerateTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& (
|
||
closeGenerateTaskIds.Contains(x.Id.ToString())
|
||
|| x.WarningId == rule.Id && deleteRuleItemIds.Contains(x.WarningSceneRuleItemId)
|
||
)
|
||
);
|
||
|
||
// 关闭关联的预警通知任务(未关闭的)
|
||
var closeNotifyTaskIds = typeIdsDict.GetValueOrDefault(HandleType.NotifyTask, new HashSet<string>());
|
||
await _warningTaskRepository.CloseWarningNotifyTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& (
|
||
closeNotifyTaskIds.Contains(x.Id.ToString())
|
||
|| x.WarningId == rule.Id
|
||
&& x.WarningType == WarningRuleType.运输时效预警.GetHashCode()
|
||
&& deleteRuleItemIds.Contains(x.WarningRuleItemId)
|
||
));
|
||
}
|
||
|
||
private async Task HandleDeleteAsync(HashSet<int> deleteRuleItemIds)
|
||
{
|
||
// 处理删除规则的相关联的【预警明细】【预警生成任务】【预警通知任务】
|
||
// 预警信息状态修改为已关闭(即将超时/已超时)
|
||
await _warningDetailsEsRepository.BulkCloseTransportWarningDetailsAsync(new BulkCloseTransportWarningDetailsVO
|
||
{
|
||
RuleItemIds = deleteRuleItemIds,
|
||
});
|
||
|
||
// 关闭相关的预警生成任务(未关闭的)
|
||
await _warningTaskRepository.CloseTransportWarningDetailsGenerateTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& deleteRuleItemIds.Contains(x.WarningSceneRuleItemId)
|
||
);
|
||
|
||
// 关闭关联的预警通知任务(未关闭的)
|
||
await _warningTaskRepository.CloseWarningNotifyTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& x.WarningType == WarningRuleType.运输时效预警.GetHashCode()
|
||
&& deleteRuleItemIds.Contains(x.WarningRuleItemId)
|
||
);
|
||
}
|
||
|
||
private static (bool follow, bool change) CanNeedFollowAndChange(TransportWarningSceneRuleItemBO oldRule, TransportWarningSceneRuleItemBO postRule)
|
||
{
|
||
var defaultResult = (true, true);
|
||
// 起始轨迹状态id
|
||
if (oldRule.StartTrackStatusId != postRule.StartTrackStatusId)
|
||
return defaultResult;
|
||
|
||
// 截止轨迹状态id
|
||
if (oldRule.EndTrackStatusId != postRule.EndTrackStatusId)
|
||
return defaultResult;
|
||
|
||
// 运输预警时效
|
||
if (Math.Abs(oldRule.TransportTimeSpan - postRule.TransportTimeSpan) > 0.01f)
|
||
return defaultResult;
|
||
|
||
// 起始轨迹生成预警时间
|
||
if (Math.Abs(oldRule.StartTrackWarningTimeSpan - postRule.StartTrackWarningTimeSpan) > 0.01f)
|
||
return defaultResult;
|
||
|
||
// 通知对象
|
||
var oldNotifyUserIds = oldRule.NotifyUsers.Select(x => x.UserId).ToHashSet();
|
||
var postNotifyUserIds = postRule.NotifyUsers.Select(x => x.UserId).ToHashSet();
|
||
var hasChange = !oldNotifyUserIds.SetEquals(postNotifyUserIds);
|
||
return (false, hasChange);
|
||
}
|
||
|
||
private enum HandleType
|
||
{
|
||
Detail = 0,
|
||
GenerateTask = 1,
|
||
NotifyTask = 2
|
||
}
|
||
|
||
private sealed class HandleBlock
|
||
{
|
||
public int OrderId { get; }
|
||
public int RuleItemId { get; }
|
||
public HandleType Type { get; }
|
||
|
||
#nullable disable
|
||
public TransportWarningDetailsIndex Detail { get; }
|
||
public TransportWarningDetailsGenerateTaskEntity GenerateTask { get; }
|
||
public WarningNotifyTaskEntity NotifyTask { get; }
|
||
public bool ReHitRule { get; set; } //是否重新命中规则条目
|
||
|
||
private HandleBlock(int orderId, int ruleItemId)
|
||
{
|
||
OrderId = orderId;
|
||
RuleItemId = ruleItemId;
|
||
}
|
||
|
||
public HandleBlock(int orderId, int ruleItemId, TransportWarningDetailsIndex detail)
|
||
: this(orderId, ruleItemId)
|
||
{
|
||
Type = HandleType.Detail;
|
||
Detail = detail;
|
||
}
|
||
|
||
public HandleBlock(int orderId, int ruleItemId, TransportWarningDetailsGenerateTaskEntity task)
|
||
: this(orderId, ruleItemId)
|
||
{
|
||
Type = HandleType.GenerateTask;
|
||
GenerateTask = task;
|
||
}
|
||
|
||
public HandleBlock(int orderId, int ruleItemId, WarningNotifyTaskEntity task)
|
||
: this(orderId, ruleItemId)
|
||
{
|
||
Type = HandleType.NotifyTask;
|
||
NotifyTask = task;
|
||
}
|
||
}
|
||
|
||
#endregion todo: 规则项目变更
|
||
|
||
#region todo-test: 批量处理预警明细生成任务
|
||
|
||
public async Task HandleWarningDetailsGenerateTask(List<TransportWarningDetailsGenerateTaskEntity> tasks)
|
||
{
|
||
// 1.查询订单,tips:只处理未完结并且未删除的订单
|
||
var orderIdSet = tasks.Select(x => x.OrderId).ToHashSet();
|
||
var orders = await _orderRepository.GetListAsync(x =>
|
||
orderIdSet.Contains(x.Id)
|
||
&& x.IsCompleted == false
|
||
);
|
||
if (orders.Count == 0) return; //不存在符合条件的订单,直接全部关闭
|
||
|
||
// 构建订单id和订单字典
|
||
var orderDict = orders.ToDictionary(x => x.Id);
|
||
|
||
// 构建订单id和任务字典
|
||
var orderIdTasksDict = tasks
|
||
.GroupBy(x => x.OrderId)
|
||
.ToDictionary(x => x.Key, y => y.ToList());
|
||
|
||
// 2.查询任务相关,订单的轨迹状态的轨迹
|
||
var orderIdTrackStatusIdsDict = orderIdTasksDict
|
||
.Where(d => orderDict.ContainsKey(d.Key)) //除此之外的任务直接关闭即可
|
||
.ToDictionary(
|
||
x => x.Key,
|
||
y => y.Value.SelectMany(x => new List<int>(2)
|
||
{
|
||
x.StartTrackStatusId,
|
||
x.EndTrackStatusId
|
||
}).ToHashSet()
|
||
);
|
||
var orderTracksDict = await _trackDetailsEsRepository.GetOrderTracksDictAsync(new GetOrderTracksDictVO
|
||
{
|
||
OrderIdTracksStatusIdsDict = orderIdTrackStatusIdsDict
|
||
});
|
||
if (orderTracksDict.Count == 0) return; //不存在轨迹,直接全部关闭
|
||
|
||
// 3.查出任务相关,运输时效规则配置
|
||
// 查出启用的规则主体
|
||
var warningIds = tasks.Select(x => x.WarningId).ToHashSet();
|
||
var waningRules = await _warningRuleRepository.GetListAsync(x => x.IsEnabled && warningIds.Contains(x.Id));
|
||
if (waningRules.Count == 0) return; //不存在启用的规则主体,直接全部关闭
|
||
|
||
// 构建规则主体id和版本号字典,稍后用于填充明细的版本号
|
||
var ruleDict = waningRules.ToDictionary(x => x.Id);
|
||
|
||
// 选出任务中规则条目id,要求订单存在,主体存在并启用
|
||
var ruleItemIds = tasks
|
||
.Where(x => orderDict.ContainsKey(x.OrderId) && ruleDict.ContainsKey(x.WarningId))
|
||
.Select(x => x.WarningSceneRuleItemId)
|
||
.ToHashSet();
|
||
if (ruleItemIds.Count == 0) return;
|
||
|
||
// 查出规则配置
|
||
var ruleItems = await _warningRuleRepository.GetTransportSceneRuleItemsAsync(x => ruleItemIds.Contains(x.Id), y => y);
|
||
if (ruleItems.Count == 0) return; //不存在规则配置,直接全部关闭
|
||
|
||
// 构建规则id和规则条目字典
|
||
var ruleItemDict = ruleItems.ToDictionary(x => x.Id);
|
||
|
||
// 4.选出可以执行的任务和需要关闭的任务
|
||
var needCloseTaskIds = new HashSet<int>();
|
||
var addDetails = new List<TransportWarningDetailsIndex>();
|
||
foreach ((int orderId, List<TransportWarningDetailsGenerateTaskEntity> orderTasks) in orderIdTasksDict)
|
||
{
|
||
var order = orderDict.GetValueOrDefault(orderId);
|
||
// 订单不存在/已完结 直接关闭任务即可
|
||
if (order == null)
|
||
{
|
||
needCloseTaskIds.UnionWith(orderTasks.Select(x => x.Id));
|
||
continue;
|
||
}
|
||
|
||
// 查看任务是否满足执行条件,不满足则直接关闭即可
|
||
// 不存在任何轨迹
|
||
var tracks = orderTracksDict.GetValueOrDefault(orderId, new List<TrackDetailsIndex>(0));
|
||
if (tracks.Count == 0)
|
||
{
|
||
needCloseTaskIds.UnionWith(orderTasks.Select(x => x.Id));
|
||
continue;
|
||
}
|
||
|
||
var trackStatusIds = tracks.Select(x => x.TrackStatusId).ToHashSet();
|
||
|
||
// 筛选任务
|
||
foreach (var task in orderTasks)
|
||
{
|
||
// 不存在任务上标明的规则
|
||
var ruleItem = ruleItemDict.GetValueOrDefault(task.WarningSceneRuleItemId);
|
||
if (ruleItem == null)
|
||
{
|
||
needCloseTaskIds.Add(task.Id);
|
||
continue;
|
||
}
|
||
|
||
// 不存在起始状态/存截止轨迹状态
|
||
if (ruleItem.CanNotHit(trackStatusIds))
|
||
{
|
||
needCloseTaskIds.Add(task.Id);
|
||
continue;
|
||
}
|
||
|
||
// 生成预警明细
|
||
addDetails.Add(BuildTransportWarningDetail(order, ruleItem, tracks));
|
||
}
|
||
}
|
||
|
||
// 5.关闭明细生成任务
|
||
await _warningTaskRepository.CloseTransportWarningDetailsGenerateTaskAsync(x =>
|
||
!x.IsClosed
|
||
&& needCloseTaskIds.Contains(x.Id)
|
||
);
|
||
|
||
if (addDetails.Count == 0) return; //无需添加预警明细,和预警通知任务
|
||
|
||
// 获取截止轨迹状态id名称字典
|
||
var endTrackStatusIds = addDetails.Select(x => x.EndTrackStatusId).ToHashSet();
|
||
var trackStatusIdNameDict = await _trackStatusRepository.GetTrackStatusIdNameDictAsync(endTrackStatusIds);
|
||
|
||
// 获取场景字典
|
||
var sceneIds = addDetails.Select(x => x.WarningSceneId).ToHashSet();
|
||
var scenes = await _warningRuleRepository.GetTransportSceneAsync(x =>
|
||
sceneIds.Contains(x.Id)
|
||
);
|
||
var sceneDict = scenes.ToDictionary(x => x.Id);
|
||
|
||
// 获取区域字典
|
||
Dictionary<int, RuleAreaEntity> areaDict = null;
|
||
var areaIds = scenes.Where(x => x.AreaId.HasValue).Select(x => x.AreaId.Value!).ToHashSet();
|
||
if (areaIds.Count > 0)
|
||
{
|
||
var areas = await _kpiAreaRepository.GetListAsync(x => areaIds.Contains(x.Id));
|
||
areaDict = areas.ToDictionary(x => x.Id);
|
||
}
|
||
|
||
// 6.生成预警明细,手动赋予EsId,规则版本号,截止轨迹状态名称
|
||
addDetails.ForEach(x =>
|
||
{
|
||
x.Id = Guid.NewGuid().ToString("N");
|
||
x.EndTrackStatus = trackStatusIdNameDict.GetValueOrDefault(x.EndTrackStatusId)
|
||
?? $"不存在id:[{x.EndTrackStatusId}]轨迹状态";
|
||
|
||
var rule = ruleDict[x.WarningId]; //一定存在
|
||
var scene = sceneDict[x.WarningSceneId]; //一定存在
|
||
x.Version = rule.Version;
|
||
x.WarningName = rule.Name;
|
||
x.WarningRuleDescription = x.GetWarningRuleDescription(scene, ruleItemDict[x.WarningSceneRuleItemId], areaDict?.GetValueOrDefault(scene.AreaId ?? 0));
|
||
});
|
||
await _warningDetailsEsRepository.BulkInsertTransportWarningDetailsAsync(addDetails);
|
||
|
||
// 7.生成预警通知任务
|
||
var notifyTasks = BuildWarningNotifyTask(addDetails);
|
||
await _warningTaskRepository.BulkInsertWarningNotifyTaskAsync(notifyTasks);
|
||
}
|
||
|
||
|
||
private TransportWarningDetailsIndex BuildTransportWarningDetail(
|
||
OrderEntity order,
|
||
TransportWarningSceneRuleItemEntity ruleItem,
|
||
List<TrackDetailsIndex> tracks
|
||
)
|
||
{
|
||
// 查询起始轨迹状态最早轨迹,经过验证后确保了一定存在
|
||
var track = tracks
|
||
.Where(x => x.TrackStatusId == ruleItem.StartTrackStatusId)
|
||
.OrderBy(x => x.TrackTime)
|
||
.First();
|
||
|
||
var detail = new TransportWarningDetailsIndex
|
||
{
|
||
WarningId = ruleItem.WarningId,
|
||
WarningSceneId = ruleItem.TransportWarningSceneId,
|
||
WarningSceneRuleItemId = ruleItem.Id,
|
||
//Version = Version, // 规则版本号,稍后填充
|
||
|
||
TransportTimeSpan = ruleItem.TransportTimeSpan,
|
||
StartTrackWarningTimeSpan = ruleItem.StartTrackWarningTimeSpan,
|
||
// NotifyModes = ruleItem.NotifyModes,
|
||
NotifyUsers = ruleItem.NotifyUsers,
|
||
|
||
//订单信息
|
||
SubSystemId = order.SubSystemId,
|
||
CustomId = order.CustomId,
|
||
OrderId = order.Id,
|
||
OrderNo = order.OrderNo,
|
||
OrderType = order.OrderType,
|
||
|
||
//轨迹信息(起始)
|
||
StartTrackStatusId = track.TrackStatusId,
|
||
StartTrackStatus = track.TrackStatus,
|
||
StartTrackContentId = track.TrackContentId,
|
||
StartTrackContent = track.TrackContent,
|
||
StartTrackTime = track.TrackTime,
|
||
StartTrackTimeZone = track.TrackTimeZone,
|
||
|
||
//轨迹信息(截止)还未产生,填充信息有限
|
||
EndTrackStatusId = ruleItem.EndTrackStatusId,
|
||
// EndTrackStatus = ruleItem.EndTrackStatus, //轨迹状态名称,稍后填充
|
||
|
||
//初始状态为即将超时
|
||
Status = TransportWarningStatus.即将超时.GetHashCode(),
|
||
|
||
//审计属性
|
||
CreateTime = DateTime.Now,
|
||
};
|
||
|
||
return detail;
|
||
}
|
||
|
||
private List<WarningNotifyTaskEntity> BuildWarningNotifyTask(List<TransportWarningDetailsIndex> details)
|
||
{
|
||
return details.Select(x => new WarningNotifyTaskEntity
|
||
{
|
||
OrderId = x.OrderId,
|
||
WarningId = x.WarningId,
|
||
WarningRuleItemId = x.WarningSceneRuleItemId,
|
||
WarningDetailsId = x.Id,
|
||
WarningType = WarningRuleType.运输时效预警.GetHashCode(),
|
||
NotifyUsers = x.NotifyUsers,
|
||
// tips:填入场景id作为辅助标识
|
||
AuxiliaryId = x.WarningSceneId.ToString()
|
||
})
|
||
.ToList();
|
||
}
|
||
|
||
#endregion
|
||
}
|