diff --git a/deploy/sql/头程数据库统一增量-MySQL8.sql b/deploy/sql/头程数据库统一增量-MySQL8.sql index f09c53b52..99808237a 100644 --- a/deploy/sql/头程数据库统一增量-MySQL8.sql +++ b/deploy/sql/头程数据库统一增量-MySQL8.sql @@ -8713,15 +8713,28 @@ DELIMITER ; CALL assert_phase3_final_schema(); DROP PROCEDURE IF EXISTS assert_phase3_final_schema; --- 二、三期对外统一使用 INBOUNDED;历史 WAIT_SPLIT 仅由查询适配为 INBOUNDED,统一迁移不改写主表或历史状态。 -SELECT 'PASS' AS migration_status, - '统一增量全部执行完成;箱号映射、单箱测量、三期订舱、拖车预约、出仓、报关、船期、装柜模型桥接、PDA收敛、费用、公共资源及可靠审计结构校验通过' AS message; - -- PDA 部分拣货释放策略:NULL/AUTO 按比例释放,MANUAL + [] 表示人工明确不释放。 USE baiyue_firstleg; -ALTER TABLE fl_loading_pick_item - ADD COLUMN IF NOT EXISTS release_mode VARCHAR(10) NULL COMMENT 'AUTO按比例释放,MANUAL按人工选择释放' AFTER item_status, - ADD COLUMN IF NOT EXISTS release_location_ids JSON NULL COMMENT '人工选择释放库位ID;空数组表示明确释放0个' AFTER release_mode; +DROP PROCEDURE IF EXISTS phase3_pda_add_column; +DELIMITER $$ +CREATE PROCEDURE phase3_pda_add_column(IN p_table VARCHAR(64), IN p_column VARCHAR(64), IN p_ddl 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 @phase3_pda_ddl = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN ', p_ddl); + PREPARE phase3_pda_stmt FROM @phase3_pda_ddl; + EXECUTE phase3_pda_stmt; + DEALLOCATE PREPARE phase3_pda_stmt; + END IF; +END$$ +DELIMITER ; + +CALL phase3_pda_add_column('fl_loading_pick_item', 'release_mode', + '`release_mode` VARCHAR(10) NULL COMMENT ''AUTO按比例释放,MANUAL按人工选择释放'' AFTER `item_status`'); +CALL phase3_pda_add_column('fl_loading_pick_item', 'release_location_ids', + '`release_location_ids` JSON NULL COMMENT ''人工选择释放库位ID;空数组表示明确释放0个'' AFTER `release_mode`'); -- PDA 多人协作:领取粒度由整柜任务下沉为单条拣货明细。 CREATE TABLE IF NOT EXISTS fl_loading_pick_item_assignment ( pick_item_id BIGINT UNSIGNED NOT NULL, @@ -8739,10 +8752,21 @@ CREATE TABLE IF NOT EXISTS fl_loading_pick_item_assignment ( CONSTRAINT chk_loading_pick_item_assignment_type CHECK (assignment_type IN ('CLAIMED', 'ASSIGNED')) ) ENGINE=InnoDB COMMENT='PDA拣货明细领取与作业员分配'; +SET @phase3_pda_check_ddl = IF( + EXISTS (SELECT 1 FROM information_schema.table_constraints + WHERE constraint_schema = DATABASE() + AND table_name = 'fl_loading_pick_operation' + AND constraint_name = 'chk_loading_pick_operation_action' + AND constraint_type = 'CHECK'), + 'ALTER TABLE fl_loading_pick_operation DROP CHECK chk_loading_pick_operation_action', + 'SELECT 1'); +PREPARE phase3_pda_check_stmt FROM @phase3_pda_check_ddl; +EXECUTE phase3_pda_check_stmt; +DEALLOCATE PREPARE phase3_pda_check_stmt; + ALTER TABLE fl_loading_pick_operation - DROP CHECK IF EXISTS chk_loading_pick_operation_action, ADD CONSTRAINT chk_loading_pick_operation_action - CHECK (action_type IN ('CLAIM', 'ASSIGN', 'ITEM_CLAIM', 'ITEM_ASSIGN', 'REVIEW')); + CHECK (action_type IN ('CLAIM', 'ASSIGN', 'ITEM_CLAIM', 'ITEM_ASSIGN', 'REVIEW')); DROP PROCEDURE IF EXISTS assert_phase3_pda_item_assignment_schema; DELIMITER $$ @@ -8763,8 +8787,20 @@ DELIMITER ; CALL assert_phase3_pda_item_assignment_schema(); DROP PROCEDURE IF EXISTS assert_phase3_pda_item_assignment_schema; -ALTER TABLE fl_inbound_pallet_allocation - ADD COLUMN IF NOT EXISTS reservation_id BIGINT UNSIGNED NULL COMMENT '仓库预占资源唯一ID,A0共享场景用于区分托盘' AFTER location_code; +CALL phase3_pda_add_column('fl_inbound_pallet_allocation', 'reservation_id', + '`reservation_id` BIGINT UNSIGNED NULL COMMENT ''仓库预占资源唯一ID,A0共享场景用于区分托盘'' AFTER `location_code`'); + +SET @phase3_pda_index_ddl = IF( + EXISTS (SELECT 1 FROM information_schema.statistics + WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_pallet_allocation' + AND index_name = 'idx_allocation_reservation'), + 'SELECT 1', + 'ALTER TABLE fl_inbound_pallet_allocation ADD INDEX idx_allocation_reservation (reservation_id)'); +PREPARE phase3_pda_index_stmt FROM @phase3_pda_index_ddl; +EXECUTE phase3_pda_index_stmt; +DEALLOCATE PREPARE phase3_pda_index_stmt; + +DROP PROCEDURE IF EXISTS phase3_pda_add_column; /* Phase 3: six-carrier schedule query tasks, truthful manual fallback and three-day rate limit. */ USE baiyue_firstleg; @@ -9080,19 +9116,23 @@ INSERT INTO tmp_phase3_warehouse_role_permission (role_code, permission_code) VA ('WAREHOUSE_TALLY', 'platform:firstleg:pda:view'), ('WAREHOUSE_TALLY', 'platform:firstleg:pda:review'), ('WAREHOUSE_TALLY', 'platform:firstleg:inbound:loading:view'), - ('WAREHOUSE_TALLY', 'platform:firstleg:inbound:loading:edit'), - ('WAREHOUSE_TALLY', 'platform:firstleg:inbound:picking:view'); + ('WAREHOUSE_TALLY', 'platform:firstleg:inbound:loading:edit'); DROP PROCEDURE IF EXISTS assert_phase3_warehouse_role_permissions_available; DELIMITER $$ CREATE PROCEDURE assert_phase3_warehouse_role_permissions_available() BEGIN - IF (SELECT COUNT(*) FROM tmp_phase3_warehouse_role_permission - WHERE role_code = 'WAREHOUSE_OPERATOR') <> 5 - OR (SELECT COUNT(*) FROM tmp_phase3_warehouse_role_permission - WHERE role_code = 'WAREHOUSE_PICKER') <> 2 - OR (SELECT COUNT(*) FROM tmp_phase3_warehouse_role_permission - WHERE role_code = 'WAREHOUSE_TALLY') <> 12 THEN + DECLARE operator_permission_count INT DEFAULT 0; + DECLARE picker_permission_count INT DEFAULT 0; + DECLARE tally_permission_count INT DEFAULT 0; + SELECT COALESCE(SUM(role_code = 'WAREHOUSE_OPERATOR'), 0), + COALESCE(SUM(role_code = 'WAREHOUSE_PICKER'), 0), + COALESCE(SUM(role_code = 'WAREHOUSE_TALLY'), 0) + INTO operator_permission_count, picker_permission_count, tally_permission_count + FROM tmp_phase3_warehouse_role_permission; + IF operator_permission_count <> 5 + OR picker_permission_count <> 2 + OR tally_permission_count <> 12 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '仓库角色初始化失败:角色权限组合数量不符合预期'; END IF; @@ -9205,3 +9245,8 @@ CALL assert_phase3_warehouse_roles_ready(); DROP PROCEDURE assert_phase3_warehouse_roles_ready; DROP TEMPORARY TABLE IF EXISTS tmp_phase3_warehouse_role_permission; + +-- 二、三期对外统一使用 INBOUNDED;历史 WAIT_SPLIT 仅由查询适配为 INBOUNDED,统一迁移不改写主表或历史状态。 +-- 必须保持为整份脚本最后一个结果,避免后续增量失败却被部署端误判为成功。 +SELECT 'PASS' AS migration_status, + '统一增量全部执行完成;PDA多人协作、船司查询和仓库角色最小权限等全部增量及断言通过' AS message; diff --git a/iam/src/main/resources/db/phase3-warehouse-roles.sql b/iam/src/main/resources/db/phase3-warehouse-roles.sql index 9a9a65ace..75bf068e1 100644 --- a/iam/src/main/resources/db/phase3-warehouse-roles.sql +++ b/iam/src/main/resources/db/phase3-warehouse-roles.sql @@ -85,19 +85,23 @@ INSERT INTO tmp_phase3_warehouse_role_permission (role_code, permission_code) VA ('WAREHOUSE_TALLY', 'platform:firstleg:pda:view'), ('WAREHOUSE_TALLY', 'platform:firstleg:pda:review'), ('WAREHOUSE_TALLY', 'platform:firstleg:inbound:loading:view'), - ('WAREHOUSE_TALLY', 'platform:firstleg:inbound:loading:edit'), - ('WAREHOUSE_TALLY', 'platform:firstleg:inbound:picking:view'); + ('WAREHOUSE_TALLY', 'platform:firstleg:inbound:loading:edit'); DROP PROCEDURE IF EXISTS assert_phase3_warehouse_role_permissions_available; DELIMITER $$ CREATE PROCEDURE assert_phase3_warehouse_role_permissions_available() BEGIN - IF (SELECT COUNT(*) FROM tmp_phase3_warehouse_role_permission - WHERE role_code = 'WAREHOUSE_OPERATOR') <> 5 - OR (SELECT COUNT(*) FROM tmp_phase3_warehouse_role_permission - WHERE role_code = 'WAREHOUSE_PICKER') <> 2 - OR (SELECT COUNT(*) FROM tmp_phase3_warehouse_role_permission - WHERE role_code = 'WAREHOUSE_TALLY') <> 12 THEN + DECLARE operator_permission_count INT DEFAULT 0; + DECLARE picker_permission_count INT DEFAULT 0; + DECLARE tally_permission_count INT DEFAULT 0; + SELECT COALESCE(SUM(role_code = 'WAREHOUSE_OPERATOR'), 0), + COALESCE(SUM(role_code = 'WAREHOUSE_PICKER'), 0), + COALESCE(SUM(role_code = 'WAREHOUSE_TALLY'), 0) + INTO operator_permission_count, picker_permission_count, tally_permission_count + FROM tmp_phase3_warehouse_role_permission; + IF operator_permission_count <> 5 + OR picker_permission_count <> 2 + OR tally_permission_count <> 12 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '仓库角色初始化失败:角色权限组合数量不符合预期'; END IF; diff --git a/iam/src/test/java/com/baiyue/iam/repository/WarehouseRoleSqlContractTest.java b/iam/src/test/java/com/baiyue/iam/repository/WarehouseRoleSqlContractTest.java index 096299544..5e514b241 100644 --- a/iam/src/test/java/com/baiyue/iam/repository/WarehouseRoleSqlContractTest.java +++ b/iam/src/test/java/com/baiyue/iam/repository/WarehouseRoleSqlContractTest.java @@ -17,7 +17,14 @@ class WarehouseRoleSqlContractTest { @Test void unifiedDeploymentContainsTheSameWarehouseRoleContract() throws Exception { - assertWarehouseRoleContract(unifiedSql()); + String sql = unifiedSql(); + assertWarehouseRoleContract(sql); + assertThat(sql) + .doesNotContain("\n ADD COLUMN IF NOT EXISTS") + .doesNotContain("DROP CHECK IF EXISTS"); + assertThat(sql.lastIndexOf("SELECT 'PASS' AS migration_status")) + .isGreaterThan(sql.indexOf("CALL assert_phase3_warehouse_roles_ready()")); + assertThat(sql.trim()).endsWith("AS message;"); } private static void assertWarehouseRoleContract(String sql) { @@ -32,9 +39,9 @@ class WarehouseRoleSqlContractTest { .contains("'WAREHOUSE_TALLY', 'platform:firstleg:pda:review'") .contains("'WAREHOUSE_TALLY', 'platform:firstleg:inbound:loading:edit'") .contains("'WAREHOUSE_OPERATOR', 'platform:firstleg:inbound:loading:edit'") - .contains("WHERE role_code = 'WAREHOUSE_OPERATOR') <> 5") - .contains("WHERE role_code = 'WAREHOUSE_PICKER') <> 2") - .contains("WHERE role_code = 'WAREHOUSE_TALLY') <> 12") + .contains("operator_permission_count <> 5") + .contains("picker_permission_count <> 2") + .contains("tally_permission_count <> 12") .contains("依赖的三期平台权限尚未完整部署") .contains("DELETE role_permission") .contains("expected.permission_code IS NULL") @@ -61,6 +68,7 @@ class WarehouseRoleSqlContractTest { .doesNotContain("platform:firstleg:outbound-dispatch:confirm"); assertThat(roleGrantBlock(sql, "WAREHOUSE_TALLY")) .doesNotContain("platform:firstleg:inbound:loading:complete") + .doesNotContain("platform:firstleg:inbound:picking:view") .doesNotContain("platform:firstleg:outbound-dispatch:confirm"); assertThat(sql) .doesNotContain("'WAREHOUSE_OPERATOR', 'platform:firstleg:pda:assign'")