【工作流】Activit入门排雷

本文介绍了Activiti流程引擎的基础知识,包括数据库结构、Spring Boot整合方法及如何提交、启动和执行流程实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Activit流程中心

简单聊一聊

​ 这两天学习工作流,网上找了很多资料但是或多或少有一定的差异,不是时间太久就是信息不完善,因此我寻思着一直找别人的不如自己来写一篇基本入门。所有的流程操作大致都是如此,在多租户情况下,需要对所有用户的权限进行控制,然后通过身份验证进行流程操作。自习阅读activit工作流的日志记录,不难发现,每一步操作都涉及到了多了多表的同步更新,原理不难,总要的是去理解这个思想。

简介

Activiti项目是一项新的基于Apache许可的开源BPM平台,从基础开始构建,旨在提供支持新的BPMN 2.0标准,包括支持对象管理组(OMG),面对新技术的机遇,诸如互操作性和云架构,提供技术实现。

数据库相关

activit为我们提供了25张表,每张表对应了不同的功能作用。

Activiti数据库支持:

Activiti的后台是有数据库的支持,所有的表都以ACT_开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的API对应。

ACT_RE_*: 'RE’表示repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。

ACT_RU_*: 'RU’表示runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。

ACT_ID_*: 'ID’表示identity。 这些表包含身份信息,比如用户,组等等。

ACT_HI_*: 'HI’表示history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。

ACT_GE_*: 通用数据, 用于不同场景下,如存放资源文件。

表结构操作:

1:资源库流程规则表
  1. act_re_deployment 部署信息表

  2. act_re_model 流程设计模型部署表

  3. act_re_procdef 流程定义数据表

2:运行时数据库表
  1. act_ru_execution 运行时流程执行实例表

  2. act_ru_identitylink 运行时流程人员表,主要存储任务节点与参与者的相关信息

  3. act_ru_task 运行时任务节点表

  4. act_ru_variable 运行时流程变量数据表

3:历史数据库表
  1. act_hi_actinst 历史节点表

  2. act_hi_attachment 历史附件表

  3. act_hi_comment 历史意见表

  4. act_hi_identitylink 历史流程人员表

  5. act_hi_detail 历史详情表,提供历史变量的查询

  6. act_hi_procinst 历史流程实例表

  7. act_hi_taskinst 历史任务实例表

  8. act_hi_varinst 历史变量表

4:组织机构表
  1. act_id_group 用户组信息表

  2. act_id_info 用户扩展信息表

  3. act_id_membership 用户与用户组对应信息表

  4. act_id_user 用户信息表

这四张表很常见,基本的组织机构管理,关于用户认证方面建议还是自己开发一套,组件自带的功能太简单,使用中有很多需求难以满足

5:通用数据表
  1. act_ge_bytearray 二进制数据表

  2. act_ge_property 属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录,

整合SpringBoot

​ 自己在整合时候一步一步填的坑,有兴趣的小伙伴可以看看。保证让你成功入门。

环境配置

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.fyyice</groupId>
    <artifactId>activiti</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
    </parent>

    <!-- activiti 的相关包 mysql的驱动包 mybatis log4j 数据库链接池-->
    <properties>
        <slf4j.version>1.6.6</slf4j.version>
        <log4j.version>1.2.12</log4j.version>
        <activiti.version>7.0.0.Beta1</activiti.version>
        <project.build.soureEnconding>UTF-8</project.build.soureEnconding>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-engine</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <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>
        <!-- activiti 云支持 -->
        <dependency>
            <groupId>org.activiti.cloud</groupId>
            <artifactId>activiti-cloud-services-api</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.20</version>
        </dependency>

        <!-- mybatis -->
        <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>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>
</project>
application.yml

这里数据库版本根据自己的装的版本号进行相关配置,没有强制要求

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: root
  activiti:
    #1.flase: 默认值。activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常
    #2.true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
    #3.create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
    #4.drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
    database-schema-update: true
    #检测历史表是否存在 activiti7默认没有开启数据库历史记录 启动数据库历史记录
    db-history-used: true
    #记录历史等级 可配置的历史级别有none, activity, audit, full
    history-level: full
    #校验流程文件,默认校验resources下的processes文件夹里的流程文件
    check-process-definitions: false
activit.cfg.xml

activit策略,可以调用相关方法自动生成数据库

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contex
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">


    <!--dbcp链接池-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///activiti"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="maxActive" value="3"/>
        <property name="maxIdle" value="1"/>
    </bean>

    <!--在默认方式下 bean的id  固定为 processEngineConfiguration-->
    <bean id="processEngineConfiguration"
          class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <!--配置数据库相关的信息-->
        <property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://activiti"/>
        <property name="jdbcUsername" value="root"/>
        <property name="jdbcPassword" value="root"/>
        <!--直接引用上面配置的链接池-->
        <property name="dataSource" ref="dataSource"/>
        <!--actviti数据库表在生成时的策略
        true - 如果数据库中已经存在相应的表,那么直接使用,
               如果不存在,那么会创建-->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
</beans>

实例

这里所有的编码都是在测试环境下进行的@Test

1.自动生成数据库表
 @org.junit.Test
    public void createDB(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        System.out.println(processEngine);
    }

概述

getDefaultProcessEngine方法会传入一个default的processEngineName进入到方法中。通过isInitialized判断配置文件中的数据库表是否存在,如果存在,则返回一个activit工作流引擎,如果没有则初始化,自动策略,根据resources = classLoader.getResources(“activiti.cfg.xml”);中的相关配置信息进行初始化。

    public static ProcessEngine getProcessEngine(String processEngineName) {
        if (!isInitialized()) {
            init();
        }
        return (ProcessEngine)processEngines.get(processEngineName);
    }
2.提交一个流程申请

这里我以请假流程进行说明。在加载流程时,我们需要对每一个节点指定对应的执行人。

请假流程: start → 创建请假流程(对应张三) → 小组长审批(对应leader) → 负责人审批(对应boss) → 结束

 @org.junit.Test
    //启动一个流程
    public void get(){
     	//获取工作流引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
     	//加载并实例化流程配置
        Deployment deployment = repositoryService.createDeployment().name("请假申请流程")
                .addClasspathResource("bnmp/myAction.bpmn20.xml")
                .deploy();
        System.out.println("流程id:"+deployment.getId());
        System.out.println("流程名:"+deployment.getName());
        System.out.println("流程key:"+deployment.getKey());
        System.out.println("流程Category:"+deployment.getCategory());
    }

这里在提交过后我们可以看到对应的流程提交记录(对应act_re_deployment表)

act_re_deployment.png

踩坑:这里可能会遇到如下

org.activiti.bpmn.exceptions.XMLException: 3 字节的 UTF-8 序列的字节 3 无效。

解决方案:

​ 网上百度了很多,但是几乎没有一个成功,比如改编码格式啊,maven中加入什么配置啊,都没用。说个简单粗暴的,把我们画好的流程图myProccess.bpmn文件复制一个,然后修改为xml文件,打开将其乱码文字修改掉过后就好了。还有一个需要注意的是**.addClasspathResource**文件格式的解析,如果不规范则会导致解析失败

 public static final String[] BPMN_RESOURCE_SUFFIXES = new String[]{"bpmn20.xml", "bpmn"};
3.启动一个流程

在提交流程申请过后,我们需要将这个流程启动。启动的方式有多种,可以更具id、key等,这里以key为例,其他详情大家可以查阅相关api

   @org.junit.Test
    public void start(){
        //创建获取流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //获取运行器
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //根据id启动流程  这个leaveProccess就是前面提交流程的key
        ProcessInstance instance = runtimeService.startProcessInstanceByKey("leaveProccess");
        System.out.println("流程定义id:"+instance.getProcessDefinitionId());
        System.out.println("流程实例id:"+instance.getId());
        System.out.println("当前活动id:"+instance.getActivityId());
    }

控制台输出:
    流程定义id:leaveProccess:2:27503
	流程实例id:42501
	当前活动id:null

在启动成功后,我们可以通过TaskService对启动的流程进行查看

    @org.junit.Test
    public void findPersonTaskList(){
        //获取流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //获取taskService
        TaskService taskService = processEngine.getTaskService();
        //根据流程key 和 任务负责人 查询任务
        List<Task> taskList = taskService.createTaskQuery()
                .processDefinitionKey("leaveProccess")
                .taskAssignee("zhangsan")
                .list();
        System.out.println(taskList);
        for (Task t: taskList ) {
            System.out.println("流程实例id:"+ t.getProcessDefinitionId());
            System.out.println("任务id:"+  t.getId());
            System.out.println("任务负责人:" + t.getAssignee());
            System.out.println("任务名称:" + t.getName());
        }
    }

控制台输出:
    流程实例id:leaveProccess:2:27503
	任务id:42505
	任务负责人:zhangsan
	任务名称:创建请假流程
4.开始任务

流程启动后,每个节点会对应我们指定的操作者,然后对应的操作者可以对自己当前任务流程进行操作

   //这里就是刚才的张三,我们通过指定流程任务的key和对应人,获取其任务id。调用taskService.complete()方法后相当于完成了当前节点的操作,节点按照顺序传达到下一个人。后面的审批操作同理。
	@org.junit.Test
    public void complete(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        TaskService taskService = processEngine.getTaskService();
        Task t = taskService.createTaskQuery().processDefinitionKey("leaveProccess")
                .taskAssignee("zhangsan").singleResult();

        taskService.complete(t.getId());
        System.out.println(t.getAssignee()+"已完成审批");
    }

完成节点操作后,再次调用该方法会报出空指针方法,因为该用户已经完成了此节点操作,数据将同步到历史记录中。

java.lang.NullPointerException
	at cn.fyyice.activiti.Test.complete(Test.java:74)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值