xuyue and Claude Opus 5.5
8f0d39bb5a
feat(题目): 结果标题说清楚错在哪——「在例子 1 上就错了」「所有测试点都对了」,答错时附上题目对输出的原话
...
- 在公开例子上就错了的:标题写「答案错误 · 在例子 N 上就错了」,不再挂「通过 x/y 个测试点」
和进度条 —— 那时候那个数字没意义,先把例子跑对
- 做对了:「答案正确 · 所有测试点都对了」
- 答案错误的说明末尾摆出题目「输出」那一节的原话:格式不对、漏了一行这类错,对着原话一看就明白
- 例子都对、错在隐藏测试点的,给「去自己输入试试」(SQL 题不给,跑不了)
- 提交详情页的标题不变
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 19:03:13 +08:00
xuyue and Claude Opus 5.5
1026b8f9a2
feat(题目): 左栏拖宽、右栏窄于 600px 时工具栏收一收,不再折成两行把编辑器往下挤
...
「课堂统计」收进「⋯」,「写代码 / 画流程图」缩成「代码 / 流程图」,按钮间距收窄。
语言下拉不收:窄于 120px 时图标和「Python」会被挤成两行
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 18:49:40 +08:00
xuyue and Claude Opus 5.5
03985fcbf9
style(题目): 暗色下「做完」的题号芯片换成深色字——浅薄荷绿底上的白字几乎看不见
...
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 18:45:59 +08:00
xuyue and Claude Opus 5.5
11ff340110
feat(题目): 语法没过时把代码里每一处中文标点都标出来,和「换成英文(N 处)」对得上
...
- 原来编辑器里只标报错点名的第一处,按钮却写着「4 处」,学生对不上、也不知道一键替换
会改哪里。字符串和注释里的中文标点是合法的,不标(和一键替换同一套判断)
- 这种情况下没交上去:结果页签那一段叫「交之前检查」,标题写「还没有交上去」,
不再叫「提交结果」
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 18:44:29 +08:00
xuyue and Claude Opus 5.5
972dac3776
feat(题目): 答错时说清楚「输出了什么、应该是什么」,交对后给相似题,零碎几处对齐设计
...
Deploy / deploy (push) Canceled after 0s
- 答案错误里「格式和内容都不一样」那一类(样例上错的 44%):原来只说「对照一下,找找
从哪里开始不一样」,现在是「你的程序输出了「E」,正确的是「D」。拿例子 2 的输入「60」,
一行一行走一遍你的程序,看它走进了哪个分支」;多行的说第几行开始不一样
- 不在这节课里的题交对之后,结果页签里给「再练几道相似的」(原来只在题面最底下)
- 提交前自动排版改了代码的,结果上面提一句「编辑器里的代码也跟着变了,按 Ctrl+Z 能撤回」
- 交对之后「提交」降成描边按钮,「下一题」是唯一的主按钮
- 「重置代码」先确认(原来点一下就把代码和本地草稿一起清掉了)
- AI 提示的档位写「第 1 档 · 反问」,不写「L0 反问」
- 成就弹窗从右下角挪到右上角,别和结果、点评弹窗叠在一起
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 18:39:05 +08:00
xuyue and Claude Opus 5.5
9110c678fc
feat(题目): 手机上「运行例子 / 提交」固定在屏幕底部,看着题目也能直接交
...
- 两个 44px 高的大按钮固定在底部,三个页签下都在;原来它们在「代码」页签顶上,
和语言、更多挤成两行,看着题面想交一次还得先切回代码页签
- 「代码」页签的编辑器一直挂着(原来切走就卸载):没打开过代码页签也能交,撤销历史也不丢
- 编辑器高度让出底部那条,最后几行不再被按钮盖住
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 18:33:17 +08:00
xuyue and Claude 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
xuyue and Claude 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
xuyue and Claude Opus 5.5
948e9a734d
feat(题目): 例子摆成一张「输入 | 输出」的表,三个例子不再占掉大半屏
...
原来每个例子各占一个小标题加两个框,三个例子就把输入输出说明挤出了一屏;
现在一行一个例子、输入输出对齐,每格右上角带复制。「自己输入数据试试」留在表头旁边
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 17:48:13 +08:00
xuyue and Claude Opus 5.5
6a0f6399b7
feat(题目): 「我的提交」按第几次列出来,点开就地看代码,一键放回编辑器(Ctrl+Z 能撤回)
...
- 每条写成「第 N 次 · 结果 · 语言 · 时间」,点一下在抽屉里展开代码,带「放回编辑器」「复制」
和「完整详情」(新标签打开,测试点、自测猫都在那边)。原来是点编号弹出 70vw 的详情弹窗,
盖住整个题目页,「复制回到题目」还要整页跳一次
- 放回编辑器是一次普通的整段替换,编辑器的撤销历史记着它,按 Ctrl+Z 就回到原来的代码
- 顶上一句话:「你是班上第 N 个做对的,班上一共 M 人做对了」(没有班级时是全站),
后面跟「看看他们的写法」
- 加入题单之前交的那几次照旧锁着,改成行内一句说明,点不开
- 协作中的老师不给「放回编辑器」:那会儿编辑器里是学生的代码,放回去会经协作通道直接盖掉
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 17:48:13 +08:00
xuyue and Claude Opus 5.5
5bb69ecce3
feat(点评): 点评改回轮盘并重新设计——每块直接写着名字,选完就地变成玫瑰图看大家怎么选
...
上一版把轮盘换成了按钮格子,改回轮盘,但重新画了一个:
- SVG 画的 7 块环形扇区,每块上直接写着表情和名字(最大 16px),不用先把鼠标移过去;
悬停的那块染上主色、往外弹出,圆心报它的名字
- 选之前哪里都没有人数;选完就地变成玫瑰图:每块从里往外填一段、长短按人数,标「N 人」,
自己选的那块是主色、弹出、带 ✓,圆心写「你选了 学到了 · 共 N 人评过」
- 没登录、没做对时轮盘照画、整个淡下去,圆心写原因
- 原来的轮盘用 CSS clip-path 加 color-mix(),后者 Chrome 111 才有,机房 105 那档扇区没有底色
- 上一版的防误触(600ms、强制弹窗里不响应 Enter / 空格、不预选)和选完的「下一题」都保留;
抽屉里能用键盘选
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 17:19:37 +08:00
xuyue and Claude Opus 5.5
c64b0a402b
feat(点评): 点评轮盘换成一排大按钮,选之前不显示人数,选完看大家怎么选、直接去下一题
...
- 七个选项不变,排成 4 + 3 的大按钮(表情 + 15px 的字)。原来的轮盘要把鼠标移到扇区上
才看得清,每块的字只有 12px 左右;还用了 Chrome 111 才有的 color-mix(),机房 105 那档
渲染不出扇区底色
- 选之前不显示「N 人选择」,免得跟风;选完换成横条图,标出自己选的那一项
- 防误触:弹出后 600ms 内点不了,强制弹窗里不响应 Enter / 空格,也不预选任何一项
- 选完弹窗不马上关,下面只有一个按钮、焦点就在上面:这节课的「下一题」,或者刚才被拦下
的换题「继续」,都没有就是「好的」
- 标题从「恭喜你成功提交」改成「做对了!」:交上去不等于做对
- 提交失败留在弹窗里提示「没记上,再点一次」;没做对的人在抽屉里只看到一句说明
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 16:22:48 +08:00
xuyue and Claude Opus 5.5
116b0a2c95
feat(题目): 左栏顶上标出「这节课做完几道」、题单进度或比赛倒计时,统计 / 点评 / 我的提交收进抽屉
...
- 上下文条(按入口三选一):课堂 = 「这节课 做完 3/5」+ 每道题号 + 上一题 / 下一题;
题单 = 题单名 + 进度 +「第 N 题」+ 上一题 / 下一题;比赛 = 比赛名 + 每道题号 +「还剩 38:12」+ 排名。
题号只放号码,状态用 ✓ / ! 字符区分,不只靠颜色;多于 5 道横向滚动,当前题保持可见。
通过之后当场 +1,不用刷新
- 左栏只剩「题目 / 结果」两个页签;统计 / 点评 / 我的提交变成页签行右侧的按钮,点开是抽屉,
只盖题面、编辑器不动,交完判出结果时自动收起。手机上收进页签行的「⋯」
- 流程图并进「题目」页签:参考流程图默认折叠;自己画到 A/S 的那张缩略显示、可放大,
一进题就能看到(原来要先切到「流程图」这门「语言」),也不再强行切页签
- 「本题已经被你解决啦」两条整宽提示改成标题下的「✓ 已解决 / ! 尝试过」标签;
时间内存限制只写「限时 1 秒」
- 入口差异收成一张表(problemPageContext):题单里不给抽屉、状态标签、相似题;
比赛里没有点评和求助。工具栏的「本题提交」挪到「我的提交」抽屉底部
- 顺手修:没有例子的题点「去自测猫」会抛错;「我的提交」没交过时一片空白
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 16:02:28 +08:00
xuyue and Claude Opus 5.5
45505e8384
feat(题目): 工具栏加「运行例子」、结果页签加「自己输入」,去掉每个例子旁的「测试」和顶栏的分栏切换
...
Deploy / deploy (push) Canceled after 0s
- 工具栏「运行例子」:一键用全部例子试跑(Judge0,不算提交),结果进「结果 › 运行例子」,
每个例子 ✓ / ✕,没对上的沿用答案错误那套中文说明;比赛里只摆输出、不给提示句
(后端在比赛里故意不做例子对比);SQL 题不给
- 「结果」页签分成提交结果 / 运行例子 / 自己输入三段;「自己输入」顶替顶栏「自测」模式
那个单独的编辑器
- 试跑出错(编译、运行、超时)给中文说明,Judge0 的英文原文折叠起来
- 例子比对两边都去掉末尾空白:原来只 trim 了运行输出,样例末尾带空格的题永远「不通过」
- 去掉题面里每个例子旁的「测试」、顶栏的「双栏 / 自测 / 题目」切换、EditorForTest 和
screenMode store
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 14:35:58 +08:00
xuyue and Claude Opus 5.5
f4db7dc81c
feat(题目): 判题结果从提交按钮下的浮层挪到左栏「结果」页签——改代码时结果就摆在旁边
...
原来结果是提交按钮下面的 n-popover:点一下外面就收起、最高 600px、盖住编辑器,
学生没法一边看「正确输出 / 你的输出」一边改代码,还得靠「上次结果」按钮找回来。
- 左栏新增「结果」页签(手机上排在「代码」后面),交上去或者语法检查没过就自动切过去;
页签标题带状态图标(✕ / ✓ / ! / 判题中转圈),不只靠颜色
- 页签用 show:lazy:切走不卸载,编辑器里的错误标记、AI 提示都留着
- 「题目描述」改叫「题目」;去掉浮层和「上次结果」按钮
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 14:21:57 +08:00
xuyue and Claude Opus 5.5
5cb7cfa924
refactor(流程图): 评分状态挪进 store——交完切去写代码,这次的评分结果不再丢
...
Deploy / deploy (push) Canceled after 0s
SubmitFlowchart 只在语言选成 Flowchart 时才挂载,评分的全部状态(WebSocket 订阅、
轮询兜底、3 分钟超时)都长在它身上:交完马上切去写代码,组件一卸载就断开连接,
正在评的那次结果丢了 —— 不提示分数,评到 A/S 也不亮到「我的流程图」。
- oj/store/flowchart.ts:提交、评分跟踪、评完的提示、A/S 亮图、最近一次分数;
换题和离开页面时收掉
- SubmitFlowchart 只剩从画布取图和评分详情弹框
- 顺手补了登录检查:原来没登录也能点,请求发出去才被拦,只看到一句「提交失败」
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 13:49:45 +08:00
xuyue and Claude Opus 5.5
26e22e2542
refactor(题目): 提交状态挪进 store——手机上切页签、桌面切分栏,判题结果和点评不再丢
...
提交按钮那个组件(SubmitCode)在编辑器的工具栏里,而提交的全部状态和判完之后的
反应都长在它身上:手机上交完切到「描述」页签(Naive 的页签切走即卸载)、桌面切到
「题目 / 自测」模式,编辑器一卸载,判题结果、AI 提示、失败计数、通过后的点评、
题单里的回题单全丢了。
- oj/store/submission.ts:这次提交、判题跟踪、语法检查报错、结果面板开合、冷却、
AI 提示(useSubmissionHint)
- SubmissionEffects.vue:挂在题目页这一层,失败计数、标成已解决、烟花、强制点评
(连同「先点评再换题」的导航拦截)、回题单
- SubmitCode 只剩按钮和结果面板
- WebSocket 通道和判题监控的收尾改成 onScopeDispose:onUnmounted 在 store 里挂不上
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 13:36:35 +08:00
xuyue and Claude Opus 5.5
a8b83ba762
refactor(题目): 比赛和题单改用题库那个编辑器——题单里的求助终于接得上,一键换中文标点也能用了
...
比赛和题单原来用的是另一个精简版编辑器(BasicEditor),没有协作绑定、也没有
错误标记扩展:题单里学生点「求助」,老师接单后编辑器绑不上;比赛和题单里
「把中文标点都换成英文」点了没反应、出错行也不标红。三种入口统一成 ProblemEditor,
删掉 BasicEditor。流程图仍然只在题库入口出现(语言列表那道闸没动)。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 13:18:58 +08:00
xuyue and Claude Opus 5.5
0daa74651d
fix(题目): 通过后马上点「下一题」或换题,强制点评会被跳过
...
Deploy / deploy (push) Canceled after 0s
从答案正确到点评弹窗出来有 1.5 秒(外加查一次评过没有),而换题会取消弹窗的
定时器 ——「下一题」在通过的那一刻就出现,学生手快就绕过了强制点评。
现在这段时间里离开这道题的导航(下一题、题号直达、顶栏导航、相似题……)先拦下:
立刻弹点评,选完再去原来要去的地方。只改 query(左栏切页签)不拦;弹窗已经开着时
也不拦,免得点评接口一直失败时把学生关在这一页。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-28 12:08:45 +08:00
xuyue and Claude 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
xuyue
8ca427b5cd
update
Deploy / deploy (push) Canceled after 0s
2026-09-27 23:28:17 +08:00
xuyue and Claude Opus 5.5
a7f6ebaf49
feat(激励): 知识点地图——每个知识点做对几道、在哪一档、再做几道升级,首页一句话提示本周进步
...
Deploy / deploy (push) Canceled after 0s
现有 38 个成就全是累计量(通过几题、提交几次、连续几天),起步的人人都有、往上的够不着,
而且没有一个在说「你学会了什么」。连续打卡在这里结构上做不到:2025 秋学生最长连续交题
天数的中位数是 1 天,连续 7 天的只有 3 人。
- GET /me/knowledge:题数 ≥10 的知识点标签(10 个),每个给做对几道、档位、周一时的档位、
下一档门槛;四档 入门 1 / 会了 3 / 熟练 题数 40%(≤10)/ 精通 75%(≤20)。
回测 2025 秋:中位数学生一学期升 5 档;「入门」一档是给只做对一两道的 71 人加的
- 同班 ≥30% 点亮过的知识点才会出现在「还没碰过」里,免得 Python 班看到「C 语言」
- 个人主页加「我的知识点」,只在看自己时显示;首页一句话:本周升级 > 离下一档最近的 >
去点亮一个班里在学的,点了去按知识点筛的题目列表
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 23:17:43 +08:00
xuyue and Claude Opus 5.5
a63bf6ea07
feat(题目): 通过之后直接给出这节课的下一题;在题目页里换题时不再带着上一道题的结果和评价弹窗
...
Deploy / deploy (push) Canceled after 0s
72% 的提交一次就通过,每次通过之后学生都要回首页或再问一遍题号去找下一道。
- 结果面板在通过时显示 LessonNext:取 /me/class-activity(老师布置的优先,否则推断),
给出排在当前题后面、还没通过的第一道,后面都做完了再从头找;全做完了显示「都做完了」
并指向题单。当前题不在列表里、比赛、题单入口都不显示
- 评价弹窗关掉后重新打开结果面板:点弹窗时面板会被当成点了外面收起来,「下一题」就在面板里
- 换题(同一组件只换路由参数,有了题号直达和下一题之后成了常规操作)时收掉上一道题的
结果面板、语法错误、还在跟的提交(新增 useSubmissionMonitor.reset),并取消通过后
那两个 1.5 秒的定时器——否则会给还没做的新题弹评价,上一道判完时还会把新题标成已解决
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 22:58:19 +08:00
xuyue and Claude Opus 5.5
a6bf845e06
feat(课堂看板): 标出最近几节课经常没交的学生,求助的学生置顶,点一下打开求助列表
...
Deploy / deploy (push) Canceled after 0s
2025 秋至少上过 5 节课的学生里,12.5% 七成以上的课一道都没交,37% 的学生贡献了
一半以上的「没交」;当堂那份「今天还没交过」把偶尔没来的和一直不动手的排在同一行。
- 看板接口给每个学生加 recentAttended:最近几节课(最多 5 节、不含今天、45 天内;
一节课 = 同班同日 ≥5 人做同一题)里交过几节,另给 recentLessons
- 「今天还没交过」拆成「经常没交」(≥3 节可比且最多交过 1 节)和其他;表格加一列
「最近 N 节」;只给老师看,投影模式不出现
- 求助读老师端常驻 WebSocket 的 collabStore.requests(不改后端):求助的学生置顶,
状态显示「举手了 / 老师在帮」,点一下打开求助列表;求助提示不论布没布置题都显示
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 22:47:24 +08:00
xuyue and Claude Opus 5.5
80106eaa04
feat(课堂): 老师的课堂看板——布置这节课的题、看全班谁还没开始谁卡住了,布置的题直接出现在学生首页
...
2025 秋平均每节课有 14 个平时在用的学生一道都没交(三分之一),交了 3 次以上还没过的
平均不到 1 个;而老师原来只能一道题一道题打开统计弹窗看。
- 新表 class_lesson(迁移 0023,仅建表):一个班一天一行,存老师布置的 problem.id 与顺序;
day 存东八区日历日文本,created_by CASCADE
- GET /classroom/board:这个班今天的题 × 全班学生(通过与否不限今天、今天交了几次、
最后提交时间);不给班级时猜最近两小时提交人数最多的班,不记老师上次选的
- PUT /classroom/lesson:按展示题号布置,对不上的题号点名拒绝,空数组 = 清掉
- /me/class-activity 优先返回老师布置的题(source = teacher),否则照旧推断
- 前端 /classroom:没交过的名单置顶,按 没交过 → 卡住 → 在做 → 做完 排序,15 秒刷新;
投影模式只显示每题完成人数、不出名字;用户菜单给老师加入口
- 学生首页:老师布置的标题写「今天老师布置的题」,没人交时不显示 0/0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 22:37:59 +08:00
xuyue and Claude Opus 5.5
7e964fdbdd
feat(题目): 答案错误时拿公开样例重跑,并排显示正确输出和你的输出,按差异类型给中文提示
...
Deploy / deploy (push) Canceled after 0s
原来答案错误只有四个字。6901 条历史答案错误在公开样例上重跑过:73% 在样例上就错了,
其中约三分之一只差格式(没输出、input() 提示文字、空格换行、中英文标点、小数位数)。
- judge/run.ts 的 checkSamples:判出答案错误后以内联测试点重跑题目样例(output: true),
记下第一个没过的样例和学生在它上面的输出,写进 statistic_info.sample_check;
样例全过则只记 passed。全是公开信息,不碰隐藏测试点;比赛不跑(有期末考试)
- 前端按差异归类(数字先于「多了内容」判,免得 007 被当成多打了文字),
并排显示输入 / 正确输出 / 你的输出,标出第一处不同的行;只差空白时把空格、换行画出来,
末尾空白不画(判题本来就忽略)
- 提示给具体值(「正确输出是 84.10,你输出的是 84.1」),input("…") 单独提示
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 22:14:24 +08:00
xuyue and Claude Opus 5.5
db70ab62e8
feat(题目): 运行时错误告诉学生第几行、为什么出错(Python 看回溯,C 看信号),不泄露测试数据
...
原来学生只看到「运行时错误」四个字:主判题 output: false,Python 回溯没回来;
C 的信号值在 info 里,而 info 只给管理员。
- Python 判出 RE 时,拿第一个失败测试点单独重跑一次(内联测试点 + output: true),
从回溯取行号(扣掉模板前置行)、异常类型和按句式归的类别。主判题仍不开 output,
免得每个点最多 16MB 的输出回到 worker
- C / C++ 取失败点的信号和退出码(段错误提示 scanf 漏 &、return 非 0 等)
- 写进 statistic_info.runtime_error;不存异常原文(里面常带测试点输入),名字只在
出现在学生代码里时才存——raise Exception(input()) 实测只落下类型
- 前端按类型翻成中文、摆出出错那行并在编辑器标红;比赛提交不诊断(ACM 只报对错)
- 没有诊断的 RE 不再说「代码格式写错了」
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 22:01:30 +08:00
xuyue and Claude 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 and Claude Opus 5.5
039992dc86
feat(前台): 顶栏可以输入题号直达;首页列出班里最近一次课一起做的题和自己的完成情况
...
Deploy / deploy (push) Canceled after 0s
课上老师报题号、全班去找,是 2025 秋七成非比赛提交的来路,界面上原本没有它的入口。
- 顶栏(桌面端)加题号框,首页「找题」同一套逻辑:题号对上直接进题,否则去列表搜
- 新接口 GET /me/class-activity:同班同一天 ≥5 人做过的题,取最近 7 天里最近的一天,
附带我自己的状态与班里通过人数;首页据此显示「今天班里在做」卡片,没有就不占位
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 20:58:15 +08:00
xuyue and Claude Opus 5.5
d07459928d
feat(标签): 题目列表的标签分成「知识点」「主题」两组,新建的标签在后台标出待归类
...
Deploy / deploy (push) Canceled after 0s
- 迁移 0022:problem_tag 加 category(knowledge / theme)和 category_confirmed;
存量 25 个题材、题集类标签归主题,其余归知识点,全部记为已确认
- 前台题目列表按两组显示标签,组内按题目数从多到少
- 后台标签管理:每行切换分类;出题时新建的标签默认知识点、标「待归类」并排在最前,
页头提示待归类数量,可点「确认」保留默认;加分类筛选
- 新接口 PUT /admin/problem-tags/:id/category,设置分类即视为确认
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 20:38:43 +08:00
xuyue and Claude Opus 5.5
e1b6a9e9fa
feat(前台): 题单卡片显示完成进度,手机上题目列表不再横向溢出,个人主页数据排得更紧凑
...
- 题单卡片:完成进度条和「已完成 x / y 题」,「已完成」改成绿色,未获得的徽章置灰;
进度按加入时的快照算,题单后来加了题就注明「新加了 N 题」,不再出现「已完成」却是 5 / 6
- 题目列表:手机上只留状态、编号、题目、难度四列;标签默认收起,选中的标签显示在按钮旁
- 个人主页:六项数据保留,改成三列两行的小卡片,头像缩小
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 20:27:00 +08:00
xuyue and Claude Opus 5.5
6bf8c6e1d9
style(前台): 列表页去掉表头 emoji、已结束的比赛改成灰色;比赛列表置顶进行中的比赛,排名页先看榜单
...
- 前台 53 处表头改成纯文字,删掉只剩这一个用途的 utils/renders.ts
- 「已结束」从红色改灰色(比赛列表 / 详情 / 后台 / 首页一起)
- 比赛列表第一页、无筛选时顶部列出进行中和即将开始的比赛
- 内容少的页面页脚贴底,不再悬在半屏
- 排名页两张柱状图挪到最后,第一屏是 Top100、本周进步榜和我的排名
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 20:13:29 +08:00
xuyue and Claude Opus 5.5
31aa5f6e74
feat(前台): 登录后首页改成个人首页;题目详情页的代码要求挪到最前面、样例并排显示
...
- 登录后 / 是个人首页:继续学习、最近提交、进行中/即将开始的比赛、公告;
没登录仍是题目列表,题目列表另有 /problem 地址,顶栏「题目」指向它
- 继续学习按服务端留痕取最近打开的一课,不用 localStorage(机房一机多人)
- 题目详情:标题下加难度与时空限制,代码要求提到最前,小节收紧,
样例输入/输出并排;工具栏「提交信息」「更多操作」改为「本题提交」「更多」
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 20:08:47 +08:00
xuyue and Claude Opus 5.5
d3823430f9
feat(后台): 侧栏分组带图标、可收起,加顶栏;所有后台页面统一页头
...
Deploy / deploy (push) Canceled after 0s
- 侧栏按 概览 / 教学内容 / 学情分析 / 站点管理 分组,去掉超管菜单里的重复项;
菜单项的角色与路由守卫一一对应,没有题目权限的教师不再看到点了就被踢回首页的「题目」
- 顶栏:面包屑、回到前台、明暗切换、用户菜单(退出登录);窄屏侧栏自动收起
- 新增 PageHeader:标题 + 右侧主操作 + 下方筛选栏,编辑页带返回按钮
- 卡点分析 / 年度趋势 / 标签管理从题目列表的按钮挪进侧栏
- 首页改成统计卡片 + 班级排名卡片;设置页按钮移到卡片右上角
- 题目、比赛、题单、用户列表的「选项」列固定在右侧,不再被挤出屏幕
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 19:54:42 +08:00
xuyue
d25b5dbe6d
update
Deploy / deploy (push) Canceled after 0s
2026-09-27 19:36:28 +08:00
xuyue and Claude Opus 5.5
a698ebf585
perf(前端): 首页和题目详情页按需加载,首页 477 → 380KB、详情页 1031 → 682KB(传输量)
...
Deploy / deploy (push) Canceled after 0s
浏览器实测(preview 构建,生产规模数据):首页解压后 1428 → 1112KB,
未登录的题目详情页 3317 → 2021KB。后端接口 6–8ms,不是瓶颈,没动。
首页:
- 登录速报弹框挂在默认布局上,静态 import 了 md-editor-v3(~160KB JS + 76KB CSS),
而 AI 分析只在登录后、提交满 3 次才有。MdPreview 改成异步组件,目标是本地包装
LoginSummaryPreview.vue —— 直接 import("md-editor-v3") 会让 rolldown 把它和
CodeMirror 语言包并成一个 467KB 的 chunk,排名页反而涨了 560KB(实测)
- 题目列表经 oj/api.ts 还带着 zod(~70KB):那个文件静态 import 了题目详情、提交详情
两个 schema。改成和请求并行动态加载(单独的 oj/detailSchemas.ts,理由同
profileSchema.ts),闸门仍是同步的 —— submissionDetailSchema 有 .catch(),
不能改 contractDeferred
题目详情页:
- skulpt(~226KB gzip)原来一选 Python 就立刻预取。改成登录后才取(没登录交不了),
且等 requestIdleCallback,不和题面、编辑器抢带宽
- StatisticsPanel 只有老师可见,却被 EditorToolbar 静态 import,每个学生都白拉一份
chart.js(~68KB gzip)。改成异步组件,打开课堂统计时才加载
- SubmissionResult(带 DataTable)改成异步组件,第一次提交弹出结果面板时才加载
浏览器核过:首页正常;详情页未登录不再加载 skulpt / chart.js / DataTable;学生登录后
skulpt 空闲预取、提交后结果面板正常弹出;老师点课堂统计按需加载图表并正常渲染。
登录速报的 AI 分析与语法错误拦截路径本机没能触发,未在浏览器里核。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 16:34:32 +08:00
xuyue and Claude Opus 5.5
9412ba3cf6
refactor(契约): 展示题号统一叫 problemDisplayId,题单 id 统一叫 problemSetId
...
Deploy / deploy (push) Canceled after 0s
前后端同时改,部署前开着的旧页面要刷新一次(题号列会是空的、提交筛选会失效)。
- 挂在非题目对象上的展示题号,原来叫 problem(字符串)或 displayId,统一成
problemDisplayId:提交列表、流程图列表、统计的 lastFailure / items /
今日最热题、站内信里的提交、后台题单的题目列表。别处的 problem 都是对象
或数字 id,这几处是字符串,最容易看错
- 列表筛选的 query 参数 ?problemId= 实际传的是展示题号,改成
?problemDisplayId=(提交列表、流程图列表、三个统计接口)
- 契约里 problemsetId / problemSetId、problemset / problemSet 两种拼法并存,
统一成 problemSetId / problemSet,和 ProblemSet* 类型名、problem-sets
路径、前端路由参数一致。drizzle 的列键跟表名走,没动
- aiDetail.user(用户名字符串,而契约里别的 user 都是对象)→ username。
ai_analysis.data 里存的历史快照仍是 user,没有代码读回它
- 后台题单题目表的列是按字符串键 'displayId' 取的,类型检查抓不到,一并改
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 16:08:31 +08:00
xuyue and Claude 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
xuyue and Claude Opus 5.5
572a818d49
style: 按 Oxfmt 重新格式化
...
Deploy / deploy (push) Canceled after 0s
换格式化器带来的差异,只有 5 个文件:长联合类型改成每项一行、以 | 开头。
和上一个提交分开,方便 blame 时跳过(git blame --ignore-rev)。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 15:37:23 +08:00
xuyue and Claude Opus 5.5
f8b04af4ea
chore: 加 oxlint,现存 19 条 warning 清零
...
Deploy / deploy (push) Canceled after 0s
原来只有 Prettier,没有任何 lint。根目录加 `bun run lint`(oxlint,覆盖后端、前端、
契约,--deny-warnings:有一条就算失败),CLAUDE.md 常用检查里加上。
现存 19 条顺手清掉:
- 11 处未使用的 import / catch 参数 / 回调参数
- oj/store/problem.ts:`...problem.value?.languages` 展开可能是 undefined
(外层 if 已经保证非空,去掉 ?.)
- admin/problem/detail.vue:`contestId = contestId` 自赋值,删掉
- judge/sql/engine.ts:注释里的 `/*x*/` 示例原来靠零宽空格防止提前闭合注释,
改成 `*\/`
- 其余:多余的 !!、new Array(n)、展开里多余的 `|| {}`
两边类型检查、前端构建、后端单二进制编译都过。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 15:25:27 +08:00
xuyue and Claude Opus 5.5
83b9d23c68
perf(前端): zod 移出首屏包,首屏 JS 721KB → 594KB(gzip 237 → 199KB)
...
首屏包里带着整个 zod 运行时(~100KB),来路有三条,缺一条都挪不出去(逐条撤回实测过):
- 契约包没有 sideEffects: false,入口从桶里取任何一个值都会把所有 schema 模块带上。
契约包里没有顶层副作用(核过),加上之后 rolldown 才能按导出摇树
- roles.ts 里的 USER_TYPE / toAdminType 是入口要的,同文件却定义了两个 z.enum。
schema 挪到 admin.ts(唯一用处),roles.ts 文件头写明不许 import zod
- 启动就要调的 getWebsiteConfig / getAILoginSummary 在 oj/api.ts 里,而那个文件同步
import 了题目详情、提交详情两个 schema。这两个函数挪到 shared/api.ts;那两道
闸门保持同步 —— submissionDetailSchema 有 .catch(),放行原文和解析结果不等价
- /me 的契约闸门改成 contractDeferred:先放行原文,schema 按需加载后在后台核、只记
日志。userProfileSchema 没有 default / transform,两者等价。动态 import 的目标是
单独的 shared/profileSchema.ts —— 直接 import 契约桶会让 rolldown 重排共享 chunk,
实测首屏反而涨到 1811KB
浏览器实跑核过:登录后 profile 正常加载、profileSchema 按需拉取、无控制台报错;
故意喂一份不符的数据,后台闸门照常打出 [契约] 分歧日志。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 15:23:31 +08:00
xuyue and Claude Opus 5.5
6b2da41882
refactor(题单后台): 添加 / 编辑弹窗两两合并成 ProblemModal、BadgeModal
...
AddBadgeModal / EditBadgeModal、AddProblemModal / EditProblemModal 各是两份八成相同
的组件,改一处表单项另一处总要漏。合并成一个组件,传了 badge / problem 就是编辑、
null 就是添加;两种模式分别 emit create / update,detail.vue 的四个 handler 不变。
表单项、校验、文案、提交的数据形状都照旧(题目弹窗添加时带 problemId、编辑时只显示
标题)。有一处刻意的行为变化:编辑弹窗原来只在换了一条记录时才重新填表,取消后再打开
同一条会看到上次没保存的改动;现在每次打开都按库里的值重新填。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 15:16:03 +08:00
xuyue and Claude Opus 5.5
f49834f19f
refactor(班级 PK): 十个柱状图 computed 合成 barChart 工厂
...
十张对比图只差标题和取哪一列,原来各写一份 18 行的 computed。合成
barChart(label, key),key 限定为 ClassComparison 里值必为数字的字段,
渲染出的数据结构(labels / datasets / 配色 / borderWidth)逐字不变。
Chart.js 在本文件只注册了一次,没有重复。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 15:16:03 +08:00
xuyue and Claude Opus 5.5
7a390323fe
refactor(前端): 接口错误改成 ApiError 类,catch 不再写 any
...
拦截器原来 reject 一个裸对象 `{ error, data }`,没有类型也没有类型守卫,于是 46 处
`catch (err: any)` 直接读 `err.data` / `err.error` —— 代码 bug 抛出的 TypeError 也按
接口错误读,弹出来的是 undefined 或英文。
- utils/api.ts:reject 改成 `ApiError extends Error`,字段名沿用 error(错误码)/
data(文案),存量读法语义不变;加 isApiError / errorCode / errorMessage。
errorMessage 对非接口错误一律用 fallback,不把 TypeError 的英文甩给学生
- 46 处 catch 全部换掉 any(45 处接口错误 + pythonSyntaxCheck 里 Skulpt 的错误对象)
- 断网等没有响应体的情况,文案从英文 "Request failed" 换成「网络异常,请检查网络后重试」
浏览器实跑核过:登录密码错误仍显示「用户名或密码不正确」(错误码分支)、
接口错误 reject 出来是 ApiError、网络不通时拿到中文文案、非接口错误走 fallback。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 15:12:40 +08:00
xuyue and Claude Opus 5.5
c43da01ff2
refactor(AI): 四份 SSE 请求代码合成 streamAI + useAIStream
...
学情分析、班级分析、班级 PK、AI 提示四处原来各有一份几乎一样的 fetch + SSE 解析,
错误处理各漏各的:
- 裸 fetch 绕过了 axios 拦截器,未登录时只显示「AI 分析生成失败」而不弹登录框。
拦截器里的全站错误处理抽成 handleGlobalApiError,两条路共用
- 班级分析、班级 PK 离开页面不中止流。useAIStream 在作用域销毁时自动 abort
- AI 提示换了一次提交不掐上一条,旧提示会写进新提交的面板
utils/stream.ts 的 streamAI 负责请求 + 解析 + 错误翻译;组件用 useAIStream
(waiting = 第一个字还没到,running = 流没结束);学情分析在 store 里直接用 streamAI。
浏览器实跑核过:学情分析逐字流出、provider 500 时显示后端的固定中文文案、
未登录调用弹出登录框。班级 PK / 班级分析卸载即中止这条没有在浏览器里点过
(本机库里没有班级数据),逻辑在 useAIStream 的 onScopeDispose。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 15:01:43 +08:00
xuyue and Claude Opus 5.5
ca0dbbf0c5
fix(登录态): 退出和换人登录时整页重载,不留上一个人的数据
...
signOut 只清了 profile 和 localStorage,AI 分析、学情小结等 store 还在内存里。
机房一台机器轮着用,下一个学生登进来就能看到上一个人的 AI 分析。
- 主动退出:router.replace 换成 window.location.replace("/")
- 被动下线(会话过期、别的标签页登出)仍然原地弹登录框,学生正在写的代码不丢;
getMyProfile 记住本页最后一个登录的用户,登进来的换了人就整页重载
挨个 store 去 reset 迟早漏一个,重载一次最彻底。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 14:51:52 +08:00
xuyue and Claude Opus 5.5
fb240d4254
feat(限流): 登录、注册、比赛密码防爆破
...
三处原来都不限次数。学生初始密码弱,/classes/:name/usernames 又能公开列出整班
用户名,等于现成的撞库名单。
services/throttling.ts 加固定窗口计数(INCR + EXPIRE NX,锁定时长就是剩余 TTL),
何时计数由调用方决定:
- 登录按用户名 15 分钟 10 次失败,成功清零;按 IP 15 分钟 100 次失败,成功不清零
(否则拿自己的号登一次就能重置)。机房一个班共用出口 IP,所以 IP 那道放得宽
- 注册按 IP 每小时 100 个成功注册
- 比赛密码按「人 × 比赛」10 分钟 10 次失败
- IP 取 X-Real-IP:api 端口不对外,Caddy 用 {client_ip} 覆盖写入,可信
前端登录、注册、比赛密码三处按新错误码给中文提示。
本机实跑核过:第 11 次错误登录被拦、成功清零(用户名大小写不敏感)、同 IP 换
100 个用户名被拦且别的 IP 不受影响、比赛密码第 11 次被拦。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 14:51:52 +08:00
xuyue and Claude Opus 5
b16e11e85d
fix(题目推荐): 已 AC 过滤下推 SQL、难度按语义排序、难度标签渲染
...
Deploy / deploy (push) Canceled after 0s
相似题目推荐(GET /problems/:displayId/similar)三个 bug:
- 排除已 AC 的题发生在 limit(5) 之后,候选被筛掉几条就少几条,甚至清零让整块
不渲染。而这个接口只在刚 AC 或连挂三次时才调用,正是最容易全中的时候。
(老栈是 .exclude(...) 在切片之前,重写时引入的回归)
- order by difficulty 是 text 字典序,High < Low < Mid,「由易到难」实际是最难的
先上。改成显式 case 排名,并加上共享标签数作为第一排序键、id 兜稳定序。
- 前端 ref<any[]> 盖住了类型错误:getSimilarProblems 已过 filterResult,难度是
中文,模板却在比 'Low' / 'High' 再查 DIFFICULTY 表,结果每条推荐都渲染成黄色
的「中等」。
顺带:比赛题不再发这个请求(接口只在公开题库按 displayId 找,撞号会在比赛中把
题库列给学生);多余的 id in (子查询) 换成 join 中间表。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-22 09:24:46 +08:00
xuyue and Claude Opus 5
954797a9a5
feat(AI 提示): 提示分级(L0 反问 / L1 定位 / L2 概念)与输出后过滤
...
Deploy / deploy (push) Canceled after 0s
提示不再一上来就把话说完。等级记在「学生 × 题目」上,没有单独的表 —— 它就是
ai_hint.level 的历史,当前等级 = 这道题上(最近一次 AC 之后)给过的最高一级。
阶梯(services/hint-level.ts)
- 只有学生点「再多一点提示」才升级(请求带 more),不带就按当前等级再生成一次
- 升一级要先再交一次:锚点是「这一级是**什么时候**开出来的」,也就是这一级最早那条
提示的 ai_hint.create_time,必须有比这个时刻更新的提交才准 +1。锚点不能用提示所在
那条提交的时间 —— 端点谁的提交 id 都认(只校验归属),拿一条老提交去要提示,锚点
就退回到那条老提交的时间,连点两下 more 就能从 L0 爬到 L2,一次新提交都不用交
- AC 之后清零;编译失败自成一档(level = -1),既不消耗也不推进阶梯
- HINT_MIN_FAILURES 3 → 1:门槛的活由阶梯接走了,第一次失败只开放 L0,而 L0 只反问、
什么都不泄露,拦着它没有意义
- canEscalate 由后端算好在 done 事件里给,前端不自己推阶梯
输出后过滤(services/hint-filter.ts)
- 「不要给代码」写在 prompt 里只是软约束,模型忍不住一次就把这一级的意义废掉了。
所以整段生成、过滤通过才推给前端,逐字显示改由前端模拟 —— 边流式边过滤做不到,
发现违规时内容已经在学生屏幕上了
- 判定只用客观、低误报的信号:代码块、过长的行内代码、整行不含中文的类代码行、
和标准答案重合 3 行以上、L0 一句问句都没有
- 违规就重生成一次,只重一次,再不过发写死的兜底话术。重试措辞按档分叉:编译档本来
就允许给片段,对它说「不要出现任何代码」等于用阶梯的标准把这一档也砍了
- 两次都留痕(filter_attempt / filter_blocked / filter_reason),7.5 的输出过滤触发率
就是从这三列出来的
services/ai.ts 加 streamWhole:事件形状和 streamChat 一样,前端不分叉。produce 期间
每 15 秒发一行 SSE 注释当心跳 —— 这条流中间有一大段静默(诊断 20s + 生成 60s +
重生成 60s,最坏 140 秒),而 NPM / nginx 的 proxy_read_timeout 默认 60 秒,超了学生
看到「请求失败」,后端却还在烧第二次调用,那条提示照样落库、照样把等级推上去。
prompt 版本另开 3 / 4(阶梯上每一级都换了 system),编译档仍走 1 / 2 的单段式基线,
两批数据不混在一起。迁移 0021 给 ai_hint 加四列,都可空、不带默认值,已有的行留 null
表示「分级上线前」。
实跑
- 阶梯:在 dev 库上用真实行驱动 decideHintLevel。正常路径 S1→S2→S3 走出 L0→L1→L2,
同级连点 more 不升,AC 之后回 L0,编译档给 L-1 且不推进阶梯。把旧锚点规则复刻出来
跑同一组数据做对照:只拿最老那条提交反复 POST,旧规则 L0→L1→L2(零新提交),
新规则钉死在 L0,正常路径两者行为一致
- 过滤:起假 AI 服务端走完整条链路。L1 摊平代码→重生成后合规(attempt 2 / 未拦),
L0 两次都甩代码块→兜底话术(blocked,reason 两条相连),编译档抄标程→命中「和标准
答案重合 3 行」且追加的是分叉后的措辞
- streamWhole:produce 拖 16.5 秒收到 1 条心跳;同一份流喂给前端 consumeJSONEventStream
只解析出 delta + done(注释行被静默跳过,前端零改动);中途 cancel 断开后 produce
跑完不抛
- api / web typecheck、check:routes、fmt 全过
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-21 19:03:21 -06:00
xuyue and Claude Opus 5
8b4d8899f9
feat(判题机): 自建镜像升级工具链,语言收敛到 C / C++ / Python
...
Deploy / deploy (push) Canceled after 0s
上游 QingdaoU/JudgeServer 停更在 2024-04(registry 上的 latest 和 1.6.1 是同一份
镜像,编译器停在 gcc-13),没有新版可拉,所以自己重编。docker/judge/ 是只改工具链
的 Dockerfile 分叉,server/ 和 Judger/ 从上游固定 commit b28aa56 拉,一行没动。
镜像 oj2-judge-2(不在任何 registry 上:本机 build.sh --save → scp → docker load)
- gcc/g++ 13 → 14.2,Python 3.12 → 3.13.5,都是 trixie 默认
- Go / JDK / Node 整套删掉:前端的题目语言复选框从来只给 C / C++ / Python / SQL,
12 万条提交里 Java 44 条、Golang 15、JavaScript 3,全是很早以前的
- 体积 1.1GB → 433MB;默认走清华源,构建 12 分钟 → 40 秒(--no-mirror 换回官方)
- deploy.sh 加一道自检:镜像不在本机就中止,并打印该跑的三条命令
C 的编译参数加三个 -Wno-error(implicit-function-declaration / int-conversion /
incompatible-pointer-types):gcc-14 把它们从 warning 提成了 error,而 -w 压不住。
语言值统一成 Python(迁移 0019 / 0020)
- 0019:Python3(104527 条提交)与 Python2(3 条)并成 Python,一并改掉 937 道题的
languages、75 个 template 键、15 个 ast_rules 键、257 条 answers、1235 个用户的
成就指标 _languages(languages_used 重算,总和 1928 → 1925,少的 3 个是同时用过
两种 Python 的人)
- 0020:把 Java / JavaScript / Golang 从 84 道题的可选语言里摘掉 —— 不摘的话那些题
的语言下拉还能选 Java,提交必 SYSTEM_ERROR
- 契约新增 normalizeLanguage() 别名表,判题侧一律走 judgeConfigFor():旧客户端
localStorage 里的 Python3、迁移前排进队列的任务都还能判;协作的语言归一也走它,
否则上线那一刻学生页面里的 Python3 会静默落到 C
- 回滚要连数据一起回,只滚代码会让所有 Python 提交变 SYSTEM_ERROR
实跑
- 判题冒烟 docker/judge/smoke.ts 13 条全过:三种语言、六种状态码、gcc 宽松度
- 拿备份里的真实代码逐文件比对新旧镜像的编译结果,0 差异:C 提交 1951 份
(1725 过 / 226 CE)、C++ 882 份、Python 2000 份、20 篇 C 教程的 93 个代码块。
不加那三个 -Wno-error 的话,C 有 26 份会从能过变成 CE
- 迁移在灌了 12.4 万行真实数据的一次性库里跑过:0 残留、没有题目被清空;
dev 库用真正的执行器跑通
- check:ast 56 个 target 全过,前后端 typecheck 均 0
顺带记下一个升级之前就有的坑(现在随 Go 一起消失,写在 README 里):GOCACHE 指向
容器的 tmpfs,判题机重启后第一次 Go 提交是冷构建,Go 1.22 要 5.6 秒 CPU、超过 3 秒
的编译预算,于是重启后第一个交 Go 的学生必吃一次 CE,后面的人缓存热了又都正常。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-09-20 06:24:05 -06:00