mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-13 14:09:00 +08:00
feat(flow): 删除旧的流程通知表和合并表的迁移脚本
- 删除了 `20261014_flow_first_batch.sql` 和 `20261014_flow_merge_tables.sql` 文件 - 更新 `FlowNoticeStatusEnum`,新增 REMOVING 状态 - 修改 `FlowNotice` 实体,确保在插入时保留 userId 值 - 在 `FlowDurableConfiguration` 中添加事务提交后撤销失效通知的逻辑 - 更新 `IFlowNoticeService` 接口,调整通知事件的处理逻辑 - 实现 `FlowNoticeServiceImpl` 中的通知撤销逻辑,确保不误删新通知 - 新增 `FlowMessageRemovalDTO` 数据传输对象,处理通知撤销请求 - 更新 `SysMessageService` 和 `SysMessageController`,支持内部调用撤销流程通知 - 添加 `SysMessageDeletionTests` 测试用例,验证撤销逻辑的正确性
This commit is contained in:
1 parent
589d17f0d3
commit
8d37e48d1f
21 files changed
+379
-282
No files matched your search
+5
-3
@@ -1568,7 +1568,8 @@ CREATE TABLE `sys_message_relation` (
|
||||
`update_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
|
||||
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '删除时间',
|
||||
`tenant_id` bigint DEFAULT NULL COMMENT '租户',
|
||||
PRIMARY KEY (`id`)
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_relation_msg_user` (`msg_id`,`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统消息推送记录';
|
||||
|
||||
|
||||
@@ -1852,7 +1853,7 @@ CREATE TABLE `flow_notice` (
|
||||
`task_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '引擎任务ID',
|
||||
`instance_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '引擎流程实例ID',
|
||||
`created_at` timestamp(3) NOT NULL COMMENT '创建时间',
|
||||
`status` varchar(16) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'SENT' COMMENT '状态:PENDING 待到期,READY 待投递,SENT 已投递,CANCELLED 已取消,DISABLED 已停用',
|
||||
`status` varchar(16) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'SENT' COMMENT '状态:PENDING 待到期,READY 待投递,SENT 已投递,REMOVING 待撤销,CANCELLED 已取消,DISABLED 已停用',
|
||||
`scheduled_at` timestamp(3) NULL DEFAULT NULL COMMENT '计划投递或到期时间',
|
||||
`sent_at` timestamp(3) NULL DEFAULT NULL COMMENT '投递到统一站内信的时间',
|
||||
`reminder_key` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '超时提醒任务ID,每个任务最多一条',
|
||||
@@ -1860,7 +1861,8 @@ CREATE TABLE `flow_notice` (
|
||||
UNIQUE KEY `uk_notice_event` (`tenant_id`,`user_id`,`event_key`),
|
||||
UNIQUE KEY `uk_notice_reminder` (`tenant_id`,`reminder_key`),
|
||||
KEY `idx_notice_owner` (`tenant_id`,`user_id`,`created_at`),
|
||||
KEY `idx_notice_due` (`status`,`scheduled_at`)
|
||||
KEY `idx_notice_due` (`status`,`scheduled_at`),
|
||||
KEY `idx_notice_task` (`tenant_id`,`task_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='流程通知事件';
|
||||
|
||||
-- ----------------------------
|
||||
|
||||
+2
-1
@@ -1519,7 +1519,8 @@ CREATE TABLE `sys_message_relation` (
|
||||
`update_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
|
||||
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '删除时间',
|
||||
`tenant_id` bigint DEFAULT NULL COMMENT '租户',
|
||||
PRIMARY KEY (`id`)
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_relation_msg_user` (`msg_id`,`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统消息推送记录';
|
||||
|
||||
DROP TABLE IF EXISTS `sys_system_config`;
|
||||
|
||||
+3
-2
@@ -319,7 +319,7 @@ CREATE TABLE `flow_notice` (
|
||||
`task_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '引擎任务ID',
|
||||
`instance_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '引擎流程实例ID',
|
||||
`created_at` timestamp(3) NOT NULL COMMENT '创建时间',
|
||||
`status` varchar(16) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'SENT' COMMENT '状态:PENDING 待到期,READY 待投递,SENT 已投递,CANCELLED 已取消,DISABLED 已停用',
|
||||
`status` varchar(16) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'SENT' COMMENT '状态:PENDING 待到期,READY 待投递,SENT 已投递,REMOVING 待撤销,CANCELLED 已取消,DISABLED 已停用',
|
||||
`scheduled_at` timestamp(3) NULL DEFAULT NULL COMMENT '计划投递或到期时间',
|
||||
`sent_at` timestamp(3) NULL DEFAULT NULL COMMENT '投递到统一站内信的时间',
|
||||
`reminder_key` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '超时提醒任务ID,每个任务最多一条',
|
||||
@@ -327,7 +327,8 @@ CREATE TABLE `flow_notice` (
|
||||
UNIQUE KEY `uk_notice_event` (`tenant_id`,`user_id`,`event_key`),
|
||||
UNIQUE KEY `uk_notice_reminder` (`tenant_id`,`reminder_key`),
|
||||
KEY `idx_notice_owner` (`tenant_id`,`user_id`,`created_at`),
|
||||
KEY `idx_notice_due` (`status`,`scheduled_at`)
|
||||
KEY `idx_notice_due` (`status`,`scheduled_at`),
|
||||
KEY `idx_notice_task` (`tenant_id`,`task_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='流程通知事件';
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
@@ -1,69 +0,0 @@
|
||||
# 节点配置版本隔离升级
|
||||
|
||||
本次只隔离节点配置。业务 flowId 不变;流程图、表单定义的完整历史快照不在本次范围。任务、抄送和实例详情使用对应版本的字段权限,当前表单中未被该快照声明的字段默认隐藏;这不替代服务端的提交字段鉴权。已结束实例的详情依赖 Flowable 历史数据来确定版本,历史已清理时会明确报错,不能回退最新版。
|
||||
|
||||
## 上线顺序
|
||||
|
||||
1. 暂停流程发布、发起、任务处理和异步执行器,备份流程业务表及 Flowable 数据。不得新旧版本混跑:旧代码会按 flowId + nodeId 删除节点记录。
|
||||
2. 对已有库执行同目录 20261013_flow_node_version.sql;新库直接使用初始化脚本。升级 SQL 只执行一次,不删除旧配置。
|
||||
3. 核对存量定义与节点配置,建立明确的版本映射。至少覆盖所有运行中定义;如需要查询历史任务、抄送或使用旧定义,也必须覆盖对应历史定义。
|
||||
4. 将已核验配置写入对应 process_definition_id,包含 root 和所有业务节点。每个租户、定义、节点只能有一条快照。按该版本的已核验表单字段集合补齐各节点 formPerms:原来未配置/空值的字段按原有前端默认语义补 E,已有 H/R/E 保留;包含 children 中的嵌套字段及带 field 的容器。不能用当前表单的新增字段补旧版权限。无法恢复原字段集合时,此版本迁移尚未完成。
|
||||
5. 复查覆盖情况,部署全部新代码后恢复业务。无法确认映射时不要恢复受影响流程;运行期会明确报“缺少节点版本快照”,不会回退读取最新版。
|
||||
|
||||
## 只读盘点
|
||||
|
||||
在工作流所在数据库执行以下查询;若自定义了 Flowable 表名前缀,请先调整。
|
||||
|
||||
SELECT DISTINCT d.TENANT_ID_, d.KEY_, d.ID_, d.VERSION_, d.DEPLOYMENT_ID_
|
||||
FROM ACT_RU_EXECUTION e
|
||||
JOIN ACT_RE_PROCDEF d ON d.ID_ = e.PROC_DEF_ID_
|
||||
ORDER BY d.TENANT_ID_, d.KEY_, d.VERSION_;
|
||||
|
||||
SELECT p.tenant_id, p.flow_id, p.unique_id, d.ID_, d.VERSION_, p.process
|
||||
FROM process p
|
||||
JOIN ACT_RE_PROCDEF d
|
||||
ON d.DEPLOYMENT_ID_ = p.unique_id
|
||||
AND d.KEY_ = p.flow_id
|
||||
AND d.TENANT_ID_ = CAST(p.tenant_id AS CHAR)
|
||||
WHERE p.del_flag = '0';
|
||||
|
||||
SELECT id, tenant_id, flow_id, node_id, del_flag, process_definition_id, data
|
||||
FROM process_node_data
|
||||
ORDER BY tenant_id, flow_id, node_id, id;
|
||||
|
||||
process.unique_id 存储部署 ID,可以定位当前模板的引擎定义。它只能帮助定位当前发布版本,不代表所有旧节点记录都属于该版本。旧节点表保留了逻辑删除数据,但没有发布版本标记;不能按行 ID 或时间顺序自动配对。
|
||||
|
||||
核对节点 JSON 与相应发布资料/备份,包括审批人、多实例模式、拒绝策略、字段权限、触发器和通知地址。配置中的敏感业务内容不要写入审查日志。
|
||||
|
||||
## 存量写入模板
|
||||
|
||||
下面只是单行映射示例,所有占位值都必须替换为已核验结果。先 SELECT 确认目标行和定义的租户、业务 key、版本一致,再在受控事务中执行。不要按 flowId 批量绑定最新版。
|
||||
|
||||
UPDATE process_node_data n
|
||||
JOIN ACT_RE_PROCDEF d
|
||||
ON d.ID_ = '<已核验的流程定义ID>'
|
||||
AND d.KEY_ = n.flow_id
|
||||
AND d.TENANT_ID_ = CAST(n.tenant_id AS CHAR)
|
||||
SET n.process_definition_id = d.ID_, n.del_flag = '0'
|
||||
WHERE n.id = <已核验的节点记录ID>
|
||||
AND n.tenant_id = <目标租户ID>
|
||||
AND n.process_definition_id IS NULL;
|
||||
|
||||
一条旧记录如果需属于多个已验证版本,应分别插入不同 ID 的快照副本;不要反复改写同一行的版本 ID。若需从原始设计 JSON 重建节点,注意前端的 nodeName/childNode 与数据库 JSON 的 name/children 映射,并保存所有节点。无法恢复的历史版本应明确列为阻塞,不能用最新版替代。
|
||||
|
||||
迁移后检查所有运行实例是否有 root 快照:
|
||||
|
||||
SELECT DISTINCT d.ID_, d.KEY_, d.TENANT_ID_
|
||||
FROM ACT_RU_EXECUTION e
|
||||
JOIN ACT_RE_PROCDEF d ON d.ID_ = e.PROC_DEF_ID_
|
||||
LEFT JOIN process_node_data n
|
||||
ON n.process_definition_id = d.ID_
|
||||
AND CAST(n.tenant_id AS CHAR) = d.TENANT_ID_
|
||||
AND n.node_id = 'root' AND n.del_flag = '0'
|
||||
WHERE n.id IS NULL;
|
||||
|
||||
该查询必须无遗漏;还需对照设计检查全部业务节点,不能仅用 root 存在代替完整性核验。新发布时程序先按当时的表单字段集合补齐默认 E 权限,再在部署事务中写快照,后续不得人工修改已发布数据。
|
||||
|
||||
## 回退
|
||||
|
||||
不能在产生新快照后直接恢复旧代码继续写流程。需要先冻结流程写入,再根据备份和期间新增实例单独制定回退方案。不要直接删除快照或批量清空版本列。
|
||||
@@ -1,6 +0,0 @@
|
||||
-- 升级已有数据库;全新数据库使用 0pigxx_boot.sql 或 3pigxx_flow.sql。
|
||||
-- 执行前停止流程写入并备份;本脚本不猜测存量节点与流程版本的对应关系。
|
||||
-- 必须完成同目录说明中的存量核对后再上线应用。
|
||||
ALTER TABLE process_node_data
|
||||
ADD COLUMN process_definition_id varchar(64) DEFAULT NULL COMMENT '引擎流程定义ID,发布版本快照',
|
||||
ADD UNIQUE KEY uk_node_definition (tenant_id, process_definition_id, node_id);
|
||||
@@ -1,61 +0,0 @@
|
||||
# 流程第一批能力升级
|
||||
|
||||
## 范围与入口
|
||||
|
||||
- 流程设计器:保存设计草稿、设计草稿箱;保存基本信息、表单和节点树,不部署流程。
|
||||
- 流程列表:申请草稿箱;动态表单发起抽屉可保存表单值和自选审批人。按用户和租户隔离,版本号防止多窗口覆盖。
|
||||
- 待办页:流程通知,包含任务分配、发起人催办和超时提醒;点击通知沿用现有任务详情权限检查。历史通知可能对应已完成或已转交任务。
|
||||
- 我发起的:运行中流程可催办,同一实例十分钟内最多一次;仅发起人可以操作。回调记录可查看状态和手动重试失败事件。
|
||||
- 审批节点 / 事件通知:配置自然时长分钟数,0 或留空关闭;最多 525600 分钟。计时基于任务创建时间,不因转交重新计时,到期只提醒当时的当前审批人一次。
|
||||
|
||||
本批申请草稿适用于流程引擎的动态表单。自定义业务组件自行提交、拥有独立的数据模型,继续由业务页面保存草稿;未擅自修改业务表单合同。申请草稿记录发布 deployment ID,恢复时版本变化会拒绝自动恢复并保留原草稿。普通草稿只在明确保存或设计器点击下一步时持久化,未保存的输入不会自动落库。
|
||||
|
||||
## 升级顺序
|
||||
|
||||
1. 备份数据库并停止本次流程写入,确认上一次节点版本隔离迁移已经完成。
|
||||
2. 在流程与引擎使用的同一 MySQL 数据源执行 `20261014_flow_first_batch.sql`。新增 `flow_draft`、`flow_notice`、`flow_callback` 三张表,不改动存量业务表;新装脚本 `0pigxx_boot.sql`、`3pigxx_flow.sql` 已包含相同建表语句。
|
||||
3. 部署后端与管理端。不要混用新旧后端:旧版本不会创建持久回调与通知记录。
|
||||
4. 使用测试租户检查保存和恢复两类草稿、任务分配与转交、催办限频、到期提醒、回调 503 后重试。
|
||||
|
||||
新部署的监听器负责此后发生的任务事件;不自动重放过去的任务通知或已完成回调,不自动为已存在且没有再次分配的任务补建超时记录。
|
||||
|
||||
## 已安装五表版本的兼容升级
|
||||
|
||||
仅已经执行过旧五表脚本的环境需要此步骤;新装或尚未安装本批能力的用户只执行三表建表脚本。
|
||||
|
||||
1. 停止旧后端及其他流程写入,完整备份原五张表。恢复备份前也必须停止新后端,不能混跑两版。
|
||||
2. 执行三表建表脚本 `20261014_flow_first_batch.sql`,再执行 `20261014_flow_merge_tables.sql`(使用支持 `DELIMITER` 的 MySQL 客户端)。合并脚本只补字段和索引;数据部分使用事务。发生异常时数据部分回滚,已完成的 DDL 保留,排查后可重新执行。
|
||||
3. 合并脚本保留所有草稿和现有通知的 ID、已读状态;把超时监听关联到已有超时通知,或转换为待触发记录。已提醒的监听不会重新排队;未启用截止时间的旧记录为 `DISABLED`。旧回调的 ID、状态、次数、租约、地址和载荷保持原值,尝试历史按原 ID 合入 JSON 数组。孤立审计记录会报错并回滚,不会静默丢弃。
|
||||
4. 核对下面两个查询结果均为 0,并核对备份中的草稿、通知和回调业务字段。合并脚本可重复执行,不会重复追加历史或重置已处理的提醒。
|
||||
5. 旧 `flow_task_watch` 和 `flow_callback_attempt` 默认保留为只读核对来源,新代码不再访问。确认备份可用、迁移完整后可清理这两张旧表。旧监听中的 `last_urge_at` 仅写不读,原值保存在旧表和备份;催办限频仍以通知记录为准。
|
||||
|
||||
```sql
|
||||
-- 未迁移的监听数
|
||||
SELECT COUNT(*) FROM flow_task_watch w
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM flow_notice n WHERE n.tenant_id=w.tenant_id AND n.reminder_key=w.task_id
|
||||
);
|
||||
-- 未迁移的回调尝试数
|
||||
SELECT COUNT(*) FROM flow_callback_attempt a
|
||||
LEFT JOIN flow_callback c ON c.id=a.callback_id AND c.tenant_id=a.tenant_id
|
||||
WHERE c.id IS NULL OR JSON_SEARCH(COALESCE(c.history,'[]'),'one',a.id,NULL,'$[*].id') IS NULL;
|
||||
```
|
||||
|
||||
## 事务和投递约定
|
||||
|
||||
- 通知与回调写入使用 `JdbcTemplate`,与 Flowable 共用 Spring 事务及数据源;独立监听器 `isFailOnException=true`,写入失败会回滚任务操作,避免任务完成但回调丢失。
|
||||
- 新表 SQL 显式指定租户、用户或引擎事件身份;后台跨租户扫描仅取内部记录,HTTP API 不允许指定他人的用户或租户。
|
||||
- `flow_notice` 同时保存已生成通知和待触发提醒。`status` 区分 `PENDING`、`SENT`、`CANCELLED`、`DISABLED`;通知列表和已读操作只接受 `SENT`。`reminder_key` 按租户和任务唯一,到期才确定当前接收人;任务结束取消尚未发送的提醒,已发送记录保留。任务暂停或暂无处理人时保留 `PENDING`,将 `scheduled_at` 推迟十秒后再检查,避免占满最早一批扫描记录;恢复后继续发送,不重新计算完整超时时长。
|
||||
- 后台默认每轮完成后间隔 10 秒扫描,可配置 `flow.delivery.scan-delay`(毫秒)。回调与提醒使用本模块独立的两线程调度器;每批上限为 20 个回调、100 个提醒,不承诺精确到秒的触发。
|
||||
- HTTP 回调使用任务完成时固定的目标地址、JSON 载荷和事件 ID。只接受 2xx;重定向、其他状态码、网络错误均进入失败处理。请求超时 10 秒,不携带用户 Bearer token。
|
||||
- 首次共尝试 5 次,失败后按 30、60、120、240 秒退避。60 秒租约与条件更新允许多实例竞争;进程崩溃后可回收过期租约继续投递,达到尝试上限的未知结果标为失败。
|
||||
- 请求头 `X-Flow-Event-Id` 和 `Idempotency-Key` 使用同一个稳定事件 ID。语义是**至少一次投递**:接收方必须把事件 ID 去重与自身业务写入放在同一事务中,才能避免“已处理但响应丢失”造成重复副作用。
|
||||
- 手动重试仅允许实例发起人对失败记录操作,保留事件 ID、目标和载荷,再给予 5 次尝试。累计次数不清零。
|
||||
- `flow_callback.history` 以 JSON 数组保存每次尝试和手动重试的审计记录,与主记录状态在同一事务中更新,不截断历史。审计归属由父记录的租户和事件 ID 确定。STARTED 表示尝试已开始但没有确认结果;可能是进程退出或租约失效。页面只展示状态、次数和最近结果,不返回回调载荷和目标 URL。
|
||||
- 本批可靠投递针对节点“事件通知”HTTP 回调;同步触发器业务调用、Redis 流程状态广播维持原有行为。
|
||||
|
||||
## 验证证据
|
||||
|
||||
`FlowWorkspaceTests` 使用真实 Spring Flowable、共享事务的 H2、生产建表语句(H2 MySQL 兼容模式)和本地 HTTP 服务,覆盖草稿持久化、所有者隔离、乐观锁、分配/转交通知、超时去重、催办限频、503 重试、固定事件 ID,以及回调写入失败后引擎任务回滚。H2 验证不等同于真实 MySQL 或完整应用运行验证。
|
||||
|
||||
相关既有节点版本、会签和任务服务测试一并执行。管理端进行指定文件 ESLint、Prettier、Vue SFC 编译,以及已有版本隔离浏览器回归。三表合并额外验证了历史追加、手动重试保留旧历史、已完成任务不再提醒。迁移在本地 OceanBase seekdb 的临时数据库使用带数据样本验证,并检查重复执行和孤立审计回滚;临时库测试后清理。真实账号完整联调尚未执行,不把接口隔离回归当成真实后端验收。
|
||||
@@ -1,27 +0,0 @@
|
||||
-- 第一批持久化能力:三张业务表。已安装旧五表版本时,建表后另执行 20261014_flow_merge_tables.sql。
|
||||
-- 升级前备份并停写,先执行 DDL 再部署应用;不自动迁移运行中的任务。
|
||||
CREATE TABLE IF NOT EXISTS flow_draft (
|
||||
id varchar(36) NOT NULL, tenant_id bigint NOT NULL, user_id bigint NOT NULL,
|
||||
kind varchar(16) NOT NULL, flow_id varchar(64) NOT NULL DEFAULT '', title varchar(200) NOT NULL,
|
||||
payload longtext NOT NULL, revision bigint NOT NULL, updated_at timestamp(3) NOT NULL,
|
||||
PRIMARY KEY(id), KEY idx_draft_owner(tenant_id,user_id,kind,updated_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
CREATE TABLE IF NOT EXISTS flow_notice (
|
||||
id varchar(36) NOT NULL, tenant_id bigint NOT NULL, user_id varchar(64) NOT NULL,
|
||||
event_key varchar(180) NOT NULL, kind varchar(16) NOT NULL, title varchar(200) NOT NULL,
|
||||
task_id varchar(64) NOT NULL, instance_id varchar(64) NOT NULL,
|
||||
created_at timestamp(3) NOT NULL, read_at timestamp(3) NULL,
|
||||
status varchar(16) NOT NULL DEFAULT 'SENT', scheduled_at timestamp(3) NULL, sent_at timestamp(3) NULL,
|
||||
reminder_key varchar(64) NULL,
|
||||
PRIMARY KEY(id), UNIQUE KEY uk_notice_event(tenant_id,user_id,event_key), KEY idx_notice_owner(tenant_id,user_id,created_at),
|
||||
UNIQUE KEY uk_notice_reminder(tenant_id,reminder_key), KEY idx_notice_due(status,scheduled_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
CREATE TABLE IF NOT EXISTS flow_callback (
|
||||
id varchar(36) NOT NULL, tenant_id bigint NOT NULL, task_id varchar(64) NOT NULL,
|
||||
instance_id varchar(64) NOT NULL, target_url varchar(2000) NOT NULL, payload longtext NOT NULL,
|
||||
status varchar(16) NOT NULL, attempts int NOT NULL DEFAULT 0, max_attempts int NOT NULL DEFAULT 5, next_at timestamp(3) NOT NULL,
|
||||
history longtext NULL,
|
||||
lease_token varchar(36) NULL, lease_until timestamp(3) NULL, last_error varchar(500) NULL,
|
||||
created_at timestamp(3) NOT NULL, updated_at timestamp(3) NOT NULL,
|
||||
PRIMARY KEY(id), UNIQUE KEY uk_callback_task(tenant_id,task_id), KEY idx_callback_due(status,next_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -1,100 +0,0 @@
|
||||
-- 仅用于已经安装旧五表版本的库。先备份、停止流程写入及后台扫描,并执行三表建表脚本。
|
||||
-- 可重复执行;旧表暂保留作为核对来源,新代码不再访问。核验并备份后再清理旧表。
|
||||
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 ;
|
||||
Reference in new issue
Block a user