activiti入门(含代码)

505 阅读8分钟

代码地址:gitee.com/zhangxin123…

BPM(business process management),业务流程管理

BPMN(business process model and notation),业务流程模型和符号

1. 使用步骤

  • 部署activiti:activiti就是一堆jar包,因此需要和业务系统一起部署

  • 定义流程:使用activiti的建模工具定义业务流程.bpmn文件

  • 部署流程定义:使用activiti提供的api把流程定义内容存储起来,再activiti执行过程汇总可以查看定义的内容。Activiti是通过数据库来存储业务流程的。

  • 启动流程实例:流程实例也叫processInstance,启动一个流程实例表示开始一次业务流程的运作。

  • 用户查询待办任务(task):因为现在系统的业务流程都交给了activiti管理,通过activiti就可以查询当前流程执行到哪个步骤了。当前用户需要办理哪些业务同样由activiti管理,不需要编写sql进行查询了。

  • 用户办理任务:用户查询到自己的待办任务后,就可以办理某个业务,如果这个业务办理完成还需要其他用户办理,就可以由activiti帮我们把工作流程往后的步骤推动。

  • 流程结束:当业务办理完成没有下一个任务节点后,这个流程实例就执行完成了。

2. 环境搭建

Idea中安装流程定义插件actiBPM。

3. 创建流程定义

  • 在resource目录下创建***.bpmn文件

图片1.png

  • 构建流程图:注意整个流程定义的id和name,以及每个节点对象的name和assignee

图片2.png

图片3.png

  • 复制***.bpmn文件,加后缀.xml,用idea的Diagrams打开,可以导出流程定义的png文件

图片4.png

  • 部署流程

图片5.png

图片6.png

图片7.png

4. Test

见代码

5. 流程定义与流程实例

流程定义ProcessDefinition和流程实例ProcessInstance是activiti中非常重要的两个概念。他们的关系类似于java中类和对象的概念。

流程定义是以BPMN文件定义的一个工作流程,是一组工作规范。例如之前请假流程

流程实例是指一个具体的业务流程。例如某个员工发起一次请假,就会实例化一个请假的流程实例,并且每个不同的流程实例之间互不影响。

在后台的表结构中,有很多表都包含了流程定义和流程实例的字段。

流程定义字段通常是PROC_DEF_ID,流程实例字段通常是PROC_INST_ID。

启动流程实例,添加businessKey

当我们去查看startProcessInstanceByKey这个方法时,有好多重载的实现方法,可以传一些不同的参数。

  • String processDefinitionKey:流程定义的唯一键,不能为空

  • String businessKey:每个流程实例上下文中关联的唯一键

这是activiti提供的一个非常重要的便利,用来将activiti的工作流程与实际业务进行关联。

例如,当我们需要对一个业务订单进行审批时,订单的详细信息并不在activiti的数据当中,但是在审批时确实需要查看这些信息。

  • Map<String,Object> variables:在流程实例中传递的流程变量

  • String tenantId:租户id,支持多租户设计

6. 流程挂起与激活

有时候我们要暂停一个流程,过一段时间恢复。例如月底不接受报销审批流程,年底不接受接待审批流程,非工作日不接收售后报销流程等,这个时候,就可以将流程进行挂起操作,挂起的流程不会再继续执行。

挂起时有两种操作方式

  • 将整个流程定义挂起,这样这个流程定义下所有的流程实例都挂起,无法进行执行

  • 挂起当前实例,该实例不允许操作

7. 流程变量

流程变量也是activiti中非常重要的角色。我们之前的请假流程并没有利用到流程变量,每个步骤都是非常固定的,但是,当我们需要实现一些负责的业务流程,比如请假3天以内部门经理审批,3天以上需要增加总经理审批这样的流程,就需要到流程变量了。

流程变量的作用域

1. global变量

  • 这个是流程变量默认的作用域,作用范围是一个完整的流程实例。

  • 变量名不可重复,重复会被覆盖

2. local变量

  • Local变量的作用域只针对一个任务或者一个执行实例的范围。

  • 变量名可以重复,互不影响

3. 使用流程变量

定义好流程变量后,就可以在整个流程定义中使用了。例如可以在某些任务属性assignee上使用assignee,或者在某些连线上使用{assignee},或者在某些连线上使用{evection.num<3}。

Activiti中可以使用UEL表达式来使用这些流程变量。UEL表达式可以直接获取一个变量的值,也可以计算一个boolean结果的表达式,还可以直接使用某些对象的属性。例如之前创建的请假流程,如果要实现3天以内部门经理审批,3天以上增加总经理审核,可以做如下调整:

     

注意事项:

1)如果UEL表达式中流程变量名不存在则报错

2)如果UEL表达式中流程变量值为null,流程不按UEL表达式执行,而是结束

3)如果UEL表达式都不符合条件,流程结束

4)如果连线不设置条件,会走flow序号小的那条线

5)设置流程变量会在当前执行流程变量表act_ru_variable中插入记录,同时也会在历史流程变量表act_hi_varinst中插入记录

8. 网关

  • 排他网关(ExclusiveGateway)

排他网关,用来在流程中实现决策。当流程执行到这个网关,所有分支都会判断条件是否为true,如果为true则执行该分支。

如果网关有两个分支条件为true,排他网关会选择id值较小的一条分支执行。

注意:线条上也可以根据条件来实现流程分支选择,与网关不同的地方在于,线条上的条件不满足的话,流程结束;排它网关的话会抛出系统异常。

  • 并行网关(ParallelGateway)

并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的。

Fork分支:并行后的所有外出顺序流,为每个顺序流都创建一个并发分支

Join汇聚:所有到达并行网关,在此等待的进入分支,直到所有进入顺序流的分支都到达以后,流程就会通过汇聚网关

与其他网关的主要区别,并行网关不会解析条件,即使顺序流中定义了条件,也会忽略

  • 包含网关

包含网关可以看作是排他网关和并行网关的结合体

和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析他们。但是主要的区别是包含网关可以选择多于一条顺序流,和并行网关一样

包含网关的功能是基于进入和外出顺序流的

Fork分支:所有外出顺序流的条件都会被解析,结果为true的顺序流会以并行方式继续执行,为每个顺序流创建一个分支

Join汇聚:所有并行分支到达包含网关,会进入等待状态,直到每个包含流程token的进入顺序流的分支都到达。这是与并行网关的最大不同。换句话说,包含网关只会等待被选中执行了的进入顺序流,汇聚之后继续执行

  • 事件网关

事件网关允许根据事件判断流向。网关的每个外出顺序流都要连接到一个中间捕获事件。当流程到达一个基于事件网关,网关会进入等待状态:会暂停执行。与此同时,会为每个外出顺序流创建相对的事件订阅。

事件网关的外出顺序流和普通顺序流不同,这些顺序流不会真的“执行”,相反他们让流程引擎去决定执行到事件网关的流程需要订阅哪些事件。要考虑以下条件:

1)事件网关必须有两条或以上外出顺序流

2)事件网关后,只能使用intermediateCarchEvent类型(activiti不支持基于事件网关后连接ReceiveTask)

3)连接到事件网关的中间捕获事件必须只有一个入口顺序流

9. 组任务分配

设置多个候选人

之前我们通过设置assignee来灵活的设定负责人。但是日常工作中,还有一类常见的需求无法支持,例如某个订单合同,需要找部门经理级别的负责人签字。而公司中有多个部门经理,业务上只需要找其中任意一个人完成审批就可以了。这种场景下,我们就无法通过设置流程变量的方式来设置负责人。这时,就需要用到activiti提供的另一个利器-任务候选人(Candidate Users)。

这时,可以给任务设置多个候选人candidate-users,多个候选人之间用逗号隔开。

图片11.png