:-: ![](https://img.kancloud.cn/c4/7e/c47e7faf15f85562f56d910b83e1605e_1156x215.png)
*本小节的源码:/activiti-01/src/test/java/-/ActivitiDemo*
下面演示上图流程操作过程,步骤如下:
[TOC]
# 1. 流程定义
**1. 绘制流程图**
根据【流程设计器的使用】一节我已经画出了上面的流程图。
一个`.bpmn`文件可以定义多个流程,不过我们建议每个文件只包含一个流程定义, 可以简化开发过程中的维护难度。
**2. 将流程图的`.bpmn`文件导出为`.png`图片**
(1)将`.bpmn`后缀改成`.xml`后缀。
![](https://img.kancloud.cn/e7/e3/e7e30caa14edac0bc30d9d8e525e03c9_1457x543.png)
(2)使用流程设计器打开`.xml`文件。
![](https://img.kancloud.cn/fd/74/fd746da750a1c84778917dd05b652821_1296x788.png)
(3)导出为图片,保存到`resources`目录下。
![](https://img.kancloud.cn/63/8c/638c720a93b8fd67a8e2c37a1e2d2972_1728x484.png)
(4)再将`.xml`后缀改回`.bpmn`后缀。
<br/>
# 2. 流程部署
流程部署就是把流程图的相关数据保存到数据库中。
**部署方式1:单个文件部署**
```java
@Test
public void testDeployment() {
//1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2、获取RepositoryServcie
RepositoryService repositoryService = processEngine.getRepositoryService();
//3、使用service进行流程的部署,定义一个流程的名字,把bpmn和png部署到数据中
Deployment deploy = repositoryService.createDeployment()
.name("出差申请流程")
.addClasspathResource("bpmn-02/evection.bpmn")
.addClasspathResource("bpmn-02/image/evection.png")
.deploy();
//4、输出部署信息
System.out.println("流程部署id=" + deploy.getId()); //id为数据库中随机分配的id
System.out.println("流程部署名字=" + deploy.getName());//名字就是上面name("出差申请流程")设置的名字
}
```
**部署方式2:压缩包文件部署**
```java
@Test
public void deployProcessByZip() {
//获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//读取资源包文件,构造成inputStream
InputStream inputStream = this.getClass()
.getClassLoader()
.getResourceAsStream("bpmn-02/evection.zip");
//用inputStream 构造ZipInputStream
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
//使用压缩包的流进行流程的部署
Deployment deploy = repositoryService.createDeployment()
.name("出差申请流程")
.addZipInputStream(zipInputStream)
.deploy();
System.out.println("流程部署id=" + deploy.getId());
System.out.println("流程部署的名称=" + deploy.getName());
}
```
<br/>
完成上面的部署后我们到数据库中查看主要的3张表。
```sql
-- 流程定义部署表:每部署一次增加一条记录,即运行上面的部署程序一次,记录就增加一条记录
mysql> select * from act_re_deployment;
+-----+--------------+-----------+------+------------+---------------------+-----------------+
| ID_ | NAME_ | CATEGORY_ | KEY_ | TENANT_ID_ | DEPLOY_TIME_ | ENGINE_VERSION_ |
+-----+--------------+-----------+------+------------+---------------------+-----------------+
| 1 | 出差申请流程 | NULL | NULL | | 2021-10-09 14:57:27 | NULL |
+-----+--------------+-----------+------+------------+---------------------+-----------------+
-- 流程定义表:部署每个新的流程定义都会在这张表中增加一条记录
-- 注意,KEY_ 这个字段是用来唯一识别不同流程的关键字
mysql> select * from act_re_procdef;
+----------------+------+------------------------------+--------------+------------+----------+----------------+-----------------------+...
| ID_ | REV_ | CATEGORY_ | NAME_ | KEY_ | VERSION_ | DEPLOYMENT_ID_ | RESOURCE_NAME_ | DGRM_...
+----------------+------+------------------------------+--------------+------------+----------+----------------+-----------------------+...
| myEvection:1:4 | 1 | http://www.activiti.org/test | 出差申请流程 | myEvection | 1 | 1 | bpmn-02/evection.bpmn | NULL ...
+----------------+------+------------------------------+--------------+------------+----------+----------------+-----------------------+...
-- 流程资源表
mysql> select id_, rev_, name_, deployment_id_, left(bytes_, 30) as bytes_, generated_ from act_ge_bytearray;
+-----+------+----------------------------+----------------+--------------------------------+------------+
| id_ | rev_ | name_ | deployment_id_ | bytes_ | generated_ |
+-----+------+----------------------------+----------------+--------------------------------+------------+
| 2 | 1 | bpmn-02/evection.bpmn | 1 | <?xml version="1.0" encoding=" | 0 |
| 3 | 1 | bpmn-02/image/evection.png | 1 | Binary/Image | 0 |
+-----+------+----------------------------+----------------+--------------------------------+------------+
act_re_deployment和act_re_procdef一对多关系,一次部署在流程部署表生成一条记录,
但一次部署可以部署多个流程定义,每个流程定义在流程定义表生成一条记录。
每一个流程定义在act_ge_bytearray会存在两个资源记录,bpmn和png。
建议:一次部署一个流程,这样部署表和流程定义表是一对一有关系,方便读取流程部署及流程定义信息。
```
<br/>
# 3. 启动流程
```java
/**
* 启动流程实例,将会依次调用下面的表,每次运行该程序就会创建一个流程实例
* `act_hi_actinst` 流程实例执行历史信息
* `act_hi_identitylink` 流程参与用户的历史信息
* `act_hi_procinst` 流程实例的历史信息
* `act_hi_taskinst` 流程任务的历史信息
* `act_ru_execution` 流程执行信息
* `act_ru_identitylink` 流程的正在参与用户信息
* `act_ru_task` 流程当前任务信息
*/
@Test
public void testStartProcess() {
//1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2、获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//3、根据流程定义的id启动流程
ProcessInstance instance = runtimeService.startProcessInstanceByKey("myEvection");
//4、输出内容
System.out.println("流程定义ID:" + instance.getProcessDefinitionId()); //myEvection:1:4
System.out.println("流程实例ID:" + instance.getId()); //2501
System.out.println("当前活动的ID:" + instance.getActivityId()); //null
}
```
# 4. 查询个人待执行任务
```java
/**
* 查询个人待执行的任务,注意查询的是未完成的任务
*/
@Test
public void testFindPersonalTaskList() {
//1、获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2、获取taskService
TaskService taskService = processEngine.getTaskService();
//3、根据流程key 和 任务的负责人 查询任务
List<Task> taskList = taskService.createTaskQuery()
.processDefinitionKey("myEvection") //流程Key
.taskAssignee("张三") //要查询的负责人
.list();
//4、输出
for (Task task : taskList) {
System.out.println("流程实例id=" + task.getProcessInstanceId());
System.out.println("任务Id=" + task.getId());
System.out.println("任务负责人=" + task.getAssignee());
System.out.println("任务名称=" + task.getName());
System.out.println();
}
////循环输出如下
//流程实例id=12501
//任务Id=12505
//任务负责人=张三
//任务名称=创建出差申请
}
```
# 5. 完成个人任务
```java
/**
* 完成个人任务
* 根据流程图任务的执行顺序为:张三->李四->王五->赵六
*/
@Test
public void completTask() {
//获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取操作任务的服务 TaskService
TaskService taskService = processEngine.getTaskService();
//查询张三待完成的任务
Task task = taskService.createTaskQuery()
.processDefinitionKey("myEvection")
.taskAssignee("张三") //按照该顺序 张三->李四->王五->赵六 依次更换参数便可按照顺序完成每个人的任务
.singleResult();
System.out.println("流程实例id=" + task.getProcessInstanceId()); //12501
System.out.println("任务Id=" + task.getId()); //12505
System.out.println("任务负责人=" + task.getAssignee()); //张三
System.out.println("任务名称=" + task.getName()); //创建出差申请
//根据任务ID来完成对应的任务,张三->李四->王五->赵六
//完成任务后,再调用上面的查询任务查询就查不到了
taskService.complete(task.getId());
}
```
# 6. 其他操作
```java
/**
* 查询流程定义
* 包含流程定义,流程部署,流程定义版本
*/
@Test
public void queryProcessDefinition() {
//获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取Repositoryservice
RepositoryService repositoryService = processEngine.getRepositoryService();
//获取ProcessDifinitionQuery对象
ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery();
//查询当前所有的流程定义 ,返回流程定义信息的集合
List<ProcessDefinition> definitionList = definitionQuery.processDefinitionKey("myEvection")
.orderByProcessDefinitionVersion() //通过版本排序
.desc() //倒序
.list();
//输出信息
for (ProcessDefinition processDefinition : definitionList) {
System.out.println("流程定义ID:" + processDefinition.getId());
System.out.println("流程定义名称:" + processDefinition.getName());
System.out.println("流程定义Key:" + processDefinition.getKey());
System.out.println("流程定义版本:" + processDefinition.getVersion());
System.out.println("流程部署ID:" + processDefinition.getDeploymentId());
}
//流程定义ID:myEvection:1:4
//流程定义名称:出差申请流程
//流程定义Key:myEvection
//流程定义版本:1
//流程部署ID:1
}
/**
* 删除流程部署信息,会根据部署表act_re_deployment中的部署ID_来删除下面三张表有关该记录的数据
* `act_ge_bytearray`
* `act_re_deployment`
* `act_re_procdef`
* 1) 使用repositoryService删除流程定义,历史表信息不会被删除
* 2)如果该流程定义下没有正在运行的流程,则可以用普通删除。如果该流程定义下存在已经运行的流程,
* 使用普通删除报错,可用级联删除方法将流程及相关记录全部删除。
* 先删除没有完成流程节点,最后就可以完全删除流程定义信息
* 项目开发中级联删除操作一般只开放给超级管理员使用.
*/
@Test
public void deleteDeployMent() {
//获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//通过引擎来获取 RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//通过部署id来删除流程部署信息
String deploymentId = "1";
//repositoryService.deleteDeployment(deploymentId); 非级联删除
//设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式,如果流程
repositoryService.deleteDeployment(deploymentId, true);
}
/**
* 下载资源文件
* 需要引入如下依赖
* <dependency>
* <groupId>commons-io</groupId>
* <artifactId>commons-io</artifactId>
* <version>2.6</version>
* </dependency>
* <p>
* 方案1: 使用Activiti提供的api,来下载资源文件,保存到文件目录
* 方案2: 自己写代码从数据库中下载文件,使用jdbc对blob 类型,clob类型数据读取出来,保存到文件目录
* 这里我们使用方案1,Activiti提供的api:RespositoryService
*/
@Test
public void getDeployment() throws IOException {
//1、得到引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2、获取api,RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//3、获取查询对象 ProcessDefinitionQuery查询流程定义信息
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("myEvection")
.singleResult();
//4、通过流程定义信息,获取部署ID
String deploymentId = processDefinition.getDeploymentId();
//5、获取png图片的流
//从流程定义表中,获取png图片的目录和名字
//提醒:bpmn-02/evection.bpmn,bpmn-02/image/evection.png,则在这里获取不到图片的名字
String pngName = processDefinition.getDiagramResourceName();
//InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, pngName);
InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, "bpmn-02/image/evection.png");
//6、获取bpmn的流
String bpmnName = processDefinition.getResourceName();
InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, bpmnName);
//7、构造OutputStream流
File pngFile = new File("e:/evectionflow01.png");
File bpmnFile = new File("e:/evectionflow01.bpmn");
FileOutputStream pngOutStream = new FileOutputStream(pngFile);
FileOutputStream bpmnOutStream = new FileOutputStream(bpmnFile);
//8、输入流,输出流的转换
IOUtils.copy(pngInput, pngOutStream);
IOUtils.copy(bpmnInput, bpmnOutStream);
//9、关闭流
pngOutStream.close();
bpmnOutStream.close();
pngInput.close();
bpmnInput.close();
}
/**
* 查看历史信息
*/
@Test
public void findHistoryInfo() {
//获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取HistoryService
HistoryService historyService = processEngine.getHistoryService();
//获取 actinst表的查询对象
HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
//查询 actinst表,条件:根据 InstanceId 查询
//instanceQuery.processInstanceId("2501");
//查询 actinst表,条件:根据 DefinitionId 查询
instanceQuery.processDefinitionId("myEvection:1:4");
//增加排序操作,orderByHistoricActivityInstanceStartTime 根据开始时间排序 asc 升序
instanceQuery.orderByHistoricActivityInstanceStartTime().asc();
//查询所有内容
List<HistoricActivityInstance> activityInstanceList = instanceQuery.list();
//输出
for (HistoricActivityInstance hi : activityInstanceList) {
System.out.println(hi.getActivityId());
System.out.println(hi.getActivityName());
System.out.println(hi.getProcessDefinitionId());
System.out.println(hi.getProcessInstanceId());
System.out.println("<==========================>");
}
//_2
//StartEvent
//myEvection:1:4
//12501
//<==========================>
//_3
//创建出差申请
//myEvection:1:4
//12501
//<==========================>
//_5
//部门经理审批
//myEvection:1:4
//12501
//<==========================>
}
```
下载的资源文件如下:
![](https://img.kancloud.cn/43/88/4388229459dcf5936edc0cd997ad905f_1298x386.png)
- Activiti流程引擎
- 工作流介绍
- Activiti是什么
- Activiti流程处理步骤
- Activiti环境搭建
- 搭建步骤
- 表结构介绍
- ActivitiAPI结构
- 认识流程符号
- 流程设计器的使用
- 流程处理步骤
- 乱码问题
- 流程实例
- 流程实例是什么
- 业务标识
- 查询流程实例
- 挂起/激活流程实例
- 个人任务
- 分配任务负责人
- 查询待办任务
- 办理权限
- 流程变量
- 流程变量类型
- 流程变量作用域
- 使用流程变量控制流程
- 组任务
- 设置任务候选人
- 组任务办理流程
- 网关
- 4种网关类型
- 排他网关
- 并行网关
- 包含网关
- 事件网关
- Spring整合Activiti
- SpringBoot整合Activiti
- Flowable流程引擎
- Flowable是什么
- Flowable与Activiti
- Flowable环境搭建
- FlowableAPI
- 流程引擎API与服务
- 流程处理步骤
- 流程部署
- 流程部署方式
- 流程定义版本
- 删除已部署的流程
- 下载资源
- 流程实例
- 什么是流程实例
- 业务标识
- 查询流程实例
- 挂起/激活流程实例
- 分配任务负责人
- 固定分配
- UEL表达式分配
- 监听器分配
- 办理权限
- 流程变量
- 流程变量类型
- 流程变量作用域
- 流程变量控制流程
- 组任务
- 设置任务候选人
- 组任务办理流程
- 网关
- 排他网关
- 并行网关
- 包含网关
- 事件网关
- 历史查询
- 查询历史
- Spring整合Flowable
- 配置文件整合
- 配置类整合
- SpringBoot整合Flowable