From d8752959e7612fade34d81c44ec70eb35f6b72e5 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Thu, 9 Jul 2026 09:44:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(order):=20=E5=BC=BA=E5=8C=96=E5=B0=8F?= =?UTF-8?q?=E5=8C=85=E4=B8=8B=E5=8D=95=E8=A7=84=E5=88=99=E5=B9=B6=E6=9A=B4?= =?UTF-8?q?=E9=9C=B2=E9=A2=84=E6=8A=A5=E5=A4=B1=E8=B4=A5=E5=8E=9F=E5=9B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 客户单号必填且同客户唯一,运单号统一 NE 前缀自动生成;模板导入不再接受跟踪号/运单号;列表与导出返回预报失败原因字段。 Co-authored-by: Cursor --- ...migrate_t_order_predeclare_fail_reason.sql | 11 +++++ .../Dtos/Orders/OrderOutput.cs | 5 ++ .../Services/Orders/OrderService.Helpers.cs | 4 ++ .../Services/Orders/OrderService.OrderEdit.cs | 14 +++++- .../Orders/OrderService.ParcelBilling.cs | 46 ++++++++++--------- .../Templates/OrderTemplateFieldRegistry.cs | 2 - .../Templates/OrderTemplateHeaderMatcher.cs | 2 - .../Templates/OrderTemplateRowMapper.cs | 2 +- 8 files changed, 59 insertions(+), 27 deletions(-) create mode 100644 sql/migrate_t_order_predeclare_fail_reason.sql diff --git a/sql/migrate_t_order_predeclare_fail_reason.sql b/sql/migrate_t_order_predeclare_fail_reason.sql new file mode 100644 index 0000000..11abf72 --- /dev/null +++ b/sql/migrate_t_order_predeclare_fail_reason.sql @@ -0,0 +1,11 @@ +-- t_order 预报失败原因(可重复执行) +-- 在 OMS 业务库执行:mysql -h -u -p < migrate_t_order_predeclare_fail_reason.sql + +SET @db = DATABASE(); + +SET @exist := (SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 't_order' AND COLUMN_NAME = 'PredeclareFailReason'); +SET @sql := IF(@exist = 0, + 'ALTER TABLE `t_order` ADD COLUMN `PredeclareFailReason` VARCHAR(512) NULL COMMENT ''预报失败原因'' AFTER `IsRemote`', + 'SELECT ''skip PredeclareFailReason'''); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/src/Oms.Application/Dtos/Orders/OrderOutput.cs b/src/Oms.Application/Dtos/Orders/OrderOutput.cs index 883d9b6..2d45a04 100644 --- a/src/Oms.Application/Dtos/Orders/OrderOutput.cs +++ b/src/Oms.Application/Dtos/Orders/OrderOutput.cs @@ -105,6 +105,11 @@ public class OrderOutput /// public string? ExportCustomsMode { get; set; } + /// + /// 预报失败原因(草稿订单)。 + /// + public string? PredeclareFailReason { get; set; } + /// /// 申报币种。 /// diff --git a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs index a3e3e23..53161ac 100644 --- a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs +++ b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs @@ -121,6 +121,9 @@ public partial class OrderService CustomerId = order.CustomerId, RequestId = order.RequestId, ExportCustomsMode = order.ExportCustomsMode, + PredeclareFailReason = string.IsNullOrWhiteSpace(order.PredeclareFailReason) + ? null + : order.PredeclareFailReason.Trim(), DeclaredCurrency = order.DeclaredCurrency, DeclaredSum = order.DeclaredSum, Quantity = order.Quantity, @@ -188,6 +191,7 @@ public partial class OrderService x.CustomerId, x.Status, x.OrderStatus, + x.PredeclareFailReason, x.ReceiveTime, x.ShipTime, x.PayTime, diff --git a/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs b/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs index deeacca..b8febf9 100644 --- a/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs +++ b/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs @@ -27,7 +27,19 @@ public partial class OrderService if (input.ClientNo != null) { - order.ClientNo = string.IsNullOrWhiteSpace(input.ClientNo) ? null : input.ClientNo.Trim(); + var newClientNo = string.IsNullOrWhiteSpace(input.ClientNo) ? null : input.ClientNo.Trim(); + if (!string.IsNullOrWhiteSpace(newClientNo) + && !string.Equals(newClientNo, order.ClientNo, StringComparison.OrdinalIgnoreCase) + && order.CustomerId.HasValue) + { + var duplicated = await OrderRepository.GetByCustomerAndClientNoAsync(order.CustomerId.Value, newClientNo); + if (duplicated != null && duplicated.Id != order.Id) + { + throw new BusinessException("客户单号已存在"); + } + } + + order.ClientNo = newClientNo; } if (input.TrackNo != null) diff --git a/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs b/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs index df87eee..d199e55 100644 --- a/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs +++ b/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs @@ -78,7 +78,7 @@ public partial class OrderService throw new BusinessException("收件地址命中黑名单区域"); } - var serviceNo = await ResolveServiceNoAsync(input.ServiceNo); + var serviceNo = await ResolveServiceNoAsync(); var order = BuildDraftOrderEntity(input, product, country.CountryCode, country.ChineseName, snapshotBinding, serviceNo); order = await OrderRepository.InsertAsync(order); @@ -165,16 +165,36 @@ public partial class OrderService } } + private const string ParcelServiceNoPrefix = "NE"; + private static void NormalizeParcelCreateInput(OrderParcelCreateInput input) { - input.TrackNo = string.IsNullOrWhiteSpace(input.TrackNo) ? null : input.TrackNo.Trim(); - input.ServiceNo = string.IsNullOrWhiteSpace(input.ServiceNo) ? null : input.ServiceNo.Trim(); + input.TrackNo = null; + input.ServiceNo = null; input.ClientNo = string.IsNullOrWhiteSpace(input.ClientNo) ? null : input.ClientNo.Trim(); input.RequestId = string.IsNullOrWhiteSpace(input.RequestId) ? null : input.RequestId.Trim(); } private async Task ValidateParcelCreateInputAsync(OrderParcelCreateInput input) { + if (string.IsNullOrWhiteSpace(input.ClientNo)) + { + throw new BusinessException("客户单号不能为空"); + } + + if (!input.CustomerId.HasValue || input.CustomerId <= 0) + { + throw new BusinessException("客户ID不能为空"); + } + + var duplicatedClientNo = await OrderRepository.GetByCustomerAndClientNoAsync( + input.CustomerId.Value, + input.ClientNo); + if (duplicatedClientNo != null) + { + throw new BusinessException("客户单号已存在"); + } + if (string.IsNullOrWhiteSpace(input.ProductCode)) { throw new BusinessException("产品代码不能为空"); @@ -185,11 +205,6 @@ public partial class OrderService throw new BusinessException("仓库ID不能为空"); } - if (!input.CustomerId.HasValue || input.CustomerId <= 0) - { - throw new BusinessException("客户ID不能为空"); - } - if (!input.Length.HasValue || !input.Width.HasValue || !input.Height.HasValue || !input.ActualWeight.HasValue || input.Length <= 0 || input.Width <= 0 || input.Height <= 0 || input.ActualWeight <= 0) { @@ -1185,22 +1200,11 @@ public partial class OrderService await OrderRepository.UpdateAsync(order); } - private async Task ResolveServiceNoAsync(string? serviceNo) + private async Task ResolveServiceNoAsync() { - var normalized = serviceNo?.Trim(); - if (!string.IsNullOrWhiteSpace(normalized)) - { - if (await OrderRepository.HasServiceNoAsync(normalized)) - { - throw new BusinessException("运单号已存在"); - } - - return normalized; - } - for (var i = 0; i < 5; i++) { - var generated = $"LP{DateTime.Now:yyyyMMddHHmmss}{Random.Shared.Next(1000, 9999)}"; + var generated = $"{ParcelServiceNoPrefix}{DateTime.Now:yyyyMMddHHmmss}{Random.Shared.Next(1000, 9999)}"; if (!await OrderRepository.HasServiceNoAsync(generated)) { return generated; diff --git a/src/Oms.Application/Services/Orders/Templates/OrderTemplateFieldRegistry.cs b/src/Oms.Application/Services/Orders/Templates/OrderTemplateFieldRegistry.cs index d743057..2de5e14 100644 --- a/src/Oms.Application/Services/Orders/Templates/OrderTemplateFieldRegistry.cs +++ b/src/Oms.Application/Services/Orders/Templates/OrderTemplateFieldRegistry.cs @@ -70,8 +70,6 @@ public static class OrderTemplateFieldRegistry Add("requestId", "幂等请求ID", false); Add("clientNo", "客户单号", true, example: "C202604230001"); - Add("trackNo", "跟踪号", false); - Add("serviceNo", "运单号", false); Add("productCode", "运输方式", true, "select", "sales_product", "basic", "PROD001"); Add("cargoAttribute", "货物属性", true, "select", "cargo_attribute", "basic", "general"); Add("exportCustomsMode", "出口报关方式", false, "select", "export_customs_mode", "basic", "9610Simple"); diff --git a/src/Oms.Application/Services/Orders/Templates/OrderTemplateHeaderMatcher.cs b/src/Oms.Application/Services/Orders/Templates/OrderTemplateHeaderMatcher.cs index cac5baf..499eb4c 100644 --- a/src/Oms.Application/Services/Orders/Templates/OrderTemplateHeaderMatcher.cs +++ b/src/Oms.Application/Services/Orders/Templates/OrderTemplateHeaderMatcher.cs @@ -15,8 +15,6 @@ public static class OrderTemplateHeaderMatcher ["客户单号"] = "clientNo", ["客户订单号"] = "clientNo", ["外部单号"] = "clientNo", - ["跟踪号"] = "trackNo", - ["运单号"] = "serviceNo", ["运输方式"] = "productCode", ["产品代码"] = "productCode", ["产品编码"] = "productCode", diff --git a/src/Oms.Application/Services/Orders/Templates/OrderTemplateRowMapper.cs b/src/Oms.Application/Services/Orders/Templates/OrderTemplateRowMapper.cs index d318551..333e644 100644 --- a/src/Oms.Application/Services/Orders/Templates/OrderTemplateRowMapper.cs +++ b/src/Oms.Application/Services/Orders/Templates/OrderTemplateRowMapper.cs @@ -11,7 +11,7 @@ public static class OrderTemplateRowMapper { private static readonly HashSet ForceStringFields = new(StringComparer.OrdinalIgnoreCase) { - "requestId", "clientNo", "trackNo", "serviceNo", "productCode", "cargoAttribute", "exportCustomsMode", + "requestId", "clientNo", "productCode", "cargoAttribute", "exportCustomsMode", "codCurrency", "declaredCurrency", "receiver.recipientsName", "receiver.companyName", "receiver.phone", "receiver.telePhone", "receiver.email", "receiver.countryCode", "receiver.province", "receiver.city", "receiver.streetNumber", "receiver.streetAddress1", "receiver.streetAddress2", "receiver.streetAddress3",