Activiti学习
Activiti学习
- 官方文档:https://www.activiti.org/userguide/
- 示例代码:https://github.com/Activiti/activiti-examples
- 在线课程:https://www.activiti.org/resources/training
- 论坛与社区支持:https://community.alfresco.com/community/activiti
- 源码:
activiti5.22:https://github.com/Activiti/Activiti/releases/download/activiti-5.22.0/activiti-5.22.0.zip
activiti6.0.0:https://github.com/Activiti/Activiti/releases/download/activiti-6.0.0/activiti-6.0.0.zip
delete from act_hi_actinst;
delete from act_hi_detail;
delete from act_hi_identitylink;
delete from act_hi_procinst;
delete from act_hi_taskinst;
delete from act_hi_varinst;
delete from act_ru_identitylink;
delete from act_ru_task;
delete from act_ru_variable;
delete from act_ru_execution;
PROC_INST_ID_ --> 指的是 act_ru_execution 表中,每个实例的第一条记录的id
学习参考:
https://vip.tulingxueyuan.cn/detail/p_6098c3afe4b0b07e9ccb0d7d/6
https://blog.csdn.net/m0_46527847/article/details/113363266
https://blog.csdn.net/qq_40451631/article/details/84937251
异常:https://blog.csdn.net/dongpo11/article/details/120254281
工作流监听器:https://www.cnblogs.com/no-celery/p/15967880.html
代办人和候选人和候选组
步骤
首先需要在 java 工程中加入 ProcessEngine 所需要的 jar 包,包括:
activiti-engine-7.0.0.beta1.jar
activiti 依赖的 jar 包:mybatis、alf4j、log4j 等
activiti 依赖的 spring 包
数据库驱动
第三方数据连接池 dbcp
单元测试 Junit-4.12.jar
我们使用 maven 来实现项目的构建,所以应当导入这些 jar 所对应的坐标到 pom.xml 文件中。
1. 安装Activiti数据库
1.1. actiBPM 插件
idea: actiBPM 插件 https://blog.csdn.net/shipfei_csdn/article/details/105157702
https://plugins.jetbrains.com/plugin/7429-actibpm/versions
本地地址: actibpm.jar
1.2. 创建 mysql 数据库 activiti
CREATE DATABASE activiti DEFAULT CHARACTER SET utf8;
1.3. 配置maven
<properties>
<activiti.version>7.1.0.M6</activiti.version>
<activiti.cloud.version>7.0.0.Beta1</activiti.cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 模型处理 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 转换 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn json数据转换 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 布局 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-layout</artifactId>
<version>${activiti.version}</version>
</dependency>
</dependencies>
1.4. 创建配置文件
创建配置文件 activiti.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contex http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--数据源配置dbcp-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/activiti?nullCatalogMeansCurrent=true" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!--activiti单独运行的ProcessEngine配置对象(processEngineConfiguration),使用单独启动方式
默认情况下:bean的id=processEngineConfiguration
-->
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!--代表数据源-->
<property name="dataSource" ref="dataSource"></property>
<!-- <property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="root" />-->
<!-- activiti数据库表处理策略 -->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
说明: 配置文件
activiti.cfg.xml如果不叫这个名称,则 需要通过ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml", "processEngineConfiguration"); 来指定
关于 processEngineConfiguration 中的 databaseSchemaUpdate **参数,**通过此参数设计 activiti 数据表的处理策略,参数如下:
false(默认):检查数据库表的版本和依赖库的版本, 如果版本不匹配就抛出异常。
true: 构建流程引擎时,执行检查,如果需要就执行更新。 如果表不存在,就创建。
create-drop: 构建流程引擎时创建数据库表, 关闭流程引擎时删除这些表。
drop-create:先删除表再创建表。
create: 构建流程引擎时创建数据库表, 关闭流程引擎时不删除这些表。
1.5. 配置日志
1.6. 通过程序创建表
创建工作流 25张表
创建 ProcessEngineConfiguration,通过 ProcessEngineConfiguration 创建 ProcessEngine,在创建 ProcessEngine 时会自动创建数据库
public static void main(String[] args) {
//条件:1.activiti配置文件名称:activiti.cfg.xml 2.bean的id="processEngineConfiguration"
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
}
如果 工作流配置文件命名不是默认的 activiti.cfg.xml 则通过 下面的方式 创建
public static void main(String[] args) {
//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);
}
2. Activiti 服务架构

在新版本中IdentityService,FormService 两个 Serivce 都已经删除了。所以后面我们对于这两个 Service 也不讲解了,但老版本中还是有这两个 Service
通过 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 获取 processEngine ,从而可以访问各个service 接口
| RepositoryService | activiti 的资源管理类,提供了管理和控制流程发布包和流程定义的操作 |
|---|---|
| RuntimeService | activiti 的流程运行管理类,可以从这个服务类中获取很多关于流程执行相关的信息 |
| TaskService | activiti 的任务管理类,可以从这个类中获取任务的信息 |
| HistoryService | activiti 的历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据(根据配置),比如流程实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执行路径,等等。 这个 |
| ManagerService | activiti 的引擎管理类提,供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动 |
| IdentityService | 组织机构管理 |
3. 操作数据库表
3.1 SELECT * FROM act_ru_execution 流程实例执行表,记录当前流程实例的执行情况

3.2 SELECT *FROM act_ru_task#任务执行表,记录当前执行的任务

3.3 SELECT *FROM act_ru_identitylink #任务参与者,记录当前参与任务的用户或组

3.4 SELECT *FROM act_hi_procinst#流程实例历史表

3.5 SELECT *FROM act_hi_taskinst #任务历史表,记录所有任务

3.6 SELECT *FROM act_hi_actinst #活动历史表,记录所有活动

4. Activiti 使用
4.1 流程定义
编辑 bpmn文件
Activiti-Designer 使用
在 eclipse 或 idea 中安装 activiti-designer 插件即可使用,画板中包括以下结点:
Connection—连接
Event---事件
Task---任务
Gateway---网关
Container—容器
Boundary event—边界事件
Intermediate event- -中间事件
流程图设计完毕保存生成.bpmn 文件
4.2 流程部署
设计到的表:act_ge_bytearray act_re_deployment
act_ge_bytearray :存储png 和bpmn文件信息
act_re_deployment:存储流程定义的一下信息
1.文件上传方式
/**
* 部署流程定义 文件上传方式
*/
@Test
public void testDeployment(){
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3、使用RepositoryService进行部署
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("bpmn/Leave.bpmn") // 添加bpmn资源
//png资源命名是有规范的。Leave.myLeave.png|jpg|gif|svg 或者Leave.png|jpg|gif|svg
.addClasspathResource("bpmn/Leave.myLeave.png") // 添加png资源
.name("请假申请流程")
.deploy();
// 4、输出部署信息
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
2.zip压缩文件上传方式
public void deployProcessByZip() {
// 定义zip输入流
InputStream inputStream = this
.getClass()
.getClassLoader()
.getResourceAsStream(
"bpmn/Leave.zip");
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
// 获取repositoryService
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
// 流程部署
Deployment deployment = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
4.3 启动一个流程实例
流程定义 **act_re_procdef ** ====> processInstance.getProcessDefinitionId()
act_hi_actinst 已完成的活动信息 act_hi_identitylink 参与者信息 act_hi_procinst 流程实例 =====> processInstance.getId() act_hi_taskinst 任务实例 act_ru_execution 执行表 act_ru_identitylink 参与者信息 act_ru_task 任务
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RunService对象
RuntimeService runtimeService = processEngine.getRuntimeService();
//3.创建流程实例 流程定义的key需要知道 holiday
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday");
//4.输出实例的相关信息
System.out.println("流程部署ID"+processInstance.getDeploymentId());//null
System.out.println("流程定义ID"+processInstance.getProcessDefinitionId());//holiday:1:4
System.out.println("流程实例ID"+processInstance.getId());//2501
System.out.println("活动ID"+processInstance.getActivityId());//null
4.4 任务查询
public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService对象
TaskService taskService = processEngine.getTaskService();
//3.根据流程定义的key,负责人assignee来实现当前用户的任务列表查询
Task task = taskService.createTaskQuery()
.processDefinitionKey("myLeave")
.taskAssignee("worker")
.singleResult();
//4.任务列表的展示
System.out.println("流程实例ID:"+task.getProcessInstanceId());
System.out.println("任务ID:"+task.getId()); //5002
System.out.println("任务负责人:"+task.getAssignee());
System.out.println("任务名称:"+task.getName());
}
//zhangsan任务列表的查询
public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService对象
TaskService taskService = processEngine.getTaskService();
//3.根据流程定义的key,负责人assignee来实现当前用户的任务列表查询
List<Task> taskList = taskService.createTaskQuery()
.processDefinitionKey("myLeave")
.taskAssignee("worker")
.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());
}
}
4.5 任务处理
act_hi_actinst 已完成的活动信息 act_hi_identitylink 参与者信息 act_hi_taskinst act_ru_identitylink act_ru_task
//查询当前用户wangwu的任务并处理掉
public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService对象
TaskService taskService = processEngine.getTaskService();
//3.查询当前用户的任务
Task task = taskService.createTaskQuery()
.processDefinitionKey("myLeave")
.taskAssignee("worker")
.singleResult();
//4.处理任务,结合当前用户任务列表的查询操作的话,任务ID:task.getId()
taskService.complete(task.getId());
//5.输出任务的id
System.out.println(task.getId());
}
4.6 历史查询
public static void main(String[] args) throws IOException {
//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());//流程定义 ===> act_re_procdef
System.out.println(instance.getProcessInstanceId());//流程实例 ====> act_re_deployment
System.out.println("=============================");
}
}
5. 流程实例
先有 流程定义 ===> act_re_procdef 在有 流程实例 ====> act_re_deployment

5.1 查询流程定义信息
public static void main(String[] args) {
//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());
}
}
5.2 删除已经部署的流程定义
act_ge_bytearray act_re_deployment act_re_procdef
/**
* 注意事项:
* 1.当我们正在执行的这一套流程没有完全审批结束的时候,此时如果要删除流程定义信息就会失败
* 2.如果公司层面要强制删除,可以使用repositoryService.deleteDeployment("1",true);
* //参数true代表级联删除,此时就会先删除没有完成的流程结点,最后就可以删除流程定义信息 false的值代表不级联
*
* @param args
*/
public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.创建RepositoryService对象
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.执行删除流程定义 参数代表流程部署的id
repositoryService.deleteDeployment("1");
}
5.3读取两个资源文件
需求: 1.从Activiti的act_ge_bytearray表中读取两个资源文件 2.将两个资源文件保存到路径: G:\Activiti7开发计划\Activiti7-day03\资料
技术方案: 1.第一种方式使用actviti的api来实现 2.第二种方式:其实就是原理层面,可以使用jdbc的对blob类型,clob类型数据的读取,并保存 IO流转换,最好commons-io.jar包可以轻松解决IO操作
public static void main(String[] args) throws IOException {
//1.得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RepositoryService对象
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.得到查询器:ProcessDefinitionQuery对象
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//4.设置查询条件
processDefinitionQuery.processDefinitionKey("holiday");//参数是流程定义的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("G:\\Activiti7开发计划\\Activiti7-day03\\资料\\"+processDefinition.getDiagramResourceName());
OutputStream bpmnOs =
new FileOutputStream("G:\\Activiti7开发计划\\Activiti7-day03\\资料\\"+processDefinition.getResourceName());
//9.输入流,输出流的转换 commons-io-xx.jar中的方法
IOUtils.copy(pngIs,pngOs);
IOUtils.copy(bpmnIs,bpmnOs);
//10.关闭流
pngOs.close();
bpmnOs.close();
pngIs.close();
bpmnIs.close();
}
5.4 Activiti与业务系统整合
通过 act_ru_execution 存储 businesskey
Businesskey:业务标识,通常为业务表的主键,业务标识和流程实例一一对应。业务标识来源于业务系统。存储业务标识就是根据业务标识来关联查询业务系统的数据。
比如:请假流程启动一个流程实例,就可以将请假单的 id 作为业务标识存储到 activiti 中,将来查询activiti 的流程实例信息就可以获取请假单的 id 从而关联查询业务系统数据库得到请假单信息。
// 根据流程定义的key启动一个流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey,businessKey);
5.5 查询流程实例
6. 个人任务
6.1分配任务负责人
总共三种写法
固定分配
表达式分配
- UEL 表达式
- 使用流程变量分配任务
监听器分配
6.1.1 固定分配
Assignee 项为任务负责人

6.1.2 表达式分配
Activiti 使用 UEL 表达式,UEL 是 java EE6 规范的一部分,UEL(Unified Expression Language)即 统一表达式语言,activiti 支持两个 UEL 表达式:UEL-value 和 UEL-method。
6.1.2.1 UEL-value 定义如下
2种写法,${assignee} 和 ${user.assignee}
其中 user 也是 activiti 的一个流程变量,user.assignee 表示通过调用 user 的 getter 方法获取值
6.1.2.2 UEL-method 方式如下:
userBean 是 spring 容器中的一个 bean,表示调用该 bean 的 getUserId()方法

6.1.2.3 UEL-method 与 UEL-value 结合
再比如:${ldapService.findManagerForEmployee(emp)}
ldapService 是 spring 容器的一个 bean,findManagerForEmployee 是该 bean 的一个方法,emp 是 activiti流程变量,emp 作为参数传到ldapService.findManagerForEmployee 方法中。
6.1.2.4 其它
表达式支持解析基础类型、bean、list、array 和 map,也可作为条件判断。
如下:
${order.price > 100 && order.price < 250}
6.1.2.5 设置流程变量写法
//启动流程实例时设计流程变量
//定义流程变量
Map<String, Object> variables = new HashMap<String, Object>();
//设置流程变量assignee
variables.put("assignee", "张三");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
6.1.3 监听器分配
任务监听器是发生对应的任务相关事件时执行自定义 java 逻辑 或表达式。
任务相当事件包括:
Create:任务创建后触发
Assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件发生都触发

定义任务监听类,且类必须实现 org.activiti.engine.delegate.TaskListener 接口
public class MyTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
//这里指定任务负责人
delegateTask.setAssignee("张三");
}
}
6.2 查询任务
// 查询当前个人待执行的任务
@Test
public void findPersonalTaskList() {
// 流程定义key
String processDefinitionKey = "holiday";
// 任务负责人
String assignee = "张三丰";
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery()//
.processDefinitionKey(processDefinitionKey)//
.includeProcessVariables().taskAssignee(assignee).list();
for (Task task : list) {
System.out.println("----------------------------");
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
在查询待办任务时,通过 businessKey(业务标识 )关联查询业务系统的请假单表,查询出请假天数等信息

6.3 办理任务
指定任务 id,调用 TaskService 完成任务
// 完成任务
@Test
public void completTask() {
//任务id
String taskId = "10305";
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
taskService.complete(taskId);
System.out.println("完成任务");
}
注意:在实际应用中,完成任务前需要校验任务的负责人是否具有该任务的办理权限。
//完成任务 zhangsan -----lishi----判断流程变量的请假天数,1天----分支:人事经理存档(zhaoliu)
public static void main3(String[] args) {
//1.得到ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到TaskService
TaskService taskService = processEngine.getTaskService();
//3.查询当前用户是否有任务
String key = "myProcess_1";
Task task = taskService.createTaskQuery().processDefinitionKey(key)
.taskAssignee("zhaoliu").singleResult();
//4.判断task!=null,说明当前用户有任务
if(task!=null){
taskService.complete(task.getId());
System.out.println("任务执行完毕");
}
}
7 流程变量
注意:虽然流程变量中可以存储业务数据可以通过activiti的api查询流程变量从而实现 查询业务数据,但是不建议这样使用,因为业务数据查询由业务系统负责,activiti设置流程变量是为了流程执行需要而创建。
流程变量的数据类型: string,duble,boolean,short,long,Integer,binary,date日期、serializable。如果流程变量需要用到pojo当中的属性字段,则该pojo需要实现序列化接口
流程变量的作用域默认是一个流程实例(processInstance),也可以是一个任务(task)或一个执行实例(execution),这三个作用域流程实例的范围最大,可以称为 global 变量,任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大,称为 local 变量。
global 变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。
Local 变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。
Local 变量名也可以和 global 变量名相同,没有影响。
private String getVariableByNameForHis(String taskId, String variableName) {
HistoricVariableInstance businessDept = historyService.createHistoricVariableInstanceQuery()
.taskId(taskId)
.variableName(variableName)
.singleResult();
if(businessDept==null){
return "";
}
return (String) businessDept.getValue();
}
/**
* 从历史中获取 属性值,启动时获取会有异常
* @param procInsId
* @param variableName
* @return
*/
private String getVariableByNameForHis(String procInsId, String variableName) {
this.historyService = BeanContext.getApplicationContext().getBean(HistoryService.class);
HistoricVariableInstance businessDept = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(procInsId)
.variableName(variableName)//business_office "business_dept"
.excludeTaskVariables().singleResult();
return (String) businessDept.getValue();
}
操作数据库表
设置流程变量会在当前执行流程变量表插入记录,同时也会在历史流程变量表也插入记录。
SELECT * FROM act_ru_variable #当前流程变量表
记录当前运行流程实例可使用的流程变量,包括 global 和 local 变量
| Id_ | 主键 |
|---|---|
| Type_ | 变量类型 |
| Name_ | 变量名称 |
| Execution_id_ | 所属流程实例执行 id,global 和 local 变量都存储 |
| Proc_inst_id_ | 所属流程实例 id,global 和 local 变量都存储 |
| Task_id_ | 所属任务 id,local 变量存储 |
| Bytearray_ | serializable 类型变量存储对应 act_ge_bytearray 表的 id |
| Double_ | double 类型变量值 |
| Long_ | long类型变量值 |
| Text_ | text 类型变量值 |
Text_:text 类型变量值
SELECT * FROM act_hi_varinst #历史流程变量表
记录所有已创建的流程变量,包括 global 和 local 变量
字段意义参考当前流程变量表。
Authentication.setAuthenticatedUserId(currentUserId);// 记录 流程申请人
// 启动流程
ProcessInstance procIns = runtimeService.startProcessInstanceByKey(procDefKey, businessTable + ":" + businessId, vars);
//runtimeService.startProcessInstanceByKey(procDefKey, vars);// 设置全局变量
- 查询流程变量的数值
List<HistoricVariableInstance> business_dept = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(procInsId)
.variableName("business_dept")
.excludeTaskVariables()
.list();
System.out.println(business_dept);
HistoricVariableInstance business_dept = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(procInsId)
.variableName("business_dept")
.excludeTaskVariables().singleResult();
System.out.println(business_dept.getValue());ja
8. 组任务
8.1 设置候选人
Candidate-users 候选人
在流程图中任务节点的配置中设置 candidate-users(候选人),多个候选人之间用逗号分开。

8.2 办理组任务
8.2.1 组任务办理流程
第一步:查询组任务指定候选人,查询该候选人当前的待办任务。 候选人不能办理任务。
第二步:拾取(claim)任务 该组任务的所有候选人都能拾取。将候选人的组任务,变成个人任务。原来候选人就变成了该任务的负责人。 如果拾取后不想办理该任务? 需要将已经拾取的个人任务归还到组里边,将个人任务变成了组任务。
第三步:查询个人任务 查询方式同个人任务部分,根据 assignee 查询用户负责的个人任务。
第四步:办理个人任务
8.2.2 用户查询组任务
根据候选人查询组任务
@Test
public void findGroupTaskList() {
// 流程定义key
String processDefinitionKey = "holiday4";
// 任务候选人
String candidateUser = "zhangsan";
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
//查询组任务
List<Task> list = taskService.createTaskQuery()//
.processDefinitionKey(processDefinitionKey)//
.taskCandidateUser(candidateUser)//根据候选人查询
.list();
for (Task task : list) {
System.out.println("----------------------------");
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
8.2.3 用户拾取组任务
候选人员拾取组任务后该任务变为自己的个人任务
@Test
public void claimTask(){
TaskService taskService = processEngine.getTaskService();
//要拾取的任务id
String taskId = "6302";
//任务候选人id
String userId = "lisi";
//拾取任务
//即使该用户不是候选人也能拾取(建议拾取时校验是否有资格)
//校验该用户有没有拾取任务的资格
Task task = taskService.createTaskQuery()//
.taskId(taskId)
.taskCandidateUser(userId)//根据候选人查询
.singleResult();
if(task!=null){
taskService.claim(taskId, userId);
System.out.println("任务拾取成功");
}
}
说明:即使该用户不是候选人也能拾取,建议拾取时校验是否有资格 ,组任务拾取后,该任务已有负责人,通过候选人将查询不到该任务
8.2.4 用户查询个人待办任务
查询方式同个人任务查询
@Test
public void findPersonalTaskList() {
// 流程定义key
String processDefinitionKey = "holiday4";
// 任务负责人
String assignee = "zhangsan";
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery()//
.processDefinitionKey(processDefinitionKey)//
.taskAssignee(assignee).list();
for (Task task : list) {
System.out.println("----------------------------");
System.out.println(" 流 程 实 例 id : " +
task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
8.2.5 用户办理个人任务
同个人任务办理
/**完成任务*/
@Test
public void completeTask(){
//任务ID
String taskId = "12304";
processEngine.getTaskService()//
.complete(taskId);
System.out.println("完成任务:"+taskId);
}
8.2.6 归还组任务
如果个人不想办理该组任务,可以归还组任务,归还后该用户不再是该任务的负责人
// 归还组任务,由个人任务变为组任务,还可以进行任务交接
@Test
public void setAssigneeToGroupTask() {
// 查询任务使用TaskService
TaskService taskService = processEngine.getTaskService();
// 当前待办任务
String taskId = "6004";
// 任务负责人
String userId = "zhangsan2";
// 校验userId是否是taskId的负责人,如果是负责人才可以归还组任务
Task task = taskService.createTaskQuery().taskId(taskId)
.taskAssignee(userId).singleResult();
if (task != null) {
// 如果设置为null,归还组任务,该 任务没有负责人
taskService.setAssignee(taskId, null);
}
}
说明:建议归还任务前校验该用户是否是该任务的负责人,也可以通过 setAssignee 方法将任务委托给其它用户负责,注意被委托的用户可以不是候选人(建议
不要这样使用)
8.2.7 任务交接
任务交接,任务负责人将任务交给其它候选人办理该任务
@Test
public void setAssigneeToCandidateUser() {
// 查询任务使用TaskService
TaskService taskService = processEngine.getTaskService();
// 当前待办任务
String taskId = "6004";
// 任务负责人
String userId = "zhangsan2";
// 校验userId是否是taskId的负责人,如果是负责人才可以归还组任务
Task task = taskService.createTaskQuery().taskId(taskId)
.taskAssignee(userId).singleResult();
if (task != null) {
// 将此任务交给其它候选人办理该 任务
String candidateuser = "zhangsan";
// 根据候选人和组任务id查询,如果有记录说明该 候选人有资格拾取该 任务
Task task2 = taskService.createTaskQuery().taskId(taskId)
.taskCandidateUser(candidateuser).singleResult();
if (task2 != null) {
// 才可以交接
taskService.setAssignee(taskId, candidateuser);
}
}
}
8.2.8 数据库表操作
SELECT * FROM act_ru_task #任务执行表,记录当前执行的任务,由于该任务当前是组任务,所有assignee 为空,当拾取任务后该字段就是拾取用户的 id

SELECT * FROM act_ru_identitylink #任务参与者,记录当前参考任务用户或组,当前任务如果设置了候选人,会向该表插入候选人记录,有几个候选就插入几个

于 act_ru_identitylink 对应的还有一张历史表 act_hi_identitylink,向 act_ru_identitylink 插入记录的同时也会向历史表插入记录。任务完成
8.3 Candiate-Groups
Candiate-Groups
9. 网关
9.1 排他网关
排他网关(也叫异或(XOR)网关,或叫基于数据的排他网关),用来在流程中实现决策。 当流程执行到这个网关,所有分支都会判断条件是否为 true,如果为 true 则执行该分支,注意,排他网关只会选择一个为 true 的分支执行。(即使有两个分支条件都为 true,排他网关也会只选择一条分支去执行)
为什么要用排他网关?
不用排他网关也可以实现分支,如下图

上图中,在连线的 condition 条件上设置分支条件。
缺点:
如果条件都不满足,不使用排他网关,流程就结束了(是异常结束)。
如果 使用排他网关决定分支的走向,如下

如果从网关出去的线所有条件都不满足则系统抛出异常。
org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway 'exclusivegateway1' could be selected for continuing the process at org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivityBehavior.java:85)
说明 :经过排他网关必须要有一条且只有一条分支走
9.2 并行网关
并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进
入和外出顺序流的:
- fork 分支:
并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。
- join 汇聚:
所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。
注意,如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。与其他网关的主要区别是,并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。
例子:

说明:
财务结算和入库是两个 execution 分支,在 act_ru_execution 表有两条记录分别是财务结算和入库,act_ru_execution 还有一条记录表示该流程实例。
待财务结算和入库任务全部完成,在汇聚点汇聚,通过 parallelGateway 并行网关。并行网关在业务应用中常用于会签任务,会签任务即多个参与者共同办理的任务。
数据库表操作
以上图为例 当执行到并行网关数据库跟踪如下:
当前任务表:SELECT * FROM act_ru_task #当前任务表

上图中:有两个(多个)任务当前执行。
通过流程实例执行表:SELECT * FROM act_ru_execution #流程实例的执行表

上图中,说明当前流程实例有多个分支(两个)在运行。
对并行任务的执行:
并行任务执行不分前后,由任务的负责人去执行即可。当完成并任务中一个任务后:
已完成的任务在当前任务表 act_ru_task_已被删除。
在流程实例执行表:SELECT * FROM act_ru_execution 有中多个分支存在且有并行网关的汇聚结点。

有并行网关的汇聚结点:说明有一个分支已经到汇聚,等待其它的分支到达。当所有分支任务都完成,都到达汇聚结点后:
流程实例执行表:SELECT * FROM act_ru_execution,执行流程实例不存在,说明流程执行结束。
总结:所有分支到达汇聚结点,并行网关执行完成。
9.3 包含网关
包含网关可以看做是排他网关和并行网关的结合体。 和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。
但是主要的区别是包含网关可以选择多于一条顺序流,这和并行网关一样。包含网关的功能是基于进入和外出顺序流的
- 分支:
所有外出顺序流的条件都会被解析,结果为 true 的顺序流会以并行方式继续执行,会为每个顺序流创建一个分支。
- 汇聚:
所有并行分支到达包含网关,会进入等待状态, 直到每个包含流程 token 的进入顺序流的分支都到达。 这是与并行网关的最大不同。换句话说,包含网关只会等待被选中执行了的进入顺序流。 在汇聚之后,流程会穿过包含网关继续执行。
案例:
以体检为列:通过流程变量 userType 来表示,如果等于 1 表示普通员工,如果等于 2 表示领导,常规体检和抽血 普通员工和领导都可以,但是附件项只能是领导才能走

10 课程总结
什么是工作流? 就是通过计算机对业务流程进行自动化管理,实现多个参与者按照预定义的流程去自动执行业务流程。 什么 activiti?
Activiti 是一个工作流的引擎,开源的架构,基本 bpmn2.0 标准进行流程定义,它的是前身是 jbpm。 Activiti 通过是要嵌入到业务系统开发使用。
如何使用 activiti 开发?
第一步:部署 activiti 的环境。 环境包括:jar 包和数据库(25 张表) 业务系统通过 spring 和 activiti 整合进行开发。
第二步:使用 activiti 提供流程设计器(和 idea 或 eclipse 集成的 designer)工具进行流程定义 流程定义生成两个文件:.bpmn 和.png(不是必须的)。
第三步;将流程定义文件部署到 activiti 的数据库 SELECT * FROM act_re_deployment #流程定义部署表 一次部署插入一条记录,记录流程定义的部署信息 SELECT * FROM act_re_procdef #流程定义表一次部署流程定义信息,如果一次部署两个流程定义,插入两条记录 建议:一次部署只部署一个流程定义,这样 act_re_deployment 和 act_re_procdef 一对一关系 常用两个方法:单个文件部署和 zip 文件部署。 建议单个文件部署。
第四步: 启动一个流程实例 业务系统就可以按照流程定义去执行业务流程,执行前需要启动一个流程实例根据流程定义来启动一个流程实例。 指定一个流程定义的 key 启动一个流程实例,activiti 根据 key 找最新版本的流程定义。 指定一个流程定义的 id 启动一个流程实例。 启动一个实例需要指定 businesskey(业务标识),businessKey 是 activiti 和业务系统整合时桥梁。 比如:请假流程,businessKey 就是请假单 id。 启动一个实例还可以指定流程变量,流程变量是全局变量(生命期是整个流程实例,流程实例结束,变量就消失)
第五步:查询待办任务 查询个人任务:使用 taskService,根据 assignee 查询该用户当前的待办任务。 查询组任务:使用 taskService,根据 candidateuser 查询候选用户当前的待办组任务。
第六步:办理任务 办理个人任务:调用 taskService 的 complete 方法完成任务。 如果是组任务,需要先拾取任务,调用 taskService 的 claim 方法拾取任务,拾取任务之后组任务就变成了个人任务(该任务就有负责人)。 网关:
- 排他网关:任务执行之后的分支,经过排他网关分支只有一条有效。
- 并行网关:任务执行后,可以多条分支,多条分支总会汇聚,汇聚完成,并行网关结束。
- 包含网关:是排他网关和并行网关结合体。
第二部分
Activiti 与spring
异常:
1.In order to use comments, history should be enabled
spring:
activiti:
db-history-used: true
history-level: audit