352 lines
22 KiB
SQL
352 lines
22 KiB
SQL
SET NAMES utf8mb4;
|
||
-- 订单服务独占数据库。其他服务只能通过 OpenAPI 或领域事件获取订单数据。
|
||
|
||
CREATE DATABASE IF NOT EXISTS baiyue_order
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_order;
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_order (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '订单ID(雪花ID)',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||
order_no VARCHAR(32) NOT NULL COMMENT '客户范围内唯一订单号',
|
||
inbound_code VARCHAR(40) NULL COMMENT '全平台唯一入仓码;来源由全局模式决定,提交后固定',
|
||
customer_id BIGINT UNSIGNED NULL COMMENT '客户逻辑ID',
|
||
customer_name VARCHAR(128) NOT NULL COMMENT '下单时客户名称快照',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'DRAFT' COMMENT '订单业务状态:DRAFT草稿、SUBMITTED已提交、APPROVED审核通过、ACCEPTED已接单、REJECTED审核驳回、PROCESSING履约中、COMPLETED已完成、CANCELLED已取消',
|
||
transport_mode VARCHAR(16) NOT NULL COMMENT '运输方式',
|
||
trade_term VARCHAR(16) NULL COMMENT '贸易条款',
|
||
destination_country_code CHAR(2) NOT NULL COMMENT '目的国家二字码',
|
||
destination_warehouse_id BIGINT UNSIGNED NULL COMMENT '目的仓逻辑ID',
|
||
destination_warehouse_name VARCHAR(128) NOT NULL COMMENT '目的仓名称快照',
|
||
expected_ship_date DATE NULL COMMENT '预计发运日期',
|
||
customer_remark VARCHAR(1000) NULL COMMENT '客户可见备注',
|
||
pickup_method VARCHAR(24) NULL COMMENT '取货方式',
|
||
requested_pickup_at DATETIME(3) NULL COMMENT '客户要求平台上门提货的预约时间',
|
||
receiving_warehouse_id BIGINT UNSIGNED NULL COMMENT '国内收货仓ID',
|
||
receiving_warehouse_name VARCHAR(128) NULL COMMENT '国内收货仓快照',
|
||
receiving_warehouse_address VARCHAR(512) NULL COMMENT '国内收货仓完整地址快照',
|
||
receiving_time VARCHAR(128) NULL COMMENT '国内收货仓收货时间快照',
|
||
channel_code VARCHAR(64) NULL COMMENT '渠道编码',
|
||
channel_name VARCHAR(128) NULL COMMENT '渠道名称快照',
|
||
carrier_id BIGINT UNSIGNED NULL COMMENT '船司逻辑ID快照',
|
||
carrier_name VARCHAR(128) NULL COMMENT '船司名称快照',
|
||
tax_mode VARCHAR(24) NULL COMMENT '买单或退税',
|
||
pricing_tax_mode VARCHAR(16) NULL COMMENT '报价包税维度:TAX_INCLUDED或TAX_EXCLUDED;独立于tax_mode',
|
||
container_mode VARCHAR(24) NULL COMMENT '整柜或散货',
|
||
customs_inspection TINYINT(1) NULL COMMENT '是否商检',
|
||
destination_type VARCHAR(24) NULL COMMENT '目的仓类型:FBA亚马逊仓、OVERSEAS海外仓、PRIVATE私人地址',
|
||
fulfillment_type VARCHAR(32) NOT NULL DEFAULT 'STANDARD' COMMENT '履约类型:STANDARD普通头程、ONE_CLICK_DROPSHIP一键代发',
|
||
amazon_warehouse_code VARCHAR(32) NULL COMMENT '亚马逊仓库编码',
|
||
shipment_id VARCHAR(64) NULL COMMENT '订单级Amazon Shipment ID',
|
||
reference_id VARCHAR(64) NULL COMMENT '订单级Amazon Reference ID',
|
||
order_role VARCHAR(12) NOT NULL DEFAULT 'MASTER' COMMENT '订单层级:MASTER主订单、CHILD子订单',
|
||
parent_order_id BIGINT UNSIGNED NULL COMMENT '直属父订单ID;主订单为空;禁止子订单嵌套',
|
||
root_order_id BIGINT UNSIGNED NULL COMMENT '根主订单ID',
|
||
split_sequence INT UNSIGNED NULL COMMENT '同一父订单下的同级子订单顺序',
|
||
express_company VARCHAR(64) NULL COMMENT '履约快递公司名称快照',
|
||
express_tracking_no VARCHAR(128) NULL COMMENT '履约快递单号',
|
||
submitted_at DATETIME(3) NULL COMMENT '提交时间',
|
||
completed_at DATETIME(3) NULL COMMENT '完成时间',
|
||
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',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_order_tenant_no (tenant_id, order_no),
|
||
UNIQUE KEY uk_order_inbound_code (inbound_code),
|
||
KEY idx_order_tenant_status_time (tenant_id, status, created_at),
|
||
KEY idx_order_tenant_role_submitted (tenant_id, order_role, submitted_at),
|
||
KEY idx_order_tenant_customer (tenant_id, customer_id),
|
||
KEY idx_order_tenant_warehouse (tenant_id, destination_warehouse_id)
|
||
) ENGINE=InnoDB COMMENT='头程订单主表:保存商户委托、目的仓、运输方式和订单当前状态';
|
||
|
||
SET @order_fulfillment_type_sql = IF(
|
||
EXISTS(
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_order' AND table_name = 'ord_order'
|
||
AND column_name = 'fulfillment_type'
|
||
),
|
||
'SELECT 1',
|
||
'ALTER TABLE baiyue_order.ord_order ADD COLUMN fulfillment_type VARCHAR(32) NULL COMMENT ''履约类型:STANDARD普通头程、ONE_CLICK_DROPSHIP一键代发'' AFTER destination_type'
|
||
);
|
||
PREPARE order_fulfillment_type_stmt FROM @order_fulfillment_type_sql;
|
||
EXECUTE order_fulfillment_type_stmt;
|
||
DEALLOCATE PREPARE order_fulfillment_type_stmt;
|
||
|
||
UPDATE baiyue_order.ord_order
|
||
SET fulfillment_type = 'STANDARD'
|
||
WHERE fulfillment_type IS NULL OR fulfillment_type = '';
|
||
ALTER TABLE baiyue_order.ord_order
|
||
MODIFY COLUMN fulfillment_type VARCHAR(32) NOT NULL DEFAULT 'STANDARD'
|
||
COMMENT '履约类型:STANDARD普通头程、ONE_CLICK_DROPSHIP一键代发';
|
||
|
||
-- 既有开发库必须先确认不存在跨客户重复码;若存在则在此处失败,不自动改写业务数据。
|
||
SET @order_global_inbound_index_sql = IF(
|
||
NOT EXISTS(
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_order' AND table_name = 'ord_order'
|
||
AND index_name = 'uk_order_inbound_code'
|
||
),
|
||
'ALTER TABLE baiyue_order.ord_order ADD UNIQUE KEY uk_order_inbound_code (inbound_code)',
|
||
'SELECT 1'
|
||
);
|
||
PREPARE order_global_inbound_index_stmt FROM @order_global_inbound_index_sql;
|
||
EXECUTE order_global_inbound_index_stmt;
|
||
DEALLOCATE PREPARE order_global_inbound_index_stmt;
|
||
|
||
SET @order_tenant_inbound_index_drop_sql = IF(
|
||
EXISTS(
|
||
SELECT 1 FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_order' AND table_name = 'ord_order'
|
||
AND index_name = 'uk_order_tenant_inbound_code'
|
||
),
|
||
'ALTER TABLE baiyue_order.ord_order DROP INDEX uk_order_tenant_inbound_code',
|
||
'SELECT 1'
|
||
);
|
||
PREPARE order_tenant_inbound_index_drop_stmt FROM @order_tenant_inbound_index_drop_sql;
|
||
EXECUTE order_tenant_inbound_index_drop_stmt;
|
||
DEALLOCATE PREPARE order_tenant_inbound_index_drop_stmt;
|
||
|
||
-- Existing development databases may still have the former NOT NULL contract.
|
||
SET @order_inbound_code_nullable_sql = IF(
|
||
EXISTS(
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_order' AND table_name = 'ord_order'
|
||
AND column_name = 'inbound_code' AND is_nullable = 'NO'
|
||
),
|
||
'ALTER TABLE baiyue_order.ord_order MODIFY COLUMN inbound_code VARCHAR(40) NULL COMMENT ''全平台唯一入仓码;来源由全局模式决定,提交后固定''',
|
||
'SELECT 1'
|
||
);
|
||
PREPARE order_inbound_code_nullable_stmt FROM @order_inbound_code_nullable_sql;
|
||
EXECUTE order_inbound_code_nullable_stmt;
|
||
DEALLOCATE PREPARE order_inbound_code_nullable_stmt;
|
||
|
||
SET @order_requested_pickup_at_sql = IF(
|
||
EXISTS(
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_order' AND table_name = 'ord_order'
|
||
AND column_name = 'requested_pickup_at'
|
||
),
|
||
'SELECT 1',
|
||
'ALTER TABLE baiyue_order.ord_order ADD COLUMN requested_pickup_at DATETIME(3) NULL COMMENT ''客户要求平台上门提货的预约时间'' AFTER pickup_method'
|
||
);
|
||
PREPARE order_requested_pickup_at_stmt FROM @order_requested_pickup_at_sql;
|
||
EXECUTE order_requested_pickup_at_stmt;
|
||
DEALLOCATE PREPARE order_requested_pickup_at_stmt;
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_inbound_code_daily_sequence (
|
||
business_date DATE NOT NULL COMMENT '上海时区业务日期',
|
||
current_value BIGINT UNSIGNED NOT NULL COMMENT '当天全平台已分配票数',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '当天首次分配时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '最近分配时间',
|
||
PRIMARY KEY (business_date)
|
||
) ENGINE=InnoDB COMMENT='入仓码每日票序;不同客户和物流渠道共用,按上海日期重新从1开始';
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_order_fulfillment_split (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '拆分台账ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||
parent_order_id BIGINT UNSIGNED NOT NULL COMMENT '直属父订单ID;主订单作为拆分根',
|
||
source_order_id BIGINT UNSIGNED NOT NULL COMMENT '本次实际消费的主订单或余量子订单ID',
|
||
child_order_id BIGINT UNSIGNED NOT NULL COMMENT '本次分片承接的子订单ID',
|
||
split_role VARCHAR(12) NOT NULL COMMENT '本台账分片角色:LOADED已装柜部分、REMAINDER剩余部分',
|
||
loading_plan_id BIGINT UNSIGNED NULL COMMENT 'Firstleg装柜计划逻辑ID',
|
||
container_booking_id BIGINT UNSIGNED NULL COMMENT 'Firstleg柜次逻辑ID',
|
||
source_package_count INT UNSIGNED NOT NULL COMMENT '来源订单本次分片前的总包装件数',
|
||
loaded_package_count INT UNSIGNED NOT NULL COMMENT '本次已装柜分片包装件数',
|
||
remaining_package_count INT UNSIGNED NOT NULL COMMENT '本次分片后剩余包装件数',
|
||
source_detail_json JSON NULL COMMENT '来源货物明细及箱数快照',
|
||
operation_id VARCHAR(64) NOT NULL COMMENT '履约分片业务操作ID',
|
||
idempotency_key VARCHAR(128) NOT NULL COMMENT '客户端幂等键',
|
||
request_hash CHAR(64) NOT NULL COMMENT '规范化请求内容SHA-256摘要',
|
||
expected_parent_version INT NOT NULL COMMENT '执行前预期父订单乐观锁版本号',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建该分片台账的用户ID',
|
||
created_by_name VARCHAR(64) NULL COMMENT '创建人名称快照',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '台账创建时间',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '台账乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_order_split_operation (tenant_id, operation_id, split_role),
|
||
UNIQUE KEY uk_order_split_idempotency (tenant_id, parent_order_id, idempotency_key, split_role),
|
||
UNIQUE KEY uk_order_split_child_role (tenant_id, child_order_id, split_role),
|
||
KEY idx_order_split_parent (tenant_id, parent_order_id, created_at, id),
|
||
KEY idx_order_split_source (tenant_id, source_order_id, split_role),
|
||
KEY idx_order_split_container (tenant_id, container_booking_id, created_at, id),
|
||
CONSTRAINT fk_order_split_parent FOREIGN KEY (parent_order_id) REFERENCES ord_order(id),
|
||
CONSTRAINT fk_order_split_child FOREIGN KEY (child_order_id) REFERENCES ord_order(id),
|
||
CONSTRAINT chk_order_split_quantity CHECK (
|
||
source_package_count > 0 AND loaded_package_count > 0
|
||
AND loaded_package_count + remaining_package_count = source_package_count
|
||
)
|
||
) ENGINE=InnoDB COMMENT='父订单到子订单的装柜数量及柜次关联台账';
|
||
|
||
DROP TRIGGER IF EXISTS trg_order_fulfillment_split_insert;
|
||
DROP TRIGGER IF EXISTS trg_order_fulfillment_split_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_order_fulfillment_split_insert
|
||
BEFORE INSERT ON ord_order_fulfillment_split FOR EACH ROW
|
||
BEGIN
|
||
IF NEW.split_role NOT IN ('LOADED','REMAINDER') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单履约分片角色非法';
|
||
END IF;
|
||
IF NEW.split_role='LOADED' AND NEW.child_order_id<>NEW.source_order_id THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='实装台账必须由来源订单自身承接';
|
||
END IF;
|
||
IF NEW.split_role='REMAINDER'
|
||
AND (NEW.child_order_id=NEW.source_order_id OR NEW.remaining_package_count=0) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='余量台账必须指向有剩余数量的新子订单';
|
||
END IF;
|
||
END$$
|
||
CREATE TRIGGER trg_order_fulfillment_split_update
|
||
BEFORE UPDATE ON ord_order_fulfillment_split FOR EACH ROW
|
||
BEGIN
|
||
IF NOT (NEW.tenant_id<=>OLD.tenant_id)
|
||
OR NOT (NEW.parent_order_id<=>OLD.parent_order_id)
|
||
OR NOT (NEW.source_order_id<=>OLD.source_order_id)
|
||
OR NOT (NEW.child_order_id<=>OLD.child_order_id)
|
||
OR NOT (NEW.split_role<=>OLD.split_role) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单履约分片来源、子片与角色不可修改';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_order_party (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '参与方快照ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '所属订单ID',
|
||
party_type VARCHAR(16) NOT NULL COMMENT '参与方类型:SHIPPER或CONSIGNEE',
|
||
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 '邮政编码',
|
||
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',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_party_order_type (tenant_id, order_id, party_type),
|
||
CONSTRAINT fk_party_order FOREIGN KEY (order_id) REFERENCES ord_order (id)
|
||
) 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和数据隔离键',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '所属订单ID',
|
||
item_no INT NOT NULL COMMENT '订单内明细序号',
|
||
cargo_name VARCHAR(128) NULL COMMENT '货物名称;草稿可空,提交前必填',
|
||
hs_code VARCHAR(20) NULL COMMENT '海关商品编码',
|
||
package_type VARCHAR(32) NULL COMMENT '包装类型;草稿可空,提交前必填',
|
||
package_count INT UNSIGNED NOT NULL COMMENT '包装件数',
|
||
gross_weight_kg DECIMAL(18,3) NULL COMMENT '毛重(千克);草稿可空,提交前必须大于0',
|
||
volume_cbm DECIMAL(18,4) NULL COMMENT '体积(立方米);草稿可空,提交前必须大于0',
|
||
length_cm DECIMAL(12,2) NULL COMMENT '单件长度(厘米)',
|
||
width_cm DECIMAL(12,2) NULL COMMENT '单件宽度(厘米)',
|
||
height_cm DECIMAL(12,2) NULL COMMENT '单件高度(厘米)',
|
||
declared_value DECIMAL(18,2) NULL COMMENT '申报价值',
|
||
currency CHAR(3) NULL COMMENT '币种三字码',
|
||
dangerous_goods TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否危险品',
|
||
cargo_remark VARCHAR(500) NULL COMMENT '货物备注',
|
||
sku_no VARCHAR(64) NULL COMMENT '货物SKU编码',
|
||
upc_no VARCHAR(64) NULL COMMENT '货物UPC条码',
|
||
cargo_name_en VARCHAR(128) NULL COMMENT '英文品名',
|
||
customs_hs_code VARCHAR(20) NULL COMMENT '目的国清关HS CODE',
|
||
units_per_package INT UNSIGNED NULL COMMENT '每箱件数',
|
||
total_quantity INT UNSIGNED NULL COMMENT '总件数',
|
||
net_weight_kg DECIMAL(18,3) NULL COMMENT '净重kg',
|
||
unit_declared_value DECIMAL(18,2) NULL COMMENT '单件申报价值',
|
||
material VARCHAR(128) NULL COMMENT '材质',
|
||
product_usage VARCHAR(128) NULL COMMENT '用途',
|
||
model VARCHAR(64) NULL COMMENT '型号',
|
||
brand VARCHAR(64) NULL COMMENT '品牌',
|
||
product_length_cm DECIMAL(12,2) NULL COMMENT '产品长度(厘米)',
|
||
product_width_cm DECIMAL(12,2) NULL COMMENT '产品宽度(厘米)',
|
||
product_height_cm DECIMAL(12,2) NULL COMMENT '产品高度(厘米)',
|
||
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',
|
||
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',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_cargo_item (tenant_id, order_id, item_no),
|
||
KEY idx_cargo_order (tenant_id, order_id),
|
||
CONSTRAINT fk_cargo_order FOREIGN KEY (order_id) REFERENCES ord_order (id)
|
||
) ENGINE=InnoDB COMMENT='订单货物明细表:记录件数、重量、体积、申报价值和危险品属性';
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_order_internal_note (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '内部备注ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '所属订单ID',
|
||
content VARCHAR(2000) NOT NULL COMMENT '仅内部人员可见的备注内容',
|
||
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',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
KEY idx_note_order_time (tenant_id, order_id, created_at),
|
||
CONSTRAINT fk_note_order FOREIGN KEY (order_id) REFERENCES ord_order (id)
|
||
) ENGINE=InnoDB COMMENT='订单内部备注表:保存平台运营或履约人员的内部协作记录,不向商户客户展示';
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_order_status_history (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '状态历史ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '所属订单ID',
|
||
from_status VARCHAR(24) NULL COMMENT '变更前状态,首次创建可为空',
|
||
to_status VARCHAR(24) NOT NULL COMMENT '变更后状态',
|
||
action VARCHAR(24) NOT NULL COMMENT '触发状态变化的业务动作',
|
||
reason VARCHAR(500) NULL COMMENT '变更原因',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
|
||
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||
occurred_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '状态变化时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_history_order_time (tenant_id, order_id, occurred_at),
|
||
CONSTRAINT fk_history_order FOREIGN KEY (order_id) REFERENCES ord_order (id)
|
||
) ENGINE=InnoDB COMMENT='订单状态不可变历史表:按时间记录每次状态迁移、动作、原因和操作人';
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_outbox_event (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '领域事件全局ID,同时作为消费幂等键',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '事件所属客户ID和数据隔离键',
|
||
aggregate_type VARCHAR(32) NOT NULL COMMENT '聚合类型,例如 ORDER',
|
||
aggregate_id BIGINT UNSIGNED NOT NULL COMMENT '发生变更的聚合根ID,例如订单ID',
|
||
event_type VARCHAR(64) NOT NULL COMMENT '领域事件类型,例如 ORDER_SUBMITTED',
|
||
payload_json JSON NOT NULL COMMENT '供下游消费的事件负载快照',
|
||
publish_status VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '发布状态:PENDING待发布、PUBLISHED已发布、FAILED发布失败',
|
||
retry_count INT NOT NULL DEFAULT 0 COMMENT '投递失败后的累计重试次数',
|
||
next_retry_at DATETIME(3) NULL COMMENT '允许再次投递的最早时间',
|
||
published_at DATETIME(3) NULL COMMENT '成功发布到消息中间件的时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '事件在本地事务中的创建时间',
|
||
PRIMARY KEY (id),
|
||
KEY idx_outbox_publish (publish_status, next_retry_at, created_at)
|
||
) ENGINE=InnoDB COMMENT='订单事务Outbox表:与订单业务变更在同一本地事务写入,供后台发布器可靠投递RocketMQ,保证数据库状态与领域事件最终一致;不是普通操作日志';
|
||
|
||
CREATE TABLE IF NOT EXISTS ord_idempotency_record (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '幂等记录ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作者ID',
|
||
operation VARCHAR(32) NOT NULL COMMENT '幂等操作类型',
|
||
resource_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '资源ID,创建草稿时为0',
|
||
idempotency_key VARCHAR(128) NOT NULL COMMENT '客户端幂等键',
|
||
request_hash CHAR(64) NOT NULL COMMENT '请求内容SHA-256摘要',
|
||
status VARCHAR(16) NOT NULL COMMENT '处理状态:PROCESSING处理中、COMPLETED已完成',
|
||
result_order_id BIGINT UNSIGNED NULL COMMENT '成功结果订单ID',
|
||
completed_at DATETIME(3) NULL COMMENT '处理完成时间',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_order_idempotency_scope (tenant_id, operator_id, operation, idempotency_key),
|
||
KEY idx_order_idempotency_created (created_at)
|
||
) ENGINE=InnoDB COMMENT='订单写接口持久化幂等记录:唯一键保证同租户同操作者并发请求只执行一次';
|