Преглед на файлове

feat(NER): 添加重新生成并保存文档块的接口

新增 POST /api/v1/ner/documents/{documentId}/regenerate-blocks 接口
用于对已解析但没有 blocks 的历史文档重新生成并保存块数据
何文松 преди 1 месец
родител
ревизия
65b37b32da
променени са 1 файла, в които са добавени 147 реда и са изтрити 5 реда
  1. 147 5
      backend/graph-service/src/main/java/com/lingyue/graph/controller/NerBlockController.java

+ 147 - 5
backend/graph-service/src/main/java/com/lingyue/graph/controller/NerBlockController.java

@@ -1,9 +1,11 @@
 package com.lingyue.graph.controller;
 
 import com.lingyue.common.domain.AjaxResult;
+import com.lingyue.graph.entity.GraphNode;
 import com.lingyue.graph.service.DocumentBlockGeneratorService;
 import com.lingyue.graph.service.DocumentBlockGeneratorService.BlockDTO;
 import com.lingyue.graph.service.GraphNerService;
+import com.lingyue.graph.service.GraphNodeService;
 import com.lingyue.graph.service.NerToBlockService;
 import com.lingyue.graph.service.NerToBlockService.TextElementDTO;
 import io.swagger.v3.oas.annotations.Operation;
@@ -11,10 +13,12 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.Data;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.client.RestTemplate;
 
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * NER 到 Block 转换控制器
@@ -32,8 +36,13 @@ import java.util.Map;
 public class NerBlockController {
     
     private final GraphNerService graphNerService;
+    private final GraphNodeService graphNodeService;
     private final NerToBlockService nerToBlockService;
     private final DocumentBlockGeneratorService blockGeneratorService;
+    private final RestTemplate restTemplate;
+    
+    @Value("${server.port:5232}")
+    private int serverPort;
     
     /**
      * 将文本和实体转换为 TextElement 列表
@@ -79,10 +88,10 @@ public class NerBlockController {
             // 获取文档文本
             String text = graphNerService.getDocumentText(documentId);
             
-            // 如果没有提供实体,尝试从图数据库获取
+            // 如果没有提供实体,从图数据库获取
             if (entities == null || entities.isEmpty()) {
-                // TODO: 从 GraphNode 表获取实体并转换
-                log.info("未提供实体,生成纯文本块: documentId={}", documentId);
+                entities = getEntitiesFromGraphNodes(documentId);
+                log.info("从图数据库获取实体: documentId={}, count={}", documentId, entities.size());
             }
             
             List<BlockDTO> blocks = blockGeneratorService.generateBlocks(documentId, text, entities);
@@ -93,6 +102,139 @@ public class NerBlockController {
         }
     }
     
+    /**
+     * 重新生成并保存文档块(用于已解析但没有 blocks 的文档)
+     */
+    @PostMapping("/documents/{documentId}/regenerate-blocks")
+    @Operation(summary = "重新生成并保存块", description = "从已存储的文档和NER结果重新生成并保存Block到数据库")
+    public AjaxResult<?> regenerateAndSaveBlocks(@PathVariable String documentId) {
+        try {
+            // 1. 获取文档文本
+            String text = graphNerService.getDocumentText(documentId);
+            if (text == null || text.isEmpty()) {
+                return AjaxResult.error("文档文本不存在: " + documentId);
+            }
+            
+            // 2. 从图数据库获取实体
+            List<Map<String, Object>> entities = getEntitiesFromGraphNodes(documentId);
+            log.info("重新生成块: documentId={}, textLength={}, entityCount={}", 
+                    documentId, text.length(), entities.size());
+            
+            // 3. 生成块
+            List<BlockDTO> blocks = blockGeneratorService.generateBlocks(documentId, text, entities);
+            if (blocks.isEmpty()) {
+                return AjaxResult.error("生成块失败:没有可用的内容");
+            }
+            
+            // 4. 保存到 document-service
+            int savedCount = saveBlocksToDocumentService(documentId, blocks);
+            
+            Map<String, Object> result = new HashMap<>();
+            result.put("blockCount", blocks.size());
+            result.put("savedCount", savedCount);
+            result.put("entityCount", entities.size());
+            
+            return AjaxResult.success("重新生成并保存成功", result);
+            
+        } catch (Exception e) {
+            log.error("重新生成块失败: documentId={}, error={}", documentId, e.getMessage(), e);
+            return AjaxResult.error("重新生成失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 从 GraphNode 表获取实体并转换为 NER 格式
+     */
+    @SuppressWarnings("unchecked")
+    private List<Map<String, Object>> getEntitiesFromGraphNodes(String documentId) {
+        List<GraphNode> nodes = graphNodeService.getNodesByDocumentId(documentId);
+        List<Map<String, Object>> entities = new ArrayList<>();
+        
+        for (GraphNode node : nodes) {
+            Map<String, Object> entity = new HashMap<>();
+            entity.put("name", node.getName());
+            entity.put("type", node.getType());
+            entity.put("tempId", node.getId());
+            
+            // 位置信息(从 GraphNode.position JSONB 字段获取)
+            Object positionObj = node.getPosition();
+            if (positionObj instanceof Map) {
+                Map<String, Object> posMap = (Map<String, Object>) positionObj;
+                Map<String, Object> position = new HashMap<>();
+                position.put("charStart", posMap.get("charStart"));
+                position.put("charEnd", posMap.get("charEnd"));
+                entity.put("position", position);
+            }
+            
+            entities.add(entity);
+        }
+        
+        return entities;
+    }
+    
+    /**
+     * 调用 document-service 保存块
+     */
+    private int saveBlocksToDocumentService(String documentId, List<BlockDTO> blocks) {
+        try {
+            String url = "http://localhost:" + serverPort + "/api/v1/documents/" + documentId + "/blocks/batch";
+            
+            // 转换为 Map 列表
+            List<Map<String, Object>> blockMaps = new ArrayList<>();
+            for (BlockDTO block : blocks) {
+                Map<String, Object> blockMap = new HashMap<>();
+                blockMap.put("blockId", block.getBlockId());
+                blockMap.put("documentId", block.getDocumentId());
+                blockMap.put("parentId", block.getParentId());
+                blockMap.put("children", block.getChildren());
+                blockMap.put("blockIndex", block.getBlockIndex());
+                blockMap.put("blockType", block.getBlockType());
+                blockMap.put("charStart", block.getCharStart());
+                blockMap.put("charEnd", block.getCharEnd());
+                
+                if (block.getElements() != null) {
+                    List<Map<String, Object>> elementMaps = new ArrayList<>();
+                    for (TextElementDTO el : block.getElements()) {
+                        Map<String, Object> elMap = new HashMap<>();
+                        elMap.put("type", el.getType());
+                        elMap.put("content", el.getContent());
+                        elMap.put("entityId", el.getEntityId());
+                        elMap.put("entityText", el.getEntityText());
+                        elMap.put("entityType", el.getEntityType());
+                        elMap.put("confirmed", el.getConfirmed());
+                        elementMaps.add(elMap);
+                    }
+                    blockMap.put("elements", elementMaps);
+                }
+                
+                blockMaps.add(blockMap);
+            }
+            
+            Map<String, Object> request = new HashMap<>();
+            request.put("blocks", blockMaps);
+            
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            
+            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(request, headers);
+            
+            ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.POST, entity, Map.class);
+            
+            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
+                Object data = response.getBody().get("data");
+                if (data instanceof Number) {
+                    return ((Number) data).intValue();
+                }
+            }
+            
+            return blocks.size();
+            
+        } catch (Exception e) {
+            log.error("保存块到 document-service 失败: documentId={}, error={}", documentId, e.getMessage());
+            throw new RuntimeException("保存块失败: " + e.getMessage(), e);
+        }
+    }
+    
     // ==================== 请求 DTO ====================
     
     @Data