驰骋 BPM(CCFlow/JFlow)四大运行模式与 Flowable/Camunda 技术对比
一、概述
1.1 驰骋 BPM 的产品定位
驰骋 BPM 由 CCFlow(.NET) 与 JFlow(Java) 双平台构成,是一套流程引擎 + 表单引擎 + 低代码平台的一体化 BPM 产品。与 Flowable/Camunda 这类"纯流程引擎"不同,驰骋在引擎之上内置了组织架构、菜单权限、单据(CCBill)、门户、报表等完整企业应用能力。
代码中通过 Platform 枚举区分运行平台:
295:305:CCFlow/Components/BP.WF/EnumLib.cs
public enum Platform
{
/// <summary>
/// CCFlow .net平台.
/// </summary>
CCFlow,
/// <summary>
/// JFlow java 平台.
/// </summary>
JFlow
}
1.2 两个维度的"模式"概念(勿混淆)
| 维度 | 配置项 | 含义 |
|---|---|---|
| 部署/组织模式 | CCBPMRunModel(0/1/2/3) |
单机、集团、SaaS、平台应用 |
| 产品使用模式 | 门户/Middleware vs BPM | 中间件模式 vs 全功能 BPM 模式 |
门户说明(Vue3/src/Portal/readme.txt):
- 中间件模式:客户仅使用流程引擎 + 表单引擎,菜单精简(发起、待办、在途、草稿等)。
- BPM 模式:完整菜单体系、权限管理、低代码设计器,可构建独立业务系统。
本文重点分析 四大运行模式(CCBPMRunModel) 的技术实现,并与 Flowable/Camunda 的多租户/组织架构能力对照。
二、四大运行模式定义与配置
2.1 枚举与配置入口
后端枚举定义(BP.Sys.CCBPMRunModel):
12:30:CCFlow/Components/BP.En30/Sys/EnumLab.cs
public enum CCBPMRunModel
{
/// <summary>
/// 单机版
/// </summary>
Single,
/// <summary>
/// 集团模式
/// </summary>
GroupInc,
/// <summary>
/// 多租户模式
/// </summary>
SAAS,
/// <summary>
/// 平台应用模式
/// </summary>
PASS
}
app.config 配置项:
xml
<!-- 0=单组织,1=集团,2=SAAS,3=平台应用(PASS) -->
<add key="CCBPMRunModel" value="0"/>
| 值 | 模式 | 英文标识 | 典型场景 |
|---|---|---|---|
| 0 | 单机版 | Single | 单一企业、内网 OA、POC 演示 |
| 1 | 集团版 | GroupInc | 集团总部 + 多家子公司共用一套 BPM |
| 2 | 多租户 SaaS | SAAS | 云 BPM 平台,租户数据强隔离 |
| 3 | 平台应用 | PASS | 云平台上的应用托管模式(CCBPMRunModelReal 识别,部分逻辑仍回落到 Single) |
2.2 安装初始化差异
安装时按模式加载不同组织种子数据 SQL:
337:355:CCFlow/Components/BP.WF/Install.cs
sqlscript = BP.Difference.SystemConfig.CCFlowAppPath + "WF/Data/Install/SQLScript/Port_Inc_CH_RunModel_" + (int)SystemConfig.CCBPMRunModel + ".sql";
DBAccess.RunSQLScript(sqlscript);
if (SystemConfig.CCBPMRunModel == CCBPMRunModel.Single)
{
BP.Port.Emp empAdmin = new Emp("admin");
BP.Web.WebUser.SignInOfGener(empAdmin);
}
if (SystemConfig.CCBPMRunModel == CCBPMRunModel.SAAS)
{
BP.WF.Dev2Interface.Port_Login("admin", "100", null);
}
if (SystemConfig.CCBPMRunModel == CCBPMRunModel.GroupInc)
{
BP.WF.Dev2Interface.Port_Login("admin", "100", null);
}
- Single :直接以
admin登录,无多组织概念。 - GroupInc / SAAS :以平台组织
100的admin登录,需选择/切换组织。
三、四大模式技术实现对比
3.1 单机版(Single)
核心特征 :无 OrgNo 数据隔离,人员编号全局唯一(如 zhangsan)。
后端实现要点:
SystemConfig.CCBPMRunModel == Single时,SQL 查询不加OrgNo条件。WebUser.RootNo对管理员返回"0",表示无组织根节点概念。- 待办统计、流程查询等直接按
FK_Emp过滤。
52:53:CCFlow/Components/BP.WF/Dev2Interface.cs
if (BP.Difference.SystemConfig.CCBPMRunModel == CCBPMRunModel.SAAS)
wfSql = " AND A.OrgNo='" + WebUser.OrgNo + "'";
(Single 模式下 wfSql 为空,不加 OrgNo 过滤。)
前端实现要点:
SystemConfig.CCBPMRunModel来自 Cookie/LocalStorage。- 人员选择、表单树、流程树等 SQL 数据源使用 Single 后缀 的全局查询(见
GloWF.ts中*Single系列 SFTable)。 - 中间件门户不展示集团专属菜单(如"管理组织"、"切换部门")。
对比 Flowable/Camunda :等价于单租户部署 ,不启用 tenantId。组织架构需自行对接 IDM 或在应用层维护。
3.2 集团版(GroupInc)
核心特征 :一套 BPM 实例服务多个法人/子公司,通过 Port_Org + OrgNo 字段实现逻辑隔离,人员编号在组织内唯一。
数据模型 (Port_Inc_CH_RunModel_1.sql 种子数据):
Port_Org:组织主表(如100集团平台、ccs驰骋公司、quanyi泉亿公司)。Port_Dept、Port_Station、Port_Emp均带OrgNo字段。- 人员
No在组织内唯一(如yuwen),跨组织可重复。
后端隔离机制:
- 查询过滤 :非 Single 模式在流程、人员、部门查询中追加
OrgNo条件。 - 组织管理员 :
Port_OrgAdminer表维护二级管理员;WebUser.IsAdmin按OrgNo判断。 - 集团岗责体系 (
GroupStationModel配置):0:每套组织独立角色体系(默认)。1:全集团共享一套岗责体系。2:每个部门独立角色体系。
924:934:CCFlow/Components/BP.En30/Difference/SystemConfig.cs
/// <summary>
/// 集团模式下的角色体系
/// @0=每套组织都有自己的角色体系@1=所有的组织共享一套岗则体系.@2=每个部门有自己的角色体系.
/// </summary>
public static int GroupStationModel
- 找人算法 (
FindWorker.cs):按直属领导找人时,GroupInc 追加And OrgNo = '@WebUser.OrgNo'。
338:343:CCFlow/Components/BP.WF/Template/FindWorker.cs
if (BP.Difference.SystemConfig.CCBPMRunModel == CCBPMRunModel.Single)
sql = "SELECT Leader,FK_Dept FROM Port_Emp WHERE No='" + empNo + "'";
if (BP.Difference.SystemConfig.CCBPMRunModel == CCBPMRunModel.GroupInc)
sql = "SELECT Leader,FK_Dept FROM Port_Emp WHERE No='" + empNo + "' And OrgNo = '" + WebUser.OrgNo + "'";
if (BP.Difference.SystemConfig.CCBPMRunModel == CCBPMRunModel.SAAS)
sql = "SELECT Leader,FK_Dept FROM Port_Emp WHERE No='" + WebUser.OrgNo + "_" + empNo + "'";
前端实现要点:
- 集团版用户菜单增加"管理组织(AdminOnly)"、"切换部门"(
PopMenuUser.ts)。 - 流程查询、单据搜索在
GroupInc下追加组织过滤(SearchFlow.vue、useSearchBill.ts)。 - 菜单、表单树按
OrgNo隔离:OrgNo='@WebUser.OrgNo'。
对比 Flowable/Camunda:
| 能力 | 驰骋 GroupInc | Flowable | Camunda |
|---|---|---|---|
| 多组织 | Port_Org + OrgNo 字段贯穿全表 |
需自建或借助 Flowable Platform | Camunda 8 有 Tenant 概念 |
| 组织切换 | 内置 Port_Login(user, orgNo) |
无内置 | Tenant 切换需自行实现 |
| 流程定义隔离 | WF_Flow 可按组织复制/共享 |
tenantId 隔离部署 |
tenantId 隔离 |
| 岗责体系 | 内置 Port_Station,支持三种集团策略 |
候选组 candidateGroups |
candidateGroups / Identity Service |
驰骋集团版的优势在于组织、岗责、流程、表单在同一产品内闭环;Flowable/Camunda 需额外集成 Identity/Org 模块。
3.3 多租户 SaaS 版(SAAS)
核心特征 :面向云 BPM 平台,租户间强隔离 ,人员主键采用 {OrgNo}_{UserID} 复合编号。
与集团版的关键差异:
| 项目 | GroupInc | SAAS |
|---|---|---|
| 人员编号 | 组织内唯一,如 yuwen |
全局唯一,如 ccs_yuwen |
| UserID 字段 | 可选 | 必须,存真实登录名 |
| 流程实例 | 部分表加 OrgNo | WF_GenerWorkFlow 等强制 OrgNo |
| 登录 | 选组织后登录 | Port_Login("admin", "100", null) 平台管理员 |
后端实现 (Emp.cs):
106:130:CCFlow/Components/BP.En30/Port/Emp.cs
if (value.StartsWith(BP.Web.WebUser.OrgNo + "_") == true)
{
this.SetValByKey(EmpAttr.UserID, value.Replace(BP.Web.WebUser.OrgNo + "_", ""));
}
else
{
this.SetValByKey(EmpAttr.No, BP.Web.WebUser.OrgNo + "_" + value);
}
SAAS 模式下实体 Map 自动注入隐藏条件 OrgNo = @WebUser.OrgNo(Station.cs、Team.cs 等)。
流程统计统一追加 OrgNo:
4680:4688:CCFlow/Components/BP.WF/Dev2Interface.cs
public static String Check_CCBPMRunModel()
{
String sql = "";
if (BP.Difference.SystemConfig.CCBPMRunModel == CCBPMRunModel.SAAS)
{
sql += " AND A.OrgNo = '" + WebUser.OrgNo + "'";
}
return sql;
}
前端实现要点:
WebUser.CCBPMRunModel == 2时,审核组件走云 Handler:BP.Cloud.HttpHandler.App。- 用户头像路径含
OrgNo前缀。 - 数据源 SQL 使用
*SaaS后缀版本,按OrgNo过滤。
对比 Flowable/Camunda:
- Flowable :企业版支持 Multi-tenancy(
tenantId贯穿引擎表);开源版需应用层隔离。 - Camunda 8:原生多租户(Tenant),与 Zeebe 集群集成。
- 驰骋 SAAS :租户隔离下沉到全产品线(组织、表单、流程、单据、菜单),不仅是引擎表隔离。
驰骋 SaaS 的不足:OrgNo 多以 SQL 字符串拼接实现隔离,需严格防范 SQL 注入;Flowable/Camunda 的 tenantId 在引擎层有框架级保障。
3.4 平台应用模式(PASS)
核心特征 :面向云平台应用商店/托管 场景,枚举值为 3,CCBPMRunModelReal 返回 PASS,但 CCBPMRunModel 属性将 0 和 3 均映射为 Single 以兼容旧逻辑。
215:250:CCFlow/Components/BP.En30/Difference/SystemConfig.cs
public static CCBPMRunModel CCBPMRunModelReal
{
get
{
int val = BP.Difference.SystemConfig.GetValByKeyInt("CCBPMRunModel", 0);
if (val == 0 ) return CCBPMRunModel.Single;
if (val == 1) return CCBPMRunModel.GroupInc;
if (val == 2) return CCBPMRunModel.SAAS;
return CCBPMRunModel.PASS;
}
}
public static CCBPMRunModel CCBPMRunModel
{
get
{
int val = BP.Difference.SystemConfig.GetValByKeyInt("CCBPMRunModel", 0);
if (val == 0 || val==3) return CCBPMRunModel.Single;
...
}
}
PASS 模式在代码库中落地较少,前端 SystemConfig.ts 甚至未导出 PASS 枚举,属于演进中的模式,与 SaaS 云平台的应用分发场景配套。
对比 Flowable/Camunda :类似 Camunda SaaS 或 Flowable Cloud 的应用市场 + 租户双层模型,但驰骋 PASS 尚未形成与 Flowable Platform 同量级的独立产品线文档。
四、架构对照:驰骋 BPM vs Flowable/Camunda
4.1 总体架构图
#mermaid-svg-vaGClo6KyGNibhR3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vaGClo6KyGNibhR3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vaGClo6KyGNibhR3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vaGClo6KyGNibhR3 .error-icon{fill:#552222;}#mermaid-svg-vaGClo6KyGNibhR3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vaGClo6KyGNibhR3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vaGClo6KyGNibhR3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vaGClo6KyGNibhR3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vaGClo6KyGNibhR3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vaGClo6KyGNibhR3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vaGClo6KyGNibhR3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vaGClo6KyGNibhR3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vaGClo6KyGNibhR3 .marker.cross{stroke:#333333;}#mermaid-svg-vaGClo6KyGNibhR3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vaGClo6KyGNibhR3 p{margin:0;}#mermaid-svg-vaGClo6KyGNibhR3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vaGClo6KyGNibhR3 .cluster-label text{fill:#333;}#mermaid-svg-vaGClo6KyGNibhR3 .cluster-label span{color:#333;}#mermaid-svg-vaGClo6KyGNibhR3 .cluster-label span p{background-color:transparent;}#mermaid-svg-vaGClo6KyGNibhR3 .label text,#mermaid-svg-vaGClo6KyGNibhR3 span{fill:#333;color:#333;}#mermaid-svg-vaGClo6KyGNibhR3 .node rect,#mermaid-svg-vaGClo6KyGNibhR3 .node circle,#mermaid-svg-vaGClo6KyGNibhR3 .node ellipse,#mermaid-svg-vaGClo6KyGNibhR3 .node polygon,#mermaid-svg-vaGClo6KyGNibhR3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vaGClo6KyGNibhR3 .rough-node .label text,#mermaid-svg-vaGClo6KyGNibhR3 .node .label text,#mermaid-svg-vaGClo6KyGNibhR3 .image-shape .label,#mermaid-svg-vaGClo6KyGNibhR3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-vaGClo6KyGNibhR3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vaGClo6KyGNibhR3 .rough-node .label,#mermaid-svg-vaGClo6KyGNibhR3 .node .label,#mermaid-svg-vaGClo6KyGNibhR3 .image-shape .label,#mermaid-svg-vaGClo6KyGNibhR3 .icon-shape .label{text-align:center;}#mermaid-svg-vaGClo6KyGNibhR3 .node.clickable{cursor:pointer;}#mermaid-svg-vaGClo6KyGNibhR3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vaGClo6KyGNibhR3 .arrowheadPath{fill:#333333;}#mermaid-svg-vaGClo6KyGNibhR3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vaGClo6KyGNibhR3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vaGClo6KyGNibhR3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vaGClo6KyGNibhR3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vaGClo6KyGNibhR3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vaGClo6KyGNibhR3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vaGClo6KyGNibhR3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vaGClo6KyGNibhR3 .cluster text{fill:#333;}#mermaid-svg-vaGClo6KyGNibhR3 .cluster span{color:#333;}#mermaid-svg-vaGClo6KyGNibhR3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vaGClo6KyGNibhR3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vaGClo6KyGNibhR3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-vaGClo6KyGNibhR3 .icon-shape,#mermaid-svg-vaGClo6KyGNibhR3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vaGClo6KyGNibhR3 .icon-shape p,#mermaid-svg-vaGClo6KyGNibhR3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vaGClo6KyGNibhR3 .icon-shape .label rect,#mermaid-svg-vaGClo6KyGNibhR3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vaGClo6KyGNibhR3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vaGClo6KyGNibhR3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vaGClo6KyGNibhR3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据库
后端 CCFlow (.NET) / JFlow (Java)
前端 Vue3
Portal 门户
Middleware / BPM
WF 流程模块
CCFast 低代码
Toolkit SDK
HttpHandler.ts
APIController / InnerForVueController
HttpHandler WF_*
Dev2Interface SDK
WorkNode 流程引擎
Port 组织架构
Sys_MapData 表单引擎
CCBill 单据引擎
WF_Flow / WF_Node 流程定义
WF_GenerWorkFlow 流程实例
WF_GenerWorkerList 待办任务
Port_* 组织人员
#mermaid-svg-PyAwlIcTdwmtix8a{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PyAwlIcTdwmtix8a .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PyAwlIcTdwmtix8a .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PyAwlIcTdwmtix8a .error-icon{fill:#552222;}#mermaid-svg-PyAwlIcTdwmtix8a .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PyAwlIcTdwmtix8a .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PyAwlIcTdwmtix8a .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PyAwlIcTdwmtix8a .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PyAwlIcTdwmtix8a .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PyAwlIcTdwmtix8a .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PyAwlIcTdwmtix8a .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PyAwlIcTdwmtix8a .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PyAwlIcTdwmtix8a .marker.cross{stroke:#333333;}#mermaid-svg-PyAwlIcTdwmtix8a svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PyAwlIcTdwmtix8a p{margin:0;}#mermaid-svg-PyAwlIcTdwmtix8a .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PyAwlIcTdwmtix8a .cluster-label text{fill:#333;}#mermaid-svg-PyAwlIcTdwmtix8a .cluster-label span{color:#333;}#mermaid-svg-PyAwlIcTdwmtix8a .cluster-label span p{background-color:transparent;}#mermaid-svg-PyAwlIcTdwmtix8a .label text,#mermaid-svg-PyAwlIcTdwmtix8a span{fill:#333;color:#333;}#mermaid-svg-PyAwlIcTdwmtix8a .node rect,#mermaid-svg-PyAwlIcTdwmtix8a .node circle,#mermaid-svg-PyAwlIcTdwmtix8a .node ellipse,#mermaid-svg-PyAwlIcTdwmtix8a .node polygon,#mermaid-svg-PyAwlIcTdwmtix8a .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PyAwlIcTdwmtix8a .rough-node .label text,#mermaid-svg-PyAwlIcTdwmtix8a .node .label text,#mermaid-svg-PyAwlIcTdwmtix8a .image-shape .label,#mermaid-svg-PyAwlIcTdwmtix8a .icon-shape .label{text-anchor:middle;}#mermaid-svg-PyAwlIcTdwmtix8a .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PyAwlIcTdwmtix8a .rough-node .label,#mermaid-svg-PyAwlIcTdwmtix8a .node .label,#mermaid-svg-PyAwlIcTdwmtix8a .image-shape .label,#mermaid-svg-PyAwlIcTdwmtix8a .icon-shape .label{text-align:center;}#mermaid-svg-PyAwlIcTdwmtix8a .node.clickable{cursor:pointer;}#mermaid-svg-PyAwlIcTdwmtix8a .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PyAwlIcTdwmtix8a .arrowheadPath{fill:#333333;}#mermaid-svg-PyAwlIcTdwmtix8a .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PyAwlIcTdwmtix8a .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PyAwlIcTdwmtix8a .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PyAwlIcTdwmtix8a .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PyAwlIcTdwmtix8a .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PyAwlIcTdwmtix8a .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PyAwlIcTdwmtix8a .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PyAwlIcTdwmtix8a .cluster text{fill:#333;}#mermaid-svg-PyAwlIcTdwmtix8a .cluster span{color:#333;}#mermaid-svg-PyAwlIcTdwmtix8a div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PyAwlIcTdwmtix8a .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PyAwlIcTdwmtix8a rect.text{fill:none;stroke-width:0;}#mermaid-svg-PyAwlIcTdwmtix8a .icon-shape,#mermaid-svg-PyAwlIcTdwmtix8a .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PyAwlIcTdwmtix8a .icon-shape p,#mermaid-svg-PyAwlIcTdwmtix8a .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PyAwlIcTdwmtix8a .icon-shape .label rect,#mermaid-svg-PyAwlIcTdwmtix8a .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PyAwlIcTdwmtix8a .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PyAwlIcTdwmtix8a .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PyAwlIcTdwmtix8a :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 引擎表
Flowable / Camunda
REST API
RuntimeService
TaskService
HistoryService
RepositoryService
BPMN 2.0 引擎
IdentityService / 外部 IDM
ACT_RE_PROCDEF
ACT_RU_EXECUTION
ACT_RU_TASK
ACT_HI_* 历史表
4.2 核心概念映射表
| 驰骋 BPM | 数据表/类 | Flowable/Camunda | 说明 |
|---|---|---|---|
| 流程定义 | WF_Flow + WF_Node + WF_Direction |
ACT_RE_PROCDEF(BPMN XML) |
驰骋用关系表存节点;Flowable 用 BPMN 标准 |
| 流程实例 | GenerWorkFlow / WF_GenerWorkFlow |
ACT_RU_EXECUTION / ACT_HI_PROCINST |
驰骋一张宽表承载实例状态 |
| 待办任务 | GenerWorkerList / WF_GenerWorkerList |
ACT_RU_TASK |
驰骋 IsPass 标识任务状态 |
| 流程发送 | WorkNode.NodeSend() |
TaskService.complete() |
驰骋"发送"=审批驱动流转 |
| 人员分配 | FindWorker + DeliveryWay 枚举 |
Candidate Users/Groups | 驰骋内置 20+ 找人策略 |
| 二次开发 API | Dev2Interface 静态方法 |
RuntimeService 等 |
命名规范:Flow_*/Node_*/Port_* |
| Web 接口 | HttpHandler(WF_MyFlow 等) |
REST Controller | 前后端通过 Handler 类名 + 方法名路由 |
| 事件扩展 | ExecEvent.DoNode() |
ExecutionListener/TaskListener | 支持 Java/C# 反射 + JS 脚本 |
| 表单 | Sys_MapData 内置表单引擎 |
无(需集成 Form.io 等) | 驰骋核心差异化能力 |
| 租户 | OrgNo + CCBPMRunModel |
tenantId |
隔离层级与范围不同 |
4.3 流程执行核心路径
驰骋流程发送(WorkNode.NodeSend)三阶段:
- 发送前检查:权限、表单校验、条件判断。
- 5×5 算法:按节点运行模式(普通/分流/合流/子线程)处理路由。
- 发送后业务:抄送、子流程、事件、数据汇总。
7606:7614:CCFlow/Components/BP.WF/WF/WorkNode.cs
/// 工作流发送业务处理.
/// ----------------------------------- 说明 -----------------------------
/// 1,方法体分为三大部分: 发送前检查\5*5算法\发送后的业务处理.
/// 2, 详细请参考代码体上的说明.
/// 3, 发送后可以直接获取它的
Flowable/Camunda 则基于 BPMN Token 推进模型,由引擎解析 BPMN 图自动流转,扩展点主要为 Listener、Delegate、External Task。
4.4 BPMN 支持
驰骋原生建模 使用自研流程设计器(节点表驱动),同时提供 BPMN 2.0 导入能力:
549:595:CCFlow/Components/BP.WF/HttpHandler/WF_Admin_AttrFlow.cs
/// 导入bpmn2.0
public string Imp_DoneBPMN()
...
Flow flow = BP.WF.Template.TemplateGlo.NewFlowByBPMN(FK_FlowSort, filePath);
BPMN 导入后转换为 WF_Node 关系数据,不是 以 BPMN XML 作为运行时定义。Flowable/Camunda 则以 BPMN XML 为唯一真相源(SSOT)。
五、前端代码结构梳理
5.1 技术栈
- 框架:Vue 3 + Vite + TypeScript + Ant Design Vue
- 包管理:pnpm monorepo(turbo)
- 通信 :
HttpHandler类封装后端 Handler 调用
5.2 目录结构与职责
| 目录 | 职责 | 与运行模式关系 |
|---|---|---|
Vue3/src/Portal/ |
登录、门户;Middleware(中间件)与 Standard 两套入口 | 中间件模式精简菜单 |
Vue3/src/WF/ |
流程发起、待办、设计器、报表 | 核心流程 UI |
Vue3/src/CCFast/ |
低代码:GPM 菜单权限、CCBill 单据、门户窗口 | BPM 模式主战场 |
Vue3/src/Toolkit/ |
可嵌入第三方系统的流程 SDK(WorkOpt、WorkCheck) | 中间件集成场景 |
Vue3/src/bp/ |
前端 ORM 层(Entity、EnMap),镜像后端 BP.En30 | 按模式切换 SQL 数据源 |
Vue3/src/App/ |
行业/客户定制应用(LIMS、Star 等) | 客户项目层 |
5.3 前后端通信机制
typescript
// Vue3/src/utils/gener/HttpHandler.ts
const handler = new HttpHandler('BP.WF.HttpHandler.WF_MyFlow');
handler.AddPara('WorkID', workID);
const data = await handler.DoMethod('MyFlow_Init');
请求路由至后端 WF_Comm 或 APIController,通过反射调用 BP.WF.HttpHandler.* 类方法。这与 Flowable 的 /runtime/process-instances RESTful 风格截然不同,更接近 RPC over HTTP。
5.4 运行模式在前端的分支模式
前端通过 SystemConfig.CCBPMRunModel / WebUser.CCBPMRunModel 在以下场景分支:
- 数据源 SQL :
GloWF.ts中*Singlevs 带OrgNo的查询。 - 权限菜单 :
StationMenu.ts非 Single 时隐藏OrgNo条件。 - 审核组件:SAAS 模式切换云 Handler 与头像路径。
- 集团菜单 :
PopMenuUser.ts注入组织管理入口。
六、后端代码结构梳理
6.1 项目模块
| 项目 | 路径 | 职责 |
|---|---|---|
CCFlow |
CCFlow/CCFlow/ |
ASP.NET Core 宿主、API Controller、配置 |
BP.WF |
CCFlow/Components/BP.WF/ |
流程引擎核心(WorkNode、Dev2Interface、HttpHandler) |
BP.En30 |
CCFlow/Components/BP.En30/ |
实体框架、组织架构(Port)、SystemConfig |
BP.App / BP.Trade 等 |
CCFlow/Components/ |
行业扩展 |
6.2 流程引擎分层
HttpHandler (WF_MyFlow, WF_WorkOpt, WF_Comm...)
↓
Dev2Interface (对外 SDK:Flow_*, Node_*, Port_*, DB_*)
↓
WorkNode.NodeSend() / WorkUnSend / FindWorker
↓
GenerWorkFlow / GenerWorkerList (持久化)
↓
WF_GenerWorkFlow / WF_GenerWorkerList (数据库)
6.3 组织架构与四大模式切点
四大模式的差异不集中在一个类,而是散落在:
BP.En30.Port.*:Emp/Dept/Station/Team 的 Map 隐藏条件。BP.WF.Template.FindWorker:找人 SQL 按模式切换。BP.WF.Dev2Interface:待办/在途统计 SQL 追加 OrgNo。BP.WF.Data.DA.SQLPort / SQLFrm:报表 SQL 模板分支。BP.WF.Install:安装脚本与初始登录。
6.4 对外 API
- REST :
CCFlow/DataUser/API/Controllers/APIController.cs(WF/APIController/{Action}) - Vue 专用 :
InnerForVueController.cs - SDK :
Dev2Interface静态类(代码集成首选)
Flowable/Camunda 提供标准 REST + Java Client;驰骋额外提供 HttpHandler 协议 与 Dev2Interface 两套集成面。
七、优势与不足分析
7.1 驰骋 BPM 优势
| 维度 | 说明 |
|---|---|
| 产品完整性 | 流程 + 表单 + 组织 + 权限 + 单据 + 门户 + 报表,一套交付 |
| 四大模式开箱即用 | 单机→集团→SaaS 升级路径清晰,无需自研多租户 |
| 中国式流程特性 | 分流/合流/会签/加签/子线程/退回策略等原生支持 |
| 找人规则丰富 | DeliveryWay 20+ 策略(直属领导、部门负责人、WebAPI 等) |
| 低代码能力 | 表单设计器、单据(CCBill)、EnMap 实体、GPM 菜单权限 |
| 双语言栈 | CCFlow(.NET) / JFlow(Java) 同源架构,适配不同技术栈甲方 |
| 中间件 + BPM 双模式 | 可嵌入 ERP,也可独立建站 |
| BPMN 导入 | 支持从 Flowable 等工具导入 BPMN(单向) |
7.2 驰骋 BPM 不足
| 维度 | 说明 |
|---|---|
| 非 BPMN 原生运行时 | 流程定义存关系表,与 BPMN 生态互操作性弱 |
| 标准开放度 | 无 DMN/CMMN 标准实现;API 非 RESTful 标准 |
| 引擎与业务耦合 | OrgNo 隔离散落各处,升级引擎需全量回归 |
| PASS 模式不成熟 | 枚举存在但前后端支持不完整 |
| 云原生架构 | 单体 + DB 为主,无内置 K8s Operator / 水平扩展方案 |
| 社区与生态 | 相比 Flowable/Camunda 国际社区,插件、咨询、人才池较小 |
| SaaS 安全 | 部分 OrgNo 过滤为字符串拼接,需审计 |
7.3 Flowable/Camunda 优势
| 维度 | 说明 |
|---|---|
| BPMN 2.0 标准 | 模型可交换、工具链成熟(Camunda Modeler 等) |
| 引擎纯粹 | 专注流程执行,易嵌入微服务 |
| 扩展机制标准 | Listener、Delegate、Connector、External Task |
| 云原生 | Camunda 8 + Zeebe 支持分布式、高吞吐 |
| 社区活跃 | 文档、案例、Spring Boot Starter 丰富 |
| 多租户 | Flowable Platform / Camunda Tenant 引擎级支持 |
7.4 Flowable/Camunda 不足(相对驰骋)
| 维度 | 说明 |
|---|---|
| 无内置表单引擎 | 需集成自研或第三方表单 |
| 无内置组织权限 | Identity 简陋,企业级需 Keycloak 等 |
| 中国式 OA 特性 | 会签、加签、退回等需大量自定义 |
| 交付周期长 | 从引擎到可用 OA 需拼装多个系统 |
| 许可成本 | 企业版多租户、高级特性需商业许可 |
八、如何选择工作流引擎------决策指南
8.1 决策树
是否需要完整 BPM/OA 产品(表单+组织+权限)?
├── 是 → 驰骋 BPM 优先考虑
│ ├── 仅嵌入现有系统 → 中间件模式 + Dev2Interface/Toolkit SDK
│ └── 独立业务系统 → BPM 模式 + 四大运行模式选型
└── 否 → 仅需流程引擎
├── 强依赖 BPMN 标准 / 微服务编排 → Flowable 或 Camunda
├── 超高吞吐、云原生 → Camunda 8 (Zeebe)
└── Spring 生态、国内案例多 → Flowable
8.2 四大运行模式选型
| 场景 | 推荐模式 | 理由 |
|---|---|---|
| 单一企业 OA | Single | 配置简单,无 OrgNo 开销 |
| 集团多法人 | GroupInc | 内置组织切换、岗责策略 |
| 云 BPM 平台 | SAAS | 人员编号隔离、全链路 OrgNo |
| 应用商店托管 | PASS(待成熟) | 关注后续版本支持 |
8.3 场景对照表
| 场景 | 推荐 | 关键原因 |
|---|---|---|
| 政府/国企 OA 全系统 | 驰骋 BPM | 表单+流程+组织一体,国产化 .NET/Java |
| 电商订单履约编排 | Camunda 8 | 高并发、事件驱动、微服务 |
| 银行核心流程(强审计) | Flowable + 自研表单 | BPMN 标准、成熟案例 |
| ERP 内嵌审批流 | 驰骋中间件模式 或 Flowable | 前者中国式特性强;后者 BPMN 标准 |
| 多租户 SaaS OA | 驰骋 SAAS 或 Flowable Platform | 前者开箱即用;后者引擎级租户 |
| 流程即代码、GitOps | Camunda | BPMN XML 版本化管理 |
| 快速 POC / 低预算 | 驰骋 Single | 安装即用,Demo 数据齐全 |
8.4 综合结论
-
选驰骋 BPM :需要"流程 + 表单 + 组织 + 低代码"一体化交付;面向中国政府/企业 OA、集团化、SaaS 运维;团队熟悉 .NET 或 Java 驰骋栈;重视中国式审批特性。
-
选 Flowable :Spring 技术栈;需要 BPMN 标准 与现有 BPMN 资产互通;流程编排是核心、表单组织已有系统;可接受较长集成周期。
-
选 Camunda :微服务/云原生架构;高吞吐流程编排;需要 DMN 决策表;国际化团队与标准化治理。
-
混合策略 :以 Flowable/Camunda 作编排引擎 ,驰骋表单/组织作业务层------技术上可行(驰骋支持 BPMN 导入),但需评估双向同步成本,一般不建议双引擎并行。
九、附录:关键代码索引
| 主题 | 文件路径 |
|---|---|
| 运行模式枚举 | CCFlow/Components/BP.En30/Sys/EnumLab.cs |
| 运行模式配置 | CCFlow/Components/BP.En30/Difference/SystemConfig.cs |
| 安装初始化 | CCFlow/Components/BP.WF/Install.cs |
| 集团种子数据 | CCFlow/CCFlow/WF/Data/Install/SQLScript/Port_Inc_CH_RunModel_1.sql |
| SaaS 种子数据 | CCFlow/CCFlow/WF/Data/Install/SQLScript/Port_Inc_CH_RunModel_2.sql |
| 流程实例 | CCFlow/Components/BP.WF/WF/GenerWorkFlow.cs |
| 待办任务 | CCFlow/Components/BP.WF/WF/GenerWorkerList.cs |
| 流程发送引擎 | CCFlow/Components/BP.WF/WF/WorkNode.cs |
| 对外 SDK | CCFlow/Components/BP.WF/Dev2Interface.cs |
| 找人算法 | CCFlow/Components/BP.WF/Template/FindWorker.cs |
| 事件机制 | CCFlow/Components/BP.WF/WF/ExecEvent.cs |
| REST API | CCFlow/CCFlow/DataUser/API/Controllers/APIController.cs |
| 前端模式枚举 | Vue3/src/bp/difference/SystemConfig.ts |
| 前端通信 | Vue3/src/utils/gener/HttpHandler.ts |
| 门户模式说明 | Vue3/src/Portal/readme.txt |
| 中间件菜单 | Vue3/src/Portal/Middleware/Sysmenu.vue |
十、参考文献
- 驰骋 BPM 官网:https://ccbpm.cn/
- Flowable 官方文档:https://www.flowable.com/open-source/docs/
- Camunda 官方文档:https://docs.camunda.io/
- BPMN 2.0 规范:https://www.omg.org/spec/BPMN/
本文基于 CCFlowWorkSpace 仓库源码静态分析生成,PASS 模式及 JFlow 侧实现以 CCFlow 为准,生产部署请以官方文档与版本发布说明为准。