flowable 流程跟踪
import cn.business.framework.common.constant.StringConstant;
import cn.business.framework.common.exception.ServiceException;
import cn.business.framework.flowable.core.util.FlowableUtils;
import cn.business.module.bpm.controller.admin.definition.vo.model.BpmModelRespVO;
import cn.business.module.bpm.service.definition.BpmModelService;
import cn.business.module.bpm.util.ELUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
import static cn.business.module.bpm.enums.ErrorCodeConstants.PROCESS_NODE_CONFIGURATION_ERROR;
/**
* @author yuanzhang
* @description 流程服务
* @date 2023/08/22
**/
@Service
@Slf4j
@RequiredArgsConstructor
public class FlowAbleService {
private final RuntimeService runtimeService;
private final RepositoryService repositoryService;
private final BpmModelService bpmModelService;
private final HistoryService historyService;
/**
* @param processInstanceId 流程实例id
* @param modelId 模型id
* @param variableMap 变量映射
* @return {@link List }<{@link FlowElement }>
* @description 通过变量推算流程走向
* @author yuanzhang
* @date 2023/08/02
**/
public List<FlowElement> calApprovePath(String processInstanceId, String modelId, Map<String, Object> variableMap) {
BpmnModel bpmnModel;
if (StringUtils.isNotBlank(processInstanceId)) {
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (null == processInstance) {
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
} else {
bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
}
BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
byte[] xmlBytes = xmlConverter.convertToXML(bpmnModel);
String xmlData = new String(xmlBytes);
bpmnModel = FlowableUtils.parseBpmnXml(xmlData);
} else {
BpmModelRespVO model = bpmModelService.getModel(modelId);
bpmnModel = bpmModelService.getBpmnModel(model.getId());
}
Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
List<FlowElement> passElements = new ArrayList<>();
this.dueStartElement(passElements, flowElements, variableMap);
return passElements;
}
/**
* 2. 找到开始节点,通过它的目标节点,然后再不断往下找。
*/
private void dueStartElement(List<FlowElement> passElements, Collection<FlowElement> flowElements, Map<String, Object> variableMap) {
flowElements.stream().filter(StartEvent.class::isInstance).findFirst().ifPresent(startElement -> {
flowElements.remove(startElement);
List<SequenceFlow> outgoingFlows = ((StartEvent) startElement).getOutgoingFlows();
String targetRef = outgoingFlows.get(0).getTargetRef();
// 根据ID找到FlowElement
FlowElement targetElementOfStartElement = getFlowElement(flowElements, targetRef);
if (targetElementOfStartElement instanceof UserTask || targetElementOfStartElement instanceof ExclusiveGateway) {
this.getPassElementList(passElements, flowElements, targetElementOfStartElement, variableMap);
}
});
}
/**
* 3. 我只用到了UserTask、ExclusiveGateway、ParallelGateway,所以代码里只列举了这三种,如果用到了其他的,可以再自己补充
*/
private void getPassElementList(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variableMap) {
// 任务节点
if (curFlowElement instanceof UserTask) {
this.dueUserTaskElement(passElements, flowElements, curFlowElement, variableMap);
return;
}
// 排他网关
if (curFlowElement instanceof ExclusiveGateway) {
this.dueExclusiveGateway(passElements, flowElements, curFlowElement, variableMap);
return;
}
// 并行网关
if (curFlowElement instanceof ParallelGateway) {
this.dueParallelGateway(passElements, flowElements, curFlowElement, variableMap);
}
}
private void dueUserTaskElement(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variableMap) {
passElements.add(curFlowElement);
List<SequenceFlow> outgoingFlows = ((UserTask) curFlowElement).getOutgoingFlows();
String targetRef = outgoingFlows.get(0).getTargetRef();
if (outgoingFlows.size() > 1) {
// 找到表达式成立的sequenceFlow
SequenceFlow sequenceFlow = getSequenceFlow(passElements, variableMap, outgoingFlows);
targetRef = sequenceFlow.getTargetRef();
}
// 根据ID找到FlowElement
FlowElement targetElement = getFlowElement(flowElements, targetRef);
this.getPassElementList(passElements, flowElements, targetElement, variableMap);
}
private void dueExclusiveGateway(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variableMap) {
// 获取符合条件的sequenceFlow的目标FlowElement
List<SequenceFlow> exclusiveGatewayOutgoingFlows = ((ExclusiveGateway) curFlowElement).getOutgoingFlows();
flowElements.remove(curFlowElement);
// 找到表达式成立的sequenceFlow
SequenceFlow sequenceFlow = getSequenceFlow(passElements, variableMap, exclusiveGatewayOutgoingFlows);
// 根据ID找到FlowElement
FlowElement targetElement = getFlowElement(flowElements, sequenceFlow.getTargetRef());
this.getPassElementList(passElements, flowElements, targetElement, variableMap);
}
private void dueParallelGateway(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variableMap) {
FlowElement targetElement;
List<SequenceFlow> parallelGatewayOutgoingFlows = ((ParallelGateway) curFlowElement).getOutgoingFlows();
for (SequenceFlow sequenceFlow : parallelGatewayOutgoingFlows) {
targetElement = getFlowElement(flowElements, sequenceFlow.getTargetRef());
this.getPassElementList(passElements, flowElements, targetElement, variableMap);
}
}
private FlowElement getFlowElement(Collection<FlowElement> flowElements, String targetRef) {
return flowElements.stream().filter(flowElement -> targetRef.equals(flowElement.getId())).findFirst().orElse(null);
}
/**
* @param passElements 流程全部节点
* @param variableMap 参数
* @param outgoingFlows 流转线路
* @return {@link SequenceFlow }
* @description 4. 根据传入的变量,计算出表达式成立的那一条SequenceFlow
* @author yuanzhang
* @date 2023/08/14
**/
private SequenceFlow getSequenceFlow(List<FlowElement> passElements, Map<String, Object> variableMap, List<SequenceFlow> outgoingFlows) {
if (outgoingFlows.size() > 1 && (outgoingFlows.stream().noneMatch(item -> StrUtil.isNotBlank(item.getConditionExpression())))) {
String string = passElements.stream().filter(element -> StrUtil.equals(element.getId(), outgoingFlows.get(0).getSourceRef())).findFirst().map(FlowElement::getName).orElse(StringConstant.EMPTY);
throw new ServiceException(PROCESS_NODE_CONFIGURATION_ERROR, string);
}
// 优先计算存在表达式的
List<SequenceFlow> sortedList = outgoingFlows.stream().sorted(
Comparator.comparing(SequenceFlow::getConditionExpression, Comparator.nullsLast(String::compareTo))
).collect(Collectors.toList());
return sortedList.stream().filter(item -> ELUtil.getValue(variableMap, item.getConditionExpression())).findFirst().orElse(outgoingFlows.get(0));
}
}
package cn.business.module.bpm.util;
import cn.business.framework.common.exception.ServiceException;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.flowable.common.engine.impl.de.odysseus.el.ExpressionFactoryImpl;
import org.flowable.common.engine.impl.de.odysseus.el.util.SimpleContext;
import org.flowable.common.engine.impl.javax.el.ExpressionFactory;
import org.flowable.common.engine.impl.javax.el.PropertyNotFoundException;
import org.flowable.common.engine.impl.javax.el.ValueExpression;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static cn.business.module.bpm.enums.ErrorCodeConstants.PROPERTY_FOR_PROCESS_VARIABLE_NOT_FOUND;
import static cn.business.module.bpm.enums.ErrorCodeConstants.WRONG_SHIFT_TRANSFER_CONDITIONS;
/**
* @author yuanzhang
* @description elutil 解析El表达式
* @date 2023/08/02
**/
@Slf4j
public class ELUtil {
/**
* 原生的解析表达式
*
* @param params 变量的值
* @param exp 表达式
* @param clazz 映射出来的值
* @return
*/
public static <T> T getValue(Map<String, Object> params, String exp, Class<T> clazz) {
ExpressionFactory factory = new ExpressionFactoryImpl();
SimpleContext context = new SimpleContext();
if (MapUtils.isNotEmpty(params)) {
params.forEach((k, v) -> {
if (v instanceof ObjectNode) {
JSONObject jsonObject = JSONObject.parseObject(v.toString());
Map<String, Object> vs = new HashMap<>();
for (String objkey : jsonObject.keySet()) {
vs.put(objkey, jsonObject.get(objkey));
}
context.setVariable(k, factory.createValueExpression(vs, Map.class));
} else {
context.setVariable(k, factory.createValueExpression(v, Object.class));
}
});
}
Object returnObj;
try {
ValueExpression e = factory.createValueExpression(context, exp, clazz);
returnObj = e.getValue(context);
} catch (PropertyNotFoundException e) {
log.error("流程变量的属性找不到,请确认!", e);
throw new ServiceException(PROPERTY_FOR_PROCESS_VARIABLE_NOT_FOUND);
}
return clazz.cast(returnObj);
}
/**
* @param params 参数个数
* @param exp 经验值
* @return {@link Boolean }
* @description 获得价值
* @author yuanzhang
* @date 2023/08/02
**/
public static Boolean getValue(Map<String, Object> params, String exp) {
// 没有表达式默认true
if (StrUtil.isBlank(exp)) {
return true;
}
return getValue(params, exp, Boolean.class);
}
/**
* @param expression 表达式
* @return {@link Integer }
* @description 获取班组派车EL表达式的值 ${deptId == 1}
* @author yuanzhang
* @date 2023/08/18
**/
public static Long getTeamDispatchCarValue(String expression) {
Pattern pattern = Pattern.compile("\\$\\{([^ ]+) ([!=<>]+) ([^ ]+)\\}");
Matcher matcher = pattern.matcher(expression);
if (matcher.matches()) {
String variable = matcher.group(1);
String operator = matcher.group(2);
String value = matcher.group(3);
if (variable.equals("deptId")) {
return Convert.toLong(value);
} else {
throw new ServiceException(WRONG_SHIFT_TRANSFER_CONDITIONS);
}
} else {
throw new ServiceException(WRONG_SHIFT_TRANSFER_CONDITIONS);
}
}
}