Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-book
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-book
Commits
74b69830
提交
74b69830
authored
7月 17, 2026
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
docs: add PC learning monorepo design
上级
dc2c8913
隐藏空白字符变更
内嵌
并排
正在显示
1 个修改的文件
包含
675 行增加
和
0 行删除
+675
-0
2026-07-17-pc-learning-monorepo-design.md
...perpowers/specs/2026-07-17-pc-learning-monorepo-design.md
+675
-0
没有找到文件。
docs/superpowers/specs/2026-07-17-pc-learning-monorepo-design.md
0 → 100644
浏览文件 @
74b69830
# PC 浏览器学习系统与管理系统 Monorepo 设计
日期:2026-07-17
状态:待用户审阅
目标分支:
`next`
## 1. 背景
数字教材当前包含两套业务系统:
-
管理系统前端:
`center-book`
-
管理系统 API:
`com-ebook-pc-api`
-
学习系统 APP:
`book-app`
-
学习系统 API:
`com-ebook-app-api`
目前学员只能通过 Flutter APP 学习。项目需要新增 PC 浏览器学习系统,并统一部署到:
-
PC 学习系统:
`https://zijingebook.ezijing.com/`
-
后台管理系统:
`https://zijingebook.ezijing.com/admin/`
PC 学习系统使用现有 APP 会员账号。会员表中的
`members.phone`
与后台账号表中的
`admin_user.tel`
相同时,视为同一个自然人;手机号不同则视为两个独立用户。
学员登录后默认进入图书馆。若当前手机号存在有效后台账号,则在页面右上角显示“进入后台管理”入口。点击后无须再次输入后台密码,但后台仍须独立校验账号状态和角色权限。
本项目由单人配合 AI 长期维护,因此采用前端 Monorepo,以减少跨仓库上下文切换和联动版本管理成本。
## 2. 已确认决策
1.
保留现有 Git 仓库名
`center-book`
,在
`next`
分支将其改造成 Monorepo。
2.
原管理系统移动到
`apps/admin`
。
3.
新 PC 学习系统放在
`apps/learning`
。
4.
管理端和学习端是两个独立应用,不合并路由、状态、权限和构建产物。
5.
两个应用统一使用 React 18、Vite 和 pnpm workspace。
6.
PC 学习端功能范围以完整覆盖现有 APP 线上业务为目标。
7.
PC 支付使用微信或支付宝扫码支付,不实现 iOS 内购。
8.
PC 学习端复用
`com-ebook-app-api`
的业务能力,但使用浏览器专用路由和鉴权方式。
9.
后台继续使用
`com-ebook-pc-api`
,不与学习 API 合并。
10.
学习端到后台采用一次性票据换取后台登录态,不共享两套系统的 Token。
11.
Jenkins 只建立一个前端 Pipeline Job,内部构建两个应用并组装一个站点 Release。
12.
`book-app-h5`
不纳入本次方案,PC 阅读能力直接作为
`apps/learning`
的业务模块设计。
## 3. 目标与非目标
### 3.1 目标
-
用户可以使用现有 APP 会员账号在 PC 浏览器登录。
-
登录成功后默认进入图书馆。
-
PC 端覆盖 APP 的核心浏览、学习、互动、个人中心和交易功能。
-
学习记录、阅读进度、笔记、讨论、错题和订单等数据与 APP 共用。
-
有后台账号的用户可以从学习端无感进入
`/admin/`
。
-
管理端与学习端可以独立开发、构建、测试,同时由一个 Jenkins Pipeline 统一发布。
-
本地开发和生产环境均通过
`/`
与
`/admin/`
区分两个前端。
-
所有浏览器端密钥、会话和支付流程符合 Web 安全边界。
### 3.2 非目标
-
不合并
`members`
与
`admin_user`
两张账号表。
-
不让学习端 Token 直接获得后台权限。
-
不把管理端和学习端做成一个 React SPA。
-
不在 PC 端实现 APP 的原生离线下载、SQLite 离线队列和系统级防截屏。
-
不在 PC 端实现 iOS 内购。
-
不在第一阶段抽取大规模公共 UI 组件库。
-
不改造
`book-app-h5`
。
## 4. 总体架构
```
text
浏览器
|
v
https://zijingebook.ezijing.com
|
v
Nginx / Ingress
|-- / -> apps/learning 构建产物
|-- /admin/ -> apps/admin 构建产物
|-- /api/web/ -> com-ebook-app-api Web 路由
`-- /api/admin/ -> com-ebook-pc-api
com-ebook-app-api ---- MySQL / Redis / OSS ---- com-ebook-pc-api
```
Monorepo只统一前端源码管理。两个 PHP API 服务继续保持独立仓库、独立容器和独立发布流程。
## 5. Monorepo 目录设计
```
text
center-book/
├── apps/
│ ├── admin/ # 原 center-book 管理端
│ │ ├── public/
│ │ ├── src/
│ │ ├── package.json
│ │ └── vite.config.js
│ └── learning/ # 新 PC 学习端
│ ├── public/
│ ├── src/
│ ├── package.json
│ └── vite.config.js
├── packages/
│ └── config/ # ESLint 等纯工程配置
├── docs/
│ ├── architecture/
│ ├── deployment/
│ └── superpowers/specs/
├── dist/
│ ├── admin/ # apps/admin 构建输出
│ └── learning/ # apps/learning 构建输出
├── AGENTS.md
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
```
### 5.1 应用边界
`apps/admin`
独立拥有:
-
管理端路由
-
后台 RBAC 权限
-
后台登录态
-
管理端 API 客户端
-
Redux、Zustand 等现有状态
-
Ant Design 管理界面
`apps/learning`
独立拥有:
-
学习端路由
-
会员登录态
-
学习端 API 客户端
-
学习端页面与状态
-
阅读器状态
-
购物、订单和扫码支付状态
两个应用禁止直接引用对方的
`src`
。需要共享的内容必须先证明存在稳定的第二个复用点,再移动到
`packages`
。第一阶段只共享工程配置,不强行共享业务组件。
## 6. 前端技术设计
### 6.1 管理端
管理端尽量保持现状,只进行 Monorepo 迁移所必需的修改:
-
源码移动到
`apps/admin`
-
Vite
`base`
改为
`/admin/`
-
React Router
`basename`
改为
`/admin`
-
API 前缀从
`/api/*`
统一为
`/api/admin/*`
-
构建输出改为仓库根目录
`dist/admin`
-
修复硬编码的根路径静态资源
-
新增
`/admin/sso`
免密票据兑换页面
不得在 Monorepo 迁移阶段顺带重构现有后台业务页面。
### 6.2 学习端
学习端采用:
-
React 18
-
Vite
-
React Router
-
Axios 请求层
-
TanStack Query 管理服务端数据和缓存
-
Zustand 管理会话、阅读器 UI 等少量客户端状态
-
独立的响应式样式与学习端设计系统
管理端的 Ant Design 组件不作为学习端的默认视觉体系。学习端可以复用成熟的无样式能力,但页面设计应面向阅读和学习场景。
### 6.3 学习端路由
第一层路由规划:
```
text
/login 登录
/library 图书馆,登录后的默认页
/library/search 图书搜索
/books/:bookId 图书详情
/courses 我的课程
/bookshelf 书架
/reader/:bookId 阅读器
/reader/:bookId/:chapterId 指定章节阅读
/notes 我的笔记
/discussions 我的讨论
/wrong-questions 我的错题
/study-report 学习报告
/messages 消息中心
/orders 订单
/coupons 优惠券
/wallet 积分和紫荆币
/profile 个人资料
/settings 设置
```
未登录访问受保护页面时跳转到
`/login`
。登录成功且没有指定回跳地址时进入
`/library`
。
## 7. APP 功能覆盖与 Web 适配
### 7.1 直接复用业务数据的功能
-
手机号密码登录
-
短信验证码登录
-
图书馆分类、标签、列表和搜索
-
图书详情、目录和评价
-
我的课程
-
书架
-
收藏
-
阅读记录和章节进度
-
学习时长
-
笔记、高亮和划线
-
讨论、回复和点赞
-
章节测评、答案和错题
-
学习报告
-
消息中心
-
个人资料
-
优惠券、积分和紫荆币记录
-
订单和评价
### 7.2 必须按 Web 重做的能力
| APP 能力 | PC Web 方案 |
|---|---|
| Flutter 页面 | React 响应式页面 |
| Flutter WebView 阅读器 |
`apps/learning`
内的浏览器阅读模块 |
| 微信、支付宝原生 SDK | 微信、支付宝扫码支付 |
| iOS 内购 | 不实现 |
| APP 本地 SQLite | 服务端数据加浏览器短期缓存 |
| 离线图书下载 | 第一版不实现 |
| 网络恢复后离线同步 | 第一版不实现 |
| 系统级防截屏 | Web 端使用水印、资源鉴权和风控替代 |
| APP 更新检查 | Web 静态资源版本和缓存更新 |
### 7.3 阅读器边界
PC 阅读器属于
`apps/learning`
,至少支持:
-
章节目录切换
-
富文本、图片、音频、公式和扩展内容展示
-
阅读位置和章节进度保存
-
高亮、划线和笔记
-
讨论和互动
-
章节测评
-
全文搜索
-
字号、行距、主题和内容宽度调整
-
键盘和鼠标操作
-
浏览器刷新后的阅读位置恢复
阅读内容的授权检查必须在服务端完成,前端隐藏入口不能替代资源权限校验。
## 8. Web API 与安全边界
现有 APP API 使用
`appId`
、
`appSecret`
和签名。
`appSecret`
不能进入浏览器构建产物,因此 PC 学习端不能原样直连 APP 路由。
`com-ebook-app-api`
增加
`/web`
路由组,外部由 Nginx 暴露为
`/api/web`
。Web Controller 可以复用已有 Event、Service 和 Model,但使用浏览器专用中间件。
### 8.1 外部接口前缀
```
text
/api/web/* PC 学习端
/api/admin/* 后台管理端
```
Nginx 内部改写:
```
text
/api/web/auth/login -> com-ebook-app-api:/web/auth/login
/api/admin/user/login -> com-ebook-pc-api:/user/login
```
### 8.2 学习端会话
PC 学习端使用服务端会话和 HttpOnly Cookie:
```
text
Cookie:ebook_web_session
Path:/api/web
HttpOnly:true
Secure:生产环境 true
SameSite:Lax
```
浏览器不保存
`appSecret`
,也不负责生成可信请求签名。服务端会话映射到会员编号,并沿用现有 token 生命周期和刷新能力。
写操作需要进行 CSRF 防护。登录、短信验证码和支付接口需要增加频率限制和审计日志。
### 8.3 Web 接口最小集合
认证接口:
```
text
POST /api/web/auth/login
POST /api/web/auth/send-code
POST /api/web/auth/logout
POST /api/web/auth/refresh
GET /api/web/auth/context
POST /api/web/auth/admin-ticket
```
业务接口以
`/api/web/v1/*`
暴露。Controller 应复用现有业务层,避免复制 APP Controller 中的数据库逻辑。
## 9. 学习端到后台的免密登录
### 9.1 入口显示条件
`GET /api/web/auth/context`
返回:
```
json
{
"is_login"
:
true
,
"has_admin_access"
:
true
}
```
`has_admin_access`
的服务端判断条件:
1.
当前会员存在有效手机号。
2.
存在
`admin_user.tel = members.phone`
的后台账号。
3.
后台账号未删除、未停用、未过期。
4.
后台账号关联的角色仍然有效。
前端仅根据该字段显示入口;后台最终权限以票据兑换时的再次校验为准。
### 9.2 一次性票据流程
```
text
用户点击“进入后台管理”
-> POST /api/web/auth/admin-ticket
-> 学习 API 再次检查同手机号后台账号
-> Redis 保存随机 ticket,TTL 60 秒
-> 浏览器跳转 /admin/sso?ticket=<opaque-ticket>
-> 管理端调用 POST /api/admin/user/exchangeWebTicket
-> 后台 API 原子读取并删除 ticket
-> 后台 API 再次校验 admin_user 和角色
-> 后台 API 生成自己的后台 Token
-> 管理端沿用现有登录态存储并进入后台首页
-> history.replaceState 移除 URL 中的 ticket
```
票据要求:
-
使用密码学安全的随机值,不使用包含用户信息的明文 JWT。
-
有效期 60 秒。
-
只能消费一次。
-
Redis 消费必须原子化。
-
绑定会员编号、手机号、目标系统和签发时间。
-
不在 URL 中传递学习 Token 或后台 Token。
-
兑换失败后回到后台无权限提示页,不要求用户输入后台密码。
### 9.3 与现有后台 SSO 的关系
现有
`checkSsoLogin`
可继续保留,服务于既有外部 SSO。新增
`exchangeWebTicket`
是学习系统进入后台的专用通道,两者最终复用同一套后台账号状态检查、权限读取和 Token 签发逻辑。
## 10. 本地开发
本地启动两个 Vite 服务:
```
text
apps/learning -> 127.0.0.1:5173
apps/admin -> 127.0.0.1:5174
```
学习端 Vite 作为日常本地入口:
```
text
http://localhost:5173/ -> 学习端
http://localhost:5173/admin/ -> 代理到管理端 Vite
```
代理关系:
```
text
/admin -> http://127.0.0.1:5174,保留路径并启用 WebSocket
/api/web -> http://127.0.0.1:7421,改写为 /web
/api/admin -> http://127.0.0.1:7419,移除 /api/admin 前缀
```
本地普通页面开发可以使用 HTTP 并在开发环境关闭 Cookie
`Secure`
。验证完整 Cookie 和免密跳转流程时,使用本地 HTTPS 域名
`https://dev.ezijing.com`
和本地 Nginx 网关。
## 11. 构建与部署
### 11.1 构建输出
```
text
apps/learning -> dist/learning
apps/admin -> dist/admin
```
学习端 Vite:
```
text
base: /
outDir: ../../dist/learning
```
管理端 Vite:
```
text
base: /admin/
outDir: ../../dist/admin
```
### 11.2 Jenkins
建立一个 Jenkins Pipeline Job,例如
`center-book-web`
。第一阶段每次同时构建两个应用,以降低产物拼装和版本不一致的复杂度。
```
text
Checkout
-> pnpm install --frozen-lockfile
-> 并行 build:learning 和 build:admin
-> 组装 Release
-> 切换 current 软链接
-> nginx -t 和 reload
-> 学习端、后台端、免密跳转冒烟测试
```
发布目录:
```
text
/srv/zijingebook/releases/<BUILD_NUMBER>/
├── index.html # dist/learning/index.html
├── assets/ # dist/learning/assets
└── admin/
├── index.html # dist/admin/index.html
├── assets/
└── formula-editor/
```
当前版本:
```
text
/srv/zijingebook/current -> /srv/zijingebook/releases/<BUILD_NUMBER>
```
回滚通过将
`current`
原子切换到上一个成功 Release 完成。
### 11.3 Nginx 路由
路由优先级:
```
text
/api/web/ -> com-ebook-app-api:7421
/api/admin/ -> com-ebook-pc-api:7419
/admin/ -> current/admin
/assets/ -> current/assets
/ -> current/index.html SPA fallback
```
核心配置:
```
nginx
upstream
ebook_learning_api
{
server
127.0.0.1
:
7421
;
keepalive
32
;
}
upstream
ebook_admin_api
{
server
127.0.0.1
:
7419
;
keepalive
16
;
}
server
{
listen
443
ssl
;
http2
on
;
server_name
zijingebook.ezijing.com
;
root
/srv/zijingebook/current
;
index
index.html
;
location
^~
/api/web/
{
rewrite
^/api/web/(.*)
$
/web/
$1
break
;
proxy_pass
http://ebook_learning_api
;
proxy_http_version
1
.1
;
proxy_set_header
Host
$host
;
proxy_set_header
X-Real-IP
$remote_addr
;
proxy_set_header
X-Forwarded-For
$proxy_add_x_forwarded_for
;
proxy_set_header
X-Forwarded-Proto
$scheme
;
proxy_buffering
off
;
}
location
^~
/api/admin/
{
rewrite
^/api/admin/(.*)
$
/
$1
break
;
proxy_pass
http://ebook_admin_api
;
proxy_http_version
1
.1
;
proxy_set_header
Host
$host
;
proxy_set_header
X-Real-IP
$remote_addr
;
proxy_set_header
X-Forwarded-For
$proxy_add_x_forwarded_for
;
proxy_set_header
X-Forwarded-Proto
$scheme
;
proxy_buffering
off
;
}
location
=
/admin
{
return
301
/admin/
;
}
location
^~
/admin/assets/
{
try_files
$uri
=
404
;
expires
1y
;
add_header
Cache-Control
"public,
immutable"
;
}
location
^~
/admin/
{
try_files
$uri
$uri
/
/admin/index.html
;
}
location
^~
/assets/
{
try_files
$uri
=
404
;
expires
1y
;
add_header
Cache-Control
"public,
immutable"
;
}
location
/
{
try_files
$uri
$uri
/
/index.html
;
}
}
```
证书路径、安全响应头、上传大小、日志、WebSocket 和生产超时由运维配置在完整站点文件中补齐。
## 12. 分阶段实施
完整复刻 APP 不应作为一次无检查的大提交实施。按以下工作包执行,每个阶段完成后必须构建、测试和人工验收。
### 阶段 0:Monorepo 基础迁移
-
引入 pnpm workspace。
-
原管理端移动到
`apps/admin`
。
-
修正脚本、别名、静态资源和构建输出。
-
配置
`/admin/`
base 和路由 basename。
-
创建空的
`apps/learning`
应用壳。
-
配置本地双 Vite 启动和
`/admin`
代理。
-
确保管理端迁移前后功能无回归。
验收:
-
`pnpm build:admin`
成功。
-
`pnpm build:learning`
成功。
-
本地
`/`
和
`/admin/`
均可热更新。
-
生产式构建产物可由 Nginx 正确刷新子路由。
### 阶段 1:Web 认证、应用壳和免密后台
-
`com-ebook-app-api`
增加 Web 会话和认证接口。
-
学习端完成登录、全局布局、图书馆默认页。
-
实现
`has_admin_access`
。
-
两个 API 服务实现一次性票据签发和兑换。
-
管理端新增
`/admin/sso`
。
验收:
-
现有会员可以登录 PC。
-
登录后默认进入图书馆。
-
无后台账号时不显示入口。
-
有效后台账号可免密进入后台。
-
停用、过期或角色无效的后台账号无法兑换。
-
ticket 过期、重放和篡改均失败。
### 阶段 2:浏览与学习核心闭环
-
图书馆、搜索、详情、目录。
-
我的课程、书架和收藏。
-
PC 阅读器。
-
阅读记录、时长和进度。
-
笔记、高亮、划线、讨论和互动。
-
章节测评、结果与错题。
验收:
-
APP 与 PC 的学习数据双向可见。
-
刷新页面后阅读位置和未保存状态处理正确。
-
无购买或无权限用户无法读取受保护内容。
-
主要页面支持常见 PC 分辨率和键盘操作。
### 阶段 3:个人中心与交易闭环
-
个人资料、消息、学习报告。
-
优惠券、积分、紫荆币和订单。
-
微信、支付宝扫码支付。
-
支付状态轮询、幂等确认、超时和取消。
-
购买后书架和阅读权限刷新。
验收:
-
订单金额在服务端计算。
-
二维码过期后可安全重新下单或刷新。
-
支付回调幂等。
-
前端轮询不会重复发货或重复记账。
-
APP 和 PC 订单、资产与权益一致。
### 阶段 4:上线加固
-
全链路错误处理与日志。
-
性能、缓存和大内容加载优化。
-
XSS、CSRF、会话固定、票据重放和越权测试。
-
关键流程端到端测试。
-
Jenkins、Nginx、回滚和监控验证。
-
灰度发布与正式域名切换。
## 13. 测试策略
### 13.1 前端
-
单元测试:格式化、权限映射、路由守卫、支付状态机。
-
组件测试:登录表单、入口显示、阅读器工具栏、订单状态。
-
集成测试:API 错误、会话过期、票据兑换、支付轮询。
-
E2E:登录到图书馆、阅读和记录进度、免密进入后台、扫码支付模拟流程。
### 13.2 API
-
Web 会话创建、刷新、注销和过期。
-
手机号匹配和后台账号状态组合。
-
ticket 生成、过期、原子消费和重放拒绝。
-
阅读资源和学习数据的对象级权限。
-
订单金额、支付回调和幂等。
### 13.3 必须通过的发布检查
-
管理端 lint 和 production build。
-
学习端 lint、test 和 production build。
-
两个 API 的健康检查。
-
`/`
、
`/library`
、
`/admin/`
和
`/admin`
子路由刷新。
-
学员登录、后台免密进入和退出。
-
阅读进度写入和再次打开恢复。
-
测试支付订单完整闭环。
## 14. 错误处理原则
-
401:学习会话不存在,回到
`/login`
并保留安全的回跳地址。
-
403:用户已登录但无资源或后台权限,展示明确无权限页面。
-
409:支付、订单或学习记录发生状态冲突,先重新读取服务端状态。
-
422:表单或业务参数错误,在字段附近展示。
-
429:短信、登录、票据或支付轮询过于频繁,展示剩余等待时间。
-
5xx:展示可重试错误并记录请求追踪编号。
票据兑换失败不得自动尝试后台密码登录,也不得退化为共享学习 Token。
## 15. 风险与控制
| 风险 | 控制措施 |
|---|---|
| Monorepo 迁移破坏现有后台 | 阶段 0 只做机械迁移,先构建和冒烟验证 |
| APP 密钥进入浏览器 | PC 只调用
`/api/web`
,使用服务端会话 |
| 只隐藏入口但后台可被越权访问 | 后台兑换时重新检查账号和 RBAC |
| ticket 被截获或重放 | 60 秒 TTL、随机值、一次性原子消费 |
| 两个应用状态相互污染 | 两套路由、Store、API 客户端和构建入口 |
| 完整复刻范围过大 | 按 0 至 4 阶段实施和验收 |
| 阅读器复杂度失控 | 独立模块边界,先满足现有内容模型和核心交互 |
| PC 支付重复确认 | 服务端幂等、前端只轮询状态,不自行确认权益 |
| 发布后无法快速恢复 | Release 目录加 current 软链接原子回滚 |
## 16. AI 实施约束
后续由 GPT-5.6 Terra 实施时必须遵守:
1.
每次只执行一个已批准的阶段或明确的子计划。
2.
阶段 0 完成并验证前,不开发学习业务页面。
3.
不覆盖或清理用户已有改动。
4.
不顺带重构与当前阶段无关的管理端代码。
5.
所有 API 协议先写测试或契约,再修改实现。
6.
任何登录、票据、权限和支付代码都必须有失败路径测试。
7.
每个阶段结束时提交构建、测试和冒烟验证证据。
8.
未经确认不提交远程仓库、不部署生产环境。
## 17. 完成标准
项目最终完成需同时满足:
-
PC 学习端完整覆盖约定的 APP 线上业务。
-
学习端数据与 APP 数据一致。
-
登录默认进入图书馆。
-
后台入口按同手机号有效账号显示。
-
有效后台账号可免密进入
`/admin/`
。
-
两套会话和权限保持隔离。
-
PC 使用微信、支付宝扫码支付。
-
本地
`/`
与
`/admin/`
开发体验稳定。
-
Jenkins 可构建并原子发布两个前端。
-
Nginx 子路径刷新、API 代理和缓存正确。
-
安全、回滚和核心 E2E 验证通过。
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论