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 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(); var needUpdateGenerateTask = new List(); var needCloseNotifyWarningDetailsIds = new HashSet(); // 以订单的维度处理预警明细 foreach (var orderId in orderIds) { var orderDetails = orderDetailsDict.GetValueOrDefault(orderId, new List(0)); var orderTracks = orderTracksDict.GetValueOrDefault(orderId, new List(0)); var orderTasks = oderGenerateTaskDict.GetValueOrDefault(orderId, new List(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 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(); var addDetailGenerateTasks = new List(); var updateDetailGenerateTasks = new List(); var closeNotifyDetailsIds = new HashSet(); // 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(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 UpdateDeatils, HashSet CloseNotifyDetailIds) HandleExistsDetails( List warningDetails, Dictionary ruleItemBoDict, Dictionary trackStatusIdEarliestTrackDict ) { var updateDetails = new List(); var closeNotifyDetailIds = new HashSet(); 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]; // 当现有轨迹的【轨迹状态最早发生时间】早于预警明细上的,则进行修改 //todo:需要关注时区 if (earliestTrack.TrackTime < detail.StartTrackTime) { 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]; // 修改为完成态 if (detail.CalcStatus() == TransportWarningStatus.已超时.GetHashCode()) detail.Status = TransportWarningStatus.超时完成.GetHashCode(); if (detail.Status == TransportWarningStatus.即将超时.GetHashCode()) detail.Status = TransportWarningStatus.已完成.GetHashCode(); // 填充终止状态轨迹信息 detail.EndTrackContentId = endTrackStatusEarliestTrack.TrackContentId; detail.EndTrackContent = endTrackStatusEarliestTrack.TrackContent; detail.EndTrackTime = endTrackStatusEarliestTrack.TrackTime; detail.EndTrackTimeZone = endTrackStatusEarliestTrack.TrackTimeZone; // 维护累计运输时间(由于变更为终止态,此时为最后一次变更) detail.CumulativeTimeSpan = detail.CalcCumulativeTimeSpan(); // 默认判断超时完成是根据当前时间判断的,但可能截止轨迹发生时间在时效要求范围内,故需要二次校验 if (detail.Status == TransportWarningStatus.超时完成.GetHashCode()) { var starTrackTime = startTrackStatusEarliestTrack.GetUtc0TrackTime(); var endTrackTime = endTrackStatusEarliestTrack.GetUtc0TrackTime(); // 如果添加的截止轨迹最早轨迹发生时间,小于起始轨迹状态最早轨迹发生时间+时效则认为是正常完成 // tips: 可能出现的原因,轨迹已经产生但没有及时加到系统中去 if (endTrackTime < starTrackTime.AddHours(detail.TransportTimeSpan)) detail.Status = TransportWarningStatus.已完成.GetHashCode(); } updateDetails.Add(detail); closeNotifyDetailIds.Add(detail.Id); continue; } // 规则没命中也没完成,直接关闭处理 detail.Status = TransportWarningStatus.已关闭.GetHashCode(); updateDetails.Add(detail); closeNotifyDetailIds.Add(detail.Id); } return (updateDetails, closeNotifyDetailIds); } // 处理【预警明细生成就绪阶段】预警明细生成任务 // 规则命中(存在起始,不存在截止) // 规则未命中(起始不存在/起始和截止都存在) // 命中:存在任务,起始轨迹状态轨迹变更则更新任务信息, // 命中:不存在任务,添加生成任务 // 未命中,已有的任务关闭,没有的忽略生成 private (List UpdateTasks, List AddTasks) HandleDetailGenerateTask(OrderEntity order, List tasks, List warningDetails, Dictionary ruleItemBoDict, Dictionary trackStatusIdEarliestTrackDict) { var insertTasks = new List(); var updateTasks = new List(); 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)) { // todo: 出现一个订单,一个规则,多个任务? 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(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(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(); var noHitSceneOrderIds = new HashSet(); var reHitSceneOrderIds = new HashSet(); foreach (var order in orders) { if (postScene.CanHitScene(order, areas)) reHitSceneOrderIds.Add(order.Id); else noHitSceneOrderIds.Add(order.Id); } // 4.仍然命中场景的订单需要修改明细中的内容(预警名称,预警规则描述) var needUpdateDetails = new List(); 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(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(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 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(); var updateFollowRuleItems = new List(); var deleteRuleItems = new List(); // 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 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 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 addRuleItems, List updateRuleItems, List updateFollowRuleItems, List 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(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()); if (closeDetailIds.Count > 0) { await _warningDetailsEsRepository.BulkCloseTransportWarningDetailsAsync(new BulkCloseTransportWarningDetailsVO { DetailsIds = closeDetailIds, }); } // 关闭相关的预警生成任务(未关闭的) var closeGenerateTaskIds = typeIdsDict.GetValueOrDefault(HandleType.GenerateTask, new HashSet()); 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()); 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 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 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(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(); var addDetails = new List(); foreach ((int orderId, List 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(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 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 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 BuildWarningNotifyTask(List 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 }