审批通过、不通过、驳回
这篇解决什么
改待办、已办、流程任务三张列表,以及详情里的通过、不通过、退回。读完能对上 BpmTaskController 的接口、任务状态怎么落本地变量,以及业务表单在「拒绝」时为什么不能再校验可编辑字段。
默认端口 48080,管理端前缀 /admin-api。PC 开发页 http://127.0.0.1:5800。办理入口在流程中心 → 任务管理。
示意图:列表和详情都打 /bpm/task/*,状态写在任务本地变量,推进节点交给 Flowable。
从哪改
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 任务接口 | @RequestMapping("/bpm/task") | ruoyi-office/yudao-module-bpm/yudao-module-bpm-server/.../controller/admin/task/BpmTaskController.java |
| 任务实现 | 通过 / 不通过 / 退回 / 批量通过 | .../service/task/BpmTaskServiceImpl.java |
| 任务状态 | BpmTaskStatusEnum | ruoyi-office/yudao-module-bpm/yudao-module-bpm-api/.../enums/task/BpmTaskStatusEnum.java |
| 状态变量名 | 本地变量 TASK_STATUS | .../framework/flowable/core/enums/BpmnVariableConstants.java |
| 参与者鉴权 | 办理人没有菜单权限也能批 | .../framework/security/BpmProcessParticipantChecker.java |
| PC API | approveTask / rejectTask / returnTask | ruoyi-office-vben/apps/web-antd/src/api/bpm/task/index.ts |
| 待办页 | 路由 /bpm/task/todo | .../src/views/bpm/task/todo/index.vue |
| 已办页 | 路由 /bpm/task/done | .../src/views/bpm/task/done/index.vue |
| 流程任务 | 路由 /bpm/manager/process-tasnk | .../src/views/bpm/task/manager/index.vue |
| 详情按钮 | 通过 / 不通过 / 退回 | .../src/views/bpm/processInstance/detail/modules/operation-button.vue |
官方菜单:1185 流程中心,1200 任务管理,1207 待办,1208 已办,1186 流程设置,2724 流程任务。按钮权限 bpm:task:query、bpm:task:update。
待办、已办的分页接口没有再标 @PreAuthorize,登录后只查当前人。通过 / 不通过 / 退回是 bpm:task:update 或 @processParticipant.hasTask:办理人、任务拥有人、发起人、抄送人都算参与者。
兼职没有「任务更新」菜单也能批
BpmProcessParticipantChecker 就是为多组织兼职准备的。待办仍按 assignee 分给你,不要只靠角色菜单卡死办理接口。
待办任务
只列出指派给我、且仍在运行的任务。点「办理」进隐藏路由 BpmProcessInstanceTodoDetail,带上 processInstanceId、taskId、nodeKey。

本系统截图:流程中心 → 任务管理 → 待办任务。勾选列只对允许批量通过的行生效,工具栏是「批量通过」。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 待办分页 | GET /bpm/task/todo-page,taskAssignee + active() | BpmTaskController#getTaskTodoPage |
| 查询条件 | 分类、单据类型(流程 key)、单号、单据日期 | .../vo/task/BpmTaskPageReqVO.java |
| 批量通过 | PUT /bpm/task/batch-approve | BpmTaskController#batchApproveTask |
| 批量资格 | 行上 batchEligible / batchIneligibleReason | BpmTaskServiceImpl#fillTodoBatchEligibility |
运行中任务在 Flowable 的 ACT_RU_TASK。引擎表没有「通过 / 不通过」列,扩展字段走 ACT_RU_VARIABLE。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
ACT_RU_TASK | 运行中任务。ASSIGNEE_ 是办理人,PROC_INST_ID_ 挂实例 | Flowable 运行表 |
ACT_RU_VARIABLE | 任务/实例变量。状态写在任务本地变量 | 同上 |
TASK_STATUS | setVariableLocal,常量 TASK_VARIABLE_STATUS | BpmnVariableConstants |
TASK_REASON | 通过、不通过、退回意见 | 同上 TASK_VARIABLE_REASON |
任务完成后,运行表里的行会删掉,只能到历史表查。这是冷热分离,不是丢了。
任务状态
BpmTaskStatusEnum 写在 api 模块,列表和详情都读这个整数。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
WAIT 0 | 待审批。前加签出来的子任务会先停在这 | BpmTaskStatusEnum |
RUNNING 1 | 审批中 | 同上 |
APPROVE 2 | 审批通过 | 同上 |
REJECT 3 | 审批不通过 | 同上 |
CANCEL 4 | 已取消。或签里别人先批、或退回时别人的任务 | 同上 |
RETURN 5 | 已退回 | 同上 |
APPROVING 7 | 审批通过中。后加签未收齐 | 同上 |
WITHDRAW 10 | 已撤回 | 同上 |
SKIP -2 | 跳过 | 同上 |
NOT_START -1 | 未提交 | 同上 |
isEndStatus 把通过、不通过、取消、退回、通过中、撤回都当成终态,避免重复改状态。
审批通过
详情点「通过」走 PUT /bpm/task/approve。入参 BpmTaskApproveReqVO:id 必填;reason、signPicUrl、attachments、variables、nextAssignees 按节点配置。
示意图:三种形态里,日常单据走普通任务:先落状态和意见,再 complete。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 通过入参 | id / reason / signPicUrl / variables / nextAssignees | .../vo/task/BpmTaskApproveReqVO.java |
| 节点签字 | BPMN 开了签字且没图 → TASK_SIGNATURE_NOT_EXISTS | BpmTaskServiceImpl#approveTask |
| 意见必填 | 节点 reasonRequire 且意见空 → TASK_REASON_REQUIRE | 同上 |
| 流程变量 | 先拷实例变量,再覆盖前端传来的字段 | 同上 |
| 下一审批人 | 策略是「发起人自选」时校验 nextAssignees | #validateAndSetNextAssignees |
方法上有 @DataPermission(enable = false),避免查办理人被数据权限滤空。通过后发 TASK_APPROVED 通知。
当前节点没有可编辑字段时 variables 会是空的
approveTask 会先把实例里已有变量装进 map,再让前端覆盖。下一节点要靠 work、day 这类变量分流时,不能只信本次表单提交。
PC 打开「通过」气泡前会先校验流程表单(formType === NORMAL)。业务表单走 beforeApproval,见下节。
审批不通过
详情点「不通过」走 PUT /bpm/task/reject。入参 BpmTaskRejectReqVO:id、reason 都必填,可选附件。
默认策略是整单结束,不是只否决当前节点。或签里一个人点不通过,实例也会变成不通过。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 不通过入参 | id + reason(@NotEmpty) | .../vo/task/BpmTaskRejectReqVO.java |
| 拒绝策略 | FINISH_PROCESS_INSTANCE 1 终止;RETURN_USER_TASK 2 驳到指定节点 | BpmUserTaskRejectHandlerTypeEnum |
| 终止 | updateProcessInstanceReject + moveTaskToEnd | BpmTaskServiceImpl#rejectTask |
| 加签根任务 | 子任务不通过时,根任务也标 REJECT | 同上 |
节点配了「驳回到指定任务」时,rejectTask 内部转调 returnTask。退回到发起人节点还会把实例状态写成 BpmProcessInstanceStatusEnum.REJECT(3),并推流程状态通知。
不通过默认结束整单
不要按「或签否一票、别人还能继续批」去改业务回写。要驳到某一级,用退回按钮,或在模型里把该节点的拒绝策略改成驳回指定节点。
驳回
界面文案是「退回」。先拉可退回节点,再提交目标节点和意见。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 可选节点 | GET /bpm/task/list-by-return?id= | BpmTaskController#getTaskListByReturn |
| 发起退回 | PUT /bpm/task/return | #returnTask |
| 退回入参 | id / targetTaskDefinitionKey / reason | .../vo/task/BpmTaskReturnReqVO.java |
getUserTaskListByReturn 只留同时满足这两条的用户任务:
- 从当前节点串行可到达(并行支路、子流程退不回去)。
- 该节点在本实例里已经办过(没走过的节点不会出现在下拉里)。
真正搬家用 Flowable 的 moveActivityIdsToSingleActivityId。当前办理人的任务标 RETURN,同一波被撤掉的别人的任务标 CANCEL。
退回前会写实例级 RETURN_FLAG。目标节点若办理人等于发起人,不会被「发起人自动通过」策略吃掉。退回到发起人节点时,实例状态同样写成不通过。
挂起中的任务会抛 TASK_IS_PENDING,先恢复再退。
已办任务
已办查的是我经办且已结束的历史任务,按结束时间倒序。运行表里已经没有这些行。

本系统截图:任务管理 → 已办任务。行内「取回」把下一节点拉回来;「历史」进详情。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 已办分页 | GET /bpm/task/done-page,finished() + taskAssignee | BpmTaskController#getTaskDonePage |
ACT_HI_TASKINST | 历史任务。END_TIME_、DURATION_、DELETE_REASON_ | Flowable 历史表 |
ACT_HI_VARINST | 历史变量,含当时的 TASK_STATUS | 同上 |
| 取回 | 流程定义允许、下一节点尚未办完 | #getWithdrawStatus / #withdrawTask |
已办分页会批量算 withdrawable。只有状态是通过、实例未结束、定义开了 allowWithdrawTask,并且下一用户任务还没被别人办完,才显示可取回。
流程任务
给管理员看全站任务,读历史表,不限当前办理人。菜单 path 是 process-tasnk(少了一个 k),前端路由必须跟着它,不要自行改成 process-task。

本系统截图:流程设置 → 流程任务。列是流程、发起人、当前任务、起止时间、审批人。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 管理分页 | GET /bpm/task/manager-page | BpmTaskController#getTaskManagerPage |
| 页面权限 | Controller 写 bpm:task:manager-query | 同上 @PreAuthorize |
| 菜单权限 | 按钮菜单 2725 写成了 bpm:task:mananger-query | system_menu.id = 2725 |
管理查询权限少了一个 a
超管不受影响。给业务角色开「流程任务」时,菜单标识和 @PreAuthorize 对不上会 403。二开时改菜单或改注解,只留一种拼写。
拒绝时不要校验业务字段
通过才需要把节点上可编辑的业务字段存回去。拒绝、退回只要意见,不应被「归还时间未填」挡住。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| PC 详情 | 只有 pass === true 才调 beforeApproval | operation-button.vue 的 handleAudit |
| 用印 PC | beforeApproval 里 validateForm + saveSealApplyBill | ruoyi-office-vben/.../views/oa/seal/sealapply/info/index.vue |
| 用车 PC | 审批页没有 beforeApproval | .../views/oa/car/carapply/info/index.vue |
| 移动端 | beforeApproval(operationType),2 是拒绝 | BpmTaskOperationButtonTypeEnum.REJECT |
| 用印 App | operationType === 2 直接 return true | ruoyi-office-uniapp/src/pages-oa/seal/detail/index.vue |
PC 的流程表单(NORMAL)在打开「通过」气泡时就会 validate。不通过气泡只校验拒绝意见,不再走业务 beforeApproval。
新单据如果节点上有可编辑字段,按用印这样做:
- PC:
defineExpose({ beforeApproval }),只在通过时被详情调用。 - App:
beforeApproval(operationType?),拒绝(2)跳过validate/ 保存。
不要在 beforeApproval 里无条件 validate
用印归还节点要填实际归还时间。若拒绝也走同一段校验,审批人无法退单。App 用 operationType === 2 短路;PC 则根本不调用。
配置与操作
办理人在 待办任务 点办理,详情底栏选通过 / 不通过 / 退回。通过才跑业务 beforeApproval;拒绝、退回只校验意见。管理员在 流程任务 看全站任务,菜单 path 必须跟 process-tasnk。
节点上的签字、意见必填、拒绝策略在模型里配,发布后新到达的任务才生效。批量通过只对行上 batchEligible 的待办有效。
开启见 流程功能设计。截图见上文待办、已办、流程任务。
