-- 仅用于已经安装旧五表版本的库。先备份、停止流程写入及后台扫描,并执行三表建表脚本。 -- 可重复执行;旧表暂保留作为核对来源,新代码不再访问。核验并备份后再清理旧表。 DELIMITER $$ DROP PROCEDURE IF EXISTS migrate_flow_three_tables$$ CREATE PROCEDURE migrate_flow_three_tables() BEGIN DECLARE finished INT DEFAULT 0; DECLARE v_task VARCHAR(64); DECLARE v_tenant BIGINT; DECLARE v_instance VARCHAR(64); DECLARE v_due TIMESTAMP(3); DECLARE v_reminded TIMESTAMP(3); DECLARE v_id VARCHAR(36); DECLARE v_callback VARCHAR(36); DECLARE v_attempt INT; DECLARE v_result VARCHAR(16); DECLARE v_detail VARCHAR(500); DECLARE v_created TIMESTAMP(3); DECLARE v_history LONGTEXT; DECLARE watches CURSOR FOR SELECT task_id,tenant_id,instance_id,due_at,reminded_at FROM flow_task_watch; DECLARE audits CURSOR FOR SELECT id,callback_id,tenant_id,attempt_no,result,detail,created_at FROM flow_callback_attempt ORDER BY created_at,id; DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished=1; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='flow_notice' AND column_name='status') THEN ALTER TABLE flow_notice ADD COLUMN status VARCHAR(16) NOT NULL DEFAULT 'SENT'; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='flow_notice' AND column_name='scheduled_at') THEN ALTER TABLE flow_notice ADD COLUMN scheduled_at TIMESTAMP(3) NULL; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='flow_notice' AND column_name='sent_at') THEN ALTER TABLE flow_notice ADD COLUMN sent_at TIMESTAMP(3) NULL; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='flow_notice' AND column_name='reminder_key') THEN ALTER TABLE flow_notice ADD COLUMN reminder_key VARCHAR(64) NULL; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name='flow_notice' AND index_name='uk_notice_reminder') THEN ALTER TABLE flow_notice ADD UNIQUE KEY uk_notice_reminder(tenant_id,reminder_key); END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name='flow_notice' AND index_name='idx_notice_due') THEN ALTER TABLE flow_notice ADD KEY idx_notice_due(status,scheduled_at); END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='flow_callback' AND column_name='history') THEN ALTER TABLE flow_callback ADD COLUMN history LONGTEXT NULL; END IF; -- DDL 自动提交;以下数据搬迁独立事务,任何异常回滚数据部分,修正后可重跑。 BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; RESIGNAL; END; START TRANSACTION; UPDATE flow_notice SET sent_at=created_at WHERE status='SENT' AND sent_at IS NULL; IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name='flow_task_watch') THEN SET finished=0; OPEN watches; watch_loop: LOOP FETCH watches INTO v_task,v_tenant,v_instance,v_due,v_reminded; IF finished=1 THEN LEAVE watch_loop; END IF; IF NOT EXISTS (SELECT 1 FROM flow_notice WHERE tenant_id=v_tenant AND reminder_key=v_task) THEN IF (SELECT COUNT(*) FROM flow_notice WHERE tenant_id=v_tenant AND event_key=CONCAT('OVERDUE:',v_task) AND kind='OVERDUE') > 1 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Multiple overdue notices for one watch; inspect before migration'; END IF; IF EXISTS (SELECT 1 FROM flow_notice WHERE tenant_id=v_tenant AND event_key=CONCAT('OVERDUE:',v_task) AND kind='OVERDUE') THEN UPDATE flow_notice SET reminder_key=v_task,scheduled_at=v_due,sent_at=COALESCE(v_reminded,sent_at) WHERE tenant_id=v_tenant AND event_key=CONCAT('OVERDUE:',v_task) AND kind='OVERDUE'; ELSE INSERT INTO flow_notice(id,tenant_id,user_id,event_key,kind,title,task_id,instance_id,created_at,status,scheduled_at,sent_at,reminder_key) VALUES(UUID(),v_tenant,'',CONCAT('OVERDUE:',v_task),'OVERDUE','流程任务',v_task,v_instance,CURRENT_TIMESTAMP(3), CASE WHEN v_reminded IS NOT NULL THEN 'SENT' WHEN v_due IS NULL THEN 'DISABLED' ELSE 'PENDING' END,v_due,v_reminded,v_task); END IF; END IF; END LOOP; CLOSE watches; END IF; IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name='flow_callback_attempt') THEN SET finished=0; OPEN audits; audit_loop: LOOP FETCH audits INTO v_id,v_callback,v_tenant,v_attempt,v_result,v_detail,v_created; IF finished=1 THEN LEAVE audit_loop; END IF; IF NOT EXISTS (SELECT 1 FROM flow_callback WHERE id=v_callback AND tenant_id=v_tenant) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Orphan callback attempt; retain old tables and inspect'; END IF; SELECT COALESCE(history,'[]') INTO v_history FROM flow_callback WHERE id=v_callback AND tenant_id=v_tenant FOR UPDATE; IF JSON_SEARCH(v_history,'one',v_id,NULL,'$[*].id') IS NULL THEN UPDATE flow_callback SET history=JSON_ARRAY_APPEND(v_history,'$',JSON_OBJECT('id',v_id,'attempt_no',v_attempt, 'result',v_result,'detail',v_detail,'created_at',DATE_FORMAT(v_created,'%Y-%m-%d %H:%i:%s.%f'))) WHERE id=v_callback AND tenant_id=v_tenant; END IF; END LOOP; CLOSE audits; END IF; COMMIT; END; END$$ CALL migrate_flow_three_tables()$$ DROP PROCEDURE migrate_flow_three_tables$$ DELIMITER ;