From 6b920cc49d955eec748b2b6d7416054e026fac48 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Wed, 16 Sep 2026 17:43:30 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AF=B9=E9=BD=90=20createOrderOne=20?= =?UTF-8?q?=E4=B8=8B=E6=B8=B8=E9=A2=84=E6=8A=A5=E8=AF=B7=E6=B1=82=E4=BD=93?= =?UTF-8?q?=EF=BC=8CproductCode=20=E5=8F=96=E9=A2=84=E6=8A=A5=E6=B8=A0?= =?UTF-8?q?=E9=81=93=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除 channelCode 等多余字段,改用专用 ItfxCreateOrderOneBody。 Co-authored-by: Cursor --- .../Dtos/Orders/ItfxCreateOrderOneBody.cs | 57 +++++++++++++++++++ .../Dtos/Orders/OrderDownstreamForwardDtos.cs | 2 +- .../Integrations/Itfx/ItfxForwardClient.cs | 1 + .../Orders/Batch/OrderBatchActionExecutor.cs | 7 ++- .../Orders/Batch/OrderBatchJobCoordinator.cs | 3 + .../Orders/OrderService.DownstreamForward.cs | 3 +- .../OrderDownstreamCreateOneBodyBuilder.cs | 57 +++++++++++++++---- ...OrderBatchJobCoordinatorGovernanceTests.cs | 3 + 8 files changed, 117 insertions(+), 16 deletions(-) create mode 100644 src/Oms.Application/Dtos/Orders/ItfxCreateOrderOneBody.cs diff --git a/src/Oms.Application/Dtos/Orders/ItfxCreateOrderOneBody.cs b/src/Oms.Application/Dtos/Orders/ItfxCreateOrderOneBody.cs new file mode 100644 index 0000000..cd36ea9 --- /dev/null +++ b/src/Oms.Application/Dtos/Orders/ItfxCreateOrderOneBody.cs @@ -0,0 +1,57 @@ +namespace Oms.Application.Dtos.Orders; + +/// +/// itfx createOrderOne(ForwardWithSubscribe)标准请求体。 +/// 仅包含下游接口字段,不含 OMS 内部扩展参数。 +/// +public class ItfxCreateOrderOneBody +{ + public string? ClientNo { get; set; } + + public string? TrackNo { get; set; } + + public string? ServiceNo { get; set; } + + /// + /// 下游产品代码:取服务渠道「预报渠道 code」(ForecastCode)。 + /// + public string? ProductCode { get; set; } + + public string? CargoAttribute { get; set; } + + public int? WarehouseId { get; set; } + + public int? CustomerId { get; set; } + + public decimal? Length { get; set; } + + public decimal? Width { get; set; } + + public decimal? Height { get; set; } + + public decimal? ActualWeight { get; set; } + + public decimal? VolumetricWeight { get; set; } + + public decimal? ChargeableWeight { get; set; } + + public decimal? Cubic { get; set; } + + public bool IsCod { get; set; } + + public string? CodCurrency { get; set; } + + public string? CodSum { get; set; } + + public string? DeclaredCurrency { get; set; } + + public string? DeclaredSum { get; set; } + + public string? Quantity { get; set; } + + public OrderParcelReceiverInput? Receiver { get; set; } + + public OrderParcelSenderInput? Sender { get; set; } + + public List Items { get; set; } = []; +} diff --git a/src/Oms.Application/Dtos/Orders/OrderDownstreamForwardDtos.cs b/src/Oms.Application/Dtos/Orders/OrderDownstreamForwardDtos.cs index 472212e..c69e78c 100644 --- a/src/Oms.Application/Dtos/Orders/OrderDownstreamForwardDtos.cs +++ b/src/Oms.Application/Dtos/Orders/OrderDownstreamForwardDtos.cs @@ -13,7 +13,7 @@ public class OrderDownstreamCreateOneBodyOutput public string SupplierCode { get; set; } = string.Empty; - public OrderParcelCreateInput Body { get; set; } = new(); + public ItfxCreateOrderOneBody Body { get; set; } = new(); } /// diff --git a/src/Oms.Application/Integrations/Itfx/ItfxForwardClient.cs b/src/Oms.Application/Integrations/Itfx/ItfxForwardClient.cs index 4ab5564..c61ad95 100644 --- a/src/Oms.Application/Integrations/Itfx/ItfxForwardClient.cs +++ b/src/Oms.Application/Integrations/Itfx/ItfxForwardClient.cs @@ -17,6 +17,7 @@ internal sealed class ItfxForwardClient( private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull, WriteIndented = true }; diff --git a/src/Oms.Application/Services/Orders/Batch/OrderBatchActionExecutor.cs b/src/Oms.Application/Services/Orders/Batch/OrderBatchActionExecutor.cs index 77113cc..a88f537 100644 --- a/src/Oms.Application/Services/Orders/Batch/OrderBatchActionExecutor.cs +++ b/src/Oms.Application/Services/Orders/Batch/OrderBatchActionExecutor.cs @@ -7,6 +7,7 @@ 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; @@ -17,6 +18,7 @@ namespace Oms.Application.Services.Orders.Batch; public sealed class OrderBatchActionExecutor( IOrderRepository orderRepository, ICountryRepository countryRepository, + IServiceChannelRepository serviceChannelRepository, IFileStorage fileStorage, ISmallBagTrackOrderClient smallBagTrackOrderClient, ISmallBagTrackUpsertRequestBuilder smallBagTrackUpsertRequestBuilder, @@ -145,12 +147,12 @@ public sealed class OrderBatchActionExecutor( try { - OrderParcelCreateInput body; + ItfxCreateOrderOneBody body; Order order; if (!string.IsNullOrWhiteSpace(message.PayloadJson)) { - body = JsonConvert.DeserializeObject(message.PayloadJson!) + body = JsonConvert.DeserializeObject(message.PayloadJson!) ?? throw new BusinessException("下游预报消息体无效"); // 凭证解析仍需订单上的渠道/下游绑定字段;有 payload 时只查主表一次 @@ -178,6 +180,7 @@ public sealed class OrderBatchActionExecutor( bundle.Declarations, bundle.Weights, countryRepository, + serviceChannelRepository, fileStorage, cancellationToken); } diff --git a/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs b/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs index b7df2fa..fed05fe 100644 --- a/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs +++ b/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs @@ -9,6 +9,7 @@ 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 System.Linq.Expressions; using Oms.Domain.OrdersAggregate.Entities; @@ -24,6 +25,7 @@ public sealed class OrderBatchJobCoordinator( IOrderBatchJobRepository batchJobRepository, IOrderRepository orderRepository, ICountryRepository countryRepository, + IServiceChannelRepository serviceChannelRepository, IFileStorage fileStorage, IOrderBatchJobPublisher publisher, OrderBatchJobProcessor batchJobProcessor, @@ -651,6 +653,7 @@ public sealed class OrderBatchJobCoordinator( bundle.Declarations, bundle.Weights, countryRepository, + serviceChannelRepository, fileStorage, cancellationToken); result[orderId] = JsonConvert.SerializeObject(body); diff --git a/src/Oms.Application/Services/Orders/OrderService.DownstreamForward.cs b/src/Oms.Application/Services/Orders/OrderService.DownstreamForward.cs index 3c90eda..b8eb919 100644 --- a/src/Oms.Application/Services/Orders/OrderService.DownstreamForward.cs +++ b/src/Oms.Application/Services/Orders/OrderService.DownstreamForward.cs @@ -82,7 +82,7 @@ public partial class OrderService }; } - private async Task<(Domain.OrdersAggregate.Entities.Order order, OrderParcelCreateInput body)> LoadDownstreamCreateOneContextAsync( + private async Task<(Domain.OrdersAggregate.Entities.Order order, ItfxCreateOrderOneBody body)> LoadDownstreamCreateOneContextAsync( int orderId) { var bundles = await OrderRepository.GetDownstreamCreateOneBundlesAsync([orderId]); @@ -98,6 +98,7 @@ public partial class OrderService bundle.Declarations, bundle.Weights, CountryRepository, + ServiceChannelRepository, FileStorage); return (bundle.Order, body); diff --git a/src/Oms.Application/Shared/OrderDownstreamCreateOneBodyBuilder.cs b/src/Oms.Application/Shared/OrderDownstreamCreateOneBodyBuilder.cs index fadb54e..aa8c17a 100644 --- a/src/Oms.Application/Shared/OrderDownstreamCreateOneBodyBuilder.cs +++ b/src/Oms.Application/Shared/OrderDownstreamCreateOneBodyBuilder.cs @@ -1,5 +1,6 @@ using Oms.Application.Dtos.Orders; using Oms.Application.Storage; +using Oms.Domain.ChannelsAggregate.Repositories; using Oms.Domain.OrdersAggregate.Consts; using Oms.Domain.OrdersAggregate.Entities; using Oms.Domain.SystemAggregate.Repositories; @@ -7,17 +8,18 @@ using Oms.Domain.SystemAggregate.Repositories; namespace Oms.Application.Shared; /// -/// 从订单表及关联子表组装 createOrderOne 标准请求体。 +/// 从订单表及关联子表组装 itfx createOrderOne 标准请求体。 /// public static class OrderDownstreamCreateOneBodyBuilder { - public static async Task BuildAsync( + public static async Task BuildAsync( Order order, OrderSender? sender, OrderRecipient? recipient, IReadOnlyList declarations, IReadOnlyList weights, ICountryRepository countryRepository, + IServiceChannelRepository serviceChannelRepository, IFileStorage? fileStorage = null, CancellationToken cancellationToken = default) { @@ -41,16 +43,18 @@ public static class OrderDownstreamCreateOneBodyBuilder string.Equals(x.Type, OrderWeightTypeCodes.Forecast, StringComparison.OrdinalIgnoreCase)) ?? weights.FirstOrDefault(); - return new OrderParcelCreateInput + var productCode = await ResolveForecastProductCodeAsync( + order, + serviceChannelRepository, + cancellationToken); + + return new ItfxCreateOrderOneBody { - RequestId = order.RequestId, ClientNo = order.ClientNo, TrackNo = order.TrackNo, ServiceNo = order.ServiceNo, - ProductCode = order.ProductCode, - ChannelCode = order.ChannelCode, + ProductCode = productCode, CargoAttribute = order.CargoAttribute, - ExportCustomsMode = order.ExportCustomsMode, WarehouseId = order.WarehouseId, CustomerId = order.CustomerId, Length = forecast?.Length, @@ -62,16 +66,45 @@ public static class OrderDownstreamCreateOneBodyBuilder Cubic = forecast?.Cubic, IsCod = order.IsCod > 0, CodCurrency = order.CodCurrency, - CodSum = order.CodSum, + CodSum = FormatDecimal(order.CodSum), DeclaredCurrency = order.DeclaredCurrency, - DeclaredSum = order.DeclaredSum, - Quantity = order.Quantity, + DeclaredSum = FormatDecimal(order.DeclaredSum), + Quantity = order.Quantity?.ToString(), Receiver = MapReceiver(recipient, order.ConsigneeCountry, idToCode), Sender = MapSender(sender, idToCode), Items = declarations.Select(x => MapItem(x, fileStorage)).ToList() }; } + /// + /// createOrderOne.productCode = 渠道「预报渠道 code」: + /// 优先订单固化的 ChannelForecastCode,否则 live 查 t_service_channel.ForecastCode。 + /// + private static async Task ResolveForecastProductCodeAsync( + Order order, + IServiceChannelRepository serviceChannelRepository, + CancellationToken cancellationToken) + { + var snapshotted = order.ChannelForecastCode?.Trim(); + if (!string.IsNullOrWhiteSpace(snapshotted)) + { + return snapshotted; + } + + var channelCode = order.ChannelCode?.Trim(); + if (string.IsNullOrWhiteSpace(channelCode)) + { + return null; + } + + var channel = await serviceChannelRepository.GetByChannelCodeAsync(channelCode, cancellationToken); + var forecastCode = channel?.ForecastCode?.Trim(); + return string.IsNullOrWhiteSpace(forecastCode) ? null : forecastCode; + } + + private static string? FormatDecimal(decimal? value) => + value.HasValue ? value.Value.ToString(System.Globalization.CultureInfo.InvariantCulture) : null; + private static string? ResolveCountryIso(int? countryId, string? orderConsigneeCountry, IReadOnlyDictionary idToCode) { if (!string.IsNullOrWhiteSpace(orderConsigneeCountry)) @@ -83,9 +116,9 @@ public static class OrderDownstreamCreateOneBodyBuilder } } - if (countryId is > 0 && idToCode.TryGetValue(countryId.Value, out var code) && !string.IsNullOrWhiteSpace(code)) + if (countryId is > 0 && idToCode.TryGetValue(countryId.Value, out var iso) && !string.IsNullOrWhiteSpace(iso)) { - return code.Trim().ToUpperInvariant(); + return iso.Trim().ToUpperInvariant(); } return null; diff --git a/src/Oms.Tests/OrderBatchJobCoordinatorGovernanceTests.cs b/src/Oms.Tests/OrderBatchJobCoordinatorGovernanceTests.cs index aa50039..444ade9 100644 --- a/src/Oms.Tests/OrderBatchJobCoordinatorGovernanceTests.cs +++ b/src/Oms.Tests/OrderBatchJobCoordinatorGovernanceTests.cs @@ -7,6 +7,7 @@ using Oms.Application.Dtos.Orders; using Oms.Application.Messaging; using Oms.Application.Services.Orders.Batch; using Oms.Application.Storage; +using Oms.Domain.ChannelsAggregate.Repositories; using Oms.Domain.OrdersAggregate.Consts; using Oms.Domain.OrdersAggregate.Entities; using Oms.Domain.OrdersAggregate.Repositories; @@ -19,6 +20,7 @@ public class OrderBatchJobCoordinatorGovernanceTests private readonly Mock _batchJobRepository = new(MockBehavior.Strict); private readonly Mock _orderRepository = new(MockBehavior.Loose); private readonly Mock _countryRepository = new(MockBehavior.Loose); + private readonly Mock _serviceChannelRepository = new(MockBehavior.Loose); private readonly Mock _fileStorage = new(MockBehavior.Loose); private readonly Mock _publisher = new(MockBehavior.Strict); private readonly OrderBatchJobCoordinator _coordinator; @@ -38,6 +40,7 @@ public class OrderBatchJobCoordinatorGovernanceTests _batchJobRepository.Object, _orderRepository.Object, _countryRepository.Object, + _serviceChannelRepository.Object, _fileStorage.Object, _publisher.Object, processor,