分数链路全链路现状分析与修改方案
分数链路全链路现状分析与修改方案
本文目录 10 节

背景
- 练习场景:需要向用户展示得分(OI 计分,passedCases / totalCases 或 caseScores 加权)
- OI 比赛场景:需要向用户展示得分,并用于排行榜
- ACM 比赛场景:不向用户展示分数,只关心是否 AC
一、数据模型现状(均已就绪,无需改动)
| 类 | 字段 | 状态 |
|---|---|---|
QuestionJudgeConfig(judge服务 & 主服务) | caseScores: List<Integer> | ✅ 已有 |
QuestionSubmitJudgeInfo(judge服务 & 主服务) | earnedScore, maxScore, passedCaseCount, totalCaseCount | ✅ 已有 |
StatsMessage(judge服务 & 主服务) | earnedScore, maxScore, passedCases, totalCases | ✅ 已有 |
QuestionSubmit.judgeInfo | JSON 列,存储整个 QuestionSubmitJudgeInfo | ✅ 已有 |
二、链路各节点现状分析
节点 1:DefaultJudgeStrategy.doJudge() ❌ 缺失
文件:backend-judge-service/.../judge/strategy/DefaultJudgeStrategy.java
现状:
- 已正确统计
passedCaseCount/totalCaseCount judgeConfig.getCaseScores()已传入但从未读取- 返回的
QuestionSubmitJudgeInfo中earnedScore/maxScore永远为null
修改方案:在判题循环中,按测试点是否通过累加分数:
// 在循环前初始化
List<Integer> caseScores = judgeConfig.getCaseScores();
int earnedScore = 0;
int maxScore = 0;
// 在每个测试点处理时
int caseScore = (caseScores != null && i < caseScores.size()) ? caseScores.get(i) : 1;
maxScore += caseScore;
if (/* 该测试点通过 */) {
earnedScore += caseScore;
}
// 构建返回值时
.earnedScore(earnedScore)
.maxScore(maxScore)
节点 2:JudgeServiceImpl:判题结果回写 DB ⚠️ 依赖节点1
文件:backend-judge-service/.../service/impl/JudgeServiceImpl.java,第 152 行
updateSubmitStatus(questionSubmitId, finalStatus, judgeInfo);
现状:
- 将整个
judgeInfo对象作为 JSON 写入question_submit.judge_info - 节点 1 修好后,
earnedScore/maxScore会自动随 JSON 落库 - 本节点无需单独修改
节点 3:JudgeServiceImpl.sendContestStatsMessage() ❌ 缺失
文件:backend-judge-service/.../service/impl/JudgeServiceImpl.java,第 535-546 行
现状:发送 MQ 消息时未填 earnedScore / maxScore:
StatsMessage.builder()
.passedCases(judgeInfo.getPassedCaseCount())
.totalCases(judgeInfo.getTotalCaseCount())
// ❌ 缺少:
// .earnedScore(judgeInfo.getEarnedScore())
// .maxScore(judgeInfo.getMaxScore())
.build()
修改方案:补上两个字段:
.earnedScore(judgeInfo.getEarnedScore())
.maxScore(judgeInfo.getMaxScore())
注意:此处仅在
scene == CONTEST时发送,练习场景不走 MQ,分数直接存 DB 即可。
节点 4:StatsConsumer → OI 排行 ⚠️ 依赖节点3
文件:backend-tyut-oj/.../mq/StatsConsumer.java,第 87-112 行
现状:
int passedCases = message.getPassedCases() != null ? message.getPassedCases() : 0;
int totalCases = message.getTotalCases() != null ? message.getTotalCases() : 0;
// ...
contestRankService.updateOIRanking(
message.getContestId(), message.getUserId(),
message.getQuestionId(), passedCases, totalCases); // ❌ 用 case 数而非分数
修改方案:优先使用 earnedScore / maxScore,回退到 passedCases / totalCases:
int earnedScore = message.getEarnedScore() != null ? message.getEarnedScore() : passedCases;
int maxScore = message.getMaxScore() != null ? message.getMaxScore() : totalCases;
contestRankService.updateOIRanking(
message.getContestId(), message.getUserId(),
message.getQuestionId(), earnedScore, maxScore);
ACM 赛制路径(
updateACMRanking)不涉及分数,无需改动。
三、前端展示策略
| 场景 | 是否展示分数 | 数据来源 |
|---|---|---|
| 练习 | ✅ 展示 | questionSubmit.judgeInfo.earnedScore / maxScore |
| OI 比赛 | ✅ 展示 | 同上 |
| ACM 比赛 | ❌ 不展示 | 只看 status == ACCEPTED |
前端判断逻辑:根据比赛的 type 字段(ACM / OI)决定是否渲染分数区域。练习提交无 contestId,默认展示分数。
四、修改清单(按优先级)
| 优先级 | 文件 | 改动 | 影响范围 |
|---|---|---|---|
| 1 | DefaultJudgeStrategy.java | 计算并填充 earnedScore/maxScore | 所有场景的分数落库 |
| 2 | JudgeServiceImpl.java(sendContestStatsMessage) | MQ 消息补填两个分数字段 | OI 比赛排行 |
| 3 | StatsConsumer.java | updateOIRanking 改用分数而非 case 数 | OI 比赛排行 |
| 4 | 前端提交详情组件 | 根据赛制决定是否展示分数 | 用户界面 |
节点 2(DB 回写)无需修改,节点 1 修好后自动生效。
五、向下兼容说明
caseScores == null时:earnedScore = passedCaseCount,maxScore = totalCaseCount,退化为等权计分,存量数据行为不变- ACM 赛制排行逻辑完全不读取分数字段,不受任何影响
评论
无需登录,审核通过后公开。只有博主可以回复。
正在加载…
已公开的评论