工作流里的驳回、退回、撤回、撤销和终止:五种操作不能混为一谈

一句话先说结论:驳回是当前审批人给出否定结论,退回是让已办环节重新办理,撤回是提交人及时收回,撤销是让已经推进甚至生效的结果失效,终止是强制结束流程实例。

五个按钮表面上都可能让流程“不再向前走”,但它们改变的是不同对象:驳回改变审批结论,退回改变办理位置,撤回改变提交状态,撤销改变业务效力,终止改变实例生命周期。若把它们都实现成“删除当前任务并跳到某节点”,权限、历史、并行分支和业务数据迟早会失真。

一、先用一张表看清五种操作

操作 典型发起人 常见时机 目标状态 原流程是否继续 是否常需业务补偿
驳回 当前审批人 当前任务办理中 拒绝路径或发起人修改 取决于模型 视副作用而定
退回 当前办理人 流程运行中 某个已办节点重新办理 有时需要
撤回 发起人 提交后、下游尚未实质办理 草稿或待提交 可重新提交 通常不需要
撤销 业务负责人或授权管理员 流程已推进或结果已生效 业务结果失效或进入冲销流程 原正向流程通常不再继续 通常需要
终止 管理员、系统或模型事件 流程仍在运行 已终止 引擎不会自动补偿

判断一个按钮到底属于哪一种,不要只看名字,至少要问五个问题:

  • 谁能发起;
  • 流程走到了什么阶段;
  • 操作后回到哪里;
  • 原流程是否还会继续;
  • 已经发生的业务副作用由谁撤销。

在这里插入图片描述

图 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:五类操作都应先校验权限和状态,再预览影响、改变引擎状态、处理业务补偿,最后统一审计与通知。

推荐把操作服务设计成六步流水线:

  1. 接收领域命令:operationId、operationType、processInstanceId、taskId、目标节点和原因;
  2. 校验权限与前置条件:操作者身份、当前任务版本、实例状态和可操作窗口;
  3. 生成影响预览:将取消哪些任务、激活哪些节点、影响哪些分支、Job 和外部系统;
  4. 执行引擎状态变更:使用公开 API,并处理乐观锁和并发;
  5. 执行业务补偿:调用库存、财务、合同、消息等反向接口;
  6. 写入审计并通知:保存前后快照、结果、失败原因和接收人。

跨系统时很难依赖一个数据库事务实现全局原子性。更现实的做法是使用 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 方法。

posted @ 2026-08-17 17:32  大龄码农有梦想  阅读(15)  评论(0)    收藏  举报