本文讨论四种常见流程模式的实现方式与选型思路,样本为一套开源工作流引擎,供做流程设计的读者参考。
在企业 OA、ERP 审批、项目管理等场景中,线性审批只是起点。真正考验工作流引擎的,是:
该平台 BPM(该引擎 / 该引擎)将上述能力抽象为四大流程运行模式,并在 WorkNode.NodeSend() 的 5×5 路由算法中落地。Flowable / Camunda 则用 BPMN 2.0 的 Parallel Gateway、Multi-Instance、Call Activity 等标准元素实现类似能力。
本文从源码出发,对照 Flowable/Camunda 架构,梳理前后端实现、优劣势,并给出倾向选择 该引擎 的选型结论。
该平台在节点级别通过 RunModel(运行模式)和 NodeType(节点类型)描述流程拓扑,面向业务人员的四大模式如下:
业务名称 | 引擎标识 | RunModel 值 | 核心含义 |
|---|---|---|---|
线性流程 | Ordinary | 0 | 串行审批,一步接一步 |
同表单分合流 | SubThreadSameWorkID | 4 | 分流后多子线程共享主表数据(FID 关联) |
异表单分合流 | SubThreadUnSameWorkID | 5 | 分流后各子线程独立表单 / 独立 WorkID |
父子流程 | SubFlowNode | NodeType=3 | 主流程嵌入子流程,支持手动/自动/延续 |
说明:引擎内部还有 分流(FL)、合流(HL)、分合流(FHL) 三种路由节点形态(RunModel 1/2/3),它们是实现「分合流」的拓扑基础,与上述四大模式配合使用,而非并列的第四种业务形态。
枚举定义(后端):
/// <summary>
/// 运行模式
/// </summary>
public enum RunModel
{
Ordinary = 0, // 普通/线性
HL = 1, // 合流
FL = 2, // 分流
FHL = 3, // 分合流
SubThreadSameWorkID = 4, // 同表单子线程
SubThreadUnSameWorkID = 5 // 异表单子线程
}
public enum NodeType
{
UserNode = 0,
RouteNode = 1,
CCNode = 2,
SubFlowNode = 3, // 子流程节点
}前端流程设计器(FlowDesignerV2)将上述模式映射为可拖拽图元:
设计器图元 | mode 值 | 形状 |
|---|---|---|
线性 | 0 | 矩形 |
分流 | 2 | 下宽梯形 |
合流 | 1 | 上宽梯形 |
分合流 | 3 | 六角形 |
同表单 | 4 | 梯形变体 |
异表单 | 5 | 梯形变体 |
子流程 | NodeType=3 | 独立图元 |
该平台流程发送的核心入口是 WorkNode.NodeSend(),采用三阶段处理:
RunModel × 目标节点 RunModel 分支 /// 1,方法体分为三大部分: 发送前检查\5*5算法\发送后的业务处理.关键实例字段:
字段 | 表 | 含义 |
|---|---|---|
WorkID | WF_GenerWorkFlow | 流程实例主键 |
FID | 同上 | 父流程 WorkID(子线程挂接干流程) |
PWorkID / PFlowNo / PNodeID | 同上 | 父子流程关联 |
FK_Node | 同上 | 当前停留节点 |
IsPass | WF_GenerWorkerList | 待办完成状态 |
flowchart LR
subgraph Linear["① 线性流程"]
A1[节点A] --> A2[节点B] --> A3[节点C]
end
subgraph SameSheet["② 同表单分合流"]
B1[分流点 FL] --> B2[同表单线程1]
B1 --> B3[同表单线程2]
B2 --> B4[合流点 HL]
B3 --> B4
end
subgraph UnSameSheet["③ 异表单分合流"]
C1[分流点] --> C2[异表单线程+独立WorkID]
C1 --> C3[异表单线程+独立WorkID]
C2 --> C4[合流点]
C3 --> C4
end
subgraph SubFlow["④ 父子流程"]
D1[主流程节点] --> D2[子流程节点]
D2 --> D3[子流程实例]
D3 -.等待.-> D1
end最常见的串行审批:发起 → 部门审批 → 领导审批 → 归档。
HisRunModel = Ordinary,HisNodeWorkType = Work(或 StartWork)NodeSend_11():计算下一节点 → FindWorker 找人 → 写 GenerWorkerList 待办 switch (this.HisNode.HisRunModel)
{
case RunModel.Ordinary:
Node toND = this.NodeSend_GenerNextStepNode();
...
switch (toND.HisRunModel)
{
case RunModel.Ordinary:
this.NodeSend_11(toND);
break;该平台 | Flowable/Camunda |
|---|---|
NodeSend_11 串行推进 | Sequence Flow 顺序流 |
WF_Cond 方向条件 | Exclusive Gateway 或 Sequence Flow 条件表达式 |
GenerWorkerList 单人待办 | ACT_RU_TASK User Task |
两者在线性场景能力相当;Flowable 以 BPMN 图表达,该平台以 WF_Node + WF_Direction 关系表表达。
一张主表(如采购申请单),按明细行或按部门拆成多条并行子线程,各线程处理人不同,但共享同一份主表数据,合流后继续主流程。
典型场景:采购明细按供应商分给不同采购员并行询价,合流后统一审批。
拓扑要求:分流点(FL/FHL)→ 同表单子线程节点 → 合流点(HL/FHL)
FID 机制:子线程 Work.FID = 干流程 WorkID,多个子线程共享数据上下文。
/// 处理分流点向下发送 to 同表单.
private void NodeSend_24_SameSheet(Node toNode)
{
Work wk = toNode.HisWork;
wk.Copy(this.rptGe);
wk.Copy(this.HisWork);
wk.FID = this.WorkID; // 把该工作FID设置成干流程上的工作ID
town = new WorkNode(wk, toNode);
current_gwls = this.Func_GenerWorkerLists(town);找人策略(DeliveryWay)支持:
ByDtlAsSubThreadEmps:按上一节点明细表字段确定子线程接收人BySQLAsSubThreadEmpsAndData:按 SQL 确定接收人与子线程数据前端配置入口:``
该平台同表单分合流 | Flowable/Camunda 等价物 |
|---|---|
FID 共享主表 | Multi-Instance 同一变量作用域 |
NodeSend_24_SameSheet | Parallel Gateway + Multi-Instance User Task(sequential=false) |
明细表驱动子线程 | Multi-Instance collection 表达式(如 ${detailList}) |
合流等待全部完成 | Parallel Gateway Join(默认全部到达) |
差异:该平台将「同表单 + FID」作为一等公民,表单引擎 Sys_MapData 原生识别 FID 字段;Flowable 需自行设计流程变量与表单绑定,明细表驱动多实例需额外开发。
分流后各子线程使用不同表单(甚至不同 WorkID),并行处理后合流。例如:项目立项后,技术、财务、法务各自填独立审批表。
核心方法 NodeSend_24_UnSameSheet(Nodes toNDs):
GenerWorkFlow 实例USSWorkIDRole 控制 WorkID 生成规则:0:同一节点仅生成一个 WorkID(多人共享)1:按接收人各生成一个 WorkIDFID 关联干流程 /// 处理分流点向下发送 to 异表单.
private void NodeSend_24_UnSameSheet(Nodes toNDs)
{
GenerWorkFlows gwfThreads = new GenerWorkFlows();
gwfThreads.Retrieve(GenerWorkFlowAttr.FID, this.WorkID);
...
if (nd.USSWorkIDRole == 0)
{
...
if (workIDSubThread == 0)
workIDSubThread = DBAccess.GenerOID("WorkID");发送时 FID/WorkID 切换逻辑(子线程 ↔ 合流点):
// 是否是分流到子线程
if (this.HisNode.ItIsFLHL == true && town.HisNode.ItIsSubThread == true)
{
nextUsersWorkID = 0;
nextUsersFID = this.WorkID;
}
// 子线程到合流点
if (this.HisNode.ItIsSubThread == true && town.HisNode.ItIsFLHL == true)
{
nextUsersWorkID = this.HisWork.FID;
nextUsersFID = 0;
}该平台异表单分合流 | Flowable/Camunda |
|---|---|
每子线程独立 Sys_MapData 表单 | 每分支独立 User Task + 独立 Form Key |
USSWorkIDRole 控制实例粒度 | Multi-Instance 或多次 Call Activity |
NodeSend_24_UnSameSheet | Parallel Gateway + 多个并行 User Task |
合流后数据汇总 CheckFrmHuiZongToDtl | 需 ExecutionListener 手动聚合变量 |
差异:该平台在合流点内置异表单向合流节点明细表的数据汇总(CheckFrmHuiZongToDtl);Flowable/Camunda 无内置表单汇总,需自定义 Listener 或外部服务。
主流程某一节点触发完整子流程,子流程结束后父流程才继续。支持:
子流程类型 | SubFlowType | 说明 |
|---|---|---|
手动子流程 | HandSubFlow | 用户选择启动 |
自动子流程 | AutoSubFlow | 到达节点自动触发 |
延续子流程 | YanXuFlow | 子流程结束后回到父流程指定节点 |
子流程层级 | SubFlowModel | 说明 |
|---|---|---|
下级子流程 | SubLevel | PWorkID 指向父流程 WorkID |
同级子流程 | SameLevel | 与父流程共享上级上下文 |
public enum SubFlowModel
{
SubLevel, // 下级
SameLevel, // 同级
}子流程启动(WF_WorkOpt.SubFlowGuid_Send):
public void SendSingleSubFlow(SubFlowHand subFlowH, Hashtable ht, GenerWorkFlow gwf)
{
if (subFlowH.SubFlowModel == SubFlowModel.SubLevel)
workidSubFlow = BP.WF.Dev2Interface.Node_CreateBlankWork(..., this.WorkID, this.FID, ...);
if (subFlowH.SubFlowModel == SubFlowModel.SameLevel)
workidSubFlow = BP.WF.Dev2Interface.Node_CreateBlankWork(..., gwf.PWorkID, gwf.PFID, ...);
BP.WF.Dev2Interface.SetParentInfo(subFlowH.SubFlowNo, workidSubFlow, ...);
BP.WF.Dev2Interface.Node_SendWork(subFlowH.SubFlowNo, workidSubFlow);父流程等待规则(SubFlowRunModel):子流程全部结束 / 到达指定节点后,父流程才发送。
子流程启动后,父流程当前待办可设为不可见(IsPass=80),用户从在途查看进度。
该平台父子流程 | Flowable/Camunda |
|---|---|
SubFlowNode + SubFlowHand 配置 | Call Activity 或 Embedded Sub-Process |
PWorkID / PFlowNo / PNodeID | superProcessInstanceId(历史表) |
手动/自动/延续三种类型 | Call Activity 的 async、触发器配置 |
子流程数据反填父流程 BackCopyRole | 需 Output Mapping / 变量传递 |
父流程待办隐藏 | 需自行实现或 BPMN Event Sub-Process |
差异:该平台父子流程与表单数据反填规则(BackCopyRole:自动字段匹配、按格式匹配、混合模式)深度集成;Flowable Call Activity 只做流程层面嵌套,业务数据回填需额外开发。
维度 | 该平台 BPM | Flowable | Camunda |
|---|---|---|---|
建模标准 | 自研节点表 + 设计器 | BPMN 2.0 XML | BPMN 2.0 XML |
线性流程 | Ordinary + NodeSend_11 | Sequence Flow | Sequence Flow |
并行分合流 | FL/HL/FHL + 5×5 算法 | Parallel Gateway | Parallel Gateway |
同表单多实例 | SubThreadSameWorkID + FID | Multi-Instance(共享作用域) | Multi-Instance |
异表单多实例 | SubThreadUnSameWorkID | 多 User Task 并行 | 多 User Task 并行 |
子流程 | SubFlowNode(手动/自动/延续) | Call Activity | Call Activity |
明细表驱线程 | ByDtlAsSubThreadEmps 内置 | 需 collection 表达式 | 需 collection 表达式 |
合流数据汇总 | CheckFrmHuiZongToDtl 内置 | 无,需 Listener | 无,需 Listener |
子流程数据反填 | BackCopyRole 内置 | 变量映射 | 变量映射 |
流程轨迹 | WF_Track + ActionType 分流/合流/子线程 | ACT_HI_ACTINST | Operate 历史 |
表单引擎 | Sys_MapData 内置 | 无(外部集成) | 无(外部集成) |
Vue 3 + Vite + TypeScript + Ant Design Vue + AntV X6(流程设计器)
模块 | 路径 | 职责 |
|---|---|---|
流程设计器 V2 | `` | 拖拽创建线性/分流/合流/同表单/异表单/子流程节点 |
图元定义 | FlowDesignerV2/shapes/index.ts | mode: 0~5 对应 RunModel |
节点属性 | WF/Admin/AttrNode/ | 找人策略、子线程规则、子流程配置 |
流程办理 | WF/MyFlow.vue、MyFlowGener.vue | 传递 WorkID、FID、PWorkID |
审核轨迹 | WF/WorkOpt/WorkCheckParseTrack.vue | 展示分流前进/合流前进/子线程前进 |
子流程操作 | WF/WorkOpt/ | 子流程发起、删除草稿 |
嵌入 SDK | Toolkit/WorkOpt/ | 第三方系统嵌入分合流审批 |
移动端 | CCMobile/MyFlow*.vue | 移动端办理 |
设计器保存节点时写入 RunModel 字段,与后端 WF_Node.RunModel 一致:
// FlowDesignerV2/shapes/index.ts
{ label: '线性', attrs: { typeInfo: { mode: 0 } } }
{ label: '分流', attrs: { typeInfo: { mode: 2 } } }
{ label: '合流', attrs: { typeInfo: { mode: 1 } } }
{ label: '同表单', attrs: { typeInfo: { mode: 4 } } }
{ label: '异表单', attrs: { typeInfo: { mode: 5 } } }分流场景下,前端需正确传递 FID(干流程)与 WorkID(子线程):
if ((currND.HisRunModel == RunModel.FL || currND.HisRunModel == RunModel.FHL) && this.FID != 0)
urlExt += "WorkID=" + this.FID + "&SubWorkID=" + this.WorkID;审核轨迹前端区分动作类型:
ActionType.ForwardFL — 分流前进ActionType.ForwardHL — 合流前进ActionType.SubThreadForward — 子线程前进统一通过 HttpHandler 调用后端 Handler:
const handler = new HttpHandler('BP.WF.HttpHandler.WF_MyFlow');
handler.AddPara('WorkID', workID);
handler.AddPara('FID', fid);
const data = await handler.DoMethod('MyFlow_Init');该引擎/该引擎/ → Web 宿主、API Controller
该引擎/Components/BP.WF/ → 流程引擎核心
├── WF/WorkNode.cs → 发送引擎(5×5 算法)
├── WF/GenerWorkFlow.cs → 流程实例
├── WF/GenerWorkerList.cs → 待办任务
├── WF/WorkUnSend.cs → 撤销/退回
├── Template/FindWorker.cs → 找人(含子线程策略)
├── Template/SFlow/ → 子流程配置
├── Dev2Interface.cs → 对外 SDK
└── HttpHandler/WF_MyFlow.cs → 办理页接口
该引擎/Components/BP.En30/ → 实体框架、表单引擎模式 | 核心类/方法 | 数据表 |
|---|---|---|
线性 | WorkNode.NodeSend_11() | WF_GenerWorkFlow、WF_GenerWorkerList |
同表单分合流 | WorkNode.NodeSend_24_SameSheet() | 同上 + FID 关联 |
异表单分合流 | WorkNode.NodeSend_24_UnSameSheet() | 多 WorkID + FID |
父子流程 | WF_WorkOpt.SendSingleSubFlow() | PWorkID、PFlowNo、PNodeID |
合流汇总 | WorkNode.CheckFrmHuiZongToDtl() | 合流节点明细表 |
子流程反填 | BackCopyRole | 父流程表单字段 |
方式 | 入口 | 适用 |
|---|---|---|
SDK | Dev2Interface.Node_SendWork() | 代码集成 |
REST | APIController | 跨语言调用 |
HttpHandler | WF_MyFlow.MyFlow_Init() | Vue 前端 |
该引擎 侧与 该引擎 同源设计(类名、方法名、表结构一致),Java 团队可无缝使用相同模式语义。
维度 | 说明 |
|---|---|
四大模式原生支持 | 线性/同表单/异表单/父子流程均为引擎内置路径,非插件 |
表单 + 流程一体 | 分合流、子流程与 Sys_MapData 表单引擎深度耦合,无需拼装 |
明细表驱动子线程 | ByDtlAsSubThreadEmps 一行配置即可按明细拆线程 |
合流数据自动汇总 | CheckFrmHuiZongToDtl 异表单向合流节点汇总 |
子流程数据反填 | BackCopyRole 自动/按规则/混合反填父流程 |
5×5 路由算法 | 覆盖普通/分流/合流/子线程任意组合,规则明确 |
中国式审批特性 | 会签、加签、退回、撤销与子线程/子流程协同 |
双栈交付 | 该引擎(.NET) + 该引擎(Java),适配不同甲方技术栈 |
设计器可视化 | FlowDesignerV2 图元直接对应四大模式,业务人员可理解 |
BPMN 导入 | 支持从 Flowable 等工具导入 BPMN(单向转换) |
维度 | 说明 |
|---|---|
非 BPMN 运行时 | 四大模式存于 WF_Node.RunModel,与 BPMN 生态互操作弱 |
5×5 算法复杂度高 | WorkNode.cs 逾万行,二次开发需深入理解 FID/WorkID 切换 |
标准 API | HttpHandler 协议非 RESTful 标准,国际化集成成本高 |
超大并行度 | 分合流基于 DB 轮询合流条件,极高并发不如事件驱动引擎 |
文档碎片化 | 四大模式语义需结合源码理解,官方文档不如 Flowable 系统 |
维度 | 说明 |
|---|---|
BPMN 2.0 标准 | 并行网关、多实例、调用活动均为标准元素,模型可交换 |
生态成熟 | Camunda Modeler、Flowable Modeler、大量社区案例 |
微服务友好 | 引擎轻量,易嵌入 Spring Cloud / K8s |
DMN 决策 | Camunda 支持决策表(该平台无原生 DMN) |
外部任务 | 长耗时异步任务模式成熟 |
维度 | 说明 |
|---|---|
同/异表单分合流 | 需组合 Gateway + Multi-Instance + 表单集成,配置复杂 |
明细表驱线程 | 无开箱即用,需写 collection 表达式 + 业务表查询 |
合流数据汇总 | 无内置,需 Listener 开发 |
子流程数据反填 | 仅变量映射,无表单字段级自动匹配 |
交付周期 | 从引擎到可上线 OA,需集成表单、组织、权限多个系统 |
业务需求 | 推荐 | 理由 |
|---|---|---|
简单串行审批 | 三者均可 | 线性模式无本质差异 |
同一张表按明细/部门并行 | 该引擎 | SubThreadSameWorkID + 明细表驱线程开箱即用 |
多部门独立表单并行 | 该引擎 | SubThreadUnSameWorkID + 合流汇总内置 |
主流程嵌套子项目流程 | 该引擎 | 手动/自动/延续子流程 + 数据反填 |
流程即代码、GitOps | Flowable/Camunda | BPMN XML 版本管理 |
百万级实例 / 事件驱动 | Camunda 8 | Zeebe 分布式架构 |
已有 BPMN 资产迁移 | Flowable | BPMN 原生运行时 |
政府/国企全栈 OA | 该引擎 | 四大模式 + 表单 + 组织一体交付 |
您的流程是否涉及「分流 + 多表单 + 合流汇总」或「父子流程数据反填」?
├── 是 → 强烈推荐 该引擎 / 该引擎
│ ├── .NET 技术栈 → 该引擎
│ └── Java 技术栈 → 该引擎
└── 否 → 是否以 BPMN 标准 / 微服务编排为核心?
├── 是 → Flowable 或 Camunda
└── 否 → 是否需要完整 OA(表单+组织+权限)?
├── 是 → 该引擎 / 该引擎(BPM 模式)
└── 否 → 按团队技术栈在 Flowable 与 该引擎 间选择综合源码分析与四大模式对照,在中文企业审批、OA、项目协同场景下,应优先选择 该引擎 / 该引擎,理由如下:
NodeSend_11 / _24_SameSheet / _24_UnSameSheet / SendSingleSubFlow),落地成本远低于在 Flowable 中拼装 Gateway + Multi-Instance + Call Activity + 自定义 Listener。
Sys_MapData 与 FID、PWorkID 原生配合,明细表驱线程、合流汇总、子流程反填均为配置级能力;Flowable/Camunda 无表单引擎,四大模式落地周期通常延长 2~5 倍。
Toolkit SDK 嵌入现有 ERP,无需全盘替换。
一句话结论: 「分合流 + 多表单 + 子流程」是中国 OA 的刚需,这是 该引擎 的设计原点,也是 Flowable/Camunda 需要大量定制才能补齐的短板。
主题 | 文件 |
|---|---|
RunModel 枚举 | 该引擎/Components/BP.WF/EnumLib.cs |
5×5 发送算法 | 该引擎/Components/BP.WF/WF/WorkNode.cs |
同表单分合流 | WorkNode.NodeSend_24_SameSheet() |
异表单分合流 | WorkNode.NodeSend_24_UnSameSheet() |
子流程模型 | 该引擎/Components/BP.WF/Template/SFlow/FrmSubFlow.cs |
子流程发送 | 该引擎/Components/BP.WF/HttpHandler/WF_WorkOpt.cs |
流程办理 | 该引擎/Components/BP.WF/HttpHandler/WF_MyFlow.cs |
找人(子线程) | 该引擎/Components/BP.WF/Template/FindWorker.cs |
对外 SDK | 该引擎/Components/BP.WF/Dev2Interface.cs |
设计器图元 | `` |
子线程找人配置 | `` |
审核轨迹 | `` |
流程办理页 | `` |
本文基于 该引擎WorkSpace 仓库静态分析生成。该引擎 与 该引擎 在四大流程模式上保持架构同源,具体 API 以各平台官方文档为准。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。