Skip to content

修复瀑布分页课程加载、教学班数据未就绪误点和重复日志刷屏问题#25

Open
shuichangdonghly wants to merge 3 commits into
ceilf6:masterfrom
shuichangdonghly:add-new-feature
Open

修复瀑布分页课程加载、教学班数据未就绪误点和重复日志刷屏问题#25
shuichangdonghly wants to merge 3 commits into
ceilf6:masterfrom
shuichangdonghly:add-new-feature

Conversation

@shuichangdonghly

Copy link
Copy Markdown

PR 标题建议
修复瀑布分页课程加载、教学班数据未就绪误点和重复日志刷屏问题

改动概述
本次修改主要增强 courseGrabber.js 在正方教务“自主选课”瀑布分页页面中的稳定性,解决目标课程位于“查看更多”后方、多教学班过滤、教学班详情未加载完成时误点击等问题。

主要修改

  1. 支持自动加载“查看更多”
  • 当目标课程尚未出现在当前已加载 DOM 中时,脚本会自动调用页面已有的 loadCoursesByPaged() 或点击 #more
  • 新增加载节流,避免多个课程同时触发重复加载。
  • 新增 grab.loadMore() / grab.loadMore("课程号") 便于手动调试。
  • grab.status() 增加已加载课程数、未加载目标课程等状态信息。
  1. 修复自动刷新导致课程列表回到前 10 门
  • 原脚本周期性触发搜索刷新,会把已经通过“查看更多”追加出来的第 60/70/80 门课程重置掉。
  • 现在检测到瀑布分页环境后,不再周期性触发全局搜索刷新,改为在当前已加载列表中持续监控。
  1. 改进课程匹配和展开逻辑
  • 课程匹配不只依赖隐藏 kch_id,也兼容标题中的显示课程号、data-kch_id 和课程名称。
  • 目标课程已加载但教学班未展开时,会主动点击课程头展开详情。
  • 即使没有时间/教师过滤器,也会展开目标课程,以便加载真实教学班详情和容量。
  1. 避免教学班详情未加载完成时误点击
  • 新增教学班数据就绪检查。
  • 容量、提交用的 do_jxb_id 未加载完成时不会点击选课。
  • 修复 42/0、未知教师、未知时间等占位状态被误判为可选的问题。
  1. 修复容量判断
  • 不再只看“已满”标签是否显示。
  • 新增容量解析,已选/容量 中容量为 0 或已选人数大于等于容量时判定为不可选。
  • 例如 139/48 会被判定为暂无余量,继续监控而不是点击。
  1. 增强失败弹窗处理
  • 识别“出现未知异常 / 请与管理员联系 / 选课失败”等弹窗。
  • 自动关闭失败弹窗并释放 selecting 状态,避免脚本卡住。
  • 关闭弹窗前先 blur() 当前焦点,缓解 aria-hidden focus 警告。
  1. 降低重复日志刷屏
  • 对“找到 N 个教学班”、过滤器检查、满员教学班监控等重复日志做节流。
  • 满员教学班仍会持续监控,但不会每秒刷完整过滤过程。
  • 只有容量变化或达到一定间隔时才输出提示。

验证

  • 已使用 node --check js/courseGrabber.js 做语法检查,通过。

@ceilf6

ceilf6 commented Jul 8, 2026

Copy link
Copy Markdown
Owner

@ceilf6/repo-guard CR

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ ceilf6/repo-guard

代码评审报告: 修复瀑布分页课程加载、教学班数据未就绪误点和重复日志刷屏问题

风险等级:
处理建议: 请求修改
决策摘要: ** 无法评审——PR 声称修改 courseGrabber.js(+2483 -2767),但提供的 diff 内容为空,没有任何可审查的代码变更证据。

级联分析

  • 变更符号: 原始模型未提供结构化级联字段。
  • 受影响流程: 原始模型未提供结构化级联字段。
  • 变更集外调用方: unknown
  • 置信度: degraded

问题发现

  1. [高] 变更规模巨大且几乎全量重写,缺乏关联 issue 与充分验证证据
    • 证据: +2483 -2767 对单文件意味着近乎整体重写;描述未发现关联 issue;唯一验证仅为 node --check(语法检查),未覆盖任何行为、边界或回归路径。
    • 受影响调用方/流程: 该脚本运行在正方教务真实选课链路上,涉及不可逆的选课提交(do_jxb_id)、容量判定和失败弹窗处理,行为回归风险直接影响用户选课结果。
    • 最小可行修复: 提供 diff 后,需针对以下高风险路径补充验证证据:容量解析(139/4842/0、容量为 0)、教学班就绪判断、误点击防护、自动加载节流。语法检查不足以支撑该风险等级的合入。

行级发现

  • 无明确变更行归属。

Karpathy 评审

  • 假设: 模型输出需要归一化为固定 Markdown 契约。
  • 简洁性: 已提取 summary、finding、evidence 与 fix;原始 prose 不再附在评论中,避免占用下游解析与代理上下文。
  • 变更范围: 原始模型未提供结构化范围字段。
  • 验证: 需要查看 CI、测试或人工 CR 证据补强合并信心。

缺失覆盖

  • 输出未命中 Repo Guard Markdown 契约;建议补充真实模型质量评估覆盖。

@ceilf6

ceilf6 commented Jul 8, 2026

Copy link
Copy Markdown
Owner

@ceilf6/repo-guard CR again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ ceilf6/repo-guard

代码评审报告: 修复瀑布分页课程加载、教学班数据未就绪误点和重复日志刷屏问题

风险等级:
处理建议: 需要人工判断
决策摘要: 功能修复方向合理,但整个文件被 2/4 空格缩进风格重排(+2483/-2767),真实逻辑变更被淹没在格式噪声里无法可靠审阅;建议先剥离格式改动再评审,否则合并风险不可控。

级联分析

  • 变更符号: isCourseCode, expandCourseByCode, findLoadedCourseHeader, requestLoadMoreCoursesForTarget, shouldSkipPeriodicRefresh, parseCapacityText, getTeachingClassReadiness, checkTeachingClassCapacity, findSelectionFailureMessage, closeVisibleAlertDialog, shouldLogState, dropCourse, selectTeachingClass
  • 受影响流程: 浏览器控制台注入运行的自动抢课单文件脚本;无外部导入方
  • 变更集外调用方: 无(单文件用户脚本,window.grab 为唯一外部入口)
  • 置信度: degraded(diff 被截断且被全局重格式化,无关联 issue,无法确认所有行为变更)

问题发现

  1. [高] 全局缩进风格重排淹没真实逻辑变更

    • 证据: 单文件 +2483 -2767,整段从 2 空格重排为 4 空格;safeFilter/safeMap 被删除并内联重写。
    • 受影响调用方/流程: 整个脚本;无法把行为变更与格式噪声分离,评审信心与回归定位能力显著下降。
    • 最小可行修复: 拆成两个提交/PR——一个纯格式化、一个纯逻辑修复;或保持原缩进只提交逻辑 diff。
  2. [中] isCourseCode 正则收窄可能破坏字母数字课程号匹配

    • 证据: 旧实现 ^[A-Za-z0-9_-]+$ 且含数字;新实现 ^\d+$(纯数字)。
    • 受影响调用方/流程: expandCourseByCodefindLoadedCourseHeaderisRowMatchingCoursefindAllTeachingClasses 全部依赖此判断;2328A009/CS102 这类课程号会被当作课程名走模糊匹配,可能误匹配或漏匹配。
    • 最小可行修复: 恢复兼容字母数字课程号的判断,或明确说明新版页面课程号一定为纯数字并在 PR 中给出依据。
  3. [中] 删除 safeFilter/safeMap 的防污染保护但未评估回归

    • 证据: 原代码显式保存原生 Array.prototype.filter/map 引用以防页面污染;本 PR 删除并改回内联手写循环。
    • 受影响调用方/流程: safeParseFilterInput 及所有依赖数组操作的过滤逻辑。手写循环虽避开 Array.prototype,但删除注释中声明的“防页面污染”设计意图属于安全属性回退。
    • 最小可行修复: 保留 PR 描述说明为何该保护不再必要,或恢复原生方法引用缓存。
  4. [中] dropCourse 退安全逻辑疑似被回退

    • 证据: 旧版对课程名匹配到多个已选课程时放弃退选(请改用课程号避免误退);新版 diff 中 dropCourse 直接取第一个含“退选”文本的教学班,未见多匹配保护。
    • 受影响调用方/流程: 替换/退选流程存在误退非目标课程的风险,这是不可逆写入。
    • 最小可行修复: 确认并恢复课程名多匹配时的放弃退选保护。

行级发现

  • [courseGrabber.js:245] isCourseCode 从兼容字母数字课程号收窄为 ^\d+$;若正方新页面存在字母数字课程号会走错匹配分支,请确认页面课程号格式或恢复兼容判断。
  • [courseGrabber.js:110] 内联重写的 safeParseFilterInput 删除了原有防 Array.prototype 污染的原生方法缓存设计;请在描述中说明该保护为何可移除,或保留缓存。
  • [courseGrabber.js:496] getTeachingClassReadinesscapacityInfo.total <= 0 判断“未加载完成”,但真实容量为 0(如 x/0)也会被永久判为“未就绪”而持续监控;确认这是否为期望行为。
  • [courseGrabber.js:539] checkTeachingClassCapacity 现同时依赖 parseCapacityText.full 元素;139/48 这类超额场景由 capacityInfo.fullselected >= total)拦截,确认解析对 已满 文本和纯数字都覆盖。

Karpathy 评审

  • 假设: 假设选课页一定是瀑布分页(jspage/isEnd/more/loadCoursesByPaged);shouldSkipPeriodicRefresh 在检测到分页环境后完全停止周期刷新,若某些页面结构不同可能导致容量永不更新。
  • 简洁性: selectTeachingClass 内嵌多层 setTimeout(点击→确认→再确认→验证)已深度嵌套,本 PR 又叠加失败检测分支,回调金字塔进一步加深,难以推理与验证。
  • 结构质量: 单文件已超 1400 行且本 PR 未拆分;新增 readiness/capacity/failure/loadMore 等关注点仍全部堆在同一 IIFE 内,缺乏模块边界。
  • 变更范围: 严重超范围——逻辑修复与全文件重格式化混在一个 PR,违反 surgical change 原则。
  • 验证: 仅 node --check 语法检查,没有任何针对容量解析、readiness、loadMore 节流、退选多匹配的行为测试;这些正是本 PR 的核心风险点。

缺失覆盖

  • parseCapacityText已满139/48x/0未知容量 的单元覆盖。
  • getTeachingClassReadinessdo_jxb_id 缺失、容量占位、时间/教师占位时返回“未就绪”的验证。
  • isCourseCode 收窄后对字母数字课程号的匹配回归验证。
  • dropCourse 在课程名匹配到多个已选课程时不误退的验证。
  • shouldSkipPeriodicRefresh 在非分页页面下仍能正常刷新的验证。

因 diff 被截断且全文件重格式化,selectTeachingClass 后半段、抢课主循环、UI 与调度逻辑无法完整审阅,剩余回归风险为 degraded;合并前建议先分离格式化提交并补充上述行为测试。

Comment thread courseGrabber.js
lastAttempt: 0,
});

function forceExpandTargetCoursesAggressive() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isCourseCode 从兼容字母数字课程号收窄为 ^\d+$;若正方新页面存在字母数字课程号会走错匹配分支,请确认页面课程号格式或恢复兼容判断。

Comment thread courseGrabber.js
kcmcSpan?.querySelector("a")?.textContent?.trim() ||
kcmcSpan?.textContent?.trim() ||
"";
const raw = input.trim();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

内联重写的 safeParseFilterInput 删除了原有防 Array.prototype 污染的原生方法缓存设计;请在描述中说明该保护为何可移除,或保留缓存。

Comment thread courseGrabber.js
// 教师模糊匹配函数
function matchesTeacherFilter(teacher, teacherFilter) {
// 如果没有配置过滤器,返回true(不过滤)
if (!teacherFilter || teacherFilter.length === 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getTeachingClassReadinesscapacityInfo.total <= 0 判断“未加载完成”,但真实容量为 0(如 x/0)也会被永久判为“未就绪”而持续监控;确认这是否为期望行为。

Comment thread courseGrabber.js
// 查找包含目标课程的所有元素
const allElements = document.querySelectorAll("*");
let courseSection = null;
/**

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checkTeachingClassCapacity 现同时依赖 parseCapacityText.full 元素;139/48 这类超额场景由 capacityInfo.fullselected >= total)拦截,确认解析对 已满 文本和纯数字都覆盖。

@shuichangdonghly

Copy link
Copy Markdown
Author

等我晚上看一下 主要我还不太熟悉git这些

@ceilf6

ceilf6 commented Jul 8, 2026

Copy link
Copy Markdown
Owner

🛡️ ceilf6/repo-guard

代码评审报告: 修复瀑布分页课程加载、教学班数据未就绪误点和重复日志刷屏问题

风险等级:
处理建议: 请求修改
决策摘要: ** 无法在当前证据下给出安全合并结论——diff 被截断到无法看到后半段(选课主循环、失败弹窗处理、容量判断修复等核心逻辑均在截断部分),且没有关联 issue 和自动化测试佐证行为变更,仅凭 node --check 语法检查不足以验证这类高风险浏览器自动化脚本的正确性。

级联分析

  • 变更符号: 原始模型未提供结构化级联字段。
  • 受影响流程: 原始模型未提供结构化级联字段。
  • 变更集外调用方: unknown
  • 置信度: degraded

问题发现

  1. [高] isCourseCode 判定放宽后可能导致课程名称误判为课程号,改变匹配路径
    • 证据: 旧实现 isCourseCode/^\d+$/(纯数字),新实现改为 /^[A-Za-z0-9_-]+$/.test(value) && /\d/.test(value),即任何含数字和字母/下划线/连字符的字符串都会被当作课程号处理(例如 CS1022024级这类夹杂数字的课程简称)。
    • 受影响调用方/流程: expandCourseByCodefindAllTeachingClassesisRowMatchingCoursedropCourse 内的退选安全判断(!isCourseCode(courseCode) && rows.length > 1 的多选保护逻辑)全部依赖这个判定分支。
    • 最小可行修复: 在 PR 描述或注释中说明该放宽的动机(支持 2328A009 类课程号),并为 dropCourse 补充针对字母数字混合课程号的回归用例,确认误判不会导致退选逻辑跳过多匹配保护。
  2. [高] diff 在选课主循环和失败弹窗处理处被截断,PR 描述的核心修复点无法验证
    • 证据: PR 描述第 4-6 点(教学班就绪检查阻止误点击、容量解析判定 139/48 不可选、失败弹窗自动关闭并 blur())对应的代码位于 attemptGrabSingleCourse 主循环和被截断部分,评审只能看到 selectTeachingClass 中新增的 findSelectionFailureMessage/closeVisibleAlertDialog,看不到主循环如何调用 getTeachingClassReadinessparseCapacityText 的完整链路。
    • 受影响调用方/流程: 抢课主循环是否真的在点击前调用了就绪检查,是否真的用新容量解析替换了旧的仅看 .full 元素判断,都无法从现有 diff 证实。
    • 最小可行修复: 提供完整未截断 diff 或指向具体代码位置,确认 attemptGrabSingleCourse 中已经用 getTeachingClassReadiness(teachingClass, courseCode).ready 门禁了点击调用,并用 parseCapacityText(...).full 替换了旧的 checkTeachingClassCapacity 单一判断(当前两者是否共存、优先级如何仍不清楚)。
  3. [中] checkTeachingClassCapacity 与新增 parseCapacityText/getTeachingClassReadiness 存在重复且部分冲突的容量判断路径
    • 证据: checkTeachingClassCapacity 仍然存在(第 786-807 行附近),且内部已经改为先调用 parseCapacityText 判断 capacityInfo.full,再退化检查 .full 元素可见性;同时 getTeachingClassReadiness 也单独解析容量(capacityInfo.total <= 0 判不就绪)。两处都在解析同一份 teachingClass.info.capacity 文本,但返回语义不同(一个是"能否点击",一个是"数据是否就绪"),如果调用顺序不当,可能出现"数据未就绪"和"已满不可选"两种状态被同一段主循环代码误合并处理。
    • 受影响调用方/流程: attemptGrabSingleCourse 主循环(不可见部分)如何组合使用这两个函数。
    • 最小可行修复: 明确两个函数的调用顺序和职责边界(就绪检查应先于容量判断执行),或合并为单一状态机式的返回值(unready | full | available),避免未来维护者混淆。
  4. [中] findSelectionFailureMessage 依赖弹窗文本关键词匹配,与已有 checkTimeConflictWarning 的弹窗遍历逻辑重复但未合并
    • 证据: 两个函数都在遍历 document.querySelectorAll('.modal, .bootbox, ...') 或类似选择器查找可见弹窗文本,checkTimeConflictWarning 用的是 .modal, .dialog, .alert, [role="dialog"], [role="alert"]findSelectionFailureMessage 用的是 .modal, .bootbox, .alert, [role="dialog"], [role="alert"],选择器不完全一致(一个包含 .dialog 不含 .bootbox,另一个相反),容易导致同一个真实弹窗被其中一个函数捕获、另一个漏判。
    • 受影响调用方/流程: selectTeachingClass 中先后调用 findSelectionFailureMessage()checkTimeConflictWarning(),如果两者对"当前弹窗是什么"判断不一致,可能出现失败弹窗被误判为无害、或反之。
    • 最小可行修复: 抽取一个统一的"查找当前可见对话框"辅助函数,两处复用同一选择器集合,避免选择器漂移导致漏判。
  5. [低] parseCapacityText已满 分支返回 selected: 0, total: 0,与"容量未加载"状态返回值相同,可能被上游误判为同一情况
    • 证据: parseCapacityText已满 分支返回 { valid: true, selected: 0, total: 0, full: true },而正则不匹配(容量未加载)时返回 { valid: false, selected: 0, total: 0, full: false }。两者 selected/total 字段相同,唯一区别是 validfull。如果后续代码只检查 total <= 0 而不检查 valid/full,容易把"已满"误判为"未加载完成"(继续监控而非跳过),或反之。
    • 受影响调用方/流程: getTeachingClassReadinesscapacityInfo.total <= 0 的判断先于 full 检查,如果是"已满",也会被判定为"容量信息尚未加载完成",导致就绪检查报错信息误导(日志会打印"容量尚未加载完成"而非"该班已满")。
    • 最小可行修复: 在 getTeachingClassReadiness 中先检查 capacityInfo.full,再检查 total <= 0,避免"已满"状态被误报为"未加载完成"。

行级发现

  • [courseGrabber.js:150] isCourseCode正则从纯数字放宽为字母数字混合(含_/-),会把更多字符串误判为课程号分支,改变 dropCourse/findAllTeachingClasses` 的匹配路径,需要补充回归验证(见 Findings 希望增加根据教师姓名或特定时间进行抢课 #1)。
  • [courseGrabber.js:786] checkTeachingClassCapacity复用了parseCapacityText,但函数依然保留旧的 .full` 元素可见性检查作为补充判断;两套容量判定并存,建议明确谁是权威来源,避免后续维护者困惑(见 Findings 教师提取错误 #3)。
  • [courseGrabber.js:659] findSelectionFailureMessage 的弹窗选择器(.modal, .bootbox, .alert, [role="dialog"], [role="alert"])与 checkTimeConflictWarning 的选择器(.modal, .dialog, .alert, [role="dialog"], [role="alert"]`)不一致,存在同一弹窗被其中一个函数漏判的风险(见 Findings 正方选课系统重写filter参数错误 #4)。
  • [courseGrabber.js:677] closeVisibleAlertDialog在点击关闭按钮前对document.activeElement调用blur(),用于缓解 aria-hidden警告;这段逻辑本身合理,但仅在dialog.contains(document.activeElement)时触发,如果焦点元素在 dialog 外部但仍会因 dialog 被隐藏触发aria-hidden` 警告,该修复覆盖面不完整——非阻塞,可在后续迭代观察实际效果。

Karpathy 评审

  • 假设: 模型输出需要归一化为固定 Markdown 契约。
  • 简洁性: 已提取 summary、finding、evidence 与 fix;原始 prose 不再附在评论中,避免占用下游解析与代理上下文。
  • 变更范围: 原始模型未提供结构化范围字段。
  • 验证: 需要查看 CI、测试或人工 CR 证据补强合并信心。

缺失覆盖

  • 输出未命中 Repo Guard Markdown 契约;建议补充真实模型质量评估覆盖。

@ceilf6

ceilf6 commented Jul 8, 2026

Copy link
Copy Markdown
Owner

🛡️ ceilf6/repo-guard

代码评审报告: 修复瀑布分页课程加载、教学班数据未就绪误点和重复日志刷屏问题

风险等级:
处理建议: 需要人工判断
决策摘要: 这是一次单文件、2483/-2767 行的全量重写(缩进风格从 4 空格改为 2 空格叠加大量逻辑变更),diff 因超过 100KB 被截断,我无法看到完整变更内容(尤其是 attemptGrabSingleCourse 循环、容量判断落地处、失败弹窗处理的完整实现,以及文件后半部分是否还有类似规模的改动)。在证据不完整的情况下,不能对这类高改动量、无自动化测试、纯浏览器油猴脚本给出批准。

级联分析

  • 变更符号: isCourseCode(正则从 ^\d+$ 放宽为 ^[A-Za-z0-9_-]+$ 且要求含数字)、expandCourseByCode/findMatchingCourseHeads(新增)、findAllTeachingClasses(新增课程头 section 兜底路径)、checkTeachingClassCapacity(新增 parseCapacityText)、dropCourse(新增 findSelectedCourseRows/isTeachingClassMatchingIdentifier)、新增 requestLoadMoreCoursesForTarget/isCoursePagingReady/shouldSkipPeriodicRefresh
  • 受影响流程: 抢课主循环 attemptGrabSingleCourse(本次截断内容中未展示完整实现,但看得到它是判断入口)、课程列表刷新逻辑、退选逻辑、选课确认/失败弹窗处理。
  • 变更集外调用方: 此脚本是独立油猴脚本,无其他仓库代码调用,因此“变更集外调用方”主要指用户手动调用的 grab.loadMore() / grab.status() 等公开 API(PR 描述提到新增,但 diff 中未展示这些函数定义,无法确认是否正确挂载到 grab 对象上)。
  • 置信度: degraded

问题发现

  1. [高] isCourseCode 判定放宽可能引发误判,影响下游所有课程号匹配逻辑

    • 证据: 旧实现 isCourseCode/^\d+$/.test(...)(纯数字);新实现改为 /^[A-Za-z0-9_-]+$/.test(value) && /\d/.test(value),即只要包含字母数字下划线连字符且至少一位数字就判定为“课程号”而非“课程名称”。
    • 受影响调用方/流程: expandCourseByCodeisRowMatchingCoursefindAllTeachingClassesdropCourseisTeachingClassMatchingIdentifier 均依赖该函数决定走“课程号精确匹配”还是“课程名称模糊匹配”分支。如果用户配置了形如 CS1022328A009 之类课程号,这个改动是合理的(PR 描述也提到要兼容这类格式);但如果用户误输入英文课程名缩写(如 AI2),会被错误当成课程号处理,导致匹配失败或匹配到错误课程。
    • 最小可行修复: 保留新逻辑的同时,在文档/注释中明确“课程号”定义扩展的边界,并考虑给纯数字课程号保留一条更严格的快速路径,避免边界输入的歧义。
  2. [高] 容量判断逻辑变更未见完整落地位置,PR 描述与已展示 diff 不完全一致

    • 证据: PR 描述第 5 点声称"新增容量解析,已选/容量中容量为 0 或已选人数大于等于容量时判定为不可选",diff 中确实新增了 parseCapacityText 函数,且 checkTeachingClassCapacity 已经用 capacityInfo.full 替换了纯粹依赖 .full DOM 元素可见性的旧逻辑。这部分改动本身是好的、有证据支持的结构性改进。但 diff 在此处发生截断,attemptGrabSingleCourse 函数体(判断“是否可选课”的最终调用点)未展示完整,无法确认 checkTeachingClassCapacity 的返回值是否被正确消费,也无法确认 PR 描述中“139/48 判定为暂无余量”的具体分支逻辑落地是否正确。
    • 受影响调用方/流程: 抢课主循环,直接决定是否点击“选课”按钮——这是本次改动风险最高的路径,误判会导致误点击或错过可选窗口。
    • 最小可行修复: 需要作者提供完整 diff 或补充截断部分内容,重点复核 attemptGrabSingleCourse 中容量判断与教学班就绪检查(getTeachingClassReadiness)的组合调用顺序,确保二者不会互相绕过。
  3. [中] dropCourse 重写引入了课程名称多重匹配时的放弃退选保护,但缺少同等测试验证

    • 证据: 新增 findSelectedCourseRowsisTeachingClassMatchingIdentifier,并在课程名称匹配到多个已选课程行时主动放弃退选("已放弃退选;请改用课程号避免误退")。这是一个好的防御性改动,方向正确。
    • 受影响调用方/流程: 换课/替换课程场景。由于只有 node --check 语法检查,没有任何单元测试或集成测试验证这条新分支的实际触发条件(例如 DOM 结构变化后 .outer_xkxx_list 选择器是否仍然准确),这是一个纯手工推理未经验证的路径。
    • 最小可行修复: 若无法在真实教务系统上做集成测试,至少应在 PR 描述中明确指出此路径未经真实环境验证,降低误导性的"验证"章节表述(目前验证章节只提到语法检查,未提及功能验证,读者需要额外注意)。
  4. [中] 周期性刷新被永久关闭(在瀑布分页环境下),可能引入新的静默失效场景

    • 证据: shouldSkipPeriodicRefresh 一旦检测到 isCoursePagingReady() 为 true,就永久返回 true,注释解释是为了避免刷新把已追加的第 60/70/80 门课程重置回前 10 门。这是合理的根因修复。但函数名为 shouldSkipPeriodicRefresh,目前的实现是“只要处于分页环境就永远跳过”,没有任何后备机制在长时间无变化时done一次强制刷新来发现容量归零之外的其他状态变化(例如课程被下架、选课窗口关闭)。
    • 受影响调用方/流程: 主抢课循环的强制刷新分支。
    • 最小可行修复: 非阻塞项,可在后续迭代加一个低频(如 5-10 分钟一次)的安全刷新,或在检测到长时间无教学班变化时提示用户手动刷新页面。

行级发现

  • [courseGrabber.js:60] isCourseCode 判定放宽为字母数字下划线连字符+至少一位数字,会把非纯数字的课程名称误判为课程号,建议为该函数补充边界注释或保留对纯数字课程号的快速判断路径。
  • [courseGrabber.js:465] parseCapacityText已满 文本返回 {selected: 0, total: 0, full: true},但对不匹配任何格式的异常文本(如空字符串以外的乱码)返回 {valid: false},调用方 checkTeachingClassCapacity!capacityInfo.valid 时直接返回 false(视为不可选),这个"未知即不可选"的保守策略是合理的,但请确认没有把正常的"容量还未渲染完成的中间态"误判为永久不可选(应结合 getTeachingClassReadiness 的容量校验一起看)。
  • [courseGrabber.js:826] dropCourse 中新增的多匹配放弃退选逻辑分支较长,建议补充一个专门的单元可测试点(例如导出 findSelectedCourseRows 供 headless 测试),避免只能靠人工在真实教务系统上验证。

Karpathy 评审

  • 假设: PR 假设所有正方教务系统的瀑布分页 DOM 结构(#jspage#isEnd#moreloadCoursesByPaged())在目标学校环境中稳定存在;假设 .rsxx/.jxbmc/.sksj 等 class 命名在所有部署实例中一致。这些假设未见于任何自动化验证,纯靠开发者经验,风险较高但对油猴脚本这类场景是可接受的常见做法。
  • 简洁性: 大量兜底/多层 fallback(findAllTeachingClasses 有三种查找方式、extractTeachingClassInfo 有类名优先+全文本兜底两套解析),复杂度确实是应对正方系统 DOM 不稳定性的必要代价,但导致单文件继续膨胀(本身已经是接近 1500 行的单文件,未做任何模块拆分)。
  • 结构质量: 该文件是自包含 IIFE,不涉及模块边界问题,但内部函数数量已经很多且互相耦合(状态对象 courseStatescourseListEndLogged 等模块级可变状态分散在多处修改),随着功能增加可维护性会持续下降。本次 PR 没有让结构变差,但也没有借机做任何拆分。
  • 变更范围: 本次 diff 几乎重写了整个文件的缩进风格(4 空格 → 2 空格),把真正的功能变更淹没在大量格式噪声里,这直接导致我在评审时无法有效区分“这一行是格式改动”还是“这一行是逻辑改动”。这是本次评审最大的可执行性问题:范围蔓延已经影响了评审质量本身。
  • 验证: 仅执行 node --check(语法检查),未提供任何函数级测试、DOM mock 测试或真实环境截图/日志验证。对于涉及"是否点击选课按钮"这种高风险副作用的逻辑改动(尤其是容量判断和数据就绪检查),语法检查完全不能代表行为正确性。

缺失覆盖

  • 缺少针对 parseCapacityText 的单元测试(覆盖 已满0/5050/5010/50、空字符串、乱码等边界输入)。
  • 缺少针对 isCourseCode 边界输入的测试(纯数字、字母数字混合课程号、纯中文课程名、含数字的中文课程名如"C语言程序设计2")。
  • 缺少针对 getTeachingClassReadiness 在各字段缺失组合下的行为验证,这是"避免误点击"这一 PR 核心目标的直接验证点,目前完全没有测试覆盖。
  • 建议提供完整(非截断)diff 或分文件/分 commit 提交,以便完整评审 attemptGrabSingleCourse 主循环和文件后半部分(约 1000-1479 行区间标注为变更但本次未展示的内容)。
  • 建议将格式化改动(缩进风格调整)与功能变更拆分为两个独立 commit 或 PR,以降低评审噪声,提高问题可追溯性。

@shuichangdonghly

Copy link
Copy Markdown
Author

已根据 review 反馈补充修改并 push 了新 commit,主要收敛了 isCourseCode 边界、增加 selfTest,并补充分页刷新跳过提示。主要我是学生,改的比较慢,谢谢。

@ceilf6

ceilf6 commented Jul 10, 2026

Copy link
Copy Markdown
Owner

已根据 review 反馈补充修改并 push 了新 commit,主要收敛了 isCourseCode 边界、增加 selfTest,并补充分页刷新跳过提示。主要我是学生,改的比较慢,谢谢。

你好,你在你们学校的选课网站上测过了吗?因为我们学校选课窗口已经过了、无法进行测试

@shuichangdonghly

Copy link
Copy Markdown
Author

测试成功了我才发的 主要当时就是感觉需要课程出现才能选课特别麻烦 就想改成自动点击“显示更多课程” 然后试了一下可以就提交了

@shuichangdonghly

Copy link
Copy Markdown
Author

已根据评审意见追加修改:

  1. dropCourse 改为优先从右侧已选课程区定位退选按钮。
  2. 课程名匹配到多个已选课程/多个教学班时会放弃退选,避免误退。
  3. 新增容量和教学班就绪判断测试,覆盖 139/4842/0、未知容量、缺 do_jxb_id、可见 .full 标记等场景。
  4. 保持 isCourseCode 为纯数字判断,避免 C语言程序设计2 / AI2 被误判为课程号。

验证:

  • node --check courseGrabber.js
  • node --test tests\courseGrabber-capacity-readiness.test.mjs tests\courseGrabber-selected-courses.test.mjs

说明:完整 UI 合约测试中仍有 2 个旧 UI 主题相关失败,经确认是当前 HEAD 既有问题,不属于本次抢课逻辑修复范围。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants