perf(题单): 学生端题目列表少两次 join 一次查询,顺带把并列的 order 排稳

## 排序

学生端 /problem-sets/:id/problems 只按 order 排,没有 tiebreaker,而卡片是按数组
下标编号的(#1 #2 #3)。order 并列时 Postgres 不保证次序,题单 8(3 道题 order 都是
10)、题单 11(2 道并列 4)、题单 14(3 道并列 0)实际就有并列,于是「第 3 题」指
哪道题每次刷新都可能变。后台那条列表一直是 order + id 排的,两边本来就不一致。

补上 asc(id)。教师进度视图里那份题目清单(同一路由文件 :398)有同样的问题,一并补。

实跑:四道题、三道 order 并列,连打 5 次次序完全一致。

## 载荷

这个接口原来复用 problemListItemSchema,为此要多 join user + user_profile 凑
createdBy、再多查一次标签表凑 tags —— 而题单卡片只渲染题号、标题、难度、分数和
完成标记。tags / submissionNumber / acceptedNumber / createdBy / contestId /
allowFlowchart / showFlowchart / hasAstRules 一个都不用,myStatus 甚至写死是 null。

而且 select 的是 schema.problem 整行,题面、样例、答案、ast_rules、flowchart_data、
sql_display 全都白拉回来一遍。

改成 problemSetProblemItemSchema(id / _id / title / difficulty 四个字段),查询相应
收窄:两次 join 去掉,标签那次查询整条去掉,行查询只取四列。每次请求从 4 条查询降到
3 条,其中最大的那条不再拖整行题面。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QqqZwxtXLo2GTqMi51C94D
This commit is contained in:
2026-08-31 08:47:57 -06:00
parent d7a6414735
commit d9e6a2a3f0
2 changed files with 43 additions and 35 deletions

View File

@@ -1,7 +1,7 @@
import { z } from "zod"
import { paginatedSchema, sampleUserSchema } from "./common"
import { problemListItemSchema } from "./problem"
import { maskedProblemDifficultySchema } from "./problem"
export const problemSetUserProgressSummarySchema = z.object({
isJoined: z.boolean(),
@@ -41,10 +41,25 @@ export const problemSetSchema = z.object({
export const problemSetListSchema = paginatedSchema(problemSetSchema)
/**
* 题单页的题目卡片只渲染题号、标题、难度,所以只下发这三样(外加题目主键)。
*
* 以前这里复用 problemListItemSchema代价是服务端每次都要多 join 一次 user +
* user_profile 凑 createdBy、再多查一次标签表凑 tags而 tags / submissionNumber /
* acceptedNumber / createdBy / flowchart / hasAstRules / myStatus 在题单页一个都不渲染
* —— myStatus 甚至是写死的 null。
*/
export const problemSetProblemItemSchema = z.object({
id: z.number().int(),
_id: z.string(),
title: z.string(),
difficulty: maskedProblemDifficultySchema,
})
export const problemSetProblemSchema = z.object({
id: z.number().int(),
problemsetId: z.number().int(),
problem: problemListItemSchema,
problem: problemSetProblemItemSchema,
order: z.number().int(),
isRequired: z.boolean(),
score: z.number().int(),