简介:本资源是一个基于 RuoYi-Vue-Plus 框架深度扩展的 Flowable 工作流二次开发项目,面向 Java 后端开发者、低代码平台学习者及高校毕业设计群体,聚焦工作流引擎集成、在线表单动态构建与可视化流程编排等核心痛点。压缩包共 1198 个文件,涵盖 502 个 Java 类(支撑流程引擎、任务调度与权限适配)、248 个 JS 脚本与 132 个 Vue 组件(实现前端流程图渲染、表单设计器及审批界面),辅以 XML 流程定义、SQL 初始化脚本、YML 配置及 Nginx/Redis 等部署配置文件,整体体积为 10.81MB。目前已有 2357 人学习下载,适合用于理解企业级工作流系统架构、掌握 Flowable 与 Spring Boot 深度整合实践,以及快速搭建含表单+流程+审批的后台管理原型。项目采用 MIT 协议开源,虽处于持续开发阶段,但已提供完整可运行的流程建模、实例启动、任务办理与历史追踪能力。
1. 项目概述:为什么要在RuoYi-Vue-Plus上扩展Flowable?
如果你正在寻找一个既能快速搭建后台管理系统,又需要强大工作流引擎支持的项目方案,那么将RuoYi-Vue-Plus与Flowable工作流引擎深度整合,绝对是一个值得投入精力的方向。我最近刚完成一个中型OA系统的升级,核心需求就是要把原来手动流转的审批流程全部线上化、自动化。在技术选型阶段,我对比了Activiti、Camunda和Flowable,最终选择了Flowable 6.x版本,并决定基于我们团队熟悉的RuoYi-Vue-Plus框架进行二次开发。原因很简单:RuoYi-Vue-Plus提供了开箱即用的前后端分离架构、完善的权限体系和代码生成器,能极大缩短基础模块的开发周期;而Flowable作为Activiti的衍生品,不仅完全兼容BPMN 2.0标准,其轻量级、高性能以及对Spring Boot的友好支持,让它成为嵌入现有系统的最佳选择。
这个项目的核心目标,不仅仅是“能用”,而是要“好用”。我们不仅要实现流程的定义与执行,更要打造一个让业务人员也能轻松上手的可视化流程设计器和表单设计器。这意味着,我们需要在RuoYi-Vue-Plus优雅的Admin管理界面中,无缝集成Flowable Modeler的设计能力,并构建一套支持动态渲染、数据绑定的在线表单系统。最终,我们实现了一个功能丰富的工作流平台:支持从流程建模、表单设计、任务处理到流程监控的全生命周期管理。对于开发者而言,它提供了清晰的API和扩展点;对于最终用户(如部门经理、HR),它提供了直观、拖拽式的操作界面。接下来,我将详细拆解整个二次开发过程中的核心思路、关键技术实现以及那些只有踩过坑才知道的宝贵经验。
2. 整体架构设计与技术选型考量
当我们决定将Flowable嵌入RuoYi-Vue-Plus时,首先要解决的是架构融合问题。RuoYi-Vue-Plus本身是一个前后端分离的权限管理系统后端基于Spring Boot、Mybatis-Plus,前端基于Vue3、Element-Plus。而Flowable也是一个Spring Boot应用,它自带REST API和管理界面(Flowable Modeler和Flowable Task)。直接部署两个独立应用显然不行,我们需要将它们“拧”成一个整体。
2.1 融合策略:嵌入式引擎与界面集成
我采用的策略是“嵌入式引擎 + 界面深度定制”。
嵌入式Flowable引擎:我们不独立部署Flowable应用,而是将Flowable作为一系列Jar包依赖引入到RuoYi-Vue-Plus的后端工程中。通过Spring Boot自动配置,将Flowable的流程引擎、各种Service(如RepositoryService、RuntimeService、TaskService)注入到Spring容器中。这样,Flowable的所有数据库表(以
ACT_开头)将与RuoYi的业务表共存于同一个数据库实例中,通过Mybatis-Plus的数据源进行统一管理。这种方式的优点是数据一致性高,事务管理方便,性能损耗小。界面集成与改造:Flowable原生的Modeler(流程设计器)和Task(任务应用)是独立的AngularJS应用,风格与我们的Vue3+Element-Plus前端格格不入。因此,我放弃了直接嵌入这些原生UI的想法,转而采用“功能借鉴 + 前端重绘”的策略。
- 流程设计器:我们保留了Flowable官方提供的
bpmn-js这个核心的BPMN 2.0图形化建模库。它是一个纯前端的JavaScript库,与框架无关。我们在Vue3项目中引入bpmn-js,并基于它封装成自己的Vue组件。同时,我们只取用了Flowable原设计器的“属性面板”逻辑,但UI完全按照Element-Plus的规范重新开发。这样,设计器在视觉和交互上完全融入了RuoYi-Vue-Plus的管理后台。 - 表单设计器:Flowable原生对动态表单的支持比较弱。我们决定自研一个更符合国内业务场景的在线表单设计器。核心思路是采用JSON Schema来描述表单结构(字段、类型、校验规则),并开发一个可视化的拖拽界面,让用户配置表单。前端根据JSON Schema动态渲染出真实的Form表单。这套设计器与流程节点绑定,实现了“一个节点,一个表单”。
- 流程设计器:我们保留了Flowable官方提供的
注意:这里有一个关键决策点。为什么不直接用Flowable的REST API对接其原生UI?因为原生UI功能固定,定制化能力弱,且与现有系统风格迥异,用户体验割裂。深度集成虽然前期开发量较大,但带来了统一的用户体验和极强的扩展性,从长远看维护成本更低。
2.2 技术栈明细与版本锁定
清晰的版本是稳定性的基石。以下是我们项目最终使用的核心依赖版本,经过生产环境验证:
| 组件 | 版本 | 选型理由 |
|---|---|---|
| RuoYi-Vue-Plus | 5.X (基于Spring Boot 2.7.x) | 社区活跃,文档齐全,集成了Sa-Token权限、Redis缓存、多数据源等实用模块。 |
| Flowable | 6.8.0 | 相较于Activiti 7,Flowable 6社区更活跃,对Spring Boot 2.x支持更稳定。6.8.0是一个长期支持版本。 |
| Spring Boot | 2.7.18 | 与RuoYi-Vue-Plus及Flowable 6.8.0版本兼容性最佳。 |
| 前端框架 | Vue 3.2 + Element-Plus 2.3+ | RuoYi-Vue-Plus前端标准技术栈,生态丰富。 |
| BPMN设计器 | bpmn-js 8.7+ | Flowable官方推荐且维护的BPMN 2.0建模库,功能强大。 |
| 表单设计器 | 自研基于Vue + JSON Schema | 高度定制化,能满足复杂业务表单需求,如级联选择、表格子表单等。 |
版本锁定的教训:在项目初期,我曾尝试使用Spring Boot 3.x和Flowable 7.0,结果在自动配置和部分API上遇到了不少兼容性问题。Flowable 7对Spring Boot 3的支持当时尚不完善。因此,我强烈建议在开始一个整合项目时,先去官方社区和GitHub Issues查看版本兼容性矩阵,选择一个经过大量项目验证的稳定组合,避免在基础环境上浪费过多时间。
3. 核心模块拆解与实现细节
整个扩展功能可以划分为四大核心模块:流程设计器、表单设计器、流程运行时API以及管理与监控。下面我逐一拆解其中的关键实现。
3.1 流程设计器:基于bpmn-js的深度定制
我们的目标是在RuoYi的管理后台中,提供一个类似Visio的、可拖拽的流程图绘制界面。bpmn-js是这个功能的核心。
前端集成步骤:
安装依赖:在Vue3项目中,通过npm安装
bpmn-js及其相关依赖。npm install bpmn-js bpmn-js-properties-panel camunda-bpmn-moddle --save注意:虽然我们用的是Flowable,但
bpmn-js默认适配Camunda的属性扩展,我们需要一个Flowable的“moddle”描述文件来告诉设计器Flowable特有的属性(如flowable:assignee)。你可以从Flowable官方源码或示例中找到flowable.json描述文件。封装Vue组件:创建一个
BpmnModeler.vue组件。在组件的onMounted生命周期中,初始化BpmnModeler实例,并配置额外的模块(如属性面板、Flowable扩展)。import BpmnModeler from 'bpmn-js/lib/Modeler'; import 'bpmn-js/dist/assets/diagram-js.css'; import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'; export default { mounted() { this.modeler = new BpmnModeler({ container: '#canvas', propertiesPanel: { parent: '#properties' }, // 关键!注入Flowable的扩展定义 moddleExtensions: { flowable: require('path/to/your/flowable.json') } }); // 加载一个空的BPMN 2.0 XML模板 this.createNewDiagram(); }, methods: { async createNewDiagram() { const xmlStr = `<?xml version="1.0" encoding="UTF-8"?>...`; // 简化的BPMN空模板 try { const { warnings } = await this.modeler.importXML(xmlStr); if (warnings.length) { console.warn('导入警告:', warnings); } } catch (err) { console.error('导入失败:', err); } } } }自定义属性面板:
bpmn-js-properties-panel提供的默认面板是德语且不符合我们的业务需求。我们需要重写其渲染逻辑。例如,对于一个“用户任务”节点,我们需要展示“办理人”、“候选组”、“到期时间”等字段。我们通过监听元素选择事件,动态生成一个基于Element-Plus表单控件的属性编辑区,并与BPMN元素的业务数据(Business Object)绑定。当用户在属性面板修改时,通过modeling.updateProperties方法更新到BPMN XML模型中。
后端对接:设计器最终产出的是一段符合BPMN 2.0标准的XML字符串。我们需要一个API来保存和部署这个模型。
@RestController @RequestMapping("/workflow/model") public class FlowableModelController { @Autowired private RepositoryService repositoryService; @PostMapping("/deploy") public R deploy(@RequestParam String name, @RequestParam String key, @RequestParam String category, @RequestParam String bpmnXml) { try { Deployment deployment = repositoryService.createDeployment() .addString(name + ".bpmn20.xml", bpmnXml) .name(name) .key(key) .category(category) .deploy(); return R.ok("部署成功", deployment.getId()); } catch (FlowableException e) { return R.fail("部署失败: " + e.getMessage()); } } }这个API接收前端传来的BPMN XML、流程名称、KEY和分类,调用Flowable的RepositoryService进行部署。部署成功后,该流程定义就可以被启动了。
3.2 在线表单设计器:JSON Schema驱动的动态表单
这是让业务人员能自主配置审批表单的关键。我们的表单设计器产出物是一个JSON对象,它描述了表单的所有信息。
表单JSON Schema结构示例:
{ "formKey": "leave_apply_form", "formName": "请假申请单", "fields": [ { "type": "input", "label": "请假类型", "model": "leaveType", "required": true, "options": [ {"label": "年假", "value": "annual"}, {"label": "病假", "value": "sick"}, {"label": "事假", "value": "personal"} ] }, { "type": "date-picker", "label": "开始时间", "model": "startTime", "required": true }, { "type": "date-picker", "label": "结束时间", "model": "endTime", "required": true }, { "type": "textarea", "label": "请假事由", "model": "reason", "rows": 3 } ], "rules": { "startTime": [{ "required": true, "message": "请选择开始时间", "trigger": "change" }], "endTime": [ { "required": true, "message": "请选择结束时间", "trigger": "change" }, { "validator": "checkEndTimeAfterStart", "trigger": "change" } ] } }设计器前端实现:
- 左侧组件库:列出所有支持的表单控件(输入框、下拉框、日期选择器、上传组件等),每个控件是一个可拖拽的Vue组件。
- 中间画布:使用Vue的拖拽库(如
vuedraggable),接收拖放过来的组件,并实时渲染预览。画布上的每个字段组件都绑定到同一个表单数据模型。 - 右侧属性配置面板:当点击画布上的某个字段时,右侧面板动态显示该字段类型对应的所有可配置属性(如
label、model、required、options等)。修改属性,画布上的组件实时更新。 - 生成JSON:点击保存时,遍历画布上的所有字段配置,组装成上述的JSON Schema结构,提交到后端存储。
后端存储与绑定:表单JSON需要与流程定义关联。我们在数据库创建了一张扩展表wf_form_def,主要字段包括form_key、form_name、form_json、proc_def_id(关联的流程定义ID)。在流程设计器里,可以为每个用户任务节点指定一个form_key。当流程实例到达该节点时,系统便根据form_key找到对应的JSON Schema,动态渲染出任务办理表单。
动态渲染引擎:这是前端另一个关键组件。它接收一个form_key,从后端获取对应的JSON Schema,然后递归遍历fields数组,根据每个字段的type,动态创建对应的Element-Plus表单组件,并绑定校验规则rules。这相当于一个微型的、声明式的UI渲染引擎。
3.3 流程运行时API封装与业务集成
流程部署好了,表单也设计好了,接下来就是如何让业务系统“跑”起流程。我们需要封装一套简洁的API供业务模块调用。
1. 启动流程实例:这是将一次具体的业务(如一次请假申请)与一个流程定义关联起来。
@Service public class FlowableRuntimeService { @Autowired private RuntimeService runtimeService; @Autowired private IdentityService identityService; public ProcessInstance startProcessInstanceByKey(String processDefinitionKey, String businessKey, Map<String, Object> variables, String starterUserId) { // 设置流程启动人 identityService.setAuthenticatedUserId(starterUserId); // 启动流程实例 ProcessInstance instance = runtimeService.startProcessInstanceByKey( processDefinitionKey, businessKey, // 通常传入业务数据的ID,如 leave_apply.id variables // 流程变量,可包含表单数据 ); identityService.setAuthenticatedUserId(null); return instance; } }businessKey至关重要,它建立了流程实例与业务数据的关联。通过它,我们可以轻松查询某个请假条对应的流程状态。variables中可以存入整个表单数据,方便在后续节点和网关条件中使用。
2. 任务查询与办理:这是工作流系统的核心交互。我们需要为当前登录用户查询待办任务。
public PageResult<TaskVO> queryTodoTasks(TaskQueryDTO queryDTO, Long userId) { // 构建Flowable原生任务查询 TaskQuery taskQuery = taskService.createTaskQuery() .taskCandidateOrAssigned(String.valueOf(userId)) // 查询当前用户候选或待办 .active() .orderByTaskCreateTime().desc(); // 支持按流程名称模糊过滤(需要关联查询) if (StringUtils.isNotBlank(queryDTO.getProcessName())) { taskQuery.processVariableValueLike("title", "%" + queryDTO.getProcessName() + "%"); } List<Task> tasks = taskQuery.listPage( (queryDTO.getPageNum() - 1) * queryDTO.getPageSize(), queryDTO.getPageSize() ); long total = taskQuery.count(); // 将Task对象转换为自定义的TaskVO,并补充业务信息 List<TaskVO> taskVOList = tasks.stream().map(task -> { TaskVO vo = new TaskVO(); vo.setTaskId(task.getId()); vo.setTaskName(task.getName()); vo.setProcessInstanceId(task.getProcessInstanceId()); // 通过processInstanceId或businessKey,去查询业务数据,填充如“申请人”、“申请时间”等 // ... return vo; }).collect(Collectors.toList()); return new PageResult<>(taskVOList, total); }办理任务时,除了调用taskService.complete(taskId, variables),通常还需要记录审批意见、更新业务状态。
@Transactional(rollbackFor = Exception.class) public void completeTask(String taskId, Map<String, Object> variables, String comment, String outcome) { // 1. 添加审批意见 if (StringUtils.isNotBlank(comment)) { taskService.addComment(taskId, null, comment); } // 2. 设置局部变量(如审批结果) if (StringUtils.isNotBlank(outcome)) { taskService.setVariableLocal(taskId, "outcome", outcome); } // 3. 完成任务 taskService.complete(taskId, variables); // 4. (可选)监听器或后续业务逻辑,如发送通知、更新业务表状态 // ... }3. 流程变量与业务数据同步:这是一个常见问题。流程变量存储在ACT_RU_VARIABLE等表中,而业务数据在自己的表里。如何保证一致性?我的经验是:
- 关键业务状态(如
status)应在业务表中维护。流程变量更多用于驱动路由(如approvalResult)或存储临时信息。 - 在流程的开始事件监听器中,将业务数据主键作为
businessKey,并将必要数据复制为流程变量。 - 在任务完成监听器或ServiceTask中,根据
businessKey找到业务数据,并根据流程结果更新其状态。这样业务数据是权威来源。
3.4 历史数据、监控与高阶功能
一个完整的工作流系统离不开历史查询和监控。
历史数据查询:Flowable提供了HistoryService,可以查询已经结束的流程实例、任务、活动记录。我们可以基于此构建“已办任务”、“我发起的流程”等功能。这里要注意性能,历史数据会随着时间增长,需要设计合理的归档或分表策略。
流程监控:集成Flowable的ProcessEngineConfiguration,可以暴露流程引擎的JMX Bean或通过其自带的REST API(需额外引入flowable-rest模块)来获取运行时信息,如作业执行情况、数据库连接池状态等。但在生产环境,我更推荐通过日志和自定义的监控端点来收集指标。
会签与或签:这是工作流中常见的多任务处理模式。
- 会签(多实例并行):在用户任务上设置
multiInstanceLoopCharacteristics,并指定集合变量(如assigneeList)和完成条件(如nrOfCompletedInstances == nrOfInstances)。所有任务同时产生,需全部完成才能继续。 - 或签(多实例顺序):与会签类似,但设置
isSequential为true。任务按顺序产生,一个完成后再产生下一个,常用于“依次审批”。 实现时,需要在启动流程或到达该节点前,准备好参与者集合变量。
自定义监听器与委托表达式:这是Flowable最强大的扩展点之一。
- 执行监听器(Execution Listener):可以挂在流程事件(开始、结束)上,用于记录日志、发送消息等。
- 任务监听器(Task Listener):挂在任务事件(创建、分配、完成)上,常用于自动设置办理人、发送任务通知。
- Java委托(Java Delegate):在
ServiceTask中实现JavaDelegate接口,可以执行复杂的业务逻辑,如调用外部系统、进行复杂计算。
在RuoYi-Vue-Plus中,我们可以很方便地将这些监听器或委托类注册为Spring Bean,然后在BPMN XML中通过delegateExpression属性引用它们,例如${myTaskCompleteListener},从而实现业务逻辑与流程引擎的松耦合集成。
4. 数据库设计与性能优化实践
将Flowable集成进来,意味着数据库里会增加近30张以ACT_开头的表。理解这些表的结构对于排查问题和性能优化至关重要。
核心表分类:
- 运行时表(ACT_RU_*):存储运行中的流程实例、任务、变量等。这些表最活跃,需要重点关注。
- 历史表(ACT_HI_*):存储已完成的流程数据。数据量增长最快,需定期归档。
- 身份表(ACT_ID_*):存储用户、组信息。强烈建议与业务系统用户表集成,而不是使用这套表。我们可以通过实现Flowable的
IdmIdentityService接口,将其指向我们自己的sys_user和sys_role表。 - 其他:如
ACT_GE_*(通用数据)、ACT_RE_*(存储部署的流程定义资源)。
集成业务用户表:这是必须做的一步。在application.yml中配置:
flowable: async-executor-activate: false # 根据需求开启异步执行器 db-history-used: true # 使用历史数据 history-level: audit # 历史级别:audit记录所有细节 # 禁用Flowable自带的身份管理 idm-enabled: false然后,编写一个自定义的IdmIdentityService实现类,将getUser()、getGroupsForUser()等方法,映射到你的SysUserService和SysRoleService上。这样,在流程中设置${assignee}为zhangsan时,引擎就能找到对应的业务用户。
性能优化点:
- 历史数据分级与归档:将
flowable.history-level设置为audit会记录所有细节,包括变量变更。对于超大规模应用,可以考虑设置为activity(只记录节点活动),或开发定时任务,将超过一定时间的ACT_HI_*数据迁移到历史归档库。 - 变量查询优化:避免使用
processVariableValueLike进行模糊查询,尤其是在大数据量表上。如果业务需要频繁按变量查询,可以考虑将关键业务变量冗余存储到业务表中,或建立合适的数据库索引。 - 异步执行器:对于耗时操作(如调用外部HTTP接口、生成复杂报表),应在
ServiceTask中设置为async=true,并启用AsyncExecutor。这样任务会被放入异步队列,立即返回,避免阻塞流程线程。 - 数据库连接池:确保Flowable使用的数据源配置了合适的连接池(如HikariCP),并监控连接使用情况。
5. 部署、运维与常见问题排查
项目开发完成后,部署和运维是另一道坎。
部署打包:由于是单体应用(前后端分离,但后端是一个Jar包),部署相对简单。将打包好的ruoyi-admin.jar和前端静态资源(Nginx配置或放入resources/static)部署到服务器即可。所有Flowable的API都通过我们自定义的Controller暴露,安全性由RuoYi-Vue-Plus的Sa-Token权限框架统一控制。
流程定义热部署:在开发环境,我们可以通过前端设计器直接部署新版本流程。在生产环境,建议通过版本控制(Git)来管理BPMN XML文件,使用CI/CD流水线进行部署。Flowable支持相同key的流程定义多次部署,新部署的版本会自动变为默认版本,而正在运行的老实例会继续使用其启动时的版本,这实现了平滑升级。
常见问题与排查技巧:
流程启动失败,提示“未找到流程定义”
- 检查点:确认流程定义的
key是否正确;确认该key的流程定义是否已成功部署(检查ACT_RE_PROCDEF表);确认调用启动API的租户ID(如果有)是否匹配。
- 检查点:确认流程定义的
任务查询不到
- 检查点:首先确认流程实例是否已成功创建并运行到该任务节点(查看
ACT_RU_TASK表)。然后检查任务候选人的设置:是直接指定了assignee(专办),还是指定了candidateUsers或candidateGroups(组办)?查询代码中是否使用了正确的查询方法(.taskCandidateOrAssigned(userId)会同时查询候选和已指派任务)。
- 检查点:首先确认流程实例是否已成功创建并运行到该任务节点(查看
网关条件不生效,流程走错分支
- 检查点:这是最常见的问题之一。首先,在BPMN设计器中双击连线,确认条件表达式已正确设置(如
${approvalResult == 'agree'})。其次,检查在流程运行时,变量approvalResult是否被正确设置到了流程上下文中(注意变量作用域:流程实例变量、任务局部变量)。最有效的调试方法是,在完成上一个任务时,打印出所有的流程变量,或者直接查询ACT_RU_VARIABLE表,查看变量的值和类型(字符串'agree'和布尔true是不同的)。
- 检查点:这是最常见的问题之一。首先,在BPMN设计器中双击连线,确认条件表达式已正确设置(如
自定义Java委托类不执行
- 检查点:确认该类已被Spring容器管理(添加了
@Component注解)。确认在BPMN XML中,ServiceTask的delegateExpression属性值是否正确引用了Bean的名字(如${myServiceTask}),注意大小写。查看应用启动日志,是否有关于Bean创建或表达式解析的错误。
- 检查点:确认该类已被Spring容器管理(添加了
历史数据量巨大,系统变慢
- 行动方案:首先,评估是否真的需要
audit级别的历史记录,可以考虑降级为activity。其次,建立历史数据归档机制。可以编写一个定时任务,定期将ACT_HI_*表中已结束超过N天的流程实例数据,迁移到另一套归档表中,并在原表中删除。Flowable本身也提供了一些历史数据清理的API,但需谨慎使用,避免误删关联数据。
- 行动方案:首先,评估是否真的需要
一个宝贵的调试技巧:在application.yml中开启Flowable的调试日志,可以让你看到引擎执行的每一步细节。
logging: level: org.flowable: DEBUG # 设置为DEBUG级别,可以看到详细的SQL和执行逻辑在排查复杂流程问题时,这能帮你节省大量时间。
整个整合过程,就像是在RuoYi-Vue-Plus这座精装修的房子内,精心安装了一套智能水管系统(Flowable)。你需要规划管线走向(架构设计),定制阀门和接口(API封装),并确保它和原有的电路、网络(权限系统、业务模块)完美协作。最终,你得到的不是一个孤立的工具,而是一个赋能所有业务模块的、强大的流程驱动能力。当看到业务人员能自己拖拽出复杂的采购审批流程时,那种成就感是对所有技术细节打磨的最好回报。
本文还有配套的精品资源,点击获取