1.Activiti数据库表
1.1 数据表介绍
Activiti的数据表都以ACT_开头。
ACT_RE_: 'RE’表示repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
ACT_RU_:'RU’表示runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。
Activiti只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快
ACT_ID_: 'ID’表示identity。 这些表包含身份信息,比如用户,组等等。
ACT_HI_: 'HI’表示history。这些表包含历史数据,比如历史流程实例, 变量,任务等等。 ACT_GE_*: 通用数据, 用于不同场景下,如存放资源文件。
1.2 创建数据库表+获得核心引擎
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.ProcessEngines;
import org.junit.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class JunitTest {
/**
* 使用代码创建工作流需要的23张表
* 硬编码数据库连接
*/
@Test
public void createTableOne() {
ProcessEngineConfiguration processEngineConfiguration =
ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration()
.setJdbcDriver("com.mysql.jdbc.Driver")
.setJdbcUrl("jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf8")
.setJdbcUsername("root")
.setJdbcPassword("123456");
/*
* public static final
* StringDB_SCHEMA_UPDATE_FALSE = "false"; * 不能自动创建表,需要表存在 public static final
* StringDB_SCHEMA_UPDATE_CREATE_DROP * = "create-drop";先删除表再创建表 public static
* final String * DB_SCHEMA_UPDATE_TRUE ="true";如果表不存在,自动创建表
*/
processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
//工作流的核心对象,ProcessEngine对象
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
System.out.println("processEngine_1:" + processEngine);
}
/**
* 使用activiti-cfg.xml或activiti-contect.xml配置文件创建数据库
*/
@Test
public void createTablesTwo() {
String resource = "activiti.cfg.xml";// 配置文件名称
String beanName = "processEngineConfiguration";// 配置id值
ProcessEngineConfiguration configuration = ProcessEngineConfiguration
.createProcessEngineConfigurationFromResource(resource, beanName);
ProcessEngine processEngine = configuration.buildProcessEngine();
System.out.println("processEngine_2:" + processEngine);
}
/**
* 默认使用activiti-cfg.xml配置文件创建数据库
*/
@Test
public void createTablesThree() {
ProcessEngineConfiguration configuration = ProcessEngineConfiguration
.createProcessEngineConfigurationFromResourceDefault();
ProcessEngine processEngine = configuration.buildProcessEngine();
System.out.println("processEngine_3:" + processEngine);
}
/**
* 默认使用activiti-cfg.xml配置文件创建数据库
* <p>
* This will look for an "activiti.cfg.xml" file on the classpath and construct an engine based on the configuration in that file. The following snippet shows an example configuration.
* The following sections will give a detailed overview of the configuration properties.
*/
@Test
public void createTablesFour() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println("processEngine_4:" + processEngine);
}
@Test
public void updateTable(){
ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
configuration.setJdbcDriver("com.mysql.jdbc.Driver")
.setJdbcUrl("jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf8")
.setJdbcUsername("root")
.setJdbcPassword("123456")
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_CREATE_DROP);
//创建引擎
ProcessEngine processEngine = configuration.buildProcessEngine();
System.out.println(processEngine);
}
使用配置文件方式下配套的activiti.cfg.xml
,以下仅为部分与数据库有关的代码
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- 配置 ProcessEngineConfiguration -->
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 配置数据库连接 -->
<property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8"></property>
<property name="jdbcUsername" value="root"></property>
<property name="jdbcPassword" value="123456"></property>
<!-- 配置创建表策略 :没有表时,自动创建 -->
<property name="databaseSchemaUpdate" value="true"></property>
</bean>
</beans>
2.流程的管理
类似于我们的关系型数据库中DML,用来定义数据库表的结构。
2.1数据库表
主要涉及到以下的4张表:
-- 流程部署相关的表
SELECT * FROM act_ge_bytearray # 通用字节资源表
SELECT * FROM act_ge_property # 通用属性表,可以生成部署id
SELECT * FROM act_re_deployment #部署表
SELECT * FROM act_re_procdef # 流程定义表
2.2定义工作流
定义工作流就需要我们刚才下载的插件了,我们是使用图形的方式来定义工作流的…
在每个流程中,我们都可以指定对应的处理人是谁,交由谁处理.
2.3部署工作流
@Test
public void deploy() {
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.createDeployment()
.addClasspathResource("processes/TestProcess.bpmn")//加载bpmn文件
.addClasspathResource("processes/TestProcess.png")//加载图片文件
.name("请求单流程")//设置部署的名称
.category("办公类别")//设置部署的类别
.deploy();
}
我们还可以加载的是ZIP类型的资源数据,就是将bpmn和png图片打包成zip文件,不能是rar格式。
//部署流程定义,资源来自zip格式
@Test
public void deployProcessDefiByZip(){
//文件输入流读取资源文件
InputStream in=getClass().getClassLoader().getResourceAsStream("BuyBill.zip");
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("采购流程")
.addZipInputStream(new ZipInputStream(in))//封装到zip输入流并加载
.deploy();
System.out.println("部署名称:"+deploy.getName());
System.out.println("部署id:"+deploy.getId());
}
相对应的数据库表就会插入数据、涉及到的数据库表后面会详细说明。现在我们只要了解到,我们工作流引擎执行操作会有数据库表记录。
2.4查看流程定义
查询流程定义的方式很多。
//查看流程定义
@Test
public void queryProcessDefination(){
String processDefiKey="buyBill";//流程定义key
//获取流程定义列表
List<ProcessDefinition> list = processEngine.getRepositoryService().createProcessDefinitionQuery()
//查询 ,好比where
// .processDefinitionId(proDefiId) //流程定义id
// 流程定义id : buyBill:2:704 组成 : proDefikey(流程定义key)+version(版本)+自动生成id
.processDefinitionKey(processDefiKey)//流程定义key 由bpmn 的 process 的 id属性决定
// .processDefinitionName(name)//流程定义名称 由bpmn 的 process 的 name属性决定
// .processDefinitionVersion(version)//流程定义的版本
.latestVersion()//最新版本
//排序
.orderByProcessDefinitionVersion().desc()//按版本的降序排序
//结果
// .count()//统计结果
// .listPage(arg0, arg1)//分页查询
.list();
//遍历结果
if(list!=null&&list.size()>0){
for(ProcessDefinition temp:list){
System.out.print("流程定义的id: "+temp.getId());
System.out.print("流程定义的key: "+temp.getKey());
System.out.print("流程定义的版本: "+temp.getVersion());
System.out.print("流程定义部署的id: "+temp.getDeploymentId());
System.out.println("流程定义的名称: "+temp.getName());
}
}
}
3.流程实例与任务执行细讲
3.1数据库表
流程实例与任务执行的常用表有以下几个:
-- 流程实例与任务
SELECT * FROM act_ru_execution # 流程执行对象信息
SELECT * FROM act_ru_task # 正在运行的任务表
SELECT * FROM act_hi_procinst # 历史流程实例表
SELECT * FROM act_hi_taskinst # 历史流程任务表
流程实例与流程对象的区别:
(1)如果是单例流程,执行对象ID就是流程实例ID
(2)如果一个流程有分支和聚合,那么执行对象ID和流程实例ID就不相同
(3)一个流程中,流程实例只有1个,执行对象可以存在多个。
3.2.执行工作流
@Test
public void startProcess(){
//指定执行我们刚才部署的工作流程
String processDefiKey="leaveBill";
ProcessInstance pi = processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefiKey);//通过流程定义的key 来执行流程
System.out.println("流程执行对象的id:"+pi.getId());//Execution 对象
System.out.println("流程实例的id:"+pi.getProcessInstanceId());//ProcessInstance 对象
System.out.println("流程定义的id:"+pi.getProcessDefinitionId());//默认执行的是最新版本的流程定义
}
startProcessInstanceByKey():
key就是工作流就是我们定义时工作流程表的id
3.3设置发起人
在实际业务中设置发起人非常有必要,可以全局查看流程的运行状态。
通常的做法是,
获取当前登陆用户的userName并设置
。
identityService.setAuthenticatedUserId( user.getUsername());
然后再执行启动流程实例的操作:
//流程发起前设置发起人,记录在流程历史中
identityService.setAuthenticatedUserId(user.getUsername());
//开始流程
runtimeService.startProcessInstanceByKey("leavebill");
设置发起人后,数据存在
ACT_HI_PROCINST
表中。
我们调用API 获取到发起人:
historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance.getId()).singleResult().getStartUserId();//获取发起人
两种方法:
identityService.setAuthenticatedUserId();//调用官方的开放API;
Authentication.setAuthenticatedUserId();//直接调用底层实现;
3.4业务关联流程(重点)
工作流的核心就是要让流程与业务关联在一起。activiti本身关注的是流程的流转,而业务需要我们自己去创建。比如说请假流程,所谓的业务就是:请假单据的内容+单据在不同工作流节点中的数据和状态变化
。因此我们必须在启动流程实例的同时将流程与业务绑定,并且在每个工作流节点修改单据的状态和数据。
3.4.1流程关联业务
在流程启动的时候关联业务。
如下图,使用了流程定义的id+业务单据的id拼接的字符串作为activiti流程中的business_key,并在启动流程实例时传入了该值,从而实现了业务与流程的关联。这里面体现了两个开发时的经验和技巧:
- 绘制流程图并设置流程的id时,使用业务单据Bean的名称作为id。当启动流程实例时,我们需要根据流程定义的key来启动流程实例,那么,流程实例的key等于业务单据bean的简单类名。在代码中使用
leaveBill.getClass().getSimpleName()
即可以拿到流程实例的key。- 使用
流程定义的id+业务单据的id
拼接的字符串作为activiti流程中的business_key。如此一来,只要能拿到这个key,工作流数据和业务数据也就都可以获取到。
4.根据办理人查询当前任务的信息
刚才我们已经开始了工作流了,随后工作流应该去到了申请请假的流程,申请请假的处理人是Jack,我们可以查询出对应的信息:
//查询任务
@Test
public void queryTask(){
//任务的办理人
String assignee="Jack";
//取得任务服务
TaskService taskService = processEngine.getTaskService();
//根据办理人查询其任务列表
List<Task> list = = taskService.createTaskQuery()//创建一个任务查询对象
.taskQuery.taskAssignee(assignee)//指定办理人
.list();
//遍历任务列表
if(list!=null&&list.size()>0){
for(Task task:list){
System.out.println("任务的办理人:"+task.getAssignee());
System.out.println("任务的id:"+task.getId());
System.out.println("任务的名称:"+task.getName());
}
}
}
执行的是查询SELECT * FROM act_ru_task
表的操作:
3.4获取流程实例的状态
有的时候,我们需要判断它是在该流程,还是该流程已经结束了。
一般情况下,我们可以通过获取流程实例对象、判断是否为空来进行判断。
//获取流程实例的状态
@Test
public void getProcessInstanceState(){
String processInstanceId="605";
ProcessInstance pi = processEngine.getRuntimeService()
.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();//返回的数据要么是单行,要么是空 ,其他情况报错
//判断流程实例的状态
if(pi!=null){
System.out.println("该流程实例"+processInstanceId+"正在运行... "+"当前活动的任务:"+pi.getActivityId());
}else{
System.out.println("当前的流程实例"+processInstanceId+" 已经结束!");
}
}
3.5查看历史流程实例的信息
//查看历史执行流程实例信息
@Test
public void queryHistoryProcInst(){
List<HistoricProcessInstance> list = processEngine.getHistoryService()
.createHistoricProcessInstanceQuery()
.list();
if(list!=null&&list.size()>0){
for(HistoricProcessInstance temp:list){
System.out.println("历史流程实例id:"+temp.getId());
System.out.println("历史流程定义的id:"+temp.getProcessDefinitionId());
System.out.println("历史流程实例开始时间--结束时间:"+temp.getStartTime()+"-->"+temp.getEndTime());
}
}
}
3.6查看历史实例执行任务信息
根据对应的实例id就可以查询出执行到哪个任务了…
@Test
public void queryHistoryTask(){
String processInstanceId="605";
List<HistoricTaskInstance> list = processEngine.getHistoryService()
.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.list();
if(list!=null&&list.size()>0){
for(HistoricTaskInstance temp:list){
System.out.print("历史流程实例任务id:"+temp.getId());
System.out.print("历史流程定义的id:"+temp.getProcessDefinitionId());
System.out.print("历史流程实例任务名称:"+temp.getName());
System.out.println("历史流程实例任务处理人:"+temp.getAssignee());
}
}
}
3.7完成当前节点任务
根据任务的id,就可以把该任务执行了。
@Test
public void compileTask(){
String taskId="608";
Map<String, Object> variable = new HashMap<>();
//taskId:任务id
processEngine.getTaskService()
// .complete(taskId);//完成任务
.complete(taskId,variable);//完成任务并设置下一节点的流程变量
System.out.println("当前任务执行完毕");
}
}
完成当前任务时我们可以设置下一节点任务的流程变量,如:办理人、判断条件等等。
需要说明的是,流程变量的key必须是String类型。
key的值要对应之前绘制流程图时,下一节点中所设置的变量表达式的变量名,如: u s e r I d , {userId}, userId,{money > 1000 || money == 1000};value就是要设置的值。
4.流程变量细讲
4.1数据库表
-- 流程变量涉及到的数据库表:
act_ru_variable:正在执行的流程变量表
act_hi_varinst:流程变量历史表
流程变量在工作流中扮演着一个非常重要的角色。例如:请假流程中有请假天数、请假原因等一些参数都为流程变量的范围。流程变量的作用域范围是只对应一个流程实例。也就是说各个流程实例的流程变量是不相互影响的。流程实例结束完成以后流程变量还保存在数据库中(存放到流程变量的历史表中)。
4.1设置流程变量
我们有两种服务可以设置流程变量,
TaskService
【任务服务】和RuntimeService
【运行时服务】。
场景
- 在流程开始的时候设置流程变量
- 在完成某个任务的时候设置流程变量
- 使用TaskService设置服务
- 使用RuntimeService设置服务
作用
- 传递业务参数
- 动态指定代理人【我们快速入门的例子是固定在流程定义图上写上代理人的】
- 指定连接【决定流程往哪边走】
4.2流程变量支持类型
- 基本数据类型都是可以的。很简单不再过多讲解。
- 如果我们使用JavaBean来作为流程的变量,那么我们需要实现两点要求:
JavaBean必须要实现Serializable接口
。- 为了防止流程变量对象的属性发生变化,JavaBean需要设置序列化ID
private static final long serialVersionUID = 1L;
4.3setVariable和setVariableLocal的区别
4.4示例
public class AppayBillBean implements Serializable{
private Integer id;
private Integer cost;//金额
private String appayPerson;//申请人
private Date date;//申请日期
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getCost() {
return cost;
}
public void setCost(Integer cost) {
this.cost = cost;
}
public String getAppayPerson() {
return appayPerson;
}
public void setAppayPerson(String appayPerson) {
this.appayPerson = appayPerson;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
//模拟流程变量设置
@Test
public void getAndSetProcessVariable(){
//有两种服务可以设置流程变量
TaskService taskService = processEngine.getTaskService();
RuntimeService runtimeService = processEngine.getRuntimeService();
/**1.通过 runtimeService 来设置流程变量
* executionId: 执行对象
* variableName:变量名
* values:变量值
*/
runtimeService.setVariable(executionId, variableName, values);
runtimeService.setVariableLocal(executionId, variableName, values);
//设置本执行对象的变量 ,该变量的作用域只在当前的execution对象
runtimeService.setVariables(executionId, variables);
//可以设置多个变量 放在 Map<key,value> Map<String,Object>
/**2. 通过TaskService来设置流程变量
* taskId:任务id
*/
taskService.setVariable(taskId, variableName, values);
taskService.setVariableLocal(taskId, variableName, values);
//设置本执行对象的变量 ,该变量的作用域只在当前的execution对象
taskService.setVariables(taskId, variables); //设置的是Map<key,values>
/**3. 当流程开始执行的时候,设置变量参数
* processDefiKey: 流程定义的key
* variables: 设置多个变量 Map<key,values>
*/
processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefiKey, variables)
/**4. 当任务完成时候,可以设置流程变量
* taskId:任务id
* variables: 设置多个变量 Map<key,values>
*/
processEngine.getTaskService().complete(taskId, variables);
/** 5. 通过RuntimeService取变量值
* exxcutionId: 执行对象
*
*/
runtimeService.getVariable(executionId, variableName);//取变量
runtimeService.getVariableLocal(executionId, variableName);//取本执行对象的某个变量
runtimeService.getVariables(variablesName);//取当前执行对象的所有变量
/** 6. 通过TaskService取变量值
* TaskId: 执行对象
*
*/
taskService.getVariable(taskId, variableName);//取变量
taskService.getVariableLocal(taskId, variableName);//取本执行对象的某个变量
taskService.getVariables(taskId);//取当前执行对象的所有变量
}
//设置流程变量值
@Test
public void setVariable(){
String taskId="1804";//任务id
//采用TaskService来设置流程变量
//1. 第一次设置流程变量
TaskService taskService = processEngine.getTaskService();
taskService.setVariable(taskId, "cost", 1000);//设置单一的变量,作用域在整个流程实例
taskService.setVariable(taskId, "申请时间", new Date());
taskService.setVariableLocal(taskId, "申请人", "何某某");//该变量只有在本任务中是有效的
//2. 在不同的任务中设置变量
TaskService taskService = processEngine.getTaskService();
taskService.setVariable(taskId, "cost", 5000);//设置单一的变量,作用域在整个流程实例
taskService.setVariable(taskId, "申请时间", new Date());
taskService.setVariableLocal(taskId, "申请人", "李某某");//该变量只有在本任务中是有效的
/**
* 3. 变量支持的类型
* - 简单的类型 :String 、boolean、Integer、double、date
* - 自定义对象bean
*/
TaskService taskService = processEngine.getTaskService();
//传递的一个自定义bean对象
AppayBillBean appayBillBean=new AppayBillBean();
appayBillBean.setId(1);
appayBillBean.setCost(300);
appayBillBean.setDate(new Date());
appayBillBean.setAppayPerson("何某某");
taskService.setVariable(taskId, "appayBillBean", appayBillBean);
System.out.println("设置成功!");
}
//查询流程变量
@Test
public void getVariable(){
String taskId="1804";//任务id
TaskService taskService = processEngine.getTaskService();
Integer cost=(Integer) taskService.getVariable(taskId, "cost");//取变量
Date date=(Date) taskService.getVariable(taskId, "申请时间");//取本任务中的变量
Date date=(Date) taskService.getVariableLocal(taskId, "申请时间");//取本任务中的变量
String appayPerson=(String) taskService.getVariableLocal(taskId, "申请人");//取本任务中的变量
String appayPerson=(String) taskService.getVariable(taskId, "申请人");//取本任务中的变量
System.out.println("金额:"+cost);
System.out.println("申请时间:"+date);
System.out.println("申请人:"+appayPerson);
//读取实现序列化的对象变量数据
TaskService taskService = processEngine.getTaskService();
AppayBillBean appayBillBean=(AppayBillBean) taskService.getVariable(taskId, "appayBillBean");
System.out.println(appayBillBean.getCost());
System.out.println(appayBillBean.getAppayPerson());
}
5.连线
有了流程变量,我们可以在**【任务服务、运行时服务、流程开始、完成某个任务时设置流程变量】**,而我们的连线就是流程变量的实际应用。
5.1定义流程图
我们并不是所有的流程都是按一条的路径来走的,我们有的时候会根据条件来走不同的路。当然了,最终该流程是会一步步走完…
例子:重要的信息交由老板来处理,不重要的信息交由经理来处理
表达式的结果必须是布尔型
#{variable=='value'}
${variable==value}
6.排他网关
上面我们使用连线的时候用了两个条件 : 要么条件是“重要”,要么条件是“不重要”…如果有另一种情况呢???就是用户把条件输入错了,写成“不知道重不重要”,那么我们的流程怎么走???岂不是奔溃了???
因此,我们要有一条默认的路来走,就是当该变量不符合任何的条件时,有一条默认的出口让流程继续执行
注意:如果是在Eclipse中使用插件的BPMN流程图,如果使用了排他网关,那么在Idea下是解析不了的…
解决方案:我们只要重新定义BPMN流程图的排他网关就行了,idea中的Activiti插件是不用制定默认流程的,只要我们不设置条件,那就是默认的连接线
6.2示例
public class ExclusiveGetWay {
private ProcessEngine processEngine = ProcessEngines
.getDefaultProcessEngine();
// 部署流程定义,资源来在bpmn格式
@Test
public void deployProcessDefi() {
Deployment deploy = processEngine.getRepositoryService()
.createDeployment().name("排他网关流程")
.addClasspathResource("ExclusiveGateway.bpmn")
.deploy();
System.out.println("部署名称:" + deploy.getName());
System.out.println("部署id:" + deploy.getId());
}
// 执行流程,开始跑流程
@Test
public void startProcess() {
String processDefiKey = "bankBill";// bpmn 的 process id属性
ProcessInstance pi = processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefiKey);
System.out.println("流程执行对象的id:" + pi.getId());// Execution 对象
System.out.println("流程实例的id:" + pi.getProcessInstanceId());// ProcessInstance
// 对象
System.out.println("流程定义的id:" + pi.getProcessDefinitionId());// 默认执行的是最新版本的流程定义
}
// 查询正在运行任务
@Test
public void queryTask() {
// 取得任务服务
TaskService taskService = processEngine.getTaskService();
// 创建一个任务查询对象
TaskQuery taskQuery = taskService.createTaskQuery();
// 办理人的任务列表
List<Task> list = taskQuery.list();
// 遍历任务列表
if (list != null && list.size() > 0) {
for (Task task : list) {
System.out.println("任务的办理人:" + task.getAssignee());
System.out.println("任务的id:" + task.getId());
System.out.println("任务的名称:" + task.getName());
}
}
}
// 完成任务
@Test
public void compileTask() {
String taskId = "2404";
Map<String,Object> params=new HashMap<String, Object>();
params.put("visitor", 6);
// taskId:任务id
processEngine.getTaskService().complete(taskId, params);
// processEngine.getTaskService().complete(taskId);
System.out.println("当前任务执行完毕");
}
}
7.个人任务指定办理人的三种方式
7.1流程图中指定办理人
在绘制流程图中硬性指定某个节点的办理人,但这种方法在实际开发中不灵活,基本不使用。
7.2使用流程变量指定办理人
在绘制流程图时,使用变量指定办理人。Assignee属性使用**${}
或者#{}
**。
在业务代码中设置具体的流程变量的值。
@Test
public void deployProcessDefi() {
Deployment deploy = processEngine.getRepositoryService()
.createDeployment().name("用户任务指定流程")
.addClasspathResource("AppayBill.bpmn")
.deploy();
System.out.println("部署名称:" + deploy.getName());
System.out.println("部署id:" + deploy.getId());
}
// 执行流程,开始跑流程
@Test
public void startProcess() {
String processDefiKey = "appayBill";// bpmn 的 process id属性
//封装流程变量
Map<String,Object> params=new HashMap<String, Object>();
params.put("userID", "王某某");
ProcessInstance pi = processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefiKey, params);
System.out.println("流程执行对象的id:" + pi.getId());// Execution 对象
System.out.println("流程实例的id:" + pi.getProcessInstanceId());// ProcessInstance
// 对象
System.out.println("流程定义的id:" + pi.getProcessDefinitionId());// 默认执行的是最新版本的流程定义
}
// 查询正在运行任务
@Test
public void queryTask() {
String assignee="王某某";//指定任务处理人
// 取得任务服务
TaskService taskService = processEngine.getTaskService();
// 创建一个任务查询对象
TaskQuery taskQuery = taskService.createTaskQuery();
// 办理人的任务列表
List<Task> list = taskQuery.taskAssignee(assignee).list();
// 遍历任务列表
if (list != null && list.size() > 0) {
for (Task task : list) {
System.out.println("任务的办理人:" + task.getAssignee());
System.out.println("任务的id:" + task.getId());
System.out.println("任务的名称:" + task.getName());
}
}
}
7.3使用类指定办理人
7.3.1:流程图中任务节点的配置
此时流程图的XML文件,如图:
7.3.2:TaskListenerImpl类,用来设置任务的办理人
public class TaskListenerImpl implements TaskListener {
/**指定个人任务和组任务的办理人*/
@Override
public void notify(DelegateTask delegateTask) {
String assignee = "张无忌";
//指定个人任务
delegateTask.setAssignee(assignee);
}
}
7.2.3:测试代码
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定义,启动流程实例
@Test
public void testTask() throws Exception {
// 1 发布流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("taskProcess.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
// 2 启动流程
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("taskProcess");
System.out.println("pid:" + pi.getId());
}
//查询我的个人任务列表
@Test
public void findMyTaskList(){
String userId = "张无忌";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskAssignee(userId)//指定个人任务查询
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("createTime="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
}
}
//完成任务
@Test
public void completeTask(){
String taskId = "3408";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任务");
}
//可以分配个人任务从一个人到另一个人(认领任务)
@Test
public void setAssigneeTask(){
//任务ID
String taskId = "3408";
//指定认领的办理者
String userId = "周芷若";
processEngine.getTaskService()//
.setAssignee(taskId, userId);
}
说明:
- 在类中使用
delegateTask.setAssignee(assignee)
;的方式分配个人任务的办理人,此时张无忌是下一个任务的办理人。。- 通过
processEngine.getTaskService().setAssignee(taskId, userId);
将个人任务从一个人分配给另一个人,此时张无忌不再是下一个任务的办理人,而换成了周芷若- 在开发中,可以将每一个任务的办理人规定好,例如张三的领导是李四,李四的领导是王五,这样张三提交任务,就可以查询出张三的领导是李四,通过类的方式设置下一个任务的办理人。
7.4 小结
个人任务及三种分配方式:
1:在taskProcess.bpmn中直接写 assignee=“张三丰"
2:在taskProcess.bpmn中写 assignee=“#{userID}”;变量的值要是String
的。
使用流程变量指定办理人
3,使用监听类方式,要实现TaskListener接口,在类中定义:
delegateTask.setAssignee(assignee);
// 指定个人任务的办理人
使用任务ID和办理人重新指定办理人:
processEngine.getTaskService()
.setAssignee(taskId, userId);
8.组任务
8.1数据库表
act_ru_identitylink
表存放任务的办理人,包括个人任务和组任务,表示正在执行的任务
act_hi_identitylink
表存放任务的办理人,包括个人任务和组任务,表示历史任务
8.2流程图
8.3分配组任务方式一(直接指定办理人)
8.3.1流程图中任务节点的配置
8.3.2示例代码
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定义,启动流程实例
@Test
public void testTask() throws Exception {
// 1 发布流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("taskProcess.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
// 2 启动流程
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("taskProcess");
System.out.println("pid:" + pi.getId());
}
//3 查询我的个人任务列表
@Test
public void findMyTaskList(){
String userId = "小A";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskAssignee(userId)//指定个人任务查询
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("createTime="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
}
}
//4 查询组任务列表
@Test
public void findGroupList(){
String userId = "小A";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskCandidateUser(userId)//指定组任务查询
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("createTime ="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
System.out.println("##################################");
}
}
//5 查询组任务成员列表
@Test
public void findGroupUser(){
String taskId = "3709";
List<IdentityLink> list = processEngine.getTaskService()//
.getIdentityLinksForTask(taskId);
//List<IdentityLink> list = processEngine.getRuntimeService()//
// .getIdentityLinksForProcessInstance(instanceId);
for(IdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//6 查询组任务成员历史列表
@Test
public void findGroupHisUser(){
String taskId = "3709";
List<HistoricIdentityLink> list = processEngine.getHistoryService()//
.getHistoricIdentityLinksForTask(taskId);
// List<HistoricIdentityLink> list = processEngine.getHistoryService()//
// .getHistoricIdentityLinksForProcessInstance(processInstanceId);
for(HistoricIdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//完成任务
@Test
public void completeTask(){
String taskId = "3709";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任务");
}
/**将组任务分配给个人任务,拾取任务*/
//由1个人去完成任务
@Test
public void claim(){
//任务ID
String taskId = "5908";
//分配的办理人
String userId = "小B";
processEngine.getTaskService()//
.claim(taskId, userId);
}
说明:
1)小A,小B,小C,小D是组任务的办理人 。
2)但是上述分配组任务的办理人的方式不够灵活,因为项目开发中任务的办理人不要放置XML文件中。
个人任务和组任务的参与者类型的区别:
如果是个人任务TYPE的类型表示
participant
(参与者)
如果是组任务TYPE的类型表示candidate
(候选者)和participant
(参与者)
8.4 分配个人任务方式二(使用流程变量)
8.4.1 流程图中任务节点的配置
8.4.2 测试代码
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定义,启动流程实例
@Test
public void testTask() throws Exception {
// 1 发布流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("taskProcess.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
// 2 启动流程
//启动流程实例,同时设置流程变量,用来指定组任务的办理人
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("userIDs", "大大,小小,中中");
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("taskProcess",variables);
System.out.println("pid:" + pi.getId());
}
//查询我的个人任务列表
@Test
public void findMyTaskList(){
String userId = "大大";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskAssignee(userId)//指定个人任务查询
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("assinee="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
}
}
//查询组任务列表
@Test
public void findGroupList(){
String userId = "大大";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskCandidateUser(userId)//指定组任务查询
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("assinee="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
System.out.println("##################################");
}
}
//查询组任务成员列表
@Test
public void findGroupUser(){
String taskId = "3709";
List<IdentityLink> list = processEngine.getTaskService()//
.getIdentityLinksForTask(taskId);
for(IdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//查询组任务成员历史列表
@Test
public void findGroupHisUser(){
String taskId = "3709";
List<HistoricIdentityLink> list = processEngine.getHistoryService()//
.getHistoricIdentityLinksForTask(taskId);
for(HistoricIdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//完成任务
@Test
public void completeTask(){
String taskId = "3709";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任务");
}
/**将组任务分配给个人任务,拾取任务*/
//由1个人去完成任务
@Test
public void claim(){
//任务ID
String taskId = "5908";
//分配的办理人
String userId = "小B";
processEngine.getTaskService()
.claim(taskId, userId);
}
说明:
1)大大,中中,小小是组任务的办理人
2)在开发中,可以在页面中指定下一个组任务的办理人,通过流程变量设置下一个任务的办理人
3) 个人任务和组任务的查询方式差别就在一行代码上:
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
//.taskAssignee(userId)//指定个人任务查询
.taskCandidateUser(userId)//指定组任务查询
.list();
8.5 分配个人任务方式三(使用类)
8.5.1 流程图中任务节点的配置
此时流程图的XML文件,如图:
8.5.2 TaskListenerImpl类,用来设置任务的办理人
public class TaskListenerImpl implements TaskListener {
/**指定个人任务和组任务的办理人*/
@Override
public void notify(DelegateTask delegateTask) {
String userId1 = "孙悟空";
String userId2 = "猪八戒";
//指定组任务
delegateTask.addCandidateUser(userId1);
delegateTask.addCandidateUser(userId2);
}
}
8.5.3 测试代码
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定义,启动流程实例
@Test
public void testTask() throws Exception {
// 1 发布流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("taskProcess.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
// 2 启动流程
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("taskProcess");
System.out.println("pid:" + pi.getId());
}
//查询我的个人任务列表
@Test
public void findMyTaskList(){
String userId = "孙悟空";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskAssignee(userId)//指定个人任务查询
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("assinee="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
}
}
//查询组任务列表
@Test
public void findGroupList(){
String userId = "孙悟空";
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskCandidateUser(userId)//指定组任务查询
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("assinee="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
System.out.println("##################################");
}
}
//查询组任务成员列表
@Test
public void findGroupUser(){
String taskId = "4008";
List<IdentityLink> list = processEngine.getTaskService()//
.getIdentityLinksForTask(taskId);
for(IdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//查询组任务成员历史列表
@Test
public void findGroupHisUser(){
String taskId = "4008";
List<HistoricIdentityLink> list = processEngine.getHistoryService()//
.getHistoricIdentityLinksForTask(taskId);
for(HistoricIdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//完成任务
@Test
public void completeTask(){
String taskId = "4008";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任务");
}
将组任务分配给个人任务(认领任务)
//将组任务分配给个人任务(认领任务)
@Test
public void claimTask(){
String taskId = "4008";
//个人任务的办理人
String userId = "如来";
processEngine.getTaskService().claim(taskId, userId);
}
可以分配个人任务回退到组任务,(前提之前是个组任务)
@Test
public void setAssigneeTask(){
//任务ID
String taskId = "4008";
processEngine.getTaskService()//
.setAssignee(taskId, null);
}
```
```java
//向组任务中添加成员
@Test
public void addUser(){
String taskId = "4008";
String userId = "沙和尚";
processEngine.getTaskService().addCandidateUser(taskId, userId);
}
//向组任务中删除成员
@Test
public void removeUser(){
String taskId = "4008";
String userId = "沙和尚";
processEngine.getTaskService().deleteCandidateUser(taskId, userId);
}
说明:
1)在类中使用delegateTask.addCandidateUser (userId);
的方式分配组任务的办理人,此时孙悟空和猪八戒是下一个任务的办理人。
2)通过processEngine.getTaskService().claim (taskId, userId);
将组任务分配给个人任务,也叫认领任务
,即指定某个人去办理这个任务,此时由如来去办理任务。
注意:认领任务的时候,可以是组任务成员中的人,也可以不是组任务成员的人,此时通过Type的类型为participant来指定任务的办理人
3)addCandidateUser()
即向组任务添加成员,deleteCandidateUser()
即删除组任务的成员。
4)在开发中,可以将每一个任务的办理人规定好,例如张三的领导是李四和王五,这样张三提交任务,由李四或者王五去查询组任务,可以看到对应张三的申请,李四或王五再通过认领任务(claim)的方式,由某个人去完成这个任务。
8.6 总结
组任务及三种分配方式:
1:在taskProcess.bpmn中直接写 candidate-users=“小A,小B,小C,小D";
2:在taskProcess.bpmn中写 candidate-users =“#{userIDs}”,变量的值要是String的。
//使用流程变量指定办理人
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("userIDs", "大大,小小,中中");
3,使用TaskListener接口,使用类实现该接口,在类中定义:
//添加组任务的用户
delegateTask.addCandidateUser(userId1);
delegateTask.addCandidateUser(userId2);
//组任务分配给个人任务(认领任务):
processEngine.getTaskService().claim(taskId, userId);
//个人任务分配给组任务:
processEngine.getTaskService(). setAssignee(taskId, null);
//向组任务添加人员:
processEngine.getTaskService().addCandidateUser(taskId, userId);
//向组任务删除人员:
processEngine.getTaskService().deleteCandidateUser(taskId, userId);
个人任务和组任务存放办理人对应的表:
act_ru_identitylink表存放任务的办理人,包括个人任务和组任务,表示正在执行的任务
act_hi_identitylink表存放任务的办理人,包括个人任务和组任务,表示历史任务
区别在于:如果是个人任务TYPE的类型表示participant(参与者)
如果是组任务TYPE的类型表示candidate(候选者)和participant(参与者)
9 工作流定义的角色组(了解)
9.1数据库表
act_id_group
:角色组表
act_id_user
:用户表
act_id_membership
:用户角色表
9.2 流程图
流程图中任务节点的配置:
分配任务负责的组:使用 candidate groups
属性指定 任务负责组。
代码:
<userTask id=“usertask1” name=“审批” activiti:candidateGroups=“部门经理”>
</userTask>
其中部门经理表示一个用户组的角色.
9.3 测试代码
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程定义,启动流程实例
@Test
public void testTask() throws Exception {
// 1 发布流程
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("taskProcess.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("taskProcess.png");
processEngine.getRepositoryService()//
.createDeployment()//
.addInputStream("userTask.bpmn", inputStreamBpmn)//
.addInputStream("userTask.png", inputStreamPng)//
.deploy();
/**在部署流程定义和启动流程实例的中间,设置组任务的办理人,向Activity表中存放组和用户的信息*/
IdentityService identityService = processEngine.getIdentityService();//认证:保存组和用户信息
identityService.saveGroup(new GroupEntity("部门经理"));//建立组
identityService.saveGroup(new GroupEntity("总经理"));//建立组
identityService.saveUser(new UserEntity(“小张”));//建立用户
identityService.saveUser(new UserEntity("小李")); //建立用户
identityService.saveUser(new UserEntity("小王")); //建立用户
identityService.createMembership("小张", "部门经理");//建立组和用户关系
identityService.createMembership("小李", "部门经理");//建立组和用户关系
identityService.createMembership(“小王”, “总经理”);//建立组和用户关系
// 2 启动流程
ProcessInstance pi = processEngine.getRuntimeService()//
.startProcessInstanceByKey("taskProcess");
System.out.println("pid:" + pi.getId());
}
//3 查询我的个人任务列表
@Test
public void findMyTaskList(){
String userId = "唐僧";
List<Task> list = processEngine.getTaskService()
.createTaskQuery()
.taskAssignee(userId)//指定个人任务查询
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("assinee="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
}
}
//4 查询组任务列表
@Test
public void findGroupList(){
String userId = "小李";//小张,小李可以查询结果,小王不可以,因为他不是部门经理
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskCandidateUser(userId)//指定组任务查询
.list();
for(Task task:list ){
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
System.out.println("assinee="+task.getAssignee());
System.out.println("assinee="+task.getCreateTime());
System.out.println("executionId="+task.getExecutionId());
System.out.println("##################################");
}
}
//5 查询组任务成员列表
@Test
public void findGroupUser(){
String taskId = "4408";
List<IdentityLink> list = processEngine.getTaskService()//
.getIdentityLinksForTask(taskId);
for(IdentityLink identityLink:list ){
System.out.println("userId="+identityLink.getUserId());
System.out.println("taskId="+identityLink.getTaskId());
System.out.println("piId="+identityLink.getProcessInstanceId());
System.out.println("######################");
}
}
//完成任务
@Test
public void completeTask(){
String taskId = "5108";
processEngine.getTaskService()//
.complete(taskId);//
System.out.println("完成任务");
}
}
9.4 核心操作
在部署流程定义和启动流程实例的中间,设置组任务的办理人,向Activiti表中存放组和用户的信息
/**在部署流程定义和启动流程实例的中间,设置组任务的办理人,向Activiti表中存放组和用户的信息*/
IdentityService identityService = processEngine.getIdentityService();//认证:保存组和用户信息
identityService.saveGroup(new GroupEntity("部门经理"));//建立组
identityService.saveGroup(new GroupEntity("总经理"));//建立组
identityService.saveUser(new UserEntity(“小张”));//建立用户
identityService.saveUser(new UserEntity("小李")); //建立用户
identityService.saveUser(new UserEntity("小王")); //建立用户
identityService.createMembership("小张", "部门经理");//建立组和用户关系
identityService.createMembership("小李", "部门经理");//建立组和用户关系
identityService.createMembership(“小王”, “总经理”);//建立组和用户关系
指定组任务的办理人,查询组任务
String userId = “小张”;//小张,小李可以查询结果,小王不可以,因为他不是部门经理角色
List<Task> list = processEngine.getTaskService()//
.createTaskQuery()//
.taskCandidateUser(userId)//指定组任务查询
.list();