liusuyi
2026-05-18 b5cd784d0cde5b7c82ea78aef4ac0e5a161d8c5d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package com.ard.agent.service;
 
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.tika.TikaDocumentReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
 
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
@Slf4j
@Service
public class KnowledgeBaseService {
 
    private final VectorStore vectorStore;
    private final TokenTextSplitter textSplitter;
 
    public KnowledgeBaseService(VectorStore vectorStore) {
        this.vectorStore = vectorStore;
        this.textSplitter = new TokenTextSplitter(800, 150, 5, 10000, true);
    }
 
    public Map<String, Object> uploadDocument(MultipartFile file) {
        Map<String, Object> result = new HashMap<>();
        String fileName = file.getOriginalFilename();
        result.put("fileName", fileName);
 
        Path tempFile = null;
        try {
            log.info("开始处理文档: {}", fileName);
 
            String contentType = file.getContentType();
            if (!isSupportedContentType(contentType)) {
                result.put("success", false);
                result.put("message", "不支持的文件类型: " + contentType);
                return result;
            }
 
            // 创建临时文件
            tempFile = Files.createTempFile("upload_", "_" + fileName);
            file.transferTo(tempFile.toFile());
            log.info("临时文件创建成功: {}", tempFile.toString());
 
            // 关键修改:使用 FileSystemResource 而不是字符串路径
            FileSystemResource resource = new FileSystemResource(tempFile.toFile());
            TikaDocumentReader reader = new TikaDocumentReader(resource);
            List<Document> documents = reader.get();
 
            if (documents == null || documents.isEmpty()) {
                result.put("success", false);
                result.put("message", "文档解析结果为空");
                return result;
            }
 
            List<Document> splitDocuments = textSplitter.apply(documents);
            log.info("文本分割完成,共 {} 个分片", splitDocuments.size());
 
            for (Document doc : splitDocuments) {
                doc.getMetadata().put("fileName", fileName);
                doc.getMetadata().put("contentType", contentType);
                doc.getMetadata().put("uploadTime", LocalDateTime.now().toString());
                doc.getMetadata().put("fileSize", file.getSize());
            }
 
            vectorStore.add(splitDocuments);
 
            log.info("文档入库完成: {},共 {} 个向量分片", fileName, splitDocuments.size());
            result.put("success", true);
            result.put("message", "入库成功");
            result.put("chunksCount", splitDocuments.size());
 
        } catch (Exception e) {
            log.error("文档入库失败: {}", fileName, e);
            result.put("success", false);
            result.put("message", "入库失败:" + e.getMessage());
        } finally {
            if (tempFile != null) {
                try {
                    Files.deleteIfExists(tempFile);
                } catch (IOException e) {
                    log.warn("删除临时文件失败: {}", tempFile, e);
                }
            }
        }
        return result;
    }
 
    private boolean isSupportedContentType(String contentType) {
        if (contentType == null) return false;
        String[] supportedTypes = {
                "text/plain",
                "application/pdf",
                "application/msword",
                "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                "application/vnd.ms-excel",
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                "text/markdown"
        };
        return Arrays.asList(supportedTypes).contains(contentType);
    }
 
 
}