该文件提供综合性的通用接口,包括:
大部分接口为公开接口,部分接口需要 Token 认证。
路由前缀:/apiv1(以 routers/__init__.py 中注册为准)
/apiv1/recommend_question — 获取推荐问题功能说明: 从 RecommendQuestion 表中查询最多 10 条推荐问题。无分页、无排序、无过滤条件。
是否需要认证: 否
请求方式: GET
请求参数: 无
成功响应字段: | 字段 | 类型 | 说明 | |------|------|------| | data[].id | int | 问题 ID | | data[].question | string | 问题内容 |
测试用例:
// 请求
GET /apiv1/recommend_question
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": [
{"id": 1, "question": "如何进行隧道安全检查?"},
{"id": 2, "question": "桥梁施工有哪些注意事项?"},
{"id": 3, "question": "安全帽的正确佩戴方式是什么?"}
]
}
// 请求
GET /apiv1/recommend_question
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": []
}
// 请求
GET /apiv1/recommend_question
// 预期行为:数据库中有 15 条记录,仅返回前 10 条
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": [...] // 最多 10 条
}
/apiv1/get_policy_file — 获取政策文件列表功能说明: 分页查询未删除的政策文件列表,可按 policy_type 筛选。按更新时间倒序排列。
是否需要认证: 否
请求方式: GET
请求参数(Query): | 字段 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | policy_type | int | 否 | None | 政策类型筛选(0 或不传表示查全部) | | page | int | 否 | 1 | 页码 | | page_size | int | 否 | 20 | 每页条数 |
业务逻辑:
policy_type 为 None 或 0 时不做类型筛选updated_at 倒序排列成功响应字段: | 字段 | 类型 | 说明 | |------|------|------| | data.total | int | 总记录数 | | data.items[].id | int | 文件 ID | | data.items[].policy_name | string | 政策文件名称 | | data.items[].policy_file_url | string | 文件 URL | | data.items[].policy_type | int | 政策类型 | | data.items[].file_type | string | 文件类型(如 pdf, docx) | | data.items[].view_count | int | 查看次数 | | data.items[].created_at | int | 创建时间 |
测试用例:
// 请求
GET /apiv1/get_policy_file
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": {
"total": 25,
"items": [
{
"id": 10,
"policy_name": "2024年安全生产管理规定",
"policy_file_url": "https://oss.example.com/policy/safety_2024.pdf",
"policy_type": 1,
"file_type": "pdf",
"view_count": 150,
"created_at": 1700000000
}
]
}
}
// 请求
GET /apiv1/get_policy_file?policy_type=2&page=1&page_size=10
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": {
"total": 5,
"items": [...]
}
}
// 请求
GET /apiv1/get_policy_file?policy_type=0
// 预期行为:policy_type=0 时不做类型筛选,查全部
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": {
"total": 25,
"items": [...]
}
}
// 请求
GET /apiv1/get_policy_file?page=2&page_size=5
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": {
"total": 25,
"items": [...] // 第 6~10 条
}
}
// 请求
GET /apiv1/get_policy_file?policy_type=99
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": {
"total": 0,
"items": []
}
}
/apiv1/get_function_card — 获取功能卡片功能说明: 从 FunctionCard 表中查询最多 4 张功能卡片,用于首页展示。
是否需要认证: 否
请求方式: GET
请求参数: 无
成功响应字段: | 字段 | 类型 | 说明 | |------|------|------| | data[].id | int | 卡片 ID | | data[].title | string | 功能标题(对应 function_title) | | data[].icon | string | 功能图标(对应 function_icon) | | data[].description | string | 功能描述(对应 function_content) | | data[].business_type | string | 业务类型(对应 function_type) |
测试用例:
// 请求
GET /apiv1/get_function_card
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": [
{
"id": 1,
"title": "智能问答",
"icon": "chat-icon",
"description": "基于AI的智能问答系统",
"business_type": "chat"
},
{
"id": 2,
"title": "隐患识别",
"icon": "hazard-icon",
"description": "AI自动识别施工现场隐患",
"business_type": "hazard"
}
]
}
// 请求
GET /apiv1/get_function_card
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": []
}
/apiv1/get_hot_question — 获取热点问题功能说明: 查询点击量最高的前 3 个热点问题,按 click_count 倒序排列。
是否需要认证: 否
请求方式: GET
请求参数: 无
成功响应字段: | 字段 | 类型 | 说明 | |------|------|------| | data[].id | int | 问题 ID | | data[].question | string | 问题内容 | | data[].click_count | int | 点击次数(null 时返回 0) |
测试用例:
// 请求
GET /apiv1/get_hot_question
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": [
{"id": 5, "question": "高空作业安全规范有哪些?", "click_count": 328},
{"id": 3, "question": "隧道施工中常见的安全隐患?", "click_count": 256},
{"id": 8, "question": "如何正确使用个人防护装备?", "click_count": 189}
]
}
// 请求
GET /apiv1/get_hot_question
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": []
}
// 请求
GET /apiv1/get_hot_question
// 预期行为:click_count 为 null 时返回 0
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": [
{"id": 1, "question": "新增的问题", "click_count": 0}
]
}
/apiv1/submit_feedback — 提交意见反馈功能说明: 提交用户反馈。反馈类型支持中文和英文标识,内部映射为数字 ID。
是否需要认证: 是(从 request.state.user 获取用户信息,user 为 None 时 user_id 设为 0)
请求方式: POST
请求体(JSON): | 字段 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | feedback_type | string | 是 | — | 反馈类型。支持值见下表 | | content | string | 是 | — | 反馈内容 | | contact | string | 否 | "" | 联系方式 |
反馈类型映射: | 输入值 | 映射 ID | 说明 | |--------|---------|------| | "功能建议" / "bug" / "问题反馈" | 1 | 功能/问题类 | | "ui" / "界面优化" | 2 | UI 类 | | "experience" / "体验问题" | 3 | 体验类 | | "other" / "其他" | 4 | 其他(默认) |
不在映射表中的值默认映射为 4(其他)。
测试用例:
// 请求
POST /apiv1/submit_feedback
token: <有效Token>
Content-Type: application/json
{
"feedback_type": "功能建议",
"content": "建议增加批量上传图片的功能",
"contact": "13800138000"
}
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "感谢您的反馈!"
}
// 请求
POST /apiv1/submit_feedback
token: <有效Token>
Content-Type: application/json
{
"feedback_type": "bug",
"content": "页面加载异常,报 500 错误"
}
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "感谢您的反馈!"
}
// 请求
POST /apiv1/submit_feedback
token: <有效Token>
Content-Type: application/json
{
"feedback_type": "unknown_type",
"content": "这是一条测试反馈"
}
// 预期行为:feedback_type 不在映射表中,映射为 4(其他)
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "感谢您的反馈!"
}
// 请求
POST /apiv1/submit_feedback
token: <有效Token>
Content-Type: application/json
{
"feedback_type": "ui",
"content": "首页配色不协调"
}
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "感谢您的反馈!"
}
// 请求
POST /apiv1/submit_feedback
Content-Type: application/json
{
"feedback_type": "other",
"content": "匿名反馈"
}
// 预期行为:user 为 None 时 user_id=0,仍正常创建记录
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "感谢您的反馈!"
}
注意:代码中
user_id = user.id if user else 0,所以未认证用户也可提交,但 user_id 为 0。
// 请求
POST /apiv1/submit_feedback
token: <有效Token>
Content-Type: application/json
{
"feedback_type": "bug"
}
// 预期响应 (HTTP 422)
{
"detail": [
{
"loc": ["body", "content"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
/apiv1/like_and_dislike — AI 消息点赞/踩功能说明: 对指定的 AI 消息进行点赞(like)或踩(dislike)操作。更新 AIMessage 表的 user_feedback 字段。
注意: 该接口未做用户认证校验。
是否需要认证: 否(代码中未做认证校验)
请求方式: POST
请求体(JSON): | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | ai_message_id | int | 是 | AI 消息 ID | | action | string | 是 | 操作类型:"like" 或 "dislike" |
业务逻辑:
action="like" → user_feedback=2(满意/赞)action="dislike" → user_feedback=3(不满意/踩)测试用例:
// 请求
POST /apiv1/like_and_dislike
Content-Type: application/json
{
"ai_message_id": 100,
"action": "like"
}
// 预期响应 (HTTP 200)
// 数据库中 AIMessage.id=100 的 user_feedback 更新为 2
{
"statusCode": 200,
"msg": "success"
}
// 请求
POST /apiv1/like_and_dislike
Content-Type: application/json
{
"ai_message_id": 100,
"action": "dislike"
}
// 预期响应 (HTTP 200)
// 数据库中 AIMessage.id=100 的 user_feedback 更新为 3
{
"statusCode": 200,
"msg": "success"
}
// 请求
POST /apiv1/like_and_dislike
Content-Type: application/json
{
"ai_message_id": 99999,
"action": "like"
}
// 预期响应 (HTTP 200, 业务码 404)
{
"statusCode": 404,
"msg": "消息不存在"
}
// 请求
POST /apiv1/like_and_dislike
Content-Type: application/json
{
"ai_message_id": 100,
"action": "invalid_action"
}
// 预期行为:action != "like",user_feedback 设为 3(dislike)
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success"
}
// 请求
POST /apiv1/like_and_dislike
Content-Type: application/json
{
"ai_message_id": 100
}
// 预期响应 (HTTP 422)
{
"detail": [
{
"loc": ["body", "action"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
/apiv1/get_user_data_id — 获取用户数据 ID功能说明: 通过当前 Token 中的 account 字段查询 UserData 表,返回对应的主键 ID。
是否需要认证: 是
请求方式: GET
请求参数: 无
业务逻辑:
request.state.user 获取用户信息user.account 作为条件查询 UserData.accountIDuser_data.id,未找到返回 404成功响应字段: | 字段 | 类型 | 说明 | |------|------|------| | data.user_data_id | int | UserData 表主键 ID |
测试用例:
// 请求
GET /apiv1/get_user_data_id
token: <有效Token>
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success",
"data": {
"user_data_id": 42
}
}
// 请求
GET /apiv1/get_user_data_id
token: <有效Token,但 UserData 表中无对应记录>
// 预期响应 (HTTP 200, 业务码 404)
{
"statusCode": 404,
"msg": "用户数据不存在"
}
// 请求
GET /apiv1/get_user_data_id
// 预期响应 (HTTP 200, 业务码 401)
{
"statusCode": 401,
"msg": "未认证"
}
/apiv1/policy_file_count — 更新政策文件查看计数功能说明: 将指定政策文件的 view_count 加 1。每次调用自增一次。
注意: 该接口未做用户认证校验,可被无限制调用增加计数。
是否需要认证: 否(代码中未做认证校验)
请求方式: POST
请求体(JSON): | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | policy_file_id | int | 是 | 政策文件 ID |
业务逻辑:
PolicyFile 记录view_count = (view_count or 0) + 1(null 安全处理)updated_at 为当前时间戳测试用例:
// 请求
POST /apiv1/policy_file_count
Content-Type: application/json
{
"policy_file_id": 5
}
// 预期响应 (HTTP 200)
// 数据库中 PolicyFile.id=5 的 view_count 加 1
{
"statusCode": 200,
"msg": "success"
}
// 请求(连续调用两次)
POST /apiv1/policy_file_count
Content-Type: application/json
{
"policy_file_id": 5
}
// 预期行为:每次调用 view_count +1
// 第一次调用后 view_count=151,第二次调用后 view_count=152
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success"
}
// 请求
POST /apiv1/policy_file_count
Content-Type: application/json
{
"policy_file_id": 99999
}
// 预期响应 (HTTP 200, 业务码 404)
{
"statusCode": 404,
"msg": "文件不存在"
}
// 请求(针对 view_count 为 null 的文件)
POST /apiv1/policy_file_count
Content-Type: application/json
{
"policy_file_id": 8
}
// 预期行为:(null or 0) + 1 = 1
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "success"
}
// 请求
POST /apiv1/policy_file_count
Content-Type: application/json
{}
// 预期响应 (HTTP 422)
{
"detail": [
{
"loc": ["body", "policy_file_id"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
/apiv1/download_file — 流式代理下载 OSS 文件功能说明: 接收加密的 OSS 代理 URL,解密后通过 HTTP 流式代理下载文件内容。返回 StreamingResponse,而非 JSON。
是否需要认证: 否
请求方式: GET
请求参数(Query): | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | pdf_oss_download_link | string | 是 | 加密的 OSS 文件代理 URL |
业务逻辑:
decrypt_url() 解密 URL 获取真实 OSS 地址httpx.AsyncClient 流式请求真实 URLStreamingResponse(透传 Content-Type 和 Content-Disposition)测试用例:
// 请求
GET /apiv1/download_file?pdf_oss_download_link=encrypted_url_string
// 预期响应 (HTTP 200)
// Content-Type: application/pdf
// Content-Disposition: attachment; filename="safety_guide.pdf"
// Body: [PDF 文件二进制流]
// 请求
GET /apiv1/download_file?pdf_oss_download_link=encrypted_docx_url
// 预期响应 (HTTP 200)
// Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
// Body: [DOCX 文件二进制流]
// 请求
GET /apiv1/download_file?pdf_oss_download_link=invalid_encrypted_string
// 预期响应 (HTTP 200, 业务码 500)
{
"statusCode": 500,
"msg": "下载失败: <解密错误信息>"
}
// 请求
GET /apiv1/download_file?pdf_oss_download_link=encrypted_url_of_deleted_file
// 预期响应 (HTTP 200, 业务码 404)
{
"statusCode": 404,
"msg": "文件下载失败"
}
// 请求
GET /apiv1/download_file?pdf_oss_download_link=encrypted_url
// 预期响应 (HTTP 200, 业务码 500)
{
"statusCode": 500,
"msg": "文件下载失败"
}
// 请求
GET /apiv1/download_file?pdf_oss_download_link=encrypted_url_with_unreachable_host
// 预期响应 (HTTP 200, 业务码 500)
{
"statusCode": 500,
"msg": "下载失败: <httpx 超时或连接错误信息>"
}
// 请求
GET /apiv1/download_file
// 预期响应 (HTTP 422)
{
"detail": [
{
"loc": ["query", "pdf_oss_download_link"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
| 依赖项 | 说明 |
|---|---|
database.get_db |
SQLAlchemy 数据库会话(Depends 注入) |
models.total.RecommendQuestion |
推荐问题模型(字段:id, question) |
models.total.PolicyFile |
政策文件模型(字段:id, policy_name, policy_file_url, policy_type, file_type, view_count, is_deleted, created_at, updated_at) |
models.total.FunctionCard |
功能卡片模型(字段:id, function_title, function_icon, function_content, function_type) |
models.total.HotQuestion |
热点问题模型(字段:id, question, click_count) |
models.total.FeedbackQuestion |
反馈问题模型(字段:id, feedback_type, feedback_content, feedback_user_phone, user_id, created_at, updated_at) |
models.chat.AIMessage |
AI 消息模型(字段:id, user_feedback, updated_at) |
models.user_data.UserData |
用户数据模型(字段:id, accountID) |
utils.crypto.decrypt_url |
URL 解密工具函数 |
httpx |
异步 HTTP 客户端,用于流式代理下载 |
request.state.user |
从中间件注入的用户信息(含 id, account) |
recommend_question 无排序条件, 返回数据库中前 10 条记录,顺序取决于数据库默认排序。get_policy_file 的 policy_type=0 等同于不筛选, 与 None 行为一致。get_function_card 的响应字段名与模型字段名不一致: title→function_title, icon→function_icon, description→function_content, business_type→function_type。get_hot_question 对 click_count 做了 null 安全处理: q.click_count or 0。submit_feedback 的认证是软性的: 未认证时 user_id=0,仍可成功创建反馈记录。like_and_dislike 未做用户认证校验, 任何人可对任何消息点赞/踩,且无法取消(只能覆盖为新状态)。like_and_dislike 的 action 判断不严格: 非 "like" 的任何值都被视为 "dislike"(user_feedback=3)。policy_file_count 未做认证和防重复校验, 可被无限调用导致计数虚高。download_file 使用流式代理模式, 服务端作为中间代理转发 OSS 文件内容,不直接暴露 OSS 真实 URL 给前端。download_file 参数名为 pdf_oss_download_link, 但实际支持任意文件类型下载,不限于 PDF。