# routers/knowledge.py 接口测试文档 ## 文件功能概述 该文件提供知识库相关的接口,包括: - ChromaDB 向量数据库文档查询(语义检索) - 知识库文件高级搜索(关键词 + 分类 + 日期 + 分页) 路由前缀:`/apiv1`(以 `routers/__init__.py` 中注册为准) > **注意:** 这两个接口均未在代码中显式检查 `request.state.user`,但仍受全局中间件的 Token 校验影响。 --- ## 接口列表 --- ### 1. GET `/apiv1/get_chromadb_document` — 获取 ChromaDB 文档 **功能说明:** 使用语义查询从 ChromaDB 向量数据库中检索相关文档。如果 ChromaDB 服务不可用,会返回基于查询关键词的模拟数据(fallback)。 **是否需要认证:** 是(全局中间件) **请求方式:** GET **请求参数(Query):** | 字段 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | query | string | 是 | — | 查询文本(用于语义检索) | | n | int | 否 | 5 | 返回结果数量(最多返回 n 条) | **业务逻辑:** - 延迟导入 `chromadb_service`(`from services.chromadb_service import chromadb_service`) - 调用 `chromadb_service.query(query, n)` 执行语义检索 - 如果 ChromaDB 服务抛出异常,返回模拟数据(最多 5 条),msg 标记为 `"success (fallback)"` **成功响应字段:** | 字段 | 类型 | 说明 | |------|------|------| | data | array | 文档结果列表,每项含 content、score、metadata | **测试用例:** #### 用例 1:正常查询(ChromaDB 可用) ```json // 请求 GET /apiv1/get_chromadb_document?query=隧道施工安全规范&n=3 token: <有效Token> // 预期响应 (HTTP 200) { "statusCode": 200, "msg": "success", "data": [ { "content": "隧道施工安全规范相关内容...", "score": 0.95, "metadata": {"source": "tunnel_safety.pdf"} }, { "content": "另一段相关内容...", "score": 0.88, "metadata": {"source": "construction_guide.pdf"} }, { "content": "第三段相关内容...", "score": 0.82, "metadata": {"source": "safety_manual.pdf"} } ] } ``` #### 用例 2:使用默认 n 值 ```json // 请求 GET /apiv1/get_chromadb_document?query=桥梁工程 token: <有效Token> // 预期响应 (HTTP 200) // 默认返回最多 5 条结果 { "statusCode": 200, "msg": "success", "data": [ // ... 最多 5 条文档结果 ] } ``` #### 用例 3:ChromaDB 服务不可用(fallback 模拟数据) ```json // 请求(ChromaDB 服务异常时) GET /apiv1/get_chromadb_document?query=路基施工&n=3 token: <有效Token> // 预期响应 (HTTP 200) { "statusCode": 200, "msg": "success (fallback)", "data": [ { "content": "关于路基施工的文档内容1", "score": 0.8, "metadata": {"source": "doc_1.pdf"} }, { "content": "关于路基施工的文档内容2", "score": 0.7, "metadata": {"source": "doc_2.pdf"} }, { "content": "关于路基施工的文档内容3", "score": 0.6, "metadata": {"source": "doc_3.pdf"} } ] } ``` > 注意:fallback 模式下 score 是固定计算的(0.9 - i * 0.1),最多返回 5 条。 #### 用例 4:未提供 query 参数 ```json // 请求 GET /apiv1/get_chromadb_document token: <有效Token> // 预期响应 (HTTP 422) { "detail": [ { "loc": ["query", "query"], "msg": "field required", "type": "value_error.missing" } ] } ``` #### 用例 5:未认证 ```json // 请求 GET /apiv1/get_chromadb_document?query=测试 // 预期响应 (HTTP 401) { "statusCode": 401, "msg": "未提供认证Token" } ``` --- ### 2. GET `/apiv1/knowledge/files/advanced-search` — 知识库高级搜索 **功能说明:** 在数据库 `PolicyFile` 表中按多条件组合搜索知识库文件,支持关键词模糊匹配、分类筛选、日期范围筛选和分页。 **是否需要认证:** 是(全局中间件) **请求方式:** GET **请求参数(Query):** | 字段 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | keyword | string | 否 | None | 关键词(模糊匹配 `policy_name`) | | category | string | 否 | None | 分类名称(支持:"国家规范"、"行业规范"、"地方规范"、"内部条例") | | date_from | string | 否 | None | 起始日期(格式 `YYYY-MM-DD`) | | date_to | string | 否 | None | 截止日期(格式 `YYYY-MM-DD`) | | page | int | 否 | 1 | 页码 | | page_size | int | 否 | 20 | 每页条数 | **业务逻辑:** - 关键词:`PolicyFile.policy_name LIKE '%keyword%'` - 分类映射:`{"国家规范": 1, "行业规范": 2, "地方规范": 3, "内部条例": 4}`,非映射值时忽略该条件 - 日期:将 `YYYY-MM-DD` 格式转为 Unix 时间戳进行范围过滤(`created_at` 字段) - 分页:`offset = (page - 1) * page_size` **成功响应字段:** | 字段 | 类型 | 说明 | |------|------|------| | data.total | int | 匹配结果总数 | | data.page | int | 当前页码 | | data.page_size | int | 每页条数 | | data.items | array | 文件列表 | | data.items[].id | int | 文件 ID | | data.items[].policy_name | string | 文件名 | | data.items[].policy_file_url | string | 文件 URL | | data.items[].policy_type | int | 文件分类(1-4) | | data.items[].view_count | int | 浏览次数 | | data.items[].file_type | string | 文件类型 | | data.items[].created_at | int | 创建时间(Unix 时间戳) | **测试用例:** #### 用例 1:无条件搜索(获取全部,默认分页) ```json // 请求 GET /apiv1/knowledge/files/advanced-search token: <有效Token> // 预期响应 (HTTP 200) { "statusCode": 200, "msg": "success", "data": { "total": 50, "page": 1, "page_size": 20, "items": [ { "id": 1, "policy_name": "公路隧道施工技术规范", "policy_file_url": "https://oss.example.com/files/tunnel_spec.pdf", "policy_type": 1, "view_count": 120, "file_type": "pdf", "created_at": 1700000000 } // ... 最多 20 条 ] } } ``` #### 用例 2:关键词搜索 ```json // 请求 GET /apiv1/knowledge/files/advanced-search?keyword=隧道 token: <有效Token> // 预期响应 (HTTP 200) { "statusCode": 200, "msg": "success", "data": { "total": 5, "page": 1, "page_size": 20, "items": [ { "id": 1, "policy_name": "公路隧道施工技术规范", "policy_file_url": "...", "policy_type": 1, "view_count": 120, "file_type": "pdf", "created_at": 1700000000 } // ... 所有 policy_name 包含"隧道"的记录 ] } } ``` #### 用例 3:分类筛选 ```json // 请求 GET /apiv1/knowledge/files/advanced-search?category=国家规范 token: <有效Token> // 预期响应 (HTTP 200) { "statusCode": 200, "msg": "success", "data": { "total": 15, "page": 1, "page_size": 20, "items": [ // ... 所有 policy_type == 1 的记录 ] } } ``` #### 用例 4:不支持的分类值(忽略该条件) ```json // 请求 GET /apiv1/knowledge/files/advanced-search?category=其他分类 token: <有效Token> // 预期响应 (HTTP 200) // category 不在映射中,等同于不筛选分类 { "statusCode": 200, "msg": "success", "data": { "total": 50, "page": 1, "page_size": 20, "items": [...] } } ``` #### 用例 5:日期范围筛选 ```json // 请求 GET /apiv1/knowledge/files/advanced-search?date_from=2024-01-01&date_to=2024-12-31 token: <有效Token> // 预期响应 (HTTP 200) { "statusCode": 200, "msg": "success", "data": { "total": 20, "page": 1, "page_size": 20, "items": [ // ... created_at 在 2024-01-01 至 2024-12-31 之间的记录 ] } } ``` #### 用例 6:多条件组合搜索 ```json // 请求 GET /apiv1/knowledge/files/advanced-search?keyword=安全&category=行业规范&date_from=2024-06-01&page=1&page_size=10 token: <有效Token> // 预期响应 (HTTP 200) { "statusCode": 200, "msg": "success", "data": { "total": 3, "page": 1, "page_size": 10, "items": [ // ... policy_name 包含"安全" 且 policy_type == 2 且 created_at >= 2024-06-01 的记录 ] } } ``` #### 用例 7:分页 — 第二页 ```json // 请求 GET /apiv1/knowledge/files/advanced-search?page=2&page_size=10 token: <有效Token> // 预期响应 (HTTP 200) { "statusCode": 200, "msg": "success", "data": { "total": 50, "page": 2, "page_size": 10, "items": [ // ... 第 11-20 条记录 ] } } ``` #### 用例 8:搜索结果为空 ```json // 请求 GET /apiv1/knowledge/files/advanced-search?keyword=不存在的关键词xxxyyy token: <有效Token> // 预期响应 (HTTP 200) { "statusCode": 200, "msg": "success", "data": { "total": 0, "page": 1, "page_size": 20, "items": [] } } ``` #### 用例 9:日期格式错误 ```json // 请求 GET /apiv1/knowledge/files/advanced-search?date_from=2024/01/01 token: <有效Token> // 预期响应 (HTTP 500) // time.strptime 会抛出 ValueError // 具体行为取决于全局异常处理器 ``` > 注意:代码中 `time.strptime(date_from, "%Y-%m-%d")` 对日期格式有严格要求,非 `YYYY-MM-DD` 格式会引发异常。 #### 用例 10:未认证 ```json // 请求 GET /apiv1/knowledge/files/advanced-search?keyword=测试 // 预期响应 (HTTP 401) { "statusCode": 401, "msg": "未提供认证Token" } ``` --- ## 依赖说明 | 依赖项 | 说明 | |--------|------| | `database.get_db` | SQLAlchemy 数据库会话 | | `models.total.PolicyFile` | 政策文件模型(字段:id, policy_name, policy_file_url, policy_type, view_count, file_type, created_at) | | `services.chromadb_service` | ChromaDB 向量数据库服务(延迟导入),提供 `query(text, n)` 方法 | | `request.state.user` | 从中间件注入的用户信息(本文件接口未显式使用但受全局中间件保护) | ## 代码备注 1. `get_chromadb_document` 使用延迟导入(函数内 `from ... import ...`),避免在 ChromaDB 服务未部署时导致模块加载失败。 2. `get_chromadb_document` 有完善的 fallback 机制:ChromaDB 不可用时返回基于查询关键词的模拟数据,不会返回错误。 3. `advanced_search` 的分类映射是硬编码的字典 `{"国家规范": 1, "行业规范": 2, "地方规范": 3, "内部条例": 4}`,不在映射内的分类值会被静默忽略(不会筛选也不会报错)。 4. `advanced_search` 的日期筛选使用 `time.strptime` 解析,仅支持 `YYYY-MM-DD` 格式,其他格式会导致未捕获的 `ValueError` 异常。 5. `advanced_search` 的两个接口参数中 `request` 都标注为 `= None`,但在全局中间件保护下 `request` 实际始终可用。