Files
OJ2/packages/contract/src/flowchart.ts
T
xuyueandClaude Opus 5.5 52bf2d6111
Deploy / deploy (push) Canceled after 0s
feat(提交列表): 题号填错一道时顶上提示「没有这道题」;← → 也能翻上一条下一条
- 列表接口回带查不到的题号(公开列表只认公开题,比赛列表只认本场的题),
  一次填好几道时打错的那道原来被悄悄忽略
- 方向键恢复成四个都能翻(9 月加的时候就是这样,老师已经用惯了)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:58:31 +08:00

160 lines
5.5 KiB
TypeScript

import { z } from "zod"
import { paginatedSchema } from "./common"
export const flowchartStatusSchema = z.union([
z.literal(0),
z.literal(1),
z.literal(2),
z.literal(3),
])
export const createFlowchartRequestSchema = z.object({
problemId: z.number().int().positive(),
mermaidCode: z
.string()
.trim()
.min(1)
.max(50_000)
.refine(
(value) => value.split("\n").filter((line) => line.trim()).length <= 200,
"Flowchart is too complex",
),
flowchartData: z.record(z.string(), z.unknown()).default({}),
})
export const flowchartSubmissionSchema = z.object({
id: z.string(),
username: z.string(),
problemId: z.number().int(),
mermaidCode: z.string(),
flowchartData: z.record(z.string(), z.unknown()),
status: flowchartStatusSchema,
createTime: z.string(),
aiScore: z.number().nullable(),
aiGrade: z.string().nullable(),
aiFeedback: z.string().nullable(),
aiSuggestions: z.string().nullable(),
aiCriteriaDetails: z.record(z.string(), z.unknown()),
aiProvider: z.string(),
aiModel: z.string(),
processingTime: z.number().nullable(),
evaluationTime: z.string().nullable(),
})
export const flowchartListItemSchema = z.object({
id: z.string(),
username: z.string(),
problemDisplayId: z.string(),
problemTitle: z.string(),
status: flowchartStatusSchema,
createTime: z.string(),
aiScore: z.number().nullable(),
aiGrade: z.string().nullable(),
aiProvider: z.string(),
aiModel: z.string(),
processingTime: z.number().nullable(),
evaluationTime: z.string().nullable(),
showLink: z.boolean(),
})
export const flowchartListSchema = paginatedSchema(flowchartListItemSchema).extend({
/** 题号框里查不到的题号,口径同 submissionListSchema.unknownProblems */
unknownProblems: z.array(z.string()),
})
export const createFlowchartResponseSchema = z.object({
submissionId: z.string(),
status: z.literal("pending"),
})
export const flowchartCurrentSchema = z.object({
count: z.number().int(),
score: z.number(),
grade: z.string(),
})
export const flowchartDetailSchema = z.object({
submission: flowchartSubmissionSchema.nullable(),
count: z.number().int(),
})
/**
* 流程图画到这两档算这道题做完:题单进度、课堂条、课堂看板、「你画的流程图」、结果页签的 ✓
* 都按它(设计文档 2026-09-28-problem-page-redesign 第 3 节决定 4、6)。前后端只有这一份
*/
export const FLOWCHART_PASS_GRADES = ["S", "A"] as const
export function isFlowchartPass(grade: string | null | undefined) {
return (FLOWCHART_PASS_GRADES as readonly string[]).includes(grade ?? "")
}
/** 自己在一道题上评完的历次分数,早的在前;hidden = 被题单闸门藏起来的次数 */
export const flowchartScoresSchema = z.object({
scores: z.array(
z.object({
id: z.string(),
score: z.number(),
grade: z.string(),
createTime: z.string(),
}),
),
hidden: z.number().int(),
})
export const flowchartStatisticsSchema = z.object({
totalCount: z.number().int(),
avgScore: z.number(),
gradeDistribution: z.record(z.string(), z.number().int()),
criteriaAverages: z.record(z.string(), z.object({ avg: z.number(), max: z.number() })),
personCount: z.number().int(),
completedCount: z.number().int(),
wordFrequencies: z.array(z.object({ word: z.string(), count: z.number().int() })),
// 与提交统计共用「未完成学生」的形状,见 submission.ts 的 unacceptedStudentSchema
dataUnaccepted: z.array(z.object({ username: z.string(), realName: z.string() })),
/**
* 每人**最好的一次**(分数最高的那张),统计页的「每人最好的一次」表用。
* 只含普通学生;按最好成绩从低到高排,没拿到 A 的排前面 —— 老师要先看的是他们
*/
people: z.array(
z.object({
username: z.string(),
realName: z.string(),
bestScore: z.number().nullable(),
bestGrade: z.string().nullable(),
count: z.number().int(),
/** 拿到 A / S 的题数(查几道题时要每道都拿到才算完成) */
goodProblems: z.number().int(),
}),
),
/** people 超过上限被截了(截掉的是成绩最好的那些) */
peopleTruncated: z.boolean(),
/** 查了几道题(没填题号为 0,此时拿到一道 A / S 就算完成) */
problemCount: z.number().int(),
})
export const flowchartUpdateSchema = z.object({
type: z.enum([
"flowchart_evaluation_completed",
"flowchart_evaluation_failed",
"flowchart_evaluation_update",
]),
submissionId: z.string(),
score: z.number().optional(),
grade: z.string().optional(),
feedback: z.string().optional(),
suggestions: z.string().optional(),
criteriaDetails: z.unknown().optional(),
error: z.string().optional(),
})
export type FlowchartUpdate = z.infer<typeof flowchartUpdateSchema>
export type FlowchartStatistics = z.infer<typeof flowchartStatisticsSchema>
export type FlowchartSubmission = z.infer<typeof flowchartSubmissionSchema>
export type FlowchartListItem = z.infer<typeof flowchartListItemSchema>
export type FlowchartList = z.infer<typeof flowchartListSchema>
export type FlowchartCurrent = z.infer<typeof flowchartCurrentSchema>
export type FlowchartDetail = z.infer<typeof flowchartDetailSchema>
export type FlowchartScores = z.infer<typeof flowchartScoresSchema>
export type CreateFlowchartResponse = z.infer<typeof createFlowchartResponseSchema>
export type CreateFlowchartRequest = z.infer<typeof createFlowchartRequestSchema>
export type FlowchartStatus = z.infer<typeof flowchartStatusSchema>