From 6b5ca745bb846d373749851cc5f7d286ec5b1590 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Mon, 26 Oct 2026 14:15:04 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E5=87=BA=E9=A2=98=E9=A1=B5):=20=E6=94=B9?= =?UTF-8?q?=E4=BA=86=E6=8C=91=E6=88=98=E7=82=B9=E4=BF=9D=E5=AD=98=EF=BC=8C?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E3=80=8C=E4=BF=9D=E5=AD=98=E5=A5=BD=E4=BA=86?= =?UTF-8?q?=E3=80=8D=E5=85=B6=E5=AE=9E=E4=B8=80=E6=9D=A1=E9=83=BD=E6=B2=A1?= =?UTF-8?q?=E5=AD=98=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 出题页发请求时漏了 challenges 这个字段,后端又把「不带」当成「不动原来的」, 所以改动被静默丢掉。现在出题页带上它,接口也改成必须带(null 是清空), 以后再漏传会直接报错,不会再悄悄丢。 Co-Authored-By: Claude Opus 5.5 --- apps/api/src/routes/admin/problem.ts | 4 +--- apps/web/src/admin/api.ts | 5 ++++- packages/contract/src/admin.ts | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/api/src/routes/admin/problem.ts b/apps/api/src/routes/admin/problem.ts index 001b565..3a02dd2 100644 --- a/apps/api/src/routes/admin/problem.ts +++ b/apps/api/src/routes/admin/problem.ts @@ -260,9 +260,7 @@ type ProblemInput = ReturnType async function validateProblem(data: ProblemInput, previousChallenges: ProblemChallenges | null = null): Promise<{ error: string } | Validated> { const checked = commonChecks(data) if ('error' in checked) return checked - // 不带挑战(旧版出题页)就原样留着,见契约 createProblemRequestSchema.challenges - const normalized = - data.challenges === undefined ? { challenges: previousChallenges } : normalizeChallenges(data.challenges, previousChallenges, data.languages) + const normalized = normalizeChallenges(data.challenges, previousChallenges, data.languages) if ('error' in normalized) return normalized const { challenges } = normalized if (!checked.sql) return { sql: false, sqlDisplay: null, challenges } diff --git a/apps/web/src/admin/api.ts b/apps/web/src/admin/api.ts index 80db786..c4cd9f0 100644 --- a/apps/web/src/admin/api.ts +++ b/apps/web/src/admin/api.ts @@ -1,6 +1,7 @@ import { toAdminType, toProblemPermission } from '@oj2/contract' import type { AddContestProblemRequest, + ChallengeInput, ChallengeSuggestionsResponse, AddProblemsToSetRequest, BatchProblemTagRequest, @@ -358,8 +359,9 @@ export function generateTestInputs(data: GenerateTestInputsRequest) { /** 出站补默认值。字段名两边已经一致,不再做键名转换 */ function toProblemBody(problem: AdminProblem | BlankProblem) { - const p = problem as Partial> & { + const p = problem as Partial> & { flowchartData?: Record | null + challenges?: Record | null } return { _id: p._id, @@ -387,6 +389,7 @@ function toProblemBody(problem: AdminProblem | BlankProblem) { flowchartData: p.flowchartData ?? null, flowchartHint: p.flowchartHint ?? null, astRules: p.astRules ?? null, + challenges: p.challenges ?? null, sqlConfig: p.sqlConfig ?? null, } } diff --git a/packages/contract/src/admin.ts b/packages/contract/src/admin.ts index 869b987..6348d5d 100644 --- a/packages/contract/src/admin.ts +++ b/packages/contract/src/admin.ts @@ -1004,10 +1004,10 @@ export const createProblemRequestSchema = z.object({ flowchartHint: z.string().nullable().default(null), astRules: astRulesSchema.nullable().default(null), /** - * 不带这个键 = 不动原来的挑战。上线那一刻浏览器里还开着旧版出题页,它保存时不带挑战, - * 给个默认 null 的话一保存就把挑战清了;null 才是「清空」 + * 必须带:null 是「清空」。不给默认值是故意的 —— 前端漏传这个键的话,给默认 null + * 会一保存就把挑战清了,而「不带 = 不动」又会让出题页改的挑战静默存不上 */ - challenges: challengeInputsSchema.nullable().optional(), + challenges: challengeInputsSchema.nullable(), // order_sensitive 缺省补 false —— 与旧后端 SQLConfigSerializer 的 // `BooleanField(default=False)` 一致 sqlConfig: sqlConfigSchema