管理流程定义
设计流程定义文档
bpmn文件
设置方式可以直接使用插件图形化界面进行设置
为某任务节点指定任务执行者
保存后的BPMN文件可以使用XML编辑器打开
BPMN 2.0根节点是definitions节点。 这个元素中,可以定义多个流程定义(不过我们建议每个文件只包含一个流程定义, 可以简化开发过程中的维护难度)。 一个空的流程定义看起来像下面这样。注意,definitions元素 最少也要包含xmlns 和 targetNamespace的声明。 targetNamespace可以是任意值,它用来对流程实例进行分类。
<?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://activiti.liuxun/test">
<!-- 流程定义部分 -->
<process id="myProcess" name="My First process" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="员工申请请假" activiti:assignee="员工"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<userTask id="usertask2" name="部门经理审批" activiti:assignee="经理"></userTask>
<sequenceFlow id="flow2" name="提交申请" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
<userTask id="usertask3" name="总经理审批" activiti:assignee="总经理"></userTask>
<sequenceFlow id="flow3" name="经理审批" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
</process>
<!-- BPMN绘图规范定义部分(用来描述节点图标的大小和坐标) -->
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="40.0" y="138.0"></omgdc:Bounds>
......
</bpmndi:BPMNPlane>
......
</bpmndi:BPMNDiagram>
</definitions>
说明:流程定义文档由两部分组成
(1) bpmn文件
流程规则文件。在部署后,每次系统启动时都会被解析,把内容封装成流程定义放入项目缓存中。Activiti框架结合XML文件自动管理流程。
(2) 展示流程图的图片
在系统里需要展示流程进展的图片
部署流程定义
部署流程定义也可以认为是增加流程定义/**
* 1.部署流程定义
*/
@Test
public void deploy() {
// 创建流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建部署环境配置对象
DeploymentBuilder deploymentBuilder = processEngine.getRepositoryService().createDeployment();
// 部署流程
// 方式一:读取单个的流程定义文件
Deployment deployment = deploymentBuilder
.name("测试") //设置部署流程的名称
.addClasspathResource("activiti/lx/processdefination/MyProcess.bpmn") //设置流程文件
.addClasspathResource("activiti/lx/processdefination/MyProcess.png") //设置流程文件
.deploy(); // 部署
System.out.println("部署ID: "+deployment.getId());
}
说明
(1) 通过getDefaultProcessEngine() 首先获得默认的流程引擎,在创建时会自动加载classpath下的activiti.cfg.xml或activiti-context.xml
(2) 通过流程引擎获取一个RepositoryService对象(仓库服务对象)
(3) 由仓库的服务对象可以获取一个部署环境的配置对象,用来封装部署环境的相关配置。
(3) 由仓库的服务对象可以获取一个部署环境的配置对象,用来封装部署环境的相关配置。
(4) 部署环境的配置对象(deploymentBuilder) 支持链式编程,在部署配置对象中设置显示名,上传规则文件相对的classpath的地址
(5) 部署,也是往数据库中存储流程定义的过程。
(5) 部署,也是往数据库中存储流程定义的过程。
(6) 部署流程定义在数据库中主要操作三张表
a) ACT_RE_DEPLOYMENT 存放流程定义的显示名和部署时间,每部署一次增加一条记录。
b) ACT_RE_PROCDEF 存放流程定义的属性信息,部署每个新的流程定义都会在这张表中插入一条记录。
c) ACT_RE_BYTEARRAY
存放流程定义相关的部署信息。即流程定义文档的存放地。每部署一次就会增加两条记录,一条是关于bpmn规则文件的,另一条是图片的(如果部署时只指定了bpmn文件,activiti会在部署时解析bpmn文件内容自动生成流程图)。两个文件不是很大,都是以二进制的形式存储在数据库中。
此种方式只适合部署单个的流程,可以以Zip格式进行部署(将所有的流程规则文件与图片压缩成ZIP格式文件)
/**
* 1.部署流程定义(部署zip格式文件)
*/
@Test
public void deploy2() {
// 创建流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建部署环境配置对象
DeploymentBuilder deploymentBuilder = processEngine.getRepositoryService().createDeployment();
// 部署流程
ZipInputStream zipInputStream = new ZipInputStream(this.getClass().getClassLoader().getResourceAsStream("activiti/lx/processdefination/process.zip"));
// 方式二:读取zip压缩文件
Deployment deployment = deploymentBuilder
.name("测试zip部署") //设置部署流程的名称
.addZipInputStream(zipInputStream )
.deploy();
System.out.println("部署ID: "+deployment.getId());
}
查看流程定义
/**
* 2. 查看流程规则的信息(流程定义)
*/
@Test
public void view() {
// 创建流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取流程定义信息
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery();
// 添加过滤条件
// definitionQuery.processDefinitionName("");
definitionQuery.processDefinitionKey("myProcess");
// 添加分页条件
// definitionQuery.listPage(0, 10);
// 添加排序条件
// definitionQuery.orderByProcessDefinitionId();
List<ProcessDefinition> list = definitionQuery.list();
// 迭代效果查看流程定义
for (ProcessDefinition pd : list) {
System.out.print("id= "+pd.getId()+", ");
System.out.print("name= "+pd.getName()+", ");
System.out.print("key= "+pd.getKey()+", ");
System.out.print("version= "+pd.getVersion()+", ");
System.out.println("deploymentId= "+pd.getDeploymentId()+", ");
}
}
结果:
再部署一次的运行结果为:
说明:
(1) 首先获取流程引擎对象
(2) 然后通过流程引擎对象获取RepositoryService
(3)通过RepositoryService实例可以获取查询对象实例
(4) 根据查询实例可以设置过滤参数,设置查询以及排序条件,获得符合条件的流程定义列表。
(5)由运行结果可以看出:
a) Key和Name的值为:bpmn文件process节点的id和name的属性值
c) 带有特定key的流程定义第一次部署时,version为1。之后每次部署都会在当前最高版本号上加1
d) Id的值的生成规则为:{processDefinitionKey}:{processDefinitionVersion}:{generated-id}, 这里的generated-id是一个自动生成的唯一的数字
e) 重复部署一次,deploymentId的值以一定的形式变化
f) 流程定义(ProcessDefinition)在数据库中没有相应的表对应,只是从act_ge_bytearray表中取出相应的bpmn和png图片,并进行解析。
<process id="myProcess" name="My First process" isExecutable="true">
b) key属性被用来区别不同的流程定义。
c) 带有特定key的流程定义第一次部署时,version为1。之后每次部署都会在当前最高版本号上加1
d) Id的值的生成规则为:{processDefinitionKey}:{processDefinitionVersion}:{generated-id}, 这里的generated-id是一个自动生成的唯一的数字
e) 重复部署一次,deploymentId的值以一定的形式变化
f) 流程定义(ProcessDefinition)在数据库中没有相应的表对应,只是从act_ge_bytearray表中取出相应的bpmn和png图片,并进行解析。
删除流程定义
删除部署到activiti中的流程定义
/**
*3.删除流程定义
*/
@Test
public void delDeploy() {
// 创建流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 设置要删除的部署的ID
String deploymentId = "101";
// 删除指定的部署信息,如果有关联信息则报错(例如启动了流程定义相关的流程实例)
// processEngine.getRepositoryService().deleteDeployment(deploymentId );
// 删除指定的部署信息,如果有关联信息则级联删除
// 第二个参数cascade,代表是否级联删除
processEngine.getRepositoryService().deleteDeployment(deploymentId, true);
}
说明:
(1) 因为删除的是流程定义,而流程定义的部署是属于仓库服务的,所以应该首先获取RespositoryService
(2) 如果该流程定义在没有正在运行的情况下,可以使用普通的删除方式。如果有关联的信息,可以使用第二种级联强制删除(删除所有关联的信息)。由于级联删除涉及的数据比较多,一般只开放给超级管理员使用。
获取流程定义文档的资源
查询流程定义文档。主要查看的是图片,用于显示流程使用。/**
* 4.获取流程定义中的资源文件(查看流程图片)
*
* @throws IOException
*/
@Test
public void getResource() throws IOException {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
String deploymentId = "1";
// 获取指定ID流程定义下的所有资源文件的名称列表
List<String> names = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId);
String resourceName = null;
// 遍历资源文件名称列表
for (String string : names) {
// 获取'.png'结尾名称为流程图片名称
if (string.endsWith(".png")) {
resourceName = string;
}
}
// 如果流程图片存在
if (resourceName != null) {
InputStream in = processEngine.getRepositoryService().getResourceAsStream(deploymentId, resourceName);
// 指定拷贝目录
File file = new File("/Users/liuxun/Downloads/" + resourceName);
// 原始方式
// OutputStream out = new FileOutputStream(file);
// byte[] b = new byte[1024];
// int len = 0;
// while((len=in.read(b))!=-1) {
// out.write(b, 0, len);
// }
// out.close();
// 使用FileUtils文件操作工具类,将流程图片拷贝到指定目录下
FileUtils.copyInputStreamToFile(in, file);
}
}
查看ACT_GE_BYTERRAY
运行结果:
1)deploymentId为流程部署ID
2)resourceName为act_ge_bytearray表中NAME_列的值
3)使用repositoryService的getDeploymentResourceNames方法可以获取指定部署下得所有文件的名称
4)使用repositoryService的getResourceAsStream方法传入部署ID和文件名称可以获取部署下指定名称文件的输入流
5)最后的有关IO流的操作,使用FileUtils工具的copyInputStreamToFile方法完成流程流程到文件的拷贝
2)resourceName为act_ge_bytearray表中NAME_列的值
3)使用repositoryService的getDeploymentResourceNames方法可以获取指定部署下得所有文件的名称
4)使用repositoryService的getResourceAsStream方法传入部署ID和文件名称可以获取部署下指定名称文件的输入流
5)最后的有关IO流的操作,使用FileUtils工具的copyInputStreamToFile方法完成流程流程到文件的拷贝
查询最新版本的流程定义
/**
* 获取最新版本的流程定义
*/
@Test
public void getLatestVersion() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery();
query.orderByProcessDefinitionId().asc();
List<ProcessDefinition> list = query.list();
HashMap<String, ProcessDefinition> map = new HashMap<String, ProcessDefinition>();
for (ProcessDefinition pd : list) {
map.put(pd.getKey(), pd);
}
ArrayList<ProcessDefinition> lastList = new ArrayList<>(map.values());
for (ProcessDefinition processDefinition : lastList) {
System.out.println(processDefinition.getName()+" "+processDefinition.getVersion());
}
}
流程实例管理
启动流程实例
在完成流程定义部署后,就可以启动流程实例了
// 启动流程实例
@Test
public void startProcess() {
// 创建流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取流程引擎服务对象
RuntimeService runtimeService = processEngine.getRuntimeService();
// 启动流程,返回流程实例对象
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");
// 显示相关信息
System.out.print("pid= "+processInstance.getId()); //流程实例ID
System.out.println("activityId= "+processInstance.getActivityId());
}
说明:
(1) 结果为:
(2) 操作数据库的ACT_RU_EXECUTION表,如果是用户任务节点,同时也会在ACT_RU_TASK中添加一条记录
注意:按照key启动流程 会默认根据最新版本的流程定义规则创建流程实例
ACT_RU_EXECUTION
ACT_RU_TASK
查询任务
在activiti任务中,主要分为两大类:
1.确切指定了办理者的任务,这个任务将成为指定者的私有任务
2.无法指定具体的某一个人来办理的任务,可以把任务分配给几个人或者一到多个小组,让这个范围内的用户可以选择性(如有空余时间时)来办理这类任务。
1.确切指定了办理者的任务,这个任务将成为指定者的私有任务
2.无法指定具体的某一个人来办理的任务,可以把任务分配给几个人或者一到多个小组,让这个范围内的用户可以选择性(如有空余时间时)来办理这类任务。
查询指定用户的代办任务
对指定用户的未完成的个人任务执行查询(由某一个人负责办理 在任务列表中通过assignee字段指定)
/**
* 2.1 查看指定用户的代办任务
*/
@Test
public void findUnfinishedTask() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取个人的代办信息
TaskService taskService = processEngine.getTaskService();
// 创建流程任务查询对象
TaskQuery taskQuery = taskService.createTaskQuery();
String assignee = "员工";
// 添加过滤条件
taskQuery.taskAssignee(assignee);
// 添加分页条件
taskQuery.listPage(0, 10);
// 添加过滤条件
taskQuery.orderByTaskCreateTime().desc();
// 执行查询
List<Task> list = taskQuery.list();
System.out.println("===============【"+assignee+"】的个人任务列表===============");
// 迭代结果,查看个人任务
for (Task task : list) {
System.out.print("id="+task.getId()+","); //获取任务的ID
System.out.print("name="+task.getName()+",");//获取任务的名称
System.out.print("assign="+task.getAssignee()+",");//查询任务的代办人
System.out.print("createTime="+task.getCreateTime()+",");//查询任务的创建时间
System.out.println("executionId="+task.getExecutionId());//获取流程执行对象的ID
}
}
运行结果:
说明:
1)因为是任务查询,所以从processEngine中应该得到TaskService
2)使用TaskService获取到任务查询对象TaskQuery
3)为查询对象添加查询过滤条件,使用taskAssignee指定任务的候选者(即查询指定用户的代办任务),添加分页排序等过滤条件
4)调用list方法执行查询,返回办理者为指定用户的任务列表
5)任务ID、名称、办理人、创建时间可以从act_ru_task表中查到。
6)Execution与ProcessInstance的关系与分支有关。在这种单线流程中,ProcessInstance相当于Execution
7)如果assignee属性为部门经理,结果为空。因为现在流程只到了”填写请假申请”阶段,后面的任务还没有执行,即在数据库中没有部门经理可以办理的任务,所以查询不到。
8)一个Task节点和Execution节点是1对1的情况,在task对象中使用Execution_来标示他们之间的关系
9)任务ID在数据库表act_ru_task中对应“ID_”列
查询指定用户的可接任务(公共任务)
对指定用户的可接收的公共任务执行查询
/**
* 2.2 查看指定用户的可接任务(公共任务)
*/
@Test
public void findCanTakeTask() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取个人的代办信息
TaskService taskService = processEngine.getTaskService();
// 创建流程任务查询对象
TaskQuery taskQuery = taskService.createTaskQuery();
String candidateUser = "员工1";
// 添加过滤条件
taskQuery.taskCandidateUser(candidateUser );
// 添加分页条件
taskQuery.listPage(0, 10);
// 添加过滤条件
taskQuery.orderByTaskCreateTime().desc();
// 执行查询
List<Task> list