- POST /admin/questions/import 支持 JSON 数组导入(1-200 条) - POST /admin/questions/import-csv 支持 CSV 文本导入 - 全有或全无事务策略,先验校验后统一插入 - 包含 categoryId 外键存在性校验 - CSV 解析器支持引号内逗号、换行和 "" 转义
1844 lines
29 KiB
Markdown
1844 lines
29 KiB
Markdown
# Duoqi API Reference
|
||
|
||
> 多奇服务端 API 接口文档
|
||
> Base URL: `http://<host>:3000/v1`
|
||
|
||
## 目录
|
||
|
||
- [通用约定](#通用约定)
|
||
- [客户端 API](#客户端-api)
|
||
- [健康检查](#健康检查)
|
||
- [认证](#认证)
|
||
- [答题](#答题)
|
||
- [进度](#进度)
|
||
- [游戏化](#游戏化)
|
||
- [支付](#支付)
|
||
- [管理端 API](#管理端-api)
|
||
- [管理端认证](#管理端认证)
|
||
- [管理员管理](#管理员管理)
|
||
- [题目管理](#题目管理)
|
||
- [分类管理](#分类管理)
|
||
- [知识点卡片](#知识点卡片)
|
||
- [技能树管理](#技能树管理)
|
||
- [用户管理](#用户管理)
|
||
- [统计数据](#统计数据)
|
||
- [反馈管理](#反馈管理)
|
||
|
||
---
|
||
|
||
## 通用约定
|
||
|
||
### 认证方式
|
||
|
||
| 类型 | 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/*` (向后兼容) |
|
||
|
||
### 统一响应格式
|
||
|
||
```typescript
|
||
// 成功响应
|
||
{
|
||
"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
|
||
|
||
健康检查端点,用于服务可用性探测。
|
||
|
||
**认证**: 无
|
||
|
||
**请求**: 无
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"status": "ok",
|
||
"timestamp": "2026-04-10T12:00:00.000Z"
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 认证
|
||
|
||
#### POST /auth/guest
|
||
|
||
游客登录,通过设备 ID 创建或获取用户账号。
|
||
|
||
**认证**: 无
|
||
**限流**: 10 次/分钟
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"deviceId": "string (必填)"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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 次/分钟
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"authorizationCode": "string (必填)"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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 次/分钟
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"refreshToken": "string (必填)"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"accessToken": "new_jwt_token",
|
||
"refreshToken": "new_refresh_token"
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### GET /auth/me
|
||
|
||
获取当前用户信息。
|
||
|
||
**认证**: JWT
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"id": "uuid",
|
||
"name": "历史",
|
||
"slug": "history",
|
||
"parentId": null
|
||
}
|
||
],
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### GET /quiz/categories/:id/chapters
|
||
|
||
获取指定分类下的章节列表。
|
||
|
||
**认证**: JWT
|
||
|
||
**路径参数**:
|
||
- `id`: 分类 ID
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"id": "uuid",
|
||
"categoryId": "uuid",
|
||
"title": "第一章",
|
||
"parentId": null,
|
||
"sortOrder": 1
|
||
}
|
||
],
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### GET /quiz/chapters/:id/questions
|
||
|
||
获取章节下的题目(包含用户答题状态)。
|
||
|
||
**认证**: JWT
|
||
|
||
**路径参数**:
|
||
- `id`: 章节 ID
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"questionId": "uuid (必填)",
|
||
"selectedAnswer": "string (必填)",
|
||
"timeMs": 1500
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"correct": true,
|
||
"correctAnswer": "B",
|
||
"xpEarned": 10,
|
||
"streakBonus": 0,
|
||
"chapterCompleted": false
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### POST /quiz/rate
|
||
|
||
评价题目质量。
|
||
|
||
**认证**: JWT
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"questionId": "uuid (必填)",
|
||
"rating": "good | bad"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": null,
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 进度
|
||
|
||
#### GET /progress/dashboard
|
||
|
||
获取用户进度概览。
|
||
|
||
**认证**: JWT
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"xpTotal": 150,
|
||
"streakDays": 3,
|
||
"heartsRemaining": 5,
|
||
"dailyXpEarned": 20,
|
||
"dailyXpGoal": 50,
|
||
"categoriesCompleted": 1,
|
||
"totalCategories": 5
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### GET /progress/streak
|
||
|
||
获取连胜信息。
|
||
|
||
**认证**: JWT
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"currentStreak": 3,
|
||
"longestStreak": 7,
|
||
"lastActiveDate": "2026-04-10"
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### GET /progress/hearts
|
||
|
||
获取红心信息。
|
||
|
||
**认证**: JWT
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"remaining": 5,
|
||
"max": 5,
|
||
"nextRestoreAt": "2026-04-10T13:00:00.000Z"
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### POST /progress/hearts/restore
|
||
|
||
恢复红心。
|
||
|
||
**认证**: JWT
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"method": "ad | wait | upgrade"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"remaining": 5,
|
||
"restored": 1
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### GET /progress/chapters
|
||
|
||
获取所有章节进度。
|
||
|
||
**认证**: JWT
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"chapterId": "uuid",
|
||
"title": "第一章",
|
||
"completedQuestions": 3,
|
||
"totalQuestions": 5,
|
||
"passed": false,
|
||
"passedAt": null
|
||
}
|
||
],
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### POST /feedback
|
||
|
||
提交用户反馈。
|
||
|
||
**认证**: JWT
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"content": "string (必填, 1-2000字符)",
|
||
"contact": "string (可选, 最多255字符)",
|
||
"pageContext": "string (可选, 最多200字符)"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": null,
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 游戏化
|
||
|
||
#### GET /leaderboard
|
||
|
||
获取排行榜。
|
||
|
||
**认证**: JWT
|
||
|
||
**查询参数**:
|
||
- `tier`: "free" | "pro" | "proplus" (可选)
|
||
- `page`: 页码 (默认: 1)
|
||
- `limit`: 每页数量 (默认: 20)
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"rank": 15,
|
||
"xpTotal": 1500
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### GET /achievements
|
||
|
||
获取成就列表。
|
||
|
||
**认证**: JWT
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"newlyUnlocked": [
|
||
{
|
||
"id": "uuid",
|
||
"code": "streak_7",
|
||
"name": "连胜达人",
|
||
"description": "连续7天活跃"
|
||
}
|
||
]
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 支付
|
||
|
||
#### POST /payment/verify-huawei
|
||
|
||
验证华为 IAP 收据并激活订阅。
|
||
|
||
**认证**: JWT
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"purchaseToken": "string (必填)",
|
||
"productId": "string (必填)",
|
||
"tier": "pro | proplus"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"tier": "pro",
|
||
"provider": "huawei",
|
||
"active": true,
|
||
"expiresAt": "2026-05-10T00:00:00.000Z"
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### GET /payment/subscription
|
||
|
||
获取当前订阅状态。
|
||
|
||
**认证**: JWT
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
管理员用户名密码登录。
|
||
|
||
**认证**: 无
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"username": "string (必填)",
|
||
"password": "string (必填, 最少8字符)"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"accessToken": "jwt_token (1h有效)",
|
||
"refreshToken": "jwt_token (30d有效)",
|
||
"admin": {
|
||
"id": "uuid",
|
||
"username": "admin",
|
||
"role": "super_admin"
|
||
}
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
**错误 (401)**:
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"error": {
|
||
"code": "UNAUTHORIZED",
|
||
"message": "Invalid username or password"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### POST /admin/auth
|
||
|
||
管理端 Token 认证(向后兼容,推荐使用 `/admin/auth/login`)。
|
||
|
||
**认证**: 无
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"token": "string (必填)"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"authenticated": true
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
**错误 (401)**:
|
||
```json
|
||
{
|
||
"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 (可选,按状态筛选)
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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)**:
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"error": {
|
||
"code": "NOT_FOUND",
|
||
"message": "Admin user not found"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### POST /admin/admins
|
||
|
||
创建新管理员(super_admin 专属)。
|
||
|
||
**认证**: Admin JWT (super_admin)
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"username": "string (必填, 3-50字符)",
|
||
"password": "string (必填, 8-128字符)",
|
||
"role": "admin | super_admin (必填)"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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)**:
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"error": {
|
||
"code": "FORBIDDEN",
|
||
"message": "Super admin privileges required"
|
||
}
|
||
}
|
||
```
|
||
|
||
**错误 (400)**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"username": "string (可选, 3-50字符)",
|
||
"role": "admin | super_admin (可选)",
|
||
"isActive": 0 | 1 (可选)"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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 (可选)
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"stem": { "text": "题目内容" },
|
||
"contentType": "text | image | video | audio",
|
||
"correctAnswer": "B (必填)",
|
||
"distractors": ["A", "C", "D"],
|
||
"categoryId": "uuid (必填)",
|
||
"difficulty": 3,
|
||
"knowledgeCard": {
|
||
"summary": "知识点摘要 (必填)",
|
||
"deepDive": "深入解析",
|
||
"sourceRef": "来源引用"
|
||
}
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"id": "uuid",
|
||
"status": "draft"
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### PUT /admin/questions/:id
|
||
|
||
更新题目。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**路径参数**:
|
||
- `id`: 题目 ID
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": null,
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### PATCH /admin/questions/:id/status
|
||
|
||
变更题目状态(带流转校验)。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**路径参数**:
|
||
- `id`: 题目 ID
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"status": "draft | reviewing | published | archived (必填)"
|
||
}
|
||
```
|
||
|
||
**允许的状态流转**:
|
||
| 当前状态 | 可变更到 |
|
||
|----------|----------|
|
||
| draft | reviewing, archived |
|
||
| reviewing | published, draft, archived |
|
||
| published | archived |
|
||
| archived | draft |
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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)**:
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"error": {
|
||
"code": "NOT_FOUND",
|
||
"message": "题目不存在"
|
||
}
|
||
}
|
||
```
|
||
|
||
**错误 (400)**:
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"error": {
|
||
"code": "INVALID_STATUS_TRANSITION",
|
||
"message": "不允许从 published 变更为 reviewing"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### POST /admin/questions/batch-publish
|
||
|
||
批量发布题目(带状态流转校验,仅 reviewing 状态可发布)。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"ids": ["uuid1", "uuid2"]
|
||
}
|
||
```
|
||
|
||
**参数说明**:
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| ids | string[] | 是 | 题目 ID 数组,1-200 个,每个为合法 UUID |
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"ids": ["uuid1", "uuid2"]
|
||
}
|
||
```
|
||
|
||
**参数说明**:
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| ids | string[] | 是 | 题目 ID 数组,1-200 个,每个为合法 UUID |
|
||
|
||
**响应**: 与 batch-publish 相同的 `BatchResult` 格式。
|
||
|
||
---
|
||
|
||
#### POST /admin/questions/batch-delete
|
||
|
||
批量删除题目(软删除,等同于批量归档)。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"ids": ["uuid1", "uuid2"]
|
||
}
|
||
```
|
||
|
||
**参数说明**:
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| ids | string[] | 是 | 题目 ID 数组,1-200 个,每个为合法 UUID |
|
||
|
||
**响应**: 与 batch-publish 相同的 `BatchResult` 格式。
|
||
|
||
---
|
||
|
||
#### POST /admin/questions/import
|
||
|
||
批量导入题目(JSON 格式,全有或全无策略)。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"questions": [
|
||
{
|
||
"stem": { "text": "题目内容" },
|
||
"contentType": "text | image | video | audio",
|
||
"correctAnswer": "正确答案 (必填)",
|
||
"distractors": ["干扰项1", "干扰项2"],
|
||
"categoryId": "分类ID (必填)",
|
||
"difficulty": 3,
|
||
"knowledgeCard": {
|
||
"summary": "知识点摘要 (必填)",
|
||
"deepDive": "深入解析",
|
||
"sourceRef": "来源引用"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**参数说明**:
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| questions | array | 是 | 题目数组,1-200 条 |
|
||
| questions[].stem | object | 是 | 题干,至少包含 text 字段 |
|
||
| questions[].contentType | string | 是 | 内容类型:text/image/video/audio |
|
||
| questions[].correctAnswer | string | 是 | 正确答案,至少1字符 |
|
||
| questions[].distractors | string[] | 是 | 干扰项,至少2个 |
|
||
| questions[].categoryId | string | 是 | 所属分类 ID |
|
||
| questions[].difficulty | number | 否 | 难度 1-5 |
|
||
| questions[].knowledgeCard | object | 否 | 知识点卡片 |
|
||
| questions[].knowledgeCard.summary | string | 是* | 知识点摘要 |
|
||
| questions[].knowledgeCard.deepDive | string | 否 | 深入解析 |
|
||
| questions[].knowledgeCard.sourceRef | string | 否 | 来源引用 |
|
||
|
||
**成功响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"total": 10,
|
||
"succeeded": 10,
|
||
"ids": ["uuid1", "uuid2", "..."]
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
**校验失败响应 (400)**:
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"error": {
|
||
"code": "VALIDATION_FAILED",
|
||
"message": "部分题目校验失败",
|
||
"details": [
|
||
{ "index": 2, "errors": ["distractors: 必须包含至少2个元素"] },
|
||
{ "index": 5, "errors": ["categoryId: 必填字段"] }
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
**说明**:
|
||
- 导入的题目默认状态为 `draft`
|
||
- 全有或全无:任一条目校验失败则全部不导入
|
||
- 所有条目先校验完毕,再统一报告错误,最后才执行事务插入
|
||
- 校验包含 categoryId 外键存在性检查:不存在的分类 ID 会触发 `VALIDATION_FAILED` 错误
|
||
|
||
---
|
||
|
||
#### POST /admin/questions/import-csv
|
||
|
||
批量导入题目(CSV 格式,全有或全无策略)。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**Content-Type**: `text/plain`
|
||
|
||
**请求体**: CSV 文本,首行为表头
|
||
|
||
**CSV 表头(固定列顺序)**:
|
||
```
|
||
categoryId,contentType,difficulty,stemText,correctAnswer,distractor1,distractor2,distractor3,distractor4,distractor5,cardSummary,cardDeepDive,cardSourceRef
|
||
```
|
||
|
||
**CSV 示例**:
|
||
```csv
|
||
categoryId,contentType,difficulty,stemText,correctAnswer,distractor1,distractor2,distractor3,distractor4,distractor5,cardSummary,cardDeepDive,cardSourceRef
|
||
history,text,1,秦始皇统一六国是在哪一年?,公元前221年,公元前206年,公元前256年,公元前230年,,,秦始皇嬴政于公元前221年完成统一,建立了中国历史上第一个大一统王朝,《史记·秦始皇本纪》
|
||
history,text,2,被称为'诗仙'的唐代诗人是谁?,李白,杜甫,白居易,王维,,李白是唐代最伟大的浪漫主义诗人,,
|
||
```
|
||
|
||
**列说明**:
|
||
| 列名 | 必填 | 说明 |
|
||
|------|------|------|
|
||
| categoryId | 是 | 分类 ID |
|
||
| contentType | 是 | text/image/video/audio |
|
||
| difficulty | 否 | 难度 1-5,留空则不设置 |
|
||
| stemText | 是 | 题目文本 |
|
||
| correctAnswer | 是 | 正确答案 |
|
||
| distractor1-5 | 至少填2个 | 干扰项,留空的列会被忽略 |
|
||
| cardSummary | 否 | 知识点摘要(填则整行知识卡片必填 summary) |
|
||
| cardDeepDive | 否 | 深入解析 |
|
||
| cardSourceRef | 否 | 来源引用 |
|
||
|
||
**成功/失败响应**: 与 JSON 导入相同格式。
|
||
|
||
**说明**:
|
||
- CSV 字段可用双引号包裹,支持字段内逗号和换行
|
||
- 字段内的双引号用 `""` 表示
|
||
- categoryId 外键存在性校验与 JSON 导入一致
|
||
- 单次导入上限 200 条,超出返回 `VALIDATION_ERROR`
|
||
|
||
**额外错误**:
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"error": {
|
||
"code": "CSV_PARSE_ERROR",
|
||
"message": "CSV 表头列数应为 13,实际 10"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### GET /admin/categories
|
||
|
||
获取分类列表。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**查询参数**:
|
||
- `page`: 页码 (默认: 1, 必须 ≥ 1)
|
||
- `limit`: 每页数量 (默认: 20, 范围: 1-50)
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"id": "uuid (必填)",
|
||
"name": "分类名称 (必填)",
|
||
"slug": "分类slug (必填)",
|
||
"parentId": "uuid",
|
||
"sortOrder": 1
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### PUT /admin/categories/:id
|
||
|
||
更新分类。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"summary": "摘要 (必填)",
|
||
"deepDive": "深入解析",
|
||
"sourceRef": "来源引用"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 技能树管理
|
||
|
||
#### GET /admin/skill-tree
|
||
|
||
获取章节列表。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**查询参数**:
|
||
- `categoryId`: 分类 ID (可选)
|
||
|
||
---
|
||
|
||
#### POST /admin/skill-tree
|
||
|
||
创建章节。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"categoryId": "uuid (必填)",
|
||
"title": "章节标题 (必填)",
|
||
"parentId": "uuid",
|
||
"sortOrder": 1,
|
||
"questionsRequired": 5,
|
||
"passThreshold": 3
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### PUT /admin/skill-tree/:id
|
||
|
||
更新章节。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"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)
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"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
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"totalUsers": 1000,
|
||
"activeUsers": 150,
|
||
"totalQuestions": 500,
|
||
"publishedQuestions": 450,
|
||
"averageXp": 200
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 反馈管理
|
||
|
||
#### GET /admin/feedback
|
||
|
||
获取用户反馈列表。
|
||
|
||
**认证**: Admin Token
|
||
|
||
**查询参数**:
|
||
- `page`: 页码 (默认: 1)
|
||
- `limit`: 每页数量 (默认: 20)
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"id": "uuid",
|
||
"userId": "uuid",
|
||
"content": "反馈内容",
|
||
"contact": "联系方式",
|
||
"pageContext": "页面上下文",
|
||
"createdAt": "2026-04-10T10:00:00.000Z"
|
||
}
|
||
],
|
||
"pagination": { ... },
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 附录
|
||
|
||
### 错误代码
|
||
|
||
| 代码 | 说明 |
|
||
|------|------|
|
||
| VALIDATION_ERROR | 请求参数验证失败 |
|
||
| VALIDATION_FAILED | 批量导入中部分题目校验失败 |
|
||
| CSV_PARSE_ERROR | CSV 解析失败(格式或表头不匹配) |
|
||
| UNAUTHORIZED | 未认证或认证失败 |
|
||
| FORBIDDEN | 权限不足(需要 super_admin) |
|
||
| NOT_FOUND | 资源不存在 |
|
||
| INVALID_STATUS_TRANSITION | 题目状态流转不合法 |
|
||
| INVALID_RECEIPT | 支付收据验证失败 |
|
||
| NOT_IMPLEMENTED | 功能未实现 |
|
||
| INTERNAL_ERROR | 服务器内部错误 |
|
||
|
||
### 数据模型
|
||
|
||
#### User (用户)
|
||
```typescript
|
||
{
|
||
id: string; // UUID
|
||
nickname: string | null; // 昵称
|
||
avatarUrl: string | null; // 头像URL
|
||
tier: 'free' | 'pro' | 'proplus'; // 会员等级
|
||
xpTotal: number; // 总经验值
|
||
streakDays: number; // 连续天数
|
||
heartsRemaining: number; // 剩余红心
|
||
banned: boolean; // 是否封禁
|
||
}
|
||
```
|
||
|
||
#### Question (题目)
|
||
```typescript
|
||
{
|
||
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 (章节)
|
||
```typescript
|
||
{
|
||
id: string; // UUID
|
||
categoryId: string; // 分类ID
|
||
title: string; // 章节标题
|
||
parentId: string | null; // 父章节ID
|
||
sortOrder: number; // 排序
|
||
questionsRequired: number; // 需要答题数
|
||
passThreshold: number; // 通过阈值
|
||
}
|
||
```
|