using Fake.EventBus; using Fake.EventBus.Distributed; using Fake.UnitOfWork; using InterfaceForward.Domain.Shared.Helpers; using TrackCenter.Application.Dtos.ProvideServices; using TrackCenter.Application.Events; using TrackCenter.Application.Kafka; using TrackCenter.Application.Queries; using TrackCenter.Application.Services.Broadcasting; using TrackCenter.Application.Services.Warning.Signals; using TrackCenter.Domain.Aggregates.OrderAggregate; using TrackCenter.Domain.Aggregates.TrackDetailsAggregate; using TrackCenter.Repository.Aggregates.OrderAggregate; namespace TrackCenter.Application.Messaging; public class TrackDeleteEventHandler( ProvideQueries provideQueries, IOrderRepository orderRepository, IDistributedEventBus eventBus, ITrackDetailsEsRepository trackDetailsEsRepository, IOrderOperationRecordEsRepository orderOperationRecordEsRepository, BroadcastingService broadcastingService, WarningSignalPublisher warningSignalPublisher) : IEventHandler, ITransientDependency { [UnitOfWork(IsTransactional = true)] public virtual async Task HandleAsync(TrackDeleteEvent @event, CancellationToken cancellationToken = default) { if (!CanDelete(@event)) return; // 处理删除轨迹 await HandleDeleteTrackAsync(@event); // 根据订单关联关系异步向下刷(异步) await DownByOrderLink(@event); } private async Task DownByOrderLink(TrackDeleteEvent @event) { var linkedOrders = await orderRepository.GetLinkedOrdersAsync(@event.OrderIds); foreach (var orderId in @event.OrderIds) { foreach (var group in linkedOrders.Where(x => x.OwnerOrderId == orderId).GroupBy(x => x.AppKey)) { var nextEvent = @event.DeepCopy(); nextEvent.Visited.AddRange(@event.OrderIds); nextEvent.TrackIdentifiers = @event.TrackIdentifiers; nextEvent.AppKey = group.Key; // 不同应用需要做内容映射 if (@event.AppKey != nextEvent.AppKey) { var codeMaps = await provideQueries.GetTrackCodeMapsAsync( @event.AppKey, nextEvent.AppKey, @event.TrackIdentifiers.Select(x => x.TrackCode).ToArray() ); if (codeMaps.Count == 0) { Log.Warning($"订单跨应用,未配置内容映射,请联系管理员:{@event}", true); continue; } //映射轨迹code foreach (var trackIdentity in nextEvent.TrackIdentifiers) { var match = codeMaps.FirstOrDefault(x => x.MapTrackContentCode == trackIdentity.TrackCode); if (match == null) continue; trackIdentity.TrackCode = match.TrackContentCode; } } foreach (var subOrderIds in group.Select(x => x.OrderId).Chunk(GlobalConst.ChunkSize)) { var subEvent = nextEvent.DeepCopy(); subEvent.OrderIds = subOrderIds; // 关联单继续走删除链路(原 CAP 误发到 Upsert topic,此处修正) await eventBus.PublishAsync(subEvent); } } } } private static bool CanDelete(TrackDeleteEvent @event) { if (@event.OrderIds.Length == 0) return true; // 订单循环关联检测 var dup = @event.Visited.Intersect(@event.OrderIds).ToArray(); if (dup.Any()) { Log.Warning($"删除轨迹时,发现循环依赖:{dup.JoinAsString(",")}", true); return false; } return true; } private async Task HandleDeleteTrackAsync(TrackDeleteEvent @event) { HashSet orderIds; List oldTracks; List deleteTracks; // 优先根据EsId查询轨迹 if (@event.EsIds.Length > 0) { deleteTracks = await trackDetailsEsRepository.GetDataByIds(@event.EsIds); // 外部api获取的轨迹不允许删除 if (deleteTracks.Any(m => m.Source == (int) TrackSourceEnum.ExternalServiceProvide)) return; // 从被删除轨迹寻找订单id,稍后会更新订单主表 orderIds = deleteTracks.Select(m => m.OrderId).ToHashSet(); // 清理已经删除过的EsId,并设置用于下刷删除的其他信息 @event.EsIds = Array.Empty(); @event.OrderIds = orderIds.ToArray(); @event.TrackIdentifiers = deleteTracks.Select(x => new TrackIdentifierDto { TrackCode = x.TrackCode, TrackTime = x.TrackTime }).ToList(); // 查出订单下的所有轨迹,以供后面回退使用 oldTracks = await trackDetailsEsRepository.GetListAsync(@event.OrderIds); } else { // 根据订单查询出所有的轨迹,查所有是因为主单上需要进行回退更新 oldTracks = await trackDetailsEsRepository.GetListAsync(@event.OrderIds); // 构建轨迹标识符 var trackIds = @event.TrackIdentifiers.Select(y => $"{y.TrackCode}{y.GetUtc0TrackTime():G}").ToHashSet(); // 选出要删除的轨迹 deleteTracks = oldTracks .Where(x => trackIds.Contains($"{x.TrackCode}{x.GetUtc0TrackTime():G}")) .WhereIf(!string.IsNullOrWhiteSpace(@event.CompareFields), y => { // 扩展信息不存在,跳过 if (string.IsNullOrWhiteSpace(y.Extra)) return false; // 从扩展信息提取比较值和传入值比较匹配 var extraDict = TextJsonHelper.DeserializeToDictionary(y.Extra); if (extraDict!.TryGetValue(@event.CompareFields!, out var compareValueJsonElement)) return TextJsonHelper.EqJsonElement(compareValueJsonElement, @event.CompareValue); return false; }) .ToList(); //从被删除轨迹寻找订单id,稍后会更新订单主表 orderIds = deleteTracks.Select(m => m.OrderId).ToHashSet(); } if (deleteTracks.Count == 0) return; // 删除轨迹批量 await trackDetailsEsRepository.DeleteTrackByIdsAsync(deleteTracks.Select(x => x.Id).ToHashSet()); // 删除轨迹后,更新订单上的轨迹信息 await TrackDeleteAfterUpdateOrderAsync(@event, oldTracks, deleteTracks, orderIds); // 删除轨迹广播 await PublicTrackTopicAsync(@event, deleteTracks); // 发布预警信号,稍后将进行预警计算 warningSignalPublisher.PublishSignal(WarningSignalType.轨迹删除, deleteTracks); } private async Task PublicTrackTopicAsync(TrackDeleteEvent @event, List delTracks) { var trackContentIds = delTracks.Select(x => x.TrackContentId).ToHashSet(); var trackContents = await provideQueries.GetTrackContentAsync(trackContentIds); var contentDict = trackContents.ToDictionary(x => x.Id); var needPublicTracks = delTracks // 轨迹内容已经不存在了,不需要广播 .Where(x => contentDict.ContainsKey(x.TrackContentId)) // 当删除操作没有标记广播时,根据轨迹状态/轨迹标记 .WhereIf(@event.IsToMq == false, x => contentDict[x.TrackContentId].IsToMq) .ToList(); if (needPublicTracks.Count == 0) return; //增加延时确保写盘成功 await Task.Delay(1000); foreach (var track in needPublicTracks) { var eto = new TrackChangeEto { TrackEsId = track.Id, Option = 2, IsSuccess = true, FailedReason = null, EstimatedDeliveryDate = track.EstimatedDeliveryDate, DestinationTimeZone = track.DestinationTimeZone, TrackPlacePostcode = track.TrackPlacePostcode, TrackPlaceNation = track.TrackPlaceNation, TrackPlaceNationId = track.TrackPlaceNationId, TrackPlaceProvince = track.TrackPlaceProvince, TrackPlaceProvinceId = track.TrackPlaceProvinceId, TrackPlaceCity = track.TrackPlaceCity, TrackPlaceCityId = track.TrackPlaceCityId, Files = track.Files?.Select(x => new FileItemDto { FileName = x.FileName, OssKey = x.OssKey }).ToList(), Remarks = track.Remarks, Source = track.Source, IsExternal = track.IsExternal, TrackTimeZone = track.TrackTimeZone, TrackTime = track.TrackTime, TrackPlace = track.TrackPlace, TrackStatusCode = track.TrackStatusCode, EnStatus = track.TrackEnContent, Content = track.TrackContent, TrackCode = track.TrackCode, OperationMan = @event.OperationMan, OrderNo = track.OrderNo, OrderIdentifiers = new[] { new OrderIdentifier { OrderId = track.OrderId, OrderNo = track.OrderNo, OrderType = track.OrderType } }, AppKey = track.AppKey, Extra = track.Extra }; var content = contentDict[track.TrackContentId]; broadcastingService.TrackChangePublicTopic(eto, content.AppKey); } } private async Task TrackDeleteAfterUpdateOrderAsync( TrackDeleteEvent @event, List oldTracks, List deleteTracks, HashSet orderIds ) { // 排除掉已经删除的轨迹后的新的轨迹 var deleteTrackIds = deleteTracks.Select(m => m.Id).ToList(); var newTracks = oldTracks.Where(x => !deleteTrackIds.Contains(x.Id)).ToList(); var orders = await orderRepository.GetListAsync(x => orderIds.Contains(x.Id), x => new OrderEntity { Id = x.Id, LatestTrackStatus = x.LatestTrackStatus, LatestTrackContent = x.LatestTrackContent, LatestTrackEnContent = x.LatestTrackEnContent, LatestTrackPlace = x.LatestTrackPlace, LatestTrackTime = x.LatestTrackTime, LatestTrackTimeZone = x.LatestTrackTimeZone, EstimatedDeliveryDate = x.EstimatedDeliveryDate, DestinationTimeZone = x.DestinationTimeZone }); if (orders.Count == 0) return; foreach (var order in orders) { //找到最新的轨迹数据 var track = newTracks?.OrderByDescending(x => x.ZeroTrackTime) .FirstOrDefault(x => x.OrderId == order.Id); // 删除全部轨迹导致的重置行为 if (track == null) { if (order.LatestTrackTime == null) continue; // tips:订单是支持从已完成态到未完成态的 order.ResetTrackInfo(); } else { if (order.LatestTrackContent == track.TrackContent) continue; // 删除引发的回退更新 order.LatestTrackStatus = track.TrackStatus; order.LatestTrackContent = track.TrackContent; order.LatestTrackEnContent = track.TrackEnContent; order.LatestTrackPlace = track.TrackPlace; order.LatestTrackTime = track.TrackTime; order.LatestTrackTimeZone = track.TrackTimeZone; order.EstimatedDeliveryDate = track.EstimatedDeliveryDate; order.DestinationTimeZone = track.DestinationTimeZone; } } //更新订单上冗余轨迹明细的字段 await orderRepository.UpdateRangeAsync(orders, x => new { x.LatestTrackStatus, x.LatestTrackContent, x.LatestTrackEnContent, x.LatestTrackPlace, x.LatestTrackTime, x.LatestTrackTimeZone, x.EstimatedDeliveryDate, x.DestinationTimeZone }); //批量写入订单操作日志 _ = orderOperationRecordEsRepository.AddManyAsync(orders.Select(order => new OrderOperationRecordIndex { OrderId = order.Id, OrderNo = order.OrderNo, OptionType = OrderOperationType.删除轨迹.ToString(), Content = deleteTracks.Where(m => m.OrderId == order.Id).ToList().ToJson(), CreateUserId = @event.OperationManId, CreateUserName = @event.OperationMan ?? "", CreateTime = DateTime.Now }).ToList()); } }