流程引擎实现(一)——串行流程实现

背景

在传统的MVC架构中,和业务逻辑相关的代码一般是编写在service层,但随着业务的发展,service层会不断充斥各种逻辑,导致service层过于臃肿、庞大,此外,职责定义不够清晰,如何对service层进行瘦身,以达到职责分离的效果,成为后续开发中不断需要面对的一个难题。

通过流程引擎,将service层的各种业务逻辑,拆分到不同的activitiy节点中,从而达到职责分离的效果,此外,通过对这些activity节点进行编排,能有效进行流程追踪和监控,以及实现流程标准化。

目前主流的开源流程引擎有activiti、flowable、camunda,在我的实际工作项目中,也用到了流程引擎,基于对流程引擎的好奇和兴趣,决定自己实现一个简单的流程引擎,一方面是锻炼自己的编程能力,另一方面是加深自己对流程引擎的一些理解和认识,下面是具体的实现思路。

流程引擎解析

无论是什么类型的流程引擎,在执行的时候,都是依据配置文件中配置的流程执行顺序,挨个执行相应的流程节点,通过这些流程节点的活动和编排的顺序,完成相关的业务逻辑,从而对外提供服务。

首先我们定义流程文件解析接口:

public interface IActivitiFileParser {
    ActivitiFlow parseActivitiFlow(String file);
}

以注册流程为例,假设我们的流程配置文件定义如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<activiti id="register_flow">
    <!--节点定义-->
    <startEvent id="startEvent"/>
    <serviceTask id="registerFormCheck" name="registerFormCheck" description="注册表单校验" class="com.yang.application.register.activiti.RegisterFormCheckActivity"/>
    <serviceTask id="userConflictCheck" name="userConflictCheck" description="用户冲突定位" class="com.yang.application.register.activiti.UserConflictCheckActivity"/>
    <serviceTask id="encryptPassword" name="encryptPassword" description="密码加密" class="com.yang.application.register.activiti.EncryptPasswordActivity"/>
    <serviceTask id="userRoleRich" name="userRoleRich" description="用户权限填充" class="com.yang.application.register.activiti.UserRoleRichActivity"/>
    <serviceTask id="createUser" name="createUser" description="创建用户" class="com.yang.application.register.activiti.CreateUserActivity"/>
    <serviceTask id="buildUserToken" name="buildUserToken" description="创建用户token" class="com.yang.application.register.activiti.BuildUserTokenActivity"/>
    <endEvent id="endEvent"/>

    <!--节点执行顺序编排-->
    <sequenceFlow source="startEvent" target="registerFormCheck"/>
    <sequenceFlow source="registerFormCheck" target="userConflictCheck"/>
    <sequenceFlow source="userConflictCheck" target="encryptPassword"/>
    <sequenceFlow source="encryptPassword" target="userRoleRich"/>
    <sequenceFlow source="userRoleRich" target="createUser"/>
    <sequenceFlow source="createUser" target="buildUserToken"/>
    <sequenceFlow source="buildUserToken" target="endEvent"/>
</activiti>

这里,我们将流程节点分为下列四类:

类型定义
startEvent开始事件
serviceTask服务任务,执行对应的业务逻辑活动
endEvent结束事件
sequenceFlow流程线,用于定义流程节点之间的执行先后顺序

我们将这些类型,抽象到相关的枚举类中

package com.yang.infrastructure.flow.activiti.enums;


public enum FlowNodeType {
    START_EVENT("startEvent"),
    SERVICE_TASK("serviceTask"),
    END_EVENT("endEvent"),
    SEQUENCE_FLOW("sequenceFlow");

    private String code;

    FlowNodeType(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }

    public static FlowNodeType parseByCode(String code) {
        for (FlowNodeType value : values()) {
            if (value.getCode().equals(code)) {
                return value;
            }
        }
        return null;
    }
}

对于上述四种类型,可以进一步划分为两类——节点类和节点连线类,我们为此定义两种模型:

// 节点类
@Data
public class FlowNode implements Serializable {

    private String id;

    private String name;

    private String description;

    private Object target;

    private String classPath;

    private FlowNodeType flowNodeType;

}


// 节点连线类
@Data
public class SequenceFlowNode implements Serializable {

    private String source;

    private String target;

}

然后我们定义流程文件相关的聚合类,该类有两个属性,一个是流程id(也就是流程文件名称),另一个是该流程中的流程节点(这里以List来实现,List中元素的顺序,也就是流程节点的执行顺序)


public class ActivitiFlow {
    private String id;

    private List<FlowNode> flowNodeList = new ArrayList<>();

    public void addFlowNode(FlowNode flowNode) {
        this.flowNodeList.add(flowNode);
    }

    public List<FlowNode> getFlowNodeList() {
        return this.flowNodeList;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

然后对于ActivitiFlow的构造,主要是根据流程连线和流程节点来实现,对于不同的流程文件解析类,他们负责的只是流程连线、流程节点的解析,构造过程则是通用的,因此我们将其构造过程提炼为抽象类


public abstract class AbstractActivitiFileParser implements IActivitiFileParser{

    /**
     * 填充流程节点内容
     * @param flowNode
     * @param flowNodeType
     */
    protected void richFlowNode(FlowNode flowNode, FlowNodeType flowNodeType) {
        if (flowNodeType != FlowNodeType.SERVICE_TASK) {
            return;
        }
        try {
            // 填充target对象
            Class<?> aClass = Class.forName(flowNode.getClassPath());
            Object target = SpringContextUtil.getBeanByType(aClass);
            flowNode.setTarget(target);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据连线顺序,对流程节点进行排序,构建相应的流程flow
     * @param fileName
     * @param flowNodeList
     * @param sequenceFlowNodeList
     * @return
     */
    protected ActivitiFlow buildActivitiFlow(String fileName,
                                             List<FlowNode> flowNodeList,
                                             List<SequenceFlowNode> sequenceFlowNodeList) {
        List<SequenceFlowNode> sortSequenceFlowNodeList = sortSequenceFlowNodeList(sequenceFlowNodeList);
        ActivitiFlow activitiFlow = new ActivitiFlow();
        activitiFlow.setId(fileName);
        Map<String, FlowNode> id2FlowNodeMap = flowNodeList.stream()
                .collect(Collectors.toMap(FlowNode::getId, Function.identity()));
        SequenceFlowNode iterator = null;
        for (int i = 0; i < sortSequenceFlowNodeList.size(); i++) {
            iterator = sortSequenceFlowNodeList.get(i);
            String source = iterator.getSource();
            FlowNode flowNode = id2FlowNodeMap.get(source);
            activitiFlow.addFlowNode(flowNode);
        }
        FlowNode endNode = id2FlowNodeMap.get(iterator.getTarget());
        activitiFlow.addFlowNode(endNode);
        return activitiFlow;
    }

    /**
     * 按照开始到结束,对连线进行排序
     * @param sequenceFlowNodeList
     * @return
     */
    private List<SequenceFlowNode> sortSequenceFlowNodeList(List<SequenceFlowNode> sequenceFlowNodeList) {
        Map<String, SequenceFlowNode> source2SequenceFlowNodeMap = sequenceFlowNodeList.stream()
                .collect(Collectors.toMap(SequenceFlowNode::getSource, Function.identity()));
        Map<String, String> target2SourceMap = new HashMap<>();
        for (SequenceFlowNode sequenceFlowNode : sequenceFlowNodeList) {
            target2SourceMap.put(sequenceFlowNode.getTarget(), sequenceFlowNode.getSource());
        }
        // 定位开始节点
        SequenceFlowNode startSequenceFlowNode = null;
        for (SequenceFlowNode sequenceFlowNode : sequenceFlowNodeList) {
            String from = target2SourceMap.get(sequenceFlowNode.getSource());
            if (from == null) {
                startSequenceFlowNode = sequenceFlowNode;
                break;
            }
        }
        if (startSequenceFlowNode == null) {
            throw new UicException(ErrorCode.ACTIVITI_PARSER_CIRCULATION_ERROR);
        }

        List<SequenceFlowNode> sortSequenceFlowNodeList = new ArrayList<>();
        SequenceFlowNode iterator = startSequenceFlowNode;
        sortSequenceFlowNodeList.add(iterator);
        while (true) {
            String target = iterator.getTarget();
            iterator = source2SequenceFlowNodeMap.get(target);
            if (iterator == null) {
                break;
            }
            sortSequenceFlowNodeList.add(iterator);
        }
        return sortSequenceFlowNodeList;
    }
}

最一般流程引擎的流程编排配置文件,是xml类型的,因此这里实现一个Xml配置文件的解析类,用于解析我们的xml流程文件,具体实现内容如下:

package com.yang.infrastructure.flow.activiti.parser;

import com.yang.infrastructure.flow.activiti.enums.FlowNodeType;
import com.yang.infrastructure.flow.activiti.model.ActivitiFlow;
import com.yang.infrastructure.flow.activiti.model.FlowNode;
import com.yang.infrastructure.flow.activiti.model.SequenceFlowNode;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class XmlActivitiFileParser extends AbstractActivitiFileParser {
    private static final String ACTIVITI_PREFIX = "yang-engine";

    @Override
    public ActivitiFlow parseActivitiFlow(String fileName) {
       return parseActivitiFlow(ACTIVITI_PREFIX, fileName);
    }

    public ActivitiFlow parseActivitiFlow(String directory, String fileName) {
        String fullFileName = directory + File.separator + fileName;
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(fullFileName);
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document document = db.parse(inputStream);
            // 解析出流程节点
            List<FlowNode> flowNodeList = parseFlowNodeList(document);
            // 解析流程执行顺序
            List<SequenceFlowNode> sequenceFlowNodeList = parseSequenceFlowNodeList(document);
            String activitiId = parseActivitiId(document);
            if (StringUtils.isEmpty(activitiId)) {
                activitiId = fileName;
            }
            return buildActivitiFlow(activitiId, flowNodeList, sequenceFlowNodeList);
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 解析流程id
     * @param document
     * @return
     */
    private String parseActivitiId(Document document) {
        NodeList nodeList = document.getElementsByTagName("activiti");
        Node activitiNode = nodeList.item(0);
        NamedNodeMap attributes = activitiNode.getAttributes();
        Node id = attributes.getNamedItem("id");
        if (id != null) {
            return id.getNodeValue();
        }
        return null;
    }

    /**
     * 解析流程节点列表
     * @param document
     * @return
     */
    private List<FlowNode> parseFlowNodeList(Document document) {
        List<FlowNode> flowNodeList = new ArrayList<>();
        NodeList activitiNodeList = document.getElementsByTagName("activiti");
        if (activitiNodeList.getLength() <= 0) {
            return flowNodeList;
        }
        Node activitiNode = activitiNodeList.item(0);
        NodeList nodeList = activitiNode.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            FlowNodeType flowNodeType = FlowNodeType.parseByCode(node.getNodeName());
            if (flowNodeType == null || flowNodeType == FlowNodeType.SEQUENCE_FLOW) {
                continue;
            }
            NamedNodeMap attributes = node.getAttributes();
            FlowNode flowNode = parseFlowNode(attributes, flowNodeType);
            flowNodeList.add(flowNode);
        }
        return flowNodeList;
    }

    /**
     * 解析流程节点
     * @param attributes
     * @param flowNodeType
     * @return
     */
    private FlowNode parseFlowNode(NamedNodeMap attributes, FlowNodeType flowNodeType) {
        if (attributes == null || attributes.getLength() <= 0) {
            return null;
        }
        FlowNode flowNode = new FlowNode();
        Node idNode = attributes.getNamedItem("id");
        Node nameNode = attributes.getNamedItem("name");
        Node descriptionNode = attributes.getNamedItem("description");
        Node classNode = attributes.getNamedItem("class");
        if (idNode == null) {
            return null;
        }
        flowNode.setId(idNode.getNodeValue());
        if (nameNode != null) {
            flowNode.setName(nameNode.getNodeValue());
        }
        if (descriptionNode != null) {
            flowNode.setDescription(descriptionNode.getNodeValue());
        }
        if (classNode != null) {
            flowNode.setClassPath(classNode.getNodeValue());
        }
        flowNode.setFlowNodeType(flowNodeType);
        richFlowNode(flowNode, flowNodeType);
        return flowNode;
    }

    /**
     * 解析流程执行顺序节点列表
     * @param document
     * @return
     */
    private List<SequenceFlowNode> parseSequenceFlowNodeList(Document document) {
        NodeList nodeList = document.getElementsByTagName(FlowNodeType.SEQUENCE_FLOW.getCode());
        List<SequenceFlowNode> sequenceFlowNodes = new ArrayList<>();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            NamedNodeMap attributes = node.getAttributes();
            SequenceFlowNode sequenceFlowNode = parseSequenceFlowNode(attributes);
            if (sequenceFlowNode != null) {
                sequenceFlowNodes.add(sequenceFlowNode);
            }
        }
        return sequenceFlowNodes;
    }

    /**
     * 解析流程执行顺序节点
     * @param attributes
     * @return
     */
    private SequenceFlowNode parseSequenceFlowNode(NamedNodeMap attributes) {
        Node sourceNode = attributes.getNamedItem("source");
        Node targetNode = attributes.getNamedItem("target");
        if (sourceNode != null && targetNode != null) {
            SequenceFlowNode sequenceFlowNode = new SequenceFlowNode();
            sequenceFlowNode.setSource(sourceNode.getNodeValue());
            sequenceFlowNode.setTarget(targetNode.getNodeValue());
            return sequenceFlowNode;
        }
        return null;
    }

    public static void main(String[] args) {
        IActivitiFileParser activitiFileParser = new XmlActivitiFileParser();
        ActivitiFlow activitiFlow = activitiFileParser.parseActivitiFlow("register_flow.xml");
    }
}

在这里插入图片描述

最后添加对应的测试方法,测试上述方式是否能成功解析流程引擎配置文件:

  @Test
    public void testParseActivitiFile() {
        IActivitiFileParser iActivitiFileParser = new XmlActivitiFileParser();
        ActivitiFlow activitiFlow = iActivitiFileParser.parseActivitiFlow("register_flow.xml");
        for (FlowNode flowNode : activitiFlow.getFlowNodeList()) {
            System.out.println(flowNode);
        }
        System.out.println(activitiFlow);
    }

解析结果如下:

懒加载流程到缓存中

创建ActivitiFlow流程管理类,在第一次获取ActivitiFlow,将其加载到缓存中,以减少后续的流程配置文件解析次数。

package com.yang.infrastructure.flow.activiti;

import com.yang.infrastructure.common.ErrorCode;
import com.yang.infrastructure.common.exception.UicException;
import com.yang.infrastructure.flow.activiti.model.ActivitiFlow;
import com.yang.infrastructure.flow.activiti.parser.IActivitiFileParser;
import com.yang.infrastructure.utils.SpringContextUtil;

import java.util.HashMap;
import java.util.Map;

public class ActivitiFlowManager {
    private static Map<String, ActivitiFlow> id2ActivitiFlowCache = new HashMap<>();

    public static ActivitiFlow getActivitiFlow(String flowName) {
        ActivitiFlow activitiFlow = id2ActivitiFlowCache.get(flowName);
        if (activitiFlow != null) {
            return activitiFlow;
        }
        IActivitiFileParser iActivitiFileParser = SpringContextUtil.getBeanByType(IActivitiFileParser.class);
        synchronized (flowName.intern()) {
            activitiFlow = id2ActivitiFlowCache.get(flowName);
            if (activitiFlow != null) {
                return activitiFlow;
            }
            activitiFlow = iActivitiFileParser.parseActivitiFlow(flowName);
            if (activitiFlow == null) {
                throw new UicException(ErrorCode.ACTIVITI_NOT_FOUND_ERROR);
            }
            id2ActivitiFlowCache.put(flowName, activitiFlow);
        }
        return activitiFlow;
    }
}

流程引擎执行

定义流程引擎入参和出参,这里采用泛形的方式,定义对应的入参和出参,因为我们流程引擎不感知具体的业务参数类型,只需提供一个通用的底层交互协议。


@Data
public class ActivitiEngineRequest <T> implements Serializable {

    /**
     * 场景code
     */
    private String scenarioCode;

    private T baseRequest;
}



@Data
public class ActivitiEngineResponse <T> implements Serializable {
    /**
     * 场景code
     */
    private String scenarioCode;

    private T response;
}

然后我们定义流程引擎执行接口

public interface IActivitiService {
    void execute(ActivitiEngineRequest request, ActivitiEngineResponse response);
}

对于startEvent, endEvent, serviceTask这几种流程节点类型,依次去实现对应的IActivitiService,因为目前暂时只用到serviceTask,所以下面只列出serviceTask的实现


public interface IServiceTask<DomainRequest, DomainResponse> extends IActivitiService {
    default void execute(ActivitiEngineRequest request, ActivitiEngineResponse response) {
        DomainRequest domainRequest = buildDomainRequest(request, response);
        if (domainRequest != null) {
            DomainResponse domainResponse = apply(domainRequest);
            attachment(domainResponse, response);
            return;
        }
        doElse(request, response);
    }

    /**
     * 默认不实现,一般先转为domain Request,然后调用领域层方法,进行转化,doElse适用于目前领域层无法支持相关实现,顾需要在activity层进行适配的逻辑
     * @param request
     * @param response
     */
    default void doElse(ActivitiEngineRequest request, ActivitiEngineResponse response) {}

    /**
     * 入参转化
     * @param request
     * @param response
     * @return
     */
    DomainRequest buildDomainRequest(ActivitiEngineRequest request, ActivitiEngineResponse response);

    /**
     * 领域服务调用
     * @param domainRequest
     * @return
     */
    DomainResponse apply(DomainRequest domainRequest);

    /**
     * 领域服务调用结果填充
     * @param domainResponse
     * @param response
     */
    void attachment(DomainResponse domainResponse, ActivitiEngineResponse response);
}

在IServiceTask的流程引擎调用中,这里的实现思路是将流程引擎入参先转化为对应的领域入参,然后调用领域服务执行对应的领域操作,最后,将领域服务返回结果,转化或填充到我们的流程引擎出参当中。示例代码如下:


@Component
public class EncryptPasswordActivity implements IServiceTask<UserPasswordEncryptDomainRequest, UserPasswordEncryptDomainResponse> {
    @Autowired
    private UserPasswordDomainService userPasswordDomainService;

    @Override
    public UserPasswordEncryptDomainRequest buildDomainRequest(ActivitiEngineRequest request, ActivitiEngineResponse response) {
        RegisterRequest registerRequest = (RegisterRequest) request.getBaseRequest();
        UserPasswordEncryptDomainRequest userPasswordEncryptDomainRequest = new UserPasswordEncryptDomainRequest();

        String encryptType = registerRequest.getExtendMaps().get("encryptType");
        EncryptTypeEnum encryptTypeEnum = EncryptTypeEnum.parseByType(encryptType);
        userPasswordEncryptDomainRequest.setEncryptType(encryptTypeEnum);
        userPasswordEncryptDomainRequest.setPlainPassword(registerRequest.getPassword());
        return userPasswordEncryptDomainRequest;
    }

    @Override
    public UserPasswordEncryptDomainResponse apply(UserPasswordEncryptDomainRequest userPasswordEncryptDomainRequest) {
        return userPasswordDomainService.encryptPassword(userPasswordEncryptDomainRequest);
    }

    @Override
    public void attachment(UserPasswordEncryptDomainResponse userPasswordEncryptDomainResponse, ActivitiEngineResponse response) {
        UserModel userModel = (UserModel) response.getResponse();
        userModel.setEncryptPassword(userPasswordEncryptDomainResponse.getEncryptPassword());
    }
}

但是这有一个缺陷,就是我们的领域服务一般都是比较原子的,当我们要执行的操作涉及多个原子操作时,上面这种思路就无法支撑,所以这里加了一个doElse来兼容,示例代码如下:


@Component
public class UserConflictCheckActivity implements IServiceTask<UserQueryDomainRequest, UserQueryDomainResponse> {
    @Autowired
    private UserDomainService userDomainService;

    @Override
    public UserQueryDomainRequest buildDomainRequest(ActivitiEngineRequest request, ActivitiEngineResponse response) {
        return null;
    }

    @Override
    public UserQueryDomainResponse apply(UserQueryDomainRequest userQueryDomainRequest) {
        return null;
    }

    @Override
    public void doElse(ActivitiEngineRequest request, ActivitiEngineResponse response) {
        RegisterRequest registerRequest = (RegisterRequest) request.getBaseRequest();
        if (StringUtils.isNotEmpty(registerRequest.getLoginId())) {
            UserQueryDomainRequest userQueryDomainRequest = new UserQueryDomainRequest.UserQueryDomainRequestBuilder()
                    .userQueryType(UserQueryType.LOGIN_ID)
                    .queryMessage(registerRequest.getLoginId())
                    .build();
            UserQueryDomainResponse userQueryDomainResponse = userDomainService.query(userQueryDomainRequest);
            if (!CollectionUtils.isEmpty(userQueryDomainResponse.getUserAccountList())) {
                throw new UicException(ErrorCode.USER_CONFLICT_ERROR);
            }
        }
        UserQueryDomainRequest userQueryDomainRequest = new UserQueryDomainRequest.UserQueryDomainRequestBuilder()
                .userQueryType(UserQueryType.EMAIL)
                .queryMessage(registerRequest.getEmail())
                .build();
        UserQueryDomainResponse userQueryDomainResponse = userDomainService.query(userQueryDomainRequest);
        if (!CollectionUtils.isEmpty(userQueryDomainResponse.getUserAccountList())) {
            throw new UicException(ErrorCode.USER_CONFLICT_ERROR);
        }
    }

    @Override
    public void attachment(UserQueryDomainResponse userQueryDomainResponse, ActivitiEngineResponse response) {

    }
}

最后,我们修改ActivitiFlowManager, 增加执行方法,执行的思路是,先解析对应的流程配置,然后根据流程节点类型,依次执行对应的流程方法


public class ActivitiFlowManager {
    private static Map<String, ActivitiFlow> id2ActivitiFlowCache = new HashMap<>();

    public static ActivitiFlow getActivitiFlow(String flowName) {
        ActivitiFlow activitiFlow = id2ActivitiFlowCache.get(flowName);
        if (activitiFlow != null) {
            return activitiFlow;
        }
        IActivitiFileParser iActivitiFileParser = SpringContextUtil.getBeanByType(IActivitiFileParser.class);
        synchronized (flowName.intern()) {
            activitiFlow = id2ActivitiFlowCache.get(flowName);
            if (activitiFlow != null) {
                return activitiFlow;
            }
            activitiFlow = iActivitiFileParser.parseActivitiFlow(flowName);
            if (activitiFlow == null) {
                throw new UicException(ErrorCode.ACTIVITI_NOT_FOUND_ERROR);
            }
            id2ActivitiFlowCache.put(flowName, activitiFlow);
        }
        return activitiFlow;
    }

    /**
     * 流程引擎执行方法
     * @param flowName
     * @param activitiEngineRequest
     * @return
     * @param <Request>
     * @param <Response>
     */
    public static <Request, Response> ActivitiEngineResponse<Response> startEngine(String flowName,
                                                                                   ActivitiEngineRequest<Request> activitiEngineRequest) {
        ActivitiFlow activitiFlow = getActivitiFlow(flowName);
        ActivitiEngineResponse<Response> activitiEngineResponse = new ActivitiEngineResponse<>();
        for (FlowNode flowNode : activitiFlow.getFlowNodeList()) {
            FlowNodeType flowNodeType = flowNode.getFlowNodeType();
            switch (flowNodeType) {
                case START_EVENT:
                    break;
                case END_EVENT:
                    break;
                case SERVICE_TASK:
                    executeServiceTask(activitiEngineRequest, activitiEngineResponse, flowNode);
                    break;
            }
        }
        return activitiEngineResponse;
    }

    private static <Request, Response> void executeServiceTask(ActivitiEngineRequest<Request> activitiEngineRequest, ActivitiEngineResponse<Response> activitiEngineResponse, FlowNode flowNode) {
        IServiceTask iServiceTask = (IServiceTask) flowNode.getTarget();
        iServiceTask.execute(activitiEngineRequest, activitiEngineResponse);
    }
}

测试代码如下:

   @Override
    public ResultT<String> register2(RegisterRequest registerRequest) {
        ActivitiEngineRequest<RegisterRequest> activitiEngineRequest = new ActivitiEngineRequest<>();
        activitiEngineRequest.setBaseRequest(registerRequest);
        ActivitiEngineResponse<UserModel> activitiEngineResponse = ActivitiFlowManager.startEngine("register_flow.xml", activitiEngineRequest);
        UserModel userModel = activitiEngineResponse.getResponse();
        if (userModel != null && userModel.getUserToken() != null) {
            String token = userModel.getUserToken().getToken();
            return ResultT.success(token);
        }
        return ResultT.fail();
    }



 @Test
    public void testEngineRegister() {
        RegisterRequest registerRequest = new RegisterRequest();
        registerRequest.setLoginId("test1");
        registerRequest.setEmail("2827523200@qq.com");
        registerRequest.setPassword("hello1234");
        ResultT<String> resultT = userService.register2(registerRequest);
        System.out.println(resultT);
        System.out.println(resultT.getData());
    }

执行结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值