【OJ项目】深入剖析 JudgeServiceImpl 类:题目的判题逻辑详解

《深入剖析 JudgeServiceImpl 类:题目的判题逻辑详解》

一、引言

在编程竞赛或者在线编程平台中,判题服务是核心功能之一。它负责对用户提交的代码进行编译、执行,并根据预设的测试用例判断代码的正确性。今天我们就来详细剖析一个名为 JudgeServiceImpl 的 Java 服务类,它实现了题目的判题逻辑。

二、代码整体概述

JudgeServiceImpl 类实现了 JudgeService 接口,主要用于处理题目的判题流程。整个判题过程可以分为以下几个主要步骤:

  1. 获取题目和提交信息:根据题目提交 ID 获取对应的题目和提交信息。
  2. 检查提交状态:确保题目提交状态为等待中,避免重复判题。
  3. 更新状态为判题中:将题目提交状态更新为“判题中”,防止重复执行。
  4. 调用代码沙箱:执行用户提交的代码,获取执行结果。
  5. 设置判题状态和信息:根据沙箱执行结果设置题目的判题状态和信息。
  6. 更新数据库判题结果:将最终的判题结果更新到数据库中。

三、代码详细解析

3.1 类的定义和依赖注入

@Service
public class JudgeServiceImpl implements JudgeService {@Resourceprivate QuestionFeignClient questionFeignClient;@Resourceprivate JudgeManager judgeManager;@Value("${codesandbox.type:example}")private String type;// ... 其他代码 ...
}
  • @Service 注解表明这是一个 Spring 服务类。
  • QuestionFeignClient 用于远程调用获取题目和提交信息,以及更新提交状态。
  • JudgeManager 负责具体的判题逻辑。
  • @Value 注解用于获取配置文件中代码沙箱的类型,默认值为 example

3.2 获取题目和提交信息

@Override
public QuestionSubmit doJudge(long questionSubmitId) {// 1)传入题目的提交 id,获取到对应的题目、提交信息(包含代码、编程语言等)QuestionSubmit questionSubmit = questionFeignClient.getQuestionSubmitById(questionSubmitId);if (questionSubmit == null) {throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "提交信息不存在");}Long questionId = questionSubmit.getQuestionId();Question question = questionFeignClient.getQuestionById(questionId);if (question == null) {throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目不存在");}// ... 其他代码 ...
}
  • 通过 questionFeignClient 根据 questionSubmitId 获取题目提交信息。
  • 如果提交信息不存在,抛出 BusinessException 异常。
  • 从提交信息中获取 questionId,再通过 questionFeignClient 获取对应的题目信息。
  • 如果题目信息不存在,同样抛出异常。

3.3 检查提交状态

// 2)如果题目提交状态不为等待中,就不用重复执行了
if (!questionSubmit.getStatus().equals(QuestionSubmitStatusEnum.WAITING.getValue())) {throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目正在判题中");
}
  • 检查题目提交状态是否为等待中,如果不是则抛出异常,避免重复判题。

3.4 更新状态为判题中

// 3)更改判题(题目提交)的状态为 “判题中”,防止重复执行
QuestionSubmit questionSubmitUpdate = new QuestionSubmit();
questionSubmitUpdate.setId(questionSubmitId);
questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.RUNNING.getValue());
boolean update = questionFeignClient.updateQuestionSubmitById(questionSubmitUpdate);
if (!update) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, "题目状态更新错误");
}
  • 创建一个 QuestionSubmit 对象,设置其 ID 和状态为“判题中”。
  • 调用 questionFeignClientupdateQuestionSubmitById 方法更新状态。
  • 如果更新失败,抛出异常。

3.5 调用代码沙箱

// 4)调用沙箱,获取到执行结果
CodeSandbox codeSandbox = CodeSandboxFactory.newInstance(type);
codeSandbox = new CodeSandboxProxy(codeSandbox);
String language = questionSubmit.getLanguage();
String code = questionSubmit.getCode();
// 获取输入用例
String judgeCaseStr = question.getJudgeCase();
List<JudgeCase> judgeCaseList = JSONUtil.toList(judgeCaseStr, JudgeCase.class);
List<String> inputList = judgeCaseList.stream().map(JudgeCase::getInput).collect(Collectors.toList());
ExecuteCodeRequest executeCodeRequest = ExecuteCodeRequest.builder().code(code).language(language).inputList(inputList).build();
ExecuteCodeResponse executeCodeResponse = codeSandbox.executeCode(executeCodeRequest);
List<String> outputList = executeCodeResponse.getOutputList();
  • 使用 CodeSandboxFactory 创建代码沙箱实例,并使用代理包装。
  • 从提交信息中获取代码和编程语言。
  • 从题目信息中获取输入用例,并转换为列表。
  • 创建 ExecuteCodeRequest 对象,包含代码、编程语言和输入用例列表。
  • 调用代码沙箱的 executeCode 方法执行代码,获取执行结果。

3.6 设置判题状态和信息

// 5)根据沙箱的执行结果,设置题目的判题状态和信息
JudgeContext judgeContext = new JudgeContext();
judgeContext.setJudgeInfo(executeCodeResponse.getJudgeInfo());
judgeContext.setInputList(inputList);
judgeContext.setOutputList(outputList);
judgeContext.setJudgeCaseList(judgeCaseList);
judgeContext.setQuestion(question);
judgeContext.setQuestionSubmit(questionSubmit);
JudgeInfo judgeInfo = judgeManager.doJudge(judgeContext);
  • 创建 JudgeContext 对象,将执行结果、输入用例、题目信息等设置到上下文中。
  • 调用 JudgeManagerdoJudge 方法进行具体的判题逻辑,得到判题信息。

3.7 更新数据库判题结果

// 6)修改数据库中的判题结果
questionSubmitUpdate = new QuestionSubmit();
questionSubmitUpdate.setId(questionSubmitId);
questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.SUCCEED.getValue());
questionSubmitUpdate.setJudgeInfo(JSONUtil.toJsonStr(judgeInfo));
update = questionFeignClient.updateQuestionSubmitById(questionSubmitUpdate);
if (!update) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, "题目状态更新错误");
}
QuestionSubmit questionSubmitResult = questionFeignClient.getQuestionSubmitById(questionSubmitId);
return questionSubmitResult;
  • 创建一个新的 QuestionSubmit 对象,设置其 ID、状态为“成功”,并将判题信息转换为 JSON 字符串。
  • 调用 questionFeignClientupdateQuestionSubmitById 方法更新数据库中的判题结果。
  • 如果更新失败,抛出异常。
  • 最后再次获取更新后的题目提交信息并返回。

四、代码优化建议

4.1 异常处理优化

在获取题目和提交信息时,如果信息不存在,直接抛出 BusinessException,可能会导致上层调用者难以处理。可以在抛出异常前记录日志,方便后续排查问题。同时,可以考虑提供更详细的错误信息。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;@Service
public class JudgeServiceImpl implements JudgeService {private static final Logger logger = LoggerFactory.getLogger(JudgeServiceImpl.class);// ... 其他代码 ...@Overridepublic QuestionSubmit doJudge(long questionSubmitId) {// 1)传入题目的提交 id,获取到对应的题目、提交信息(包含代码、编程语言等)QuestionSubmit questionSubmit = questionFeignClient.getQuestionSubmitById(questionSubmitId);if (questionSubmit == null) {logger.error("提交信息不存在,提交 ID: {}", questionSubmitId);throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "提交信息不存在,提交 ID: " + questionSubmitId);}Long questionId = questionSubmit.getQuestionId();Question question = questionFeignClient.getQuestionById(questionId);if (question == null) {logger.error("题目不存在,题目 ID: {}", questionId);throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目不存在,题目 ID: " + questionId);}// ... 其他代码 ...}
}

4.2 重复更新操作优化

在更新题目提交状态和判题结果时,都调用了 questionFeignClient.updateQuestionSubmitById 方法,代码存在重复。可以将更新操作封装成一个单独的方法,提高代码的复用性。

private boolean updateQuestionSubmit(QuestionSubmit questionSubmitUpdate) {return questionFeignClient.updateQuestionSubmitById(questionSubmitUpdate);
}@Override
public QuestionSubmit doJudge(long questionSubmitId) {// ... 其他代码 ...// 3)更改判题(题目提交)的状态为 “判题中”,防止重复执行QuestionSubmit questionSubmitUpdate = new QuestionSubmit();questionSubmitUpdate.setId(questionSubmitId);questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.RUNNING.getValue());if (!updateQuestionSubmit(questionSubmitUpdate)) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, "题目状态更新错误");}// ... 其他代码 ...// 6)修改数据库中的判题结果questionSubmitUpdate = new QuestionSubmit();questionSubmitUpdate.setId(questionSubmitId);questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.SUCCEED.getValue());questionSubmitUpdate.setJudgeInfo(JSONUtil.toJsonStr(judgeInfo));if (!updateQuestionSubmit(questionSubmitUpdate)) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, "题目状态更新错误");}// ... 其他代码 ...
}

4.3 错误处理优化

在调用代码沙箱执行代码时,如果出现异常,没有进行处理。可以添加异常处理逻辑,将题目提交状态更新为“失败”。

try {ExecuteCodeResponse executeCodeResponse = codeSandbox.executeCode(executeCodeRequest);List<String> outputList = executeCodeResponse.getOutputList();// ... 其他代码 ...
} catch (Exception e) {logger.error("代码沙箱执行代码出错,提交 ID: {}", questionSubmitId, e);QuestionSubmit questionSubmitUpdate = new QuestionSubmit();questionSubmitUpdate.setId(questionSubmitId);questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.FAILED.getValue());if (!updateQuestionSubmit(questionSubmitUpdate)) {logger.error("题目状态更新为失败出错,提交 ID: {}", questionSubmitId);}throw new BusinessException(ErrorCode.SYSTEM_ERROR, "代码沙箱执行代码出错");
}

4.4 最后获取结果的 ID 错误修正

questionFeignClient.getQuestionSubmitById(questionId); 这里传入的是 questionId,应该传入 questionSubmitId

QuestionSubmit questionSubmitResult = questionFeignClient.getQuestionSubmitById(questionSubmitId);
return questionSubmitResult;

五、总结

通过对 JudgeServiceImpl 类的详细剖析,我们了解了题目的判题逻辑的具体实现。同时,通过优化建议,我们可以提高代码的健壮性、可维护性和可读性,避免一些潜在的错误。在实际开发中,我们可以根据具体需求对代码进行进一步的扩展和优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/69849.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL常见错误码及解决方法(1130、1461、2003、1040、2000、1049、1062、1129、2002、1690等)

目录 【问题1】、FATAL: error 1130: Unknown error 1130 【问题2】、FATAL: error: 1461 【问题3】、ERROR 2003 (HY000): Cant connect to MySQL server on "" (113) 【问题4】、FATAL: error 2003: Cant connect to MySQL server on 172.19.111.151 (111) 【问…

5种解决方式来应对deepseek暂时无法回答

在工作中&#xff0c;你是否常常遇到deepseek回复“暂时无法回答”的情况&#xff1f;根据某权威机构的调研数据显示&#xff0c;约73%的用户在使用此类工具时遇到过类似问题&#xff0c;这严重影响了工作效率和体验。本文将为你提供5种实测有效的解决方法&#xff0c;帮助你快…

单调队列与栈

一.题 1. 思路&#xff1a; 构建小压大的单调递减栈&#xff0c;对于每个栈的元素都进行处理并加到结果上 class Solution { public:int sumSubarrayMins(vector<int>& arr) {int stk[10000000],top 0;long long ans 0;for(int i 0;i<arr.size();i){while(top…

在带有Intel Arc GPU的Windows上安装IPEX-LLM

在带有Intel Arc GPU的Windows上安装IPEX-LLM 在带有Intel Arc GPU的Windows上安装IPEX-LLM先决条件安装 GPU 驱动安装 Visual Studio 2022 社区版安装 Intel oneAPI Base Toolkit安装 IPEX-LLM创建虚拟环境环境验证 可能遇到的问题 在带有Intel Arc GPU的Windows上安装IPEX-LL…

AWS上基于高德API验证Amazon Redshift里国内地址数据正确性的设计方案

该方案通过无服务架构实现高可扩展性&#xff0c;结合分页查询和批量更新确保高效处理海量数据&#xff0c;同时通过密钥托管和错误重试机制保障安全性及可靠性。 一、技术栈 组件技术选型说明计算层AWS Lambda无服务器执行&#xff0c;适合事件驱动、按需处理&#xff0c;成…

03-微服务01(服务拆分、RestTemplate,nacos、OpenFeign、日志)

之前我们学习的项目一是单体项目&#xff0c;可以满足小型项目或传统项目的开发。而在互联网时代&#xff0c;越来越多的一线互联网公司都在使用微服务技术。 从谷歌搜索指数来看&#xff0c;国内从自2016年底开始&#xff0c;微服务热度突然暴涨&#xff1a; 那么&#xff1…

SPSS—回归分析

一、如何选择 回归方法的选择是根据因变量的类型进行选择&#xff0c;无论自变量是哪种类型。 如果因变量&#xff0c;也就是目标变量是连续的数值型变量&#xff0c;当自变量也是连续数值型&#xff0c;研究自变量是否对因变量有影响。选择普通的线性回归即可&#xff0c;根…

2.SpringSecurity在mvc项目中的使用

SpringSecurity认证过程 参考 来源于黑马程序员&#xff1a; 手把手教你精通新版SpringSecurity 设置用户状态 用户认证业务里&#xff0c;我们封装User对象时&#xff0c;选择了三个构造参数的构造方法&#xff0c;其实还有另一个构造方法&#xff1a; public User(Strin…

Java数组二:数组的使用

for-each循环 打印数组所有元素 public class Demo04 {public static void main(String[] args) {int[] num {1,5,2,3,4};for (int num1:num) {System.out.println(num1);}} }多维数组 多维数组可以看成是数组的数组&#xff0c;比如二维数组就是一个特殊的一维数组&#x…

JAVA EE初阶 - 预备知识(一)

一、管道 在计算机编程和操作系统环境中&#xff0c;输入、输出和错误管道是用于在不同进程或程序之间传递数据和信息的重要机制&#xff0c;下面分别对它们进行详细介绍&#xff1a; 输入管道&#xff08;Standard Input&#xff0c;stdin&#xff09; 定义&#xff1a;输入…

STM32HAL库快速入门教程——常用外设学习(2)

目录 一、STM32HAL库开发&#xff08;8&#xff09;——CubeMX配置DMA 1.1、什么是DMA&#xff1f; 1.2、内存内存之间的传输&#xff08;单次&#xff09; ​编辑 1.3、内存外设之间的传输&#xff08;ADC&#xff09; 二、STM32HAL库开发&#xff08;9&#xff09;——…

Spring Boot 整合 SSE(Server-Sent Events)

1、简述 SSE&#xff08;Server-Sent Events&#xff09;是一种基于HTTP协议的单向通信机制&#xff0c;允许服务器向浏览器持续发送实时更新。与WebSocket不同&#xff0c;SSE更简单&#xff0c;使用HTTP/1.1协议即可&#xff0c;不需要额外的协议升级。 SSE的特点&#xff…

汽车ECU实现数据安全存储的一种方案

一、 综述 在车辆ECU中总是有一些密钥或重要数据需进行机密性保护&#xff0c;但因产品选型、成本等考虑&#xff0c;导致一些ECU的芯片不支持硬件安全模块&#xff08;例如HSM、TEE等&#xff09;。此时&#xff0c;为保障数据的机密性&#xff0c;可考虑通过软件实现数据的安…

十进制数到十六进制数的转换

十进制数x&#xff0c;若能表示为&#xff0c;n为大于等于0的整数。ni4j。i取值为0、1、2和3。综合i和j&#xff0c;若i为0&#xff0c;则该16进制数为1后面加j个0&#xff1b;若i为1&#xff0c;则该16进制数为2后面加j个0&#xff1b;若i为2&#xff0c;则该16进制数为4后面加…

基于YoloV11+PaddleOCR的车牌检测系统

文章目录 一、CCPD数据集进行处理1.1 从文件夹构建txt格式数据集1.2 运行脚本按照8&#xff1a;2划分训练集&#xff0c;测试集 二 、YOLOV11训练模型2.1 编写car_plate.yaml文件2.2 编写train脚本&#xff1a;2.3 训练过程 三、PaddleOCR识别车牌号3.1 安装paddleocr&#xff…

2月12日鸿蒙生态日日新PLOG,多款应用上架

2月12日鸿蒙生态日日新PLOG &#xff1a;北京医院挂号通、有度、远光商旅等多款应用上架&#xff1b;钉钉、得到、航班管家等多款重点应用功能更新。 ​​​

Python----PyQt开发(PyQt高级:手搓一个简单的记事本)

一、效果展示 二、设计PyQt界面 2.1、设置图标 self.setWindowIcon(QIcon(./images/icon/1.png)) # 窗口图标 2.2、设置标题 self.file_name 无标题-新建文本文档 # 默认文件名 self.setWindowTitle(self.file_name) # 窗口标题 2.3、添加菜单栏、工具栏、状态栏 # 创…

Java 大视界 -- 大数据伦理与法律:Java 技术在合规中的作用与挑战(87)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

什么是AI Agent、Chat、RAG、MoE

什么是AI Agent、Chat、RAG、MoE 目录 什么是AI Agent、Chat、RAG、MoE定义与原理功能特点应用场景AI Agent有哪些关键组成部分感知模块决策模块知识模块行动模块学习模块AI Agent、Chat、RAG、MoE是人工智能领域中不同的概念和技术,它们在功能、原理和应用等方面存在一些区别…

在 debian 12 上安装 mysqlclient 报错

报错如下 Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple Collecting mysqlclientUsing cached https://pypi.tuna.tsinghua.edu.cn/packages/61/68/810093cb579daae426794bbd9d88aa830fae296e85172d18cb0f0e5dd4bc/mysqlclient-2.2.7.tar.gz (91 kB)Installi…