using Fake.Application; using Fake.DependencyInjection; using Fake.ObjectStorage; using Newtonsoft.Json; using Oms.Application.Dtos.Orders; using Oms.Application.Integrations.Itfx; using Oms.Application.Integrations.SmallBagTrack; using Oms.Application.Messaging; using Oms.Application.Shared; using Oms.Application.Storage; using Oms.Domain.ChannelsAggregate.Repositories; using Oms.Domain.OrdersAggregate.Consts; using Oms.Domain.OrdersAggregate.Entities; using Oms.Domain.OrdersAggregate.Repositories; using Oms.Domain.SystemAggregate.Repositories; namespace Oms.Application.Services.Orders.Batch; public sealed class OrderBatchActionExecutor( IOrderRepository orderRepository, ICountryRepository countryRepository, IServiceChannelRepository serviceChannelRepository, IObjectStorage fileStorage, ISmallBagTrackOrderClient smallBagTrackOrderClient, ISmallBagTrackUpsertRequestBuilder smallBagTrackUpsertRequestBuilder, OrderDownstreamCredentialResolver downstreamCredentialResolver, IItfxForwardClient itfxForwardClient, OrderFileManager orderFileManager, OrderOperationLogWriter operationLogWriter, OrderBatchCreateActionHandler createActionHandler, OrderBatchOrderActionHandler orderActionHandler) : IOrderBatchActionExecutor, IScopedDependency { public async Task ExecuteAsync( OrderBatchJobMessage message, CancellationToken cancellationToken = default) { try { return message.Action switch { OrderBatchActionCodes.TrackUpsertSmallBag => await ExecuteTrackUpsertAsync(message, cancellationToken), OrderBatchActionCodes.DownstreamForwardCreateOne => await ExecuteDownstreamForwardAsync(message, cancellationToken), OrderBatchActionCodes.ImportRow => await createActionHandler.ExecuteImportRowAsync(message, cancellationToken), OrderBatchActionCodes.ParcelCreate => await createActionHandler.ExecuteParcelCreateAsync(message, cancellationToken), OrderBatchActionCodes.Pay => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.PreDeclare => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.Intercept => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.CancelIntercept => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.Delete => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.Print => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.GenerateLabelPdf => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.InboundBilling => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.ForecastBilling => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.Reallocate => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.MarkProblematic => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.MarkClaimed => await orderActionHandler.ExecuteAsync(message, cancellationToken), OrderBatchActionCodes.HandleStatusUpdate => await orderActionHandler.ExecuteAsync(message, cancellationToken), _ => OrderBatchActionExecuteResult.BusinessFail($"未实现的批量操作: {message.Action}") }; } catch (BusinessException ex) { return OrderBatchActionExecuteResult.BusinessFail(ex.Message); } catch (Exception ex) { return OrderBatchActionExecuteResult.SystemFail(ex.Message); } } private async Task ExecuteTrackUpsertAsync( OrderBatchJobMessage message, CancellationToken cancellationToken) { SmallBagTrackUpsertRequest request; if (!string.IsNullOrWhiteSpace(message.PayloadJson)) { request = JsonConvert.DeserializeObject(message.PayloadJson!) ?? throw new BusinessException("轨迹同步消息体无效"); } else { var order = await orderRepository.FirstOrDefaultAsync(x => x.Id == message.OrderId, cancellationToken); if (order == null) { return OrderBatchActionExecuteResult.BusinessFail("订单不存在"); } if (string.IsNullOrWhiteSpace(order.ServiceNo)) { return OrderBatchActionExecuteResult.BusinessFail("订单运单号为空,无法同步轨迹"); } request = await smallBagTrackUpsertRequestBuilder.BuildAsync(order, cancellationToken: cancellationToken); // 手动「创建轨迹」无 payload:创单成功后补默认轨迹节点 A0101 request.AppendInitialTrackOnSuccess = true; } if (string.IsNullOrWhiteSpace(request.Authorization) && !string.IsNullOrWhiteSpace(message.Authorization)) { request.Authorization = message.Authorization.Trim(); } var trackResult = await smallBagTrackOrderClient.UpsertDebugAsync(request, cancellationToken); var serviceNo = request.OrderNo?.Trim(); if (trackResult.Skipped) { var reason = string.IsNullOrWhiteSpace(trackResult.SkipReason) ? "轨迹服务未启用或配置不完整" : trackResult.SkipReason.Trim(); return OrderBatchActionExecuteResult.BusinessFail( reason, resultJson: OrderBatchTrackUpsertResultBuilder.BuildFailure( trackResult, message.OrderId, serviceNo, reason)); } if (!trackResult.Success) { var reason = $"轨迹订单创建失败: HTTP {trackResult.StatusCode}"; return OrderBatchActionExecuteResult.BusinessFail( reason, resultJson: OrderBatchTrackUpsertResultBuilder.BuildFailure( trackResult, message.OrderId, serviceNo, reason)); } return OrderBatchActionExecuteResult.Ok( serviceNo: serviceNo, resultJson: OrderBatchTrackUpsertResultBuilder.BuildSuccess( trackResult, message.OrderId, serviceNo)); } private async Task ExecuteDownstreamForwardAsync( OrderBatchJobMessage message, CancellationToken cancellationToken) { var authorization = message.Authorization; if (!string.IsNullOrWhiteSpace(authorization)) { ItfxAuthorizationContext.Authorization = authorization.Trim(); } try { ItfxCreateOrderOneBody body; Order order; if (!string.IsNullOrWhiteSpace(message.PayloadJson)) { body = JsonConvert.DeserializeObject(message.PayloadJson!) ?? throw new BusinessException("下游预报消息体无效"); // 凭证解析仍需订单上的渠道/下游绑定字段;有 payload 时只查主表一次 var existing = await orderRepository.FirstOrDefaultAsync(x => x.Id == message.OrderId, cancellationToken); if (existing == null) { return OrderBatchActionExecuteResult.BusinessFail("订单不存在"); } order = existing; } else { var bundles = await orderRepository.GetDownstreamCreateOneBundlesAsync([message.OrderId], cancellationToken); if (!bundles.TryGetValue(message.OrderId, out var bundle)) { return OrderBatchActionExecuteResult.BusinessFail("订单不存在"); } order = bundle.Order; body = await OrderDownstreamCreateOneBodyBuilder.BuildAsync( bundle.Order, bundle.Sender, bundle.Recipient, bundle.Declarations, bundle.Weights, countryRepository, serviceChannelRepository, fileStorage, cancellationToken); } if (string.IsNullOrWhiteSpace(order.ServiceNo)) { return OrderBatchActionExecuteResult.BusinessFail("订单运单号为空,无法预报下游"); } var creds = await downstreamCredentialResolver.ResolveAsync(order, cancellationToken); var forward = await itfxForwardClient.ForwardCreateOrderOneAsync( creds.SupplierCode, creds.AppKey, creds.AppSecret, body, cancellationToken); ItfxForwardResultEvaluator.IsBusinessSuccess( forward.StatusCode, forward.ResponseBody, out var businessCode, out var businessMessage); if (!forward.Success) { var reason = ItfxForwardResultEvaluator.BuildFailReason( forward.StatusCode, forward.ResponseBody, businessCode, businessMessage); return OrderBatchActionExecuteResult.BusinessFail( reason, resultJson: OrderBatchDownstreamForwardResultBuilder.BuildFailure( forward, message.OrderId, order.ServiceNo, creds.SupplierCode, reason, businessCode, businessMessage)); } var beforeSnap = OrderOperationLogSnapshot.CaptureOrder(order); if (ItfxCreateOrderOneDataParser.TryParse(forward.ResponseBody, out var data) && data != null) { if (ItfxCreateOrderOneDataParser.ApplyToOrder(order, data)) { await orderRepository.UpdateAsync(order, cancellationToken); } if (!string.IsNullOrWhiteSpace(data.LabelUrl)) { await orderFileManager.AddOrReplaceDownstreamLabelAsync( order.Id, data.LabelUrl!, message.OperatorUserId, cancellationToken); } } var afterSnap = OrderOperationLogSnapshot.CaptureOrder(order); var remarkParts = new List { $"供应商 {creds.SupplierCode}" }; if (!string.IsNullOrWhiteSpace(order.DownstreamServiceNo)) { remarkParts.Add($"服务商单号 {order.DownstreamServiceNo}"); } if (!string.IsNullOrWhiteSpace(order.TrackNo)) { remarkParts.Add($"跟踪号 {order.TrackNo}"); } await operationLogWriter.TryWriteAsync( order, OrderOperationActionCodes.DownstreamForward, beforeSnap, afterSnap, string.Join(";", remarkParts), message.OperatorUserId > 0 ? message.OperatorUserId : null, operatorName: null, operatorType: "staff", source: "job"); return OrderBatchActionExecuteResult.Ok( serviceNo: order.ServiceNo, trackNo: order.TrackNo, resultJson: OrderBatchDownstreamForwardResultBuilder.BuildSuccess( forward, message.OrderId, order.ServiceNo, creds.SupplierCode, businessCode, businessMessage, order.DownstreamServiceNo, order.TrackNo)); } finally { ItfxAuthorizationContext.Authorization = null; } } }