一、工作流介绍

Activiti7是开源的工作流引擎

工作流(Workflow)就是工作流程的计算模型,即将工作流程中的工作如何前后组织在一起的逻辑和规则在计算机中以恰当的模型进行表示并对其实施计算。工作流要解决的主要问题是:为实现某个业务目标,在多个参与者之间,利用计算机,按某种预定规则自动传递文档、信息或者任务。

二、安装插件

插件名字

actiBPM

离线安装

IDEA新版本需要到插件市场下载并离线安装。

image-20210515011816100

重启IDEA

·

三、BPMN介绍

所有符号

image-20210516005720342

完整的工作流由StartEvent开始,由EndEvent结束,中间穿插着各种Task任务,Gateway负责构建复杂的流程(如请假审批,请假1天的,和请假3天的流程是不一样的),SubProcess子流程,Annotation注解。

四、Activiti使用步骤

  1. 部署Activiti,整合Activiti依赖
  2. 定义流程,BPMN建模
  3. 部署流程,把流程内容存储起来
  4. 启动流程实例,开始一次业务流程运作
  5. 用户查询代办任务,activiti查询流程到哪个步骤
  6. 用户办理业务,查询到自己的待办任务后就可以办理某个业务了
  7. 结束流程,没有下一个任务节点流程就完成了

五、创建数据库表

创建数据库

image-20210516013653719

创建项目

image-20210516012943714

导入依赖

<properties>
    <activiti.version>7.0.0.Beta3</activiti.version>
</properties>
<!-- 工作流 -->
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-engine</artifactId>
    <version>${activiti.version}</version>
    <!-- 解决与mybatis冲突问题 -->
    <exclusions>
        <exclusion>
            <artifactId>mybatis</artifactId>
            <groupId>org.mybatis</groupId>
        </exclusion>
    </exclusions>
</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数据转换 -->
<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>

创建配置文件

image-20210516022824881

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 id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl"
                  value="jdbc:mysql://**********/activiti-service-dev?useUnicode=true&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="**********"/>
    </bean>

    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <!-- 使用上面的数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!--是否生成表结构,初始化为true,生成后为false-->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
</beans>

生成表结构

package com.qiang.activiti;

import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@Slf4j
class ActivitiServiceApplicationTests {

    /**
     * 生成表结构
     */
    @Test
    public void testCreateTable() {
        ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
        log.info("testCreateTable{}", defaultProcessEngine);
    }

}

启动程序

image-20210516023436344

运行结果

生成25张表。

image-20210516023535518

六、流程引擎主要方法

image-20210516192412132

七、BPMN流程建模

新建文件

image-20210515012308769

新建一个请假流程

image-20210516193216252

整体流程配置,设置流程ID为leave,名字为员工请假审批流程

image-20210516224845863

配置提交请假申请的责任人为worker

image-20210516224945578

配置部门经理审批的责任人为manager

image-20210516225108056

配置财务主管审批的责任人为financer

image-20210516225214808

八、导出流程图

将BPMN文件复制一份为XML文件

image-20210516225827950

选择BPMN设计器

image-20210516225936618

查看流程图

image-20210516231630399

导出流程图

image-20210516232005294

九、部署流程

9.1 单个流程部署

/**
 * 部署流程(单个流程)
 */
@Test
public void testDeployment() {
    // 创建 processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 获取 repositoryService 实例
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 使用 repositoryService 进行部署
    Deployment deployment = repositoryService.createDeployment()
            // 对应的 bpmn 文件
            .addClasspathResource("bpmn/leave.bpmn")
            // 对应的流程图文件,命名规范(流程ID.png)为 leave.png/jpg/gif/svg
            .addClasspathResource("bpmn/image/leave.png")
            .name("请假申请流程")
            .deploy();
    // 获取部署后的信息
    log.info("部署的流程ID:{}", deployment.getId());
    log.info("部署流程名称:{}", deployment.getName());
}

启动实例

image-20210516233723611

数据已经存到数据库

image-20210516233816025

包括XML文件以及图片文件

image-20210516233917605

9.2 批量流程部署

将bpmn文件以及流程图文件按规定名字打成压缩包

image-20210516235731896

文件位置

image-20210517000607570

实例代码

/**
 * 批量部署流程
 */
@Test
public void testDeploymentByZip() {
    InputStream inputStream = this
            .getClass()
            .getClassLoader().getResourceAsStream("bpmn/zip/test.zip");
    assert inputStream != null;
    ZipInputStream zipInputStream = new ZipInputStream(inputStream);
    // 创建 processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 获取 repositoryService 实例
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 使用 repositoryService 进行部署
    Deployment deployment = repositoryService.createDeployment()
            .addZipInputStream(zipInputStream)
            .deploy();
    // 获取部署后的信息
    log.info("部署的流程ID:{}", deployment.getId());
    log.info("部署流程名称:{}", deployment.getName());
}

启动实例

image-20210517000231886

数据已经存到数据库

image-20210517000326217

包括XML文件以及图片文件

image-20210517000432476

十、启动流程实例

根据流程 ID 启动流程

/**
 * 启动流程实例
 */
@Test
public void testStartProcess() {
    // 创建 processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 获取 runtimeService 实例
    RuntimeService runtimeService = processEngine.getRuntimeService();
    // 根据流程 ID 启动流程
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave");
    // 获取启动流程后的信息
    log.info("流程定义ID:{}", processInstance.getProcessDefinitionId());
    log.info("流程实例ID:{}", processInstance.getId());
    log.info("当前活动ID:{}", processInstance.getActivityId());
}

执行结果

image-20210517001447958

十一、查询待办任务

启动流程实例后,该节点到达了提交请假申请的节点,需要任务负责人查询当前负责的待办任务(当前负责人为worker要提交请假申请)。

/**
 * 查询当前负责人的待办任务
 */
@Test
public void testFindPersonalTaskList() {
    // 当前负责人
    String assignee = "worker";
    // 创建 processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 创建 taskService
    TaskService taskService = processEngine.getTaskService();
    // 根据流程 Key 和任务负责人查询代办任务
    List<Task> tasks = taskService.createTaskQuery()
            // 流程 Key
            .processDefinitionKey("leave")
            // 负责人
            .taskAssignee(assignee)
            .list();
    tasks.forEach(task -> {
        log.info("流程实例ID:{}", task.getProcessInstanceId());
        log.info("流程任务ID:{}", task.getId());
        log.info("流程任务负责人:{}", task.getAssignee());
        log.info("流程任务名称:{}", task.getName());
    });
}

启动实例

image-20210517002817261

十二、完成任务

当前任务节点到了worker审批,根据查询到的任务ID完成任务。

/**
 * 完成任务
 */
@Test
public void testCompleteTask() {
    // 当前负责人
    String assignee = "worker";
    // 创建 processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 创建 taskService
    TaskService taskService = processEngine.getTaskService();
    Task task = taskService.createTaskQuery()
            .processDefinitionKey("leave")
            // 负责人
            .taskAssignee(assignee)
            // 返回一个任务
            .singleResult();
    // 根据任务ID完成任务
    taskService.complete(task.getId());
}

启动实例

image-20210520220948735

完成任务后,再次查询worker的代办任务已经没有了,任务已经流转到了部门经理审批了,此时可以查询到manager的待办任务了。

/**
 * 查询当前负责人的待办任务
 */
@Test
public void testFindPersonalTaskList() {
    // 当前负责人
    String assignee = "manager";
    // 创建 processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 创建 taskService
    TaskService taskService = processEngine.getTaskService();
    // 根据流程 Key 和任务负责人查询代办任务
    List<Task> tasks = taskService.createTaskQuery()
            // 流程 Key
            .processDefinitionKey("leave")
            // 负责人
            .taskAssignee(assignee)
            .list();
    tasks.forEach(task -> {
        log.info("流程实例ID:{}", task.getProcessInstanceId());
        log.info("流程任务ID:{}", task.getId());
        log.info("流程任务负责人:{}", task.getAssignee());
        log.info("流程任务名称:{}", task.getName());
    });
}

启动实例

image-20210520221202910

十三、查询当前流程定义

查询当前流程定义下有哪些实例正在跑。

/**
 * 查询当前流程定义
 */
@Test
public void testQueryProcessInstance(){
    // 流程定义Key
    String processDefinitionKey = "leave";
    // 创建 processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 获取 runtimeService 实例
    RuntimeService runtimeService = processEngine.getRuntimeService();
    List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
            .processDefinitionKey(processDefinitionKey)
            .list();
    processInstances.forEach(processInstance -> {
        log.info("===========================================");
        log.info("流程实例ID:{}", processInstance.getProcessInstanceId());
        log.info("所属流程定义ID:{}", processInstance.getProcessDefinitionId());
        log.info("是否执行完成:{}", processInstance.isEnded());
        log.info("是否暂停流程:{}", processInstance.isSuspended());
        log.info("当前活动标识:{}", processInstance.getActivityId());
        log.info("业务关键字:{}", processInstance.getBusinessKey());
    });
}

启动实例

image-20210520224118283

这里两个流程

image-20210520224408873

十四、查询所有流程定义

查询在一个BPMN文件中定义了哪些流程。

/**
 * 查询所有流程定义
 */
@Test
public void testQueryProcessDefinition() {
    // 创建 processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 获取 repositoryService 实例
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 获取 processDefinitionQuery 实例
    ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
    List<ProcessDefinition> processDefinitions = processDefinitionQuery.processDefinitionKey("leave")
            // 按照版本排序
            .orderByProcessDefinitionVersion()
            // 倒序
            .desc()
            // 返回集合
            .list();
    processDefinitions.forEach(processDefinition -> {
        log.info("===========================================");
        log.info("流程定义ID:{}", processDefinition.getId());
        log.info("流程定义NAME:{}", processDefinition.getName());
        log.info("流程定义KEY:{}", processDefinition.getKey());
        log.info("流程定义Version:{}", processDefinition.getVersion());
        log.info("流程部署ID:{}", processDefinition.getDeploymentId());
    });
}

启动实例

image-20210520222645736

十五、删除流程

/**
 * 删掉流程
 */
@Test
public void testDeleteDeployment() {
    // 流程部署Id
    String deploymentId = "1";
    // 创建 processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 获取 repositoryService 实例
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 根据Id删除,如果该流程有实例正在启动则报错
    repositoryService.deleteDeployment(deploymentId);
    // 根据Id删除,如果该流程有实例正在启动则会级联删除
    repositoryService.deleteDeployment(deploymentId,true);
}

九十九、常见问题

1.1 BPMN文件乱码

设置IDEA的编码格式为UTF-8。

image-20210516230817398

加上这一行然后重启IDEA即可。

-Dfile.encoding=UTF-8

image-20210516230849360

乱码解决。

image-20210516231542646