diff --git a/CONTEXT.md b/CONTEXT.md deleted file mode 100644 index 28364abfe..000000000 --- a/CONTEXT.md +++ /dev/null @@ -1,26 +0,0 @@ -# PIGX 后端 - -PIGX 后端承载平台业务规则与运行状态。本上下文中的工作流语言用于描述审批任务及其参与者关系。 - -## 工作流审批 - -**会签**: -同一审批节点为多名审批人分别生成独立待办,并以全部审批人同意作为节点通过条件。 - -**动态加签**: -在会签节点运行期间追加一名或多名独立审批人;新增审批人与原审批人共同遵守该节点的会签完成条件。 -_避免使用_:委托、转办 - -**原始审批人**: -会签节点进入运行状态时,根据流程定义确定并获得独立待办的审批人。 - -**加签审批人**: -会签节点运行期间通过动态加签加入并获得独立待办的审批人;不属于该节点的原始审批人。 - -**委托**: -当前审批人临时将自己的待办交给另一人处理,处理结果随后返回原审批人;委托不增加会签人数。 -_避免使用_:加签 - -**转办**: -当前审批人将自己的待办及处理权永久移交给另一人,原审批人不再处理该待办。 -_避免使用_:加签、委托 diff --git a/db/upgrade/20260716_dynamic_add_sign.sql b/db/upgrade/20260716_dynamic_add_sign.sql deleted file mode 100644 index e79a4594b..000000000 --- a/db/upgrade/20260716_dynamic_add_sign.sql +++ /dev/null @@ -1,30 +0,0 @@ --- 动态会签加签:存量数据库一次性升级脚本 - -ALTER TABLE `process_node_record_assign_user` - ADD COLUMN `node_record_id` bigint DEFAULT NULL COMMENT '本轮节点执行记录id' AFTER `local_data`, - ADD COLUMN `assignment_origin` varchar(20) NOT NULL DEFAULT 'ORIGINAL' COMMENT '分配来源 ORIGINAL/ADD_SIGN' AFTER `node_record_id`, - ADD COLUMN `initial_user_id` bigint DEFAULT NULL COMMENT '本份待办最初分配用户id' AFTER `assignment_origin`, - ADD COLUMN `add_sign_record_id` bigint DEFAULT NULL COMMENT '动态加签批次记录id' AFTER `initial_user_id`, - ADD UNIQUE KEY `uk_node_record_user` (`tenant_id`, `node_record_id`, `user_id`), - ADD KEY `idx_add_sign_record_id` (`add_sign_record_id`); - -CREATE TABLE `process_task_add_sign_record` ( - `id` bigint NOT NULL COMMENT '主键id', - `del_flag` char(1) DEFAULT '0' COMMENT '删除标志', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `flow_id` varchar(255) NOT NULL COMMENT '流程id', - `process_instance_id` varchar(50) NOT NULL COMMENT '流程实例id', - `node_record_id` bigint NOT NULL COMMENT '本轮节点执行记录id', - `node_id` varchar(50) NOT NULL COMMENT '节点id', - `source_task_id` varchar(255) NOT NULL COMMENT '发起加签的任务id', - `added_by_user_id` bigint NOT NULL COMMENT '加签发起人id', - `reason` varchar(100) NOT NULL COMMENT '加签原因', - `target_user_ids` longtext NOT NULL COMMENT '本批次目标用户快照', - `participant_user_ids_before` longtext NOT NULL COMMENT '操作前参与人快照', - `participant_user_ids_after` longtext DEFAULT NULL COMMENT '操作后参与人快照', - `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '所属租户id', - PRIMARY KEY (`id`), - KEY `idx_add_sign_node_record` (`tenant_id`, `node_record_id`), - KEY `idx_add_sign_process_instance` (`tenant_id`, `process_instance_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='流程动态加签批次记录'; diff --git a/docs/adr/0001-model-add-sign-as-dynamic-countersign-append.md b/docs/adr/0001-model-add-sign-as-dynamic-countersign-append.md deleted file mode 100644 index 15626f380..000000000 --- a/docs/adr/0001-model-add-sign-as-dynamic-countersign-append.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -status: accepted ---- - -# 将加签建模为动态追加会签人 - -工作流中的“加签”采用动态追加会签人的语义:在当前会签节点仍处于运行状态时,为新增审批人创建独立待办,并将其纳入同一节点的会签完成条件。只有仍持有未完成待办的原始审批人可以发起动态加签;加签审批人、已经完成审批的原始审批人、流程发起人和普通管理员均不能发起。加签成功后,发起人的原待办保持待审批,且不需要等待新增审批人;原审批人与新增审批人的待办并行处理,加签操作本身不代表审批同意。同一流程实例的同一会签节点内,审批人按用户唯一,每个用户最多拥有一份待办;该约束由后端保证,前端同时过滤已有审批人。首版只允许并行会签模式使用动态加签;或签、依次审批和单人审批不提供加签能力。动态加签由流程设计者在每个并行会签节点上显式开启,默认关闭;配置直接保存在现有节点数据中,以简单实现优先,不提供发布版本隔离。一次加签可以选择多名审批人,但必须整批通过校验并原子提交;任何目标不合法时不创建任何新增待办,一次提交只形成一条包含全部新增人员的加签记录。同一原始审批人在完成自己的待办前可以多次发起加签,每次分别校验并记录;其待办完成后立即失去加签权限。首版不提供撤销加签或减签:新增待办一旦创建便作为正式会签待办参与完成条件。加签审批人可以转办自己的待办,但不能发起加签;转办目标不能是当前节点已有审批人,转办仅替换该待办的处理人,不改变会签任务总数。发起加签必须填写 2~100 字的原因;系统记录发起人、新增审批人、原因、时间、流程实例、节点、发起待办及变更前后的会签人员,并在审批记录中展示。桌面端与移动端在同一版本提供一致的加签入口和规则,流程设计配置仍由桌面端承担。现有基于任务委托的 `delegateTask` 保留为“委托”,不作为加签实现,以免委托返回原审批人的生命周期与会签人数变化混为一谈。 diff --git a/docs/adr/0002-use-flowable-dynamic-multi-instance-for-add-sign.md b/docs/adr/0002-use-flowable-dynamic-multi-instance-for-add-sign.md deleted file mode 100644 index 1703b29e7..000000000 --- a/docs/adr/0002-use-flowable-dynamic-multi-instance-for-add-sign.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -status: accepted ---- - -# 使用 Flowable 动态多实例实现加签 - -动态加签使用 Flowable 原生 `RuntimeService.addMultiInstanceExecution` 向运行中的会签活动添加执行实例,并由 Flowable 作为任务生命周期、实例数量和节点完成状态的唯一事实来源。PIGX 业务表只保存审批人映射、查询投影和加签审计,不创建一套独立的附加审批任务,以避免引擎状态与业务状态发生双写分歧。加签与节点完成发生竞争时,以先提交成功的事务为准:加签先成功则节点等待新增实例,节点先完成则加签失败;系统绝不重新打开已经完成的节点。 diff --git a/docs/design/workflow-dynamic-add-sign.md b/docs/design/workflow-dynamic-add-sign.md deleted file mode 100644 index 39408d257..000000000 --- a/docs/design/workflow-dynamic-add-sign.md +++ /dev/null @@ -1,229 +0,0 @@ -# 工作流动态加签设计 - -> 状态:已实现(2026-07-16) - -## 目标 - -为运行中的并行会签节点动态追加一名或多名独立审批人。新增审批人与原审批人并行处理,并进入同一会签完成条件。 - -首版不实现前加签、后加签、撤销加签或减签;现有 `delegateTask` 继续表示委托,不复用为加签。 - -## 业务规则 - -1. 仅配置为并行会签且开启“允许审批人加签”的节点可用。 -2. 只有仍持有未完成待办的原始审批人可以发起;加签审批人、转办接手人、已完成审批人、流程发起人和普通管理员均不可以发起。 -3. 加签不完成或暂停发起人的待办;所有待办继续并行处理。 -4. 同一轮节点执行中,每个用户最多参与一次。已有原始审批人、已有加签审批人及曾经接手过该轮任务的用户均不可再次加入。 -5. 一次可以选择多人,整批校验、整批成功或整批失败。 -6. 原始审批人在自己的待办完成前可以多次加签。 -7. 加签原因必填,长度为 2~100 字。 -8. 加签审批人可以转办,但不能继续加签;转办不改变会签实例总数。 -9. 任意新增审批人拒绝时,继续执行当前会签的拒绝规则;全部参与人同意后节点才通过。 -10. 加签与节点完成竞争时,以先提交成功的事务为准,不重新打开已完成节点。 - -## 配置模型 - -在 `Node` 增加: - -```java -private Boolean allowAddSign; -``` - -- 设计器仅在多人审批模式为会签时显示开关。 -- 新节点默认 `false`;字段为空时后端也按 `false` 处理。 -- 开关直接保存在现有 `process_node_data.data` JSON 中。 -- 为保持实现简单,不提供发布版本隔离;修改节点配置可能影响正在运行的实例。 - -## 接口设计 - -### 查询任务详情 - -扩展现有 `GET /task/task/queryTask` 响应: - -```json -{ - "canAddSign": true, - "participantUserIds": [1, 2, 3] -} -``` - -`canAddSign` 必须由后端根据当前登录人、活动任务、原始分配关系、节点模式和开关综合计算,前端不能自行推导权限。 - -### 发起加签 - -新增 `POST /task/task/addSign`: - -```json -{ - "taskId": "12501", - "targetUserIdList": [31, 32], - "reason": "需要法务和财务共同确认" -} -``` - -客户端不提交操作人、流程实例或节点 ID;这些值由后端根据登录人和 `taskId` 获取,避免越权和参数篡改。 - -成功返回本次加签记录 ID 和新增任务摘要;业务校验失败时整批不产生任务。 - -## 后端处理流程 - -```mermaid -flowchart TD - A["接收 taskId、目标用户、原因"] --> B["查询当前登录人的活动任务"] - B --> C["锁定当前 ProcessNodeRecord"] - C --> D["校验原始审批人、会签模式和允许加签开关"] - D --> E["批量校验租户用户、输入重复和节点参与历史"] - E --> F["创建一条批次加签审计记录"] - F --> G["逐人调用 addMultiInstanceExecution"] - G --> H["任务监听器写入执行人记录"] - H --> I["保存变更后参与人快照并提交事务"] -``` - -核心调用使用 Flowable 8 的: - -```java -runtimeService.addMultiInstanceExecution(nodeId, parentExecutionId, executionVariables); -``` - -处理要点: - -- 通过当前任务 `executionId` 找到并行多实例父执行实例。 -- `executionVariables` 写入模型已有的 `${nodeId}_assignee_temp` 执行人变量。 -- 同时写入 `assignmentOrigin=ADD_SIGN`、`initialUserId`、`addSignRecordId` 和 `addedByUserId` 等局部元数据。 -- Flowable 负责增加 `nrOfInstances`、创建任务和计算节点完成状态。 -- 不手工修改 Flowable 运行表,不在业务表中另造可完成的审批任务。 - -## 数据记录 - -### 执行人记录扩展 - -在 `process_node_record_assign_user` 增加: - -- `node_record_id`:本轮节点执行记录 ID,用于区分退回后再次进入同一节点。 -- `assignment_origin`:`ORIGINAL` 或 `ADD_SIGN`。 -- `initial_user_id`:本份待办最初分配的用户;转办后保持不变。 -- `add_sign_record_id`:加签产生的任务关联到对应批次记录,原始任务为空。 - -为新记录建立 `(tenant_id, node_record_id, user_id)` 唯一约束,配合节点行锁保证同一轮节点内人员唯一。历史兼容和数据回填不在本次范围内。 - -权限判断同时要求: - -- `assignment_origin = ORIGINAL`; -- 当前登录人等于 `initial_user_id`; -- 当前登录人仍是任务现任 `assignee`; -- 任务和节点都处于活动状态。 - -因此原始任务转办给其他用户后,接手人不能发起加签。 - -### 加签审计记录 - -新增 `process_task_add_sign_record`,一批操作一条记录,保存: - -- 租户、流程、流程实例、本轮节点记录、节点和来源任务; -- 发起人、加签原因、操作时间; -- 本次目标用户快照; -- 操作前和操作后的参与人快照。 - -只记录事务成功的加签;失败请求记录应用日志,不生成半成品审计数据。 - -## 并发与事务 - -- `addSign` 与会签任务 `completeTask` 在操作同一节点前,锁定相同的活动 `process_node_record` 行。 -- 所有目标用户先完成校验,再创建任何多实例执行。 -- 批次记录、Flowable 多实例、任务监听记录处于同一事务;任一步失败全部回滚。 -- 两个审批人同时加签同一用户时,一个成功,另一个在获得锁后因重复参与失败。 -- 节点先完成时返回“当前审批节点已结束,无法加签”;不做节点重开或补偿任务。 -- Flowable 乐观锁异常转换为明确的并发操作提示,不向前端暴露引擎异常文本。 - -## 转办约束 - -现有转办接口应收紧为单目标用户,并增加当前节点参与人校验: - -- 转办目标不得在本轮节点参与历史中出现。 -- 转办沿用原任务的 `assignment_origin`、`initial_user_id` 和 `add_sign_record_id`。 -- 加签任务转办后仍是加签任务;原始任务转办后,接手人不获得加签权限。 - -## 前端设计 - -### 流程设计器 - -在审批人抽屉的“多人审批方式”下增加“允许审批人加签”开关。切换到或签、依次审批或单人审批时隐藏并重置为 `false`。 - -### 桌面待办 - -- 在“提交、拒绝、转办”操作区增加“加签”。 -- 仅当后端返回 `canAddSign=true` 时显示。 -- 弹窗复用组织选择器,支持多选,过滤 `participantUserIds`。 -- 原因输入 2~100 字;提交期间禁用重复点击。 -- 成功后关闭弹窗、刷新任务详情与流程进度。 - -### 移动端待办 - -移动端使用相同接口和规则,增加“加签”入口及适配小屏的底部弹层;成功后刷新当前任务,不改变原待办的可审批状态。 - -## 审批记录展示 - -在流程进度时间线中增加独立事件: - -> 张三加签李四、王五:需要法务和财务共同确认 - -新增审批人的任务处理记录继续按普通会签任务展示,并标注“加签”。转办时继续展示完整的处理人变化轨迹。 - -## 错误提示 - -- 当前节点未开启加签 -- 仅并行会签节点支持加签 -- 当前任务已完成或已转办 -- 只有未完成审批的原始审批人可以加签 -- 请选择加签人员 -- 加签原因需为 2~100 字 -- 用户已参与当前会签:{姓名} -- 当前审批节点已结束,无法加签 -- 节点状态已变化,请刷新后重试 - -## 验收矩阵 - -### 后端 - -1. A、B 会签,A 加签 C 后产生三份并行待办,A 的原待办保持活动。 -2. A、B、C 全部同意后节点通过;C 拒绝时执行当前拒绝策略。 -3. A 一次加签 C、D,任一目标不合法时 C、D 都不产生任务。 -4. A 可以在完成前多次加签,完成后再次请求被拒绝。 -5. C 不能继续加签;C 可转办给未参与的 D,实例总数不变。 -6. 原始任务 A 转办给 D 后,D 不能加签。 -7. 重复用户、跨租户用户、无效用户、空原因和超长原因均被拒绝。 -8. 或签、依次审批、单人审批及未开启开关的节点均被拒绝。 -9. 并发加签同一用户只允许一个请求成功。 -10. 加签与最后审批并发时符合先提交者生效规则,不出现孤儿任务或节点重开。 -11. 审计记录、执行人记录与 Flowable 活动任务一致。 - -### 前端 - -1. 设计器开关仅在会签配置下可见并正确保存。 -2. 桌面端和移动端均按 `canAddSign` 控制入口。 -3. 已参与人员不可选择,批量选择和原因校验一致。 -4. 加签成功后流程进度、待办列表和审批记录即时刷新。 -5. 后端拒绝并发或越权请求时,页面保留输入并显示明确原因。 - -## 主要改动位置 - -后端集中在: - -- `pigx-flow-api/.../dto/Node.java` -- `pigx-flow-api/.../entity/ProcessNodeRecordAssignUser.java` -- `pigx-flow-biz/.../controller/ProcessTaskController.java` -- `pigx-flow-biz/.../service/ITaskService.java` -- `pigx-flow-biz/.../service/impl/TaskServiceImpl.java` -- `pigx-flow-biz/.../service/impl/FlowableEngineServiceImpl.java` -- `pigx-flow-biz/.../support/listeners/FlowProcessEventListener.java` -- `db/3pigxx_flow.sql` 与 `db/0pigxx_boot.sql` -- `db/upgrade/20260716_dynamic_add_sign.sql`(存量数据库升级) - -前端集中在: - -- `src/views/flow/workflow/components/drawer/approverDrawer.vue` -- `src/views/flow/workflow/components/addNode.vue` -- `src/api/flow/task/index.ts` -- `src/views/flow/task/pending.vue` -- `src/views/flow/task/handler/` -- `src/views/flow/mobile/task.vue`