diff --git a/src/Oms.Application/Services/Orders/Batch/OrderBatchOrderActionHandler.cs b/src/Oms.Application/Services/Orders/Batch/OrderBatchOrderActionHandler.cs index f363cd8..664bb91 100644 --- a/src/Oms.Application/Services/Orders/Batch/OrderBatchOrderActionHandler.cs +++ b/src/Oms.Application/Services/Orders/Batch/OrderBatchOrderActionHandler.cs @@ -72,7 +72,8 @@ public sealed class OrderBatchOrderActionHandler(IServiceScopeFactory scopeFacto message.OrderId, message.PayloadJson, message.OperatorUserId, - cancellationToken); + cancellationToken, + message.Authorization); return OrderBatchActionExecuteResult.Ok(); case OrderBatchActionCodes.Reallocate: diff --git a/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs b/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs index f1f1a5f..56ef739 100644 --- a/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs +++ b/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs @@ -2329,20 +2329,26 @@ public partial class OrderService await OrderRepository.UpdateAsync(order); var operatorId = operatorUserId ?? (CurrentUser.IsAuthenticated ? CurrentUser.GetId() : 0); + var authorization = CaptureRequestAuthorization(); var publisher = LazyServiceProvider.GetRequiredService(); if (!publisher.IsEnabled) { var payloadJson = JsonConvert.SerializeObject(new ForecastBillingJobPayload { - CouponInstanceId = order.CouponInstanceId + CouponInstanceId = order.CouponInstanceId, + Authorization = authorization }); - await ProcessForecastBillingForBatchAsync(order.Id, payloadJson, operatorId); + await ProcessForecastBillingForBatchAsync( + order.Id, + payloadJson, + operatorId, + authorization: authorization); } else { var orderId = order.Id; var couponInstanceId = order.CouponInstanceId; - await ScheduleForecastEnqueueAsync(orderId, couponInstanceId, operatorId); + await ScheduleForecastEnqueueAsync(orderId, couponInstanceId, operatorId, authorization); } await ScheduleForecastSideEffectsAsync(order.Id, operatorId); @@ -2663,7 +2669,8 @@ public partial class OrderService int orderId, string? payloadJson = null, int operatorUserId = 0, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default, + string? authorization = null) { var order = await OrderRepository.FirstOrDefaultAsync(x => x.Id == orderId, cancellationToken); if (order == null) @@ -2687,6 +2694,7 @@ public partial class OrderService var beforeSnap = await CaptureOrderAuditSnapshotAsync(order, includeFees: true, includeWeights: true); int? couponInstanceId = order.CouponInstanceId; + string? trackAuthorization = authorization; if (!string.IsNullOrWhiteSpace(payloadJson)) { try @@ -2696,6 +2704,12 @@ public partial class OrderService { couponInstanceId = payload.CouponInstanceId; } + + if (string.IsNullOrWhiteSpace(trackAuthorization) + && !string.IsNullOrWhiteSpace(payload?.Authorization)) + { + trackAuthorization = payload.Authorization.Trim(); + } } catch { @@ -2829,9 +2843,10 @@ public partial class OrderService await OrderRepository.UpdateAsync(order); // 轨迹订单依赖排货后的渠道/ApiCode,只有排货成功才创建。 + // 消费端没有 HttpContext,须用创单时捕获的 Authorization,否则轨迹网关返回 401。 if (allocated && !string.IsNullOrWhiteSpace(order.ChannelCode)) { - await ScheduleTrackAfterAllocationAsync(order.Id); + await ScheduleTrackAfterAllocationAsync(order.Id, trackAuthorization); } var afterSnap = await CaptureOrderAuditSnapshotAsync(order, includeFees: true, includeWeights: true); @@ -2850,6 +2865,11 @@ public partial class OrderService private sealed class ForecastBillingJobPayload { public int? CouponInstanceId { get; set; } + + /// + /// 创单/预报请求的 Authorization,供排货后异步创建轨迹订单转发到轨迹网关。 + /// + public string? Authorization { get; set; } } /// @@ -2868,6 +2888,8 @@ public partial class OrderService var logger = LazyServiceProvider.GetRequiredService().CreateLogger("Oms.OrderParcelCreate"); var platformCode = input.PlatformCode; var couponInstanceId = input.CouponInstanceId; + // 事务提交后 HttpContext 已不可用,这里先捕获登录态供轨迹网关使用。 + var authorization = CaptureRequestAuthorization(); await OmsAfterCommit.ScheduleAsync(unitOfWorkManager, async () => { @@ -2882,7 +2904,11 @@ public partial class OrderService forecastQueued = await OmsAfterCommit.RunStepAsync(scopeFactory, logger, "forecast-billing", async sp => { var service = sp.GetRequiredService(); - await service.EnqueueForecastBillingByIdAsync(orderId, couponInstanceId, operatorUserId); + await service.EnqueueForecastBillingByIdAsync( + orderId, + couponInstanceId, + operatorUserId, + authorization); }); await OmsAfterCommit.RunStepAsync(scopeFactory, logger, "label-generate", sp => sp.GetRequiredService().PublishForecastLabelAsync(orderId, operatorUserId)); @@ -2906,7 +2932,11 @@ public partial class OrderService }); } - private async Task ScheduleForecastEnqueueAsync(int orderId, int? couponInstanceId, int operatorUserId) + private async Task ScheduleForecastEnqueueAsync( + int orderId, + int? couponInstanceId, + int operatorUserId, + string? authorization) { var unitOfWorkManager = LazyServiceProvider.GetRequiredService(); var scopeFactory = LazyServiceProvider.GetRequiredService(); @@ -2916,7 +2946,8 @@ public partial class OrderService sp.GetRequiredService().EnqueueForecastBillingByIdAsync( orderId, couponInstanceId, - operatorUserId))); + operatorUserId, + authorization))); } /// @@ -2939,14 +2970,14 @@ public partial class OrderService /// /// 预报计费事务提交后,再创建轨迹订单(此时已有渠道和 ApiCode)。 /// - private async Task ScheduleTrackAfterAllocationAsync(int orderId) + private async Task ScheduleTrackAfterAllocationAsync(int orderId, string? authorization) { var unitOfWorkManager = LazyServiceProvider.GetRequiredService(); var scopeFactory = LazyServiceProvider.GetRequiredService(); var logger = LazyServiceProvider.GetRequiredService().CreateLogger("Oms.ForecastBilling"); await OmsAfterCommit.ScheduleAsync(unitOfWorkManager, () => OmsAfterCommit.RunStepAsync(scopeFactory, logger, "track-upsert", sp => - sp.GetRequiredService().PublishForecastTrackAsync(orderId))); + sp.GetRequiredService().PublishForecastTrackAsync(orderId, authorization))); } private async Task PublishCustomsForOrderAsync(int orderId, string? platformCode) @@ -2961,7 +2992,11 @@ public partial class OrderService await ParcelCreateEventPublisher.PublishCustoms9610UpsertAsync(order, platformCode); } - private async Task EnqueueForecastBillingByIdAsync(int orderId, int? couponInstanceId, int operatorUserId) + private async Task EnqueueForecastBillingByIdAsync( + int orderId, + int? couponInstanceId, + int operatorUserId, + string? authorization) { var order = await OrderRepository.FirstOrDefaultAsync(x => x.Id == orderId); if (order == null @@ -2970,7 +3005,7 @@ public partial class OrderService return; } - await EnqueueForecastBillingAsync(order, couponInstanceId, operatorUserId); + await EnqueueForecastBillingAsync(order, couponInstanceId, operatorUserId, authorization); } private async Task MarkForecastEnqueueFailedIfStillProcessingAsync(int orderId) @@ -2985,7 +3020,7 @@ public partial class OrderService await MarkParcelDraftFailedAsync(order, ForecastBillingEnqueueFailReason); } - private async Task PublishForecastTrackAsync(int orderId) + private async Task PublishForecastTrackAsync(int orderId, string? authorization = null) { var order = await OrderRepository.FirstOrDefaultAsync(x => x.Id == orderId); if (order == null @@ -3000,7 +3035,7 @@ public partial class OrderService : null; var request = await BuildSmallBagTrackUpsertRequestAsync(order, receiverInput: null); request.AppendInitialTrackOnSuccess = true; - ApplyTrackAuthorization(request); + ApplyTrackAuthorization(request, authorization); await ParcelCreateEventPublisher.PublishTrackUpsertAsync(order, request, customer?.Id); } @@ -3031,13 +3066,16 @@ public partial class OrderService private async Task EnqueueForecastBillingAsync( Order order, int? couponInstanceId, - int operatorUserId) + int operatorUserId, + string? authorization = null) { var logger = LazyServiceProvider.GetRequiredService() .CreateLogger("Oms.ForecastBilling"); + authorization ??= CaptureRequestAuthorization(); var payloadJson = JsonConvert.SerializeObject(new ForecastBillingJobPayload { - CouponInstanceId = couponInstanceId + CouponInstanceId = couponInstanceId, + Authorization = authorization }); try @@ -3047,7 +3085,11 @@ public partial class OrderService { try { - await ProcessForecastBillingForBatchAsync(order.Id, payloadJson, operatorUserId); + await ProcessForecastBillingForBatchAsync( + order.Id, + payloadJson, + operatorUserId, + authorization: authorization); } catch (Exception ex) { @@ -3073,7 +3115,8 @@ public partial class OrderService RequestId = requestId, PayloadJson = payloadJson }, - operatorUserId); + operatorUserId, + authorization); } catch (Exception ex) { @@ -3375,18 +3418,37 @@ public partial class OrderService await ParcelCreateEventPublisher.PublishTrackUpsertAsync(order, request, customer?.Id); } - private void ApplyTrackAuthorization(SmallBagTrackUpsertRequest request) + private void ApplyTrackAuthorization(SmallBagTrackUpsertRequest request, string? authorization = null) { - var authorization = LazyServiceProvider - .GetRequiredService() - .HttpContext? - .Request - .Headers - .Authorization - .ToString(); if (!string.IsNullOrWhiteSpace(authorization)) { - request.Authorization = authorization; + request.Authorization = authorization.Trim(); + return; + } + + var fromRequest = CaptureRequestAuthorization(); + if (!string.IsNullOrWhiteSpace(fromRequest)) + { + request.Authorization = fromRequest; + } + } + + private string? CaptureRequestAuthorization() + { + try + { + var authorization = LazyServiceProvider + .GetRequiredService() + .HttpContext? + .Request + .Headers + .Authorization + .ToString(); + return string.IsNullOrWhiteSpace(authorization) ? null : authorization.Trim(); + } + catch (ObjectDisposedException) + { + return null; } } diff --git a/src/Oms.Application/Services/Orders/OrderService.cs b/src/Oms.Application/Services/Orders/OrderService.cs index 2e5fe6d..1932297 100644 --- a/src/Oms.Application/Services/Orders/OrderService.cs +++ b/src/Oms.Application/Services/Orders/OrderService.cs @@ -623,6 +623,7 @@ public partial class OrderService : ApplicationService /// /// 【预报计费消费后】计费重、应收、排货应付、营销快照 → 订单改已预报。 /// 排货成功且已有 ChannelCode 时,再投递轨迹订单 + A0101(track-upsert); + /// 轨迹网关需要登录 Authorization,创单时写入预报任务 Payload,消费端原样转发; /// 排货失败不阻断预报,也不建轨迹(可页面补做)。 /// ///