保姆级教程:在RuoYi-Vue-Pro项目中,从零搭建一个请假审批工作流(Flowable实战)
从零构建RuoYi-Vue-Pro请假审批工作流全流程实战
1. 工作流技术选型与项目准备
在当今企业级应用开发中,工作流引擎已成为处理复杂业务流程的核心组件。Flowable作为Activiti的分支项目,以其轻量级、高性能和丰富的功能特性,成为众多Java开发者的首选。而RuoYi-Vue-Pro作为一款基于Spring Boot和Vue.js的快速开发平台,与Flowable的整合能够为开发者提供开箱即用的工作流解决方案。
为什么选择Flowable?
- 完整的BPMN 2.0支持:可视化建模与标准兼容
- 嵌入式设计:可直接集成到Spring应用中
- 高性能运行时:优化的执行引擎处理高并发
- 丰富的REST API:便于前后端分离架构调用
- 活跃的社区:持续更新维护,问题响应快
在开始前,请确保你的开发环境已具备以下条件:
# 基础环境检查清单 Java 8+ Maven 3.6+ MySQL 5.7+ Node.js 12+RuoYi-Vue-Pro项目结构中对Flowable的支持主要体现在以下几个模块:
ruoyi-vue-pro ├── ruoyi-admin # 后台管理模块 ├── ruoyi-common # 通用工具模块 ├── ruoyi-flowable # 工作流引擎模块 ★核心 ├── ruoyi-generator # 代码生成模块 └── ruoyi-system # 系统基础模块2. 请假审批表单设计与实现
2.1 数据库表结构设计
工作流表单是业务流程的数据载体,在RuoYi-Vue-Pro中主要通过bpm_form表进行管理。对于请假审批场景,我们需要设计包含以下字段的表单:
CREATE TABLE `bpm_oa_leave` ( `id` bigint NOT NULL COMMENT '主键ID', `user_id` bigint NOT NULL COMMENT '申请人ID', `type` tinyint NOT NULL COMMENT '请假类型(1事假 2病假 3年假)', `reason` varchar(255) NOT NULL COMMENT '请假原因', `start_time` datetime NOT NULL COMMENT '开始时间', `end_time` datetime NOT NULL COMMENT '结束时间', `duration` decimal(10,1) NOT NULL COMMENT '请假时长(天)', `status` tinyint DEFAULT '0' COMMENT '状态(0审批中 1通过 2拒绝)', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='OA请假表';2.2 前端表单开发
在Vue组件中,我们使用Element UI构建表单界面:
<template> <el-form :model="form" :rules="rules" ref="formRef"> <el-form-item label="请假类型" prop="type"> <el-select v-model="form.type"> <el-option label="事假" :value="1"></el-option> <el-option label="病假" :value="2"></el-option> <el-option label="年假" :value="3"></el-option> </el-select> </el-form-item> <el-form-item label="开始时间" prop="start_time"> <el-date-picker v-model="form.start_time" type="datetime"></el-date-picker> </el-form-item> <!-- 其他表单项 --> </el-form> </template>2.3 后端接口实现
Spring Boot控制器处理表单提交:
@PostMapping("/submit") public Result<String> submitLeave(@Valid @RequestBody LeaveCreateReqVO reqVO) { Long leaveId = leaveService.createLeave(reqVO); return Result.success(leaveId.toString()); }3. Flowable流程模型设计与部署
3.1 流程模型创建
在RuoYi-Vue-Pro的管理后台,我们可以通过可视化设计器创建请假审批流程:
- 开始节点:设置表单关联
- 用户任务:领导审批节点
- 排他网关:根据审批结果分流
- 用户任务:HR备案节点(审批通过时)
- 结束节点:流程终止
关键BPMN元素说明:
| 元素类型 | 作用 | 配置要点 |
|---|---|---|
| StartEvent | 流程开始 | 关联表单KEY |
| UserTask | 人工任务 | 设置办理人/组 |
| ExclusiveGateway | 条件分流 | 设置流转条件 |
| EndEvent | 流程结束 | 可记录结果 |
3.2 流程部署实现
流程部署涉及的主要数据库表:
// 部署代码示例 Deployment deployment = repositoryService.createDeployment() .addBpmnModel("leave-approval.bpmn", bpmnModel) .name("请假审批流程") .category("OA") .deploy();部署后主要数据表变化:
ACT_RE_DEPLOYMENT:部署记录ACT_RE_PROCDEF:流程定义ACT_GE_BYTEARRAY:存储BPMN文件
4. 流程任务分配与规则配置
4.1 任务分配策略
RuoYi-Vue-Pro通过bpm_task_assign_rule表实现灵活的任务分配:
INSERT INTO `bpm_task_assign_rule` (`id`, `model_id`, `process_definition_id`, `task_definition_key`, `type`, `options`) VALUES (1, 'leave_model', 'leave_process:1', 'leader_approve', 30, '[1]'); -- 用户ID=1常用分配类型:
| 类型值 | 说明 | 适用场景 |
|---|---|---|
| 10 | 角色 | 固定角色审批 |
| 20 | 部门成员 | 本部门人员审批 |
| 30 | 指定用户 | 特定人员审批 |
| 40 | 用户组 | 用户组轮询 |
4.2 动态任务分配
对于更复杂的场景,可以使用Spring EL表达式:
<userTask id="hrTask" name="HR备案" flowable:assignee="${hrService.getHrAssignee(execution)}"/>对应的Java服务类:
public class HrService { public String getHrAssignee(DelegateExecution execution) { // 根据业务逻辑返回处理人 return "hr1"; } }5. 流程实例运行与任务处理
5.1 流程实例启动
前端发起流程请求:
axios.post('/bpm/process-instance/create', { processDefinitionId: 'leave_process:1', formVariables: { leaveId: 123, applicant: 'user1' } })后端处理逻辑:
public String startProcess(ProcessStartReqVO reqVO) { // 1. 校验流程定义 BpmProcessDefinition definition = validateProcessDefinition(reqVO.getProcessDefinitionId()); // 2. 创建流程实例 ProcessInstance instance = runtimeService.startProcessInstanceById( reqVO.getProcessDefinitionId(), reqVO.getBusinessKey(), reqVO.getVariables()); // 3. 记录扩展信息 processInstanceExtMapper.insert(new BpmProcessInstanceExt() .setId(instance.getProcessInstanceId()) .setStatus(ProcessInstanceStatusEnum.RUNNING.getStatus())); return instance.getProcessInstanceId(); }5.2 任务审批实现
审批接口的核心处理逻辑:
@Transactional public void approveTask(TaskApproveReqVO reqVO) { // 1. 校验任务 Task task = validateTask(reqVO.getId()); // 2. 处理审批意见 if (reqVO.getApproved()) { taskService.complete(task.getId(), reqVO.getVariables()); } else { handleReject(task, reqVO.getReason()); } // 3. 记录审批日志 createTaskExt(task, reqVO); }6. 流程监控与数据分析
6.1 运行时数据查询
常用查询场景及对应表:
| 查询需求 | 主要表 | 示例查询 |
|---|---|---|
| 待办任务 | ACT_RU_TASK | SELECT * FROM ACT_RU_TASK WHERE ASSIGNEE_ = ? |
| 已办任务 | ACT_HI_TASKINST | SELECT * FROM ACT_HI_TASKINST WHERE ASSIGNEE_ = ? AND END_TIME_ IS NOT NULL |
| 流程状态 | ACT_RU_EXECUTION | SELECT * FROM ACT_RU_EXECUTION WHERE PROC_INST_ID_ = ? |
6.2 历史数据分析
通过历史表可以生成各类统计报表:
-- 部门请假统计 SELECT d.dept_name, COUNT(*) as total, SUM(CASE WHEN l.status = 1 THEN 1 ELSE 0 END) as approved, SUM(l.duration) as days FROM bpm_oa_leave l JOIN sys_user u ON l.user_id = u.user_id JOIN sys_dept d ON u.dept_id = d.dept_id GROUP BY d.dept_id7. 性能优化与常见问题排查
7.1 性能优化建议
数据库索引优化:
ALTER TABLE ACT_RU_TASK ADD INDEX idx_assignee (ASSIGNEE_); ALTER TABLE ACT_HI_TASKINST ADD INDEX idx_assignee_end (ASSIGNEE_, END_TIME_);历史数据归档:
historyService.createHistoricProcessInstanceQuery() .finishedBefore(archiveDate) .delete();异步任务处理:
<serviceTask id="notifyService" flowable:async="true" flowable:class="com.example.flowable.AsyncNotifyService"/>
7.2 常见问题解决方案
问题1:流程实例卡住不执行
- 检查点:
ACT_RU_EXECUTION表中相关实例的IS_ACTIVE_字段ACT_RU_TASK是否存在未处理任务- 流程日志中是否有异常堆栈
问题2:任务分配不正确
- 排查步骤:
- 确认
bpm_task_assign_rule配置 - 检查流程定义XML中的assignee设置
- 验证候选人或组的查询逻辑
- 确认
问题3:高并发下性能下降
- 优化方案:
- 启用Flowable的异步执行器
- 增加流程引擎配置中的线程池大小
- 对频繁查询添加缓存层
8. 扩展功能与最佳实践
8.1 消息通知集成
审批过程中的消息通知方案:
// 任务创建时发送通知 taskService.addTaskListener(TaskListener.EVENTNAME_CREATE, delegateTask -> { String assignee = delegateTask.getAssignee(); String message = "您有新的待办任务:" + delegateTask.getName(); smsService.send(assignee, message); });8.2 与业务系统深度集成
深度集成方案:
数据关联:
runtimeService.setVariable(executionId, "businessData", businessData);业务规则引擎:
<businessRuleTask id="checkRule" flowable:ruleVariablesInput="${order}" flowable:resultVariable="ruleResult"/>自定义事件监听:
runtimeService.addEventListener(new CustomEventListener(), ExecutionListener.EVENTNAME_START, ExecutionListener.EVENTNAME_END);
8.3 移动端适配技巧
针对移动端的优化策略:
- 简化表单字段:只保留核心审批信息
- 离线处理能力:使用Service Worker缓存待办数据
- 推送通知:集成WebPush或App推送
- 审批快捷操作:提供"同意/拒绝"快速按钮
// 移动端审批API简化示例 POST /mobile/task/approve { "taskId": "123", "action": "approve", // or "reject" "comment": "OK" }