refactor: React 前端重构,新旧平台合并为莞易学

This commit is contained in:
study-tool
2026-10-29 23:50:46 +08:00
parent 35853891c9
commit e8634a6317
62 changed files with 8923 additions and 4623 deletions
+104
View File
@@ -2,6 +2,60 @@
已实现、但还需要你实测确认的事项。确认没问题后把对应条目删掉。
## 新旧平台合并为「莞易学」
**状态**:已实现,待实测确认。
### 做了什么
- `jspx.dgjy.net`(登录入口)和 `study.dgjy.net`(学习入口)在注册表里合并成一个平台:
`id=guanyixue`、`label=莞易学`。卡片右上角只显示「莞易学」。
- 课程发现时按 URL 和页面标记判断代际:旧版 `legacy`(`tier-tree-one` / `tier3`)、
新版 `spa`(Vue SPA / `/training/*`),写进 `course["generation"]`。
- 学习实现按代际分发:`legacy` 走 `engine._study_builtin`,`spa` 走 `engine.run_study_course`。
- 课程日志里的「平台:」一行会显示 `莞易学 · 旧版页面` 或 `莞易学 · 新版页面`。
- `GET /api/platforms` 返回 `[{id:"guanyixue",label:"莞易学"}]`,前端用 label 展示。
### 怎么测
1. 分别用一个旧版课程页账号和一个新版课程页账号(或同一账号的两门课)各跑一次自动学习。
2. 看卡片「课程日志」:应出现 `平台:莞易学 · 旧版页面` / `平台:莞易学 · 新版页面`,不再出现 `jspx` / `study` 平台名。
3. 卡片头部徽标应为「莞易学」;打开测验,交卷后后台日志正常,说明 `generation` 分支没走错。
### 如果没生效
- 日志报「无法识别课程页结构」:把页面快照发出来,可能站点又改版了,
需要补 `platforms/detect.py` 的代际标记。
- 日志报 `平台 xxx 没有可用的学习实现`:`course["platform"]` 不是 `guanyixue`,
检查 `platforms/jspx.py` 的 `_discover_once()` 是否被其他分支覆盖了。
## 前端 React 重构
**状态**:已实现,待实测确认。
### 做了什么
- 删除单文件 `static/index.html`,前端源码迁到 `web/`:React 19 + TypeScript + Vite +
Tailwind CSS v4,构建产物输出到 `static/`(`index.html` + `assets/*` + `favicon.svg`)。
- 页面功能保持不变:账号轮询(5s)、卡片内日志(1.5s)、课程勾选、登录 / 刷新 / 开始 / 停止 /
抓包 / 删除、自动交测验 / 反馈表开关、测验弹窗(手动作答 / AI 流式答题 / 再考一次)、
系统设置、系统日志、离线提示、Toast。
- FastAPI 新增 `/assets` 静态挂载和 `/favicon.svg`,`/` 仍返回 `static/index.html` 且禁止缓存。
### 怎么测
1. 直接 `python app.py`,打开页面:账号卡片、状态、课程列表、进度条、日志折叠都正常。
2. 走一遍登录 → 刷新课程 → 勾选 → 开始学习 → 停止,观察按钮 loading 和日志面板。
3. 打开测验(含单选 / 多选 / 判断 / 填空),提交后看标准答案和「再考一次」。
4. 设置里改大模型 / 学习模式并保存,刷新后值仍在;测试连接正常。
5. 右上角系统日志可查看、清空;断网后出现「后端未连接」提示。
### 如果没生效
- 页面空白 / 404 `/assets/...`:确认 `static/` 里有构建产物,且 `python app.py` 是在仓库根启动。
- 改了 `web/` 但页面没变:执行 `cd web && pnpm build`,构建产物才会进 `static/`。
- 端口不是 8848 时 `pnpm dev` 的 `/api` 代理会打错:改 `web/vite.config.ts` 的 `server.proxy`。
## 首次进入课程自动加入(syncSelf)
**状态**:已实现,待实测确认。
@@ -39,3 +93,53 @@ GET /mod/school/wanpeng/syncSelf?courseCode=<courseCode>
(抓包时重点看 `syncSelf?courseCode=` 参数是从哪个响应/本地状态来的)。
- 日志显示 syncSelf 成功但 `getById` 一直 `isJoin=0`
→ 同步在服务端没落库,先看 `syncSelf` 响应的 `result.code`,再考虑补 `/mod/gjt/roleList` 之外的接口。
## AI 答题(流式 + 重试 + 日志)
**状态**:已实现,待实测确认。
### 做了什么
- `/api/quiz/answer` 从「等全部批次算完再返回」改成流式(`text/event-stream`):每批 5 题算完就推一行,连接一直有数据,反代不会空闲断连。
- 每批失败按错误类型决定是否重试一次:超时 / 5xx / 429 / 空内容 / 非 JSON 会重试(第二次关掉 `response_format`、口头要求 JSON),鉴权类错误直接放弃这一批。
- 一批里只答出部分题时,第二轮只补问没答出来的题。
- 解析加了兜底:标准 JSON、markdown 包裹、截断 JSON、`1.A / 第3题:B / 3. 错误` 这类自由文本都能识别;多选连写 `AC` 会拆成 `A#C`。
- 日志:开始(题数/批数/模型/超时)、每批耗时与题数、失败原因、模型原文摘要、结束统计;同时打到控制台、SQLite(`/api/log/history?key=<账号>`)和该账号卡片的「课程日志」。
### 怎么测
1. 打开一个测验,点「AI 答题」,看状态栏是否逐批更新「Ns · 已填 x/y 题」。
2. 结束后展开该账号卡片的「课程日志」,应能看到 `AI答题 开始/第 1/2 批…/结束` 若干行;控制台同样能看到。
3. 故意把 base_url 改错或断开网络再点一次,日志里应出现 `失败 x.xs:LLM 404/超时…`,界面提示「详见该账号课程日志」。
### 如果没生效
- 状态栏一直停在 0/0、日志里没有任何 `AI答题` 行 → 说明请求没到后端(看浏览器 Network 里 `/api/quiz/answer` 的状态码)。
- 日志里每批都 `失败 … LLM 401` → api_key 不对;`LLM 404` → base_url 少了 `/v1`。
- 每批都是 `本批只答出 0 题` 且模型原文很长 → 模型没按要求给题号,可把批次调小(`quiz.py` 里 `QUIZ_BATCH`)或换个指令遵循更好的模型。
## 模块拆分 + 统一日志源 + 平台插件
**状态**:已实现,待实测确认。
### 做了什么
- 代码改成 `src/` 包布局:`src/study_tool/` 下按层分 `config`(常量)、`runtime`(状态 + 日志)、`store`(SQLite)、`engine`(学习引擎)、`capture`、`models`、`settings_store`、`llm`、`quiz`、`platforms/`(平台插件)、`routes/`(HTTP 接口)。根目录 `app.py` 只做启动壳,`python app.py` / `start.bat` / Dockerfile 的启动方式不变;新增 `pyproject.toml`(`pip install -e .` 后可用 `study-tool` 命令)。
- 平台插件化:`platforms/base.py` 定义 `Platform(id, label, study)`,`platforms/registry.py` 提供 `register/get/all`,`platforms/jspx.py` 与 `platforms/study.py` 各自注册;`engine.run_one_course` 发现课程后改成 `registry.get(course["platform"]).study(...)`,不再写死 `if platform == "study"`。新增平台 = 加一个模块 + 一行 import,`docs/architecture.md` 有完整步骤。
- 日志统一走 `runtime.log(key, msg, level)`:控制台、内存(`/api/log`)、SQLite(`/api/log/history`)同一份;设置保存、服务启动都会写。
- 新增右上角「系统日志」弹窗,看 `key=system` 的全局日志;卡片「课程日志」看该账号的日志。
- GBK 控制台编不了的字符(⚠ 等)自动降级,不再让 worker 线程抛异常。
### 怎么测
1. 启动服务,控制台应出现 `[system] SQLite: …` 和 `[system] 控制台启动 -> …`。
2. 页面右上角「系统日志」→ 看到上面两行;保存一次设置 → 多一行「设置已更新:模型 …」。
3. 点账号卡片的「课程日志」→ 学习日志和以前一致;点「清空」后不再出现旧行。
4. 跑一门课(旧平台 / 新平台各一次),日志里应出现该课程的流程,说明插件分发走到了对应平台。
5. `python -c "import sys; sys.path.insert(0,'src'); from study_tool.platforms import load_builtin, registry; load_builtin(); print(registry.all())"` → 应看到 jspx / study 两个平台。
4. 跑一次 AI 答题 → 该账号「课程日志」里是 `AI答题 …`,控制台同步同一份内容。
### 如果没生效
- 控制台没有 `[system]` 行 → 跑的是旧进程(端口被占用时新代码会提示后直接退出),先结束旧 PID 再启动。
- 「系统日志」弹窗空白 → `GET /api/log?key=system` 有 lines 就是前端缓存问题,刷新即可;没有 lines 说明还没产生 system 日志,保存一次设置就能看到。