activiti学习总结(全面)

525 阅读22分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

activiti学习总结

1.工作流介绍

工作流(workflow)就是工作流程的计算模型,即将工作流程中的工作如何前后组织在一起的逻辑和规 则在计算机中以恰当的模型进行表示并对其实施计算。它主要解决的是“使在多个参与者之间按照某种 预定义的规则传递文档、信息或任务的过程自动进行,从而实现某个预期的业务目标,或者促使此目标 的实现”

核心机制及实现原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cTvvC2fa-1625151638025)(./activiti核心机制及原理.jpg)]

BPMN2.0 规范

为了让业务流程的全部参与人员对流程可以进行可视化管理,提供一套让所有参与人员都易于理解的语言和标记,注重流程执行语法和标准交换格式。

BPMN2.0 的目的是建立简单的并且易懂的业务流程模型,但是同时又需要处理高度复 杂的业务流程,因此要解决这两个矛盾的要求,需要在规范中定义标准的图形和符号。BPMN 中定义了 5 类基础的元素分类:

流对象(Flow Objects)

在一个业务流程中,流对象是用于定义行为的图形元素, 主要有事件(Events)、活动(Activities)和网关(Gateways)三种流对象。

事件(Events):用于描述流程中发生的事件,事件会对流程产生影响,事件会被触发或者会产生结果。分为:

  • 开始:表示一个流程的开始
  • 中间:发生的开始和结束事件之间,影响处理的流程
  • 结束:表示该过程结束

活动(Activities):包括任务和子流程两类。子流程在图形的下方中间外加一个小加号(+)来区分。

网关(Gateways):网关主要用于控制流程中的顺序流的走向,使用网关可以控制流程进行分支与合并。

  • 排他网关:只有一条路径会被选择
  • 并行网关:所有路径会被同时选择
  • 包容网关:可以同时执行多条线路,也可以在网关上设置条件
  • 事件网关:专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。

数据(Data)

主要有4种元素:

  • 数据对象(Data Objects)
  • 数据输入(Data Inputs)
  • 数据输出(Data Inputs)
  • 数据存储(Data Stores)

连接对象(Connecting Objects)

用于连接流对象,主要有 3 种连接流对象的方式,包括顺序流(Sequence Flows)、消息流(Message Flows)、关联(Associations)。

  • 顺序流:用一个带实心箭头的实心线表示,用于指定活动执行的顺序

  • 消息流:用一条带箭头的虚线表示,用于描述两个独立的业务参与者(业务实体/业务角色)之间发送和接受的消息传递的情况

  • 关联:用一根带有线箭头的点线表示,用于将相关的数据、文本和其他人工信息与流对象联系起来。用于展示活动的输入和输出。

泳道(Participant)

通过泳道对主要的建模元素进行分组,将活动划分到不同的可视化类别中来描述由不同的参与者的责任与职责。

泳道提供了有 2 种途径组织基础的模型元素,分别是池(Pools)和道(Lanes)。

  • 池(Pools): 存放道的容器
  • 道(Lanes): 用于区分流程参与人的职能范围

制品(Artifacts)

制品主要用于为流程提供附加信息,当前制品包括组(Group)和注释(Text Annotation)。

  • 组(Group): 主要用于存放一些流程信息,包括流程文档、流程分析信息等
  • 注释(Text Annotation):主要为阅读流程图的人提供附加的文字信息

BPMN实例

  • 实例1

  • 实例2

activiti

定义

Activiti是一个工作流的引擎,开源的架构,基本 bpmn2.0 标准进行流程定义。Activiti通过是要嵌入到业务系统开发使用。

开发流程

activiti 工作流开发的几个声明周期:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gGnv1wr5-1625151638034)(z3.ax1x.com/2021/05/27/…)]

综上所知:在项目中使用Activiti工作流引擎进行流程控制。一般而言可以分为三个阶段:

静态阶段 : 包含流程设计和流程部署

运行时阶段: 用户发起流程,各责任人对流程进行审核驳回等操作,Activiti 自动的根据流程状态

历史阶段:对于以关闭或审核通过的历史流程,进行查询的管理

详细步骤

  • 第一步:部署 activiti的环境

Activiti 是一个工作流引擎(其实就是一堆 jar 包 API),业务系统使用 activiti 来对系统的业务流程进行自动化管理(25张表),为了方便业务系统访问(操作)activiti 的接口或功能,通常将activiti 环境与业务系统的环境集成在一起。

  • 第二步:流程定义

使用 activiti提供流程设计器(和 idea 或 eclipse 集成的 designer)工具进行流程定义流程,定义生成.bpmn 文件

  • 第三步: 流程定义部署

向 activiti 部署业务流程定义(.bpmn 文件)。使用 activiti 提供的 api 向 activiti 中部署.bpmn 文件(一般情况还需要一块儿部署业务流程的图片.png)

SELECT * FROM act_re_deployment #流程定义部署表 一次部署插入一条记录,记录流程定义的部署信息

SELECT * FROM act_re_procdef #流程定义表 一次部署流程定义信息,如果一次部署两个流程定义,插入两条记录 建议:一次部署只部署一个流程定义,这样 act_re_deployment 和 act_re_procdef 一对一关系

常用两个方法:单个文件部署和 zip 文件部署。 建议单个文件部署。

  • 第四步: 启动一个流程实例

业务系统就可以按照流程定义去执行业务流程,执行前需要启动一个流程实例 根据流程定义来启动一个流程实例。 指定一个流程定义的 key 启动一个流程实例,activiti根据 key 找最新版本的流程定义。 指定一个流程定义的 id 启动一个流程实例。

启动一个实例需要指定 businesskey(业务标识),businessKey 是 activiti和业务系统整合时桥梁。 比如:请假流程,businessKey就是请假单 id。 启动一个实例还可以指定流程变量,流程变量是全局变量(生命期是整个流程实例,流程实例结束, 变量就消失)

  • 第五步:查询待办任务

查询个人任务:使用 taskService,根据 assignee 查询该用户当前的待办任务。 查询组任务:使用 taskService,根据 candidateuser 查询候选用户当前的待办组任务。

  • 第六步:办理任务

办理个人任务:调用 taskService 的 complete 方法完成任务。 如果是组任务,需要先拾取任务,调用 taskService 的 claim 方法拾取任务,拾取任务之后组任务就 变成了个人任务(该任务就有负责人)。 当任务办理完成没有下一个任务/结点了,这个流程实例就完成了。

  • 第七步:流程结束

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

25张表

表名规则

Activiti 使用到的表都是 ACT_ 开头的。表名的第二部分用两个字母表明表的用途。

  • ACT_GE_ ( GE ) 表示 general 全局通用数据及设置,各种情况都使用的数据。
  • ACT_HI_ ( HI ) 表示 history 历史数据表,包含着程执行的历史相关数据,如结束的流程实例,变量,任务,等等
  • ACT_ID_ ( ID ) 表示 identity 组织机构,用户记录,流程中使用到的用户和组。这些表包含标识的信息,如用户,用户组,等等。
  • ACT_RE_ ( RE ) 表示 repository 存储,包含的是静态信息,如,流程定义,流程的资源(图片,规则等)。
  • ACT_RU_ ( RU ) 表示 runtime 运行时,运行时的流程变量,用户任务,变量,职责(job)等运行时的数据。Activiti 只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。

表详情

字段详细信息,参见网址

表名介绍
act_evt_log流程引擎通用日志表
act_ge_bytearray二进制表,存储通用的流程资源
act_ge_property系统存储表,存储整个流程引擎数据,默认存储三条数据
act_hi_actinst历史节点表
act_hi_attachment历史附件表
act_hi_comment历史意见表
act_hi_detail历史详情表
act_hi_identitylink历史用户信息表
act_hi_procinst历史流程实例表
act_hi_taskinst历史任务实例表
act_hi_varinst历史变量表
act_procdef_info流程定义的动态变更信息
act_re_deployment部署信息表
act_re_model流程设计实体表
act_re_procdef流程定义数据表
act_ru_deadletter_job作业失败表,失败次数>重试次数
act_ru_event_subscr运行时事件表
act_ru_execution运行时流程执行实例表
act_ru_identitylink运行时用户信息表
act_ru_integration运行时综合表
act_ru_job作业表
act_ru_suspended_job作业暂停表
act_ru_task运行时任务信息表
act_ru_timer_job运行时定时器表
act_ru_variable运行时变量表

2.Activiti 服务架构图

在Activiti7中,IdentityService,FormService 两个Serivce 都已经删除了

activiticfg.xml文件

  Activiti的引擎配置文件,包括ProcessEngineConfiguration的定义,数据源定义,事务管理器等,此文件其实就是一个spring配置文件,下面是一个基本的配置,只配置了ProcessEngineConfiguration和数据源;

<?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">
    <!--数据源-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/activiti"/>
        <property name="username" value="root"/>
        <property name="password" value="123"/>
    </bean>

    <!--配置Activiti使用的processEngine对象   默认命名为processEngineConfiguration-->
    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--配置数据源方式二:-->
        <!--<property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>-->

        <!--指定数据库生成策略-->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
</beans>

processEngineConfiguration

创建processEngineConfiguration

    public static void main(String[] args) {
        //加载配置
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        //获取ProcessEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
    }

ProcessEngine

工作流引擎,相当于一个门面接口,通过ProcessEngineConfiguration创建ProcessEngine,通过ProcessEngine创建各个service接口;

一般创建方式

  //通过ProcessEngineConfiguration创建ProcessEngine
  ProcessEngine processEngine = configuration.buildProcessEngine();

简单创建方式

将activiti.cfg.xml文件名及路径固定,且activiti.cfg.xml文件中有processEngineConfiguration的配置,使用如下代码创建processEngine

    //使用classpath下的activiti.cfg.xml中的配置创建processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

service

service创建方式

  通过ProcessEngine创建service,service是工作流引擎提供用于进行工作部署,执行,管理的服务接口;

  方式如下:

   //获取RepositoryService对象进行流程部署
   RepositoryService repositoryService = processEngine.getRepositoryService();
   //获取RuntimeService对象
   RuntimeService runtimeService = processEngine.getRuntimeService();
   //获取TaskService对象进行任务管理
   TaskService taskService = processEngine.getTaskService();  

Service 总览

service说明
RepositoryServiceactiviti 的资源管理类
RuntimeServiceactiviti 的流程运行管理类
TaskServiceactiviti 的任务管理类
HistoryServiceactiviti 的历史管理类
ManagerServiceactiviti 的引擎管理类

RepositoryService

  • 是 activiti 的资源管理类,提供了管理和控制流程发布包和流程定义的操作。
  • 使用工作流建模工具设计的业务流程图需要使用此 service 将流程定义文件的内容部署到计算机。

除了部署流程定义以外还可以:

  • 查询引擎中的发布包和流程定义。
  • 暂停或激活发布包,对应全部和特定流程定义。 暂停意味着它们不能再执行任何操作了,激活是对应的反向操作。
  • 获得多种资源,像是包含在发布包里的文件, 或引擎自动生成的流程图。
  • 获得流程定义的 pojo 版本, 可以用来通过 java 解析流程,而不必通过 xml。

RuntimeService

它是 activiti 的流程运行管理类。可以从这个服务类中获取很多关于流程执行相关的信息

TaskService

是 activiti 的任务管理类。可以从这个类中获取任务的信息。

HistoryService

是 activiti 的历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据(根据配置),比 如流程实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执行路径,等等。 这个 服务主要通过查询功能来获得这些数据。

ManagerService

是 activiti 的引擎管理类,提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动 的应用程序中使用,主要用于 Activiti 系统的日常维护。

IdentityService

用户、组管理服务接口,用于管理Group、User的增删改查,并维护Membership,涉及到的API有newUser、newGroup、saveUser、saveGroup、createMembership以及相关的deleteXXX方法。

FormService

表单服务用于访问表单数据以及在启动新的流程实例时或完成任务时所需的渲染后的表单,提供UI界面辅助用户填写相关值以保存至流程变量。该服务在实际业务应用中并不常用,属于引擎的非核心服务。

3.Activiti 小试牛刀

步骤:

(1)第一步:IDEA安装Activiti插件 actBpm

(2)创建Activiti流图

(3)创建一个流程图(这里就用一个学生请假实例来进行)

1:首先选择一个startEvent,也就是流程的开始,并且点击一个额外的界面地方,然后输入该处理流程的名称和ID,这里就输入为shenqing

2:在选择一个UserTask按钮,表示一个处理任务,同理命名为“请假申请”

3:在选择一个UserTask按钮,表示一个处理任务,同理命名为“班主任”

4:在选择一个UserTask按钮,表示一个处理任务,同理命名为“教务处”

5:选择一个EndEvent按钮,表示流程的结束;

6:将各个按钮进行连线。(将鼠标放到每个按钮的“正中心”,然后拖着到想要链接的另外一个按钮即可,出现线条)

7:最终的效果。描述:就是学生提交请假申请——》班主任审核——》教务处审核

(4)将第三步中创建的shenqing.bpmn文件生成一个png格式或xml格式的内容。

(5): 创建一个activiti.cfg.xml文件


<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="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
		<property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activititest?useUnicode=true&characterEncoding=utf8"></property>
		<property name="jdbcUsername" value="xxxxxx"></property>
		<property name="jdbcPassword" value="xxxxxxxx"></property>
		<!--
			创建表的策略
		 -->
		<property name="databaseSchemaUpdate" value="true"></property>
	</bean>
</beans>

(6).导入Activiti的包--------或通过Maven进行依赖包的管理

(7)创建一个数据库生成的测试。(注意:要保证本地有对应名字的数据库)

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.junit.Test;
 
public class ActivitiTable {
    /**
     * 创建Activiti流的相关的数据库表
     */
    @Test
    public void creatTable(){
        ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml")
                .buildProcessEngine();
    }
}

运行测试方法成功之后,再进入数据库,我们会看到产生了25张数据表

(8)进行流程部署的重点开发(按照下面的流程进行)------------重点的重点

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.task.Task;
import org.junit.Test;
import java.util.List;
/**
 * 描述如何在代码中实现学生进行请假申请,班主任审核,教务处审核
 **/
public class ActivitiTest {
 
    /**
     * 1、部署流程
     * 2、启动流程实例
     * 3、请假人发出请假申请
     * 4、班主任查看任务
     * 5、班主任审批
     * 6、最终的教务处Boss审批
     */
    /**
     * 1:部署一个Activiti流程
     * 运行成功后,查看之前的数据库表,就会发现多了很多内容
     */
    @Test
    public void creatActivitiTask(){
        //加载的那两个内容就是我们之前已经弄好的基础内容哦。
        //得到了流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getRepositoryService()
                .createDeployment()
                .addClasspathResource("shenqing.bpmn")
                .addClasspathResource("shenqing.png")
                .deploy();
    }
    /**
     * 2:启动流程实例
     */
    @Test
    public void testStartProcessInstance(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getRuntimeService()
                .startProcessInstanceById("shenqing:1:4");  //这个是查看数据库中act_re_procdef表
    }
    /**
     * 完成请假申请
     */
    @Test
    public void testQingjia(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getTaskService()
                .complete("104"); //查看act_ru_task表
    }
 
    /**
     * 小明学习的班主任小毛查询当前正在执行任务
     */
    @Test
    public void testQueryTask(){
        //下面代码中的小毛,就是我们之前设计那个流程图中添加的班主任内容
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        List<Task> tasks = processEngine.getTaskService()
                .createTaskQuery()
                .taskAssignee("小毛")
                .list();
        for (Task task : tasks) {
            System.out.println(task.getName());
        }
    }
 
    /**
     * 班主任小毛完成任务
     */
    @Test
    public void testFinishTask_manager(){
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        engine.getTaskService()
                .complete("202"); //查看act_ru_task数据表
    }
 
    /**
     * 教务处的大毛完成的任务
     */
    @Test
    public void testFinishTask_Boss(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getTaskService()
                .complete("302");  //查看act_ru_task数据表
    }
}

4.详细解析Task任务(非常重要)

任务的概念:需要有人进行审批或者申请的为任务

任务的执行人的情况类型:

情况一:当没有进入该节点之前,就可以确定任务的执行人

实例:比如进行“请假申请”的流程时候,最开始执行的就是提交”请假申请“,那么就需要知道,谁提交的“请假”,很明显,在一个系统中,谁登陆到系统里面,谁就有提交“请假任务”的提交人,那么执行人就可以确定就是登录人。

情况二:有可能一个任务节点的执行人是固定的。

   实例:比如,在“公司财务报账”的流程中,最后一个审批的人,一定是财务部的最大的BOSS,所以,这样该流程的最后一个节点执行人,是不是就已经确定是为“财务部最大BOSS”了。

情况三:一个节点任务,之前是不存在执行人(未知),只有当符合身份的人,登陆系统,进入该系统,才能确定执行人。

实例:比如,如果当前的流程实例正在执行“自荐信审批”,这个时候,自荐信审批没有任务执行人,因为审批人是可以很多个,无法确定到底是谁,只有当咨询员登录系统以后才能给该任务赋值执行人,即存在只要是咨询员登陆,那么就可以看到所有的“自荐信”。

情况四:一个任务节点有n多人能够执行该任务,但是只要有一个人执行完毕就完成该任务了:组任务

实例:比如,“进入地铁站通道”的流程,我们一般地铁都是有N个安全检查的入口,有很多个人在进行检查,那么我们要想通过检查,那么任意一个检察员只要通过即可。

详细分析:

针对情况一:

步骤:

(1)首先构建流程图:

2)bpm及部署

/**
    * 部署流程
    */
   @Test
   public void startDeployTest(){
       ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
       processEngine.getRepositoryService()
               .createDeployment()
               .name("请假流程:情况一")
               .addClasspathResource("com/hnu/scw/task/shenqing.bpmn")
               .deploy();
   }

数据库情况:

启动流程实例

/**
     * 启动流程实例
     *    可以设置一个流程变量
     */
    @Test
    public void testStartPI(){
        /**
         * 流程变量
         *   给<userTask id="请假申请" name="请假申请" activiti:assignee="#{student}"></userTask>
         *     的student赋值
         */
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("student", "小明");
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getRuntimeService()
                .startProcessInstanceById("shenqing1:1:1304",variables);

分析:如果,我们安装下面的代码执行,那么就出出现如下的错误

 /**
     * 启动流程实例
     *    可以设置一个流程变量
     */
    @Test
    public void testStartPI(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getRuntimeService()
                .startProcessInstanceById("shenqing1:1:1304");
    }

因:是否还记得,我们在画流程图的时候,对该请假申请的节点,分配了一个#{student},这个变量,这个其实含义就是说,当我们进行该节点的处理的时候,就需要分配一个执行人,如果没有分配,就会发生上面的错误。然后再回头想一下,是不是就是我们的第一种情况呢?因为,在进行请假的流程的执行开始的时候,其实申请人是已经可以确定了,就是登陆的用户。

(3):后面的代码如下:

/**
     * 在完成请假申请的任务的时候,给班主任审批的节点赋值任务的执行人
     */
    @Test
    public void testFinishTask_Teacher(){
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("teacher", "我是小明的班主任");
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getTaskService()
                .complete("1405", variables); //完成任务的同时设置流程变量
    }
 
    /**
     * 在完成班主任审批的情况下,给教务处节点赋值
     */
    @Test
    public void testFinishTask_Manager(){
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("manager", "我是小明的教务处处长");
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getTaskService()
                .complete("1603", variables); //完成任务的同时设置流程变量
    }
 
    /**
     * 结束流程实例
     */
    @Test
    public void testFinishTask(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getTaskService()
                .complete("1703");
    }

总结:针对情况一,那么我们必须要进入该节点执行前,就要分配一个执行人。

情况二:有可能一个任务节点的执行人是固定的。与情况一类似,都是在画流程图的时候就已经分配这个执行人了

情况三:一个节点任务,之前是不存在执行人(未知),只有当符合身份的人,登陆系统,进入该系统,才能确定执行人。

步骤:

(1)画流程图, 需要修改的地方

(2)编写的TaskListener监听类

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
/**
 *  tack任务的监听,主要是为了动态分配执行人
 **/
public class MyTaskListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        /**
         * 任务的执行人可以动态的赋值
         *   1、流程变量
         *        可以通过提取流程变量的方式给任务赋值执行人
         *   2、可以操作数据库
         *      方法一:(必须在web环境) WebApplicationContext ac = WebApplicationContextUtils
         *       	.getWebApplicationContext(ServletActionContext.getServletContext());
                    xxxxService xxxxService = (xxxxService) ac.getBean("xxxxService");
                方法二:通过JDBC来进行数据库操作
         */
        //动态分配(这里是从上一节点中的tack变量的map中获取,只有流程没有结束,所有的变量都是可以获取)
        /*String value = (String)delegateTask.getVariable("aaa");
        delegateTask.setAssignee(value);*/
        //静态分配(用于确定该执行人就只有一种情况,是一种固定的)
        delegateTask.setAssignee("我是班主任");
    }
}

通过这样的方式的话,当有“请假申请”进行提交之后,“班主任”的这个节点,就会自动进行分配执行人。

情况四:一个任务节点有n多人能够执行该任务,但是只要有一个人执行完毕就完成该任务了:组任务

流程图如下:

具体的测试代码:(注意看写的注释内容,就明白了对应的数据库的什么表)

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.junit.Test;
import java.util.List;
 
/**
 * @desc 关于对于组任务的测试内容
 **/
public class GroupTaskTest {
    /**
     * 主要是对于某些任务流程中,有N个人,但是只需要其中的某一个通过,
     * 则该任务就通过了,所以针对这样的业务需求,就有如下的内容
     */
    @Test
    public void deployTashTest(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getRepositoryService()
                .createDeployment()
                .addClasspathResource("com/hnu/scw/test/task3.bpmn")
                .addClasspathResource("com/hnu/scw/test/task3.png")
                .name("组任务的测试")
                .deploy();
    }
    /**
     * 当启动完流程实例以后,进入了"电脑维修"节点,该节点是一个组任务
     *    这个时候,组任务的候选人就会被插入到两张表中
     *       act_ru_identitylink  存放的是当前正在执行的组任务的候选人
     *       act_hi_identitylink
     */
    @Test
    public void processTaskStartTest(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getRuntimeService()
                .startProcessInstanceByKey("task3");
    }
    /**
     * 对于act_hi_identitylink表,根据任务ID,即TASK_ID字段查询候选人
     */
    @Test
    public void testQueryCandidateByTaskId(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        List<IdentityLink> identityLinks = processEngine.getTaskService()
                .getIdentityLinksForTask("2104");
        for (IdentityLink identityLink : identityLinks) {
            System.out.println(identityLink.getUserId());
        }
    }
 
    /**
     * 对于act_hi_identitylink表,根据候选人,即USER_ID_查看组任务
     */
    @Test
    public void testQueryTaskByCandidate(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        List<Task> tasks = processEngine.getTaskService()
                .createTaskQuery()
                .taskCandidateUser("工程师1")
                .list();
        for (Task task : tasks) {
            System.out.println(task.getName());
        }
    }
    /**
     * 候选人中的其中一个人认领任务
     */
    @Test
    public void testClaimTask(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        processEngine.getTaskService()
                /**
                 * 第一个参数为taskId
                 * 第二个参数为认领人
                 */
                .claim("2104", "工程师2");
    }
 
}

5.实际项目中的关于Activiti的工具类方法封装

package com.hnu.scw.activiti.utils;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;
 
/**
 * @author scw
 * @create 2018-01-24 9:51
 * @desc 针对流程管理的工具类
 **/
@Component("activitiUtils")
public class ActivitiUtils {
    @Resource(name = "processEngine")
    private ProcessEngine processEngine;
 
    /**
     * 部署流程
     * @param file 流程的zip文件
     * @param processName  流程的名字
     * @throws IOException
     */
    public void deployeProcess(File file , String processName)throws IOException{
        InputStream inputStream = new FileInputStream(file);
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        this.processEngine.getRepositoryService()
                .createDeployment()
                .name(processName)
                .addZipInputStream(zipInputStream)
                .deploy();
    }
 
    /**
     * 通过字节流来进行部署流程
     * @param io
     * @param processName
     */
    public  void deplyoProcessByInputSterm(InputStream io , String processName){
        ZipInputStream zipInputStream = new ZipInputStream(io);
        this.processEngine.getRepositoryService()
                .createDeployment()
                .name(processName)
                .addZipInputStream(zipInputStream)
                .deploy();
    }
 
 
    /**
     * 查询所有的部署流程
     * @return
     */
    public List<Deployment> getAllDeplyoment(){
        return this.processEngine.getRepositoryService()
                .createDeploymentQuery()
                .orderByDeploymenTime()
                .desc()
                .list();
    }
    /**
     * 查询所有的部署定义信息
     * @return
     */
    public List<ProcessDefinition> getAllProcessInstance(){
        return this.processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .orderByProcessDefinitionVersion()
                .desc()
                .list();
    }
 
    /**
     * 根据部署ID,来删除部署
     * @param deplyomenId
     */
    public void deleteDeplyomentByPID(String deplyomenId){
        this.processEngine.getRepositoryService()
                .deleteDeployment(deplyomenId , true);
    }
 
    /**
     * 查询某个部署流程的流程图
     * @param pid
     * @return
     */
    public InputStream lookProcessPicture(String pid){
        return this.processEngine.getRepositoryService()
                .getProcessDiagram(pid);
    }
 
    /**
     * 开启请假的流程实例
     * @param billId
     * @param userId
     */
    public void startProceesInstance(Long billId , String userId){
        Map<String , Object> variables = new HashMap<>();
        variables.put("userID" , userId);
        this.processEngine.getRuntimeService()
                .startProcessInstanceByKey("shenqingtest" , ""+billId , variables); //第一个参数,就是流程中自己定义的名字,这个一定要匹配,否则是找不到的。
    }
 
    /**
     * 查询当前登陆人的所有任务
     * @param userId
     * @return
     */
    public List<Task> queryCurretUserTaskByAssignerr(String userId){
        return this.processEngine.getTaskService()
                .createTaskQuery()
                .taskAssignee(userId)
                .orderByTaskCreateTime()
                .desc()
                .list();
    }
 
    /**
     * 根据TaskId,获取到当前的执行节点实例对象
     * @param taskId
     * @return
     */
    public ActivityImpl getActivityImplByTaskId(String taskId){
        //首先得到任务
        Task task = this.getTaskByTaskId(taskId);
        //其次,得到流程实例
        ProcessInstance processInstance = this.getProcessInstanceByTask(task);
        //再次,根据流程实例来获取到流程定义
        ProcessDefinitionEntity processDefinitionEntity = this.getProcessDefinitionEntityByTask(task);
        //再根据,流程定义,通过流程实例中来获取到activiti的ID,从而得到acitviImp
        ActivityImpl activity = processDefinitionEntity.findActivity(processInstance.getActivityId());
        return activity;
    }
 
    /**
     * 根据taskId,判断对应的流程实例是否结束
     * 如果结束了,那么得到的流程实例就是返回一个null
     * 否则就是返回对应的流程实例对象
     * 当然也可以选择返回boolean类型的
     * @param taskId  任务ID
     * @return
     */
    public ProcessInstance isFinishProcessInstancs(String taskId){
        //1,先根据taskid,得到任务
        Task task = getTaskByTaskId(taskId);
        //2:完成当前任务
        finishCurrentTaskByTaskId(taskId);
        //3:得到当前任务对应得的流程实例对象
        ProcessInstance processInstance = getProcessInstanceByTask(task);
        return processInstance;
    }
 
    /**
     * 获取当前执行节点的所有出口
     * @param activity
     * @return
     */
    public List<PvmTransition> getCurrentActivitiImplPvm(ActivityImpl activity){
        List<PvmTransition> outgoingTransitions = activity.getOutgoingTransitions();
        return outgoingTransitions;
    }
 
    /**
     * 根据taskId获取到task
     * @param taskId
     * @return
     */
    public Task getTaskByTaskId(String taskId) {
        //得到当前的任务
        Task task = this.processEngine.getTaskService()
                .createTaskQuery()
                .taskId(taskId)
                .singleResult();
        return task;
    }
 
    /**
     * 根据Task中的流程实例的ID,来获取对应的流程实例
     * @param task 流程中的任务
     * @return
     */
    public ProcessInstance getProcessInstanceByTask(Task task) {
        //得到当前任务的流程
        ProcessInstance processInstance = this.processEngine.getRuntimeService()
                .createProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .singleResult();
        return processInstance;
    }
 
    /**
     * 根据Task来获取对应的流程定义信息
     * @param task
     * @return
     */
    public ProcessDefinitionEntity getProcessDefinitionEntityByTask(Task task){
        ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) this.processEngine.getRepositoryService()
                .getProcessDefinition(task.getProcessDefinitionId());
        return processDefinitionEntity;
    }
 
    /**
     * 根据taskId获取到businesskey,这个值是管理activiti表和自己流程业务表的关键之处
     * @param taskId 任务的ID
     * @return
     */
    public String getBusinessKeyByTaskId(String taskId){
        Task task = this.getTaskByTaskId(taskId);
        ProcessInstance processInstance = this.getProcessInstanceByTask(task);
        //返回值
        return processInstance.getBusinessKey();
    }
 
    /**
     * 根据taskId,完成任务
     * @param taskId
     */
    public void finishCurrentTaskByTaskId(String taskId){
        this.processEngine.getTaskService().complete(taskId);
    }
 
    /**
     * 完成任务的同时,进行下一个节点的审批人员的信息的传递
     * @param taskId
     * @param object
     */
    public void finishCurrentTaskByTaskId(String taskId , Object object){
        Map<String , Object> map = new HashMap<>();
        map.put("assigeUser" , object);
        this.processEngine.getTaskService().complete(taskId , map);
    }
}

附:activiti示例代码

设计模式

命令模式和责任链模式,参见《疯狂工作流讲义第2版.pdf》-7 Activiti 的设计模式

源码分析参见Activiti7源码分析

附录

相关网址: