一、方案梳理
之前的文章已经完成前端bpmn-js和后端的Flowable集成整合,那么现在考虑的就是持久化用户绘制的流程定义文件了,这就又引申出两个议题,持久化方式,要保存什么业务要素,接下来我们逐步来分析解决。
1.1 提出方案持久化方案
关于【流程定义】,我们应该采取什么样的方式保存,我初步想到有两种方案,下面是两个方案的对比分析:MySQL vs MySQL+MongoDB 混合存储
比较维度 | MySQL 单库方案 | MySQL + MongoDB 混合方案 |
---|---|---|
数据一致性 | ✅ 强一致性,单库事务管理方便 | ⚠ 跨库一致性需额外处理(例如通过最终一致性设计) |
存储结构适配 | ⚠ BPMN XML 是结构不规则的大文本,不利于关系型存储 | ✅ MongoDB 更适合存储结构灵活的大文档 |
查询效率 | ⚠ 查询大字段(XML)成本高,全文检索较弱 | ✅ MongoDB 支持文档级索引和全文检索,查询更灵活高效 |
开发成本 | ✅ 成本低,统一使用一种数据库,易维护 | ⚠ 成本高,需开发跨库读写逻辑和一致性机制 |
系统扩展性 | ⚠ 不适合支撑大规模流程/大文件XML,垂直扩展有限 | ✅ MongoDB 横向扩展能力强,更适应未来复杂场景 |
读写性能 | ⚠ XML 文件频繁读写会拖慢表性能 | ✅ 热点数据可缓存,MongoDB 对文档型数据访问更高效 |
复杂性控制 | ✅ 所有流程定义都在 MySQL 中集中管理 | ⚠ 两套数据存储方案,需要更成熟的 DevOps 和运维配合 |
全文搜索能力 | ⚠ MySQL 对 XML/大文本字段支持弱,不适合全文索引场景 | ✅ MongoDB 可启用全文索引/模糊查询等高级能力 |
1.2 选择混合方案以及理由
① 数据结构匹配度高
BPMN 2.0 XML 是半结构化数据,具有:
-
文本体积大(可能数 KB ~ 数 MB);
-
结构不规则(不同流程包含节点种类、数量差异大);
-
不适合用关系型字段建模存储。
使用 MySQL 保存 XML:
-
查询慢(尤其在流程列表中分页时读取 XML 字段);
-
存储膨胀(长文本字段占用磁盘资源,影响表性能);
-
缺乏灵活性(如无法轻松进行 XPath 模糊搜索或局部字段匹配)。
MongoDB 则天然适合保存类似 XML/JSON 的文档型数据,查询、检索更高效、弹性好。
② 提高系统查询性能与灵活性
混合方案中:
-
列表查询只走 MySQL(元数据表,快速分页);
-
点击详情时才从 MongoDB 读取对应 XML 内容,降低了数据冗余和响应压力;
-
MongoDB 可在
process_key/version
上加索引,甚至可做 XML 内容的关键词全文检索(类似 elasticsearch); -
XML 数据未来若用于模型可视化或节点级智能分析,MongoDB 提供了更多结构灵活的操作空间。
③ 便于大规模扩展和未来分布式部署
-
MongoDB 支持水平分片(sharding)、副本集(replica set),可以根据流程数量扩展存储节点;
-
如果未来 BPM 平台演进为多租户 SaaS 服务,MongoDB 支持多租户文档隔离更加自然;
-
MySQL 可以保留核心元数据的事务保障,而 MongoDB 提供灵活数据承载能力,两者优势互补。
④ 更合理的数据生命周期管理
-
XML 文件作为原始流程模型,有生命周期(可废弃、归档、历史版本保存);
-
MongoDB 更适合用于归档、异步处理和版本控制(例如一个流程有多个版本,保存在文档数组中);
-
MySQL 保留流程的最新版本索引,业务访问更聚焦。
⑤ 缓存与性能优化更自由
-
结合 Redis 作为中间缓存层,可缓存热点 XML 文档;
-
MySQL 保证索引和元数据快速检索,MongoDB 缓解大文档读取压力;
-
分离式架构也方便日后拆分子服务,提高微服务治理能力。
⑥ 小结
因此,虽然混合存储方案开发和运维成本略高,但在 功能灵活性、性能、可维护性和长期扩展性 上全面优于单一 MySQL 方案,特别适合 BPMN 流程平台这类文档型数据为主的场景,是更加可持续的架构选型。
1.3 确认流程定义业务信息
参照了很多开源的工作流项目,我采用了以下这些要素信息,如果大佬们觉得有需要补充的请在评论区告知我,谢谢。
① mysql数据库中保存的主要业务信息
-
流程唯一标识(业务中使用的 key)
-
流程名称
-
流程版本号
-
BPMN XML存放在MongoDB中的ID
-
状态:0-草稿, 1-发布, 2-停用
-
流程描述/说明
② 完整的mysql表设计
/*
Navicat Premium Data TransferSource Server : 192.168.30.129
Source Server Type : MySQL
Source Server Version : 80300 (8.3.0)
Source Host : 192.168.30.129:3306
Source Schema : pm-processTarget Server Type : MySQL
Target Server Version : 80300 (8.3.0)
File Encoding : 65001Date: 11/04/2025 16:34:32
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for process_definition
-- ----------------------------
DROP TABLE IF EXISTS `process_definition`;
CREATE TABLE `process_definition` (
`id` bigint NOT NULL COMMENT '流程定义表主键ID',
`process_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '流程唯一标识(业务中使用的 key)',
`process_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '流程名称',
`process_version` int NOT NULL DEFAULT 1 COMMENT '流程版本号',
`xml_mongo_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'BPMN XML存放在MongoDB中的ID',
`process_status` tinyint(1) NULL DEFAULT NULL COMMENT '状态:0-草稿, 1-发布, 2-停用',
`description` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '流程描述/说明',
`deleted` tinyint(1) NOT NULL COMMENT '0:启用,1:删除',
`creator_id` bigint NULL DEFAULT NULL COMMENT '创建人id',
`creator_name` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '' COMMENT '创建人名称',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`modifier_id` bigint NULL DEFAULT NULL COMMENT '修改人id',
`modifier_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '修改人名称',
`modify_time` datetime NULL DEFAULT NULL COMMENT '修改时间',
`record_version` int NULL DEFAULT NULL COMMENT '版本号',
`attribute1` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '扩展字段1',
`attribute2` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '扩展字段2',
`attribute3` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '扩展字段3',
`attribute4` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '扩展字段4',
`attribute5` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '扩展字段5',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_process_key_version`(`process_key` ASC, `process_version` ASC) USING BTREE COMMENT '流程唯一标识索引'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '流程定义表' ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;
③ MongoDB中保存的业务信息
-
_id
MongoDB 自动生成的唯一标识,确保每个文档唯一。这个 _id 在 MySQL 中对应字段 xml_mongo_id。 -
process_key & process_version
保留和 MySQL 中一致的关键字,在 MongoDB 中可以联合建立复合索引,以便快速定位某个流程的具体版本。 -
process_name 与 process_status
用于在 MongoDB 层进行必要的展示或筛选操作(例如查询所有发布状态的流程),避免每次都通过 MySQL 查找后再反查 XML 内容。 -
description、deleted、creator、create_time、modifier、modify_time、record_version
这些元数据可帮助业务系统进行版本管理、审计和状态监控,在某些场景下(例如复杂的搜索条件)能减少跨库查询调用。 -
attributes
将扩展字段合并到内嵌对象中,保持数据模型的层次性与清晰度;同时避免顶层字段过多导致索引体积膨胀和文档更新时的锁定问题。 -
bpmn_xml
存储 BPMN2.0 的完整 XML 定义。由于文本较长,建议该字段不参与频繁的全文搜索,而是根据业务需求建立合适的全文索引或加密存储。如果需要对 XML 内容做增量更新,也可以考虑存储为 BSON 对象或借助其他 XML 解析方案。
④ MongoDB集合命名与文档示例
命名:bpmn_definitions
文档示例如下:
{
"_id": ObjectId("64a8f2cbb4e123456789abcd"),
"process_key": "order_approval", // 对应 MySQL 的 process_key
"process_version": 2, // 对应 MySQL 的 process_version
"process_name": "订单审批流程", // 同 MySQL 中的 process_name,可选保留
"process_status": 1, // 状态:0-草稿, 1-发布, 2-停用
"description": "订单审批流程说明", // 流程描述
"deleted": false, // 转换 MySQL 的 deleted 字段(0-启用,1-删除),建议用布尔类型
"creator": { // 结合 creator_id 与 creator_name
"id": NumberLong(10001),
"name": "张三"
},
"create_time": ISODate("2025-04-11T16:30:00Z"), // 创建时间
"modifier": { // 结合 modifier_id 与 modifier_name
"id": NumberLong(10002),
"name": "李四"
},
"modify_time": ISODate("2025-04-11T16:34:00Z"), // 修改时间
"record_version": 1, // 版本号字段
"attributes": { // 将扩展字段合并到一个内嵌对象中,可方便扩展
"attribute1": "扩展1",
"attribute2": "扩展2",
"attribute3": "扩展3",
"attribute4": "扩展4",
"attribute5": "扩展5"
},
"bpmn_xml": "<definitions xmlns='http://www.omg.org/spec/BPMN/20100524/MODEL'> ... </definitions>" // BPMN 2.0 的 XML 文本内容
}
⑤ Mongodb索引设计
复合唯一索引:根据业务查询频率,也可对 process_status 建立索引在 process_key 与 process_version 上建立复合唯一索引,确保每个流程的同一版本只存一条文档
db.bpmn_definitions.createIndex(
{ process_key: 1, process_version: 1 },
{ unique: true }
)
状态索引: 根据业务查询频率,也可对 process_status 建立索引
db.bpmn_definitions.createIndex({ process_status: 1 })
二、流程定义管理静态界面搭建
2.1 创建 vue 本体文件
完整静态界面代码如下,方便大家cv
<script lang="ts" setup>
import { ref, watch } from 'vue'
// 定义响应式数据 processName 收集查询条件 【流程名称】
const processName = ref('')
// 定义响应式数据 currentPage 收集当前页码
const currentPage = ref<number>(1)
// 定义响应式数据 pageSize 收集每页显示的条数
const pageSize = ref<number>(10)
// 定义响应式数据 total 收集总数据条数
const total = ref<number>(0)
// 监听 currentPage 和 pageSize 的变化,防止非法值
watch([currentPage, pageSize], ([newPage, newSize]) => {
if (newPage < 1)
currentPage.value = 1
if (newSize < 1 || newSize > 100)
pageSize.value = 10
})
// 重置查询条件
function resetQuery() {
processName.value = ''
}
// 表格列定义
const tableColumns = [
{ label: '#', type: 'index', align: 'center', width: '50px' },
{ label: 'ID', prop: 'id', align: 'center' },
{ label: '流程名称', prop: 'name', align: 'center' },
{ label: '流程唯一标识', prop: 'uniqueId', align: 'center' },
{ label: '流程版本号', prop: 'version', align: 'center' },
{ label: '流程状态', prop: 'status', align: 'center' },
{ label: '流程描述/说明', prop: 'description', align: 'center' },
{ label: '创建时间', prop: 'createTime', align: 'center' },
{ label: '创建人', prop: 'creator', align: 'center' },
{ label: '修改时间', prop: 'updateTime', align: 'center' },
{ label: '修改人', prop: 'updater', align: 'center' },
]
</script>
<template>
<!-- 查询条件区域 -->
<el-card style="height: 75px;">
<el-form :inline="true" class="form-inline">
<el-form-item label="流程名称">
<el-input
v-model.trim="processName"
placeholder="请输入流程名称"
maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search">
查询
</el-button>
<el-button icon="Refresh" @click="resetQuery">
重置
</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 查询结果列表区域 -->
<el-card style="margin: 10px 0px;">
<el-button type="primary" icon="Plus">
添加流程
</el-button>
<el-table style="margin: 10px 0px;" :border="true">
<el-table-column type="selection" align="center" width="50px" />
<el-table-column
v-for="(column, index) in tableColumns"
:key="index"
:type="column.type"
:label="column.label"
:prop="column.prop"
:align="column.align"
:width="column.width"
/>
</el-table>
<!-- 分页器 -->
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 30, 40, 50]"
layout="prev, pager, next, jumper,->, sizes, total"
:total="Math.max(total, 0)"
/>
</el-card>
</template>
<style scoped>
.form-inline {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap; /* 适配小屏幕 */
}
</style>
2.2 创建菜单&按钮
2.3 给当前登录人的角色-超级管理员分配权限
2.4 重新登录看到静态界面
后记
本篇主要是论证流程定义的保存方案设计以及流程定义的初始静态界面的搭建,之后就开始实现保存的操作。
前端工程地址请查看专栏第一篇文章,本篇文章的代码地址分支是:process-2