feat: enforce practice access controls

This commit is contained in:
Codex
2026-06-28 23:23:02 +08:00
parent 3647c2bc2d
commit 6a7294224d
12 changed files with 1047 additions and 110 deletions

View File

@@ -52,11 +52,11 @@
| 任意深度分类树 | 可联调 | `/api/catalog/content-nodes` |
| 题目列表/集合 | 可联调 | `/api/catalog/question-collections``question-collections/questions` |
| 顺序/随机/全真模拟规则 | 可联调 | `/api/catalog/practice-blueprints` |
| 创建练习 session | 可联调 | `POST /api/learning/practice-sessions` |
| 答题记录 | 可联调 | `POST /api/learning/answers` |
| 创建练习 session | 可联调 | `POST /api/learning/practice-sessions`后端强制校验免费额度、SVIP 范围和内容访问规则 |
| 答题记录 | 可联调 | `POST /api/learning/answers`;题目必须属于本人有效 session 快照 |
| 错题本 | 可联调 | `/api/learning/wrong-questions` |
| 收藏夹 | 可联调 | `/api/learning/favorites/questions` |
| 免费用户题量限制 | 待补齐 | 旧项目有保护逻辑,新后端需按租户/套餐/内容范围实现 |
| 免费用户题量限制 | 可联调 | `practice_daily_usage` + `practice_access_events`;支持内容 accessRules、每日额度、session 截断、SVIP-only 拦截 |
| 模考交卷报告 | 待补齐 | 已有 session/answer 基础,缺完整交卷、评分报告、错题解析汇总 |
## 背单词、知识手册、分数线、视频

View File

@@ -17,6 +17,7 @@
- `tenant-admin`:租户资料、品牌、公开设置、域名、支付账户、登录 provider、私密密钥掩码、活动内容、激活码批次、优惠券、成员管理、权限矩阵、审计查询。
- `tenant-content`:租户后台内容入口、任意深度分类树、考试意向标记、题目集合、练习蓝图、题目、视频、分数线、单词、知识手册、资料资源、题目/单词/知识手册 JSON 导入维护。
- `tenant`:域名/租户解析。
- `learning` 已接入商用访问控制免费用户每日题量、SVIP 范围、SVIP-only 内容、答题 session 快照保护由后端强制执行。
- `src/services/supabaseApi.ts` 已加入新 API 客户端方法,供旧 Web 逐步替换和后续 Taro 复用。
- 已新增 `npm run db:smoke-seed`,用于 `supabase:reset` 后恢复最小烟测数据。
- 已新增 `npm run smoke:core-api`,用于验证个人中心、分数线、题目视频、背单词进度/收藏等学生端核心 API。
@@ -178,6 +179,7 @@ GET /api/tenant-admin/audit-logs
- CRM 当前完成配置、密钥入私密表、客资入队和队列查询;真实 webhook 发送、重试、签名在后续 `apps/worker` 中实现。
- 内容资源当前完成台账、租户后台维护、学生端 SVIP 下载权限,以及 `local_dev`、阿里云 OSS、腾讯 COS、Supabase Storage 的上传/下载签名 provider。真实对象存在性校验、PDF 预览渲染、防盗链、水印和大文件上传后 worker 校验仍需继续补。
- 题库内容导航当前以 `content_entries/content_nodes` 为主模型,可表达“入口 -> 多级分类 -> 院校/专业/学科/销售意向标记”;题目集合和练习方式由 `question_collections/practice_blueprints` 管理,练习 session 会保存当次题目 ID 快照。
- 练习访问控制由 `content_entries/content_nodes/question_collections/practice_blueprints``accessRules` 合并决定;普通用户消耗 `practice_daily_usage`,事件写入 `practice_access_events`SVIP/staff 不消耗免费额度。
- 批量导入当前支持题目、单词、知识手册 JSON 预览、逐行 issue、job/item 台账、执行导入、幂等跳过,并可落到新内容入口和分类节点。旧单词模板的 `vocabulary_units_示例数据` / `vocabulary_示例数据`、知识手册的书籍/章节/小节/知识点嵌套结构都由后端规范化。Excel/CSV、分数线/视频导入会继续复用同一套 `content_import_jobs` 管线。
## 下一步

View File

@@ -20,8 +20,8 @@
| 首页/学生看板 | `pages/StudentDashboardNew.tsx` | 部分覆盖 | 品牌、Banner、公告、入口、个人统计有基础缺完整运营动态/学习任务聚合 |
| 题库入口 | `pages/SubjectSelector.tsx``RegionArchitectureEditor.tsx` | 已覆盖 | 前端应改接 `content_entries/content_nodes` |
| 多级分类树 | 旧 module/subject/category 树 | 已覆盖 | 新后端支持任意深度和 `marker_type`;前端不要写死层级 |
| 顺序刷题 | `pages/Quiz.tsx` | 已覆盖 | 继续补题量限制、断点续练、更多题型渲染 |
| 随机刷题 | `pages/Quiz.tsx` | 已覆盖 | 已有 blueprint/session 快照,前端需按 mode 调用 |
| 顺序刷题 | `pages/Quiz.tsx` | 已覆盖 | 免费额度/SVIP 校验已由后端强制;继续补断点续练、更多题型渲染 |
| 随机刷题 | `pages/Quiz.tsx` | 已覆盖 | 已有 blueprint/session 快照和访问控制,前端需按 mode 调用 |
| 全真模拟 | `components/AdminMockexam``MockExamConfigModal.tsx` | 部分覆盖 | 后端有 blueprint 基础;缺完整交卷报告、排名、复盘 |
| 错题本 | 用户 stats/错题逻辑 | 已覆盖 | 后续补错题复习计划 |
| 收藏夹 | `WordFavoritesPage.tsx`、题目收藏 | 已覆盖 | 题目和单词收藏已有 |
@@ -49,7 +49,7 @@
| 商户收款配置 | 部分覆盖 | 配置 API 有;缺真实支付 provider 和验签 |
| 登录配置 | 部分覆盖 | 配置 API 有;缺真实短信/OAuth provider 实现 |
| Banner/公告/FAQ/活动 | 已覆盖 | 前端运营后台可以接 |
| SVIP 套餐 | 覆盖 | 后续补地区/分类/专业增项限制规则 |
| SVIP 套餐 | 部分覆盖 | 地区/科目/题库范围校验已接入练习/资料/视频;后续补分类/专业增项购买和套餐规则 UI |
| 优惠券 | 部分覆盖 | 后台配置有;前台兑换、下单抵扣待补 |
| 激活码 | 已覆盖 | 批次、生成、兑换主链路已有 |
| 勋章管理 | 部分覆盖 | 表结构有 badges/user_badges缺后台和学生端 API |

View File

@@ -14,6 +14,7 @@
- 销售/代理/CRM 增长链路邀请码、扫码事件、首绑保护、团队、统计、CRM 队列。
- 内容导航:`content_entries/content_nodes` 支持任意深度入口和分类。
- 练习组卷:`question_collections/practice_blueprints` 支持顺序、随机、全真模拟快照。
- 练习访问控制:`practice_daily_usage/practice_access_events` 支持免费每日额度、SVIP 范围校验、SVIP-only 内容拦截和答题 session 快照保护。
- 内容导入:题目、单词、知识手册 JSON 预览、校验、导入、幂等、审计。
- 本地验证:`npm run check:refactor` 已通过。
@@ -76,7 +77,8 @@
- 单题视频和通用知识视频混合推荐。
6. 学习统计
- 练习历史、正确率趋势、题型分布、错题复习计划
- 已完成免费额度和练习访问事件基础
- 继续补练习历史、正确率趋势、题型分布、错题复习计划。
- 单词复习算法、每日计划、排行榜。
- 模考交卷、评分报告、错题解析汇总。

View File

@@ -152,6 +152,51 @@ tenant:<tenantId>:theme
| 个人中心 | `GET/PATCH /api/profile/me` |
| 销售分享 | `/api/referral/resolve``track-event``bind` |
## 练习访问控制契约
前端不要先拉完整题目列表再自行判断免费额度。用户点击顺序刷题、随机刷题、全真模拟时,统一调用 `POST /api/learning/practice-sessions`,后端会根据 `content_entries.accessRules``content_nodes.accessRules``question_collections.accessRules``practice_blueprints.accessRules` 和当前用户权益决定最终题目快照。
请求示例:
```json
{
"mode": "sequential",
"collectionId": "00000000-0000-0000-0000-000000000615",
"questionLimit": 50
}
```
响应关键字段:
```json
{
"item": {
"id": "...",
"mode": "sequential",
"questionIds": ["..."],
"questionCount": 25,
"accessMode": "free",
"consumedFreeQuota": 25,
"accessSnapshot": {
"grantedBy": "free_quota",
"requestedCount": 50,
"grantedCount": 25,
"truncated": true,
"dailyLimit": 25
}
}
}
```
前端处理规则:
- 以返回的 `questionIds` 为准渲染本次练习,不要自行追加题目。
- `accessSnapshot.truncated=true` 时,可提示“今日免费额度有限,已为你开放 N 题”并引导开通 SVIP。
- `PRACTICE_FREE_LIMIT_REACHED`:弹出会员购买/激活码兑换入口。
- `PRACTICE_SVIP_REQUIRED`:提示该内容需要对应地区/科目/题库 SVIP。
- `PRACTICE_SESSION_QUESTION_FORBIDDEN`:说明提交答案的题目不在本次 session 快照内,应清理本地异常进度并重新开始。
- 提交答案必须传 `practiceSessionId`;后端会拒绝不属于本人有效 session 的题目。
## 视频播放契约
题目视频分为 `free``svip``video_quota` 三种访问模式。列表接口只用于展示标题、封面、时长、访问模式和试看秒数;除免费公开视频外,列表和搜索接口不会返回可播放 URL。