-- 每日菜谱预约订单过期未核销自动核销。 -- 幂等迁移脚本:可反复执行。每个字段/索引先查 information_schema,已存在则跳过(执行 SELECT 1 占位), -- 不存在才执行 DDL;部分应用过的库重跑不会报 Duplicate column / Duplicate key。 -- 前置条件:20260822(wallet 配置列、订单列)与 20260823(订单核销时间列)的锚点列需已存在, -- 否则对应语句会因 AFTER 锚点缺失报错——这是有意的失败提示,请先补齐前置迁移。 -- 任务表部分兼容 20260822 任务表语句未成功落库(scene_biz_id 缺失)的环境,会自动补齐。 -- 权限复用 wallet_mchManage_setting、hard_hardAfterSale_view、hard_hardAfterSale_adminRefund,无需新增 sys_menu 记录。 -- 【可选诊断】任务表当前列与索引,执行后可对照确认脚本将补齐的内容。 SELECT COLUMN_NAME, COLUMN_TYPE, IFNULL(GENERATION_EXPRESSION, '') AS generation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_auto_refund_task' AND COLUMN_NAME IN ('task_type', 'trigger_time', 'restore_inventory', 'scene_biz_id') ORDER BY ORDINAL_POSITION; SELECT INDEX_NAME, GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX SEPARATOR ',') AS index_columns FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_auto_refund_task' GROUP BY INDEX_NAME ORDER BY INDEX_NAME; -- 一、Wallet 配置列(三列各自独立判断)。 SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_wallet`.`mch_auto_refund_config` ADD COLUMN `auto_verify_enabled` char(1) NOT NULL DEFAULT ''0'' COMMENT ''过期未核销自动核销开关,0关闭,1开启'' AFTER `expired_reserve_refund_restore_inventory`', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_wallet' AND TABLE_NAME = 'mch_auto_refund_config' AND COLUMN_NAME = 'auto_verify_enabled'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_wallet`.`mch_auto_refund_config` ADD COLUMN `auto_verify_days` int(11) DEFAULT NULL COMMENT ''支付后自动核销天数,允许1至365,开关开启时必填'' AFTER `auto_verify_enabled`', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_wallet' AND TABLE_NAME = 'mch_auto_refund_config' AND COLUMN_NAME = 'auto_verify_days'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_wallet`.`mch_auto_refund_config` ADD COLUMN `auto_verify_restore_inventory` char(1) NOT NULL DEFAULT ''1'' COMMENT ''自动核销是否恢复线上池库存,0不恢复,1恢复'' AFTER `auto_verify_days`', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_wallet' AND TABLE_NAME = 'mch_auto_refund_config' AND COLUMN_NAME = 'auto_verify_restore_inventory'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- 二、订单快照与核销方式列(四列各自独立判断)。 SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_hardware`.`hard_order` ADD COLUMN `auto_verify_config_id` bigint(20) DEFAULT NULL COMMENT ''支付成功时命中的自动核销配置ID快照,为空表示不参与自动核销'' AFTER `expired_refund_restore_inventory`', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_order' AND COLUMN_NAME = 'auto_verify_config_id'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_hardware`.`hard_order` ADD COLUMN `auto_verify_deadline` datetime DEFAULT NULL COMMENT ''支付时间加配置天数后的自动核销触发时间'' AFTER `auto_verify_config_id`', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_order' AND COLUMN_NAME = 'auto_verify_deadline'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_hardware`.`hard_order` ADD COLUMN `auto_verify_restore_inventory` char(1) DEFAULT NULL COMMENT ''自动核销退库存快照,0不恢复,1恢复线上池库存'' AFTER `auto_verify_deadline`', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_order' AND COLUMN_NAME = 'auto_verify_restore_inventory'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_hardware`.`hard_order` ADD COLUMN `verification_type` char(1) DEFAULT NULL COMMENT ''核销方式,1人工核销,2自动核销;历史已核销订单为空视为人工'' AFTER `verification_time`', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_order' AND COLUMN_NAME = 'verification_type'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- 三、任务表:兼容 20260822 任务表语句未落库的环境,先确保任务场景列存在(幂等 MODIFY 注释与空值语义)。 SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_hardware`.`hard_auto_refund_task` ADD COLUMN `task_type` char(10) NOT NULL DEFAULT ''10'' COMMENT ''任务场景,10用户免审售后,20过期预约退款,30过期未核销自动核销'' AFTER `after_sale_id`', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_auto_refund_task' AND COLUMN_NAME = 'task_type'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_hardware`.`hard_auto_refund_task` ADD COLUMN `trigger_time` datetime DEFAULT NULL COMMENT ''过期退款和自动核销任务按订单快照触发时间,旧售后任务为空'' AFTER `eligibility_deadline`', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_auto_refund_task' AND COLUMN_NAME = 'trigger_time'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_hardware`.`hard_auto_refund_task` ADD COLUMN `restore_inventory` char(1) DEFAULT NULL COMMENT ''任务退库存快照,0不恢复,1恢复'' AFTER `trigger_time`', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_auto_refund_task' AND COLUMN_NAME = 'restore_inventory'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- 幂等注释与空值语义修正(重复执行无副作用,兼为 20260822 未落库环境补齐)。 ALTER TABLE `pjcloudx_hardware`.`hard_auto_refund_task` MODIFY COLUMN `after_sale_id` bigint(20) DEFAULT NULL COMMENT '售后单ID,过期退款和自动核销任务为空', MODIFY COLUMN `rule_type` varchar(32) DEFAULT NULL COMMENT '普通售后任务规则快照,过期预约任务为空', MODIFY COLUMN `eligibility_deadline` datetime DEFAULT NULL COMMENT '普通售后免审资格截止时间,过期预约任务为空', MODIFY COLUMN `task_type` char(10) NOT NULL DEFAULT '10' COMMENT '任务场景,10用户免审售后,20过期预约退款,30过期未核销自动核销', MODIFY COLUMN `status` char(10) NOT NULL COMMENT '任务状态,10待执行,20等待重试,30开关关闭暂停,31配置不可用暂停,40重试耗尽,50成功,60订单已终态跳过'; -- scene_biz_id 生成列:缺失则新增(表达式含自动核销场景 30),已存在则 MODIFY 刷新表达式。 SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_hardware`.`hard_auto_refund_task` ADD COLUMN `scene_biz_id` bigint(20) GENERATED ALWAYS AS (CASE WHEN `task_type` IN (''20'', ''30'') THEN `order_id` ELSE `after_sale_id` END) STORED COMMENT ''任务场景业务唯一值,系统生成,禁止业务代码写入'' AFTER `restore_inventory`', 'ALTER TABLE `pjcloudx_hardware`.`hard_auto_refund_task` MODIFY COLUMN `scene_biz_id` bigint(20) GENERATED ALWAYS AS (CASE WHEN `task_type` IN (''20'', ''30'') THEN `order_id` ELSE `after_sale_id` END) STORED COMMENT ''任务场景业务唯一值,系统生成,禁止业务代码写入''') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_auto_refund_task' AND COLUMN_NAME = 'scene_biz_id'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- 新建场景唯一键前应确认本查询返回 0 行;存在重复说明历史任务数据冲突,必须先人工核对,禁止自动清理。 SELECT tenant_id, task_type, del_flag, CASE WHEN task_type = '20' THEN order_id ELSE after_sale_id END AS scene_biz_id, COUNT(*) AS duplicate_count FROM pjcloudx_hardware.hard_auto_refund_task GROUP BY tenant_id, task_type, del_flag, CASE WHEN task_type = '20' THEN order_id ELSE after_sale_id END HAVING COUNT(*) > 1; -- 旧售后单唯一键:存在才删除,给场景唯一键让位。 SET @ddl = (SELECT IF(COUNT(*) > 0, 'ALTER TABLE `pjcloudx_hardware`.`hard_auto_refund_task` DROP INDEX `uk_hard_auto_refund_task_after_sale`', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_auto_refund_task' AND INDEX_NAME = 'uk_hard_auto_refund_task_after_sale'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- 场景唯一键:缺失才创建(存在重复数据时本语句会失败,先处理上方诊断结果)。 SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_hardware`.`hard_auto_refund_task` ADD UNIQUE KEY `uk_hard_auto_refund_task_scene_biz` (`tenant_id`, `task_type`, `scene_biz_id`, `del_flag`)', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_auto_refund_task' AND INDEX_NAME = 'uk_hard_auto_refund_task_scene_biz'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- 扫描索引:缺失才创建。 SET @ddl = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE `pjcloudx_hardware`.`hard_auto_refund_task` ADD KEY `idx_hard_auto_refund_task_scan_type` (`tenant_id`, `task_type`, `status`, `next_retry_time`, `del_flag`)', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_auto_refund_task' AND INDEX_NAME = 'idx_hard_auto_refund_task_scan_type'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- 四、尝试日志注释更新。幂等,任何状态都可执行。 ALTER TABLE `pjcloudx_hardware`.`hard_auto_refund_attempt_log` MODIFY COLUMN `result_status` char(10) NOT NULL COMMENT '尝试结果,10执行中,20成功,30失败,40因开关关闭跳过,50订单已终态跳过', MODIFY COLUMN `after_sale_id` bigint(20) DEFAULT NULL COMMENT '售后单ID,过期退款和自动核销任务为空'; -- 【验收】生成列表达式应包含 '20' 和 '30',且三张表的新列均已存在。 SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, IFNULL(GENERATION_EXPRESSION, '') AS generation FROM information_schema.COLUMNS WHERE (TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_auto_refund_task' AND COLUMN_NAME = 'scene_biz_id') OR (TABLE_SCHEMA = 'pjcloudx_wallet' AND TABLE_NAME = 'mch_auto_refund_config' AND COLUMN_NAME LIKE 'auto_verify_%') OR (TABLE_SCHEMA = 'pjcloudx_hardware' AND TABLE_NAME = 'hard_order' AND COLUMN_NAME = 'verification_type'); SET @ddl = NULL;