feat: protect video playback access

This commit is contained in:
Codex
2026-06-28 23:00:05 +08:00
parent db65295ddc
commit 3647c2bc2d
12 changed files with 581 additions and 24 deletions

View File

@@ -139,7 +139,7 @@ tenant:<tenantId>:theme
| 提交答案 | `POST /api/learning/answers` |
| 错题本 | `GET /api/learning/wrong-questions``POST /api/learning/wrong-questions/resolve` |
| 收藏夹 | `GET/POST /api/learning/favorites/questions` |
| 题目视频 | `GET /api/questions/{questionId}/videos``POST /api/questions/videos/batch` |
| 题目视频 | `GET /api/questions/{questionId}/videos``POST /api/questions/videos/batch``POST /api/videos/play` |
| 背单词 | `/api/catalog/vocabulary-units``/api/catalog/vocabulary-words` |
| 单词进度 | `/api/learning/vocabulary/progress``/api/learning/vocabulary/stats` |
| 单词收藏 | `/api/learning/vocabulary/favorites` |
@@ -152,6 +152,60 @@ tenant:<tenantId>:theme
| 个人中心 | `GET/PATCH /api/profile/me` |
| 销售分享 | `/api/referral/resolve``track-event``bind` |
## 视频播放契约
题目视频分为 `free``svip``video_quota` 三种访问模式。列表接口只用于展示标题、封面、时长、访问模式和试看秒数;除免费公开视频外,列表和搜索接口不会返回可播放 URL。
播放步骤:
1. 进入题目页后调用 `GET /api/questions/{questionId}/videos` 或批量预加载 `POST /api/questions/videos/batch`
2. 用户点击播放时调用 `POST /api/videos/play`
3. 后端校验当前 session 用户、租户、题目绑定关系、SVIP 权益或视频次数权益。
4. 后端返回短期签名 URL、播放 token、权益来源和过期时间。
5. 前端播放器只使用本次返回的 `playback.url`,不要缓存为长期资源地址。
请求示例:
```json
{
"videoId": "00000000-0000-0000-0000-000000000821",
"questionId": "00000000-0000-0000-0000-000000000401"
}
```
响应关键字段:
```json
{
"item": {
"id": "...",
"title": "...",
"accessMode": "svip",
"freePreviewSeconds": 15
},
"playToken": "vp_...",
"playback": {
"url": "https://...",
"expiresAt": "2026-06-28T12:00:00.000Z",
"signatureMode": "signed"
},
"access": {
"mode": "svip",
"entitlementId": "...",
"quotaAccountId": null,
"consumedQuota": 0
}
}
```
前端处理规则:
- `VIDEO_SVIP_REQUIRED`:弹出开通或升级会员。
- `VIDEO_QUOTA_REQUIRED`:提示购买视频次数包或套餐。
- `VIDEO_ASSET_REQUIRED`:展示“视频暂不可播放”,同时上报前端日志。
- 签名 URL 过期后必须重新调用 `/api/videos/play`,不要重试旧 URL。
- 小程序/H5 不保存对象存储真实 key不把播放 URL 写入本地持久缓存。
## 题库新模型接入方式
旧项目常按“地区 -> 科目 -> 章节/试卷”固定层级处理。新项目不要写死层级,按下面模型渲染: