1171 lines
20 KiB
Markdown
1171 lines
20 KiB
Markdown
# Duoqi API Reference
|
||
|
||
> 多奇服务端 API 接口文档。本文按当前 Fastify 路由和 TypeScript DTO 更新。
|
||
|
||
## Base URL
|
||
|
||
| 环境 | Base URL |
|
||
|------|----------|
|
||
| 生产 | `https://api.duoqi.me/v1` |
|
||
| 本地开发 | `http://localhost:3000/v1` |
|
||
|
||
健康检查是唯一不带 `/v1` 前缀的客户端端点:`GET /health`。
|
||
|
||
## 通用约定
|
||
|
||
### 认证
|
||
|
||
| 类型 | Header | 适用路径 |
|
||
|------|--------|----------|
|
||
| 无需认证 | - | `/health`, `/v1/auth/guest`, `/v1/auth/huawei`, `/v1/auth/refresh`, `/v1/admin/login` |
|
||
| JWT | `Authorization: Bearer <jwt_token>` | 大多数客户端 API |
|
||
| Admin JWT | `Authorization: Bearer <admin_jwt_token>` | `/v1/admin/*` |
|
||
|
||
### 统一响应
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"error": {
|
||
"code": "VALIDATION_ERROR",
|
||
"message": "Invalid input"
|
||
}
|
||
}
|
||
```
|
||
|
||
分页响应会额外包含:
|
||
|
||
```json
|
||
{
|
||
"pagination": {
|
||
"total": 100,
|
||
"page": 1,
|
||
"limit": 20
|
||
}
|
||
}
|
||
```
|
||
|
||
## 客户端 API
|
||
|
||
### 健康检查
|
||
|
||
#### GET /health
|
||
|
||
认证:无
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"status": "ok",
|
||
"timestamp": "2026-05-05T12:00:00.000Z"
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
### 认证
|
||
|
||
#### POST /auth/guest
|
||
|
||
认证:无
|
||
限流:10 次/分钟
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"deviceId": "device-id"
|
||
}
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"user": {
|
||
"id": "uuid",
|
||
"nickname": null,
|
||
"avatarUrl": null,
|
||
"tier": "free"
|
||
},
|
||
"tokens": {
|
||
"accessToken": "jwt",
|
||
"refreshToken": "jwt"
|
||
}
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
#### POST /auth/huawei
|
||
|
||
认证:无
|
||
限流:10 次/分钟
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"authorizationCode": "authorization-code"
|
||
}
|
||
```
|
||
|
||
响应同 `/auth/guest`。
|
||
|
||
#### POST /auth/refresh
|
||
|
||
认证:无
|
||
限流:10 次/分钟
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"refreshToken": "jwt"
|
||
}
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"accessToken": "jwt",
|
||
"refreshToken": "jwt"
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
#### GET /auth/me
|
||
|
||
认证:JWT
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"id": "uuid",
|
||
"nickname": "用户昵称",
|
||
"avatarUrl": "https://example.com/avatar.png",
|
||
"tier": "free",
|
||
"xpTotal": 150,
|
||
"streakDays": 3,
|
||
"heartsRemaining": 5,
|
||
"dailyXpEarned": 20,
|
||
"dailyXpGoal": 50
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
### Flutter 客户端聚合 API
|
||
|
||
这组接口对应 Flutter 原型使用的主题路线、挑战、资源摘要、商店和订阅合同。
|
||
|
||
#### GET /app/bootstrap
|
||
|
||
认证:JWT
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"user": {
|
||
"id": "uuid",
|
||
"nickname": "知识探险家",
|
||
"avatarUrl": null,
|
||
"tier": "free",
|
||
"level": 1
|
||
},
|
||
"progress": {
|
||
"hearts": 5,
|
||
"maxHearts": 5,
|
||
"nextHeartRestoreAt": null,
|
||
"dailyAttemptsLeft": 5,
|
||
"dailyAttemptsMax": 5,
|
||
"nextAttemptResetAt": "2026-05-06T00:00:00.000Z",
|
||
"xp": 0,
|
||
"level": 1,
|
||
"xpToNextLevel": 400,
|
||
"streakDays": 0,
|
||
"checkInDays": 0,
|
||
"streakProtectedUntil": null,
|
||
"activeTrackId": null,
|
||
"isSubscribed": false
|
||
},
|
||
"tracks": [],
|
||
"shopBenefits": [],
|
||
"shop": {
|
||
"benefits": [],
|
||
"products": [
|
||
{
|
||
"id": "hint-feather",
|
||
"type": "item",
|
||
"itemId": "hint_feather",
|
||
"title": "提示羽毛",
|
||
"description": "答题时排除 1 个错误选项",
|
||
"priceCoins": 80,
|
||
"quantity": 1,
|
||
"enabled": true
|
||
}
|
||
]
|
||
},
|
||
"wallet": {
|
||
"coinsBalance": 260
|
||
},
|
||
"inventory": {
|
||
"items": [
|
||
{
|
||
"itemId": "hint_feather",
|
||
"quantity": 2,
|
||
"activeUntil": null,
|
||
"metadata": null
|
||
}
|
||
]
|
||
},
|
||
"subscription": {
|
||
"status": "none",
|
||
"tier": "free",
|
||
"expiresAt": null,
|
||
"autoRenew": false
|
||
}
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
说明:`shopBenefits` 为兼容旧客户端保留,内容等同于 `shop.benefits`。新客户端应优先读取 `shop.products`、`wallet.coinsBalance` 和 `inventory.items` 来展示金币、背包和可购买商品。
|
||
|
||
#### GET /tracks
|
||
|
||
认证:JWT
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"id": "history",
|
||
"name": "历史",
|
||
"icon": "🏛",
|
||
"progress": 25,
|
||
"nodes": [
|
||
{
|
||
"id": "uuid",
|
||
"title": "第一章",
|
||
"status": "current",
|
||
"reward": "+40 XP",
|
||
"questionCount": 4
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
`nodes[].status` 取值:`done`, `current`, `locked`, `chest`。
|
||
|
||
#### GET /tracks/:trackId
|
||
|
||
认证:JWT
|
||
路径参数:`trackId` 可以是分类 `id` 或 `slug`。
|
||
|
||
响应:单个 `ThemeTrackDto`,不存在时 `data` 为 `null`。
|
||
|
||
#### GET /challenges/next
|
||
|
||
认证:JWT
|
||
|
||
查询参数:
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `trackId` | string | 是 | 分类 `id` 或 `slug` |
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"challengeId": "challenge-session-uuid",
|
||
"trackId": "history",
|
||
"nodeId": "chapter-uuid",
|
||
"totalQuestions": 5,
|
||
"questions": [
|
||
{
|
||
"challengeId": "challenge-session-uuid",
|
||
"trackId": "history",
|
||
"nodeId": "chapter-uuid",
|
||
"question": {
|
||
"id": "question-uuid-1",
|
||
"prompt": "题目文本",
|
||
"options": [
|
||
{ "id": "a", "text": "选项 A" },
|
||
{ "id": "b", "text": "选项 B" },
|
||
{ "id": "c", "text": "选项 C" }
|
||
]
|
||
}
|
||
}
|
||
]
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
服务端会创建挑战组会话并一次返回 5 题,题目选项不包含正确答案标记。题库不足 5 题或没有可用题目时 `data` 为 `null`。
|
||
|
||
#### POST /challenges/answer
|
||
|
||
认证:JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"challengeId": "challenge-session-uuid",
|
||
"questionId": "question-uuid",
|
||
"selectedOptionId": "a",
|
||
"timeMs": 1500,
|
||
"comboCount": 0,
|
||
"submitRequestId": "client-submit-id"
|
||
}
|
||
```
|
||
|
||
`submitRequestId` 可选;未传时服务端会使用 `challengeId + questionId` 作为默认幂等 key。同一挑战组内重复提交同一道题会返回第一次裁决结果,不会重复扣资源或重复发放奖励。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"challengeId": "challenge-session-uuid",
|
||
"answerState": "correct",
|
||
"correctOptionId": "a",
|
||
"xpDelta": 10,
|
||
"progress": {
|
||
"hearts": 5,
|
||
"dailyAttemptsLeft": 5,
|
||
"xp": 10,
|
||
"streakDays": 0
|
||
},
|
||
"knowledgeCard": {
|
||
"id": "card-uuid",
|
||
"title": "知识点摘要",
|
||
"summary": "知识点摘要",
|
||
"fact": "深入解析"
|
||
},
|
||
"rewards": [
|
||
{ "type": "xp", "amount": 10, "title": "+10 XP" }
|
||
]
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
`answerState` 取值:`correct`, `wrong`。
|
||
|
||
#### GET /progress/summary
|
||
|
||
认证:JWT
|
||
|
||
响应:`ProgressSummaryDto`,字段同 `/app/bootstrap` 中的 `progress`。
|
||
|
||
#### PATCH /progress/preferences
|
||
|
||
认证:JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"activeTrackId": "history"
|
||
}
|
||
```
|
||
|
||
响应:更新后的 `ProgressSummaryDto`。
|
||
|
||
#### POST /progress/check-in
|
||
|
||
认证:JWT
|
||
请求:无
|
||
|
||
响应:更新后的 `ProgressSummaryDto`。
|
||
|
||
#### GET /leaderboards
|
||
|
||
认证:JWT
|
||
|
||
查询参数:
|
||
|
||
| 参数 | 类型 | 默认 | 说明 |
|
||
|------|------|------|------|
|
||
| `scope` | `region` 或 `topic` | `region` | 排行榜范围 |
|
||
| `trackId` | string | - | `scope=topic` 时可传 |
|
||
| `page` | number | 1 | 页码 |
|
||
| `limit` | number | 20 | 1-100 |
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"rank": 1,
|
||
"userId": "uuid",
|
||
"displayName": "玩家昵称",
|
||
"avatarUrl": null,
|
||
"xp": 5000,
|
||
"badge": "王者",
|
||
"isMe": false
|
||
}
|
||
],
|
||
"pagination": {
|
||
"total": 100,
|
||
"page": 1,
|
||
"limit": 20
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
#### GET /leaderboards/me
|
||
|
||
认证:JWT
|
||
|
||
查询参数同 `/leaderboards`。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"rank": 15,
|
||
"userId": "uuid",
|
||
"displayName": "我",
|
||
"avatarUrl": null,
|
||
"xp": 1500,
|
||
"badge": "新秀",
|
||
"isMe": true
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
#### GET /shop
|
||
|
||
认证:JWT
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"benefits": [
|
||
{
|
||
"id": "restore-hearts",
|
||
"type": "hearts",
|
||
"title": "恢复满血",
|
||
"description": "血量不足时继续挑战",
|
||
"enabled": true,
|
||
"requiresAd": true
|
||
}
|
||
],
|
||
"products": [
|
||
{
|
||
"id": "hint-feather",
|
||
"type": "item",
|
||
"itemId": "hint_feather",
|
||
"title": "提示羽毛",
|
||
"description": "答题时排除 1 个错误选项",
|
||
"priceCoins": 80,
|
||
"quantity": 1,
|
||
"enabled": true
|
||
}
|
||
]
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
说明:`requiresAd=true` 的权益应通过 `/rewards/ad-recovery/session` 和 `/rewards/ad-recovery/complete` 完成资格检查和结算。
|
||
|
||
可购买商品价格:提示羽毛 80 金币,爱心补给 150 金币,双倍 XP 药水 250 金币,连胜护盾 400 金币,第一版吉祥物装扮 800 金币。
|
||
|
||
#### POST /shop/purchase
|
||
|
||
认证:JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"productId": "hint-feather",
|
||
"clientRequestId": "request-uuid"
|
||
}
|
||
```
|
||
|
||
`productId` 取值:`hint-feather`, `heart-supply`, `double-xp-potion`, `streak-shield`, `mascot-outfit-starter`。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"product": {
|
||
"id": "hint-feather",
|
||
"type": "item",
|
||
"itemId": "hint_feather",
|
||
"title": "提示羽毛",
|
||
"description": "答题时排除 1 个错误选项",
|
||
"priceCoins": 80,
|
||
"quantity": 1,
|
||
"enabled": true
|
||
},
|
||
"coinsSpent": 80,
|
||
"coinsBalance": 220,
|
||
"item": {
|
||
"itemId": "hint_feather",
|
||
"quantity": 3,
|
||
"activeUntil": null,
|
||
"metadata": null
|
||
},
|
||
"rewards": [
|
||
{
|
||
"type": "item",
|
||
"source": "inventory",
|
||
"itemId": "hint_feather",
|
||
"quantity": 1,
|
||
"title": "提示羽毛 x1"
|
||
}
|
||
]
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
购买使用 `clientRequestId` 作为幂等边界;金币不足时返回统一错误格式,`error.code` 为 `VALIDATION_ERROR`。
|
||
|
||
#### POST /inventory/items/use
|
||
|
||
认证:JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"itemId": "hint_feather",
|
||
"clientRequestId": "request-uuid",
|
||
"questionId": "question-uuid"
|
||
}
|
||
```
|
||
|
||
`itemId` 取值:`heart_supply`, `double_xp_potion`, `hint_feather`, `streak_shield`。使用 `hint_feather` 时必须传 `questionId`。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"itemId": "hint_feather",
|
||
"quantityRemaining": 2,
|
||
"effect": {
|
||
"type": "hint",
|
||
"excludedOptions": ["错误选项 A"]
|
||
}
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
效果说明:`heart_supply` 恢复当前用户爱心到上限;`double_xp_potion` 返回 15 分钟有效期 `activeUntil`;`hint_feather` 返回可排除选项;`streak_shield` 返回 `streakProtectedUntil`。`clientRequestId` 用于道具消耗幂等。
|
||
|
||
#### GET /subscription
|
||
|
||
认证:JWT
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"status": "active",
|
||
"tier": "pro",
|
||
"expiresAt": "2026-06-05T00:00:00.000Z",
|
||
"autoRenew": true
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
`status` 取值:`none`, `active`, `expired`, `cancelled`。`tier` 取值:`free`, `pro`, `proplus`。
|
||
|
||
#### POST /subscription/verify
|
||
|
||
认证:JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"platform": "huawei",
|
||
"purchaseToken": "purchase-token",
|
||
"productId": "duoqi_plus_monthly",
|
||
"tier": "pro"
|
||
}
|
||
```
|
||
|
||
响应:更新后的订阅 DTO。当前仅支持 `platform=huawei`,其他平台返回 `UNSUPPORTED_PLATFORM`。
|
||
|
||
### 激励广告恢复
|
||
|
||
#### POST /rewards/ad-recovery/session
|
||
|
||
认证:JWT
|
||
|
||
用途:展示广告前创建恢复会话,服务端返回会话 ID、广告位和资格状态。
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"type": "hearts",
|
||
"clientRequestId": "uuid-from-client",
|
||
"platform": "ios",
|
||
"adProvider": "mock"
|
||
}
|
||
```
|
||
|
||
`type` 取值:`hearts`, `bonusAttempts`, `streakProtection`。
|
||
`platform` 取值:`ios`, `android`, `harmony`, `web`。
|
||
|
||
符合资格响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"sessionId": "uuid",
|
||
"eligible": true,
|
||
"type": "hearts",
|
||
"adPlacementId": "duoqi_restore_hearts_ios",
|
||
"remainingToday": 2,
|
||
"expiresAt": "2026-05-05T12:30:00.000Z"
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
不符合资格响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"sessionId": null,
|
||
"eligible": false,
|
||
"reason": "daily_limit_reached",
|
||
"nextAvailableAt": "2026-05-06T00:00:00.000Z"
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
#### POST /rewards/ad-recovery/complete
|
||
|
||
认证:JWT
|
||
|
||
用途:广告 SDK 返回完整播放后提交凭证,由服务端幂等结算奖励。`mock` provider 用于测试;真实 provider 需要提交 `providerRewardToken`。
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"sessionId": "uuid",
|
||
"clientRequestId": "uuid-from-client",
|
||
"adProvider": "admob",
|
||
"providerRewardToken": "opaque-provider-token",
|
||
"completedAt": "2026-05-05T12:03:00.000Z"
|
||
}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"status": "completed",
|
||
"type": "hearts",
|
||
"reward": {
|
||
"heartsDelta": 3,
|
||
"dailyAttemptsDelta": 0,
|
||
"streakProtectionGranted": false
|
||
},
|
||
"progress": {
|
||
"hearts": 5,
|
||
"maxHearts": 5,
|
||
"dailyAttemptsLeft": 2,
|
||
"dailyAttemptsMax": 5,
|
||
"streakDays": 21,
|
||
"streakProtectedUntil": null
|
||
},
|
||
"limits": {
|
||
"remainingHeartsRecoveriesToday": 2,
|
||
"remainingAttemptRecoveriesToday": 3,
|
||
"nextStreakProtectionAvailableAt": "2026-05-12T00:00:00.000Z"
|
||
}
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
失败响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"status": "failed",
|
||
"reason": "provider_verification_failed",
|
||
"message": "广告未完整播放,未发放奖励。",
|
||
"progress": {
|
||
"hearts": 2,
|
||
"maxHearts": 5,
|
||
"dailyAttemptsLeft": 1,
|
||
"dailyAttemptsMax": 5
|
||
}
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
`reason` 取值:`ad_not_completed`, `provider_verification_failed`, `session_expired`, `daily_limit_reached`, `cooldown_active`, `already_subscribed`, `invalid_type`。
|
||
|
||
## 管理端 API
|
||
|
||
管理端路由统一带 `/v1/admin` 前缀。
|
||
|
||
### 管理端认证
|
||
|
||
#### POST /admin/login
|
||
|
||
认证:无
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"username": "admin",
|
||
"password": "password123"
|
||
}
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"accessToken": "jwt",
|
||
"refreshToken": "jwt",
|
||
"admin": {
|
||
"id": "uuid",
|
||
"username": "admin",
|
||
"role": "super_admin"
|
||
}
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
#### PUT /admin/change-password
|
||
|
||
认证:Admin JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"currentPassword": "old-password",
|
||
"newPassword": "new-password"
|
||
}
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"message": "Password changed successfully"
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
### 管理员管理
|
||
|
||
#### GET /admin/admins
|
||
|
||
认证:Admin JWT
|
||
查询参数:`page`, `limit`, `role`, `isActive`。
|
||
响应:管理员数组 + `pagination`。
|
||
|
||
#### GET /admin/admins/:id
|
||
|
||
认证:Admin JWT
|
||
|
||
#### POST /admin/admins
|
||
|
||
认证:Admin JWT,且 `role=super_admin`
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"username": "newadmin",
|
||
"password": "password123",
|
||
"role": "admin"
|
||
}
|
||
```
|
||
|
||
#### PUT /admin/admins/:id
|
||
|
||
认证:Admin JWT,且 `role=super_admin`
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"username": "updated",
|
||
"role": "admin",
|
||
"isActive": 1
|
||
}
|
||
```
|
||
|
||
#### DELETE /admin/admins/:id
|
||
|
||
认证:Admin JWT,且 `role=super_admin`。软删除管理员。
|
||
|
||
#### POST /admin/admins/:id/reset-password
|
||
|
||
认证:Admin JWT,且 `role=super_admin`。响应包含一次性明文密码 `plainPassword`。
|
||
|
||
### 题目管理
|
||
|
||
#### GET /admin/questions
|
||
|
||
认证:Admin JWT
|
||
|
||
查询参数:`page`, `limit`, `status`, `categoryId`, `keyword`, `difficulty`, `source`, `sortBy`, `sortOrder`。
|
||
`sortBy` 取值:`createdAt`, `updatedAt`, `difficulty`。`sortOrder` 取值:`asc`, `desc`。
|
||
|
||
#### GET /admin/questions/:id
|
||
|
||
认证:Admin JWT
|
||
|
||
#### POST /admin/questions
|
||
|
||
认证:Admin JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"stem": { "text": "题目内容" },
|
||
"contentType": "text",
|
||
"correctAnswer": "正确答案",
|
||
"distractors": ["干扰项1", "干扰项2"],
|
||
"categoryId": "history",
|
||
"difficulty": 3,
|
||
"knowledgeCard": {
|
||
"summary": "知识点摘要",
|
||
"deepDive": "深入解析",
|
||
"sourceRef": "来源"
|
||
}
|
||
}
|
||
```
|
||
|
||
`contentType` 取值:`text`, `image`, `video`, `audio`。
|
||
|
||
#### PUT /admin/questions/:id
|
||
|
||
认证:Admin JWT。请求字段同创建接口,均可选,额外支持 `status`。
|
||
|
||
#### PATCH /admin/questions/:id/status
|
||
|
||
认证:Admin JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"status": "published"
|
||
}
|
||
```
|
||
|
||
`status` 取值:`draft`, `reviewing`, `published`, `archived`。服务会校验状态流转。
|
||
|
||
#### DELETE /admin/questions/:id
|
||
|
||
认证:Admin JWT。归档题目。
|
||
|
||
#### POST /admin/questions/batch-publish
|
||
|
||
认证:Admin JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"ids": ["uuid"]
|
||
}
|
||
```
|
||
|
||
#### POST /admin/questions/batch-archive
|
||
|
||
认证:Admin JWT。请求同 `batch-publish`。
|
||
|
||
#### POST /admin/questions/batch-delete
|
||
|
||
认证:Admin JWT。软删除,等同批量归档。
|
||
|
||
#### POST /admin/questions/import
|
||
|
||
认证:Admin JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"questions": [
|
||
{
|
||
"stem": { "text": "题目内容" },
|
||
"contentType": "text",
|
||
"correctAnswer": "正确答案",
|
||
"distractors": ["干扰项1", "干扰项2"],
|
||
"categoryId": "history",
|
||
"difficulty": 3,
|
||
"knowledgeCard": {
|
||
"summary": "知识点摘要",
|
||
"deepDive": "深入解析",
|
||
"sourceRef": "来源"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
单次 1-200 条,全有或全无。
|
||
|
||
#### POST /admin/questions/import-csv
|
||
|
||
认证:Admin JWT
|
||
`Content-Type`: `text/plain`
|
||
|
||
CSV 表头:
|
||
|
||
```csv
|
||
categoryId,contentType,difficulty,stemText,correctAnswer,distractor1,distractor2,distractor3,distractor4,distractor5,cardSummary,cardDeepDive,cardSourceRef
|
||
```
|
||
|
||
### 分类管理
|
||
|
||
#### GET /admin/categories
|
||
|
||
认证:Admin JWT
|
||
查询参数:`page`, `limit`。
|
||
响应:分类数组 + `pagination`。
|
||
|
||
#### POST /admin/categories
|
||
|
||
认证:Admin JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"id": "history",
|
||
"name": "历史",
|
||
"slug": "history",
|
||
"parentId": null,
|
||
"sortOrder": 1
|
||
}
|
||
```
|
||
|
||
#### PUT /admin/categories/:id
|
||
|
||
认证:Admin JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"name": "历史",
|
||
"slug": "history",
|
||
"parentId": null,
|
||
"sortOrder": 1,
|
||
"status": "active"
|
||
}
|
||
```
|
||
|
||
#### DELETE /admin/categories/:id
|
||
|
||
认证:Admin JWT。归档分类。
|
||
|
||
### 知识点卡片
|
||
|
||
#### GET /admin/knowledge-cards
|
||
|
||
认证:Admin JWT
|
||
查询参数:`page`, `limit`。
|
||
响应:知识点卡片数组 + `pagination`。
|
||
|
||
#### GET /admin/knowledge-cards/by-question/:questionId
|
||
|
||
认证:Admin JWT
|
||
|
||
#### PUT /admin/knowledge-cards/:id
|
||
|
||
认证:Admin JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"summary": "摘要",
|
||
"deepDive": "深入解析",
|
||
"sourceRef": "来源"
|
||
}
|
||
```
|
||
|
||
所有字段均可选,但至少应传一个需要更新的字段。
|
||
|
||
### 技能树管理
|
||
|
||
#### GET /admin/skill-tree
|
||
|
||
认证:Admin JWT
|
||
查询参数:`categoryId`。
|
||
|
||
#### POST /admin/skill-tree
|
||
|
||
认证:Admin JWT
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"categoryId": "history",
|
||
"title": "第一章",
|
||
"parentId": null,
|
||
"sortOrder": 1,
|
||
"questionsRequired": 4,
|
||
"passThreshold": 2
|
||
}
|
||
```
|
||
|
||
#### PUT /admin/skill-tree/:id
|
||
|
||
认证:Admin JWT。请求字段同创建接口,均可选。
|
||
|
||
#### DELETE /admin/skill-tree/:id
|
||
|
||
认证:Admin JWT。
|
||
|
||
### 用户管理
|
||
|
||
#### GET /admin/users
|
||
|
||
认证:Admin JWT
|
||
查询参数:`page`, `limit`, `search`。
|
||
响应:用户数组 + `pagination`。
|
||
|
||
#### GET /admin/users/:id
|
||
|
||
认证:Admin JWT
|
||
|
||
#### PUT /admin/users/:id/ban
|
||
|
||
认证:Admin JWT
|
||
|
||
#### PUT /admin/users/:id/unban
|
||
|
||
认证:Admin JWT
|
||
|
||
### 统计数据
|
||
|
||
#### GET /admin/stats
|
||
|
||
认证:Admin JWT
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"totalUsers": 1000,
|
||
"activeUsers": 150,
|
||
"totalQuestions": 500,
|
||
"publishedQuestions": 450,
|
||
"averageXp": 200
|
||
},
|
||
"error": null
|
||
}
|
||
```
|
||
|
||
### 反馈管理
|
||
|
||
#### GET /admin/feedback
|
||
|
||
认证:Admin JWT
|
||
查询参数:`page`, `limit`。
|
||
响应:反馈数组 + `pagination`。
|
||
|
||
## 错误代码
|
||
|
||
| 代码 | 说明 |
|
||
|------|------|
|
||
| `VALIDATION_ERROR` | 请求参数验证失败 |
|
||
| `VALIDATION_FAILED` | 批量导入中部分题目校验失败 |
|
||
| `CSV_PARSE_ERROR` | CSV 解析失败 |
|
||
| `UNAUTHORIZED` | 未认证或认证失败 |
|
||
| `FORBIDDEN` | 权限不足 |
|
||
| `NOT_FOUND` | 资源不存在 |
|
||
| `INVALID_STATUS_TRANSITION` | 题目状态流转不合法 |
|
||
| `INVALID_RECEIPT` | 支付收据验证失败 |
|
||
| `UNSUPPORTED_PLATFORM` | 订阅平台暂不支持 |
|
||
| `INTERNAL_ERROR` | 服务器内部错误 |
|