首先说一下, Parallel Gateway 并不是首选做会签的方法,具体请看 https://www.baidu.com/s?wd=activiti会签, 但这里还是通过 Parallel Gateway来实现会签,下图是一个简单的会签流程图
利用并行网关来做的话相对更加直观,也不需要在发起会签之前设置额外的会签用户列表,但最大的问题是,如何汇总并行网关的结果,一般来说,如果直接设置表达式的话,会比较繁琐,例如只要一个人审核通过,那么整个会签就通过:
${task1 == 'true'} || ${task2 == 'true'} || ${task3 == 'true'} || ${task4 || true}
这种还是简单的情况,如果要有两个人审核通过,会签才算通过的话,表达式能写死人,在这种情况下,可以借助一个额外的bean来简化表达式:
@Component
public class V {
public final ProcessEngine processEngine;
public V(ProcessEngine processEngine) {
this.processEngine = processEngine;
}
public boolean rate(double rate) {
Execution e = Context.getBpmnExecutionContext().getExecution();
Calc calc = getCalc((ExecutionEntity) e);
return calc.rate(rate);
}
private Calc getCalc(ExecutionEntity execution) {
Gateway gateway = getGateway(execution);
Calc calc = new Calc();
for (SequenceFlow flow : gateway.getIncoming()) {
FlowNode node = flow.getSource();
if (node instanceof Task) {
calc.results.add(getTaskResult(flow, execution));
break;
}
if (node instanceof ParallelGateway) {
for (SequenceFlow pflow : node.getIncoming()) {
calc.results.add(getTaskResult(pflow, execution));
}
}
}
calc.processInstanceId = execution.getProcessInstanceId();
return calc;
}
private Gateway getGateway(ExecutionEntity execution) {
FlowElement flowElement = execution.getBpmnModelElementInstance();
if (!(flowElement instanceof Gateway)) {
if (flowElement instanceof SequenceFlow) {
flowElement = ((SequenceFlow) flowElement).getSource();
}
}
if (flowElement instanceof Gateway) {
return (Gateway) flowElement;
}
throw new RuntimeException("表达式必须指向一个网关");
}
private String getTaskResult(SequenceFlow flow, Execution execution) {
FlowNode node = flow.getSource();
if (!(node instanceof Task)) {
return null;
}
Task task = (Task) node;
org.camunda.bpm.engine.task.Task dbTask = processEngine.getTaskService().createTaskQuery().active().taskDefinitionKey(task.getId()).singleResult();
if (dbTask != null) {
Object variable = processEngine.getTaskService().getVariable(dbTask.getId(), dbTask.getId());
return Objects.toString(variable, null);
}
List<HistoricTaskInstance> taskInstances = processEngine.getHistoryService()
.createHistoricTaskInstanceQuery()
.taskDefinitionKey(task.getId())
.processInstanceId(execution.getProcessInstanceId())
.finished()
.orderByHistoricTaskInstanceEndTime().desc()
.listPage(0, 1);
if (!taskInstances.isEmpty()) {
HistoricVariableInstance variableInstance = processEngine.getHistoryService().createHistoricVariableInstanceQuery().variableName(taskInstances.get(0).getId()).singleResult();
if (variableInstance != null) {
return Objects.toString(variableInstance.getValue(), null);
}
}
return null;
}
private final class Calc {
private String processInstanceId;
private List<String> results = new ArrayList<>();
public boolean rate(double rate) {
if (results.isEmpty()) {
return false;
}
boolean flag = (results.stream().filter(s -> "approve".equals(s)).count() / results.size()) > rate;
String value = flag ? "approve" : "reject";
processEngine.getRuntimeService().setVariable(processInstanceId, processInstanceId, value);
return flag;
}
}
}
上述类用于判断通过的比例,例如表达式${v.rate(0.6)}
表达式,如果有2/3的人投票通过,那么会签就通过,表达式设置在sequence flow上即可:
另外,如果会签结束之后整个流程就结束了,这个时候,我们没有办法判断整个流程是否审核通过,例如:
如果碰到这种情况,我们需要在结束前的sequence flow上设置一个 listener 表达式,例如:
这样的话,如果有一半人通过,那么代表整个流程通过