架构更新,上线部署
This commit is contained in:
parent
8c100cf4ba
commit
ddfaa27d06
59
README.md
59
README.md
|
@ -2,15 +2,14 @@
|
|||
|
||||
基于前后端分离,分布式架构的在线测评平台(hoj)
|
||||
|
||||
> 当前任务
|
||||
在线Demo:[http://oj.hcode.top](http://oj.hcode.top)
|
||||
|
||||
- [x] 测试HDU判题整套流程
|
||||
- [x] 修改前端编辑器样式以及md格式转换
|
||||
- [x] 修复代码编辑器bug
|
||||
- [x] 测试比赛相关接口,验证权限及数据计算
|
||||
- [x] 增加codeforce的vj判题
|
||||
- [ ] 部署判题服务器到云服务器,半正式上线HOJ
|
||||
- [ ] 完善文档
|
||||
> 上线日记
|
||||
|
||||
| 时间 | 内容 | 更新者 |
|
||||
| ---------- | ------------ | -------- |
|
||||
| 2020-10-26 | 正式开发 | Himit_ZH |
|
||||
| 2021-04-10 | 首次上线测试 | Himit_ZH |
|
||||
|
||||
> 简略介绍
|
||||
|
||||
|
@ -53,37 +52,6 @@
|
|||
|
||||
|
||||
|
||||
> 开发记录
|
||||
|
||||
| 时间 | 更新内容 | 更新者 |
|
||||
| ---------- | ------------------------------------------------------------ | --------------- |
|
||||
| 2020-10-26 | 数据库设计,登录和注册接口,文档记录开始。 | Himit_ZH |
|
||||
| 2020-10-28 | 用户模块接口,题目模块接口,比赛模块接口,排行模块接口 | Himit_ZH |
|
||||
| 2020-10-30 | 评测模块接口,判题服务系统,初始化前端vue项目 | Himit_ZH |
|
||||
| 2020-11-08 | 前端vue主页,题目列表页,登录,注册,重置密码弹窗逻辑 | Himit_ZH |
|
||||
| 2020-11-16 | 前端提交列表页,提交详情页,题目详情页,排行(ACM,OI)页,比赛列表页,个人主页,个人设置页 | Himit_ZH |
|
||||
| 2020-11-22 | 前端比赛首页,比赛题目列表,比赛排行榜,比赛公告,首页布局调整 | Himit_ZH |
|
||||
| 2020-11-24 | 介绍页,导航栏移动端优化,首页优化,公告栏优化 | Himit_ZH |
|
||||
| 2020-11-28 | 前端项目重构,加入管理端部分页面,增加case表 | Himit_ZH |
|
||||
| 2020-12-01 | 前端管理端基本完成,准备开始前后端接口对接与测试 | Himit_ZH |
|
||||
| 2020-12-21 | 管理端前后端接口对接基本完成,准备客户端接口对接 | Himit_ZH |
|
||||
| 2021-01-04 | 客户端首页,题目,提交模块的接口对接完毕 | Himit_ZH |
|
||||
| 2021-01-08 | 比赛列表页,排行榜,用户主页的接口对接完毕 | Himit_ZH |
|
||||
| 2021-01-11 | 个人设置页,用户信息更新,头像上传,登录优化 | Himit_ZH、Howie |
|
||||
| 2021-01-16 | 比赛首页,比赛题目列表,比赛题目详情等接口对接完毕,定时爬取其它oj比赛及rating分完成 | Himit_ZH、Howie |
|
||||
| 2021-01-19 | 比赛排行榜,比赛题目对应提交重判,比赛AC助手完成 | Himit_ZH |
|
||||
| 2021-02-02 | 正式加入安全沙箱,判题机服务器 | Himit_ZH |
|
||||
| 2021-02-04 | 完善判题机制,加入pe可选择性判断,加入测试点可选择性公开 | Himit_ZH |
|
||||
| 2021-02-08 | 完善判题服务调度机制(负载均衡策略),完善题目评测数据上传与下载 | Himit_ZH |
|
||||
| 2021-02-12 | 初始化构建HDU虚拟判题流程 | Howie、Himit_ZH |
|
||||
| 2021-02-16 | 完善测试特殊判题题目的操作 | Himit_ZH |
|
||||
| 2021-02-19 | 正式完善HDU虚拟判题以及题目添加 | Himit_ZH |
|
||||
| 2021-02-20 | 测试HDU判题整套流程完成 | Himit_ZH |
|
||||
| 2021-02-22 | 修改前端编辑器样式以及md格式转换 | Himit_ZH |
|
||||
| 2021-02-24 | 完善测试比赛相关接口,验证权限及数据计算 | Himit_ZH |
|
||||
| 2021-03-03 | 增加Codeforces的题目爬取 | Himit_ZH |
|
||||
| 2021-03-26 | 增加Codeforces的虚拟判题,修复题目爬取部分问题 | Himit_ZH |
|
||||
|
||||
# 二、系统架构
|
||||
|
||||
> 总概四大系统
|
||||
|
@ -100,13 +68,16 @@
|
|||
|
||||
1. 前端用户提交数据。
|
||||
2. 后端数据服务(DataBackup)获取到数据,先将提交数据初始化,同时将该提交的状态变成等待中,写入数据库。
|
||||
3. 通过Redis的发布订阅者,将该判题信息发送给对应的==等待判题频道订阅者==
|
||||
4. 订阅者收到信息,初始化传输数据,使用springcloud alibaba通过nacos注册中心调用判题微服务。
|
||||
5. 若是调用判题服务失败(没有空闲的判题服务器),则重新通过发布者将该提交信息发布到对应==等待判题频道==,重回3。
|
||||
6. 若是调用失败超过40次,则将提交的状态修改为提交失败,不再进行判题服务的调用。
|
||||
3. 通过Redis,写入**等待判题队列**
|
||||
4. 调用work处理者,初始化传输数据,使用springcloud alibaba通过nacos注册中心调用判题微服务。
|
||||
5. 若是调用判题服务失败(没有空闲的判题服务器),则重新通过发布者将该提交信息发布到对应**等待判题队列**,重回3。
|
||||
6. 若是调用失败超过30次,则将提交的状态修改为提交失败,不再进行判题服务的调用。
|
||||
7. 前端用户可看到提交变成提交失败,可点击状态进行重新提交,重回2(注:重新提交不影响提交时间等数据)
|
||||
8. 判题微服务获取到提交数据:
|
||||
- 若是远程调用,则通过Redis的发布订者发布到对应的==远程判题等待提交频道==,对应==频道接收者==获取提交信息,进行远程提交,获取对应的提交ID,再通过==远程判题等待结果频道===发布给对应频道接收者,该频道接收者接受到信息,进行结果获取,获取结果失败,则再次重复发布;获取成功,写回数据库。
|
||||
- 若是远程调用,进行远程提交,获取对应的提交ID,若是获取失败则判为提交失败,若是获取成功,就启用线程定时器每2秒根据提交ID获取判题结果。
|
||||
|
||||
定时尝试30次,获取成功,写回数据库,获取失败就修改此次提交为提交失败。
|
||||
|
||||
- 若是自家题目提交,则启用线程池多线程使用Http将对应测试点数据与代码提交给Go-Judge判题沙盒进行编译与评测,最后获取各个评测点结果,进行结果计算写回数据库。
|
||||
|
||||
> HOJ基本逻辑架构图
|
||||
|
|
|
@ -3,7 +3,6 @@ package top.hcode.hoj.common.exception;
|
|||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.hcode.hoj.common.result.CommonResult;
|
||||
import top.hcode.hoj.judge.remote.RemoteJudgeDispatcher;
|
||||
|
@ -44,7 +43,7 @@ public class CloudHandler implements ToJudgeService {
|
|||
// 调度判题服务器失败,可能是判题服务器有故障,或者全部达到判题最大数,那么将该提交重新进入等待队列
|
||||
@Override
|
||||
public CommonResult submitProblemJudge(ToJudge toJudge) {
|
||||
if (toJudge.getTryAgainNum() == 40) {
|
||||
if (toJudge.getTryAgainNum() == 30) {
|
||||
Judge judge = toJudge.getJudge();
|
||||
judge.setStatus(Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus());
|
||||
judge.setErrorMessage("Failed to connect the judgeServer. Please resubmit this submission again!");
|
||||
|
@ -58,7 +57,7 @@ public class CloudHandler implements ToJudgeService {
|
|||
}
|
||||
Judge judge = toJudge.getJudge();
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), toJudge.getToken(),
|
||||
judge.getCid() == 0, toJudge.getTryAgainNum() + 1);
|
||||
judge.getCid() != 0, toJudge.getTryAgainNum() + 1);
|
||||
}
|
||||
return CommonResult.errorResponse("判题服务器繁忙或出错,提交进入重判队列,请等待管理员处理!", CommonResult.STATUS_ERROR);
|
||||
}
|
||||
|
@ -70,14 +69,13 @@ public class CloudHandler implements ToJudgeService {
|
|||
|
||||
@Override
|
||||
public CommonResult remoteJudge(ToJudge toJudge) {
|
||||
System.out.println(toJudge.getTryAgainNum());
|
||||
// 将使用的账号放回对应列表
|
||||
JSONObject account = new JSONObject();
|
||||
account.set("username", toJudge.getUsername());
|
||||
account.set("password", toJudge.getPassword());
|
||||
redisUtils.llPush(Constants.Judge.getListNameByOJName(toJudge.getRemoteJudge().split("-")[0]), JSONUtil.toJsonStr(account));
|
||||
|
||||
if (toJudge.getTryAgainNum() == 40) {
|
||||
if (toJudge.getTryAgainNum() == 30) {
|
||||
Judge judge = toJudge.getJudge();
|
||||
judge.setStatus(Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus());
|
||||
judge.setErrorMessage("Failed to connect the judgeServer. Please resubmit this submission again!");
|
||||
|
@ -93,7 +91,7 @@ public class CloudHandler implements ToJudgeService {
|
|||
|
||||
Judge judge = toJudge.getJudge();
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), toJudge.getToken(),
|
||||
toJudge.getRemoteJudge(), judge.getCid() == 0, toJudge.getTryAgainNum() + 1);
|
||||
toJudge.getRemoteJudge(), judge.getCid() != 0, toJudge.getTryAgainNum() + 1);
|
||||
}
|
||||
|
||||
return CommonResult.errorResponse("判题服务器繁忙或出错,提交进入重判队列,请等待管理员处理!", CommonResult.STATUS_ERROR);
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
|||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import top.hcode.hoj.common.result.CommonResult;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -55,13 +56,13 @@ public class GlobalExceptionHandler {
|
|||
|
||||
/**
|
||||
* 401 -UnAuthorized UnauthenticatedException,token相关异常 即是认证出错 可能无法处理!
|
||||
* 没有登录(没有token),访问有@RequiresAuthentication的请求路径会报这个异常
|
||||
* 没有登录(没有token),访问有@RequiresAuthentication的请求路径会报这个异常
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||
@ExceptionHandler(value = UnauthenticatedException.class)
|
||||
public CommonResult handleUnauthenticatedException(UnauthenticatedException e,
|
||||
HttpServletRequest httpRequest,
|
||||
HttpServletResponse httpResponse) {
|
||||
HttpServletRequest httpRequest,
|
||||
HttpServletResponse httpResponse) {
|
||||
httpResponse.setHeader("Url-Type", httpRequest.getHeader("Url-Type")); // 为了前端能区别请求来源
|
||||
return CommonResult.errorResponse("请您先登录!", CommonResult.STATUS_ACCESS_DENIED);
|
||||
}
|
||||
|
@ -195,7 +196,7 @@ public class GlobalExceptionHandler {
|
|||
@ExceptionHandler(value = MessagingException.class)
|
||||
public CommonResult handler(MessagingException e) throws Exception {
|
||||
log.error("邮箱系统异常-------------->{}", e.getMessage());
|
||||
return CommonResult.errorResponse(e.getMessage(), CommonResult.STATUS_ERROR);
|
||||
return CommonResult.errorResponse("服务器异常,请稍后尝试!", CommonResult.STATUS_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,7 +205,8 @@ public class GlobalExceptionHandler {
|
|||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
@ExceptionHandler(ServiceException.class)
|
||||
public CommonResult handleServiceException(ServiceException e) {
|
||||
return CommonResult.errorResponse("业务逻辑异常:" + e.getCause(), CommonResult.STATUS_ERROR);
|
||||
log.error("业务逻辑异常-------------->{}", e.getMessage());
|
||||
return CommonResult.errorResponse("服务器异常,请稍后尝试!", CommonResult.STATUS_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,7 +216,7 @@ public class GlobalExceptionHandler {
|
|||
@ExceptionHandler(DataIntegrityViolationException.class)
|
||||
public CommonResult handleDataIntegrityViolationException(DataIntegrityViolationException e) {
|
||||
log.error("操作数据库出现异常-------------->{}", e.getMessage());
|
||||
return CommonResult.errorResponse("操作数据库出现异常:字段重复、有外键关联等", CommonResult.STATUS_ERROR);
|
||||
return CommonResult.errorResponse("服务器异常,请稍后尝试!", CommonResult.STATUS_ERROR);
|
||||
}
|
||||
|
||||
|
||||
|
@ -225,7 +227,7 @@ public class GlobalExceptionHandler {
|
|||
@ExceptionHandler(SQLException.class)
|
||||
public CommonResult handleSQLException(SQLException e) {
|
||||
log.error("操作数据库出现异常-------------->{}", e.getMessage());
|
||||
return CommonResult.errorResponse("操作失败!错误提示:"+e.getMessage(), CommonResult.STATUS_ERROR);
|
||||
return CommonResult.errorResponse("操作失败!错误提示:" + e.getMessage(), CommonResult.STATUS_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -245,6 +247,6 @@ public class GlobalExceptionHandler {
|
|||
@ExceptionHandler(Exception.class)
|
||||
public CommonResult handleException(Exception e) {
|
||||
log.error("系统通用异常-------------->{}", e.getMessage());
|
||||
return CommonResult.errorResponse("系统通用异常:" + e.getCause(), CommonResult.STATUS_ERROR);
|
||||
return CommonResult.errorResponse("服务器异常,请稍后尝试!", CommonResult.STATUS_ERROR);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@ public class AsyncTaskConfig implements AsyncConfigurer {
|
|||
public Executor getAsyncExecutor() {
|
||||
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
|
||||
// 线程池维护线程的最少数量
|
||||
taskExecutor.setCorePoolSize(10);
|
||||
taskExecutor.setCorePoolSize(50);
|
||||
// 线程池维护线程的最大数量
|
||||
taskExecutor.setMaxPoolSize(50);
|
||||
taskExecutor.setMaxPoolSize(100);
|
||||
// 缓存队列
|
||||
taskExecutor.setQueueCapacity(200);
|
||||
// 对拒绝task的处理策略
|
||||
|
|
|
@ -7,6 +7,8 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
|
|||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import top.hcode.hoj.utils.Constants;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 解决跨域问题
|
||||
*/
|
||||
|
@ -25,8 +27,8 @@ public class CorsConfig implements WebMvcConfigurer {
|
|||
// 前端直接通过/public/img/图片名称即可拿到
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler(Constants.File.IMG_API.getPath()+"**") // /public/img/**
|
||||
.addResourceLocations("file:" + Constants.File.USER_AVATAR_FOLDER.getPath(),
|
||||
"file:" + Constants.File.MARKDOWN_IMG_FOLDER.getPath());
|
||||
registry.addResourceHandler(Constants.File.IMG_API.getPath() + "**") // /api/public/img/**
|
||||
.addResourceLocations("file:" + Constants.File.USER_AVATAR_FOLDER.getPath() + File.separator,
|
||||
"file:" + Constants.File.MARKDOWN_IMG_FOLDER.getPath() + File.separator);
|
||||
}
|
||||
}
|
|
@ -18,11 +18,14 @@ import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
|
|||
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
|
||||
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
|
||||
import org.crazycake.shiro.RedisCacheManager;
|
||||
import org.crazycake.shiro.RedisManager;
|
||||
import org.crazycake.shiro.RedisSessionDAO;
|
||||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
import top.hcode.hoj.shiro.AccountRealm;
|
||||
import top.hcode.hoj.shiro.JwtFilter;
|
||||
|
||||
|
@ -39,12 +42,17 @@ import java.util.Map;
|
|||
public class ShiroConfig {
|
||||
@Autowired
|
||||
JwtFilter jwtFilter;
|
||||
|
||||
|
||||
@Bean
|
||||
public SessionManager sessionManager(RedisSessionDAO redisSessionDAO) {
|
||||
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
|
||||
sessionManager.setSessionDAO(redisSessionDAO);
|
||||
return sessionManager;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Bean
|
||||
public DefaultWebSecurityManager securityManager(AccountRealm accountRealm,
|
||||
SessionManager sessionManager,
|
||||
|
|
|
@ -30,7 +30,7 @@ import java.util.List;
|
|||
* @Description:
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin")
|
||||
@RequestMapping("/api/admin")
|
||||
public class AdminAccountController {
|
||||
|
||||
@Autowired
|
||||
|
|
|
@ -30,7 +30,7 @@ import java.util.List;
|
|||
* @Description:
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/contest")
|
||||
@RequestMapping("/api/admin/contest")
|
||||
public class AdminContestController {
|
||||
|
||||
@Autowired
|
||||
|
|
|
@ -30,7 +30,7 @@ import java.util.List;
|
|||
*/
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/admin/judge")
|
||||
@RequestMapping("/api/admin/judge")
|
||||
@RefreshScope
|
||||
public class AdminJudgeController {
|
||||
|
||||
|
@ -102,9 +102,9 @@ public class AdminJudgeController {
|
|||
Problem problem = problemService.getById(judge.getPid());
|
||||
if (problem.getIsRemote()) { // 如果是远程oj判题
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, problem.getProblemId(),
|
||||
judge.getCid() == 0, 1);
|
||||
judge.getCid() != 0, 1);
|
||||
} else {
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getCid() == 0, 1);
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getCid() != 0, 1);
|
||||
}
|
||||
return CommonResult.successResponse(judge, "重判成功!该提交已进入判题队列!");
|
||||
} else {
|
||||
|
@ -150,12 +150,12 @@ public class AdminJudgeController {
|
|||
for (Judge judge : rejudgeList) {
|
||||
// 进入重判队列,等待调用判题服务
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, problem.getProblemId(),
|
||||
judge.getCid() == 0, 1);
|
||||
judge.getCid() != 0, 1);
|
||||
}
|
||||
} else {
|
||||
for (Judge judge : rejudgeList) {
|
||||
// 进入重判队列,等待调用判题服务
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getCid() == 0, 1);
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getCid() != 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ import java.util.*;
|
|||
*/
|
||||
@RestController
|
||||
@RefreshScope
|
||||
@RequestMapping("/admin/problem")
|
||||
@RequestMapping("/api/admin/problem")
|
||||
public class AdminProblemController {
|
||||
|
||||
@Autowired
|
||||
|
@ -66,7 +66,8 @@ public class AdminProblemController {
|
|||
queryWrapper
|
||||
.like("title", keyword).or()
|
||||
.like("author", keyword).or()
|
||||
.like("problem_id", keyword);
|
||||
.like("problem_id", keyword)
|
||||
.orderByAsc("gmt_create");
|
||||
problemList = problemService.page(iPage, queryWrapper);
|
||||
} else {
|
||||
problemList = problemService.page(iPage, queryWrapper);
|
||||
|
|
|
@ -29,7 +29,7 @@ import java.util.*;
|
|||
* @Description:
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/user")
|
||||
@RequestMapping("/api/admin/user")
|
||||
public class AdminUserController {
|
||||
|
||||
@Autowired
|
||||
|
|
|
@ -25,7 +25,7 @@ public class AnnouncementController {
|
|||
@Autowired
|
||||
private AnnouncementServiceImpl announcementDao;
|
||||
|
||||
@GetMapping("/admin/announcement")
|
||||
@GetMapping("/api/admin/announcement")
|
||||
@RequiresRoles("root") // 只有超级管理员能操作
|
||||
public CommonResult getAnnouncementList(@RequestParam(value = "limit", required = false) Integer limit,
|
||||
@RequestParam(value = "currentPage", required = false) Integer currentPage){
|
||||
|
@ -39,7 +39,7 @@ public class AnnouncementController {
|
|||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/admin/announcement")
|
||||
@DeleteMapping("/api/admin/announcement")
|
||||
@RequiresRoles("root") // 只有超级管理员能操作
|
||||
public CommonResult deleteAnnouncement(@Valid @RequestParam("aid")long aid){
|
||||
boolean result = announcementDao.removeById(aid);
|
||||
|
@ -50,7 +50,7 @@ public class AnnouncementController {
|
|||
}
|
||||
}
|
||||
|
||||
@PostMapping("/admin/announcement")
|
||||
@PostMapping("/api/admin/announcement")
|
||||
@RequiresRoles("root") // 只有超级管理员能操作
|
||||
public CommonResult addAnnouncement(@RequestBody Announcement announcement){
|
||||
boolean result = announcementDao.save(announcement);
|
||||
|
@ -61,7 +61,7 @@ public class AnnouncementController {
|
|||
}
|
||||
}
|
||||
|
||||
@PutMapping("/admin/announcement")
|
||||
@PutMapping("/api/admin/announcement")
|
||||
@RequiresRoles("root") // 只有超级管理员能操作
|
||||
public CommonResult updateAnnouncement(@RequestBody Announcement announcement){
|
||||
boolean result = announcementDao.saveOrUpdate(announcement);
|
||||
|
|
|
@ -24,7 +24,7 @@ import java.util.List;
|
|||
* @Description:
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/config")
|
||||
@RequestMapping("/api/admin/config")
|
||||
public class ConfigController {
|
||||
|
||||
@Autowired
|
||||
|
@ -92,6 +92,7 @@ public class ConfigController {
|
|||
MapUtil.builder().put("username", configVo.getEmailUsername())
|
||||
.put("password", configVo.getEmailPassword())
|
||||
.put("host", configVo.getEmailHost())
|
||||
.put("port", configVo.getEmailPort())
|
||||
.put("ssl", configVo.getEmailSsl()).map()
|
||||
);
|
||||
}
|
||||
|
@ -133,6 +134,7 @@ public class ConfigController {
|
|||
.put("dbPassword", configVo.getMysqlPassword())
|
||||
.put("redisHost", configVo.getRedisHost())
|
||||
.put("redisPort", configVo.getRedisPort())
|
||||
.put("redisPassword", configVo.getRedisPassword())
|
||||
.map()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import java.util.List;
|
|||
* @Description:
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/dashboard")
|
||||
@RequestMapping("/api/admin/dashboard")
|
||||
public class DashboardController {
|
||||
|
||||
@Autowired
|
||||
|
|
|
@ -52,7 +52,7 @@ import java.util.stream.Collectors;
|
|||
* @Description:
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/file")
|
||||
@RequestMapping("/api/file")
|
||||
@Slf4j
|
||||
public class FileController {
|
||||
|
||||
|
@ -120,16 +120,13 @@ public class FileController {
|
|||
if (!"jpg,jpeg,gif,png,webp".toUpperCase().contains(suffix.toUpperCase())) {
|
||||
return CommonResult.errorResponse("请选择jpg,jpeg,gif,png,webp格式的头像图片!");
|
||||
}
|
||||
File savePathFile = new File(Constants.File.USER_AVATAR_FOLDER.getPath());
|
||||
if (!savePathFile.exists()) {
|
||||
//若不存在该目录,则创建目录
|
||||
savePathFile.mkdir();
|
||||
}
|
||||
//若不存在该目录,则创建目录
|
||||
FileUtil.mkdir(Constants.File.USER_AVATAR_FOLDER.getPath());
|
||||
//通过UUID生成唯一文件名
|
||||
String filename = IdUtil.simpleUUID() + "." + suffix;
|
||||
try {
|
||||
//将文件保存指定目录
|
||||
image.transferTo(new File(Constants.File.USER_AVATAR_FOLDER.getPath() + filename));
|
||||
image.transferTo(FileUtil.file(Constants.File.USER_AVATAR_FOLDER.getPath() + File.separator + filename));
|
||||
} catch (Exception e) {
|
||||
log.error("头像文件上传异常-------------->{}", e.getMessage());
|
||||
return CommonResult.errorResponse("服务器异常:头像上传失败!", CommonResult.STATUS_ERROR);
|
||||
|
@ -152,7 +149,7 @@ public class FileController {
|
|||
// 插入file表记录
|
||||
top.hcode.hoj.pojo.entity.File imgFile = new top.hcode.hoj.pojo.entity.File();
|
||||
imgFile.setName(filename).setFolderPath(Constants.File.USER_AVATAR_FOLDER.getPath())
|
||||
.setFilePath(Constants.File.USER_AVATAR_FOLDER.getPath() + filename)
|
||||
.setFilePath(Constants.File.USER_AVATAR_FOLDER.getPath() + File.separator + filename)
|
||||
.setSuffix(suffix)
|
||||
.setType("avatar")
|
||||
.setUid(userRolesVo.getUid());
|
||||
|
@ -437,7 +434,7 @@ public class FileController {
|
|||
|
||||
for (Judge judge : userSubmissionList) {
|
||||
String filePath = userDir + File.separator + cpIdMap.getOrDefault(judge.getCpid(), "null")
|
||||
+ "_(" + threadLocal.get().format(judge.getSubmitTime())+")";
|
||||
+ "_(" + threadLocal.get().format(judge.getSubmitTime()) + ")";
|
||||
|
||||
// OI模式只取最后一次提交
|
||||
if (!isACM) {
|
||||
|
@ -453,7 +450,7 @@ public class FileController {
|
|||
|
||||
}
|
||||
}
|
||||
String zipFileName = "contest_"+contest.getId() + "_" + System.currentTimeMillis() + ".zip";
|
||||
String zipFileName = "contest_" + contest.getId() + "_" + System.currentTimeMillis() + ".zip";
|
||||
String zipPath = Constants.File.CONTEST_AC_SUBMISSION_TMP_FOLDER.getPath() + File.separator + zipFileName;
|
||||
ZipUtil.zip(tmpFilesDir, zipPath);
|
||||
// 将zip变成io流返回给前端
|
||||
|
@ -569,16 +566,15 @@ public class FileController {
|
|||
if (!"jpg,jpeg,gif,png,webp".toUpperCase().contains(suffix.toUpperCase())) {
|
||||
return CommonResult.errorResponse("请选择jpg,jpeg,gif,png,webp格式的图片!");
|
||||
}
|
||||
File savePathFile = new File(Constants.File.MARKDOWN_IMG_FOLDER.getPath());
|
||||
if (!savePathFile.exists()) {
|
||||
//若不存在该目录,则创建目录
|
||||
savePathFile.mkdir();
|
||||
}
|
||||
|
||||
//若不存在该目录,则创建目录
|
||||
FileUtil.mkdir(Constants.File.MARKDOWN_IMG_FOLDER.getPath());
|
||||
|
||||
//通过UUID生成唯一文件名
|
||||
String filename = IdUtil.simpleUUID() + "." + suffix;
|
||||
try {
|
||||
//将文件保存指定目录
|
||||
image.transferTo(new File(Constants.File.MARKDOWN_IMG_FOLDER.getPath() + filename));
|
||||
image.transferTo(FileUtil.file(Constants.File.MARKDOWN_IMG_FOLDER.getPath() + File.separator + filename));
|
||||
} catch (Exception e) {
|
||||
log.error("图片文件上传异常-------------->{}", e.getMessage());
|
||||
return CommonResult.errorResponse("服务器异常:图片文件上传失败!", CommonResult.STATUS_ERROR);
|
||||
|
@ -586,7 +582,7 @@ public class FileController {
|
|||
|
||||
return CommonResult.successResponse(MapUtil.builder()
|
||||
.put("link", Constants.File.USER_FILE_HOST.getPath() + Constants.File.IMG_API.getPath() + filename)
|
||||
.put("filePath", Constants.File.MARKDOWN_IMG_FOLDER.getPath() + filename).map(),
|
||||
.put("filePath", Constants.File.MARKDOWN_IMG_FOLDER.getPath() + File.separator + filename).map(),
|
||||
"上传图片成功!");
|
||||
|
||||
}
|
||||
|
|
|
@ -25,10 +25,7 @@ import top.hcode.hoj.pojo.vo.ConfigVo;
|
|||
import top.hcode.hoj.pojo.vo.UserHomeVo;
|
||||
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||
import top.hcode.hoj.service.UserInfoService;
|
||||
import top.hcode.hoj.service.impl.EmailServiceImpl;
|
||||
import top.hcode.hoj.service.impl.UserAcproblemServiceImpl;
|
||||
import top.hcode.hoj.service.impl.UserInfoServiceImpl;
|
||||
import top.hcode.hoj.service.impl.UserRecordServiceImpl;
|
||||
import top.hcode.hoj.service.impl.*;
|
||||
import top.hcode.hoj.utils.Constants;
|
||||
import top.hcode.hoj.utils.IpUtils;
|
||||
import top.hcode.hoj.utils.JwtUtils;
|
||||
|
@ -71,6 +68,9 @@ public class AccountController {
|
|||
@Autowired
|
||||
private UserAcproblemServiceImpl userAcproblemService;
|
||||
|
||||
@Autowired
|
||||
private ProblemServiceImpl problemService;
|
||||
|
||||
@Autowired
|
||||
private SessionMapper sessionDao;
|
||||
|
||||
|
@ -92,11 +92,11 @@ public class AccountController {
|
|||
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("email", email);
|
||||
UserInfo userInfo = userInfoDao.getOne(queryWrapper);
|
||||
if(userInfo!=null){
|
||||
if (userInfo != null) {
|
||||
return CommonResult.errorResponse("对不起!该邮箱已被注册,请更换新的邮箱!");
|
||||
}
|
||||
String numbers = RandomUtil.randomNumbers(6); // 随机生成6位数字的组合
|
||||
redisUtils.set(Constants.Email.REGISTER_KEY_PREFIX.getValue()+email, numbers, 5 * 60);//默认验证码有效5分钟
|
||||
redisUtils.set(Constants.Email.REGISTER_KEY_PREFIX.getValue() + email, numbers, 5 * 60);//默认验证码有效5分钟
|
||||
emailService.sendCode(email, numbers);
|
||||
return CommonResult.successResponse(MapUtil.builder()
|
||||
.put("email", email)
|
||||
|
@ -171,7 +171,7 @@ public class AccountController {
|
|||
userInfoQueryWrapper.eq("email", email.trim());
|
||||
UserInfo userInfo = userInfoDao.getOne(userInfoQueryWrapper);
|
||||
String code = IdUtil.simpleUUID().substring(0, 21); // 随机生成20位数字与字母的组合
|
||||
redisUtils.set(Constants.Email.RESET_PASSWORD_KEY_PREFIX.getValue()+userInfo.getUsername(), code, 10 * 60);//默认链接有效10分钟
|
||||
redisUtils.set(Constants.Email.RESET_PASSWORD_KEY_PREFIX.getValue() + userInfo.getUsername(), code, 10 * 60);//默认链接有效10分钟
|
||||
// 发送邮件
|
||||
emailService.sendResetPassword(userInfo.getUsername(), code, email.trim());
|
||||
return CommonResult.successResponse(null, "重置密码邮件已发送至指定邮箱,请稍稍等待一会。");
|
||||
|
@ -194,7 +194,7 @@ public class AccountController {
|
|||
if (StringUtils.isEmpty(password) || StringUtils.isEmpty(username) || StringUtils.isEmpty(code)) {
|
||||
return CommonResult.errorResponse("用户名,新密码或验证码不能为空");
|
||||
}
|
||||
String codeKey = Constants.Email.RESET_PASSWORD_KEY_PREFIX.getValue()+username;
|
||||
String codeKey = Constants.Email.RESET_PASSWORD_KEY_PREFIX.getValue() + username;
|
||||
|
||||
if (!redisUtils.hasKey(codeKey)) {
|
||||
return CommonResult.errorResponse("重置密码链接不存在或已过期,请重新发送重置邮件");
|
||||
|
@ -227,7 +227,7 @@ public class AccountController {
|
|||
if (!configVo.getRegister()) { // 需要判断一下网站是否开启注册
|
||||
return CommonResult.errorResponse("对不起!本站暂未开启注册功能!", CommonResult.STATUS_ACCESS_DENIED);
|
||||
}
|
||||
String codeKey = Constants.Email.REGISTER_KEY_PREFIX.getValue()+registerDto.getEmail();
|
||||
String codeKey = Constants.Email.REGISTER_KEY_PREFIX.getValue() + registerDto.getEmail();
|
||||
if (!redisUtils.hasKey(codeKey)) {
|
||||
return CommonResult.errorResponse("验证码不存在或已过期");
|
||||
}
|
||||
|
@ -281,8 +281,10 @@ public class AccountController {
|
|||
response.setHeader("Access-Control-Expose-Headers", "Authorization");
|
||||
|
||||
// 会话记录
|
||||
sessionDao.insert(new Session().setUid(userRoles.getUid())
|
||||
.setIp(IpUtils.getUserIpAddr(request)).setUserAgent(request.getHeader("User-Agent")));
|
||||
sessionDao.insert(new Session()
|
||||
.setUid(userRoles.getUid())
|
||||
.setIp(IpUtils.getUserIpAddr(request))
|
||||
.setUserAgent(request.getHeader("User-Agent")));
|
||||
|
||||
return CommonResult.successResponse(MapUtil.builder()
|
||||
.put("uid", userRoles.getUid())
|
||||
|
@ -327,7 +329,6 @@ public class AccountController {
|
|||
* @Since 2021/01/07
|
||||
*/
|
||||
@GetMapping("/get-user-home-info")
|
||||
@RequiresAuthentication
|
||||
public CommonResult getUserHomeInfo(@RequestParam(value = "uid", required = false) String uid, HttpServletRequest request) {
|
||||
HttpSession session = request.getSession();
|
||||
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||
|
@ -343,7 +344,19 @@ public class AccountController {
|
|||
acProblemList.forEach(acProblem -> {
|
||||
pidList.add(acProblem.getPid());
|
||||
});
|
||||
userHomeInfo.setSolvedList(pidList);
|
||||
|
||||
List<String> disPlayIdList = new LinkedList<>();
|
||||
|
||||
if (pidList.size()>0) {
|
||||
QueryWrapper<Problem> problemQueryWrapper = new QueryWrapper<>();
|
||||
problemQueryWrapper.in("id", pidList);
|
||||
List<Problem> problems = problemService.list(problemQueryWrapper);
|
||||
problems.forEach(problem -> {
|
||||
disPlayIdList.add(problem.getProblemId());
|
||||
});
|
||||
}
|
||||
|
||||
userHomeInfo.setSolvedList(disPlayIdList);
|
||||
return CommonResult.successResponse(userHomeInfo, "查询成功!");
|
||||
}
|
||||
|
||||
|
@ -440,7 +453,7 @@ public class AccountController {
|
|||
if (StringUtils.isEmpty(password) || StringUtils.isEmpty(newEmail) || StringUtils.isEmpty(oldEmail)) {
|
||||
return CommonResult.errorResponse("请求参数不能为空!");
|
||||
}
|
||||
if (!Validator.isEmail(newEmail)){
|
||||
if (!Validator.isEmail(newEmail)) {
|
||||
return CommonResult.errorResponse("邮箱格式错误!");
|
||||
}
|
||||
// 获取当前登录的用户
|
||||
|
@ -517,7 +530,7 @@ public class AccountController {
|
|||
|
||||
@PostMapping("/change-userInfo")
|
||||
@RequiresAuthentication
|
||||
public CommonResult changeEmail(@RequestBody HashMap<String,Object> params, HttpServletRequest request) {
|
||||
public CommonResult changeEmail(@RequestBody HashMap<String, Object> params, HttpServletRequest request) {
|
||||
|
||||
// 获取当前登录的用户
|
||||
HttpSession session = request.getSession();
|
||||
|
@ -536,7 +549,7 @@ public class AccountController {
|
|||
|
||||
boolean result = userInfoDao.updateById(userInfo);
|
||||
|
||||
if (result){
|
||||
if (result) {
|
||||
return CommonResult.successResponse(MapUtil.builder()
|
||||
.put("uid", userRolesVo.getUid())
|
||||
.put("username", userRolesVo.getUsername())
|
||||
|
@ -553,7 +566,7 @@ public class AccountController {
|
|||
.put("cfUsername", userInfo.getCfUsername())
|
||||
.put("roleList", userRolesVo.getRoles().stream().map(Role::getRole))
|
||||
.map(), "更新个人信息成功!");
|
||||
}else{
|
||||
} else {
|
||||
return CommonResult.errorResponse("更新个人信息失败!");
|
||||
}
|
||||
|
||||
|
|
|
@ -220,7 +220,8 @@ public class JudgeController {
|
|||
// 将提交加入任务队列
|
||||
if (judgeDto.getIsRemote()) { // 如果是远程oj判题
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getDisplayPid(),
|
||||
judge.getCid() == 0, 1);
|
||||
judge.getCid() != 0, 1);
|
||||
|
||||
} else {
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getCid() == 0, 1);
|
||||
}
|
||||
|
@ -280,10 +281,10 @@ public class JudgeController {
|
|||
// 将提交加入任务队列
|
||||
if (problem.getIsRemote()) { // 如果是远程oj判题
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, problem.getProblemId(),
|
||||
judge.getCid() == 0, 1);
|
||||
judge.getCid() != 0, 1);
|
||||
} else {
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken,
|
||||
judge.getCid() == 0, 1);
|
||||
judge.getCid() != 0, 1);
|
||||
}
|
||||
return CommonResult.successResponse(judge, "重新提交成功!");
|
||||
}
|
||||
|
@ -340,6 +341,14 @@ public class JudgeController {
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @MethodName updateSubmission
|
||||
* @Params * @param null
|
||||
* @Description 修改单个提交详情的分享权限
|
||||
* @Return CommonResult
|
||||
* @Since 2021/1/2
|
||||
*/
|
||||
@PutMapping("/submission")
|
||||
@RequiresAuthentication
|
||||
public CommonResult updateSubmission(@RequestBody Judge judge, HttpServletRequest request) {
|
||||
|
@ -349,10 +358,12 @@ public class JudgeController {
|
|||
if (!userRolesVo.getUid().equals(judge.getUid())) { // 判断该提交是否为当前用户的
|
||||
return CommonResult.errorResponse("对不起,您不能修改他人的代码分享权限!");
|
||||
}
|
||||
if (judge.getCid() == 0) { // 如果是比赛提交,不可分享!
|
||||
Judge judgeInfo = judgeService.getById(judge.getSubmitId());
|
||||
if (judgeInfo.getCid() != 0) { // 如果是比赛提交,不可分享!
|
||||
return CommonResult.errorResponse("对不起,您不能分享比赛题目的提交代码!");
|
||||
}
|
||||
boolean result = judgeService.updateById(judge);
|
||||
judgeInfo.setShare(judge.getShare());
|
||||
boolean result = judgeService.updateById(judgeInfo);
|
||||
if (result) {
|
||||
if (judge.getShare()) {
|
||||
return CommonResult.successResponse(null, "设置代码公开成功!");
|
||||
|
|
|
@ -69,7 +69,8 @@ public class ProblemController {
|
|||
@RequestParam(value = "currentPage", required = false) Integer currentPage,
|
||||
@RequestParam(value = "keyword", required = false) String keyword,
|
||||
@RequestParam(value = "tagId", required = false) Long tagId,
|
||||
@RequestParam(value = "difficulty", required = false) Integer difficulty) {
|
||||
@RequestParam(value = "difficulty", required = false) Integer difficulty,
|
||||
@RequestParam(value = "oj", required = false) String oj) {
|
||||
// 页数,每页题数若为空,设置默认值
|
||||
if (currentPage == null || currentPage < 1) currentPage = 1;
|
||||
if (limit == null || limit < 1) limit = 10;
|
||||
|
@ -83,7 +84,9 @@ public class ProblemController {
|
|||
pid = Long.valueOf(keyword);
|
||||
}
|
||||
}
|
||||
Page<ProblemVo> problemList = problemService.getProblemList(limit, currentPage, pid, keyword, difficulty, tagId);
|
||||
|
||||
Page<ProblemVo> problemList = problemService.getProblemList(limit, currentPage, pid, keyword,
|
||||
difficulty, tagId, oj);
|
||||
if (problemList.getTotal() == 0) { // 未查询到一条数据
|
||||
return CommonResult.successResponse(problemList, "暂无数据");
|
||||
} else {
|
||||
|
|
|
@ -23,5 +23,6 @@ import java.util.List;
|
|||
@Repository
|
||||
public interface ProblemMapper extends BaseMapper<Problem> {
|
||||
List<ProblemVo> getProblemList(IPage page, @Param("pid") Long pid, @Param("keyword")String keyword,
|
||||
@Param("difficulty")Integer difficulty, @Param("tid")Long tid);
|
||||
@Param("difficulty")Integer difficulty, @Param("tid")Long tid,
|
||||
@Param("oj")String oj);
|
||||
}
|
||||
|
|
|
@ -44,8 +44,11 @@
|
|||
<if test="tid != null">
|
||||
and pt.tid = #{tid}
|
||||
</if>
|
||||
<if test="oj != null">
|
||||
and p.problem_id like concat(#{oj},'%')
|
||||
</if>
|
||||
</where>
|
||||
order by p.problem_id asc
|
||||
order by length(p.problem_id) asc,p.problem_id asc
|
||||
</select>
|
||||
|
||||
<!-- 子查询 :为了防止分页总数据数出错-->
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
package top.hcode.hoj.judge;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.listener.PatternTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
|
||||
import top.hcode.hoj.judge.remote.RemoteJudgeReceiver;
|
||||
import top.hcode.hoj.judge.self.JudgeReceiver;
|
||||
import top.hcode.hoj.utils.Constants;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/2/5 16:50
|
||||
* @Description: redis的发布订阅者模式的消息频道与订阅者的注册绑定
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureAfter({JudgeReceiver.class, RemoteJudgeReceiver.class})
|
||||
public class SubscriberConfig {
|
||||
|
||||
|
||||
/**
|
||||
* 消息监听适配器,注入接受消息方法,注入自己的判题服务的消息接收者
|
||||
*
|
||||
* @param receiver
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public MessageListenerAdapter selfJudgeMessageListenerAdapter(JudgeReceiver receiver) {
|
||||
return new MessageListenerAdapter(receiver); //当没有继承MessageListener时需要写方法名字
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 消息监听适配器,注入接受消息方法,注入远程判题服务的消息接收者
|
||||
*
|
||||
* @param receiver
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public MessageListenerAdapter remoteJudgeMessageListenerAdapter(RemoteJudgeReceiver receiver) {
|
||||
return new MessageListenerAdapter(receiver); //当没有继承MessageListener时需要写方法名字
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建消息监听容器
|
||||
*
|
||||
* @param redisConnectionFactory
|
||||
* @param selfJudgeMessageListenerAdapter
|
||||
* @param remoteJudgeMessageListenerAdapter
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory,
|
||||
MessageListenerAdapter selfJudgeMessageListenerAdapter,
|
||||
MessageListenerAdapter remoteJudgeMessageListenerAdapter) {
|
||||
RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
|
||||
redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
|
||||
// 注册信息接收者绑定自己判题服务的频道
|
||||
redisMessageListenerContainer.addMessageListener(selfJudgeMessageListenerAdapter, new PatternTopic(Constants.Judge.STATUS_JUDGE_WAITING.getName()));
|
||||
// 注册信息接收者绑定远程虚拟判题的频道
|
||||
redisMessageListenerContainer.addMessageListener(remoteJudgeMessageListenerAdapter, new PatternTopic(Constants.Judge.STATUS_REMOTE_JUDGE_WAITING_HANDLE.getName()));
|
||||
return redisMessageListenerContainer;
|
||||
}
|
||||
|
||||
}
|
|
@ -6,6 +6,7 @@ import cn.hutool.json.JSONUtil;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.hcode.hoj.pojo.entity.Judge;
|
||||
import top.hcode.hoj.service.impl.JudgeServiceImpl;
|
||||
import top.hcode.hoj.utils.Constants;
|
||||
import top.hcode.hoj.utils.RedisUtils;
|
||||
|
@ -21,6 +22,9 @@ public class RemoteJudgeDispatcher {
|
|||
@Autowired
|
||||
private JudgeServiceImpl judgeService;
|
||||
|
||||
@Autowired
|
||||
private RemoteJudgeReceiver remoteJudgeReceiver;
|
||||
|
||||
public void sendTask(Long submitId, Long pid, String token, String remoteJudge, Boolean isContest, Integer tryAgainNum) {
|
||||
JSONObject task = new JSONObject();
|
||||
task.set("submitId", submitId);
|
||||
|
@ -30,21 +34,19 @@ public class RemoteJudgeDispatcher {
|
|||
task.set("isContest", isContest);
|
||||
task.set("tryAgainNum", tryAgainNum);
|
||||
try {
|
||||
// 对应列表右边取出账号
|
||||
String account = (String) redisUtils.lrPop(Constants.Judge.getListNameByOJName(remoteJudge.split("-")[0]));
|
||||
if (account != null) {
|
||||
JSONObject accountJson = JSONUtil.parseObj(account);
|
||||
task.set("username", accountJson.getStr("username", null));
|
||||
task.set("password", accountJson.getStr("password", null));
|
||||
} else {
|
||||
task.set("username", null);
|
||||
task.set("password", null);
|
||||
boolean isOk = redisUtils.llPush(Constants.Judge.STATUS_REMOTE_JUDGE_WAITING_HANDLE.getName(), JSONUtil.toJsonStr(task));
|
||||
if (!isOk) {
|
||||
judgeService.updateById(new Judge()
|
||||
.setSubmitId(submitId)
|
||||
.setStatus(Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus())
|
||||
.setErrorMessage("Please try to submit again!")
|
||||
);
|
||||
}
|
||||
|
||||
redisUtils.sendMessage(Constants.Judge.STATUS_REMOTE_JUDGE_WAITING_HANDLE.getName(), JSONUtil.toJsonStr(task));
|
||||
} catch (Exception e) {
|
||||
log.error("调用redis消息发布异常,此次判题任务判为系统错误--------------->{}", e.getMessage());
|
||||
log.error("调用redis将判题纳入判题等待队列异常,此次判题任务判为系统错误--------------->{}", e.getMessage());
|
||||
judgeService.failToUseRedisPublishJudge(submitId, pid, isContest);
|
||||
}finally {
|
||||
remoteJudgeReceiver.processWaitingTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,10 @@ package top.hcode.hoj.judge.remote;
|
|||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import top.hcode.hoj.pojo.entity.Judge;
|
||||
import top.hcode.hoj.pojo.entity.ToJudge;
|
||||
import top.hcode.hoj.service.ToJudgeService;
|
||||
|
@ -17,8 +13,11 @@ import top.hcode.hoj.service.impl.JudgeServiceImpl;
|
|||
import top.hcode.hoj.utils.Constants;
|
||||
import top.hcode.hoj.utils.RedisUtils;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class RemoteJudgeReceiver implements MessageListener {
|
||||
@Async
|
||||
public class RemoteJudgeReceiver {
|
||||
|
||||
@Autowired
|
||||
private ToJudgeService toJudgeService;
|
||||
|
@ -32,41 +31,80 @@ public class RemoteJudgeReceiver implements MessageListener {
|
|||
@Autowired
|
||||
private RedisUtils redisUtils;
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
jackson2JsonRedisSerializer.setObjectMapper(om);
|
||||
String taskJson = (String) jackson2JsonRedisSerializer.deserialize(message.getBody());
|
||||
JSONObject task = JSONUtil.parseObj(taskJson);
|
||||
|
||||
public void processWaitingTask() {
|
||||
// 如果队列中还有任务,则继续处理
|
||||
if (redisUtils.lGetListSize(Constants.Judge.STATUS_REMOTE_JUDGE_WAITING_HANDLE.getName()) > 0) {
|
||||
String taskJsonStr = (String) redisUtils.lrPop(Constants.Judge.STATUS_REMOTE_JUDGE_WAITING_HANDLE.getName());
|
||||
// 再次检查
|
||||
if (taskJsonStr != null) {
|
||||
handleJudgeMsg(taskJsonStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void handleJudgeMsg(String taskJsonStr) {
|
||||
|
||||
JSONObject task = JSONUtil.parseObj(taskJsonStr);
|
||||
|
||||
Long submitId = task.getLong("submitId");
|
||||
String token = task.getStr("token");
|
||||
Long pid = task.getLong("pid");
|
||||
String remoteJudge = task.getStr("remoteJudge");
|
||||
Boolean isContest = task.getBool("isContest");
|
||||
String username = task.getStr("username");
|
||||
String password = task.getStr("password");
|
||||
Integer tryAgainNum = task.getInt("tryAgainNum");
|
||||
|
||||
System.out.println(username);
|
||||
System.out.println(password);
|
||||
// 如果对应远程判题oj的账号列表还有账号
|
||||
String remoteJudgeAccountListName = Constants.Judge.getListNameByOJName(remoteJudge.split("-")[0]);
|
||||
|
||||
if (username == null || password == null) {
|
||||
remoteJudgeDispatcher.sendTask(submitId, pid, token, remoteJudge, isContest, tryAgainNum);
|
||||
return;
|
||||
String account, username, password;
|
||||
if (redisUtils.lGetListSize(remoteJudgeAccountListName) > 0) {
|
||||
account = (String) redisUtils.lrPop(remoteJudgeAccountListName);
|
||||
if (StringUtils.isEmpty(account)) {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
remoteJudgeDispatcher.sendTask(submitId, pid, token, remoteJudge, isContest, tryAgainNum + 1);
|
||||
|
||||
} else {
|
||||
JSONObject accountJson = JSONUtil.parseObj(account);
|
||||
username = accountJson.getStr("username");
|
||||
password = accountJson.getStr("password");
|
||||
Judge judge = judgeService.getById(submitId);
|
||||
// 调用判题服务
|
||||
toJudgeService.remoteJudge(new ToJudge()
|
||||
.setJudge(judge)
|
||||
.setToken(token)
|
||||
.setRemoteJudge(remoteJudge)
|
||||
.setUsername(username)
|
||||
.setPassword(password)
|
||||
.setTryAgainNum(tryAgainNum));
|
||||
|
||||
// 如果队列中还有任务,则继续处理
|
||||
processWaitingTask();
|
||||
}
|
||||
} else {
|
||||
|
||||
if (tryAgainNum >= 40) {
|
||||
// 获取调用多次失败可能为系统忙碌,判为提交失败
|
||||
Judge judge = new Judge();
|
||||
judge.setSubmitId(submitId);
|
||||
judge.setStatus(Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus());
|
||||
judge.setErrorMessage("Failed to connect the judgeServer. Please resubmit this submission again!");
|
||||
judgeService.updateById(judge);
|
||||
} else {
|
||||
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
remoteJudgeDispatcher.sendTask(submitId, pid, token, remoteJudge, isContest, tryAgainNum + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Judge judge = judgeService.getById(submitId);
|
||||
|
||||
// 调用判题服务
|
||||
toJudgeService.remoteJudge(new ToJudge()
|
||||
.setJudge(judge)
|
||||
.setToken(token)
|
||||
.setRemoteJudge(remoteJudge)
|
||||
.setUsername(username)
|
||||
.setPassword(password)
|
||||
.setTryAgainNum(tryAgainNum));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import cn.hutool.json.JSONUtil;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.hcode.hoj.pojo.entity.Judge;
|
||||
import top.hcode.hoj.service.impl.JudgeServiceImpl;
|
||||
import top.hcode.hoj.utils.Constants;
|
||||
import top.hcode.hoj.utils.RedisUtils;
|
||||
|
@ -25,7 +26,10 @@ public class JudgeDispatcher {
|
|||
@Autowired
|
||||
private JudgeServiceImpl judgeService;
|
||||
|
||||
public void sendTask(Long submitId, Long pid, String token, Boolean isContest,Integer tryAgainNum) {
|
||||
@Autowired
|
||||
private JudgeReceiver judgeReceiver;
|
||||
|
||||
public void sendTask(Long submitId, Long pid, String token, Boolean isContest, Integer tryAgainNum) {
|
||||
JSONObject task = new JSONObject();
|
||||
task.set("submitId", submitId);
|
||||
task.set("pid", pid);
|
||||
|
@ -34,10 +38,20 @@ public class JudgeDispatcher {
|
|||
task.set("tryAgainNum", tryAgainNum);
|
||||
|
||||
try {
|
||||
redisUtils.sendMessage(Constants.Judge.STATUS_JUDGE_WAITING.getName(), JSONUtil.toJsonStr(task));
|
||||
boolean isOk = redisUtils.llPush(Constants.Judge.STATUS_JUDGE_WAITING.getName(), JSONUtil.toJsonStr(task));
|
||||
if (!isOk) {
|
||||
judgeService.updateById(new Judge()
|
||||
.setSubmitId(submitId)
|
||||
.setStatus(Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus())
|
||||
.setErrorMessage("Please try to submit again!")
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("调用redis消息发布异常,此次判题任务判为系统错误--------------->{}", e.getMessage());
|
||||
log.error("调用redis将判题纳入判题等待队列异常,此次判题任务判为系统错误--------------->{}", e.getMessage());
|
||||
judgeService.failToUseRedisPublishJudge(submitId, pid, isContest);
|
||||
} finally {
|
||||
// 调用判题任务处理
|
||||
judgeReceiver.processWaitingTask();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,18 +2,16 @@ package top.hcode.hoj.judge.self;
|
|||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.hcode.hoj.common.result.CommonResult;
|
||||
import top.hcode.hoj.pojo.entity.Judge;
|
||||
import top.hcode.hoj.pojo.entity.ToJudge;
|
||||
import top.hcode.hoj.service.ToJudgeService;
|
||||
import top.hcode.hoj.service.impl.JudgeServiceImpl;
|
||||
import top.hcode.hoj.utils.Constants;
|
||||
import top.hcode.hoj.utils.RedisUtils;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
|
@ -23,26 +21,35 @@ import top.hcode.hoj.service.impl.JudgeServiceImpl;
|
|||
* 3. 再次接受到信息,再次查询是否有空闲判题服务器,若有则进行判题,否则回到2
|
||||
*/
|
||||
@Component
|
||||
public class JudgeReceiver implements MessageListener {
|
||||
@Async
|
||||
public class JudgeReceiver {
|
||||
|
||||
@Autowired
|
||||
private ToJudgeService toJudgeService;
|
||||
|
||||
@Autowired
|
||||
private RedisUtils redisUtils;
|
||||
|
||||
@Autowired
|
||||
private JudgeServiceImpl judgeService;
|
||||
|
||||
@Autowired
|
||||
private JudgeDispatcher judgeDispatcher;
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
public void processWaitingTask() {
|
||||
// 如果队列中还有任务,则继续处理
|
||||
if (redisUtils.lGetListSize(Constants.Judge.STATUS_JUDGE_WAITING.getName()) > 0) {
|
||||
String taskJsonStr = (String) redisUtils.lrPop(Constants.Judge.STATUS_JUDGE_WAITING.getName());
|
||||
// 再次检查
|
||||
if (taskJsonStr != null) {
|
||||
handleJudgeMsg(taskJsonStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
jackson2JsonRedisSerializer.setObjectMapper(om);
|
||||
private void handleJudgeMsg(String taskJsonStr) {
|
||||
|
||||
String taskJson = (String) jackson2JsonRedisSerializer.deserialize(message.getBody());
|
||||
JSONObject task = JSONUtil.parseObj(taskJson);
|
||||
JSONObject task = JSONUtil.parseObj(taskJsonStr);
|
||||
Long submitId = task.getLong("submitId");
|
||||
String token = task.getStr("token");
|
||||
Integer tryAgainNum = task.getInt("tryAgainNum");
|
||||
|
@ -53,5 +60,10 @@ public class JudgeReceiver implements MessageListener {
|
|||
.setToken(token)
|
||||
.setRemoteJudge(null)
|
||||
.setTryAgainNum(tryAgainNum));
|
||||
|
||||
// 接着处理任务
|
||||
processWaitingTask();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -25,6 +25,8 @@ public class ProblemDto {
|
|||
|
||||
private String uploadTestcaseDir;
|
||||
|
||||
private Boolean isSpj;
|
||||
|
||||
private List<Language> languages;
|
||||
|
||||
private List<Tag> tags;
|
||||
|
|
|
@ -44,6 +44,9 @@ public class ConfigVo {
|
|||
@Value("${hoj.redis.port}")
|
||||
private Integer redisPort;
|
||||
|
||||
@Value("${hoj.redis.password}")
|
||||
private String redisPassword;
|
||||
|
||||
// jwt配置
|
||||
@Value("${hoj.jwt.secret}")
|
||||
private String tokenSecret;
|
||||
|
@ -64,6 +67,9 @@ public class ConfigVo {
|
|||
@Value("${hoj.mail.host}")
|
||||
private String emailHost;
|
||||
|
||||
@Value("${hoj.mail.port}")
|
||||
private Integer emailPort;
|
||||
|
||||
@Value("${hoj.mail.ssl}")
|
||||
private Boolean emailSsl;
|
||||
|
||||
|
|
|
@ -43,6 +43,6 @@ public class UserHomeVo {
|
|||
private Integer score;
|
||||
|
||||
@ApiModelProperty(value = "已解决题目列表")
|
||||
private List<Long> solvedList;
|
||||
private List<String> solvedList;
|
||||
|
||||
}
|
|
@ -20,7 +20,8 @@ import java.util.List;
|
|||
*/
|
||||
|
||||
public interface ProblemService extends IService<Problem> {
|
||||
Page<ProblemVo> getProblemList(int limit, int currentPage, Long pid, String title, Integer difficulty, Long tid);
|
||||
Page<ProblemVo> getProblemList(int limit, int currentPage, Long pid, String title,
|
||||
Integer difficulty, Long tid, String oj);
|
||||
|
||||
boolean adminUpdateProblem(ProblemDto problemDto);
|
||||
|
||||
|
@ -28,5 +29,5 @@ public interface ProblemService extends IService<Problem> {
|
|||
|
||||
ProblemStrategy.RemoteProblemInfo getOtherOJProblemInfo(String OJName, String problemId, String author) throws Exception;
|
||||
|
||||
boolean adminAddOtherOJProblem(ProblemStrategy.RemoteProblemInfo remoteProblemInfo,String OJName);
|
||||
boolean adminAddOtherOJProblem(ProblemStrategy.RemoteProblemInfo remoteProblemInfo, String OJName);
|
||||
}
|
||||
|
|
|
@ -115,9 +115,14 @@ public class ConfigServiceImpl implements ConfigService {
|
|||
if (!StringUtils.isEmpty(params.get("emailPassword"))) {
|
||||
configVo.setEmailPassword((String) params.get("emailPassword"));
|
||||
}
|
||||
if (!StringUtils.isEmpty(params.get("emailHost"))) {
|
||||
configVo.setEmailHost((String) params.get("emailHost"));
|
||||
if (!StringUtils.isEmpty(params.get("emailPort"))) {
|
||||
configVo.setEmailPort((Integer) params.get("emailPort"));
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(params.get("emailUsername"))) {
|
||||
configVo.setEmailUsername((String) params.get("emailUsername"));
|
||||
}
|
||||
|
||||
if (params.get("emailSsl") != null) {
|
||||
configVo.setEmailSsl((Boolean) params.get("emailSsl"));
|
||||
}
|
||||
|
@ -176,6 +181,9 @@ public class ConfigServiceImpl implements ConfigService {
|
|||
if (params.get("redisPort") != null) {
|
||||
configVo.setRedisPort((Integer) params.get("redisPort"));
|
||||
}
|
||||
if (params.get("redisPassword") != null) {
|
||||
configVo.setRedisPassword((String) params.get("redisPassword"));
|
||||
}
|
||||
return sendNewConfigToNacos();
|
||||
}
|
||||
|
||||
|
|
|
@ -65,12 +65,13 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
|
|||
|
||||
|
||||
@Override
|
||||
public Page<ProblemVo> getProblemList(int limit, int currentPage, Long pid, String title, Integer difficulty, Long tid) {
|
||||
public Page<ProblemVo> getProblemList(int limit, int currentPage, Long pid, String title, Integer difficulty,
|
||||
Long tid, String oj) {
|
||||
|
||||
//新建分页
|
||||
Page<ProblemVo> page = new Page<>(currentPage, limit);
|
||||
|
||||
return page.setRecords(problemMapper.getProblemList(page, pid, title, difficulty, tid));
|
||||
return page.setRecords(problemMapper.getProblemList(page, pid, title, difficulty, tid, oj));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,6 +79,10 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
|
|||
public boolean adminUpdateProblem(ProblemDto problemDto) {
|
||||
|
||||
Problem problem = problemDto.getProblem();
|
||||
|
||||
if (!problemDto.getIsSpj()) {
|
||||
problem.setSpjLanguage(null).setSpjCode(null);
|
||||
}
|
||||
problem.setProblemId(problem.getProblemId().toUpperCase());
|
||||
// 后面许多表的更新或删除需要用到题目id
|
||||
long pid = problemDto.getProblem().getId();
|
||||
|
@ -300,6 +305,11 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
|
|||
public boolean adminAddProblem(ProblemDto problemDto) {
|
||||
|
||||
Problem problem = problemDto.getProblem();
|
||||
|
||||
if (!problemDto.getIsSpj()) {
|
||||
problem.setSpjLanguage(null).setSpjCode(null);
|
||||
}
|
||||
|
||||
// 设置测试样例的版本号
|
||||
problem.setCaseVersion(String.valueOf(System.currentTimeMillis()));
|
||||
problem.setProblemId(problem.getProblemId().toUpperCase());
|
||||
|
@ -431,9 +441,9 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
|
|||
tagFlag.put(hasTag.getName(), hasTag);
|
||||
}
|
||||
for (Tag tmp : addTagList) {
|
||||
if (tagFlag.get(tmp.getName()) == null){
|
||||
if (tagFlag.get(tmp.getName()) == null) {
|
||||
needAddTagList.add(tmp);
|
||||
}else{
|
||||
} else {
|
||||
needAddTagList.add(tagFlag.get(tmp.getName()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,47 +81,19 @@ public class ScheduleServiceImpl implements ScheduleService {
|
|||
if (files.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
List<Long> idLists = new LinkedList<>();
|
||||
for (File file : files) {
|
||||
stringBuilder.append(file.getFilePath()).append(" ");
|
||||
idLists.add(file.getId());
|
||||
}
|
||||
//
|
||||
// WINDOWS系统下删除 DEL + 指令符
|
||||
// /P 删除每一个文件之前提示确认。
|
||||
// /F 强制删除只读文件。
|
||||
// /S 删除所有子目录中的指定的文件。
|
||||
// /Q 安静模式。删除全局通配符时,不要求确认
|
||||
// /A 根据属性选择要删除的文件
|
||||
Process process = null;
|
||||
try {
|
||||
String osName = System.getProperty("os.name");
|
||||
String execution;
|
||||
// 判断系统,执行不同删除代码
|
||||
if ("Windows 10".compareTo(osName) == 0) {
|
||||
execution = "cmd.exe /c DEL /F /S /Q " + stringBuilder.toString();
|
||||
} else {
|
||||
execution = "rm -rf " + stringBuilder.toString();
|
||||
}
|
||||
process = Runtime.getRuntime().exec(execution);
|
||||
process.waitFor();
|
||||
int result = process.exitValue(); //为0表示执行成功,非0表示shell执行出错
|
||||
if (result != 0) {
|
||||
log.error("头像删除异常----------------->Shell执行失败");
|
||||
} else {
|
||||
boolean isSuccess = fileService.removeByIds(idLists);
|
||||
if (!isSuccess) {
|
||||
log.error("数据库file表删除头像数据失败----------------->sql语句执行失败");
|
||||
if(file.getDelete()) {
|
||||
boolean delSuccess = FileUtil.del(file.getFilePath());
|
||||
if (delSuccess) {
|
||||
idLists.add(file.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("头像删除异常--------------------->{}", e.getMessage());
|
||||
} finally {
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
boolean isSuccess = fileService.removeByIds(idLists);
|
||||
if (!isSuccess) {
|
||||
log.error("数据库file表删除头像数据失败----------------->sql语句执行失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.stereotype.Component;
|
||||
import top.hcode.hoj.pojo.vo.ConfigVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2020/12/2 23:17
|
||||
|
@ -15,43 +17,66 @@ public class ConfigUtils {
|
|||
private ConfigVo configVo;
|
||||
|
||||
public String getConfigContent() {
|
||||
|
||||
return "hoj:\n" +
|
||||
" jwt:\n" +
|
||||
" # 加密秘钥\n" +
|
||||
" secret: "+configVo.getTokenSecret()+"\n" +
|
||||
" secret: " + configVo.getTokenSecret() + "\n" +
|
||||
" # token有效时长,1小时,单位秒\n" +
|
||||
" expire: "+configVo.getTokenExpire()+"\n" +
|
||||
" checkRefreshExpire: "+configVo.getCheckRefreshExpire()+"\n"+
|
||||
" expire: " + configVo.getTokenExpire() + "\n" +
|
||||
" checkRefreshExpire: " + configVo.getCheckRefreshExpire() + "\n" +
|
||||
" header: token\n" +
|
||||
" judge:\n" +
|
||||
" # 调用判题服务器的token\n" +
|
||||
" token: "+configVo.getJudgeToken()+"\n"+
|
||||
" token: " + configVo.getJudgeToken() + "\n" +
|
||||
" db:\n" +
|
||||
" host: "+configVo.getMysqlHost()+"\n" +
|
||||
" port: "+configVo.getMysqlPort()+"\n" +
|
||||
" name: "+configVo.getMysqlDBName()+"\n" +
|
||||
" username: "+configVo.getMysqlUsername()+"\n" +
|
||||
" password: "+configVo.getMysqlPassword()+"\n" +
|
||||
" host: " + configVo.getMysqlHost() + "\n" +
|
||||
" port: " + configVo.getMysqlPort() + "\n" +
|
||||
" name: " + configVo.getMysqlDBName() + "\n" +
|
||||
" username: " + configVo.getMysqlUsername() + "\n" +
|
||||
" password: " + configVo.getMysqlPassword() + "\n" +
|
||||
" mail:\n" +
|
||||
" ssl: "+configVo.getEmailSsl()+"\n" +
|
||||
" username: "+configVo.getEmailUsername()+"\n" +
|
||||
" password: "+configVo.getEmailPassword()+"\n" +
|
||||
" host: "+configVo.getEmailHost()+"\n" +
|
||||
" ssl: " + configVo.getEmailSsl() + "\n" +
|
||||
" username: " + configVo.getEmailUsername() + "\n" +
|
||||
" password: " + configVo.getEmailPassword() + "\n" +
|
||||
" host: " + configVo.getEmailHost() + "\n" +
|
||||
" port: " + configVo.getEmailPort() + "\n" +
|
||||
" redis:\n" +
|
||||
" host: "+configVo.getRedisHost()+"\n" +
|
||||
" port: "+configVo.getRedisPort()+"\n" +
|
||||
" host: " + configVo.getRedisHost() + "\n" +
|
||||
" port: " + configVo.getRedisPort() + "\n" +
|
||||
" password: " + configVo.getRedisPassword() + "\n" +
|
||||
" web-config:\n" +
|
||||
" base-url: "+configVo.getBaseUrl()+"\n" +
|
||||
" name: "+configVo.getName()+"\n" +
|
||||
" short-name: "+configVo.getShortName()+"\n" +
|
||||
" register: "+configVo.getRegister()+"\n" +
|
||||
" base-url: " + configVo.getBaseUrl() + "\n" +
|
||||
" name: " + configVo.getName() + "\n" +
|
||||
" short-name: " + configVo.getShortName() + "\n" +
|
||||
" register: " + configVo.getRegister() + "\n" +
|
||||
" footer:\n" +
|
||||
" record:\n" +
|
||||
" name: "+configVo.getRecordName()+"\n" +
|
||||
" url: "+configVo.getRecordUrl()+"\n" +
|
||||
" name: " + configVo.getRecordName() + "\n" +
|
||||
" url: " + configVo.getRecordUrl() + "\n" +
|
||||
" project:\n" +
|
||||
" name: "+configVo.getProjectName()+"\n" +
|
||||
" url: "+configVo.getProjectUrl();
|
||||
" name: " + configVo.getProjectName() + "\n" +
|
||||
" url: " + configVo.getProjectUrl() + "\n" +
|
||||
" hdu:\n" +
|
||||
" account:\n" +
|
||||
" username: "+listToStr(configVo.getHduUsernameList())+ "\n" +
|
||||
" password: "+listToStr(configVo.getHduPasswordList())+ "\n" +
|
||||
" cf:\n" +
|
||||
" account:\n" +
|
||||
" username: "+listToStr(configVo.getCfUsernameList())+ "\n" +
|
||||
" password: "+listToStr(configVo.getCfPasswordList());
|
||||
}
|
||||
|
||||
private String listToStr(List<String> list) {
|
||||
StringBuilder listStr = new StringBuilder();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (i != list.size() - 1) {
|
||||
listStr.append(list.get(i)).append(",");
|
||||
} else {
|
||||
listStr.append(list.get(i));
|
||||
}
|
||||
}
|
||||
return listStr.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,53 @@
|
|||
package top.hcode.hoj.utils;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/1/1 13:00
|
||||
* @Description: 常量枚举类
|
||||
*/
|
||||
@Component
|
||||
public class Constants {
|
||||
|
||||
public static String ojAddr;
|
||||
|
||||
public static String ojName;
|
||||
|
||||
public static String ojShortName;
|
||||
|
||||
public static String ojEmailBg;
|
||||
|
||||
public static String ojEmailFrom;
|
||||
|
||||
|
||||
|
||||
@Value("${hoj-backstage.addr}")
|
||||
public void setOjAddr(String ojAddr) {
|
||||
Constants.ojAddr = ojAddr;
|
||||
}
|
||||
|
||||
@Value("${hoj-backstage.name}")
|
||||
public void setOjName(String ojName) {
|
||||
Constants.ojName = ojName;
|
||||
}
|
||||
|
||||
@Value("${hoj-backstage.short-name}")
|
||||
public void setOjShortName(String ojShortName) {
|
||||
Constants.ojShortName = ojShortName;
|
||||
}
|
||||
|
||||
@Value("${hoj-backstage.email-bg}")
|
||||
public void setOjEmailBg(String ojEmailBg) {
|
||||
Constants.ojEmailBg = ojEmailBg;
|
||||
}
|
||||
|
||||
@Value("${hoj.mail.username}")
|
||||
public void setOjEmailFrom(String ojEmailFrom) {
|
||||
Constants.ojEmailFrom = ojEmailFrom;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 提交评测结果的状态码
|
||||
* @Since 2021/1/1
|
||||
|
@ -143,19 +185,19 @@ public class Constants {
|
|||
*/
|
||||
public enum File {
|
||||
|
||||
USER_FILE_HOST("http://localhost:9010"),
|
||||
USER_FILE_HOST(ojAddr),
|
||||
|
||||
USER_AVATAR_FOLDER("D:\\avatar\\"),
|
||||
USER_AVATAR_FOLDER("/hoj/file/avatar"),
|
||||
|
||||
MARKDOWN_IMG_FOLDER("D:\\md\\"),
|
||||
MARKDOWN_IMG_FOLDER("/hoj/md"),
|
||||
|
||||
IMG_API("/public/img/"),
|
||||
IMG_API("/api/public/img/"),
|
||||
|
||||
TESTCASE_BASE_FOLDER("D:\\zip\\"),
|
||||
TESTCASE_BASE_FOLDER("/hoj/file/zip"),
|
||||
|
||||
TESTCASE_DOWNLOAD_TMP_FOLDER("D:\\zip\\download"),
|
||||
TESTCASE_DOWNLOAD_TMP_FOLDER("/hoj/file/zip/download"),
|
||||
|
||||
CONTEST_AC_SUBMISSION_TMP_FOLDER("D:\\zip\\contest_ac");
|
||||
CONTEST_AC_SUBMISSION_TMP_FOLDER("/hoj/file/zip/contest_ac");
|
||||
|
||||
private final String path;
|
||||
|
||||
|
@ -175,14 +217,16 @@ public class Constants {
|
|||
*/
|
||||
|
||||
public enum Email {
|
||||
OJ_URL("http://localhost:8080"),
|
||||
OJ_NAME("Hcode Online Judge"),
|
||||
OJ_SHORT_NAME("HOJ"),
|
||||
EMAIL_FROM("oj.hcode@qq.com"),
|
||||
EMAIL_BACKGROUND_IMG("https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/HCODE.png"),
|
||||
|
||||
OJ_URL(ojAddr),
|
||||
OJ_NAME(ojName),
|
||||
OJ_SHORT_NAME(ojShortName),
|
||||
EMAIL_FROM(ojEmailFrom),
|
||||
EMAIL_BACKGROUND_IMG(ojEmailBg),
|
||||
REGISTER_KEY_PREFIX("register-user:"),
|
||||
RESET_PASSWORD_KEY_PREFIX("reset-password:");
|
||||
private String value;
|
||||
|
||||
private final String value;
|
||||
|
||||
Email(String value) {
|
||||
this.value = value;
|
||||
|
|
|
@ -51,13 +51,14 @@ public class IpUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static String getServiceIp(){
|
||||
public static String getServiceIp() {
|
||||
InetAddress address = null;
|
||||
try {
|
||||
address = InetAddress.getLocalHost();
|
||||
return address.getHostAddress(); //返回IP地址
|
||||
} catch (UnknownHostException e) {
|
||||
log.error("本地ip获取异常---------->{}", e.getMessage());
|
||||
}
|
||||
return address.getHostAddress(); //返回IP地址
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
active: prod
|
||||
|
||||
---
|
||||
# 消费者将要去访问的微服务名称(注册成功进入nacos的微服务提供者)
|
||||
|
@ -9,7 +9,7 @@ service-url:
|
|||
name: hoj-judge-server # 服务名
|
||||
|
||||
spring:
|
||||
profiles: dev
|
||||
profiles: prod
|
||||
# 配置文件上传限制
|
||||
servlet:
|
||||
multipart:
|
||||
|
@ -17,9 +17,12 @@ spring:
|
|||
max-request-size: 50MB
|
||||
mail:
|
||||
properties.mail.smtp.ssl.enable: ${hoj.mail.ssl}
|
||||
properties.mail.smtp.starttls.enable: true
|
||||
properties.mail.smtp.starttls.required: true
|
||||
username: ${hoj.mail.username}
|
||||
password: ${hoj.mail.password}
|
||||
host: ${hoj.mail.host}
|
||||
port: ${hoj.mail.port}
|
||||
redis:
|
||||
host: ${hoj.redis.host}
|
||||
port: ${hoj.redis.port}
|
||||
|
@ -30,6 +33,7 @@ spring:
|
|||
max-idle: 200 #连接池中的最大空闲连接
|
||||
max-active: 500 #连接池最大连接数(使用负值表示没有限制)
|
||||
max-wait: -1 #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
password: ${hoj.redis.password}
|
||||
datasource:
|
||||
username: ${hoj.db.username}
|
||||
password: ${hoj.db.password}
|
||||
|
@ -69,7 +73,7 @@ shiro-redis:
|
|||
enabled: true
|
||||
redis-manager:
|
||||
host: ${hoj.redis.host}:${hoj.redis.port}
|
||||
|
||||
password: ${hoj.redis.password}
|
||||
|
||||
ribbon:
|
||||
# 指的是建立连接所用的时间,适用于网络状况正常的情况下,俩端连接所用的时间 单位是秒
|
||||
|
@ -84,6 +88,7 @@ logging:
|
|||
nacos:
|
||||
client:
|
||||
naming: error
|
||||
root: error
|
||||
|
||||
# 开启Hystrix断路器
|
||||
feign:
|
||||
|
|
|
@ -1,23 +1,31 @@
|
|||
hoj-backstage:
|
||||
port: 6688
|
||||
nacos-url: 172.18.0.2:8848
|
||||
addr: http://oj.hcode.top
|
||||
name: Hcode Online Judge
|
||||
short-name: HOJ
|
||||
email-bg: https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/HCODE.png # 邮箱系统发生邮件模板的背景图片地址
|
||||
|
||||
server:
|
||||
port: 9010
|
||||
port: ${hoj-backstage.port}
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
active: prod
|
||||
application:
|
||||
name: hoj-data-backup
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 129.204.177.72:8848 # Nacos 作为服务注册中心
|
||||
server-addr: ${hoj-backstage.nacos-url} # Nacos 作为服务注册中心 nacos的地址
|
||||
config:
|
||||
server-addr: 129.204.177.72:8848 #Nacos 作为配置中心地址
|
||||
server-addr: ${hoj-backstage.nacos-url} #Nacos 作为配置中心地址 nacos的地址
|
||||
file-extension: yml #指定yaml格式的配置
|
||||
group: DEFAULT_GROUP # 指定分组
|
||||
data-id: hoj-data-backup-dev.yml
|
||||
data-id: hoj-data-backup-prod.yml
|
||||
type: yaml
|
||||
#namespace:命名空间ID 默认为public
|
||||
url: http://129.204.177.72:8848
|
||||
url: http://${hoj-backstage.nacos-url}
|
||||
|
||||
|
||||
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.naces.config.file-extension}
|
||||
# hoj-data-backup-dev.yml
|
||||
# hoj-data-backup-prod.yml
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
active: prod
|
||||
|
||||
---
|
||||
# 消费者将要去访问的微服务名称(注册成功进入nacos的微服务提供者)
|
||||
|
@ -9,7 +9,7 @@ service-url:
|
|||
name: hoj-judge-server # 服务名
|
||||
|
||||
spring:
|
||||
profiles: dev
|
||||
profiles: prod
|
||||
# 配置文件上传限制
|
||||
servlet:
|
||||
multipart:
|
||||
|
@ -17,9 +17,12 @@ spring:
|
|||
max-request-size: 50MB
|
||||
mail:
|
||||
properties.mail.smtp.ssl.enable: ${hoj.mail.ssl}
|
||||
properties.mail.smtp.starttls.enable: true
|
||||
properties.mail.smtp.starttls.required: true
|
||||
username: ${hoj.mail.username}
|
||||
password: ${hoj.mail.password}
|
||||
host: ${hoj.mail.host}
|
||||
port: ${hoj.mail.port}
|
||||
redis:
|
||||
host: ${hoj.redis.host}
|
||||
port: ${hoj.redis.port}
|
||||
|
@ -30,6 +33,7 @@ spring:
|
|||
max-idle: 200 #连接池中的最大空闲连接
|
||||
max-active: 500 #连接池最大连接数(使用负值表示没有限制)
|
||||
max-wait: -1 #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
password: ${hoj.redis.password}
|
||||
datasource:
|
||||
username: ${hoj.db.username}
|
||||
password: ${hoj.db.password}
|
||||
|
@ -69,7 +73,7 @@ shiro-redis:
|
|||
enabled: true
|
||||
redis-manager:
|
||||
host: ${hoj.redis.host}:${hoj.redis.port}
|
||||
|
||||
password: ${hoj.redis.password}
|
||||
|
||||
ribbon:
|
||||
# 指的是建立连接所用的时间,适用于网络状况正常的情况下,俩端连接所用的时间 单位是秒
|
||||
|
@ -84,6 +88,7 @@ logging:
|
|||
nacos:
|
||||
client:
|
||||
naming: error
|
||||
root: error
|
||||
|
||||
# 开启Hystrix断路器
|
||||
feign:
|
||||
|
|
|
@ -1,23 +1,31 @@
|
|||
hoj-backstage:
|
||||
port: 6688
|
||||
nacos-url: 172.18.0.2:8848
|
||||
addr: http://oj.hcode.top
|
||||
name: Hcode Online Judge
|
||||
short-name: HOJ
|
||||
email-bg: https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/HCODE.png # 邮箱系统发生邮件模板的背景图片地址
|
||||
|
||||
server:
|
||||
port: 9010
|
||||
port: ${hoj-backstage.port}
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
active: prod
|
||||
application:
|
||||
name: hoj-data-backup
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 129.204.177.72:8848 # Nacos 作为服务注册中心
|
||||
server-addr: ${hoj-backstage.nacos-url} # Nacos 作为服务注册中心 nacos的地址
|
||||
config:
|
||||
server-addr: 129.204.177.72:8848 #Nacos 作为配置中心地址
|
||||
server-addr: ${hoj-backstage.nacos-url} #Nacos 作为配置中心地址 nacos的地址
|
||||
file-extension: yml #指定yaml格式的配置
|
||||
group: DEFAULT_GROUP # 指定分组
|
||||
data-id: hoj-data-backup-dev.yml
|
||||
data-id: hoj-data-backup-prod.yml
|
||||
type: yaml
|
||||
#namespace:命名空间ID 默认为public
|
||||
url: http://129.204.177.72:8848
|
||||
url: http://${hoj-backstage.nacos-url}
|
||||
|
||||
|
||||
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.naces.config.file-extension}
|
||||
# hoj-data-backup-dev.yml
|
||||
# hoj-data-backup-prod.yml
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -44,8 +44,11 @@
|
|||
<if test="tid != null">
|
||||
and pt.tid = #{tid}
|
||||
</if>
|
||||
<if test="oj != null">
|
||||
and p.problem_id like concat(#{oj},'%')
|
||||
</if>
|
||||
</where>
|
||||
order by p.problem_id asc
|
||||
order by length(p.problem_id) asc,p.problem_id asc
|
||||
</select>
|
||||
|
||||
<!-- 子查询 :为了防止分页总数据数出错-->
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,8 +1,7 @@
|
|||
package top.hcode.hoj.controller;
|
||||
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
@ -12,13 +11,11 @@ import top.hcode.hoj.common.CommonResult;
|
|||
import top.hcode.hoj.common.exception.CompileError;
|
||||
import top.hcode.hoj.common.exception.SystemError;
|
||||
import top.hcode.hoj.pojo.entity.*;
|
||||
import top.hcode.hoj.remoteJudge.submit.RemoteJudgeSubmitDispatcher;
|
||||
import top.hcode.hoj.remoteJudge.RemoteJudgeToSubmit;
|
||||
import top.hcode.hoj.service.impl.*;
|
||||
import top.hcode.hoj.util.Constants;
|
||||
import top.hcode.hoj.util.IpUtils;
|
||||
import top.hcode.hoj.util.RedisUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -41,16 +38,31 @@ public class JudgeController {
|
|||
private SystemConfigServiceImpl systemConfigService;
|
||||
|
||||
@Autowired
|
||||
private RemoteJudgeSubmitDispatcher remoteJudgeSubmitDispatcher;
|
||||
private RemoteJudgeToSubmit remoteJudgeToSubmit;
|
||||
|
||||
@Value("${hoj.judge.token}")
|
||||
private String judgeToken;
|
||||
|
||||
@Value("${hoj-judger.max-task-num}")
|
||||
private Integer maxTaskNum;
|
||||
|
||||
@Value("${hoj-judger.name}")
|
||||
private String name;
|
||||
|
||||
@Autowired
|
||||
private RedisUtils redisUtils;
|
||||
|
||||
@RequestMapping("/version")
|
||||
public CommonResult getVersion() {
|
||||
|
||||
return CommonResult.successResponse(MapUtil.builder()
|
||||
.put("version", "1.0.0")
|
||||
.put("currentTime", new Date())
|
||||
.put("judgeServerName", name)
|
||||
.put("cpu", Runtime.getRuntime().availableProcessors())
|
||||
.put("maxTaskNum", maxTaskNum)
|
||||
.map(),
|
||||
"运行正常"
|
||||
);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/judge")
|
||||
public CommonResult submitProblemJudge(@RequestBody ToJudge toJudge) {
|
||||
|
@ -136,30 +148,8 @@ public class JudgeController {
|
|||
String userCode = toJudge.getJudge().getCode();
|
||||
String language = toJudge.getJudge().getLanguage();
|
||||
|
||||
// 发送消息
|
||||
try {
|
||||
remoteJudgeSubmitDispatcher.sendTask(username, password, remoteJudge, remotePid, submitId, uid, cid, pid, language, userCode);
|
||||
return CommonResult.successResponse(null, "提交成功");
|
||||
} catch (Exception e) {
|
||||
// 将使用的账号放回对应列表
|
||||
JSONObject account = new JSONObject();
|
||||
account.set("username", username);
|
||||
account.set("password", password);
|
||||
redisUtils.llPush(Constants.RemoteJudge.getListNameByOJName(remoteJudge), JSONUtil.toJsonStr(account));
|
||||
Judge judge = new Judge();
|
||||
judge.setSubmitId(submitId)
|
||||
.setStatus(Constants.Judge.STATUS_SYSTEM_ERROR.getStatus())
|
||||
.setErrorMessage("Oops, something has gone wrong with the judgeServer. Please report this to administrator.");
|
||||
judgeService.updateById(judge);
|
||||
// 更新其它表
|
||||
judgeService.updateOtherTable(submitId,
|
||||
Constants.Judge.STATUS_SYSTEM_ERROR.getStatus(),
|
||||
cid,
|
||||
uid,
|
||||
pid,
|
||||
null);
|
||||
log.error("调用redis消息发布异常,此次远程判题任务判为系统错误--------------->{}", e.getMessage());
|
||||
return CommonResult.errorResponse(e.getMessage(), CommonResult.STATUS_ERROR);
|
||||
}
|
||||
// 调用远程判题
|
||||
remoteJudgeToSubmit.sendTask(username, password, remoteJudge, remotePid, submitId, uid, cid, pid, language, userCode);
|
||||
return CommonResult.successResponse(null, "提交成功");
|
||||
}
|
||||
}
|
|
@ -660,6 +660,7 @@ public class JudgeStrategy {
|
|||
String testCasesDir = Constants.JudgeDir.TEST_CASE_DIR.getContent() + "/problem_" + problemId;
|
||||
|
||||
// 无论有没有测试数据,一旦执行该函数,一律清空,重新生成该题目对应的测试数据文件
|
||||
|
||||
FileUtil.del(testCasesDir);
|
||||
for (int index = 0; index < testCases.size(); index++) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
package top.hcode.hoj.remoteJudge;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import top.hcode.hoj.pojo.entity.Judge;
|
||||
|
||||
import top.hcode.hoj.remoteJudge.task.RemoteJudgeFactory;
|
||||
import top.hcode.hoj.remoteJudge.task.RemoteJudgeStrategy;
|
||||
import top.hcode.hoj.service.impl.JudgeServiceImpl;
|
||||
import top.hcode.hoj.util.Constants;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RemoteJudgeGetResult {
|
||||
|
||||
@Autowired
|
||||
private JudgeServiceImpl judgeService;
|
||||
|
||||
@Transactional
|
||||
public void sendTask(String remoteJudge, String username, Long submitId, String uid,
|
||||
Long cid, Long pid, Long resultSubmitId, String token,
|
||||
HashMap<String, String> cookies) {
|
||||
|
||||
RemoteJudgeStrategy remoteJudgeStrategy = RemoteJudgeFactory.selectJudge(remoteJudge);
|
||||
|
||||
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
Runnable getResultTask = new Runnable() {
|
||||
@Override
|
||||
@Transactional
|
||||
public void run() {
|
||||
count.getAndIncrement();
|
||||
try {
|
||||
Map<String, Object> result = remoteJudgeStrategy.result(resultSubmitId, username, token, cookies);
|
||||
Integer status = (Integer) result.getOrDefault("status", Constants.Judge.STATUS_SYSTEM_ERROR.getStatus());
|
||||
if (status.intValue() != Constants.Judge.STATUS_PENDING.getStatus() &&
|
||||
status.intValue() != Constants.Judge.STATUS_JUDGING.getStatus()) {
|
||||
Judge judge = new Judge();
|
||||
judge.setSubmitId(submitId);
|
||||
Integer time = (Integer) result.getOrDefault("time", null);
|
||||
Integer memory = (Integer) result.getOrDefault("memory", null);
|
||||
String CEInfo = (String) result.getOrDefault("CEInfo", null);
|
||||
judge.setStatus(status)
|
||||
.setTime(time)
|
||||
.setMemory(memory);
|
||||
|
||||
if (status.intValue() == Constants.Judge.STATUS_COMPILE_ERROR.getStatus()) {
|
||||
judge.setErrorMessage(CEInfo);
|
||||
} else if (status.intValue() == Constants.Judge.STATUS_SYSTEM_ERROR.getStatus()) {
|
||||
judge.setErrorMessage("There is something wrong with the " + remoteJudge + ", please try again later");
|
||||
}
|
||||
// 写回数据库
|
||||
judgeService.updateById(judge);
|
||||
// 同步其它表
|
||||
judgeService.updateOtherTable(submitId, status, cid, uid, pid, null);
|
||||
scheduler.shutdown();
|
||||
}
|
||||
|
||||
} catch (Exception ignored) {
|
||||
|
||||
} finally {
|
||||
if (count.get() == 60) { // 60次失败则判为提交失败
|
||||
// 更新此次提交状态为提交失败!
|
||||
UpdateWrapper<Judge> judgeUpdateWrapper = new UpdateWrapper<>();
|
||||
judgeUpdateWrapper.set("status", Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus())
|
||||
.eq("submit_id", submitId);
|
||||
judgeService.update(judgeUpdateWrapper);
|
||||
scheduler.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
final ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(
|
||||
getResultTask, 2, 2, TimeUnit.SECONDS);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package top.hcode.hoj.remoteJudge;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.listener.PatternTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
|
||||
import top.hcode.hoj.remoteJudge.result.RemoteJudgeResultReceiver;
|
||||
import top.hcode.hoj.remoteJudge.submit.RemoteJudgeSubmitReceiver;
|
||||
import top.hcode.hoj.util.Constants;
|
||||
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureAfter({RemoteJudgeSubmitReceiver.class, RemoteJudgeResultReceiver.class})
|
||||
public class RemoteJudgeSubscriberConfig {
|
||||
|
||||
|
||||
/**
|
||||
* 消息监听适配器,注入接受消息方法,处理提交代码到其它oj的消息
|
||||
*
|
||||
* @param receiver
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public MessageListenerAdapter toSubmitMessageListenerAdapter(RemoteJudgeSubmitReceiver receiver) {
|
||||
return new MessageListenerAdapter(receiver); //当没有继承MessageListener时需要写方法名字
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 消息监听适配器,注入接受消息方法,处理获取提交结果的消息
|
||||
*
|
||||
* @param receiver
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public MessageListenerAdapter getResultMessageListenerAdapter(RemoteJudgeResultReceiver receiver) {
|
||||
return new MessageListenerAdapter(receiver); //当没有继承MessageListener时需要写方法名字
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建消息监听容器
|
||||
*
|
||||
* @param redisConnectionFactory
|
||||
* @param toSubmitMessageListenerAdapter
|
||||
* @param getResultMessageListenerAdapter
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory,
|
||||
MessageListenerAdapter toSubmitMessageListenerAdapter,
|
||||
MessageListenerAdapter getResultMessageListenerAdapter) {
|
||||
RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
|
||||
redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
|
||||
// 添加频道名字
|
||||
redisMessageListenerContainer.addMessageListener(toSubmitMessageListenerAdapter, new PatternTopic(Constants.RemoteJudge.JUDGE_SUBMIT_HANDLER.getName()));
|
||||
redisMessageListenerContainer.addMessageListener(getResultMessageListenerAdapter, new PatternTopic(Constants.RemoteJudge.JUDGE_RESULT_HANDLER.getName()));
|
||||
return redisMessageListenerContainer;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +1,13 @@
|
|||
package top.hcode.hoj.remoteJudge.submit;
|
||||
package top.hcode.hoj.remoteJudge;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.hcode.hoj.pojo.entity.Judge;
|
||||
import top.hcode.hoj.remoteJudge.result.RemoteJudgeResultDispatcher;
|
||||
import top.hcode.hoj.remoteJudge.task.RemoteJudgeFactory;
|
||||
import top.hcode.hoj.remoteJudge.task.RemoteJudgeStrategy;
|
||||
import top.hcode.hoj.service.impl.JudgeServiceImpl;
|
||||
|
@ -22,48 +19,43 @@ import java.util.Map;
|
|||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class RemoteJudgeSubmitReceiver implements MessageListener {
|
||||
public class RemoteJudgeToSubmit {
|
||||
|
||||
@Autowired
|
||||
private RedisUtils redisUtils;
|
||||
|
||||
@Autowired
|
||||
private RemoteJudgeResultDispatcher remoteJudgeResultDispatcher;
|
||||
private RemoteJudgeGetResult remoteJudgeGetResult;
|
||||
|
||||
@Autowired
|
||||
private JudgeServiceImpl judgeService;
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
// log.debug("RemoteJudgeSubmitReceiver获取到消息{}", Arrays.toString(message.getBody()));
|
||||
String source = (String) redisUtils.lrPop(Constants.RemoteJudge.JUDGE_WAITING_SUBMIT_QUEUE.getName());
|
||||
// 如果竞争不到提交队列,结束
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
JSONObject task = JSONUtil.parseObj(source);
|
||||
String remotePid = task.getStr("remotePid");
|
||||
Long submitId = task.getLong("submitId");
|
||||
String uid = task.getStr("uid");
|
||||
Long cid = task.getLong("cid");
|
||||
Long pid = task.getLong("pid");
|
||||
String remoteJudge = task.getStr("remoteJudge");
|
||||
String language = task.getStr("language");
|
||||
String userCode = task.getStr("userCode");
|
||||
String username = task.getStr("username");
|
||||
String password = task.getStr("password");
|
||||
@Value("${hoj-judger.name}")
|
||||
private String name;
|
||||
|
||||
|
||||
public void sendTask(String username, String password, String remoteJudge, String remotePid, Long submitId,
|
||||
String uid, Long cid, Long pid, String language, String userCode) {
|
||||
|
||||
RemoteJudgeStrategy remoteJudgeStrategy = RemoteJudgeFactory.selectJudge(remoteJudge);
|
||||
// 获取不到对应的题库或者题库写错了
|
||||
if (remoteJudgeStrategy == null) {
|
||||
log.error("暂不支持该{}题库---------------->请求失败", remoteJudge);
|
||||
// 更新此次提交状态为系统失败!
|
||||
UpdateWrapper<Judge> judgeUpdateWrapper = new UpdateWrapper<>();
|
||||
judgeUpdateWrapper.set("status", Constants.Judge.STATUS_SYSTEM_ERROR.getStatus())
|
||||
.set("error_message", "The judge server does not support this oj:" + remoteJudge)
|
||||
.eq("submit_id", submitId);
|
||||
judgeService.update(judgeUpdateWrapper);
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> submitResult = null;
|
||||
try {
|
||||
submitResult = remoteJudgeStrategy.submit(username, password, remotePid, language, userCode);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
log.error(remoteJudge + "的远程提交发生异常---------->{}", e.getMessage());
|
||||
} finally {
|
||||
// 将使用的账号放回对应列表
|
||||
JSONObject account = new JSONObject();
|
||||
account.set("username", username);
|
||||
|
@ -78,19 +70,28 @@ public class RemoteJudgeSubmitReceiver implements MessageListener {
|
|||
judgeUpdateWrapper.set("status", Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus())
|
||||
.eq("submit_id", submitId);
|
||||
judgeService.update(judgeUpdateWrapper);
|
||||
// 更新其它表
|
||||
judgeService.updateOtherTable(submitId,
|
||||
Constants.Judge.STATUS_SYSTEM_ERROR.getStatus(),
|
||||
cid,
|
||||
uid,
|
||||
pid,
|
||||
null);
|
||||
log.error("网络错误---------------->获取不到提交ID");
|
||||
return;
|
||||
}
|
||||
|
||||
// 提交成功顺便更新状态为-->STATUS_JUDGING 判题中...
|
||||
judgeService.updateById(new Judge().setSubmitId(submitId).setStatus(Constants.Judge.STATUS_JUDGING.getStatus()));
|
||||
try {
|
||||
remoteJudgeResultDispatcher.sendTask(remoteJudge, username, submitId, uid, cid, pid,
|
||||
(Long) submitResult.get("runId"), (String) submitResult.get("token"),
|
||||
(HashMap<String, String>) submitResult.get("cookies"));
|
||||
} catch (Exception e) {
|
||||
log.error("调用redis消息发布异常,此次远程查询结果任务判为系统错误--------------->{}", e.getMessage());
|
||||
}
|
||||
judgeService.updateById(new Judge()
|
||||
.setSubmitId(submitId)
|
||||
.setStatus(Constants.Judge.STATUS_JUDGING.getStatus())
|
||||
.setJudger(name)
|
||||
);
|
||||
|
||||
// 调用获取远程判题结果
|
||||
remoteJudgeGetResult.sendTask(remoteJudge, username, submitId, uid, cid, pid,
|
||||
(Long) submitResult.get("runId"), (String) submitResult.get("token"),
|
||||
(HashMap<String, String>) submitResult.get("cookies"));
|
||||
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package top.hcode.hoj.remoteJudge.result;
|
||||
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.hcode.hoj.util.Constants;
|
||||
import top.hcode.hoj.util.RedisUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class RemoteJudgeResultDispatcher {
|
||||
|
||||
private final RedisUtils redisUtils;
|
||||
|
||||
public RemoteJudgeResultDispatcher(RedisUtils redisUtils) {
|
||||
this.redisUtils = redisUtils;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param remoteJudge 远程判题oj名字
|
||||
* @param resultSubmitId 远程判题提交后返回的提交ID
|
||||
* @param submitId 此次提交在HOJ的judge表中的主键,主要用于更新提交结果
|
||||
* @MethodName sendTask
|
||||
* @Description TODO
|
||||
* @Return
|
||||
* @Since 2021/2/12
|
||||
*/
|
||||
public void sendTask(String remoteJudge,String username, Long submitId, String uid,
|
||||
Long cid, Long pid, Long resultSubmitId, String token,
|
||||
HashMap<String, String> cookies) throws Exception {
|
||||
JSONObject task = new JSONObject();
|
||||
task.set("submitId", submitId);
|
||||
task.set("username", username);
|
||||
task.set("uid", uid);
|
||||
task.set("cid", cid);
|
||||
task.set("pid", pid);
|
||||
task.set("resultSubmitId", resultSubmitId);
|
||||
task.set("remoteJudge", remoteJudge);
|
||||
task.set("token", token);
|
||||
task.set("cookies", JSONUtil.toJsonStr(cookies));
|
||||
|
||||
// 提醒判题机有待查询结果题目
|
||||
redisUtils.sendMessage(Constants.RemoteJudge.JUDGE_RESULT_HANDLER.getName(), "New Result Query Added");
|
||||
// 队列中插入待判ID和题库名称
|
||||
redisUtils.lrPush(Constants.RemoteJudge.JUDGE_WAITING_RESULT_QUEUE.getName(), JSONUtil.toJsonStr(task));
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
package top.hcode.hoj.remoteJudge.result;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import top.hcode.hoj.pojo.entity.Judge;
|
||||
import top.hcode.hoj.remoteJudge.task.RemoteJudgeFactory;
|
||||
import top.hcode.hoj.remoteJudge.task.RemoteJudgeStrategy;
|
||||
import top.hcode.hoj.service.impl.JudgeServiceImpl;
|
||||
import top.hcode.hoj.util.Constants;
|
||||
import top.hcode.hoj.util.IpUtils;
|
||||
import top.hcode.hoj.util.RedisUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RemoteJudgeResultReceiver implements MessageListener {
|
||||
@Autowired
|
||||
private RedisUtils redisUtils;
|
||||
|
||||
@Autowired
|
||||
private RemoteJudgeResultDispatcher remoteJudgeResultDispatcher;
|
||||
|
||||
@Autowired
|
||||
private JudgeServiceImpl judgeService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
String source = (String) redisUtils.lrPop(Constants.RemoteJudge.JUDGE_WAITING_RESULT_QUEUE.getName());
|
||||
// 如果竞争不到查询队列,结束
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
JSONObject task = JSONUtil.parseObj(source);
|
||||
Long resultSubmitId = task.getLong("resultSubmitId");
|
||||
String username = task.getStr("username"); // HDU不需要,但是CF需要
|
||||
Long submitId = task.getLong("submitId");
|
||||
String uid = task.getStr("uid");
|
||||
Long cid = task.getLong("cid");
|
||||
Long pid = task.getLong("pid");
|
||||
String remoteJudge = task.getStr("remoteJudge");
|
||||
String token = task.getStr("token");
|
||||
HashMap<String, String> cookies = JsonObjectToHashMap(JSONUtil.parseObj(task.getStr("cookies")));
|
||||
RemoteJudgeStrategy remoteJudgeStrategy = RemoteJudgeFactory.selectJudge(remoteJudge);
|
||||
// 获取不到对应的题库或者题库写错了
|
||||
if (remoteJudgeStrategy == null) {
|
||||
log.error("暂不支持该{}题库---------------->请求失败", remoteJudge);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO 获取对应的result,修改到数据库
|
||||
try {
|
||||
Map<String, Object> result = remoteJudgeStrategy.result(resultSubmitId, username, token, cookies);
|
||||
Judge judge = new Judge();
|
||||
judge.setSubmitId(submitId);
|
||||
Integer status = (Integer) result.getOrDefault("status", Constants.Judge.STATUS_SYSTEM_ERROR.getStatus());
|
||||
// TODO 如果结果没出来,重新放入队列并更新状态为Waiting
|
||||
if (status.intValue() == Constants.Judge.STATUS_PENDING.getStatus() ||
|
||||
status.intValue() == Constants.Judge.STATUS_JUDGING.getStatus()) {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
remoteJudgeResultDispatcher.sendTask(remoteJudge, username, submitId, uid, cid, pid, resultSubmitId, token, cookies);
|
||||
} catch (Exception e) {
|
||||
log.error("重新查询结果任务出错------{}", e.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
Integer time = (Integer) result.getOrDefault("time", null);
|
||||
Integer memory = (Integer) result.getOrDefault("memory", null);
|
||||
String CEInfo = (String) result.getOrDefault("CEInfo", null);
|
||||
judge.setStatus(status)
|
||||
.setTime(time)
|
||||
.setMemory(memory);
|
||||
|
||||
// 写入当前远程判题OJ名字
|
||||
judge.setJudger(remoteJudge);
|
||||
|
||||
if (status.intValue() == Constants.Judge.STATUS_COMPILE_ERROR.getStatus()) {
|
||||
judge.setErrorMessage(CEInfo);
|
||||
} else if (status.intValue() == Constants.Judge.STATUS_SYSTEM_ERROR.getStatus()) {
|
||||
judge.setErrorMessage("There is something wrong with the " + remoteJudge + ", please try again later");
|
||||
}
|
||||
// 写回数据库
|
||||
judgeService.updateById(judge);
|
||||
|
||||
/**
|
||||
* @Description TODO 注意! 如果是OI题目肯定score得分,若不是则请传入null,该方法是为了更新关联表,保持数据一致!
|
||||
* @Since 2021/2/12
|
||||
**/
|
||||
judgeService.updateOtherTable(submitId, status, cid, uid, pid, null);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取结果出错------------>{}", e.getLocalizedMessage());
|
||||
// 更新此次提交状态为提交失败!
|
||||
UpdateWrapper<Judge> judgeUpdateWrapper = new UpdateWrapper<>();
|
||||
judgeUpdateWrapper.set("status", Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus())
|
||||
.eq("submit_id", submitId);
|
||||
judgeService.update(judgeUpdateWrapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static HashMap<String, String> JsonObjectToHashMap(JSONObject jsonObj) {
|
||||
HashMap<String, String> data = new HashMap<String, String>();
|
||||
for (String key : jsonObj.keySet()) {
|
||||
data.put(key, jsonObj.getStr(key));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package top.hcode.hoj.remoteJudge.submit;
|
||||
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.hcode.hoj.util.Constants;
|
||||
import top.hcode.hoj.util.RedisUtils;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class RemoteJudgeSubmitDispatcher {
|
||||
|
||||
@Autowired
|
||||
private RedisUtils redisUtils;
|
||||
|
||||
|
||||
public void sendTask(String username, String password, String remoteJudge, String remotePid, Long submitId,
|
||||
String uid, Long cid, Long pid, String language, String userCode) throws Exception {
|
||||
JSONObject task = new JSONObject();
|
||||
task.set("submitId", submitId);
|
||||
task.set("uid", uid);
|
||||
task.set("cid", cid);
|
||||
task.set("pid", pid);
|
||||
task.set("remoteJudge", remoteJudge);
|
||||
task.set("remotePid", remotePid);
|
||||
task.set("userCode", userCode);
|
||||
task.set("language", language);
|
||||
task.set("username", username);
|
||||
task.set("password", password);
|
||||
redisUtils.lrPush(Constants.RemoteJudge.JUDGE_WAITING_SUBMIT_QUEUE.getName(), JSONUtil.toJsonStr(task));
|
||||
redisUtils.sendMessage(Constants.RemoteJudge.JUDGE_SUBMIT_HANDLER.getName(), "New Problem Added");
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -62,25 +62,6 @@ public class Constants {
|
|||
|
||||
public enum RemoteJudge {
|
||||
|
||||
/**
|
||||
* 提交问题队列
|
||||
*/
|
||||
JUDGE_WAITING_SUBMIT_QUEUE("Remote Waiting Submit Queue"),
|
||||
|
||||
/**
|
||||
* 等待结果队列
|
||||
*/
|
||||
JUDGE_WAITING_RESULT_QUEUE("Remote Waiting Result Queue"),
|
||||
|
||||
/**
|
||||
* 提交消息通知
|
||||
*/
|
||||
JUDGE_SUBMIT_HANDLER("Remote Submit Handler"),
|
||||
|
||||
/**
|
||||
* 查询结果通知
|
||||
*/
|
||||
JUDGE_RESULT_HANDLER("Remote Result Handler"),
|
||||
|
||||
HDU_JUDGE("HDU"),
|
||||
|
||||
|
@ -121,20 +102,7 @@ public class Constants {
|
|||
return name;
|
||||
}
|
||||
}
|
||||
// public enum JudgeServer {
|
||||
// HDU_JUDGE("HDU", "hdu.com");
|
||||
// private String judgeName;
|
||||
// private final String judgeHost;
|
||||
//
|
||||
// JudgeServer(String judgeName, String judgeHost) {
|
||||
// this.judgeName = judgeName;
|
||||
// this.judgeHost = judgeHost;
|
||||
// }
|
||||
//
|
||||
// public final String getName() {
|
||||
// return judgeName;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
public enum JudgeDir {
|
||||
|
@ -162,7 +130,7 @@ public class Constants {
|
|||
public static List<String> defaultEnv = Arrays.asList(
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"LANG=en_US.UTF-8",
|
||||
"LANGUAGE=en_US:en", "LC_ALL=en_US.UTF-8");
|
||||
"LANGUAGE=en_US:en", "LC_ALL=en_US.UTF-8","HOME=/w");
|
||||
|
||||
public static List<String> python3Env = Arrays.asList("LANG=en_US.UTF-8",
|
||||
"LANGUAGE=en_US:en", "LC_ALL=en_US.UTF-8", "PYTHONIOENCODING=utf-8");
|
||||
|
@ -185,9 +153,9 @@ public class Constants {
|
|||
|
||||
PYTHON2("Python2", "main.py", "main.pyc", 3000L, 10000L, 128 * 1024 * 1024L, "/usr/bin/python -m py_compile ./{1}", defaultEnv),
|
||||
|
||||
PYTHON3("Python3", "main.py", "__pycache__/main.cpython-36.pyc", 3000L, 10000L, 128 * 1024 * 1024L, "/usr/bin/python3 -m py_compile ./{1}", defaultEnv),
|
||||
PYTHON3("Python3", "main.py", "__pycache__/main.cpython-37.pyc", 3000L, 10000L, 128 * 1024 * 1024L, "/usr/bin/python3 -m py_compile ./{1}", defaultEnv),
|
||||
|
||||
GOLANG("Golang", "main.go", "main", 3000L, 5000L, 1024 * 1024 * 1024L, "/usr/bin/go build -o {1} {2}", defaultEnv),
|
||||
GOLANG("Golang", "main.go", "main", 3000L, 5000L, 1024 * 1024 * 1024L, "/usr/bin/go build -o {2} {1}", defaultEnv),
|
||||
|
||||
SPJ_C("SPJ-C", "spj.c", "spj", 3000L, 5000L, 1024 * 1024 * 1024L, "/usr/bin/gcc -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c99 {1} -lm -o {2}", defaultEnv),
|
||||
|
||||
|
|
|
@ -31,13 +31,14 @@ spring:
|
|||
redis:
|
||||
host: ${hoj.redis.host}
|
||||
port: ${hoj.redis.port}
|
||||
timeout: 100000
|
||||
timeout: 60000
|
||||
jedis:
|
||||
pool:
|
||||
min-idle: 20 #连接池中的最小空闲连接
|
||||
min-idle: 40 #连接池中的最小空闲连接
|
||||
max-idle: 100 #连接池中的最大空闲连接
|
||||
max-active: 200 #连接池最大连接数(使用负值表示没有限制)
|
||||
max-wait: -1 #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
password: ${hoj.redis.password}
|
||||
|
||||
|
||||
mybatis-plus:
|
||||
|
@ -49,7 +50,8 @@ logging:
|
|||
level:
|
||||
com:
|
||||
alibaba:
|
||||
nacos: warn
|
||||
nacos: error
|
||||
root: error
|
||||
# 暴露监控
|
||||
management:
|
||||
endpoints:
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
hoj-judger:
|
||||
task-num: 0 #当前正在进行判题任务数
|
||||
max-task-num: 4 # -1表示最大并行任务数为cpu核心数*2
|
||||
ip: -1 # -1表示使用默认本地ipv4,若是部署其它服务器,务必使用公网ip
|
||||
port: 8088
|
||||
name: hoj-judger-1
|
||||
|
||||
|
||||
ip: 127.0.0.1 # -1表示使用默认本地ipv4,若是部署其它服务器,务必使用公网ip
|
||||
port: 8088 # 端口号
|
||||
name: hoj-judger-1 # 判题机名字
|
||||
nacos-url: 127.0.0.1:8848 # nacos地址
|
||||
server:
|
||||
port: ${hoj-judger.port}
|
||||
spring:
|
||||
|
@ -16,13 +15,13 @@ spring:
|
|||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 129.204.177.72:8848 #配置Nacos地址
|
||||
server-addr: ${hoj-judger.nacos-url} #配置Nacos地址
|
||||
config:
|
||||
server-addr: 129.204.177.72:8848 #Nacos 作为配置中心地址
|
||||
server-addr: ${hoj-judger.nacos-url} #Nacos 作为配置中心地址
|
||||
file-extension: yml #指定yaml格式的配置
|
||||
group: DEFAULT_GROUP # 指定分组
|
||||
#namespace:命名空间ID 默认为public
|
||||
url: http://129.204.177.72:8848
|
||||
url: http://${hoj-judger.nacos-url}
|
||||
|
||||
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.naces.config.file-extension}
|
||||
# hoj-judge-server-prod.yml
|
||||
|
|
|
@ -31,13 +31,14 @@ spring:
|
|||
redis:
|
||||
host: ${hoj.redis.host}
|
||||
port: ${hoj.redis.port}
|
||||
timeout: 100000
|
||||
timeout: 60000
|
||||
jedis:
|
||||
pool:
|
||||
min-idle: 20 #连接池中的最小空闲连接
|
||||
min-idle: 40 #连接池中的最小空闲连接
|
||||
max-idle: 100 #连接池中的最大空闲连接
|
||||
max-active: 200 #连接池最大连接数(使用负值表示没有限制)
|
||||
max-wait: -1 #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
password: ${hoj.redis.password}
|
||||
|
||||
|
||||
mybatis-plus:
|
||||
|
@ -49,7 +50,8 @@ logging:
|
|||
level:
|
||||
com:
|
||||
alibaba:
|
||||
nacos: warn
|
||||
nacos: error
|
||||
root: error
|
||||
# 暴露监控
|
||||
management:
|
||||
endpoints:
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
hoj-judger:
|
||||
task-num: 0 #当前正在进行判题任务数
|
||||
max-task-num: 4 # -1表示最大并行任务数为cpu核心数*2
|
||||
ip: -1 # -1表示使用默认本地ipv4,若是部署其它服务器,务必使用公网ip
|
||||
port: 8088
|
||||
name: hoj-judger-1
|
||||
|
||||
|
||||
ip: 127.0.0.1 # -1表示使用默认本地ipv4,若是部署其它服务器,务必使用公网ip
|
||||
port: 8088 # 端口号
|
||||
name: hoj-judger-1 # 判题机名字
|
||||
nacos-url: 127.0.0.1:8848 # nacos地址
|
||||
server:
|
||||
port: ${hoj-judger.port}
|
||||
spring:
|
||||
|
@ -16,13 +15,13 @@ spring:
|
|||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 129.204.177.72:8848 #配置Nacos地址
|
||||
server-addr: ${hoj-judger.nacos-url} #配置Nacos地址
|
||||
config:
|
||||
server-addr: 129.204.177.72:8848 #Nacos 作为配置中心地址
|
||||
server-addr: ${hoj-judger.nacos-url} #Nacos 作为配置中心地址
|
||||
file-extension: yml #指定yaml格式的配置
|
||||
group: DEFAULT_GROUP # 指定分组
|
||||
#namespace:命名空间ID 默认为public
|
||||
url: http://129.204.177.72:8848
|
||||
url: http://${hoj-judger.nacos-url}
|
||||
|
||||
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.naces.config.file-extension}
|
||||
# hoj-judge-server-prod.yml
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue