Files
baiyue-firstleg/finance/src/main/resources/db/fee-source-event-migration.sql
T
2026-08-20 19:10:16 +08:00

202 lines
13 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- Firstleg费用来源事件消费模型存量升级(MySQL 8,可重复执行)
USE baiyue_finance;
DROP PROCEDURE IF EXISTS finance_fee_source_add_column;
DELIMITER $$
CREATE PROCEDURE finance_fee_source_add_column(
IN target_table VARCHAR(64), IN target_column VARCHAR(64), IN column_definition TEXT
)
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = target_table AND column_name = target_column
) THEN
SET @ddl = CONCAT('ALTER TABLE `', target_table, '` ADD COLUMN `', target_column, '` ', column_definition);
PREPARE statement FROM @ddl;
EXECUTE statement;
DEALLOCATE PREPARE statement;
END IF;
END$$
DELIMITER ;
CALL finance_fee_source_add_column('fin_fee_source', 'source_type',
"VARCHAR(32) NULL COMMENT '来源费用类型:INBOUND_FEE入库单费用、CONTAINER_FEE柜子费用' AFTER customer_id");
CALL finance_fee_source_add_column('fin_fee_source', 'source_no',
"VARCHAR(64) NULL COMMENT '来源入库单号或柜号快照,可为空' AFTER source_type");
CALL finance_fee_source_add_column('fin_fee_source', 'direction',
"VARCHAR(16) NULL COMMENT '费用方向:RECEIVABLE应收、PAYABLE应付' AFTER source_no");
CALL finance_fee_source_add_column('fin_fee_source', 'container_id',
"BIGINT UNSIGNED NULL COMMENT '来源Firstleg装柜计划ID,可为空' AFTER inbound_id");
CALL finance_fee_source_add_column('fin_fee_source', 'fee_item_code',
"VARCHAR(64) NULL COMMENT '来源费用项目编码快照' AFTER container_id");
CALL finance_fee_source_add_column('fin_fee_source', 'source_hash',
"CHAR(64) NULL COMMENT 'Firstleg费用来源完整快照SHA-256哈希' AFTER currency_code");
CALL finance_fee_source_add_column('fin_fee_source', 'settlement_mode',
"VARCHAR(16) NULL COMMENT '仅PAYABLE:ON_ACCOUNT账期应付、DIRECT_PAID业务已付;RECEIVABLE为空' AFTER currency_code");
CALL finance_fee_source_add_column('fin_fee_source', 'paid_at',
"DATETIME(3) NULL COMMENT 'DIRECT_PAID业务已付时间' AFTER settlement_mode");
CALL finance_fee_source_add_column('fin_fee_source', 'payment_method',
"VARCHAR(32) NULL COMMENT 'DIRECT_PAID付款方式' AFTER paid_at");
CALL finance_fee_source_add_column('fin_fee_source', 'payment_reference_no',
"VARCHAR(128) NULL COMMENT 'DIRECT_PAID付款流水号' AFTER payment_method");
CALL finance_fee_source_add_column('fin_fee_source', 'payment_voucher_file_ids',
"JSON NULL COMMENT 'DIRECT_PAID付款凭证File ID数组' AFTER payment_reference_no");
CALL finance_fee_source_add_column('fin_fee_source', 'source_snapshot',
"JSON NULL COMMENT 'Firstleg费用来源完整不可变JSON快照' AFTER source_hash");
CALL finance_fee_source_add_column('fin_fee_source', 'review_status',
"VARCHAR(16) NOT NULL DEFAULT 'DRAFT' COMMENT 'Finance费用状态:DRAFT草稿、PENDING待审核、APPROVED已通过、REJECTED已驳回' AFTER source_snapshot");
CALL finance_fee_source_add_column('fin_fee_source', 'review_reason',
"VARCHAR(500) NULL COMMENT 'Finance费用审核说明或驳回原因' AFTER review_status");
CALL finance_fee_source_add_column('fin_fee_source', 'operation_version',
"INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '费用编辑、提交、审核、调整和作废操作版本号' AFTER review_reason");
CALL finance_fee_source_add_column('fin_fee_source', 'updated_at',
"DATETIME(3) NULL COMMENT '来源状态最后更新时间,数据修改时自动刷新' AFTER created_at");
CALL finance_fee_source_add_column('fin_bill', 'customer_confirmation_status',
"VARCHAR(24) NOT NULL DEFAULT 'NOT_REQUIRED' COMMENT '客户确认状态:NOT_REQUIRED无需确认、PENDING_CONFIRMATION待确认、CONFIRMED已确认' AFTER issued_by");
CALL finance_fee_source_add_column('fin_bill', 'customer_confirmed_by',
"BIGINT UNSIGNED NULL COMMENT '客户确认人用户ID' AFTER customer_confirmation_status");
CALL finance_fee_source_add_column('fin_bill', 'customer_confirmed_by_name',
"VARCHAR(128) NULL COMMENT '客户确认人姓名快照' AFTER customer_confirmed_by");
CALL finance_fee_source_add_column('fin_bill', 'customer_confirmed_at',
"DATETIME(3) NULL COMMENT '客户确认时间' AFTER customer_confirmed_by_name");
CALL finance_fee_source_add_column('fin_inbox_event', 'payload_hash',
"CHAR(64) NULL COMMENT '规范化事件内容SHA-256哈希,用于同事件ID冲突检测' AFTER payload");
CALL finance_fee_source_add_column('fin_inbox_event', 'result_resource_id',
"BIGINT UNSIGNED NULL COMMENT '成功处理生成或定位的费用来源快照ID' AFTER payload_hash");
CALL finance_fee_source_add_column('fin_inbox_event', 'error_code',
"VARCHAR(64) NULL COMMENT '处理失败时的稳定FIN错误码' AFTER result_resource_id");
CALL finance_fee_source_add_column('fin_cost', 'cost_origin',
"VARCHAR(24) NOT NULL DEFAULT 'MANUAL' COMMENT '成本来源:MANUAL人工补充、FIRSTLEG_DIRECT_PAID头程直接已付' AFTER cost_type");
CALL finance_fee_source_add_column('fin_cost', 'source_system',
"VARCHAR(32) NULL COMMENT '来源系统,头程直接已付固定FIRSTLEG' AFTER cost_origin");
CALL finance_fee_source_add_column('fin_cost', 'source_type',
"VARCHAR(32) NULL COMMENT '来源费用类型快照' AFTER source_system");
CALL finance_fee_source_add_column('fin_cost', 'source_fee_id',
"BIGINT UNSIGNED NULL COMMENT '来源费用ID' AFTER source_type");
CALL finance_fee_source_add_column('fin_cost', 'source_fee_version',
"INT UNSIGNED NULL COMMENT '来源费用版本' AFTER source_fee_id");
CALL finance_fee_source_add_column('fin_cost', 'source_hash',
"CHAR(64) NULL COMMENT '来源不可变快照SHA-256' AFTER source_fee_version");
CALL finance_fee_source_add_column('fin_cost', 'paid_at',
"DATETIME(3) NULL COMMENT '直接已付时间' AFTER voucher_file_ids");
CALL finance_fee_source_add_column('fin_cost', 'payment_method',
"VARCHAR(32) NULL COMMENT '直接已付方式' AFTER paid_at");
CALL finance_fee_source_add_column('fin_cost', 'payment_reference_no',
"VARCHAR(128) NULL COMMENT '直接已付流水号' AFTER payment_method");
DROP PROCEDURE IF EXISTS finance_fee_source_add_column;
DROP PROCEDURE IF EXISTS finance_fee_source_add_index;
DELIMITER $$
CREATE PROCEDURE finance_fee_source_add_index()
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema=DATABASE() AND table_name='fin_cost'
AND index_name='uk_fin_cost_source_version'
) THEN
ALTER TABLE fin_cost ADD UNIQUE KEY uk_fin_cost_source_version
(source_system, source_type, source_fee_id, source_fee_version);
END IF;
END$$
DELIMITER ;
CALL finance_fee_source_add_index();
DROP PROCEDURE IF EXISTS finance_fee_source_add_index;
ALTER TABLE fin_cost
MODIFY COLUMN cost_type VARCHAR(24) NOT NULL COMMENT '成本归属类型:SINGLE_ORDER单票、CONTAINER_SHARED整柜公共、SOURCE_DIRECT_PAID来源直接已付';
DROP PROCEDURE IF EXISTS finance_fee_source_replace_cost_checks;
DELIMITER $$
CREATE PROCEDURE finance_fee_source_replace_cost_checks()
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.table_constraints
WHERE constraint_schema=DATABASE() AND table_name='fin_cost' AND constraint_name='chk_fin_cost_target') THEN
ALTER TABLE fin_cost DROP CHECK chk_fin_cost_target;
END IF;
IF EXISTS (SELECT 1 FROM information_schema.table_constraints
WHERE constraint_schema=DATABASE() AND table_name='fin_cost' AND constraint_name='chk_fin_cost_origin') THEN
ALTER TABLE fin_cost DROP CHECK chk_fin_cost_origin;
END IF;
ALTER TABLE fin_cost
ADD CONSTRAINT chk_fin_cost_target CHECK (
(cost_type='SINGLE_ORDER' AND order_id IS NOT NULL AND container_id IS NULL)
OR (cost_type='CONTAINER_SHARED' AND container_id IS NOT NULL)
OR (cost_type='SOURCE_DIRECT_PAID' AND (order_id IS NOT NULL OR inbound_id IS NOT NULL OR container_id IS NOT NULL))
),
ADD CONSTRAINT chk_fin_cost_origin CHECK (
(cost_origin='MANUAL' AND source_system IS NULL AND source_type IS NULL AND source_fee_id IS NULL AND source_fee_version IS NULL AND source_hash IS NULL)
OR (cost_origin='FIRSTLEG_DIRECT_PAID' AND source_system='FIRSTLEG' AND source_type IS NOT NULL
AND source_fee_id IS NOT NULL AND source_fee_version IS NOT NULL AND source_hash IS NOT NULL
AND status IN ('CONFIRMED','VOIDED') AND paid_at IS NOT NULL
AND payment_method IN ('ONLINE','BANK_TRANSFER','CASH','OTHER'))
);
END$$
DELIMITER ;
CALL finance_fee_source_replace_cost_checks();
DROP PROCEDURE IF EXISTS finance_fee_source_replace_cost_checks;
-- 产品确认无历史财务数据;以下兼容值仅用于空环境重复迁移时保证结构收敛。
UPDATE fin_fee_source
SET source_type = COALESCE(source_type, 'INBOUND_FEE'),
direction = COALESCE(direction, 'RECEIVABLE'),
fee_item_code = COALESCE(fee_item_code, 'LEGACY'),
source_hash = COALESCE(source_hash, SHA2(CONCAT_WS('|', source_system, source_fee_id, source_fee_version), 256)),
source_snapshot = COALESCE(source_snapshot, JSON_OBJECT('migrated', TRUE)),
updated_at = COALESCE(updated_at, created_at),
review_status = COALESCE(review_status, 'DRAFT'),
operation_version = COALESCE(operation_version, 0);
UPDATE fin_inbox_event
SET payload_hash = COALESCE(payload_hash, SHA2(CAST(payload AS CHAR), 256));
ALTER TABLE fin_fee_source
MODIFY COLUMN source_type VARCHAR(32) NOT NULL COMMENT '来源费用类型:INBOUND_FEE入库单费用、CONTAINER_FEE柜子费用',
MODIFY COLUMN direction VARCHAR(16) NOT NULL COMMENT '费用方向:RECEIVABLE应收、PAYABLE应付',
MODIFY COLUMN fee_item_code VARCHAR(64) NOT NULL COMMENT '来源费用项目编码快照',
MODIFY COLUMN source_hash CHAR(64) NOT NULL COMMENT 'Firstleg费用来源完整快照SHA-256哈希',
MODIFY COLUMN source_snapshot JSON NOT NULL COMMENT 'Firstleg费用来源完整不可变JSON快照',
MODIFY COLUMN status VARCHAR(16) NOT NULL DEFAULT 'AVAILABLE' COMMENT '费用来源状态:AVAILABLE可结算、SUPERSEDED已被新版本替代、VOIDED已作废',
MODIFY COLUMN updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '来源状态最后更新时间,数据修改时自动刷新';
ALTER TABLE fin_inbox_event
MODIFY COLUMN payload_hash CHAR(64) NOT NULL COMMENT '规范化事件内容SHA-256哈希,用于同事件ID冲突检测',
MODIFY COLUMN status VARCHAR(16) NOT NULL DEFAULT 'RECEIVED' COMMENT '事件处理状态:RECEIVED已接收、PROCESSED已完成、FAILED处理失败';
DROP PROCEDURE IF EXISTS finance_fee_source_assert_schema;
DELIMITER $$
CREATE PROCEDURE finance_fee_source_assert_schema()
BEGIN
IF (SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'fin_fee_source'
AND column_name IN ('source_type','source_no','direction','container_id','fee_item_code',
'settlement_mode','paid_at','payment_method','payment_reference_no',
'payment_voucher_file_ids','source_hash','source_snapshot','review_status','review_reason',
'operation_version','updated_at')) <> 16 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Finance费用来源迁移失败:来源快照字段不完整';
END IF;
IF (SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'fin_inbox_event'
AND column_name IN ('payload_hash','result_resource_id','error_code')) <> 3 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Finance费用来源迁移失败:Inbox幂等字段不完整';
END IF;
IF (SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'fin_bill'
AND column_name IN ('customer_confirmation_status','customer_confirmed_by',
'customer_confirmed_by_name','customer_confirmed_at')) <> 4 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Finance客户对账单确认字段不完整';
END IF;
IF (SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'fin_cost'
AND column_name IN ('cost_origin','source_system','source_type','source_fee_id',
'source_fee_version','source_hash','paid_at','payment_method',
'payment_reference_no')) <> 9 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Finance直接已付成本字段不完整';
END IF;
IF (SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema=DATABASE() AND table_name='fin_cost'
AND index_name='uk_fin_cost_source_version') <> 4 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Finance直接已付成本唯一索引不完整';
END IF;
END$$
DELIMITER ;
CALL finance_fee_source_assert_schema();
DROP PROCEDURE IF EXISTS finance_fee_source_assert_schema;