Activiti7概述+基础流程(3.7)
一、工作流介绍
1.概念:
工作流(Workflow),就是通过计算机对业务流程自动化执行管理。它主要解决的是“使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现”。
2.传统工作流的弊端:
-
申请人 --> 部门主管审批 --> 总经理审批 --> 财务审批
-
申请人填写报销单。
-
业务表
id 姓名 部门id 报销金额 报销类别 电子发票单 status(决定当前流程走到哪里)
1 张三 1 1000 洗脚 .... 7
-
突然某个月,总经理临时需要出差学习2个月。就需要有人来临时顶替总经理的审批工作。由人事助理+人事经理共同完成。
-
此时流程需要更改。
申请人 --> 部门主管审批 --> 人事助理 --> 人事经理 --> 财务审批
申请人填写请假单 ==> 将写好的输入插入到数据库中。 status = 0;
若需要查询草稿(未发起申请的报销单) where status = 0
申请人发起审批 ==> 将 status = 1。
部门主管查询本部门中所有发起报销流程的员工。 where status = 1 and 主管所在部门id = 部门id
审批拒绝:将 status 修改为 2; (流程结束)
审批同意:将 status 修改为 3;
// 总经理查询所有员工发起的报销流程。 where status = 3
// 审批拒绝:将 status 修改为 4; (流程结束)
// 审批同意:将 status 修改为 5;
人事助理查询所有员工发起的报销流程。 where status = 5
审批拒绝:将 status 修改为 8; (流程结束)
审批同意:将 status 修改为 9;
人事经理查询所有员工发起的报销流程。 where status = 9
审批拒绝:将 status 修改为 10; (流程结束)
审批同意:将 status 修改为 11;
财务查询所有员工发起的报销流程。 where status = 11
审批拒绝:将 status 修改为 6; (流程结束)
审批同意:将 status 修改为 7;
总结:
- 灵活度缺失:流程变更需更改代码,status 值管理混乱。
- 耦合度高:流程逻辑与业务代码绑定。
二、工作流引擎(工作流框架)

三、Activiti使用步骤
1.整合Activiti
2.业务流程建模
3.部署业务流程 --> 流程定义
4.启动流程实例 --> 基于流程定义发起流程
5.查询待办任务
6.处理待办任务
7.结束流程
四、Activiti的表结构和Service
1.表结构:
- 此时我们查看数据库,发现25张表,结果如下所示:

Activiti的表都是以ACT_开头。第二部分是表示表的用途的两个字母标识。用途也和服务的API对应。
ACT_RE_*:'RE'表示Repository。这个前缀的表包含了流程定义和流程静态资源(图片、规则等等)。ACT_RU_*:'RU'表示Runtime。这些运行时的表,包含流程实例,任务、变量,异步任务等运行中的数据。Activiti只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录。这些运行时表可以一直很小并且速度很快。ACT_HI_*:'HI'表示History。这些表包含历史数据,比如历史流程实例,变量,任务等等。ACT_GE_*:'GE'表示General。通用数据,用于不同场景下。
2.Service总览:
| Service接口 | 说明 |
|---|---|
| RepositoryService(流程定义相关) | Activiti的资源管理接口 |
| RuntimeService(流程实例相关) | Activiti的流程运行管理接口 |
| TaskService(任务相关) | Activiti的任务管理接口 |
| HistoryService(历史相关) | Activiti的历史管理接口 |
| ManagementService | Activiti的引擎管理接口 |
五、Activiti配置(准备工作)
1.Activiti配置:
①关闭项目 ---> 自定义 ---> 所有设置

②编辑器 ---> 常规 ---> 自动导入 ---> 自动导包

③编辑器 ---> 常规 ---> 代码补全 ---> 区分大小写(取消勾选)

④编辑器 ---> 常规 ---> 内联补全 ---> 取消勾选

⑤编辑器 ---> 代码样式 ---> 文件编码 ---> 选择UTF-8 + 不含BOM

⑥构建、执行、部署 ---> 构建工具 ---> Maven ---> 检查自己的Maven和settings.xml

2.创建项目:
①新建普通Java项目,选择Maven。

②安装插件:

③添加依赖:在pom.xml中添加所需依赖。不要忘记更新哦~
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<activiti.version>7.0.0.SR1</activiti.version>
</properties>
<dependencies>
<!-- activiti引擎 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- 整合Spring -->
<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>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.4</version>
</dependency>
</dependencies>
④添加log4j日志配置:在resources下创建log4j.properties。
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=./activiti.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p %30.30c %x - %m\n
⑤ 添加核心配置文件:在resource目录中添加配置文件activiti.cfg.xml。
注意要提前创建好所对应的数据库 ---> activiti_03_demo,检查好用户名和密码以及数据库字符集为utf8。
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 数据源对象 Druid 连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/activiti_03_demo?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8" />
<property name="username" value="root" />
<property name="password" value="1234" />
</bean>
<!-- 创建 流程引擎对象 -->
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 引入数据源对象(上面配置的那个) 前面是属性名 后面是容器中的另一个Bean -->
<property name="dataSource" ref="dataSource"/>
<!--
activiti数据库表处理策略(数据库更新规则)
false(默认值):检查数据库的版本和依赖库的版本,如果不匹配就抛出异常
true:构建流程引擎时,执行检查,如果需要就执行更新。如果表不存在,就创建。
create-drop:构建流程引擎时创建数据库报表,关闭流程引擎时就删除这些表。
drop-create:先删除表再创建表。
create:构建流程引擎时创建数据库表,关闭流程引擎时不删除这些表
-->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
⑥在测试类中测试:
ActivitiTest01测试类:创建流程引擎对象,此时会生成25张表。
public class ActivitiTest01 {
@Test
public void init(){
//创建流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
}
}
小结:
- 什么是 Bean。
- 是 Bean 就一定是对象。是对象不一定是 Bean。
- Bean 就是被 Spring 容器所管理的对象我们称之为 Bean。 与普通对象区分。
- Spring 配置 Bean 的三种方式。
- XML 文件方式:创建 Spring XML 格式配置文件。在里面使用 XML 方式配置。
- 注解方式: 例如 @Controller @Service @Repository @Component。
- JavaConfig 方式配置:我们自己书写一个配置类。 在上面 @Configure
- 在该类中创建方法。返回值就是需要放到容器中的对象类型。使用 return 返回对应对象。在该方法上面贴 @Bean 注解
activiti.cfg.xml就是xml形式配置Bean。
六、Activiti7入门(代码实战)
1.业务流程建模:
resources下新建目录bpmn
bpmn右键 ---> 选择 New Activiti 6.x BPMN 2.0 file ---> 创建名为leaveProcess ---> 空白处右键 选择 View BPMN (Activiti) Diagram ---> 绘制流程图 ---> 生成png格式流程图(在图中右键 save to png),要求文件名与xml文件名一致,一般放在bpmn下,与xml文件同包




2.部署业务流程:
ActivitiTest01测试类:
// 部署 (生成规则 --> 流程定义)
@Test
public void testDeploy(){
// 获取流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取 仓库 RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//调用activiti7 中的 API 完成部署
Deployment deployment = repositoryService.createDeployment() //创建一个部署
.addClasspathResource("bpmn/leaveProcess.bpmn20.xml") //需要部署什么资源(bpmn文件)
.addClasspathResource("bpmn/leaveProcess.png") //需要部署什么资源(png图片)
.deploy();
//部署对象只能获取 部署Id 和 部署时间
System.out.println("部署Id: " + deployment.getId());
System.out.println("部署时间: " + deployment.getDeploymentTime());
System.out.println("部署名称(获取不到): " + deployment.getName());
System.out.println("部署key(获取不到): " + deployment.getKey());
}
更改了哪几张表呢?(两张通用表+流程定义表+部署表)
- act_ge_property ---> Id生成器 生成id
2501 - act_re_procdef --->
- NAME
请假流程我们图中的name - KEY
leaveProcess我们图中的id - ID 他自己的id
leaveProcess:1:4
- NAME
- act_re_deployment --->只有ID和DEPLOY_NAME,没有KEY和NAME,所以查不到
- act_ge_bytearray ---> 存储部署的资源,xml文件和png图片
3.启动流程实例:
// 启动流程实例
@Test
public void startProcess(){
// 获取流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取运行时 RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//调用 Activiti 的 API 启动流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveProcess");
//流程实例可以获取到自己的id 同时也可以获取到对应规则(流程定义的)id 和 key
System.out.println("流程实例Id: " + processInstance.getId());
System.out.println("流程实例Id: " + processInstance.getProcessInstanceId());
System.out.println("流程实例对应的流程定义id: " + processInstance.getProcessDefinitionId());
System.out.println("流程实例对应的流程定义key: " + processInstance.getProcessDefinitionKey());
System.out.println("流程实例对应的部署(获取不到): " + processInstance.getDeploymentId());
}

部署只能通过流程定义获取。
4.查询待办任务:
//查询“我”的待办任务
@Test
public void testQueryTask(){
// 获取流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取任务查询对象 TaskService
TaskService taskService = processEngine.getTaskService();
//我 = 审批人 = 当前登录用户
String userId = "张三";
/*
查询待办任务
所有的查询 createXxxQuery ---> 相当于 select * from act_ru_ task
.processDefinitionKey("leaveProcess") ---> 连表 where processDefinitionKey = "leaveProcess"
.taskAssignee(userId) ---> and Assignee = "张三"
.singleResult() 确定只返回一条结果使用它
.list() 返回多条
.listPage() 返回多条并分页
*/
List<Task> taskList= taskService.createTaskQuery()
.processDefinitionKey("leaveProcess") // 根据Key确定某个流程(请假流程)
.taskAssignee(userId)
.list();
for (Task task : taskList) {
System.out.println("任务id: " + task.getId());
System.out.println("任务名称: " + task.getName());
System.out.println("任务负责人: " + task.getAssignee());
System.out.println("任务创建时间:" + task.getCreateTime());
System.out.println("任务对应的流程实例id: " + task.getProcessInstanceId());
System.out.println("任务对应的流程定义id: " + task.getProcessDefinitionId());
}
}

5.1任务审批:
//任务审批
@Test
public void testCompleteTask(){
//获取流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取 taskService
TaskService taskService = processEngine.getTaskService();
//任务Id
String taskId = "2505";
//调用 activiti7 的 API 来完成审批
taskService.complete(taskId);
}
任务Id ---> act_ru_task表中找ID = 2505 ---> 部门经理审批节点的任务 ID
act_ru_task表中 NAME 由 部门经理审批 改为 人事复批 。
5.2任务审批并添加批注信息:
//添加审批意见 任务完成之前添加 |查看是任务完成之后
@Test
public void testAddCommend(){
//获取流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取 taskService
TaskService taskService = processEngine.getTaskService();
//模拟任务Id 和 流程实例Id (正常是查询之后获得的)
String processInstanceId = "2501";
String taskId = "5002";
//添加审批意见
taskService.addComment(taskId, processInstanceId, "允许休假");
//完成任务
taskService.complete(taskId);
}
- 任务Id --->
act_ru_task表中找ID= 5002 ---> 人事复批节点的任务 ID - 流程实例Id --->
act_ru_task表中找PROC_INST_ID= 2501EXECUTION_ID执行流 ID = 2502,它对应act_ru_execution表中的子执行流,代表这个任务当前所处的执行节点。业务代码中几乎不会直接用到,是 Activiti 内部流转用的标识。
此时,act_ru_task表中就没有正在运行的任务了,那么所添加的批注信息会被加到act_hi_comment表中。
以上的标准流程走完一遍后,就会成为历史,那么接下来我们将查询历史任务和批注信息。
6.查询历史任务和审批记录:
//查询 历史任务 和 任务审批记录
@Test
public void testHistoryTaskQuery() {
//获取流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取 historyService
HistoryService historyService = processEngine.getHistoryService();
//查询审批意见需要 任务 taskService
TaskService taskService = processEngine.getTaskService();
//模拟历史流程实例Id
String processInstanceId = "2501";
//使用 createXxxQuery 进行查询 需要什么条件就添加什么条件
List<HistoricTaskInstance> historyTaskList =
historyService.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId) //根据流程实例Id查询
.finished() //已完成的任务
.list();
//遍历任务集合
for (HistoricTaskInstance historyTask : historyTaskList) {
System.out.println("任务Id: " + historyTask.getId());
System.out.println("任务名称: " + historyTask.getName());
System.out.println("任务负责人: " + historyTask.getAssignee());
System.out.println("任务创建时间: " + historyTask.getCreateTime());
System.out.println("任务开始时间: " + historyTask.getStartTime());
System.out.println("任务结束时间: " + historyTask.getEndTime());
System.out.println("任务总耗时: " + historyTask.getDurationInMillis()/1000/60 + "分"); //毫秒-->秒-->分
System.out.println("任务对应的流程实例Id: " + historyTask.getProcessInstanceId());
System.out.println("任务对应的流程定义Id: " + historyTask.getProcessDefinitionId());
//查询任务审批记录
List<Comment> taskComments = taskService.getTaskComments(historyTask.getId());
//对于集合或数组,我们除了判断 != null,还需要判断 长度 > 0
if (taskComments != null && !taskComments.isEmpty()) {
for (Comment taskComment : taskComments) {
if (taskComment != null) {
System.out.println("批注信息为:" + taskComment.getFullMessage());
}
}
}
System.out.println("--------------");
}
}
小结:
- 查历史任务 --->
historyService - 查审批批注 --->
taskService
taskComments != null && !taskComments.isEmpty()---> 防护整个批注列表为空 / 长度 = 0taskComment != null---> 防护列表里的单个批注对象为空
浙公网安备 33010602011771号