feat: 故障推送 / 公需课自动重学 / 插件解耦,发版 v0.0.8
体检修复:启动日志不再打印 AUTH_TOKEN、密钥可清空、推送失败不占整段冷却、目录名与文案统一成 plugins/。
This commit is contained in:
+289
-12
@@ -2,6 +2,264 @@
|
||||
|
||||
已实现、但还需要你实测确认的事项。确认没问题后把对应条目删掉。
|
||||
|
||||
## 发版体检修复(`src/study_tool/app.py`、`routes/settings.py`、`notify.py`、`web/src/components/SettingsDialog.tsx`)
|
||||
|
||||
**状态**:已实现,已用独立端口 + 独立库实例复测(首页 / `/query` / 系统设置 / 插件管理均正常渲染,无 console 报错);「清空密钥」「推送失败重试」需你实测。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- 启动日志不再输出 `AUTH_TOKEN` 明文(日志同时进控制台、内存和 SQLite,前端「系统日志」里能看到),改成「(需要访问令牌:见 .env 的 AUTH_TOKEN)」。
|
||||
- 系统设置:温度留空 / 填 0 = 不改,不再把已保存的温度清成 0;API Key、app_secret 右侧加「清空」按钮(`clear_api_key`、`notify.clear_app_secret`),不用再拿一个假值覆盖。
|
||||
- 故障推送:发送失败只冷却 1 分钟,原来一次网络抖动会把整段 `cooldown_min` 的冷却占掉。
|
||||
- 文案与文档:`platforms/`、`pugins/` 统一成 `plugins/`;`docs/pugin.md` 更名为 `docs/plugin.md`。
|
||||
- 工程:`pyproject.toml` 版本与 `metadata.yaml` 对齐,`bumpversion` 同时更新两处;`.gitignore` 补 `.idea/`、`.ruff_cache/`。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 重启服务,看控制台和右上角「系统日志」:启动那行应是 `控制台启动 -> http://127.0.0.1:8848(需要访问令牌:见 .env 的 AUTH_TOKEN)`,不应出现令牌本身。
|
||||
2. 已保存 API Key 时打开「系统设置 → 大模型」:API Key 右侧有「清空」,点了保存后 `has_key` 应变成 false;「故障推送」里的 app_secret 同理。
|
||||
3. 把推送 `base_url` 改成打不通的地址,触发一次故障推送:日志应出现「故障推送失败」,约 1 分钟后再次触发会重新尝试,而不是等满 10 分钟。
|
||||
|
||||
## 卡片标题显示学员姓名 + 日志面板明亮模式(`plugins/gdgxk/main.py`、`utils/learn.py`、`web/src/components/ui/LogLines.tsx`、`AccountCard.tsx`、`SystemLogDialog.tsx`)
|
||||
|
||||
**状态**:已实现,已用隔离实例截图核对(标题显示平台姓名、日志面板明亮 + 分级配色);「登录后回填姓名」和「进度上报新文案」需你下次学习时确认。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- 广东公需课 `profile()` 改成先解析个人中心头像区的 `<a class="name">`:学员账号平台显示的是打码姓名(如「*栋」),老师账号是完整姓名;解析不到才退回原来的「你好,X 老师」。卡片标题仍是 `user → username` 的优先级,所以登录 / 刷新课程后标题就变成姓名,第二行继续显示登录账号。
|
||||
- 进度上报日志改成能对得上的说法:`进度上报:平台已记 1200/2580 秒(46%),本次上报到 1201 秒`;课件结束改 `课件完成:xxx(平台已记 2580/2580 秒)`,不再出现「还差一半却说 100%」。
|
||||
- 新增 `ui/LogLines.tsx`:按 `时间 [级别] 内容` 拆行渲染,info 灰蓝、warn 琥珀、error / fail 红色、success 绿色,时间戳淡灰。
|
||||
- 账号卡片的「课程日志」和右上角「系统日志」都从黑底改成浅色底(`bg-slate-50` + 细边框),长行自动换行不再横向滚动。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 刷新页面后点一次「刷新课程」(或「重新登录」):卡片标题应从手机号变成平台姓名(广东公需课学员是「*栋」这种打码名),第二行仍显示登录账号。
|
||||
2. 下次学习时看日志:应出现 `进度上报:平台已记 x/y 秒(z%),本次上报到 n 秒`,百分比与秒数对得上;课件跑完是 `平台已记 y/y 秒`。
|
||||
3. 日志面板应是浅色底:info 灰蓝、warn 琥珀色、error 红色;点右上角「系统日志」也是同样的浅色配色,长行自动换行。
|
||||
|
||||
## 账号卡片:修改账号信息弹窗 + 自绘确认弹窗(`web/src/components/AccountEditDialog.tsx`、`ui/ConfirmDialog.tsx`、`ui/Modal.tsx`、`ui/Button.tsx`、`AccountCard.tsx`、`QuizDialog.tsx`、`App.tsx`)
|
||||
|
||||
**状态**:已实现,已用独立端口 + 复制库实例截图核对「修改账号信息」弹窗与「删除账号」确认框;「保存后自动重新登录」这一条还需你实测。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- 账号卡片「更多操作」菜单最上面新增「修改账号信息」;账号带 `lastError` 时,红条下方也直接给一个「修改账号信息」按钮,出错账号不用再翻菜单。
|
||||
- 新增 `AccountEditDialog`:可改学习平台 / 登录账号 / 密码 / 备注。密码留空表示不修改(后端 `account_upsert` 保留原密码),带显示/隐藏切换。保存后写库并立刻用新凭据重新登录一次:弹窗先关,登录在后台跑,失败走 toast + 卡片红条。
|
||||
- 新增 `ui/ConfirmDialog.tsx` 的 `useConfirm()`:项目内统一风格的确认弹窗(图标 + 标题 + 说明 + 主次按钮,危险操作用红色实心按钮)。替换了三处 `window.confirm`:删除账号(`App.tsx`)、提交测验缺题(`QuizDialog.tsx`)、再考一次(`QuizDialog.tsx`)。
|
||||
- `Modal` 增加 `sm` 尺寸;`Button` 增加 `danger-solid` 变体。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 刷新页面,点账号卡片右下「⋯」→「修改账号信息」:弹窗应回填当前平台 / 账号 / 备注,密码框为空并提示「已保存密码,需要更换时再填」。
|
||||
2. 只改备注后保存:应提示「账号信息已保存,正在用新凭据重新登录…」,随后登录成功并刷新课程;密码留空不应把库里密码清空。
|
||||
3. 故意填错密码保存:卡片应变成「登录失败」+ 红条,红条下出现「修改账号信息」按钮,点它能直接再改。
|
||||
4. 点「⋯」→「删除账号」:弹自绘确认框(红色图标 + 红色「删除账号」按钮),取消不删除、确定才删;测验缺题提交、再考一次也应弹同样的自绘弹窗,不再出现浏览器默认框。
|
||||
|
||||
|
||||
## 故障推送:呆呆面板推送渠道(`study_tool/notify.py`、`plugins/daidai_push/`、`routes/settings.py`、`engine.py`、`web/src/components/SettingsDialog.tsx`)
|
||||
|
||||
**状态**:已实现并实测通过(本机已用给定 app_key / app_secret 换到 token 并成功发出测试推送;进度异常 / 多次重试的自动触发还需一次真实学习验证)。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- 新增 `study_tool/notify.py`:渠道注册表 + `fault()` 故障上报 + `retry()` 重试计数 + 冷却(同账号同类型 `cooldown_min` 分钟内只推一次,重试只在刚达到阈值时推)。
|
||||
- 新增插件 `plugins/daidai_push/`(`plugin.json` 里 `"kind": "notify"`):`POST {base_url}/api/open-api/token` 换 token,内存缓存并按 `expires_in` 过期,发送遇 401/403 自动换新 token 重发一次;`POST {base_url}/api/notifications/send` 发通知。
|
||||
- 设置项 `notify`:`base_url`(默认 `https://corn.hanhan.ltd`)、`app_key`、`app_secret`、`channel_name`(留空走默认渠道)、`enabled`、四个分类开关、`retry_threshold`(默认 3)、`cooldown_min`(默认 10)。`GET/POST /api/settings` 读写,`app_secret` 只回显前后几位;新增 `POST /api/notify/test`。
|
||||
- 触发点:学完仍未完成 / 进度没推进 / 没有可识别活动、课程处理异常、整体运行异常、登录失败、会话失效且无法续期;公需课掉线重登与「未完成任务点自动重学」、莞易学单课时重报都计入重试次数。
|
||||
- 前端「系统设置」新增「故障推送」区块(含「测试推送」按钮),插件管理里该插件显示能力「推送」。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 重启服务,打开「系统设置」:左侧是「大模型 / 学习模式 / 故障推送」三栏,右侧只显示当前分类;切到「故障推送」应能看到已填好的 app_key / app_secret(secret 显示为掩码),点面板右上「测试推送」应收到一条「【测试】学习控制台推送」。
|
||||
2. 「插件管理」里应有「呆呆面板推送」卡片,能力显示「推送」;停用后点「测试推送」应提示没有可用渠道,重新启用后恢复。
|
||||
3. 跑一门**学不完**的课(或故意让会话失效):课程结束应收到「学习进度异常」或「登录/会话异常」推送,且同一账号 10 分钟内不会重复推。
|
||||
4. 公需课连续 3 轮进度不增加、或莞易学单课时连续 3 次上报未生效时,应收到「多次重试」推送(阈值可在设置里改)。
|
||||
|
||||
## 插件管理卡片:能力中文 + 适配网站(`web/src/components/PluginsDialog.tsx`、`plugins/__init__.py`、`platforms/contract.py`)
|
||||
|
||||
**状态**:已实现,待实测确认(前端已 `cd web && pnpm build`;`/api/plugins` 多了 `sites`,需重启服务才生效)。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- 卡片上的能力名由 `login / session / …` 改成中文:登录 / 会话 / 续期 / 账号信息 / 课程列表 / 课程详情 / 学习 / 测验 / 抓包(映射在 `PluginsDialog.tsx`,接口仍返回能力键)。
|
||||
- `/api/plugins` 每条插件多返回 `sites`:取平台 `entries` 并去掉被更具体域名覆盖的父域(莞易学的 `dgjy.net` 不会单独列)。
|
||||
- 卡片内的信息改成白底分组面板:`适配网站` 一行用等宽字体标签列出域名,`能力` 一行用蓝色标签(chip)逐个列出,`平台 / id` 放在最下面一行弱化展示。
|
||||
- 顺手把卡片里过期的 `plugins/`、`plugin.json` 文案改成 `plugins/`、`plugin.json`。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 重启服务后打开右上角「插件管理」:两张卡片都显示中文能力,且各有一行「适配网站」(公需课 `jsglpt.gds.edu.cn · jsxx.gds.edu.cn`,莞易学 `jspx.dgjy.net · study.dgjy.net`)。
|
||||
2. 点停用 / 启用 / 重载:清单刷新后网站与能力仍正确。
|
||||
|
||||
## 公需课交互式测验(`plugins/gdgxk/main.py` 的 `quiz` / `quiz_submit`)
|
||||
|
||||
**状态**:取题已验证(本机账号那门课解析出 30 道题、题型 01、选项 A–D);**交卷未实测**(该课考核早已提交,`done=true`,所以弹窗只会显示题目、按钮禁用)。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- 公需课补上 `quiz` / `quiz_submit` 两个槽位,前端卡片的「打开测验」因此出现(能力由方法推导)。
|
||||
- 取题:考核是**独立活动页** `jsxx.gds.edu.cn/<aid>/study/course/<plan_id>`(课程主页上没有 `#testForm`),从课程目录里找标题含「考核 / 考试」的活动,打开它再 `exam.parse`;题型映射成弹窗认的 `01/02/03`。已交卷的课也返回题目,方便回看。
|
||||
- 交卷:`exam.submit_letters` 重取一次考核页拿隐藏字段,把用户选的字母按选项 `value`(`Choice0..4`)换成平台要的值,再走原来的 `finishTest2` 提交并回读得分。
|
||||
- `/api/quiz/submit` 改为按平台分发;莞易学的交卷逻辑挪进 `quiz.submit_quiz`,行为不变。
|
||||
- 顺带修好学习流程里的自动考核:`exam.run` 之前也读课程主页(拿不到表单),现在同样改为读考核活动页。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 找一门**考核还没交**的公需课,点「打开测验」:应列出 30 道题的题干 / 题型 / 选项。
|
||||
2. 点「AI 答题」:应分批把答案填进选项(走 `/api/quiz/answer`,纯大模型)。
|
||||
3. 改几道答案后点交卷:应返回得分;再点一次「打开测验」应显示已交卷且题目可见。
|
||||
4. 若提示「考核页里没有找到题目」,把该考核页 HTML 发我,`exam.parse` 的选择器要跟着改。
|
||||
|
||||
## 刷新课程反复重登 / 空平台报错(`web/src/App.tsx`、`routes/accounts.py`、`plugins/gdgxk/main.py`)
|
||||
|
||||
**状态**:已实现,待实测确认(前端需先 `cd web && pnpm build`)。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- `refreshCourses` 在 `session_expired` 自动重登后的重试补上 `platform`,不再出现「平台「」还没有实现刷新课程」,登录成功的课程也会正常写回卡片。
|
||||
- 前端只在运行中(`discovering` / `studying`)才把 `/api/runs` 的 `cookies` 回写账号;运行结束后不再用开跑时的旧会话覆盖刚登录的新会话。
|
||||
- `_clear_done_run` 一并清掉运行状态里的 `cookies`,重新登录后旧会话不会继续挂在 `/api/runs` 里。
|
||||
- 公需课学习中掉线重登(`_relogin`)成功后,用 `database.account_save_cookies` 把新会话写回账号,并 `set_state(cookies=…)` 同步运行状态。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 让账号会话失效(隔一段时间或换设备登录一次),点「刷新课程」:应看到「会话已失效,正在自动重新登录…」后直接读到课程,不再弹「平台「」还没有实现刷新课程」。
|
||||
2. 自动重登后等 10 秒以上,再点一次「刷新课程」:不应再次触发登录(说明旧 cookie 没有被写回去)。
|
||||
3. 学习中故意让会话掉线,日志出现「重新登录成功,继续学习」后结束运行,再点「刷新课程」:应一次成功、不再重登。
|
||||
|
||||
## 公需课自动重学(`plugins/gdgxk/main.py` 的 `study`)
|
||||
|
||||
**状态**:已实现,待实测确认。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- 每刷完一轮活动,都重新请求进度页 `/study/course/progess` 取任务点状态;全部已完成(或一开始就全部已完成)时也会重取一次,以平台状态为准。
|
||||
- 进度页里还有未完成任务点,就等 60 秒(`ROUND_WAIT`)后自动重新开始学习,只重刷未完成的活动。
|
||||
- 连续 3 轮(`MAX_STALL`)完成数没增加,或达到 5 轮(`MAX_ROUNDS`)就停止,日志列出剩余任务点,避免考核等非视频任务点死循环。
|
||||
- 重试等待期间点「停止」会立即退出。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 选一个有未完成任务点的账号点「开始学习」,观察日志有没有「第 N 轮结束……稍后自动重新学习」,并且自动开始下一轮。
|
||||
2. 选一个全部任务点已完成的账号,日志应显示「平台进度:N/N 个活动已完成」后结束。
|
||||
3. 若某个任务点平台迟迟不记完成,应在 3 轮后停下并列出该任务点;如果 60 秒不够平台刷新状态,调大 `ROUND_WAIT`。
|
||||
|
||||
## 公需课选课兜底(`plugins/gdgxk/utils/register.py`)
|
||||
|
||||
**状态**:已实现,**未实测**(写的时候没有账号,选课超市的页面结构和选课请求是按截图推断的)。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- `list_courses` 发现课程列表里没有目标年度的课时,调用 `register.ensure`:读 `/uc/store/course/` 里所有课程链接,逐个打开详情页判断状态。
|
||||
- 状态判断:任一页按钮是「进入学习」= 已经选中,直接重读课程列表;选中的课位置对应页写「已有选课」= 已选了别的课,不再选;都没选就选第 `pick` 门。
|
||||
- 配置在 `plugins/gdgxk/config.json`:`year`(空 = 当前年份)、`pick`(第几门,从 1 开始)。暂未做弹窗让用户挑。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 用一个当年还没选课的账号点「刷新课程」,看系统日志有没有「公需课选课:…」。
|
||||
2. 若提示「没有解析到课程链接」或「没有找到选课按钮的请求地址」,把 `/uc/store/course/` 页面 HTML、未选状态的详情页 HTML、点「选课」时的请求(cURL)发我,据此改 `register.py` 里的正则和请求方式(现在假定选课是 GET)。
|
||||
|
||||
## 公开查询页 `/query`
|
||||
|
||||
**状态**:已实现,待实测确认(前端需先 `cd web && pnpm build`)。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- 新增 `routes/query.py`:`GET /api/query?username=` 按用户名返回账号的课程列表(名称 / 章节 / 状态 / 进度)和运行进度,不含密码 / cookie / 账号 id。
|
||||
- `app.py` 的 token 中间件放行 `/query`、`/api/query`、`/favicon.svg` 和 `/assets/*`;`/query` 与 `/` 共用同一个前端入口。
|
||||
- 前端 `main.tsx` 按路径渲染:`/query` 显示新页面 `pages/QueryPage.tsx`(上方输入框 + 查询按钮,下方课程列表与进度),其余路径仍是控制台。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 设置 `AUTH_TOKEN` 后启动,不带 token 打开 `/query`,应能正常显示;打开 `/` 仍要求 token。
|
||||
2. 输入已添加账号的用户名点「查询」,应显示课程列表和进度;输入不存在的用户名显示「没有找到该账号」。
|
||||
3. 注意:该接口任何人知道用户名就能查到课程与进度,公网部署请确认可接受。
|
||||
|
||||
## 题库匹配(核心 `bank.py` + 公需课题库数据)
|
||||
|
||||
**状态**:已实现,待实测确认。匹配部分已完成;公需课考核已接到它上面(见下一节)。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- 新增 `src/study_tool/bank.py`(与平台无关):题干归一化后按「精确 → 互相包含(两边至少 8 字)→ 相似度 ≥ 0.9」查题库;没命中的题按 5 题一批问设置里的大模型;没配大模型就留空并打 warn 日志。入口 `bank.answer(key, questions, bank)`。
|
||||
- 新增 `plugins/gdgxk/utils/bank.py` 与 `plugins/gdgxk/bank/2026.json`:题库数据由脚本的 121 条 `videoBank` 转成 `[{"q": 题干, "a": "B"}]`。明年换题库只需往 `bank/` 加 JSON,同目录所有 `*.json` 会合并。
|
||||
- `answer.py`(考核按序答案)没动:考核改走题干匹配,按序答案留着备用。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 在项目根目录用 Python 执行:`from study_tool.plugins.gdgxk.utils import bank; bank.answer("t", [{"no":1,"type":"判断","title":"生成式人工智能(GenAI)目前还没有在教育领域得到广泛应用。","options":[{"key":"A","text":"正确"},{"key":"B","text":"错误"}]}])`,应返回 `{"1": {"answer": ["B"], "source": "bank"}}`。
|
||||
2. 换一道题库里没有的题:没配大模型时日志有「还没配置大模型」警告;配了大模型会返回 `source: "llm"`。
|
||||
|
||||
## 公需课课程考核(`plugins/gdgxk/utils/exam.py`)
|
||||
|
||||
**状态**:已实现,待实测确认(本轮只验证了解析与选项映射,交卷响应体还没在真实环境跑过)。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- 考核不是 JSON 接口:题目内嵌在课程学习页 HTML(`#topicList .m-topic-item`),交卷是 `POST .../ncts/test/delivery/<id>/finishTest2`,body 为 `_method=put` + 表单序列化(Spring method override)。
|
||||
- 题型与字段:单选 `radio`、多选 `checkbox`(最多 5 个选项)、是非 `radio`,字段名 `qti_response_Q{n}:{n+1}:1_RESPONSE`,值 `Choice0..4`;隐藏字段 `qti_item_*` 原样回传。
|
||||
- `exam.parse` 解析题面 / 选项字母 / 提交值;`exam.answer` 把题库或 AI 给的字母换成 `ChoiceN`;`exam.submit` 按页面 `ajaxSubmit` 的做法交卷。
|
||||
- 题库没覆盖全部题目时不交卷,只打 warn 并列出缺答案的题号(避免瞎蒙把分数拉低);交完卷解析「已得 N 分」,低于 60 分打 warn 提示人工重考。
|
||||
- `main.py` 的 `study()` 在视频轮次跑完、进度仍有缺口时调用一次考核,交完再按进度页复核。`opts["exam_auto"]=False` 可关掉自动交卷。
|
||||
- 页面上已显示「您已完成测验」时直接跳过,不会重复交卷。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. 找一门视频已刷完、只差考核的课跑一次,日志应出现「考核已交卷,得分 N」。
|
||||
2. 故意让题库缺题(临时改 `bank/2026.json`):应出现「考核共 N 题,M 题没答案,本次不交卷」,且不提交。
|
||||
3. 重跑已完成的课:日志应是「考核已完成」,不再请求 `finishTest2`。
|
||||
|
||||
## 目录整理:config / database / llm 包,平台代码收进插件
|
||||
|
||||
**状态**:已实现,待实测确认。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- 二次清理:莞易学专属常量(站点地址、登录授权入口、课程板块、`NODE` / `DESENC`、`DEBUG_DIR` 等)迁到 `plugins/guanyixue/config.py`;测验请求模型 / 答题提示词 / 题型名迁到 `plugins/guanyixue/quiz.py`;`config.LOGDIR`、`SessionExpired` 再导出、`AccountsIn` 无人使用,已删除;核心模块的 star import 改为显式。
|
||||
- `config.py` → `config/` 包:`config/__init__.py` 放常量 / 环境变量 / 根目录定位,`config/settings.py` 放系统设置默认值与读写。
|
||||
- `settings_store.py` 拆开:`/api/settings` 移到 `routes/settings.py`;设置读写留在 `config/settings.py`(插件不再反向 import 路由)。
|
||||
- `llm.py` → `llm/` 包:`llm_chat()` 在 `llm/__init__.py`,`/api/llm/test` 移到 `routes/llm.py`;后续大模型相关代码都放这个目录。
|
||||
- `store.py` → `database/` 包,改用 SQLAlchemy 2.0 ORM:`database/models.py` 定义 settings / kv / accounts / login_log / run_log / run_state / snapshots 七张表,`database/__init__.py` 提供 engine、线程级 scoped Session 和原来的读写函数(函数名与返回结构不变)。
|
||||
- `capture.py` → `plugins/guanyixue/capture.py`;抓包接口按账号所属平台走 `registry.get(...)` 的 `capture` / `captures` / `capture_reset`。
|
||||
- `quiz.py` → `plugins/guanyixue/quiz.py`(莞易学专用);插件 `main.py` import 它来注册 `/api/quiz/*`。
|
||||
- `app.py` 调 `routes.load_routes()` 自动扫描并导入 `routes/*`(新增路由文件不用改 `app.py`,之前漏写 import 会导致全部 `/api/*` 404);新增依赖 `sqlalchemy>=2.0`(`pyproject.toml`,需 `uv sync`)。
|
||||
- `docs/architecture.md`、`README.md`、`docs/plugin.md` 同步更新目录与模块引用。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. `uv sync` 后启动,`/api/health` 正常,`data/study.db` 能正常建表 / 读写。
|
||||
2. 账号列表 / 登录 / 登录记录、课程日志、运行状态(`/api/runs`)、快照、设置保存、LLM 测试接口,行为与改造前一致。
|
||||
3. 抓包开始 / 停止 / 查看记录正常;插件停用 / 启用 / 重载正常。
|
||||
4. 莞易学插件启用时,`/api/quiz`、`/api/quiz/submit`、`/api/quiz/answer` 仍可用。
|
||||
5. 莞易学账号登录(旧版 jspx 与新版 study 两条链路都走一遍)、刷新课程、学习正常;新版课程学习开头仍有「新版课程 ID」日志。
|
||||
6. 首次启动不再创建 `data/logs` 目录(只要 `data/debug` 与数据库)。
|
||||
7. 启动后不会再出现 `src/data/`:`database` 的 `DATA_DIR` 复用 `config.DATA`(仓库根 `data/`)。之前 `database/` 比旧 `store.py` 深一层,路径多上了一级才落到 `src/data`;原 `src/data/study.db` 里的账号 / 登录记录 / 日志已合并进根目录 `data/study.db`,页面应能看到两个 gdgxk 账号。
|
||||
|
||||
### 已知取舍
|
||||
|
||||
- `/api/quiz/submit`、`/api/quiz/answer` 仍由莞易学插件自己注册;加载器负责在停用 / 重载时摘掉,所以不再重复注册。要做成通用路由需等第二个平台有测验。
|
||||
|
||||
## 插件可删除:路由随插件进出、前端按能力显示、残件归位
|
||||
|
||||
**状态**:已实现,待实测确认(我只做了静态检查、前端 typecheck / build,以及在临时副本里实际启动验证)。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- `plugins/__init__.py`:加载器记录每个插件 import 期间注册到 `app` 的路由,停用 / 重载 / 加载失败时一并摘除;重载不再重复注册 `/api/quiz*`。
|
||||
- `platforms/contract.py` / `base.py`:能力新增 `quiz`(`quiz(self, s, d, b)`)与 `capture`;`/api/platforms` 的 `capabilities` 会带上它们。莞易学 `quiz.py` 的取题拆成 `fetch_quiz`,`/api/quiz` 改走 `pf.quiz`。
|
||||
- 去掉写死的 `guanyixue` 默认值:`accounts.platform` 默认空串、请求模型默认空串、`_capture_platform` 不再兜底;前端平台列表初值为空,「打开测验」「抓包」按 `capabilities` 显示,页脚和添加账号提示不再写「莞易学」。
|
||||
- 残件归位:`script/des.js`、`desenc.js`、`study_video.py` → `plugins/guanyixue/script/`;`script/capture-jsglpt.py`、`test-gdgxk.py` → `plugins/gdgxk/script/`;`DESENC` 路径随插件目录走;`test-gdgxk.py` 改成走新的插件 API。
|
||||
- 已验证(临时副本):正常启动;停用 → 启用 → 重载两次后 `/api/quiz*` 恰好各一条;删掉莞易学目录 / 删光所有插件目录后服务都能启动,`/api/platforms` 分别只剩 gdgxk / 为空。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. `cd web && pnpm build` 后启动,界面能打开;平台下拉里有「莞易学」「广东公需课」。
|
||||
2. 莞易学账号卡片「更多」菜单有「打开测验」「抓包」;广东公需课账号的菜单里没有这两项。
|
||||
3. 「插件」弹窗里停用莞易学:`/api/quiz` 返回 404;再启用恢复;反复点「重载」不报错。
|
||||
4. 把 `plugins/guanyixue` 整个目录挪走再启动:服务正常,添加账号只能选公需课;之前的莞易学账号登录时提示平台未实现,不影响其它账号。
|
||||
5. 在仓库根目录执行 `python src/study_tool/plugins/guanyixue/script/study_video.py verify <trainCourseId>` 能跑(脚本自己把 `src` 加进 `sys.path`)。
|
||||
6. Docker 构建后 nodejs 仍能被登录调用(`DESENC` 现在指向插件目录)。
|
||||
|
||||
## 本机启动读取根目录 .env
|
||||
|
||||
**状态**:已实现,待实测确认。
|
||||
@@ -11,7 +269,7 @@
|
||||
- 修问题:以前只有 docker compose 会读 `.env`,本机 `python app.py` / `uv run app.py` / `start.bat`
|
||||
下 `AUTH_TOKEN` / `PORT` / `RUN_LOG_KEEP` / `SNAPSHOT_KEEP` 全部不生效。
|
||||
- `study_tool/__init__.py` 用 `python-dotenv` 加载仓库根 `.env`;放在包初始化处,早于 store / config
|
||||
读环境变量(`store.py` 是 import 时就读 `DB_PATH` / `RUN_LOG_KEEP`)。已存在的环境变量优先,Docker 由 compose 注入的不受影响。
|
||||
读环境变量(`database/__init__.py` 是 import 时就读 `DB_PATH` / `RUN_LOG_KEEP`)。已存在的环境变量优先,Docker 由 compose 注入的不受影响。
|
||||
- 新依赖 `python-dotenv>=1.0`(`pyproject.toml` / `requirements.txt`,需 `uv sync`)。
|
||||
- `app.py` 监听地址改成 `HOST` → `BIND_ADDR` → `127.0.0.1`,`.env` 里的 `BIND_ADDR` 本机也生效。
|
||||
- README 环境变量表补 `.env` 说明与 `BIND_ADDR` / `TZ` 两行;`.env.example` 说明改为「本机与 Docker 都读」。
|
||||
@@ -23,17 +281,36 @@
|
||||
3. `.env` 改 `PORT=8849` 重启 → 实际监听 8849;命令行 `set PORT=8850` 再启动应压过 `.env`。
|
||||
4. `.env` 里 `BIND_ADDR=127.0.0.1` → 只绑本机;改 `0.0.0.0` → 同网段可访问。
|
||||
|
||||
## 莞易学工具按阶段拆到 utils
|
||||
|
||||
**状态**:已实现,待实测确认。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- `plugins/guanyixue/` 下的 `jspx.py` / `study.py` 按阶段拆到 `utils/`:`http.py`(会话 / cookie /
|
||||
token)、`login.py`(登录 / SSO / 续期)、`course.py`(旧版课程抓取与代际分发)、
|
||||
`feedback.py`(反馈表 / 测验)、`learn.py`(旧版学习流程)、`spa.py`(新版 SPA)。
|
||||
- `main.py` 只留 `Platform` 子类,按 `course["generation"]` 分发到 `learn` / `spa`。
|
||||
- 新模块全部改成显式 import,不再用 `from study_tool.config import *` / `jspx import *`;
|
||||
`quiz.py` 的引用同步改成 `utils.*`。
|
||||
|
||||
### 怎么测
|
||||
|
||||
1. `GET /api/plugins` 里 `guanyixue` 的 loaded 为 true。
|
||||
2. 用莞易学账号走一遍登录、刷新课程、发现课程(旧版 / 新版各一门更好)。
|
||||
3. 启动一次学习并停止,确认日志里仍有进度与完成状态输出。
|
||||
|
||||
## 莞易学迁到插件目录
|
||||
|
||||
**状态**:已实现,待实测确认。
|
||||
|
||||
### 做了什么
|
||||
|
||||
- `platforms/guanyixue.py` → `pugins/guanyixue/main.py`;`jspx.py` / `study.py` / `detect.py`
|
||||
一并搬进 `pugins/guanyixue/`,插件自包含,热重载能覆盖全部实现模块。
|
||||
- 导入路径改成 `study_tool.pugins.guanyixue.*`;`quiz.py` 里对 `jspx` / `study` 的引用同步更新。
|
||||
- `platforms/guanyixue.py` → `plugins/guanyixue/main.py`;`jspx.py` / `study.py` / `detect.py`
|
||||
一并搬进 `plugins/guanyixue/`,插件自包含,热重载能覆盖全部实现模块。
|
||||
- 导入路径改成 `study_tool.plugins.guanyixue.*`;`quiz.py` 里对 `jspx` / `study` 的引用同步更新。
|
||||
- `platforms/__init__.py` 只剩运行时,`load_builtin()` 只调 `load_plugins()`。
|
||||
- 新增 `pugins/guanyixue/pugin.json`(id / title / name)。
|
||||
- 新增 `plugins/guanyixue/plugin.json`(id / title / name)。
|
||||
|
||||
### 怎么测
|
||||
|
||||
@@ -47,8 +324,8 @@
|
||||
|
||||
### 做了什么
|
||||
|
||||
- `pugins/__init__.py` 重写:`load_plugins()` / `discover()` / `set_enabled()` / `reload()`。
|
||||
- 热加载:把 `study_tool.pugins.<名字>`(含子模块)从 `sys.modules` 摘掉后重新 import,
|
||||
- `plugins/__init__.py` 重写:`load_plugins()` / `discover()` / `set_enabled()` / `reload()`。
|
||||
- 热加载:把 `study_tool.plugins.<名字>`(含子模块)从 `sys.modules` 摘掉后重新 import,
|
||||
import 时 `registry.register()` 覆盖;重载前先 `registry.unregister()` 旧 id,改 id 不留残影。
|
||||
- 启用清单存在设置项 `plugins`(`{"disabled": [...]}`):没列进去的默认启用,
|
||||
新丢进目录的插件自动生效;停用的插件被摘出注册表,`/api/platforms` 与账号平台下拉同步消失。
|
||||
@@ -63,7 +340,7 @@
|
||||
|
||||
1. `GET /api/plugins` 列出 `gdgxk`(enabled / loaded 为 true,capabilities 七项)。
|
||||
2. `POST /api/plugins/gdgxk/disable` → `GET /api/platforms` 里 gdgxk 消失;再 enable 回来。
|
||||
3. 改 `pugins/gdgxk/main.py` 里一句日志文案 → `POST /api/plugins/gdgxk/reload` →
|
||||
3. 改 `plugins/gdgxk/main.py` 里一句日志文案 → `POST /api/plugins/gdgxk/reload` →
|
||||
不重启服务再跑一次学习,日志应变成新文案。
|
||||
4. 重启服务:停用状态应被记住(设置项 `plugins`)。
|
||||
5. 前端顶栏「插件管理」:停用 gdgxk → 「添加账号」的平台下拉立刻少一项;重新启用后恢复。
|
||||
@@ -76,15 +353,15 @@
|
||||
|
||||
- `Platform` 从 dataclass「注册时逐项传函数」改成基类:`id` / `label` / `entries` 是类属性,
|
||||
能力是子类方法;没实现的阶段保持基类的 `None`,`capabilities()` 与 `/api/platforms` 不变。
|
||||
- `pugins/gdgxk/main.py`(原 `platforms/gdgxk.py`):`session`(原 `new_session`)/ `login` /
|
||||
- `plugins/gdgxk/main.py`(原 `platforms/gdgxk.py`):`session`(原 `new_session`)/ `login` /
|
||||
`list_courses` / `discover` / `study` 等收进 `class Gdgxk(Platform)`;解析 / 上报类 helper
|
||||
仍是模块级函数。
|
||||
- `platforms/guanyixue.py`:改成 `class Guanyixue(Platform)`,方法转调 `jspx` / `study`。
|
||||
- `pugins/` 改成插件目录:`pugins/<名字>/pugin.json`(只放 `id` / `title` / `name`)+ `main.py`
|
||||
(`Platform` 子类);`pugins/__init__.py::load_plugins()` 扫目录 import,`load_builtin()` 调它。
|
||||
- `plugins/` 改成插件目录:`plugins/<名字>/plugin.json`(只放 `id` / `title` / `name`)+ `main.py`
|
||||
(`Platform` 子类);`plugins/__init__.py::load_plugins()` 扫目录 import,`load_builtin()` 调它。
|
||||
插件里不再声明任何能力绑定。
|
||||
- 引擎 / 路由 / `quiz.py` / 前端 / `platforms/contract.py` 均未改动。
|
||||
- 新增 [docs/pugin.md](plugin.md):插件骨架、槽位契约、新增与迁移流程、常见坑。
|
||||
- 新增 [docs/plugin.md](plugin.md):插件骨架、槽位契约、新增与迁移流程、常见坑。
|
||||
|
||||
### 怎么测
|
||||
|
||||
|
||||
Reference in New Issue
Block a user