一、什么是流程定义
流程定义是线下按照 bpmn2.0 标准去描述 业务流程,通常使用 activiti-explorer(web 控制台)或 activiti-eclipse-designer 插件对业务流程进行建模,这两种方式都遵循 bpmn2.0 标准。本教程使用activiti-eclipse-designer 插件完成流程建模。使用 designer 设计器绘制流程,会生成两个文件:.bpmn和.png
<?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.activiti.org/processdef">
<process id="holiday" name="holiday" isExecutable="true">
<startEvent id="sid-4b7dde5e-76f4-4523-b85a-e9b6b9121742"/>
<userTask id="sid-a974627b-411f-4476-8291-152a97fb24dd" name="填写请假单" activiti:assignee="zhangsan"/>
<userTask id="sid-02f0a0dc-c66b-4cb1-bb7d-0c93c2b8f656" name="部门经理审批" activiti:assignee="lisi"/>
<userTask id="sid-244ec10f-b963-4250-8685-c2bbea5c8978" name="总经理审批" activiti:assignee="wangwu"/>
<endEvent id="sid-fb4ee57e-d6b7-4d35-bc62-7c0663754591"/>
<sequenceFlow id="sid-3afcfa32-99de-456d-9cd0-f4332e044ff3" sourceRef="sid-4b7dde5e-76f4-4523-b85a-e9b6b9121742" targetRef="sid-a974627b-411f-4476-8291-152a97fb24dd"/>
<sequenceFlow id="sid-ce0f52ac-55d5-47ef-ba00-e453dd50a562" sourceRef="sid-a974627b-411f-4476-8291-152a97fb24dd" targetRef="sid-02f0a0dc-c66b-4cb1-bb7d-0c93c2b8f656"/>
<sequenceFlow id="sid-f37e21e6-c18a-4b79-ac3b-61de52822f5e" sourceRef="sid-02f0a0dc-c66b-4cb1-bb7d-0c93c2b8f656" targetRef="sid-244ec10f-b963-4250-8685-c2bbea5c8978"/>
<sequenceFlow id="sid-a5dcdb25-d594-45d5-ad67-3dec14d87bae" sourceRef="sid-244ec10f-b963-4250-8685-c2bbea5c8978" targetRef="sid-fb4ee57e-d6b7-4d35-bc62-7c0663754591"/>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_holiday">
<bpmndi:BPMNPlane bpmnElement="holiday" id="BPMNPlane_holiday">
<bpmndi:BPMNShape id="shape-44920f81-b800-48b5-a7bf-34eb9ee72346" bpmnElement="sid-4b7dde5e-76f4-4523-b85a-e9b6b9121742">
<omgdc:Bounds x="50.0" y="-15.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-3bd97d4e-6b69-4028-8eb7-3027e4fe1ba5" bpmnElement="sid-a974627b-411f-4476-8291-152a97fb24dd">
<omgdc:Bounds x="150.0" y="-25.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-cbb6e54a-c071-4415-96e6-02c7c4fff75e" bpmnElement="sid-02f0a0dc-c66b-4cb1-bb7d-0c93c2b8f656">
<omgdc:Bounds x="340.0" y="-25.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-5e6deff4-ba08-4f93-bb97-0779255e8f47" bpmnElement="sid-244ec10f-b963-4250-8685-c2bbea5c8978">
<omgdc:Bounds x="515.0" y="-25.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-a8156f64-4f61-4719-9feb-d86fddcf1d3a" bpmnElement="sid-fb4ee57e-d6b7-4d35-bc62-7c0663754591">
<omgdc:Bounds x="695.0" y="0.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="edge-317cb2e8-fa8b-4905-8e7e-ea4c6eb8a7b7" bpmnElement="sid-3afcfa32-99de-456d-9cd0-f4332e044ff3">
<omgdi:waypoint x="80.0" y="-7.5"/>
<omgdi:waypoint x="150.0" y="-5.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-fa838e8a-31e5-4aea-9eaf-dbb496fedf95" bpmnElement="sid-ce0f52ac-55d5-47ef-ba00-e453dd50a562">
<omgdi:waypoint x="250.0" y="15.0"/>
<omgdi:waypoint x="340.0" y="15.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-d8f28788-da77-4baa-bd10-fc60d7c76ab8" bpmnElement="sid-f37e21e6-c18a-4b79-ac3b-61de52822f5e">
<omgdi:waypoint x="440.0" y="15.0"/>
<omgdi:waypoint x="515.0" y="15.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-47ff9047-c23d-4bf6-a06b-a81ade328f81" bpmnElement="sid-a5dcdb25-d594-45d5-ad67-3dec14d87bae">
<omgdi:waypoint x="615.0" y="15.0"/>
<omgdi:waypoint x="695.0" y="15.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
BPMN 2.0 根节点是 definitions 节点。 这个元素中,可以定义多个流程定义(不过我们建议每个文件只包含一个流程定义, 可以简化开发过程中的维护难度)。 注意,definitions 元素 最少也要包含 xmlns 和 targetNamespace 的声明。 targetNamespace 可以是任意值,它用来对流程实例进行分类。
流程定义部分:定义了流程每个结点的描述及结点之间的流程流转。
流程布局定义:定义流程每个结点在流程图上的位置坐标等信息。
二、流程定义部署
2.1什么是流程定义部署
将线下定义的流程部署到 activiti 数据库中,这就是流程定义部署,通过调用 activiti 的 api 将流程定义的 bpmn 和 png 两个文件一个一个添加部署到 activiti 中,也可以将两个文件打成 zip 包进行部署。
2.2流程定义部署的方式
单个文件部署:
@Test
public void deployProcess() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deploy = repositoryService.createDeployment().
addClasspathResource("bpmn/holiday.bpmn20.xml").
addClasspathResource("bpmn/holiday.bpmn20.png").
name("请假申请流程").deploy();
System.out.println("流程部署id:"+deploy.getId());
System.out.println("流程部署名称:"+deploy.getName());
System.out.println("流程部署key:"+deploy.getKey());
}
压缩包部署:
@Test
public void deployProcess2() {
//1.创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.转化出ZipInputStream流对象
InputStream is = ActivitiDeployment.class.getClassLoader().getResourceAsStream("diagram/holidayBPMN.zip");
//将 inputstream流转化为ZipInputStream流
ZipInputStream zipInputStream = new ZipInputStream(is);
//3.进行部署
Deployment deployment = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.name("请假申请单流程")
.deploy();
//4.输出部署的一些信息
System.out.println(deployment.getName());
System.out.println(deployment.getId());
}
三、流程定义查询
@Test
public void queryProcessDefinition(){
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.创建RepositoryService对象
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.得到ProcessDefinitionQuery对象,可以认为它就是一个查询器
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//4.设置条件,并查询出当前的所有流程定义 查询条件:流程定义的key=holiday
//orderByProcessDefinitionVersion() 设置排序方式,根据流程定义的版本号进行排序
List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey("holiday")
.orderByProcessDefinitionVersion()
.desc().list();
//5.输出流程定义信息
for(ProcessDefinition processDefinition :list){
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());
}
}
四、流程定义删除
@Test
/**
* 删除已经部署的流程定义
*
* 背后影响的表:
act_ge_bytearray
act_re_deployment
act_re_procdef
*/
public void deleteProcessDefinition(){
/*
* * 注意事项:
* 1.当我们正在执行的这一套流程没有完全审批结束的时候,此时如果要删除流程定义信息就会失败
* 2.如果公司层面要强制删除,可以使用repositoryService.deleteDeployment("1",true);
* //参数true代表级联删除,此时就会先删除没有完成的流程结点,最后就可以删除流程定义信息 false的值代表不级联
* */
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.创建RepositoryService对象
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.执行删除流程定义 参数代表流程部署的id
repositoryService.deleteDeployment("1");
}
说明:
1) 使用 repositoryService 删除流程定义
2) 如果该流程定义下没有正在运行的流程,则可以用普通删除。
3) 如果该流程定义下存在已经运行的流程,使用普通删除报错,可用级联删除方法将流程及相关
记录全部删除。项目开发中使用级联删除的情况比较多,删除操作一般只开放给超级管理员使用。
五、流程定义资源查询
/**
*
* 查询流程定义的资源
*
*
* 需求:
* 1.从Activiti的act_ge_bytearray表中读取两个资源文件
* 2.将两个资源文件保存到路径: G:\Activiti7开发计划\Activiti7-day03\资料
*
* 技术方案:
* 1.第一种方式使用actviti的api来实现
* 2.第二种方式:其实就是原理层面,可以使用jdbc的对blob类型,clob类型数据的读取,并保存
* IO流转换,最好commons-io.jar包可以轻松解决IO操作
*
* 真实应用场景:用户想查看这个请假流程具体有哪些步骤要走?
*
*
*/
@Test
public void queryResources() throws Exception{
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RepositoryService对象
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.得到查询器:ProcessDefinitionQuery对象
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//4.设置查询条件
processDefinitionQuery.processDefinitionKey("holiday2");//参数是流程定义的key
//5.执行查询操作,查询出想要的流程定义
ProcessDefinition processDefinition = processDefinitionQuery.singleResult();
//6.通过流程定义信息,得到部署ID
String deploymentId = processDefinition.getDeploymentId();
//7.通过repositoryService的方法,实现读取图片信息及bpmn文件信息(输入流)
//getResourceAsStream()方法的参数说明:第一个参数部署id,第二个参数代表资源名称
//processDefinition.getDiagramResourceName() 代表获取png图片资源的名称
//processDefinition.getResourceName()代表获取bpmn文件的名称
InputStream pngIs = repositoryService
.getResourceAsStream(deploymentId,processDefinition.getDiagramResourceName());
InputStream bpmnIs = repositoryService
.getResourceAsStream(deploymentId,processDefinition.getResourceName());
//8.构建出OutputStream流
OutputStream pngOs =
new FileOutputStream("D:\\StudyProject\\"+processDefinition.getDiagramResourceName());
OutputStream bpmnOs =
new FileOutputStream("D:\\StudyProject\\"+processDefinition.getResourceName());
//9.输入流,输出流的转换 commons-io-xx.jar中的方法
IOUtils.copy(pngIs,pngOs);
IOUtils.copy(bpmnIs,bpmnOs);
//10.关闭流
pngOs.close();
bpmnOs.close();
pngIs.close();
bpmnIs.close();
}
六、 流程历史信息的查看
即使流程定义已经删除了,流程执行的历史信息通过前面的分析,依然保存在 activiti 的 act_hi_*相关的表中。所以我们还是可以查询流程执行的历史信息,可以通过 HistoryService 来查看相关的历史记录。
/**
* 需求:
* * 历史数据的查看
*/
@Test
public void queryHistrory(){
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到HistoryService
HistoryService historyService = processEngine.getHistoryService();
//3.得到HistoricActivitiInstanceQuery对象
HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
historicActivityInstanceQuery.processInstanceId("2501");//设置流程实例的id
//4.执行查询
List<HistoricActivityInstance> list = historicActivityInstanceQuery
.orderByHistoricActivityInstanceStartTime().asc().list();//排序StartTime
//5.遍历查询结果
for (HistoricActivityInstance instance :list){
System.out.println(instance.getActivityId());
System.out.println(instance.getActivityName());
System.out.println(instance.getProcessDefinitionId());
System.out.println(instance.getProcessInstanceId());
System.out.println("=============================");
}
}
七、关于流程定义的全部代码
package com.devtao;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.zip.ZipInputStream;
public class ActivitiDefinitionTest {
/**
* 测试25张表的生成,初始化数据库方式1
*/
@Test
public void testGenTable(){
//1.创建ProcessEngineConfiguration对象 第一个参数:配置文件名称 第二个参数是processEngineConfiguration的bean的id
ProcessEngineConfiguration configuration = ProcessEngineConfiguration
.createProcessEngineConfigurationFromResource("activiti.cfg.xml","processEngineConfiguration");
//2.创建ProcesEngine对象
ProcessEngine processEngine = configuration.buildProcessEngine();
//3.输出processEngine对象
System.out.println(processEngine);
}
/**
* 测试25张表的生成,初始化数据库方式2
*/
@Test
public void testGenTable2(){
//条件:1.activiti配置文件名称:activiti.cfg.xml 2.bean的id="processEngineConfiguration"
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
}
/**
* 部署流程定义 方式1,单个文件
*/
@Test
public void deployProcess() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deploy = repositoryService.createDeployment().
addClasspathResource("bpmn/holiday.bpmn20.xml").
addClasspathResource("bpmn/holiday.png").
name("请假申请流程").deploy();
System.out.println("流程部署id:"+deploy.getId());
System.out.println("流程部署名称:"+deploy.getName());
System.out.println("流程部署key:"+deploy.getKey());
}
/**
* 部署流程定义 方式2 压缩包
*/
@Test
public void deployProcess2() {
//1.创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.转化出ZipInputStream流对象
InputStream is = ActivitiDeployment.class.getClassLoader().getResourceAsStream("diagram/holidayBPMN.zip");
//将 inputstream流转化为ZipInputStream流
ZipInputStream zipInputStream = new ZipInputStream(is);
//3.进行部署
Deployment deployment = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.name("请假申请单流程")
.deploy();
//4.输出部署的一些信息
System.out.println(deployment.getName());
System.out.println(deployment.getId());
}
/**
* 查询流程定义
*/
@Test
public void queryProcessDefinition(){
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.创建RepositoryService对象
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.得到ProcessDefinitionQuery对象,可以认为它就是一个查询器
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//4.设置条件,并查询出当前的所有流程定义 查询条件:流程定义的key=holiday
//orderByProcessDefinitionVersion() 设置排序方式,根据流程定义的版本号进行排序
List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey("holiday")
.orderByProcessDefinitionVersion()
.desc().list();
//5.输出流程定义信息
for(ProcessDefinition processDefinition :list){
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());
}
}
@Test
/**
* 删除已经部署的流程定义
*
* 背后影响的表:
act_ge_bytearray
act_re_deployment
act_re_procdef
*/
public void deleteProcessDefinition(){
/*
* * 注意事项:
* 1.当我们正在执行的这一套流程没有完全审批结束的时候,此时如果要删除流程定义信息就会失败
* 2.如果公司层面要强制删除,可以使用repositoryService.deleteDeployment("1",true);
* //参数true代表级联删除,此时就会先删除没有完成的流程结点,最后就可以删除流程定义信息 false的值代表不级联
* */
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.创建RepositoryService对象
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.执行删除流程定义 参数代表流程部署的id
repositoryService.deleteDeployment("15001");
}
/**
*
* 查询流程定义的资源
*
*
* 需求:
* 1.从Activiti的act_ge_bytearray表中读取两个资源文件
* 2.将两个资源文件保存到路径: G:\Activiti7开发计划\Activiti7-day03\资料
*
* 技术方案:
* 1.第一种方式使用actviti的api来实现
* 2.第二种方式:其实就是原理层面,可以使用jdbc的对blob类型,clob类型数据的读取,并保存
* IO流转换,最好commons-io.jar包可以轻松解决IO操作
*
* 真实应用场景:用户想查看这个请假流程具体有哪些步骤要走?
*
*
*/
@Test
public void queryResources() throws Exception{
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RepositoryService对象
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.得到查询器:ProcessDefinitionQuery对象
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//4.设置查询条件
processDefinitionQuery.processDefinitionKey("holiday2");//参数是流程定义的key
//5.执行查询操作,查询出想要的流程定义
ProcessDefinition processDefinition = processDefinitionQuery.singleResult();
//6.通过流程定义信息,得到部署ID
String deploymentId = processDefinition.getDeploymentId();
//7.通过repositoryService的方法,实现读取图片信息及bpmn文件信息(输入流)
//getResourceAsStream()方法的参数说明:第一个参数部署id,第二个参数代表资源名称
//processDefinition.getDiagramResourceName() 代表获取png图片资源的名称
//processDefinition.getResourceName()代表获取bpmn文件的名称
InputStream pngIs = repositoryService
.getResourceAsStream(deploymentId,processDefinition.getDiagramResourceName());
InputStream bpmnIs = repositoryService
.getResourceAsStream(deploymentId,processDefinition.getResourceName());
//8.构建出OutputStream流
OutputStream pngOs =
new FileOutputStream("D:\\StudyProject\\"+processDefinition.getDiagramResourceName());
OutputStream bpmnOs =
new FileOutputStream("D:\\StudyProject\\"+processDefinition.getResourceName());
//9.输入流,输出流的转换 commons-io-xx.jar中的方法
IOUtils.copy(pngIs,pngOs);
IOUtils.copy(bpmnIs,bpmnOs);
//10.关闭流
pngOs.close();
bpmnOs.close();
pngIs.close();
bpmnIs.close();
}
/**
* 需求:
* * 历史数据的查看
*/
@Test
public void queryHistrory(){
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到HistoryService
HistoryService historyService = processEngine.getHistoryService();
//3.得到HistoricActivitiInstanceQuery对象
HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
historicActivityInstanceQuery.processInstanceId("2501");//设置流程实例的id
//4.执行查询
List<HistoricActivityInstance> list = historicActivityInstanceQuery
.orderByHistoricActivityInstanceStartTime().asc().list();//排序StartTime
//5.遍历查询结果
for (HistoricActivityInstance instance :list){
System.out.println(instance.getActivityId());
System.out.println(instance.getActivityName());
System.out.println(instance.getProcessDefinitionId());
System.out.println(instance.getProcessInstanceId());
System.out.println("=============================");
}
}
}