diff --git a/src/main/java/com/metis/controller/TestController.java b/src/main/java/com/metis/controller/TestController.java index 53dbd80..657f89f 100644 --- a/src/main/java/com/metis/controller/TestController.java +++ b/src/main/java/com/metis/controller/TestController.java @@ -1,17 +1,25 @@ package com.metis.controller; +import com.metis.flow.domain.bo.BuildApp; +import com.metis.flow.validator.ValidatorService; import com.metis.result.Result; -import org.springframework.web.bind.annotation.GetMapping; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") +@RequiredArgsConstructor public class TestController { - @GetMapping - public Result test() { + private final ValidatorService validatorService; + + @PostMapping + public Result test(@RequestBody BuildApp app) { + validatorService.validate(app); return Result.ok("测试成功"); } diff --git a/src/main/java/com/metis/flow/domain/entity/base/NodeData.java b/src/main/java/com/metis/flow/domain/entity/base/NodeData.java index 5ae8089..c6add39 100644 --- a/src/main/java/com/metis/flow/domain/entity/base/NodeData.java +++ b/src/main/java/com/metis/flow/domain/entity/base/NodeData.java @@ -3,6 +3,7 @@ package com.metis.flow.domain.entity.base; import com.alibaba.fastjson2.JSONObject; import com.metis.flow.enums.PositionType; import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Data; @@ -15,6 +16,7 @@ public class NodeData { /** * 标签 */ + @NotBlank(message = "标签不能为空") private String label; /** diff --git a/src/main/java/com/metis/flow/domain/entity/config/node/DocumentExtractorNodeConfig.java b/src/main/java/com/metis/flow/domain/entity/config/node/DocumentExtractorNodeConfig.java new file mode 100644 index 0000000..76d1fc4 --- /dev/null +++ b/src/main/java/com/metis/flow/domain/entity/config/node/DocumentExtractorNodeConfig.java @@ -0,0 +1,12 @@ +package com.metis.flow.domain.entity.config.node; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +@Data +public class DocumentExtractorNodeConfig { + + @NotBlank(message = "文件类型不能为空") + private String fileType; + +} diff --git a/src/main/java/com/metis/flow/domain/entity/config/package-info.java b/src/main/java/com/metis/flow/domain/entity/config/package-info.java new file mode 100644 index 0000000..a2ccdb1 --- /dev/null +++ b/src/main/java/com/metis/flow/domain/entity/config/package-info.java @@ -0,0 +1 @@ +package com.metis.flow.domain.entity.config; \ No newline at end of file diff --git a/src/main/java/com/metis/flow/enums/NodeType.java b/src/main/java/com/metis/flow/enums/NodeType.java index 0c16ad4..dea39f3 100644 --- a/src/main/java/com/metis/flow/enums/NodeType.java +++ b/src/main/java/com/metis/flow/enums/NodeType.java @@ -2,6 +2,7 @@ package com.metis.flow.enums; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; +import com.metis.flow.domain.entity.config.node.DocumentExtractorNodeConfig; import lombok.AllArgsConstructor; import lombok.Getter; @@ -13,6 +14,7 @@ public enum NodeType { START(1, "start", "开始", Object.class), END(2, "end", "结束", Object.class), + DOCUMENT_EXTRACTOR(3, "document-extractor", "文档提取器", DocumentExtractorNodeConfig.class) ; diff --git a/src/main/java/com/metis/flow/validator/ValidatorCodeService.java b/src/main/java/com/metis/flow/validator/ValidatorCodeService.java new file mode 100644 index 0000000..9ee3d25 --- /dev/null +++ b/src/main/java/com/metis/flow/validator/ValidatorCodeService.java @@ -0,0 +1,65 @@ +package com.metis.flow.validator; + +import cn.hutool.core.collection.CollUtil; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validator; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Set; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ValidatorCodeService { + + private final Validator globalValidator; + + /** + * 验证, 出现校验未通过则抛出异常 + * + * @param validObject 有效对象 + */ + public void validateThrow(T validObject) { + // validation 编程式校验 + Set> validates = globalValidator.validate(validObject); + if (CollUtil.isNotEmpty(validates)) { + List errorMessage = validates.stream() + .map(ConstraintViolation::getMessage).toList(); + throw new RuntimeException(String.join(",", errorMessage)); + } + } + + /** + * 验证错误信息 + * + * @param validObject 有效对象 + * @return {@link String } + */ + public String validateErrorMsg(T validObject) { + // validation 编程式校验 + Set> validates = globalValidator.validate(validObject); + if (CollUtil.isNotEmpty(validates)) { + List errorMessage = validates.stream() + .map(ConstraintViolation::getMessage).toList(); + return String.join(",", errorMessage); + } + return null; + } + + + /** + * 验证 + * + * @param validObject 有效对象 + * @return boolean + */ + public boolean validate(T validObject) { + // validation 编程式校验 + Set> validates = globalValidator.validate(validObject); + return CollUtil.isEmpty(validates); + } + +} diff --git a/src/main/java/com/metis/flow/validator/impl/ValidatorServiceImpl.java b/src/main/java/com/metis/flow/validator/impl/ValidatorServiceImpl.java index f11af15..d6630fe 100644 --- a/src/main/java/com/metis/flow/validator/impl/ValidatorServiceImpl.java +++ b/src/main/java/com/metis/flow/validator/impl/ValidatorServiceImpl.java @@ -9,39 +9,28 @@ import com.metis.flow.domain.entity.base.Edge; import com.metis.flow.domain.entity.base.Node; import com.metis.flow.enums.EdgeType; import com.metis.flow.enums.NodeType; -import com.metis.flow.validator.EdgeValidator; -import com.metis.flow.validator.NodeValidator; -import com.metis.flow.validator.ValidatorResult; -import com.metis.flow.validator.ValidatorService; +import com.metis.flow.validator.*; import com.metis.flow.validator.factory.EdgeValidatorFactory; import com.metis.flow.validator.factory.NodeValidatorFactory; -import jakarta.validation.ConstraintViolation; -import jakarta.validation.Validator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; @Slf4j @Service @RequiredArgsConstructor public class ValidatorServiceImpl implements ValidatorService { - private final Validator globalValidator; + private final ValidatorCodeService validatorCodeService; @Override public void validate(BuildApp graph) { // validation 编程式校验 - Set> validates = globalValidator.validate(graph); - if (CollUtil.isNotEmpty(validates)) { - List errorMessage = validates.stream() - .map(ConstraintViolation::getMessage).toList(); - throw new RuntimeException(String.join(",", errorMessage)); - } - + validatorCodeService.validateThrow(graph); Graph model = graph.getGraph(); // 节点参数校验 validateNode(model.getNodes()); @@ -78,7 +67,7 @@ public class ValidatorServiceImpl implements ValidatorService { /** * 验证边缘 * - * @param edges + * @param edges 线 */ private void validateEdge(List edges) { List errorMessage = new ArrayList<>(); @@ -105,7 +94,81 @@ public class ValidatorServiceImpl implements ValidatorService { * @param edges 边缘 */ private void validateRelation(List nodes, List edges) { + // 1. 检查线是否连接有效节点 + validateEdgeConnections(nodes, edges); + // 2. 检查是否存在环结构 + validateCycle(nodes, edges); + + // 3. 检查是否存在孤立节点 + validateIsolatedNodes(nodes, edges); + } + + /** + * 检查线是否连接有效节点 + */ + private void validateEdgeConnections(List nodes, List edges) { + Map nodeMap = nodes.stream().collect(Collectors.toMap(Node::getId, Function.identity())); + for (Edge edge : edges) { + String source = edge.getSource(); + String target = edge.getTarget(); + Assert.isTrue(nodeMap.containsKey(source), "边 {} 的源节点 {} 不存在", edge.getLabel(), source); + Assert.isTrue(nodeMap.containsKey(target), "边 {} 的目标节点 {} 不存在", edge.getLabel(), target); + } + } + + /** + * 检查是否存在环结构 + */ + private void validateCycle(List nodes, List edges) { + Map> adjacencyList = buildAdjacencyList(edges); + for (Node node : nodes) { + if (hasCycle(node.getId(), adjacencyList, new HashSet<>())) { + throw new IllegalArgumentException("图中存在环结构,起始节点: " + node.getData().getLabel()); + } + } + } + + /** + * 检查是否存在孤立节点 + */ + private void validateIsolatedNodes(List nodes, List edges) { + Set connectedNodes = new HashSet<>(); + for (Edge edge : edges) { + connectedNodes.add(edge.getSource()); + connectedNodes.add(edge.getTarget()); + } + for (Node node : nodes) { + Assert.isTrue(connectedNodes.contains(node.getId()), "节点 {} 是孤立节点,未与任何边连接", node.getId()); + } + } + + /** + * 构建邻接表 + */ + private Map> buildAdjacencyList(List edges) { + Map> adjacencyList = new HashMap<>(); + for (Edge edge : edges) { + adjacencyList.computeIfAbsent(edge.getSource(), k -> new ArrayList<>()).add(edge.getTarget()); + } + return adjacencyList; + } + + /** + * 深度优先搜索(DFS)检查环 + */ + private boolean hasCycle(String nodeId, Map> adjacencyList, Set visited) { + if (visited.contains(nodeId)) { + return true; // 发现环 + } + visited.add(nodeId); + for (String neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) { + if (hasCycle(neighbor, adjacencyList, visited)) { + return true; + } + } + visited.remove(nodeId); + return false; } } diff --git a/src/main/java/com/metis/flow/validator/impl/node/DocumentExtractorNodeValidator.java b/src/main/java/com/metis/flow/validator/impl/node/DocumentExtractorNodeValidator.java new file mode 100644 index 0000000..042d42f --- /dev/null +++ b/src/main/java/com/metis/flow/validator/impl/node/DocumentExtractorNodeValidator.java @@ -0,0 +1,31 @@ +package com.metis.flow.validator.impl.node; + +import com.metis.flow.domain.entity.base.Node; +import com.metis.flow.domain.entity.config.node.DocumentExtractorNodeConfig; +import com.metis.flow.enums.NodeType; +import com.metis.flow.validator.NodeValidator; +import com.metis.flow.validator.ValidatorCodeService; +import com.metis.flow.validator.ValidatorResult; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DocumentExtractorNodeValidator implements NodeValidator { + private final ValidatorCodeService validatorCodeService; + + @Override + public ValidatorResult validate(Node node) { + DocumentExtractorNodeConfig config = node.getConfig(); + validatorCodeService.validateThrow(config); + // 业务检查未通过 + return ValidatorResult.invalid("业务报错"); + } + + @Override + public NodeType getType() { + return NodeType.DOCUMENT_EXTRACTOR; + } +} diff --git a/src/main/java/com/metis/flow/validator/impl/node/EndNodeValidator.java b/src/main/java/com/metis/flow/validator/impl/node/EndNodeValidator.java index caebef9..5c8eba9 100644 --- a/src/main/java/com/metis/flow/validator/impl/node/EndNodeValidator.java +++ b/src/main/java/com/metis/flow/validator/impl/node/EndNodeValidator.java @@ -4,13 +4,17 @@ import com.metis.flow.domain.entity.base.Node; import com.metis.flow.enums.NodeType; import com.metis.flow.validator.NodeValidator; import com.metis.flow.validator.ValidatorResult; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Slf4j @Service +@RequiredArgsConstructor public class EndNodeValidator implements NodeValidator { + + @Override public ValidatorResult validate(Node node) { return ValidatorResult.valid();