L.Leverage Learn从第一行代码,到读懂真实后端查看项目源码

OJ 评测系统 / 第 17 课

一次提交如何变成可查询状态

从创建提交 API 追踪数据库记录、代码附属数据和评测状态,区分接收请求与得到判题结果。

◷ 预计 18 分钟 · 面向初学者

上一课已经看过 NestJS 的 Controller 与 Service;这节开始把它们放回 OJ 的真实业务里。提交不是一次 HTTP 调用就能完成的计算:接口负责接受并登记,队列负责稍后安排工作,评测器运行后才有最终结果。状态是数据库里这段过程的可观察标记,不是浏览器推测出来的进度条。

请求入口先做什么

公开实现的入口是登录保护的 POST /submissions。Controller 从 JWT 当前用户取得 user.sub,把正文 DTO 交给 SubmissionService.create;用户 ID 因此来自已验证身份,而非让调用者在正文里替别人提交。DTO 中 problemId 是至少为 1 的整数,code 是非空字符串、最长 65536 字符;language 则是数字,contestId、courseId 可选但必须是整数。

格式合法不代表运行环境支持该语言。Service 先做每用户频率限制,再确认题目存在,然后按内部语言映射检查支持情况。此版本 Python 3 使用数字 9,不是诸如 "python3" 的字符串。DTO 的 @IsNumber() 只回答“是不是数字”,Service 的支持语言表才回答“评测器认不认识它”。题目不存在或语言不支持都会在任务入队前被拒绝。

两份数据和状态起点

Service 保存一条 Submission:记录用户、题目、语言、比赛或课程关联、provider: 'internal'、评测尝试标识 judgeAttempt,初始状态 PENDING。提交代码另外存入 SubmissionMisc,以 submissionId 关联。这样列表/状态与代码、评测详情分开存储;它们仍属于同一次提交。阅读这段逻辑时要区分“两个数据库实体”和“一条 HTTP 请求”:服务依次保存基本记录、保存代码、再派发任务。教学上可把它看成提交登记过程,但不要仅凭这段代码就说两次保存包在一个事务里;出错时具体补偿由可见的派发异常分支处理。

POST /submissions
  → JWT + DTO → SubmissionService.create
  → 限频 / 查题目 / 检查内部语言
  → Submission(PENDING) + SubmissionMisc(code)
  → 投递队列 → 返回提交记录

此处返回成功只说明创建路径走到了响应,不等于程序已编译,更不等于 AC。创建之后可以用 GET /submissions/:id/status 读取 { status };代码实现从数据库查询 id,status。Controller 注释提到 Redis,但读该版本实现时应以 Service 的 SQL 查询为准。详情接口与状态接口是两种读取方式,终态以状态枚举数字保存。轮询方应把响应视为数字枚举,而不是看到状态数字变大就显示“更完成”:例如 COMPILING=11 大于 JUDGING=10,只是历史编号不同,不是进度大小。用户界面可用状态映射成文案,但接口和数据库依旧保存原编号。

状态枚举保留历史编号:PENDING=9、JUDGING=10、COMPILING=11,结果类状态包括 AC=0、WA=1、TLE=2、MLE=3、CE=4、SE=5、RE=6、PE=7、OLE=12。数字不按“先后顺序”排序,不能把更大的值理解成进展更多。评测服务先把有效任务置为 COMPILING,引擎准备好程序、即将运行用例时通过回调改成 JUDGING;最后 ReceiveService.finalize 结算结果。若派发队列抛错,创建代码会尝试以 SE 结算并重新抛出错误。

小练习

语言字段是数字 999,为什么它能通过 DTO 类型检查却不能创建内部评测?看到 PENDING 是否能告诉你代码已经开始运行?

答案:@IsNumber() 仅验证类型,Service 还要用支持语言映射核对 ID;不在表中的语言会被拒绝。PENDING 只表示尚在等待/待处理阶段,具体是否进入编译要看后续状态。

去真实仓库看看

以下均固定到公开提交 de34b268e75f293e904d7734c194a4ff2467b7a8:

互动练习:状态编号不是进度百分比

这个公开版本的 COMPILING 编号是 11,JUDGING 是 10。看到 11 比 10 大,能说编译比运行更接近结束吗?

选择一个答案

✓
这一课,你已经能……

提交先以数字语言标识和代码通过校验,写入 Submission 与 SubmissionMisc 后进入 PENDING;评测执行阶段再推进到 COMPILING、JUDGING 和终态。