Files
study-tool/CHANGELOG.md
T
study-tool c4bc97dd70 feat(gdgxk): 课程卡片显示培训年度 / 学时 / 完成状态
- list_courses 解析 showCourse 卡片里的「培训年度」「课程学时」「未完成/已完成」,
  返回 section / info / status_label,前端拼成「2026年公需课 · 未完成 · 30 学时」
- 修正完成判定:以平台状态为准(需所有教学活动 100% + 试卷考核 ≥60),
  不再用视频秒数够不够来推断,避免把未完成的课当已学完跳过
2026-10-30 22:33:46 +08:00

5.6 KiB
Raw Blame History

更新日志

Unreleased

重构

  • 前端从单文件 static/index.html(原生 JS)改为 web/ 工程:React 19 + TypeScript + Vite + Tailwind CSS v4;Vite 直接输出到 static/,FastAPI 托管 / 与 /assets,运行 Python 不再需要 Node
  • 新旧学习平台合并为「莞易学」:jspx.dgjy.net(登录入口)与 study.dgjy.net(学习入口)统一注册为 Platform(id="guanyixue", label="莞易学"),课程页代际(legacy / spa)由 platforms/detect.py 判断,平台内部按代际分发到 jspx.py / study.py
  • Platform 新增 entries(登录 / 学习入口 URL 片段)与 registry.detect();课程发现按入口识别平台,新增平台不再需要改引擎。新增 GET /api/platforms 供前端展示平台名
  • 添加账号新增「学习平台」下拉(数据来自 GET /api/platforms);accounts 表新增 platform 字段(老库启动时自动补列),账号选定平台后,登录 / 刷新课程 / 发现课程分别走该平台的 login / list_courses / discover,学习与测验按 course["platform"] + generation 分发
  • Platform 增加账号级可选入口:session / dump_cookies / login / list_courses / discover / renew_session / profile;引擎和路由按 registry.get(account["platform"]) 取实现,接入第二个平台只需写一个模块
  • 测验交卷从 platform(jspx / study)改为 generation(legacy / spa),接口返回 platform="guanyixue" + generation
  • 代码改为标准 src/ 包布局:业务代码全部在 src/study_tool/,根目录 app.py 只做启动壳(python app.py、start.bat、Dockerfile 都不用改);新增 pyproject.toml,pip install -e . 后可用 study-tool 命令
  • 平台拆成插件包 src/study_tool/platforms/:base.py 定义 Platform 契约、registry.py 注册表;引擎按 course["platform"] 从注册表分发
  • HTTP 接口收进 routes/(accounts / misc / logs),docs/architecture.md 说明分层、平台识别与新增平台的步骤
  • 散落的 JS 脚本统一收进 script/:运行必需的 des.js / desenc.js 放 script/(config.py 的 DESENC 指向 script/desenc.js),不参与运行的 dgjy-toolkit.user.js、study-axios.mjs 移到 script/reference/ 作参考

新增

  • 新增平台「广东公需课」(platforms/gdgxk.py):jsglpt.gds.edu.cn 账号密码(base64)登录后换 ITLMS 票进 jsxx.gds.edu.cn,课程列表读 /uc/store/courseRegister;学习只跑 updateViewTime1 进度心跳(服务端按 lastViewTime 直接记账,不需要真播)。票按 ITLMS-xxx||ticketId 两段交给 jsxx.gds.edu.cn/nts/loginToStoreByAccount;登录要过 4 位图形验证码,用 ddddocr 离线识别并自动重试。配套 script/capture-jsglpt.py(抓包取证)与 script/test-gdgxk.py(接口自测);课程列表解析出培训年度 / 学时 / 完成状态,卡片显示成「2026年公需课 · 未完成 · 30 学时」
  • 统一日志源 runtime.log():控制台、内存(/api/log)、SQLite(/api/log/history)同一份内容;新增右上角「系统日志」弹窗查看 key=system 的启动/设置类日志
  • 设置保存、服务启动都写入统一日志;卡片「课程日志」和「系统日志」的「清空」会同时清内存和 SQLite
  • 前端:卡片状态徽标、学习进度、课程勾选、卡片内实时日志、测验 / 设置 / 系统日志弹窗全部保留;新增平台名徽标与空态引导

修复

  • 账号接口的错误分支引用未导入的 JSONResponse、删除账号引用未导入的 RUNS / RUNS_LOCK,登录失败 / 删除账号会 500:补齐导入
  • discover() 引用未定义的 detect_platform / has_study_marker 导致课程发现 NameError:代际识别集中到 platforms/detect.py,两代页面共用同一套判断
  • 系统设置里的大模型配置保存后重新打开会回到默认值:save_settings 写的是嵌套结构 {llm, study},load_settings 却按顶层字段读,现在改成从 llm 子对象读取
  • AI 答题整段同步等待导致浏览器 / 反代超时:/api/quiz/answer 改为流式(text/event-stream)分批返回,每批失败自动重试一次
  • AI 答题解析不出 JSON 时直接判空:补 max_tokens、空内容 / 非 JSON 抛错重试,并支持从自由文本、截断 JSON 里抠答案,多选连写(AC)也能识别
  • 控制台是 GBK 时日志里含 ⚠ 之类字符会让工作线程抛 UnicodeEncodeError:runtime.log 现在编码失败会自动降级

v0.0.1

首个版本。

新增

  • 学习方式配置:快速学习(只调 period/study/log 接口)/ 常规学习(开浏览器真播视频,1–5 路并发)
  • 常规学习:每路独立无头浏览器,可见性补丁防暂停;平台按课节顺序放行时自动降为 1 路,并把被指定的课时提到队首
  • 抓包模式:POST /api/capture 记录所有 XHR/fetch(不限域名),window.open 新开的标签页同样挂监听,结果落 data/capture-*.json
  • 界面:系统设置新增「学习方式」(模式 + 并发数),卡片「更多」菜单新增「抓包 / 停止抓包」

修复

  • 视频完成上报补齐学时台账 study/log/detail/save(stime/etime),完成包不再带 lastProgressTime
  • 培训里的「课程」容器节点(periodType 10/13)不会自动完成:补一发 isComplete,并用 PUT /course/front/calFinalScore 重算课程完成计数
  • 并发上报导致课程完成计数丢失(实测 14 个课时只记 11 个),现在每门课跑完自动重算