一、工作流介绍
工作流(Workflow)就是工作流程的计算模型,即将工作流程中的工作如何前后组织在一起的逻辑和规则在计算机中以恰当的模型进行表示并对其实施计算。工作流要解决的主要问题是:为实现某个业务目标,在多个参与者之间,利用计算机,按某种预定规则自动传递文档、信息或者任务。
二、安装插件
插件名字
actiBPM
离线安装
IDEA新版本需要到插件市场下载并离线安装。
重启IDEA
三、BPMN介绍
所有符号
完整的工作流由StartEvent
开始,由EndEvent
结束,中间穿插着各种Task
任务,Gateway
负责构建复杂的流程(如请假审批,请假1天的,和请假3天的流程是不一样的),SubProcess
子流程,Annotation
注解。
四、Activiti使用步骤
- 部署Activiti,整合Activiti依赖
- 定义流程,BPMN建模
- 部署流程,把流程内容存储起来
- 启动流程实例,开始一次业务流程运作
- 用户查询代办任务,activiti查询流程到哪个步骤
- 用户办理业务,查询到自己的待办任务后就可以办理某个业务了
- 结束流程,没有下一个任务节点流程就完成了
五、创建数据库表
创建数据库
创建项目
导入依赖
<properties>
<activiti.version>7.0.0.Beta3</activiti.version>
</properties>
<!-- 工作流 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activiti.version}</version>
<!-- 解决与mybatis冲突问题 -->
<exclusions>
<exclusion>
<artifactId>mybatis</artifactId>
<groupId>org.mybatis</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 工作流与Spring相关依赖 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- BPMN模型处理 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- BPMN模型转换 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- BPMN数据转换 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- BPMN布局 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-layout</artifactId>
<version>${activiti.version}</version>
</dependency>
创建配置文件
activiti.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置数据源-->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl"
value="jdbc:mysql://**********/activiti-service-dev?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="**********"/>
</bean>
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 使用上面的数据源 -->
<property name="dataSource" ref="dataSource"/>
<!--是否生成表结构,初始化为true,生成后为false-->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
生成表结构
package com.qiang.activiti;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@Slf4j
class ActivitiServiceApplicationTests {
/**
* 生成表结构
*/
@Test
public void testCreateTable() {
ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
log.info("testCreateTable{}", defaultProcessEngine);
}
}
启动程序
运行结果
生成25张表。
六、流程引擎主要方法
七、BPMN流程建模
新建文件
新建一个请假流程
整体流程配置,设置流程ID为leave
,名字为员工请假审批流程
。
配置提交请假申请的责任人为worker
。
配置部门经理审批的责任人为manager
。
配置财务主管审批的责任人为financer
。
八、导出流程图
将BPMN文件复制一份为XML文件
选择BPMN设计器
查看流程图
导出流程图
九、部署流程
9.1 单个流程部署
/**
* 部署流程(单个流程)
*/
@Test
public void testDeployment() {
// 创建 processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取 repositoryService 实例
RepositoryService repositoryService = processEngine.getRepositoryService();
// 使用 repositoryService 进行部署
Deployment deployment = repositoryService.createDeployment()
// 对应的 bpmn 文件
.addClasspathResource("bpmn/leave.bpmn")
// 对应的流程图文件,命名规范(流程ID.png)为 leave.png/jpg/gif/svg
.addClasspathResource("bpmn/image/leave.png")
.name("请假申请流程")
.deploy();
// 获取部署后的信息
log.info("部署的流程ID:{}", deployment.getId());
log.info("部署流程名称:{}", deployment.getName());
}
启动实例
数据已经存到数据库
包括XML文件以及图片文件
9.2 批量流程部署
将bpmn文件以及流程图文件按规定名字打成压缩包
文件位置
实例代码
/**
* 批量部署流程
*/
@Test
public void testDeploymentByZip() {
InputStream inputStream = this
.getClass()
.getClassLoader().getResourceAsStream("bpmn/zip/test.zip");
assert inputStream != null;
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
// 创建 processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取 repositoryService 实例
RepositoryService repositoryService = processEngine.getRepositoryService();
// 使用 repositoryService 进行部署
Deployment deployment = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.deploy();
// 获取部署后的信息
log.info("部署的流程ID:{}", deployment.getId());
log.info("部署流程名称:{}", deployment.getName());
}
启动实例
数据已经存到数据库
包括XML文件以及图片文件
十、启动流程实例
根据流程 ID 启动流程
/**
* 启动流程实例
*/
@Test
public void testStartProcess() {
// 创建 processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取 runtimeService 实例
RuntimeService runtimeService = processEngine.getRuntimeService();
// 根据流程 ID 启动流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave");
// 获取启动流程后的信息
log.info("流程定义ID:{}", processInstance.getProcessDefinitionId());
log.info("流程实例ID:{}", processInstance.getId());
log.info("当前活动ID:{}", processInstance.getActivityId());
}
执行结果
十一、查询待办任务
启动流程实例后,该节点到达了提交请假申请的节点,需要任务负责人查询当前负责的待办任务(当前负责人为worker要提交请假申请)。
/**
* 查询当前负责人的待办任务
*/
@Test
public void testFindPersonalTaskList() {
// 当前负责人
String assignee = "worker";
// 创建 processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建 taskService
TaskService taskService = processEngine.getTaskService();
// 根据流程 Key 和任务负责人查询代办任务
List<Task> tasks = taskService.createTaskQuery()
// 流程 Key
.processDefinitionKey("leave")
// 负责人
.taskAssignee(assignee)
.list();
tasks.forEach(task -> {
log.info("流程实例ID:{}", task.getProcessInstanceId());
log.info("流程任务ID:{}", task.getId());
log.info("流程任务负责人:{}", task.getAssignee());
log.info("流程任务名称:{}", task.getName());
});
}
启动实例
十二、完成任务
当前任务节点到了worker审批,根据查询到的任务ID完成任务。
/**
* 完成任务
*/
@Test
public void testCompleteTask() {
// 当前负责人
String assignee = "worker";
// 创建 processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建 taskService
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery()
.processDefinitionKey("leave")
// 负责人
.taskAssignee(assignee)
// 返回一个任务
.singleResult();
// 根据任务ID完成任务
taskService.complete(task.getId());
}
启动实例
完成任务后,再次查询worker的代办任务已经没有了,任务已经流转到了部门经理审批了,此时可以查询到manager的待办任务了。
/**
* 查询当前负责人的待办任务
*/
@Test
public void testFindPersonalTaskList() {
// 当前负责人
String assignee = "manager";
// 创建 processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建 taskService
TaskService taskService = processEngine.getTaskService();
// 根据流程 Key 和任务负责人查询代办任务
List<Task> tasks = taskService.createTaskQuery()
// 流程 Key
.processDefinitionKey("leave")
// 负责人
.taskAssignee(assignee)
.list();
tasks.forEach(task -> {
log.info("流程实例ID:{}", task.getProcessInstanceId());
log.info("流程任务ID:{}", task.getId());
log.info("流程任务负责人:{}", task.getAssignee());
log.info("流程任务名称:{}", task.getName());
});
}
启动实例
十三、查询当前流程定义
查询当前流程定义下有哪些实例正在跑。
/**
* 查询当前流程定义
*/
@Test
public void testQueryProcessInstance(){
// 流程定义Key
String processDefinitionKey = "leave";
// 创建 processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取 runtimeService 实例
RuntimeService runtimeService = processEngine.getRuntimeService();
List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
.processDefinitionKey(processDefinitionKey)
.list();
processInstances.forEach(processInstance -> {
log.info("===========================================");
log.info("流程实例ID:{}", processInstance.getProcessInstanceId());
log.info("所属流程定义ID:{}", processInstance.getProcessDefinitionId());
log.info("是否执行完成:{}", processInstance.isEnded());
log.info("是否暂停流程:{}", processInstance.isSuspended());
log.info("当前活动标识:{}", processInstance.getActivityId());
log.info("业务关键字:{}", processInstance.getBusinessKey());
});
}
启动实例
这里两个流程
十四、查询所有流程定义
查询在一个BPMN文件中定义了哪些流程。
/**
* 查询所有流程定义
*/
@Test
public void testQueryProcessDefinition() {
// 创建 processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取 repositoryService 实例
RepositoryService repositoryService = processEngine.getRepositoryService();
// 获取 processDefinitionQuery 实例
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
List<ProcessDefinition> processDefinitions = processDefinitionQuery.processDefinitionKey("leave")
// 按照版本排序
.orderByProcessDefinitionVersion()
// 倒序
.desc()
// 返回集合
.list();
processDefinitions.forEach(processDefinition -> {
log.info("===========================================");
log.info("流程定义ID:{}", processDefinition.getId());
log.info("流程定义NAME:{}", processDefinition.getName());
log.info("流程定义KEY:{}", processDefinition.getKey());
log.info("流程定义Version:{}", processDefinition.getVersion());
log.info("流程部署ID:{}", processDefinition.getDeploymentId());
});
}
启动实例
十五、删除流程
/**
* 删掉流程
*/
@Test
public void testDeleteDeployment() {
// 流程部署Id
String deploymentId = "1";
// 创建 processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取 repositoryService 实例
RepositoryService repositoryService = processEngine.getRepositoryService();
// 根据Id删除,如果该流程有实例正在启动则报错
repositoryService.deleteDeployment(deploymentId);
// 根据Id删除,如果该流程有实例正在启动则会级联删除
repositoryService.deleteDeployment(deploymentId,true);
}
九十九、常见问题
1.1 BPMN文件乱码
设置IDEA的编码格式为UTF-8。
加上这一行然后重启IDEA即可。
-Dfile.encoding=UTF-8
乱码解决。
1.2 数据库版本不一致
问题:
org.activiti.engine.ActivitiException: Could not update Activiti database schema: unknown version from database: '6.0.0.4'
解决:
<activiti.version>6.0.0</activiti.version>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${activiti.version}</version>
</dependency>
作者(Author):小强崽
来源(Source):https://www.wuduoqiang.com/archives/Activiti7工作流
协议(License):署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
版权(Copyright):商业转载请联系作者获得授权,非商业转载请注明出处。 For commercial use, please contact the author for authorization. For non-commercial use, please indicate the source.