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";
+}