工作流里的驳回、退回、撤回、撤销和终止:五种操作不能混为一谈
一句话先说结论:驳回是当前审批人给出否定结论,退回是让已办环节重新办理,撤回是提交人及时收回,撤销是让已经推进甚至生效的结果失效,终止是强制结束流程实例。
五个按钮表面上都可能让流程“不再向前走”,但它们改变的是不同对象:驳回改变审批结论,退回改变办理位置,撤回改变提交状态,撤销改变业务效力,终止改变实例生命周期。若把它们都实现成“删除当前任务并跳到某节点”,权限、历史、并行分支和业务数据迟早会失真。
一、先用一张表看清五种操作
| 操作 | 典型发起人 | 常见时机 | 目标状态 | 原流程是否继续 | 是否常需业务补偿 |
|---|---|---|---|---|---|
| 驳回 | 当前审批人 | 当前任务办理中 | 拒绝路径或发起人修改 | 取决于模型 | 视副作用而定 |
| 退回 | 当前办理人 | 流程运行中 | 某个已办节点重新办理 | 是 | 有时需要 |
| 撤回 | 发起人 | 提交后、下游尚未实质办理 | 草稿或待提交 | 可重新提交 | 通常不需要 |
| 撤销 | 业务负责人或授权管理员 | 流程已推进或结果已生效 | 业务结果失效或进入冲销流程 | 原正向流程通常不再继续 | 通常需要 |
| 终止 | 管理员、系统或模型事件 | 流程仍在运行 | 已终止 | 否 | 引擎不会自动补偿 |
判断一个按钮到底属于哪一种,不要只看名字,至少要问五个问题:
- 谁能发起;
- 流程走到了什么阶段;
- 操作后回到哪里;
- 原流程是否还会继续;
- 已经发生的业务副作用由谁撤销。

图 1:撤回通常发生在提交之后、实质办理之前;驳回与退回发生在审批中;撤销面向已经推进或生效的结果;终止直接结束仍在运行的实例。
二、驳回:审批结论为否,不等于随意往回跳
驳回首先是一项业务决定。当前审批人认为申请不符合条件,提交“不同意”以及原因,流程再按照预先定义的拒绝路径运行。
常见的驳回结果有三种:
- 直接进入“审批未通过”结束事件;
- 回到发起人修改,允许再次提交;
- 进入补充材料、风险复核等专门分支。
更稳妥的实现是:完成当前用户任务,写入结构化结论,例如 approved=false、decision=REJECT,再由 BPMN 排他网关选择后续路径。这样模型图、运行历史和业务含义保持一致。
驳回不能天然等同于“退回上一步”。否定结论是业务事实;回到哪里是路由策略。某些流程在驳回后结束,另一些流程允许修改重提,两者都合理,但必须在模型中明确。
会签中的驳回还要定义汇总口径:一票否决、拒绝人数达到阈值、负责人终审,还是所有人完成后再汇总。单个审批人的“拒绝”不一定立即终止整个会签节点。
三、退回:回到已办节点重办,重点是恢复执行上下文
退回的核心是返工,不一定代表最终否决。例如财务发现合同附件缺失,可以把任务退回经办人补充;经办人补齐后,流程仍沿原审批链继续。
退回必须回答“退到谁、退到哪一个实际活动实例”。静态流程图上的上一个节点,不一定是本次运行真正经过的节点:
- 排他网关可能选择了另一条路径;
- 并行网关可能同时存在多个令牌;
- 会签节点可能有多条未完成任务;
- 子流程和调用活动可能跨越不同作用域;
- 循环审批可能多次经过同一个 activityId。
因此,退回目标应优先来自真实历史轨迹,并同时保存目标 activityId、历史活动实例、原办理人或重新计算后的候选人。只按 BPMN XML 查 incoming sequenceFlow,无法可靠处理复杂运行实例。
退回后还要定义重办边界:
- 中间已经完成的任务标记为失效、撤销还是保留;
- 原审批意见是否继续可见;
- 表单字段恢复到哪个版本;
- 已发送的消息、已生成的单号是否需要补偿;
- 重办完成后从原路线继续,还是重新进行人员解析。
四、撤回:提交人把流程及时收回
撤回的典型操作者是发起人,语义是“我刚提交,想收回来修改”。它通常发生在下游尚未实质办理、也没有不可逆业务副作用时。
常见撤回条件包括:
- 第一审批人尚未完成任务;
- 当前任务无人签收,或者产品允许签收后但办理前撤回;
- 流程尚未进入会签、子流程或外部系统;
- 没有付款、出库、盖章、发布等不可逆动作;
- 发起人仍有该业务单据的编辑权限。
撤回后通常回到草稿,而不是生成一条“拒绝”记录。历史中应保留“已提交 → 已撤回 → 草稿”的完整轨迹,并允许用户修改后再次提交。
不要通过删除历史数据制造“从未提交过”的假象。撤回本身也是需要审计的行为,尤其是采购、用印、合同和财务流程。
如果审批人已经完成任务,或者审批结果已经对外生效,再由发起人点击“撤回”,名称就容易误导。此时更接近撤销,需要权限升级和业务补偿。
五、撤销:让已经推进或生效的业务结果失效
撤销面向已经产生效力的结果。例如采购审批通过后取消采购单,合同发布后作废,付款申请审批后发起冲销。它不是简单回到某个旧节点,而是承认原过程真实发生过,再用新的操作使其失效。
撤销通常需要:
- 更高权限或双人复核;
- 必填撤销原因和附件;
- 判断单据是否已经被下游引用;
- 执行库存、财务、合同、消息等补偿动作;
- 通知原审批参与人和业务接收方;
- 保存原结果与撤销结果之间的关联。
BPMN 的补偿事件适合表达“撤销已经成功完成、但现在不再需要的步骤”。补偿处理器负责执行反向业务动作,例如释放库存、撤销预占、冲销凭证。补偿不是数据库事务回滚:外部系统动作通常已经提交,只能通过新的反向指令恢复业务一致性。
复杂企业系统更适合为撤销建立独立的“撤销申请流程”。它可以再次审批,按顺序执行补偿,并保留原流程实例、撤销流程实例和业务单据之间的引用关系。
六、终止:强制结束实例,不承诺恢复业务现场
终止的目标是让运行中的流程实例立即结束,不再产生正常后续任务。它常用于严重异常、重复发起、合规叫停或管理员处置。
终止可以有两种来源:
- 模型内终止:流程到达 BPMN Terminate End Event,结束当前作用域;Flowable 还提供 terminateAll 扩展,可结束根流程实例;
- 模型外终止:管理员或业务服务调用引擎取消、删除运行实例的 API,并写入终止原因。
无论哪种来源,终止都不等于撤销。引擎可以清除待办、执行实例、定时器和订阅,但不会自动取消已经发送的邮件、撤回已支付款项或恢复库存。外部副作用仍需单独补偿。
终止也不等于普通结束。正常结束表示流程按模型完成;终止表示运行被强制打断。历史状态、结束原因和审计报表必须能区分 COMPLETED 与 TERMINATED。

图 2:五类操作分别改变审批结论、办理位置、提交状态、业务效力和实例生命周期。
七、三组最容易混淆的边界
1. 驳回与退回:否定结论,还是要求返工
驳回强调“这次审批不同意”;退回强调“请回去补充或重办”。驳回可以直接结束流程,退回通常保留继续办理的机会。
产品上最好把按钮和结果分开设计:
| 页面动作 | 必填信息 | 典型结果 |
|---|---|---|
| 驳回 | 拒绝原因 | 进入拒绝分支或结束 |
| 退回 | 目标节点、返工要求 | 目标节点重新生成任务 |
如果企业确实把“驳回到发起人”作为习惯叫法,也应在内部领域模型中拆成 decision=REJECT 与 route=RETURN_TO_INITIATOR,避免一个模糊字段承担两种语义。
2. 撤回与撤销:及时收回,还是事后失效
撤回的窗口通常较早,操作者多为发起人,目标是恢复草稿;撤销发生得更晚,操作者需要更高权限,目标是使已推进或生效的结果失效。
可以用“是否发生实质办理和外部副作用”划线。一旦审批已经完成、订单已经下发、凭证已经生成,就不要继续使用轻量的撤回逻辑,应进入撤销和补偿流程。
3. 驳回与终止:业务路径,还是实例处置
驳回是正常业务路径的一部分,应该形成审批结论并按模型继续;终止是对实例生命周期的强制处置,通常不再走正常路径。
如果管理员发现重复实例并终止,不应该伪造一条“某审批人驳回”的意见;如果审批人不同意申请,也不应该用管理员删除实例代替驳回。
八、在 Flowable 中怎样映射
Flowable 提供的是流程执行原语,不直接定义中国式 OA 的五种按钮。平台可以做如下映射:
| 产品操作 | Flowable 可复用能力 | 平台仍需负责 |
|---|---|---|
| 驳回 | 完成任务、流程变量、排他网关 | 结论模型、意见、会签汇总和拒绝路径 |
| 退回 | RuntimeService 创建 ChangeActivityStateBuilder,移动活动状态 | 目标选择、执行树、多实例、变量、历史和权限 |
| 撤回 | 前置条件通过后进行受控状态变更或结束当前提交实例 | 草稿恢复、再次提交、下游未办理校验和留痕 |
| 撤销 | 补偿事件、补偿子流程或独立撤销流程 | 业务效力、反向接口、审批授权和补偿结果 |
| 终止 | Terminate End Event 或 deleteProcessInstance 并写原因 | 权限、影响预览、外部补偿、通知和审计 |
ChangeActivityStateBuilder 能改变流程实例的活动状态,但它不会替产品回答“哪些节点允许退回”“会签剩余任务怎么办”“表单数据恢复到哪一版”。同样,deleteProcessInstance 能结束运行实例,却不能自动让业务单据失效。
Camunda 7 的 Process Instance Modification、Camunda 8 的 Process Instance Modification 与 Cancel Process Instance 也体现同样原则:引擎提供取消活动、激活新活动或取消实例的技术能力,操作语义和业务一致性仍由平台负责。
九、为什么不能直接改运行表或只移动 Token
直接更新 ACT_RU_TASK、ACT_RU_EXECUTION 等运行表,看似能让页面出现一条新任务,实际上可能破坏执行树的不变量。
高风险场景包括:
- 并行分支:只移动一个令牌,汇聚网关永远等不到其他分支;
- 会签:任务、执行实例和完成计数不一致;
- 子流程:父子作用域、调用实例和边界事件残留;
- 定时器:旧节点的 Job 仍会触发;
- 消息事件:旧订阅未取消,新订阅未创建;
- 局部变量:变量作用域随执行实例丢失;
- 历史记录:运行态与历史态互相矛盾;
- 监听器:任务创建、取消、完成事件未被正常触发。
即使使用官方状态变更 API,也要先读取当前执行树和历史轨迹,生成影响计划,再在受控事务中执行。状态变更是基础设施能力,不是完整的 OA 操作服务。
十、一次高风险操作应该怎样执行

图 3:五类操作都应先校验权限和状态,再预览影响、改变引擎状态、处理业务补偿,最后统一审计与通知。
推荐把操作服务设计成六步流水线:
- 接收领域命令:operationId、operationType、processInstanceId、taskId、目标节点和原因;
- 校验权限与前置条件:操作者身份、当前任务版本、实例状态和可操作窗口;
- 生成影响预览:将取消哪些任务、激活哪些节点、影响哪些分支、Job 和外部系统;
- 执行引擎状态变更:使用公开 API,并处理乐观锁和并发;
- 执行业务补偿:调用库存、财务、合同、消息等反向接口;
- 写入审计并通知:保存前后快照、结果、失败原因和接收人。
跨系统时很难依赖一个数据库事务实现全局原子性。更现实的做法是使用 operationId 和幂等键,配合本地事务、Outbox、重试和补偿状态机,保证同一命令不会重复退回、重复撤销或重复冲销。
十一、权限和前置条件必须产品化
五类操作不应共享一个“流程管理员”权限。推荐至少拆分:
| 权限 | 典型范围 | 风险等级 |
|---|---|---|
| REJECT_TASK | 当前可办理任务 | 中 |
| RETURN_TASK | 当前任务及允许的历史节点 | 高 |
| WITHDRAW_SUBMISSION | 本人发起且满足撤回窗口的实例 | 中 |
| REVOKE_BUSINESS_RESULT | 指定业务类型和组织范围 | 很高 |
| TERMINATE_INSTANCE | 指定流程定义、租户和组织范围 | 很高 |
按钮是否显示,应由“权限 + 实例状态 + 节点策略 + 业务状态”共同决定,而不是只判断当前用户是不是发起人或管理员。
对撤销和终止,应提供影响预览、必填原因和二次确认。预览至少列出将被取消的待办、定时器、子流程、未完成会签任务以及需要补偿的业务动作。
十二、审计记录不能只写一条操作日志
一次流程操作至少应保存:
- operationId、operationType、操作者、代理身份和发生时间;
- 流程实例、任务、活动、历史活动实例和业务单据;
- 操作前后的执行树摘要、任务列表和变量摘要;
- 目标节点、目标办理人、原因、意见、附件和电子签名;
- 被取消的任务、Job、事件订阅和子流程;
- 业务补偿步骤、外部请求号、重试次数和最终结果;
- 通知对象、渠道、送达状态;
- 租户、组织、客户端、IP、幂等键和版本号。
状态编码也应分开,例如 REJECTED、RETURNED、WITHDRAWN、REVOKED、TERMINATED。不要全部压成 CANCELLED,否则运营报表无法回答“审批未通过”“用户主动撤回”和“管理员强制终止”分别有多少。
十三、设计器和任务中心应该怎样呈现
设计阶段应配置:
- 哪些节点允许驳回、允许退回到哪些目标;
- 驳回后结束、回发起人还是进入补充材料;
- 撤回窗口以签收、打开、完成还是外部副作为截止;
- 撤销是否发起独立流程、需要几级审批;
- 终止事件结束当前子流程还是整个根实例;
- 每种操作需要执行哪些补偿、通知和表单权限变化。
运行阶段应让用户在点击前看到:
- 当前操作属于驳回、退回、撤回、撤销还是终止;
- 谁会失去任务、谁会收到新任务;
- 流程会继续、回到草稿、进入撤销流程还是立即结束;
- 哪些业务结果不会自动恢复;
- 操作成功后是否还能反向恢复。
按钮文案应使用动词加结果,例如“退回经办人重办”“撤回为草稿”“撤销已生效申请”“终止整个流程”,比单独显示“退回”或“取消”更不容易误操作。
十四、低代码平台怎样统一五种语义
云程低代码开发平台可以在流程引擎之上设置统一 Workflow Operation Service,将五种操作定义为稳定的领域命令,再通过 Engine Adapter 映射到 Flowable 等引擎能力。

平台层需要集中处理:
- Operation Policy:节点允许的操作、目标范围和截止条件;
- Impact Analyzer:执行树、任务、Job、子流程和外部副作用预览;
- Business Compensation:业务反向接口编排与重试;
- Form Snapshot:退回、撤回时恢复正确的数据版本;
- Audit Center:前后状态、原因、意见和补偿结果;
- Task Center:根据操作者和状态展示准确按钮;
- Notification Hub:站内信、uniAPP、钉钉和企业微信通知。
![在这里插入图片描述]()
这样,业务应用面对的是清楚、稳定的中国式工作流语义,而不是直接修改引擎表,或把所有异常流转都塞进一个 jumpTo 方法。


浙公网安备 33010602011771号