• Flowable 之任务分配



    提示:以下是本篇文章正文内容,Java 系列学习将会持续更新

    在这里插入图片描述

    一、任务分配和流程变量

    1.1 任务分配

    1.1.1 固定分配

    固定分配就是我们前面介绍的,在绘制流程图或者直接在流程文件中通过 Assignee 来指定的方式。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    1.1.2 表达式分配

    Flowable 使用 UEL 进行表达式解析。UEL代表Unified Expression Language,是EE6规范的一部分.Flowable支持两种UEL表达式: UEL-value 和 UEL-method。

    ① 值表达式

    值表达式 Value expression:解析为一个值。默认情况下,所有流程变量都可以使用。(若使用Spring)所有的Spring bean也可以用在表达式里。例如
    在这里插入图片描述
    可以看到通过表达式处理的效果。
    在这里插入图片描述

    先部署流程,然后在启动流程实例的时候绑定表达式对应的值。

    /**
     * 启动流程实例
     */
    @Test
    public void testRunProcess(){
        // 设置 assignee 的取值
        Map<String,Object> variables = new HashMap<>();
        variables.put("assignee0","张三") ;
        variables.put("assignee1","李四"); 
        // 启动流程实例,第一个参数是流程定义的id
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceById("MyHolidayUI:1:4", variables);// 启动流程实例
        // 输出相关的流程实例信息
        System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
        System.out.println("流程实例的ID:" + processInstance.getId());
        System.out.println("当前活动的ID:" + processInstance.getActivityId());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在流程变量表中我们可以看到对应的流程变量信息。
    在这里插入图片描述
    同时在 Task 表中,可以看到流程当前的分配人是 张三,说明 UEL 表达式被解析了。
    在这里插入图片描述

    ② 方法表达式

    方法表达式 Method expression: 调用一个方法,可以带或不带参数。当调用不带参数的方法时,要确保在方法名后添加空括号(以避免与值表达式混淆)。传递的参数可以是字面值(literal value),也可以是表达式,它们会被自动解析。例如:

    ${printer.print()}
    ${myBean.addNewOrder('orderName')}
    ${myBean.doSomething(myVar, execution)}
    
    • 1
    • 2
    • 3

    myBean 是 Spring 容器中的个 Bean 对象,表示调用的是 bean 的 addNewOrder 方法。

    1.1.3 监听器分配

    可以使用监听器来完成很多 Flowable 的流程业务。我们在此处使用监听器来完成负责人的指定,那么我们在流程设计的时候就不需要指定 assignee。

    ①创建自定义监听器

    public class MyTaskListener implements TaskListener {
        @Override
        public void notify(DelegateTask delegateTask) {
            System.out.println("MyTaskListener 监听器被触发了!");
            if("提交请假流程".equals(delegateTask.getName()) && "create".equals(delegateTask.getEventName())) {
                delegateTask.setAssignee("小明");
            }else {
                delegateTask.setAssignee("王经理");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ②然后在 FlowableUI 中关联对应的监听器

    • create:任务创建后触发。
    • assignment:任务分配后触发。
    • Delete:任务完成后触发。
    • All:所有事件都触发。

    在这里插入图片描述
    效果如下:
    在这里插入图片描述

    ③然后我们启动流程,执行查看效果
    在这里插入图片描述

    在 Task 表中我们可以看到对应的分配人为 小明 说明通过监听也完成了任务分配的工作了。在这里插入图片描述

    回到目录…

    1.2 流程变量

      流程实例按步骤执行时,需要使用一些数据。在Flowable中,这些数据称作变量(variable),并会存储在数据库中。变量可以用在表达式中(例如在排他网关中用于选择正确的出口路径),也可以在Java服务任务(service task)中用于调用外部服务(例如为服务调用提供输入或结果存储)等等。

      流程实例可以持有变量(称作流程变量 process variables);用户任务以及执行(executions)——流程当前活动节点的指针——也可以持有变量。流程实例可以持有任意数量的变量,每个变量存储为ACT_RU_VARIABLE数据库表的一行。

    所有的 startProcessInstanceXXX 方法都有一个可选参数,用于在流程实例创建及启动时设置变量。例如,在 RuntimeService 中:

    ProcessInstance startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables);
    
    • 1

    也可以在流程执行中加入变量。例如,(RuntimeService):

    void setVariable(String executionId, String variableName, Object value);
    void setVariableLocal(String executionId, String variableName, Object value);
    void setVariables(String executionId, Map<String, ? extends Object> variables);
    void setVariablesLocal(String executionId, Map<String, ? extends Object> variables);
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    1.2.1 全局变量

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

    注意:如: Global变量:userId(变量名)、zhangsan(变量值)

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

    1.2.2 局部变量

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

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

    1.2.3 案例讲解

    当部门经理进行审批任务时,他需要设置流程变量为下一步流程指明方向。
    在这里插入图片描述

    ①部署流程。

    @Test
    public void deploy(){
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("evectionUI.bpmn20.xml")
                .name("20230904出差申请")
                .deploy();
        System.out.println("deploy.getId() = " + deploy.getId());
        System.out.println("deploy.getName() = " + deploy.getName());
        System.out.println("deploy.getCategory() = " + deploy.getCategory());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ②启动流程实例:并且指定全局流程变量。

    /**
     * 在启动流程实例的时候设置流程变量
     */
    @Test
    public void runProcess(){
        // 设置流程变量
        Map<String, Object> variables = new HashMap<>();
        variables.put("worker", "张三");
        variables.put("manager1", "张经理");
        variables.put("manager2", "王总经理");
        variables.put("manager3", "孙钱务");
        // 启动流程实例,第一个参数是流程定义的id
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceById("evection:1:4",variables);// 启动流程实例
        // 输出相关的流程实例信息
        System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
        System.out.println("流程实例的ID:" + processInstance.getId());
        System.out.println("当前活动的ID:" + processInstance.getActivityId());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    ③完成 Task 任务,同时也可以指定流程变量。

    /**
     * 完成任务时指定流程变量
     */
    @Test
    public void completeTask(){
        Task task = taskService.createTaskQuery()
                .processDefinitionId("evection:1:4")
                .taskAssignee("李四")
                .singleResult();
        // 添加流程变量
        Map<String, Object> map = task.getProcessVariables();
        map.put("num",4);
        // 完成任务
        taskService.complete(task.getId(),map);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    当然,我们也可以在处理流程之外通过 Task 编号来修改流程变量。

    /**
     * 通过当前任务设置
     */
    @Test
    public void currentTask(){
        Task task = taskService.createTaskQuery()
                .processDefinitionId("evection:1:4")
                .taskAssignee("王五")
                .singleResult();
        // 添加流程变量
        Map<String, Object> map = task.getProcessVariables();
        map.put("num",1);
        //  一次设置多个值 设置局部变量
        taskService.setVariables(task.getId(), map);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    回到目录…

    二、候选人和候选人组

    在流程定义中在任务结点的 assignee 固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn 文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。针对这种情况可以给任务设置多个候选人或者候选人组,可以从候选人中选择参与者来完成任务。

    2.1 候选人

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2.1.1 部署和启动流程

    // 部署流程
    @Test
    public void deploy(){
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("Test3.bpmn20.xml")
                .name("请求流程-候选人")
                .deploy();
        System.out.println("deploy.getId() = " + deploy.getId());
        System.out.println(deploy.getName());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    启动流程实例,并为候选人赋值。

    @Test
    public void runProcess(){
        // 给流程定义中的UEL表达式赋值
        Map<String,Object> variables = new HashMap<>();
        variables.put("manager1","张经理");
        variables.put("manager2","李经理");
        variables.put("manager3","王经理");
        runtimeService.startProcessInstanceById("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b", variables);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    act_ru_variable 表中有了三个候选人的变量信息:
    在这里插入图片描述

    2.1.2 任务的查询

    根据候选人查询可拾取的任务。

    @Test
    void queryTaskCandidate() {
        List<Task> tasks = taskService.createTaskQuery()
                .processDefinitionId("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b")
                .taskCandidateUser("李经理")
                .list();
        for(Task task : tasks) {
            System.out.println("task.getId() = " + task.getId());
            System.out.println("task.getName() = " + task.getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    act_ru_task 表中的任务执行人还是 null,但候选人是可以查出来该任务的。
    在这里插入图片描述

    2.1.3 任务的拾取

    候选人知道有可拾取的任务后,拾取任务。一个候选人拾取任务后,其他候选人就不能再拾取该任务了。

    @Test
    void claimTaskCandidate() {
        Task task = taskService.createTaskQuery()
                .processDefinitionId("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b")
                .taskCandidateUser("李经理")
                .singleResult();
        if(task != null) {
            taskService.claim(task.getId(), "李经理");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    拾取后,act_ru_task 表中的任务执行人也显示出来了。
    在这里插入图片描述

    2.1.4 任务的退还

    @Test
    void unclaimTaskCandidate() {
        Task task = taskService.createTaskQuery()
                .processDefinitionId("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b")
                .taskAssignee("李经理")
                .singleResult();
        if(task != null) {
            taskService.unclaim(task.getId());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    退还后,act_ru_task 表中的任务执行人又变为 null。
    在这里插入图片描述

    2.1.5 任务的交接

    如果我获取了任务,但是不想执行,那么我可以把这个任务交接给其他的用户。

    @Test
    void taskCandidate(){
        // 李经理 --> 王经理
        Task task = taskService.createTaskQuery()
                .processDefinitionId("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b")
                .taskAssignee("李经理")
                .singleResult();
        if(task != null) {
            taskService.setAssignee(task.getId(), "王经理");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    交接前,act_ru_task 表中的任务执行人为 “李经理”。
    在这里插入图片描述
    交接后,act_ru_task 表中的任务执行人为 “王经理”。
    在这里插入图片描述

    2.1.6 任务的完成

    由被指派的任务执行人 assignee 完成任务。

    @Test
    void complete() {
        Task task = taskService.createTaskQuery()
                .processDefinitionId("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b")
                .taskAssignee("王经理")
                .singleResult();
        if(task != null) {
            taskService.complete(task.getId());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    流程结束,act_ru_task 表中的信息被清空。
    在这里插入图片描述

    回到目录…

    2.2 候选人组

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2.2.1 管理用户和组

    ①创建用户

    @Test
    void createUser() {
        User user = identityService.newUser("诸葛亮");
        user.setFirstName("亮");
        user.setLastName("孔明");
        user.setEmail("123456@163.com");
        user.setPassword("123456");
        identityService.saveUser(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在 act_id_user 表中可以看到创建的用户信息。

    ②创建组

    @Test
    void createGroup() {
        Group group = identityService.newGroup("group2");
        group.setName("生产部");
        group.setType("type2");
        identityService.saveGroup(group);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在 act_id_group 表中可以看到创建的组信息。
    在这里插入图片描述

    ③将用户分配给组

    @Test
    void userGroup() {
        Group group = identityService.createGroupQuery()
                .groupId("group2")
                .singleResult();
        User user = identityService.createUserQuery()
                .userId("诸葛亮")
                .singleResult();
        identityService.createMembership(user.getId(), group.getId());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在 act_id_membership 表中可以看到用户和组的关系了。

    2.2.2 流程的部署启动

    部署流程定义:

    @Test
    public void deploy(){
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("Test4-候选人组.bpmn20.xml")
                .name("请求流程-候选人组")
                .deploy();
        System.out.println("deploy.getId() = " + deploy.getId());
        System.out.println(deploy.getName());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    启动流程实例:需要将候选组ID添加到运行时变量表中。

    @Test
    void runProcess(){
        List<Group> groups = identityService.createGroupQuery().list();
        Group group1 = groups.get(0);
        Group group2 = groups.get(1);
        Map<String,Object> variables = new HashMap<>();
        variables.put("g1", group1.getId());
        variables.put("g2", group2.getId());
        runtimeService.startProcessInstanceById("test4:1:44ae7634-4bb2-11ee-9fc5-200db0c7aa5b", variables);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.2.3 任务的拾取完成

    任务的查询和拾取:根据登录的用户查询对应的组,再根据组查询可以拾取的任务。

    @Test
    void queryTaskCandidateGroup() {
        String userId = "赵云";
        Group group = identityService.createGroupQuery().groupMember(userId).singleResult();
        Task task = taskService.createTaskQuery()
                .processDefinitionId("test4:1:44ae7634-4bb2-11ee-9fc5-200db0c7aa5b")
                .taskCandidateGroup(group.getId())
                .singleResult();
        if(task != null) {
            taskService.claim(task.getId(), userId);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    完成任务。

    @Test
    void complete() {
        Task task = taskService.createTaskQuery()
                .processDefinitionId("test4:1:44ae7634-4bb2-11ee-9fc5-200db0c7aa5b")
                .taskAssignee("赵云")
                .singleResult();
        if(task != null) {
            taskService.complete(task.getId());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    回到目录…


    总结:
    提示:这里对文章进行总结:
    本文是对flowable的进阶学习,学习了流程中任务分配的方式,候选人和候选组拾取任务,以及四种网关的使用。之后的学习内容将持续更新!!!

  • 相关阅读:
    世上最全NVDIA GPU参数列表: 3090,4090, A40, A30,V100, A100, A800性能参数
    将asp.net core程序部署到Linux服务器上(1)
    Mybatis插件功能
    线性代数学习笔记9-2:基变换(求变换后的坐标、同一个线性变换不同相似矩阵)、图像压缩(基变换的应用)
    短视频账号矩阵源码saas开发-----
    vscode按ctrl+鼠标左键没反应
    神经网络算法应用广泛吗,神经网络算法应用实例
    关于.Net 7.0 RC gRPC JSON 转码为 Swagger/OpenAPI文档的注意事项
    什么是云计算?应用场景有哪些?
    二分查找(c语言)
  • 原文地址:https://blog.csdn.net/qq15035899256/article/details/132711421