别再傻傻分不清了!Camunda 7 多实例任务(会签)的三种审批规则,我用一个请假流程给你讲明白
Camunda 7多实例任务审批规则实战:从请假流程看会签、或签与比例签
想象一下这样的场景:公司市场部的小张需要申请一周的年假,按照公司规定,这个请假申请需要经过部门经理、HR主管和分管副总的三重审批。但问题来了——这三位领导是必须全部同意才能通过?还是只要其中任意一人同意即可?又或者需要超过半数同意?这些不同的审批规则,正是Camunda工作流引擎中多实例任务(Multi-Instance Task)的典型应用场景。
对于刚接触Camunda的开发者来说,理解"会签"、"或签"和"比例签"这些概念可能有些抽象。本文将通过一个完整的请假审批流程示例,带你深入理解这三种审批规则的实际应用。我们将从业务需求出发,反向推导技术配置,重点分析不同规则下Assignee、Collection和Completion Condition的关键差异,并提供可直接运行的流程定义XML和Java测试代码。
1. 业务场景分析与技术映射
在开始编码之前,我们需要先明确业务需求与技术实现的对应关系。以请假流程为例,常见的审批规则有三种:
会签(全体通过):所有审批人必须全部同意,请假申请才能通过。适用于重要决策场景,如高管休假或长期病假审批。
或签(一人通过):任意一位审批人同意即可通过。适用于紧急情况或简单事务审批,如短时请假。
比例签(多数通过):达到特定比例的审批人同意即可通过(如超过50%)。适用于需要民主决策但又不必全体同意的场景。
在Camunda中,这三种规则都通过多实例用户任务(Multi-Instance User Task)实现,核心区别在于完成条件(Completion Condition)的配置。以下是技术实现的关键要素对照表:
| 要素 | 会签 | 或签 | 比例签 |
|---|---|---|---|
| 完成条件表达式 | ${nrOfCompletedInstances == nrOfInstances} | ${nrOfCompletedInstances >= 1} | ${nrOfCompletedInstances/nrOfInstances > 0.5} |
| 业务语义 | 必须全部同意 | 任意一人同意即可 | 超过半数同意 |
| 适用场景 | 高风险决策 | 低风险常规审批 | 中等重要性决策 |
2. 流程定义设计与XML配置
让我们构建一个包含三种审批规则的完整请假流程。流程包含四个主要节点:
- 请假申请提交(发起人节点)
- 或签审批节点(一人通过即可)
- 比例签审批节点(超过30%通过)
- 会签审批节点(全部通过)
对应的BPMN XML配置如下:
<bpmn:process id="leave_approval" isExecutable="true"> <!-- 发起人节点 --> <bpmn:userTask id="submit_leave" name="提交请假申请" camunda:assignee="${initiator}"> <bpmn:incoming>StartEvent_1</bpmn:incoming> <bpmn:outgoing>SequenceFlow_1</bpmn:outgoing> </bpmn:userTask> <!-- 或签审批节点 --> <bpmn:userTask id="or_approval" name="或签审批" camunda:assignee="${approver}"> <bpmn:multiInstanceLoopCharacteristics camunda:collection="${orApprovers}" camunda:elementVariable="approver"> <bpmn:completionCondition> ${nrOfCompletedInstances >= 1} </bpmn:completionCondition> </bpmn:multiInstanceLoopCharacteristics> </bpmn:userTask> <!-- 比例签审批节点 --> <bpmn:userTask id="ratio_approval" name="比例签审批" camunda:assignee="${approver}"> <bpmn:multiInstanceLoopCharacteristics camunda:collection="${ratioApprovers}" camunda:elementVariable="approver"> <bpmn:completionCondition> ${nrOfCompletedInstances/nrOfInstances > 0.3} </bpmn:completionCondition> </bpmn:multiInstanceLoopCharacteristics> </bpmn:userTask> <!-- 会签审批节点 --> <bpmn:userTask id="all_approval" name="会签审批" camunda:assignee="${approver}"> <bpmn:multiInstanceLoopCharacteristics camunda:collection="${allApprovers}" camunda:elementVariable="approver"> <bpmn:completionCondition> ${nrOfCompletedInstances == nrOfInstances} </bpmn:completionCondition> </bpmn:multiInstanceLoopCharacteristics> </bpmn:userTask> </bpmn:process>关键配置说明:
camunda:collection:指定审批人列表变量camunda:elementVariable:定义当前审批人变量completionCondition:使用表达式定义完成条件
3. Java实现与测试案例
下面我们通过Java代码部署流程定义并测试不同审批场景:
@RestController @RequestMapping("/leave") public class LeaveApprovalController { @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; // 启动请假流程 @PostMapping("/start") public String startProcess(@RequestBody LeaveRequest request) { Map<String, Object> variables = new HashMap<>(); // 设置审批人列表 variables.put("orApprovers", Arrays.asList("manager1", "manager2")); variables.put("ratioApprovers", Arrays.asList("hr1", "hr2", "hr3")); variables.put("allApprovers", Arrays.asList("vp1", "vp2", "vp3")); variables.put("initiator", request.getApplicantId()); ProcessInstance instance = runtimeService.startProcessInstanceByKey( "leave_approval", request.getBusinessKey(), variables ); return instance.getId(); } // 完成任务审批 @PostMapping("/complete") public void completeTask(@RequestBody ApprovalRequest request) { Map<String, Object> taskVariables = new HashMap<>(); taskVariables.put("approved", request.isApproved()); taskService.complete(request.getTaskId(), taskVariables); } }测试不同审批规则时,需要注意以下行为差异:
或签测试:
- 场景:两个审批人(manager1, manager2)
- 预期:任意一人审批通过后,其他待审批任务自动取消
- 验证SQL:
SELECT * FROM ACT_RU_TASK观察任务状态变化
比例签测试:
- 场景:三个审批人(hr1, hr2, hr3),设置通过比例为>30%
- 预期:一人同意不足(33%),两人同意则通过(66%)
- 关键日志:查看
nrOfCompletedInstances值变化
会签测试:
- 场景:三个审批人(vp1, vp2, vp3)
- 预期:必须全部审批通过,任一拒绝则流程终止
- 异常处理:需要在ServiceTask中添加边界事件处理拒绝情况
4. 高级配置与性能优化
在实际企业应用中,我们还需要考虑以下高级配置和优化点:
4.1 并行与串行执行模式
多实例任务支持两种执行模式:
- 并行(parallel):默认模式,所有审批任务同时创建
- 串行(sequential):按顺序逐个创建审批任务
配置示例:
<bpmn:multiInstanceLoopCharacteristics camunda:collection="${approvers}" camunda:elementVariable="approver" isSequential="true"> ... </bpmn:multiInstanceLoopCharacteristics>4.2 动态审批人分配
审批人列表可以通过表达式动态获取:
camunda:collection="${approvalService.getApprovers(execution)}"对应的Java服务类:
@Component public class ApprovalService { public List<String> getApprovers(DelegateExecution execution) { String department = (String) execution.getVariable("department"); // 根��部门返回不同的审批人列表 return approverRepository.findByDepartment(department); } }4.3 性能优化建议
当审批人数量较多时(如超过20人),需要考虑以下优化措施:
批量任务创建:
// 在流程引擎配置中设置批量大小 processEngineConfiguration.setBatchSize(10);异步延续:
<bpmn:userTask id="mass_approval" camunda:asyncBefore="true"> <bpmn:multiInstanceLoopCharacteristics.../> </bpmn:userTask>历史级别配置:
# 适当降低历史记录级别 camunda.history.level=audit
5. 常见问题排查指南
在实际开发中,可能会遇到以下典型问题:
问题1:审批任务没有按预期创建或完成
排查步骤:
- 检查
collection变量是否正确注入 - 验证表达式语法,特别是比较运算符
- 查看ACT_RU_TASK表确认任务是否创建
问题2:完成条件不生效
解决方案:
- 确保使用正确的实例计数变量:
// 正确写法 ${nrOfCompletedInstances >= 1} // 错误写法(缺少Instances后缀) ${nrOfCompleted >= 1}
问题3:审批人变量传递错误
调试方法:
- 在任务监听器中打印变量:
execution.getVariables().forEach((k,v) -> System.out.println(k + "=" + v)); - 检查
elementVariable名称是否与Assignee表达式一致
问题4:历史记录不完整
配置调整:
<process id="leave_approval" camunda:historyTimeToLive="90"> ... </process>通过这个请假审批流程的完整示例,相信你已经掌握了Camunda中多实例任务的三种审批规则实现方式。在实际项目中,可以根据业务需求灵活组合这些模式,构建出既符合业务流程又高效可靠的审批系统。
