fix: 比赛结束后回退提交不再被静默丢弃

Merged
BO
2mo ago
+136-67

What does this PR aim to accomplish?:

修复 #1017:从已结束比赛的提交页提交时,#287 / #671 实现的"自动向原题提交"回退功能失效,页面显示"提交失败!请关闭脚本后重试!",且状态里没有新的提交记录。

回退路径本身是好的——它能正确检测 没有这个比赛!、抓取 contest.php、解析出真实题号(issue 附的日志里 Real PID: 1865 就是对的)。坏的是 POST 之后的部分:

  1. 内层 fetch 只 console.log(await Response.text()),从不判断提交是否成功。
  2. 回退分支后面没有 return/else,控制流无条件继续执行外层的失败分支,把状态覆盖成"提交失败"——即使回退提交成功了也一样。

至于"状态里没有提交记录":日志里那次回退提交收到的其实是 XMOJ 的提交冷却页。HUSTOJ submit.php 里有 $OJ_SUBMIT_COOLDOWN_TIME(默认 5 秒)的冷却检查,上游命中时返回 302,但 XMOJ 改成了直接渲染页面,.jumbotron 里写 请勿重复提交。所以 Response.redirected 是 false,响应体被丢掉,提交也就从未发生。顺序上比赛校验在冷却检查之前,因此第一次 cid+pid 的失败提交本身不会写 solution 表。

How does this PR accomplish the above?:

把回退逻辑抽成 SubmitToEndedContestProblem(Source, O2Switch, ReportStatus),返回 {Success, Message}:

  • 只把 redirected 当成功信号,成功后 return,外层不再覆盖状态。
  • 处理提交冷却:遇到 请勿重复提交 时等冷却过去再重试,3 秒一次、最多 5 次,期间在 ErrorMessage 里提示进度。这是 #1017 里"没有提交记录"的直接原因。
  • 失败时显示服务端原文,从响应的 .jumbotron 取,不再一律显示"提交失败!请关闭脚本后重试!"。
  • 遇到 验证码错误 立即 RefreshCaptcha("") 并停止重试——答案已经被消耗,重试没有意义。
  • 补上 GetCaptchaParameter():原回退提交没带 vcode,评测队列繁忙时会因缺验证码失败。
  • 题号改用 /\d+/ 提取:原来的 substring(2, 6) 会把 5 位题号截成 4 位(18650 → 1865),静默提交到错误的题目。
  • 补齐失败路径:contest.php 取不到、#problemset > tbody 不存在、题号解析不出来时都会显示原因并恢复提交按钮。原代码在这些分支直接 return,按钮会永远停在"正在提交..."。
  • 顺手移除了隐式全局变量 rPID,并把重复的错误显示代码收拢成 ShowSubmitStatus。

验证方式:node --check 通过;另外写了一个一次性回归脚本,从 XMOJ.user.js 里抽出 SubmitToEndedContestProblem 本体,用 mock 的 submit.php 响应跑 10 个场景(成功重定向、冷却后重试成功、冷却始终不放行、导航栏里的 status.php 不被误判成成功、带上 vcode、验证码错误、contest.php 不可达、缺 tbody、5 位题号、题号无数字),全部通过;并用 4 个 mutation 反向确认这些断言不是空跑。


By submitting this pull request, I confirm the following:

  1. I have read and understood the contributor's guide, as well as this entire template.
  2. I have commented on my proposed changes within the code.
  3. I have tested my changes.
  4. I am willing to help maintain this change if there are issues with it later.
  5. It is compatible with the GNU General Public License v3.0.
  6. I have squashed any insignificant commits.
  7. I have checked that another pull request for this purpose does not exist.
  8. I have considered and confirmed that this submission will be valuable to others.
  9. I accept that this submission may not be used, and the PR can be closed at the will of the maintainer.
  10. I give this submission freely and claim no ownership to its content.
  11. ⚠️ 未验证 —— 无法自行在浏览器里测试,请人工确认在新 UI 和旧/经典 UI 下均正常。
  12. ⚠️ 未验证 —— 未在真实的已结束比赛上做端到端提交(会产生真实提交记录),请人工复现 #1017 的步骤确认。

  • I have read the above and my PR is ready for review.

关于 #7:#1018 也在修这个 issue。那个 PR 目前仍然冲突、且 XMOJ.user.js 语法不通过,所以这里另开一个。如果 #1018 先修好,这个可以关掉。

🤖 Generated with Claude Code

Summary by Sourcery

确保已结束比赛的提交能够可靠回退到原题,并在冷却或失败时提供可操作的状态反馈。

Bug Fixes:

  • 修复比赛结束后回退提交被静默丢弃的问题,确保成功提交后正确跳转并保留提交记录。
  • 处理提交冷却、验证码错误及网络或页面解析失败,向用户显示明确的服务端错误信息并恢复提交按钮。
  • 修复五位题号解析错误,并确保回退提交携带验证码参数。

Enhancements:

  • 将结束比赛回退提交流程集中封装并统一返回结果,避免失败状态被外层逻辑覆盖。

Build:

  • 将项目及用户脚本版本更新至 3.6.5。

Chores:

  • 新增 3.6.5 发布条目。

Summary by cubic

Fixes the fallback that resubmits to the original problem after a contest ends, so ended-contest submissions no longer fail silently with a generic "提交失败" message and the status page now records the new submission. Closes #1017.

  • Extracts the fallback into SubmitToEndedContestProblem(), returns {Success, Message}, and only treats a redirect as success so the outer failure branch no longer overwrites the status.
  • Retries up to 5 times with 3-second waits when the server returns the cooldown page (请勿重复提交), which previously discarded the response and never created a submission.
  • Re-checks the captcha with CaptchaIsMissing() before every retry POST, so an empty answer sent during the cooldown wait can't make the server swap the 4-digit captcha for an 8-digit one; its message and button restoration are kept intact.
  • Wraps both fetch calls in try/catch, so a network error returns an error message instead of escaping the click handler and leaving the button stuck on "正在提交...".
  • Shows the server's own message from .jumbotron on failure instead of the generic error.
  • Refreshes the captcha immediately on 验证码错误 and stops retrying, because the answer is already consumed.
  • Adds the missing GetCaptchaParameter() to the fallback POST body.
  • Extracts real problem IDs with /\d+/ instead of substring(2, 6), which truncated 5-digit IDs.
  • Restores the submit button when page or problem-ID parsing fails; earlier those branches returned early and left it stuck on "正在提交...".
  • Bumps the script and package.json versions to 3.6.5 and adds the release entry to Update.json.

Summary by Sourcery

确保已结束比赛的提交能够可靠回退到原题,并在冷却或失败时提供可操作的状态反馈。

Bug Fixes:

  • 修复比赛结束后回退提交被静默丢弃的问题,确保成功提交后正确跳转并保留提交记录。
  • 处理提交冷却、验证码错误及网络或页面解析失败,向用户显示明确的服务端错误信息并恢复提交按钮。
  • 修复五位题号解析错误,并确保回退提交携带验证码参数。

Enhancements:

  • 将结束比赛回退提交流程集中封装并统一返回结果,避免失败状态被外层逻辑覆盖。

Build:

  • 将项目及用户脚本版本更新至 3.6.5。

Chores:

  • 新增 3.6.5 发布条目。