From a8918b41b187f42507be5293ee32c6393279f897 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Wed, 30 Sep 2026 12:21:00 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E9=A2=98=E7=9B=AE=E7=BB=9F=E8=AE=A1):=20?= =?UTF-8?q?=E3=80=8C=E7=AD=94=E6=A1=88=E9=94=99=E8=AF=AF=E5=87=A0=E4=B9=8E?= =?UTF-8?q?=E9=83=BD=E5=8D=A1=E5=9C=A8=E7=AC=AC=201=20=E4=B8=AA=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=82=B9=E3=80=8D=E5=87=A0=E4=B9=8E=E6=AF=8F=E9=81=93?= =?UTF-8?q?=E9=A2=98=E9=83=BD=E5=BC=B9=EF=BC=8C=E8=AF=B4=E4=BA=86=E7=AD=89?= =?UTF-8?q?=E4=BA=8E=E6=B2=A1=E8=AF=B4=E2=80=94=E2=80=94=E7=AD=94=E6=A1=88?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=9C=80=E5=A4=9A=E6=97=B6=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E7=BB=99=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 七成多的题错得最多的都是答案错误(快照 349 / 484),卡在第 1 个测试点过六成的也有 185 / 320 道, 两句提示对哪道题都一样。具体错在哪,每次提交的结果里已经有「题目里的例子 N 就错了」和三栏对照。 运行时错误、编译失败排第一时的提示保留。后端那条扫 info JSONB 数第 1 个点的查询一起删掉。 Co-Authored-By: Claude Opus 5.5 --- apps/api/src/routes/problem-stats.ts | 16 ++-------------- .../src/oj/problem/components/ProblemInfo.vue | 19 ++++++++++--------- packages/contract/src/problem.ts | 2 -- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/apps/api/src/routes/problem-stats.ts b/apps/api/src/routes/problem-stats.ts index 86fba65..c73cbd8 100644 --- a/apps/api/src/routes/problem-stats.ts +++ b/apps/api/src/routes/problem-stats.ts @@ -5,7 +5,7 @@ import { Hono } from "hono" import { optionalAuth, type AppEnv } from "../auth/middleware" import { db, schema } from "../db" import { failure, success } from "../http" -import { JudgeStatus, UNJUDGED_RESULTS, type JudgeStatusValue } from "../judge/status" +import { UNJUDGED_RESULTS, type JudgeStatusValue } from "../judge/status" import { canAccessContest, contestDetailsAllowed, findAccessibleContest } from "../services/contest" import { accepted } from "../services/learning-stats" import { isAdminRole, isTeacherOrAbove, queryInteger } from "./helpers" @@ -18,7 +18,6 @@ const EMPTY: ProblemStats = { solved: 0, tries: { one: 0, few: 0, many: 0 }, failures: [], - wrongAnswerFirstCase: 0, me: null, myClass: null, classes: null, @@ -78,7 +77,7 @@ problemStatsRoutes.get("/problems/:id/stats", optionalAuth, async (c) => { sql`, `, ) // 每人一行:窗口函数先给每条提交标上这个人第一次做对的时刻,再按人数到那一刻为止交了几次 - const [users, failureRows, firstCase] = await Promise.all([ + const [users, failureRows] = await Promise.all([ db.execute(sql` select s.user_id, u.username, u.class_name, count(*)::int as attempts, @@ -103,16 +102,6 @@ problemStatsRoutes.get("/problems/:id/stats", optionalAuth, async (c) => { and s.result not in (${unjudgedList}) group by u.class_name, s.result `), - // 判题机按测试点回一个数组,test_case 是 "1"、"2"…… - db.execute<{ n: number }>(sql` - select count(*)::int as n from ${schema.submission} - where problem_id = ${problem.id} and result = ${JudgeStatus.WRONG_ANSWER} - and jsonb_typeof(info->'data') = 'array' - and exists ( - select 1 from jsonb_array_elements(info->'data') e - where e->>'test_case' = '1' and (e->>'result')::int <> 0 - ) - `), ]) const solvedUsers = users.filter((row) => row.solved) @@ -142,7 +131,6 @@ problemStatsRoutes.get("/problems/:id/stats", optionalAuth, async (c) => { solved: solvedUsers.length, tries, failures: sortFailures(siteFailures), - wrongAnswerFirstCase: firstCase[0]?.n ?? 0, me: user ? { attempts: mine?.attempts ?? 0, solved: mine?.solved ?? false } : null, myClass: myClassName ? { diff --git a/apps/web/src/oj/problem/components/ProblemInfo.vue b/apps/web/src/oj/problem/components/ProblemInfo.vue index d7dbf4b..6c8d90e 100644 --- a/apps/web/src/oj/problem/components/ProblemInfo.vue +++ b/apps/web/src/oj/problem/components/ProblemInfo.vue @@ -103,16 +103,17 @@ const failedTotal = computed(() => ) const topFailures = computed(() => (stats.value?.failures ?? []).slice(0, 3)) -/** 错得最多的那一种,给一句该怎么办。都指向「运行例子」:提交前自己就能看出来 */ +/** + * 错得最多的那一种,给一句该怎么办。都指向「运行例子」:提交前自己就能看出来。 + * + * **答案错误最多时不给**:七成多的题错得最多的都是答案错误(快照 349 / 484),「卡在第 1 个 + * 测试点」也是常态(320 道里 185 道过 60%),这句话几乎每道题都一样,等于没说(用户说的)。 + * 具体错在哪,每次提交的结果里有「题目里的例子 N 就错了」和三栏对照。 + * 运行时错误、编译失败排第一的题少,出现了才说明这题有特点 + */ const tip = computed(() => { - const s = stats.value - const top = s?.failures[0] - if (!s || !top) return "" - if (top.result === -1) { - return s.wrongAnswerFirstCase >= top.count * 0.6 - ? "答案错误几乎都卡在第 1 个测试点:多半是例子都没对上。先点「运行例子」,把你的输出和例子一行一行对一对。" - : "答案错误最多:输出和标准答案对不上。留意空格、换行和标点,交之前先点「运行例子」对一对。" - } + const top = stats.value?.failures[0] + if (!top) return "" if (top.result === 4) { return "运行时错误最多:程序跑到一半出错了。交之前先点「运行例子」,看看有没有红色的报错。" } diff --git a/packages/contract/src/problem.ts b/packages/contract/src/problem.ts index 1811c01..2bb678d 100644 --- a/packages/contract/src/problem.ts +++ b/packages/contract/src/problem.ts @@ -464,8 +464,6 @@ export const problemStatsSchema = z.object({ tries: z.object({ one: z.number().int(), few: z.number().int(), many: z.number().int() }), /** 没通过的提交按结果分,多的在前(不含判题中) */ failures: z.array(failureCountSchema), - /** 答案错误里第 1 个测试点就没过的条数 —— 多半是例子都没对上,提示先点「运行例子」 */ - wrongAnswerFirstCase: z.number().int(), /** 我自己:交了几次、做对没有。没登录为 null */ me: z.object({ attempts: z.number().int(), solved: z.boolean() }).nullable(), /** 学生自己班这题 x / y 人做对(y 是班里交过的人)。没班级、比赛里为 null */