1. 项目背景与目标
最近在Jeecgboot3.9.0项目中集成Flowable7.2.0工作流引擎时,遇到了一个典型需求:为OA系统增加请假审批功能。这个看似简单的需求实际上涉及前后端全链路开发,包括流程定义、表单设计、接口对接和权限控制等多个环节。
我选择Jeecgboot作为基础框架,主要看中它强大的代码生成能力和企业级后台管理功能。而Flowable7.2.0作为Activiti的分支版本,提供了更轻量级的工作流实现,特别适合需要快速搭建审批流程的中小型项目。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与依赖配置
2.1 基础环境检查
确保你的开发环境满足以下条件:
- JDK 1.8+
- Maven 3.6+
- MySQL 5.7+
- Redis(用于流程缓存)
2.2 关键依赖引入
在pom.xml中添加Flowable核心依赖:
xml复制<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>7.2.0</version>
</dependency>
同时需要添加流程设计器前端依赖:
javascript复制// package.json
"dependencies": {
"bpmn-js": "^9.4.0",
"flowable-vue": "^1.2.0"
}
注意:Flowable7.x与6.x的API有较大变化,特别是流程变量处理方式,建议直接使用7.2.0完整发行包。
3. 数据库表结构设计
3.1 业务表设计
请假单核心表结构设计:
sql复制CREATE TABLE `oa_leave` (
`id` varchar(32) NOT NULL COMMENT '主键ID',
`process_instance_id` varchar(64) DEFAULT NULL COMMENT '流程实例ID',
`user_id` varchar(32) NOT NULL COMMENT '申请人ID',
`leave_type` tinyint(4) NOT NULL COMMENT '请假类型(1事假 2病假 3年假)',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`reason` varchar(500) NOT NULL COMMENT '请假事由',
`status` tinyint(4) DEFAULT '0' COMMENT '状态(0草稿 1审批中 2通过 3驳回)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 Flowable表自动生成
启动项目后,Flowable会自动创建以下核心表:
- ACT_RE_*:流程定义存储
- ACT_RU_*:运行时实例
- ACT_HI_*:历史数据
- ACT_ID_*:身份信息
实测发现:在MySQL8.0+环境下,需要手动调整默认字符集为utf8mb4,否则中文流程名会乱码。
4. 流程定义与模型设计
4.1 使用BPMN设计器
- 访问Flowable Modeler(通常集成在Jeecgboot的/flowable-modeler路径)
- 创建新的流程定义,设置ID为"leaveProcess"
- 设计关键节点:
- 开始事件 → 申请人提交
- 用户任务 → 部门经理审批
- 排他网关 → 判断请假天数
- 用户任务 → HR备案(>3天时触发)
- 结束事件
4.2 流程变量配置
在部门经理审批节点配置:
xml复制<extensionElements>
<flowable:formProperty id="approveResult"
name="审批结果"
type="enum"
required="true">
<flowable:value id="agree" name="同意"/>
<flowable:value id="reject" name="驳回"/>
</flowable:formProperty>
</extensionElements>
5. 前后端对接实现
5.1 后端接口开发
在Jeecgboot中创建LeaveController:
java复制@PostMapping("/start")
public Result<?> startProcess(@RequestBody LeaveDTO dto) {
// 1. 保存业务数据
Leave leave = new Leave();
BeanUtils.copyProperties(dto, leave);
leaveService.save(leave);
// 2. 启动流程
Map<String, Object> variables = new HashMap<>();
variables.put("leaveId", leave.getId());
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"leaveProcess", variables);
// 3. 关联流程实例ID
leave.setProcessInstanceId(instance.getId());
leave.setStatus(1); // 审批中
leaveService.updateById(leave);
return Result.OK("流程已启动");
}
5.2 前端Vue3实现
请假表单关键代码:
vue复制<template>
<a-form-model :model="form" :rules="rules">
<a-form-model-item label="请假类型" prop="leaveType">
<a-select v-model="form.leaveType">
<a-select-option :value="1">事假</a-select-option>
<a-select-option :value="2">病假</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="时间范围" required>
<a-range-picker
v-model="dateRange"
:show-time="{ format: 'HH:mm' }"
format="YYYY-MM-DD HH:mm"
/>
</a-form-model-item>
</a-form-model>
</template>
6. 流程审批功能实现
6.1 任务列表查询
创建TaskController:
java复制@GetMapping("/list")
public Result<?> queryTodoTasks(
@RequestParam String userId,
@RequestParam(defaultValue = "1") Integer pageNo,
@RequestParam(defaultValue = "10") Integer pageSize) {
List<Task> tasks = taskService.createTaskQuery()
.taskCandidateOrAssigned(userId)
.orderByTaskCreateTime().desc()
.listPage((pageNo-1)*pageSize, pageSize);
return Result.OK(tasks);
}
6.2 审批操作处理
审批接口核心逻辑:
java复制@PostMapping("/complete")
public Result<?> completeTask(@RequestBody ApproveDTO dto) {
// 1. 查询任务
Task task = taskService.createTaskQuery()
.taskId(dto.getTaskId())
.singleResult();
// 2. 设置流程变量
Map<String, Object> variables = new HashMap<>();
variables.put("approveResult", dto.getResult());
variables.put("comment", dto.getComment());
// 3. 完成任务
taskService.complete(task.getId(), variables);
// 4. 更新业务状态
Leave leave = leaveService.getById(dto.getBusinessKey());
leave.setStatus(dto.getResult().equals("agree") ? 2 : 3);
leaveService.updateById(leave);
return Result.OK("操作成功");
}
7. 常见问题与解决方案
7.1 流程定义部署失败
典型错误:Error deploying process definition
排查步骤:
- 检查bpmn20.xml是否规范
- 确认数据库连接池大小足够(建议≥10)
- 查看ACT_GE_BYTEARRAY表是否有旧版本残留
7.2 任务认领异常
现象:多人同时处理同一任务时报错
解决方案:
java复制// 在查询时添加锁
taskService.claim(taskId, userId);
7.3 历史数据查询慢
优化方案:
- 为ACT_HI_TASKINST表的PROC_INST_ID_字段加索引
- 定期归档历史数据:
sql复制INSERT INTO ACT_HI_TASKINST_ARCHIVE
SELECT * FROM ACT_HI_TASKINST
WHERE END_TIME_ < DATE_SUB(NOW(), INTERVAL 3 MONTH);
8. 进阶优化建议
8.1 动态表单集成
将Jeecgboot的表单设计器与Flowable结合:
- 通过
flowable:formProperty绑定动态表单字段 - 使用Vue的
v-for动态渲染表单
8.2 消息通知增强
在流程关键节点添加消息提醒:
java复制// 在流程监听器中
@EventListener
public void onTaskCreated(TaskCreatedEvent event) {
String assignee = event.getTask().getAssignee();
// 调用Jeecgboot的消息接口
sysBaseAPI.sendSysAnnouncement(
assignee, "您有新的待办任务",
"流程ID:"+event.getProcessInstanceId());
}
8.3 性能监控
添加Flowable的JMX监控:
properties复制# application.yml
flowable:
management:
enabled: true
jmx:
enabled: true
实际部署后发现,当并发流程实例>100时,需要特别注意:
- 增加
flowable.async-executor-activate启用异步执行 - 调整
flowable.async-executor-thread-pool-size(建议CPU核心数×2)
这个请假流程实现过程中,最大的收获是理解了Flowable的变量作用域机制。流程变量、任务变量、执行变量的生命周期不同,在跨节点传递时需要特别注意。比如在网关条件表达式中,应该使用执行变量而非任务变量。
