track/src/SJZY.Track.Application/Services/Warning/Handlers/TransportWarningHandler.cs
2025-04-01 10:42:08 +08:00

1298 lines
59 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 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
}