duoqi-api/docs/api-reference.md
Wang Zhuoxuan 1b142f2866 feat: 添加题目批量发布、归档和删除接口
- 新增 batchUpdateStatus 通用方法,带状态流转校验和 BatchResult 报告
- 改造 batchPublish 使用新方法,返回成功/失败详情
- 新增 batchArchive 和 batch-delete 端点(软删除)
- 使用 inArray 批量查询和更新,优化数据库往返
- 更新 API 文档,补充三个批量接口说明
2026-04-11 22:19:02 +08:00

25 KiB
Raw Blame History

Duoqi API Reference

多奇服务端 API 接口文档 Base URL: http://<host>:3000/v1

目录


通用约定

认证方式

类型 Header 适用路径
无需认证 - /v1/health, /v1/auth/*
JWT Authorization: Bearer <jwt_token> 大多数客户端 API
Admin JWT Authorization: Bearer <admin_jwt_token> /v1/admin/* (推荐)
Admin Token Authorization: Bearer <admin_token> /v1/admin/* (向后兼容)

统一响应格式

// 成功响应
{
  "success": true,
  "data": <T>,
  "error": null
}

// 错误响应
{
  "success": false,
  "data": null,
  "error": {
    "code": "ERROR_CODE",
    "message": "错误描述"
  }
}

// 分页响应(额外包含)
{
  "success": true,
  "data": [...],
  "pagination": {
    "total": 100,
    "page": 1,
    "limit": 20
  },
  "error": null
}

状态码

状态码 含义
200 请求成功
400 请求参数验证失败
401 未认证 / 认证失败
404 资源不存在
501 功能未实现

客户端 API

健康检查

GET /health

健康检查端点,用于服务可用性探测。

认证: 无

请求: 无

响应:

{
  "success": true,
  "data": {
    "status": "ok",
    "timestamp": "2026-04-10T12:00:00.000Z"
  },
  "error": null
}

认证

POST /auth/guest

游客登录,通过设备 ID 创建或获取用户账号。

认证: 无 限流: 10 次/分钟

请求体:

{
  "deviceId": "string (必填)"
}

响应:

{
  "success": true,
  "data": {
    "user": {
      "id": "uuid",
      "nickname": null,
      "avatarUrl": null,
      "tier": "free"
    },
    "tokens": {
      "accessToken": "jwt_token",
      "refreshToken": "jwt_token"
    }
  },
  "error": null
}

POST /auth/huawei

华为账号登录。

认证: 无 限流: 10 次/分钟

请求体:

{
  "authorizationCode": "string (必填)"
}

响应:

{
  "success": true,
  "data": {
    "user": {
      "id": "uuid",
      "nickname": "用户昵称",
      "avatarUrl": "头像URL",
      "tier": "free"
    },
    "tokens": {
      "accessToken": "jwt_token",
      "refreshToken": "jwt_token"
    }
  },
  "error": null
}

POST /auth/phone

手机号登录(未实现)。

认证: 无 状态: 501 Not Implemented


POST /auth/refresh

刷新访问令牌。

认证: 无 限流: 10 次/分钟

请求体:

{
  "refreshToken": "string (必填)"
}

响应:

{
  "success": true,
  "data": {
    "accessToken": "new_jwt_token",
    "refreshToken": "new_refresh_token"
  },
  "error": null
}

GET /auth/me

获取当前用户信息。

认证: JWT

响应:

{
  "success": true,
  "data": {
    "id": "uuid",
    "nickname": "用户昵称",
    "avatarUrl": "头像URL",
    "tier": "free | pro | proplus",
    "xpTotal": 150,
    "streakDays": 3,
    "heartsRemaining": 5,
    "dailyXpEarned": 20,
    "dailyXpGoal": 50
  },
  "error": null
}

答题

GET /quiz/categories

获取所有题目分类列表。

认证: JWT

响应:

{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "name": "历史",
      "slug": "history",
      "parentId": null
    }
  ],
  "error": null
}

GET /quiz/categories/:id/chapters

获取指定分类下的章节列表。

认证: JWT

路径参数:

  • id: 分类 ID

响应:

{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "categoryId": "uuid",
      "title": "第一章",
      "parentId": null,
      "sortOrder": 1
    }
  ],
  "error": null
}

GET /quiz/chapters/:id/questions

获取章节下的题目(包含用户答题状态)。

认证: JWT

路径参数:

  • id: 章节 ID

响应:

{
  "success": true,
  "data": {
    "chapterId": "uuid",
    "title": "第一章",
    "questionsRequired": 5,
    "passThreshold": 3,
    "questions": [
      {
        "id": "uuid",
        "stem": { "text": "题目内容" },
        "contentType": "text",
        "options": ["A", "B", "C", "D"],
        "answered": false,
        "isCorrect": null
      }
    ]
  },
  "error": null
}

POST /quiz/answer

提交答案。

认证: JWT

请求体:

{
  "questionId": "uuid (必填)",
  "selectedAnswer": "string (必填)",
  "timeMs": 1500
}

响应:

{
  "success": true,
  "data": {
    "correct": true,
    "correctAnswer": "B",
    "xpEarned": 10,
    "streakBonus": 0,
    "chapterCompleted": false
  },
  "error": null
}

POST /quiz/rate

评价题目质量。

认证: JWT

请求体:

{
  "questionId": "uuid (必填)",
  "rating": "good | bad"
}

响应:

{
  "success": true,
  "data": null,
  "error": null
}

进度

GET /progress/dashboard

获取用户进度概览。

认证: JWT

响应:

{
  "success": true,
  "data": {
    "xpTotal": 150,
    "streakDays": 3,
    "heartsRemaining": 5,
    "dailyXpEarned": 20,
    "dailyXpGoal": 50,
    "categoriesCompleted": 1,
    "totalCategories": 5
  },
  "error": null
}

GET /progress/streak

获取连胜信息。

认证: JWT

响应:

{
  "success": true,
  "data": {
    "currentStreak": 3,
    "longestStreak": 7,
    "lastActiveDate": "2026-04-10"
  },
  "error": null
}

GET /progress/hearts

获取红心信息。

认证: JWT

响应:

{
  "success": true,
  "data": {
    "remaining": 5,
    "max": 5,
    "nextRestoreAt": "2026-04-10T13:00:00.000Z"
  },
  "error": null
}

POST /progress/hearts/restore

恢复红心。

认证: JWT

请求体:

{
  "method": "ad | wait | upgrade"
}

响应:

{
  "success": true,
  "data": {
    "remaining": 5,
    "restored": 1
  },
  "error": null
}

GET /progress/chapters

获取所有章节进度。

认证: JWT

响应:

{
  "success": true,
  "data": [
    {
      "chapterId": "uuid",
      "title": "第一章",
      "completedQuestions": 3,
      "totalQuestions": 5,
      "passed": false,
      "passedAt": null
    }
  ],
  "error": null
}

POST /feedback

提交用户反馈。

认证: JWT

请求体:

{
  "content": "string (必填, 1-2000字符)",
  "contact": "string (可选, 最多255字符)",
  "pageContext": "string (可选, 最多200字符)"
}

响应:

{
  "success": true,
  "data": null,
  "error": null
}

游戏化

GET /leaderboard

获取排行榜。

认证: JWT

查询参数:

  • tier: "free" | "pro" | "proplus" (可选)
  • page: 页码 (默认: 1)
  • limit: 每页数量 (默认: 20)

响应:

{
  "success": true,
  "data": [
    {
      "rank": 1,
      "userId": "uuid",
      "nickname": "玩家昵称",
      "avatarUrl": "头像URL",
      "xpTotal": 5000
    }
  ],
  "pagination": {
    "total": 100,
    "page": 1,
    "limit": 20
  },
  "error": null
}

GET /leaderboard/me

获取当前用户排名。

认证: JWT

响应:

{
  "success": true,
  "data": {
    "rank": 15,
    "xpTotal": 1500
  },
  "error": null
}

GET /achievements

获取成就列表。

认证: JWT

响应:

{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "code": "first_win",
      "name": "初出茅庐",
      "description": "完成第一道题",
      "iconUrl": "图标URL",
      "unlocked": true,
      "unlockedAt": "2026-04-10T10:00:00.000Z"
    }
  ],
  "error": null
}

POST /achievements/check

检查并解锁新成就。

认证: JWT

响应:

{
  "success": true,
  "data": {
    "newlyUnlocked": [
      {
        "id": "uuid",
        "code": "streak_7",
        "name": "连胜达人",
        "description": "连续7天活跃"
      }
    ]
  },
  "error": null
}

支付

POST /payment/verify-huawei

验证华为 IAP 收据并激活订阅。

认证: JWT

请求体:

{
  "purchaseToken": "string (必填)",
  "productId": "string (必填)",
  "tier": "pro | proplus"
}

响应:

{
  "success": true,
  "data": {
    "tier": "pro",
    "provider": "huawei",
    "active": true,
    "expiresAt": "2026-05-10T00:00:00.000Z"
  },
  "error": null
}

GET /payment/subscription

获取当前订阅状态。

认证: JWT

响应:

{
  "success": true,
  "data": {
    "tier": "pro",
    "provider": "huawei",
    "active": true,
    "expiresAt": "2026-05-10T00:00:00.000Z",
    "autoRenew": true
  },
  "error": null
}

管理端 API

管理端认证

POST /admin/auth/login

管理员用户名密码登录。

认证: 无

请求体:

{
  "username": "string (必填)",
  "password": "string (必填, 最少8字符)"
}

响应:

{
  "success": true,
  "data": {
    "accessToken": "jwt_token (1h有效)",
    "refreshToken": "jwt_token (30d有效)",
    "admin": {
      "id": "uuid",
      "username": "admin",
      "role": "super_admin"
    }
  },
  "error": null
}

错误 (401):

{
  "success": false,
  "data": null,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid username or password"
  }
}

POST /admin/auth

管理端 Token 认证(向后兼容,推荐使用 /admin/auth/login)。

认证: 无

请求体:

{
  "token": "string (必填)"
}

响应:

{
  "success": true,
  "data": {
    "authenticated": true
  },
  "error": null
}

错误 (401):

{
  "success": false,
  "data": null,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid admin token"
  }
}

管理员管理

仅 super_admin 角色可执行写操作POST、PUT、DELETE读操作GET所有管理员均可访问。

GET /admin/admins

获取管理员列表。

认证: Admin JWT

查询参数:

  • page: 页码 (默认: 1, 必须 ≥ 1)
  • limit: 每页数量 (默认: 20, 范围: 1-50)
  • role: "admin" | "super_admin" (可选,按角色筛选)
  • isActive: 0 | 1 (可选,按状态筛选)

响应:

{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "username": "admin",
      "role": "super_admin",
      "isActive": 1,
      "lastLoginAt": "2026-04-11T10:00:00.000Z",
      "createdAt": "2026-04-01T00:00:00.000Z",
      "updatedAt": "2026-04-11T10:00:00.000Z"
    }
  ],
  "pagination": {
    "total": 5,
    "page": 1,
    "limit": 20
  },
  "error": null
}

GET /admin/admins/:id

获取管理员详情。

认证: Admin JWT

路径参数:

  • id: 管理员 ID

响应:

{
  "success": true,
  "data": {
    "id": "uuid",
    "username": "admin",
    "role": "super_admin",
    "isActive": 1,
    "lastLoginAt": "2026-04-11T10:00:00.000Z",
    "createdAt": "2026-04-01T00:00:00.000Z",
    "updatedAt": "2026-04-11T10:00:00.000Z"
  },
  "error": null
}

错误 (404):

{
  "success": false,
  "data": null,
  "error": {
    "code": "NOT_FOUND",
    "message": "Admin user not found"
  }
}

POST /admin/admins

创建新管理员super_admin 专属)。

认证: Admin JWT (super_admin)

请求体:

{
  "username": "string (必填, 3-50字符)",
  "password": "string (必填, 8-128字符)",
  "role": "admin | super_admin (必填)"
}

响应:

{
  "success": true,
  "data": {
    "id": "uuid",
    "username": "newadmin",
    "role": "admin",
    "isActive": 1,
    "lastLoginAt": null,
    "createdAt": "2026-04-11T12:00:00.000Z",
    "updatedAt": "2026-04-11T12:00:00.000Z",
    "plainPassword": "随机生成的初始密码"
  },
  "error": null
}

错误 (403):

{
  "success": false,
  "data": null,
  "error": {
    "code": "FORBIDDEN",
    "message": "Super admin privileges required"
  }
}

错误 (400):

{
  "success": false,
  "data": null,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Username \"admin\" already exists"
  }
}

PUT /admin/admins/:id

更新管理员信息super_admin 专属)。

认证: Admin JWT (super_admin)

路径参数:

  • id: 管理员 ID

请求体:

{
  "username": "string (可选, 3-50字符)",
  "role": "admin | super_admin (可选)",
  "isActive": 0 | 1 (可选)"
}

响应:

{
  "success": true,
  "data": {
    "id": "uuid",
    "username": "updated_username",
    "role": "admin",
    "isActive": 0,
    "lastLoginAt": "2026-04-11T10:00:00.000Z",
    "createdAt": "2026-04-01T00:00:00.000Z",
    "updatedAt": "2026-04-11T12:00:00.000Z"
  },
  "error": null
}

安全规则:

  • 禁止删除或降级最后一个 super_admin
  • 用户名必须唯一

DELETE /admin/admins/:id

软删除管理员super_admin 专属)。

认证: Admin JWT (super_admin)

路径参数:

  • id: 管理员 ID

响应:

{
  "success": true,
  "data": {
    "id": "uuid",
    "username": "deleted_admin",
    "role": "admin",
    "isActive": 0,
    "lastLoginAt": "2026-04-11T10:00:00.000Z",
    "createdAt": "2026-04-01T00:00:00.000Z",
    "updatedAt": "2026-04-11T12:00:00.000Z"
  },
  "error": null
}

说明:

  • 软删除:将 isActive 设为 0不删除记录
  • 禁止删除最后一个 super_admin

POST /admin/admins/:id/reset-password

重置管理员密码super_admin 专属)。

认证: Admin JWT (super_admin)

路径参数:

  • id: 管理员 ID

响应:

{
  "success": true,
  "data": {
    "adminId": "uuid",
    "username": "admin",
    "plainPassword": "新随机生成的12位密码"
  },
  "error": null
}

说明:

  • 生成 12 位随机密码,包含大小写字母、数字、符号
  • 明文密码仅在响应中返回一次,请妥善保存
  • 密码使用 BCrypt 哈希后存储到数据库

题目管理

GET /admin/questions

获取题目列表。

认证: Admin Token

查询参数:

  • page: 页码 (默认: 1)
  • limit: 每页数量 (默认: 20)
  • status: draft | reviewing | published | archived (可选)
  • categoryId: 分类 ID (可选)

响应:

{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "stem": { "text": "题目内容" },
      "contentType": "text",
      "correctAnswer": "B",
      "distractors": ["A", "C", "D"],
      "categoryId": "uuid",
      "difficulty": 3,
      "status": "published"
    }
  ],
  "pagination": {
    "total": 100,
    "page": 1,
    "limit": 20
  },
  "error": null
}

GET /admin/questions/:id

获取题目详情。

认证: Admin Token

路径参数:

  • id: 题目 ID

响应:

{
  "success": true,
  "data": {
    "id": "uuid",
    "stem": { "text": "题目内容" },
    "contentType": "text",
    "correctAnswer": "B",
    "distractors": ["A", "C", "D"],
    "categoryId": "uuid",
    "difficulty": 3,
    "status": "published",
    "knowledgeCard": {
      "id": "uuid",
      "summary": "知识点摘要",
      "deepDive": "深入解析",
      "sourceRef": "来源引用"
    }
  },
  "error": null
}

POST /admin/questions

创建新题目。

认证: Admin Token

请求体:

{
  "stem": { "text": "题目内容" },
  "contentType": "text | image | video | audio",
  "correctAnswer": "B (必填)",
  "distractors": ["A", "C", "D"],
  "categoryId": "uuid (必填)",
  "difficulty": 3,
  "knowledgeCard": {
    "summary": "知识点摘要 (必填)",
    "deepDive": "深入解析",
    "sourceRef": "来源引用"
  }
}

响应:

{
  "success": true,
  "data": {
    "id": "uuid",
    "status": "draft"
  },
  "error": null
}

PUT /admin/questions/:id

更新题目。

认证: Admin Token

路径参数:

  • id: 题目 ID

请求体:

{
  "stem": { "text": "题目内容" },
  "contentType": "text | image | video | audio",
  "correctAnswer": "B",
  "distractors": ["A", "C", "D"],
  "categoryId": "uuid",
  "difficulty": 3,
  "status": "draft | reviewing | published | archived"
}

DELETE /admin/questions/:id

归档题目。

认证: Admin Token

路径参数:

  • id: 题目 ID

响应:

{
  "success": true,
  "data": null,
  "error": null
}

PATCH /admin/questions/:id/status

变更题目状态(带流转校验)。

认证: Admin Token

路径参数:

  • id: 题目 ID

请求体:

{
  "status": "draft | reviewing | published | archived (必填)"
}

允许的状态流转:

当前状态 可变更到
draft reviewing, archived
reviewing published, draft, archived
published archived
archived draft

响应:

{
  "success": true,
  "data": {
    "id": "uuid",
    "stem": { "text": "题目内容" },
    "contentType": "text",
    "correctAnswer": "B",
    "distractors": ["A", "C", "D"],
    "categoryId": "uuid",
    "difficulty": 3,
    "status": "published",
    "knowledgeCard": { ... }
  },
  "error": null
}

错误 (404):

{
  "success": false,
  "data": null,
  "error": {
    "code": "NOT_FOUND",
    "message": "题目不存在"
  }
}

错误 (400):

{
  "success": false,
  "data": null,
  "error": {
    "code": "INVALID_STATUS_TRANSITION",
    "message": "不允许从 published 变更为 reviewing"
  }
}

POST /admin/questions/batch-publish

批量发布题目(带状态流转校验,仅 reviewing 状态可发布)。

认证: Admin Token

请求体:

{
  "ids": ["uuid1", "uuid2"]
}

参数说明:

字段 类型 必填 说明
ids string[] 题目 ID 数组1-200 个,每个为合法 UUID

响应:

{
  "success": true,
  "data": {
    "total": 3,
    "succeeded": 2,
    "failed": [
      { "id": "uuid3", "reason": "不允许从 draft 变更为 published" }
    ]
  },
  "error": null
}

data 字段说明:

字段 类型 说明
total number 提交的 ID 总数
succeeded number 成功更新的数量
failed array 失败记录列表(包含 id 和 reason

POST /admin/questions/batch-archive

批量归档题目带状态流转校验draft/reviewing/published 状态可归档)。

认证: Admin Token

请求体:

{
  "ids": ["uuid1", "uuid2"]
}

参数说明:

字段 类型 必填 说明
ids string[] 题目 ID 数组1-200 个,每个为合法 UUID

响应: 与 batch-publish 相同的 BatchResult 格式。


POST /admin/questions/batch-delete

批量删除题目(软删除,等同于批量归档)。

认证: Admin Token

请求体:

{
  "ids": ["uuid1", "uuid2"]
}

参数说明:

字段 类型 必填 说明
ids string[] 题目 ID 数组1-200 个,每个为合法 UUID

响应: 与 batch-publish 相同的 BatchResult 格式。


分类管理

GET /admin/categories

获取分类列表。

认证: Admin Token

查询参数:

  • page: 页码 (默认: 1, 必须 ≥ 1)
  • limit: 每页数量 (默认: 20, 范围: 1-50)

响应:

{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "name": "历史",
      "slug": "history",
      "parentId": null,
      "sortOrder": 1,
      "questionCount": 120,
      "status": "active"
    }
  ],
  "pagination": {
    "total": 45,
    "page": 1,
    "limit": 20
  },
  "error": null
}

POST /admin/categories

创建分类。

认证: Admin Token

请求体:

{
  "id": "uuid (必填)",
  "name": "分类名称 (必填)",
  "slug": "分类slug (必填)",
  "parentId": "uuid",
  "sortOrder": 1
}

PUT /admin/categories/:id

更新分类。

认证: Admin Token

请求体:

{
  "name": "分类名称",
  "slug": "分类slug",
  "parentId": "uuid | null",
  "sortOrder": 1,
  "status": "active | inactive"
}

DELETE /admin/categories/:id

归档分类。

认证: Admin Token


知识点卡片

GET /admin/knowledge-cards

获取知识点卡片列表。

认证: Admin Token

查询参数:

  • page: 页码 (默认: 1)
  • limit: 每页数量 (默认: 20)

GET /admin/knowledge-cards/by-question/:questionId

根据题目 ID 获取知识点卡片。

认证: Admin Token


PUT /admin/knowledge-cards/:id

更新知识点卡片。

认证: Admin Token

请求体:

{
  "summary": "摘要 (必填)",
  "deepDive": "深入解析",
  "sourceRef": "来源引用"
}

技能树管理

GET /admin/skill-tree

获取章节列表。

认证: Admin Token

查询参数:

  • categoryId: 分类 ID (可选)

POST /admin/skill-tree

创建章节。

认证: Admin Token

请求体:

{
  "categoryId": "uuid (必填)",
  "title": "章节标题 (必填)",
  "parentId": "uuid",
  "sortOrder": 1,
  "questionsRequired": 5,
  "passThreshold": 3
}

PUT /admin/skill-tree/:id

更新章节。

认证: Admin Token

请求体:

{
  "title": "章节标题",
  "parentId": "uuid | null",
  "sortOrder": 1,
  "questionsRequired": 5,
  "passThreshold": 3
}

DELETE /admin/skill-tree/:id

删除章节。

认证: Admin Token


用户管理

GET /admin/users

获取用户列表。

认证: Admin Token

查询参数:

  • page: 页码 (默认: 1)
  • limit: 每页数量 (默认: 20)
  • search: 搜索关键词 (昵称/ID)

响应:

{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "nickname": "玩家昵称",
      "avatarUrl": "头像URL",
      "tier": "free",
      "xpTotal": 150,
      "streakDays": 3,
      "banned": false
    }
  ],
  "pagination": { ... },
  "error": null
}

GET /admin/users/:id

获取用户详情。

认证: Admin Token


PUT /admin/users/:id/ban

封禁用户。

认证: Admin Token


PUT /admin/users/:id/unban

解封用户。

认证: Admin Token


统计数据

GET /admin/stats

获取仪表盘统计数据。

认证: Admin Token

响应:

{
  "success": true,
  "data": {
    "totalUsers": 1000,
    "activeUsers": 150,
    "totalQuestions": 500,
    "publishedQuestions": 450,
    "averageXp": 200
  },
  "error": null
}

反馈管理

GET /admin/feedback

获取用户反馈列表。

认证: Admin Token

查询参数:

  • page: 页码 (默认: 1)
  • limit: 每页数量 (默认: 20)

响应:

{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "userId": "uuid",
      "content": "反馈内容",
      "contact": "联系方式",
      "pageContext": "页面上下文",
      "createdAt": "2026-04-10T10:00:00.000Z"
    }
  ],
  "pagination": { ... },
  "error": null
}

附录

错误代码

代码 说明
VALIDATION_ERROR 请求参数验证失败
UNAUTHORIZED 未认证或认证失败
FORBIDDEN 权限不足(需要 super_admin
NOT_FOUND 资源不存在
INVALID_STATUS_TRANSITION 题目状态流转不合法
INVALID_RECEIPT 支付收据验证失败
NOT_IMPLEMENTED 功能未实现
INTERNAL_ERROR 服务器内部错误

数据模型

User (用户)

{
  id: string;              // UUID
  nickname: string | null; // 昵称
  avatarUrl: string | null; // 头像URL
  tier: 'free' | 'pro' | 'proplus'; // 会员等级
  xpTotal: number;         // 总经验值
  streakDays: number;      // 连续天数
  heartsRemaining: number; // 剩余红心
  banned: boolean;         // 是否封禁
}

Question (题目)

{
  id: string;              // UUID
  stem: Record<string, unknown>; // 题目内容(支持多语言)
  contentType: 'text' | 'image' | 'video' | 'audio';
  correctAnswer: string;   // 正确答案
  distractors: string[];   // 干扰项
  categoryId: string;      // 分类ID
  difficulty: 1-5;         // 难度等级
  status: 'draft' | 'reviewing' | 'published' | 'archived';
}

Chapter (章节)

{
  id: string;              // UUID
  categoryId: string;      // 分类ID
  title: string;           // 章节标题
  parentId: string | null; // 父章节ID
  sortOrder: number;       // 排序
  questionsRequired: number; // 需要答题数
  passThreshold: number;   // 通过阈值
}