转办、委派、抄送
这篇解决什么
改待办详情底部的转办、委派、抄送,以及任务管理里的「抄送我的」。读完能对上 BpmTaskController 的三个接口、Flowable 的 assignee / owner / DelegationState,以及抄送表 bpm_process_instance_copy。
默认端口 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 |
| 任务实现 | transferTask / delegateTask / copyTask | .../service/task/BpmTaskServiceImpl.java |
| 抄送接口 | @RequestMapping("/bpm/process-instance/copy") | .../controller/admin/task/BpmProcessInstanceCopyController.java |
| 抄送实现 | 写行、分页、标已读 | .../service/task/BpmProcessInstanceCopyServiceImpl.java |
| 抄送表 | bpm_process_instance_copy | .../dal/dataobject/task/BpmProcessInstanceCopyDO.java |
| 抄送节点 | 仿钉钉抄送节点落到同一张表 | .../framework/flowable/core/listener/BpmCopyTaskDelegate.java |
| 参与者鉴权 | 办理人没有菜单也能点这三个按钮 | .../framework/security/BpmProcessParticipantChecker.java |
| PC 任务 API | transferTask / delegateTask / copyTask | ruoyi-office-vben/apps/web-antd/src/api/bpm/task/index.ts |
| PC 抄送 API | getProcessInstanceCopyPage / viewProcessInstanceCopy | .../src/api/bpm/processInstance/index.ts |
| 详情按钮 | 转办 / 委派 / 抄送气泡 | .../src/views/bpm/processInstance/detail/modules/operation-button.vue |
| 抄送我的 | 路由 /bpm/task/copy | .../src/views/bpm/task/copy/index.vue |
官方菜单:1185 流程中心,1200 任务管理,1207 待办,2713 抄送我的。转办 / 委派 / 抄送是 bpm:task:update 或 @processParticipant.hasTask。抄送列表权限 bpm:process-instance-cc:query。
节点按钮号:TRANSFER 3、DELEGATE 4、COPY 7。详情读 runningTask.buttonsSetting;没配时三个都显示。

本系统截图:流程中心 → 任务管理 → 待办任务。行内「办理」进隐藏路由 BpmProcessInstanceTodoDetail。

本系统截图:待办点「办理」后,底栏是通过、拒绝、评论、抄送、转办、委派、加签、退回。
转办和委派
两者都换当前办理人,差在「换完还要不要交回来」。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 转办 | A 交给 B,B 通过后进下一节点 | BpmTaskController#transferTask → TaskService.setAssignee |
| 委派 | A 临时交给 B,B 通过后交回 A,A 再批 | BpmTaskController#delegateTask → TaskService.delegateTask |
| 办理人 | Flowable ACT_RU_TASK.ASSIGNEE_ | #validateTask |
| 原审批人 | OWNER_。第一次转办或委派才写入 | #transferTask / #delegateTask |
| 委派中 | DelegationState.PENDING,不单独写 TASK_STATUS | #approveDelegateTask |
界面文案是「转办」。接口路径 /bpm/task/transfer,评论类型 BpmCommentTypeEnum.TRANSFER 写成「转派」,对的是同一件事。
转办换人,委派要交回
转办只改 assignee,被转办人点通过会 complete。委派会把任务标成 PENDING,被委派人点通过只 resolveTask,节点还在原审批人手里。
Service 层 validateTask 只认当前 assignee。注解放行发起人、owner、抄送人之后,转办 / 委派仍可能抛 TASK_OPERATE_FAIL_ASSIGN_NOT_SELF。
转办
详情点「转办」:必填新审批人 assigneeUserId、意见 reason。不能转给自己,目标用户必须存在。
示意图:和委派的分叉就在最后一步,转办调用 setAssignee。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 转办入参 | id / assigneeUserId / reason 都必填 | .../vo/task/BpmTaskTransferReqVO.java |
| 转给自己 | TASK_TRANSFER_FAIL_USER_REPEAT | ErrorCodeConstants |
| 目标不存在 | TASK_TRANSFER_FAIL_USER_NOT_EXISTS | 同上 |
| 评论 | [{}]将任务转派给[{}],转派理由为:{} | BpmCommentTypeEnum.TRANSFER |
| PC 提交 | handleTransfer | operation-button.vue |
| 运维台 | 管理员列表也能转办 | .../src/views/bpm/ops/task/index.vue |
方法上有 @DataPermission(enable = false),避免查用户被数据权限滤空。
owner 只写第一次
owner 已经有值时不再覆盖。任务被转办或委派过一次后,原审批人一直留在 owner,方便时间线回看。
委派
详情点「委派」:必填接收人 delegateUserId、意见 reason。不能委派给自己。
示意图:owner 必须留下,后面交回靠它。
被委派人在自己的待办里看到这条任务。点「通过」不会结束节点:
示意图:resolveTask 底层把 owner 设回 assignee,原审批人继续批。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 委派入参 | id / delegateUserId / reason 都必填 | .../vo/task/BpmTaskDelegateReqVO.java |
| 委派给自己 | TASK_DELEGATE_FAIL_USER_REPEAT | ErrorCodeConstants |
| 目标不存在 | TASK_DELEGATE_FAIL_USER_NOT_EXISTS | 同上 |
| 发起评论 | [{}]将任务委派给[{}],委派理由为:{} | BpmCommentTypeEnum.DELEGATE_START |
| 交回评论 | [{}]完成委派任务,任务重新回到[{}]手中 | BpmCommentTypeEnum.DELEGATE_END |
| 交回实现 | approveDelegateTask | BpmTaskServiceImpl |
| 批量通过 | 委派中直接判不合格 | BpmTaskBatchEligibilityHelper |
委派中不要走 complete
approveTask 先判断 PENDING。此时只 resolveTask,状态写成 RUNNING,节点不会往下走。批量通过的原因文案是「委派中,不能批量通过」。
委派后再转办:owner 不改,只换 assignee,PENDING 还在。新人点通过仍然交回第一次的 owner,不会进下一节点。
抄送
抄送不改办理人,只给人看。两条入口:详情按钮、模型上的抄送节点。
抄送表
bpm_process_instance_copy 是业务表,不在 Flowable 里。通用字段 creator / create_time / deleted / tenant_id 走 BaseDO。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
user_id | 被抄送人 | BpmProcessInstanceCopyDO |
start_user_id | 流程发起人,冗余实例 | 同上 |
process_instance_id | 流程实例 | 同上 |
process_instance_name | 实例名,列表当单据类型 | 同上 |
process_definition_id | 用来拼摘要 | 同上 |
category | 流程分类 | 同上 |
activity_id | BPMN 节点 id。抄送节点没有 taskId,靠它查表单权限 | 同上 |
activity_name | 节点名,列表「抄送节点」 | 同上 |
task_id | 点按钮抄送时有值;抄送节点是 null | 同上 |
reason | 抄送意见,可空 | 同上 |
read_status | 0 未读,1 已读 | 同上 |
read_time | 第一次打开详情时写入 | 同上 |
activity_id 不是多余列。仿钉钉抄送节点是 ServiceTask,没有用户任务 id,字段权限只能挂在活动编号上。
发起抄送
详情点「抄送」:必填 copyUserIds,意见可空。提交后关当前页。
示意图:按钮和抄送节点最后都 insertBatch 同一张表。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 按钮入参 | id / copyUserIds 必填,reason 可空 | .../vo/task/BpmTaskCopyReqVO.java |
| 按钮实现 | copyTask 只转发,不再 validateTask | BpmTaskServiceImpl#copyTask |
| 抄送节点 | Bean 名 bpmCopyTaskDelegate | BpmCopyTaskDelegate |
| 候选人 | 按节点策略算出 userIds | BpmTaskCandidateInvoker#calculateUsersByTask |
| 节点落库 | taskId = null,意见 null | createProcessInstanceCopy(..., activityId, ...) |
抄送接口靠 @PreAuthorize。发起人、owner、被抄送人过了 hasTask 就能抄,不要求你是当前 assignee。
查看抄送
被抄送人到任务管理 → 抄送我的。分页按当前登录人的 user_id 过滤,按 id 倒序。

本系统截图:任务管理 → 抄送我的。列是单据类型、单号、摘要、发起人、公司部门、抄送节点。点「详情」带 viewType=copy。
| 名称 | 说明 | 仓库路径 |
|---|---|---|
| 抄送分页 | GET /bpm/process-instance/copy/page | BpmProcessInstanceCopyController#getProcessInstanceCopyPage |
| 查询条件 | 流程名、抄送时间、已读状态 | .../vo/instance/BpmProcessInstanceCopyPageReqVO.java |
| 列表行 | 发起人、摘要、单号、已读 | .../vo/cc/BpmProcessInstanceCopyRespVO.java |
| 标已读 | PUT /bpm/process-instance/copy/view | #viewProcessInstanceCopy |
| 只读详情 | viewType=copy,底部只留关闭 | processInstance/detail/index.vue |
打开详情会带 copyId、activityId、copyReason。view 只把当前人、未读的行标成已读,并写 read_time。
抄送人也算流程参与者:BpmProcessParticipantChecker#hasProcess 会查这张表。被抄送人没有「任务更新」菜单,仍能进详情。
配置与操作
管理员在节点打开转办 / 委派 / 抄送按钮,或放一个抄送节点。办理人在待办详情选人;被抄送人到 抄送我的 看只读详情。
转办改 assignee;委派先给别人、别人办完再回到 owner。抄送只写 bpm_process_instance_copy,不推进节点。
开启见 流程功能设计。截图见上文待办和抄送列表。
