Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-book
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-book
Commits
a33c2ebd
提交
a33c2ebd
authored
7月 17, 2026
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
docs: align learning architecture with saas-bi
上级
74b69830
显示空白字符变更
内嵌
并排
正在显示
1 个修改的文件
包含
141 行增加
和
6 行删除
+141
-6
2026-07-17-pc-learning-monorepo-design.md
...perpowers/specs/2026-07-17-pc-learning-monorepo-design.md
+141
-6
没有找到文件。
docs/superpowers/specs/2026-07-17-pc-learning-monorepo-design.md
浏览文件 @
a33c2ebd
# PC 浏览器学习系统与管理系统 Monorepo 设计
日期:2026-07-17
状态:
待用户审阅
状态:
已确认,待实施计划
目标分支:
`next`
## 1. 背景
...
...
@@ -38,6 +38,9 @@ PC 学习系统使用现有 APP 会员账号。会员表中的 `members.phone`
10.
学习端到后台采用一次性票据换取后台登录态,不共享两套系统的 Token。
11.
Jenkins 只建立一个前端 Pipeline Job,内部构建两个应用并组装一个站点 Release。
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. 目标与非目标
...
...
@@ -156,15 +159,131 @@ center-book/
-
React 18
-
Vite
-
React Router
-
TypeScript,业务源码使用
`.ts`
和
`.tsx`
-
React Router 7
-
Axios 请求层
-
TanStack Query 管理服务端数据和缓存
-
Zustand 管理会话、阅读器 UI 等少量客户端状态
-
TanStack React Query 管理所有服务端数据、请求生命周期和缓存
-
Zustand 只管理无法放入 URL、也不属于服务端数据的少量客户端共享状态
-
`nuqs`
管理页面 URL 查询状态
-
独立的响应式样式与学习端设计系统
管理端的 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¬eId=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 {
-
原管理端移动到
`apps/admin`
。
-
修正脚本、别名、静态资源和构建输出。
-
配置
`/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`
代理。
-
确保管理端迁移前后功能无回归。
...
...
@@ -533,6 +655,8 @@ server {
-
`pnpm build:learning`
成功。
-
本地
`/`
和
`/admin/`
均可热更新。
-
生产式构建产物可由 Nginx 正确刷新子路由。
-
示例页面的查询、分页、Tab 和弹窗状态写入 URL,刷新与前进后退均能恢复。
-
学习端不包含 Redux 依赖,服务端示例数据不进入 Zustand。
### 阶段 1:Web 认证、应用壳和免密后台
...
...
@@ -600,6 +724,9 @@ server {
-
组件测试:登录表单、入口显示、阅读器工具栏、订单状态。
-
集成测试:API 错误、会话过期、票据兑换、支付轮询。
-
E2E:登录到图书馆、阅读和记录进度、免密进入后台、扫码支付模拟流程。
-
URL 状态测试:parser、默认值、非法值回退、多字段原子更新和 history 行为。
-
刷新恢复测试:列表筛选、Tab、弹窗、详情、阅读章节和锚点在刷新后保持一致。
-
Query 测试:URL 状态进入 query key,mutation 只失效相关资源缓存。
### 13.2 API
...
...
@@ -656,6 +783,8 @@ server {
6.
任何登录、票据、权限和支付代码都必须有失败路径测试。
7.
每个阶段结束时提交构建、测试和冒烟验证证据。
8.
未经确认不提交远程仓库、不部署生产环境。
9.
`apps/learning`
不得引入 Redux;不得把 React Query 数据复制到 Zustand。
10.
每个页面在实施前必须列出 URL 状态协议,并使用
`nuqs`
实现刷新恢复。
## 17. 完成标准
...
...
@@ -673,3 +802,9 @@ server {
-
Nginx 子路径刷新、API 代理和缓存正确。
-
安全、回滚和核心 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
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论