前言
在项目中需要用到工作流引擎来设计部分业务流程,框架选型最终选择了Camunda7。本文将介绍如何在Spring Boot项目中集成Camunda流程引擎,并提供一个完整的业务流程的样例demo。
整合Camunda流程引擎
版本
SpringBoot:2.6.14
Camunda:7.18.0
Camunda-modeler:5.13.0
添加依赖
在Spring Boot项目中添加Camunda的依赖,可以使用Maven或Gradle等构建工具。以下是一个Maven的依赖示例:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.6.14</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.camunda.bpm</groupId>
<artifactId>camunda-bom</artifactId>
<version>7.18.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
</dependency>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
</dependencies>
Application xml配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/camunda?serverTimezone=Asia/Shanghai
username: root
password: 123456
camunda.bpm:
webapp:
# 设置管理控制台的访问上下文
application-path: /workflow
auto-deployment-enabled: false
admin-user:
# 配置登录管理控制台的用户
id: admin
password: admin
firstName: admin
filter:
create: All tasks
database:
#数据库类型
type: mysql
#是否自动更新表信息
schema-update: true
#logging:
# level:
#配置日志,这样在开发过程中就能看到每步执行的SQL语句了
# '[org.camunda.bpm.engine.impl.persistence.entity]': debug
注*:提前创建好数据库camunda(数据库名可改成自己的数据库名),首次启动服务数据库camunda中会自动生成相关表结构和数据,如果报错如下,说明数据源下已经存在act_*相关的表,所以不会再次生成。
### Cause: java.sql.SQLSyntaxErrorException: Table 'camunda2.act_ge_property' doesn't exist
创建BPMN流程案例
demo1.bpmn
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_03yaiy9" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.13.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.19.0">
<bpmn:process id="demo1" name="审批退回" isExecutable="true">
<bpmn:startEvent id="StartEvent_1" name="审批流程">
<bpmn:outgoing>Flow_01ndo4p</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_01ndo4p" sourceRef="StartEvent_1" targetRef="Activity_1gko2k5" />
<bpmn:sequenceFlow id="Flow_1tl602a" sourceRef="Activity_1gko2k5" targetRef="Activity_15pxhgv" />
<bpmn:endEvent id="Event_0hdfd5c">
<bpmn:incoming>Flow_1xy7g6r</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1xy7g6r" sourceRef="Activity_15pxhgv" targetRef="Event_0hdfd5c" />
<bpmn:userTask id="Activity_1gko2k5" name="提交申请">
<bpmn:extensionElements>
<camunda:formData>
<camunda:formField id="name" label="名称" type="string" />
<camunda:formField id="age" label="年龄" type="long" />
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_01ndo4p</bpmn:incoming>
<bpmn:outgoing>Flow_1tl602a</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Activity_15pxhgv" name="审批">
<bpmn:incoming>Flow_1tl602a</bpmn:incoming>
<bpmn:outgoing>Flow_1xy7g6r</bpmn:outgoing>
</bpmn:userTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="demo1">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="99" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="175" y="142" width="45" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0hdfd5c_di" bpmnElement="Event_0hdfd5c">
<dc:Bounds x="592" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1jipdur_di" bpmnElement="Activity_1gko2k5">
<dc:Bounds x="270" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0kbrp5d_di" bpmnElement="Activity_15pxhgv">
<dc:Bounds x="430" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_01ndo4p_di" bpmnElement="Flow_01ndo4p">
<di:waypoint x="215" y="117" />
<di:waypoint x="270" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1tl602a_di" bpmnElement="Flow_1tl602a">
<di:waypoint x="370" y="117" />
<di:waypoint x="430" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1xy7g6r_di" bpmnElement="Flow_1xy7g6r">
<di:waypoint x="530" y="117" />
<di:waypoint x="592" y="117" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
demo2.bpmn
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0zgtpwl" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.13.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.19.0">
<bpmn:process id="qjlc" name="请假流程" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0jsyu31</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0jsyu31" sourceRef="StartEvent_1" targetRef="Activity_0tjwb9m" />
<bpmn:sequenceFlow id="Flow_01p9vvr" sourceRef="Activity_0tjwb9m" targetRef="Activity_09tuiqf" />
<bpmn:sequenceFlow id="Flow_0isdipx" sourceRef="Activity_09tuiqf" targetRef="Gateway_0nk0jwh" />
<bpmn:endEvent id="Event_0wmdaqi">
<bpmn:incoming>Flow_0b2trtt</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0zu2a2p" sourceRef="Activity_0c6x52o" targetRef="Gateway_0xhl1us" />
<bpmn:userTask id="Activity_0tjwb9m" name="请假">
<bpmn:extensionElements>
<camunda:formData>
<camunda:formField id="name" label="名称" type="string" />
<camunda:formField id="sj" label="时间" type="date" />
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_0jsyu31</bpmn:incoming>
<bpmn:incoming>Flow_0nk9gqk</bpmn:incoming>
<bpmn:outgoing>Flow_01p9vvr</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Activity_0c6x52o" name="人事">
<bpmn:incoming>Flow_15re4jy</bpmn:incoming>
<bpmn:outgoing>Flow_0zu2a2p</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Activity_09tuiqf" name="经理">
<bpmn:extensionElements>
<camunda:formData>
<camunda:formField id="qjr" label="请假人" type="string" />
<camunda:formField id="json" label="内容" type="string" />
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_01p9vvr</bpmn:incoming>
<bpmn:incoming>Flow_0ot47ia</bpmn:incoming>
<bpmn:outgoing>Flow_0isdipx</bpmn:outgoing>
</bpmn:userTask>
<bpmn:exclusiveGateway id="Gateway_0xhl1us" name="判断">
<bpmn:incoming>Flow_0zu2a2p</bpmn:incoming>
<bpmn:outgoing>Flow_0b2trtt</bpmn:outgoing>
<bpmn:outgoing>Flow_0ot47ia</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="Flow_0b2trtt" name="通过" sourceRef="Gateway_0xhl1us" targetRef="Event_0wmdaqi">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${pd2=='通过'}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="Flow_0ot47ia" name="退回" sourceRef="Gateway_0xhl1us" targetRef="Activity_09tuiqf">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${pd2!='通过'}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:exclusiveGateway id="Gateway_0nk0jwh" name="判断">
<bpmn:incoming>Flow_0isdipx</bpmn:incoming>
<bpmn:outgoing>Flow_15re4jy</bpmn:outgoing>
<bpmn:outgoing>Flow_0nk9gqk</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="Flow_15re4jy" name="通过" sourceRef="Gateway_0nk0jwh" targetRef="Activity_0c6x52o">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${pd=='通过'}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="Flow_0nk9gqk" name="退回" sourceRef="Gateway_0nk0jwh" targetRef="Activity_0tjwb9m">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${pd!='通过'}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="qjlc">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="152" y="199" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0wmdaqi_di" bpmnElement="Event_0wmdaqi">
<dc:Bounds x="1252" y="199" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1iqqjo3_di" bpmnElement="Activity_0tjwb9m">
<dc:Bounds x="250" y="177" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1ytibhn_di" bpmnElement="Activity_0c6x52o">
<dc:Bounds x="780" y="177" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_05y4k6q_di" bpmnElement="Activity_09tuiqf">
<dc:Bounds x="430" y="177" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0xhl1us_di" bpmnElement="Gateway_0xhl1us" isMarkerVisible="true">
<dc:Bounds x="1045" y="192" width="50" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="1058.5" y="168" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0nk0jwh_di" bpmnElement="Gateway_0nk0jwh" isMarkerVisible="true">
<dc:Bounds x="625" y="192" width="50" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="639" y="249" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0jsyu31_di" bpmnElement="Flow_0jsyu31">
<di:waypoint x="188" y="217" />
<di:waypoint x="250" y="217" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_01p9vvr_di" bpmnElement="Flow_01p9vvr">
<di:waypoint x="350" y="217" />
<di:waypoint x="430" y="217" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0isdipx_di" bpmnElement="Flow_0isdipx">
<di:waypoint x="530" y="217" />
<di:waypoint x="625" y="217" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0zu2a2p_di" bpmnElement="Flow_0zu2a2p">
<di:waypoint x="880" y="217" />
<di:waypoint x="1045" y="217" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0b2trtt_di" bpmnElement="Flow_0b2trtt">
<di:waypoint x="1095" y="217" />
<di:waypoint x="1252" y="217" />
<bpmndi:BPMNLabel>
<dc:Bounds x="1163" y="199" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0ot47ia_di" bpmnElement="Flow_0ot47ia">
<di:waypoint x="1070" y="242" />
<di:waypoint x="1070" y="380" />
<di:waypoint x="480" y="380" />
<di:waypoint x="480" y="260" />
<bpmndi:BPMNLabel>
<dc:Bounds x="765" y="362" width="21" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_15re4jy_di" bpmnElement="Flow_15re4jy">
<di:waypoint x="675" y="217" />
<di:waypoint x="780" y="217" />
<bpmndi:BPMNLabel>
<dc:Bounds x="717" y="199" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0nk9gqk_di" bpmnElement="Flow_0nk9gqk">
<di:waypoint x="650" y="192" />
<di:waypoint x="650" y="100" />
<di:waypoint x="300" y="100" />
<di:waypoint x="300" y="177" />
<bpmndi:BPMNLabel>
<dc:Bounds x="465" y="82" width="21" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
demo3
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0h3vbuf" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.13.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.19.0">
<bpmn:process id="zdsp" name="自动审批" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0edugib</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0edugib" sourceRef="StartEvent_1" targetRef="Activity_0pvheo9" />
<bpmn:sequenceFlow id="Flow_0fcxiem" sourceRef="Activity_0pvheo9" targetRef="Activity_08s87r4" />
<bpmn:endEvent id="Event_16ew4n3">
<bpmn:incoming>Flow_0uzct3z</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0uzct3z" sourceRef="Activity_08s87r4" targetRef="Event_16ew4n3" />
<bpmn:serviceTask id="Activity_0pvheo9" name="自动审批" camunda:class="com.ph.workflow.task.ATask">
<bpmn:extensionElements>
<camunda:executionListener delegateExpression="${monitorExecution}" event="start" />
</bpmn:extensionElements>
<bpmn:incoming>Flow_0edugib</bpmn:incoming>
<bpmn:outgoing>Flow_0fcxiem</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:userTask id="Activity_08s87r4" name="人工审批" camunda:assignee="${userName}">
<bpmn:incoming>Flow_0fcxiem</bpmn:incoming>
<bpmn:outgoing>Flow_0uzct3z</bpmn:outgoing>
</bpmn:userTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="zdsp">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1yjbweb_di" bpmnElement="Activity_0pvheo9">
<dc:Bounds x="270" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_16ew4n3_di" bpmnElement="Event_16ew4n3">
<dc:Bounds x="682" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1meixwq_di" bpmnElement="Activity_08s87r4">
<dc:Bounds x="460" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0edugib_di" bpmnElement="Flow_0edugib">
<di:waypoint x="215" y="117" />
<di:waypoint x="270" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0uzct3z_di" bpmnElement="Flow_0uzct3z">
<di:waypoint x="560" y="117" />
<di:waypoint x="682" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0fcxiem_di" bpmnElement="Flow_0fcxiem">
<di:waypoint x="370" y="117" />
<di:waypoint x="460" y="117" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
完整的业务流程样例demo代码Controller
import com.alibaba.fastjson.JSONObject;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.impl.RepositoryServiceImpl;
import org.camunda.bpm.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/workflow")
public class CamundaController {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private HistoryService historyService;
@Autowired
private TaskService taskService;
@Autowired
private IdentityService identityService;
/**
* 查询已部署的流程定义列表
*/
@GetMapping("/deployed-cases")
public List<String> getDeployedCases() {
// and convert it to a list of WorkflowCase objects
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
.list();
return list.stream().map(e -> e.getId() + "," + e.getName() + "," + e.getVersion()).collect(Collectors.toList());
}
/**
* 部署流程案例
*
* @param file BPMN文件
* @param name 流程名称
*/
@PostMapping("/deploy-case")
public String deployCase(@RequestParam("file") MultipartFile file, @RequestParam("name") String name) {
try (InputStream is = file.getInputStream()) {
// 获取文件的名称
String originalFilename = file.getOriginalFilename();
Deployment deployment = repositoryService.createDeployment()
.addInputStream(originalFilename, is)
.name(name)
.deploy();
return deployment.getId();
} catch (Exception e) {
return e.getMessage();
}
}
/**
* 启动流程实例
*
* @param caseId 流程定义id
*/
@PostMapping("/start-case/{caseId}")
public String startCase(@PathVariable(value = "caseId") String caseId, @RequestBody String json) {
Map map = new HashMap();
if (json != null && !"".equals(json)) {
map = JSONObject.parseObject(json, Map.class);
}
// 部署流程
ProcessInstance processInstance = runtimeService.startProcessInstanceById(caseId, map);
return processInstance.getId();
}
/**
* 查询正在执行的任务列表
*
* @param caseId 流程实例id
*/
@GetMapping("/active-tasks")
public List<String> getActiveTasks(@RequestParam(value = "caseId", required = false) String caseId) {
List<Task> list = taskService.createTaskQuery().processInstanceId(caseId).list();
// Implement the logic here
return list.stream().map(e -> {
return "任务id:" + e.getId() + ", 实例id:" + e.getProcessInstanceId() + ",流程定义id:" + e.getProcessDefinitionId();
}).collect(Collectors.toList());
}
/**
* 审批通过任务
*
* @param taskId 任务id
* @param json 参数json字符串
*/
@PostMapping("/approve-task/{taskId}")
public String approveTask(@PathVariable("taskId") String taskId, @RequestBody String json) {
if (json != null && !"".equals(json)) {
Map map = JSONObject.parseObject(json, Map.class);
taskService.setVariables(taskId, map);
}
taskService.complete(taskId);
return "Task approved successfully";
}
/**
* 查询历史流程案例列表
*
* @param caseId 流程实例id
*/
@GetMapping("/historical-cases")
public List<HistoricTaskInstance> getHistoricalCases(@RequestParam(value = "caseId", required = false) String caseId) {
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(caseId).list();
return list;
}
/**
* 查询流程实例参数列表
*
* @param taskId 任务id
*/
@GetMapping("/task-variables")
public Map<String, Object> getVariables(@RequestParam(value = "taskId", required = false) String taskId) {
Map<String, Object> variables = taskService.getVariables(taskId);
return variables;
}
/**
* 回退
*
* @param processInstanceId 流程实例id
*/
@PostMapping("/rollback/{instanceId}")
public String rollback(@PathVariable("instanceId") String processInstanceId) {
Task activeTask = taskService.createTaskQuery()
.processInstanceId(processInstanceId)
.active()
.singleResult();
HistoricTaskInstance taskInstance = historyService.createHistoricTaskInstanceQuery()
.taskId(activeTask.getId())
.singleResult();
// 获取流程定义
ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(taskInstance.getProcessDefinitionId());
// 获取当前活动
ActivityImpl currentActivity = processDefinitionEntity.findActivity(taskInstance.getTaskDefinitionKey());
// 获取起始活动
List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
.activityType("userTask")
.processInstanceId(processInstanceId)
.finished()
.orderByHistoricActivityInstanceEndTime()
.desc()
.list();
if (historicActivityInstances.size() == 0) {
return "没有历史活动";
}
ActivityImpl lastActivity = processDefinitionEntity.findActivity(historicActivityInstances.get(0).getActivityId());
// 退回至起点
runtimeService.createProcessInstanceModification(processInstanceId)
.cancelAllForActivity(currentActivity.getActivityId())
.startBeforeActivity(lastActivity.getActivityId())
.setAnnotation("退回")
.execute();
return "Rollback successful";
}
/**
* 删除流程案例
*
* @param caseId 流程定义id
*/
@DeleteMapping("/delete-case/{caseId}")
public String deleteCase(@PathVariable(value = "caseId") String caseId) {
repositoryService.deleteProcessDefinition(caseId);
return "successful";
}
}
监听类代码
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
@Component("monitorExecution")
public class MonitorExecution implements JavaDelegate {
@Override
public void execute(DelegateExecution delegateExecution) throws Exception {
String processInstanceId = delegateExecution.getProcessInstanceId();
System.out.println("自动执行: " + processInstanceId);
}
}
--------------------------------------------------------------------
import org.camunda.bpm.engine.delegate.*;
public class ATask implements ExecutionListener, JavaDelegate {
@Override
public void execute(DelegateExecution delegateExecution) throws Exception {
System.out.println("待发布....");
}
@Override
public void notify(DelegateExecution delegateExecution) throws Exception {
}
}
以下步骤:
- 创建一个简单的审批流程demo1.bpmn。
- 部署流程定义。
- 启动流程实例。
- 查询任务列表。
- 完成任务。
- 验证流程是否已完成。
- 删除流程定义。
结论
本文介绍了如何在Spring Boot项目中集成Camunda流程引擎,并提供了一个完整的业务流程的样例demo。希望这篇文章对您有所帮助。