From e7dfd2790dc7752cafe01e253ad925bcb3e23cad Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Wed, 12 Aug 2026 19:14:41 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/sql/头程数据库统一增量-MySQL8.sql | 4 ++ .../db/phase3-smart-loading-schema.sql | 4 ++ ...ChannelContainerMigrationContractTest.java | 67 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 firstleg/src/test/java/com/baiyue/firstleg/repository/Phase3ChannelContainerMigrationContractTest.java diff --git a/deploy/sql/头程数据库统一增量-MySQL8.sql b/deploy/sql/头程数据库统一增量-MySQL8.sql index dc43fd4f9..23b34f840 100644 --- a/deploy/sql/头程数据库统一增量-MySQL8.sql +++ b/deploy/sql/头程数据库统一增量-MySQL8.sql @@ -5091,6 +5091,10 @@ BEGIN IF current_plan_status = 'LOADING' AND ( NOT (OLD.id <=> NEW.id) OR NOT (OLD.booking_no <=> NEW.booking_no) OR NOT (OLD.cabinet_no <=> NEW.cabinet_no) + OR NOT (OLD.channel_code <=> NEW.channel_code) + OR NOT (OLD.channel_name <=> NEW.channel_name) + OR NOT (OLD.ocean_booking_id <=> NEW.ocean_booking_id) + OR NOT (OLD.ocean_booking_requirement_id <=> NEW.ocean_booking_requirement_id) OR NOT (OLD.warehouse_id <=> NEW.warehouse_id) OR NOT (OLD.warehouse_name <=> NEW.warehouse_name) OR NOT (OLD.bill_of_lading_no <=> NEW.bill_of_lading_no) diff --git a/firstleg/src/main/resources/db/phase3-smart-loading-schema.sql b/firstleg/src/main/resources/db/phase3-smart-loading-schema.sql index 35f20fabe..29b8e54ae 100644 --- a/firstleg/src/main/resources/db/phase3-smart-loading-schema.sql +++ b/firstleg/src/main/resources/db/phase3-smart-loading-schema.sql @@ -563,6 +563,10 @@ BEGIN IF current_plan_status = 'LOADING' AND ( NOT (OLD.id <=> NEW.id) OR NOT (OLD.booking_no <=> NEW.booking_no) OR NOT (OLD.cabinet_no <=> NEW.cabinet_no) + OR NOT (OLD.channel_code <=> NEW.channel_code) + OR NOT (OLD.channel_name <=> NEW.channel_name) + OR NOT (OLD.ocean_booking_id <=> NEW.ocean_booking_id) + OR NOT (OLD.ocean_booking_requirement_id <=> NEW.ocean_booking_requirement_id) OR NOT (OLD.warehouse_id <=> NEW.warehouse_id) OR NOT (OLD.warehouse_name <=> NEW.warehouse_name) OR NOT (OLD.bill_of_lading_no <=> NEW.bill_of_lading_no) diff --git a/firstleg/src/test/java/com/baiyue/firstleg/repository/Phase3ChannelContainerMigrationContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/repository/Phase3ChannelContainerMigrationContractTest.java new file mode 100644 index 000000000..ab6db414c --- /dev/null +++ b/firstleg/src/test/java/com/baiyue/firstleg/repository/Phase3ChannelContainerMigrationContractTest.java @@ -0,0 +1,67 @@ +package com.baiyue.firstleg.repository; + +import org.junit.jupiter.api.Test; +import org.springframework.core.io.ClassPathResource; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; + +class Phase3ChannelContainerMigrationContractTest { + + @Test + void moduleMigrationRestoresGuardsBeforeSurfacingFailures() throws Exception { + assertSafeWindow(moduleMigration(), false); + } + + @Test + void unifiedMigrationRestoresGuardsBeforeSurfacingFailures() throws Exception { + assertSafeWindow(unifiedMigration(), true); + } + + private static void assertSafeWindow(String sql, boolean qualified) { + String containerTrigger = qualified + ? "CREATE TRIGGER baiyue_firstleg.trg_container_booking_before_update" + : "CREATE TRIGGER trg_container_booking_before_update"; + String pickTrigger = qualified + ? "CREATE TRIGGER baiyue_firstleg.trg_loading_pick_task_before_update" + : "CREATE TRIGGER trg_loading_pick_task_before_update"; + int call = sql.indexOf("CALL migrate_phase3_channel_containers();"); + int containerRestore = sql.indexOf(containerTrigger, call); + int pickRestore = sql.indexOf(pickTrigger, call); + int failureAssertion = sql.indexOf("CALL assert_phase3_channel_container_migration();", call); + + assertThat(sql) + .contains("DECLARE EXIT HANDLER FOR SQLEXCEPTION") + .contains("GET DIAGNOSTICS CONDITION 1") + .contains("@phase3_channel_sqlstate = RETURNED_SQLSTATE") + .contains("@phase3_channel_errno = MYSQL_ERRNO") + .contains("@phase3_channel_message = MESSAGE_TEXT") + .contains("渠道柜迁移失败:货柜只读触发器未恢复") + .contains("渠道柜迁移失败:PDA任务只读触发器未恢复") + .contains("装柜完成后货柜全部只读,不允许修改") + .contains("已完成PDA拣货任务不可修改") + .contains("NOT (OLD.channel_code <=> NEW.channel_code)") + .contains("NOT (OLD.channel_name <=> NEW.channel_name)") + .contains("NOT (OLD.ocean_booking_id <=> NEW.ocean_booking_id)") + .contains("NOT (OLD.ocean_booking_requirement_id <=> NEW.ocean_booking_requirement_id)") + .doesNotContain("p.plan_status='COMPLETED'"); + assertThat(call).isGreaterThan(0); + assertThat(containerRestore).isGreaterThan(call); + assertThat(pickRestore).isGreaterThan(call); + assertThat(failureAssertion).isGreaterThan(containerRestore).isGreaterThan(pickRestore); + } + + private static String moduleMigration() throws Exception { + return new ClassPathResource("db/phase3-channel-container-migration.sql") + .getContentAsString(StandardCharsets.UTF_8); + } + + private static String unifiedMigration() throws Exception { + Path direct = Path.of("deploy", "sql", "头程数据库统一增量-MySQL8.sql"); + Path path = Files.exists(direct) ? direct : Path.of("..", "deploy", "sql", "头程数据库统一增量-MySQL8.sql"); + return Files.readString(path.normalize(), StandardCharsets.UTF_8); + } +}