submission_id / time_cost / memory_cost / err_info 是 OJ2 两端自己定的线上格式,
没有第三方消费(/ws/submissions 只有 apps/web 一个客户端),没理由留着 snake。
flowchart 那条更别扭:同一个对象里 submission_id 是 snake、criteriaDetails 是驼峰。
三个字段直接删掉而不是改名 —— 它们是从 statistic_info 原样抄出来的一份,前端
一处都没读过(useSubmissionMonitor 只用 submissionId / result / status)。耗时和
错误信息在提交详情里本来就有,判完了去拉一次就是,不必让推送顺带背一份 JSONB
的形状。score 保留,它不涉及大小写。
**没动的都是有外部约束的**,别顺手一起改:
- 发给判题沙箱的请求体(language_config / max_cpu_time / max_memory /
test_case_id / io_mode)和它回的字段(cpu_time / memory / test_case)——
那是沙箱的 API,不是我们的
- 测试点 info 文件的键,沙箱直接读那个文件
- submission.statistic_info 里的 time_cost / err_info / ast_results ——
判题机按这套写,12 万条历史提交就是这形状
- acm_problems_status、progress_detail 这些存量 JSONB
- AST 规则键(for_loop)、成就指标(accepted_count)、reaction 语义键 ——
那是词汇表标识符不是字段名,for_loop 还要映射到 tree-sitter 的 while_statement
验证:起 dev 栈(api + worker + 沙箱),学生账号真提一次代码,抓 /ws/submissions
的帧:
{"type":"submission_update","submissionId":"bf13b7f0…","result":6,"status":"pending"}
{"type":"submission_update","submissionId":"bf13b7f0…","result":7,"status":"judging"}
{"type":"submission_update","submissionId":"bf13b7f0…","result":-2,"status":"finished","score":0}
subscribe 帧也换成 submissionId 并被接受(否则会回一个 error 帧,没有)。
流程图那条路径要 AI 评分才跑得起来,本机没配,只做了类型检查。
前后端同一个 docker 栈一起构建部署,没有版本错配窗口。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
101 lines
3.7 KiB
TypeScript
101 lines
3.7 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(),
|
|
problem: 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)
|
|
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() })
|
|
|
|
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() }),
|
|
),
|
|
})
|
|
|
|
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 CreateFlowchartResponse = z.infer<typeof createFlowchartResponseSchema>
|
|
export type CreateFlowchartRequest = z.infer<typeof createFlowchartRequestSchema>
|
|
|
|
export type FlowchartStatus = z.infer<typeof flowchartStatusSchema>
|