flowable 流程跟踪

flowable 流程跟踪

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);
        }
    }


}