Activiti7概述+基础流程(3.7)

一、工作流介绍

1.概念:

工作流(Workflow),就是通过计算机对业务流程自动化执行管理。它主要解决的是“使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现”。

2.传统工作流的弊端:

  1. 申请人 --> 部门主管审批 --> 总经理审批 --> 财务审批

  2. 申请人填写报销单。

  3. 业务表

    id 姓名 部门id 报销金额 报销类别 电子发票单 status(决定当前流程走到哪里)

    1 张三 1 1000 洗脚 .... 7

  4. 突然某个月,总经理临时需要出差学习2个月。就需要有人来临时顶替总经理的审批工作。由人事助理+人事经理共同完成。

  5. 此时流程需要更改。

    申请人 --> 部门主管审批 --> 人事助理 --> 人事经理 --> 财务审批


​ 申请人填写请假单 ==> 将写好的输入插入到数据库中。 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张表,结果如下所示:

image-20260308222138916

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配置:

①关闭项目 ---> 自定义 ---> 所有设置

image-20260308210103230

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

image-20260308210352355

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

image-20260308210619367

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

image-20260308211803635

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

image-20260308212109275

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

image-20260308212501415

2.创建项目:

①新建普通Java项目,选择Maven。

image-20260308212902956

②安装插件:

image-20260308213114183

③添加依赖:在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&amp;useUnicode=true&amp;characterEncoding=utf8&amp;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文件同包

image-20260308224851540

image-20260308224932359

image-20260308224958921

image-20260308230104041

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
  • 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());


    }

image-20260308233344697

部署只能通过流程定义获取。

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());
        }
    }

image-20260308234517236

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 = 2501
    • EXECUTION_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() ---> 防护整个批注列表为空 / 长度 = 0
  • taskComment != null ---> 防护列表里的单个批注对象为空
posted on 2026-03-09 01:44  冬冬咚  阅读(1)  评论(0)    收藏  举报