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

11 KiB
Raw Blame History

题目更新接口文档

对应接口: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支持两种格式
① 自增数字ID378
② UUID格式的 questionIde6bb2566-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_choicemultiple_choicetrue_falsefill_blanksubjective
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 审核状态 pendingapprovedrejected
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_idUUID查询
  • 非法格式:抛出 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 序列化

  • 后台使用 JacksonTypeHandlercontent 字段序列化为 JSON 存入数据库
  • 使用独立的 ObjectMapper 实例(非 Spring 管理的全局配置)
  • 避免传入 NaNInfinity 等非法 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"}