11 KiB
11 KiB
题目更新接口文档
对应接口:
PUT /api/question/{id}
功能说明:更新题目信息,支持部分字段更新(只传需要修改的字段即可)
接口版本:V1.0
更新日期:2026-06-08
目录
1. 接口定义
| 项目 | 内容 |
|---|---|
| 请求方法 | PUT |
| 接口路径 | /api/question/{id} |
| Content-Type | application/json |
| 认证方式 | Bearer Token (JWT) |
| 基础URL | http://localhost:8080(开发环境) |
| 完整URL示例 | PUT http://localhost:8080/api/question/378 |
1.1 路径参数说明
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id |
String | 是 | 题目ID,支持两种格式: ① 自增数字ID(如 378)② UUID格式的 questionId(如 e6bb2566-90a8-4651-b553-083a1d695b3c) |
1.2 认证要求
需要在请求头中携带 JWT Token:
Authorization: Bearer <token>
Token 通过 /api/auth/login 接口登录获取。
2. 请求参数
所有请求字段均为可选(部分更新),只传需要修改的字段即可。
2.1 请求体结构
{
"question_type": "single_choice",
"difficulty": 3,
"content": {
"stem": "题目标题",
"data": {},
"answer": "A"
},
"tags": ["数学", "代数"],
"score": 5.00,
"status": "pending",
"reviewer_comment": "审核意见",
"document_id": "uuid-string",
"document_name": "文件名.doc",
"knowledge_base_path": "public/dept_1",
"source_context": "原文上下文内容",
"chunk_ids": ["chunk_001", "chunk_002"],
"source_chunks_used": 3,
"page_numbers": [12, 15]
}
2.2 字段详情
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|---|---|---|---|---|
question_type |
String | 否 | 题目类型 | 必须是以下之一:single_choice、multiple_choice、true_false、fill_blank、subjective |
difficulty |
Integer | 否 | 难度等级 | 1-5 |
content |
Object | 否 | 题目内容(JSON对象) | 不能为空的JSON对象 |
content.stem |
String | 视题型 | 题干 | 主观题必填 |
content.data |
Object | 视题型 | 额外数据(如选择题的选项列表) | - |
content.answer |
String | 视题型 | 答案 | 选择题为选项字母,判断题 "T"/"F" |
tags |
Array<String> | 否 | 知识点标签 | - |
score |
BigDecimal | 否 | 分值 | 范围:-999.99 ~ 999.99(数据库 decimal(5,2) 约束) |
status |
String | 否 | 审核状态 | pending、approved、rejected |
reviewer_comment |
String | 否 | 审核评论 | - |
document_id |
String | 否 | 关联文档ID | - |
document_name |
String | 否 | 文档名称 | - |
knowledge_base_path |
String | 否 | 知识库路径 | 最大500字符 |
source_context |
String | 否 | 原文上下文 | - |
chunk_ids |
Array<String> | 否 | 切片ID列表 | - |
source_chunks_used |
Integer | 否 | 使用的源切片数量 | - |
page_numbers |
Array<Integer> | 否 | 页码列表 | - |
3. 请求示例
3.1 修改题型和难度
// PUT /api/question/378
{
"question_type": "multiple_choice",
"difficulty": 4
}
3.2 修改题目内容和分值
// PUT /api/question/378
{
"content": {
"stem": "以下哪项是正确的?",
"data": {
"options": [
{ "label": "A", "text": "选项A" },
{ "label": "B", "text": "选项B" },
{ "label": "C", "text": "选项C" }
]
},
"answer": "B"
},
"score": 10.00
}
3.3 审核通过题目
// PUT /api/question/378
{
"status": "approved",
"reviewer_comment": "内容正确,通过审核"
}
3.4 使用 UUID 更新
// PUT /api/question/e6bb2566-90a8-4651-b553-083a1d695b3c
{
"difficulty": 5,
"score": 8.00
}
4. 响应说明
4.1 通用响应格式
{
"code": 200,
"message": "操作成功",
"data": {},
"timestamp": "2026-06-08 10:00:00",
"success": true
}
4.2 更新成功响应(code=200)
{
"code": 200,
"message": "题目更新成功",
"data": {
"id": 378,
"questionId": "41a890f2-c9b9-466f-b9f6-e0bd237329b7",
"questionType": "multiple_choice",
"difficulty": 4,
"tags": null,
"score": 5.00,
"version": "1.0",
"documentId": null,
"excludeStems": null,
"knowledgeBasePath": null,
"sourceContext": null,
"chunkIds": null,
"sourceChunksUsed": 0,
"examCount": 0,
"correctCount": 0,
"lastExamTime": null,
"pageNumbers": null,
"content": {
"stem": "以下哪项是正确的?",
"data": {
"options": [
{ "label": "A", "text": "选项A" },
{ "label": "B", "text": "选项B" }
]
},
"answer": "A"
},
"status": "pending",
"reviewerComment": null,
"createdAt": "2026-06-08 09:00:00",
"updatedAt": "2026-06-08 10:00:00",
"isDeleted": 0
},
"timestamp": "2026-06-08 10:00:00",
"success": true
}
4.3 data 字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
id |
Long | 自增主键ID |
questionId |
String | 题目UUID(全局唯一标识) |
questionType |
String | 题目类型 |
difficulty |
Integer | 难度等级 1-5 |
tags |
JsonNode | 知识点标签(JSON数组或null) |
score |
BigDecimal | 分值 |
version |
String | 版本号(默认"1.0") |
documentId |
String | 关联文档ID |
excludeStems |
String | 题干内容(AI去重用) |
knowledgeBasePath |
String | 知识库路径 |
sourceContext |
String | 原文上下文 |
chunkIds |
JsonNode | 切片ID列表 |
sourceChunksUsed |
Integer | 使用切片数 |
examCount |
Integer | 出题次数 |
correctCount |
Integer | 正确次数 |
lastExamTime |
DateTime | 最后作答时间 |
pageNumbers |
JsonNode | 页码列表 |
content |
JsonNode | 题目内容(JSON对象) |
status |
String | 审核状态 |
reviewerComment |
String | 审核评论 |
createdAt |
DateTime | 创建时间 |
updatedAt |
DateTime | 最后更新时间 |
isDeleted |
Integer | 逻辑删除标志(0=正常,1=已删除) |
5. 错误码说明
5.1 业务错误码
| HTTP状态码 | code | message | 触发场景 |
|---|---|---|---|
| 200 | 200 | 题目更新成功 | 更新成功 |
| 404 | 404 | 题目不存在: {id} | 指定ID的题目不存在 |
| 400 | 400 | 分值超出范围,允许范围: -999.99 ~ 999.99 | score 超出 decimal(5,2) 精度范围 |
| 400 | 400 | 知识库路径过长,最大允许500字符 | knowledge_base_path 超过500字符 |
| 400 | 400 | 无效的题目类型: {type} | question_type 不在枚举值内 |
| 400 | 400 | 题目内容不能为空 | content 传了空对象 {} |
| 400 | 400 | 题目可能已被删除或归档 | 记录已被逻辑删除,updateById 影响行数为0 |
| 400 | 400 | 题目不存在 | getById 返回 null |
5.2 异常错误码
| HTTP状态码 | code | message | 触发场景 |
|---|---|---|---|
| 500 | 500 | 更新题目失败: {异常信息} | 数据库异常、JSON序列化异常等(Controller 兜底catch) |
| 401 | 401 | 用户未登录 | 未携带 Token 或 Token 无效 |
| 403 | 403 | 无权限访问 | 当前用户无操作权限 |
6. 边界情况说明
6.1 ID 格式自动识别
接口自动识别传入的 {id} 格式:
- 纯数字(如
378):按自增主键id查询 - 非数字(如
e6bb2566-...):按question_id(UUID)查询 - 非法格式:抛出
NumberFormatException,返回 400
6.2 部分更新机制
接口支持部分字段更新(PATCH 语义),请求体中只需包含要修改的字段:
- 未传的字段保持原值不变
- 传了
null的字段会被设为null content为完整替换,非合并更新
6.3 防御性校验
Service 层对以下输入进行校验:
| 校验项 | 拦截条件 | 错误信息 |
|---|---|---|
| score 范围 | score > 999.99 || score < -999.99 |
"分值超出范围,允许范围: -999.99 ~ 999.99" |
| 路径长度 | knowledge_base_path.length > 500 |
"知识库路径过长,最大允许500字符" |
| 题型枚举 | question_type ∉ validTypes |
"无效的题目类型: {type}" |
| 内容非空 | content != null && content.isEmpty() |
"题目内容不能为空" |
| 存在性检查 | getById(id) == null |
"题目不存在" |
| 更新影响行数 | updateById 返回 false |
"题目可能已被删除或归档" |
6.4 逻辑删除
- 已逻辑删除的记录(
is_deleted = 1)仍可通过getById查到 - 但
updateById由于 MyBatis-Plus 自动追加WHERE is_deleted = 0条件,将返回false - 此时接口会抛出
IllegalStateException,返回 400 错误
6.5 JSON 序列化
- 后台使用
JacksonTypeHandler将content字段序列化为 JSON 存入数据库 - 使用独立的
ObjectMapper实例(非 Spring 管理的全局配置) - 避免传入
NaN、Infinity等非法 JSON 值,否则可能触发JsonProcessingException
7. curl 测试命令
7.1 获取 Token
curl -X POST "http://localhost:8080/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456"}'
7.2 正常更新(数字ID)
curl -X PUT "http://localhost:8080/api/question/378" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"question_type": "multiple_choice",
"difficulty": 4,
"score": 5.00,
"content": {
"stem": "更新后的题干",
"data": {
"options": [
{"label": "A", "text": "选项A"},
{"label": "B", "text": "选项B"}
]
},
"answer": "A"
}
}'
7.3 使用 UUID 更新
curl -X PUT "http://localhost:8080/api/question/e6bb2566-90a8-4651-b553-083a1d695b3c" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"difficulty": 3}'
7.4 score 超出范围(测试防御性校验)
curl -X PUT "http://localhost:8080/api/question/378" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"score": 1000}'
# 期望响应: {"code": 500, "message": "更新题目失败: 分值超出范围,允许范围: -999.99 ~ 999.99"}
7.5 无效题型(测试防御性校验)
curl -X PUT "http://localhost:8080/api/question/378" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"question_type": "invalid_type"}'
# 期望响应: {"code": 500, "message": "更新题目失败: 无效的题目类型: invalid_type"}
7.6 不存在的题目
curl -X PUT "http://localhost:8080/api/question/99999" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"difficulty": 3}'
# 期望响应: {"code": 404, "message": "题目不存在: 99999"}