feat: 文档解析器demo

This commit is contained in:
2025-04-07 22:02:14 +08:00
parent 7cf545bf44
commit f82aa0e94d
9 changed files with 209 additions and 21 deletions

View File

@@ -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<String> test() {
private final ValidatorService validatorService;
@PostMapping
public Result<String> test(@RequestBody BuildApp app) {
validatorService.validate(app);
return Result.ok("测试成功");
}

View File

@@ -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;
/**

View File

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

View File

@@ -0,0 +1 @@
package com.metis.flow.domain.entity.config;

View File

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

View File

@@ -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 <T> void validateThrow(T validObject) {
// validation 编程式校验
Set<ConstraintViolation<T>> validates = globalValidator.validate(validObject);
if (CollUtil.isNotEmpty(validates)) {
List<String> errorMessage = validates.stream()
.map(ConstraintViolation::getMessage).toList();
throw new RuntimeException(String.join(",", errorMessage));
}
}
/**
* 验证错误信息
*
* @param validObject 有效对象
* @return {@link String }
*/
public <T> String validateErrorMsg(T validObject) {
// validation 编程式校验
Set<ConstraintViolation<T>> validates = globalValidator.validate(validObject);
if (CollUtil.isNotEmpty(validates)) {
List<String> errorMessage = validates.stream()
.map(ConstraintViolation::getMessage).toList();
return String.join(",", errorMessage);
}
return null;
}
/**
* 验证
*
* @param validObject 有效对象
* @return boolean
*/
public <T> boolean validate(T validObject) {
// validation 编程式校验
Set<ConstraintViolation<T>> validates = globalValidator.validate(validObject);
return CollUtil.isEmpty(validates);
}
}

View File

@@ -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<ConstraintViolation<BuildApp>> validates = globalValidator.validate(graph);
if (CollUtil.isNotEmpty(validates)) {
List<String> 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<Edge> edges) {
List<String> errorMessage = new ArrayList<>();
@@ -105,7 +94,81 @@ public class ValidatorServiceImpl implements ValidatorService {
* @param edges 边缘
*/
private void validateRelation(List<Node> nodes, List<Edge> edges) {
// 1. 检查线是否连接有效节点
validateEdgeConnections(nodes, edges);
// 2. 检查是否存在环结构
validateCycle(nodes, edges);
// 3. 检查是否存在孤立节点
validateIsolatedNodes(nodes, edges);
}
/**
* 检查线是否连接有效节点
*/
private void validateEdgeConnections(List<Node> nodes, List<Edge> edges) {
Map<String, Node> 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<Node> nodes, List<Edge> edges) {
Map<String, List<String>> adjacencyList = buildAdjacencyList(edges);
for (Node node : nodes) {
if (hasCycle(node.getId(), adjacencyList, new HashSet<>())) {
throw new IllegalArgumentException("图中存在环结构,起始节点: " + node.getData().getLabel());
}
}
}
/**
* 检查是否存在孤立节点
*/
private void validateIsolatedNodes(List<Node> nodes, List<Edge> edges) {
Set<String> 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<String, List<String>> buildAdjacencyList(List<Edge> edges) {
Map<String, List<String>> 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<String, List<String>> adjacencyList, Set<String> 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;
}
}

View File

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

View File

@@ -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();