1. 项目背景与核心需求
在Jeecgboot3.9.0框架中集成Flowable7.2.0工作流引擎实现OA请假单功能,是当前企业级应用开发的典型场景。这个组合方案解决了传统OA系统审批流程僵化、二次开发成本高的痛点。我最近在金融行业客户现场实施时,就遇到原有审批流程无法适应新业务部门架构调整的情况,最终通过这套技术栈实现了流程的动态配置。
从技术架构看,Jeecgboot提供的前后端分离基础(Vue3+Ant Design)与Flowable的BPMN2.0引擎形成互补。实际开发中需要重点解决三个问题:表单数据与流程实例的绑定、审批节点的动态路由、以及业务状态与流程状态的同步。下面通过请假单这个经典案例,演示如何构建完整的流程应用。
2. 环境准备与依赖配置
2.1 基础环境搭建
首先确保已正确部署Jeecgboot3.9.0基础环境,包括:
- JDK1.8+(推荐Amazon Corretto11)
- MySQL5.7+(需开启大小写敏感)
- Redis5.0+(用于流程引擎缓存)
- Node.js16+(前端构建环境)
在pom.xml中添加Flowable关键依赖:
xml复制<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>7.2.0</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-rest</artifactId>
<version>7.2.0</version>
</dependency>
2.2 数据库初始化
执行Flowable提供的MySQL脚本(位于flowable-engine.jar中的org/flowable/db/create目录),特别注意需要额外创建业务表关联字段:
sql复制ALTER TABLE act_ru_task
ADD COLUMN biz_id VARCHAR(64) COMMENT '业务ID';
ALTER TABLE act_hi_taskinst
ADD COLUMN biz_id VARCHAR(64) COMMENT '业务ID';
3. 请假单功能实现
3.1 前端表单设计
使用Jeecgboot Online表单生成器创建请假单:
-
字段配置示例:
- 请假类型(select):年假、病假、事假
- 开始时间(datetime)
- 结束时间(datetime)
- 请假天数(number)
- 请假事由(textarea)
-
在/src/views/modules/oa目录下创建leaveForm.vue,关键代码:
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="annual">年假</a-select-option>
<a-select-option value="sick">病假</a-select-option>
</a-select>
</a-form-model-item>
<!-- 其他表单项 -->
</a-form-model>
</template>
3.2 后端流程集成
3.2.1 流程定义部署
在resources/processes目录下创建leave.bpmn20.xml:
xml复制<process id="leaveProcess" name="请假流程">
<startEvent id="start"/>
<userTask id="leaderAudit" name="部门领导审批"/>
<sequenceFlow sourceRef="start" targetRef="leaderAudit"/>
<!-- 更多节点定义 -->
</process>
通过Java代码动态部署:
java复制@Autowired
private RepositoryService repositoryService;
public void deployProcess() {
repositoryService.createDeployment()
.addClasspathResource("processes/leave.bpmn20.xml")
.name("请假流程")
.deploy();
}
3.2.2 服务层实现
在LeaveServiceImpl中实现流程启动逻辑:
java复制@Override
@Transactional
public void startProcess(LeaveDTO dto) {
// 1. 保存业务数据
Leave leave = new Leave();
BeanUtils.copyProperties(dto, leave);
leaveMapper.insert(leave);
// 2. 启动流程实例
Map<String, Object> variables = new HashMap<>();
variables.put("applicant", getCurrentUserId());
variables.put("days", dto.getDays());
runtimeService.startProcessInstanceByKey(
"leaveProcess",
leave.getId().toString(),
variables
);
}
4. 审批功能开发
4.1 任务列表查询
创建TaskController提供待办接口:
java复制@GetMapping("/todo")
public Result<?> todoTasks(
@RequestParam(required = false) String processDefinitionName) {
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee(getCurrentUserId())
.orderByTaskCreateTime().desc()
.list();
return Result.OK(tasks.stream().map(t -> {
Map<String, Object> map = new HashMap<>();
map.put("id", t.getId());
map.put("name", t.getName());
map.put("bizId", t.getBusinessKey());
return map;
}).collect(Collectors.toList()));
}
4.2 审批操作处理
审批服务实现示例:
java复制public void completeTask(String taskId, Boolean approved, String comment) {
// 获取当前任务
Task task = taskService.createTaskQuery()
.taskId(taskId)
.singleResult();
// 设置审批变量
Map<String, Object> variables = new HashMap<>();
variables.put("approved", approved);
// 添加审批意见
if(StringUtils.isNotBlank(comment)){
taskService.addComment(taskId, task.getProcessInstanceId(), comment);
}
// 完成任务
taskService.complete(taskId, variables);
// 更新业务状态
Leave leave = leaveMapper.selectById(task.getBusinessKey());
leave.setStatus(approved ? "APPROVED" : "REJECTED");
leaveMapper.updateById(leave);
}
5. 高级功能实现
5.1 动态审批人配置
在流程定义中使用JUEL表达式动态指定审批人:
xml复制<userTask id="hrAudit" name="HR审批">
<extensionElements>
<flowable:assignee>${hrService.getHrManager(applicant)}</flowable:assignee>
</extensionElements>
</userTask>
实现对应的Java服务:
java复制@Service
public class HrService {
public String getHrManager(String userId) {
// 根据员工ID查询所属HR
return sysUserMapper.selectHrByUserId(userId);
}
}
5.2 条件路由配置
根据请假天数实现不同的审批路径:
xml复制<sequenceFlow id="toHr" sourceRef="leaderAudit" targetRef="hrAudit">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${days > 3}]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow id="toEnd" sourceRef="leaderAudit" targetRef="endEvent">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${days <= 3}]]>
</conditionExpression>
</sequenceFlow>
6. 常见问题与解决方案
6.1 流程实例与业务数据同步
推荐采用事件监听器实现状态同步:
java复制@Component
public class LeaveProcessEventListener implements ExecutionListener {
@Override
public void notify(DelegateExecution execution) {
if("end".equals(execution.getEventName())){
String bizId = execution.getProcessInstanceBusinessKey();
leaveMapper.updateStatus(bizId, "COMPLETED");
}
}
}
6.2 历史数据查询优化
对于高频查询的审批记录,建议创建视图:
sql复制CREATE VIEW v_leave_approval AS
SELECT
h.ID_ AS hist_id,
h.BUSINESS_KEY_ AS leave_id,
h.NAME_ AS task_name,
h.ASSIGNEE_ AS approver,
h.END_TIME_ AS approve_time
FROM act_hi_taskinst h
JOIN oa_leave l ON h.BUSINESS_KEY_ = l.id
WHERE h.PROC_DEF_ID_ LIKE 'leaveProcess%';
7. 前端界面优化技巧
7.1 审批进度可视化
使用Ant Design的Timeline组件展示审批进度:
vue复制<a-timeline>
<a-timeline-item v-for="item in history" :key="item.id">
{{item.taskName}} - {{item.approver}}
<template v-if="item.comment">
<br/><a-comment :content="item.comment"/>
</template>
</a-timeline-item>
</a-timeline>
7.2 表单验证增强
添加自定义验证规则:
javascript复制const validateDays = (rule, value, callback) => {
if (value > 15) {
callback(new Error('超过最大请假天数'));
} else {
callback();
}
};
export default {
data() {
return {
rules: {
days: [
{ required: true, message: '请输入天数' },
{ validator: validateDays }
]
}
}
}
}
在项目实际部署时,我们发现当并发用户数超过200时,Flowable的默认配置会出现任务查询延迟。通过调整以下参数显著提升了性能:
yaml复制flowable:
async-executor-activate: true
async-executor-thread-pool-size: 20
history-level: audit
另一个值得注意的细节是,在Jeecgboot的表单设计器中,字段命名不要使用Flowable的保留字(如processInstanceId、taskId等),这会导致流程变量注入异常。建议业务字段统一添加前缀如"biz_"。
