Spring Boot项目集成Camunda工作流:从环境搭建到第一个审批流程的保姆级教程
想象一下这样的场景:周一晨会上,项目经理拍了拍你的肩膀说:"我们需要在下周迭代中为内部报销系统加入审批流程,你来负责技术实现。"作为刚接触工作流的开发者,你可能会瞬间感到压力山大——该选哪个流程引擎?如何快速搭建环境?怎样避免那些教科书上没写的坑?别担心,这篇指南将手把手带你用Spring Boot和Camunda完成这个任务,就像有个经验丰富的同事坐在你身边指导一样。
Camunda作为当前最活跃的开源流程引擎之一,其优势在于开箱即用的企业级功能和出色的性能表现。根据第三方压力测试,在1000并发场景下,Camunda比同类产品性能提升10-39%且零报错。更重要的是,它与Spring Boot的整合异常简单,甚至不需要你事先掌握BPMN规范细节。下面我们就从零开始,用Docker快速搭建开发环境,完成第一个报销审批流程。
1. 开发环境准备
1.1 Docker快速部署Camunda与MySQL
现代开发讲究效率,我们使用Docker Compose一键部署Camunda官方镜像和MySQL数据库。在你的项目根目录创建docker-compose.yml文件:
version: '3' services: camunda: image: camunda/camunda-bpm-platform:7.17.0 ports: - "8080:8080" environment: - DB_DRIVER=com.mysql.cj.jdbc.Driver - DB_URL=jdbc:mysql://mysql:3306/camunda?characterEncoding=UTF-8 - DB_USERNAME=camunda - DB_PASSWORD=camunda depends_on: - mysql mysql: image: mysql:8.0 environment: - MYSQL_DATABASE=camunda - MYSQL_USER=camunda - MYSQL_PASSWORD=camunda - MYSQL_ROOT_PASSWORD=root volumes: - mysql_data:/var/lib/mysql volumes: mysql_data:启动服务只需执行:
docker-compose up -d注意:首次启动可能需要2-3分钟初始化数据库,访问http://localhost:8080/camunda-welcome/index.html 看到登录界面即表示成功。默认账号密码:demo/demo
1.2 Spring Boot项目基础配置
使用Spring Initializr创建项目时,除了基础的Web和JPA依赖,需要特别添加:
<!-- pom.xml --> <dependency> <groupId>org.camunda.bpm.springboot</groupId> <artifactId>camunda-bpm-spring-boot-starter</artifactId> <version>7.17.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>配置数据库连接和Camunda参数:
# application.properties spring.datasource.url=jdbc:mysql://localhost:3306/camunda spring.datasource.username=camunda spring.datasource.password=camunda spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver camunda.bpm.admin-user.id=demo camunda.bpm.admin-user.password=demo camunda.bpm.filter.create=All tasks2. 设计第一个BPMN审批流程
2.1 安装Camunda Modeler可视化工具
从Camunda官网下载对应系统的Modeler工具,这是我们的流程设计利器。启动后新建BPMN文件,我们将设计一个简易报销审批流程:
- 开始事件:命名为"报销申请提交"
- 用户任务:添加"部门经理审批"节点
- 排他网关:设置金额>5000的分支条件
- 用户任务:添加"财务总监审批"节点(仅大额报销需要)
- 结束事件:命名为"报销流程完成"
关键配置点:
- 每个用户任务的"Assignee"字段设置为
${approver}变量 - 排他网关的条件表达式为
${amount > 5000}
2.2 流程部署与验证
将设计好的BPMN文件保存为reimbursement.bpmn,复制到Spring Boot项目的src/main/resources/processes目录。启动应用时,Camunda会自动部署该流程定义。
验证部署是否成功:
@SpringBootTest class ProcessDeploymentTest { @Autowired private RepositoryService repositoryService; @Test void shouldDeployProcessDefinition() { long count = repositoryService.createProcessDefinitionQuery() .processDefinitionKey("reimbursement") .count(); assertTrue(count > 0); } }3. 实现报销审批业务逻辑
3.1 编写流程启动接口
创建REST接口启动报销流程实例:
@RestController @RequestMapping("/api/reimbursement") public class ReimbursementController { @Autowired private RuntimeService runtimeService; @PostMapping("/start") public String startProcess(@RequestBody ReimbursementRequest request) { Map<String, Object> variables = new HashMap<>(); variables.put("employee", request.getEmployee()); variables.put("amount", request.getAmount()); variables.put("description", request.getDescription()); variables.put("approver", "departmentManager"); // 初始审批人 ProcessInstance instance = runtimeService.startProcessInstanceByKey( "reimbursement", variables ); return "流程已启动,ID:" + instance.getId(); } }3.2 处理用户审批任务
实现任务查询和完成接口:
@GetMapping("/tasks") public List<TaskDto> getTasks(@RequestParam String assignee) { return taskService.createTaskQuery() .taskAssignee(assignee) .list() .stream() .map(task -> new TaskDto( task.getId(), task.getName(), (String) taskService.getVariable(task.getId(), "employee"), (Double) taskService.getVariable(task.getId(), "amount") )) .collect(Collectors.toList()); } @PostMapping("/complete/{taskId}") public String completeTask(@PathVariable String taskId, @RequestBody ApprovalDecision decision) { Map<String, Object> variables = new HashMap<>(); variables.put("approved", decision.isApproved()); if(decision.isApproved() && decision.getNextApprover() != null) { variables.put("approver", decision.getNextApprover()); } taskService.complete(taskId, variables); return "审批操作已完成"; }4. 生产环境优化与常见问题解决
4.1 性能调优配置
在高并发场景下,建议调整以下参数:
# 异步执行器配置 camunda.bpm.job-execution.enabled=true camunda.bpm.job-execution.wait-time-in-millis=5000 camunda.bpm.job-execution.max-jobs-per-acquisition=100 # 数据库连接池 spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.minimum-idle=54.2 中文乱码解决方案
遇到流程图中文显示为方框时,在application.properties中添加:
spring.http.encoding.force=true spring.http.encoding.charset=UTF-8 spring.http.encoding.enabled=true server.tomcat.uri-encoding=UTF-84.3 历史数据清理策略
Camunda默认会保存所有历史数据,生产环境需要配置自动清理:
@Configuration public class CamundaConfig { @Bean public ProcessEnginePlugin historyCleanupPlugin() { return new AbstractProcessEnginePlugin() { @Override public void postInit(ProcessEngineConfigurationImpl config) { HistoryCleanupHandler handler = config.getHistoryCleanupHandler(); handler.setBatchWindowStartTime("03:00"); handler.setBatchSize(500); } }; } }5. 进阶:动态审批人与业务规则集成
5.1 使用DelegateTaskListener动态分配审批人
实现更灵活的审批人分配逻辑:
@Component public class ApproverAssignmentListener implements TaskListener { @Override public void notify(DelegateTask task) { String department = (String) task.getVariable("department"); double amount = (double) task.getVariable("amount"); if("finance".equals(task.getTaskDefinitionKey())) { task.setAssignee("financeDirector"); } else { String approver = department + "Manager"; task.setAssignee(approver); } if(amount > 10000) { task.setVariable("requiresCEOApproval", true); } } }在BPMN中引用该监听器:
<userTask id="departmentApproval" name="部门审批"> <extensionElements> <camunda:taskListener event="create" class="com.example.listener.ApproverAssignmentListener" /> </extensionElements> </userTask>5.2 集成DMN决策表实现智能路由
对于复杂的审批规则,可以使用Camunda的DMN引擎:
- 在Modeler中创建
.dmn文件定义决策表 - 部署到resources/decisions目录
- 在流程中使用业务规则任务调用:
<businessRuleTask id="approvalRouting" camunda:decisionRef="approvalRoutingDecision"> <extensionElements> <camunda:inputOutput> <camunda:inputParameter name="amount"> ${amount} </camunda:inputParameter> </camunda:inputOutput> </extensionElements> </businessRuleTask>实际项目中,我们团队发现将审批规则外置到DMN后,业务人员可以直接修改决策表而无需重新部署流程,大大提高了系统的灵活性。特别是在金融行业合规要求频繁变更的场景下,这种设计减少了约40%的发布次数。