工作流引擎之activiti会签功能实现

什么是会签

在流程业务管理中,任务是通常都是由一个人去处理的,而多个人同时处理一个任务,这种任务我们称之为会签任务。这种业务需求很常见,如一个请款单,领导审批环节中,就需要多个部门领导签字。在流程业务中,我们可以把每个领导签字的环节都定义为任务,并且这个会签的人员是不固定的,若固定的我们可以通过Activiti的并行任务或串行任务来处理。会签的引入说明,无非就是为了流程流转至某一环节点,其审批的人员是动态的,并且需要根据会签审批的结果实现流程的不同流转。

如何利用activiti实现会签功能

原理就是基于activit的多实例任务,将节点设置成多实例,主要通过在UserTask节点的属性上配置

图中的领导审批即为多实例配置

其节点生成的BPMN的配置文件如下所示

<userTask id="usertask2" name="领导审批" activiti:assignee="${leader}">
      <extensionElements>
        <activiti:taskListener event="complete" class="com.demo.activiti.SignTaskListener"></activiti:taskListener>
      </extensionElements>
      <multiInstanceLoopCharacteristics isSequential="false" activiti:collection="${leaderList}" activiti:elementVariable="leader">
        <completionCondition>${nrOfCompletedInstances/nrOfInstances == 1}</completionCondition>
      </multiInstanceLoopCharacteristics>
    </userTask>

userTask节点配置核心属性说明

1、isSequential指定多实例是按照并行或者串行的方式进行,当isSequential=true时,表示的串行执行,即虽然该节点有多条任务,但只有上一条执行完,才可以执行下一条。当isSequential=false时,表示的并行执行,即该节点下的多条任务可以同时执行,如三个人参与会签,是三个人同时收到待办,任务实例是同时产生的。 2、activiti:collection:用于执行该会签环节的参与参与的人,可以用形如实例中的${leaderList}获取,也可以用户可以通过定义自身的服务类来获取 3、activiti:elementVariable:此处表示的是每一个分支都有一个名叫leader的流程变量和userTask节点属性中的activiti:assignee="${leader}"一致 4、completionCondition:指定会签环节的结束条件,表示是任务往下跳转的完成条件,返回true是,表示条件成立,流程会跳至下一审批环节

会签(多实例节点)环节中涉及的几个默认的流程变量

1、nrOfInstances 该会签环节中总共有多少个实例 2、nrOfActiveInstances 当前活动的实例的数量,即还没有 完成的实例数量。 3、nrOfCompletedInstances 已经完成的实例的数量

会签人员部分示例代码

1、分配会签人员

Map<String, Object> vars = new HashMap<>();
        List<String> leaderList = new ArrayList<>();
        leaderList.add("zhangsan");
        leaderList.add("lisi");
        leaderList.add("wangwu");

        vars.put("leaderList", leaderList);

        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), vars);

2、会签人员审批

Map<String, Object> leaderOneAudit = new HashMap<>();
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入领导审批意见。。。。");
        String auditOne = scanner.nextLine();
        leaderOneAudit.put("audit", auditOne);
        System.out.println("张三的审批意见为:" + (auditOne.equals("yes") ? "同意" : "不同意"));
        task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).taskAssignee("zhangsan").singleResult();
        taskService.complete(task.getId(), leaderOneAudit);

3、示例代码控制台结果输出

taskName1:填写申请|assignee:worker
请输入领导审批意见。。。。
yes
张三的审批意见为:同意
流程实例总数:3
当前活动的流程实例总数:3
已经完成实例的数目:0
leader:zhangsan
---------------------------------------分隔线----------------------------------------------------
taskName2:领导审批|assignee:zhangsan
--------------------------------------------
请输入领导审批意见。。。。
yes
李四的审批意见为:同意
流程实例总数:3
当前活动的流程实例总数:2
已经完成实例的数目:1
leader:lisi
---------------------------------------分隔线----------------------------------------------------
taskName3:领导审批|assignee:lisi
--------------------------------------------
请输入领导审批意见。。。。
yes
李四的审批意见为:同意
流程实例总数:3
当前活动的流程实例总数:1
已经完成实例的数目:2
leader:wangwu
---------------------------------------分隔线----------------------------------------------------
taskName4:领导审批|assignee:wangwu
taskName5:人事归档|assignee:hr
----------------------------------流程实例流转-----------------------
任务ID:75014
流程实例ID:75005
任务名称:填写申请
办理人:worker
开始时间:Tue Jul 31 10:12:39 CST 2018
结束时间:Tue Jul 31 10:12:39 CST 2018
=================================
任务ID:75028
流程实例ID:75005
任务名称:领导审批
办理人:zhangsan
开始时间:Tue Jul 31 10:12:39 CST 2018
结束时间:Tue Jul 31 10:12:43 CST 2018
=================================
任务ID:75031
流程实例ID:75005
任务名称:领导审批
办理人:lisi
开始时间:Tue Jul 31 10:12:39 CST 2018
结束时间:Tue Jul 31 10:12:49 CST 2018
=================================
任务ID:75034
流程实例ID:75005
任务名称:领导审批
办理人:wangwu
开始时间:Tue Jul 31 10:12:39 CST 2018
结束时间:Tue Jul 31 10:12:56 CST 2018
=================================
任务ID:75038
流程实例ID:75005
任务名称:人事归档
办理人:hr
开始时间:Tue Jul 31 10:12:56 CST 2018
结束时间:Tue Jul 31 10:12:56 CST 2018
=================================

如何通过代码设置多实例属性

本例中的流程图都是通过ide的插件画出来,而在web开发中,如何通过代码把会签的多实例的属性给实现出来,可能不同人有不同的实现方式。我这边提供一种实现,废话有点多,直接上代码

1、 设置会签节点属性

public static void setMultiInstance(String modelId, String nodelId) throws Exception {
        // 获取模型
        byte[] mes = repositoryService.getModelEditorSource(modelId);
        // 转换成JsonNode
        JsonNode jsonNode = objectMapper.readTree(mes);
        // 转换成BpmnModel
        BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();
        BpmnModel bpmnModel = bpmnJsonConverter.convertToBpmnModel(jsonNode);
        // 获取物理形态的流程
        Process process = bpmnModel.getProcesses().get(0);
        // 获取节点信息
        FlowElement flowElement = process.getFlowElement(nodelId);
        // 只有人工任务才可以设置会签节点
        UserTask userTask = (UserTask) flowElement;
        // 设置受理人,这里应该和ElementVariable的值是相同的
        userTask.setAssignee("${" + Constant.ACT_MUIT_VAR_NAME + "}");
        // userTask.setOwner("${user}");

        // 获取多实例配置
        MultiInstanceLoopCharacteristics characteristics = new MultiInstanceLoopCharacteristics();
        // 设置集合变量,统一设置成users
        characteristics.setInputDataItem(Constant.ACT_MUIT_LIST_NAME);
        // 设置变量
        characteristics.setElementVariable(Constant.ACT_MUIT_VAR_NAME);
        // 设置为同时接收(false 表示不按顺序执行)
        characteristics.setSequential(false);
        // 设置条件(暂时处理成,全部会签完转下步)
        characteristics.setCompletionCondition("${nrOfCompletedInstances==nrOfInstances}");

        userTask.setLoopCharacteristics(characteristics);
        // 保存
        ObjectNode objectNode = new BpmnJsonConverter().convertToJson(bpmnModel);
        repositoryService.addModelEditorSource(modelId, objectNode.toString().getBytes("utf-8"));
    }

2、清空会签属性

public static void clearMultiInstance(String modelId, String nodelId) throws Exception {
        // 获取模型
        byte[] mes = repositoryService.getModelEditorSource(modelId);
        // 转换成JsonNode
        JsonNode jsonNode = new ObjectMapper().readTree(mes);
        // 转换成BpmnModel
        BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();
        BpmnModel bpmnModel = bpmnJsonConverter.convertToBpmnModel(jsonNode);
        // 获取物理形态的流程
        Process process = bpmnModel.getProcesses().get(0);
        // 获取节点信息
        FlowElement flowElement = process.getFlowElement(nodelId);
        // 只有人工任务才可以设置会签节点
        UserTask userTask = (UserTask) flowElement;
        // 清空受理人
        userTask.setAssignee("");
        // 获取多实例配置
        MultiInstanceLoopCharacteristics characteristics = userTask.getLoopCharacteristics();
        if (characteristics != null) {
            // 清空集合
            characteristics.setInputDataItem("");
            // 清空变量
            characteristics.setElementVariable("");
            // 设置为顺序接收(true 表示不按顺序执行)
            characteristics.setSequential(true);
            // 清空条件
            characteristics.setCompletionCondition("");
        }

        // 保存
        ObjectNode objectNode = new BpmnJsonConverter().convertToJson(bpmnModel);
        repositoryService.addModelEditorSource(modelId, objectNode.toString().getBytes("utf-8"));
    }

demo代码链接

https://github.com/lyb-geek/demo-activiti-sign

原文发布于微信公众号 - Linyb极客之路(gh_c420b2cf6b47)

原文发表时间:2018-08-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏斑斓

利用Actor实现管道过滤器模式

《基于Actor的响应式编程》计划分为三部分,第一部分剖析响应式编程的本质思想,为大家介绍何谓响应式编程(Reactive Programming)。第二部分则...

3104
来自专栏美团技术团队

Hades:移动端静态分析框架

作为全球最大的互联网 + 生活服务平台,美团点评近年来在业务上取得了飞速的发展。为支持业务的快速发展,移动研发团队规模也逐渐从零星的小作坊式运营,演变为千人级研...

1113
来自专栏CSDN技术头条

6个编写优质干净代码的技巧

编写干净的代码并不是一件容易的事情,这需要尝试不同的技巧和实践。问题是,在这个问题上有太多的实践和技巧,因此开发人员很难进行选择,所以要把这个问题简化一下。在本...

19710
来自专栏程序员宝库

我的编程之路:知识管理与知识体系

本文的资料放到了Github Repo(https://github.com/wxyyxc1992/Coder-Knowledge-Graph)(本文介绍的这种...

3775
来自专栏web前端教室

《一个陌生同学的留言》-- “老尚,你一定要讲设计模式”

image.png 以前就曾经有人问过我,“老尚,你说是不是有的面试官以虐新人为乐?”,,,我说,“传说中,据说有”,,,他说,“我觉得这应该不是传说。” //...

2038
来自专栏PHP在线

整洁代码之道——重构

写在前面 现在的软件系统开发难度主要在于其复杂度和规模,客户需求也不再像Winston Royce瀑布模型期望那样在系统编码前完成所有的设计满足用户软件需求。...

4406
来自专栏Golang语言社区

厚土Go学习笔记 | 01. Hello World开篇

Go语言的众多特性,是我很长时间以来一直寻找的集合体。一直想在服务器编程方面有一个更舒适的语言。想舒适的实现跨平台,想舒适的实现并发,想舒适的实现纯代码解决问题...

3678
来自专栏牛客网

百度,美团,鼎信,涂鸦面经

8.12 百度运维部共三面(offer) 1.自我介绍 2.聊项目 3.epoll和select的区别,epoll两种触发方式,踩过那些坑 4.讲讲re...

41813
来自专栏快乐八哥

JavaScript中this关键字使用

在Web开发中,前端掌握JavaScript,后台掌握PHP成为一个趋势。当然后台掌握C#/Java,当然还有Python,Ruby其中的一种,都是可以的。其实...

2359
来自专栏企鹅号快讯

神级程序员教你如何写代码——十年编程内功心法

写代码就是学一门语言然后开始撸代码吗?看完了我一系列文章的同学或者本身已经就是老鸟的同学显然不会这么认为。编程是一项非常严谨的工作!虽然我们自嘲为码农,但是这工...

2175

扫码关注云+社区

领取腾讯云代金券