系列学习 SpringBoot + Activiti7 工作流之第 4 篇 —— 【进阶篇】流程变量

查看之前的博客可以点击顶部的【分类专栏】
 

流程变量

1、什么是流程变量?

流程变量在 activiti 中是一个非常重要的角色,流程运转有时需要靠流程变量,业务系统和 activiti 结合时少不了流程变量,流程变量就是 activiti 在管理工作流时根据管理需要而设置的变量
比如:在请假流程流转时如果请假天数大于 3 天则由总经理审核,否则由主管直接审核, 请假天数就可以设置为流程变量,在流程流转时使用。 

注意:虽然流程变量中可以存储业务数据可以通过 activiti 的api查询流程变量从而实现查询业务数据,但是不建议这样使用,因为业务数据查询由业务系统负责,尽量不要存放到 Activiti 流程管理中,activiti 设置流程变量是为了流程执行需要而创建。

2、流程变量类型

如果将 pojo 存储到流程变量中,必须实现序列化接口 serializable,为了防止由于新增字段无法反序列化,还需要生成 serialVersionUID。 

 

3、流程变量作用域

流程变量的作用域可以是一个流程实例(processInstance),或一个任务(task),或一个执行实例(execution)

3-1 globa变量(全局变量)

流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程定义时,可以称为 global 变量。

global 变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前面设置的变量值。

 

3-2 local变量(局部变量)


任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大, 称为 local 变量。

Local 变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local 变量名也可以和 global 变量名相同,没有影响。 
 

3-3 流程变量的使用方法 

1、在属性上使用UEL表达式

可以在 assignee 处设置 UEL 表达式,表达式的值为任务的负责人,比如: ${assignee}, assignee 就是一个流程变量名称。

Activiti获取UEL表达式的值,即流程变量assignee的值 ,将assignee的值作为任务的负责人进行任务分配。
 

2、在连线上使用UEL表达式

可以在连线上设置UEL表达式,决定流程走向。
比如:${price<10000} 。price就是一个流程变量名称,uel表达式结果类型为布尔类型。如果UEL表达式是true,要决定流程执行走向。
 

3-4 使用Global变量控制流程

需求:请假流程。员工请假,主管审批,如果在3天内(包含3天),部门经理审批,通过。如果超过3天,还要增加人资审批。

我们增加 apply2.bpmn 文件:

如果超过 3 天也要设置条件值。 

 

 

 

最终效果图:

 

完整代码:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1623605100648" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="apply2" isClosed="false" isExecutable="true" name="请假流程2" processType="None">
    <startEvent id="_2" name="创建请假流程"/>
    <endEvent id="_3" name="请假流程结束"/>
    <userTask activiti:assignee="${assignee0}" activiti:exclusive="true" id="_4" name="主管审批">
      <extensionElements>
        <activiti:taskListener class="com.study.listener.MyListener" event="create"/>
      </extensionElements>
    </userTask>
    <userTask activiti:assignee="${assignee1}" activiti:exclusive="true" id="_5" name="经理审批">
      <extensionElements>
        <activiti:taskListener class="com.study.listener.MyListener" event="create"/>
      </extensionElements>
    </userTask>
    <userTask activiti:assignee="${assignee2}" activiti:exclusive="true" id="_6" name="人资审批">
      <extensionElements>
        <activiti:taskListener class="com.study.listener.MyListener" event="create"/>
      </extensionElements>
    </userTask>
    <sequenceFlow id="_7" sourceRef="_2" targetRef="_4"/>
    <sequenceFlow id="_8" sourceRef="_4" targetRef="_5"/>
    <sequenceFlow id="_9" sourceRef="_5" targetRef="_6">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${applyEntity.day > 3}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_10" sourceRef="_5" targetRef="_3">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${applyEntity.day <= 3}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_12" sourceRef="_6" targetRef="_3"/>
  </process>
  <bpmndi:BPMNDiagram documentation="background=#FFFFFF;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
    <bpmndi:BPMNPlane bpmnElement="apply2">
      <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
        <omgdc:Bounds height="32.0" width="32.0" x="235.0" y="70.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
        <omgdc:Bounds height="32.0" width="32.0" x="235.0" y="485.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
        <omgdc:Bounds height="55.0" width="85.0" x="210.0" y="160.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
        <omgdc:Bounds height="55.0" width="85.0" x="210.0" y="265.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
        <omgdc:Bounds height="55.0" width="85.0" x="360.0" y="380.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_6" targetElement="_3">
        <omgdi:waypoint x="360.0" y="407.5"/>
        <omgdi:waypoint x="267.0" y="501.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7" sourceElement="_2" targetElement="_4">
        <omgdi:waypoint x="251.0" y="102.0"/>
        <omgdi:waypoint x="251.0" y="160.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_4" targetElement="_5">
        <omgdi:waypoint x="252.5" y="215.0"/>
        <omgdi:waypoint x="252.5" y="265.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9" sourceElement="_5" targetElement="_6">
        <omgdi:waypoint x="295.0" y="292.5"/>
        <omgdi:waypoint x="360.0" y="407.5"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="_5" targetElement="_3">
        <omgdi:waypoint x="251.0" y="320.0"/>
        <omgdi:waypoint x="251.0" y="485.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

然后我们创建一个 ApplyEntity(后者叫其它 Entity 都可以,只需要在创建流程的时候赋一个属性 applyEntity 就可以了),

package com.study.entity;

import lombok.Data;

import java.io.Serializable;

/**
 * @author biandan
 * @description
 * @signature 让天下没有难写的代码
 * @create 2021-06-14 上午 11:10
 */
@Data
public class ApplyEntity implements Serializable {

    private static final long serialVersionUID = 3605953160343375618L;

    //主键id
    private Integer id;

    //请假人员
    private String username;

    //请假天数,可能有0.5天
    private Double day;

    //请假事由
    private String reason;

}

如果将 pojo 存储到流程变量中,必须实现序列化接口 serializable,为了防止由于新增字段无法反序列化,还需要生成 serialVersionUID。 

然后流程定义的部署。部署 apply2 流程定义。

    /**
     * 部署流程定义
     */
    @Test
    public void testApply2Deployment() {
        //创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //得到 RepositoryService 实例
        RepositoryService service = processEngine.getRepositoryService();
        //使用 RepositoryService 实例进行部署
        Deployment deployment = service.createDeployment()
                .addClasspathResource("bpmn/apply2.bpmn") //添加 bpmn 资源
                .addClasspathResource("bpmn/apply2.png") //添加 png 资源
                .name("请假流程2")
                .deploy();
        System.out.println("流程部署Id=" + deployment.getId());
        System.out.println("流程部署name=" + deployment.getName());
    }

流程部署Id=40001
流程部署name=请假流程2

然后编写启动流程的测试类:

    /**
     * 启动流程实例
     */
    @Test
    public void testStartApply2Process() {
        //1、创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2、获取 RunTimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();

        //创建 ApplyEntity 实体对象
        ApplyEntity applyEntity = new ApplyEntity();
        applyEntity.setUsername("吴某");
        applyEntity.setDay(new Double("2.5"));//请假2.5天
        applyEntity.setReason("回家相亲");

        //设置 assignee 的取值,用户可以在页面上设置流程的执行
        Map<String, Object> map = new HashMap<>();
        map.put("assignee0", "刘主管");
        map.put("assignee1", "杨经理");
        map.put("assignee2", "朱人资");
        //把整个对象放进去,会自动查找 day 的参数
        map.put("applyEntity", applyEntity);

        //根据流程定义ID启动流程
        ProcessInstance instance = runtimeService.startProcessInstanceByKey("apply2", map);
        System.out.println("流程定义id=" + instance.getProcessDefinitionId());
        System.out.println("流程实例id=" + instance.getId());
    }

测试结果:

流程定义id=apply2:1:40004
流程实例id=42501

数据库:act_ru_variable

 

然后模拟【刘主管】完成任务,也就是审批通过。

   /**
     * 完成任务
     */
    @Test
    public void completeApply2Task(){
        //1、创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();

        String key = "apply2";
        String assingee = "刘主管";

        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskAssignee(assingee)
                .singleResult();
        //判断是否当前人执行
        if(task != null){
            taskService.complete(task.getId());
            System.out.println(assingee+" 的任务执行完毕!");
        }else{
            System.out.println("当前节点未流转到:"+assingee+",无需处理!");
        }
    }

结果:

刘主管 的任务执行完毕!

然后流程就到 【杨经理】审批,查看数据库表:act_ru_task

 

然后,我们让【杨经理】完成任务,即审批通过。

只需要修改 assignee 为【杨经理】即可。

测试

杨经理 的任务执行完毕!

说明:

因为请假天数 2.5 天,小于3天,因此审批流程直接结束了。这时候,查看数据库表:act_ru_task 已经不存在这条记录了。因为:

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

我们可以查看历史记录信息:ACT_HI

历史的流程实例表:act_hi_actinst

历史的任务实例表:act_hi_taskinst    

 

然后我们测试请假天数大于3天的情况:修改以下信息,点击运行。

测试

流程定义id=apply2:1:40004
流程实例id=50001

然后【刘主管】审批通过

测试

刘主管 的任务执行完毕!

然后【杨经理】审批通过:

测试

杨经理 的任务执行完毕!

查看数据库表:act_ru_task   这时候需要人资审批。

让【朱人资】审批通过;

测试:

朱人资 的任务执行完毕!

审批流程结束!这时候,查看数据库表:act_ru_task 已经不存在这条记录了。

这就是 Global 全局变量控制流程案例。

3-5-1 注意事项

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

2、  如果UEL表达式中流程变量值为空NULL,流程不按UEL表达式去执行,而流程结束 。

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

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

 

 

3-5 设置local流程变量

任务办理时设置:任务办理时设置local流程变量,当前运行的流程实例只能在该任务结束前使用任务结束该变量无法在当前流程实例使用,可以通过查询历史任务查询。

怎么理解【任务结束该变量无法在当前流程实例使用】呢,比如:刘主管审批时,重新设置请假天数为2天,那么当刘主管审批结束后,天数为2的变量就失效了。

例子:

测试

流程定义id=apply2:1:40004
流程实例id=60001

然后到【刘主管】审批,我们修改天数为2,完整代码:

    /**
     * 完成任务
     */
    @Test
    public void completeApply2Task(){
        //1、创建 ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();

        String key = "apply2";
        String assingee = "刘主管";
        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskAssignee(assingee)
                .singleResult();
        //判断是否当前人执行
        if(task != null){
            Map<String, Object> map = new HashMap<>();
            ApplyEntity applyEntity = new ApplyEntity();
            applyEntity.setDay(new Double("2"));//local 变量改为2天
            map.put("applyEntity", applyEntity);
            taskService.setVariablesLocal(task.getId(), map);

            taskService.complete(task.getId());
            System.out.println(assingee+" 的任务执行完毕!");
        }else{
            System.out.println("当前节点未流转到:"+assingee+",无需处理!");
        }
    }

测试

刘主管 的任务执行完毕!

然后,测试【杨经理】完成任务。

测试:

杨经理 的任务执行完毕!

然后,查看数据库:

说明请假天数超过了3天,需要人资审批。进而说明,在【刘主管】完成任务的时候,临时变量 day=2 在下一个审批节点【杨经理】是不生效的。

 

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值