提交 a33c2ebd authored 作者: 王鹏飞's avatar 王鹏飞

docs: align learning architecture with saas-bi

上级 74b69830
# PC 浏览器学习系统与管理系统 Monorepo 设计 # PC 浏览器学习系统与管理系统 Monorepo 设计
日期:2026-07-17 日期:2026-07-17
状态:待用户审阅 状态:已确认,待实施计划
目标分支:`next` 目标分支:`next`
## 1. 背景 ## 1. 背景
...@@ -38,6 +38,9 @@ PC 学习系统使用现有 APP 会员账号。会员表中的 `members.phone` ...@@ -38,6 +38,9 @@ PC 学习系统使用现有 APP 会员账号。会员表中的 `members.phone`
10. 学习端到后台采用一次性票据换取后台登录态,不共享两套系统的 Token。 10. 学习端到后台采用一次性票据换取后台登录态,不共享两套系统的 Token。
11. Jenkins 只建立一个前端 Pipeline Job,内部构建两个应用并组装一个站点 Release。 11. Jenkins 只建立一个前端 Pipeline Job,内部构建两个应用并组装一个站点 Release。
12. `book-app-h5` 不纳入本次方案,PC 阅读能力直接作为 `apps/learning` 的业务模块设计。 12. `book-app-h5` 不纳入本次方案,PC 阅读能力直接作为 `apps/learning` 的业务模块设计。
13. `apps/learning` 参考 `/Users/max/code/saas/saas-bi` 的 TypeScript、模块化路由、API、Query Hook 和 Zustand 分层方式组织。
14. 学习端不引入 Redux;服务端状态统一使用 TanStack React Query,必要的客户端共享状态最多使用 Zustand。
15. 页面中可恢复、可分享的操作状态必须通过 `nuqs` 写入 URL,刷新、前进、后退后可以恢复。
## 3. 目标与非目标 ## 3. 目标与非目标
...@@ -156,15 +159,131 @@ center-book/ ...@@ -156,15 +159,131 @@ center-book/
- React 18 - React 18
- Vite - Vite
- React Router - TypeScript,业务源码使用 `.ts` 和 `.tsx`
- React Router 7
- Axios 请求层 - Axios 请求层
- TanStack Query 管理服务端数据和缓存 - TanStack React Query 管理所有服务端数据、请求生命周期和缓存
- Zustand 管理会话、阅读器 UI 等少量客户端状态 - Zustand 只管理无法放入 URL、也不属于服务端数据的少量客户端共享状态
- `nuqs` 管理页面 URL 查询状态
- 独立的响应式样式与学习端设计系统 - 独立的响应式样式与学习端设计系统
管理端的 Ant Design 组件不作为学习端的默认视觉体系。学习端可以复用成熟的无样式能力,但页面设计应面向阅读和学习场景。 管理端的 Ant Design 组件不作为学习端的默认视觉体系。学习端可以复用成熟的无样式能力,但页面设计应面向阅读和学习场景。
### 6.3 学习端路由 学习端根节点使用与 React Router 7 对应的 `NuqsAdapter`。适配器导入路径必须锁定主版本,例如 `nuqs/adapters/react-router/v7`,不使用会随 `nuqs` 主版本改变含义的通用适配器路径。
### 6.3 参考 `saas-bi` 的代码组织
学习端沿用 `saas-bi` 已验证的分层思路,但修正其中直接使用 `useSearchParams` 和 Zustand 请求服务端数据的做法:
```text
apps/learning/src/
├── api/ # 跨模块基础 API、响应类型
├── components/ # 跨模块通用展示组件
│ └── layout/ # 应用布局
├── hooks/ # 跨模块组合 Hook
├── modules/ # 按业务域组织
│ ├── auth/
│ ├── library/
│ ├── books/
│ ├── courses/
│ ├── bookshelf/
│ ├── reader/
│ ├── notes/
│ ├── discussions/
│ ├── assessment/
│ ├── orders/
│ ├── wallet/
│ └── profile/
├── router/ # 路由聚合、守卫和导航封装
├── stores/ # 少量 Zustand 客户端状态
├── utils/ # Axios、格式化、错误处理
├── App.tsx
└── main.tsx
```
每个业务模块按实际复杂度使用下列结构:
```text
modules/library/
├── api.ts # 纯 HTTP 函数,不包含 React 状态
├── query.ts # queryOptions、useQuery、useMutation
├── query-state.ts # nuqs parser 和页面 URL 状态 Hook
├── routes.tsx # 模块路由
├── types.ts # 业务类型
├── components/ # 模块内部组件
└── views/ # 路由页面
```
路由继续采用 `saas-bi` 的模块化聚合思路:各业务模块导出自己的 `routes.tsx`,根路由只负责聚合、登录守卫、错误边界和默认跳转。不得建立一个持续膨胀的全局路由文件。
API 和 Query 分离:
- `api.ts` 只负责请求参数、HTTP 调用和响应类型。
- `query.ts` 负责 query key、缓存策略、请求启用条件、mutation 和失效策略。
- 页面组件不直接调用 Axios。
- mutation 成功后精确失效相关 query,不使用全局清空缓存。
- 查询函数依赖的分页、筛选、排序和资源 ID 必须进入 query key。
### 6.4 状态来源规则
学习端按以下优先级确定状态归属:
1. **服务端事实**:TanStack React Query。
2. **页面可恢复操作状态**:`nuqs` 和 URL。
3. **跨组件但不可放 URL 的临时客户端状态**:Zustand。
4. **仅组件内部、刷新后无需恢复的瞬时状态**:React `useState`。
禁止把 React Query 已管理的数据再次复制进 Zustand。Zustand Store 不直接请求列表、详情、订单、用户资料等服务端数据。学习端不得新增 Redux、Redux Toolkit 或 Redux Persist。
适合 Zustand 的状态包括:
- 当前会话是否完成初始化,但会员资料本身仍来自 Query。
- 阅读器工具栏展开状态、临时选区和尚未提交的标注交互。
- 全局音频播放器的播放状态。
- 不适合进入 URL 的短期跨组件 UI 协调状态。
### 6.5 URL 状态与 `nuqs`
每个页面必须在开发前定义自己的 URL 状态协议,并在模块的 `query-state.ts` 中集中声明 parser、默认值和序列化规则。禁止在页面组件中零散读取 `window.location.search` 或直接手写 `URLSearchParams`。
必须进入 URL 的状态包括:
- 列表页码、每页数量、排序字段和排序方向。
- 搜索词、分类、标签和其他筛选条件。
- 当前 Tab、视图模式和展开的业务面板。
- 当前打开的详情、笔记、讨论、错题等抽屉或弹窗及其资源 ID。
- 图书 ID、章节 ID、目录锚点和阅读位置标识。
- 订单状态筛选、优惠券筛选和学习报告时间范围。
- 用户刷新后期望恢复的其他页面操作状态。
示例 URL:
```text
/library?q=人工智能&category=12&sort=latest&page=2
/books/100?tab=reviews&reviewPage=3
/reader/100/200?anchor=p-36&panel=notes&noteId=9001
/orders?status=paid&page=2&detail=80001
```
URL 状态更新规则:
- 连续输入、阅读位置和高频筛选使用 history `replace`,避免污染浏览器历史。
- 用户明确切换 Tab、打开详情、翻页等可回退操作使用 history `push`。
- 多字段筛选使用 `useQueryStates` 原子更新,避免中间状态触发多次请求。
- URL parser 必须有类型、默认值和非法值回退策略。
- React Query 的 query key 直接使用经过 parser 标准化的 URL 状态。
- 重置筛选时删除默认值对应的查询参数,保持 URL 简洁。
以下内容禁止进入 URL:
- 登录 Token、后台票据以外的长期凭据;一次性后台 ticket 使用后必须立即从 URL 移除。
- 手机号、支付密钥、二维码内容和其他敏感信息。
- 未提交的笔记正文、讨论正文和大型 JSON 数据。
- 可以从服务端重新取得的完整实体数据。
未提交的表单正文只保存在组件状态或受控的草稿存储中;URL 只记录 `dialog`、`mode` 和实体 ID,使页面结构可以恢复,但不会泄露正文。
### 6.6 学习端路由
第一层路由规划: 第一层路由规划:
...@@ -523,7 +642,10 @@ server { ...@@ -523,7 +642,10 @@ server {
- 原管理端移动到 `apps/admin`。 - 原管理端移动到 `apps/admin`。
- 修正脚本、别名、静态资源和构建输出。 - 修正脚本、别名、静态资源和构建输出。
- 配置 `/admin/` base 和路由 basename。 - 配置 `/admin/` base 和路由 basename。
- 创建空的 `apps/learning` 应用壳。 - 创建 TypeScript `apps/learning` 应用壳。
- 建立 `api`、`components`、`hooks`、`modules`、`router`、`stores` 和 `utils` 分层。
- 配置 TanStack React Query、Zustand 和 React Router 7 对应的 `NuqsAdapter`。
- 建立一个示例模块,验证 `api.ts`、`query.ts`、`query-state.ts`、`routes.tsx` 和 `views` 的边界。
- 配置本地双 Vite 启动和 `/admin` 代理。 - 配置本地双 Vite 启动和 `/admin` 代理。
- 确保管理端迁移前后功能无回归。 - 确保管理端迁移前后功能无回归。
...@@ -533,6 +655,8 @@ server { ...@@ -533,6 +655,8 @@ server {
- `pnpm build:learning` 成功。 - `pnpm build:learning` 成功。
- 本地 `/` 和 `/admin/` 均可热更新。 - 本地 `/` 和 `/admin/` 均可热更新。
- 生产式构建产物可由 Nginx 正确刷新子路由。 - 生产式构建产物可由 Nginx 正确刷新子路由。
- 示例页面的查询、分页、Tab 和弹窗状态写入 URL,刷新与前进后退均能恢复。
- 学习端不包含 Redux 依赖,服务端示例数据不进入 Zustand。
### 阶段 1:Web 认证、应用壳和免密后台 ### 阶段 1:Web 认证、应用壳和免密后台
...@@ -600,6 +724,9 @@ server { ...@@ -600,6 +724,9 @@ server {
- 组件测试:登录表单、入口显示、阅读器工具栏、订单状态。 - 组件测试:登录表单、入口显示、阅读器工具栏、订单状态。
- 集成测试:API 错误、会话过期、票据兑换、支付轮询。 - 集成测试:API 错误、会话过期、票据兑换、支付轮询。
- E2E:登录到图书馆、阅读和记录进度、免密进入后台、扫码支付模拟流程。 - E2E:登录到图书馆、阅读和记录进度、免密进入后台、扫码支付模拟流程。
- URL 状态测试:parser、默认值、非法值回退、多字段原子更新和 history 行为。
- 刷新恢复测试:列表筛选、Tab、弹窗、详情、阅读章节和锚点在刷新后保持一致。
- Query 测试:URL 状态进入 query key,mutation 只失效相关资源缓存。
### 13.2 API ### 13.2 API
...@@ -656,6 +783,8 @@ server { ...@@ -656,6 +783,8 @@ server {
6. 任何登录、票据、权限和支付代码都必须有失败路径测试。 6. 任何登录、票据、权限和支付代码都必须有失败路径测试。
7. 每个阶段结束时提交构建、测试和冒烟验证证据。 7. 每个阶段结束时提交构建、测试和冒烟验证证据。
8. 未经确认不提交远程仓库、不部署生产环境。 8. 未经确认不提交远程仓库、不部署生产环境。
9. `apps/learning` 不得引入 Redux;不得把 React Query 数据复制到 Zustand。
10. 每个页面在实施前必须列出 URL 状态协议,并使用 `nuqs` 实现刷新恢复。
## 17. 完成标准 ## 17. 完成标准
...@@ -673,3 +802,9 @@ server { ...@@ -673,3 +802,9 @@ server {
- Nginx 子路径刷新、API 代理和缓存正确。 - Nginx 子路径刷新、API 代理和缓存正确。
- 安全、回滚和核心 E2E 验证通过。 - 安全、回滚和核心 E2E 验证通过。
## 18. 架构参考
- 本地参考项目:`/Users/max/code/saas/saas-bi`
- `nuqs` React Router Adapter:`https://nuqs.dev/docs/adapters`
- TanStack Query Query Keys:`https://tanstack.com/query/latest/docs/framework/react/guides/query-keys`
- TanStack Query Query Options:`https://tanstack.com/query/latest/docs/framework/react/guides/query-options`
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论