fix(iam): 收敛仓库角色权限并纳入统一迁移
This commit is contained in:
@@ -47,10 +47,19 @@ CALL phase3_inbound_add_column(
|
||||
CALL phase3_inbound_add_column(
|
||||
'fl_inbound_order', 'package_count',
|
||||
"INT UNSIGNED NULL COMMENT '入仓单箱数;三期唯一数量口径' AFTER total_package_count");
|
||||
CALL phase3_inbound_add_column(
|
||||
'fl_inbound_order', 'split_purpose',
|
||||
"VARCHAR(16) NOT NULL DEFAULT 'MASTER' COMMENT '履约分片用途:MASTER、LOADED、REMAINDER' AFTER package_count");
|
||||
CALL phase3_inbound_add_column(
|
||||
'fl_inbound_order', 'master_order_id',
|
||||
"BIGINT UNSIGNED GENERATED ALWAYS AS (CASE WHEN inbound_role = 'MASTER' AND deleted = 0 THEN order_id ELSE NULL END) STORED COMMENT '仅用于约束有效主单订单唯一性'");
|
||||
|
||||
-- 旧实现只生成未装余量子单,可无歧义映射为 REMAINDER。
|
||||
UPDATE fl_inbound_order
|
||||
SET split_purpose = 'REMAINDER'
|
||||
WHERE inbound_role = 'CHILD'
|
||||
AND split_purpose = 'MASTER';
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase3_inbound_add_column;
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase3_inbound_add_index;
|
||||
@@ -78,9 +87,6 @@ DELIMITER ;
|
||||
CALL phase3_inbound_add_index(
|
||||
'fl_inbound_order', 'uk_inbound_master_order',
|
||||
'UNIQUE KEY `uk_inbound_master_order` (`tenant_id`, `master_order_id`)');
|
||||
CALL phase3_inbound_add_index(
|
||||
'fl_inbound_order', 'uk_inbound_one_child',
|
||||
'UNIQUE KEY `uk_inbound_one_child` (`tenant_id`, `parent_inbound_id`)');
|
||||
CALL phase3_inbound_add_index(
|
||||
'fl_inbound_order', 'idx_inbound_root_role',
|
||||
'KEY `idx_inbound_root_role` (`tenant_id`, `root_inbound_id`, `inbound_role`, `created_at`, `id`)');
|
||||
@@ -105,10 +111,21 @@ BEGIN
|
||||
) THEN
|
||||
ALTER TABLE fl_inbound_order DROP INDEX uk_inbound_parent_sequence;
|
||||
END IF;
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'fl_inbound_order'
|
||||
AND index_name = 'uk_inbound_one_child'
|
||||
) THEN
|
||||
ALTER TABLE fl_inbound_order DROP INDEX uk_inbound_one_child;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL phase3_inbound_drop_legacy_index();
|
||||
DROP PROCEDURE IF EXISTS phase3_inbound_drop_legacy_index;
|
||||
CALL phase3_inbound_add_index(
|
||||
'fl_inbound_order', 'uk_inbound_parent_split_sequence',
|
||||
'UNIQUE KEY `uk_inbound_parent_split_sequence` (`tenant_id`, `parent_inbound_id`, `split_sequence`)');
|
||||
DROP PROCEDURE IF EXISTS phase3_inbound_add_index;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_inbound_split_batch (
|
||||
@@ -215,6 +232,27 @@ CALL phase3_inbound_add_column(
|
||||
CALL phase3_inbound_add_column(
|
||||
'fl_inbound_split_item', 'line_no',
|
||||
"INT UNSIGNED NOT NULL DEFAULT 1 COMMENT '同一拆分批次内的行号' AFTER child_inbound_id");
|
||||
CALL phase3_inbound_add_column(
|
||||
'fl_inbound_split_batch', 'loaded_child_inbound_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '本批次实装子单ID' AFTER package_count");
|
||||
CALL phase3_inbound_add_column(
|
||||
'fl_inbound_split_batch', 'source_inbound_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '本批次实际消耗的MASTER或REMAINDER分片ID' AFTER parent_inbound_id");
|
||||
CALL phase3_inbound_add_column(
|
||||
'fl_inbound_split_batch', 'remainder_child_inbound_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '本批次余量子单ID' AFTER loaded_child_inbound_id");
|
||||
CALL phase3_inbound_add_column(
|
||||
'fl_inbound_split_batch', 'loaded_package_count',
|
||||
"INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '本批次实装箱数' AFTER remainder_child_inbound_id");
|
||||
CALL phase3_inbound_add_column(
|
||||
'fl_inbound_split_batch', 'remaining_package_count',
|
||||
"INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '本批次未装箱数' AFTER loaded_package_count");
|
||||
CALL phase3_inbound_add_column(
|
||||
'fl_inbound_split_batch', 'loading_plan_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '关联装柜计划/柜次' AFTER remaining_package_count");
|
||||
CALL phase3_inbound_add_column(
|
||||
'fl_inbound_split_batch', 'container_booking_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '关联柜次标识(兼容查询)' AFTER loading_plan_id");
|
||||
DROP PROCEDURE IF EXISTS phase3_inbound_add_column;
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase3_inbound_add_relation;
|
||||
@@ -245,6 +283,15 @@ CALL phase3_inbound_add_relation(
|
||||
CALL phase3_inbound_add_relation(
|
||||
'fl_inbound_order', 'fk_inbound_root_order',
|
||||
'FOREIGN KEY (`root_inbound_id`) REFERENCES `fl_inbound_order` (`id`)');
|
||||
CALL phase3_inbound_add_relation(
|
||||
'fl_inbound_split_batch', 'fk_inbound_split_loaded_child',
|
||||
'FOREIGN KEY (`loaded_child_inbound_id`) REFERENCES `fl_inbound_order` (`id`)');
|
||||
CALL phase3_inbound_add_relation(
|
||||
'fl_inbound_split_batch', 'fk_inbound_split_source',
|
||||
'FOREIGN KEY (`source_inbound_id`) REFERENCES `fl_inbound_order` (`id`)');
|
||||
CALL phase3_inbound_add_relation(
|
||||
'fl_inbound_split_batch', 'fk_inbound_split_remainder_child',
|
||||
'FOREIGN KEY (`remainder_child_inbound_id`) REFERENCES `fl_inbound_order` (`id`)');
|
||||
CALL phase3_inbound_add_relation(
|
||||
'fl_inbound_measurement', 'fk_measure_split_batch',
|
||||
'FOREIGN KEY (`split_batch_id`) REFERENCES `fl_inbound_split_batch` (`id`)');
|
||||
@@ -290,9 +337,8 @@ CALL phase3_inbound_add_index(
|
||||
'fl_inbound_split_item', 'uk_inbound_split_batch_line',
|
||||
'UNIQUE KEY `uk_inbound_split_batch_line` (`tenant_id`, `split_batch_id`, `line_no`)');
|
||||
CALL phase3_inbound_add_index(
|
||||
'fl_inbound_split_item', 'uk_inbound_split_batch_source_item',
|
||||
'UNIQUE KEY `uk_inbound_split_batch_source_item` (`tenant_id`, `split_batch_id`, `source_measurement_item_id`)');
|
||||
DROP PROCEDURE IF EXISTS phase3_inbound_add_index;
|
||||
'fl_inbound_split_batch', 'idx_inbound_split_loading_plan',
|
||||
'KEY `idx_inbound_split_loading_plan` (`tenant_id`, `loading_plan_id`, `container_booking_id`, `status`, `id`)');
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase3_inbound_drop_split_legacy_index;
|
||||
DELIMITER $$
|
||||
@@ -306,10 +352,18 @@ BEGIN
|
||||
AND table_name = 'fl_inbound_split_item' AND index_name = 'uk_inbound_split_batch_child') THEN
|
||||
ALTER TABLE fl_inbound_split_item DROP INDEX uk_inbound_split_batch_child;
|
||||
END IF;
|
||||
IF EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE()
|
||||
AND table_name = 'fl_inbound_split_item' AND index_name = 'uk_inbound_split_batch_source_item') THEN
|
||||
ALTER TABLE fl_inbound_split_item DROP INDEX uk_inbound_split_batch_source_item;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL phase3_inbound_drop_split_legacy_index();
|
||||
DROP PROCEDURE IF EXISTS phase3_inbound_drop_split_legacy_index;
|
||||
CALL phase3_inbound_add_index(
|
||||
'fl_inbound_split_item', 'uk_inbound_split_batch_source_child',
|
||||
'UNIQUE KEY `uk_inbound_split_batch_source_child` (`tenant_id`, `split_batch_id`, `source_measurement_item_id`, `child_inbound_id`)');
|
||||
DROP PROCEDURE IF EXISTS phase3_inbound_add_index;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_inbound_order_parent_before_insert;
|
||||
DROP TRIGGER IF EXISTS trg_inbound_order_parent_before_update;
|
||||
@@ -332,6 +386,9 @@ BEGIN
|
||||
IF NEW.split_status NOT IN ('NOT_SPLIT', 'PARTIALLY_SPLIT', 'FULLY_SPLIT') THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单拆分状态非法';
|
||||
END IF;
|
||||
IF NEW.split_purpose NOT IN ('MASTER', 'LOADED', 'REMAINDER') THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单分片用途非法';
|
||||
END IF;
|
||||
IF NEW.package_count IS NOT NULL AND NEW.package_count = 0 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单箱数必须大于0';
|
||||
END IF;
|
||||
@@ -343,8 +400,8 @@ BEGIN
|
||||
OR NEW.split_sequence IS NULL OR NEW.package_count IS NULL THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '子入仓单缺少主单、顺序号或箱数';
|
||||
END IF;
|
||||
IF NEW.split_status <> 'NOT_SPLIT' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主子仅两级,子入仓单不可继续拆分';
|
||||
IF NEW.split_purpose = 'MASTER' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '子入仓单必须标记LOADED或REMAINDER';
|
||||
END IF;
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM fl_inbound_order parent_order
|
||||
@@ -365,9 +422,13 @@ BEGIN
|
||||
IF NOT (OLD.inbound_role <=> NEW.inbound_role)
|
||||
OR NOT (OLD.parent_inbound_id <=> NEW.parent_inbound_id)
|
||||
OR NOT (OLD.root_inbound_id <=> NEW.root_inbound_id)
|
||||
OR NOT (OLD.split_sequence <=> NEW.split_sequence) THEN
|
||||
OR NOT (OLD.split_sequence <=> NEW.split_sequence)
|
||||
OR NOT (OLD.split_purpose <=> NEW.split_purpose) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单主子归属创建后不可修改';
|
||||
END IF;
|
||||
IF NEW.split_purpose NOT IN ('MASTER', 'LOADED', 'REMAINDER') THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单分片用途非法';
|
||||
END IF;
|
||||
IF NEW.inbound_role = 'CHILD'
|
||||
AND (NEW.package_count IS NULL OR NEW.package_count = 0) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '子入仓单箱数必须大于0';
|
||||
@@ -375,8 +436,8 @@ BEGIN
|
||||
IF NEW.split_status NOT IN ('NOT_SPLIT', 'PARTIALLY_SPLIT', 'FULLY_SPLIT') THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入仓单拆分状态非法';
|
||||
END IF;
|
||||
IF NEW.inbound_role = 'CHILD' AND NEW.split_status <> 'NOT_SPLIT' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主子仅两级,子入仓单不可继续拆分';
|
||||
IF NEW.inbound_role = 'CHILD' AND NEW.split_purpose = 'MASTER' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '子入仓单用途非法';
|
||||
END IF;
|
||||
IF NEW.inbound_role = 'MASTER'
|
||||
AND ((NEW.package_count = 0 AND NEW.split_status <> 'FULLY_SPLIT')
|
||||
@@ -402,6 +463,7 @@ BEGIN
|
||||
AND parent_order.inbound_role = 'MASTER'
|
||||
AND child_order.tenant_id = NEW.tenant_id
|
||||
AND child_order.inbound_role = 'CHILD'
|
||||
AND child_order.split_purpose IN ('LOADED', 'REMAINDER')
|
||||
AND child_order.parent_inbound_id = NEW.parent_inbound_id
|
||||
AND child_order.package_count >= NEW.package_count
|
||||
AND child_order.deleted = 0
|
||||
@@ -412,7 +474,7 @@ BEGIN
|
||||
WHERE source_item.id = NEW.source_measurement_item_id
|
||||
AND source_item.tenant_id = NEW.tenant_id
|
||||
AND source_item.measurement_id = NEW.source_measurement_id
|
||||
AND source_batch.inbound_id = NEW.parent_inbound_id
|
||||
AND source_batch.inbound_id = COALESCE(batch.source_inbound_id, batch.parent_inbound_id)
|
||||
))
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分明细与批次、主单或子单不一致';
|
||||
@@ -435,6 +497,27 @@ BEGIN
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分批次主单不存在或客户归属不一致';
|
||||
END IF;
|
||||
IF NEW.source_inbound_id IS NOT NULL AND NOT EXISTS (
|
||||
SELECT 1 FROM fl_inbound_order source_order
|
||||
WHERE source_order.id = NEW.source_inbound_id
|
||||
AND source_order.tenant_id = NEW.tenant_id
|
||||
AND source_order.deleted = 0
|
||||
AND (source_order.id = NEW.parent_inbound_id OR
|
||||
(source_order.inbound_role = 'CHILD'
|
||||
AND source_order.parent_inbound_id = NEW.parent_inbound_id
|
||||
AND source_order.split_purpose = 'REMAINDER'))
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分批次来源必须是根主单或其余量子单';
|
||||
END IF;
|
||||
IF NEW.loaded_package_count > 0 AND (
|
||||
NEW.loading_plan_id IS NULL OR NEW.container_booking_id IS NULL OR NOT EXISTS (
|
||||
SELECT 1 FROM fl_loading_plan plan
|
||||
WHERE plan.id = NEW.loading_plan_id
|
||||
AND plan.container_booking_id = NEW.container_booking_id
|
||||
)
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '实装拆分批次必须绑定匹配的装柜计划和柜次';
|
||||
END IF;
|
||||
END$$
|
||||
|
||||
CREATE TRIGGER trg_inbound_split_batch_before_update
|
||||
@@ -444,15 +527,29 @@ BEGIN
|
||||
IF OLD.status IN ('APPLIED', 'CANCELLED') THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已结束拆分批次不可修改';
|
||||
END IF;
|
||||
IF NOT (OLD.parent_inbound_id <=> NEW.parent_inbound_id)
|
||||
OR NOT (OLD.source_inbound_id <=> NEW.source_inbound_id)
|
||||
OR NOT (OLD.loaded_child_inbound_id <=> NEW.loaded_child_inbound_id)
|
||||
OR NOT (OLD.remainder_child_inbound_id <=> NEW.remainder_child_inbound_id)
|
||||
OR NOT (OLD.package_count <=> NEW.package_count)
|
||||
OR NOT (OLD.loaded_package_count <=> NEW.loaded_package_count)
|
||||
OR NOT (OLD.remaining_package_count <=> NEW.remaining_package_count)
|
||||
OR NOT (OLD.loading_plan_id <=> NEW.loading_plan_id)
|
||||
OR NOT (OLD.container_booking_id <=> NEW.container_booking_id) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆分批次来源、子单、柜次与数量创建后不可修改';
|
||||
END IF;
|
||||
IF NEW.status = 'APPLIED' THEN
|
||||
IF NEW.applied_at IS NULL
|
||||
OR (NEW.loaded_package_count > 0 AND (
|
||||
NEW.loading_plan_id IS NULL OR NEW.container_booking_id IS NULL OR NOT EXISTS (
|
||||
SELECT 1 FROM fl_loading_plan plan
|
||||
WHERE plan.id = NEW.loading_plan_id
|
||||
AND plan.container_booking_id = NEW.container_booking_id)))
|
||||
OR NOT EXISTS (
|
||||
SELECT 1 FROM fl_inbound_order parent_order
|
||||
WHERE parent_order.id = NEW.parent_inbound_id
|
||||
AND parent_order.tenant_id = NEW.tenant_id
|
||||
AND parent_order.inbound_role = 'MASTER'
|
||||
AND parent_order.package_count > 0
|
||||
AND parent_order.split_status = 'PARTIALLY_SPLIT'
|
||||
AND parent_order.version > NEW.expected_parent_version
|
||||
) OR
|
||||
(SELECT COALESCE(SUM(item.package_count), 0)
|
||||
@@ -462,7 +559,26 @@ BEGIN
|
||||
(SELECT COUNT(DISTINCT item.child_inbound_id)
|
||||
FROM fl_inbound_split_item item
|
||||
WHERE item.tenant_id = NEW.tenant_id
|
||||
AND item.split_batch_id = NEW.id) <> 1 OR
|
||||
AND item.split_batch_id = NEW.id) NOT BETWEEN 1 AND 2 OR
|
||||
(SELECT COALESCE(SUM(item.package_count), 0)
|
||||
FROM fl_inbound_split_item item
|
||||
WHERE item.tenant_id = NEW.tenant_id
|
||||
AND item.split_batch_id = NEW.id
|
||||
AND item.child_inbound_id = NEW.loaded_child_inbound_id) <> NEW.loaded_package_count OR
|
||||
(SELECT COALESCE(SUM(item.package_count), 0)
|
||||
FROM fl_inbound_split_item item
|
||||
WHERE item.tenant_id = NEW.tenant_id
|
||||
AND item.split_batch_id = NEW.id
|
||||
AND item.child_inbound_id = NEW.remainder_child_inbound_id) <> NEW.remaining_package_count OR
|
||||
NEW.loaded_package_count + NEW.remaining_package_count <> NEW.package_count OR
|
||||
(NEW.loaded_package_count > 0 AND NEW.loaded_child_inbound_id IS NULL) OR
|
||||
(NEW.remaining_package_count > 0 AND NEW.remainder_child_inbound_id IS NULL) OR
|
||||
(NEW.loaded_child_inbound_id IS NOT NULL AND NOT EXISTS (
|
||||
SELECT 1 FROM fl_inbound_order c WHERE c.id = NEW.loaded_child_inbound_id
|
||||
AND c.parent_inbound_id = NEW.parent_inbound_id AND c.split_purpose = 'LOADED')) OR
|
||||
(NEW.remainder_child_inbound_id IS NOT NULL AND NOT EXISTS (
|
||||
SELECT 1 FROM fl_inbound_order c WHERE c.id = NEW.remainder_child_inbound_id
|
||||
AND c.parent_inbound_id = NEW.parent_inbound_id AND c.split_purpose = 'REMAINDER')) OR
|
||||
EXISTS (SELECT 1
|
||||
FROM fl_inbound_split_item item
|
||||
JOIN fl_inbound_order child_order ON child_order.id = item.child_inbound_id
|
||||
@@ -541,12 +657,12 @@ BEGIN
|
||||
AND table_name = 'fl_inbound_order'
|
||||
AND column_name IN ('inbound_role', 'parent_inbound_id', 'root_inbound_id',
|
||||
'split_sequence', 'split_status', 'package_count',
|
||||
'master_order_id')) <> 7 THEN
|
||||
'master_order_id', 'split_purpose')) <> 8 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主子入仓单关系字段未完整创建';
|
||||
END IF;
|
||||
IF (SELECT COUNT(DISTINCT index_name) FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND index_name IN ('uk_inbound_master_order', 'uk_inbound_one_child',
|
||||
AND index_name IN ('uk_inbound_master_order', 'uk_inbound_parent_split_sequence',
|
||||
'uk_inbound_split_operation', 'uk_inbound_split_idempotency',
|
||||
'uk_inbound_split_history_operation')) <> 5 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主子入仓单唯一约束未完整创建';
|
||||
@@ -554,8 +670,9 @@ BEGIN
|
||||
IF (SELECT COUNT(*) FROM information_schema.table_constraints
|
||||
WHERE constraint_schema = DATABASE()
|
||||
AND constraint_name IN ('fk_inbound_parent_order', 'fk_inbound_root_order',
|
||||
'fk_inbound_split_source', 'fk_inbound_split_loaded_child', 'fk_inbound_split_remainder_child',
|
||||
'fk_measure_split_batch', 'fk_measure_source_batch',
|
||||
'fk_measure_item_split_item', 'fk_measure_item_source_item')) <> 6 THEN
|
||||
'fk_measure_item_split_item', 'fk_measure_item_source_item')) <> 9 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主子入仓单与测量关联约束未完整创建';
|
||||
END IF;
|
||||
IF (SELECT COUNT(*) FROM information_schema.triggers
|
||||
@@ -575,7 +692,7 @@ BEGIN
|
||||
SELECT 1 FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'fl_inbound_order'
|
||||
AND index_name = 'uk_inbound_tenant_order'
|
||||
AND index_name IN ('uk_inbound_tenant_order', 'uk_inbound_one_child')
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '旧订单唯一索引仍阻止创建子入仓单';
|
||||
END IF;
|
||||
|
||||
@@ -40,9 +40,8 @@ CREATE TABLE IF NOT EXISTS fl_split_operation (
|
||||
|
||||
/*
|
||||
* 部分装柜拆单契约:
|
||||
* 1. parent_inbound_id 为空的是主单,非空的是唯一二级子单;
|
||||
* 2. 锁定阶段只记录箱数,完成阶段绑定由剩余箱数生成的子单;
|
||||
* 3. 子单不能再作为父单,锁定数量创建后不可修改。
|
||||
* 本文件仅保留历史部分锁定明细;主单、LOADED/REMAINDER 多分片及层级约束
|
||||
* 统一由 phase3-inbound-parent-child-schema.sql 维护。
|
||||
*/
|
||||
DROP PROCEDURE IF EXISTS phase3_split_add_column;
|
||||
DELIMITER $$
|
||||
@@ -76,8 +75,8 @@ SET @phase3_split_index_exists = (
|
||||
WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_order'
|
||||
AND index_name = 'uk_inbound_one_secondary'
|
||||
);
|
||||
SET @phase3_split_ddl = IF(@phase3_split_index_exists = 0,
|
||||
'ALTER TABLE fl_inbound_order ADD UNIQUE KEY uk_inbound_one_secondary (tenant_id, parent_inbound_id)',
|
||||
SET @phase3_split_ddl = IF(@phase3_split_index_exists > 0,
|
||||
'ALTER TABLE fl_inbound_order DROP INDEX uk_inbound_one_secondary',
|
||||
'SELECT 1');
|
||||
PREPARE phase3_split_stmt FROM @phase3_split_ddl;
|
||||
EXECUTE phase3_split_stmt;
|
||||
@@ -88,8 +87,8 @@ SET @phase3_split_fk_exists = (
|
||||
WHERE constraint_schema = DATABASE() AND table_name = 'fl_inbound_order'
|
||||
AND constraint_name = 'fk_inbound_parent' AND constraint_type = 'FOREIGN KEY'
|
||||
);
|
||||
SET @phase3_split_ddl = IF(@phase3_split_fk_exists = 0,
|
||||
'ALTER TABLE fl_inbound_order ADD CONSTRAINT fk_inbound_parent FOREIGN KEY (parent_inbound_id) REFERENCES fl_inbound_order (id)',
|
||||
SET @phase3_split_ddl = IF(@phase3_split_fk_exists > 0,
|
||||
'ALTER TABLE fl_inbound_order DROP FOREIGN KEY fk_inbound_parent',
|
||||
'SELECT 1');
|
||||
PREPARE phase3_split_stmt FROM @phase3_split_ddl;
|
||||
EXECUTE phase3_split_stmt;
|
||||
@@ -140,58 +139,6 @@ DROP TRIGGER IF EXISTS trg_inbound_split_detail_before_insert;
|
||||
DROP TRIGGER IF EXISTS trg_inbound_split_detail_before_update;
|
||||
DROP TRIGGER IF EXISTS trg_inbound_split_detail_before_delete;
|
||||
DELIMITER $$
|
||||
CREATE TRIGGER trg_inbound_split_before_insert
|
||||
BEFORE INSERT ON fl_inbound_order
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
DECLARE v_parent_tenant BIGINT UNSIGNED;
|
||||
DECLARE v_parent_parent BIGINT UNSIGNED;
|
||||
IF NEW.parent_inbound_id IS NOT NULL THEN
|
||||
SELECT tenant_id, parent_inbound_id INTO v_parent_tenant, v_parent_parent
|
||||
FROM fl_inbound_order WHERE id = NEW.parent_inbound_id AND deleted = 0;
|
||||
IF v_parent_tenant <> NEW.tenant_id THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二级子入库单与主单客户归属不一致';
|
||||
END IF;
|
||||
IF v_parent_parent IS NOT NULL THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二级子入库单不能继续拆分';
|
||||
END IF;
|
||||
IF NEW.split_status <> 'NOT_SPLIT' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二级子入库单拆单状态必须为NOT_SPLIT';
|
||||
END IF;
|
||||
END IF;
|
||||
END$$
|
||||
|
||||
CREATE TRIGGER trg_inbound_split_before_update
|
||||
BEFORE UPDATE ON fl_inbound_order
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NOT (NEW.parent_inbound_id <=> OLD.parent_inbound_id) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '入库单主子关系创建后不可修改';
|
||||
END IF;
|
||||
IF OLD.parent_inbound_id IS NOT NULL AND NEW.split_status <> 'NOT_SPLIT' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二级子入库单不能继续拆分';
|
||||
END IF;
|
||||
IF OLD.parent_inbound_id IS NULL
|
||||
AND NEW.split_status NOT IN ('NOT_SPLIT', 'PARTIALLY_SPLIT', 'FULLY_SPLIT') THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '主入库单拆单状态非法';
|
||||
END IF;
|
||||
IF OLD.split_status = 'FULLY_SPLIT' AND NEW.split_status <> 'FULLY_SPLIT' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已完成拆单不能回退';
|
||||
END IF;
|
||||
IF NEW.split_status = 'FULLY_SPLIT' AND (
|
||||
(SELECT COUNT(*) FROM fl_inbound_order child
|
||||
WHERE child.tenant_id = NEW.tenant_id AND child.parent_inbound_id = NEW.id
|
||||
AND child.deleted = 0) <> 1
|
||||
OR (SELECT COUNT(*) FROM fl_inbound_split_detail detail
|
||||
WHERE detail.tenant_id = NEW.tenant_id AND detail.parent_inbound_id = NEW.id) = 0
|
||||
OR EXISTS (SELECT 1 FROM fl_inbound_split_detail detail
|
||||
WHERE detail.tenant_id = NEW.tenant_id AND detail.parent_inbound_id = NEW.id
|
||||
AND detail.status <> 'COMPLETED')
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '剩余箱数二级子单及拆分明细未完成';
|
||||
END IF;
|
||||
END$$
|
||||
|
||||
CREATE TRIGGER trg_inbound_split_detail_before_insert
|
||||
BEFORE INSERT ON fl_inbound_split_detail
|
||||
FOR EACH ROW
|
||||
@@ -239,6 +186,11 @@ BEGIN
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
-- 主子关系及不可回退规则由 phase3-inbound-parent-child-schema.sql 统一维护。
|
||||
-- 旧触发器要求“一父一子”,会阻止 LOADED/REMAINDER 多分片,迁移完成后必须移除。
|
||||
DROP TRIGGER IF EXISTS trg_inbound_split_before_insert;
|
||||
DROP TRIGGER IF EXISTS trg_inbound_split_before_update;
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase3_split_assert_contract;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE phase3_split_assert_contract()
|
||||
@@ -254,17 +206,16 @@ BEGIN
|
||||
END IF;
|
||||
IF (SELECT COUNT(DISTINCT index_name) FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND index_name IN ('uk_inbound_one_secondary', 'uk_inbound_split_parent_detail',
|
||||
'uk_inbound_split_child_detail', 'uk_inbound_split_operation')) <> 4 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆单迁移失败:唯一子单或部分锁定幂等索引缺失';
|
||||
AND index_name IN ('uk_inbound_split_parent_detail',
|
||||
'uk_inbound_split_child_detail', 'uk_inbound_split_operation')) <> 3 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆单迁移失败:部分锁定幂等索引缺失';
|
||||
END IF;
|
||||
IF (SELECT COUNT(*) FROM information_schema.triggers
|
||||
WHERE trigger_schema = DATABASE()
|
||||
AND trigger_name IN ('trg_inbound_split_before_insert', 'trg_inbound_split_before_update',
|
||||
'trg_inbound_split_detail_before_insert',
|
||||
AND trigger_name IN ('trg_inbound_split_detail_before_insert',
|
||||
'trg_inbound_split_detail_before_update',
|
||||
'trg_inbound_split_detail_before_delete')) <> 5 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆单迁移失败:主子层级或部分锁定触发器未完整创建';
|
||||
'trg_inbound_split_detail_before_delete')) <> 3 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拆单迁移失败:部分锁定触发器未完整创建';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
@@ -3,6 +3,21 @@ info:
|
||||
title: Phase 3 Loading Evidence and Picking API
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/evidence/pda:
|
||||
get:
|
||||
summary: List loading evidence for the active PDA worker
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}]
|
||||
responses:
|
||||
'200': {description: Business envelope; requires platform:firstleg:pda:work}
|
||||
post:
|
||||
summary: Append one PDA-captured loading evidence photo
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json: {schema: {$ref: '#/components/schemas/UpsertEvidenceRequest'}}
|
||||
responses:
|
||||
'200': {description: Business envelope; requires platform:firstleg:pda:work}
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/evidence:
|
||||
get:
|
||||
summary: List loading evidence bound to one customer inbound
|
||||
@@ -18,6 +33,16 @@ paths:
|
||||
application/json: {schema: {$ref: '#/components/schemas/UpsertEvidenceRequest'}}
|
||||
responses:
|
||||
'200': {description: Business envelope; ownership, type and version failures use business error codes}
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/evidence/{evidenceId}/void/pda:
|
||||
post:
|
||||
summary: Void an incorrect PDA loading evidence photo
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/EvidenceId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json: {schema: {$ref: '#/components/schemas/VoidEvidenceRequest'}}
|
||||
responses:
|
||||
'200': {description: Business envelope; requires platform:firstleg:pda:work}
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/evidence/{evidenceId}/void:
|
||||
post:
|
||||
summary: Void an incorrect loading evidence photo without hard deletion
|
||||
|
||||
Reference in New Issue
Block a user