activiti6中,关于下一个节点的预判,是躲不开的一个小课题。有两个思路:
1. 假提交。附带的条件参数提交后,再回滚。但是在回滚之前,返回下一个任务的信息。
2. 在内存中对排他网关、子流程、兼容网关进行递归判断,最终得到目标用户任务。
废话不多说,上代码:
1.主要方法:
/**
* 获取下一个用户任务 - 获取下一个任务定义
* <p>
* 如果下一个节点为用户任务则直接返回,
* <p>
* 如果下一个节点为排他网关, 获取排他网关Id信息, 根据排他网关Id信息和execution获取流程实例排他网关Id为key的变量值,
* 根据变量值分别执行排他网关后线路中的el表达式, 并找到el表达式通过的线路后的用户任务
*
* @param flowElement 流程节点信息
* @param activitiId 当前流程节点Id信息
* @param businessType 用户基本签审参数 - 按钮参数
* @param businessValue 用户基本签审参数 - 按钮值
* @param varMap 混合了用户基本签审参数与业务实体参数的参数Map
* @return
*/
public List<FlowElement> nextFlowElement(FlowElement flowElement, String activitiId, String businessType, Boolean businessValue, Map<String, Object> varMap) {
List<FlowElement> returnFlowElements = new ArrayList<>();
if ((flowElement instanceof UserTask || flowElement instanceof EndEvent) && !activitiId.equals(flowElement.getId())) {
//1. 如果递归传入的flowElement对象是UserTask类型(我们最终需要的类型)&&UserTask的actId和我们的起点flowElement的actId不一样
//说明已经找到了起点根据condition应该流向的UserTask,则寻找终止,返回UserTask
returnFlowElements.add(flowElement);
} else if (flowElement instanceof ExclusiveGateway) {
//2. 当前节点是排他网关时,需要根据上一个节点提交的【条件参数】接着向下寻找该网关指向的任务。
List<SequenceFlow> outgoingFlows = ((ExclusiveGateway) flowElement).getOutgoingFlows();
//2.1 根据排他网关的线路数目做判断分支处理
returnFlowElements.add(returnExclusiveGateWayNextFlowElement(outgoingFlows, activitiId, businessType, businessValue, varMap));
} else if (flowElement instanceof InclusiveGateway) {
//3. 当前节点是兼容网关时,直接返回所有兼容网关的出线递归的所有最终元素
returnFlowElements.addAll(returnInclusiveGateWayNextFlowElement(flowElement, activitiId, businessType, businessValue, varMap));
} else if (flowElement instanceof CallActivity) {
//4. 当前节点是调用式活动时,直接返回所有调用式活动的所有起始用户任务
return returnCallActivityRelativeUserTasks(flowElement, true, null);
} else if (flowElement instanceof UserTask && activitiId.equals(flowElement.getId())) {//5. 当前用户任务节点发出不同线路
List<SequenceFlow> userTaksOutgoingFlows = ((UserTask) flowElement).getOutgoingFlows();
List<SequenceFlow> gatewayOutGoingFlows;
FlowElement targetFe;
for (SequenceFlow userTaskOutGoingFlow : userTaksOutgoingFlows) {
//5.1 获取线路的终点节点
targetFe = userTaskOutGoingFlow.getTargetFlowElement();
//5.2 终点为排他网关,说明判断分支条件在网关的下一层
if (targetFe instanceof ExclusiveGateway) {
gatewayOutGoingFlows = ((ExclusiveGateway) targetFe).getOutgoingFlows();
returnFlowElements.add(returnExclusiveGateWayNextFlowElement(gatewayOutGoingFlows, activitiId, businessType, businessValue, varMap));
} else if (targetFe instanceof InclusiveGateway) {
//5.3 终点为兼容网关,返回的元素在网关的下一层
returnFlowElements.addAll(returnInclusiveGateWayNextFlowElement(targetFe, activitiId, businessType, businessValue, varMap));
} else if (targetFe instanceof UserTask || targetFe instanceof EndEvent) {
//5.4 终点为用户任务,同样判断分支条件
if (returnJudgement(userTaskOutGoingFlow, businessType, businessValue, varMap,activitiId.contains(ActivitiConst.ID_CALLACTIVITY))) {
returnFlowElements.add(targetFe);
}
} else if (targetFe instanceof CallActivity) {
//5.5 终点为调用式活动,则返回调用式活动的开始事件连接的所有任务
return returnCallActivityRelativeUserTasks(targetFe, true, null);
} else {
//todo 其它类型节点
returnFlowElements.add(targetFe);
}
}
} else {
//todo 其它类型节点
returnFlowElements.add(flowElement);
}
return returnFlowElements;
}
2. 判断排他网关下一节点:
/**
* 获取下一个用户任务 - 对排他网关做递归,返回最终的task/endEvent
*
* @param outgoingFlows
* @param activitiId
* @param businessType 用户基本签审参数 - 按钮参数
* @param businessValue 用户基本签审参数 - 按钮值
* @param varMap 混合了用户基本签审参数与业务实体参数的参数Map
* @return
*/
public FlowElement returnExclusiveGateWayNextFlowElement(List<SequenceFlow> outgoingFlows, String activitiId, String businessType, Boolean businessValue, Map<String, Object> varMap) {
//todo 未对outgoingFlows做判空,暂时先这样,估计不会出什么问题。
List<FlowElement> returnFlowElements = null;
if (outgoingFlows.size() == 1) {
//2.1.1 只有一条线路信息的情况下,不确定该线路的下一个target是不是UserTask类型,所以仍需递归
returnFlowElements = nextFlowElement(outgoingFlows.get(0).getTargetFlowElement(), activitiId, businessType, businessValue,
varMap);
} else if (outgoingFlows.size() > 1) {
//2.1.2 如果排他网关有多条线路信息,则遍历排他网关线路,并判断条件分支
for (SequenceFlow sf : outgoingFlows) {
if (returnJudgement(sf, businessType, businessValue, varMap,activitiId.contains(ActivitiConst.ID_CALLACTIVITY))) {
returnFlowElements = nextFlowElement(sf.getTargetFlowElement(), activitiId, businessType, businessValue, varMap);
}
}
}
if (CollectionUtils.isNotEmpty(returnFlowElements)) {
return returnFlowElements.get(0);
}
//should never get here
return null;
}
3. 判断兼容网关下一节点:
/**
* 获取下一个用户任务 - 对兼容网关做递归,返回最终的task/endEvent
*
* @param currentGateway
* @param activitiId
* @param businessType 用户基本签审参数 - 按钮参数
* @param businessValue 用户基本签审参数 - 按钮值
* @param varMap 混合了用户基本签审参数与业务实体参数的参数Map
* @return
*/
public List<FlowElement> returnInclusiveGateWayNextFlowElement(FlowElement currentGateway, String activitiId, String businessType, Boolean businessValue, Map<String, Object> varMap) {
List<FlowElement> returnFlowElements = new ArrayList<>();
List<SequenceFlow> outgoingFlows = ((InclusiveGateway) currentGateway).getOutgoingFlows();
//根据并行网关的出线做判断
if (outgoingFlows.size() > 0) {
//2.1.2 如果排他网关有多条线路信息,则遍历排他网关线路,并判断条件分支
for (SequenceFlow sf : outgoingFlows) {
List<FlowElement> singleFlowFinishElements = nextFlowElement(sf.getTargetFlowElement(), activitiId, businessType, businessValue, varMap);
returnFlowElements.addAll(singleFlowFinishElements);
}
return returnFlowElements;
}
//should never get here
return null;
}
4. 判断子流程下一节点:
/**
* 返回调用式活动的的相关任务。根据ifJumpIn参数不同,返回活动内的任务或活动结束后后续的任务。
*
* @param callActivity
* @param ifJumpIn 是否跳出当前调用式活动
* @return
*/
public List<FlowElement> returnCallActivityRelativeUserTasks(FlowElement callActivity, Boolean ifJumpIn, Map varMap) {
List<FlowElement> returnUserTasks = new ArrayList<>();
if (ifJumpIn) {
String procDefKey = ((CallActivity) callActivity).getCalledElement();
ProcessDefinition subProcess = repositoryService.createProcessDefinitionQuery().processDefinitionKey(procDefKey).latestVersion().singleResult();
if (subProcess != null) {
Process process = getProcess(subProcess.getId());
Collection<FlowElement> flowElements = process.getFlowElements();
List<SequenceFlow> sequenceFlows = new ArrayList<>();
for (FlowElement element : flowElements) {
if (element instanceof SequenceFlow && ((SequenceFlow) element).getSourceRef().equals(ActivitiConst.ID_STARTEVENT)) {
sequenceFlows.add((SequenceFlow) element);
}
}
sequenceFlows.forEach(s -> {
FlowElement fe = s.getTargetFlowElement();
if (fe instanceof UserTask) {
returnUserTasks.add(fe);
}
});
}
} else {
List<SequenceFlow> callActivityOutgoingFlows = ((CallActivity) callActivity).getOutgoingFlows();
//根据调用式活动的出线做判断
if (callActivityOutgoingFlows.size() > 0) {
//如果调用式活动有多条线路信息,则遍历排他网关线路,并判断条件分支
SequenceFlow sf = callActivityOutgoingFlows.get(0);
List<FlowElement> singleFlowFinishElements = nextFlowElement(sf.getTargetFlowElement(), callActivity.getId(), null, null, varMap);
returnUserTasks.addAll(singleFlowFinishElements);
}
}
return returnUserTasks;
}
5. 于内存中判断流程走向:
/**
* 获取下一个用户任务 - 确认根据条件是否执行当前分支
*
* @param sf
* @param businessType 用户基本签审参数 - 按钮参数
* @param businessValue 用户基本签审参数 - 按钮值
* @param varMap 混合了用户基本签审参数与业务实体参数的参数Map
* @return
*/
public Boolean returnJudgement(SequenceFlow sf, String businessType, Boolean businessValue, Map<String, Object> varMap,Boolean isRootCallActivity) {
String conditionExpressionStr = sf.getConditionExpression();
if (StringUtils.isEmpty(conditionExpressionStr)) {
//如果当前SequenceFlow没有条件判断,说明无条件执行
return true;
}
//1. 将连线的业务流转条件分割为小条件的集合
Boolean ifJustExtra = false;
if (isRootCallActivity) {
//如果
ifJustExtra = true;
}
List<ActBPMNSequenceFlowConditionExpression> allConditionExpressions = actCompanyProcdefService.parseConditionExpressionToList(conditionExpressionStr, ifJustExtra);
return processVarmapAndFinalbool(sf, allConditionExpressions, varMap, businessType, businessValue);
}
6. 递归方法,根据参数和sequenceflow条件,判断当前流向是否是当前sequenceflow:
/**
* 处理最终布尔值和varMap,迭代方法
* @param sf
* @param conditionExpressions
* @param varMap
* @param businessType
* @param businessValue
* @return
*/
public Boolean processVarmapAndFinalbool(SequenceFlow sf, List<ActBPMNSequenceFlowConditionExpression> conditionExpressions, Map<String, Object> varMap, String businessType, Object businessValue) {
for (ActBPMNSequenceFlowConditionExpression conditionExpression : conditionExpressions) {
Boolean result = null;
String basicAuditCondition = conditionExpression.getBasicAuditCondition();
if (StringUtils.isBlank(basicAuditCondition)) {
//1.1 判断额外条件
//1.1.1 根据条件中的参数名称去varMap中拿对应的参数值
String varName = conditionExpression.getField();
if (StringUtils.isNotBlank(varName)) {
//单一字段判断布尔值与varMap设置:
if (!varMap.containsKey(varName)) {
throw new BusinessException(String.format(ErrMsg.PARAM_BUSINESSENTITYMAPPING_NOTEXISTS, sf.getName(), varName));
}
String operator = conditionExpression.getOperator();
//提交值
Object varValue = varMap.get(varName);
Double varValueDouble = null;
if (varValue.toString().matches(ActivitiConst.IS_NUMBER)) {
varValueDouble = new Double(varValue.toString());
}
//表达式值
String conValue = conditionExpression.getValue();
Double conValueDouble = null;
if (conValue.matches(ActivitiConst.IS_NUMBER)) {
conValueDouble = new Double(conValue);
}
conditionExpression.setVarValue(varValue.toString());
switch (operator) {
case ActivitiConst.VAR_EQ:
result = varValue.toString().equals(conValue);
break;
case ActivitiConst.VAR_NEQ:
result = !(varValue.toString().equals(conValue));
break;
case ActivitiConst.VAR_GT:
if (varValueDouble != null && conValueDouble != null) {
result = varValueDouble.compareTo(conValueDouble) > 0;
} else {
result = varValue.toString().compareTo(conValue) > 0;
}
break;
case ActivitiConst.VAR_LT:
if (varValueDouble != null && conValueDouble != null) {
result = varValueDouble.compareTo(conValueDouble) < 0;
} else {
result = varValue.toString().compareTo(conValue) < 0;
}
break;
case ActivitiConst.VAR_EGT:
if (varValueDouble != null && conValueDouble != null) {
result = varValueDouble.compareTo(conValueDouble) >= 0;
} else {
result = varValue.toString().compareTo(conValue) >= 0;
}
break;
case ActivitiConst.VAR_ELT:
if (varValueDouble != null && conValueDouble != null) {
result = varValueDouble.compareTo(conValueDouble) <= 0;
} else {
result = varValue.toString().compareTo(conValue) <= 0;
}
break;
case ActivitiConst.VAR_CONTAINS:
result = varValue.toString().contains(conValue);
varMap.put(varName + operator + conValue.replaceAll(ActivitiConst.VAR_COMMA, ActivitiConst.VAR_SPLIT_IN_COLLECTION), result.toString());
break;
case ActivitiConst.VAR_NOTCONTAINS:
result = !(varValue.toString().contains(conValue));
varMap.put(varName + operator + conValue.replaceAll(ActivitiConst.VAR_COMMA, ActivitiConst.VAR_SPLIT_IN_COLLECTION), result.toString());
break;
default:
result = false;
}
} else {
//复合条件判断布尔值与varMap设置:
if (CollectionUtils.isNotEmpty(conditionExpression.getChildren())) {
result = processVarmapAndFinalbool(sf, conditionExpression.getChildren(), varMap, businessType, businessValue);
}
}
} else {
//1.2 判断用户基本签审条件
//1.2.1 校验流程图中定义的基本签审条件格式是否正确
String[] conditionOrPair = basicAuditCondition.split(ActivitiConst.VAR_OR_REGEX);
for (String subCondition : conditionOrPair) {
String[] conditionPair = subCondition.split("==");
String conditionBusinessType = conditionPair[0];
String conditionValue = conditionPair[1];
if ((StringUtils.isNotEmpty(conditionBusinessType) && StringUtils.isEmpty(conditionValue))
|| (StringUtils.isEmpty(conditionBusinessType) && StringUtils.isNotEmpty(conditionValue))) {
throw new BusinessException(String.format(ErrMsg.SEQUENCEFLOW_CONDITION_ERROR, sf.getName()));
}
}
//1.2.2 判断用户传来的基本签审参数是否可以使连线的判断条件为真
result = basicAuditCondition.contains(businessType + "==" + businessValue.toString());
}
conditionExpression.setResult(result);
}
//2. 对上述结果进行整理,得到最终的布尔值
StringBuilder finalBoolStr = new StringBuilder();
Boolean finalBool = null;
for (ActBPMNSequenceFlowConditionExpression conditionExpression : conditionExpressions) {
finalBoolStr.append(conditionExpression.getField()).append(conditionExpression.getOperator()).append(conditionExpression.getValue()).
append(",varValue=").append(conditionExpression.getVarValue()).append(",singleBool=").append(conditionExpression.getResult()).append("\n");
Boolean singleBool = conditionExpression.getResult();
String connector = conditionExpression.getConnector();
if (finalBool == null) {
finalBool = singleBool;
}
if (StringUtils.isNotBlank(connector)) {
if (connector.equals(ActivitiConst.VAR_AND)) {
finalBool = finalBool && singleBool;
} else if (connector.equals(ActivitiConst.VAR_OR)) {
finalBool = finalBool || singleBool;
}
}
}
log.info("业务流转判断过程如下:" + finalBoolStr.toString());
return finalBool;
}