feat(phase3): 收口父子单与全链路验收
This commit is contained in:
@@ -49,6 +49,10 @@ public class OrderEntity extends BaseEntity {
|
||||
private String amazonWarehouseCode;
|
||||
private String shipmentId;
|
||||
private String referenceId;
|
||||
private String orderRole;
|
||||
private Long parentOrderId;
|
||||
private Long rootOrderId;
|
||||
private Integer splitSequence;
|
||||
private String expressCompany;
|
||||
private String expressTrackingNo;
|
||||
private LocalDateTime submittedAt;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.baiyue.order.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/** 父订单→子订单→柜次的数量台账;柜次ID仅为Firstleg逻辑引用。 */
|
||||
@Getter @Setter
|
||||
@TableName("ord_order_fulfillment_split")
|
||||
public class OrderFulfillmentSplitEntity {
|
||||
@TableId(type = IdType.ASSIGN_ID) private Long id;
|
||||
private Long tenantId;
|
||||
private Long parentOrderId;
|
||||
private Long childOrderId;
|
||||
private Long loadingPlanId;
|
||||
private Long containerBookingId;
|
||||
private Integer sourcePackageCount;
|
||||
private Integer loadedPackageCount;
|
||||
private Integer remainingPackageCount;
|
||||
private String sourceDetailJson;
|
||||
private String operationId;
|
||||
private String idempotencyKey;
|
||||
private String requestHash;
|
||||
private String splitRole;
|
||||
private Integer expectedParentVersion;
|
||||
private Long createdBy;
|
||||
private String createdByName;
|
||||
private LocalDateTime createdAt;
|
||||
private Integer version;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.baiyue.order.mapper;
|
||||
|
||||
import com.baiyue.order.entity.OrderFulfillmentSplitEntity;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface OrderFulfillmentSplitMapper extends BaseMapper<OrderFulfillmentSplitEntity> {}
|
||||
@@ -37,6 +37,10 @@ CREATE TABLE IF NOT EXISTS ord_order (
|
||||
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 '提交时间',
|
||||
@@ -55,6 +59,40 @@ CREATE TABLE IF NOT EXISTS ord_order (
|
||||
KEY idx_order_tenant_warehouse (tenant_id, destination_warehouse_id)
|
||||
) ENGINE=InnoDB COMMENT='头程订单主表:保存商户委托、目的仓、运输方式和订单当前状态';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ord_order_fulfillment_split (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '拆分台账ID',
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
parent_order_id BIGINT UNSIGNED NOT NULL,
|
||||
child_order_id BIGINT UNSIGNED NOT NULL,
|
||||
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,
|
||||
loaded_package_count INT UNSIGNED NOT NULL,
|
||||
remaining_package_count INT UNSIGNED NOT NULL,
|
||||
source_detail_json JSON NULL COMMENT '来源货物明细及箱数快照',
|
||||
operation_id VARCHAR(64) NOT NULL,
|
||||
idempotency_key VARCHAR(128) NOT NULL,
|
||||
request_hash CHAR(64) NOT NULL,
|
||||
expected_parent_version INT NOT NULL,
|
||||
created_by BIGINT UNSIGNED NOT NULL,
|
||||
created_by_name VARCHAR(64) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
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 (tenant_id, child_order_id),
|
||||
KEY idx_order_split_parent (tenant_id, parent_order_id, created_at, id),
|
||||
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='父订单到子订单的装柜数量及柜次关联台账';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ord_order_party (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '参与方快照ID',
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
SET NAMES utf8mb4;
|
||||
USE baiyue_order;
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase3_order_add_column;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE phase3_order_add_column(IN p_table VARCHAR(64), IN p_column VARCHAR(64), IN p_definition TEXT)
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||||
AND table_name=p_table AND column_name=p_column) THEN
|
||||
SET @ddl = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN `', p_column, '` ', p_definition);
|
||||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL phase3_order_add_column('ord_order','order_role',"VARCHAR(12) NOT NULL DEFAULT 'MASTER' COMMENT '订单层级:MASTER、CHILD'");
|
||||
CALL phase3_order_add_column('ord_order','parent_order_id','BIGINT UNSIGNED NULL COMMENT ''直属父订单ID;禁止子订单嵌套''');
|
||||
CALL phase3_order_add_column('ord_order','root_order_id','BIGINT UNSIGNED NULL COMMENT ''根主订单ID''');
|
||||
CALL phase3_order_add_column('ord_order','split_sequence','INT UNSIGNED NULL COMMENT ''同一父订单下的同级顺序''');
|
||||
DROP PROCEDURE IF EXISTS phase3_order_add_column;
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase3_order_add_index;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE phase3_order_add_index(IN p_table VARCHAR(64), IN p_name VARCHAR(64), IN p_definition TEXT)
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||||
AND table_name=p_table AND index_name=p_name) THEN
|
||||
SET @ddl = CONCAT('ALTER TABLE `',p_table,'` ADD ',p_definition);
|
||||
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL phase3_order_add_index('ord_order','uk_order_parent_sequence',
|
||||
'UNIQUE KEY `uk_order_parent_sequence` (`tenant_id`,`parent_order_id`,`split_sequence`)');
|
||||
CALL phase3_order_add_index('ord_order','idx_order_root_role',
|
||||
'KEY `idx_order_root_role` (`tenant_id`,`root_order_id`,`order_role`,`created_at`,`id`)');
|
||||
DROP PROCEDURE IF EXISTS phase3_order_add_index;
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase3_order_parent_fk;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE phase3_order_parent_fk()
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE()
|
||||
AND table_name='ord_order' AND constraint_name='fk_order_parent') THEN
|
||||
ALTER TABLE ord_order DROP FOREIGN KEY fk_order_parent;
|
||||
END IF;
|
||||
ALTER TABLE ord_order ADD CONSTRAINT fk_order_parent FOREIGN KEY (parent_order_id) REFERENCES ord_order(id);
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL phase3_order_parent_fk();
|
||||
DROP PROCEDURE IF EXISTS phase3_order_parent_fk;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ord_order_fulfillment_split (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
parent_order_id BIGINT UNSIGNED NOT NULL,
|
||||
child_order_id BIGINT UNSIGNED NOT NULL,
|
||||
split_role VARCHAR(12) NOT NULL COMMENT 'LOADED或REMAINDER',
|
||||
loading_plan_id BIGINT UNSIGNED NULL,
|
||||
container_booking_id BIGINT UNSIGNED NULL,
|
||||
source_package_count INT UNSIGNED NOT NULL,
|
||||
loaded_package_count INT UNSIGNED NOT NULL,
|
||||
remaining_package_count INT UNSIGNED NOT NULL,
|
||||
source_detail_json JSON NULL,
|
||||
operation_id VARCHAR(64) NOT NULL,
|
||||
idempotency_key VARCHAR(128) NOT NULL,
|
||||
request_hash CHAR(64) NOT NULL,
|
||||
expected_parent_version INT NOT NULL,
|
||||
created_by BIGINT UNSIGNED NOT NULL,
|
||||
created_by_name VARCHAR(64) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
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 (tenant_id,child_order_id),
|
||||
KEY idx_order_split_parent (tenant_id,parent_order_id,created_at,id),
|
||||
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_ord_order_parent_insert;
|
||||
DROP TRIGGER IF EXISTS trg_ord_order_parent_update;
|
||||
DELIMITER $$
|
||||
CREATE TRIGGER trg_ord_order_parent_insert BEFORE INSERT ON ord_order FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.order_role='MASTER' THEN
|
||||
SET NEW.parent_order_id=NULL;
|
||||
IF NEW.root_order_id IS NULL THEN SET NEW.root_order_id=NEW.id; END IF;
|
||||
ELSEIF NEW.order_role='CHILD' THEN
|
||||
IF NEW.parent_order_id IS NULL OR NEW.root_order_id IS NULL OR NEW.split_sequence IS NULL THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='子订单必须有父订单、根订单和顺序号';
|
||||
END IF;
|
||||
IF NEW.root_order_id<>NEW.parent_order_id OR EXISTS(
|
||||
SELECT 1 FROM ord_order p WHERE p.id=NEW.parent_order_id AND p.order_role<>'MASTER') THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='子订单只允许直接挂在主订单下';
|
||||
END IF;
|
||||
IF NOT EXISTS(SELECT 1 FROM ord_order p WHERE p.id=NEW.parent_order_id AND p.tenant_id=NEW.tenant_id AND p.deleted=0) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='父订单不存在或客户归属不一致';
|
||||
END IF;
|
||||
ELSE
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单层级非法';
|
||||
END IF;
|
||||
END$$
|
||||
CREATE TRIGGER trg_ord_order_parent_update BEFORE UPDATE ON ord_order FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.order_role<>OLD.order_role OR NOT (NEW.parent_order_id<=>OLD.parent_order_id)
|
||||
OR NOT (NEW.root_order_id<=>OLD.root_order_id) OR NOT (NEW.split_sequence<=>OLD.split_sequence) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单父子关系不可修改';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
CALL phase3_order_add_column('ord_order_fulfillment_split','split_role',"VARCHAR(12) NOT NULL DEFAULT 'LOADED' COMMENT 'LOADED或REMAINDER'");
|
||||
CALL phase3_order_add_column('ord_order_fulfillment_split','request_hash',"CHAR(64) NULL COMMENT '请求内容SHA-256;存量记录允许为空'");
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase3_order_drop_split_index;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE phase3_order_drop_split_index()
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||||
AND table_name='ord_order_fulfillment_split' AND index_name='uk_order_split_operation') THEN
|
||||
ALTER TABLE ord_order_fulfillment_split DROP INDEX uk_order_split_operation;
|
||||
END IF;
|
||||
IF EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||||
AND table_name='ord_order_fulfillment_split' AND index_name='uk_order_split_idempotency') THEN
|
||||
ALTER TABLE ord_order_fulfillment_split DROP INDEX uk_order_split_idempotency;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL phase3_order_drop_split_index();
|
||||
DROP PROCEDURE IF EXISTS phase3_order_drop_split_index;
|
||||
CALL phase3_order_add_index('ord_order_fulfillment_split','uk_order_split_operation',
|
||||
'UNIQUE KEY `uk_order_split_operation` (`tenant_id`,`operation_id`,`split_role`)');
|
||||
CALL phase3_order_add_index('ord_order_fulfillment_split','uk_order_split_idempotency',
|
||||
'UNIQUE KEY `uk_order_split_idempotency` (`tenant_id`,`parent_order_id`,`idempotency_key`,`split_role`)');
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase3_order_assert;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE phase3_order_assert()
|
||||
BEGIN
|
||||
IF (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE()
|
||||
AND table_name='ord_order' AND column_name IN ('order_role','parent_order_id','root_order_id','split_sequence'))<>4 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单父子字段未完整创建';
|
||||
END IF;
|
||||
IF (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=DATABASE()
|
||||
AND table_name='ord_order_fulfillment_split')<>1 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='订单拆分台账未创建';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL phase3_order_assert();
|
||||
DROP PROCEDURE IF EXISTS phase3_order_assert;
|
||||
Reference in New Issue
Block a user