
做审批系统的主流技术方案有两种:一种是引一个成熟的工作流引擎(Activiti、Flowable、Camunda 之类),再自己写业务表单;另一种直接用 geejing WebBuilder 快速开发平台内置的工作流。前者看起来"专业、生态好",后者看起来"够用"。本篇把两者放在一起对比,重点看开发时与运行一年后这两种视角下的差异——很多决策的代价,只有在二期、三期才浮出水面。
选"引擎 + 自研表单"路线,项目通常会形成这样的结构:
.bpmn20.xml,部署到引擎;ACT_* 表;JavaDelegate / TaskListener,把引擎的流程变量与业务数据互相同步;这套结构技术上完全可行,很多企业的核心审批系统就这么跑着。它的问题不在"跑不动",而在每一层都在维护两份真相。
成本一:流程变量与业务字段的双写。 引擎需要知道"金额是多少"来做条件路由,业务表里也要存金额。于是每次发起、每次审批后都要同步一遍:引擎侧 runtimeService.setVariable(...),业务侧 mapper.update(...)。两个地方写错一处,就会出现"流转按 A 金额走了、单据显示 B 金额"这类极难排查的问题。
成本二:表单与流程的错位。 业务流程一变(多一个审批节点、加一个抄送、从串行改并行),表单往往要跟着改——因为新节点可能需要看新字段。但表单是独立工程,改完要重新发版、重新测试。结果是:流程设计师在建模器里画五分钟,研发排期要两周。
成本三:待办与追踪页面的重复实现。 引擎提供了 API,但"我该看到什么"是业务问题:待办要显示单据标题、金额、发起人、停留时长;追踪要画出流转路径与每个节点的处理意见。这些页面每个系统都要重做一遍,且极易与引擎版本升级冲突。
成本四:引擎的表结构侵入。 ACT_RE_*、ACT_RU_*、ACT_HI_* 加起来几十张表进入你自己的库,备份策略、查询性能、版本升级都要一并考虑。想换引擎?成本约等于重写。
WebBuilder 的工作流不是外挂引擎,而是平台的一部分。它的设计取舍非常明确——约定优于配置,把桥接层消灭掉。
一个流程 = 一个目录。 请假流程的文件结构是这样的:
workflow/leave/
├── start-form.xwl 发起表单
├── handle-form.xwl 审批表单
├── before-action.xwl 动作前钩子
├── after-action.xwl 动作后钩子
└── actions.xwl 业务数据服务端流程定义是 example/leave.flw,业务表只有 wb_leave 一张,靠一个 flow_id 字段与流程实例关联。
表单与流程通过约定接口对接。 表单模块实现 init(flowId, type, record),引擎按三种形态调用它:
init(flowId, type, record) {
if (flowId) {
Wb.ajax({
url: xpath + '/../actions&xaction=selectLeave',
params: { flowId }, json: true,
success(resp) { Wb.setValue(app.main, resp); app.main.show(); }
});
} else {
app.main.show(); // 新建:显示空表单
}
}type 为 start 是发起、handle 是审批、view 是查阅——同一个表单模块服务三种视角,不需要写三个页面,也不需要为"审批时要看到什么"另开工程。
动作是方法名,不是 API 调用。 审批表单里的操作直接调用引擎注入的方法:doPass() 通过、doBack(节点) 退回、doTransfer(用户) 转办、doReject() 驳回、doSignBefore(用户) 前加签。业务代码不需要 import 任何引擎包,也不需要处理流程变量同步——引擎自己的表管流程状态,你的表管业务数据,flow_id 是唯一的接口。
业务数据落库挂在钩子里。
afterAction(action, flow) {
if (flow.restarting) {
Params.$flow_id = flow.flowId;
Wb.sync({ tableName: 'wb_leave', update: Params, whereFields: 'flow_id' });
} else {
Wb.apply(Params, { sid: Wb.getId(), flow_id: flow.flowId, user_id: flow.handleUser });
Wb.sync({ tableName: 'wb_leave', insert: Params });
}
}restarting 判断处理了"驳回后修改重提"这个实际场景,handleUser 由引擎提供——这些细节如果自研,都是要单独写代码、单独测的。
待办、已办、追踪是平台页面。 平台自带 my/my-flow.xwl 等流程相关页面,不需要每个项目重做一遍。
对比维度 | 工作流引擎 + 自研表单 | geejing WebBuilder |
|---|---|---|
流程定义 | BPMN 文件 + 建模器 | .flw 文件,与业务模块同目录 |
表单开发 | 独立前端工程 | xwl 模块,一表三态(start/handle/view) |
桥接层 | 需写 JavaDelegate/TaskListener | 无(约定接口 + 钩子) |
数据同步 | 流程变量与业务字段双写 | 引擎管流程、业务表管数据,靠 flow_id 关联 |
审批动作 | 调引擎 API | 引擎注入 doPass/doBack/doTransfer/doReject/doSignBefore |
待办/已办/追踪 | 自行开发 | 平台内置页面 |
组织与权限 | 需同步到引擎用户组 | 共用平台用户/角色/部门体系 |
数据库侵入 | 几十张 ACT_* 表 | 引擎表由平台统一管理 |
流程变更成本 | 表单常需同步改造并发版 | 多数情况只改流程定义 |
技术栈数量 | Java + BPMN + 前端 + 引擎 API | xwl + 服务端 JS |
要客观:Activiti/Flowable 这类专业引擎在以下场景仍不可替代——
但如果你的需求是"表单 + 串行/并行审批 + 退回转办加签 + 待办已办"这个国内企业系统里最常见的组合——WebBuilder 的内置工作流覆盖度足够,而且不需要桥接层这一点带来的维护收益是长期的。
一个更实在的判别方法:算一算项目的"审批流种类数"和"表单变更多少次"。种类少、变更少、以表单与数据为主 → 内置工作流更划算;种类多(几十条)、层级深、网关复杂、且企业有流程中台 → 专业引擎更合适。
把时间轴拉长到上线一年后,两种方案的差异会集中体现在四个问题上:
.flw 加表单上的字段,改动范围小、可当天上线。before-action / after-action 钩子本身就适合挂日志与通知,观察点位置一目了然。关键问题 | 引擎 + 自研表单 | geejing WebBuilder |
|---|---|---|
桥接层 | 必须写、必须维护 | 不存在 |
表单视角切换 | 多页面 / 多工程 | 一个模块三态 |
流程变更连带成本 | 高(常牵连表单) | 低(多为配置) |
高级建模能力 | 强 | 覆盖常见审批场景 |
适合场景 | 复杂编排、有流程中台 | 表单密集的国内企业审批 |
选工作流方案的本质是选一条你愿意长期支付的维护账单。专业引擎买的是"能力的上限",WebBuilder 买的是"常见场景下的维护下限"。对绝大多数企业内部审批系统而言,后者才是决定项目能不能长期活下去的那个变量。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。