Activiti官方文档:Activiti User Guide
本篇博客代码地址:https://gitee.com/zhangyin_0116/springboot-activiti-learn.git
一、Activiti数据库表名说明
Activiti 的数据库名称都以 ACT_ 开头。第二部分是表用例的双字符标识。此用例也将大致匹配服务 API。
- *ACT_RE_*:RE* 代表 。具有此前缀的表包含静态信息,例如流程定义和流程资源(图像、规则等)。
repository
- **ACT_RU_**:RU* 代表 。这些是包含流程实例、用户任务、变量、作业等的运行时数据的运行时表。Activiti 仅在流程实例执行期间存储运行时数据,并在流程实例结束时删除记录。这使运行时表保持小而快速。
runtime
- **ACT_ID_**:ID* 代表 。这些表包含标识信息,例如用户、组等。
identity
- **ACT_HI_**:HI* 代表 。这些是包含历史数据的表,例如过去的流程实例、变量、任务等。
history
- **ACT_GE_***:数据,用于各种用例。
general
二、Spring boot整合activiti
1. 创建springboot项目
2. 引入activiti依赖及项目依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.activiti/activiti-spring-boot-starter -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
</dependency>
</dependencies>
通过引入activiti-spring-boot-starter依赖项,可以轻松地在Spring Boot应用程序中集成和配置Activiti引擎,而无需手动配置所有的Activiti相关组件和依赖项。
该依赖项提供了一系列自动配置类和默认配置,使得Activiti的集成变得更加简单。它会自动配置数据库连接、事务管理器、流程引擎以及与Spring Boot框架的其他集成。
使用activiti-spring-boot-starter依赖项,可以通过@Autowired注解轻松注入Activiti的各种服务接口(如RepositoryService、RuntimeService、TaskService等),并使用它们操作流程引擎。
此外,该依赖项还提供了一些自定义属性配置,您可以通过在application.properties或application.yml文件中进行相应配置来定制Activiti的行为。
3. 配置数据源
(1)创建数据源配置文件
@Configuration
public class DruidConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClassName);
// 其他 Druid 相关配置,如最大连接数、初始连接数等
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
(2)配置文件
# 数据源配置
spring:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/activiti-learn?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
username: root
password: 123456
4. 配置Acitviti引擎
@Configuration
public class ActivitiConfiguration {
@Autowired
private DataSource dataSource;
@Autowired
private PlatformTransactionManager platformTransactionManager;
public ActivitiConfiguration() {
}
//通过@Bean注解将SpringProcessEngineConfiguration实例声明为Spring Bean,使其可供其他组件注入和使用
@Bean
public SpringProcessEngineConfiguration springProcessEngineConfiguration() {
SpringProcessEngineConfiguration spec = new SpringProcessEngineConfiguration();
//设置数据源,将注入的数据源设置到SpringProcessEngineConfiguration实例中
spec.setDataSource(this.dataSource);
//设置事务管理器将注入的事务管理器设置到SpringProcessEngineConfiguration实例中
spec.setTransactionManager(this.platformTransactionManager);
//设置数据库模式更新策略 true表示在启动时自动创建或更新Activiti引擎所需的数据库表结构
spec.setDatabaseSchemaUpdate("true");
Resource[] resources = null;
//配置流程部署资源
//使用PathMatchingResourcePatternResolver从classpath中的bpmn目录下加载所有以.bpmn为扩展名的文件作为流程定义资源,
// 并将它们设置到SpringProcessEngineConfiguration实例中。
try {
resources = (new PathMatchingResourcePatternResolver()).getResources("classpath*:bpmn/*.bpmn");
} catch (IOException var4) {
var4.printStackTrace();
}
spec.setDeploymentResources(resources);
return spec;
}
}
5. 启动项目
启动项目,可以看到对应的数据库中自动生成了与Activiti相关的表。
三、Activiti接口
1. 流程引擎API和服务
从 ProcessEngine 中,可以获取包含工作流/BPM 方法的各种服务。
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService = processEngine.getIdentityService();
HistoryService historyService = processEngine.getHistoryService();
FormService formService = processEngine.getFormService();
DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
2. 使用Activiti服务
(1)绘制流程图,生成bpmn文件
这里是一个请假的流程定义bpmn文件:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.kafeitu.me/activiti/leave">
<process id="leave" name="请假流程-普通表单" isExecutable="true">
<documentation>请假流程演示</documentation>
<startEvent id="startevent1" name="提交申请" activiti:initiator="applyUserId"></startEvent>
<userTask id="deptLeaderVerify" name="部门领导审批" activiti:candidateGroups="deptLeader" ></userTask>
<exclusiveGateway id="exclusivegateway5"></exclusiveGateway>
<userTask id="modifyApply" name="调整申请" activiti:assignee="${applyUserId}">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<userTask id="hrVerify" name="人事审批" activiti:candidateGroups="hr"></userTask>
<exclusiveGateway id="exclusivegateway6"></exclusiveGateway>
<userTask id="reportBack" name="销假" activiti:assignee="${applyUserId}">
</userTask>
<endEvent id="endevent1" name="结束"></endEvent>
<exclusiveGateway id="exclusivegateway7"></exclusiveGateway>
<sequenceFlow id="flow2" sourceRef="startevent1" targetRef="deptLeaderVerify"></sequenceFlow>
<sequenceFlow id="flow3" sourceRef="deptLeaderVerify" targetRef="exclusivegateway5"></sequenceFlow>
<sequenceFlow id="flow6" sourceRef="hrVerify" targetRef="exclusivegateway6"></sequenceFlow>
<sequenceFlow id="flow8" sourceRef="reportBack" targetRef="endevent1"></sequenceFlow>
<sequenceFlow id="flow11" sourceRef="modifyApply" targetRef="exclusivegateway7"></sequenceFlow>
<sequenceFlow id="flow5" name="同意" sourceRef="exclusivegateway5" targetRef="hrVerify">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow4" name="不同意" sourceRef="exclusivegateway5" targetRef="modifyApply">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow7" name="同意" sourceRef="exclusivegateway6" targetRef="reportBack">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow9" name="不同意" sourceRef="exclusivegateway6" targetRef="modifyApply">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow12" name="结束流程" sourceRef="exclusivegateway7" targetRef="endevent1">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow10" name="重新申请" sourceRef="exclusivegateway7" targetRef="deptLeaderVerify">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${pass}]]></conditionExpression>
</sequenceFlow>
</process>
(2)部署流程定义
部署流程定义的方法有很多种,这里只介绍一种实际项目中最常用的。
@Autowired
private ProcessEngine processEngine;
@Autowired
private RepositoryService repositoryService;
@Test
void deployProcessDefinition() throws FileNotFoundException {
String processDefinitionFilePath = "D:\\Java_Dev\\IDEA_Projects\\Personal_project\\springboot-activiti-learn\\src\\main\\resources\\leave.bpmn";
Deployment deploy = this.repositoryService.createDeployment()
.addInputStream(processDefinitionFilePath, new FileInputStream(processDefinitionFilePath))
.deploy();
System.out.println("部署流程定义成功:"+ deploy);
}
查看数据库表 act_re_procdef:
可以看到已经部署成功
(3)启动流程
启动流程的方法也有很多种,这里介绍的方法是比较常用的启动流程的方法,需要传递三个参数,分别是:
processDefinitionKey(流程定义key), businessKey(业务key), variables(参数)
这里我们使用流程定义的id去查询流程定义的key,用流程定义的key来启动流程
@Test
void startProcessInstance() {
//启动流程时传递的参数列表 这里根据实际情况 也可以选择不传
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("姓名", "张三");
variables.put("请假天数", new Integer(4));
variables.put("请假原因", "我很累!");
// 根据流程定义ID查询流程定义 leave:1:10004是我们刚才部署的流程定义的id
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionId("leave:1:10004")
.singleResult();
// 获取流程定义的Key
String processDefinitionKey = processDefinition.getKey();
//定义businessKey businessKey一般为流程实例key与实际业务数据的结合
//假设一个请假的业务 在数据库中的id是1001
String businessKey = processDefinitionKey + ":" + "1001";
//设置启动流程的人
Authentication.setAuthenticatedUserId("xxyin");
ProcessInstance processInstance = this.runtimeService
.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
System.out.println("流程启动成功:" + processInstance);
}
查看数据库表 act_hi_procinst:
(4)查询待办任务列表
@Test
void findTodoTask() {
TaskQuery taskQuery = ((TaskQuery)taskService.createTaskQuery().orderByTaskCreateTime()).asc();
// <userTask id="deptLeaderVerify" name="部门领导审批" activiti:assignee="zhangsan" ></userTask>
//添加查询条件 查询指派给 zhangsan 的任务 假设这个任务指派给了zhangsan
taskQuery.taskAssignee("zhangsan");
//添加查询条件 查询流程定义key为 leave 的任务
taskQuery.processDefinitionKey("leave");
List<Task> tasks = taskQuery.list();
// 处理查询结果
for (Task task : tasks) {
System.out.println("Task ID: " + task.getId());
System.out.println("Task Name: " + task.getName());
// 其他任务属性的获取和处理
}
}
运行结果:
Task ID: 15010
Task Name: 部门领导审批
(5)完成任务
通过查询可以看到当前流转到的任务是“部门领导审批”,可以在表act_ru_task中看到:
@Test
void complete() {
//这里模拟传进来的参数
String taskId = "15010";
String assignee = "zhangsan";
Map<String, Object> variables = new HashMap<>();
variables.put("pass",true);
variables.put("comment","同意请假");
//根据任务id获取到当前的任务
TaskQuery query = this.taskService.createTaskQuery();
Task task = (Task)((TaskQuery)query.taskId(taskId)).singleResult();
//将参数列表的评论赋值给comment
String taskComment = variables.remove("comment").toString();
//向特定的任务添加评论
if (!taskComment.equals("")) {
taskService.addComment(taskId, task.getProcessInstanceId(), taskComment);
}
//如果当前任务没有指派人,需要先使用 claim() 方法领取任务
taskService.claim(taskId,assignee);
//如果有指派人,直接完成任务
taskService.complete(taskId,variables);
}
完成该任务后,再次查看 act_ru_task 表,可以看到任务已经流转到了“人事审批”:
查看评论表act_hi_comment,可以看到任务评论已经添加到对应的任务上:
(6)暂停和激活流程定义
可以挂起进程定义。挂起流程定义时,无法创建新的流程实例(将引发异常)。暂停进程定义是通过 :**
RepositoryService
**
@Test
void suspendProcessDefinition() {
repositoryService.suspendProcessDefinitionByKey("leave");
try {
runtimeService.startProcessInstanceByKey("leave");
} catch (ActivitiException e) {
e.printStackTrace();
}
}
运行结果:
2023-05-21 21:10:08.425 ERROR 29036 --- [ main] o.a.e.impl.interceptor.CommandContext : Error while closing command context
org.activiti.engine.ActivitiException: Cannot start process instance. Process definition 请假流程-普通表单 (id = leave:1:10004) is suspended
如果想要激活,执行:
repositoryService.activateProcessDefinitionByKey("leave");
(7)查询接口
有两种方式可以从引擎查询数据,查询API和native查询
查询 API
允许使用流畅的 API 对完全类型安全的查询进行编程,例如:
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("zhangsan")
.processVariableValueEquals("name", "zhangsan")
.orderByDueDate().asc()
.list();
native查询
允许您编写自己的 SQL 查询。返回类型由您使用的 Query 对象定义,数据映射到正确的对象中,例如 Task、ProcessInstance、Execution 等。由于查询将在数据库中触发,因此必须使用数据库中定义的表名和列名;
这个是查询任务名为“人事审批”的任务:
@Test
void nativeAPI() {
// 创建原生任务查询对象 查询任务名为 人事审批 的任务
NativeTaskQuery nativeTaskQuery = taskService.createNativeTaskQuery()
.sql("SELECT * FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}")
.parameter("taskName", "人事审批");
// 执行查询并获取结果
List<Task> tasks = nativeTaskQuery.list();
// 处理查询结果
for (Task task : tasks) {
System.out.println("Task ID: " + task.getId());
System.out.println("Task Name: " + task.getName());
// 其他任务属性的获取和处理
}
}
这个是查询任务id为 35006 ,并且没有指派人的任务:
@Test
void nativeAPI2() {
// 创建原生任务查询对象
NativeTaskQuery nativeTaskQuery = taskService.createNativeTaskQuery()
.sql("SELECT * FROM " + managementService.getTableName(Task.class) +
" T WHERE T.ID_ = #{taskId} AND T.ASSIGNEE_ = #{assignee}" )
.parameter("taskId","35006")
.parameter("assignee", "");
// 执行查询并获取结果
List<Task> tasks = nativeTaskQuery.list();
// 处理查询结果
for (Task task : tasks) {
System.out.println("Task ID: " + task.getId());
System.out.println("Task Name: " + task.getName());
// 其他任务属性的获取和处理
}
}
}
运行结果:
Task ID: 35006
Task Name: 人事审批
(8)EUL表达式
Activiti 使用 UEL 进行表达式解析。UEL 代表统一表达式语言,是 EE6 规范的一部分。分为两种:值表达式和方法表达式
- 值表达式:解析为一个值。默认情况下,所有流程变量都可供使用。
${myVar}
${myBean.myProperty}
- 方法表达式:调用方法,带或不带参数。调用不带参数的方法时,请确保在方法名称后添加空括号(因为这会将表达式与值表达式区分开来)。传递的参数可以是文本值或自行解析的表达式。例子:
${printer.print()}
${myBean.addNewOrder('orderName')}
${myBean.doSomething(myVar, execution)}
在所有流程变量之上,还有一些默认对象可用于表达式:
execution
:保存有关正在进行的执行的其他信息的 。DelegateExecution
task
:保存有关当前任务的其他信息的 。注意:仅适用于从任务侦听器计算的表达式。DelegateTask
- **
authenticatedUserId
**:当前经过身份验证的用户的 ID。如果没有用户经过身份验证,则该变量不可用。
四、BPMN2.0简介
1. 定义一个过程
- process:有两个属性 id 和 name。
<definitions
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="Examples">
<process id="myProcess" name="My First Process">
..
</process>
</definitions>
- sequenceFlow:序列流
<sequenceFlow id="flow5" name="同意" sourceRef="exclusivegateway5" targetRef="hrVerify">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${pass}]]>
</conditionExpression>
</sequenceFlow>
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${order.price > 100 && order.price < 250}]]>
</conditionExpression>
- exclusiveGateway 排他网关
在XML中的表示:
<exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" />
<sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="theTask1">
<conditionExpression xsi:type="tFormalExpression">${input == 1}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="theTask2">
<conditionExpression xsi:type="tFormalExpression">${input == 2}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow4" sourceRef="exclusiveGw" targetRef="theTask3">
<conditionExpression xsi:type="tFormalExpression">${input == 3}</conditionExpression>
</sequenceFlow>
- parallelGateway:并行网关
XML定义如下:
<startEvent id="theStart" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="fork" />
<parallelGateway id="fork" />
<sequenceFlow sourceRef="fork" targetRef="receivePayment" />
<sequenceFlow sourceRef="fork" targetRef="shipOrder" />
<userTask id="receivePayment" name="Receive Payment" />
<sequenceFlow sourceRef="receivePayment" targetRef="join" />
<userTask id="shipOrder" name="Ship Order" />
<sequenceFlow sourceRef="shipOrder" targetRef="join" />
<parallelGateway id="join" />
<sequenceFlow sourceRef="join" targetRef="archiveOrder" />
<userTask id="archiveOrder" name="Archive Order" />
<sequenceFlow sourceRef="archiveOrder" targetRef="theEnd" />
<endEvent id="theEnd" />
五、查询历史
在 API 中,可以查询所有 5 个历史记录实体:
1. 历史过程实例查询
@Test
void historyAPI1() {
List<HistoricProcessInstance> processInstances = historyService.createHistoricProcessInstanceQuery()
.processDefinitionKey("leave")
.orderByProcessInstanceDuration()
.desc()
.listPage(0, 10);
System.out.println("ProcessInstance: "+ processInstances);
}
2. 历史变量实例查询
@Test
void historyAPI2() {
List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery()
.processInstanceId("60002")
.orderByVariableName().desc()
.list();
System.out.println("historicVariableInstances: "+ historicVariableInstances);
}
3. 历史活动实例查询
查询已经完成的任务(用户任务):
@Test
void historyAPI3() {
List<HistoricActivityInstance> userTask = historyService.createHistoricActivityInstanceQuery()
.activityType("userTask")
.processDefinitionId("leave:1:10004")
.finished()
.orderByHistoricActivityInstanceEndTime()
.desc()
.listPage(0, 1);
System.out.println("userTask: "+ userTask);
}
4. 历史细节查询
@Test
void historyAPI4() {
List<HistoricDetail> historicDetails = historyService.createHistoricDetailQuery()
.variableUpdates()
.processInstanceId("60002")
.orderByVariableName().asc()
.list();
System.out.println("historicDetails: "+ historicDetails);
}
5. 历史任务实例查询
查询已完成的任务:
@Test
void historyAPI5() {
List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
.finished()
.orderByHistoricTaskInstanceDuration()
.desc()
.listPage(0, 10);
System.out.println("historicTaskInstances: "+ historicTaskInstances);
}
查询未完成的任务:
@Test
void historyAPI6() {
List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
.unfinished()
.orderByHistoricTaskInstanceDuration()
.desc()
.listPage(0, 10);
System.out.println("historicTaskInstances: "+ historicTaskInstances);
}
6. 历史配置
- **
none
**:跳过所有历史记录存档。这是运行时进程执行的最高性能,但没有历史信息可用。 - **
activity
**:存档所有流程实例和活动实例。在流程实例结束时,顶级流程实例变量的最新值将被复制到历史变量实例中。不会存档任何详细信息。 - **
audit
**:这是默认值。它存档所有流程实例、活动实例,使变量值持续同步以及提交的所有表单属性,以便通过表单进行的所有用户交互都是可追溯的并且可以审核的。 - **
full
**:这是历史存档的最高级别,因此速度最慢。此级别存储级别中的所有信息以及所有其他可能的详细信息,主要是过程变量更新。audit
版权归原作者 小小印z 所有, 如有侵权,请联系我们删除。