Files
biz-api-cloud-zax/db/20260825_third_payment_schema.sql
2026-10-20 13:40:25 +08:00

363 lines
31 KiB
SQL

-- thirdparty 支付、聚合支付、任务补偿和商户分账关系统一迁移脚本。
-- 本文件为统一支付基线迁移;后续功能增量在 db 中独立提供,不通过重跑本文件交付。
-- 本脚本不删除支付任务、订单、分账关系或历史配置数据;仅在迁移核对通过后清理旧配置字段。
USE `pjcloudx_thirdparty`;
-- 一、聚合订单码支付会话
CREATE TABLE IF NOT EXISTS `third_aggregate_pay_session`
(
`id` bigint NOT NULL COMMENT '聚合会话主键',
`aggregate_no` varchar(64) NOT NULL COMMENT '聚合支付会话唯一编号',
`access_token_hash` char(64) NOT NULL COMMENT '收银URL随机访问令牌SHA-256摘要',
`access_token_ciphertext` varchar(512) NOT NULL COMMENT '收银URL随机访问令牌Jasypt密文,仅用于幂等复用',
`biz_type` varchar(32) NOT NULL COMMENT '支付业务类型编码,首版为YXFJ_PAY对应编码',
`biz_id` varchar(128) NOT NULL COMMENT '调用方业务订单唯一标识',
`user_id` bigint NOT NULL COMMENT '付款用户ID,匿名消费机订单为-1',
`mch_id` bigint NOT NULL COMMENT '收款商户ID',
`price` int NOT NULL COMMENT '应付金额,单位为人民币分',
`status` varchar(32) NOT NULL COMMENT '聚合会话状态编码',
`selected_platform` varchar(32) DEFAULT NULL COMMENT '首个有效扫码选定的支付平台编码',
`pay_no` varchar(128) DEFAULT NULL COMMENT '绑定的third_pay_order.pay_no',
`payer_identity_hash` char(64) DEFAULT NULL COMMENT '支付宝用户ID或微信服务商公众号sp_openid的SHA-256摘要',
`expire_time` datetime NOT NULL COMMENT '聚合二维码与第三方子订单服务端失效时间',
`resolved_time` datetime DEFAULT NULL COMMENT '会话进入成功、关闭、过期或创建失败终态的时间',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '修改人',
`update_time` datetime NOT NULL COMMENT '修改时间',
`del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除标记,0有效,1删除',
`tenant_id` bigint NOT NULL COMMENT '租户ID',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_third_aggregate_pay_no` (`tenant_id`, `aggregate_no`),
UNIQUE KEY `uk_third_aggregate_access_token` (`tenant_id`, `access_token_hash`),
UNIQUE KEY `uk_third_aggregate_child_pay_no` (`tenant_id`, `pay_no`),
KEY `idx_third_aggregate_biz` (`tenant_id`, `biz_type`, `biz_id`, `create_time`),
KEY `idx_third_aggregate_job` (`status`, `expire_time`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci
COMMENT = '支付宝与微信聚合订单码支付会话';
-- 二、Quartz 触发的可恢复支付任务和执行尝试
CREATE TABLE IF NOT EXISTS `third_payment_task`
(
`id` bigint NOT NULL COMMENT '任务主键',
`tenant_id` bigint NOT NULL COMMENT '租户ID',
`task_type` varchar(48) NOT NULL COMMENT '任务类型编码',
`task_key` varchar(192) NOT NULL COMMENT '租户内稳定幂等任务键',
`pay_order_id` bigint DEFAULT NULL COMMENT 'third_pay_order主键',
`fz_order_id` bigint DEFAULT NULL COMMENT 'third_fz_order主键',
`fz_relation_id` bigint DEFAULT NULL COMMENT 'third_mch_fz_relation主键',
`aggregate_session_id` bigint DEFAULT NULL COMMENT 'third_aggregate_pay_session主键',
`pay_no` varchar(128) DEFAULT NULL COMMENT '平台支付单号',
`refund_no` varchar(128) DEFAULT NULL COMMENT '平台退款单号',
`fz_no` varchar(128) DEFAULT NULL COMMENT '平台分账单号',
`aggregate_no` varchar(128) DEFAULT NULL COMMENT '聚合会话单号',
`status` varchar(16) NOT NULL COMMENT '任务状态:READY/PROCESSING/RETRY/SUCCESS/FAILED/ALERT/CANCELLED',
`next_run_time` datetime NOT NULL COMMENT '下一次可执行时间',
`priority` int NOT NULL DEFAULT 0 COMMENT '任务优先级,数值越大越优先',
`lease_owner` varchar(128) DEFAULT NULL COMMENT 'Worker实例标识',
`lease_token` varchar(64) DEFAULT NULL COMMENT '本次执行租约令牌',
`lease_expire_time` datetime DEFAULT NULL COMMENT '租约失效时间',
`attempt_count` int NOT NULL DEFAULT 0 COMMENT '累计执行次数',
`last_external_status` varchar(64) DEFAULT NULL COMMENT '最近一次渠道状态',
`last_error_code` varchar(128) DEFAULT NULL COMMENT '最近一次错误码',
`last_error_message` varchar(1024) DEFAULT NULL COMMENT '最近一次错误摘要',
`finished_time` datetime DEFAULT NULL COMMENT '任务终态时间',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '修改人',
`update_time` datetime NOT NULL COMMENT '更新时间',
`del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除标记',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_third_payment_task_tenant_key` (`tenant_id`, `task_key`),
KEY `idx_third_payment_task_due` (`task_type`, `status`, `next_run_time`, `priority`, `id`),
KEY `idx_third_payment_task_lease` (`task_type`, `status`, `lease_expire_time`),
KEY `idx_third_payment_task_pay_no` (`tenant_id`, `pay_no`),
KEY `idx_third_payment_task_fz_no` (`tenant_id`, `fz_no`),
KEY `idx_third_payment_task_relation` (`tenant_id`, `fz_relation_id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '第三方支付任务事实';
CREATE TABLE IF NOT EXISTS `third_payment_task_attempt`
(
`id` bigint NOT NULL COMMENT '尝试记录主键',
`task_id` bigint NOT NULL COMMENT '任务主键',
`tenant_id` bigint NOT NULL COMMENT '租户ID',
`attempt_no` int NOT NULL COMMENT '任务内尝试序号',
`trigger_source` varchar(32) NOT NULL COMMENT '触发来源:QUARTZ/CALLBACK/SYNC/MANUAL',
`worker_id` varchar(128) DEFAULT NULL COMMENT 'Worker实例标识',
`lease_token` varchar(64) DEFAULT NULL COMMENT '执行租约令牌',
`request_snapshot` text DEFAULT NULL COMMENT '脱敏请求快照',
`response_snapshot` text DEFAULT NULL COMMENT '脱敏响应快照',
`external_status` varchar(64) DEFAULT NULL COMMENT '渠道状态',
`error_code` varchar(128) DEFAULT NULL COMMENT '错误码',
`error_message` varchar(1024) DEFAULT NULL COMMENT '错误摘要',
`started_time` datetime NOT NULL COMMENT '开始时间',
`finished_time` datetime DEFAULT NULL COMMENT '结束时间',
PRIMARY KEY (`id`),
KEY `idx_third_payment_attempt_task` (`task_id`, `attempt_no`),
KEY `idx_third_payment_attempt_tenant_time` (`tenant_id`, `started_time`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '第三方支付任务执行尝试';
-- 三、商户支付分账关系(微信、支付宝共用)
CREATE TABLE IF NOT EXISTS `third_mch_fz_relation`
(
`id` bigint NOT NULL COMMENT '关系主键,应用侧雪花算法生成',
`tenant_id` bigint NOT NULL COMMENT '租户ID,与mch_manage所属租户一致',
`mch_id` bigint NOT NULL COMMENT 'mch_manage商户主键',
`pay_setting_id` bigint NOT NULL COMMENT 'mch_pay_setting支付配置主键',
`platform_type` varchar(32) NOT NULL COMMENT '支付平台类型:10微信、20支付宝',
`relation_scene` varchar(64) NOT NULL COMMENT '本地关系场景,默认平台分佣为DEFAULT_PLATFORM_COMMISSION',
`channel_app_id` varchar(64) NOT NULL COMMENT '微信服务商AppID或支付宝平台AppID快照',
`channel_sub_mch_id` varchar(64) NOT NULL COMMENT '微信子商户号或历史渠道子商户标识快照',
`account_key` varchar(64) DEFAULT NULL COMMENT '支付宝命名平台账号键;默认账号及微信为空',
`app_auth_token` varchar(512) DEFAULT NULL COMMENT '支付宝第三方代调用授权令牌;仅服务端渠道调用使用',
`receiver_type` varchar(32) NOT NULL COMMENT '渠道接收方类型,如MERCHANT_ID、userId、loginName',
`receiver_account` varchar(128) NOT NULL COMMENT '平台分佣接收账号;管理端展示时必须脱敏',
`receiver_name` varchar(128) DEFAULT NULL COMMENT '平台分佣接收方名称快照',
`fz_ratio` decimal(6,3) NOT NULL DEFAULT '0.000' COMMENT '平台抽佣比例,单位百分比,范围0到100,精确到0.001%',
`channel_relation_type` varchar(64) DEFAULT NULL COMMENT '渠道关系类型;微信默认SERVICE_PROVIDER,支付宝允许为空',
`out_request_no` varchar(64) NOT NULL COMMENT '支付宝绑定稳定幂等请求号;微信作为本地稳定审计号',
`status` varchar(32) NOT NULL COMMENT 'PENDING_BIND、BINDING、BOUND、BIND_FAILED或SUPERSEDED',
`is_default` char(1) NOT NULL DEFAULT '1' COMMENT '是否为当前支付配置默认关系:0否、1是',
`bind_req` text DEFAULT NULL COMMENT '脱敏后的关系绑定请求快照',
`bind_res` text DEFAULT NULL COMMENT '渠道关系绑定或查询响应快照',
`bind_error` varchar(1000) DEFAULT NULL COMMENT '最近一次绑定错误摘要,不含凭据',
`last_external_status` varchar(128) DEFAULT NULL COMMENT '最近一次渠道结果码或外部状态',
`last_query_time` datetime DEFAULT NULL COMMENT '最近一次关系绑定或查询时间',
`bound_time` datetime DEFAULT NULL COMMENT '渠道明确确认关系已绑定的时间',
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除标记:0正常、1删除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_third_mch_fz_relation_identity`
(`tenant_id`, `pay_setting_id`, `platform_type`, `channel_app_id`, `channel_sub_mch_id`,
`receiver_type`, `receiver_account`, `relation_scene`),
KEY `idx_third_mch_fz_relation_default`
(`tenant_id`, `mch_id`, `platform_type`, `is_default`, `status`, `del_flag`),
KEY `idx_third_mch_fz_relation_task`
(`status`, `is_default`, `update_time`, `id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci
COMMENT = '商户第三方支付分账关系';
-- 四、旧环境补齐关系引用字段。每个字段独立判断,避免一个缺表/缺列阻断后续迁移。
SET @task_relation_column_exists = (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_payment_task' AND COLUMN_NAME = 'fz_relation_id'
);
SET @task_relation_column_sql = IF(
@task_relation_column_exists = 0,
'ALTER TABLE `third_payment_task` ADD COLUMN `fz_relation_id` bigint DEFAULT NULL COMMENT ''third_mch_fz_relation主键''',
'SELECT 1'
);
PREPARE task_relation_column_stmt FROM @task_relation_column_sql;
EXECUTE task_relation_column_stmt;
DEALLOCATE PREPARE task_relation_column_stmt;
SET @pay_relation_column_exists = (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND COLUMN_NAME = 'fz_relation_id'
);
SET @pay_order_table_exists = (
SELECT COUNT(1) FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order'
);
SET @pay_relation_column_sql = IF(
@pay_order_table_exists = 1 AND @pay_relation_column_exists = 0,
'ALTER TABLE `third_pay_order` ADD COLUMN `fz_relation_id` bigint DEFAULT NULL COMMENT ''支付创建时绑定的平台分佣关系主键''',
'SELECT 1'
);
PREPARE pay_relation_column_stmt FROM @pay_relation_column_sql;
EXECUTE pay_relation_column_stmt;
DEALLOCATE PREPARE pay_relation_column_stmt;
SET @relation_auth_column_exists = (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_mch_fz_relation' AND COLUMN_NAME = 'app_auth_token'
);
SET @relation_auth_column_sql = IF(
@relation_auth_column_exists = 0,
'ALTER TABLE `third_mch_fz_relation` ADD COLUMN `app_auth_token` varchar(512) DEFAULT NULL COMMENT ''支付宝第三方代调用授权令牌;仅服务端渠道调用使用''',
'SELECT 1'
);
PREPARE relation_auth_column_stmt FROM @relation_auth_column_sql;
EXECUTE relation_auth_column_stmt;
DEALLOCATE PREPARE relation_auth_column_stmt;
SET @relation_ratio_column_exists = (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_mch_fz_relation' AND COLUMN_NAME = 'fz_ratio'
);
SET @relation_ratio_column_sql = IF(
@relation_ratio_column_exists = 0,
'ALTER TABLE `third_mch_fz_relation` ADD COLUMN `fz_ratio` decimal(6,3) NOT NULL DEFAULT ''0.000'' COMMENT ''平台抽佣比例,单位百分比,范围0到100,精确到0.001%''',
'ALTER TABLE `third_mch_fz_relation` MODIFY COLUMN `fz_ratio` decimal(6,3) NOT NULL DEFAULT ''0.000'' COMMENT ''平台抽佣比例,单位百分比,范围0到100,精确到0.001%'''
);
PREPARE relation_ratio_column_stmt FROM @relation_ratio_column_sql;
EXECUTE relation_ratio_column_stmt;
DEALLOCATE PREPARE relation_ratio_column_stmt;
-- 五、补齐任务和业务扫描索引。索引已存在时不重复创建。
SET @index_ddl = IF(EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_payment_task' AND INDEX_NAME = 'idx_third_payment_task_relation'), 'SELECT 1', 'ALTER TABLE `third_payment_task` ADD INDEX `idx_third_payment_task_relation` (`tenant_id`, `fz_relation_id`)');
PREPARE index_stmt FROM @index_ddl; EXECUTE index_stmt; DEALLOCATE PREPARE index_stmt;
SET @pay_task_scan_ready = (SELECT COUNT(DISTINCT COLUMN_NAME) = 5 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND COLUMN_NAME IN ('status', 'del_flag', 'pay_platform_type', 'update_time', 'id'));
SET @pay_fz_scan_ready = (SELECT COUNT(DISTINCT COLUMN_NAME) = 5 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND COLUMN_NAME IN ('status', 'enable_fz', 'del_flag', 'pay_time', 'id'));
SET @pay_callback_scan_ready = (SELECT COUNT(DISTINCT COLUMN_NAME) = 5 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND COLUMN_NAME IN ('status', 'is_biz_callback', 'del_flag', 'update_time', 'id'));
SET @pay_close_scan_ready = (SELECT COUNT(DISTINCT COLUMN_NAME) = 5 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND COLUMN_NAME IN ('status', 'biz_type', 'del_flag', 'create_time', 'id'));
SET @pay_tenant_no_ready = (SELECT COUNT(DISTINCT COLUMN_NAME) = 3 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND COLUMN_NAME IN ('tenant_id', 'pay_no', 'del_flag'));
SET @pay_relation_ready = (SELECT COUNT(DISTINCT COLUMN_NAME) = 2 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND COLUMN_NAME IN ('tenant_id', 'fz_relation_id'));
SET @fz_task_scan_ready = (SELECT COUNT(DISTINCT COLUMN_NAME) = 4 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_fz_order' AND COLUMN_NAME IN ('status', 'del_flag', 'update_time', 'id'));
SET @fz_tenant_no_ready = (SELECT COUNT(DISTINCT COLUMN_NAME) = 3 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_fz_order' AND COLUMN_NAME IN ('tenant_id', 'pay_no', 'del_flag'));
SET @aggregate_task_scan_ready = (SELECT COUNT(DISTINCT COLUMN_NAME) = 4 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_aggregate_pay_session' AND COLUMN_NAME IN ('status', 'del_flag', 'update_time', 'id'));
SET @aggregate_close_scan_ready = (SELECT COUNT(DISTINCT COLUMN_NAME) = 4 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_aggregate_pay_session' AND COLUMN_NAME IN ('status', 'del_flag', 'create_time', 'id'));
SET @index_ddl = IF(@pay_task_scan_ready = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND INDEX_NAME = 'idx_third_pay_order_task_scan'), 'ALTER TABLE `third_pay_order` ADD INDEX `idx_third_pay_order_task_scan` (`status`, `del_flag`, `pay_platform_type`, `update_time`, `id`)', 'SELECT 1');
PREPARE index_stmt FROM @index_ddl; EXECUTE index_stmt; DEALLOCATE PREPARE index_stmt;
SET @index_ddl = IF(@pay_fz_scan_ready = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND INDEX_NAME = 'idx_third_pay_order_fz_scan'), 'ALTER TABLE `third_pay_order` ADD INDEX `idx_third_pay_order_fz_scan` (`status`, `enable_fz`, `del_flag`, `pay_time`, `id`)', 'SELECT 1');
PREPARE index_stmt FROM @index_ddl; EXECUTE index_stmt; DEALLOCATE PREPARE index_stmt;
SET @index_ddl = IF(@pay_callback_scan_ready = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND INDEX_NAME = 'idx_third_pay_order_callback_scan'), 'ALTER TABLE `third_pay_order` ADD INDEX `idx_third_pay_order_callback_scan` (`status`, `is_biz_callback`, `del_flag`, `update_time`, `id`)', 'SELECT 1');
PREPARE index_stmt FROM @index_ddl; EXECUTE index_stmt; DEALLOCATE PREPARE index_stmt;
SET @index_ddl = IF(@pay_close_scan_ready = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND INDEX_NAME = 'idx_third_pay_order_close_scan'), 'ALTER TABLE `third_pay_order` ADD INDEX `idx_third_pay_order_close_scan` (`status`, `biz_type`, `del_flag`, `create_time`, `id`)', 'SELECT 1');
PREPARE index_stmt FROM @index_ddl; EXECUTE index_stmt; DEALLOCATE PREPARE index_stmt;
SET @index_ddl = IF(@pay_tenant_no_ready = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND INDEX_NAME = 'idx_third_pay_order_tenant_pay_no'), 'ALTER TABLE `third_pay_order` ADD INDEX `idx_third_pay_order_tenant_pay_no` (`tenant_id`, `pay_no`, `del_flag`)', 'SELECT 1');
PREPARE index_stmt FROM @index_ddl; EXECUTE index_stmt; DEALLOCATE PREPARE index_stmt;
SET @index_ddl = IF(@pay_relation_ready = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_pay_order' AND INDEX_NAME = 'idx_third_pay_order_fz_relation'), 'ALTER TABLE `third_pay_order` ADD INDEX `idx_third_pay_order_fz_relation` (`tenant_id`, `fz_relation_id`)', 'SELECT 1');
PREPARE index_stmt FROM @index_ddl; EXECUTE index_stmt; DEALLOCATE PREPARE index_stmt;
SET @index_ddl = IF(@fz_task_scan_ready = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_fz_order' AND INDEX_NAME = 'idx_third_fz_order_task_scan'), 'ALTER TABLE `third_fz_order` ADD INDEX `idx_third_fz_order_task_scan` (`status`, `del_flag`, `update_time`, `id`)', 'SELECT 1');
PREPARE index_stmt FROM @index_ddl; EXECUTE index_stmt; DEALLOCATE PREPARE index_stmt;
SET @index_ddl = IF(@fz_tenant_no_ready = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_fz_order' AND INDEX_NAME = 'idx_third_fz_order_tenant_pay_no'), 'ALTER TABLE `third_fz_order` ADD INDEX `idx_third_fz_order_tenant_pay_no` (`tenant_id`, `pay_no`, `del_flag`)', 'SELECT 1');
PREPARE index_stmt FROM @index_ddl; EXECUTE index_stmt; DEALLOCATE PREPARE index_stmt;
SET @index_ddl = IF(@aggregate_task_scan_ready = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_aggregate_pay_session' AND INDEX_NAME = 'idx_third_aggregate_task_scan'), 'ALTER TABLE `third_aggregate_pay_session` ADD INDEX `idx_third_aggregate_task_scan` (`status`, `del_flag`, `update_time`, `id`)', 'SELECT 1');
PREPARE index_stmt FROM @index_ddl; EXECUTE index_stmt; DEALLOCATE PREPARE index_stmt;
SET @index_ddl = IF(@aggregate_close_scan_ready = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'third_aggregate_pay_session' AND INDEX_NAME = 'idx_third_aggregate_close_scan'), 'ALTER TABLE `third_aggregate_pay_session` ADD INDEX `idx_third_aggregate_close_scan` (`status`, `del_flag`, `create_time`, `id`)', 'SELECT 1');
PREPARE index_stmt FROM @index_ddl; EXECUTE index_stmt; DEALLOCATE PREPARE index_stmt;
-- 六、迁移钱包历史配置。只有关系完整、JSON合法且数据已核对时,才清理旧配置键和旧比例列。
SET @wallet_table_exists = (
SELECT COUNT(1) FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'pjcloudx_wallet' AND TABLE_NAME = 'mch_pay_setting'
);
SET @legacy_ratio_column_exists = (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'pjcloudx_wallet' AND TABLE_NAME = 'mch_pay_setting' AND COLUMN_NAME = 'fz_ratio'
);
SET @legacy_invalid_json_count = 0;
SET @legacy_invalid_json_sql = IF(
@wallet_table_exists = 1,
'SELECT COUNT(1) INTO @legacy_invalid_json_count FROM `pjcloudx_wallet`.`mch_pay_setting` WHERE platform_type IN (''10'', ''20'') AND setting_json IS NOT NULL AND TRIM(setting_json) <> '''' AND JSON_VALID(setting_json) = 0',
'SELECT 1'
);
PREPARE legacy_invalid_json_stmt FROM @legacy_invalid_json_sql;
EXECUTE legacy_invalid_json_stmt;
DEALLOCATE PREPARE legacy_invalid_json_stmt;
SET @legacy_channel_update_sql = IF(
@wallet_table_exists = 1 AND @legacy_invalid_json_count = 0,
'UPDATE `pjcloudx_thirdparty`.`third_mch_fz_relation` r JOIN `pjcloudx_wallet`.`mch_pay_setting` s ON s.id = r.pay_setting_id AND s.mch_id = r.mch_id AND s.tenant_id = r.tenant_id SET r.channel_sub_mch_id = CASE WHEN NULLIF(TRIM(r.channel_sub_mch_id), '''') IS NULL THEN NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.subMchid''))), ''''), ''null'') ELSE r.channel_sub_mch_id END, r.account_key = CASE WHEN s.platform_type = ''20'' AND NULLIF(TRIM(r.account_key), '''') IS NULL THEN NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.accountKey''))), ''''), ''null'') ELSE r.account_key END, r.app_auth_token = CASE WHEN s.platform_type = ''20'' AND NULLIF(TRIM(r.app_auth_token), '''') IS NULL THEN NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.appAuthToken''))), ''''), ''null'') ELSE r.app_auth_token END WHERE s.platform_type IN (''10'', ''20'') AND JSON_VALID(s.setting_json) = 1 AND r.del_flag = ''0'' AND r.is_default = ''1''',
'SELECT 1'
);
PREPARE legacy_channel_update_stmt FROM @legacy_channel_update_sql;
EXECUTE legacy_channel_update_stmt;
DEALLOCATE PREPARE legacy_channel_update_stmt;
SET @legacy_ratio_update_sql = IF(
@wallet_table_exists = 1 AND @legacy_ratio_column_exists = 1,
'UPDATE `pjcloudx_thirdparty`.`third_mch_fz_relation` r JOIN `pjcloudx_wallet`.`mch_pay_setting` s ON s.id = r.pay_setting_id AND s.mch_id = r.mch_id AND s.tenant_id = r.tenant_id SET r.fz_ratio = LEAST(GREATEST(COALESCE(s.fz_ratio, 0), 0), 100) WHERE s.del_flag = ''0'' AND r.del_flag = ''0'' AND r.is_default = ''1'' AND COALESCE(s.fz_ratio, 0) > 0 AND COALESCE(r.fz_ratio, 0) = 0',
'SELECT 1'
);
PREPARE legacy_ratio_update_stmt FROM @legacy_ratio_update_sql;
EXECUTE legacy_ratio_update_stmt;
DEALLOCATE PREPARE legacy_ratio_update_stmt;
SET @legacy_channel_unmigrated_count = 0;
SET @legacy_channel_unmigrated_sql = IF(
@wallet_table_exists = 1 AND @legacy_invalid_json_count = 0,
'SELECT COUNT(1) INTO @legacy_channel_unmigrated_count FROM `pjcloudx_wallet`.`mch_pay_setting` s WHERE s.del_flag = ''0'' AND s.platform_type IN (''10'', ''20'') AND JSON_VALID(s.setting_json) = 1 AND (NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.subMchid''))), ''''), ''null'') IS NOT NULL OR NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.accountKey''))), ''''), ''null'') IS NOT NULL OR NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.appAuthToken''))), ''''), ''null'') IS NOT NULL) AND NOT EXISTS (SELECT 1 FROM `pjcloudx_thirdparty`.`third_mch_fz_relation` r WHERE r.tenant_id = s.tenant_id AND r.mch_id = s.mch_id AND r.pay_setting_id = s.id AND r.platform_type = s.platform_type AND r.is_default = ''1'' AND r.del_flag = ''0'' AND (NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.subMchid''))), ''''), ''null'') IS NULL OR r.channel_sub_mch_id = NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.subMchid''))), ''''), ''null'')) AND (s.platform_type <> ''20'' OR NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.accountKey''))), ''''), ''null'') IS NULL OR r.account_key = NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.accountKey''))), ''''), ''null'')) AND (s.platform_type <> ''20'' OR NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.appAuthToken''))), ''''), ''null'') IS NULL OR r.app_auth_token = NULLIF(NULLIF(TRIM(JSON_UNQUOTE(JSON_EXTRACT(s.setting_json, ''$.appAuthToken''))), ''''), ''null'')))',
'SELECT 1'
);
PREPARE legacy_channel_unmigrated_stmt FROM @legacy_channel_unmigrated_sql;
EXECUTE legacy_channel_unmigrated_stmt;
DEALLOCATE PREPARE legacy_channel_unmigrated_stmt;
SET @legacy_ratio_unmigrated_count = 0;
SET @legacy_ratio_unmigrated_sql = IF(
@wallet_table_exists = 1 AND @legacy_ratio_column_exists = 1,
'SELECT COUNT(1) INTO @legacy_ratio_unmigrated_count FROM `pjcloudx_wallet`.`mch_pay_setting` s WHERE COALESCE(s.fz_ratio, 0) > 0 AND s.del_flag = ''0'' AND NOT EXISTS (SELECT 1 FROM `pjcloudx_thirdparty`.`third_mch_fz_relation` r WHERE r.tenant_id = s.tenant_id AND r.mch_id = s.mch_id AND r.pay_setting_id = s.id AND r.is_default = ''1'' AND r.fz_ratio > 0 AND r.del_flag = ''0'')',
'SELECT 1'
);
PREPARE legacy_ratio_unmigrated_stmt FROM @legacy_ratio_unmigrated_sql;
EXECUTE legacy_ratio_unmigrated_stmt;
DEALLOCATE PREPARE legacy_ratio_unmigrated_stmt;
SET @legacy_json_cleanup_sql = IF(
@wallet_table_exists = 1 AND @legacy_invalid_json_count = 0 AND @legacy_channel_unmigrated_count = 0 AND @legacy_ratio_unmigrated_count = 0,
'UPDATE `pjcloudx_wallet`.`mch_pay_setting` SET setting_json = CASE WHEN JSON_LENGTH(JSON_REMOVE(setting_json, ''$.subMchid'', ''$.accountKey'', ''$.appAuthToken'')) = 0 THEN NULL ELSE JSON_REMOVE(setting_json, ''$.subMchid'', ''$.accountKey'', ''$.appAuthToken'') END WHERE platform_type IN (''10'', ''20'') AND JSON_VALID(setting_json) = 1',
'SELECT 1'
);
PREPARE legacy_json_cleanup_stmt FROM @legacy_json_cleanup_sql;
EXECUTE legacy_json_cleanup_stmt;
DEALLOCATE PREPARE legacy_json_cleanup_stmt;
SET @drop_legacy_ratio_sql = IF(
@wallet_table_exists = 1 AND @legacy_ratio_column_exists = 1 AND @legacy_invalid_json_count = 0 AND @legacy_channel_unmigrated_count = 0 AND @legacy_ratio_unmigrated_count = 0,
'ALTER TABLE `pjcloudx_wallet`.`mch_pay_setting` DROP COLUMN `fz_ratio`',
'SELECT 1'
);
PREPARE drop_legacy_ratio_stmt FROM @drop_legacy_ratio_sql;
EXECUTE drop_legacy_ratio_stmt;
DEALLOCATE PREPARE drop_legacy_ratio_stmt;
-- 七、输出迁移结果。出现BLOCKED时不要上线新代码,先处理对应数据或数据库前置问题。
SET @required_table_count = (
SELECT COUNT(DISTINCT TABLE_NAME)
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME IN ('third_aggregate_pay_session', 'third_payment_task',
'third_payment_task_attempt', 'third_mch_fz_relation',
'third_pay_order', 'third_fz_order')
);
SET @required_column_count = (
SELECT COUNT(1)
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND ((TABLE_NAME = 'third_mch_fz_relation' AND COLUMN_NAME IN ('app_auth_token', 'fz_ratio'))
OR (TABLE_NAME = 'third_payment_task' AND COLUMN_NAME = 'fz_relation_id')
OR (TABLE_NAME = 'third_pay_order' AND COLUMN_NAME = 'fz_relation_id'))
);
SET @skipped_scan_index_count =
(@pay_task_scan_ready = 0) + (@pay_fz_scan_ready = 0) + (@pay_callback_scan_ready = 0)
+ (@pay_close_scan_ready = 0) + (@pay_tenant_no_ready = 0) + (@pay_relation_ready = 0)
+ (@fz_task_scan_ready = 0) + (@fz_tenant_no_ready = 0)
+ (@aggregate_task_scan_ready = 0) + (@aggregate_close_scan_ready = 0);
SELECT CASE
WHEN @required_table_count <> 6 THEN 'BLOCKED_REQUIRED_THIRD_TABLE_NOT_FOUND'
WHEN @required_column_count <> 4 THEN 'BLOCKED_REQUIRED_THIRD_COLUMN_NOT_FOUND'
WHEN @skipped_scan_index_count > 0 THEN 'BLOCKED_SCAN_INDEX_SOURCE_COLUMN_NOT_FOUND'
WHEN @wallet_table_exists = 0 THEN 'BLOCKED_WALLET_TABLE_NOT_FOUND'
WHEN @legacy_invalid_json_count > 0 THEN 'BLOCKED_INVALID_SETTING_JSON'
WHEN @legacy_channel_unmigrated_count > 0 THEN 'BLOCKED_UNMIGRATED_CHANNEL_CONFIG'
WHEN @legacy_ratio_unmigrated_count > 0 THEN 'BLOCKED_UNMIGRATED_POSITIVE_RATIO'
ELSE 'SUCCESS_THIRD_PAYMENT_SCHEMA_MIGRATED'
END AS migration_status,
@legacy_invalid_json_count AS invalid_setting_json_count,
@legacy_channel_unmigrated_count AS unmigrated_channel_config_count,
@legacy_ratio_unmigrated_count AS unmigrated_positive_ratio_count,
@required_table_count AS required_third_table_count,
@required_column_count AS required_third_column_count,
@skipped_scan_index_count AS skipped_scan_index_count;
SELECT table_name, column_name, column_type
FROM information_schema.COLUMNS
WHERE table_schema = DATABASE()
AND ((table_name = 'third_mch_fz_relation' AND column_name IN ('app_auth_token', 'fz_ratio'))
OR (table_name = 'third_payment_task' AND column_name = 'fz_relation_id')
OR (table_name = 'third_pay_order' AND column_name = 'fz_relation_id'))
ORDER BY table_name, ordinal_position;