Merge test fixes into dev dropship flow

This commit is contained in:
2026-09-10 18:18:41 +08:00
131 changed files with 3062 additions and 651 deletions
+11 -3
View File
@@ -1,8 +1,9 @@
# 测试环境 SQL 执行说明
## 唯一数据库初始化与发布入口
## 数据库初始化与权限发布入口
- 国内区域固定入口:`头程数据库统一增量-MySQL8.sql`。文件名保留历史兼容,内容维护为当前完整数据库基线和存量环境兼容升级,不再按日期拆分发布脚本。
- 国内区域全量入口:`头程数据库统一增量-MySQL8.sql`。文件名保留历史兼容,内容维护为当前完整数据库基线和存量环境升级,不再按日期拆分结构发布脚本。
- 通用权限点独立入口:`权限点初始化-MySQL8.sql`。国内与海外业务的 Admin/Client 权限主数据均在 `baiyue_iam` 统一管理。当发布内容只新增或更新 IAM 权限资源及默认角色授权,且 IAM 基础表、父菜单和系统角色已存在时,可只执行该文件。它不建表,不创建账号、客户、仓库或业务数据。
- 海外区域固定入口:`海外区域统一增量-MySQL8.sql`。该文件已经内联 Lastleg 与 Warehouse Overseas 的完整空库基线,会自行创建并初始化 `baiyue_lastleg`、`baiyue_lastlag_warehouse` 和独立的 `baiyue_event_bridge_overseas`,清库后不得再额外执行模块基线。IAM 安全事件 Inbox 和可恢复投影位于 `baiyue_lastleg.ll_security_*`,Redis 只作可重建镜像。
- Lastleg 基线和海外固定增量仅确定性初始化国内固定 `platform_admin`(`authUserId=1000001`、`tenantId=0`)的 USER/AUTHORIZATION version=0 安全投影;AUTHORIZATION 同步冻结当前 `PLATFORM_ADMIN` 角色及 IAM 跨区前缀下的完整权限集合,保证空库重建后海外 Gateway 可立即鉴权。统一 SQL 重放只修正仍处于固定 baseline version=0 的该行,其他既有开发账号不建立兼容表或长期补投入口。
- 国内、海外 Compose 部署默认设置 `SPRING_SQL_INIT_MODE=never`,执行任一 SQL 均需用户单独授权。
@@ -25,6 +26,13 @@
4. 每个增量区块末尾增加只读断言,并同步维护统一脚本末尾的总体验证。
5. MySQL DDL 会隐式提交,执行前仍应在目标环境确认连接和授权;不能把跨库脚本视为可整体回滚事务。
### 权限点独立入口边界
- 新增权限时,同步维护 IAM 模块来源、`权限点初始化-MySQL8.sql`、对应区域的全量统一 SQL、前端按钮/路由、Controller `@PreAuthorize` 和 OpenAPI。
- 权限点入口只用于既有 IAM 基线上的权限新增或元数据/默认授权更新。涉及建表、字段、索引、角色创建或其他业务数据时,仍执行全量统一 SQL。
- 独立入口必须可重复执行,执行前校验稳定权限 ID/权限码没有被其他语义占用,执行后只在授权完整时返回 `permission_initialization_status = PASS`。
- 权限写入后不修改 Redis 中已签发的会话快照;受影响用户必须退出并重新登录。权限撤销不通过该 SQL 直接处理,应走 IAM 角色授权命令以同步撤销会话。
## Finance 首期人工财务基线
- Finance 数据库为 `baiyue_finance`,模块溯源脚本为 `finance/src/main/resources/db/finance.sql`,测试和存量环境仍只执行统一入口。
@@ -53,7 +61,7 @@
固定入口同时幂等补齐审核自动开户所需的全局 `MERCHANT_OWNER` 角色模板、全局商户菜单/按钮权限和角色授权;执行后应使用 `iam/src/test/resources/db/verify-phase1-admin-rbac.sql` 确认模板存在且权限完整。
测试环境只执行当前固定入口;历史脚本和模块脚本不应在固定入口执行后再次串行执行。
测试环境结构发布只执行全量固定入口;纯权限点发布可改用权限点独立入口。历史脚本和模块脚本不应在任一发布入口执行后再次串行执行。
## 执行方式
+507 -161
View File
@@ -1345,18 +1345,19 @@ VALUES
(5102019, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:create', '平台代客创建订单', NULL, NULL, 90, 'ACTIVE', 0),
(5102024, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:edit', '平台编辑订单草稿', NULL, NULL, 100, 'ACTIVE', 0),
(5102025, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:submit', '平台代客提交订单', NULL, NULL, 110, 'ACTIVE', 0),
(5102026, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:export', '导出订单列表', NULL, NULL, 120, 'ACTIVE', 0)
(5102026, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:export', '导出订单列表', NULL, NULL, 120, 'ACTIVE', 0),
(5102027, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:declaration:edit', '修改订单申报与运营预估', NULL, NULL, 130, 'ACTIVE', 0)
ON DUPLICATE KEY UPDATE
permission_name = VALUES(permission_name), parent_id = VALUES(parent_id), status = 'ACTIVE', deleted = 0;
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
SELECT 4000001, id, 0 FROM iam_permission
WHERE id IN (5102019, 5102024, 5102025, 5102026)
WHERE id IN (5102019, 5102024, 5102025, 5102026, 5102027)
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
SELECT 4000003, id, 0 FROM iam_permission
WHERE id IN (5102019, 5102024, 5102025, 5102026)
WHERE id IN (5102019, 5102024, 5102025, 5102026, 5102027)
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
INSERT INTO iam_membership_role (membership_id, role_id, tenant_id) VALUES
@@ -3053,6 +3054,7 @@ CREATE TABLE IF NOT EXISTS fl_customer_charge_rule (
effective_to DATETIME(3) NULL COMMENT '生效结束时间',
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '规则状态:ACTIVE启用、DISABLED停用',
remark VARCHAR(500) NULL COMMENT '规则备注',
initialization_key VARCHAR(128) NULL COMMENT '客户创建执行器幂等键及补偿归属',
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
@@ -3061,6 +3063,7 @@ CREATE TABLE IF NOT EXISTS fl_customer_charge_rule (
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_charge_rule_version (tenant_id, rule_code, rule_version),
KEY idx_charge_rule_initialization (tenant_id, initialization_key),
KEY idx_charge_rule_match (tenant_id, customer_id, business_type, status, effective_from, effective_to)
) ENGINE=InnoDB COMMENT='客户头程计费规则版本';
@@ -3717,6 +3720,7 @@ CALL phase2_fba_add_column('fl_customer_charge_rule', 'length_factor', "DECIMAL(
CALL phase2_fba_add_column('fl_customer_charge_rule', 'width_factor', "DECIMAL(18,6) NOT NULL DEFAULT 1 COMMENT '宽度计算系数' AFTER length_factor");
CALL phase2_fba_add_column('fl_customer_charge_rule', 'height_factor', "DECIMAL(18,6) NOT NULL DEFAULT 1 COMMENT '高度计算系数' AFTER width_factor");
CALL phase2_fba_add_column('fl_customer_charge_rule', 'default_rule', "TINYINT(1) NOT NULL DEFAULT 0 COMMENT '客户默认生效规则' AFTER status");
CALL phase2_fba_add_column('fl_customer_charge_rule', 'initialization_key', "VARCHAR(128) NULL COMMENT '客户创建执行器幂等键及补偿归属' AFTER remark");
CALL phase2_fba_add_column('fl_carrier_fba_rate', 'version', "INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号' AFTER deleted");
CALL phase2_fba_add_column('fl_inbound_fee', 'rule_code', "VARCHAR(50) NOT NULL DEFAULT '' COMMENT '客户规则编码快照' AFTER carrier_rate_version");
CALL phase2_fba_add_column('fl_inbound_fee', 'measurement_id', "BIGINT UNSIGNED NULL COMMENT '费用绑定的测量批次ID快照' AFTER inbound_id");
@@ -3760,6 +3764,14 @@ PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
DROP PROCEDURE IF EXISTS phase2_fba_add_column;
SET @idx_exists = (SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_customer_charge_rule'
AND INDEX_NAME = 'idx_charge_rule_initialization');
SET @sql = IF(@idx_exists = 0,
'ALTER TABLE fl_customer_charge_rule ADD INDEX idx_charge_rule_initialization (tenant_id, initialization_key)',
'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
CREATE TABLE IF NOT EXISTS fl_price_channel_catalog (
id BIGINT UNSIGNED NOT NULL COMMENT '渠道目录ID',
channel_code VARCHAR(64) NOT NULL COMMENT '稳定渠道编码',
@@ -5005,7 +5017,6 @@ CREATE TABLE IF NOT EXISTS sys_audit_dead_letter (
-- ============================================================
-- 三期智能装柜、预装冻结与装柜清单
-- 来源:firstleg/src/main/resources/db/phase3-smart-loading-schema.sql
-- ============================================================
SET NAMES utf8mb4;
CREATE DATABASE IF NOT EXISTS baiyue_firstleg
@@ -5060,7 +5071,7 @@ CREATE TABLE IF NOT EXISTS fl_container_booking (
bill_of_lading_no VARCHAR(128) NULL COMMENT '提单号',
vessel_name VARCHAR(128) NULL COMMENT '船名快照',
voyage_no VARCHAR(64) NULL COMMENT '航次;确认前允许修订',
container_type VARCHAR(32) NOT NULL COMMENT '箱型,例如20GP、40GP、40HQ',
container_type VARCHAR(32) NOT NULL COMMENT '平台固定柜型:20GP、40GP、40HC、40NR、45HC',
shipping_company_id BIGINT UNSIGNED NULL COMMENT '船司ID快照',
shipping_company_name VARCHAR(128) NULL COMMENT '船司名称快照',
departure_port_code VARCHAR(32) NULL COMMENT '起运港编码快照',
@@ -5080,6 +5091,11 @@ CREATE TABLE IF NOT EXISTS fl_container_booking (
actual_port_arrival_at DATETIME(3) NULL COMMENT '实际到港时间',
estimated_warehouse_arrival_at DATETIME(3) NULL COMMENT '预计到仓时间',
actual_warehouse_arrival_at DATETIME(3) NULL COMMENT '实际到仓时间',
unloading_completed_at DATETIME(3) NULL COMMENT '海外仓卸柜完成时间(UTC)',
unloading_photo_uploaded_at DATETIME(3) NULL COMMENT '海外仓卸柜照片上传成功时间(UTC)',
estimated_empty_reported_at DATETIME(3) NULL COMMENT '预计还空时间(卸柜完成时间加72小时,UTC)',
actual_empty_reported_at DATETIME(3) NULL COMMENT '实际报空时间(UTC)',
empty_report_source_version BIGINT UNSIGNED NULL COMMENT '海外货柜事件聚合版本,用于阻止乱序覆盖',
pickup_code VARCHAR(64) NULL COMMENT 'PU数字码;按字符串保存以保留前导零',
pickup_code_updated_by BIGINT UNSIGNED NULL COMMENT 'PU码最后填写人ID',
pickup_code_updated_by_name VARCHAR(64) NULL COMMENT 'PU码最后填写人名称快照',
@@ -5089,8 +5105,7 @@ CREATE TABLE IF NOT EXISTS fl_container_booking (
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
updated_by_name VARCHAR(64) NULL COMMENT '最后修改人名称快照',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)
COMMENT '货柜资料最后更新时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '货柜资料最后更新时间',
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_container_booking_no (booking_no),
@@ -5099,17 +5114,31 @@ CREATE TABLE IF NOT EXISTS fl_container_booking (
KEY idx_container_booking_merge (record_status,merged_into_container_booking_id),
KEY idx_container_booking_warehouse (warehouse_id, updated_at, id),
KEY idx_container_booking_voyage (voyage_no, updated_at),
KEY idx_container_booking_transit_summary (actual_sailing_at, actual_warehouse_arrival_at, ocean_booking_id, container_type),
CONSTRAINT chk_container_booking_source CHECK (source_type IN ('SIMULATION','OCEAN_BOOKING')),
CONSTRAINT chk_container_booking_record_status CHECK (record_status IN ('ACTIVE','MERGED')),
CONSTRAINT chk_container_booking_capacity CHECK (capacity_cbm IS NULL OR capacity_cbm > 0),
CONSTRAINT chk_container_booking_weight CHECK (max_weight_kg IS NULL OR max_weight_kg > 0),
CONSTRAINT chk_container_booking_pickup_code CHECK (pickup_code IS NULL OR pickup_code REGEXP '^[0-9]{1,64}$'),
CONSTRAINT chk_container_booking_iso6346 CHECK
(container_no IS NULL OR container_no REGEXP '^[A-Z]{3}[UJZ][0-9]{7}$'),
CONSTRAINT chk_container_booking_pickup_code CHECK
(pickup_code IS NULL OR pickup_code REGEXP '^[0-9]{1,64}$')
(container_no IS NULL OR container_no REGEXP '^[A-Z]{3}[UJZ][0-9]{7}$')
) ENGINE=InnoDB COMMENT='平台货柜预订资料;预装状态唯一来源为装柜计划';
CREATE TABLE IF NOT EXISTS fl_container_event_inbox (
event_id VARCHAR(64) NOT NULL COMMENT '跨区事件全局唯一ID',
event_type VARCHAR(64) NOT NULL COMMENT '事件类型',
event_version INT UNSIGNED NOT NULL COMMENT '事件契约版本',
aggregate_id VARCHAR(64) NOT NULL COMMENT '跨区业务聚合ID',
aggregate_version BIGINT UNSIGNED NOT NULL COMMENT '跨区业务聚合版本',
payload_hash VARCHAR(80) NOT NULL COMMENT '规范化负载SHA-256摘要',
payload_json JSON NOT NULL COMMENT '原始事件负载',
status VARCHAR(16) NOT NULL COMMENT 'RECEIVED或PROCESSED',
received_at DATETIME(3) NOT NULL COMMENT '接收时间(UTC)',
processed_at DATETIME(3) NULL COMMENT '处理完成时间(UTC)',
PRIMARY KEY (event_id),
KEY idx_container_event_aggregate (aggregate_id, aggregate_version)
) ENGINE=InnoDB COMMENT='海外到国内业务事件消费Inbox';
DROP PROCEDURE IF EXISTS phase3_container_real_unit_columns;
DELIMITER $$
CREATE PROCEDURE phase3_container_real_unit_columns()
@@ -5139,10 +5168,40 @@ BEGIN
ALTER TABLE fl_container_booking ADD COLUMN customer_name VARCHAR(128) NULL
COMMENT '已关联真实订舱客户名称快照' AFTER customer_id;
END IF;
UPDATE fl_container_booking container_booking
JOIN fl_ocean_booking ocean_booking ON ocean_booking.id=container_booking.ocean_booking_id
SET container_booking.customer_id=COALESCE(container_booking.customer_id,ocean_booking.customer_id),
container_booking.customer_name=COALESCE(container_booking.customer_name,ocean_booking.customer_name)
WHERE container_booking.customer_id IS NULL OR container_booking.customer_name IS NULL;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
AND table_name='fl_container_booking' AND column_name='unloading_completed_at') THEN
ALTER TABLE fl_container_booking ADD COLUMN unloading_completed_at DATETIME(3) NULL
COMMENT '海外仓卸柜完成时间(UTC)' AFTER actual_warehouse_arrival_at;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
AND table_name='fl_container_booking' AND column_name='unloading_photo_uploaded_at') THEN
ALTER TABLE fl_container_booking ADD COLUMN unloading_photo_uploaded_at DATETIME(3) NULL
COMMENT '海外仓卸柜照片上传成功时间(UTC)' AFTER unloading_completed_at;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
AND table_name='fl_container_booking' AND column_name='estimated_empty_reported_at') THEN
ALTER TABLE fl_container_booking ADD COLUMN estimated_empty_reported_at DATETIME(3) NULL
COMMENT '预计还空时间(卸柜完成时间加72小时,UTC)' AFTER unloading_photo_uploaded_at;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
AND table_name='fl_container_booking' AND column_name='actual_empty_reported_at') THEN
ALTER TABLE fl_container_booking ADD COLUMN actual_empty_reported_at DATETIME(3) NULL
COMMENT '实际报空时间(UTC)' AFTER estimated_empty_reported_at;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
AND table_name='fl_container_booking' AND column_name='empty_report_source_version') THEN
ALTER TABLE fl_container_booking ADD COLUMN empty_report_source_version BIGINT UNSIGNED NULL
COMMENT '海外货柜事件聚合版本,用于阻止乱序覆盖' AFTER actual_empty_reported_at;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
AND table_name='fl_container_booking' AND column_name='pickup_code') THEN
ALTER TABLE fl_container_booking ADD COLUMN pickup_code VARCHAR(64) NULL
COMMENT 'PU数字码;按字符串保存以保留前导零' AFTER actual_warehouse_arrival_at;
COMMENT 'PU数字码;按字符串保存以保留前导零' AFTER empty_report_source_version;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
AND table_name='fl_container_booking' AND column_name='pickup_code_updated_by') THEN
@@ -5159,14 +5218,6 @@ BEGIN
ALTER TABLE fl_container_booking ADD COLUMN pickup_code_updated_at DATETIME(3) NULL
COMMENT 'PU码最后填写时间' AFTER pickup_code_updated_by_name;
END IF;
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema=DATABASE()
AND table_name='fl_ocean_booking') THEN
UPDATE fl_container_booking container_booking
JOIN fl_ocean_booking ocean_booking ON ocean_booking.id=container_booking.ocean_booking_id
SET container_booking.customer_id=COALESCE(container_booking.customer_id,ocean_booking.customer_id),
container_booking.customer_name=COALESCE(container_booking.customer_name,ocean_booking.customer_name)
WHERE container_booking.customer_id IS NULL OR container_booking.customer_name IS NULL;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
AND table_name='fl_container_booking' AND index_name='uk_container_booking_container_no') THEN
ALTER TABLE fl_container_booking ADD UNIQUE KEY uk_container_booking_container_no (container_no);
@@ -5220,8 +5271,7 @@ CREATE TABLE IF NOT EXISTS fl_loading_plan (
id BIGINT UNSIGNED NOT NULL COMMENT '智能装柜计划ID(雪花ID)',
plan_no VARCHAR(40) NOT NULL COMMENT '平台唯一装柜计划号',
container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '货柜预订ID',
plan_status VARCHAR(24) NOT NULL DEFAULT 'EDITING'
COMMENT '装柜计划状态:EDITING编辑中、LOADING装柜中、COMPLETED装柜完成;建议须经人工确认',
plan_status VARCHAR(24) NOT NULL DEFAULT 'EDITING' COMMENT '装柜计划状态:EDITING编辑中、LOADING装柜中、COMPLETED装柜完成;建议须经人工确认',
preload_completed_by BIGINT UNSIGNED NULL COMMENT '预装完成操作人ID',
preload_completed_by_name VARCHAR(64) NULL COMMENT '预装完成操作人名称快照',
preload_completed_at DATETIME(3) NULL COMMENT '预装完成时间',
@@ -5234,8 +5284,7 @@ CREATE TABLE IF NOT EXISTS fl_loading_plan (
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
updated_by_name VARCHAR(64) NULL COMMENT '最后修改人名称快照',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)
COMMENT '装柜计划最后更新时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '装柜计划最后更新时间',
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_loading_plan_no (plan_no),
@@ -5347,6 +5396,7 @@ CREATE TABLE IF NOT EXISTS fl_loading_pallet_label (
CONSTRAINT chk_loading_pallet_index CHECK (pallet_index BETWEEN 1 AND 999)
) ENGINE=InnoDB COMMENT='散货按柜号和FBA仓冻结的可打印托盘码';
-- 柜次级简单文件仅承载目的港清关和提货单;PU资料作为数字码保存在货柜主表。
CREATE TABLE IF NOT EXISTS fl_container_document (
id BIGINT UNSIGNED NOT NULL COMMENT '货柜文件关联ID(雪花ID)',
container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '货柜预订ID',
@@ -5361,8 +5411,7 @@ CREATE TABLE IF NOT EXISTS fl_container_document (
completed_by_name VARCHAR(64) NULL COMMENT '一键完成操作人名称快照',
completed_at DATETIME(3) 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 '货柜文件关联最后更新时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '货柜文件关联最后更新时间',
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_container_document_type (container_booking_id, document_type),
@@ -5373,13 +5422,42 @@ CREATE TABLE IF NOT EXISTS fl_container_document (
CHECK (document_type IN ('DESTINATION_CLEARANCE', 'DELIVERY_ORDER'))
) ENGINE=InnoDB COMMENT='平台货柜清关与DO文件关联';
SET @has_container_document_type_check=(SELECT COUNT(*) FROM information_schema.table_constraints
WHERE table_schema=DATABASE() AND table_name='fl_container_document'
AND constraint_name='chk_container_document_type' AND constraint_type='CHECK');
SET @ddl=IF(@has_container_document_type_check=0,'SELECT 1',
'ALTER TABLE fl_container_document DROP CHECK chk_container_document_type');
PREPARE statement FROM @ddl; EXECUTE statement; DEALLOCATE PREPARE statement;
ALTER TABLE fl_container_document ADD CONSTRAINT chk_container_document_type
CHECK (document_type IN ('DESTINATION_CLEARANCE','DELIVERY_ORDER'));
CREATE TABLE IF NOT EXISTS fl_container_exception_history (
id BIGINT UNSIGNED NOT NULL COMMENT '货柜异常历史ID', container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '物理货柜ID',
action VARCHAR(16) NOT NULL COMMENT '操作类型:MARK标记异常、CANCEL取消异常', reason VARCHAR(500) NOT NULL COMMENT '异常原因',
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID', operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
operation_id VARCHAR(128) NOT NULL COMMENT '同一操作人幂等键', request_hash CHAR(64) NOT NULL COMMENT '请求摘要',
occurred_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作发生时间', PRIMARY KEY(id),
UNIQUE KEY uk_container_exception_operation(container_booking_id,operator_id,operation_id),
KEY idx_container_exception_history(container_booking_id,occurred_at,id),
CONSTRAINT fk_container_exception_booking FOREIGN KEY(container_booking_id) REFERENCES fl_container_booking(id),
CONSTRAINT chk_container_exception_action CHECK(action IN ('MARK','CANCEL'))
) ENGINE=InnoDB COMMENT='货柜异常标记及取消原因历史';
DROP TRIGGER IF EXISTS trg_container_exception_history_before_update;
DROP TRIGGER IF EXISTS trg_container_exception_history_before_delete;
DELIMITER $$
CREATE TRIGGER trg_container_exception_history_before_update BEFORE UPDATE ON fl_container_exception_history FOR EACH ROW
BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='货柜异常历史不可修改'; END$$
CREATE TRIGGER trg_container_exception_history_before_delete BEFORE DELETE ON fl_container_exception_history FOR EACH ROW
BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='货柜异常历史不可删除'; END$$
DELIMITER ;
CREATE TABLE IF NOT EXISTS fl_loading_plan_history (
id BIGINT UNSIGNED NOT NULL COMMENT '装柜操作历史ID(雪花ID)',
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '智能装柜计划ID',
loading_plan_item_id BIGINT UNSIGNED NULL COMMENT '装柜计划明细ID',
operation_id VARCHAR(128) NOT NULL COMMENT '操作幂等ID',
action_type VARCHAR(32) NOT NULL
COMMENT '动作类型:CREATE_PLAN创建计划、ADD_ITEM新增明细、ADJUST_ITEM调整明细、REMOVE_ITEM移除明细、START_LOADING开始装柜、COMPLETE_LOADING完成装柜、PICK_ITEM拣货',
action_type VARCHAR(32) NOT NULL COMMENT '动作类型:CREATE_PLAN创建计划、ADD_ITEM新增明细、ADJUST_ITEM调整明细、REMOVE_ITEM移除明细、START_LOADING开始装柜、COMPLETE_LOADING完成装柜、PICK_ITEM拣货',
from_status VARCHAR(24) NULL COMMENT '变更前状态',
to_status VARCHAR(24) NULL COMMENT '变更后状态',
before_snapshot JSON NULL COMMENT '变更前快照',
@@ -5399,26 +5477,27 @@ CREATE TABLE IF NOT EXISTS fl_loading_plan_history (
-- fl_loading_pick_task 曾被另一套入库汇总模型复用。先按字段签名识别并迁走,
-- 再创建智能装柜任务表;未知或混合结构必须人工处理,禁止静默套用错误表型。
DROP PROCEDURE IF EXISTS assert_loading_pick_task_shapes;
DROP PROCEDURE IF EXISTS migrate_legacy_loading_pick_task;
DELIMITER $$
CREATE PROCEDURE assert_loading_pick_task_shapes(IN require_both TINYINT)
CREATE PROCEDURE migrate_legacy_loading_pick_task()
BEGIN
DECLARE source_exists INT DEFAULT 0;
DECLARE source_total_columns INT DEFAULT 0;
DECLARE source_smart_columns INT DEFAULT 0;
DECLARE source_inbound_columns INT DEFAULT 0;
DECLARE target_exists INT DEFAULT 0;
DECLARE source_total INT DEFAULT 0;
DECLARE source_smart INT DEFAULT 0;
DECLARE source_stable_smart INT DEFAULT 0;
DECLARE source_inbound INT DEFAULT 0;
DECLARE target_total INT DEFAULT 0;
DECLARE target_inbound INT DEFAULT 0;
SELECT COUNT(*) INTO source_exists FROM information_schema.tables
DECLARE target_total_columns INT DEFAULT 0;
DECLARE target_inbound_columns INT DEFAULT 0;
SELECT COUNT(*) INTO source_exists
FROM information_schema.tables
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task';
SELECT COUNT(*) INTO target_exists FROM information_schema.tables
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task';
IF source_exists = 1 THEN
SELECT COUNT(*) INTO source_total FROM information_schema.columns
SELECT COUNT(*) INTO source_total_columns
FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task';
SELECT COUNT(*) INTO source_smart FROM information_schema.columns
SELECT COUNT(*) INTO source_smart_columns
FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'loading_plan_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
@@ -5430,113 +5509,57 @@ BEGIN
OR (column_name = 'completed_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'YES')
OR (column_name = 'completed_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'completed_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'YES')
OR (column_name = 'version' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0'));
SELECT COUNT(*) INTO source_inbound FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'tenant_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'inbound_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'PENDING')
OR (column_name = 'version' AND LOWER(column_type) = 'int' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
OR (column_name = 'expected_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'picked_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'remark' AND LOWER(column_type) = 'varchar(1000)' AND is_nullable = 'YES')
OR (column_name = 'updated_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'updated_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'created_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
OR (column_name = 'updated_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%' AND LOWER(extra) LIKE '%on update current_timestamp%'));
SELECT COUNT(*) INTO source_stable_smart FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'loading_plan_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'container_booking_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'cabinet_no' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'NO')
OR (column_name = 'container_no' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'task_status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'LOADING')
OR (column_name = 'started_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'started_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'started_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
OR (column_name = 'completed_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'YES')
OR (column_name = 'completed_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'completed_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'YES')
OR (column_name = 'version' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0'));
IF NOT (
(source_total = 11 AND source_smart = 11
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'uk_loading_pick_task_plan' AND non_unique = 0 AND seq_in_index = 1 AND column_name = 'loading_plan_id') = 1
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'idx_loading_pick_container' AND ((seq_in_index = 1 AND column_name = 'container_no') OR (seq_in_index = 2 AND column_name = 'task_status') OR (seq_in_index = 3 AND column_name = 'started_at'))) = 3
AND (SELECT COUNT(*) FROM information_schema.key_column_usage WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND column_name = 'loading_plan_id' AND referenced_table_name = 'fl_loading_plan' AND referenced_column_name = 'id') = 1)
OR
(source_total = 12 AND source_inbound = 12
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'uk_loading_pick_tenant_inbound' AND non_unique = 0 AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'inbound_id'))) = 2
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'idx_loading_pick_status' AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'status') OR (seq_in_index = 3 AND column_name = 'updated_at'))) = 3
AND (SELECT COUNT(*) FROM information_schema.key_column_usage WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND column_name = 'inbound_id' AND referenced_table_name = 'fl_inbound_order' AND referenced_column_name = 'id') = 1)
OR
(source_total = 13 AND source_stable_smart = 13
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'uk_loading_pick_task_plan' AND non_unique = 0 AND seq_in_index = 1 AND column_name = 'loading_plan_id') = 1
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'uk_loading_pick_task_booking' AND non_unique = 0 AND seq_in_index = 1 AND column_name = 'container_booking_id') = 1
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'idx_loading_pick_container' AND ((seq_in_index = 1 AND column_name = 'container_no') OR (seq_in_index = 2 AND column_name = 'task_status') OR (seq_in_index = 3 AND column_name = 'started_at'))) = 3
AND (SELECT COUNT(*) FROM information_schema.key_column_usage WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND column_name = 'loading_plan_id' AND referenced_table_name = 'fl_loading_plan' AND referenced_column_name = 'id') = 1)
) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拣货任务迁移失败:fl_loading_pick_task 不是完整 canonical 表型';
END IF;
END IF;
IF target_exists = 1 THEN
SELECT COUNT(*) INTO target_total FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task';
SELECT COUNT(*) INTO target_inbound FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND (
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'tenant_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'inbound_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'PENDING')
OR (column_name = 'version' AND LOWER(column_type) = 'int' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
OR (column_name = 'expected_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'picked_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'remark' AND LOWER(column_type) = 'varchar(1000)' AND is_nullable = 'YES')
OR (column_name = 'updated_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'updated_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'created_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
OR (column_name = 'updated_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%' AND LOWER(extra) LIKE '%on update current_timestamp%'));
IF target_total <> 12 OR target_inbound <> 12
OR (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND index_name = 'uk_loading_pick_tenant_inbound' AND non_unique = 0 AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'inbound_id'))) <> 2
OR (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND index_name = 'idx_loading_pick_status' AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'status') OR (seq_in_index = 3 AND column_name = 'updated_at'))) <> 3
OR (SELECT COUNT(*) FROM information_schema.key_column_usage WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND column_name = 'inbound_id' AND referenced_table_name = 'fl_inbound_order' AND referenced_column_name = 'id') <> 1 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拣货任务迁移失败:fl_inbound_loading_pick_task 不是完整 canonical 表型';
END IF;
END IF;
IF require_both = 1 AND (source_exists <> 1 OR target_exists <> 1 OR source_stable_smart <> 13) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拣货任务终验失败:两张 canonical 表未同时就绪';
END IF;
END$$
DELIMITER ;
CALL assert_loading_pick_task_shapes(0);
DROP PROCEDURE IF EXISTS migrate_legacy_loading_pick_task;
DELIMITER $$
CREATE PROCEDURE migrate_legacy_loading_pick_task()
BEGIN
DECLARE source_exists INT DEFAULT 0;
DECLARE source_smart_columns INT DEFAULT 0;
DECLARE source_inbound_columns INT DEFAULT 0;
DECLARE target_exists INT DEFAULT 0;
DECLARE target_inbound_columns INT DEFAULT 0;
SELECT COUNT(*) INTO source_exists
FROM information_schema.tables
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task';
IF source_exists = 1 THEN
SELECT COUNT(*) INTO source_smart_columns
FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
AND column_name IN ('loading_plan_id', 'container_no', 'task_status');
OR (column_name = 'version' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
);
SELECT COUNT(*) INTO source_inbound_columns
FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
AND column_name IN ('tenant_id', 'inbound_id', 'status', 'expected_count', 'picked_count');
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'tenant_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'inbound_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'PENDING')
OR (column_name = 'version' AND LOWER(column_type) = 'int' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
OR (column_name = 'expected_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'picked_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'remark' AND LOWER(column_type) = 'varchar(1000)' AND is_nullable = 'YES')
OR (column_name = 'updated_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'updated_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'created_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
OR (column_name = 'updated_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%' AND LOWER(extra) LIKE '%on update current_timestamp%')
);
IF source_smart_columns = 3 AND source_inbound_columns = 0 THEN
IF source_total_columns = 11 AND source_smart_columns = 11 AND source_inbound_columns < 12
AND (SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
AND index_name = 'uk_loading_pick_task_plan' AND non_unique = 0
AND seq_in_index = 1 AND column_name = 'loading_plan_id') = 1
AND (SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
AND index_name = 'idx_loading_pick_container'
AND ((seq_in_index = 1 AND column_name = 'container_no')
OR (seq_in_index = 2 AND column_name = 'task_status')
OR (seq_in_index = 3 AND column_name = 'started_at'))) = 3
AND (SELECT COUNT(*) FROM information_schema.key_column_usage
WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
AND column_name = 'loading_plan_id' AND referenced_table_name = 'fl_loading_plan'
AND referenced_column_name = 'id') = 1 THEN
SET source_exists = 1;
ELSEIF source_smart_columns = 0 AND source_inbound_columns = 5 THEN
ELSEIF source_total_columns = 12 AND source_inbound_columns = 12 AND source_smart_columns < 11
AND (SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
AND index_name = 'uk_loading_pick_tenant_inbound' AND non_unique = 0
AND ((seq_in_index = 1 AND column_name = 'tenant_id')
OR (seq_in_index = 2 AND column_name = 'inbound_id'))) = 2
AND (SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
AND index_name = 'idx_loading_pick_status'
AND ((seq_in_index = 1 AND column_name = 'tenant_id')
OR (seq_in_index = 2 AND column_name = 'status')
OR (seq_in_index = 3 AND column_name = 'updated_at'))) = 3
AND (SELECT COUNT(*) FROM information_schema.key_column_usage
WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
AND column_name = 'inbound_id' AND referenced_table_name = 'fl_inbound_order'
AND referenced_column_name = 'id') = 1 THEN
SELECT COUNT(*) INTO target_exists
FROM information_schema.tables
WHERE table_schema = 'baiyue_firstleg'
@@ -5544,14 +5567,44 @@ BEGIN
IF target_exists = 0 THEN
RENAME TABLE fl_loading_pick_task TO fl_inbound_loading_pick_task;
ELSE
SELECT COUNT(*) INTO target_total_columns
FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task';
SELECT COUNT(*) INTO target_inbound_columns
FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg'
AND table_name = 'fl_inbound_loading_pick_task'
AND column_name IN ('tenant_id', 'inbound_id', 'status', 'expected_count', 'picked_count');
IF target_inbound_columns <> 5 THEN
AND table_name = 'fl_inbound_loading_pick_task' AND (
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'tenant_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'inbound_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'PENDING')
OR (column_name = 'version' AND LOWER(column_type) = 'int' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
OR (column_name = 'expected_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'picked_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'remark' AND LOWER(column_type) = 'varchar(1000)' AND is_nullable = 'YES')
OR (column_name = 'updated_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'updated_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'created_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
OR (column_name = 'updated_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%' AND LOWER(extra) LIKE '%on update current_timestamp%')
);
IF target_total_columns <> 12 OR target_inbound_columns <> 12
OR (SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task'
AND index_name = 'uk_loading_pick_tenant_inbound' AND non_unique = 0
AND ((seq_in_index = 1 AND column_name = 'tenant_id')
OR (seq_in_index = 2 AND column_name = 'inbound_id'))) <> 2
OR (SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task'
AND index_name = 'idx_loading_pick_status'
AND ((seq_in_index = 1 AND column_name = 'tenant_id')
OR (seq_in_index = 2 AND column_name = 'status')
OR (seq_in_index = 3 AND column_name = 'updated_at'))) <> 3
OR (SELECT COUNT(*) FROM information_schema.key_column_usage
WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task'
AND column_name = 'inbound_id' AND referenced_table_name = 'fl_inbound_order'
AND referenced_column_name = 'id') <> 1 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '拣货任务迁移失败:fl_inbound_loading_pick_task 不是入库汇总结构';
SET MESSAGE_TEXT = '拣货任务迁移失败:fl_inbound_loading_pick_task 不是完整入库汇总结构';
END IF;
INSERT INTO fl_inbound_loading_pick_task
(id, tenant_id, inbound_id, status, version, expected_count, picked_count,
@@ -5617,6 +5670,49 @@ CREATE TABLE IF NOT EXISTS fl_loading_pick_task (
CONSTRAINT chk_loading_pick_task_status CHECK (task_status IN ('LOADING','COMPLETING','COMPLETION_FAILED','COMPLETED'))
) ENGINE=InnoDB COMMENT='PDA装柜拣货任务';
CREATE TABLE IF NOT EXISTS fl_loading_completion_command (
id BIGINT UNSIGNED NOT NULL COMMENT '可靠装柜完成命令ID',
pick_task_id BIGINT UNSIGNED NOT NULL COMMENT 'PDA任务ID',
operation_id VARCHAR(128) NOT NULL COMMENT '客户端全局幂等操作ID',
request_hash CHAR(64) NOT NULL COMMENT '规范化请求摘要',
command_status VARCHAR(16) NOT NULL COMMENT '命令状态:PROCESSING处理中、RETRYABLE可重试、FAILED失败、COMPLETED已完成',
expected_task_version INT UNSIGNED NOT NULL COMMENT '受理时客户端任务版本',
remark VARCHAR(500) NULL COMMENT '完成备注',
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
attempt_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'worker认领次数',
next_retry_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '下次处理时间',
lease_owner VARCHAR(128) NULL COMMENT 'worker租约持有者',
lease_until DATETIME(3) NULL COMMENT 'worker租约截止时间',
failure_stage VARCHAR(40) NULL COMMENT '脱敏失败阶段',
failure_code VARCHAR(32) NULL COMMENT '下游或业务错误码',
failure_request_id VARCHAR(64) NULL COMMENT '下游请求ID',
failure_trace_id VARCHAR(64) 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 '命令更新时间',
completed_at DATETIME(3) NULL COMMENT '命令完成时间',
PRIMARY KEY (id),
UNIQUE KEY uk_loading_completion_operation (operation_id),
KEY idx_loading_completion_dispatch (command_status,next_retry_at,lease_until),
CONSTRAINT fk_loading_completion_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task(id),
CONSTRAINT chk_loading_completion_status CHECK (command_status IN ('PROCESSING','RETRYABLE','FAILED','COMPLETED'))
) ENGINE=InnoDB COMMENT='PDA装柜完成可靠异步命令';
CREATE TABLE IF NOT EXISTS fl_loading_completion_checkpoint (
command_id BIGINT UNSIGNED NOT NULL COMMENT '可靠装柜完成命令ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '入仓单客户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入仓单ID',
owner_sequence INT UNSIGNED NOT NULL COMMENT '稳定处理顺序',
checkpoint_stage VARCHAR(24) NOT NULL COMMENT '处理阶段:READY待处理、SETTLED资源已结算、INBOUND_COMPLETED入仓已完成',
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 (command_id,tenant_id,inbound_id),
KEY idx_loading_completion_owner (command_id,owner_sequence),
CONSTRAINT fk_loading_completion_checkpoint_command FOREIGN KEY (command_id)
REFERENCES fl_loading_completion_command(id),
CONSTRAINT chk_loading_completion_checkpoint CHECK (checkpoint_stage IN ('READY','SETTLED','INBOUND_COMPLETED'))
) ENGINE=InnoDB COMMENT='PDA装柜完成按入库单恢复检查点';
CREATE TABLE IF NOT EXISTS fl_loading_pick_item (
id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货明细ID',
pick_task_id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货任务ID',
@@ -5626,6 +5722,8 @@ CREATE TABLE IF NOT EXISTS fl_loading_pick_item (
planned_package_count INT UNSIGNED NOT NULL COMMENT '计划拣货箱数;上层拆分按业务规则向上取整',
picked_package_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已拣货箱数',
item_status VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '拣货明细状态:PENDING待拣、PICKING拣货中、PICKED已拣完、PARTIAL部分拣货、SKIPPED跳拣',
release_mode VARCHAR(10) NULL COMMENT 'AUTO按比例释放,MANUAL按人工选择释放',
release_location_ids JSON NULL COMMENT '人工选择释放库位ID;空数组表示明确释放0个',
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'PDA拣货明细创建时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT 'PDA拣货明细最后更新时间',
@@ -5658,8 +5756,6 @@ CREATE TABLE IF NOT EXISTS fl_loading_pick_history (
CONSTRAINT fk_loading_pick_history_item FOREIGN KEY (pick_item_id) REFERENCES fl_loading_pick_item (id)
) ENGINE=InnoDB COMMENT='PDA拣货幂等和操作审计历史';
DROP PROCEDURE IF EXISTS repair_phase3_loading_pick_task_foreign_keys;
DELIMITER $$
CREATE PROCEDURE repair_phase3_loading_pick_task_foreign_keys()
@@ -6031,8 +6127,10 @@ BEGIN
OR NOT (NEW.cabinet_no <=> OLD.cabinet_no) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货任务稳定归属不可修改';
END IF;
IF NOT (OLD.task_status = 'LOADING' AND NEW.task_status IN ('LOADING', 'COMPLETED')) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货任务状态只允许完成';
IF NOT ((OLD.task_status = 'LOADING' AND NEW.task_status IN ('LOADING','COMPLETING'))
OR (OLD.task_status = 'COMPLETING' AND NEW.task_status IN ('COMPLETING','COMPLETION_FAILED','COMPLETED'))
OR (OLD.task_status = 'COMPLETION_FAILED' AND NEW.task_status = 'COMPLETING')) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货任务状态流转非法';
END IF;
END$$
DELIMITER ;
@@ -6194,10 +6292,11 @@ DELIMITER ;
-- 三期智能装柜、预装清单和装柜清单平台权限;与一期平台根菜单幂等合并。
USE baiyue_iam;
DROP PROCEDURE IF EXISTS assert_smart_loading_permission_ids_available;
DROP PROCEDURE IF EXISTS relocate_legacy_inbound_loading_permissions;
DELIMITER $$
CREATE PROCEDURE assert_smart_loading_permission_ids_available()
CREATE PROCEDURE relocate_legacy_inbound_loading_permissions()
BEGIN
DECLARE legacy_count INT DEFAULT 0;
IF EXISTS (
SELECT 1 FROM iam_permission
WHERE id IN (5102091, 5102092, 5102093, 5102094, 5102096, 5102097, 5102098, 5102099)
@@ -6215,10 +6314,89 @@ BEGIN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '三期权限迁移失败:5102091-5102099 存在未知权限占用';
END IF;
SELECT COUNT(*) INTO legacy_count
FROM iam_permission
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
AND permission_code IN (
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
);
IF legacy_count > 0 THEN
IF EXISTS (
SELECT 1 FROM iam_permission
WHERE id BETWEEN 5102121 AND 5102129
AND permission_code <> CASE id
WHEN 5102121 THEN 'firstleg:inbound:split'
WHEN 5102122 THEN 'firstleg:inbound:preload'
WHEN 5102123 THEN 'firstleg:inbound:preload-view'
WHEN 5102124 THEN 'platform:firstleg:inbound:loading:edit'
WHEN 5102125 THEN 'platform:firstleg:inbound:loading:view'
WHEN 5102126 THEN 'platform:firstleg:inbound:loading:complete'
WHEN 5102127 THEN 'platform:firstleg:inbound:delivery:complete'
WHEN 5102128 THEN 'platform:firstleg:inbound:picking:edit'
WHEN 5102129 THEN 'platform:firstleg:inbound:picking:view'
END
) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '三期权限迁移失败:5102121-5102129 已被其他权限占用';
END IF;
DROP TEMPORARY TABLE IF EXISTS tmp_phase3_inbound_role_grant;
CREATE TEMPORARY TABLE tmp_phase3_inbound_role_grant (
role_id BIGINT UNSIGNED NOT NULL,
tenant_id BIGINT UNSIGNED NOT NULL,
permission_code VARCHAR(128) NOT NULL,
PRIMARY KEY (role_id, tenant_id, permission_code)
) ENGINE=MEMORY;
INSERT IGNORE INTO tmp_phase3_inbound_role_grant (role_id, tenant_id, permission_code)
SELECT role_permission.role_id, role_permission.tenant_id, permission.permission_code
FROM iam_role_permission role_permission
JOIN iam_permission permission ON permission.id = role_permission.permission_id
WHERE permission.permission_code IN (
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
);
DELETE role_permission FROM iam_role_permission role_permission
JOIN iam_permission permission ON permission.id = role_permission.permission_id
WHERE permission.permission_code IN (
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
);
DELETE FROM iam_permission WHERE permission_code IN (
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
);
INSERT INTO iam_permission
(id, tenant_id, app_code, permission_type, parent_id, permission_code,
permission_name, route_path, http_method, sort_no, status, built_in, deleted)
VALUES
(5102121, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:split', '智能分柜', NULL, NULL, 140, 'ACTIVE', 1, 0),
(5102122, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:preload', '编辑并确认预装清单', NULL, NULL, 150, 'ACTIVE', 1, 0),
(5102123, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:preload-view', '查看预装清单', NULL, NULL, 160, 'ACTIVE', 1, 0),
(5102124, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:loading:edit', '维护装柜证据', NULL, NULL, 170, 'ACTIVE', 1, 0),
(5102125, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:loading:view', '查看装柜证据', NULL, NULL, 180, 'ACTIVE', 1, 0),
(5102126, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:loading:complete', '完成装柜', NULL, NULL, 190, 'ACTIVE', 1, 0),
(5102127, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:delivery:complete', '完成派送', NULL, NULL, 200, 'ACTIVE', 1, 0),
(5102128, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:picking:edit', '执行拣货', NULL, NULL, 210, 'ACTIVE', 1, 0),
(5102129, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:picking:view', '查看拣货任务', NULL, NULL, 220, 'ACTIVE', 1, 0);
INSERT IGNORE INTO iam_role_permission (role_id, permission_id, tenant_id)
SELECT grant_row.role_id, permission.id, grant_row.tenant_id
FROM tmp_phase3_inbound_role_grant grant_row
JOIN iam_permission permission ON permission.permission_code = grant_row.permission_code
AND permission.tenant_id = 0 AND permission.app_code = 'PLATFORM';
DROP TEMPORARY TABLE tmp_phase3_inbound_role_grant;
END IF;
END$$
DELIMITER ;
CALL assert_smart_loading_permission_ids_available();
DROP PROCEDURE assert_smart_loading_permission_ids_available;
CALL relocate_legacy_inbound_loading_permissions();
DROP PROCEDURE relocate_legacy_inbound_loading_permissions;
DELETE role_permission FROM iam_role_permission role_permission
JOIN iam_permission permission ON permission.id = role_permission.permission_id
@@ -6274,7 +6452,7 @@ FROM iam_role role
'platform:firstleg:container:list', 'platform:firstleg:container:view',
'platform:firstleg:container:create',
'platform:firstleg:container:edit', 'platform:firstleg:smart-loading:list',
'platform:firstleg:smart-loading:assign', 'platform:firstleg:smart-loading:delete',
'platform:firstleg:smart-loading:assign', 'platform:firstleg:smart-loading:delete',
'platform:firstleg:preload:list', 'platform:firstleg:preload:view',
'platform:firstleg:preload:edit', 'platform:firstleg:preload:complete',
'platform:firstleg:preload:confirm', 'platform:firstleg:loading-list:list',
@@ -6285,6 +6463,12 @@ WHERE role.tenant_id = 0
USE baiyue_firstleg;
DROP PROCEDURE IF EXISTS assert_phase3_smart_loading_schema;
DELETE rp FROM baiyue_iam.iam_role_permission rp
JOIN baiyue_iam.iam_permission p ON p.tenant_id=rp.tenant_id AND p.id=rp.permission_id
WHERE p.permission_code IN ('platform:firstleg:container:view','platform:firstleg:preload:view','platform:firstleg:loading-list:view');
UPDATE baiyue_iam.iam_permission SET status='INACTIVE',deleted=1
WHERE permission_code IN ('platform:firstleg:container:view','platform:firstleg:preload:view','platform:firstleg:loading-list:view');
DELIMITER $$
CREATE PROCEDURE assert_phase3_smart_loading_schema()
BEGIN
@@ -6426,9 +6610,118 @@ BEGIN
END$$
DELIMITER ;
-- ============================================================
DROP PROCEDURE IF EXISTS assert_loading_pick_task_shapes;
DELIMITER $$
CREATE PROCEDURE assert_loading_pick_task_shapes(IN require_both TINYINT)
BEGIN
DECLARE source_exists INT DEFAULT 0;
DECLARE target_exists INT DEFAULT 0;
DECLARE source_total INT DEFAULT 0;
DECLARE source_smart INT DEFAULT 0;
DECLARE source_stable_smart INT DEFAULT 0;
DECLARE source_inbound INT DEFAULT 0;
DECLARE target_total INT DEFAULT 0;
DECLARE target_inbound INT DEFAULT 0;
SELECT COUNT(*) INTO source_exists FROM information_schema.tables
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task';
SELECT COUNT(*) INTO target_exists FROM information_schema.tables
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task';
IF source_exists = 1 THEN
SELECT COUNT(*) INTO source_total FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task';
SELECT COUNT(*) INTO source_smart FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'loading_plan_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'container_no' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'NO')
OR (column_name = 'task_status' AND LOWER(column_type) = 'varchar(16)' AND is_nullable = 'NO' AND column_default = 'LOADING')
OR (column_name = 'started_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'started_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'started_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
OR (column_name = 'completed_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'YES')
OR (column_name = 'completed_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'completed_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'YES')
OR (column_name = 'version' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0'));
SELECT COUNT(*) INTO source_inbound FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'tenant_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'inbound_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'PENDING')
OR (column_name = 'version' AND LOWER(column_type) = 'int' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
OR (column_name = 'expected_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'picked_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'remark' AND LOWER(column_type) = 'varchar(1000)' AND is_nullable = 'YES')
OR (column_name = 'updated_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'updated_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'created_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
OR (column_name = 'updated_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%' AND LOWER(extra) LIKE '%on update current_timestamp%'));
SELECT COUNT(*) INTO source_stable_smart FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'loading_plan_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'container_booking_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'cabinet_no' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'NO')
OR (column_name = 'container_no' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'task_status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'LOADING')
OR (column_name = 'started_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'started_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'started_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
OR (column_name = 'completed_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'YES')
OR (column_name = 'completed_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'completed_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'YES')
OR (column_name = 'version' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0'));
IF NOT (
(source_total = 11 AND source_smart = 11
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'uk_loading_pick_task_plan' AND non_unique = 0 AND seq_in_index = 1 AND column_name = 'loading_plan_id') = 1
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'idx_loading_pick_container' AND ((seq_in_index = 1 AND column_name = 'container_no') OR (seq_in_index = 2 AND column_name = 'task_status') OR (seq_in_index = 3 AND column_name = 'started_at'))) = 3
AND (SELECT COUNT(*) FROM information_schema.key_column_usage WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND column_name = 'loading_plan_id' AND referenced_table_name = 'fl_loading_plan' AND referenced_column_name = 'id') = 1)
OR
(source_total = 12 AND source_inbound = 12
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'uk_loading_pick_tenant_inbound' AND non_unique = 0 AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'inbound_id'))) = 2
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'idx_loading_pick_status' AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'status') OR (seq_in_index = 3 AND column_name = 'updated_at'))) = 3
AND (SELECT COUNT(*) FROM information_schema.key_column_usage WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND column_name = 'inbound_id' AND referenced_table_name = 'fl_inbound_order' AND referenced_column_name = 'id') = 1)
OR
(source_total = 13 AND source_stable_smart = 13
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'uk_loading_pick_task_plan' AND non_unique = 0 AND seq_in_index = 1 AND column_name = 'loading_plan_id') = 1
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'uk_loading_pick_task_booking' AND non_unique = 0 AND seq_in_index = 1 AND column_name = 'container_booking_id') = 1
AND (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND index_name = 'idx_loading_pick_container' AND ((seq_in_index = 1 AND column_name = 'container_no') OR (seq_in_index = 2 AND column_name = 'task_status') OR (seq_in_index = 3 AND column_name = 'started_at'))) = 3
AND (SELECT COUNT(*) FROM information_schema.key_column_usage WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND column_name = 'loading_plan_id' AND referenced_table_name = 'fl_loading_plan' AND referenced_column_name = 'id') = 1)
) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拣货任务迁移失败:fl_loading_pick_task 不是完整 canonical 表型';
END IF;
END IF;
IF target_exists = 1 THEN
SELECT COUNT(*) INTO target_total FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task';
SELECT COUNT(*) INTO target_inbound FROM information_schema.columns
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND (
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'tenant_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'inbound_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'PENDING')
OR (column_name = 'version' AND LOWER(column_type) = 'int' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
OR (column_name = 'expected_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'picked_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
OR (column_name = 'remark' AND LOWER(column_type) = 'varchar(1000)' AND is_nullable = 'YES')
OR (column_name = 'updated_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
OR (column_name = 'updated_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
OR (column_name = 'created_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
OR (column_name = 'updated_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%' AND LOWER(extra) LIKE '%on update current_timestamp%'));
IF target_total <> 12 OR target_inbound <> 12
OR (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND index_name = 'uk_loading_pick_tenant_inbound' AND non_unique = 0 AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'inbound_id'))) <> 2
OR (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND index_name = 'idx_loading_pick_status' AND ((seq_in_index = 1 AND column_name = 'tenant_id') OR (seq_in_index = 2 AND column_name = 'status') OR (seq_in_index = 3 AND column_name = 'updated_at'))) <> 3
OR (SELECT COUNT(*) FROM information_schema.key_column_usage WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task' AND column_name = 'inbound_id' AND referenced_table_name = 'fl_inbound_order' AND referenced_column_name = 'id') <> 1 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拣货任务迁移失败:fl_inbound_loading_pick_task 不是完整 canonical 表型';
END IF;
END IF;
IF require_both = 1 AND (source_exists <> 1 OR target_exists <> 1 OR source_stable_smart <> 13) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '拣货任务终验失败:两张 canonical 表未同时就绪';
END IF;
END$$
DELIMITER ;
CALL assert_loading_pick_task_shapes(0);
-- 2026-08-06 平台公共仓库正式主数据:固定作业仓 + FBA 地址库
-- 来源:warehouse/src/main/resources/db/warehouse-public-master-data.sql。必须位于最终 PASS 校验之前,可重复执行。
USE baiyue_warehouse;
@@ -10833,7 +11126,7 @@ CREATE TABLE IF NOT EXISTS fl_carrier_schedule_query (
CONSTRAINT fk_carrier_schedule_query_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id),
CONSTRAINT fk_carrier_schedule_query_container FOREIGN KEY (container_booking_id) REFERENCES fl_container_booking(id),
CONSTRAINT fk_carrier_schedule_query_result FOREIGN KEY (result_port_schedule_id) REFERENCES fl_port_schedule(id),
CONSTRAINT chk_carrier_schedule_query_customer CHECK (customer_id > 0),
CONSTRAINT chk_carrier_schedule_query_customer CHECK (customer_id >= 0),
CONSTRAINT chk_carrier_schedule_query_carrier CHECK
(carrier_code IN ('MATSON','SML','ZIM','COSCO','EMC','HEDE','OTHER')),
CONSTRAINT chk_carrier_schedule_query_tracking CHECK
@@ -10895,7 +11188,8 @@ CREATE TABLE IF NOT EXISTS fl_carrier_schedule_query_history (
CREATE TABLE IF NOT EXISTS fl_carrier_schedule_node_fact (
id BIGINT UNSIGNED NOT NULL COMMENT '标准船节点事实ID',
query_id BIGINT UNSIGNED NOT NULL COMMENT '所属查询任务ID',
query_id BIGINT UNSIGNED NULL COMMENT '自动跟踪任务ID;人工补录时为空',
container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '所属真实货柜ID',
event_key VARCHAR(192) NOT NULL COMMENT '供应商编码与供应商事件ID组成的幂等键',
provider_code VARCHAR(32) NOT NULL COMMENT '轨迹供应商编码',
provider_event_id VARCHAR(128) NOT NULL COMMENT '供应商稳定事件ID',
@@ -10911,9 +11205,11 @@ CREATE TABLE IF NOT EXISTS fl_carrier_schedule_node_fact (
raw_payload_hash CHAR(64) NOT NULL COMMENT '原始响应SHA-256;不保存敏感原文',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '事实入库时间',
PRIMARY KEY (id),
UNIQUE KEY uk_carrier_schedule_node_event (query_id,event_key),
UNIQUE KEY uk_carrier_schedule_node_event (container_booking_id,event_key),
KEY idx_carrier_schedule_node_timeline (query_id,node_sequence,observed_at,id),
KEY idx_container_shipping_node_timeline (container_booking_id,observed_at,id),
CONSTRAINT fk_carrier_schedule_node_query FOREIGN KEY (query_id) REFERENCES fl_carrier_schedule_query(id),
CONSTRAINT fk_carrier_schedule_node_container FOREIGN KEY (container_booking_id) REFERENCES fl_container_booking(id),
CONSTRAINT chk_carrier_schedule_node_standard CHECK
(standard_node IN ('DEPARTED_WAREHOUSE','AT_ORIGIN_PORT','LOADED_ON_VESSEL','DEPARTED_ORIGIN_PORT',
'IN_TRANSIT','ARRIVED_DESTINATION_PORT','DISCHARGED_FROM_VESSEL','PICKUP_READY','PICKED_UP','EMPTY_RETURNED')),
@@ -11109,6 +11405,34 @@ END$$
DELIMITER ;
CALL assert_carrier_schedule_query_schema();
DROP PROCEDURE IF EXISTS assert_carrier_schedule_query_schema;
DELIMITER $$
CREATE PROCEDURE assert_carrier_schedule_query_schema()
BEGIN
IF (SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema='baiyue_firstleg' AND table_name IN
('fl_carrier_schedule_query','fl_carrier_schedule_query_attempt',
'fl_carrier_schedule_query_history','fl_carrier_schedule_query_operation',
'fl_carrier_schedule_node_fact','fl_carrier_schedule_exception_fact')) <> 6 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询任务表未完整创建';
END IF;
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
WHERE tenant_id=0 AND id IN (5102184,5102186,5102188)
AND permission_code LIKE 'platform:firstleg:port-schedule-query:%') <> 3 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询任务权限未完整创建';
END IF;
IF (SELECT COUNT(*)
FROM baiyue_iam.iam_role_permission role_permission
JOIN baiyue_iam.iam_role role ON role.id=role_permission.role_id
JOIN baiyue_iam.iam_permission permission ON permission.id=role_permission.permission_id
WHERE role.tenant_id=0 AND role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR')
AND permission.tenant_id=0
AND permission.permission_code LIKE 'platform:firstleg:port-schedule-query:%') <> 6 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询任务角色授权未完整创建';
END IF;
END$$
DELIMITER ;
CALL assert_carrier_schedule_query_schema();
DROP PROCEDURE IF EXISTS assert_carrier_schedule_query_schema;
DROP PROCEDURE IF EXISTS migrate_carrier_schedule_retry_columns;
DELIMITER $$
@@ -13564,6 +13888,7 @@ CALL queue_column_comment('baiyue_firstleg', 'fl_customer_charge_rule', 'deleted
CALL queue_column_comment('baiyue_firstleg', 'fl_customer_charge_rule', 'effective_from', '生效开始时间');
CALL queue_column_comment('baiyue_firstleg', 'fl_customer_charge_rule', 'effective_to', '生效结束时间');
CALL queue_column_comment('baiyue_firstleg', 'fl_customer_charge_rule', 'id', '客户计费规则ID');
CALL queue_column_comment('baiyue_firstleg', 'fl_customer_charge_rule', 'initialization_key', '客户创建执行器幂等键及补偿归属');
CALL queue_column_comment('baiyue_firstleg', 'fl_customer_charge_rule', 'remark', '规则备注');
CALL queue_column_comment('baiyue_firstleg', 'fl_customer_charge_rule', 'rounding_mode', '舍入方式');
CALL queue_column_comment('baiyue_firstleg', 'fl_customer_charge_rule', 'rounding_scale', '结果保留小数位数');
@@ -15754,6 +16079,16 @@ BEGIN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:管理员或订单运营角色缺少订单导出与下载中心权限';
END IF;
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
WHERE id = 5102027 AND tenant_id = 0 AND app_code = 'PLATFORM'
AND permission_code = 'platform:order:declaration:edit'
AND parent_id = 5102010 AND status = 'ACTIVE' AND deleted = 0) <> 1
OR (SELECT COUNT(DISTINCT role_id) FROM baiyue_iam.iam_role_permission
WHERE tenant_id = 0 AND permission_id = 5102027
AND role_id IN (4000001, 4000003)) <> 2 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '统一迁移失败:订单申报与运营预估权限未正确授予平台管理员和订单运营角色';
END IF;
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
AND id BETWEEN 5102121 AND 5102129
@@ -18867,6 +19202,17 @@ DROP PROCEDURE IF EXISTS add_finance_p0_column;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema='baiyue_finance' AND table_name='fin_fee_source' AND index_name='idx_fin_fee_source_wallet_hold'), 'SELECT 1', 'ALTER TABLE baiyue_finance.fin_fee_source ADD KEY idx_fin_fee_source_wallet_hold(wallet_hold_id)'));
PREPARE statement FROM @ddl; EXECUTE statement; DEALLOCATE PREPARE statement;
-- 跨客户合柜的订舱属于平台技术作用域,船节点任务使用 customer_id=0。
USE baiyue_firstleg;
SET @ddl = (SELECT IF(EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_schema=DATABASE() AND table_name='fl_carrier_schedule_query'
AND constraint_name='chk_carrier_schedule_query_customer' AND constraint_type='CHECK'
), 'ALTER TABLE fl_carrier_schedule_query DROP CHECK chk_carrier_schedule_query_customer', 'SELECT 1'));
PREPARE statement FROM @ddl; EXECUTE statement; DEALLOCATE PREPARE statement;
ALTER TABLE fl_carrier_schedule_query
ADD CONSTRAINT chk_carrier_schedule_query_customer CHECK (customer_id >= 0);
CALL baiyue_firstleg.assert_unified_final_schema();
DROP PROCEDURE IF EXISTS baiyue_firstleg.assert_unified_final_schema;
SELECT 'PASS' AS migration_status,
+66
View File
@@ -0,0 +1,66 @@
-- 通用 IAM 权限点独立初始化入口(MySQL 8.0)。
-- 国内与海外业务的 Admin/Client 权限主数据均在 baiyue_iam 统一管理。
-- 只允许写入 baiyue_iam 的权限资源和角色授权;
-- 不建表、不创建账号/客户/仓库,不写业务测试数据。
-- 前置:IAM 基础表、相关父菜单及系统角色已由全量基线创建。
USE baiyue_iam;
SET NAMES utf8mb4;
DROP PROCEDURE IF EXISTS apply_permission_points;
DELIMITER $$
CREATE PROCEDURE apply_permission_points()
BEGIN
IF NOT EXISTS (SELECT 1 FROM iam_permission WHERE id = 5102010 AND tenant_id = 0
AND permission_code = 'platform:order:list' AND permission_type = 'MENU') THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '权限初始化失败:订单列表父菜单不存在,请先执行全量基线';
END IF;
IF (SELECT COUNT(*) FROM iam_role WHERE id IN (4000001, 4000003) AND tenant_id = 0
AND app_code = 'PLATFORM' AND status = 'ACTIVE' AND deleted = 0) <> 2 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '权限初始化失败:平台管理员或订单运营角色不存在';
END IF;
IF EXISTS (SELECT 1 FROM iam_permission WHERE id = 5102027
AND permission_code <> 'platform:order:declaration:edit')
OR EXISTS (SELECT 1 FROM iam_permission
WHERE permission_code = 'platform:order:declaration:edit' AND id <> 5102027) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '权限初始化失败:5102027 或权限码已被其他语义占用';
END IF;
START TRANSACTION;
INSERT INTO iam_permission
(id, tenant_id, app_code, permission_type, parent_id, permission_code, permission_name,
route_path, http_method, sort_no, status, deleted)
VALUES
(5102027, 0, 'PLATFORM', 'BUTTON', 5102010, 'platform:order:declaration:edit',
'修改订单申报与运营预估', NULL, NULL, 130, 'ACTIVE', 0)
ON DUPLICATE KEY UPDATE
permission_name = VALUES(permission_name), parent_id = VALUES(parent_id),
permission_type = VALUES(permission_type), app_code = VALUES(app_code),
sort_no = VALUES(sort_no), status = 'ACTIVE', deleted = 0;
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
SELECT role_id, 5102027, 0
FROM (SELECT 4000001 AS role_id UNION ALL SELECT 4000003 AS role_id) roles
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
IF (SELECT COUNT(*) FROM iam_permission WHERE id = 5102027 AND tenant_id = 0
AND app_code = 'PLATFORM' AND permission_type = 'BUTTON' AND parent_id = 5102010
AND permission_code = 'platform:order:declaration:edit'
AND status = 'ACTIVE' AND deleted = 0) <> 1
OR (SELECT COUNT(DISTINCT role_id) FROM iam_role_permission
WHERE tenant_id = 0 AND permission_id = 5102027
AND role_id IN (4000001, 4000003)) <> 2 THEN
ROLLBACK;
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '权限初始化失败:订单申报与运营预估权限未完整写入';
END IF;
COMMIT;
END$$
DELIMITER ;
CALL apply_permission_points();
DROP PROCEDURE IF EXISTS apply_permission_points;
SELECT 'PASS' AS permission_initialization_status;
@@ -1,6 +1,7 @@
-- 海外区域唯一数据库初始化入口;空库和存量测试库均只执行本文件。
-- 本文件内联 Lastleg 与海外仓空库基线,再执行统一增量和最终结构校验。
-- 本文件只由人工审核执行,服务部署默认不自动执行 SQL。
-- 空库先执行 lastleg-service/src/main/resources/db/lastleg.sql 基线;本入口同时内联该基线用于固定部署。
SET NAMES utf8mb4;
-- 海外 Event Bridge 空库基线必须先于业务服务表初始化。
@@ -112,7 +113,7 @@ CREATE TABLE IF NOT EXISTS ll_loading_event_inbox (
CREATE TABLE IF NOT EXISTS ll_container_shipping_gate (
container_booking_id VARCHAR(128) NOT NULL COMMENT '来源头程柜预订ID',
ocean_booking_id VARCHAR(128) NOT NULL COMMENT '来源海运订舱ID', container_no VARCHAR(64) NOT NULL COMMENT '实际集装箱号',
ocean_booking_id VARCHAR(128) NULL COMMENT '来源海运订舱ID;货柜未关联订舱时为空', container_no VARCHAR(64) NOT NULL COMMENT '实际集装箱号',
status VARCHAR(32) NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE',
standard_node VARCHAR(64) NULL COMMENT '最新有效标准船节点', node_sequence INT NOT NULL DEFAULT 0 COMMENT '最新有效节点单调序号',
observed_at DATETIME(3) NULL COMMENT '供应商节点观测时间', actual_at DATETIME(3) NULL COMMENT '节点实际发生时间',
@@ -3321,7 +3322,7 @@ PREPARE phase4_inventory_stmt FROM @ddl; EXECUTE phase4_inventory_stmt; DEALLOCA
-- 海运节点达到可提柜后才允许海外 PDA 认领;装柜完成只建立等待门禁快照。
CREATE TABLE IF NOT EXISTS ll_container_shipping_gate (
container_booking_id VARCHAR(128) NOT NULL COMMENT '来源头程柜预订ID',
ocean_booking_id VARCHAR(128) NOT NULL COMMENT '来源海运订舱ID',
ocean_booking_id VARCHAR(128) NULL COMMENT '来源海运订舱ID;货柜未关联订舱时为空',
container_no VARCHAR(64) NOT NULL COMMENT '实际集装箱号',
status VARCHAR(32) NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE',
standard_node VARCHAR(64) NULL COMMENT '最新有效标准船节点',
@@ -4587,3 +4588,7 @@ CALL assert_overseas_finance_p0_ready();
DROP PROCEDURE IF EXISTS assert_overseas_finance_p0_ready;
SELECT 'PASS' AS migration_status, '海外财务P0费用来源字段已就绪' AS message;
-- 人工货柜节点独立于订舱:货柜尚未关联海运订舱时仍可同步当前节点。
ALTER TABLE ll_container_shipping_gate
MODIFY ocean_booking_id VARCHAR(128) NULL COMMENT '来源海运订舱ID;货柜未关联订舱时为空';