流程设计器
流程设计器
业务用途
流程设计器是 BPM 工作流的可视化编排工具,管理员通过拖拽式界面定义审批流程的节点链路、审批人规则和会签策略。平台提供两套设计器:
- SimpleProcessDesignerV2(主用):钉钉式简易设计器,产出
simpleModelJSON,存入bpm_process_definition_info.simple_model字段。 - BpmnDesigner(备用):基于 bpmn-js 的标准 BPMN 2.0 设计器,产出 BPMN XML,存入
bpm_definition.bpmn_xml字段。
生产环境主要使用 SimpleProcessDesignerV2,SimpleFlowEngine 引擎直接解析 simpleModel JSON 驱动流程运转。
涉及文件
后端
| 文件 | 说明 |
|---|---|
backend/src/main/java/com/lowcode/entity/bpm/BpmProcessDefinitionInfo.java | 流程模型实体,simpleModel 字段存 JSON |
backend/src/main/java/com/lowcode/controller/bpm/BpmModelController.java | 模型 CRUD + simple/get + simple/update |
backend/src/main/java/com/lowcode/service/bpm/SimpleFlowEngine.java | 引擎 parseSimpleModel() 方法解析 JSON |
backend/src/main/java/com/lowcode/controller/BpmController.java | 旧版 /api/bpm/design/* 接口 |
前端
| 文件 | 说明 |
|---|---|
frontend/src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue | 设计器主组件 |
frontend/src/components/SimpleProcessDesignerV2/src/SimpleProcessViewer.vue | 只读查看器 |
frontend/src/components/SimpleProcessDesignerV2/src/ProcessNodeTree.vue | 节点树渲染 |
frontend/src/components/SimpleProcessDesignerV2/src/NodeHandler.vue | 节点添加操作按钮 |
frontend/src/components/SimpleProcessDesignerV2/src/consts.ts | 节点类型 / 候选人策略 / 审批方式常量 |
frontend/src/components/SimpleProcessDesignerV2/src/node.ts | 节点表单逻辑(候选人参数序列化/反序列化) |
frontend/src/components/SimpleProcessDesignerV2/src/nodes/StartUserNode.vue | 发起人节点 |
frontend/src/components/SimpleProcessDesignerV2/src/nodes/UserTaskNode.vue | 审批节点配置面板 |
frontend/src/components/SimpleProcessDesignerV2/src/nodes/CopyTaskNode.vue | 抄送节点配置面板 |
frontend/src/components/SimpleProcessDesignerV2/src/nodes/EndEventNode.vue | 结束节点 |
frontend/src/views/bpm/model/design.vue | 模型设计页(嵌入设计器) |
frontend/src/components/BpmnDesigner/ | bpmn-js 标准设计器(备用) |
数据库表
-- bpm_process_definition_info.simple_model 字段(CLOB 类型)
-- 存储 simpleModel JSON 字符串
CREATE TABLE bpm_process_definition_info (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
process_definition_id VARCHAR(64), -- 流程定义编号
model_id VARCHAR(64), -- 模型编号
model_type TINYINT DEFAULT 10,
name VARCHAR(200),
`key` VARCHAR(200), -- 模型标识
status VARCHAR(20) DEFAULT 'draft', -- draft / deployed
category VARCHAR(64),
form_type TINYINT, -- 表单类型
form_id BIGINT, -- 关联表单
simple_model CLOB, -- ★ simpleModel JSON
-- ... 其他字段省略
);后端实现
simpleModel 解析逻辑
SimpleFlowEngine.parseSimpleModel() 方法(第 107-130 行)负责将 JSON 字符串解析为节点列表:
private List<Map<String, Object>> parseSimpleModel(String simpleModel) {
List<Map<String, Object>> nodes = new ArrayList<>();
if (simpleModel == null || simpleModel.isEmpty()) {
return nodes;
}
try {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> model = mapper.readValue(simpleModel, Map.class);
// 优先解析 processList 数组格式
List<Map<String, Object>> processList = (List<Map<String, Object>>) model.get("processList");
if (processList != null) {
for (Map<String, Object> node : processList) {
String type = String.valueOf(node.get("type"));
// 引擎只关心这四种节点类型
if ("start".equals(type) || "approve".equals(type)
|| "sign".equals(type) || "copy".equals(type)) {
nodes.add(node);
}
}
return nodes;
}
// 兼容树形嵌套格式(childNode / conditionNodes)
collectSimpleNodes(model, nodes);
} catch (Exception e) {
log.error("解析simpleModel失败: " + e.getMessage(), e);
}
return nodes;
}引擎支持两种 JSON 结构:
- processList 数组格式(推荐):
{ "processList": [node1, node2, ...] } - 树形嵌套格式(兼容):节点通过
childNode串联,通过conditionNodes分支
节点类型判定
// 审批节点:approve / sign / 数字编码 11 / 13
private boolean isApprovalNode(Map<String, Object> node) {
Object type = node.get("type");
return "approve".equals(type) || "sign".equals(type)
|| "11".equals(String.valueOf(type)) || "13".equals(String.valueOf(type));
}
// 抄送节点:copy / 数字编码 12
private boolean isCopyNode(Map<String, Object> node) {
Object type = node.get("type");
return "copy".equals(type) || "12".equals(String.valueOf(type));
}simpleModel 读写接口
| 端点 | 方法 | 说明 |
|---|---|---|
/api/bpm/model/simple/get?id={id} | GET | 获取模型的 simpleModel JSON |
/api/bpm/model/simple/update?id={id} | POST | 更新模型的 simpleModel(Body 为 JSON 字符串) |
// BpmModelController.java
@GetMapping("/simple/get")
public Result<String> getSimpleModel(@RequestParam Long id) {
return Result.success(modelService.getSimpleModel(id));
}
@PostMapping("/simple/update")
public Result<Void> updateSimpleModel(@RequestParam Long id, @RequestBody String simpleModel) {
modelService.updateSimpleModel(id, simpleModel);
return Result.success("更新成功");
}前端实现
SimpleProcessDesignerV2 组件结构
SimpleProcessDesignerV2/src/
├── index.ts ← 导出入口
├── SimpleProcessDesigner.vue ← 设计器主组件(可编辑)
├── SimpleProcessViewer.vue ← 查看器组件(只读)
├── SimpleProcessModel.vue ← 流程模型封装
├── ProcessNodeTree.vue ← 递归渲染节点树
├── NodeHandler.vue ← 节点之间 "+" 添加按钮
├── consts.ts ← 所有常量定义
├── node.ts ← 节点表单逻辑 Hook
└── nodes/
├── StartUserNode.vue ← 发起人节点
├── UserTaskNode.vue ← 审批节点
├── CopyTaskNode.vue ← 抄送节点
├── EndEventNode.vue ← 结束节点
├── ChildProcessNode.vue ← 子流程节点
├── ParallelNode.vue ← 并行分支
├── ExclusiveNode.vue ← 排他分支
├── InclusiveNode.vue ← 包容分支
├── RouterNode.vue ← 路由分支
├── DelayTimerNode.vue ← 延时器
└── TriggerNode.vue ← 触发器节点类型常量(consts.ts)
export const NodeType = {
END_EVENT_NODE: 1,
START_USER_NODE: 10, // 发起人
USER_TASK_NODE: 11, // 审批节点
COPY_TASK_NODE: 12, // 抄送节点
TRANSACTOR_NODE: 13, // 办理节点(会签)
DELAY_TIMER_NODE: 14, // 延时器
TRIGGER_NODE: 15, // 触发器
CHILD_PROCESS_NODE: 20, // 子流程
CONDITION_NODE: 50, // 条件
CONDITION_BRANCH_NODE: 51, // 条件分支
PARALLEL_BRANCH_NODE: 52, // 并行分支
INCLUSIVE_BRANCH_NODE: 53, // 包容分支
ROUTER_BRANCH_NODE: 54 // 路由分支
} as const候选人策略常量(consts.ts)
export const CandidateStrategy = {
ROLE: 10, // 指定角色
DEPT_MEMBER: 20, // 部门成员
DEPT_LEADER: 21, // 部门负责人
MULTI_LEVEL_DEPT_LEADER: 23, // 连续多级部门负责人
POST: 22, // 指定岗位
USER: 30, // 指定成员
APPROVE_USER_SELECT: 34, // 审批人自选
START_USER_SELECT: 35, // 发起人自选
START_USER: 36, // 发起人本人
START_USER_DEPT_LEADER: 37, // 发起人部门负责人
START_USER_MULTI_LEVEL_DEPT_LEADER: 38, // 发起人连续部门负责人
USER_GROUP: 40, // 用户组
FORM_USER: 50, // 表单内用户字段
FORM_DEPT_LEADER: 51, // 表单内部门负责人
EXPRESSION: 60 // 流程表达式
} as const多人审批方式(consts.ts)
export const ApproveMethodType = {
RANDOM_SELECT_ONE_APPROVE: 1, // 随机挑选一人审批
APPROVE_BY_RATIO: 2, // 会签(按比例通过)
ANY_APPROVE: 3, // 或签(任一通过即可)
SEQUENTIAL_APPROVE: 4 // 依次审批
} as constSimpleFlowNode 接口定义(consts.ts)
export interface SimpleFlowNode {
id: string
type: NodeType
name: string
showText?: string
childNode?: SimpleFlowNode // 下游节点
conditionNodes?: SimpleFlowNode[] // 条件分支
candidateStrategy?: number // 候选人策略
candidateParam?: string // 候选人参数
approveMethod?: ApproveMethodType // 多人审批方式
approveRatio?: number // 通过比例
buttonsSetting?: any[] // 操作按钮配置
fieldsPermission?: Array<Record<string, any>> // 表单字段权限
timeoutHandler?: TimeoutHandler // 超时处理
rejectHandler?: RejectHandler // 拒绝处理
assignEmptyHandler?: AssignEmptyHandler // 审批人为空处理
signEnable?: boolean // 是否签名
reasonRequire?: boolean // 是否必填审批意见
// ...
}候选人参数序列化(node.ts)
handleCandidateParam() 方法将表单配置序列化为 candidateParam 字符串:
| candidateStrategy | candidateParam 格式 | 示例 |
|---|---|---|
| 30 (USER) | userId1,userId2 | 1,2,3 |
| 10 (ROLE) | roleId1,roleId2 | 1,2 |
| 22 (POST) | postId1,postId2 | 1,2 |
| 40 (USER_GROUP) | groupId1,groupId2 | 1,2 |
| 20/21 (DEPT_MEMBER/LEADER) | deptId1,deptId2 | 100,101 |
| 23 (MULTI_LEVEL) | deptIds|level | 100,101|3 |
| 36 (START_USER) | (无参数) | -- |

design.vue 页面
frontend/src/views/bpm/model/design.vue 是流程模型设计页面:
- 从路由参数获取模型 ID
- 调用
GET /bpm/model/simple/get?id=xxx加载 simpleModel JSON - 嵌入
<SimpleProcessDesigner>组件,传入flowNodeprop - 用户编辑后,调用
POST /bpm/model/simple/update?id=xxx保存

simpleModel JSON 完整示例
基本审批流(发起人 -> 主管审批 -> 抄送)
{
"processList": [
{
"type": "start",
"id": "StartUserNode",
"name": "发起人",
"candidateStrategy": 36
},
{
"type": "approve",
"id": "node_1",
"name": "部门主管审批",
"candidateStrategy": 10,
"candidateParam": "1",
"childNode": {
"type": "copy",
"id": "node_2",
"name": "抄送人事",
"candidateStrategy": 30,
"candidateParam": "5"
}
}
]
}会签流程(发起人 -> 多人会签 -> 结束)
{
"processList": [
{
"type": "start",
"id": "StartUserNode",
"name": "发起人"
},
{
"type": "sign",
"id": "node_1",
"name": "财务会签",
"candidateStrategy": 30,
"candidateParam": "3,4,5",
"signType": "AND",
"signRule": "ALL",
"passRatio": 100
}
]
}树形嵌套格式(兼容)
当 JSON 不含 processList 字段时,引擎使用 collectSimpleNodes() 递归遍历 childNode 和 conditionNodes:
{
"type": "start",
"id": "StartUserNode",
"name": "发起人",
"childNode": {
"type": "approve",
"id": "node_1",
"name": "主管审批",
"candidateStrategy": 10,
"candidateParam": "1",
"childNode": {
"type": "copy",
"id": "node_2",
"name": "抄送",
"candidateStrategy": 30,
"candidateParam": "5"
}
}
}操作步骤
1. 打开流程设计器
在前端访问「工作流 -> 流程模型 -> 设计」,进入 design.vue 页面。
2. 配置发起人节点
点击发起人节点,在右侧面板选择候选人策略(通常选择「发起人本人」,candidateStrategy=36)。
3. 添加审批节点
点击节点之间的 "+" 按钮(NodeHandler.vue),选择「审批人」,配置候选人策略和参数。
4. 配置会签节点(可选)
添加「审批人」节点后,在配置面板将「多人审批方式」改为「会签(按比例通过)」,设置通过比例。

5. 添加抄送节点(可选)
点击 "+" 选择「抄送人」,配置抄送对象。
6. 保存模型
点击保存按钮,前端调用 POST /bpm/model/simple/update?id={modelId},将 simpleModel JSON 写入数据库。
7. 部署模型
在模型列表页点击「部署」,将 status 从 draft 改为 deployed,之后才能发起流程。
常见问题
simpleModel 保存后引擎解析不到节点
检查 JSON 中 type 字段值:引擎只识别 start / approve / sign / copy 四种字符串,或对应的数字编码 10 / 11 / 13 / 12。其他类型节点(条件分支、并行等)在设计器中可见但引擎会跳过。
processList 格式 vs 树形嵌套格式
设计器 SimpleProcessDesignerV2 默认产出树形嵌套格式(通过 childNode 串联)。引擎优先尝试 processList 数组格式,如果没有则回退到 collectSimpleNodes() 递归遍历树形结构。两种格式引擎都能解析。
候选人参数中的竖线分隔符
MULTI_LEVEL_DEPT_LEADER(23)和 FORM_DEPT_LEADER(51)的 candidateParam 使用 | 分隔:左边是部门 ID 列表,右边是部门层级。引擎在 addIds() 方法中会截取 | 前面的部分作为 ID 列表。
