
💖专栏简介
✔️本专栏将从Camunda(卡蒙达) 7中的关键概念到实现中国式工作流相关功能。
✔️文章中只包含演示核心代码及测试数据,完整代码可查看作者的开源项目snail-camunda
✔️请给snail-camunda 点颗星吧😘
💖说在前面
请无视标题,无论是或签还是比例签都是会签,只是该节点通过的规则不同。本文将演示会签通过的三种规则:【所有人审批通过】、【一人审批通过】、【按比例投票】。
注意:设置的是通过规则,对于驳回操作均为一人驳回则驳回。
💖设计流程定义
在《认识BPMN2.0》中提及用户任务可以直接分配给单个用户、用户列表或组列表,本文将演示分配给单个用户、用户列表两种方式,用户组在后续文章中也会演示。
以下变量名是在整个过程中比较重要的,结合示例理解并使用:
- nrOfInstances : 实例总数
- nrOfActiveInstances:当前活动的实例的数量。对于串行而言该值始终为1
- nrOfCompletedInstances:已经完成的实例数
- loopCounter :循环计数器
- Loop cardinality:循环基数
- Collection:会签人数的集合
- Element variable:变量元素。选择Collection时必选,为collection集合每次遍历的元素。
- Completion condition:完成条件
用户任务分配给单个用户可按如下图所示设置:

一人通过 通常被称为【或签】,设置完成条件:
${nrOfCompletedInstances == 1}
还需注意变量元素名和Assignee中设置的变量名保持一致。类似于在Java中的fori循环,变量名是i,使用该变量时也应该用i。

比例通过和全部通过就不截图了,完成条件分别改为:
//已完成的实例数占总实例数的三成以上就算通过
${nrOfCompletedInstances/nrOfInstances > 0.3}
//已完成的实例数 等于 总实例数才算通过
${nrOfCompletedInstances == nrOfInstances}
好吧,直接把流程定义放上来:
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1o78fuh" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.19.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.20.0">
<bpmn:process id="Process_19w1rrm" isExecutable="true" camunda:historyTimeToLive="180">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0g1nmt1</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0g1nmt1" sourceRef="StartEvent_1" targetRef="root" />
<bpmn:userTask id="root" name="发起人" camunda:assignee="${initiator}">
<bpmn:incoming>Flow_0g1nmt1</bpmn:incoming>
<bpmn:outgoing>Flow_178rknz</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_178rknz" sourceRef="root" targetRef="Activity_0163wxf" />
<bpmn:userTask id="Activity_0163wxf" name="一人通过" camunda:assignee="${assignee}">
<bpmn:incoming>Flow_178rknz</bpmn:incoming>
<bpmn:outgoing>Flow_1t1uand</bpmn:outgoing>
<bpmn:multiInstanceLoopCharacteristics camunda:collection="${userOneList}" camunda:elementVariable="assignee">
<bpmn:completionCondition xsi:type="bpmn:tFormalExpression">${nrOfCompletedInstances == 1}</bpmn:completionCondition>
</bpmn:multiInstanceLoopCharacteristics>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_1t1uand" sourceRef="Activity_0163wxf" targetRef="Activity_1hgbacv" />
<bpmn:userTask id="Activity_1hgbacv" name="比例通过" camunda:assignee="${assignee}">
<bpmn:incoming>Flow_1t1uand</bpmn:incoming>
<bpmn:outgoing>Flow_1giv9ue</bpmn:outgoing>
<bpmn:multiInstanceLoopCharacteristics camunda:collection="${userTwoList}" camunda:elementVariable="assignee">
<bpmn:completionCondition xsi:type="bpmn:tFormalExpression">${nrOfCompletedInstances/nrOfInstances > 0.3 }</bpmn:completionCondition>
</bpmn:multiInstanceLoopCharacteristics>
</bpmn:userTask>
<bpmn:userTask id="Activity_14cwgmh" name="全部通过" camunda:assignee="${assignee}">
<bpmn:incoming>Flow_1giv9ue</bpmn:incoming>
<bpmn:outgoing>Flow_0xb5gog</bpmn:outgoing>
<bpmn:multiInstanceLoopCharacteristics camunda:collection="${userThreeList}" camunda:elementVariable="assignee">
<bpmn:completionCondition xsi:type="bpmn:tFormalExpression">${nrOfCompletedInstances == nrOfInstances}</bpmn:completionCondition>
</bpmn:multiInstanceLoopCharacteristics>
</bpmn:userTask>
<bpmn:endEvent id="Event_0a7muzc">
<bpmn:incoming>Flow_0xb5gog</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0xb5gog" sourceRef="Activity_14cwgmh" targetRef="Event_0a7muzc" />
<bpmn:sequenceFlow id="Flow_1giv9ue" sourceRef="Activity_1hgbacv" targetRef="Activity_14cwgmh" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_19w1rrm">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0fh3xaa_di" bpmnElement="root">
<dc:Bounds x="270" y="77" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_06ae8hl_di" bpmnElement="Activity_0163wxf">
<dc:Bounds x="430" y="77" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0edrq19_di" bpmnElement="Activity_1hgbacv">
<dc:Bounds x="590" y="77" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1j25q3g_di" bpmnElement="Activity_14cwgmh">
<dc:Bounds x="760" y="77" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0a7muzc_di" bpmnElement="Event_0a7muzc">
<dc:Bounds x="912" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0g1nmt1_di" bpmnElement="Flow_0g1nmt1">
<di:waypoint x="215" y="117" />
<di:waypoint x="270" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_178rknz_di" bpmnElement="Flow_178rknz">
<di:waypoint x="370" y="117" />
<di:waypoint x="430" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1t1uand_di" bpmnElement="Flow_1t1uand">
<di:waypoint x="530" y="117" />
<di:waypoint x="590" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0xb5gog_di" bpmnElement="Flow_0xb5gog">
<di:waypoint x="860" y="117" />
<di:waypoint x="912" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1giv9ue_di" bpmnElement="Flow_1giv9ue">
<di:waypoint x="690" y="117" />
<di:waypoint x="760" y="117" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
💖部署流程定义
在resources下新建目录bpmn用于存放流程定义文件,启动项目后调用部署接口:
/**
* 流程定义相关接口
* @author lonewalker
*/
@RequestMapping("/process/definition")
@AllArgsConstructor
@RestController
public class ProcessDefinitionController {
private final RepositoryService repositoryService;
/**
* 部署流程定义
*
* @return 提示信息
*/
@PostMapping("/deploy")
public String deployProcessDefinition(){
repositoryService.createDeployment()
.addClasspathResource("bpmn/2.bpmn")
.name("演示")
.deploy();
return "部署成功";
}
}
💖流程实例测试
/**
* 流程实例相关接口
*
* @author lonewalker
*/
@RequestMapping("/process/instance")
@RequiredArgsConstructor
@RestController
public class ProcessInstanceController {
private final RuntimeService runtimeService;
private final TaskService taskService;
/**
* 根据流程定义key发起流程实例
*
* @param requestParam 请求参数
* @return 流程实例id
*/
@PostMapping("/startProcessInstanceByKey")
public String startProcessInstanceByKey(@RequestBody StartProcessRequest requestParam) {
Map<String, Object> paramMap = new HashMap<>(8);
List<String> userOneList = new ArrayList<>();
List<String> userTwoList = new ArrayList<>();
List<String> userThreeList = new ArrayList<>();
//一人通过节点的审批人
userOneList.add("10086");
userOneList.add("10087");
//比例通过节点的审批人
userTwoList.add("10087");
userTwoList.add("10088");
userTwoList.add("10089");
userTwoList.add("10090");
userTwoList.add("10091");
//全部通过节点的审批人
userThreeList.add("10090");
userThreeList.add("10091");
paramMap.put("initiator", "10086");
paramMap.put("userOneList", userOneList);
paramMap.put("userTwoList", userTwoList);
paramMap.put("userThreeList", userThreeList);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(requestParam.getProcessDefinitionKey(), requestParam.getBusinessKey(), paramMap);
return processInstance.getProcessInstanceId();
}
/**
* 完成单个任务
*
* @param requestParam 请求参数
* @return 任务所在节点信息
*/
@PostMapping("/completeSingleTask")
public Boolean completeSingleTask(@RequestBody @Validated CompleteTaskRequest requestParam) {
taskService.complete(requestParam.getTaskId());
return true;
}
}
发起流程实例后让流程来到【一人通过】节点

在【比例通过】节点设置完成条件是通过人数占总人数的三成,所以只需两个人审批通过即通过:

两人审批通过后是符合预期来到最后一个节点

查看任务的历史表【act_hi_taskinst】,两个任务被完成,其他任务则被删除了。

💖扩展
多实例节点可以配置串行或并行。三条垂直线表示实例将并行执行,而三条水平线表示顺序执行。
该部分就不单独做演示了。


本文详细介绍了如何在Camunda7中使用BPMN2.0设计会签工作流,包括一人审批、按比例投票和全部通过的规则,并通过实例演示了流程定义、部署和实例测试过程。
1990

被折叠的 条评论
为什么被折叠?



