前言

实际业务中的会签,代表一个任务节点由多个人进行审批,可能会衍生出多种情况:

  • 只要一个人审批后,就到下一个节点
  • 全部审批后,才能到下一个节点
  • 审批人数占比多少,就到下一个节点,本案例就选择这个最麻烦的。

在Flowable BPMN 用户手册中,”会签“的介绍在”多实例“的章节,也就是说,会签,其实就是多实例。

多实例活动(multi-instance activity)是在业务流程中,为特定步骤定义重复的方式。在编程概念中,多实例类似for each结构:可以为给定集合中的每一条目,顺序或并行地,执行特定步骤,甚至是整个子流程。多实例是一个普通活动,加上定义(被称作“多实例特性的”)额外参数,会使得活动在运行时被多次执行。

具体的可参考:https://tkjohn.github.io/flowable-userguide/#bpmnBusinessRuleTask
特别注意其描述的xml部分:
要将活动变成多实例,该活动的XML元素必须有multiInstanceLoopCharacteristics子元素

<multiInstanceLoopCharacteristics isSequential="false|true">...
</multiInstanceLoopCharacteristics>

isSequential属性代表了活动的实例为顺序还是并行执行。
实例的数量在进入活动时,计算一次。有几种不同方法可以配置数量。一个方法是通过loopCardinality子元素,直接指定数字。

<multiInstanceLoopCharacteristics isSequential="false|true"><loopCardinality>5</loopCardinality>
</multiInstanceLoopCharacteristics>

也可以使用解析为正整数的表达式:

<multiInstanceLoopCharacteristics isSequential="false|true"><loopCardinality>${nrOfOrders-nrOfCancellations}</loopCardinality>
</multiInstanceLoopCharacteristics>

另一个定义实例数量的方法,是使用loopDataInputRef子元素,指定一个集合型流程变量的名字。对集合中的每一项,都会创建一个实例。可以使用inputDataItem子元素,将该项设置给该实例的局部变量。在下面的XML示例中展示:

<userTask id="miTasks" name="My Task ${loopCounter}" flowable:assignee="${assignee}"><multiInstanceLoopCharacteristics isSequential="false"><loopDataInputRef>assigneeList</loopDataInputRef><inputDataItem name="assignee" /></multiInstanceLoopCharacteristics>
</userTask>

假设变量assigneeList包含[kermit, gonzo, fozzie]。上面的代码会创建三个并行的用户任务。每一个执行都有一个名为assignee的(局部)流程变量,含有集合中的一项,并在这个例子中被用于指派用户任务。

loopDataInputRef与inputDataItem的缺点是名字很难记,并且由于BPMN 2.0概要的限制,不能使用表达式。Flowable通过在multiInstanceCharacteristics上提供collection与elementVariable属性解决了这些问题:

<userTask id="miTasks" name="My Task" flowable:assignee="${assignee}"><multiInstanceLoopCharacteristics isSequential="true"flowable:collection="${myService.resolveUsersForTask()}" flowable:elementVariable="assignee" ></multiInstanceLoopCharacteristics>
</userTask>

请注意collection属性会作为表达式进行解析。如果表达式解析为字符串而不是一个集合,不论是因为本身配置的就是静态字符串值,还是表达式计算结果为字符串,这个字符串都会被当做变量名,在流程变量中用于获取实际的集合。

例如,下面的代码片段会要求集合存储在assigneeList流程变量中:

<userTask id="miTasks" name="My Task" flowable:assignee="${assignee}"><multiInstanceLoopCharacteristics isSequential="true"flowable:collection="assigneeList" flowable:elementVariable="assignee" ></multiInstanceLoopCharacteristics>
</userTask>

假如myService.getCollectionVariableName()返回字符串值,引擎就会用这个值作为变量名,获取流程变量保存的集合。

<userTask id="miTasks" name="My Task" flowable:assignee="${assignee}"><multiInstanceLoopCharacteristics isSequential="true"flowable:collection="${myService.getCollectionVariableName()}" flowable:elementVariable="assignee" ></multiInstanceLoopCharacteristics>
</userTask>

多实例活动在所有实例都完成时结束。然而,也可以指定一个表达式,在每个实例结束时进行计算。当表达式计算为true时,将销毁所有剩余的实例,并结束多实例活动,继续执行流程。这个表达式必须通过completionCondition子元素定义。

<userTask id="miTasks" name="My Task" flowable:assignee="${assignee}"><multiInstanceLoopCharacteristics isSequential="false"flowable:collection="assigneeList" flowable:elementVariable="assignee" ><completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition></multiInstanceLoopCharacteristics>
</userTask>

在这个例子里,会为assigneeList集合中的每个元素创建并行实例。当60%的任务完成时,其他的任务将被删除,流程继续运行。

接下来具体介绍本文的案例

案例中,只有一个风控审批的节点,审核人员是风控人员组的成员,审核人数占比超过50%就结束会签。

流程

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef" exporter="Flowable Open Source Modeler" exporterVersion="6.7.2"><process id="shenpi1" name="审批1" isExecutable="true"><startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent><userTask id="sid-B0A762DE-D600-4998-96D6-49F7BE676159" name="风控审批" flowable:candidateGroups="${风控人员组}" flowable:formFieldValidation="true"><multiInstanceLoopCharacteristics isSequential="false" flowable:collection="persons" flowable:elementVariable="person"><extensionElements></extensionElements><completionCondition>${compCondition.getComCondition(execution)}</completionCondition></multiInstanceLoopCharacteristics></userTask><sequenceFlow id="sid-0129BF6A-C57C-4383-A14C-879FD48EED4E" sourceRef="startEvent1" targetRef="sid-B0A762DE-D600-4998-96D6-49F7BE676159"></sequenceFlow><endEvent id="sid-80CB6AF8-5DD5-4AC1-A1B2-05A5569B402D"></endEvent><sequenceFlow id="sid-3349C437-5FF2-4565-B2CD-75CCA54FAF03" sourceRef="sid-B0A762DE-D600-4998-96D6-49F7BE676159" targetRef="sid-80CB6AF8-5DD5-4AC1-A1B2-05A5569B402D"></sequenceFlow></process><bpmndi:BPMNDiagram id="BPMNDiagram_shenpi1"><bpmndi:BPMNPlane bpmnElement="shenpi1" id="BPMNPlane_shenpi1"><bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1"><omgdc:Bounds height="30.0" width="30.0" x="100.0" y="163.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="sid-B0A762DE-D600-4998-96D6-49F7BE676159" id="BPMNShape_sid-B0A762DE-D600-4998-96D6-49F7BE676159"><omgdc:Bounds height="80.0" width="100.0" x="225.0" y="135.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="sid-80CB6AF8-5DD5-4AC1-A1B2-05A5569B402D" id="BPMNShape_sid-80CB6AF8-5DD5-4AC1-A1B2-05A5569B402D"><omgdc:Bounds height="28.0" width="28.0" x="435.0" y="161.00000216744158"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNEdge bpmnElement="sid-3349C437-5FF2-4565-B2CD-75CCA54FAF03" id="BPMNEdge_sid-3349C437-5FF2-4565-B2CD-75CCA54FAF03" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="2.0" flowable:targetDockerY="14.0"><omgdi:waypoint x="324.9499999999799" y="175.0000006682945"></omgdi:waypoint><omgdi:waypoint x="434.99999863624566" y="175.00000214068302"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="sid-0129BF6A-C57C-4383-A14C-879FD48EED4E" id="BPMNEdge_sid-0129BF6A-C57C-4383-A14C-879FD48EED4E" flowable:sourceDockerX="15.0" flowable:sourceDockerY="15.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0"><omgdi:waypoint x="129.9474255708386" y="177.71879843198602"></omgdi:waypoint><omgdi:waypoint x="225.0" y="175.9365625"></omgdi:waypoint></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</definitions>

注意

这里有必要说一下设置的重点,我采用的是flowable-ui绘制的

  • 多实例类型:并行或者串行,案例中是并行的
  • 基数:创造几个实例,除非特别固定,否则不会用这个
  • 集合:很常用,比如案例中风控审批人员组有多少个人,就根据这个集合创造多少实例,也就是多少人来会签
  • 元素:对应集合中的元素,也就是每个风控人员,会使用变量保存下来
  • 完成条件:什么条件下会签结束,我这里传递了一个类的方法${compCondition.getComCondition(execution)},当有人会签后,就调用这个方法,计算会签完成比例是否大于0.5,如果是,就会签结束,没有完成的task自动结束。

代码核心

 @Transactionalpublic void startProcess() {Map<String, Object> variables = new HashMap<String, Object>();//根据当前用户获取所在的用户组Group group = identityService.createGroupQuery().groupMember("三井寿1").singleResult();variables.put("风控人员组",group.getId());//获取当前组的人员集合List<String> persons = identityService.createUserQuery().memberOfGroup(group.getId()).list().stream().map((li) -> {return li.getId();}).collect(Collectors.toList());variables.put("persons",persons);runtimeService.startProcessInstanceByKey("shenpi1",variables);}//领取任务,act_ru_task表中ASSIGNEE_字段之前为空,谁领取就变成谁。@Transactionalpublic void claimTask() {//根据当前用户获取所在的组Group group = identityService.createGroupQuery().groupMember("三井寿2").singleResult();//根据组查询任务,查询到没有分配处理人的任务就申领这个任务,然后结束循环List<Task> list = taskService.createTaskQuery().processInstanceId("a4bdf5bb-b57b-11ec-85bf-3c9c0f202230").taskCandidateGroup(group.getId()).list();if (list != null){for (int i = 0; i < list.size(); i++) {Task task = list.get(i);if (task.getAssignee() == null){taskService.claim(task.getId(),"三井寿2");//领取任务System.out.println(task.getAssignee());break;}}}}//    完成任务@Transactionalpublic void completeTask() {Task task = taskService.createTaskQuery().processInstanceId("a4bdf5bb-b57b-11ec-85bf-3c9c0f202230").taskAssignee("三井寿2").singleResult();if (task != null){taskService.complete(task.getId());}}@Transactionalpublic void addGroupAndUser() {//实际开发中,可将本身的用户表、角色表、部门表以及之间的关系同步到flowable的act_id_user、act_id_group、act_id_membership表中//其中角色和部门可当作组User user3 =identityService.newUser("三井寿1");user3.setFirstName("三井");user3.setLastName("寿1");identityService.saveUser(user3);//注意必须这样保存User user4 =identityService.newUser("三井寿2");user4.setFirstName("三井");user4.setLastName("寿2");identityService.saveUser(user4);//注意必须这样保存User user5 =identityService.newUser("三井寿3");user5.setFirstName("三井");user5.setLastName("寿3");identityService.saveUser(user5);//注意必须这样保存Group group2 = identityService.newGroup("风控人员组");group2.setName("风控员");identityService.saveGroup(group2);//注意必须这样保存//创建关联关系identityService.createMembership("三井寿1","风控人员组");identityService.createMembership("三井寿2","风控人员组");identityService.createMembership("三井寿3","风控人员组");

完成条件代码

package com.example.flowable.service;import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;import java.io.Serializable;@Component("compCondition")
public class CompCondition implements Serializable {//    多实例活动在所有实例都完成时结束。
//    然而,也可以指定一个表达式(或类的方法),在每个实例结束时进行计算。
//    当表达式计算为true时,将销毁所有剩余的实例,并结束多实例活动,继续执行流程。
//    这个表达式必须通过completionCondition子元素定义。public boolean getComCondition(DelegateExecution execution){Object nrOfInstances = execution.getVariable("nrOfInstances");//实例总数Object nrOfActiveInstances = execution.getVariable("nrOfActiveInstances");//未完成的实例Object nrOfCompletedInstances = execution.getVariable("nrOfCompletedInstances");//已完成实例System.out.println("总实例数量"+Integer.parseInt(nrOfCompletedInstances.toString()));System.out.println("未完成的实例"+Integer.parseInt(nrOfActiveInstances.toString()));System.out.println("已完成实例"+Integer.parseInt(nrOfCompletedInstances.toString()));if (Float.parseFloat(nrOfCompletedInstances.toString())/Float.parseFloat(nrOfInstances.toString()) > 0.5){return true;//如果完成的比例高于50%就返回ture,代表会签结束,没有完成的任务就自动结束了}else {return false;//如果完成的比例不高于50%就返回false,还需要继续会签}}
}

测试与内部机制思考

当部署后开启实例,在风控审批节点创造三个任务,因为会签组有三个人。

我们分别用会签组成员去领取任务并完成,当第一个完成后,还剩两个任务。

然后再完成一个任务,任务列表就没有了,原因是,根据”完成条件代码“,超过了50%的成员会签了,其余任务就自动结束。

可以查看act_hi_taskinst 历史任务表,确实有个任务没有申领,就结束了。

对我而言,我必须用自己的钱支持自己的观点。我的亏损已经教会我,在无法确定自己必须撤退前,我根本不应该前进。但如果我不前进,我就不会有任何行动。讲这一点,我意思并不是说,当一个人犯错的时候他不应该去止损。他应该这样做,但那不能让他变得优柔寡断。我一辈子都在犯错,但是在亏损中我收获了经验,积累了诸多宝贵的教训。我破产过好多次,但我的亏损从来都不是彻底的失败,否则我现在也不会在这里了。我一直相信我有其他机会,而且犯过的错误我不会再犯第二次。只有一件事情可以让我相信自己犯错误了,那就是赔钱。只有赚到了钱,我才是正确的,这就是投机

学习记录556@flowable多人会签相关推荐

  1. 学习记录605@flowable之moveExecutionsToSingleActivityId并行网关或者包容网关回退

    遇到网关的回退场景,记录一下,部分内容是推测的. 流程图 bpmn <?xml version="1.0" encoding="UTF-8"?> & ...

  2. 学习记录557@flowable流程回退与终止

    虽说本文的题目是流程回退和终止,但是其实抽象出来就是流程跳转,可以从某个节点跳转到另一个节点(顺序流下). 流程 <?xml version="1.0" encoding=& ...

  3. 学习记录553@flowable候选人机制

    流程 <?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http ...

  4. 学习记录554@flowable候选人组机制

    候选人比较多的时候就需要用到候选人组了,类似于实际工作中的什么角色或者部门的人审批什么节点. 需要再flowable独有的用户表.用户组表.两者的关联表中同步自己业务系统的数据.act_id_user ...

  5. 学习记录555@flowable排他网关、并行网关、包容网关

    排他网关 排他网关(exclusive gateway)(也叫异或网关 XOR gateway,或者更专业的,基于数据的排他网关 exclusive data-based gateway),用于对流程 ...

  6. flowable的多人会签和一票否决

    首先直接上案例! 项目结构: 接下来代码: Duorenhuiqian.bpmn20.xml: <?xml version="1.0" encoding="UTF- ...

  7. 【第十八篇】Flowable之多人会签

    一.多人会签 1.流程图绘制   多人会签是指一个任务需要多个人来处理,案例讲解 完整的xml内容 <?xml version="1.0" encoding="UT ...

  8. 零基础大龄人士的Python学习记录(1)

    学习记录(1)11.24 <零基础入门学习Python>(小甲鱼) P19 函数:灵活即强大 小笔记 1.形参和实参 def test(name): 这里的name就是形参 test(花蝴 ...

  9. [Python学习]PycURL简单学习 - limodou的学习记录 - limodou是一个程序员,他关心的焦点是Python, DocBook, Open Source …...

    [Python学习]PycURL简单学习 - limodou的学习记录 - limodou是一个程序员,他关心的焦点是Python, DocBook, Open Source - [Python学习] ...

最新文章

  1. php微信jssdk下载图片,微信JSSDK上传多张图片回调方法以及服务器端处理下载媒体...
  2. C++ warning:’xxx‘ has no out-of-line virtual method definitions...
  3. 【303】C# 复制窗体 修改名称
  4. sbt笔记二 Running
  5. “成功”没那么有道理
  6. Ubuntu 安装 TPM-2.0 TSS 软件栈
  7. [转载] python 闭包和装饰器详解
  8. 使用HTTrack克隆网站
  9. HTTP常见状态码及常见错误
  10. 51系列、arduino、stm32系列驱动DAC模块TLC5615输出指定电压(可修改为波形输出)
  11. C++继承——以应用抽象类,求圆、圆内接正方形、圆外切正方形的面积和周长为例
  12. 虚幻四学习笔记(2)—— 学习途径
  13. jks bks 等的定义 如何将jks转化为bks的
  14. 解决linux系统网络时常断开的问题
  15. 计算机网络课论文参考文献,热门计算机网络课程论文参考文献 计算机网络课程专著类参考文献哪里找...
  16. 基于Java的建筑工程综合管理信息系统
  17. 用 Delphi 学设计模式(一) 之 简单工厂篇 (原创)
  18. java全月应纳税所得额_全月应纳税所得额是什么意思?
  19. Java代码去除空格的几种方法
  20. 耶鲁大学——我们应该如何交流思想

热门文章

  1. Hulu(北京)推荐算法负责人周涵宁:怎样应对基于深度学习的视频推荐系统...
  2. 消费级AR眼镜爆发将近:Rokid+无影突破算力,打造“第三块屏幕”
  3. 商品进销存管理程序 (C语言)
  4. 《SpringBoot+vue全栈开发实战项目》笔记
  5. c#之MVC ---初始
  6. 深度学习预处理工具---DALI详解
  7. wwise移植到linux平台,设立Wwise项目 - Lumberyard 用户指南
  8. C++轻量级界面开发框架ImGUI介绍
  9. 全景丨0基础学习VR全景制作系列教程,第九节:720VR全景,地拍拍摄方法
  10. 初步了解3d关节动画的概念