feat(ai-knowledge-base): 实现AI知识库文档上传与管理功能
- 新增AiDocument实体类,映射数据库ai_document表结构 - 添加AiDocumentMapper接口,提供文档增删改查及状态更新等数据库操作 - 实现AiKnowledgeBaseService接口及其实现类AiKnowledgeBaseServiceImpl,支持文件上传、文档列表查询、删除和重新索引 - 在AiKnowledgeBaseController中提供REST接口支持文件上传、文档管理和异步重新索引操作 - 实现DocumentProcessor组件,负责文档解析、文本切片、摘要生成和向量化存储 - 集成MinioService实现文件的上传、下载和删除操作 - 设计KbDocumentVO作为知识库文档视图对象,方便接口数据传输和展示 - 增加文件类型支持和上传文件校验,限制最大50MB文件大小 - 使用异步机制处理文档解析和向量化,提高系统处理性能和响应速度 - 实现文档状态管理和错误处理,确保文档处理流程的正确性和稳定性
This commit is contained in:
@@ -13,7 +13,6 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI知识库控制器
|
* AI知识库控制器
|
||||||
@@ -98,7 +97,7 @@ public class AiKnowledgeBaseController {
|
|||||||
*/
|
*/
|
||||||
@DeleteMapping("/document/{docId}")
|
@DeleteMapping("/document/{docId}")
|
||||||
@Operation(summary = "删除文档", description = "删除指定的知识库文档")
|
@Operation(summary = "删除文档", description = "删除指定的知识库文档")
|
||||||
public BaseResponse<Void> deleteDocument(@PathVariable UUID docId) {
|
public BaseResponse<Void> deleteDocument(@PathVariable String docId) {
|
||||||
Long userId = SecurityUtils.getCurrentUserId();
|
Long userId = SecurityUtils.getCurrentUserId();
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
return ResultUtils.error("用户未登录");
|
return ResultUtils.error("用户未登录");
|
||||||
@@ -121,7 +120,7 @@ public class AiKnowledgeBaseController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/document/{docId}/reindex")
|
@PostMapping("/document/{docId}/reindex")
|
||||||
@Operation(summary = "重新索引文档", description = "重新解析并索引指定的文档")
|
@Operation(summary = "重新索引文档", description = "重新解析并索引指定的文档")
|
||||||
public BaseResponse<Void> reindexDocument(@PathVariable UUID docId) {
|
public BaseResponse<Void> reindexDocument(@PathVariable String docId) {
|
||||||
Long userId = SecurityUtils.getCurrentUserId();
|
Long userId = SecurityUtils.getCurrentUserId();
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
return ResultUtils.error("用户未登录");
|
return ResultUtils.error("用户未登录");
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import lombok.Data;
|
|||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI文档向量实体
|
* AI文档向量实体
|
||||||
@@ -23,7 +22,7 @@ public class AiDocument {
|
|||||||
/**
|
/**
|
||||||
* 文档唯一标识(UUID)
|
* 文档唯一标识(UUID)
|
||||||
*/
|
*/
|
||||||
private UUID docId;
|
private String docId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关联项目ID
|
* 关联项目ID
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package cn.yinlihupo.domain.vo;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识库文档VO
|
* 知识库文档VO
|
||||||
@@ -19,7 +18,7 @@ public class KbDocumentVO {
|
|||||||
/**
|
/**
|
||||||
* 文档UUID
|
* 文档UUID
|
||||||
*/
|
*/
|
||||||
private UUID docId;
|
private String docId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文档标题
|
* 文档标题
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import org.apache.ibatis.annotations.Mapper;
|
|||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI文档向量Mapper
|
* AI文档向量Mapper
|
||||||
@@ -30,7 +29,7 @@ public interface AiDocumentMapper extends BaseMapper<AiDocument> {
|
|||||||
* @param docId 文档UUID
|
* @param docId 文档UUID
|
||||||
* @return 文档实体
|
* @return 文档实体
|
||||||
*/
|
*/
|
||||||
AiDocument selectByDocId(@Param("docId") UUID docId);
|
AiDocument selectByDocId(@Param("docId") String docId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据docId删除文档
|
* 根据docId删除文档
|
||||||
@@ -38,7 +37,7 @@ public interface AiDocumentMapper extends BaseMapper<AiDocument> {
|
|||||||
* @param docId 文档UUID
|
* @param docId 文档UUID
|
||||||
* @return 影响行数
|
* @return 影响行数
|
||||||
*/
|
*/
|
||||||
int deleteByDocId(@Param("docId") UUID docId);
|
int deleteByDocId(@Param("docId") String docId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量查询引用文档信息
|
* 批量查询引用文档信息
|
||||||
@@ -63,7 +62,7 @@ public interface AiDocumentMapper extends BaseMapper<AiDocument> {
|
|||||||
* @param status 状态
|
* @param status 状态
|
||||||
* @return 影响行数
|
* @return 影响行数
|
||||||
*/
|
*/
|
||||||
int updateStatus(@Param("docId") UUID docId, @Param("status") String status);
|
int updateStatus(@Param("docId") String docId, @Param("status") String status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新文档错误信息
|
* 更新文档错误信息
|
||||||
@@ -72,7 +71,7 @@ public interface AiDocumentMapper extends BaseMapper<AiDocument> {
|
|||||||
* @param errorMessage 错误信息
|
* @param errorMessage 错误信息
|
||||||
* @return 影响行数
|
* @return 影响行数
|
||||||
*/
|
*/
|
||||||
int updateErrorMessage(@Param("docId") UUID docId, @Param("errorMessage") String errorMessage);
|
int updateErrorMessage(@Param("docId") String docId, @Param("errorMessage") String errorMessage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 增加文档查看次数
|
* 增加文档查看次数
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import cn.yinlihupo.domain.vo.KbDocumentVO;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI知识库服务接口
|
* AI知识库服务接口
|
||||||
@@ -35,7 +34,7 @@ public interface AiKnowledgeBaseService {
|
|||||||
* @param docId 文档UUID
|
* @param docId 文档UUID
|
||||||
* @param userId 用户ID
|
* @param userId 用户ID
|
||||||
*/
|
*/
|
||||||
void deleteDocument(UUID docId, Long userId);
|
void deleteDocument(String docId, Long userId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新索引文档
|
* 重新索引文档
|
||||||
@@ -43,7 +42,7 @@ public interface AiKnowledgeBaseService {
|
|||||||
* @param docId 文档UUID
|
* @param docId 文档UUID
|
||||||
* @param userId 用户ID
|
* @param userId 用户ID
|
||||||
*/
|
*/
|
||||||
void reindexDocument(UUID docId, Long userId);
|
void reindexDocument(String docId, Long userId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理文档(解析、切片、向量化)
|
* 处理文档(解析、切片、向量化)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class AiKnowledgeBaseServiceImpl implements AiKnowledgeBaseService {
|
|||||||
validateFile(file);
|
validateFile(file);
|
||||||
|
|
||||||
// 2. 生成文档UUID
|
// 2. 生成文档UUID
|
||||||
UUID docId = UUID.randomUUID();
|
String docId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
// 3. 上传文件到MinIO
|
// 3. 上传文件到MinIO
|
||||||
String originalFilename = file.getOriginalFilename();
|
String originalFilename = file.getOriginalFilename();
|
||||||
@@ -63,6 +63,7 @@ public class AiKnowledgeBaseServiceImpl implements AiKnowledgeBaseService {
|
|||||||
doc.setFileType(fileExtension);
|
doc.setFileType(fileExtension);
|
||||||
doc.setFileSize(file.getSize());
|
doc.setFileSize(file.getSize());
|
||||||
doc.setFilePath(filePath);
|
doc.setFilePath(filePath);
|
||||||
|
doc.setContent("");
|
||||||
doc.setStatus("pending"); // 待处理状态
|
doc.setStatus("pending"); // 待处理状态
|
||||||
doc.setChunkTotal(0);
|
doc.setChunkTotal(0);
|
||||||
doc.setCreateBy(userId);
|
doc.setCreateBy(userId);
|
||||||
@@ -86,7 +87,7 @@ public class AiKnowledgeBaseServiceImpl implements AiKnowledgeBaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteDocument(UUID docId, Long userId) {
|
public void deleteDocument(String docId, Long userId) {
|
||||||
// 1. 查询文档
|
// 1. 查询文档
|
||||||
AiDocument doc = documentMapper.selectByDocId(docId);
|
AiDocument doc = documentMapper.selectByDocId(docId);
|
||||||
if (doc == null) {
|
if (doc == null) {
|
||||||
@@ -111,7 +112,7 @@ public class AiKnowledgeBaseServiceImpl implements AiKnowledgeBaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reindexDocument(UUID docId, Long userId) {
|
public void reindexDocument(String docId, Long userId) {
|
||||||
// 1. 查询文档
|
// 1. 查询文档
|
||||||
AiDocument doc = documentMapper.selectByDocId(docId);
|
AiDocument doc = documentMapper.selectByDocId(docId);
|
||||||
if (doc == null) {
|
if (doc == null) {
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import org.springframework.scheduling.annotation.Async;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,7 +169,7 @@ public class DocumentProcessor {
|
|||||||
* @param chunks 切片列表
|
* @param chunks 切片列表
|
||||||
*/
|
*/
|
||||||
private void storeChunks(AiDocument parentDoc, List<String> chunks) {
|
private void storeChunks(AiDocument parentDoc, List<String> chunks) {
|
||||||
UUID docId = parentDoc.getDocId();
|
String docId = parentDoc.getDocId();
|
||||||
Long parentId = parentDoc.getId();
|
Long parentId = parentDoc.getId();
|
||||||
|
|
||||||
for (int i = 0; i < chunks.size(); i++) {
|
for (int i = 0; i < chunks.size(); i++) {
|
||||||
@@ -206,7 +208,7 @@ public class DocumentProcessor {
|
|||||||
*
|
*
|
||||||
* @param docId 文档UUID
|
* @param docId 文档UUID
|
||||||
*/
|
*/
|
||||||
public void deleteDocumentVectors(UUID docId) {
|
public void deleteDocumentVectors(String docId) {
|
||||||
try {
|
try {
|
||||||
// 查询所有相关切片
|
// 查询所有相关切片
|
||||||
// 注意:pgvector store的删除需要根据metadata过滤
|
// 注意:pgvector store的删除需要根据metadata过滤
|
||||||
|
|||||||
Reference in New Issue
Block a user