OJ在线评测系统 后端 使用代理模式编写测试类 并 实现核心业务判题流程

news/2024/9/28 17:17:02/

编写测试类(代理模式)

实现示例的代码沙箱

package com.dduo.dduoj.judge.codesandbox.impl;import com.dduo.dduoj.judge.codesandbox.CodeSandbox;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeRequest;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeResponse;
import com.dduo.dduoj.model.dto.questionsubmit.JudgeInfo;
import com.dduo.dduoj.model.enums.JudgeInfoMessageEnum;
import com.dduo.dduoj.model.enums.QuestionSubmitStatusEnum;
import lombok.extern.slf4j.Slf4j;import java.util.List;//示例代码沙箱 (仅供测试 跑通业务流程)
@Slf4j
public class ExampleCodeSandbox implements CodeSandbox {@Overridepublic ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest) {List<String> inputList = executeCodeRequest.getInputList();String code = executeCodeRequest.getCode();String language = executeCodeRequest.getLanguage();ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();executeCodeResponse.setOutputList(inputList);executeCodeResponse.setMessage("测试执行成功");executeCodeResponse.setStatus(QuestionSubmitStatusEnum.SUCCESS.getValue());JudgeInfo judgeInfo=new JudgeInfo();judgeInfo.setMessage(JudgeInfoMessageEnum.Accepted.getText());judgeInfo.setMemoryLimit(100L);judgeInfo.setTime(100L);executeCodeResponse.setJudgeInfo(judgeInfo);return executeCodeResponse;}
}

我们先把示例代码沙箱跑通 然后再直接把远程代码沙箱接进去

我们在调用代码沙箱前 输出请求参数日志 在代码沙箱调用后 输出响应结果日志 便于管理员去分析

每一个代码沙箱类都会写一遍log.info 难道每次调用代码沙箱前后都要执行log吗

我的理解是使用代理模式 提供一个Proxy 来增强代码沙箱的能力

代理模式的本质就是增强能力

原本:需要用户自己去调用多次

实现核心业务判题流程

在之前问题提交实现类里面加上判题服务

先写一个JudgeService

梳理一下判题服务的逻辑

第一步是获取到传入题目的id 获取到对应的题目信息 提交信息(代码 编程语言

第二步调用沙箱 获取到执行结果

第三步是根据沙箱的执行结果 设置题目的判题状态

我们接下来在实现类里面补全代码

根据沙箱的执行结果 我们设置题目的判题状态和信息

节约系统资源 解决一些不一致的问题

更改判题(题目提交)状态为判题中 防止重复执行

判断逻辑

先判断沙箱的执行结果输出数量是否和预期输出的数量相等

依次判断每一项输出和预期输出是否相等

判断题目的限制是否满足要求

可能还有其他的情况

首先我们拿到题目提交信息 拿到题目

进行简单的判断

证明了题目存在 我们开始判题 首先我们要更改题目的状态

之后我我们要把我们代码放入代码沙箱

根据代码沙箱的执行结果

我们首先要设置题目的判题状态和信息是否正确

从之前注入的两个对象中拿到输入用例 和预计输出用例

开始校验

首先我们校验输出的用例数量和预计输出是否相等

再去判断每一项输出的具体内容和预计输出是否相等

通过循环校验

到最后拿到题目限制

进行判断

可能还会有其他的异常情况

package com.dduo.dduoj.judge;import cn.hutool.json.JSONUtil;
import com.dduo.dduoj.common.ErrorCode;
import com.dduo.dduoj.exception.BusinessException;
import com.dduo.dduoj.judge.codesandbox.CodeSandbox;
import com.dduo.dduoj.judge.codesandbox.CodeSandboxFactory;
import com.dduo.dduoj.judge.codesandbox.CodeSandboxProxy;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeRequest;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeResponse;
import com.dduo.dduoj.model.dto.question.JudgeCase;
import com.dduo.dduoj.model.dto.question.JudgeConfig;
import com.dduo.dduoj.model.dto.questionsubmit.JudgeInfo;
import com.dduo.dduoj.model.entity.Question;
import com.dduo.dduoj.model.entity.QuestionSubmit;
import com.dduo.dduoj.model.enums.JudgeInfoMessageEnum;
import com.dduo.dduoj.model.enums.QuestionSubmitLanguageEnum;
import com.dduo.dduoj.model.enums.QuestionSubmitStatusEnum;
import com.dduo.dduoj.model.vo.QuestionSubmitVO;
import com.dduo.dduoj.service.QuestionService;
import com.dduo.dduoj.service.QuestionSubmitService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;@Service
public class JudgeServiceImpl implements JudgeService {// 题目服务@Resourceprivate QuestionService questionService;// 题目提交服务@Resourceprivate QuestionSubmitService questionSubmitService;@Value("${codesandbox.type:example}")private String value;@Overridepublic QuestionSubmitVO doJudge(Long questionSubmitId) {QuestionSubmit questionSubmit = questionSubmitService.getById(questionSubmitId);if (questionSubmit == null) {throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "提交信息不存在");}//拿到题目提交信息Long questionId = questionSubmit.getQuestionId();//拿到题目Question question = questionService.getById(questionId);if (question == null) {throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目不存在");}// 题目存在// 开始判题// 更改题目的状态 status// 如果不为等待状态if (questionSubmit.getStatus().equals(QuestionSubmitStatusEnum.WAITING.getValue())) {throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目正在判题中");}// 重新设置QuestionSubmit questionSubmitUpdate = new QuestionSubmit();questionSubmitUpdate.setId(questionSubmitId);questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.RUNNING.getValue());boolean judge = questionSubmitService.updateById(questionSubmitUpdate);if (!judge) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, "题目状态更新错误");}// 接下来放代码沙箱CodeSandbox codeSandbox = CodeSandboxFactory.NewInstance(value);codeSandbox = new CodeSandboxProxy(codeSandbox);// 拿出数据String code = questionSubmit.getCode();String language = questionSubmit.getLanguage();// 获取输入用例String judgeCaseStr = question.getJudgeCase();List<JudgeCase> judgeCaselist = JSONUtil.toList(judgeCaseStr, JudgeCase.class);List<String> inputList = judgeCaselist.stream().map(JudgeCase::getInput).collect(Collectors.toList());ExecuteCodeRequest executeRequest = ExecuteCodeRequest.builder().code(code).language(language).inputList(inputList).build();ExecuteCodeResponse executeCodeResponse=codeSandbox.executeCode(executeRequest);// 根据沙箱的执行结果 设置题目的判题状态和信息是否正确JudgeInfoMessageEnum judgeInfoMessageEnum=JudgeInfoMessageEnum.Waiting;// 校验输出数量是否和预计输出数量相等List<String> outputList = executeCodeResponse.getOutputList();if(outputList.size()!=inputList.size()){judgeInfoMessageEnum=judgeInfoMessageEnum.Wrong_Answer;return null;}// 校验每一项输出和预期输出是否相等for(int i=0;i<judgeCaselist.size();i++){JudgeCase judgeCase = judgeCaselist.get(i);if(judgeCase.getOutput().equals(outputList.get(i))){judgeInfoMessageEnum=JudgeInfoMessageEnum.Wrong_Answer;return null;}}// 判断题目的限制JudgeInfo judgeInfo = executeCodeResponse.getJudgeInfo();Long memory = judgeInfo.getMemoryLimit();Long time = judgeInfo.getTime();String judgeConfigStr = question.getJudgeConfig();JudgeConfig judgeConfig = JSONUtil.toBean(judgeConfigStr, JudgeConfig.class);Long memoryLimit = judgeConfig.getMemoryLimit();Long timeLimit = judgeConfig.getTimeLimit();// 内存限制if(memory>memoryLimit){judgeInfoMessageEnum=JudgeInfoMessageEnum.Memory_Limit_Exceeded;return null;}// 时间限制if(time>timeLimit){judgeInfoMessageEnum=JudgeInfoMessageEnum.Time_Limit_Exceeded;return null;}return null;}
}

http://www.ppmy.cn/news/1531005.html

相关文章

【Java】字符串处理 —— String、StringBuffer 与 StringBuilder

由于String类是final类型的&#xff0c;所以使用String定义的字符串是一个常量&#xff0c;因此它一旦创建&#xff0c;其内容和长度是不可改变的。如果需要对一个字符串进行修改&#xff0c;则只能创建新的字符串。为了便于对字符串进行修改&#xff0c;在JDK中提供了一个Stri…

5步了解 地理处理合成孔径雷达工具集

摘要: 本文将带大家了解 ArcGIS Pro 合成孔径雷达工具集中的所有地理处理工具。有了 Image Analyst 许可证,就可以访问 Image Analyst 工具箱中的此工具集。此工具集是锦上添花,它使处理 SAR Ground Range Detect... 本文将带大家了解 ArcGIS Pro 合成孔径雷达工具集中的所有…

安装镜像烧录软件Etcher

一、下载Etcher安装包 访问官方网站&#xff1a; 打开浏览器&#xff0c;访问Etcher的官方网站https://etcher.balena.io/#download-etcher 下载安装包&#xff1a; 在官方网站找到Etcher的下载链接。 点击下载链接 二、安装Etcher 命令安装 点击下载链接会跳转至以下界面…

Vue 响应式监听 Watch 最佳实践

一. 前言 上一篇文章我们学习了 watch 的基础知识&#xff0c;了解了它的基本使用方法及注意事项&#xff0c;本篇文章我们继续了解在Vue 中 响应式监听 watch 的妙用。了解 watch 的基础使用请参考上一篇文章&#xff1a; 详解 Vue 中 Watch 的使用方法及注意事项https://bl…

《深度学习》—— ResNet 残差神经网络

文章目录 一、什么是ResNet&#xff1f;二、残差结构&#xff08;Residual Structure&#xff09;三、Batch Normalization&#xff08;BN----批归一化&#xff09; 一、什么是ResNet&#xff1f; ResNet 网络是在 2015年 由微软实验室中的何凯明等几位大神提出&#xff0c;斩获…

Ubuntu下简易安装openjdk8的命令行

1. 搜索 openjdk sudo apt search openjdk2. 在搜索结果中&#xff0c;选择带有jdk名字的进行安装 sudo apt install openjdk-8-jdk3. 安装成功 java -version

NLP 序列标注任务核心梳理

句向量标注 用 bert 生成句向量用 lstm 或 bert 承接 bert 的输出&#xff0c;保证模型可以学习到内容的连续性。此时 lstm 输入形状为&#xff1a; pooled_output.unsqueeze(0) (1, num_sentence, vector_size) 应用场景 词性标注句法分析 文本加标点 相当于粗粒度的分词任…

【初阶数据结构】详解二叉树 - 树和二叉树(三)(递归的魅力时刻)

文章目录 前言1. 二叉树链式结构的意义2. 手搓一棵二叉树3. 二叉树的遍历&#xff08;重要&#xff09;3.1 遍历的规则3.2 先序遍历3.3 中序遍历3.4 后序遍历3.5 遍历的代码实现3.5.1 先序遍历代码实现3.5.2 中序遍历代码实现3.5.3 后序遍历代码实现 4. 统计二叉树结点的个数5.…