外观
流程框架封装
概述
BPM 工作流采用一套自研的轻量流程引擎,位于 backend/app/api/v1/module_bpm/framework。它不依赖 Activiti、Flowable 等外部引擎,而是围绕「钉钉飞书风格设计器导出的 simple_model JSON 模型」实现令牌(Token)式的顺序推进。
核心设计要点:
- 模型驱动:流程以一棵
SimpleFlowNode节点树描述,节点的type决定推进行为。 - 状态自管理:所有流程状态都落在本项目自建表中(
bpm_process_instance/bpm_process_node/bpm_task),不依赖外部流程引擎的运行时表。 - 事务一致:所有推进操作都在调用方的数据库事务内完成,节点记录与任务生成要么全部成功、要么整体回滚。
- 阻塞即挂起:审批节点会阻塞流程,直到人工或自动动作完成;延迟器、HTTP 回调等节点通过「节点记 WAIT 挂起 + 定时/外部恢复」继续推进。
引擎核心模块
引擎由一组职责单一的模块协作组成:
| 模块 | 职责 |
|---|---|
engine.py | 引擎核心:实例创建、节点推进、审批动作、加签/转办/退回等 |
condition.py | 条件求值:条件规则组与表达式两种配置方式 |
candidate.py | 候选人计算:按候选策略解析审批人/抄送人列表 |
trigger.py | 触发器节点运行时:HTTP 请求、HTTP 回调、表单变量更新/删除 |
event.py | 轻量异步事件总线:发布/订阅 |
notifier.py | 站内信通知:待办提醒、流程结果通知 |
process_listener.py | 流程级监听器:执行配置在管理页的监听逻辑 |
http_listener.py | 节点级 HTTP 监听器:节点任务创建/完成时回调外部系统 |
timeout.py | 定时扫描:审批超时处理、延迟器唤醒 |
serializer.py | 响应组装:将引擎数据转换为前端期望的驼峰结构 |
流程模型
simple_model 是一棵以 childNode 串联的单链,分支节点通过 conditionNodes 数组挂载多条分支。两种结构描述如下。
线性节点链(审批流主干)
分支容器(网关)
条件分支、并行分支、包容分支都是「分支容器」节点,容器内通过 conditionNodes 挂多个分支,每个分支再以 childNode 串联自己的节点体:
json
{
"type": 51,
"conditionNodes": [
{ "type": 50, "conditionSetting": { "defaultFlow": true }, "childNode": { "...": "分支体..." } },
{ "type": 50, "conditionSetting": { "conditionType": 2, "conditionGroups": "..." }, "childNode": { "...": "分支体..." } }
],
"childNode": { "...": "汇合后的后续节点" }
}引擎在部署(validate_simple_model)时会递归校验节点链,例如分支容器至少需要两个分支、审批节点必须配置候选策略、延迟器与时间相关节点必须有合法配置,校验不通过则拒绝部署。
节点类型
引擎支持以下节点类型(对应 BpmNodeTypeEnum):
| 类型 | 说明 | 推进行为 |
|---|---|---|
发起人节点 10 | 流程发起 | 直接记录通过,子流程可配置跳过 |
审批人节点 11 | 人工/自动审批 | 阻塞等待,生成审批任务 |
办理人节点 13 | 办理(无拒绝语义) | 同审批人节点推进 |
抄送节点 12 | 知会他人 | 记录抄送人后直接通过 |
延迟器节点 14 | 等待一段时间 | 记 WAIT 挂起,定时唤醒 |
触发器节点 15 | 自动动作 | HTTP 请求/表单更新,或 HTTP 回调挂起 |
子流程节点 20 | 调用另一流程 | 同步屏障,等待子流程终态 |
条件分支 51 | 排他网关 | 求值选择一条分支 |
并行分支 52 | 并行网关 | 全部分支同时激活 |
包容分支 53 | 包容网关 | 命中条件的分支激活 |
结束节点 1 | 流程终点 | 实例置为通过 |
注:条件节点 50(分支体内的具体分支)、路由分支 54 当前版本暂未启用,分支结构由容器节点(51/52/53)承载。
节点推进机制
引擎的核心方法是 _run_chain:从某个节点开始,沿 childNode 顺序推进,直到遇到「需要阻塞的节点」或流程进入终态时停止,返回链是否自然走完。
推进规则概览:
- 发起人节点、抄送节点、结束节点、
AUTO_APPROVE自动通过节点 —— 立即落库并继续。 - 审批/办理节点 —— 计算候选人,生成任务并阻塞(返回
False)。 - 延迟器节点 —— 记 WAIT 挂起,返回
False等待定时唤醒。 - 触发器
HTTP_CALLBACK类型 —— 记 WAIT 挂起,返回False等待业务方回调。 - 子流程节点 —— 发起子流程并记 RUNNING,返回
False等待子流程终态事件。 - 条件/并行/包容分支 —— 处理后进入分支体,分支体阻塞则整体阻塞;分支体自然走完后由
_continue_after_branch检查汇合条件并推进后续。
任务被审批后,引擎在 _after_task_approved 中检查多人审批是否满足条件(会签比例、或签、依次审批),满足则闭合当前节点,并从 childNode 继续推进;若链尾落在分支体内,则逐层向外做分支汇合检查。
条件与分支
分支选择依赖条件求值模块 condition.py,支持两种配置方式(对应 BpmConditionTypeEnum):
- 条件规则
RULE:由「规则组」组成。单个规则是字段 + 运算符 + 值;组内规则按「且/或」组合,组间也按「且/或」组合。运算符涵盖==、!=、>、>=、<、<=以及包含/不包含,并支持字符串与数字的自动类型协调。 - 条件表达式
EXPRESSION:基于 Pythonast的白名单表达式求值,仅允许变量引用、比较与逻辑运算(&&/||会适配为and/or),禁止属性访问、函数调用等危险语法。表达式上下文注入了流程变量与内置变量(如发起人编号)。
分支容器语义:
- 条件分支(排他网关):按顺序选择第一个命中的分支,未命中分支记 SKIP;可配置
defaultFlow默认分支兜底。 - 并行分支(并行网关):容器下所有分支同时激活并各自推进,全部已激活分支都达终态后才放行汇合后续节点。
- 包容分支(包容网关):按条件逐个求值,命中的分支才激活,未命中记 SKIP;激活分支全部终态后汇合。
未激活(SKIP)的分支不参与汇合等待,保证分支结构正确收敛。
候选人策略
审批节点与抄送节点的执行人由 candidate.py 根据「候选策略 + 参数」计算,并对结果去重。当前支持的常用策略:
| 策略 | 说明 |
|---|---|
指定用户 USER(30) | 直接指定用户编号 |
指定角色 ROLE(10) | 取拥有该角色的全部用户 |
指定岗位 POST(22) | 取拥有该岗位的用户 |
部门成员 DEPT_MEMBER(20) | 取部门下用户 |
部门负责人 DEPT_LEADER(21) | 取部门负责人 |
连续多级部门负责人 MULTI_LEVEL_DEPT_LEADER(23) | 向上逐级取负责人 |
指定用户组 USER_GROUP(40) | 取用户组成员 |
发起人自己 START_USER(36) | 流程发起人 |
发起人自选 START_USER_SELECT(35) | 发起时由发起人填写审批人 |
审批人自选 APPROVE_USER_SELECT(34) | 当前审批人指定下一节点审批人 |
发起人部门负责人 START_USER_DEPT_LEADER(37) | 取发起人所在部门负责人 |
流程表达式 EXPRESSION(60) | 表达式解析出的用户编号(支持 startUser、${变量}、字面量等) |
当审批人与发起人相同时,可配置处理策略:由本人审批、自动跳过、或转交部门负责人。当计算出的审批人为空时,可配置:自动通过、自动拒绝、指定人员、或转交流程管理员。
审批任务与多人审批
审批节点激活后会生成 bpm_task 任务,多人审批方式由 approveMethod 决定:
- 随机一人:从候选人中取一人生成任务,通过即节点完成。
- 或签
ANY_APPROVE:任意一人通过即完成。 - 会签
APPROVE_BY_RATIO:按通过比例(如超过 50%)判定是否完成,未达比例继续等待。 - 依次审批
SEQUENTIAL_APPROVE:按候选人顺序逐个生成任务,前一人通过后自动为下一人建任务。
任务完成时,引擎通过乐观更新(status == RUNNING 才生效)防并发重复处理,并据审批方式判定节点是否完成。
子流程
子流程节点支持在一个流程中调用另一个流程定义(calledProcessDefinitionKey),引擎以同步屏障方式等待其结束:
- 子流程结束时通过实例终态事件回调主流程;子流程通过则主流程继续,被拒绝/取消则主流程联动终止(状态传播)。
- 通过
inVariables将主流程变量映射为子流程变量,通过outVariables在子流程结束后回传变量到主流程。 - 子流程发起人可配置为「同主流程发起人」或「表单字段用户」,为空时按兜底策略(主流程/子流程管理员)处理。
- 可配置是否跳过子流程的发起人节点;当前版本暂不支持异步子流程与多实例子流程。
事件总线与监听器
引擎内置轻量异步事件总线(event.py),在关键节点发布事件,用于解耦通知与扩展逻辑。处理器异常不会中断主流程,仅记录日志。
事件类型:
bpm.process_instance.create—— 实例创建bpm.process_instance.finish—— 实例终态(通过/拒绝/取消)bpm.task.create—— 任务创建(待办生成)bpm.task.finish—— 任务完成(仅通过时触发)
基于事件总线的两类监听器:
- 流程级监听器
process_listener:在管理页配置的全局监听器,绑定到上述事件。支持两种值类型:class:按module.path:attr动态导入并调用处理函数;expression:基于 ast 白名单的表达式求值(结果仅记录日志)。- 任务完成监听仅在任务通过时触发。
- 节点级 HTTP 监听器
http_listener:配置在审批/办理节点上的taskCreateListener/taskCompleteListener,在任务创建或(通过时)完成时向外部系统发起 HTTP 回调,参数与请求头支持固定值或流程变量取值。
两类监听器均在引擎模块加载时通过 register() 幂等订阅(见 engine.py 末尾导入处)。
触发器节点
触发器节点(TRIGGER_NODE)用于在工作流中自动执行动作,由 trigger.py 按类型分发:
- HTTP 请求
HTTP_REQUEST:同步发起 HTTP 请求,可按响应映射把返回字段写回流程变量,流程直接继续。 - HTTP 回调
HTTP_CALLBACK:发起请求(请求体携带taskDefineKey)后节点挂起,等待业务方调用回调恢复接口engine.trigger_callback推进流程(幂等设计)。 - 表单更新
FORM_UPDATE/ 表单删除FORM_DELETE:按条件配置批量更新或删除流程变量。
HTTP 请求支持失败回滚(停留在触发器前可重试),响应回写兼容统一响应结构 { code, data }。
超时与延迟扫描
审批任务的超时处理与延迟器节点唤醒由基础设施的定时任务(module_infra 的 job)周期调用,对应两个处理器:
module_bpm.framework.timeout:scan_timeout_tasks:扫描运行中的审批任务,按节点timeoutHandler配置执行——自动提醒(按maxRemindCount轮次、基于 Redis 去重)、自动同意、或自动拒绝。module_bpm.framework.timeout:scan_delay_nodes:扫描 WAIT 状态的延迟器节点,到点(固定时长自激活起算 / 固定日期为绝对时间)后将其置为通过并从childNode继续推进。
超时时长使用 ISO 8601 时长格式(如 PT1H30M)。两个扫描均通过引擎侧的任务/节点状态校验与乐观更新做重复执行防护。
运行时任务操作
引擎在审批任务上提供丰富的运行时操作(见 engine.py):
- 委派
delegate_task:交由他人审批,对方完成后回到原审批人。 - 转办
transfer_task:直接更换审批人。 - 加签
create_sign_task/ 减签delete_sign_task:向前加签(父任务挂起,前置审批人先审)或向后加签(父任务进入审批通过中,后置审批人后审)。 - 退回
return_task:驳回到指定审批人节点重新审批,同时收尾兄弟分支与子流程。 - 撤回
withdraw_task:审批人撤回自己的已通过任务,若流程已推进到下游则取消下游运行中的任务与子流程后恢复节点。 - 人工抄送
copy_task:为任务补充抄送人。
前端设计器与查看器
前端位于 frontend/apps/web-ele/src/views/bpm,配合 packages/constants/src/biz-bpm-enum.ts 中的枚举与 frontend/apps/web-ele/src/api/bpm 的接口封装。
- 设计器:流程模型支持
SIMPLE(20)钉钉飞书风格设计器(bpm-model-editor.vue)。钉钉飞书风格设计器产出引擎直接消费的simple_model。 - 流程查看器:流程详情页提供
simple-bpm-viewer.vue(钉钉飞书风格模型视图)流程图渲染,展示实例流转轨迹与节点状态。 - 隐藏路由:发起流程、流程详情、模型创建/修改、流程定义、数据报表等均以
hideInMenu隐藏路由存在,由列表页跳转进入(见router/routes/modules/bpm.ts)。 - 任务操作按钮:前端按钮与
BpmTaskOperationButtonTypeEnum(通过/拒绝/转办/委派/加签/退回/抄送)一一对应,结合后端返回的可执行动作动态展示。
扩展点小结
- 事件订阅:通过
event.subscribe注册自定义处理器,监听实例与任务生命周期。 - 流程级监听器:用
class或expression类型在管理页配置,无需改代码即可接业务逻辑。 - 节点级 HTTP 监听器:在节点上配置外部回调地址,实现审批节点与第三方系统的联动。
- 定时任务:通过基础设施 job 的
handlerName挂载超时与延迟扫描,可按需调整扫描策略。