中国式审批语义
wf_task 上闭环,经 REST API 与 Go API 暴露。 审批方式(approveMode)
节点 configuration.approveMode 取值(wf_task.approval_type 同值落库),枚举为大小写敏感的精确取值:
| approveMode | 通过规则 | 说明 |
|---|---|---|
single 单人 | 办理人处理即出结果 | 缺省值;assignee 为单个办理人 |
any 或签 | 任一人通过即过,一票拒绝即驳 | 多候选人同时收到,先办先得 |
all 会签 | 全员通过,一票否决 | 全员并行办理,人人出结果 |
sequential 顺序审批 | 前一人办理完,下一人才收到任务 | 引擎按需逐个创建单人任务,前一人不出结果后一人不可见 |
vote 票签 | 按 voteRule 阈值出结果 | 全员并行表决,适合评审表决场景,见下表 |
system / cc 为引擎内部使用的审批类型(系统自动任务与抄送任务),userTask 节点不接受这两个配置值。
三种多人方式的任务流转差异:
或签 any —— 多人同时可见,先办先得:
会签 all / 票签 vote —— 全员并行办理,出齐后聚合:
顺序审批 sequential —— 串行逐人办理,前一人不出结果后一人不可见:
票签阈值(voteRule)
approveMode: vote 时的通过阈值写在 configuration.voteRule,落库到 wf_task.approval_rule(JSON):
| 字段 | 类型 | 说明 |
|---|---|---|
type | string | 阈值类型:majority / percent / count |
value | float | 规则值:percent 取 0~100;count 为票数;majority 不消费 |
type 阈值明细(未配 voteRule 时缺省按 majority):
| type | 通过条件 | 拒绝条件 |
|---|---|---|
majority | 过半通过(total/2+1) | 多数拒绝 |
percent | 通过票占比 ≥ value(向上取整:3 人 60% 需 2 票) | 剩余票不可能达标即驳 |
count | 通过票数 ≥ value | 剩余票不可能达标即驳 |
典型组合:
// 票签 · 过半通过(缺省,可省略 voteRule)
{ "type": "majority" }
// 票签 · 60% 通过(评审表决)
{ "type": "percent", "value": 60 }
// 票签 · 至少 3 票通过
{ "type": "count", "value": 3 }
vote为全员并行表决,先签收先办(见下文签收/抢单);需要逐个办理时用approveMode: sequential。
审批人含发起人时(自审策略)
审批链路中出现发起人本人时,按节点配置的 selfApproval 处理:
| selfApproval | 行为 |
|---|---|
none | 缺省,不过滤,发起人照常出现在审批人中 |
skip | 移除发起人,直接到下一节点(initiatorSelf 审批人配 skip 会无人可审,部署期校验拒绝) |
autoApprove | 发起人的任务创建即由系统按通过办结,审批记录注明「发起人自动通过」;或签场景下发起人的票计入或签判定(候选池认领型任务不受影响,认领后正常审批) |
delegateToManager | 发起人的任务转交其直接上级 |
delegateToDeptManager | 发起人的任务转交部门负责人 |
审批人解析
任务按节点 configuration.approver 的 type 发起,候选人池(wf_task_assignee)只存原始引用,查询时经 IdentityService 展开:
| approver.type | 解析方式 |
|---|---|
user | userIds 直接给用户 ID |
role / dept | 按角色/部门查用户,产生待认领任务 |
manager | 发起人的第 levels 级主管(默认 1 级) |
multiLevelManager | 发起人的多级主管:levels > 0 固定审批到第 N 级,levels < 0 直到最上层 |
initiatorSelect | 发起人提交时自选:expression 表达式模板(如 ${msg.selectedUsers})从流程变量解析审批人 |
initiatorSelf | 发起人本人(自审场景) |
审批人为空兜底(emptyApproverPolicy)
解析结果为空集合(角色下暂无成员、发起人未自选到人、自审过滤后无人)时,节点按 configuration.emptyApproverPolicy 兜底,实例按策略兜底处理,不会因审批人为空而失败:
| 策略 | 行为 |
|---|---|
tenant_admin(缺省) | 转交租户管理员办理:唯一管理员直接建单任务,多名管理员产生待认领任务;解析不到管理员时降级 park。发起人自己就是唯一管理员时改为自动通过(防自批绕过) |
auto_approve | 任务照建后由系统以发起人名义按通过办结,审批记录注明兜底来源;无发起人时降级 park。会签/票签场景合成单票结构(any 1 票即过),避免原阈值把自动通过判成自动拒绝 |
park | 任务照建并停泊待指派:候选为空时写入 __parked__ 占位(封死被任意同租户用户认领的口子),等待管理员在任务监控中直接指派或补候选人 |
兜底发生时任务变量写入 fallback_policy / fallback_from / fallback_reason / fallback_time 四件套留痕(口径对齐 reassign_*),详情页与通知据此展示"这单为何绕过了原审批人配置"。自动通过钩子只认带 fallback_reason 的任务,常规流转里"审批人恰好是发起人"的任务不受影响。
办理中的动作
加签 / 减签
审批中途动态插入审批人(加签)或移除未办理的加签人(减签)。加签为前加签语义:新加签人先审,加签子任务全部完成后原审批人再出结果。引擎通过 wf_task.parent_id + sequence_order 维护任务父子链,加签产生子任务,主任务等待链上任务全部完成。
减签只作用于未办理的子任务,资格按子任务来源分层:流程配置产生的会签 / 票签子任务(无加签标记)不受加签人限制,节点参与人照旧可减(会签减到 0 终止实例的既有语义);经加签产生的子任务只能由加签人本人移除——加签时引擎在子任务变量写入 sign_added_by 留痕(引擎保留键,审批提交 / 变量 API 同名键一律剥离,无法伪造),减签时据此校验,别人加的签减不掉。已出结果的审批人不可移除。减签后按剩余票重算——已满足阈值则直接出结果流转;全部移除(无人能审)时终止实例。减签经 ReduceSign 事件留痕(审计 / 通知用)。
退回
可退回到上一个已办审批节点重新办理:
- 退回目标固定为最近一个已办
userTask(不能跨节点挑目标,也不能直接退发起人) - 被退回任务归档为
returned,目标节点任务重建,表单数据与变量随行带回 - 需要「退回发起人」时,给节点配驳回
reject.strategy: toStarter
转办 / 委派
两个动作都把任务交给别人,差别在最终由谁出结果(面向用户的操作说明见审批动作指南):
- 转办:任务转给他人办理,办理人变更,原审批人出局,新办理人审批即流转。留痕写入任务变量
transfer_from/transfer_reason/transfer_time - 委派:受托人先行把关,
owner保留原拥有人、delegate_from落表留痕。被委派人通过 / 拒绝后任务不流转,自动归还原审批人并通知(TaskEventResolved),审批意见保留在时间线;原审批人再审一次才出结果。委派禁止指派给自己
签收 / 抢单
角色/部门候选任务先到先签:候选池中任何人可签收(claimed_at 记录时间),签收后其他人不可再办。
收回
审批人撤销自己最近投出的一票(通过或驳回)重新待审(面向用户的操作说明见审批动作指南)。引擎守卫全集:本人最近一票(通过或驳回)、该票非管理员代审产生、无更晚办理记录(同会签轮次的同伴投票除外)、实例仍停泊在 active/pending 的 userTask 上、收回路径上无自动化节点(沿 Success 出边 BFS,未知节点类型 fail-closed)。收回执行时:自己之后的在途任务归档(end_reason 前缀「审批人收回」)、该票标记 recalled 并在时间线显示「已收回」、收回人任务重建(沿用父任务 / 审批规则 / 顺序位次,剔除旧 approved/comment 变量防静默自动通过)。已完结实例的终态收回(按原主键复活运行行、重入末节点整轮重审)当前下线:已完成的申请不可逆,需要重来时由发起人重新发起新申请。
撤回
发起人可撤回在途申请:实例终止(状态 terminated,end_reason 记「申请人撤回」),在途任务作废——含并行分支的跨节点任务,作废票统一归档 withdrawn 前缀的 end_reason(前端时间线 / 已办据此显示「已作废(撤回)」,不与实例终止混淆),任务行与实例行均从运行表物理删除,审批人待办即时消失。之后可修改表单重新发起。
实例级操作
| 操作 | 实例状态 | 说明 |
|---|---|---|
| 挂起 / 恢复 | suspended → active | 暂停全部未办任务,恢复时单节点恢复不丢父上下文 |
| 终止 | terminated | 管理员强杀,记录 end_reason |
| 完成 | completed | 正常走完 end 节点,归档入历史表 |
后续审批节点预测(upcoming):活态实例的详情响应带 upcoming 字段——从活跃节点沿 Success 出边向前遍历,预测后续审批节点(nodeId / nodeName / approverType / assignees / unresolved)。预测非承诺:转办、加签都可能改变实际走向;条件分支处截断,发起人自选在变量未落定时 unresolved 为 initiatorSelect。
超时(timeout)
节点的 configuration.timeout(dueInMinutes 截止时长 + action 动作)决定逾期处理方式,时长相对每个任务创建时刻计算,由宿主的逾期巡检执行(gflow 平台每 30 分钟定时扫描 wf_task.due_date 已超期的在途任务):
remind(默认):经内置通知中心发站内提醒,不改变任务状态autoApprove:以系统身份自动通过逾期任务,流程继续autoReject:以系统身份自动拒绝,按节点驳回配置流转
due_date 也可经 TaskService.SetDueDate Go API 手工设置。多实例节点(顺序审批)的每个后续子任务按各自创建时刻重新求值 dueInMinutes,避免整条链共用同一个静态截止时间。直接嵌引擎(无 gflow 平台)时,巡检逻辑需宿主自行实现(参考 gflow 的逾期扫描器)。
驳回(reject)
节点的 configuration.reject 决定审批拒绝后流程的去向:
{ "strategy": "toNode", "target": "node_supplement" }| strategy | 行为 |
|---|---|
terminate | 终止实例(缺省) |
toStarter | 跳回开始节点(退回发起人改材料重提)。链首下游存在并行网关(fork/join/inclusive)时不支持,部署期拒绝 |
toPrev | 跳到上一个 userTask 节点重办。回退路径穿过并行网关(fork/join/inclusive)时不支持,部署期拒绝 |
toNode | 跳到 target 指定节点(必填,须为链中存在的节点 ID,且必须是当前节点的上游节点;回退路径穿过并行网关 fork/join/inclusive 的跨并行分支回退不支持,部署期拒绝) |
跨并行分支回退被全面禁止的原因:重入 fork/inclusive 会向各分支重复派发任务,重入 join 会因兄弟分支的消息不再到来而永久等待。该限制在部署期强制执行;存量定义若绕过校验落库,运行期回跳时也会降级为沿 Reject/Failure 出边或终止实例,不会双派发。
跳转目标不可达(条件不满足、边不存在等)时,按节点的 Reject / Failure 出边兜底;连出边都没有则终止实例。多个审批人模式下任一人拒绝即触发驳回(或签先到先得,会签一票否决)。
抄送
ccTask 节点生成抄送记录(不阻塞流程),经 CCTaskCreatedListener 回调;gflow 前端有「抄送我」列表,抄送人可评论。
动作权限
每个 userTask 可在 additionalInfo.actionPermissions 里精细开关审批人可用的动作。通过(approve)/驳回(reject)由后端强制开放,不可关闭;其余动作默认关闭,需显式开启:
{
"transfer": true,
"return": true,
"delegate": true,
"addSign": true,
"reduceSign": true,
"urge": true,
"uploadAttachment": true
}发起人侧另有 suspend / withdraw / terminate / resubmit 等实例级开关。设计器里逐节点可视化配置。
收回(recall)是流程级开关,配在 ruleChain.additionalInfo.actionPermissions,与上述节点级动作不同层:缺省开启(opt-out),显式置 false 才禁用。该开关只作用于在途收回;已完结实例终态收回已下线,历史配置键 recallWindowDays 不再生效。
部署期校验
部署 / 更新流程时,引擎对各节点 configuration 做校验:未知取值、必填缺失、互斥组合(如票签阈值配在非票签节点、toNode 缺 target、驳回目标非上游节点或跨并行分支)会直接拒绝部署,并给出节点级错误信息——配置错误拦在写入前而不是运行期。