一、概述
官网地址:https://www.activiti.org/
Activiti由Alfresco软件开发,目前最高版本Activiti 7。是BPMN的一个基于java的软件实现,不过Activiti 不仅仅包括BPMN,还有DMN决策表和CMMN Case管理引擎,并且有自己的用户管理、微服务API等一系列功能,是一个服务平台。
二、入门案例
官方手册:http://jeecg.com/activiti5.21/
1.创建springboot项目
现在开发中或者我们自己学习写案例都是通过SpringBoot脚手架工具来快速构建项目的。那么我们也就直接通过创建SpringBoot项目来给大家讲解相关的案例。创建一个普通的SpringBoot项目。指定版本为2.4.2即可。JDK版本得是11
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo01</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.获取ProcessEngine
2.1 默认的方式
在工作流引擎框架中, ProcessEngine 是一个非常核心的对象,我们需要首先解决这个对象的获取。获取方式很多。先来看最简单的一个基于 activiti.cfg.xml 的XML文件的配置方式。
/**
* 获取ProcessEngine对象的第一种方式
*/
@Test
public void test1(){
//通过 getDefaultProcessEngine()方法获取流程引擎对象 会加载resources下的activiti.cfg.xml
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
}
通过 getDefaultProcessEngine
方法加载会默认的从classpath路径下加载 activiti.cfg.xml
配置文件。我们添加该文件。内容如下:
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<!-- id必须是 processEngineConfiguration -->
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti7" />
<property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="root" />
<property name="databaseSchemaUpdate" value="true" />
<property name="asyncExecutorActivate" value="false" />
<property name="mailServerHost" value="mail.my-corp.com" />
<property name="mailServerPort" value="5025" />
</bean>
</beans>
2.2 编程方式获取
上面的配置文件的方式中的配置文件其实是一个Spring的配置文件,但是这并不意味着Activiti只能用于Spring环境。我们也可以通过编程的方式来使用配置文件,从而来构建ProcessEngineConfiguration对象,具体的实现如下:
/**
* 基于Java代码获取ProcessEngine对象
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngineConfiguration
.createStandaloneInMemProcessEngineConfiguration()
.setJdbcUrl("jdbc:mysql://localhost:3306/activiti7")
.setJdbcDriver("com.mysql.cj.jdbc.Driver")
.setJdbcPassword("root")
.setJdbcUsername("root")
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE)
.buildProcessEngine();
System.out.println(engine);
}
上面讲解中的相关属性说明:
databaseSchemaUpdate:用于设置流程引擎启动关闭时使用的数据库表结构控制策略
- false (默认): 当引擎启动时,检查数据库表结构的版本是否匹配库文件版本。版本不匹配时抛出异常。
- true : 构建引擎时,检查并在需要时更新表结构。表结构不存在则会创建。
- create-drop : 引擎创建时创建表结构,并在引擎关闭时删除表结构。
2.3 表结构介绍
在Activiti7中。我们启动服务会自动维护Activiti7需要使用到的相关的表结构。在这块我们需要有个大概的了解。首先是支持的数据库有:
Activiti数据库类型 | 示例JDBC URL | 备注 |
---|---|---|
h2 | jdbc:h2:tcp://localhost/activiti | 默认配置的数据库 |
oracle | jdbc:oracle:thin:@localhost:1521:xe | |
postgres | jdbc:postgresql://localhost:5432/activiti | |
db2 | jdbc:db2://localhost:50000/activiti | – |
Activiti的所有数据库表都以ACT_开头。第二部分是说明表用途的两字符标示符。服务API的命名也大略符合这个规则。
- ACT_RE_*: RE 代表 repository 。带有这个前缀的表包含“静态”信息,例如流程定义与流程资源(图片、规则等)。
- ACT_RU_*: RU 代表 runtime 。这些表存储运行时信息,例如流程实例(process instance)、用户任务(user task)、变量(variable)、作业(job)等。Activiti只在流程实例运行中保存运行时数据,并在流程实例结束时删除记录。这样保证运行时表小和快。
- ACT_ID_*: ID 代表 identity 。这些表包含身份信息,例如用户、组等。
- ACT_HI_*: HI 代表 history 。这些表存储历史数据,例如已完成的流程实例、变量、任务等。
- ACT_GE_*: 通用数据。用于不同场景下
注意:MySQL数据库最好使用5.7及以上的版本
会发现act_ge_property表会有数据
3.在线流程设计器
接下来我们通过官方提供的流程设计器来实现一个简单流程的设计。然后完成相关的部署和流程整体操作。
官网下载地址:https://www.activiti.org/get-started 下载下来后解压缩
进入到wars中。提供的有Activiti-app.war
把这war包拷贝到Tomcat服务器中即可。注意Tomcat的版本不要高于8.5,然后Tomcat服务。访问 http://localhost:8080/activiti-app 即可。登录的账号密码是 admin test
将activiti-app.war放入tomcat的webapps目录下
然后启动tomcat
访问http://localhost:8080/activiti-app,输入用户名admin和密码test,即可登录成功进入
点击“Create Process ”,弹出窗口。录入相关的流程定义信息
录入流程图的名称和key和描述
整体流程图如上图所示
点击“人事审批”的节点,然后下面会有相关属性出现,这里我们点击“Assignments”,然后弹出Assignment的框出现
表示该节点由zhangsan来进行审批。
配置好各自节点的审批人后,进行保存
进行下载xml文件
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef">
<process id="test" name="test" isExecutable="true">
<documentation>test</documentation>
<startEvent id="startEvent1"></startEvent>
<userTask id="sid-69E77A57-E14F-4C79-AD4F-9AD1929406A9" name="人事审批" activiti:assignee="zhangsan">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sid-776DF814-E68F-49CB-90D8-6B98A1801508" sourceRef="startEvent1" targetRef="sid-69E77A57-E14F-4C79-AD4F-9AD1929406A9"></sequenceFlow>
<userTask id="sid-732A12A2-AD4D-4406-8B23-A34BB1F60D1C" name="经理审批" activiti:assignee="lisi">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sid-DE9336AD-7E6D-45F8-B589-6AA3A210E8C0" sourceRef="sid-69E77A57-E14F-4C79-AD4F-9AD1929406A9" targetRef="sid-732A12A2-AD4D-4406-8B23-A34BB1F60D1C"></sequenceFlow>
<endEvent id="sid-6CA4C975-410D-4C1B-B6A6-58894E492622"></endEvent>
<sequenceFlow id="sid-DE19D371-8E2C-4E55-A507-68EF07CAC0B3" sourceRef="sid-732A12A2-AD4D-4406-8B23-A34BB1F60D1C" targetRef="sid-6CA4C975-410D-4C1B-B6A6-58894E492622"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_test">
<bpmndi:BPMNPlane bpmnElement="test" id="BPMNPlane_test">
<bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
<omgdc:Bounds height="30.0" width="30.0" x="100.0" y="163.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-69E77A57-E14F-4C79-AD4F-9AD1929406A9" id="BPMNShape_sid-69E77A57-E14F-4C79-AD4F-9AD1929406A9">
<omgdc:Bounds height="80.0" width="100.0" x="175.0" y="138.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-732A12A2-AD4D-4406-8B23-A34BB1F60D1C" id="BPMNShape_sid-732A12A2-AD4D-4406-8B23-A34BB1F60D1C">
<omgdc:Bounds height="80.0" width="100.0" x="320.0" y="138.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-6CA4C975-410D-4C1B-B6A6-58894E492622" id="BPMNShape_sid-6CA4C975-410D-4C1B-B6A6-58894E492622">
<omgdc:Bounds height="28.0" width="28.0" x="465.0" y="164.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sid-776DF814-E68F-49CB-90D8-6B98A1801508" id="BPMNEdge_sid-776DF814-E68F-49CB-90D8-6B98A1801508">
<omgdi:waypoint x="130.0" y="178.0"></omgdi:waypoint>
<omgdi:waypoint x="175.0" y="178.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-DE9336AD-7E6D-45F8-B589-6AA3A210E8C0" id="BPMNEdge_sid-DE9336AD-7E6D-45F8-B589-6AA3A210E8C0">
<omgdi:waypoint x="275.0" y="178.0"></omgdi:waypoint>
<omgdi:waypoint x="320.0" y="178.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-DE19D371-8E2C-4E55-A507-68EF07CAC0B3" id="BPMNEdge_sid-DE19D371-8E2C-4E55-A507-68EF07CAC0B3">
<omgdi:waypoint x="420.0" y="178.0"></omgdi:waypoint>
<omgdi:waypoint x="465.0" y="178.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
然后我们就可以做流程的部署操作了
4.流程操作
4.1 流程部署
设计好了流程图我们就可以通过如下的代码完成流程的部署。
/**
* 流程部署操作
*/
@Test
public void test3(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/test.bpmn20.xml")
.name("第一个流程")
.deploy();//是一个流程部署的行为 可以部署多个流程定义
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
流程部署的行为会涉及到数据库中的这三张表
- act_ge_bytearray 流程字节表
- act_re_deployment 流程部署表
- act_re_procdef 流程定义表
act_ge_bytearray:流程字节表
act_re_deployment:流程部署表
act_re_procdef 流程定义表
如果在执行一次部署代码,则act_ge_bytearray、act_re_deployment、act_re_procdef三个表又会添加。
然后我们可以通过Activiti提供的相关的API来获取流程部署和流程定义的相关信息
/**
* 查询当前部署的流程有哪些
*/
@Test
public void test4(){
ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = defaultProcessEngine.getRepositoryService();
// 查询有哪些部署的流程-->查询相关的流程定义信息
// repositoryService.createDeploymentQuery() 查询流程部署的相关信息
// repositoryService.createProcessDefinitionQuery() 查询部署的流程的相关的定义
//查询的是act_re_deployment(流程部署表)这张表的数据
List<Deployment> list = repositoryService.createDeploymentQuery().list();
for (Deployment deployment : list) {
System.out.println(deployment.getId());
System.out.println(deployment.getName());
}
//查询的是act_re_procdef(流程定义表)这张表的数据
List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().list();
for (ProcessDefinition processDefinition : processDefinitions) {
System.out.println(processDefinition.getId());
System.out.println(processDefinition.getName());
System.out.println(processDefinition.getDescription());
}
//查询的是act_re_model这张表的数据
List<Model> models = repositoryService.createModelQuery().list();
for (Model model : models) {
System.out.println(model.getId());
}
}
4.2 发起流程
部署流程成功后。我们就可以发起一个流程。发起流程需要通过RuntimeService 来实现。
/**
* 发起一个流程
*/
@Test
public void test5(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("test:1:3");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
发起流程成功后。在对应的 act_ru_task 中就有一条对应的待办记录。
对应的流程状态如下:
在发起流程中,涉及到的表有:
- act_hi_actinst
- act_hi_identitylink
- act_hi_procinst
- act_hi_taskinst
- act_ru_execution
- act_ru_identitylink
- act_ru_task
act_hi_actinst:任务活动节点
act_hi_identitylink:当前负责人
act_hi_procinst:结束的节点
act_hi_taskinst:当前代办流程节点
act_ru_execution:运行流程节点数据
act_ru_identitylink:运行某个流程定义的当前负责人
act_ru_task:待办节点流程
4.3 查询流程
用户登录后要查看待办的任务信息。我们需要通过 TaskService 来实现查询操作。具体代码如下:
/**
* 待办查询
*/
@Test
public void test6(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 待办查询 执行中的任务处理通过 TaskService来实现
TaskService taskService = engine.getTaskService();
// Task 对象对应的其实就是 act_ru_task 这张表的记录
List<Task> list = taskService.createTaskQuery().taskAssignee("zhangsan").list();
if(list != null && list.size() > 0){
for (Task task : list) {
System.out.println(task.getId());//对应act_ru_task表的ID_字段
System.out.println(task.getName());//对应act_ru_task表的NAME_字段
System.out.println(task.getAssignee());//对应act_ru_task表的ASSIGNEE_字段
}
}else{
System.out.println("当前没有待办任务");
}
}
4.4 审批流程
当前登录用户查看到相关的待办信息后。可以做流程的审批处理。
/**
* 任务审批-张三
*/
@Test
public void test7(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 做任务申请 也需要通过 TaskService 来实现
TaskService taskService = engine.getTaskService();
// 完成任务
taskService.complete("2505");//2505拿的是act_ru_task表的ID_字段值
}
会发现act_ru_task
由zhangsan变成lisi了,因为这张表展示的是待办任务的数据
当最后一个流程节点也审批结束后,act_ru_task
表就没有数据了
5.涉及表结构
上面一个审批涉及到的表结构的介绍
6.流程设计器持久化
流程设计器默认是通过 H2 来完成数据的存储的。而 H2 是基于内存来存储的。所以重启服务后数据就丢失了。这时我们可以设置流程设计器的存储方式为MySQL。这样就能持久化的实现存储了。具体步骤如下:
6.1 修改配置信息
在webapps里的activiti-app项目里如上图的位置找到activiti-app.properties文件进行修改
调整数据库的连接信息。记得同时需要创建对应的数据库activiti6ui
jdbc:mysql://localhost:3306/activiti6ui?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&nullCatalogMeansCurrent=true
6.2 加入mysql依赖
在…\apache-tomcat-8.5.91\webapps\activiti-app\WEB-INF\lib目录下计入mysql依赖包
6.3 重新启动tomcat
启动成功后。在数据库中会维护相关的表结构
该操作中需要注意的点:
- 修改配置文件中的信息关键是连接地址的路径:jdbc:mysql://localhost:3306/activiti6ui?
serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&nullCatalogMeansCurrent=true - MySQL的驱动版本不要高于8.0.19,不然会出现LocalDataTime转换的问题
7.流程设计器汉化说明
三、任务分配
1.固定分配
在指派 用户任务
的审批人时。我们是直接指派的固定账号。但是为了保证流程设计审批的灵活性。我们需要各种不同的分配方式,所以这节我们就详细的来介绍先在Activiti7中我们可以使用的相关的分配方式。固定分配就是我们前面介绍的,在绘制流程图或者直接在流程文件中通过Assignee来指定的方式
2. 表达式
Activiti使用UEL进行表达式解析。UEL代表Unified Expression Language ,是 EE6规范的一部分(查看EE6规范了解更多信息)。为了在所有环境上支持UEL标准的所有最新特性,我们使用JUEL的修改版本。
表达式可以用于例如 Java服务任务 Java Service tasks , 执行监听器 Execution Listeners , 任务监听器Task Listeners 与 条件流 Conditional sequence flows。尽管有值表达式与方法表达式两种表达式,通过Activiti的抽象,使它们都可以在需要 expression
(表达式)的地方使用。
${myVar}
${myBean.myProperty}
2.1 值表达式
我们在处理的位置通过UEL表达式来占位。
然后做流程的部署操作:
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/test2.bpmn20.xml")
.name("请假流程-流程变量")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
然后我们发起请假流程:
/**
* 发起一个流程
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 对流程变量做赋值操作
Map<String,Object> map = new HashMap<>();
map.put("assign1","张三");
map.put("assign2","李四");
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("test:2:10003",map);//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
然后我们就可以看到对应的表(act_ru_task)结构中的待办记录
同时需要了解 : act_ru_variable
2.2 方法表达式
方法表达式 Method expression: 调用一个方法,可以带或不带参数。当调用不带参数的方法时,要确保在方法名后添加空括号(以避免与值表达式混淆)。传递的参数可以是字面值(literal value),也可以是表达式,它们会被自动解析。例如:
${printer.print()}
${myBean.getAssignee()}
${myBean.addNewOrder(‘orderName’)}
${myBean.doSomething(myVar, execution)}
myBean是Spring容器中的个Bean对象,表示调用的是bean的addNewOrder方法.我们通过案例来演示下。我们先定义对应的Service
先定义Bean
package com.example.service;
public class MyBean {
public String getAssignee(){
System.out.println("本方法执行了....");
return "波哥";
}
}
然后在Spring的配置文件(activiti.cfg.xml)中注册
<bean name="myBean" class="com.example.service.MyBean"/>
然后在绘制流程图的时候就可以对应的指派了。
然后我们先部署流程
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/test3.bpmn20.xml")
.name("请假流程-方法表达式")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
然后我们发起新的流程。注意在这块我们可以不用添加流程变量信息了。因为 人事审批节点
的审批人是通过流程方法来赋值的
/**
* 发起一个流程
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("test:3:25003");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
可以看到操作成功。方法表达式被执行了
同时待办中的审批人就是方法表达式返回的结果
3.监听器分配
可以使用监听器来完成很多Activiti的流程业务。我们在此处使用监听器来完成负责人的指定,那么我们在流程设计的时候就不需要指定assignee。创建自定义监听器:
package com.example.listener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
public class MyFirstListener implements TaskListener {
/**
* 监听器触发的回调方法
* @param delegateTask
*/
@Override
public void notify(DelegateTask delegateTask) {
System.out.println("---->自定义的监听器执行了");
if(EVENTNAME_CREATE.equals(delegateTask.getEventName())){
// 表示是Task的创建事件被触发了
// 指定当前Task节点的处理人
delegateTask.setAssignee("boge666");
}
}
}
在配置流程的时候关联监听器。注意对应的事件。CREATE。先将Assignment的值取消掉
然后我们部署流程
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/test4.bpmn20.xml")
.name("请假流程-监听器分配")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
启动流程后。
/**
* 发起一个流程
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("test:4:30003");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
待办中的任务的处理人就是监听器中设置的信息
当发起该流程定义,可以看到自定义的监听器触发了
四、流程变量
1.运行时变量
流程实例运行时的变量,存入act_ru_variable
表中。在流程实例运行结束时,此实例的变量在表中删除。在流程实例创建及启动时,可设置流程变量。所有的 startProcessInstanceXXX
方法都有一个可选参数用于设置变量。例如, RuntimeService
中
ProcessInstance startProcessInstanceById(String processDefinitionId, Map<String,Object> variables);
也可以在流程执行中加入变量。例如,( RuntimeService ):
void setVariable(String executionId, String variableName, Object value);
void setVariableLocal(String executionId, String variableName, Object value);
void setVariables(String executionId, Map<String, ? extends Object> variables);
void setVariablesLocal(String executionId, Map<String, ? extends Object>
variables);
读取变量方法:
Map<String, Object> getVariables(String executionId);
Map<String, Object> getVariablesLocal(String executionId);
Map<String, Object> getVariables(String executionId, Collection<String>
variableNames);
Map<String, Object> getVariablesLocal(String executionId, Collection<String>
variableNames);
Object getVariable(String executionId, String variableName);
<T> T getVariable(String executionId, String variableName, Class<T> variableClass);
........
........
........
**注意:**由于流程实例结束时,对应在运行时表的数据跟着被删除。所以查询一个已经完结流程实例的变量,只能在历史变量表中查找。
当然运行时变量我们也可以根据对应的作用域把他分为 全局变量
和 局部变量
.
1.1 全局变量
流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例时,可以称为 global 变量
global 变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。
定义监听器
package com.example.listener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import java.util.Map;
import java.util.Set;
/**
* 自定义的监听器
* 处理相关的变量信息
*/
public class MySecondListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
// 获取所有的流程变量
Map<String, Object> variables = delegateTask.getVariables();
Set<String> keys = variables.keySet();
for (String key : keys) {
Object obj = variables.get(key);
System.out.println(key + " = " + obj);
if(obj instanceof String){
// 修改 流程变量的信息
// variables.put(key,obj + ":boge3306"); 直接修改Map中的数据 达不到修改流程变量的效果
delegateTask.setVariable(key,obj + "-boge3306");
}
}
}
}
package com.example.listener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import java.util.Map;
import java.util.Set;
public class MyThirdListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
// 获取所有的流程变量
Map<String, Object> variables = delegateTask.getVariables();
Set<String> keys = variables.keySet();
for (String key : keys) {
Object obj = variables.get(key);
System.out.println(key + " = " + obj);
if(obj instanceof String){
// 修改 流程变量的信息
// variables.put(key,obj + ":boge3306"); 直接修改Map中的数据 达不到修改流程变量的效果
delegateTask.setVariable(key,obj + "[]");
}
}
}
}
设计流程
人事审批,设置审批人和监听器
经理审批,设置审批人和监听器
然后完成流程的部署操作
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/test5.bpmn20.xml")
.name("请假流程-流程变量")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
查看act_re_procdef表可以看到该部署情况
然后启动流程实例。注意在启动流程实例时我们需要指定相关的流程变量
/**
* 发起一个流程
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 定义流程变量信息
Map<String,Object> map = new HashMap<>();
map.put("assignee1","张三");
map.put("assignee2","李四");
map.put("ass1","变量1");
map.put("ass2",299);
map.put("ass3","湖南长沙");
map.put("ass4","波哥666");
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("test:5:35003",map);//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
启动流程和触发对应的监听器,同时会在 act_ru_variable 中记录当前的变量信息
当然我们也可以通过 RunTimeService 来查询当前对应的流程实例的流程变量信息
/**
* 查询当前的流程变量信息
*/
@Test
public void testVal(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = engine.getRuntimeService();
// 获取流程变量信息 获取某个流程实例的变量信息
Map<String, VariableInstance> variableInstances =
runtimeService.getVariableInstances("37508");//37508值是act_ru_task表中的EXECUTION_ID_字段的值
Set<String> keys = variableInstances.keySet();
for (String key : keys) {
System.out.println(key+"="+variableInstances.get(key));
}
}
然后进行人事审批后,查看act_ru_task表记录
同时执行上述的testVal单元测试方法,发现也是可以正常运行的
同时act_ru_variable表也发生了变化
这个是全局变量,可以在不同的节点都可以获取以及调整,可以看到在人事审批可以获取或修改,在经理审批也可以进行获取或者修改。
1.2 局部变量
任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大, 称为 local 变量。
Local 变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local 变量名也可以和 global 变量名相同,没有影响。
设计流程图
修改全局流程图的人事审批的监听器
package com.example.listener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import java.util.Map;
import java.util.Set;
/**
* 自定义的监听器
* 处理相关的变量信息
*/
public class MyFourthListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
// 获取所有的流程变量
Map<String, Object> variables = delegateTask.getVariables();
Set<String> keys = variables.keySet();
for (String key : keys) {
Object obj = variables.get(key);
System.out.println("---fourth---->"+key + " = " + obj);
if(obj instanceof String){
// 修改 流程变量的信息
// variables.put(key,obj + ":boge3306"); 直接修改Map中的数据 达不到修改流程变量的效果
//delegateTask.setVariable(key,obj + "-boge3306");
}
}
}
}
修改全局流程图的经理审批的监听器
package com.example.listener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import java.util.Map;
import java.util.Set;
public class MyFifthListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
// 获取所有的流程变量
Map<String, Object> variables = delegateTask.getVariables();
Set<String> keys = variables.keySet();
for (String key : keys) {
Object obj = variables.get(key);
System.out.println("---fifth---->"+key + " = " + obj);
if(obj instanceof String){
// 修改 流程变量的信息
// variables.put(key,obj + ":boge3306"); 直接修改Map中的数据 达不到修改流程变量的效果
//delegateTask.setVariable(key,obj + "[]");
}
}
}
}
然后进行部署
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/test6.bpmn20.xml")
.name("请假流程-局部流程变量")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
再然后我们进行流程的发起
/**
* 发起一个流程
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 定义流程变量信息
Map<String,Object> map = new HashMap<>();
map.put("assignee1","张三");
map.put("assignee2","李四");
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("test:6:45003",map);//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
进行局部变量定义
/**
* 设置局部变量
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = engine.getRuntimeService();
runtimeService.setVariableLocal("47504","orderId","o123456");//47504是act_ru_task表的EXECUTION_ID_字段值
runtimeService.setVariableLocal("47504","orderPrice",6666);
}
查看act_ru_variable
表,会发现有我们定义的具备变量
然后进行局部变量的获取
/**
* 读取本地流程变量
*/
@Test
public void test4(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = engine.getRuntimeService();
// 获取流程变量信息 获取某个流程实例的变量信息
Map<String, Object> variablesLocal = runtimeService.getVariablesLocal("47504");//47504值是act_ru_task表中的EXECUTION_ID_字段的值
System.out.println(variablesLocal);
}
还可以通过TaskService
来设置局部变量
/**
* 通过TaskService设置局部变量
*/
@Test
public void test5(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
taskService.setVariableLocal("47507","positionName","总经理");//47507值是act_ru_task表的ID_字段的值
}
通过test4()测试方法会发现无法获取到局部变量名positionName的值,想要获取positionName的值,需要借助TaskService类
/**
* 通过TaskService获取局部变量
*/
@Test
public void test6(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
Map<String, Object> variablesLocal = taskService.getVariablesLocal("47507");
System.out.println(variablesLocal);
}
然后我们进行人事审批
/**
* 任务审批-张三
*/
@Test
public void test11(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 做任务申请 也需要通过 TaskService 来实现
TaskService taskService = engine.getTaskService();
// 完成任务
taskService.complete("47507");//47507拿的是act_ru_task表的ID_字段值
}
然后我们看act_ru_variable
表,会发现positionName
变量没有了
然后通过测试演示我们可以看到通过TaskService绑定的Local变量的作用域只是在当前的Task有效。而通过RuntimeService绑定的Local变量作用的范围是executionId
如下图所示,当任务到并行网关后的任务的执行ID(executionId)就不一样了,但实例ID是一样的。需要注意:executionId和processInstanceId的区别
2.历史变量
历史变量,存入 act_hi_varinst
表中。在流程启动时,流程变量会同时存入历史变量表中;在流程结束时,历史表中的变量仍然存在。可理解为“永久代”的流程变量。
/**
* 流程变量历史信息查询
*/
@Test
public void testHistoryInfo(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
List<HistoricVariableInstance> list = engine.getHistoryService().createHistoricVariableInstanceQuery().list();
System.out.println(list);
}
五、身份服务
在流程定义中在任务结点的 assignee 固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn 文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。针对这种情况可以给任务设置多个候选人或者候选人组,可以从候选人中选择参与者来完成任务。
1.审批人
前面案例中直接指派审批的用户的处理
2.候选人
一个审批节点可能有多个人同时具有审批的权限。这时我们就可以通过候选人来处理。
2.1 绘制流程图
我们定义一个简单的审批流程图。如下:
人事审批中我们设置多个候选人来处理,分别是 张三 , 李四 , 王五。表示这三人中任意一人审批了该任务即该任务完成。
在总经理的位置我们统一设置几个候选人来审批
2.2 部署和启动流程
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/test7.bpmn20.xml")
.name("候选人")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
/**
* 发起一个流程
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("admin:1:57503");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
可以在act_ru_identitylink
表里可以看到当前任务的候选人信息
2.3 任务的拾取
候选要操作我们需要通过 拾取
的行为把 候选人
转换为处理人
.那么候选人登录后需要能查询出来他可以 拾取
的任务。
/**
* 候选人 审批任务查询
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery().
taskCandidateUser("张三") //根据候选人查询审批任务
.list();
if (list!=null && list.size()>0){
System.out.println(list);
} else {
System.out.println("没有任务");
}
}
/**
* 候选人 拾取操作
* 拾取操作就是从候选人-->处理人
*/
@Test
public void test4(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery().
taskCandidateUser("张三") //根据候选人查询审批任务
.list();
if (list!=null && list.size()>0){
for (Task task : list) {
//张三 拾取了 这个任务的审批权限 --> 变成了这个任务的审批人
taskService.claim(task.getId(),"张三");
}
} else {
System.out.println("没有任务");
}
}
发现act_ru_identitylink
里的候选人数据是没有变的,只有act_ru_task
的人事审批的审批人字段从开始空的变成了张三。
当其他候选人如李四进行候选任务查询,会发现没有任务,因为当拾取之后该任务就确认下来了审批人了,则其他候选人则没有候选任务了。
/**
* 候选人 审批任务查询
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery().
taskCandidateUser("李四") //根据候选人查询审批任务
.list();
if (list!=null && list.size()>0){
System.out.println(list);
} else {
System.out.println("没有任务");
}
}
2.4.任务的归还
/**
* 归还:拾取的用户 可能没有时间进行审批,就放弃审批人的操作
* 其他的候选人可以重新拾取任务了
*/
@Test
public void test5(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery()
.taskCandidateOrAssigned("张三")
.list();
if (list!=null && list.size()>0){
for (Task task : list) {
// 规范其实就是将审批人字段设置为空
taskService.unclaim(task.getId());
}
} else {
System.out.println("没有任务");
}
}
再使用test3()测试方法可以看李四或者王五的候选任务是否有,运行发现是有数据的。
3.候选人组
当获选人很多的情况下,我们可以分组来处理,先创建组,然后把用户分配到这个组中。
3.1 绘制流程图
然后在人事审批节点里,添加获选人组
3.2 部署流程
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/test8.bpmn20.xml")
.name("候选人组")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
3.3 发起流程
/**
* 发起一个流程
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("test8:1:3");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
3.4查询代办任务
/**
* 候选人组
* 具体的用户。比如张三登录了系统
* 查询张三对应是什么部门,然后根据部门查询代办的任务
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
String group = "销售部"; //根据当前登录用户查询得来的
List<Task> list = taskService.createTaskQuery()
.taskCandidateGroup(group)
.list();
if (list!=null && list.size()>0){
System.out.println(list);
} else {
System.out.println("没有任务");
}
}
3.4拾取任务
/**
* 候选人组其中一人 拾取操作
* 拾取操作就是从候选人-->处理人
* 一个任务如果被拾取后,其他的候选人就查询不到该任务信息了
*/
@Test
public void test4(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
String group = "销售部"; //根据当前登录用户查询得来的
List<Task> list = taskService.createTaskQuery()
.taskCandidateGroup(group)
.list();
if (list!=null && list.size()>0){
for (Task task : list) {
//张三 拾取了 这个任务的审批权限 --> 变成了这个任务的审批人
taskService.claim(task.getId(),"张三");
}
} else {
System.out.println("没有任务");
}
}
然后调用test3()进行查询,发现该获选人组没有代办任务了。
3.5交接任务
/**
* 获取用户审批权限的用户没有时间审批了
* 但是他也可以不用归还而是做任务的交接。把这个任务让另外一个人来审批
*/
@Test
public void test6(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery()
.taskAssignee("张三")
.list();
if (list!=null && list.size()>0){
for (Task task : list) {
// 任务交接
taskService.setAssignee(task.getId(),"李四");
}
} else {
System.out.println("没有任务");
}
}
3.6 归还任务
/**
* 归还:拾取的用户 可能没有时间进行审批,就放弃审批人的操作
* 其他的候选人可以重新拾取任务了
*/
@Test
public void test5(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery()
.taskAssignee("李四")
.list();
if (list!=null && list.size()>0){
for (Task task : list) {
// 规范其实就是将审批人字段设置为空
taskService.unclaim(task.getId());
}
} else {
System.out.println("没有任务");
}
}
六、网关篇
6.1 排他网关
6.1.1 绘制流程图
在创建请假单
流程节点进行审批人设置
在部门经理审批
流程节点进行审批人设置
在总经理审批
流程节点进行审批人设置
在人事审批
流程节点进行审批人设置
进行线条的排他网关的条件设置
表示请假天数如果<3则走部门经理审批,>=3则走总经理审批。
6.1.2 部署流程
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/gateway1.bpmn20.xml")
.name("排他网关")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
6.1.2 发起流程
/**
* 发起一个流程
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("gateway:1:2503");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
6.1.3 创建请假单审批
/**
* 创建请假单 流程审批 张三
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery().taskAssignee("zhangsan").list();
Map<String,Object> map = new HashMap<>();
//绑定对应的请假天数
map.put("days",6);
for (Task task : list) {
taskService.complete(task.getId(),map);
}
}
6.2 并行网关
6.2.1 绘制流程图
分别给创建请假单
、技术经理审批
、项目经理审批
、人事经理审批
、总经理审批
设置审批人分别为zhangsan,lisi,wangwu,zhaoliu,boos
6.2.2 流程部署
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/gateway2.bpmn20.xml")
.name("并行网关")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
6.2.3 发起流程
/**
* 发起一个流程
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("gateway2:1:3");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
6.2.4 流程审批
/**
* 创建请假单 流程审批 张三
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery().taskAssignee("zhangsan").list();
for (Task task : list) {
taskService.complete(task.getId());
}
}
在act_ru_execution
执行流程表中,存在一条PARENT_ID_为null的主执行流程实例,其余为PARENT_ID_为主执行流程实例ID为2501的子执行流程实例,分别表示是技术经理审批
、项目经理审批
、人事经理审批
。
然后技术经理李四进行审批,会发现act_ru_task
表中没有了lisi的代办审批任务
会发现主执行流程随着审批任务运行一次,REV_字段主执行流程会累加,第一条子执行流程也是会累加。
然后项目经理王五进行审批,会发现act_ru_task
表中没有了wangwu的代办审批任务
会发现主执行流程随着审批任务运行一次,REV_字段主执行流程会累加,第二条子执行流程也是会累加为2了。
然后人事经理赵六进行审批,会发现act_ru_task
表中没有了zhaoliu的代办审批任务
然后总经理boos进行审批,会发现act_ru_task
表中没有了boos的代办审批任务
6.3 包容网关
6.3.1 绘制流程图
说明:人事审批是不过请假天数如何是一定会走的。
分别给创建请假单
、项目经理
、人事审批
、技术总监
、总经理审批
设置审批人分别为zhangsan,lisi,wangwu,zhaoliu,boos
6.3.2 部署流程
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/gateway3.bpmn20.xml")
.name("并行网关")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
6.3.3 发起流程
/**
* 发起一个流程
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("gateway3:1:3");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
6.3.4 流程审批
/**
* 创建请假单 流程审批 张三
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
Map<String,Object> map = new HashMap<>();
map.put("days",3);
List<Task> list = taskService.createTaskQuery().taskAssignee("zhangsan").list();
for (Task task : list) {
taskService.complete(task.getId(),map);
}
}
然后技术总监zhaoliu进行审批
然后人事wangwu审批
然后总经理boss审批,两个表都空空如也。
七、事件篇
7.1 定时器事件
7.1.1 定时器开始事件
7.1.1.1 绘制流程图
注意的点:需要在activiti.cfg.xml
表中加入
<property name="asyncExecutorActivate" value="true" />
7.1.1.2 流程部署
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event01.bpmn20.xml")
.name("定时器开始事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
会发现没到2023年8月19日的19时35分钟后几秒,act_ru_timer_job
表会有一条记录
当时间到了2023年8月19日的19时35分钟后几秒,act_ru_timer_job
表没有任何数据了
同时也会发现act_ru_task
有了一条用户任务的任务记录
使用场景:如每个月1号进行一些审核任务,如月度绩效考核任务填写。
其中R3/PT30S
表示每隔30秒,执行三次。
package com.example.delegate;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import java.time.LocalDateTime;
/**
* 自定义委托类
*/
public class MyFirstDelegate implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("服务任务执行了......" + LocalDateTime.now().toString());
}
}
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event02.bpmn20.xml")
.name("定时器开始事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
从控制台搜索服务任务执行了
,会发现有三条打印日志,且事件相差30秒
7.1.2 定时器中间事件
7.1.2.1 绘制流程图
PT1M
表示申请出库审批通过后等待1分钟后定时器触发然后进入出库处理流程
7.1.2.2 部署流程
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event03.bpmn20.xml")
.name("定时器中间事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
7.1.2.3 发起流程
/**
* 发起一个流程
*/
@Test
public void test2(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("event03:1:3");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
7.1.2.4 审批流程
/**
* 流程审批 张三
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery().taskAssignee("zhangsan").list();
for (Task task : list) {
taskService.complete(task.getId());
}
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
执行test3()测试方法,可以看到act_ru_timer_job
表有一条数据
当执行1分钟之后,会发现,原本空空如也的act_ru_task
出现了出库处理的任务,同时act_ru_timer_job
表里的数据被清空了。
7.1.3 定时器边界事件
7.1.3.1 绘制流程图
PT1M
表示“合同审批-总经理审批”如果一分钟之后没有处理,则定时器边界事件会被触发,从而会执行下图的服务任务。
Time date in ISO-8601为这个节点的特定时间
Time End Date in ISO-8601结束时间
Time cycle:循环。也就是总经理审批这个节点两天没有审批,那么我每隔半小时给他发送个消息
Time duration:持续。表示这个节点多久没去审批,我可以给他做个处理。
Cancel activity为勾选(true)表示这个整个流程发生表示中断的,也就是财务审批不再进行了。为非勾选,则表示这个流程还是继续做流转的和审批的,即财务审批还是继续执行的。
需要 注意将Boundary timer event要放在用户任务的边上
7.1.3.2 流程部署
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event04.bpmn20.xml")
.name("定时器边界事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
7.1.3.3 流程发起
部署后启动流程。那么会进入到合同审批-总经理审批
的这个节点,同时在act_ru_timer_job
中可以看到这个边界事件的定义
等待了一分钟定时器边界事件触发。我们可以在控制台中看到MySecondDelegate
任务的执行
因为这块的边界事件我们定义的是非中断
,所以用户任务还在。即使定时器边界事件也结束。只是在边界事件中触发了服务任务来通过用户审批处理
7.1.3.4 流程审批
/**
* 流程审批 张三
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery().taskAssignee("zhangsan").list();
for (Task task : list) {
taskService.complete(task.getId());
}
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
总经理审批通过后,会进入到财务审批的节点
同时会开启我们的中间边界事件。act_ru_timer_job
中会生成对应的记录
等待一分钟之后。因为边界事件设置的是中断
类型。所以触发后财务审核
终止。只剩下出发后的新的出口中的财务实习审批
7.2.消息事件
消息事件(message event),是指引用具名消息的事件。消息具有名字与载荷。与信号不同,消息事件只有一个接受者
7.2.1 开始事件
消息开始事件
,也就是我们通过接收到某些消息后来启动流程实例,比如我们接收到了一封邮件,一条短信等,具体通过案例来讲解。
7.2.1.1 绘制流程图
做消息的定义
在消息开始事件中我们需要绑定上面定义的消息
7.2.1.2 流程部署
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event05.bpmn20.xml")
.name("消息启动事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
部署完流程后,消息启动事件会在act_ru_event_subscr
表中记录我们的定义事件
7.2.1.3 流程发起
/**
* 发送消息,触发流程
*/
@Test
public void test2() throws InterruptedException {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = engine.getRuntimeService();
//发送消息
runtimeService.startProcessInstanceByMessage("msg01");//值为act_ru_event_subscr表中的EVENT_NAME_字段值
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}
注意:消息的名称我们不要使用驼峰命名法
来定义
这时候可以看到消息启动事件之后的任务
7.2.2 中间事件
消息中间事件就是在流程运作中需要消息来触发的场景,案例演示,用户任务1
处理完成后,需要接收特定的消息之后才能进入到用户任务2
节点。
7.2.2.1 绘制流程图
做消息定义
然后在消息中间事件的图标中我们需要绑定刚刚定义的消息msg02
7.2.2.2 流程部署
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event06.bpmn20.xml")
.name("消息启动事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
7.2.2.3 流程发起
/**
* 发起一个流程
*/
@Test
public void test2() throws InterruptedException {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("event06:1:3");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
7.2.2.4 流程审核
/**
* 流程审批 张三
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery().taskAssignee("zhangsan").list();
for (Task task : list) {
taskService.complete(task.getId());
}
}
然后发现act_ru_task
表中没有了任务
同时在act_ru_event_subscr
表中有一条msg02
的数据
部署启动和审批流程后进入到消息中间事件的节点,然后发送消息触发事件,则执行7.2.2.5
7.2.2.5 触发消息中间事件
/**
* 触发消息中间事件-第一种方式
*/
@Test
public void test41(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = engine.getRuntimeService();
// 查询当前的 执行实例的编号
Execution execution = runtimeService.createExecutionQuery()
.processInstanceId("2501")
.onlyChildExecutions()
.singleResult();//值为act_ru_suspended_job表中的PROC_INST_ID_字段的值
runtimeService.messageEventReceived("msg02", execution.getId());
}
/**
* 触发消息中间事件-第二种方式
*/
@Test
public void test42(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = engine.getRuntimeService();
// 查询当前的 执行实例的编号
Execution execution = runtimeService.createExecutionQuery()
.processInstanceId("2501")
.messageEventSubscriptionName("msg02")
.singleResult();//值为act_ru_event_subscr表中的PROC_INST_ID_字段的值
runtimeService.messageEventReceived("msg02", execution.getId());
}
建议使用第二种方式
然后进入到了用户任务2
的审批任务节点,说明消息中间事件被触发了。
7.2.3 边界事件
消息边界事件同样的针对是用户节点在消息触发前如果还没有审批,就会触发消息事件的处理逻辑。同样我们通过具体的案例来介绍
7.2.3.1 绘制流程图
package com.example.delegate;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import java.time.LocalDateTime;
/**
* 自定义委托类
*/
public class MyThirdDelegate implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("消息边界的服务任务执行了......" + LocalDateTime.now().toString());
}
}
7.2.3.2 流程部署
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event07.bpmn20.xml")
.name("消息边界事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
7.2.3.3 流程发起
/**
* 发起一个流程
*/
@Test
public void test2() throws InterruptedException {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("event07:1:3");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
7.2.3.4 触发消息边界事件
/**
* 触发消息边界事件
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = engine.getRuntimeService();
runtimeService.messageEventReceived("msg03","2505");//第二个值为:act_ru_event_subscr表中的EXECUTION_ID_字段值
}
然后在控制台就可以看到JavaDelegate
被执行调用了
这里我们需要注意当前的边界事件是非中断的。所以还是需要用户任务1
来审批
7.2.3.5 任务审核
/**
* 流程审批 张三
*/
@Test
public void test4(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
List<Task> list = taskService.createTaskQuery().taskAssignee("zhangsan").list();
for (Task task : list) {
taskService.complete(task.getId());
}
}
7.2.3.6 触发消息边界事件
/**
* 触发消息边界事件
*/
@Test
public void test5(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = engine.getRuntimeService();
runtimeService.messageEventReceived("msg04","7502");//第二个值为:act_ru_event_subscr表中的EXECUTION_ID_字段值
}
当我们触发了第二个消息边界事件,那么任务会进入到用户任务3
,同时用户任务2
被中断了,然后msg04
的任务也结束了。
7.3 错误事件
7.3.1 开始事件
7.3.1.1 绘制流程图
package com.example.delegate;
import org.activiti.engine.delegate.BpmnError;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import java.time.LocalDateTime;
/**
* 自定义委托类
*/
public class MyErrorDelegate1 implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("错误开始事件的任务服务执行了1111......" + LocalDateTime.now().toString());
// 显示的抛出error1 的异常信息 触发 错误开始事件
throw new BpmnError("error1");
}
}
package com.example.delegate;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import java.time.LocalDateTime;
/**
* 自定义委托类
*/
public class MyErrorDelegate2 implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("错误开始事件的任务服务执行了2222......" + LocalDateTime.now().toString());
}
}
7.3.1.2 部署流程
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event08.bpmn20.xml")
.name("错误开始事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
7.3.1.3 发起流程
/**
* 发起一个流程
*/
@Test
public void test2() throws InterruptedException {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("event08:1:3");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
7.3.2 边界事件
7.3.2.1 绘制流程图
自动任务1
、自动任务2
、自动任务3
、自动任务4
、自动任务5
的class属性分别对应MyBoundaryErrorDelegate1
、MyBoundaryErrorDelegate2
、MyBoundaryErrorDelegate3
、MyBoundaryErrorDelegate4
、MyBoundaryErrorDelegate5
的全限定名
package com.example.delegate;
import org.activiti.engine.delegate.BpmnError;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import java.time.LocalDateTime;
/**
* 自定义委托类
*/
public class MyBoundaryErrorDelegate1 implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("错误边界事件的任务服务执行了1111......" + LocalDateTime.now().toString());
// 显示的抛出error2 的异常信息 触发 错误开始事件
throw new BpmnError("error2");
}
}
package com.example.delegate;
import org.activiti.engine.delegate.BpmnError;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import java.time.LocalDateTime;
/**
* 自定义委托类
*/
public class MyBoundaryErrorDelegate2 implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("错误边界事件的任务服务执行了2222......" + LocalDateTime.now().toString());
// 显示的抛出error3 的异常信息 触发 错误开始事件
throw new BpmnError("error3");
}
}
package com.example.delegate;
import org.activiti.engine.delegate.BpmnError;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import java.time.LocalDateTime;
/**
* 自定义委托类
*/
public class MyBoundaryErrorDelegate3 implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("错误边界事件的任务服务执行了3333......" + LocalDateTime.now().toString());
}
}
package com.example.delegate;
import org.activiti.engine.delegate.BpmnError;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import java.time.LocalDateTime;
/**
* 自定义委托类
*/
public class MyBoundaryErrorDelegate4 implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("错误边界事件的任务服务执行了4444......" + LocalDateTime.now().toString());
}
}
package com.example.delegate;
import org.activiti.engine.delegate.BpmnError;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import java.time.LocalDateTime;
/**
* 自定义委托类
*/
public class MyBoundaryErrorDelegate5 implements JavaDelegate {
/**
* 回调方法
* @param execution
*/
@Override
public void execute(DelegateExecution execution) {
System.out.println("错误边界事件的任务服务执行了5555......" + LocalDateTime.now().toString());
}
}
7.3.2.2 部署流程
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event09.bpmn20.xml")
.name("错误边界事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
7.3.2.3 发起流程
/**
* 发起一个流程
*/
@Test
public void test2() throws InterruptedException {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("event09:1:2503");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
当将MyBoundaryErrorDelegate1
类注释掉throw new BpmnError("error2");
在发起一次流程,搜索日志会发现走了1,2,5
当将MyBoundaryErrorDelegate2
类注释掉throw new BpmnError("error3");
在发起一次流程,搜索日志会发现走了1,2,3
7.3.3 结束事件
7.3.3.1 绘制流程图
当子流程中支付失败的情况下会触发错误结束事件,该事件会被错误边界事件捕获,错误边界事件捕获后会重新发起支付的流程。这就是我们介绍的案例流程
7.3.3.2 流程部署
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event10.bpmn20.xml")
.name("错误结束事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
7.3.3.3 发起流程
/**
* 发起一个流程
*/
@Test
public void test2() throws InterruptedException {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("event10:1:3");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
7.3.3.4 审批流程
/**
* 任务审批-张三
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 做任务申请 也需要通过 TaskService 来实现
TaskService taskService = engine.getTaskService();
// 完成任务
taskService.complete("2505");//2505拿的是act_ru_task表的ID_字段值
}
/**
* 任务审批-李四
*/
@Test
public void test4(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 做任务申请 也需要通过 TaskService 来实现
TaskService taskService = engine.getTaskService();
Map<String,Object> map = new HashMap<>();
map.put("payFlag",false);
// 完成任务
taskService.complete("5007",map);//5007拿的是act_ru_task表的ID_字段值
}
会发现ID是发生了变化的,流程的名称还是支付
/**
* 任务审批-李四
*/
@Test
public void test4(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 做任务申请 也需要通过 TaskService 来实现
TaskService taskService = engine.getTaskService();
Map<String,Object> map = new HashMap<>();
map.put("payFlag",true);
// 完成任务
taskService.complete("7511",map);//5007拿的是act_ru_task表的ID_字段值
}
在进行重新支付后,支付成功后,就会流转到“发货”的流程
7.4 信息事件
7.4.1 开始事件
7.4.1.1 绘制流程图
7.4.1.2 部署流程
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event11.bpmn20.xml")
.name("信息启动事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
7.4.1.3 发起流程
/**
* 通过信息启动事件
* 发起一个流程
* 1.通过runtimeService中提供的API来发送信号
* 2.通过其他流程实例中的信号中间抛出事件来触发
* 3.作为普通的流程实例来启动即可
*/
@Test
public void test21() throws InterruptedException {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过runtimeService中提供的API来发布信号
runtimeService.signalEventReceived("signal01");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}
/**
* 发起一个流程
*/
@Test
public void test22() throws InterruptedException {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("event11:1:3");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
7.4.2 中间事件
7.4.2.1 绘制流程图
7.4.2.2 部署流程
/**
* 流程部署操作
*/
@Test
public void test1(){
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.完成流程的部署操作 需要通过RepositoryService来完成
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("flow/event12.bpmn20.xml")
.name("信息中间事件")
.deploy();//是一个流程部署的行为 可以部署多个流程定义(act_re_procdef表)
System.out.println(deploy.getId());
System.out.println(deploy.getName());
}
7.4.2.3 发起流程
/**
* 发起一个流程
*/
@Test
public void test2() throws InterruptedException {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 发起流程 需要通过 runtimeService 来实现
RuntimeService runtimeService = engine.getRuntimeService();
// 通过流程定义来启动流程 返回的是流程实例对象
//这里流程实例和流程定义 有点像实例和类的关系
ProcessInstance processInstance = runtimeService.startProcessInstanceById("event12:1:5003");//值是流程定义表act_re_procdef的ID_字段值
System.out.println(processInstance.getId());
System.out.println(processInstance.getDeploymentId());
System.out.println(processInstance.getDescription());
}
7.4.2.4 审核流程
/**
* 任务审批-张三
*/
@Test
public void test3(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 做任务申请 也需要通过 TaskService 来实现
TaskService taskService = engine.getTaskService();
// 完成任务
taskService.complete("7509");//2505拿的是act_ru_task表的ID_字段值
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
发现他触发了同样信号为signal01的开始事件,有了如上红框里的任务。