From a1daea6b3507c16cf842c4cc6a632e5cb30515d1 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Wed, 22 Jul 2026 16:18:59 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BB=93=E5=82=A8=E8=A1=A8=20C=5F*?= =?UTF-8?q?=20=E6=94=B9=E4=B8=BA=20c=5F*=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=B8=93=E7=BA=BF=E5=B0=8F=E5=8C=85=E6=89=B9=E9=87=8F=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 仓储实体与 db.sql 统一为小写 c_ 前缀,新增 migrate_rename_warehouse_C_to_c.sql - 专线小包批量模板精简版/标准版双模板,Excel 必填标红与发件地址上传参数 Co-authored-by: Cursor --- db.sql | 66 ++--- sql/C_parcel_arrival.sql | 6 +- sql/C_parcel_item_AttributeText.sql | 14 +- sql/C_parcel_item_ImageUrl.sql | 14 +- sql/README.md | 2 +- sql/fix_concurrency_stamp_missing.sql | 2 +- sql/migrate_rename_warehouse_C_to_c.sql | 34 +++ sql/migrate_t_order_stock_bind.sql | 106 ++++---- .../Dtos/Orders/OrderBatchPayloadDtos.cs | 7 + .../Dtos/Orders/OrderTemplateMappingDtos.cs | 2 + .../Dtos/Orders/OrderUploadInput.cs | 5 + .../Orders/OrderService.BatchCreate.cs | 50 +++- .../Services/Orders/OrderService.Helpers.cs | 7 + .../Orders/OrderService.SenderAddress.cs | 72 +++++ .../Services/Orders/OrderService.Template.cs | 37 ++- .../Templates/OrderTemplateExcelGenerator.cs | 43 ++- .../OrderTemplateExcelOpenXmlEnhancer.cs | 36 ++- .../Templates/OrderTemplateFieldRegistry.cs | 245 ++++++++++++------ .../Entities/CBinLocation.cs | 2 +- .../Entities/CInboundBox.cs | 2 +- .../Entities/CInboundBoxLine.cs | 2 +- .../Entities/CInboundNotice.cs | 2 +- .../WarehouseAggregate/Entities/CInventory.cs | 2 +- .../WarehouseAggregate/Entities/CParcel.cs | 2 +- .../Entities/CParcelArrival.cs | 2 +- .../Entities/CParcelItem.cs | 2 +- .../WarehouseAggregate/Entities/CSku.cs | 2 +- .../Entities/CSkuPlatformMap.cs | 2 +- .../WarehouseAggregate/WarehouseTableNames.cs | 18 ++ 29 files changed, 562 insertions(+), 224 deletions(-) create mode 100644 sql/migrate_rename_warehouse_C_to_c.sql create mode 100644 src/Oms.Domain/WarehouseAggregate/WarehouseTableNames.cs diff --git a/db.sql b/db.sql index b1963c6..48e3955 100644 --- a/db.sql +++ b/db.sql @@ -1,4 +1,4 @@ --- OMS Admin schema +-- OMS Admin schema -- Target database: MySQL 8.0+ -- Notes: -- 1. Physical foreign keys are intentionally omitted. The project uses logical foreign keys plus indexes to reduce import friction, @@ -36,16 +36,16 @@ DROP TABLE IF EXISTS `t_xhs_analysis_session`; DROP TABLE IF EXISTS `t_xhs_publish_record`; DROP TABLE IF EXISTS `t_xhs_task`; DROP TABLE IF EXISTS `t_xhs_agent`; -DROP TABLE IF EXISTS `C_inbound_box_line`; -DROP TABLE IF EXISTS `C_inbound_box`; -DROP TABLE IF EXISTS `C_inbound_notice`; -DROP TABLE IF EXISTS `C_parcel_item`; -DROP TABLE IF EXISTS `C_parcel_arrival`; -DROP TABLE IF EXISTS `C_parcel`; -DROP TABLE IF EXISTS `C_inventory`; -DROP TABLE IF EXISTS `C_sku_platform_map`; -DROP TABLE IF EXISTS `C_sku`; -DROP TABLE IF EXISTS `C_bin_location`; +DROP TABLE IF EXISTS `c_inbound_box_line`; +DROP TABLE IF EXISTS `c_inbound_box`; +DROP TABLE IF EXISTS `c_inbound_notice`; +DROP TABLE IF EXISTS `c_parcel_item`; +DROP TABLE IF EXISTS `c_parcel_arrival`; +DROP TABLE IF EXISTS `c_parcel`; +DROP TABLE IF EXISTS `c_inventory`; +DROP TABLE IF EXISTS `c_sku_platform_map`; +DROP TABLE IF EXISTS `c_sku`; +DROP TABLE IF EXISTS `c_bin_location`; DROP TABLE IF EXISTS `t_sales_store_order_line`; DROP TABLE IF EXISTS `t_sales_store_order`; DROP TABLE IF EXISTS `t_sales_store`; @@ -1229,10 +1229,10 @@ CREATE TABLE `t_customer_account_flow` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户账户资金流水'; -- --------------------------------------------------------------------------- --- 仓储模块(C_*)+ 店铺订单(t_sales_store*) +-- 仓储模块(c_*)+ 店铺订单(t_sales_store*) -- --------------------------------------------------------------------------- -CREATE TABLE `C_sku` ( +CREATE TABLE `c_sku` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `CustomerId` INT NOT NULL COMMENT '客户ID → t_customer.Id', `SkuCode` VARCHAR(64) NOT NULL COMMENT '库内SKU编码', @@ -1254,7 +1254,7 @@ CREATE TABLE `C_sku` ( KEY `idx_c_sku_customer` (`CustomerId`, `IsDeleted`) ) COMMENT='仓储-SKU主数据'; -CREATE TABLE `C_bin_location` ( +CREATE TABLE `c_bin_location` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `WarehouseId` INT NOT NULL COMMENT '仓库ID → t_warehouse_address.Id', `BinCode` VARCHAR(32) NOT NULL COMMENT '库位编码', @@ -1273,12 +1273,12 @@ CREATE TABLE `C_bin_location` ( KEY `idx_c_bin_warehouse` (`WarehouseId`, `IsEnabled`) ) COMMENT='仓储-库位主数据'; -CREATE TABLE `C_inventory` ( +CREATE TABLE `c_inventory` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `CustomerId` INT NOT NULL COMMENT '客户ID', `WarehouseId` INT NOT NULL COMMENT '仓库ID', - `SkuId` INT NOT NULL COMMENT 'SKU ID → C_sku.Id', - `BinLocationId` INT NOT NULL COMMENT '库位ID → C_bin_location.Id', + `SkuId` INT NOT NULL COMMENT 'SKU ID → c_sku.Id', + `BinLocationId` INT NOT NULL COMMENT '库位ID → c_bin_location.Id', `Qty` INT NOT NULL DEFAULT 0 COMMENT '可用库存数量', `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是', `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳', @@ -1291,7 +1291,7 @@ CREATE TABLE `C_inventory` ( KEY `idx_c_inv_sku_wh` (`CustomerId`, `SkuId`, `WarehouseId`) ) COMMENT='仓储-库存余额(按库位)'; -CREATE TABLE `C_inbound_notice` ( +CREATE TABLE `c_inbound_notice` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `CustomerId` INT NOT NULL COMMENT '客户ID', `WarehouseId` INT NOT NULL COMMENT '仓库ID', @@ -1313,9 +1313,9 @@ CREATE TABLE `C_inbound_notice` ( KEY `idx_c_inbound_customer_status` (`CustomerId`, `ReceiptStatus`, `CreateTime`) ) COMMENT='仓储-入库预报主表'; -CREATE TABLE `C_inbound_box` ( +CREATE TABLE `c_inbound_box` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', - `InboundNoticeId` INT NOT NULL COMMENT '入库预报ID → C_inbound_notice.Id', + `InboundNoticeId` INT NOT NULL COMMENT '入库预报ID → c_inbound_notice.Id', `BoxSeqNo` VARCHAR(16) NOT NULL COMMENT '箱序号', `MarkBoxNo` VARCHAR(64) NULL COMMENT '箱唛号/箱号', `WeightG` DECIMAL(10,2) NULL COMMENT '箱重(g)', @@ -1333,14 +1333,14 @@ CREATE TABLE `C_inbound_box` ( KEY `idx_c_inbound_box_notice` (`InboundNoticeId`, `IsDeleted`) ) COMMENT='仓储-入库预报箱'; -CREATE TABLE `C_inbound_box_line` ( +CREATE TABLE `c_inbound_box_line` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', - `InboundBoxId` INT NOT NULL COMMENT '入库箱ID → C_inbound_box.Id', - `SkuId` INT NOT NULL COMMENT 'SKU ID → C_sku.Id', + `InboundBoxId` INT NOT NULL COMMENT '入库箱ID → c_inbound_box.Id', + `SkuId` INT NOT NULL COMMENT 'SKU ID → c_sku.Id', `SkuCode` VARCHAR(64) NOT NULL COMMENT '冗余SKU编码', `PlannedQty` INT NOT NULL DEFAULT 0 COMMENT '计划入库数量', `ReceivedQty` INT NULL COMMENT '实收数量', - `BinLocationId` INT NULL COMMENT '上架库位ID → C_bin_location.Id', + `BinLocationId` INT NULL COMMENT '上架库位ID → c_bin_location.Id', `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是', `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳', `CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人', @@ -1352,7 +1352,7 @@ CREATE TABLE `C_inbound_box_line` ( KEY `idx_c_inbound_box_line_sku` (`SkuId`) ) COMMENT='仓储-入库箱内SKU行'; -CREATE TABLE `C_parcel` ( +CREATE TABLE `c_parcel` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `CustomerId` INT NOT NULL COMMENT '客户ID', `WarehouseId` INT NOT NULL COMMENT '仓库ID', @@ -1372,11 +1372,11 @@ CREATE TABLE `C_parcel` ( KEY `idx_c_parcel_status` (`CustomerId`, `LifecycleStatus`) ) COMMENT='仓储-客户包裹'; -CREATE TABLE `C_parcel_arrival` ( +CREATE TABLE `c_parcel_arrival` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `CustomerId` INT NOT NULL COMMENT '客户ID', `WarehouseId` INT NOT NULL COMMENT '仓库ID', - `ParcelId` INT NULL COMMENT '关联包裹 C_parcel.Id', + `ParcelId` INT NULL COMMENT '关联包裹 c_parcel.Id', `TrackingNo` VARCHAR(64) NOT NULL COMMENT '国内快递单号', `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是', `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳', @@ -1389,14 +1389,14 @@ CREATE TABLE `C_parcel_arrival` ( KEY `idx_c_parcel_arrival_ctime` (`CreateTime`) ) COMMENT='仓储-快递到仓记录'; -CREATE TABLE `C_parcel_item` ( +CREATE TABLE `c_parcel_item` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', - `ParcelId` INT NOT NULL COMMENT '包裹ID → C_parcel.Id', + `ParcelId` INT NOT NULL COMMENT '包裹ID → c_parcel.Id', `ProductName` VARCHAR(512) NULL COMMENT '商品名称', `AttributeText` VARCHAR(256) NULL COMMENT '规格属性', `ImageUrl` VARCHAR(1024) NULL COMMENT '商品图片URL', `Quantity` INT NOT NULL DEFAULT 0 COMMENT '数量', - `SkuId` INT NULL COMMENT '库内SKU ID → C_sku.Id', + `SkuId` INT NULL COMMENT '库内SKU ID → c_sku.Id', `SkuCode` VARCHAR(64) NULL COMMENT '冗余SKU编码', `BinLocationId` INT NULL COMMENT '上架库位ID', `Remark` VARCHAR(512) NULL COMMENT '备注', @@ -1410,12 +1410,12 @@ CREATE TABLE `C_parcel_item` ( KEY `idx_c_parcel_item_parcel` (`ParcelId`, `IsDeleted`) ) COMMENT='仓储-包裹明细'; -CREATE TABLE `C_sku_platform_map` ( +CREATE TABLE `c_sku_platform_map` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `CustomerId` INT NOT NULL COMMENT '客户ID', `StoreId` INT NOT NULL COMMENT '店铺ID → t_sales_store.Id', `PlatformSku` VARCHAR(128) NOT NULL COMMENT '平台listing SKU', - `SkuId` INT NOT NULL COMMENT '库内SKU ID → C_sku.Id', + `SkuId` INT NOT NULL COMMENT '库内SKU ID → c_sku.Id', `SkuCode` VARCHAR(64) NOT NULL COMMENT '冗余SKU编码', `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是', `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳', @@ -1518,7 +1518,7 @@ CREATE TABLE `t_sales_store_order_line` ( `Quantity` INT NOT NULL DEFAULT 0 COMMENT '数量', `Currency` VARCHAR(8) NULL COMMENT '币种', `IsShipExcluded` TINYINT NOT NULL DEFAULT 0 COMMENT '是否拒绝发货', - `MappedSkuId` INT NULL COMMENT '映射库内SKU → C_sku.Id', + `MappedSkuId` INT NULL COMMENT '映射库内SKU → c_sku.Id', `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是', `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳', `CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人', diff --git a/sql/C_parcel_arrival.sql b/sql/C_parcel_arrival.sql index e4e3513..a891d1a 100644 --- a/sql/C_parcel_arrival.sql +++ b/sql/C_parcel_arrival.sql @@ -1,12 +1,12 @@ --- 已有库增量:快递到仓表 C_parcel_arrival +-- 已有库增量:快递到仓表 c_parcel_arrival -- 对应功能:操作 → 快递到仓;全量结构见根目录 db.sql -- 可重复执行(IF NOT EXISTS) -CREATE TABLE IF NOT EXISTS `C_parcel_arrival` ( +CREATE TABLE IF NOT EXISTS `c_parcel_arrival` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `CustomerId` INT NOT NULL COMMENT '客户ID', `WarehouseId` INT NOT NULL COMMENT '仓库ID', - `ParcelId` INT NULL COMMENT '关联包裹 C_parcel.Id', + `ParcelId` INT NULL COMMENT '关联包裹 c_parcel.Id', `TrackingNo` VARCHAR(64) NOT NULL COMMENT '国内快递单号', `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是', `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳', diff --git a/sql/C_parcel_item_AttributeText.sql b/sql/C_parcel_item_AttributeText.sql index 4eebf85..47dbc1a 100644 --- a/sql/C_parcel_item_AttributeText.sql +++ b/sql/C_parcel_item_AttributeText.sql @@ -1,20 +1,16 @@ --- 已有库增量:包裹明细增加属性字段 AttributeText +-- 已有库增量:c_parcel_item.AttributeText -- 可重复执行 SET @col_exists := ( SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'C_parcel_item' + AND TABLE_NAME = 'c_parcel_item' AND COLUMN_NAME = 'AttributeText' ); - SET @sql := IF( @col_exists = 0, - 'ALTER TABLE `C_parcel_item` ADD COLUMN `AttributeText` VARCHAR(256) NULL COMMENT ''规格属性'' AFTER `ProductName`', - 'SELECT ''C_parcel_item.AttributeText already exists'' AS info' + 'ALTER TABLE `c_parcel_item` ADD COLUMN `AttributeText` VARCHAR(256) NULL COMMENT ''规格属性'' AFTER `ProductName`', + 'SELECT ''c_parcel_item.AttributeText already exists'' AS info' ); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/sql/C_parcel_item_ImageUrl.sql b/sql/C_parcel_item_ImageUrl.sql index 01709ef..c9dd548 100644 --- a/sql/C_parcel_item_ImageUrl.sql +++ b/sql/C_parcel_item_ImageUrl.sql @@ -1,20 +1,16 @@ --- 已有库增量:包裹明细增加商品图片 ImageUrl +-- 已有库增量:c_parcel_item.ImageUrl -- 可重复执行 SET @col_exists := ( SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'C_parcel_item' + AND TABLE_NAME = 'c_parcel_item' AND COLUMN_NAME = 'ImageUrl' ); - SET @sql := IF( @col_exists = 0, - 'ALTER TABLE `C_parcel_item` ADD COLUMN `ImageUrl` VARCHAR(1024) NULL COMMENT ''商品图片URL'' AFTER `AttributeText`', - 'SELECT ''C_parcel_item.ImageUrl already exists'' AS info' + 'ALTER TABLE `c_parcel_item` ADD COLUMN `ImageUrl` VARCHAR(1024) NULL COMMENT ''商品图片URL'' AFTER `AttributeText`', + 'SELECT ''c_parcel_item.ImageUrl already exists'' AS info' ); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/sql/README.md b/sql/README.md index 073e2be..ddb4def 100644 --- a/sql/README.md +++ b/sql/README.md @@ -3,5 +3,5 @@ OMS / 共用库的建表脚本已统一到仓库根目录 `db.sql`。 - 新环境:直接执行 `db.sql`(会 DROP 后重建,勿在有生产数据的库上执行) -- 已有库:用 diff 对比 `db.sql` 与现库结构后手工 ALTER;本目录下单个表脚本(如 `C_parcel_arrival.sql`、`C_parcel_item_AttributeText.sql`、`C_parcel_item_ImageUrl.sql`)可直接在已有库执行增量建表 +- 已有库:用 diff 对比 `db.sql` 与现库结构后手工 ALTER;本目录下单个表脚本(如 `c_parcel_arrival.sql`、`c_parcel_item_AttributeText.sql`、`c_parcel_item_ImageUrl.sql`)可直接在已有库执行增量建表;若仍为旧名 `C_*`,先执行 `migrate_rename_warehouse_C_to_c.sql` - 数据权限表(`user_org_perm` 等)若 RBAC 与 OMS 分库,请只在 RBAC 库执行 `db.sql` 末尾对应段落 diff --git a/sql/fix_concurrency_stamp_missing.sql b/sql/fix_concurrency_stamp_missing.sql index 6658e5a..4faee18 100644 --- a/sql/fix_concurrency_stamp_missing.sql +++ b/sql/fix_concurrency_stamp_missing.sql @@ -8,7 +8,7 @@ -- -- 用法:在业务库执行本脚本;若仍报错,把报错 SQL 里的表名追加到下方列表。 -SET @tables := 't_order_stock_bind,C_inventory,C_parcel,C_parcel_item,C_parcel_arrival,C_sku,C_bin_location,C_inbound_notice,C_inbound_box,C_inbound_box_line,C_sku_platform_map,t_sales_store,t_sales_store_order,t_sales_store_order_line,t_customer_sender_address'; +SET @tables := 't_order_stock_bind,c_inventory,c_parcel,c_parcel_item,c_parcel_arrival,c_sku,c_bin_location,c_inbound_notice,c_inbound_box,c_inbound_box_line,c_sku_platform_map,t_sales_store,t_sales_store_order,t_sales_store_order_line,t_customer_sender_address'; DROP PROCEDURE IF EXISTS sp_add_concurrency_stamp_if_missing; DELIMITER $$ diff --git a/sql/migrate_rename_warehouse_C_to_c.sql b/sql/migrate_rename_warehouse_C_to_c.sql new file mode 100644 index 0000000..c41ef77 --- /dev/null +++ b/sql/migrate_rename_warehouse_C_to_c.sql @@ -0,0 +1,34 @@ +-- 仓储表 C_* → c_*(小写前缀,对齐 t_* 命名风格) +-- 可重复执行:源表不存在或目标表已存在则跳过 + +DROP PROCEDURE IF EXISTS sp_rename_warehouse_table_if_needed; +DELIMITER $$ +CREATE PROCEDURE sp_rename_warehouse_table_if_needed(IN old_name VARCHAR(128), IN new_name VARCHAR(128)) +BEGIN + IF EXISTS ( + SELECT 1 FROM information_schema.TABLES + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = old_name + ) AND NOT EXISTS ( + SELECT 1 FROM information_schema.TABLES + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = new_name + ) THEN + SET @ddl := CONCAT('RENAME TABLE `', old_name, '` TO `', new_name, '`'); + PREPARE stmt FROM @ddl; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; +END$$ +DELIMITER ; + +CALL sp_rename_warehouse_table_if_needed('C_sku', 'c_sku'); +CALL sp_rename_warehouse_table_if_needed('C_bin_location', 'c_bin_location'); +CALL sp_rename_warehouse_table_if_needed('C_inventory', 'c_inventory'); +CALL sp_rename_warehouse_table_if_needed('C_inbound_notice', 'c_inbound_notice'); +CALL sp_rename_warehouse_table_if_needed('C_inbound_box', 'c_inbound_box'); +CALL sp_rename_warehouse_table_if_needed('C_inbound_box_line', 'c_inbound_box_line'); +CALL sp_rename_warehouse_table_if_needed('C_parcel', 'c_parcel'); +CALL sp_rename_warehouse_table_if_needed('C_parcel_arrival', 'c_parcel_arrival'); +CALL sp_rename_warehouse_table_if_needed('C_parcel_item', 'c_parcel_item'); +CALL sp_rename_warehouse_table_if_needed('C_sku_platform_map', 'c_sku_platform_map'); + +DROP PROCEDURE IF EXISTS sp_rename_warehouse_table_if_needed; diff --git a/sql/migrate_t_order_stock_bind.sql b/sql/migrate_t_order_stock_bind.sql index 3df34ca..c27dcb5 100644 --- a/sql/migrate_t_order_stock_bind.sql +++ b/sql/migrate_t_order_stock_bind.sql @@ -1,4 +1,4 @@ --- 物流单绑定包裹/库存:t_order_stock_bind + C_inventory/C_parcel_item 冻结与可用字段 +-- 物流单绑定包裹/库存:t_order_stock_bind + c_inventory/c_parcel_item 冻结与可用字段 -- 见 oms系统设计-050801.md §4.2.11 / §4.4.6 / §3.1.2 -- ConcurrencyStamp 默认用固定 GUID,避免 DEFAULT (UUID()) 触发复制 1674 -- 可重复执行 @@ -10,10 +10,10 @@ CREATE TABLE IF NOT EXISTS `t_order_stock_bind` ( `WarehouseId` INT NOT NULL COMMENT '仓库', `BindType` VARCHAR(20) NOT NULL COMMENT 'inventory / parcelItem', `SkuId` INT NULL COMMENT 'SKU', - `InventoryId` INT NULL COMMENT 'C_inventory.Id,可空=按SKU分摊', + `InventoryId` INT NULL COMMENT 'c_inventory.Id,可空=按SKU分摊', `BinLocationId` INT NULL COMMENT '库位快照', - `ParcelId` INT NULL COMMENT 'C_parcel.Id', - `ParcelItemId` INT NULL COMMENT 'C_parcel_item.Id', + `ParcelId` INT NULL COMMENT 'c_parcel.Id', + `ParcelItemId` INT NULL COMMENT 'c_parcel_item.Id', `BindQty` INT NOT NULL COMMENT '绑定数量', `Status` VARCHAR(20) NOT NULL DEFAULT 'bound' COMMENT 'bound/deducted/released', `SkuCode` VARCHAR(64) NULL, @@ -47,60 +47,74 @@ SET @sql := IF( ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; --- C_inventory.FrozenQty -SET @col_exists := ( - SELECT COUNT(1) FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'C_inventory' AND COLUMN_NAME = 'FrozenQty' +-- c_inventory.FrozenQty(兼容尚未 rename 的旧表名 C_inventory) +SET @inv_table := IF( + EXISTS (SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'c_inventory'), + 'c_inventory', + IF( + EXISTS (SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'C_inventory'), + 'C_inventory', + NULL + ) ); -SET @sql := IF( - @col_exists = 0, - 'ALTER TABLE `C_inventory` ADD COLUMN `FrozenQty` INT NOT NULL DEFAULT 0 COMMENT ''冻结数量'' AFTER `Qty`', - 'SELECT ''C_inventory.FrozenQty already exists'' AS info' +SET @col_exists := IF(@inv_table IS NULL, 1, + (SELECT COUNT(1) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @inv_table AND COLUMN_NAME = 'FrozenQty') +); +SET @sql := IF(@inv_table IS NULL OR @col_exists > 0, + 'SELECT ''c_inventory.FrozenQty already exists or table missing'' AS info', + CONCAT('ALTER TABLE `', @inv_table, '` ADD COLUMN `FrozenQty` INT NOT NULL DEFAULT 0 COMMENT ''冻结数量'' AFTER `Qty`') ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; --- C_inventory.AvailableQty -SET @col_exists := ( - SELECT COUNT(1) FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'C_inventory' AND COLUMN_NAME = 'AvailableQty' +SET @col_exists := IF(@inv_table IS NULL, 1, + (SELECT COUNT(1) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @inv_table AND COLUMN_NAME = 'AvailableQty') ); -SET @sql := IF( - @col_exists = 0, - 'ALTER TABLE `C_inventory` ADD COLUMN `AvailableQty` INT NOT NULL DEFAULT 0 COMMENT ''可用=Qty-FrozenQty'' AFTER `FrozenQty`', - 'SELECT ''C_inventory.AvailableQty already exists'' AS info' +SET @sql := IF(@inv_table IS NULL OR @col_exists > 0, + 'SELECT ''c_inventory.AvailableQty already exists or table missing'' AS info', + CONCAT('ALTER TABLE `', @inv_table, '` ADD COLUMN `AvailableQty` INT NOT NULL DEFAULT 0 COMMENT ''可用=Qty-FrozenQty'' AFTER `FrozenQty`') ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -UPDATE `C_inventory` -SET `AvailableQty` = GREATEST(0, `Qty` - IFNULL(`FrozenQty`, 0)) -WHERE `AvailableQty` <> GREATEST(0, `Qty` - IFNULL(`FrozenQty`, 0)) - OR (`AvailableQty` = 0 AND `Qty` > 0 AND IFNULL(`FrozenQty`, 0) = 0); - --- C_parcel_item.FrozenQty -SET @col_exists := ( - SELECT COUNT(1) FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'C_parcel_item' AND COLUMN_NAME = 'FrozenQty' -); -SET @sql := IF( - @col_exists = 0, - 'ALTER TABLE `C_parcel_item` ADD COLUMN `FrozenQty` INT NOT NULL DEFAULT 0 COMMENT ''冻结数量'' AFTER `Quantity`', - 'SELECT ''C_parcel_item.FrozenQty already exists'' AS info' +SET @sql := IF(@inv_table IS NULL, + 'SELECT ''c_inventory table missing'' AS info', + CONCAT('UPDATE `', @inv_table, '` SET `AvailableQty` = GREATEST(0, `Qty` - IFNULL(`FrozenQty`, 0)) WHERE `AvailableQty` <> GREATEST(0, `Qty` - IFNULL(`FrozenQty`, 0)) OR (`AvailableQty` = 0 AND `Qty` > 0 AND IFNULL(`FrozenQty`, 0) = 0)') ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; --- C_parcel_item.AvailableQty -SET @col_exists := ( - SELECT COUNT(1) FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'C_parcel_item' AND COLUMN_NAME = 'AvailableQty' +-- c_parcel_item.FrozenQty +SET @pi_table := IF( + EXISTS (SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'c_parcel_item'), + 'c_parcel_item', + IF( + EXISTS (SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'C_parcel_item'), + 'C_parcel_item', + NULL + ) ); -SET @sql := IF( - @col_exists = 0, - 'ALTER TABLE `C_parcel_item` ADD COLUMN `AvailableQty` INT NOT NULL DEFAULT 0 COMMENT ''可用=Quantity-FrozenQty'' AFTER `FrozenQty`', - 'SELECT ''C_parcel_item.AvailableQty already exists'' AS info' +SET @col_exists := IF(@pi_table IS NULL, 1, + (SELECT COUNT(1) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @pi_table AND COLUMN_NAME = 'FrozenQty') +); +SET @sql := IF(@pi_table IS NULL OR @col_exists > 0, + 'SELECT ''c_parcel_item.FrozenQty already exists or table missing'' AS info', + CONCAT('ALTER TABLE `', @pi_table, '` ADD COLUMN `FrozenQty` INT NOT NULL DEFAULT 0 COMMENT ''冻结数量'' AFTER `Quantity`') ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -UPDATE `C_parcel_item` -SET `AvailableQty` = GREATEST(0, `Quantity` - IFNULL(`FrozenQty`, 0)) -WHERE `AvailableQty` <> GREATEST(0, `Quantity` - IFNULL(`FrozenQty`, 0)) - OR (`AvailableQty` = 0 AND `Quantity` > 0 AND IFNULL(`FrozenQty`, 0) = 0); +SET @col_exists := IF(@pi_table IS NULL, 1, + (SELECT COUNT(1) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @pi_table AND COLUMN_NAME = 'AvailableQty') +); +SET @sql := IF(@pi_table IS NULL OR @col_exists > 0, + 'SELECT ''c_parcel_item.AvailableQty already exists or table missing'' AS info', + CONCAT('ALTER TABLE `', @pi_table, '` ADD COLUMN `AvailableQty` INT NOT NULL DEFAULT 0 COMMENT ''可用=Quantity-FrozenQty'' AFTER `FrozenQty`') +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := IF(@pi_table IS NULL, + 'SELECT ''c_parcel_item table missing'' AS info', + CONCAT('UPDATE `', @pi_table, '` SET `AvailableQty` = GREATEST(0, `Quantity` - IFNULL(`FrozenQty`, 0)) WHERE `AvailableQty` <> GREATEST(0, `Quantity` - IFNULL(`FrozenQty`, 0)) OR (`AvailableQty` = 0 AND `Quantity` > 0 AND IFNULL(`FrozenQty`, 0) = 0)') +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/src/Oms.Application/Dtos/Orders/OrderBatchPayloadDtos.cs b/src/Oms.Application/Dtos/Orders/OrderBatchPayloadDtos.cs index 8d3c032..43cc7a2 100644 --- a/src/Oms.Application/Dtos/Orders/OrderBatchPayloadDtos.cs +++ b/src/Oms.Application/Dtos/Orders/OrderBatchPayloadDtos.cs @@ -40,6 +40,8 @@ public class OrderBatchImportRowPayload public int TemplateId { get; set; } + public int? CustomerSenderAddressId { get; set; } + public string RowDataJson { get; set; } = string.Empty; } @@ -53,4 +55,9 @@ public class OrderBatchImportJobPayload public int TemplateId { get; set; } public int CustomerId { get; set; } + + /// + /// 批量导入统一使用的默认发件地址。 + /// + public int? CustomerSenderAddressId { get; set; } } diff --git a/src/Oms.Application/Dtos/Orders/OrderTemplateMappingDtos.cs b/src/Oms.Application/Dtos/Orders/OrderTemplateMappingDtos.cs index f6c678a..29a9d63 100644 --- a/src/Oms.Application/Dtos/Orders/OrderTemplateMappingDtos.cs +++ b/src/Oms.Application/Dtos/Orders/OrderTemplateMappingDtos.cs @@ -27,6 +27,8 @@ public class OrderTemplateFieldSchemaItemOutput public string Group { get; set; } = string.Empty; public string Example { get; set; } = string.Empty; + + public string Hint { get; set; } = string.Empty; } public class OrderTemplateDictOptionsOutput diff --git a/src/Oms.Application/Dtos/Orders/OrderUploadInput.cs b/src/Oms.Application/Dtos/Orders/OrderUploadInput.cs index 34463ba..ee4028b 100644 --- a/src/Oms.Application/Dtos/Orders/OrderUploadInput.cs +++ b/src/Oms.Application/Dtos/Orders/OrderUploadInput.cs @@ -16,4 +16,9 @@ public class OrderUploadInput /// 模板主键。 /// public int TemplateId { get; set; } + + /// + /// 默认发件地址 ID(地址管理中的发件人地址)。 + /// + public int? SenderAddressId { get; set; } } diff --git a/src/Oms.Application/Services/Orders/OrderService.BatchCreate.cs b/src/Oms.Application/Services/Orders/OrderService.BatchCreate.cs index 510e863..2fc3084 100644 --- a/src/Oms.Application/Services/Orders/OrderService.BatchCreate.cs +++ b/src/Oms.Application/Services/Orders/OrderService.BatchCreate.cs @@ -30,7 +30,21 @@ public partial class OrderService ?? throw new BusinessException("行数据无效"); var input = OrderTemplateRowMapper.MapToParcelCreateInput(sourceValues, mapping, payload.CustomerId); - return await CreateParcelForBatchAsync(input, operatorUserId, cancellationToken); + if (payload.CustomerSenderAddressId is > 0) + { + await ApplyCustomerSenderAddressToParcelInputAsync( + input, + payload.CustomerId, + payload.CustomerSenderAddressId.Value, + cancellationToken); + } + + return await CreateParcelForBatchAsync( + input, + operatorUserId, + orderSource: null, + customerSenderAddressId: payload.CustomerSenderAddressId, + cancellationToken); } public async Task TryResolveImportOrderIdAsync( @@ -78,24 +92,42 @@ public partial class OrderService int operatorUserId, CancellationToken cancellationToken = default) { - return await CreateParcelForBatchAsync(input, operatorUserId, orderSource: null, cancellationToken); + return await CreateParcelForBatchAsync(input, operatorUserId, orderSource: null, customerSenderAddressId: null, cancellationToken); + } + + /// + /// 批量 Worker 创建小包订单(可指定 与默认发件地址)。 + /// + public async Task CreateParcelForBatchAsync( + OrderParcelCreateInput input, + int operatorUserId, + string? orderSource, + int? customerSenderAddressId, + CancellationToken cancellationToken = default) + { + ParcelCreateContext? createContext = null; + if (!string.IsNullOrWhiteSpace(orderSource) || customerSenderAddressId is > 0) + { + createContext = new ParcelCreateContext + { + OrderSource = string.IsNullOrWhiteSpace(orderSource) ? null : orderSource.Trim(), + CustomerSenderAddressId = customerSenderAddressId, + }; + } + + return await CreateParcelCoreAsync(input, operatorUserId, createContext); } /// /// 批量 Worker 创建小包订单(可指定 )。 /// + [Obsolete("Use CreateParcelForBatchAsync with customerSenderAddressId when importing.")] public async Task CreateParcelForBatchAsync( OrderParcelCreateInput input, int operatorUserId, string? orderSource, CancellationToken cancellationToken = default) { - ParcelCreateContext? createContext = null; - if (!string.IsNullOrWhiteSpace(orderSource)) - { - createContext = new ParcelCreateContext { OrderSource = orderSource.Trim() }; - } - - return await CreateParcelCoreAsync(input, operatorUserId, createContext); + return await CreateParcelForBatchAsync(input, operatorUserId, orderSource, null, cancellationToken); } } diff --git a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs index ad93154..14c7ff0 100644 --- a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs +++ b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs @@ -380,6 +380,11 @@ public partial class OrderService throw new BusinessException($"单次最多导入 {ParcelBatchMaxCount} 行"); } + var senderAddressId = await ResolveImportSenderAddressIdAsync( + effectiveCustomer.Id, + input.SenderAddressId, + cancellationToken: default); + var taskName = $"批量下单_{DateTime.Now:yyyyMMddHHmmss}"; var requestId = $"import-{effectiveCustomer.Id}-{template.Id}-{DateTime.Now:yyyyMMddHHmmssfff}"; @@ -388,6 +393,7 @@ public partial class OrderService TaskName = taskName, TemplateId = template.Id, CustomerId = effectiveCustomer.Id, + CustomerSenderAddressId = senderAddressId, }); var payloadItems = parsedRows.Select(row => @@ -404,6 +410,7 @@ public partial class OrderService RowNumber = row.RowNumber, CustomerId = effectiveCustomer.Id, TemplateId = template.Id, + CustomerSenderAddressId = senderAddressId, RowDataJson = JsonConvert.SerializeObject(row.SourceValues), }), }; diff --git a/src/Oms.Application/Services/Orders/OrderService.SenderAddress.cs b/src/Oms.Application/Services/Orders/OrderService.SenderAddress.cs index c05d4e7..51287ce 100644 --- a/src/Oms.Application/Services/Orders/OrderService.SenderAddress.cs +++ b/src/Oms.Application/Services/Orders/OrderService.SenderAddress.cs @@ -298,4 +298,76 @@ public partial class OrderService { return string.IsNullOrWhiteSpace(value) ? null : value.Trim(); } + + private async Task ResolveImportSenderAddressIdAsync( + int customerId, + int? senderAddressId, + CancellationToken cancellationToken = default) + { + if (senderAddressId is > 0) + { + var specified = await CustomerSenderAddressRepository.GetOwnedAsync(senderAddressId.Value, customerId, cancellationToken) + ?? throw new BusinessException("发件地址不存在"); + EnsureSenderAddressType(specified); + return specified.Id; + } + + var defaultAddress = await CustomerSenderAddressRepository.FirstOrDefaultAsync( + x => x.CustomerId == customerId + && x.AddressType == CustomerAddressTypeCodes.Sender + && x.IsDefault > 0, + cancellationToken); + if (defaultAddress != null) + { + return defaultAddress.Id; + } + + throw new BusinessException("请先在批量下单页选择默认发件地址,或在地址管理中设置默认发件地址"); + } + + private static void EnsureSenderAddressType(CustomerSenderAddress address) + { + var addressType = string.IsNullOrWhiteSpace(address.AddressType) + ? CustomerAddressTypeCodes.Sender + : address.AddressType; + if (!string.Equals(addressType, CustomerAddressTypeCodes.Sender, StringComparison.OrdinalIgnoreCase)) + { + throw new BusinessException("请选择发件类型地址"); + } + } + + private async Task ApplyCustomerSenderAddressToParcelInputAsync( + OrderParcelCreateInput input, + int customerId, + int senderAddressId, + CancellationToken cancellationToken = default) + { + var address = await CustomerSenderAddressRepository.GetOwnedAsync(senderAddressId, customerId, cancellationToken) + ?? throw new BusinessException("发件地址不存在"); + EnsureSenderAddressType(address); + + string? countryCode = null; + if (address.Country.HasValue) + { + var country = await CountryRepository.FirstOrDefaultAsync(x => x.Id == address.Country.Value, cancellationToken); + countryCode = country?.CountryCode; + } + + input.Sender = new OrderParcelSenderInput + { + SenderName = address.SenderName, + Company = address.Company, + Phone = address.Phone, + TelePhone = address.TelePhone, + Email = address.Email, + CountryCode = countryCode ?? "CN", + Province = address.Province, + City = address.City, + ZipCode = address.ZipCode, + StreetAddress1 = address.StreetAddress1, + StreetAddress2 = address.StreetAddress2, + StreetAddress3 = address.StreetAddress3, + HouseNumber = address.HouseNumber, + }; + } } diff --git a/src/Oms.Application/Services/Orders/OrderService.Template.cs b/src/Oms.Application/Services/Orders/OrderService.Template.cs index 43ab1e0..e838702 100644 --- a/src/Oms.Application/Services/Orders/OrderService.Template.cs +++ b/src/Oms.Application/Services/Orders/OrderService.Template.cs @@ -17,12 +17,12 @@ public partial class OrderService [HttpGet("/order/template/field-schema")] public Task GetTemplateFieldSchemaAsync([FromQuery] string? templateKey) { - var key = string.IsNullOrWhiteSpace(templateKey) ? OrderTemplateKeys.DedicatedSmallParcel : templateKey.Trim(); + var key = string.IsNullOrWhiteSpace(templateKey) ? OrderTemplateKeys.DedicatedSmallParcelStandard : templateKey.Trim(); var fields = OrderTemplateFieldRegistry.GetFields(key); return Task.FromResult(new OrderTemplateFieldSchemaOutput { TemplateKey = key, - TemplateName = key == OrderTemplateKeys.DedicatedSmallParcel ? "专线小包模板" : key, + TemplateName = OrderTemplateFieldRegistry.GetTemplateDisplayName(key), Fields = fields.Select(MapFieldSchemaItem).ToList() }); } @@ -79,13 +79,13 @@ public partial class OrderService customerId, WebHostEnvironment); - var suggested = OrderTemplateHeaderMatcher.BuildSuggestedMapping(OrderTemplateKeys.DedicatedSmallParcel, headers); + var suggested = OrderTemplateHeaderMatcher.BuildSuggestedMapping(OrderTemplateKeys.DedicatedSmallParcelStandard, headers); var entity = new OrderTemplate { Name = name, Type = "custom", TemplateKey = $"custom_{Guid.NewGuid():N}".Substring(0, 24), - StandardTemplateKey = OrderTemplateKeys.DedicatedSmallParcel, + StandardTemplateKey = OrderTemplateKeys.DedicatedSmallParcelStandard, MappingStatus = OrderTemplateMappingStatus.Draft, CustomerId = customerId, SourceFileName = originalName, @@ -236,23 +236,39 @@ public partial class OrderService } /// - /// 列表查询前确保标准「专线小包模板」存在(未执行种子脚本时自动补齐)。 + /// 列表查询前确保标准「专线小包」精简版与标准版模板存在。 /// private async Task EnsureDedicatedSmallParcelStandardTemplateAsync() + { + await EnsureBuiltinStandardTemplateAsync( + OrderTemplateKeys.DedicatedSmallParcelStandard, + "专线小包模板(标准版)"); + await EnsureBuiltinStandardTemplateAsync( + OrderTemplateKeys.DedicatedSmallParcelSimple, + "专线小包模板(精简版)"); + } + + private async Task EnsureBuiltinStandardTemplateAsync(string templateKey, string name) { var existing = await OrderTemplateRepository.FirstOrDefaultAsync(x => - x.Type == "standard" && x.TemplateKey == OrderTemplateKeys.DedicatedSmallParcel); + x.Type == "standard" && x.TemplateKey == templateKey); if (existing != null) { + if (!string.Equals(existing.Name, name, StringComparison.Ordinal)) + { + existing.Name = name; + await OrderTemplateRepository.UpdateAsync(existing); + } + return; } await OrderTemplateRepository.InsertAsync(new OrderTemplate { - Name = "专线小包模板", + Name = name, Type = "standard", - TemplateKey = OrderTemplateKeys.DedicatedSmallParcel, - StandardTemplateKey = OrderTemplateKeys.DedicatedSmallParcel, + TemplateKey = templateKey, + StandardTemplateKey = templateKey, MappingStatus = OrderTemplateMappingStatus.Ready, CustomerId = null, }); @@ -283,7 +299,8 @@ public partial class OrderService DataType = field.DataType, DictType = field.DictType, Group = field.Group, - Example = field.Example + Example = field.Example, + Hint = field.Hint, }; private static List DeserializeStringList(string? json) diff --git a/src/Oms.Application/Services/Orders/Templates/OrderTemplateExcelGenerator.cs b/src/Oms.Application/Services/Orders/Templates/OrderTemplateExcelGenerator.cs index 4f2cdfc..7d0e0cd 100644 --- a/src/Oms.Application/Services/Orders/Templates/OrderTemplateExcelGenerator.cs +++ b/src/Oms.Application/Services/Orders/Templates/OrderTemplateExcelGenerator.cs @@ -26,7 +26,21 @@ public static class OrderTemplateExcelGenerator emptyDataRow[field.Label] = string.Empty; } - var guideRows = fields.Select(f => new Dictionary + var guideIntro = new Dictionary + { + ["列名"] = "【填写说明】", + ["字段Key"] = string.Empty, + ["是否必填"] = string.Empty, + ["数据类型"] = string.Empty, + ["字典类型"] = string.Empty, + ["可选值"] = string.Empty, + ["分组"] = string.Empty, + ["示例"] = string.Empty, + ["填写说明"] = BuildGuideIntroText(templateKey), + }; + + var guideRows = new List> { guideIntro }; + guideRows.AddRange(fields.Select(f => new Dictionary { ["列名"] = f.Label, ["字段Key"] = f.FieldKey, @@ -34,9 +48,10 @@ public static class OrderTemplateExcelGenerator ["数据类型"] = f.DataType, ["字典类型"] = f.DictType ?? string.Empty, ["可选值"] = BuildGuideOptionText(f, dictOptions ?? []), - ["分组"] = f.Group, - ["示例"] = f.Example - }).ToList(); + ["分组"] = MapGroupLabel(f.Group), + ["示例"] = f.Example, + ["填写说明"] = string.IsNullOrWhiteSpace(f.Hint) ? f.Example : f.Hint, + })); var dictRows = (dictOptions ?? []) .Select(x => new Dictionary @@ -91,4 +106,24 @@ public static class OrderTemplateExcelGenerator return options.Count == 0 ? string.Empty : string.Join(";", options); } + + private static string MapGroupLabel(string group) => + group switch + { + "basic" => "订单信息", + "receiver" => "收件人", + "declaration" => "申报", + _ => group, + }; + + private static string BuildGuideIntroText(string templateKey) + { + var common = "红色表头为必填;带下拉的列请点选编码;发件人请在网页批量下单页选择默认地址,无需在 Excel 填写。"; + if (OrderTemplateFieldRegistry.IsSimpleTemplate(templateKey)) + { + return $"【精简版】仅含日常批量所需核心列,推荐大多数客户使用。{common}"; + } + + return $"【标准版】含 COD、税号、申报扩展字段等不常用列,有特殊清关/平台要求时使用。{common}"; + } } diff --git a/src/Oms.Application/Services/Orders/Templates/OrderTemplateExcelOpenXmlEnhancer.cs b/src/Oms.Application/Services/Orders/Templates/OrderTemplateExcelOpenXmlEnhancer.cs index 71ce87e..f0ebd34 100644 --- a/src/Oms.Application/Services/Orders/Templates/OrderTemplateExcelOpenXmlEnhancer.cs +++ b/src/Oms.Application/Services/Orders/Templates/OrderTemplateExcelOpenXmlEnhancer.cs @@ -11,7 +11,8 @@ internal static class OrderTemplateExcelOpenXmlEnhancer { private const uint DataValidationStartRow = 2; private const uint DataValidationEndRow = 5000; - private const string YellowHeaderRgb = "FFFFFF00"; + private const string RequiredHeaderFillRgb = "FFFFE5E5"; + private const string RequiredHeaderFontRgb = "FFC00000"; public static byte[] Enhance( byte[] workbookBytes, @@ -96,6 +97,10 @@ internal static class OrderTemplateExcelOpenXmlEnhancer AllowBlank = !field.Required, ShowInputMessage = true, ShowErrorMessage = true, + ErrorTitle = "输入无效", + Error = field.Required ? "请从下拉列表选择或填写有效值" : "请填写有效值", + PromptTitle = field.Label, + Prompt = BuildValidationPrompt(field), SequenceOfReferences = new ListValue { InnerText = $"{columnName}{DataValidationStartRow}:{columnName}{DataValidationEndRow}" @@ -130,6 +135,21 @@ internal static class OrderTemplateExcelOpenXmlEnhancer return null; } + private static string BuildValidationPrompt(OrderTemplateFieldDefinition field) + { + if (!string.IsNullOrWhiteSpace(field.Hint)) + { + return field.Hint; + } + + if (string.Equals(field.DataType, "boolean", StringComparison.OrdinalIgnoreCase)) + { + return "填写 true 或 false"; + } + + return field.Required ? "必填" : "选填"; + } + private static Dictionary BuildDictTypeRanges( WorkbookPart workbookPart, IReadOnlyList dictOptions) @@ -255,20 +275,22 @@ internal static class OrderTemplateExcelOpenXmlEnhancer cellFormats.Count = 1; } - var yellowFill = new Fill + var requiredFill = new Fill { PatternFill = new PatternFill { PatternType = PatternValues.Solid, - ForegroundColor = new ForegroundColor { Rgb = YellowHeaderRgb }, + ForegroundColor = new ForegroundColor { Rgb = RequiredHeaderFillRgb }, BackgroundColor = new BackgroundColor { Indexed = 64 } } }; - fills.Append(yellowFill); - var yellowFillId = (uint)fills.ChildElements.Count - 1; + fills.Append(requiredFill); + var requiredFillId = (uint)fills.ChildElements.Count - 1; fills.Count = (uint)fills.ChildElements.Count; - var headerFont = new Font(new Bold()); + var headerFont = new Font( + new Bold(), + new Color { Rgb = RequiredHeaderFontRgb }); fonts.Append(headerFont); var headerFontId = (uint)fonts.ChildElements.Count - 1; fonts.Count = (uint)fonts.ChildElements.Count; @@ -276,7 +298,7 @@ internal static class OrderTemplateExcelOpenXmlEnhancer var headerFormat = new CellFormat { FontId = headerFontId, - FillId = yellowFillId, + FillId = requiredFillId, BorderId = 0, ApplyFont = true, ApplyFill = true diff --git a/src/Oms.Application/Services/Orders/Templates/OrderTemplateFieldRegistry.cs b/src/Oms.Application/Services/Orders/Templates/OrderTemplateFieldRegistry.cs index c7d18f3..ba2043d 100644 --- a/src/Oms.Application/Services/Orders/Templates/OrderTemplateFieldRegistry.cs +++ b/src/Oms.Application/Services/Orders/Templates/OrderTemplateFieldRegistry.cs @@ -2,7 +2,14 @@ namespace Oms.Application.Services.Orders.Templates; public static class OrderTemplateKeys { - public const string DedicatedSmallParcel = "dedicated_small_parcel_v1"; + /// 专线小包标准版(含扩展字段)。 + public const string DedicatedSmallParcelStandard = "dedicated_small_parcel_v1"; + + /// 专线小包精简版(日常批量推荐)。 + public const string DedicatedSmallParcelSimple = "dedicated_small_parcel_simple_v1"; + + /// 自定义模板字段映射默认对齐标准版字段集。 + public const string DedicatedSmallParcel = DedicatedSmallParcelStandard; } public static class OrderTemplateMappingStatus @@ -20,20 +27,37 @@ public sealed class OrderTemplateFieldDefinition public string? DictType { get; init; } public string Group { get; init; } = string.Empty; public string Example { get; init; } = string.Empty; + public string Hint { get; init; } = string.Empty; public int Sort { get; init; } } public static class OrderTemplateFieldRegistry { - private static readonly IReadOnlyList DedicatedSmallParcelFields = BuildDedicatedSmallParcelFields(); + private static readonly IReadOnlyList SimpleFields = BuildDedicatedSmallParcelSimpleFields(); + private static readonly IReadOnlyList StandardFields = BuildDedicatedSmallParcelStandardFields(); + + public static string GetTemplateDisplayName(string? templateKey) + { + var key = NormalizeTemplateKey(templateKey); + return key switch + { + OrderTemplateKeys.DedicatedSmallParcelSimple => "专线小包模板(精简版)", + OrderTemplateKeys.DedicatedSmallParcelStandard => "专线小包模板(标准版)", + _ => key, + }; + } + + public static bool IsSimpleTemplate(string? templateKey) => + string.Equals(NormalizeTemplateKey(templateKey), OrderTemplateKeys.DedicatedSmallParcelSimple, StringComparison.OrdinalIgnoreCase); public static IReadOnlyList GetFields(string? templateKey) { - var key = string.IsNullOrWhiteSpace(templateKey) ? OrderTemplateKeys.DedicatedSmallParcel : templateKey.Trim(); + var key = NormalizeTemplateKey(templateKey); return key switch { - OrderTemplateKeys.DedicatedSmallParcel => DedicatedSmallParcelFields, - _ => DedicatedSmallParcelFields + OrderTemplateKeys.DedicatedSmallParcelSimple => SimpleFields, + OrderTemplateKeys.DedicatedSmallParcelStandard => StandardFields, + _ => StandardFields, }; } @@ -48,11 +72,22 @@ public static class OrderTemplateFieldRegistry string.Equals(x.FieldKey, fieldKey.Trim(), StringComparison.OrdinalIgnoreCase)); } - private static IReadOnlyList BuildDedicatedSmallParcelFields() + private static string NormalizeTemplateKey(string? templateKey) => + string.IsNullOrWhiteSpace(templateKey) ? OrderTemplateKeys.DedicatedSmallParcelStandard : templateKey.Trim(); + + private static IReadOnlyList BuildDedicatedSmallParcelSimpleFields() { var list = new List(); var sort = 0; - void Add(string key, string label, bool required, string dataType = "string", string? dictType = null, string group = "basic", string example = "") + void Add( + string key, + string label, + bool required, + string dataType = "string", + string? dictType = null, + string group = "basic", + string example = "", + string hint = "") { sort += 10; list.Add(new OrderTemplateFieldDefinition @@ -64,87 +99,133 @@ public static class OrderTemplateFieldRegistry DictType = dictType, Group = group, Example = example, - Sort = sort + Hint = hint, + Sort = sort, }); } - Add("requestId", "幂等请求ID", false); - Add("clientNo", "客户单号", true, example: "C202604230001"); - 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"); - Add("warehouseId", "仓库", true, "select", "warehouse", "basic", "1"); - Add("length", "长(cm)", false, "number", example: "10"); - Add("width", "宽(cm)", false, "number", example: "10"); - Add("height", "高(cm)", false, "number", example: "10"); - Add("actualWeight", "实重(kg)", true, "number", example: "1.2"); - Add("isCod", "是否COD", false, "boolean", example: "false"); - Add("codCurrency", "COD币种", false, "select", "currency", example: "USD"); - Add("codSum", "COD金额", false, "number", example: "0"); - Add("quantity", "包裹数量", true, "number", example: "1"); + Add("clientNo", "客户单号", true, example: "C202604230001", hint: "您自己的订单编号,同一客户不可重复"); + Add("productCode", "运输方式", true, "select", "sales_product", "basic", "PROD001", hint: "从下拉选择产品编码(与查价一致)"); + Add("cargoAttribute", "货物属性", true, "select", "cargo_attribute", "basic", "general", hint: "普货选 general,带电/敏感品选对应用编码"); + Add("warehouseId", "发货仓库", true, "select", "warehouse", "basic", "1", hint: "从下拉选择发货仓库 ID"); + Add("actualWeight", "实重(kg)", true, "number", example: "1.2", hint: "包裹实际重量,单位千克"); + Add("quantity", "包裹件数", true, "number", example: "1", hint: "一般为 1"); + Add("length", "长(cm)", false, "number", example: "10", hint: "选填,用于材积计费"); + Add("width", "宽(cm)", false, "number", example: "10", hint: "选填,用于材积计费"); + Add("height", "高(cm)", false, "number", example: "10", hint: "选填,用于材积计费"); + Add("exportCustomsMode", "出口报关", false, "select", "export_customs_mode", "basic", "9610Simple", hint: "不选则按简易申报"); - Add("receiver.recipientsName", "收件人名称", true, group: "receiver", example: "Demo Receiver"); - Add("receiver.companyName", "收件人公司", false, group: "receiver", example: "Demo Receiver LLC"); - Add("receiver.phone", "收件人手机", true, group: "receiver", example: "13900139000"); - Add("receiver.telePhone", "收件人电话", false, group: "receiver"); - Add("receiver.email", "收件人邮箱", false, group: "receiver", example: "receiver@example.com"); - Add("receiver.countryCode", "收件国家代码", true, group: "receiver", example: "US"); - Add("receiver.province", "收件省/州", false, group: "receiver", example: "California"); - Add("receiver.city", "收件城市", false, group: "receiver", example: "Los Angeles"); - Add("receiver.streetNumber", "收件人门牌号", false, group: "receiver", example: "1001"); - Add("receiver.streetAddress1", "收件地址1", true, group: "receiver", example: "Sunset Blvd"); - Add("receiver.streetAddress2", "收件地址2", false, group: "receiver"); - Add("receiver.streetAddress3", "收件地址3", false, group: "receiver"); - Add("receiver.zipCode", "收件邮编", true, group: "receiver", example: "90001"); - Add("receiver.taxType", "税号类型", false, group: "receiver", example: "0"); - Add("receiver.taxNo", "税号", false, group: "receiver"); - Add("receiver.certificateType", "证件类型", false, group: "receiver", example: "0"); - Add("receiver.certificateNo", "证件号码", false, group: "receiver"); - Add("receiver.eori", "EORI", false, group: "receiver"); - Add("receiver.vat", "VAT", false, group: "receiver"); + Add("receiver.recipientsName", "收件人姓名", true, group: "receiver", example: "John Smith", hint: "必填"); + Add("receiver.phone", "收件人手机", true, group: "receiver", example: "13900139000", hint: "必填"); + Add("receiver.countryCode", "目的国代码", true, group: "receiver", example: "US", hint: "二字码,如 US、GB"); + Add("receiver.province", "省/州", false, group: "receiver", example: "California", hint: "建议填写"); + Add("receiver.city", "城市", false, group: "receiver", example: "Los Angeles", hint: "建议填写"); + Add("receiver.streetAddress1", "详细地址", true, group: "receiver", example: "100 Main St", hint: "街道门牌等主地址"); + Add("receiver.zipCode", "邮编", true, group: "receiver", example: "90001", hint: "必填"); + Add("receiver.companyName", "收件公司", false, group: "receiver", example: "Demo LLC", hint: "选填"); - Add("sender.senderName", "发件人姓名", false, group: "sender", example: "Demo Sender"); - Add("sender.company", "发件人公司", false, group: "sender", example: "Demo Sender Co.,Ltd"); - Add("sender.phone", "发件人手机", false, group: "sender", example: "13800138000"); - Add("sender.telePhone", "发件人电话", false, group: "sender"); - Add("sender.email", "发件人邮箱", false, group: "sender", example: "sender@example.com"); - Add("sender.countryCode", "发件国家代码", false, group: "sender", example: "CN"); - Add("sender.province", "发件省/州", false, group: "sender", example: "Guangdong"); - Add("sender.city", "发件城市", false, group: "sender", example: "Shenzhen"); - Add("sender.zipCode", "发件邮编", false, group: "sender", example: "518000"); - Add("sender.streetAddress1", "发件地址1", false, group: "sender", example: "Nanshan District"); - Add("sender.streetAddress2", "发件地址2", false, group: "sender"); - Add("sender.streetAddress3", "发件地址3", false, group: "sender"); - Add("sender.houseNumber", "发件门牌号", false, group: "sender", example: "88"); + Add("declaredCurrency", "申报币种", true, "select", "currency", "declaration", "USD", hint: "默认 USD"); + Add("items[0].cnName", "申报品名(中文)", false, group: "declaration", example: "手机壳", hint: "选填"); + Add("items[0].enName", "申报品名(英文)", true, group: "declaration", example: "Phone Case", hint: "必填,用于清关"); + Add("items[0].unitPrice", "申报单价", true, "number", group: "declaration", example: "12.5", hint: "与申报币种一致"); + Add("items[0].unitWeight", "单品重量(kg)", true, "number", group: "declaration", example: "0.3", hint: "单个 SKU 重量"); + Add("items[0].quantity", "申报数量", true, "number", group: "declaration", example: "1", hint: "必填"); - Add("declaredCurrency", "申报币种", true, "select", "currency", "declaration", "USD"); - Add("declaredSum", "申报总金额", false, "number", group: "declaration", example: "可选,不填自动汇总"); - Add("items[0].cnName", "申报品名(中)", false, group: "declaration", example: "手机壳"); - Add("items[0].enName", "申报品名(英)", true, group: "declaration", example: "Phone Case"); - Add("items[0].sku", "SKU", false, group: "declaration", example: "SKU001"); - Add("items[0].unit", "单位", false, group: "declaration", example: "pcs"); - Add("items[0].unitPrice", "单价(元)", true, "number", group: "declaration", example: "12.5"); - Add("items[0].unitWeight", "单重(kg)", true, "number", group: "declaration", example: "0.3"); - Add("items[0].quantity", "申报数量", true, "number", group: "declaration", example: "1"); - Add("items[0].amount", "总金额(元)", false, "number", group: "declaration", example: "自动计算=单价×数量"); - Add("items[0].weight", "总重量(kg)", false, "number", group: "declaration", example: "自动计算=单重×数量"); - Add("items[0].imHsCode", "进口HsCode", false, group: "declaration"); - Add("items[0].exHsCode", "出口HsCode", false, group: "declaration"); - Add("items[0].skuImage", "商品图片URL", false, group: "declaration"); - Add("items[0].category", "类别", false, group: "declaration", example: "Accessories"); - Add("items[0].brand", "品牌", false, group: "declaration", example: "DemoBrand"); - Add("items[0].brandType", "品牌类型", false, group: "declaration"); - Add("items[0].isMagnetic", "是否带磁带电", false, "boolean", group: "declaration", example: "false"); - Add("items[0].distribution", "配货信息", false, group: "declaration"); - Add("items[0].material", "材质", false, group: "declaration", example: "plastic"); - Add("items[0].enMaterial", "英文材质", false, group: "declaration", example: "plastic"); - Add("items[0].purpose", "用途", false, group: "declaration", example: "protection"); - Add("items[0].attributes", "属性", false, group: "declaration"); - Add("items[0].originCountry", "原产国", false, group: "declaration", example: "CN"); - Add("items[0].salesLink", "销售链接", false, group: "declaration"); - Add("items[0].model", "型号", false, group: "declaration", example: "Model-X"); - Add("items[0].specifications", "规格", false, group: "declaration", example: "Standard"); - Add("items[0].comment", "备注", false, group: "declaration", example: "示例订单数据"); + return list; + } + + private static IReadOnlyList BuildDedicatedSmallParcelStandardFields() + { + var list = new List(); + var sort = 0; + void Add( + string key, + string label, + bool required, + string dataType = "string", + string? dictType = null, + string group = "basic", + string example = "", + string hint = "") + { + sort += 10; + list.Add(new OrderTemplateFieldDefinition + { + FieldKey = key, + Label = label, + Required = required, + DataType = dataType, + DictType = dictType, + Group = group, + Example = example, + Hint = hint, + Sort = sort, + }); + } + + Add("requestId", "幂等请求ID", false, example: "可选", hint: "防重复提交,一般留空"); + Add("clientNo", "客户单号", true, example: "C202604230001", hint: "您自己的订单编号,同一客户不可重复"); + Add("productCode", "运输方式", true, "select", "sales_product", "basic", "PROD001", hint: "从下拉选择产品编码"); + Add("cargoAttribute", "货物属性", true, "select", "cargo_attribute", "basic", "general", hint: "普货选 general"); + Add("exportCustomsMode", "出口报关方式", false, "select", "export_customs_mode", "basic", "9610Simple", hint: "不选则简易申报"); + Add("warehouseId", "发货仓库", true, "select", "warehouse", "basic", "1", hint: "从下拉选择仓库 ID"); + Add("length", "长(cm)", false, "number", example: "10", hint: "材积计费用"); + Add("width", "宽(cm)", false, "number", example: "10", hint: "材积计费用"); + Add("height", "高(cm)", false, "number", example: "10", hint: "材积计费用"); + Add("actualWeight", "实重(kg)", true, "number", example: "1.2", hint: "必填"); + Add("isCod", "是否COD", false, "boolean", example: "false", hint: "货到付款填 true"); + Add("codCurrency", "COD币种", false, "select", "currency", example: "USD", hint: "COD 订单填写"); + Add("codSum", "COD金额", false, "number", example: "0", hint: "COD 订单填写"); + Add("quantity", "包裹件数", true, "number", example: "1", hint: "一般为 1"); + + Add("receiver.recipientsName", "收件人姓名", true, group: "receiver", example: "Demo Receiver", hint: "必填"); + Add("receiver.companyName", "收件公司", false, group: "receiver", example: "Demo LLC", hint: "选填"); + Add("receiver.phone", "收件人手机", true, group: "receiver", example: "13900139000", hint: "必填"); + Add("receiver.telePhone", "收件人电话", false, group: "receiver", hint: "选填"); + Add("receiver.email", "收件人邮箱", false, group: "receiver", example: "receiver@example.com", hint: "选填"); + Add("receiver.countryCode", "目的国代码", true, group: "receiver", example: "US", hint: "二字码"); + Add("receiver.province", "省/州", false, group: "receiver", example: "California", hint: "建议填写"); + Add("receiver.city", "城市", false, group: "receiver", example: "Los Angeles", hint: "建议填写"); + Add("receiver.streetNumber", "门牌号", false, group: "receiver", example: "1001", hint: "选填"); + Add("receiver.streetAddress1", "地址1", true, group: "receiver", example: "Sunset Blvd", hint: "主地址,必填"); + Add("receiver.streetAddress2", "地址2", false, group: "receiver", hint: "选填"); + Add("receiver.streetAddress3", "地址3", false, group: "receiver", hint: "选填"); + Add("receiver.zipCode", "邮编", true, group: "receiver", example: "90001", hint: "必填"); + Add("receiver.taxType", "税号类型", false, group: "receiver", example: "0", hint: "欧盟等场景选填"); + Add("receiver.taxNo", "税号", false, group: "receiver", hint: "选填"); + Add("receiver.certificateType", "证件类型", false, group: "receiver", example: "0", hint: "选填"); + Add("receiver.certificateNo", "证件号码", false, group: "receiver", hint: "选填"); + Add("receiver.eori", "EORI", false, group: "receiver", hint: "选填"); + Add("receiver.vat", "VAT", false, group: "receiver", hint: "选填"); + + Add("declaredCurrency", "申报币种", true, "select", "currency", "declaration", "USD", hint: "默认 USD"); + Add("declaredSum", "申报总金额", false, "number", group: "declaration", example: "可选", hint: "不填则按明细汇总"); + Add("items[0].cnName", "申报品名(中)", false, group: "declaration", example: "手机壳", hint: "选填"); + Add("items[0].enName", "申报品名(英)", true, group: "declaration", example: "Phone Case", hint: "必填"); + Add("items[0].sku", "SKU", false, group: "declaration", example: "SKU001", hint: "选填"); + Add("items[0].unit", "单位", false, group: "declaration", example: "pcs", hint: "选填"); + Add("items[0].unitPrice", "单价", true, "number", group: "declaration", example: "12.5", hint: "与申报币种一致"); + Add("items[0].unitWeight", "单重(kg)", true, "number", group: "declaration", example: "0.3", hint: "必填"); + Add("items[0].quantity", "申报数量", true, "number", group: "declaration", example: "1", hint: "必填"); + Add("items[0].amount", "总金额", false, "number", group: "declaration", example: "自动计算", hint: "不填=单价×数量"); + Add("items[0].weight", "总重量(kg)", false, "number", group: "declaration", example: "自动计算", hint: "不填=单重×数量"); + Add("items[0].imHsCode", "进口HsCode", false, group: "declaration", hint: "选填"); + Add("items[0].exHsCode", "出口HsCode", false, group: "declaration", hint: "选填"); + Add("items[0].skuImage", "商品图片URL", false, group: "declaration", hint: "选填"); + Add("items[0].category", "类别", false, group: "declaration", example: "Accessories", hint: "选填"); + Add("items[0].brand", "品牌", false, group: "declaration", example: "DemoBrand", hint: "选填"); + Add("items[0].brandType", "品牌类型", false, group: "declaration", hint: "选填"); + Add("items[0].isMagnetic", "是否带磁带电", false, "boolean", group: "declaration", example: "false", hint: "选填"); + Add("items[0].distribution", "配货信息", false, group: "declaration", hint: "选填"); + Add("items[0].material", "材质", false, group: "declaration", example: "plastic", hint: "选填"); + Add("items[0].enMaterial", "英文材质", false, group: "declaration", example: "plastic", hint: "选填"); + Add("items[0].purpose", "用途", false, group: "declaration", example: "protection", hint: "选填"); + Add("items[0].attributes", "属性", false, group: "declaration", hint: "选填"); + Add("items[0].originCountry", "原产国", false, group: "declaration", example: "CN", hint: "选填"); + Add("items[0].salesLink", "销售链接", false, group: "declaration", hint: "选填"); + Add("items[0].model", "型号", false, group: "declaration", example: "Model-X", hint: "选填"); + Add("items[0].specifications", "规格", false, group: "declaration", example: "Standard", hint: "选填"); + Add("items[0].comment", "备注", false, group: "declaration", example: "示例", hint: "选填"); return list; } diff --git a/src/Oms.Domain/WarehouseAggregate/Entities/CBinLocation.cs b/src/Oms.Domain/WarehouseAggregate/Entities/CBinLocation.cs index 6288eb8..41368bd 100644 --- a/src/Oms.Domain/WarehouseAggregate/Entities/CBinLocation.cs +++ b/src/Oms.Domain/WarehouseAggregate/Entities/CBinLocation.cs @@ -3,7 +3,7 @@ using SqlSugar; namespace Oms.Domain.WarehouseAggregate.Entities; -[SugarTable("C_bin_location")] +[SugarTable(WarehouseTableNames.BinLocation)] public class CBinLocation : OmsAuditedAggregateRoot { public int WarehouseId { get; set; } diff --git a/src/Oms.Domain/WarehouseAggregate/Entities/CInboundBox.cs b/src/Oms.Domain/WarehouseAggregate/Entities/CInboundBox.cs index 49be713..99fa83f 100644 --- a/src/Oms.Domain/WarehouseAggregate/Entities/CInboundBox.cs +++ b/src/Oms.Domain/WarehouseAggregate/Entities/CInboundBox.cs @@ -3,7 +3,7 @@ using SqlSugar; namespace Oms.Domain.WarehouseAggregate.Entities; -[SugarTable("C_inbound_box")] +[SugarTable(WarehouseTableNames.InboundBox)] public class CInboundBox : OmsAuditedAggregateRoot { public int InboundNoticeId { get; set; } diff --git a/src/Oms.Domain/WarehouseAggregate/Entities/CInboundBoxLine.cs b/src/Oms.Domain/WarehouseAggregate/Entities/CInboundBoxLine.cs index 248d099..9330603 100644 --- a/src/Oms.Domain/WarehouseAggregate/Entities/CInboundBoxLine.cs +++ b/src/Oms.Domain/WarehouseAggregate/Entities/CInboundBoxLine.cs @@ -3,7 +3,7 @@ using SqlSugar; namespace Oms.Domain.WarehouseAggregate.Entities; -[SugarTable("C_inbound_box_line")] +[SugarTable(WarehouseTableNames.InboundBoxLine)] public class CInboundBoxLine : OmsAuditedAggregateRoot { public int InboundBoxId { get; set; } diff --git a/src/Oms.Domain/WarehouseAggregate/Entities/CInboundNotice.cs b/src/Oms.Domain/WarehouseAggregate/Entities/CInboundNotice.cs index b8d1b2b..707f435 100644 --- a/src/Oms.Domain/WarehouseAggregate/Entities/CInboundNotice.cs +++ b/src/Oms.Domain/WarehouseAggregate/Entities/CInboundNotice.cs @@ -3,7 +3,7 @@ using SqlSugar; namespace Oms.Domain.WarehouseAggregate.Entities; -[SugarTable("C_inbound_notice")] +[SugarTable(WarehouseTableNames.InboundNotice)] public class CInboundNotice : OmsAuditedAggregateRoot { public int CustomerId { get; set; } diff --git a/src/Oms.Domain/WarehouseAggregate/Entities/CInventory.cs b/src/Oms.Domain/WarehouseAggregate/Entities/CInventory.cs index 91aa6d1..886db91 100644 --- a/src/Oms.Domain/WarehouseAggregate/Entities/CInventory.cs +++ b/src/Oms.Domain/WarehouseAggregate/Entities/CInventory.cs @@ -3,7 +3,7 @@ using SqlSugar; namespace Oms.Domain.WarehouseAggregate.Entities; -[SugarTable("C_inventory")] +[SugarTable(WarehouseTableNames.Inventory)] public class CInventory : OmsAuditedAggregateRoot { public int CustomerId { get; set; } diff --git a/src/Oms.Domain/WarehouseAggregate/Entities/CParcel.cs b/src/Oms.Domain/WarehouseAggregate/Entities/CParcel.cs index efae6c5..19b3b65 100644 --- a/src/Oms.Domain/WarehouseAggregate/Entities/CParcel.cs +++ b/src/Oms.Domain/WarehouseAggregate/Entities/CParcel.cs @@ -3,7 +3,7 @@ using SqlSugar; namespace Oms.Domain.WarehouseAggregate.Entities; -[SugarTable("C_parcel")] +[SugarTable(WarehouseTableNames.Parcel)] public class CParcel : OmsAuditedAggregateRoot { public int CustomerId { get; set; } diff --git a/src/Oms.Domain/WarehouseAggregate/Entities/CParcelArrival.cs b/src/Oms.Domain/WarehouseAggregate/Entities/CParcelArrival.cs index d4e22aa..81b6dc0 100644 --- a/src/Oms.Domain/WarehouseAggregate/Entities/CParcelArrival.cs +++ b/src/Oms.Domain/WarehouseAggregate/Entities/CParcelArrival.cs @@ -6,7 +6,7 @@ namespace Oms.Domain.WarehouseAggregate.Entities; /// /// 快递到仓扫描记录。 /// -[SugarTable("C_parcel_arrival")] +[SugarTable(WarehouseTableNames.ParcelArrival)] public class CParcelArrival : OmsAuditedAggregateRoot { public int CustomerId { get; set; } diff --git a/src/Oms.Domain/WarehouseAggregate/Entities/CParcelItem.cs b/src/Oms.Domain/WarehouseAggregate/Entities/CParcelItem.cs index 0cb75e1..5178a51 100644 --- a/src/Oms.Domain/WarehouseAggregate/Entities/CParcelItem.cs +++ b/src/Oms.Domain/WarehouseAggregate/Entities/CParcelItem.cs @@ -3,7 +3,7 @@ using SqlSugar; namespace Oms.Domain.WarehouseAggregate.Entities; -[SugarTable("C_parcel_item")] +[SugarTable(WarehouseTableNames.ParcelItem)] public class CParcelItem : OmsAuditedAggregateRoot { public int ParcelId { get; set; } diff --git a/src/Oms.Domain/WarehouseAggregate/Entities/CSku.cs b/src/Oms.Domain/WarehouseAggregate/Entities/CSku.cs index 1466853..058a91b 100644 --- a/src/Oms.Domain/WarehouseAggregate/Entities/CSku.cs +++ b/src/Oms.Domain/WarehouseAggregate/Entities/CSku.cs @@ -3,7 +3,7 @@ using SqlSugar; namespace Oms.Domain.WarehouseAggregate.Entities; -[SugarTable("C_sku")] +[SugarTable(WarehouseTableNames.Sku)] public class CSku : OmsAuditedAggregateRoot { public int CustomerId { get; set; } diff --git a/src/Oms.Domain/WarehouseAggregate/Entities/CSkuPlatformMap.cs b/src/Oms.Domain/WarehouseAggregate/Entities/CSkuPlatformMap.cs index bacdbee..5a9b22d 100644 --- a/src/Oms.Domain/WarehouseAggregate/Entities/CSkuPlatformMap.cs +++ b/src/Oms.Domain/WarehouseAggregate/Entities/CSkuPlatformMap.cs @@ -3,7 +3,7 @@ using SqlSugar; namespace Oms.Domain.WarehouseAggregate.Entities; -[SugarTable("C_sku_platform_map")] +[SugarTable(WarehouseTableNames.SkuPlatformMap)] public class CSkuPlatformMap : OmsAuditedAggregateRoot { public int CustomerId { get; set; } diff --git a/src/Oms.Domain/WarehouseAggregate/WarehouseTableNames.cs b/src/Oms.Domain/WarehouseAggregate/WarehouseTableNames.cs new file mode 100644 index 0000000..058d70d --- /dev/null +++ b/src/Oms.Domain/WarehouseAggregate/WarehouseTableNames.cs @@ -0,0 +1,18 @@ +namespace Oms.Domain.WarehouseAggregate; + +/// +/// 仓储模块表名(c_ 前缀 + 小写 snake_case,与 t_order 等命名风格对齐)。 +/// +public static class WarehouseTableNames +{ + public const string Sku = "c_sku"; + public const string BinLocation = "c_bin_location"; + public const string Inventory = "c_inventory"; + public const string InboundNotice = "c_inbound_notice"; + public const string InboundBox = "c_inbound_box"; + public const string InboundBoxLine = "c_inbound_box_line"; + public const string Parcel = "c_parcel"; + public const string ParcelArrival = "c_parcel_arrival"; + public const string ParcelItem = "c_parcel_item"; + public const string SkuPlatformMap = "c_sku_platform_map"; +}