fix(比赛): 比赛进行中,学生打开比赛题必 500

contest.ts 两处把「藏起来的难度」表示成空串,而 problemDifficultySchema
是严格的三值枚举,parse 直接抛 ZodError。contestDetailsAllowed 在
「比赛进行中 + 看的人不是管理员」时就是假 —— 也就是正常学生在正常比赛里,
比赛题列表和详情页一开就挂。dev 库 contest 表一直是空的,这条路径没被跑到过。

改成下发 null,契约里给这两个字段单独起了 maskedProblemDifficultySchema。
语义上对齐旧 Django:ProblemSafeSerializer 把 difficulty 放在 exclude 里,
字段整个不下发,而不是给一个假值。

端上顺着 null 补了四处:ProblemInfo 的「难度」项整条不渲染(和旧栈表现一致),
题目列表、题单列表同理,transforms 的 ProblemFiltered.difficulty 也放开为可空。
这几处是 vue-tsc 逐个揪出来的,正是严格枚举该起的作用。

实测(学生 / 超管 × 比赛题 / 普通题 四种组合):学生看比赛题详情与列表
从 500 变 200 且 difficulty 为 null,超管仍拿到真值 Low,普通题不受影响;
页面上「难度」整项消失,统计面板其余内容正常。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016DHxhKNxXfG89JnVzHbvgj
This commit is contained in:
2026-08-30 09:01:56 -06:00
parent c7132025f7
commit 4b6242ba70
7 changed files with 23 additions and 8 deletions

View File

@@ -9,6 +9,16 @@ import { paginatedSchema, sampleUserSchema } from "./common"
*/
export const problemDifficultySchema = z.enum(["Low", "Mid", "High"])
/**
* 题目详情/列表里的难度。**可为 null** —— 比赛进行中、看的人又不是管理员时,
* 难度和提交数一样属于「先别告诉参赛者」的信息(旧 Django 的
* `ProblemSafeSerializer` 直接把 difficulty 放进 exclude字段整个不下发
*
* 端上必须当「没有」处理,不要拿它去查 DIFFICULTY 映射表 —— 这正是
* problemDifficultySchema 写成严格枚举要防的那件事。
*/
export const maskedProblemDifficultySchema = problemDifficultySchema.nullable()
/**
* SQL 题配置与展示数据。两者都是 `problem.sql_config` / `problem.sql_display`
* 的 **JSONB 原文**,所以键名保持 snake_case —— 生产库 9 道 SQL 题存的就是这个形状
@@ -292,7 +302,7 @@ export const problemDetailSchema = z.object({
lastUpdateTime: z.string().nullable(),
timeLimit: z.number().int(),
memoryLimit: z.number().int(),
difficulty: problemDifficultySchema,
difficulty: maskedProblemDifficultySchema,
source: z.string().nullable(),
prompt: z.string().nullable(),
submissionNumber: z.number().int(),
@@ -327,7 +337,7 @@ export const problemListItemSchema = z.object({
title: z.string(),
submissionNumber: z.number().int(),
acceptedNumber: z.number().int(),
difficulty: problemDifficultySchema,
difficulty: maskedProblemDifficultySchema,
createdBy: sampleUserSchema,
tags: z.array(z.string()),
contestId: z.number().int().nullable(),