数据库表
-- 创建库
create database if not exists oj;
-- 切换库
use oj;
-- 用户表
create table if not exists user
(
id bigint auto_increment comment 'id' primary key,
userAccount varchar(256) not null comment '账号',
userPassword varchar(512) not null comment '密码',
unionId varchar(256) null comment '微信开放平台id',
mpOpenId varchar(256) null comment '公众号openId',
userName varchar(256) null comment '用户昵称',
userAvatar varchar(1024) null comment '用户头像',
userProfile varchar(512) null comment '用户简介',
userRole varchar(256) default 'user' not null comment '用户角色:user/admin/ban',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_unionId (unionId)
) comment '用户' collate = utf8mb4_unicode_ci;
-- 题目表
create table if not exists question
(
id bigint auto_increment comment 'id' primary key,
title varchar(512) null comment '标题',
content text null comment '内容',
tags varchar(1024) null comment '标签列表(json 数组)',
answer text null comment '题目答案',
submitNum int default 0 not null comment '题目提交数',
acceptedNum int default 0 not null comment '题目通过数',
judgeCase text null comment '判题用例(json 数组)',
judgeConfig text null comment '判题配置(json 对象)',
thumbNum int default 0 not null comment '点赞数',
favourNum int default 0 not null comment '收藏数',
userId bigint not null comment '创建用户 id',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_userId (userId)
) comment '题目' collate = utf8mb4_unicode_ci;
-- 题目提交表
create table if not exists question_submit
(
id bigint auto_increment comment 'id' primary key,
language varchar(128) not null comment '编程语言',
code text not null comment '用户代码',
judgeInfo text null comment '判题信息(json 对象)',
status int default 0 not null comment '判题状态(0 - 待判题、1 - 判题中、2 - 成功、3 - 失败)',
questionId bigint not null comment '题目 id',
userId bigint not null comment '创建用户 id',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_questionId (questionId),
index idx_userId (userId)
) comment '题目提交';生成代码编写业务逻辑
- 使用 mybatisX 生成生成entity、service、mapper层以及xml代码,并复制到对应的包
|--------------|-------------------------------|
| annotation | None [ ] |
| | Mybatis-Plus 2 [ ] |
| | Mybatis-Plus 3 [x] |
| | JPA [ ] |
|--------------|-------------------------------|
| options | Comment [x] |
| | Actual Column [x] |
| | Model [x] |
| | toString/hashCode/equals [ ] |
| | Actual Column Annotation [ ] |
| | Lombok [x] |
| | JSR310: Date API [ ] |
|--------------|-------------------------------|
| template | custom-model-swagger [ ] |
| | mybatis-plus2 [ ] |
| | default-all [ ] |
| | mybatis-plus3 [x] |
| | default-empty [ ] |
|--------------|-------------------------------|- 从功能相似的 controller 基础上创建所需要的 QuestionController 和 QuestionSubmitController 单表复制单表、多表复制多表
- 创建对应的 DTO VO,直接复制实体类,在实体类的基础上修改
- JudgeConfig、JudgeCase、JudgeInfo 定义为单独的类。需要复用时不加业务前缀
- 校验 controller 层代码
- 实现 service 层代码(复制,全局替换)
- 编写枚举类
- 编写 questionSubmitQueryController
@EqualsAndHashCode(callSuper = true)
判题机框架
- 定义代码沙箱的接口
- 使用工厂模式,根据字符串生成代码沙箱实例;通过读取
application.yml获得配置 - 单例模式(如果保证线程安全可使用)
- 代理模式
- 策略模式
重新创建项目
Java 原生代码沙箱
执行原理:
javac .\Main.java
java Mainlinux 查看系统的字符集
localeStopWatch 计算运行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 代码
stopWatch.stop();
stopWatch.getTotalTimeMillis();ProcessUtils 工具类:process 实例和输入,返回包含 状态、标准输出、错误输出 的封装类
根据系统编码调整输出流和错误流的解析编码 System.getProperty("sun.jnu.encoding")
public class ProcessUtils {
private static String readAllCharacters(InputStream inputStream) throws IOException {
Charset charset = Charset.forName(System.getProperty("sun.jnu.encoding"));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, charset));
StringBuffer stringBuffer = new StringBuffer();
String lineString;
while ((lineString = bufferedReader.readLine()) != null) {
stringBuffer.append(lineString).append("\n");
}
String output = stringBuffer.toString();
return output;
}
public static ExecuteMessage runProcess(Process process, String input) throws IOException, InterruptedException {
ExecuteMessage executeMessage = new ExecuteMessage();
InputStream inputStream = process.getInputStream();
InputStream errorStream = process.getErrorStream();
OutputStream outputStream = process.getOutputStream();
// 输入数据
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
outputStreamWriter.write(input + '\n');
outputStreamWriter.flush();
// 退出码
int exitCode = process.waitFor();
executeMessage.setExitCode(exitCode);
// 输出数据
String output = readAllCharacters(inputStream);
executeMessage.setOutput(output);
// 错误数据
String error = readAllCharacters(errorStream);
executeMessage.setError(error);
return executeMessage;
}
}恶意代码处理
时间超限
内存超限
读写文件、运行程序和高危命令
java 操作 docker
https://github.com/docker-java/docker-java
maven 依赖
<!-- https://mvnrepository.com/artifact/com.github.docker-java/docker-java -->
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.docker-java/docker-java-transport-httpclient5 -->
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java-transport-httpclient5</artifactId>
<version>3.3.6</version>
</dependency>