From 7a4486b1bc6ff8fdfa225420dd148e0666e7fe86 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Mon, 28 Sep 2026 15:06:01 +0800 Subject: [PATCH] test(db): align customs baseline and relabel contracts --- deploy/sql/README.md | 4 +- deploy/sql/头程数据库统一增量-MySQL8.sql | 61 ++++++++++++++++++- .../Phase3FinalUnifiedSqlContractTest.java | 9 ++- .../lastleg/relabel/RelabelContractTest.java | 5 +- .../initialization-baseline-contract.test.mjs | 4 +- 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/deploy/sql/README.md b/deploy/sql/README.md index 52c301a9b..bb04b1612 100644 --- a/deploy/sql/README.md +++ b/deploy/sql/README.md @@ -6,7 +6,7 @@ | 区域 | 完整入口 | 服务 schema | 表数 | |---|---|---|---:| -| 国内 | `头程数据库统一增量-MySQL8.sql` | `baiyue_auth`、`baiyue_iam`、`baiyue_order`、`baiyue_warehouse`、`baiyue_firstleg`、`baiyue_file`、`baiyue_audit`、`baiyue_event_bridge`、`baiyue_finance` | 221 | +| 国内 | `头程数据库统一增量-MySQL8.sql` | `baiyue_auth`、`baiyue_iam`、`baiyue_order`、`baiyue_warehouse`、`baiyue_firstleg`、`baiyue_file`、`baiyue_audit`、`baiyue_event_bridge`、`baiyue_finance` | 223 | | 海外 | `海外区域统一增量-MySQL8.sql` | `baiyue_lastleg`、`baiyue_lastlag_warehouse`、`baiyue_event_bridge_overseas` | 138 | - 国内和海外是两个部署区域,不是把微服务合并为两个 schema。`baiyue_lastlag_warehouse` 保留当前正式的 `lastlag` 拼写。 @@ -24,7 +24,7 @@ 5. DDL 隐式提交,不可整体回滚。失败后停止并保留错误;经另行授权准备空库后从头执行,不针对半初始化库追加迁移或从失败行续跑。 6. Firstleg 测量项与拆分项互为外键:建空表期间暂设会话 `foreign_key_checks=0`,随后立即恢复 `1`,再插入种子。失败时关闭连接。两边外键完整保留在 CREATE TABLE 内。 -成功时只在最末输出一行 `initialization_status=PASS`,并带 `region=CN/OVERSEAS`、`initialized_tables=221/138`。必须同时确认客户端退出码 0;忽略错误后出现的 PASS 不算成功。 +成功时只在最末输出一行 `initialization_status=PASS`,并带 `region=CN/OVERSEAS`、`initialized_tables=223/138`。必须同时确认客户端退出码 0;忽略错误后出现的 PASS 不算成功。 ## 平台管理员密码 diff --git a/deploy/sql/头程数据库统一增量-MySQL8.sql b/deploy/sql/头程数据库统一增量-MySQL8.sql index a03b9c636..4dcf76941 100644 --- a/deploy/sql/头程数据库统一增量-MySQL8.sql +++ b/deploy/sql/头程数据库统一增量-MySQL8.sql @@ -1833,10 +1833,10 @@ CREATE TABLE fl_customs_customer_document ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户整份退税报送资料逻辑根'; CREATE TABLE fl_customs_completion_document ( - id bigint unsigned NOT NULL COMMENT '报关完成文件ID', + id bigint unsigned NOT NULL COMMENT '报关行回执文件ID', source_document_id bigint unsigned NOT NULL COMMENT '一份冻结客户资料严格对应一份完成文件逻辑根', file_id bigint unsigned NOT NULL COMMENT 'File服务文件ID', - file_name varchar(255) NOT NULL COMMENT '完成文件名称快照', + file_name varchar(255) NOT NULL COMMENT '回执文件名称快照', file_hash char(64) NOT NULL COMMENT '完成文件SHA-256摘要', content_type varchar(128) DEFAULT NULL COMMENT '完成文件媒体类型', size_bytes bigint unsigned NOT NULL COMMENT '完成文件大小(字节)', @@ -1844,6 +1844,11 @@ CREATE TABLE fl_customs_completion_document ( uploaded_by_name varchar(64) DEFAULT NULL COMMENT '上传操作人名称快照', uploaded_at datetime(3) NOT NULL COMMENT '上传时间', version int unsigned NOT NULL DEFAULT '1' COMMENT '完成文件版本号', + customer_confirm_status varchar(20) NOT NULL DEFAULT 'PENDING' COMMENT '客户回执确认状态:PENDING/CONFIRMED/REJECTED', + customer_confirmed_by bigint unsigned DEFAULT NULL COMMENT '客户确认人ID', + customer_confirmed_by_name varchar(64) DEFAULT NULL COMMENT '客户确认人名称快照', + customer_confirmed_at datetime(3) DEFAULT NULL COMMENT '客户确认时间', + customer_reject_reason varchar(500) DEFAULT 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), @@ -1944,6 +1949,56 @@ CREATE TABLE fl_customs_frozen_document_inbound ( CONSTRAINT fk_customs_frozen_document FOREIGN KEY (document_id) REFERENCES fl_customs_customer_document (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='开始报关时冻结的客户资料覆盖范围'; +CREATE TABLE fl_customs_declaration_batch ( + id BIGINT UNSIGNED NOT NULL COMMENT '正式申报批次ID', + customs_container_id BIGINT UNSIGNED NOT NULL COMMENT '所属整柜报关聚合ID', + loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '装柜计划ID快照', + batch_no VARCHAR(64) NULL COMMENT '平台正式申报批次号', + status VARCHAR(32) NOT NULL COMMENT '批次状态:READY/SUBMITTED/ACKNOWLEDGED/RESULT_PENDING/CLOSED/CANCELLED', + source_version INT UNSIGNED NOT NULL COMMENT '创建时整柜报关聚合版本', + source_snapshot_json JSON NOT NULL COMMENT '提交时资料、回执和确认版本快照', + customs_entry_no VARCHAR(64) NULL COMMENT '报关单号', + submitted_by BIGINT UNSIGNED NULL COMMENT '正式申报提交人', + submitted_by_name VARCHAR(64) NULL COMMENT '正式申报提交人名称快照', + submitted_at DATETIME(3) NULL COMMENT '正式申报提交时间', + acknowledged_by BIGINT UNSIGNED NULL COMMENT '报关行确认受理人', + acknowledged_by_name VARCHAR(64) NULL COMMENT '报关行确认受理人名称快照', + acknowledged_at DATETIME(3) NULL COMMENT '报关行确认受理时间', + remark VARCHAR(500) NULL COMMENT '批次说明', + version INT UNSIGNED NOT NULL DEFAULT 0 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_customs_declaration_batch_container(customs_container_id, id), + KEY idx_customs_declaration_batch_plan(loading_plan_id,status,created_at), + CONSTRAINT fk_customs_declaration_batch_container FOREIGN KEY(customs_container_id) REFERENCES fl_customs_container(id), + CONSTRAINT chk_customs_declaration_batch_status CHECK(status IN ('READY','SUBMITTED','ACKNOWLEDGED','RESULT_PENDING','CLOSED','CANCELLED')) +) ENGINE=InnoDB COMMENT='整柜正式申报批次事实'; + +CREATE TABLE fl_customs_clearance_result ( + id BIGINT UNSIGNED NOT NULL COMMENT '海关结果事实ID', + declaration_batch_id BIGINT UNSIGNED NOT NULL COMMENT '所属正式申报批次ID', + customs_container_id BIGINT UNSIGNED NOT NULL COMMENT '所属整柜报关聚合ID', + result_type VARCHAR(32) NOT NULL COMMENT '结果类型:INSPECTION/SUPPLEMENT_REQUIRED/RELEASED/DETAINED/OTHER', + result_status VARCHAR(16) NOT NULL DEFAULT 'OPEN' COMMENT '结果状态:OPEN/RESOLVED/CANCELLED', + customs_entry_no VARCHAR(64) NULL COMMENT '报关单号快照', + occurred_at DATETIME(3) NOT NULL COMMENT '海关事实发生时间', + resolution VARCHAR(1000) NULL COMMENT '处理结果或异常说明', + evidence_file_id BIGINT UNSIGNED NULL COMMENT '结果凭证File文件ID', + recorded_by BIGINT UNSIGNED NOT NULL COMMENT '平台录入人', + recorded_by_name VARCHAR(64) NULL COMMENT '平台录入人名称快照', + recorded_at DATETIME(3) NOT NULL COMMENT '平台录入时间', + previous_result_id BIGINT UNSIGNED NULL COMMENT '前序结果事实ID', + 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), + KEY idx_customs_clearance_result_batch(declaration_batch_id,occurred_at,id), + KEY idx_customs_clearance_result_container(customs_container_id, result_type, result_status), + CONSTRAINT fk_customs_clearance_result_batch FOREIGN KEY(declaration_batch_id) REFERENCES fl_customs_declaration_batch(id), + CONSTRAINT chk_customs_clearance_result_type CHECK(result_type IN ('INSPECTION','SUPPLEMENT_REQUIRED','RELEASED','DETAINED','OTHER')), + CONSTRAINT chk_customs_clearance_result_status CHECK(result_status IN ('OPEN','RESOLVED','CANCELLED')) +) ENGINE=InnoDB COMMENT='正式申报后的海关结果不可变事实'; + CREATE TABLE fl_document_sequence ( sequence_key varchar(32) NOT NULL COMMENT '单据序列键;日序列如CB:20260818,柜次长期序列固定为CABINET', current_value int unsigned NOT NULL COMMENT '该类型当日已分配的最大序号', @@ -8087,4 +8142,4 @@ CREATE TABLE fl_sailing_booking_link ( CONSTRAINT chk_sailing_booking_released CHECK (booking_status_snapshot='RELEASED') ) ENGINE=InnoDB COMMENT='船期计划与已放舱订舱的最小关联及历史'; -SELECT 'PASS' AS initialization_status, 'CN' AS region, 221 AS initialized_tables; +SELECT 'PASS' AS initialization_status, 'CN' AS region, 223 AS initialized_tables; diff --git a/firstleg/src/test/java/com/baiyue/firstleg/repository/Phase3FinalUnifiedSqlContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/repository/Phase3FinalUnifiedSqlContractTest.java index 9482eb047..9d0fa239d 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/repository/Phase3FinalUnifiedSqlContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/repository/Phase3FinalUnifiedSqlContractTest.java @@ -20,6 +20,13 @@ class Phase3FinalUnifiedSqlContractTest { .contains("CREATE TABLE fl_truck_appointment") .contains("CREATE TABLE fl_outbound_dispatch") .contains("CREATE TABLE fl_customs_container") + .contains("CREATE TABLE fl_customs_declaration_batch") + .contains("CREATE TABLE fl_customs_clearance_result") + .contains("customer_confirm_status varchar(20) NOT NULL DEFAULT 'PENDING'") + .contains("customer_confirmed_at datetime(3) DEFAULT NULL") + .contains("customer_reject_reason varchar(500) DEFAULT NULL") + .contains("UNIQUE KEY uk_customs_declaration_batch_container(customs_container_id, id)") + .contains("KEY idx_customs_clearance_result_batch(declaration_batch_id,occurred_at,id)") .contains("CREATE TABLE fl_port_schedule") .contains("CREATE TABLE fl_carrier_schedule_query") .contains("CREATE TABLE fl_terminal_drayage") @@ -73,7 +80,7 @@ class Phase3FinalUnifiedSqlContractTest { @Test void initializationPassIsUniqueAndFinal() throws Exception { String sql = sql(); - String pass = "SELECT 'PASS' AS initialization_status, 'CN' AS region, 221 AS initialized_tables;"; + String pass = "SELECT 'PASS' AS initialization_status, 'CN' AS region, 223 AS initialized_tables;"; assertThat(count(sql, pass)).isEqualTo(1); assertThat(sql.trim()).endsWith(pass); assertThat(sql) diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/relabel/RelabelContractTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/relabel/RelabelContractTest.java index bf90f8c2f..6f99457f8 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/relabel/RelabelContractTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/relabel/RelabelContractTest.java @@ -136,7 +136,8 @@ class RelabelContractTest { void returnCartonConstraintsAllowRelabelReservationWithPositiveInventory() throws Exception { String baseline = Files.readString(Path.of("src/main/resources/db/lastleg.sql"), StandardCharsets.UTF_8) .replaceAll("\\s+", "").toUpperCase(); - assertTrue(baseline.contains("STATUSIN('AVAILABLE','RELABEL_PENDING','OUTBOUNDED')ANDSKU_TYPES>0ANDTOTAL_QUANTITY>0")); - assertTrue(baseline.contains("STATUSIN('AVAILABLE','RELABEL_PENDING','MERGED','OUTBOUNDED')")); + assertTrue(baseline.contains("STATUSIN('AVAILABLE','RELABEL_PENDING','OUTBOUNDED','DISCARD_PENDING')ANDSKU_TYPES>0ANDTOTAL_QUANTITY>0")); + assertTrue(baseline.contains("STATUSIN('MERGED','DISCARDED')ANDSKU_TYPES=0ANDTOTAL_QUANTITY=0")); + assertTrue(baseline.contains("STATUSIN('AVAILABLE','RELABEL_PENDING','MERGED','OUTBOUNDED','DISCARD_PENDING','DISCARDED')")); } } diff --git a/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs b/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs index ddb5c3e66..9f67e26ef 100644 --- a/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs +++ b/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs @@ -9,7 +9,7 @@ const domesticSchemas = ['baiyue_auth', 'baiyue_iam', 'baiyue_order', 'baiyue_wa const overseasSchemas = ['baiyue_event_bridge_overseas', 'baiyue_lastleg', 'baiyue_lastlag_warehouse'] test('两个入口分别只操作国内九库与海外三库,包括限定名引用', () => { - for (const [sql, allowed, other, count] of [[cn, domesticSchemas, overseasSchemas, 221], [overseas, overseasSchemas, domesticSchemas, 138]]) { + for (const [sql, allowed, other, count] of [[cn, domesticSchemas, overseasSchemas, 223], [overseas, overseasSchemas, domesticSchemas, 138]]) { assert.deepEqual([...sql.matchAll(/^CREATE DATABASE IF NOT EXISTS (\w+)/gm)].map(m => m[1]), allowed) assert.deepEqual([...new Set([...sql.matchAll(/^USE (\w+);/gm)].map(m => m[1]))], allowed) const body = sql.replace(/^--.*$/gm, '') @@ -28,7 +28,7 @@ test('纯初始化禁止迁移、数据回填、清理、重复建表和提前 assert.doesNotMatch(body.replace(/^CREATE TRIGGER [\s\S]*?\$\$/gm, ''), /^\s*(?:UPDATE|DELETE FROM|REPLACE INTO)\b/gmi) assert.doesNotMatch(body, /CREATE TABLE IF NOT EXISTS/i) assert.equal((body.match(/AS initialization_status/g) ?? []).length, 1) - assert.match(body.trim(), /SELECT 'PASS' AS initialization_status, '(CN|OVERSEAS)' AS region, (221|138) AS initialized_tables;$/) + assert.match(body.trim(), /SELECT 'PASS' AS initialization_status, '(CN|OVERSEAS)' AS region, (223|138) AS initialized_tables;$/) } })