Files
ShiErFeng/shierfeng-fastapi-backend/sql/patches/20261008_shipment_receipt_mysql.sql
T

126 lines
7.7 KiB
SQL

-- 发货收货核对:MySQL 8 单次增量迁移(最低 8.0.16,需强制执行 CHECK)。
-- 执行顺序:20260922_test_data_batch_mysql.sql
-- -> 20261005_tunnel_material_flow_mysql.sql
-- -> 20261008_material_dates_mysql.sql -> 本脚本 -> 部署收货核对版本。
-- 执行前必须停写(业务接口、后台任务、导入及其他写入来源),完成备份并验证可恢复。
-- 由 DBA 审核后在明确选定的目标库单次执行;不得使用 --force 或遇错继续的 SQL 客户端模式。
-- 本脚本仅随代码交付,不在应用启动时执行;本次开发未连接数据库、未运行 SQL。
-- 警告:MySQL DDL 会隐式提交,下面的 ROLLBACK 仅能撤销 DML,不能整体回滚已完成的 DDL。
-- 中途失败应保持停写,依据备份和已完成步骤人工恢复;禁止直接重跑或启动新旧应用。
-- 旧 CONFIRMED 单只标 LEGACY;不迁移旧库存状态、不补扣库存、不造收货事件、不伪造收货时间。
-- 正式数据与各测试批次采用相同历史标记规则;所有原 test_batch_id 保持不变。
-- 权限只新增 sys_menu,不写 sys_role_menu;上线后由管理员按现场职责手动授权。
DELIMITER $$
CREATE PROCEDURE migrate_shipment_receipt_20261008()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
-- 所有环境、前置结构、菜单冲突检查均先于业务表 DDL。
IF DATABASE() IS NULL THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Select the target database before migration';
END IF;
IF SUBSTRING_INDEX(VERSION(), '.', 1) <> '8'
OR (CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(VERSION(), '.', 2), '.', -1) AS UNSIGNED) = 0
AND CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(VERSION(), '.', 3), '.', -1) AS UNSIGNED) < 16) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'MySQL 8.0.16 or newer MySQL 8 is required';
END IF;
IF (SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema = DATABASE() AND engine = 'InnoDB'
AND table_name IN ('biz_shipment_order', 'biz_pallet_inventory', 'biz_pallet_event',
'biz_filling_record', 'biz_tunnel', 'sys_menu', 'sys_role_menu', 'sys_user')) <> 8 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Required InnoDB tables or tunnel migration are missing';
END IF;
IF (SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE()
AND ((table_name = 'biz_filling_record' AND column_name IN ('production_date', 'expiry_date'))
OR (table_name = 'biz_pallet_inventory' AND column_name IN ('tunnel_id', 'trace_no'))
OR (table_name = 'biz_pallet_event'
AND column_name IN ('before_tunnel_id', 'after_tunnel_id', 'trace_no', 'batch_no_snapshot')))) <> 8 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Apply tunnel and material date migrations first';
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.check_constraints
WHERE constraint_schema = DATABASE() AND constraint_name = 'ck_biz_filling_record_material_dates')
OR NOT EXISTS (SELECT 1 FROM information_schema.check_constraints
WHERE constraint_schema = DATABASE() AND constraint_name = 'ck_biz_pallet_event_event_type'
AND UPPER(check_clause) LIKE '%TUNNEL_ENTERED%')
OR NOT EXISTS (SELECT 1 FROM information_schema.check_constraints
WHERE constraint_schema = DATABASE() AND constraint_name = 'ck_biz_pallet_inventory_flow_state') THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Required material date or flow CHECK constraints are missing';
END IF;
-- 任何新增字段已存在均说明已执行或部分执行,禁止将已收货数据重新标成 LEGACY。
IF EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE()
AND ((table_name = 'biz_shipment_order'
AND column_name IN ('receipt_state', 'received_by', 'received_at',
'receipt_idempotency_key', 'receipt_request_hash'))
OR (table_name = 'biz_pallet_inventory' AND column_name = 'site_arrived_at'))) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Receipt columns already exist; do not rerun this migration';
END IF;
IF EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 1119 OR perms = 'business:shipment:receive') THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Menu 1119 or shipment receive permission already exists';
END IF;
-- 避免已有孤立授权在插入 1119 时自动变成有效收货权限。
IF EXISTS (SELECT 1 FROM sys_role_menu WHERE menu_id = 1119) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Orphan role grants for menu 1119 must be reviewed first';
END IF;
IF NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 510 AND perms = 'business:shipment:list'
AND menu_type = 'C') THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Shipment parent menu 510 does not match the expected contract';
END IF;
ALTER TABLE biz_shipment_order
ADD COLUMN receipt_state VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '收货状态;LEGACY表示历史未登记' AFTER confirmed_at,
ADD COLUMN received_by BIGINT NULL COMMENT '收货核对人ID' AFTER receipt_state,
ADD COLUMN received_at DATETIME NULL COMMENT '实际收货核对时间' AFTER received_by,
ADD COLUMN receipt_idempotency_key VARCHAR(64) NULL COMMENT '收货确认幂等键' AFTER received_at,
ADD COLUMN receipt_request_hash VARCHAR(64) NULL COMMENT '收货核对请求摘要' AFTER receipt_idempotency_key,
ADD CONSTRAINT uq_biz_shipment_receipt_site_key UNIQUE (target_site_id, receipt_idempotency_key),
ADD CONSTRAINT fk_biz_shipment_order_received_by FOREIGN KEY (received_by) REFERENCES sys_user (user_id),
ADD CONSTRAINT ck_biz_shipment_order_receipt_state CHECK (receipt_state IN ('PENDING', 'RECEIVED', 'LEGACY'));
ALTER TABLE biz_pallet_inventory
ADD COLUMN site_arrived_at DATETIME NULL COMMENT '本轮实际收货核对时间;历史未登记为空' AFTER remaining_packages,
DROP CHECK ck_biz_pallet_inventory_flow_state,
ADD CONSTRAINT ck_biz_pallet_inventory_flow_state CHECK (
flow_state IN ('UNSTOCKED', 'EMPTY_IN_WAREHOUSE', 'FILLED_IN_WAREHOUSE', 'IN_TRANSIT',
'AT_SITE_UNUSED', 'AT_SITE_PARTIAL', 'AT_SITE_EMPTY', 'RETURNING')
);
ALTER TABLE biz_pallet_event
DROP CHECK ck_biz_pallet_event_event_type,
ADD CONSTRAINT ck_biz_pallet_event_event_type CHECK (
event_type IN ('INBOUND_CONFIRMED', 'FILLING_CONFIRMED', 'SHIPMENT_CONFIRMED', 'SHIPMENT_RECEIVED',
'TUNNEL_ENTERED', 'USAGE_RECORDED', 'RETURN_REQUESTED', 'RETURN_CONFIRMED')
);
-- DDL 已隐式提交;以下事务只覆盖历史标记及权限菜单插入。
START TRANSACTION;
UPDATE biz_shipment_order SET receipt_state = 'LEGACY' WHERE status = 'CONFIRMED';
INSERT INTO sys_menu (
menu_id, menu_name, parent_id, order_num, path, component, query, route_name,
is_frame, is_cache, menu_type, visible, status, perms, icon, create_by,
create_time, update_by, update_time, remark
) VALUES (
1119, '收货核对', 510, 6, '', '', '', '',
1, 0, 'F', '0', '0', 'business:shipment:receive', '#', 'admin',
SYSDATE(), '', NULL, ''
);
COMMIT;
END$$
DELIMITER ;
CALL migrate_shipment_receipt_20261008();
DROP PROCEDURE migrate_shipment_receipt_20261008;
-- 核验:历史确认单应为 LEGACY,新草稿为 PENDING,收货时间/人员和原库存保持未登记。
SELECT status, receipt_state, COUNT(*) AS order_count
FROM biz_shipment_order GROUP BY status, receipt_state;
SELECT menu_id, parent_id, perms FROM sys_menu WHERE menu_id = 1119;