feat: 2.6.0
1. 优化查询DAG详情
This commit is contained in:
		
							parent
							
								
									594013f65b
								
							
						
					
					
						commit
						9c99330adb
					
				@ -0,0 +1,29 @@
 | 
			
		||||
package com.aizuda.easy.retry.server.common.enums;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author xiaowoniu
 | 
			
		||||
 * @date 2023-12-15 22:42:17
 | 
			
		||||
 * @since 2.6.0
 | 
			
		||||
 */
 | 
			
		||||
@Getter
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public enum WorkflowNodeType {
 | 
			
		||||
    TASK(1, "任务节点"),
 | 
			
		||||
    Condition(1, "条件节点"),
 | 
			
		||||
    callback(1, "回调节点");
 | 
			
		||||
 | 
			
		||||
    private final Integer nodeType;
 | 
			
		||||
    private final String desc;
 | 
			
		||||
 | 
			
		||||
    public static WorkflowNodeType get(Integer nodeType) {
 | 
			
		||||
        for (WorkflowNodeType workflowNodeType : WorkflowNodeType.values()) {
 | 
			
		||||
            if (workflowNodeType.nodeType.equals(nodeType)) {
 | 
			
		||||
                return workflowNodeType;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,33 +1,19 @@
 | 
			
		||||
package com.aizuda.easy.retry.server.web.controller;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.ReUtil;
 | 
			
		||||
import com.aizuda.easy.retry.server.common.config.SystemProperties;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.annotation.LoginRequired;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.annotation.LoginUser;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.annotation.RoleEnum;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.model.base.PageResult;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.model.request.GroupConfigQueryVO;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.model.request.GroupConfigRequestVO;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.model.request.UserSessionVO;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.model.response.GroupConfigResponseVO;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.annotation.LoginRequired;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.annotation.RoleEnum;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.service.GroupConfigService;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.enums.DbTypeEnum;
 | 
			
		||||
import com.google.common.collect.Lists;
 | 
			
		||||
import com.zaxxer.hikari.HikariDataSource;
 | 
			
		||||
import org.jetbrains.annotations.NotNull;
 | 
			
		||||
import org.slf4j.helpers.MessageFormatter;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.jdbc.core.JdbcTemplate;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
 | 
			
		||||
import javax.sql.DataSource;
 | 
			
		||||
import java.sql.Connection;
 | 
			
		||||
import java.sql.SQLException;
 | 
			
		||||
import java.text.MessageFormat;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 重试组接口
 | 
			
		||||
 | 
			
		||||
@ -41,8 +41,9 @@ public class WorkflowController {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PutMapping
 | 
			
		||||
    public void updateWorkflow() {
 | 
			
		||||
 | 
			
		||||
    @LoginRequired(role = RoleEnum.USER)
 | 
			
		||||
    public Boolean updateWorkflow(@RequestBody @Validated WorkflowRequestVO workflowRequestVO) {
 | 
			
		||||
        return workflowService.updateWorkflow(workflowRequestVO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @GetMapping("{id}")
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,8 @@ import java.util.List;
 | 
			
		||||
@Data
 | 
			
		||||
public class WorkflowRequestVO {
 | 
			
		||||
 | 
			
		||||
    private Long id;
 | 
			
		||||
 | 
			
		||||
    @NotBlank(message = "组名称不能为空")
 | 
			
		||||
    @Pattern(regexp = "^[A-Za-z0-9_]{1,64}$", message = "仅支持长度为1~64字符且类型为数字、字母和下划线")
 | 
			
		||||
    private String groupName;
 | 
			
		||||
 | 
			
		||||
@ -21,4 +21,6 @@ public interface WorkflowService {
 | 
			
		||||
    WorkflowDetailResponseVO getWorkflowDetail(Long id) throws IOException;
 | 
			
		||||
 | 
			
		||||
    PageResult<List<WorkflowResponseVO>> listPage(WorkflowQueryVO queryVO);
 | 
			
		||||
 | 
			
		||||
    Boolean updateWorkflow(WorkflowRequestVO workflowRequestVO);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,16 +11,13 @@ import com.aizuda.easy.retry.server.web.model.request.WorkflowQueryVO;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.model.request.WorkflowRequestVO;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.model.request.WorkflowRequestVO.NodeConfig;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.model.request.WorkflowRequestVO.NodeInfo;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.model.response.JobResponseVO;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.model.response.WorkflowDetailResponseVO;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.model.response.WorkflowResponseVO;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.service.WorkflowService;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.service.convert.JobResponseVOConverter;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.service.convert.WorkflowConverter;
 | 
			
		||||
import com.aizuda.easy.retry.server.web.util.UserSessionUtils;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.mapper.WorkflowMapper;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.mapper.WorkflowNodeMapper;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.po.Job;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.po.Workflow;
 | 
			
		||||
import com.aizuda.easy.retry.template.datasource.persistence.po.WorkflowNode;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
@ -28,6 +25,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
 | 
			
		||||
import com.fasterxml.jackson.core.type.TypeReference;
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import com.google.common.collect.Lists;
 | 
			
		||||
import com.google.common.collect.Sets;
 | 
			
		||||
import com.google.common.graph.GraphBuilder;
 | 
			
		||||
import com.google.common.graph.MutableGraph;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
@ -101,8 +99,9 @@ public class WorkflowServiceImpl implements WorkflowService {
 | 
			
		||||
 | 
			
		||||
        String flowInfo = workflow.getFlowInfo();
 | 
			
		||||
        try {
 | 
			
		||||
            MutableGraph<Long> graph = deserializeJsonToGraph(flowInfo);
 | 
			
		||||
            // 反序列化构建图
 | 
			
		||||
            WorkflowDetailResponseVO.NodeConfig config = deserializeJsonToGraph(flowInfo, workflowNodeMap);
 | 
			
		||||
            WorkflowDetailResponseVO.NodeConfig config = buildNodeConfig(graph, root, new HashMap<>(), workflowNodeMap);
 | 
			
		||||
            responseVO.setNodeConfig(config);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.error("反序列化失败. json:[{}]", flowInfo, e);
 | 
			
		||||
@ -128,50 +127,100 @@ public class WorkflowServiceImpl implements WorkflowService {
 | 
			
		||||
        return new PageResult<>(pageDTO, jobResponseList);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Boolean updateWorkflow(WorkflowRequestVO workflowRequestVO) {
 | 
			
		||||
 | 
			
		||||
        Assert.notNull(workflowRequestVO.getId(), () -> new EasyRetryServerException("工作流ID不能为空"));
 | 
			
		||||
 | 
			
		||||
        Assert.isTrue(workflowMapper.selectCount(new LambdaQueryWrapper<Workflow>()
 | 
			
		||||
                        .eq(Workflow::getId, workflowRequestVO.getId())) > 0,
 | 
			
		||||
                () -> new EasyRetryServerException("工作流不存在"));
 | 
			
		||||
 | 
			
		||||
        MutableGraph<Long> graph = GraphBuilder.directed().allowsSelfLoops(false).build();
 | 
			
		||||
        // 添加虚拟头节点
 | 
			
		||||
        graph.addNode(root);
 | 
			
		||||
 | 
			
		||||
        // 获取DAG节点配置
 | 
			
		||||
        NodeConfig nodeConfig = workflowRequestVO.getNodeConfig();
 | 
			
		||||
 | 
			
		||||
        // 递归构建图
 | 
			
		||||
        buildGraph(Lists.newArrayList(root), workflowRequestVO.getGroupName(), workflowRequestVO.getId(), nodeConfig, graph);
 | 
			
		||||
 | 
			
		||||
        log.info("图构建完成. graph:[{}]", graph);
 | 
			
		||||
 | 
			
		||||
        // 保存图信息
 | 
			
		||||
        Workflow workflow = new Workflow();
 | 
			
		||||
        workflow.setId(workflowRequestVO.getId());
 | 
			
		||||
        workflow.setFlowInfo(JsonUtil.toJsonString(convertGraphToAdjacencyList(graph)));
 | 
			
		||||
        Assert.isTrue(workflowMapper.updateById(workflow) > 0, () -> new EasyRetryServerException("更新失败"));
 | 
			
		||||
 | 
			
		||||
        return Boolean.TRUE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 从JSON反序列化为Guava图
 | 
			
		||||
    private static  WorkflowDetailResponseVO.NodeConfig deserializeJsonToGraph(String jsonGraph,
 | 
			
		||||
         Map<Long, WorkflowDetailResponseVO.NodeInfo> workflowNodeMap) throws IOException {
 | 
			
		||||
    private static MutableGraph<Long> deserializeJsonToGraph(String jsonGraph) throws IOException {
 | 
			
		||||
        ObjectMapper objectMapper = new ObjectMapper();
 | 
			
		||||
 | 
			
		||||
        // 将JSON字符串转换为Map<String, Iterable<String>>
 | 
			
		||||
        // 将JSON字符串转换为Map<Long, Iterable<Long>>
 | 
			
		||||
        Map<Long, Iterable<Long>> adjacencyList = objectMapper.readValue(
 | 
			
		||||
            jsonGraph, new TypeReference<Map<Long, Iterable<Long>>>() {});
 | 
			
		||||
 | 
			
		||||
        Map<Long, WorkflowDetailResponseVO.NodeConfig> configMap = new HashMap<>();
 | 
			
		||||
        WorkflowDetailResponseVO.NodeConfig rootConfig = new WorkflowDetailResponseVO.NodeConfig();
 | 
			
		||||
                jsonGraph, new TypeReference<Map<Long, Iterable<Long>>>() {
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
        // 创建Guava图并添加节点和边
 | 
			
		||||
        MutableGraph<Long> graph = GraphBuilder.directed().build();
 | 
			
		||||
        for (Map.Entry<Long, Iterable<Long>> entry : adjacencyList.entrySet()) {
 | 
			
		||||
            Long node = entry.getKey();
 | 
			
		||||
            Iterable<Long> successors = entry.getValue();
 | 
			
		||||
            WorkflowDetailResponseVO.NodeConfig previousConfig = configMap.getOrDefault(node, new WorkflowDetailResponseVO.NodeConfig());
 | 
			
		||||
 | 
			
		||||
            WorkflowDetailResponseVO.NodeConfig currentConfig = new WorkflowDetailResponseVO.NodeConfig();
 | 
			
		||||
            graph.addNode(node);
 | 
			
		||||
            for (Long successor : successors) {
 | 
			
		||||
                graph.putEdge(node, successor);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return graph;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private WorkflowDetailResponseVO.NodeConfig buildNodeConfig(MutableGraph<Long> graph,
 | 
			
		||||
                                                                Long parentId,
 | 
			
		||||
                                                                Map<Long, WorkflowDetailResponseVO.NodeConfig> nodeConfigMap,
 | 
			
		||||
                                                                Map<Long, WorkflowDetailResponseVO.NodeInfo> workflowNodeMap) {
 | 
			
		||||
 | 
			
		||||
        Set<Long> successors = graph.successors(parentId);
 | 
			
		||||
        if (CollectionUtils.isEmpty(successors)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        WorkflowDetailResponseVO.NodeInfo previousNodeInfo = workflowNodeMap.get(parentId);
 | 
			
		||||
        WorkflowDetailResponseVO.NodeConfig currentConfig = new WorkflowDetailResponseVO.NodeConfig();
 | 
			
		||||
        currentConfig.setConditionNodes(Lists.newArrayList());
 | 
			
		||||
 | 
			
		||||
        boolean mount = false;
 | 
			
		||||
 | 
			
		||||
        for (Long successor : successors) {
 | 
			
		||||
            Set<Long> predecessors = graph.predecessors(successor);
 | 
			
		||||
            WorkflowDetailResponseVO.NodeInfo nodeInfo = workflowNodeMap.get(successor);
 | 
			
		||||
                // 第一层节点
 | 
			
		||||
                if (node == root) {
 | 
			
		||||
                    rootConfig.setNodeType(nodeInfo.getNodeType());
 | 
			
		||||
                    List<WorkflowDetailResponseVO.NodeInfo> nodeInfos = Optional.ofNullable(
 | 
			
		||||
                        rootConfig.getConditionNodes()).orElse(Lists.newArrayList());
 | 
			
		||||
                    nodeInfos.add(nodeInfo);
 | 
			
		||||
                    rootConfig.setConditionNodes(nodeInfos);
 | 
			
		||||
                    configMap.put(nodeInfo.getId(), rootConfig);
 | 
			
		||||
                } else {
 | 
			
		||||
            currentConfig.setNodeType(nodeInfo.getNodeType());
 | 
			
		||||
                    List<WorkflowDetailResponseVO.NodeInfo> nodeInfos = Optional.ofNullable(
 | 
			
		||||
                        currentConfig.getConditionNodes()).orElse(Lists.newArrayList());
 | 
			
		||||
                    nodeInfos.add(nodeInfo);
 | 
			
		||||
                    currentConfig.setConditionNodes(nodeInfos);
 | 
			
		||||
                    configMap.put(nodeInfo.getId(), currentConfig);
 | 
			
		||||
                    previousConfig.setChildNode(currentConfig);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            currentConfig.getConditionNodes().add(nodeInfo);
 | 
			
		||||
            nodeConfigMap.put(successor, currentConfig);
 | 
			
		||||
 | 
			
		||||
            if (predecessors.size() >= 2) {
 | 
			
		||||
                WorkflowDetailResponseVO.NodeConfig parentNodeConfig = nodeConfigMap.get(new ArrayList<>(predecessors).get(0));
 | 
			
		||||
                parentNodeConfig.setChildNode(currentConfig);
 | 
			
		||||
                mount = false;
 | 
			
		||||
            } else {
 | 
			
		||||
                mount = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        return rootConfig;
 | 
			
		||||
            buildNodeConfig(graph, successor, nodeConfigMap, workflowNodeMap);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (parentId != root && mount) {
 | 
			
		||||
            previousNodeInfo.setChildNode(currentConfig);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return currentConfig;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Map<Long, Iterable<Long>> convertGraphToAdjacencyList(MutableGraph<Long> graph) {
 | 
			
		||||
        Map<Long, Iterable<Long>> adjacencyList = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user