Commit Graph
14 Commits
Author SHA1 Message Date
xuyueandClaude Opus 5.5 83b6b7a81a fix(题目): 修掉代码审查查出的几处问题——流程图拿到的徽章不弹、点评弹窗读失败时关不掉等
- 流程图画到 A / S 拿到的题单徽章、成就原来不弹:流程图那条 WebSocket 只认评分完成 / 失败,
  成就消息被丢掉了,要等下次换路由拉 pending 才出来
- 强制点评弹窗读不出点评、或者一直交不上时,「下一题 / 继续」按钮也给出来:原来弹窗关不掉,
  只能按浏览器后退;拦下换题时就记上「继续」,按钮文字不会写错
- 「放回编辑器」走正常的换语言路子(画流程图时切回代码编辑器、读那门语言的草稿);
  这道题已经不收的语言(0020 摘掉的 Java / JS / Go)置灰,只能复制
- 举着手时刚交对、1.5 秒内点下一题:原来「确定要换题吗」要问两遍(点评完再问一次),
  现在同意过的那个地址不再问
- 课堂看板上评分中、评失败的流程图不再算「尝试过」,和学生的课堂条一个口径;
  新加的流程图查询和其他查询并行,不再多等一轮数据库
- 课堂条的数据只认最后发出的那次请求:进页面那次后到的话会把刚做完的那道盖回「没做」
- 「A / S 算做完」收进契约,前后端只留一份;协作中老师的判断统一用 useTeacherCollab;
  几处放错位置、说法过时的注释

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 19:35:07 +08:00
xuyueandClaude Opus 5.5 a538e60e28 feat(题目): 手机上页签行的「⋯」里能直接复制、重置代码、去自测猫,不用先切到「代码」页签
Deploy / deploy (push) Canceled after 0s
- 「⋯」分两组:统计 / 点评 / 我的提交,分隔线,再是去自测猫 / 复制代码 / 重置代码
  (老师还有课堂统计)。原来后几项在「代码」页签工具栏的「更多」里,代码页签那一行
  现在只剩语言选择
- 菜单抽成 useEditorMenu,桌面工具栏的「更多」和手机的「⋯」共用一份;「课堂统计」弹窗
  挂在页面上,两个入口开的是同一个
- 草稿键抽成 useDraftKey:编辑器存取草稿、「重置代码」删草稿用同一份算法。原来键在编辑器里拼、
  当 prop 传给工具栏,挪出去之后再拼一份迟早对不上(题单入口还多了一种)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 19:09:06 +08:00
xuyueandClaude Opus 5.5 9110c678fc feat(题目): 手机上「运行例子 / 提交」固定在屏幕底部,看着题目也能直接交
- 两个 44px 高的大按钮固定在底部,三个页签下都在;原来它们在「代码」页签顶上,
  和语言、更多挤成两行,看着题面想交一次还得先切回代码页签
- 「代码」页签的编辑器一直挂着(原来切走就卸载):没打开过代码页签也能交,撤销历史也不丢
- 编辑器高度让出底部那条,最后几行不再被按钮盖住

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 18:33:17 +08:00
xuyueandClaude Opus 5.5 0c2ff68e9b feat(协作): 老师协作时右栏顶上一条协作条,结果页签先摆学生最近一次交的,抽屉变成「他的提交」
- 协作条:「正在帮 谁 · 语言 · 看他交过什么 ↗ · 结束协作」,从工具栏里挪出来,一眼看得出
  编辑器里是学生的代码。「看他交过什么」带上学生用户名、开新标签(原来只带题号,还得再筛一遍)
- 老师接单后「结果」页签原来是空的:现在摆学生在这道题上最近一次交的结果,可刷新;
  不给 AI 提示(那是按登录的人自己的失败次数解锁的)
- 抽屉「我的提交」在协作中变成「他的提交」,列的是学生的提交,只能复制、不能放回编辑器
  (放回去会经协作通道直接盖掉学生正在写的代码),也不显示老师自己的排名
- 学生排队求助中、或者老师正在帮他时换题:先确认一句。换题会断开协作 / 撤掉举手,
  原来一点「下一题」就把正在帮他的老师踢出去了
- 求助按钮文案:「举手求助」→「已举手 · 前面 2 人」→「某某 老师在帮你」
- 提交列表加 exactUsername=1:用户名精确匹配。原来只有「包含」,ks24a1 会把 ks24a10 的提交
  也捞进来

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 18:28:53 +08:00
xuyueandClaude Opus 5.5 11368813cc feat(流程图): 「写代码 / 画流程图」变成工具栏上的切换,AI 的评分和历次分数挪进结果页签,题单里也能画
- 流程图题的工具栏最左边是「写代码 / 画流程图」。原来流程图藏在语言下拉里,叫一个名叫
  Flowchart 的「语言」;画完一道题之后它还被记成语言偏好,下一道能画的题一打开就是画布
- 「交给 AI 点评」交完切到「结果」页签:评的时候写着「AI 正在看你的图」、画布照样能改;
  评完是分数、评语、改进建议、四项得分,顶上一排历次分数,点哪次看哪次。
  原来是提交按钮旁一个 1000px 的弹框,盖住画布,翻历次评分要一页一页点
- 画到 A / S:给「照着它写代码 →」,题单里 1.5 秒后回题单(和代码通过一样),
  课堂条当场标成做完;只在这一页上刚评完的那次才算,进页面读到以前的 A 不跳
- 「把这一版载回画布」只在画布和那一版不一样时出现;载回、进页面读回草稿后都缩放到装下整张图
- 题单入口能画流程图了(原来只有题库入口能画),题单里的代码草稿、流程图草稿各存一份,
  加入题单之前写的、画的不会一进题单就摆在编辑器里
- 手机上不给「画流程图」(节点库是拖放,手机上拖不动),题面里以前画好的那张照样看得到

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 18:19:16 +08:00
xuyueandClaude Opus 5.5 1b149a4f3f fix(题目): 在题目页里直接换题(下一题、题号直达)后,例子和语言还停在上一道题
Deploy / deploy (push) Canceled after 0s
题目页换题是同一个组件复用、只换路由参数,几处只在创建时读一次的状态就停在了上一道题:

- 例子:新题下面摆着旧题的例子,「测试」也拿旧例子比对
- 语言:下拉选项不跟着变,也不回退——从 Python 题跳到 SQL 题,语言停在 Python,提交必被拒
- 比赛 / 题单的编辑器只在挂载时载入一次代码
- 流程图:分数按钮、评分弹框、「我的流程图」还是上一道题的,上一道题还没评完的那次会挂到新题上

语言回退挪进 problem store 的 supportedLanguage(),两个编辑器在载入草稿之前调用,
保证先定语言再读这门语言的草稿。没有用给整块加 key 强制重新挂载的办法:课堂协作的
绑定依赖组件复用,重挂载会让协作中切题的老师语言恢复不了。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:15:50 +08:00
xuyueandClaude Opus 5.5 2a4405d2ea feat(题目): Python 语法错误改成中文说明,在编辑器里标出位置,中文标点可以一键换成英文
Deploy / deploy (push) Canceled after 0s
中职学生看不懂英文报错。原来提交前用浏览器里的 Skulpt 拦语法错误,只弹一句
「第 N 行存在语法错误」——Skulpt 九成报的是 bad input / bad token,没有列号。

- 提交前的语法检查挪到服务端:/code/format 先用 CPython 编译一遍(只编译不执行),
  api 镜像加 python3-minimal(3.13,与判题机同版本,+17MB),前端删掉 Skulpt
- 报错按规则表翻成中文(pythonError.ts),生产库 2695 条 Python 编译错误覆盖 99.7%;
  提交前拦下的和判题回来的编译错误共用同一张卡片
- 编辑器里标红出错行和 ^ 指的位置
- 中文标点(历史编译错误的 22%)一键换成英文,跳过字符串和注释、处理 f 字符串花括号;
  594 条里修好 593 条,经 tokenize 核对没有改到字符串内容;走编辑器事务,可 Ctrl+Z

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:47:01 +08:00
xuyue d25b5dbe6d update
Deploy / deploy (push) Canceled after 0s
2026-09-27 19:36:28 +08:00
xuyueandClaude Opus 5.5 5ae830a7f9 refactor: 一批名不副实的命名改掉(只动一端,不碰契约)
后端:
- getUserByToken → authenticateToken:它会删会话、续 TTL、记在线,不是纯读
- todayStart → dayStart(instant):本来就接受任意时刻,不只是「今天」
- objectValue → asRecord,删掉 4 份一模一样的私有拷贝和 `as toObject` 别名
- 报错文案 "does not exists" / "Already be a public problem" 改通顺

前端:
- utils/types 里 Contest / User / Problem 三个别名删掉:Contest 原来指后台
  比赛、契约里同名的却是学生侧。现在 Contest = 学生侧、AdminContest = 后台,
  后台用户表单叫 AdminUserForm,题目详情直接用 ProblemDetail
- SubmissionInfo → ContestSubmissionInfo,免得和 submission.info 撞名
- utoa / atou → compressToBase64 / decompressFromBase64(实际是 zlib + base64),
  encode / decode → base64EncodeUtf8 / base64DecodeUtf8
- filterResult → toProblemRow、toProblemListItem → toAdminProblemRow,
  行字段 submission / rate → submissionCount / acRate
- 组件:Modal → AddFromLibraryModal、Form → EditorToolbar、
  ContestEditor → BasicEditor(题单也在用它)、rank 的 Index → RankMedal、
  Name → UserNameCell、Password → PasswordCell、ContestType → ContestAccessTag

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:08:21 +08:00
xuyueandClaude Opus 5 a0ef204bd2 feat(提交): 采集编辑过程信号,落进 submission_trace
Deploy / deploy (push) Has been cancelled
AI 时代 OJ 设计的第 1 步:给「可信 AC」和学情分析攒数据,本身不判任何事。
- 契约:提交请求加可选的 trace(活跃时长、键入/粘贴/删除字符数、切后台次数等,
  只有计数、不含按键内容);写成 .optional().catch(undefined),坏了就当没带,
  不让附带数据把提交挡成 400
- 迁移 0016 建 submission_trace,与 submission 一对一、CASCADE;since_prev_ms
  由服务端在同一条 INSERT 里算(排掉自身),bigint —— 实测已有 44 天的间隔,int4 装不下
- 后端写 trace 失败只记日志,不影响提交
- 前端 oj/problem/utils/editTrace.ts 是模块单例(扩展对象不能过 Pinia 的响应式代理),
  只数带 userEvent 的事务:格式化回写 / 载入草稿 / 协作对方的改动天然排除;
  closeBrackets 越过右括号时是原样替换,按 no-op 跳过。比赛编辑器同样挂上
- 实跑:后端四种请求、前端浏览器里键入/粘贴/删除/setCode 回写/切后台/提交后清零,
  计数与预期逐项一致

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 05:46:55 -06:00
xuyueandClaude Opus 5 88b695d34f feat(课堂求助): 教师端接单直接跳题目页协作,删掉 CollabModal
Deploy / deploy (push) Has been cancelled
点求助列表里的一条 = 跳到 /problem/:id,在页面自带的那个编辑器里和学生同步。
原来是弹一个 CollabModal(弹框里再挂一个 CodeMirror + 一份只读题面):

- 按一下 Esc 弹框就关、协作跟着结束(`show` 的 setter 里 leave 是唯一的关闭语义,
  Esc 和点遮罩都走它),上课太容易误触;
- 弹框里那份题面不能跑测试、不能提交,老师还是得另开标签页对照。

现在结束协作只有两条明确的路:工具栏的「结束协作」按钮、离开这一页。教师端也少了
一个 CodeMirror 实例 —— setBinaryHandler 那个单例槽位只剩 SyncCodeEditor 一个使用者。

改造中撞出两个真问题,根因是同一个:`/problem/1002` → `/problem/1001` 是同一条路由
换 params,**组件被复用、不卸载**。

- 老师在别的题上接单跳过来,`@ready` 不会再触发,而 room 早在导航前就 open 了 ——
  老师停在一个空编辑器上干等。watch 的源从 room 一个变成 [room, roomIsHere] 两个。
- 反过来老师协作中切去别的题,onUnmounted 不触发 → 不发 leave,学生一直挂着
  「老师正在帮你」而老师的字一个也过不去。加 `bound` 标记区分「房间从我这儿挪走了」
  (补发 leave)和「房间在别处开起来了」(跟我无关,接单那一瞬间就是这种)。

`leave` 分两种语义(协议加 reason):

| 前端 | 求助记录 | 对面看到 |
|---|---|---|
| leave("done") 点「结束协作」 | 删除 | 老师已结束这次帮忙 |
| leave("left") 离开这道题的页面 | 教师走→退回排队;学生走→删除 | 老师暂时离开,已重新排队 |

"left" 复用的是老师掉线那条路(requeueAfterTeacherGone),两件事语义相同。原来两者
都按 done 处理 —— 老师点一下「提交信息」,学生就得重新举手,而他看到的是
「协作已结束」,会以为被处理完了。发起方收到的 reason 单独一份(self_left/done),
不然学生自己切走了却看到「老师已结束这次帮忙」。

其余为这套交互补的闸:

- 学生排队期间离开那道题 → 自动取消求助。那条求助说的是「我卡在这道题」,人走了
  就不成立;原来它会一直挂在队列里,老师接进来时学生的编辑器不在这道题上、根本
  不会绑,老师对着空编辑器敲字,两边都没提示。
- 协作中**教师的语言选择跟着 room.language 走**(写 codeStore,不只是改高亮),
  结束后还原。只改高亮的话老师会拿自己那档语言提交学生的代码(学生写 C、老师选的
  是 Python,当场 CE),工具栏还可能显示「提交流程图」。学生端一个字不动 ——
  服务端的 room_language 只发给教师,学生本地那份停在建房那一刻。
- 协作中不给「重置代码」:v-model 一写回就顺着 Yjs 同步过去,等于一键清空学生的作业。
- 协作中「提交信息」走新标签 —— 那是教师工具栏上唯一会跳路由的按钮。
- 接单时 resetScreenMode():分屏在「题目」「自测」两档时右侧编辑器根本没挂出来,
  停在同一道题上接单会落进一个没有编辑器的页面(跳到别的题时 detail.vue 的 init()
  会重置,同页不会)。
- 老师已在一个房间里时前端也拦住接下一单(服务端本来就拦),不拦的话前端已经跳到
  新题目上,等于把手上那场协作断掉。
- 文案:协议层那四条英文(Invalid problemId 之类)会被原样弹成 toast,改成中文;
  「请先退出当前协作」统一成「结束」;room_closed 的提示按 reason × 角色展开。

验证:本机起 api + web,三个账号两两组合实跑 —— 跨题接单 / 同页接单、双向编辑、
连按 Esc、结束协作、老师跳走后再接、学生跳走、学生排队中切题、学生协作中切语言、
分屏在「题目」档接单、协作中重复接单,以及上面每一句提示的实际文案。
api typecheck / check:routes、前端 type-check / build、fmt 全过。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 19:13:42 -06:00
xuyueandClaude Opus 5 bb33e0f0e5 refactor(web): 删掉 SyncCodeEditor 的 problem prop
老实现的残留:sync.ts 拿它拼房间名 problem-${problemId}。新实现的房间以
学生为键、由服务端分配,前端不需要题号 —— 这个必填 prop 声明之后
整个组件再没用过第二次。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016DHxhKNxXfG89JnVzHbvgj
2026-08-30 09:05:48 -06:00
xuyueandClaude Opus 5 6f873be367 feat(web): 题目页的「开启同步」换成「求助」
学生发起求助,状态与排队位置显示在按钮旁。
教师端入口不在题目页,所以对教师隐藏这个按钮。

顺带修正 handleHelpCancel:去掉 request.socket !== ws 的校验。
学生取消自己的求助,不管从他哪个标签页发起都合法——
getRequest(ws.data.userId) 已经把范围锁在这一个用户上了,
不是跨用户操作,不需要再比对是不是同一条连接。这层校验此前
会让「第二个标签页点取消」被服务端静默丢弃,前端却已经乐观
地把按钮变回「求助」,是一处 UI 说谎;现在两边状态一致。
handleCollabClose 里排队分支的 socket 归属校验不受影响,
那里的关闭事件是顺带触发的,仍然需要认出是不是本人这条连接。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K1d8B3f4SXJwDvUY625eQd
2026-08-28 06:33:25 -06:00
xuyue ae1fb329b5 feat(阶段1): 搬入 ojnext 为 apps/web,未改业务代码 2026-08-06 21:18:16 -06:00