feat(order): add customer order foundations

This commit is contained in:
2026-09-01 19:17:43 +08:00
parent 222c3790dc
commit caffcb90a9
24 changed files with 992 additions and 49 deletions
+39
View File
@@ -236,6 +236,33 @@ CREATE TABLE IF NOT EXISTS ord_order_party (
CONSTRAINT fk_party_order FOREIGN KEY (order_id) REFERENCES ord_order (id)
) ENGINE=InnoDB COMMENT='订单收发货方快照表:固化下单时发货人和收货人信息,避免主数据变化影响历史订单';
CREATE TABLE IF NOT EXISTS ord_customer_address (
id BIGINT UNSIGNED NOT NULL COMMENT '客户地址ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
address_type VARCHAR(32) NOT NULL COMMENT 'PICKUP取货地址或PRIVATE_DESTINATION私人目的仓',
address_name VARCHAR(64) NOT NULL COMMENT '客户自定义地址名称',
company_name VARCHAR(128) NOT NULL COMMENT '公司名称',
contact_name VARCHAR(64) NOT NULL COMMENT '联系人',
phone VARCHAR(32) NOT NULL COMMENT '联系电话',
email VARCHAR(128) NULL COMMENT '联系邮箱',
country_code CHAR(2) NOT NULL COMMENT '国家二字码',
province VARCHAR(64) NULL COMMENT '省州',
city VARCHAR(64) NOT NULL COMMENT '城市',
district VARCHAR(64) NULL COMMENT '区县',
address_line VARCHAR(255) NOT NULL COMMENT '详细地址',
postal_code VARCHAR(20) NULL COMMENT '邮政编码',
default_address TINYINT(1) NOT NULL DEFAULT 0 COMMENT '同类型默认地址',
active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否可选',
created_by BIGINT UNSIGNED NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_by BIGINT UNSIGNED NOT NULL,
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
deleted TINYINT(1) NOT NULL DEFAULT 0,
version INT NOT NULL DEFAULT 0,
PRIMARY KEY (id),
KEY idx_customer_address_scope (tenant_id, address_type, active, updated_at)
) ENGINE=InnoDB COMMENT='客户自有下单地址簿;不承载平台仓库主档';
CREATE TABLE IF NOT EXISTS ord_order_cargo (
id BIGINT UNSIGNED NOT NULL COMMENT '货物明细ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
@@ -272,6 +299,7 @@ CREATE TABLE IF NOT EXISTS ord_order_cargo (
shipment_id VARCHAR(64) NULL COMMENT '货物级 Amazon Shipment ID',
reference_id VARCHAR(64) NULL COMMENT '货物级 Amazon Reference ID',
product_image_file_id BIGINT UNSIGNED NULL COMMENT '文件中心产品图片逻辑ID',
wooden_product TINYINT(1) NOT NULL DEFAULT 0 COMMENT '木制品权威判定快照;1时订单必须商检',
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
@@ -284,6 +312,17 @@ CREATE TABLE IF NOT EXISTS ord_order_cargo (
CONSTRAINT fk_cargo_order FOREIGN KEY (order_id) REFERENCES ord_order (id)
) ENGINE=InnoDB COMMENT='订单货物明细表:记录件数、重量、体积、申报价值和危险品属性';
SET @order_wooden_product_sql = IF(
EXISTS(SELECT 1 FROM information_schema.columns
WHERE table_schema = 'baiyue_order' AND table_name = 'ord_order_cargo'
AND column_name = 'wooden_product'),
'SELECT 1',
'ALTER TABLE baiyue_order.ord_order_cargo ADD COLUMN wooden_product TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''木制品权威判定快照;1时订单必须商检'' AFTER product_image_file_id'
);
PREPARE order_wooden_product_stmt FROM @order_wooden_product_sql;
EXECUTE order_wooden_product_stmt;
DEALLOCATE PREPARE order_wooden_product_stmt;
CREATE TABLE IF NOT EXISTS ord_order_internal_note (
id BIGINT UNSIGNED NOT NULL COMMENT '内部备注ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
+95 -3
View File
@@ -6,6 +6,76 @@ info:
security:
- bearerAuth: []
paths:
/api/merchant/v1/order-addresses:
get:
summary: 查询当前客户下单地址簿
parameters:
- name: addressType
in: query
required: false
schema: {type: string, enum: [PICKUP, PRIVATE_DESTINATION]}
responses:
'200': {description: 只返回登录客户可用地址}
'403': {description: 需要订单查看、创建或编辑权限}
post:
summary: 保存当前客户下单地址
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/CustomerOrderAddressSaveRequest'}
responses:
'200': {description: 返回地址内部 ID,Long 按字符串序列化}
'403': {description: 需要 merchant:order:create 权限}
/api/merchant/v1/order-addresses/{id}:
put:
summary: 修改当前客户下单地址
description: 已创建订单保存独立快照,本操作不会修改历史订单。
parameters:
- {name: id, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/CustomerOrderAddressSaveRequest'}
responses: {'200': {description: 成功}, '403': {description: 需要 merchant:order:edit 权限}}
delete:
summary: 停用当前客户下单地址
description: 停用后不能用于新订单,历史订单快照不受影响。
parameters:
- {name: id, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
responses: {'200': {description: 成功}, '403': {description: 需要 merchant:order:edit 权限}}
/api/merchant/v1/orders/first-leg/batch-imports/template:
get:
summary: 下载 Excel 批量下单模板
responses:
'200': {description: xlsx 模板}
'403': {description: 需要 merchant:order:create 权限}
/api/merchant/v1/orders/first-leg/batch-imports:
post:
summary: 上传 Excel 批量创建订单草稿
description: >-
ATOMIC 在任一行失败时整批回滚;PARTIAL 每行独立事务,错误行不影响成功行。
同一操作者重试必须复用 Idempotency-Key,每行再以 clientRowId 派生稳定幂等键。
原文件和错误报告分别通过 File 的 ORDER_BATCH_IMPORT_SOURCE、ORDER_BATCH_IMPORT_ERROR_REPORT 契约保存。
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
- name: mode
in: query
required: true
schema: {type: string, enum: [ATOMIC, PARTIAL]}
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file: {type: string, format: binary, description: 最大 5MB,仅 xlsx;最多 50 行}
responses:
'200': {description: 返回逐行结果、原文件 ID 及可选错误报告文件 ID}
'403': {description: 需要 merchant:order:create 权限}
/api/platform/v1/orders/first-leg/inbound-code-policy:
get:
summary: 平台端查询全系统入仓码模式
@@ -686,7 +756,7 @@ components:
description: 报价包税维度;独立于订单 taxMode 的买单/退税语义
FirstLegOrderDraft:
type: object
required: [pricingTaxMode, cargoes]
required: [customerName, transportMode, cargoes, pickupMethod, receivingWarehouseId, receivingWarehouseName, channelCode, channelName, taxMode, pricingTaxMode, destinationType]
properties:
fulfillmentType:
type: string
@@ -699,6 +769,9 @@ components:
pattern: '^[A-Za-z0-9._:-]+$'
description: 仅 LEGACY_MANUAL 模式填写的旧系统入仓码;AUTO 模式必须省略
pricingTaxMode: {$ref: '#/components/schemas/PricingTaxMode'}
senderAddressId: {type: string, pattern: '^[1-9][0-9]*$', description: PICKUP 时必填;必须是本客户 PICKUP 地址}
privateDestinationAddressId: {type: string, pattern: '^[1-9][0-9]*$', description: PRIVATE 时必填;必须是本客户 PRIVATE_DESTINATION 地址}
customsInspection: {type: boolean, nullable: true, description: 非木制品可空;任一货物 woodenProduct=true 时必须为 true}
cargoes:
type: array
minItems: 1
@@ -707,7 +780,25 @@ components:
订单保存请求中的完整字段以服务端 FirstLegOrderSaveRequest 为准;fulfillmentType
省略时固定为 STANDARD,一键代发必须显式传 ONE_CLICK_DROPSHIP,且不复用计费 businessType;pricingTaxMode
是新建和编辑订单的必填报价维度;customerId 不接受客户端指定,服务端始终使用认证企业 ID;
发货单位和收货单位仅通过 sender/receiver 保存业务快照。历史存量订单的响应字段仍允许为空。
发货与私人目的仓只接受客户地址 ID,服务端在订单参与方表冻结快照;地址簿后续修改不回写历史订单。
私人目的仓不得写入或伪装为 Warehouse 平台仓主档。历史存量订单的响应字段仍允许为空。
CustomerOrderAddressSaveRequest:
type: object
required: [addressType, addressName, companyName, contactName, phone, countryCode, city, addressLine, defaultAddress]
properties:
addressType: {type: string, enum: [PICKUP, PRIVATE_DESTINATION]}
addressName: {type: string, maxLength: 64}
companyName: {type: string, maxLength: 128}
contactName: {type: string, maxLength: 64}
phone: {type: string, maxLength: 32}
email: {type: string, format: email, maxLength: 128, nullable: true}
countryCode: {type: string, minLength: 2, maxLength: 2}
province: {type: string, maxLength: 64, nullable: true}
city: {type: string, maxLength: 64}
district: {type: string, maxLength: 64, nullable: true}
addressLine: {type: string, maxLength: 255}
postalCode: {type: string, maxLength: 20, nullable: true}
defaultAddress: {type: boolean}
InboundCodePolicy:
type: object
required: [mode, manualEntryRequired]
@@ -769,7 +860,7 @@ components:
errorMessage: {type: string}
FirstLegOrderCargo:
type: object
required: [skuNo, packageCount]
required: [packageCount, woodenProduct]
properties:
skuNo: {type: string, maxLength: 64}
cargoName: {type: string, maxLength: 128}
@@ -801,6 +892,7 @@ components:
cargoRemark: {type: string, maxLength: 500}
packageType: {type: string, maxLength: 32}
dangerousGoods: {type: boolean}
woodenProduct: {type: boolean, description: 木制品权威判定快照;为 true 时订单 customsInspection 必须为 true}
description: >-
原型货物明细字段。FBA 货物的 skuNo、upcNo、shipmentId、referenceId 由服务端按每条货物校验;
productImageFileId 必须属于当前登录客户。