Files
my-springboot-project/docs/题目更新接口文档.md

388 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 题目更新接口文档
> 对应接口:`PUT /api/question/{id}`
> 功能说明:更新题目信息,支持部分字段更新(只传需要修改的字段即可)
> 接口版本V1.0
> 更新日期2026-06-08
---
## 目录
- [1. 接口定义](#1-接口定义)
- [2. 请求参数](#2-请求参数)
- [3. 请求示例](#3-请求示例)
- [4. 响应说明](#4-响应说明)
- [5. 错误码说明](#5-错误码说明)
- [6. 边界情况说明](#6-边界情况说明)
- [7. curl 测试命令](#7-curl-测试命令)
---
## 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支持两种格式<br>① 自增数字ID`378`<br>② UUID格式的 questionId`e6bb2566-90a8-4651-b553-083a1d695b3c` |
### 1.2 认证要求
需要在请求头中携带 JWT Token
```
Authorization: Bearer <token>
```
Token 通过 `/api/auth/login` 接口登录获取。
---
## 2. 请求参数
所有请求字段均为**可选**(部分更新),只传需要修改的字段即可。
### 2.1 请求体结构
```json
{
"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 修改题型和难度
```json
// PUT /api/question/378
{
"question_type": "multiple_choice",
"difficulty": 4
}
```
### 3.2 修改题目内容和分值
```json
// 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 审核通过题目
```json
// PUT /api/question/378
{
"status": "approved",
"reviewer_comment": "内容正确,通过审核"
}
```
### 3.4 使用 UUID 更新
```json
// PUT /api/question/e6bb2566-90a8-4651-b553-083a1d695b3c
{
"difficulty": 5,
"score": 8.00
}
```
---
## 4. 响应说明
### 4.1 通用响应格式
```json
{
"code": 200,
"message": "操作成功",
"data": {},
"timestamp": "2026-06-08 10:00:00",
"success": true
}
```
### 4.2 更新成功响应code=200
```json
{
"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
```bash
curl -X POST "http://localhost:8080/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456"}'
```
### 7.2 正常更新数字ID
```bash
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 更新
```bash
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 超出范围(测试防御性校验)
```bash
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 无效题型(测试防御性校验)
```bash
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 不存在的题目
```bash
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"}
```