文档管理
在本节中,我们将实现 文档管理 功能。此前,我们已完成数据库设计、用户登录、知识库管理、文件拆分、片段向量化功能以及命中率测试。文档管理功能将进一步完善我们的系统,使用户能够高效地管理和检索文档。
功能概述
文档管理 功能包括以下主要操作:
- 获取文档列表:根据数据集 ID 获取分页文档列表。
- 获取单个文档详情:根据数据集 ID 和文档 ID 获取特定文档的详细信息。
- 获取所有文档:根据数据集 ID 获取该数据集下的所有文档。
接口实现
我们提供了三个主要的 API 接口来支持文档管理功能:
- 获取文档列表
- 获取单个文档详情
- 获取所有文档
1. 获取文档列表
请求
GET http://localhost:3000/api/dataset/{datasetId}/document/{pageNo}/{pageSize}
- 路径参数:
datasetId
:数据集的唯一标识。pageNo
:当前页码。pageSize
:每页显示的文档数量。
示例请求
GET http://localhost:3000/api/dataset/443309276048408576/document/1/10
响应
{
"message": null,
"data": {
"size": 10,
"total": 1,
"current": 1,
"records": [
{
"tenant_id": "0",
"directly_return_similarity": 0.9,
"creator": "",
"is_active": true,
"create_time": 1730853299657,
"dataset_id": "443309276048408576",
"title": null,
"type": "pdf",
"updater": "",
"update_time": 1730853299657,
"deleted": 0,
"user_id": "1",
"char_length": 22837,
"meta": null,
"file_id": "443431990348681216",
"name": "ICS111_31391_Miller_Syllabus_F24.pdf",
"paragraph_count": 6,
"hit_handling_method": "optimization",
"files": null,
"id": "443662133182980096",
"status": "1"
}
]
},
"code": 200
}
响应字段说明
message
:响应消息,成功时为null
。data
:响应数据对象。size
:每页文档数量。total
:总文档数。current
:当前页码。records
:文档列表数组,每个文档包含以下字段:tenant_id
:租户 ID。directly_return_similarity
:直接返回的相似度评分。creator
:文档创建者。is_active
:文档是否激活。create_time
:文档创建时间(时间戳)。dataset_id
:所属数据集 ID。title
:文档标题。type
:文档类型(如 PDF)。updater
:文档更新者。update_time
:文档更新时间(时间戳)。deleted
:删除标识(0 表示未删除)。user_id
:用户 ID。char_length
:文档字符长度。meta
:元数据。file_id
:文件 ID。name
:文档名称。paragraph_count
:段落数量。hit_handling_method
:命中处理方法。files
:关联文件信息。id
:文档唯一标识。status
:文档状态。
code
:响应状态码,200
表示成功。
2. 获取单个文档详情
请求
GET http://localhost:3000/api/dataset/{datasetId}/document/{documentId}
- 路径参数:
datasetId
:数据集的唯一标识。documentId
:文档的唯一标识。
示例请求
GET http://localhost:3000/api/dataset/443309276048408576/document/1
响应
{
"message": null,
"data": {
"tenant_id": "0",
"creator": "",
"create_time": 1730769172064,
"embedding_mode_id": "443263808507674624",
"remark": null,
"type": "0",
"updater": "",
"update_time": 1730769172064,
"deleted": 0,
"user_id": "1",
"meta": null,
"name": "ICS 141",
"id": "443309276048408576",
"desc": "ICS 141 课程资料"
},
"code": 200
}
响应字段说明
message
:响应消息,成功时为null
。data
:文档详细信息对象,包含以下字段:tenant_id
:租户 ID。creator
:文档创建者。create_time
:文档创建时间(时间戳)。embedding_mode_id
:嵌入模式 ID。remark
:备注信息。type
:文档类型。updater
:文档更新者。update_time
:文档更新时间(时间戳)。deleted
:删除标识(0 表示未删除)。user_id
:用户 ID。meta
:元数据。name
:文档名称。id
:文档唯一标识。desc
:文档描述。
code
:响应状态码,200
表示成功。
3. 获取所有文档
请求
GET http://localhost:3000/api/dataset/{datasetId}/document
- 路径参数:
datasetId
:数据集的唯一标识。
示例请求
GET http://localhost:3000/api/dataset/443309276048408576/document
响应
{
"message": null,
"data": [
{
"tenant_id": "0",
"directly_return_similarity": 0.9,
"creator": "",
"is_active": true,
"create_time": 1730853299657,
"dataset_id": "443309276048408576",
"title": null,
"type": "pdf",
"updater": "",
"update_time": 1730853299657,
"deleted": 0,
"user_id": "1",
"char_length": 22837,
"meta": null,
"file_id": "443431990348681216",
"name": "ICS111_31391_Miller_Syllabus_F24.pdf",
"paragraph_count": 6,
"hit_handling_method": "optimization",
"files": null,
"id": "443662133182980096",
"status": "1"
}
],
"code": 200
}
响应字段说明
message
:响应消息,成功时为null
。data
:文档列表数组,每个文档包含以下字段:tenant_id
:租户 ID。directly_return_similarity
:直接返回的相似度评分。creator
:文档创建者。is_active
:文档是否激活。create_time
:文档创建时间(时间戳)。dataset_id
:所属数据集 ID。title
:文档标题。type
:文档类型(如 PDF)。updater
:文档更新者。update_time
:文档更新时间(时间戳)。deleted
:删除标识(0 表示未删除)。user_id
:用户 ID。char_length
:文档字符长度。meta
:元数据。file_id
:文件 ID。name
:文档名称。paragraph_count
:段落数量。hit_handling_method
:命中处理方法。files
:关联文件信息。id
:文档唯一标识。status
:文档状态。
code
:响应状态码,200
表示成功。
代码实现
以下是文档管理功能的核心代码实现,包括 API 控制器和服务层。
1. API 控制器
文件:ApiDatasetController.java
import java.util.List;
import com.litongjava.annotation.Get;
import com.litongjava.annotation.RequestPath;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.maxkb.service.MaxKbDocumentService;
import com.litongjava.model.result.ResultVo;
import com.litongjava.tio.boot.http.TioRequestContext;
@RequestPath("/api/dataset")
public class ApiDatasetController {
/**
* 获取指定数据集下的文档列表(分页)
*
* @param datasetId 数据集 ID
* @param pageNo 当前页码
* @param pageSize 每页文档数量
* @return ResultVo 包含分页文档列表
*/
@Get("/{datasetId}/document/{pageNo}/{pageSize}")
public ResultVo pageDocument(Long datasetId, Integer pageNo, Integer pageSize) {
// 获取当前用户 ID
Long userId = TioRequestContext.getUserIdLong();
// 调用文档服务获取分页文档列表
return Aop.get(MaxKbDocumentService.class).page(userId, datasetId, pageNo, pageSize);
}
/**
* 获取指定数据集下的单个文档详情
*
* @param datasetId 数据集 ID
* @param documentId 文档 ID
* @return ResultVo 包含文档详细信息
*/
@Get("/{datasetId}/document/{documentId}")
public ResultVo getDocument(Long datasetId, Long documentId) {
// 获取当前用户 ID
Long userId = TioRequestContext.getUserIdLong();
// 调用文档服务获取文档详情
return Aop.get(MaxKbDocumentService.class).get(userId, datasetId, documentId);
}
/**
* 获取指定数据集下的所有文档
*
* @param datasetId 数据集 ID
* @return ResultVo 包含文档列表
*/
@Get("/{datasetId}/document")
public ResultVo documentList(Long datasetId) {
Long userId = TioRequestContext.getUserIdLong();
return Aop.get(MaxKbDocumentService.class).list(userId, datasetId);
}
}
代码说明
- 注解
@RequestPath("/api/dataset")
:定义控制器的基础路径为/api/dataset
。 - 方法
pageDocument
:- 注解
@Get("/{datasetId}/document/{pageNo}/{pageSize}")
:定义 GET 请求路径。 - 参数:
datasetId
:数据集 ID。pageNo
:当前页码。pageSize
:每页文档数量。
- 功能:获取指定数据集下的分页文档列表。
- 注解
- 方法
getDocument
:- 注解
@Get("/{datasetId}/document/{documentId}")
:定义 GET 请求路径。 - 参数:
datasetId
:数据集 ID。documentId
:文档 ID。
- 功能:获取指定数据集下的单个文档详情。
- 注解
- 方法
documentList
:- 注解
@Get("/{datasetId}/document")
:定义 GET 请求路径。 - 参数:
datasetId
:数据集 ID。
- 功能:获取指定数据集下的所有文档列表。
- 注解
- 依赖注入:通过
Aop.get(MaxKbDocumentService.class)
获取MaxKbDocumentService
服务实例。
2. 服务层
文件:MaxKbDocumentService.java
package com.litongjava.maxkb.service;
import java.util.List;
import com.jfinal.kit.Kv;
import com.litongjava.db.TableInput;
import com.litongjava.db.TableResult;
import com.litongjava.db.activerecord.Row;
import com.litongjava.kit.RecordUtils;
import com.litongjava.maxkb.constant.TableNames;
import com.litongjava.maxkb.model.ResultPage;
import com.litongjava.model.page.Page;
import com.litongjava.model.result.ResultVo;
import com.litongjava.table.services.ApiTable;
public class MaxKbDocumentService {
/**
* 分页获取文档列表
*
* @param userId 当前用户 ID
* @param datasetId 数据集 ID
* @param pageNo 当前页码
* @param pageSize 每页文档数量
* @return ResultVo 包含分页文档列表
*/
public ResultVo page(Long userId, Long datasetId, Integer pageNo, Integer pageSize) {
TableInput tableInput = new TableInput();
if (userId != 1) { // 假设 userId 为 1 的用户为管理员
tableInput.set("user_id", userId);
}
tableInput.set("dataset_id", datasetId)
.setPageNo(pageNo)
.setPageSize(pageSize);
TableResult<Page<Row>> tableResult = ApiTable.page(TableNames.max_kb_document, tableInput);
Page<Row> page = tableResult.getData();
int totalRow = page.getTotalRow();
List<Row> list = page.getList();
List<Kv> kvs = RecordUtils.recordsToKv(list, false);
ResultPage<Kv> resultPage = new ResultPage<>(pageNo, pageSize, totalRow, kvs);
return ResultVo.ok(resultPage);
}
/**
* 获取所有文档列表
*
* @param userId 当前用户 ID
* @param datasetId 数据集 ID
* @return ResultVo 包含文档列表
*/
public ResultVo list(Long userId, Long datasetId) {
TableInput tableInput = new TableInput();
if (userId != 1) { // 假设 userId 为 1 的用户为管理员
tableInput.set("user_id", userId);
}
tableInput.set("dataset_id", datasetId);
TableResult<List<Row>> tableResult = ApiTable.list(TableNames.max_kb_document, tableInput);
List<Row> records = tableResult.getData();
List<Kv> kvs = RecordUtils.recordsToKv(records, false);
return ResultVo.ok(kvs);
}
/**
* 获取单个文档详情
*
* @param userId 当前用户 ID
* @param datasetId 数据集 ID
* @param documentId 文档 ID
* @return ResultVo 包含文档详细信息
*/
public ResultVo get(Long userId, Long datasetId, Long documentId) {
TableInput tableInput = TableInput.by("id", documentId);
if (userId != 1) { // 非管理员用户需额外过滤
tableInput.set("user_id", userId);
}
tableInput.set("dataset_id", datasetId);
Row data = ApiTable.get(TableNames.max_kb_document, tableInput).getData();
if (data == null) {
return ResultVo.error("文档未找到");
}
return ResultVo.ok(data.toKv());
}
}
代码说明
- 方法
page
:- 参数:
userId
:当前用户的 ID,用于权限控制。datasetId
:数据集 ID,用于过滤文档。pageNo
:当前页码。pageSize
:每页文档数量。
- 功能:
- 创建
TableInput
对象,用于构建查询参数。 - 根据用户 ID 进行权限控制,如果用户不是管理员(假设
userId != 1
),则仅查询该用户创建的文档。 - 设置数据集 ID、页码和页大小。
- 调用
ApiTable.page
方法执行分页查询,获取TableResult
。 - 从
TableResult
中提取Page<Row>
对象,获取总行数和记录列表。 - 将记录列表转换为键值对列表 (
List<Kv>
)。 - 构建
ResultPage<Kv>
对象,包含分页信息和文档列表。 - 返回成功的
ResultVo
对象,包含ResultPage
数据。
- 创建
- 参数:
- 方法
list
:- 参数:
userId
:当前用户的 ID,用于权限控制。datasetId
:数据集 ID,用于过滤文档。
- 功能:
- 创建
TableInput
对象,用于构建查询参数。 - 根据用户 ID 进行权限控制,如果用户不是管理员,则仅查询该用户创建的文档。
- 设置数据集 ID。
- 调用
ApiTable.list
方法获取文档列表。 - 将记录列表转换为键值对列表 (
List<Kv>
)。 - 返回成功的
ResultVo
对象,包含文档列表数据。
- 创建
- 参数:
- 方法
get
:- 参数:
userId
:当前用户的 ID,用于权限控制。datasetId
:数据集 ID,用于过滤文档。documentId
:文档 ID,用于查询特定文档。
- 功能:
- 创建
TableInput
对象,并设置文档 ID 作为查询条件。 - 根据用户 ID 进行权限控制:
- 如果用户是管理员 (
userId == 1
),则仅根据数据集 ID 过滤。 - 否则,增加用户 ID 过滤,确保用户只能访问自己的文档。
- 如果用户是管理员 (
- 调用
ApiTable.get
方法获取单条记录 (Row
)。 - 检查记录是否存在,若不存在则返回错误信息。
- 将
Row
转换为键值对 (Kv
)。 - 返回成功的
ResultVo
对象,包含文档详细信息。
- 创建
- 参数:
- 依赖组件:
ApiTable
:用于执行数据库表的分页查询和单条记录查询。RecordUtils
:用于将数据库记录转换为键值对格式。ResultVo
和ResultPage
:用于标准化 API 响应结构。
权限控制
在上述实现中,我们假设 userId
为 1
的用户为管理员,具有查看所有数据集和文档的权限。非管理员用户只能查看自己创建的数据集和文档。这种权限控制确保了数据的安全性和隐私性。
异常处理
当前实现未详细展示异常处理逻辑。建议在实际开发中加入以下内容:
- 参数校验:确保请求参数的有效性,如
datasetId
和documentId
的存在性、pageNo
和pageSize
的合理性。 - 权限验证:在服务层进一步验证用户是否有权访问指定的数据集和文档。
- 错误响应:在发生异常时,返回适当的错误消息和状态码,如
400 Bad Request
、401 Unauthorized
、404 Not Found
等。
测试
为了确保文档管理功能的正确性和稳定性,建议编写以下测试用例:
- 获取文档列表:
- 查询存在的数据集,验证返回的文档列表是否正确。
- 查询不存在的数据集,验证返回的错误信息。
- 测试不同的
pageNo
和pageSize
参数,确保分页逻辑正确。
- 获取单个文档详情:
- 查询存在的文档,验证返回的文档详细信息是否正确。
- 查询不存在的文档,验证返回的错误信息。
- 验证权限控制,确保用户无法访问未授权的文档。
- 获取所有文档:
- 查询存在的数据集,验证返回的所有文档列表是否正确。
- 查询不存在的数据集,验证返回的错误信息。
- 验证权限控制,确保用户只能获取自己有权限查看的文档。
总结
通过上述接口和代码实现,我们成功地为系统添加了文档管理功能。该功能允许用户根据数据集管理和检索文档,支持分页查询、详细信息查看以及获取所有文档。未来,可以进一步扩展此功能,如添加文档上传、编辑、删除等操作,以满足更全面的文档管理需求。