diff --git a/apps/api/src/routes/admin/contest.ts b/apps/api/src/routes/admin/contest.ts index b945afe..0204d83 100644 --- a/apps/api/src/routes/admin/contest.ts +++ b/apps/api/src/routes/admin/contest.ts @@ -5,7 +5,7 @@ import { type AdminContest, type AdminContestList, } from "@oj2/contract" -import { and, count, desc, eq, ilike, inArray } from "drizzle-orm" +import { and, count, countDistinct, desc, eq, ilike, inArray } from "drizzle-orm" import { Hono } from "hono" import { requireTeacher, type AppEnv } from "../../auth/middleware" @@ -93,12 +93,39 @@ adminContestRoutes.get("/contests", requireTeacher, async (c) => { .innerJoin(schema.user, eq(schema.contest.createdById, schema.user.id)) .leftJoin(schema.userProfile, eq(schema.userProfile.userId, schema.user.id)) .where(where) - .orderBy(desc(schema.contest.createTime)) + // 按开始时间:原来按创建时间,「复制一份」出来的旧比赛会排到最前面 + .orderBy(desc(schema.contest.startTime), desc(schema.contest.id)) .limit(limit) .offset(offset), ]) + const ids = rows.map((row) => row.contest.id) + const [problemCounts, participantCounts] = ids.length + ? await Promise.all([ + db + .select({ contestId: schema.problem.contestId, value: count() }) + .from(schema.problem) + .where(inArray(schema.problem.contestId, ids)) + .groupBy(schema.problem.contestId), + db + .select({ + contestId: schema.submission.contestId, + value: countDistinct(schema.submission.userId), + }) + .from(schema.submission) + .where(inArray(schema.submission.contestId, ids)) + .groupBy(schema.submission.contestId), + ]) + : [[], []] + const problemsOf = new Map(problemCounts.map((row) => [row.contestId, row.value])) + const peopleOf = new Map(participantCounts.map((row) => [row.contestId, row.value])) return success(c, { - results: await Promise.all(rows.map(serialize)), + results: await Promise.all( + rows.map(async (row) => ({ + ...(await serialize(row)), + problemCount: problemsOf.get(row.contest.id) ?? 0, + participantCount: peopleOf.get(row.contest.id) ?? 0, + })), + ), total: totalRows[0]?.value ?? 0, } satisfies AdminContestList) }) diff --git a/packages/contract/src/admin.ts b/packages/contract/src/admin.ts index ed0ec21..d049465 100644 --- a/packages/contract/src/admin.ts +++ b/packages/contract/src/admin.ts @@ -394,7 +394,13 @@ export const adminContestSchema = z.object({ contestType: z.enum(["Public", "Password Protected"]), }) -export const adminContestListSchema = paginatedSchema(adminContestSchema) +/** 后台比赛列表的一行:比赛本身,加几道题、几人参加(交过题的人数) */ +export const adminContestListItemSchema = adminContestSchema.extend({ + problemCount: z.number().int(), + participantCount: z.number().int(), +}) + +export const adminContestListSchema = paginatedSchema(adminContestListItemSchema) export const createContestRequestSchema = z.object({ title: z.string().trim().min(1).max(128), @@ -1008,6 +1014,7 @@ export type CreateProblemRequest = z.infer export type AdminContest = z.infer export type AdminContestList = z.infer +export type AdminContestListItem = z.infer export type AdminUser = z.infer export type AdminUserList = z.infer export type CheckUsernamesResponse = z.infer