1、流程定义
流程定义是线下按照bpmn2.0标准去描述 业务流程,通常使用activiti-explorer(web控制台)或activiti-eclipse-designer插件对业务流程进行建模,这两种方式都遵循bpmn2.0标准。
1.1 .bpmn文件和.png图片文件
在IDEA开发工具中,使用activiti-desinger设计业务流程,会生成.bpmn文件,根据该文件可以生成.png图片文件,具体方法可以参考上一篇文章。
.bpmn文件的实质是.xml文件,它的具体内容结构如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/testm1564730183518" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1564730183518" name="" targetNamespace="http://www.activiti.org/testm1564730183518" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="myholiday" isClosed="false" isExecutable="true" processType="None">
<startEvent id="_2" name="StartEvent"/>
<userTask activiti:assignee="zhangsan" activiti:exclusive="true" id="_3" name="填写请假单"/>
<sequenceFlow id="_4" sourceRef="_2" targetRef="_3"/>
<userTask activiti:assignee="lisi" activiti:exclusive="true" id="_5" name="部门经理审批"/>
<userTask activiti:assignee="wangwu" activiti:exclusive="true" id="_6" name="总经理审批"/>
<userTask activiti:assignee="zhaoliu" activiti:exclusive="true" id="_7" name="财务审批"/>
<endEvent id="_8" name="EndEvent"/>
<sequenceFlow id="_9" sourceRef="_3" targetRef="_5"/>
<sequenceFlow id="_10" sourceRef="_5" targetRef="_6"/>
<sequenceFlow id="_11" sourceRef="_6" targetRef="_7"/>
<sequenceFlow id="_12" sourceRef="_7" targetRef="_8"/>
</process>
<bpmndi:BPMNDiagram documentation="background=#FFFFFF;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
<bpmndi:BPMNPlane bpmnElement="myholiday">
<bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
<dc:Bounds height="32.0" width="32.0" x="265.0" y="35.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
<dc:Bounds height="55.0" width="85.0" x="240.0" y="110.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
<dc:Bounds height="55.0" width="85.0" x="240.0" y="195.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
<dc:Bounds height="55.0" width="85.0" x="240.0" y="285.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_7" id="Shape-_7">
<dc:Bounds height="55.0" width="85.0" x="240.0" y="370.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_8" id="Shape-_8">
<dc:Bounds height="32.0" width="32.0" x="265.0" y="465.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_7" targetElement="_8">
<di:waypoint x="281.0" y="425.0"/>
<di:waypoint x="281.0" y="465.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_4" id="BPMNEdge__4" sourceElement="_2" targetElement="_3">
<di:waypoint x="281.0" y="67.0"/>
<di:waypoint x="281.0" y="110.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9" sourceElement="_3" targetElement="_5">
<di:waypoint x="282.5" y="165.0"/>
<di:waypoint x="282.5" y="195.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="_6" targetElement="_7">
<di:waypoint x="282.5" y="340.0"/>
<di:waypoint x="282.5" y="370.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="_5" targetElement="_6">
<di:waypoint x="282.5" y="250.0"/>
<di:waypoint x="282.5" y="285.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
BPMN 2.0根节点是definitions节点。 这个元素中,可以定义多个流程定义(不过我们建议每个文件只包含一个流程定义, 可以简化开发过程中的维护难度)。 注意,definitions元素 最少也要包含xmlns 和 targetNamespace的声明。 targetNamespace可以是任意值,它用来对流程实例进行分类。
流程定义部分:定义了流程每个结点的描述及结点之间的流程流转。
流程布局定义:定义流程每个结点在流程图上的位置坐标等信息。
2、流程定义部署
2.1 什么是流程定义部署
将线下定义的流程部署到activiti数据库中,这就是流程定义部署,通过调用activiti的api将流程定义的bpmn和png两个文件一个一个添加部署到activiti中,也可以将两个文件打成zip包进行部署。
2.2 单个文件部署方式
单个文件部署方式,就是分别把.bpmn文件和.png文件部署到activiti数据库中。
package com.zdw.activiti;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import java.io.InputStream;
import java.util.zip.ZipInputStream;
/**
* 流程定义部署
* activiti表有哪些?
* act_re_deployment 部署信息
act_re_procdef 流程定义的一些信息
act_ge_bytearray 流程定义的bpmn文件及png文件
*/
public class ActivitiDeployment {
//流程定义部署 流程制作出来后要上传到服务器 zip文件更便于上传(bpmn和png单独上传也可以,下面也会演示)
public static void main(String[] args) {
//创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//得到RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment().addClasspathResource("bpmn/holiday.bpmn")
.addClasspathResource("bpmn/holiday.png").name("请假流程").deploy();
//4.输出部署的一些信息
System.out.println(deployment.getName());
System.out.println(deployment.getId());
}
}
2.3 压缩包部署方式
实际开发中,一般用的多的是这种方式,因为在项目中,一般是要通过把文件上传到服务器,然后进行部署的,进行压缩包部署就只需要上传一次就行了,比较简洁。不过activiti底层还是把这个压缩包解压成了.bpmn和.png文件存到了数据库中。
package com.zdw.activiti;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import java.io.InputStream;
import java.util.zip.ZipInputStream;
/**
* 流程定义部署
* activiti表有哪些?
* act_re_deployment 部署信息
act_re_procdef 流程定义的一些信息
act_ge_bytearray 流程定义的bpmn文件及png文件
*/
public class ActivitiDeployment {
//流程定义部署 流程制作出来后要上传到服务器 zip文件更便于上传(bpmn和png单独上传也可以,下面也会演示)
public static void main(String[] args) {
//创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//得到RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//转化出ZipInputStream流对象
InputStream inputStream = ActivitiDeployment.class.getClassLoader().getResourceAsStream("bpmn/holiday.zip");
//将 inputstream流转化为ZipInputStream流
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
//部署
Deployment deployment = repositoryService.createDeployment().addZipInputStream(zipInputStream).name("请假流程").deploy();
//4.输出部署的一些信息
System.out.println(deployment.getName());
System.out.println(deployment.getId());
}
}
2.4 操作数据表
流程定义部署后操作activiti数据表如下:
SELECT * FROM act_re_deployment #流程定义部署表,记录流程部署信息
SELECT * FROM act_re_procdef #流程定义表,记录流程定义信息
SELECT * FROM act_ge_bytearray #资源表,存的是.png和.bpmn文件信息,还有一些序列化参数信息
说明:
act_re_deployment和act_re_procdef一对多关系,一次部署在流程部署表生成一条记录,但一次部署可以部署多个流程定义,每个流程定义在流程定义表生成一条记录。每一个流程定义在act_ge_bytearray会存在两个资源记录,bpmn和png。
建议:一次部署一个流程,这样部署表和流程定义表是一对一有关系,方便读取流程部署及流程定义信息。
3、流程定义查询
3.1 查询部署的流程定义:
package com.zdw.activiti02;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import java.util.List;
/**
* 流程定义查询
*/
public class QueryProceccDefinitionTest {
public static void main(String[] args) {
//得到工作流引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//得到RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//得到流程定义查询对象 ProcessDefinitionQuery
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//根据流程定义的key查询所有的流程定义效果
List<ProcessDefinition> processDefinitionList = processDefinitionQuery.processDefinitionKey("myholiday").orderByProcessDefinitionKey().desc().list();
for (ProcessDefinition processDefinition : processDefinitionList) {
System.out.println("------------------------");
System.out.println("流程部署id:" + processDefinition.getDeploymentId());
System.out.println("流程定义id:" + processDefinition.getId());
System.out.println("流程定义名称:" + processDefinition.getName());
System.out.println("流程定义key:" + processDefinition.getKey());
System.out.println("流程定义版本:" + processDefinition.getVersion());
}
}
}
3.2 流程定义删除
删除已经部署成功的流程定义:
package com.zdw.activiti02;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import java.util.List;
/**
* 删除部署成功的流程定义
*/
public class DeleteProceccDefinitionTest {
public static void main(String[] args) {
//得到工作流引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//得到RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//得到流程定义查询对象 ProcessDefinitionQuery
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//查询流程定义
ProcessDefinition processDefinition = processDefinitionQuery.processDefinitionKey("myholiday").singleResult();
//删除流程定义,如果该流程定义已有流程实例启动则删除时出错
//repositoryService.deleteDeployment(processDefinition.getDeploymentId());
//设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式
repositoryService.deleteDeployment(processDefinition.getDeploymentId(),true);
}
}
说明:
1) 使用repositoryService删除流程定义
2) 如果该流程定义下没有正在运行的流程,则可以用普通删除。
3) 如果该流程定义下存在已经运行的流程,使用普通删除报错,可用级联删除方法将流程及相关记录全部删除。项目开发中使用级联删除的情况比较多,删除操作一般只开放给超级管理员使用。
4、流程定义资源查询
4.1 方式一
通过流程定义对象获取流程定义资源,获取bpmn和png。
package com.zdw.activiti02;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.apache.commons.io.IOUtils;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 得到流程定义的资源:.bpmn和.png文件
* 需求:把流程定义的.bpmn和.png文件下载到本地电脑的 D 盘下面
*/
public class GetProcessResource {
public static void main(String[] args) throws IOException {
//得到工作流引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//得到RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//获取流程定义对象查询对象
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//查询流程定义对象
ProcessDefinition processDefinition = processDefinitionQuery.processDefinitionKey("myholiday").singleResult();
//得到.bpmn和.png文件的名称
String bpmn_name = processDefinition.getResourceName();
String png_name = processDefinition.getDiagramResourceName();
//得到.bpmn和.png文件的输入流对象
InputStream bpmn_inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), bpmn_name);
InputStream png_inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),png_name);
//创建输出流对象
FileOutputStream bpmn_outputStream = new FileOutputStream("d:/holiday.bpmn");
FileOutputStream png_outputStream = new FileOutputStream("d:/holiday.png");
//通过commons-io里面的IOUtils工具类把输入流内容复制到输出流中
IOUtils.copy(bpmn_inputStream,bpmn_outputStream);
IOUtils.copy(png_inputStream,png_outputStream);
//释放资源
bpmn_outputStream.close();
png_outputStream.close();
bpmn_inputStream.close();
png_inputStream.close();
}
}
4.2 方式2
通过查询流程部署信息获取流程定义资源
//获取流程定义图片资源
public static void main(String[] args) throws IOException {
//得到工作流引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//得到RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//流程部署ID
String deploymentId = "7501";
//读取资源名称
List<String> resourceNames = repositoryService.getDeploymentResourceNames(deploymentId);
//定义图片资源的名称
String png_name = null;
for (String resourceName : resourceNames) {
if(resourceName.indexOf(".png")>0){
png_name = resourceName;
}
}
//得到图片输入流
InputStream png_inputStream = repositoryService.getResourceAsStream(deploymentId,png_name);
//定义输出流对象
FileOutputStream png_outputStream = new FileOutputStream("d:/holiday.png");
//通过commons-io里面的IOUtils工具类把输入流内容复制到输出流中
IOUtils.copy(png_inputStream,png_outputStream);
//释放资源
png_outputStream.close();
png_inputStream.close();
}
说明:
1) deploymentId为流程部署ID
2) resource_name为act_ge_bytearray表中NAME_列的值
3) 使用repositoryService的getDeploymentResourceNames方法可以获取指定部署下得所有文件的名称
4) 使用repositoryService的getResourceAsStream方法传入部署ID和资源图片名称可以获取部署下指定名称文件的输入流
5) 最后的将输入流中的图片资源进行输出。
5、流程历史信息的查看
即使流程定义已经删除了,流程执行的历史信息通过前面的分析,依然保存在activiti的act_hi_*相关的表中。所以我们还是可以查询流程执行的历史信息,可以通过HistoryService来查看相关的历史记录。
package com.zdw.activiti02;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import java.util.List;
/**
* 查询流程实例的历史信息
*/
public class QueryHistoryProcessInfo {
public static void main(String[] args) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
HistoryService historyService = processEngine.getHistoryService();
//得到活动的流程实例的历史查询对象
HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
//根据流程实例ID查询
List<HistoricActivityInstance> list = historicActivityInstanceQuery.processInstanceId("10001").list();
for (HistoricActivityInstance historicActivityInstance : list) {
System.out.println(historicActivityInstance.getActivityId());
System.out.println(historicActivityInstance.getActivityName());
System.out.println(historicActivityInstance.getProcessDefinitionId());
System.out.println(historicActivityInstance.getProcessInstanceId());
System.out.println("==============================");
}
}
}