最近在重构公司内部OA系统时,我选择了Activiti 7作为工作流引擎核心。这个决定源于一次痛苦的经历:当HR部门第5次调整请假审批规则时,我们不得不连夜修改硬编码的审批逻辑。而采用工作流引擎后,类似的流程变更只需简单调整BPMN流程图即可。本文将分享如何用SpringBoot和Activiti 7.1.0.M6实现完整的请假审批流程,包含从流程图设计到API调用的全链路实践。
新建SpringBoot 2.7.x项目时,建议使用以下依赖组合:
xml复制<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
</dependency>
<!-- 排除冲突的MyBatis依赖 -->
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependencies>
关键配置项说明:
yaml复制spring:
activiti:
database-schema-update: true # 开发环境自动建表
db-history-used: true # 启用历史表
history-level: full # 记录完整历史
check-process-definitions: false # 禁用自动校验流程文件
Activiti启动时会自动创建28张表,主要分为四类:
| 表前缀 | 功能说明 | 典型表举例 |
|---|---|---|
| ACT_RE | 流程定义和静态资源存储 | ACT_RE_PROCDEF(流程定义表) |
| ACT_RU | 运行时流程数据 | ACT_RU_TASK(运行中任务) |
| ACT_HI | 历史数据记录 | ACT_HI_TASKINST(历史任务) |
| ACT_GE | 通用数据存储 | ACT_GE_BYTEARRAY(二进制数据) |
提示:生产环境应将database-schema-update设为false,通过SQL脚本手动管理表结构变更
推荐两种流程设计方式:
请假审批流程的核心节点设计:
bpmn复制<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
<process id="leaveApproval" name="请假审批流程">
<startEvent id="startEvent"/>
<userTask id="deptLeaderVerify" name="部门领导审批"/>
<exclusiveGateway id="decisionGateway"/>
<userTask id="hrConfirm" name="HR备案"/>
<endEvent id="endEvent"/>
<sequenceFlow sourceRef="startEvent" targetRef="deptLeaderVerify"/>
<sequenceFlow sourceRef="deptLeaderVerify" targetRef="decisionGateway"/>
<sequenceFlow sourceRef="decisionGateway" targetRef="hrConfirm"
conditionExpression="${days > 3}"/>
<sequenceFlow sourceRef="decisionGateway" targetRef="endEvent"
conditionExpression="${days <= 3}"/>
<sequenceFlow sourceRef="hrConfirm" targetRef="endEvent"/>
</process>
</definitions>
合理的变量设计能增强流程灵活性:
| 变量名 | 类型 | 作用域 | 说明 |
|---|---|---|---|
| applicant | String | 流程实例 | 申请人ID |
| leaveType | String | 流程实例 | 请假类型(年假/病假等) |
| days | Integer | 流程实例 | 请假天数 |
| approvalMemo | String | 任务级别 | 审批意见 |
实现流程定义的版本化管理:
java复制@Service
public class ProcessDeployer {
@Autowired
private RepositoryService repositoryService;
public String deploy(InputStream bpmnStream, InputStream pngStream) {
Deployment deployment = repositoryService.createDeployment()
.addInputStream("leaveProcess.bpmn20.xml", bpmnStream)
.addInputStream("leaveProcess.png", pngStream)
.name("请假流程_v" + System.currentTimeMillis())
.deploy();
return deployment.getId();
}
}
启动流程时绑定业务数据:
java复制public class LeaveProcessService {
@Autowired
private RuntimeService runtimeService;
public String startProcess(String businessKey, Map<String, Object> variables) {
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"leaveApproval",
businessKey,
variables
);
return instance.getId();
}
}
实现动态任务分配(基于Spring EL表达式):
java复制public List<TaskDTO> queryTasks(String assignee) {
return taskService.createTaskQuery()
.taskAssignee(assignee)
.list()
.stream()
.map(task -> new TaskDTO(
task.getId(),
task.getName(),
runtimeService.getVariable(task.getProcessInstanceId(), "applicant")
))
.collect(Collectors.toList());
}
public void completeTask(String taskId, Map<String, Object> variables) {
taskService.complete(taskId, variables);
}
在BPMN中使用表达式指定处理人:
xml复制<userTask id="deptLeaderVerify" name="部门领导审批"
activiti:assignee="${leaveService.findDeptLeader(applicant)}"/>
对应的Spring Bean方法:
java复制@Service
public class LeaveService {
public String findDeptLeader(String employeeId) {
// 实现根据员工ID查找部门领导的逻辑
return "leader_" + employeeId.substring(0,3);
}
}
当需要更新流程定义时:
java复制public String redeployProcess(String processDefinitionKey) {
// 1. 挂起旧版本
repositoryService.suspendProcessDefinitionByKey(processDefinitionKey);
// 2. 部署新版本
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("newProcess.bpmn")
.deploy();
// 3. 激活新版本
repositoryService.activateProcessDefinitionByKey(processDefinitionKey);
return deployment.getId();
}
构建实时监控接口:
java复制@GetMapping("/process/statistics")
public ProcessStatistics getStatistics() {
return new ProcessStatistics(
runtimeService.createProcessInstanceQuery().count(),
taskService.createTaskQuery().active().count(),
historyService.createHistoricProcessInstanceQuery()
.finished().count()
);
}
开发中遇到的典型问题及解决方案:
针对高并发场景的配置调整:
yaml复制spring:
activiti:
async-executor-activate: true # 启用异步执行器
async-executor:
core-pool-size: 10
max-pool-size: 50
queue-capacity: 1000
历史数据归档策略:
java复制historyService.createHistoricProcessInstanceQuery()
.finished()
.listPage(0, 100)
.forEach(instance -> {
historyService.deleteHistoricProcessInstance(instance.getId());
});
在最近的生产环境压测中,通过调整异步执行器参数,我们的请假审批流程TPS从原来的150提升到了420。关键发现是当任务处理包含IO操作(如邮件通知)时,必须使用异步任务避免阻塞流程引擎。