跳至主要內容

Activiti7 笔记

Zenghr大约 12 分钟Activiti7

Activiti7 笔记

一、Activiti7 介绍

Activiti 是一个工作流引擎, activiti 可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言(BPMN2.0)进行定义,业务系统按照预先定义的流程进行执行,实现了业务系统的业务流程由 activiti 进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。

官方网址:https://www.activiti.org/open in new window

文章所使用的的 Activiti7 版本为 7.0.0

二、Activiti7 使用

业务流程建模

  • 使用Activiti流程建模工具(Activity-designer)定义业务流程(.bpmn文件) - IDE 插件(actiBPM 插件)
  • bpmn文件就是业务流程定义文件,通过xml定义业务流程

部署业务流程

  • 向 Activiti 部署业务流程定义(bpmn文件)
  • 使用 Activiti 提供的API向Activiti中部署 bpmn文件

启动流程实例

  • 启动一个流程实例表示开始一次业务流程的运行,流程定义就好比一个模板,流程实例根据模板创建而来
  • 就好比定义一个 java 类,实例化两个对象一样

查询待办任务

  • 通过Activiti 提供的 API 就可以查询当前流程执行到哪里了,当前用户需要办理什么任务了,这些 Activiti 都帮我们管理了

处理待办任务

  • 用户查询待办任务后,就可以办理某个任务,通过 api 完成任务,扭转到下一个节点

结束流程

  • 当任务办理完成没有下一个任务/结点了,这个流程实例就完成了

三、集成 Activiti7 环境

提示

Activiti7 生成的表结构详情请查看 Activiti7 表结构

1. 普通 Maven 项目集成

在项目工程中添加 Activiti7 所需要的一些 Jar 包等,如下:

<properties>
	<slf4j.version>1.6.6</slf4j.version>
	<log4j.version>1.2.12</log4j.version>
</properties>
<dependencies>
	<dependency>
		<groupId>org.activiti</groupId>
		<artifactId>activiti-engine</artifactId>
		<version>7.0.0.Beta1</version>
	</dependency>
	<dependency>
		<groupId>org.activiti</groupId>
		<artifactId>activiti-spring</artifactId>
		<version>7.0.0.Beta1</version>
	</dependency>
	<dependency>
		<groupId>org.activiti</groupId>
		<artifactId>activiti-bpmn-model</artifactId>
		<version>7.0.0.Beta1</version>
	</dependency>
	<dependency>
		<groupId>org.activiti</groupId>
		<artifactId>activiti-bpmn-converter</artifactId>
		<version>7.0.0.Beta1</version>
	</dependency>
	<dependency>
		<groupId>org.activiti</groupId>
		<artifactId>activiti-json-converter</artifactId>
		<version>7.0.0.Beta1</version>
	</dependency>
	<dependency>
		<groupId>org.activiti</groupId>
		<artifactId>activiti-bpmn-layout</artifactId>
		<version>7.0.0.Beta1</version>
	</dependency>
	<dependency>
		<groupId>org.activiti.cloud</groupId>
		<artifactId>activiti-cloud-services-api</artifactId>
		<version>7.0.0.Beta1</version>
	</dependency>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.40</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>org.mybatis</groupId>
		<artifactId>mybatis</artifactId>
		<version>3.4.5</version>
	</dependency>
	<dependency>
		<groupId>commons-dbcp</groupId>
		<artifactId>commons-dbcp</artifactId>
		<version>1.4</version>
	</dependency>
</dependencies>

添加 log4j.properties ,我们使用log4j日志包,可以对日志进行配置

# 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 配置,注意该配置文件名称不能修改

<?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">

    <bean class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration" id="processEngineConfiguration">
        <property name="dataSource" ref="dataSource" />
        <!--
            activiti数据库表处理策略
                false(默认值):检查数据库的版本和依赖库的版本,如果不匹配就抛出异常
                true:构建流程引擎时,执行检查,如果需要就执行更新。如果表不存在,就创建。
                create-drop:构建流程引擎时创建数据库报表,关闭流程引擎时就删除这些表。
                drop-create:先删除表再创建表。
                create:构建流程引擎时创建数据库表,关闭流程引擎时不删除这些表
        -->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql:///act_demo?characterEncoding=utf-8&amp;nullCatalogMeansCurrent=true&amp;serverTimezone=UTC" />

        <property name="username" value="root"/>
        <property name="password" value="admin"/>
    </bean>
</beans>

需要在配置文件中配置如下信息

  • 数据库连接池

  • Activiti单独运行的ProcessEngine配置

测试 运行以下程序段即可完成 activiti 数据库创建

/**
 * 生成Activiti的相关的表结构
 */
@Test
public void test01(){
  // 使用classpath下的activiti.cfg.xml中的配置来创建 ProcessEngine对象
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  System.out.println(engine);
}

2. SpringBoot 集成

添加依赖 注意事项如下:

  • 添加 activiti 和 SpringBoot 整合的依赖 MyBatis 版本会有冲突,所以需要排除
  • Activiti7 默认和 Spring Security 集成了
<!--添加activiti和SpringBoot整合的依赖 MyBatis版本会有冲突,所以需要排除-->
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter</artifactId>
    <version>7.0.0.SR1</version>
    <exclusions>
        <exclusion>
            <artifactId>mybatis</artifactId>
            <groupId>org.mybatis</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!--activiti可以绘制流程的的依赖-->
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-image-generator</artifactId>
    <version>7.0.0.SR1</version>
</dependency>

添加 activiti 配置信息

spring:
  activiti:
    database-schema-update: true
    db-history-used: true
    history-level: full
    check-process-definitions: false
    use-strong-uuids: false

配置文件参数说明:

  • database-schema-update 属性:
    • flase :默认值。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常
    • true : activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
    • create_drop : 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
    • drop-create : 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
  • db-history-used :检测历史表是否存在 activiti7默认没有开启数据库历史记录,true启动数据库历史记录
  • history-level :记录历史等级 可配置的历史级别有none, activity, audit, full
    1. none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。
    2. activity:级别高于none,保存流程实例与流程行为,其他数据不保存。
    3. audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。
    4. full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等
  • check-process-definitions :校验流程文件,默认校验resources下的processes文件夹里的流程文件
  • use-strong-uuids :是否使用UUID作为主键生成策略

排除Spring Security配置

Activiti7 默认和 Spring Security 集成了, 但是有时候我们的项目中并没有使用, 所以我们需要在项目中排除掉 Spring Security 的自动装配配置, 否则我们的登录页会被覆盖

/**
 * 启动程序,运行项目,如果在数据库中出现25张act_开头的表说明集成是没问题的
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
        SecurityAutoConfiguration.class,
        ManagementWebSecurityAutoConfiguration.class})
public class RuoYiApplication {
    public static void main(String[] args) {
        SpringApplication.run(RuoYiApplication.class, args);
    }
}

四、绘制流程图

resource目录下新建bpmn目录用于存放所有的流程文件 , 在bpmn目录下新建leave.bpmn,内容如下:

OSjcWe
OSjcWe

为每个任务结点指定负责人,如部门经理的审核人是李四

xAPWcc
xAPWcc

生成png格式流程图

bpmn文件本质上是xml格式,我们打开看到的是图片格式是因为我们在IDEA中安装了 actiBPM 的插件,如果我们把bpmn文件部署到Web环境,那么就只能看到 xml 信息,无法看到类似上面的图形了

我们可以再部署之前,根据bpmn文件生成png文件,然后把bpmnpng文件同时部署到Web环境中.如果我们想查看流程图的话,我们就可以通过Activiti把这个png文件读取出来. 接下来我们把bpmn导出为png文件

  • 将 leave.bpmn 文件重命名为 leave.xml 文件

    OmlsH0
    OmlsH0
  • 右键点击leave.xml文件 --> Diagrams --> Show BPMN Designer...

    Dsxu1M
    Dsxu1M
  • 点击导出按钮,保存在resource/bpmn同级目录下

    qRnOIP
    qRnOIP
  • 导出png之后,把leave.xml更名为leave.bpmn

    B2qIUS
    B2qIUS

五、流程引擎 Api

在使用 Activiti7 的 Api 之前需要先了解一下 Activiti 中常用的 Service

Service 接口描述
RepositoryServiceactiviti 的资源管理类
RuntimeServiceactiviti 的流程运行管理类
TaskServiceactiviti 的任务管理类
HistoryServiceactiviti 的历史管理类
WJHbfH
WJHbfH
  • RepositoryService,是Activiti的资源管理接口,提供了管理和控制流程发布包和流程定义的操作。使用工作流建模工具设计的业务流程图需要使用此Service将流程定义文件的内容部署到计算机中。
  • RuntimeService,是Activiti的流程运行管理接口,可以从这个接口中获取很多关于流程执行相关的信息。
  • TaskService,是Activiti的任务管理接口,可以从这个接口中获取任务的信息。
  • HistoryService,是Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会包含很多数据(根据配置),比如流程实例启动时间,任务的参与者,完成任务的时间,每个流程实例的执行路径,等等。
  • ManagementService,是Activiti的引擎管理接口,提供了对Activiti流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于Activiti系统的日常维护。

六、Activiti7 Api 使用

部署流程定义

/**
 * 实现文件的单个部署
 */
@Test
public void test01(){
  // 1.获取ProcessEngine对象
  ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
  // 2.获取RepositoryService进行部署操作
  RepositoryService repositoryService = defaultProcessEngine.getRepositoryService();
  // 3.使用RepositoryService进行部署操作
  Deployment deploy = repositoryService.createDeployment()
    .addClasspathResource("bpmn/leave.bpmn")
    .addClasspathResource("bpmn/leave.png")
    .name("请假流程")
    .deploy();

  // 4.输出流程部署的信息
  System.out.println("流程部署的id:" + deploy.getId());
  System.out.println("流程部署的名称:" + deploy.getName());
}

部署zip文件 - 将bpmn文件和png文件两个打包为一个zip文件,统一上传

/**
 * 通过一个zip文件来部署操作
 */
@Test
public void test02(){
  // 定义zip文件的输入流
  InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("bpmn/evection.zip");
  // 对 inputStream 做装饰
  ZipInputStream zipInputStream = new ZipInputStream(inputStream);
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  RepositoryService repositoryService = engine.getRepositoryService();
  Deployment deploy = repositoryService.createDeployment()
    .addZipInputStream(zipInputStream)
    .name("请假流程")
    .deploy();
  // 4.输出流程部署的信息
  System.out.println("流程部署的id:" + deploy.getId());
  System.out.println("流程部署的名称:" + deploy.getName());
}

部署流程文件涉及的表结构:

  • act_ge_bytearray 二进制数据表
  • act_re_deployment 部署信息表
  • act_re_procdef 流程定义数据表

启动流程实例

// 启动一个流程实例
@Test
public void test03(){
  // 1.创建ProcessEngine对象
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // 2.获取RuntimeService对象
  RuntimeService runtimeService = engine.getRuntimeService();
  // 3.根据流程定义的id启动流程
  String id= "leaveProcess";
  ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(id);
  // 4.输出相关的流程实例信息
  System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
  System.out.println("流程实例的ID:" + processInstance.getId());
  System.out.println("当前活动的ID:" + processInstance.getActivityId());
}

如果该流程定义文件中使用 UEL表达式分配 了负责人,则在启动时需要传入对应的参数,如下:

posPxS
posPxS
// 启动一个流程实例,带参数
@Test
public void test03(){
  // 1.创建ProcessEngine对象
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // 2.获取RuntimeService对象
  RuntimeService runtimeService = engine.getRuntimeService();
  // 3.根据流程定义的id启动流程
  String id= "leaveProcess";
  // 设置对应的参数
  Map<String, Object> variables = new HashMap<String, Object>();
  // 设置部门审核人
  variables.put("deptLeader", "张三");
  ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(id, variables);
  // 4.输出相关的流程实例信息
  System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
  System.out.println("流程实例的ID:" + processInstance.getId());
  System.out.println("当前活动的ID:" + processInstance.getActivityId());
}

启动流程实例涉及的表结构:

  • act_ru_execution - 代表正在执行的流程实例表,如果当期正在执行的流程实例结束以后,该行在这张表中就被删除掉了,所以该表也是一个临时表
  • act_ru_task - 代表正在执行的任务表,该表是一个临时表,如果当前任务被完成以后,任务在这张表中就被删除掉了
  • act_hi_actinst - 流程图上出现的每一个元素都称为activity,流程图上正在执行的元素或者已经执行完成的元素称为activity instance
  • act_hi_procinst - 流程历史表
  • act_ru_identitylink - 流程的参与用户信息
  • act_hi_taskinst - 历史任务表

查找用户任务

流程启动后,任务的负责人就可以查询自己当前能够处理的任务了,查询出来的任务都是当前用户的待办任务

/**
 * 任务查询
 */
@Test
public void test06(){
  String assignee ="张三";
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // 任务查询 需要获取一个 TaskService 对象
  TaskService taskService = engine.getTaskService();
  // 根据流程的key和任务负责人 查询任务
  List<Task> list = taskService.createTaskQuery()
    .processDefinitionKey("leaveProcess")
    .taskAssignee(assignee)
    .list();
  // 输出当前用户具有的任务
  for (Task task : list) {
    System.out.println("流程实例id:" + task.getProcessInstanceId());
    System.out.println("任务id:" + task.getId());
    System.out.println("任务负责人:" + task.getAssignee());
    System.out.println("任务名称:" + task.getName());
  }
}

处理任务、以及添加批注

@Test
public void testAddComment(){
    //任务负责人
    String assignee = "张三";
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取TaskService
    TaskService taskService = processEngine.getTaskService();
    //获取任务集合
    List<Task> taskList = taskService.createTaskQuery()
            .processDefinitionKey("leaveProcess")
            .taskAssignee(assignee)
            .list();
    //遍历任务列表
    for(Task task:taskList){
        //在任务执行之前任务添加批注信息: 任务 Id、流程实例 Id、批注信息
        taskService.addComment(task.getId(), task.getProcessInstanceId(), task.getName()+"审批通过");
        taskService.complete(task.getId());
    }
}

查询历史批注信息

@Test
public void testSelectHistoryTask(){
    //流程实例ID
    String processInstanceId = "2501";
    //任务审核人
    String taskAssignee = "张三";
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取historyService
    HistoryService historyService = processEngine.getHistoryService();
    //获取taskService
    TaskService taskService = processEngine.getTaskService();
    //获取历史审核信息
    List<HistoricActivityInstance> list = historyService
            .createHistoricActivityInstanceQuery()
            .activityType("userTask") //只获取用户任务
            .processInstanceId(processInstanceId)
            .taskAssignee(taskAssignee)
            .finished()
            .list();
    for(HistoricActivityInstance instance:list){
        System.out.println("任务名称:"+instance.getActivityName());
        System.out.println("任务开始时间:"+instance.getStartTime());
        System.out.println("任务结束时间:"+instance.getEndTime());
        System.out.println("任务耗时:"+instance.getDurationInMillis());
        //获取审核批注信息
        List<Comment> taskComments = taskService.getTaskComments(instance.getTaskId());
        if(taskComments.size()>0){
            System.out.println("审批批注:"+taskComments.get(0).getFullMessage());
        }
    }
}

涉及的 Api

  • taskService.addComment - 添加批注 Api
  • taskService.complete - 完成任务、自动转到下一个节点