Skip to content

审批方式与驳回策略

审批人节点是工作流中最核心的节点。每个审批人节点都需要配置审批方式(多人时如何决策)和驳回策略(被驳回时如何处理)。

审批方式

当审批人节点配置了多人时,需要指定审批方式,决定多少人通过才算节点通过。

审批方式说明适用场景
或签任一审批人通过即节点通过,其余待审批人自动跳过只需要一人同意即可,如"部门经理审批"
会签所有审批人都通过才节点通过需要多人共同确认,如"财务+法务双签"
依次审批按指定顺序逐一审批,前一人通过后才轮到下一人需要逐级审批,如"总监→副总裁→总裁"
自动通过不生成审批任务,节点自动通过流程自动化的节点,无需人工干预

审批方式配置示例

场景 1:请假申请中的部门经理审批

  • 审批人:指定角色「部门经理」
  • 审批方式:或签(部门经理有多个,只需其中一人审批)

场景 2:合同审批中的财务+法务双签

  • 审批人:指定用户组(财务、法务各一人)
  • 审批方式:会签(财务和法务都必须通过)

场景 3:项目立项的逐级审批

  • 审批人:连续多级上级
  • 审批方式:依次审批(按层级顺序,一级一级往上)

驳回策略

当审批人选择驳回时,系统根据配置的驳回策略决定流程走向。

驳回策略说明适用场景
终止流程直接结束流程,实例状态变为「已驳回」通用场景,驳回后不再继续
退回上一步退回到上一个审批/办理节点,重新生成任务需要修改后重新审批
退回发起人退回到发起人,重新发起需要发起人修改表单
退回指定节点退回到指定的节点(通过节点标识引用)需要退回到特定环节

驳回策略配置示例

场景 1:普通审批驳回

  • 驳回策略:终止流程
  • 结果:审批人驳回后,流程直接结束,发起人收到驳回通知

场景 2:多级审批中的退回修改

  • 驳回策略:退回发起人
  • 结果:审批人驳回后,流程回到发起人,发起人修改后可重新提交

场景 3:跨部门协作中的退回指定环节

  • 驳回策略:退回指定节点
  • 节点标识:设置目标节点的 key,如 dept_manager_review
  • 结果:审批人驳回后,流程回到「部门经理审批」节点,而非发起人

审批类型

每个审批人节点还可以配置审批类型,作为节点级的总开关:

审批类型说明
人工审批正常生成审批任务,需要人工审批(默认)
自动通过节点自动通过,不生成任务
自动拒绝节点自动拒绝

使用场景

  • 人工审批:绝大多数场景
  • 自动通过:根据前置条件判断,某类申请无需审批(如小额报销自动通过)
  • 自动拒绝:根据前置条件判断,某类申请自动拒绝(如超出预算的申请)

边界情况处理

空审批人

当审批人解析为空时(如部门负责人不存在、表单字段为空),根据「空审批人处理策略」自动处理:

策略行为
自动通过节点自动通过
转交管理员任务转交给系统管理员处理
自动拒绝节点自动拒绝,流程按驳回策略处理
转交指定人任务转交给预设的指定人员

发起人与审批人同一人

当审批人就是发起人自己时,可根据「发起人与审批人同一人时的处理策略」设置:

策略行为
由发起人自己审批发起人自己审批
自动跳过自动跳过该节点
转交给直接上级审批转交给发起人的直接上级
转交给部门负责人审批转交给发起人的部门负责人

审批人去重

当流程中多个节点的审批人为同一人时,可根据「审批人去重策略」设置:

策略行为
自动跳过(默认)后续节点自动同意,无需重复审批
仍需审批每个节点都需重新审批

撤回

谁可以撤回

只有发起人才能撤回流程。

什么时候可以撤回

只有当流程状态为「运行中」时,发起人才可以撤回。

撤回后的影响

  1. 流程实例状态变为「已撤回」
  2. 所有未处理的任务自动标记为「已跳过」
  3. 触发撤回事件,可订阅该事件做后续处理

加签 / 减签

允许审批人在不修改流程定义的前提下,临时拉人参与或移除已加签的审批人。前端入口在「待办审批」页面,需要节点配置 actionButtons.addSign.enabled = trueactionButtons.reduceSign.enabled = true

加签位置

位置语义任务 comment 前缀
before(前加签)原任务挂起(status = waiting),加签人先审批;全部完成后原任务自动恢复 pending[加签-前]
after(后加签)原审批人继续完成,再交给加签人接力[加签-后]
parallel(并加签)加签人与原审批人并行处理,按节点 approveMethod 汇总[加签-并]

减签

只能减掉「由加签产生的待办任务」(comment[加签- 开头且 status = pending),减签后任务标记为 skipped

事件

事件触发时机
task.addSigned加签产生新任务时,每个加签任务触发一次
task.reduceSigned减签使任务变为 skipped 时,每个被减签任务触发一次

事件 payload 与其他 task.* 事件一致(WorkflowTaskEventPayload),meta.comment 携带加签 / 减签时填写的备注。

催办

发起人和管理员(super_admin / tenant_admin)可以对仍在运行中的流程实例的「待办」任务发起催办,提醒审批人尽快处理。

谁可以催办

  • 流程实例的发起人 initiatorId === currentUser.userId
  • 拥有超管 / 租户管理员角色的用户

何时可催办

  • 实例 status = running
  • 目标任务 status = pending

节流策略

每个任务在最近 5 分钟内只允许被催办 1 次,再次提交将返回 429

json
{ "code": 429, "message": "催办过于频繁,请 XXXs 后再试" }

实例级批量催办接口会静默跳过节流命中的任务,并在 message 中汇报跳过人数。

数据存储

催办记录持久化到 workflow_task_urges 表:

字段类型说明
idserial主键
task_idint被催办的任务 ID(外键 → workflow_tasks.id
instance_idint所属实例 ID(外键 → workflow_instances.id
urger_idint催办发起人用户 ID
urger_namevarchar(64)催办发起人姓名快照
messagevarchar(256)催办留言(可空)
created_attimestamp催办时间

事件

催办成功时触发 task.urged 事件,payload 为 WorkflowTaskEventPayloadmeta.comment 携带催办留言、meta.actor 携带催办人。该事件已加入事件订阅白名单,可在「事件订阅」中配置 WebHook / 通知。

REST 接口

Method路径说明
POST/api/workflows/tasks/{taskId}/urge催办单个任务,body:{ message?: string }
GET/api/workflows/tasks/{taskId}/urges查询任务的催办历史
GET/api/workflows/instances/{id}/urges查询实例的全部催办流水
POST/api/workflows/instances/{id}/urge实例级批量催办(对所有 pending 任务发起一次催办,节流命中的任务静默跳过)

前端入口

「我的申请」详情抽屉内为「审批中」的实例提供「催办」按钮,可选填留言;提交后调用实例级批量催办接口。

转办 / 委派 增强

任务的处理人轨迹

workflow_tasks 新增三个字段维护处理人轨迹:

字段类型说明
original_assignee_idint任务最初的处理人;转办/委派均不会修改
transfer_chainjsonb(int[])转办/委派经手过的处理人 ID 链(最早 → 最近,不含当前 assignee)
delegated_from_idint仅委派期间设置,指向「最早一次委派的发起人」;委派回执时清空

禁止折返转办 / 委派

转办、委派接口都会检查目标用户是否出现在 transfer_chain 中或等于 original_assignee_id;命中即返回 400

json
{ "code": 400, "message": "禁止将任务转回曾经经手的处理人" }

这样可以避免出现 A → B → A 之类的死循环,强制流转链路单向延伸。

委派反馈后原人接手

当任务带有 delegated_from_id 时,委派人对该任务做 同意 / 拒绝 操作不会推进流程,而是:

  1. 当前任务状态置为 approved / rejected,留言写入 [委派回执] {委派人} 建议同意/拒绝:{意见}
  2. 自动为最早一次委派的发起人(delegated_from_id)生成一条新的 pending 任务,同节点 / 同类型,留言为上一步的回执内容;
  3. 新任务 delegated_from_id = nulltransfer_chain = []original_assignee_id = 原委派人
  4. 由原委派人对新任务进行最终的同意 / 拒绝,正常推进流程。

多次委派(A → B → C)也只生成一次回执:C 反馈后任务回到 A(最早的委派人)。

外部审批回调入口(approveTaskByCallback / rejectTaskByCallback)不会触发委派回执逻辑。

MSW 行为

前端 Demo 模式(MSW)已同步实现:

  • 转办 / 委派接口校验 transferChain 防折返;
  • approve / reject 接口对带 delegatedFromId 的任务自动生成回执任务,并返回 已提交委派回执,等待原审批人确认

抄送规则(ccNode)

接收人解析与去重

抄送节点(ccNode)的接收人不再由引擎直接展开,而是在 expandTasksToRows 阶段统一通过 resolveAssigneeIds 解析:

  • 引擎层只为每个抄送节点生成一个 TaskActionassigneeId = null,携带 nodeConfig);
  • 服务层根据 nodeConfig.assigneeType 解析最终接收人,结果由内部 Set 自动去重;
  • 未声明 assigneeType 时自动回退到 assigneeIds + assigneeId,兼容旧定义。

变量插值

得益于复用 resolveAssigneeIds,抄送节点支持与审批/办理节点一致的变量化接收人策略:

  • assigneeType=formUser / formDepartment:从表单字段动态取人;
  • assigneeType=initiator / initiatorLeader / initiatorDept / manager:基于发起人上下文;
  • assigneeType=expression:JSON 表达式按表单/上下文计算接收人;
  • 所有策略都受 resolveAssigneeIds 的统一去重保护,不会重复抄送给同一人。

动态补加抄送

提供运行时补加抄送的接口:

POST /api/workflows/instances/{id}/cc/add
Body: { "nodeKey": string, "userIds": number[] }
  • 仅对 running 状态的实例生效;
  • 仅发起人或 super_admin / tenant_admin 可调用;
  • 校验目标节点必须存在且 type === 'ccNode'
  • 与该实例 + 该节点已抄送过的 assigneeId 自动去重;
  • 新插入的任务 status = 'skipped'actionAt = null,并触发 task.created 事件,供消息/订阅链路二次分发。

前端入口

「我的申请」详情抽屉中,当实例为「审批中」且定义包含至少一个 ccNode 时,提供「添加抄送人」按钮,弹窗中可选择目标抄送节点与抄送人列表,提交后调用上述接口。

MSW 行为

Demo 模式(MSW)已同步实现 /api/workflows/instances/:id/cc/add,行为与后端保持一致(去重 + 节点校验 + 运行中校验)。

Built with VitePress for local documentation preview.