丰富前端操作,增加POJ的vjudge判题

This commit is contained in:
Himit_ZH 2021-06-25 11:14:24 +08:00
parent 270d10d96b
commit 8329f0d420
36 changed files with 686 additions and 86 deletions

View File

@ -85,6 +85,7 @@ docker ps # 查看当前运行的容器状态
| 2021-06-08 | 添加后台i18n,路由懒加载 | Himit_ZH | | 2021-06-08 | 添加后台i18n,路由懒加载 | Himit_ZH |
| 2021-06-12 | 完善比赛赛制,具体请看在线文档 | Himit_ZH | | 2021-06-12 | 完善比赛赛制,具体请看在线文档 | Himit_ZH |
| 2021-06-14 | 完善后台管理员权限控制恢复CF的vjudge判题 | Himit_ZH | | 2021-06-14 | 完善后台管理员权限控制恢复CF的vjudge判题 | Himit_ZH |
| 2021-06-25 | 丰富前端操作增加POJ的vjudge判题 | Himit_ZH |

View File

@ -142,6 +142,7 @@ problem表
| open_case_result | boolean | | 是否默认开启该题目的测试样例结果查看 | | open_case_result | boolean | | 是否默认开启该题目的测试样例结果查看 |
| caseVersion | String | | 题目测试数据的版本号 | | caseVersion | String | | 题目测试数据的版本号 |
| is_upload_case | boolean | | 是否是上传zip评测数据的 | | is_upload_case | boolean | | 是否是上传zip评测数据的 |
| modified_user | String | | 最新修改题目的用户 |
| gmt_create | datetime | | 创建时间 | | gmt_create | datetime | | 创建时间 |
| gmt_modified | datetime | | 修改时间 | | gmt_modified | datetime | | 修改时间 |
@ -529,6 +530,7 @@ discussion表
| view_num | int | | 浏览数量 | | view_num | int | | 浏览数量 |
| like_num | int | | 点赞数量 | | like_num | int | | 点赞数量 |
| top_priority | boolean | | 优先级,是否置顶 | | top_priority | boolean | | 优先级,是否置顶 |
| comment_num | int | | 评论数量 |
| status | int | | 是否封禁或逻辑删除该讨论 | | status | int | | 是否封禁或逻辑删除该讨论 |
| gmt_create | datetime | | 创建时间 | | gmt_create | datetime | | 创建时间 |
| gmt_modified | datetime | | 修改时间 | | gmt_modified | datetime | | 修改时间 |

View File

@ -126,6 +126,12 @@
<version>5.3.3</version> <version>5.3.3</version>
</dependency> </dependency>
<dependency>
<groupId>com.vdurmont</groupId>
<artifactId>emoji-java</artifactId>
<version>4.0.0</version>
</dependency>
<!--redis整合--> <!--redis整合-->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -105,6 +105,12 @@ public class StartupRunner implements CommandLineRunner {
@Value("${CF_ACCOUNT_PASSWORD_LIST:}") @Value("${CF_ACCOUNT_PASSWORD_LIST:}")
private List<String> cfPasswordList; private List<String> cfPasswordList;
@Value("${POJ_ACCOUNT_USERNAME_LIST:}")
private List<String> pojUsernameList;
@Value("${POJ_ACCOUNT_PASSWORD_LIST:}")
private List<String> pojPasswordList;
@Value("${spring.profiles.active}") @Value("${spring.profiles.active}")
private String profile; private String profile;
@ -151,6 +157,10 @@ public class StartupRunner implements CommandLineRunner {
configVo.setCfUsernameList(cfUsernameList); configVo.setCfUsernameList(cfUsernameList);
configVo.setCfPasswordList(cfPasswordList); configVo.setCfPasswordList(cfPasswordList);
configVo.setPojUsernameList(pojUsernameList);
configVo.setPojPasswordList(pojPasswordList);
configService.sendNewConfigToNacos(); configService.sendNewConfigToNacos();
if (openRemoteJudge.equals("true")) { if (openRemoteJudge.equals("true")) {
@ -203,6 +213,22 @@ public class StartupRunner implements CommandLineRunner {
log.error("Codeforces账号添加失败------------>{}", "请检查配置文件,然后重新启动!"); log.error("Codeforces账号添加失败------------>{}", "请检查配置文件,然后重新启动!");
} }
} }
List<RemoteJudgeAccount> pojRemoteAccountList = new LinkedList<>();
for (int i = 0; i < pojUsernameList.size(); i++) {
pojRemoteAccountList.add(new RemoteJudgeAccount()
.setUsername(pojUsernameList.get(i))
.setPassword(pojPasswordList.get(i))
.setStatus(true)
.setVersion(0L)
.setOj("POJ"));
}
if (pojRemoteAccountList.size()>0) {
boolean addPOJOk = remoteJudgeAccountService.saveOrUpdateBatch(pojRemoteAccountList);
if (!addPOJOk) {
log.error("POJ账号添加失败------------>{}", "请检查配置文件,然后重新启动!");
}
}
} }
} }

View File

@ -326,7 +326,11 @@ public class AdminContestController {
if (problem != null && problem.getId().longValue() != problemDto.getProblem().getId()) { if (problem != null && problem.getId().longValue() != problemDto.getProblem().getId()) {
return CommonResult.errorResponse("当前的Problem ID 已被使用,请重新更换新的!", CommonResult.STATUS_FAIL); return CommonResult.errorResponse("当前的Problem ID 已被使用,请重新更换新的!", CommonResult.STATUS_FAIL);
} }
// 获取当前登录的用户
HttpSession session = request.getSession();
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
// 记录修改题目的用户
problemDto.getProblem().setModifiedUser(userRolesVo.getUsername());
boolean result = problemService.adminUpdateProblem(problemDto); boolean result = problemService.adminUpdateProblem(problemDto);
if (result) { // 更新成功 if (result) { // 更新成功
return CommonResult.successResponse(null, "修改成功!"); return CommonResult.successResponse(null, "修改成功!");

View File

@ -144,7 +144,7 @@ public class AdminProblemController {
@RequiresAuthentication @RequiresAuthentication
@RequiresRoles(value = {"root", "admin", "problem_admin"}, logical = Logical.OR) @RequiresRoles(value = {"root", "admin", "problem_admin"}, logical = Logical.OR)
@Transactional @Transactional
public CommonResult updateProblem(@RequestBody ProblemDto problemDto) { public CommonResult updateProblem(@RequestBody ProblemDto problemDto, HttpServletRequest request) {
QueryWrapper<Problem> queryWrapper = new QueryWrapper<>(); QueryWrapper<Problem> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("problem_id", problemDto.getProblem().getProblemId().toUpperCase()); queryWrapper.eq("problem_id", problemDto.getProblem().getProblemId().toUpperCase());
@ -154,6 +154,11 @@ public class AdminProblemController {
if (problem != null && problem.getId().longValue() != problemDto.getProblem().getId()) { if (problem != null && problem.getId().longValue() != problemDto.getProblem().getId()) {
return CommonResult.errorResponse("当前的Problem ID 已被使用,请重新更换新的!", CommonResult.STATUS_FAIL); return CommonResult.errorResponse("当前的Problem ID 已被使用,请重新更换新的!", CommonResult.STATUS_FAIL);
} }
// 获取当前登录的用户
HttpSession session = request.getSession();
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
// 记录修改题目的用户
problemDto.getProblem().setModifiedUser(userRolesVo.getUsername());
boolean result = problemService.adminUpdateProblem(problemDto); boolean result = problemService.adminUpdateProblem(problemDto);
if (result) { // 更新成功 if (result) { // 更新成功

View File

@ -12,13 +12,16 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import top.hcode.hoj.common.result.CommonResult; import top.hcode.hoj.common.result.CommonResult;
import top.hcode.hoj.pojo.dto.ReplyDto;
import top.hcode.hoj.pojo.entity.Comment; import top.hcode.hoj.pojo.entity.Comment;
import top.hcode.hoj.pojo.entity.CommentLike; import top.hcode.hoj.pojo.entity.CommentLike;
import top.hcode.hoj.pojo.entity.Discussion;
import top.hcode.hoj.pojo.entity.Reply; import top.hcode.hoj.pojo.entity.Reply;
import top.hcode.hoj.pojo.vo.CommentsVo; import top.hcode.hoj.pojo.vo.CommentsVo;
import top.hcode.hoj.pojo.vo.UserRolesVo; import top.hcode.hoj.pojo.vo.UserRolesVo;
import top.hcode.hoj.service.impl.CommentLikeServiceImpl; import top.hcode.hoj.service.impl.CommentLikeServiceImpl;
import top.hcode.hoj.service.impl.CommentServiceImpl; import top.hcode.hoj.service.impl.CommentServiceImpl;
import top.hcode.hoj.service.impl.DiscussionServiceImpl;
import top.hcode.hoj.service.impl.ReplyServiceImpl; import top.hcode.hoj.service.impl.ReplyServiceImpl;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -45,6 +48,9 @@ public class CommentController {
@Autowired @Autowired
private ReplyServiceImpl replyService; private ReplyServiceImpl replyService;
@Autowired
private DiscussionServiceImpl discussionService;
@GetMapping("/comments") @GetMapping("/comments")
public CommonResult getComments(@RequestParam(value = "cid", required = false) Long cid, public CommonResult getComments(@RequestParam(value = "cid", required = false) Long cid,
@ -126,6 +132,13 @@ public class CommentController {
commentsVo.setLikeNum(0); commentsVo.setLikeNum(0);
commentsVo.setGmtCreate(comment.getGmtCreate()); commentsVo.setGmtCreate(comment.getGmtCreate());
commentsVo.setReplyList(new LinkedList<>()); commentsVo.setReplyList(new LinkedList<>());
// 如果是讨论区的回复发布成功需要添加统计该讨论的回复数
if (comment.getDid() != null) {
UpdateWrapper<Discussion> discussionUpdateWrapper = new UpdateWrapper<>();
discussionUpdateWrapper.eq("id", comment.getDid())
.setSql("comment_num=comment_num+1");
discussionService.update(discussionUpdateWrapper);
}
return CommonResult.successResponse(commentsVo, "评论成功"); return CommonResult.successResponse(commentsVo, "评论成功");
} else { } else {
return CommonResult.errorResponse("评论失败,请重新尝试!"); return CommonResult.errorResponse("评论失败,请重新尝试!");
@ -151,6 +164,13 @@ public class CommentController {
replyService.remove(new UpdateWrapper<Reply>().eq("comment_id", comment.getId())); replyService.remove(new UpdateWrapper<Reply>().eq("comment_id", comment.getId()));
if (isDeleteComment) { if (isDeleteComment) {
// 如果是讨论区的回复删除成功需要减少统计该讨论的回复数
if (comment.getDid() != null) {
UpdateWrapper<Discussion> discussionUpdateWrapper = new UpdateWrapper<>();
discussionUpdateWrapper.eq("id", comment.getDid())
.setSql("comment_num=comment_num-1");
discussionService.update(discussionUpdateWrapper);
}
return CommonResult.successResponse(null, "删除成功"); return CommonResult.successResponse(null, "删除成功");
} else { } else {
return CommonResult.errorResponse("删除失败,请重新尝试"); return CommonResult.errorResponse("删除失败,请重新尝试");
@ -222,11 +242,11 @@ public class CommentController {
@PostMapping("/reply") @PostMapping("/reply")
@RequiresPermissions("reply_add") @RequiresPermissions("reply_add")
@RequiresAuthentication @RequiresAuthentication
public CommonResult addReply(@RequestBody Reply reply, HttpServletRequest request) { public CommonResult addReply(@RequestBody ReplyDto replyDto, HttpServletRequest request) {
// 获取当前登录的用户 // 获取当前登录的用户
HttpSession session = request.getSession(); HttpSession session = request.getSession();
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo"); UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
Reply reply = replyDto.getReply();
reply.setFromAvatar(userRolesVo.getAvatar()) reply.setFromAvatar(userRolesVo.getAvatar())
.setFromName(userRolesVo.getUsername()) .setFromName(userRolesVo.getUsername())
.setFromUid(userRolesVo.getUid()); .setFromUid(userRolesVo.getUid());
@ -244,6 +264,13 @@ public class CommentController {
boolean isOk = replyService.saveOrUpdate(reply); boolean isOk = replyService.saveOrUpdate(reply);
if (isOk) { if (isOk) {
// 如果是讨论区的回复发布成功需要增加统计该讨论的回复数
if (replyDto.getDid() != null) {
UpdateWrapper<Discussion> discussionUpdateWrapper = new UpdateWrapper<>();
discussionUpdateWrapper.eq("id", replyDto.getDid())
.setSql("comment_num=comment_num+1");
discussionService.update(discussionUpdateWrapper);
}
return CommonResult.successResponse(reply, "评论成功"); return CommonResult.successResponse(reply, "评论成功");
} else { } else {
return CommonResult.errorResponse("评论失败,请重新尝试!"); return CommonResult.errorResponse("评论失败,请重新尝试!");
@ -252,19 +279,24 @@ public class CommentController {
@DeleteMapping("/reply") @DeleteMapping("/reply")
@RequiresAuthentication @RequiresAuthentication
public CommonResult deleteReply(@RequestBody Reply reply, HttpServletRequest request) { public CommonResult deleteReply(@RequestBody ReplyDto replyDto, HttpServletRequest request) {
// 获取当前登录的用户 // 获取当前登录的用户
HttpSession session = request.getSession(); HttpSession session = request.getSession();
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo"); UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
Reply reply = replyDto.getReply();
// 如果不是评论本人 或者不是管理员 无权限删除该评论 // 如果不是评论本人 或者不是管理员 无权限删除该评论
if (reply.getFromUid().equals(userRolesVo.getUid()) || SecurityUtils.getSubject().hasRole("root") if (reply.getFromUid().equals(userRolesVo.getUid()) || SecurityUtils.getSubject().hasRole("root")
|| SecurityUtils.getSubject().hasRole("admin") || SecurityUtils.getSubject().hasRole("problem_admin")) { || SecurityUtils.getSubject().hasRole("admin") || SecurityUtils.getSubject().hasRole("problem_admin")) {
// 删除该数据 // 删除该数据
boolean isOk = replyService.removeById(reply.getId()); boolean isOk = replyService.removeById(reply.getId());
if (isOk) { if (isOk) {
// 如果是讨论区的回复删除成功需要减少统计该讨论的回复数
if (replyDto.getDid() != null) {
UpdateWrapper<Discussion> discussionUpdateWrapper = new UpdateWrapper<>();
discussionUpdateWrapper.eq("id", replyDto.getDid())
.setSql("comment_num=comment_num-1");
discussionService.update(discussionUpdateWrapper);
}
return CommonResult.successResponse(null, "删除成功"); return CommonResult.successResponse(null, "删除成功");
} else { } else {
return CommonResult.errorResponse("删除失败,请重新尝试"); return CommonResult.errorResponse("删除失败,请重新尝试");

View File

@ -0,0 +1,58 @@
package top.hcode.hoj.crawler.problem;
import cn.hutool.core.util.ReUtil;
import org.jsoup.Connection;
import org.jsoup.nodes.Document;
import org.springframework.util.Assert;
import top.hcode.hoj.pojo.entity.Problem;
import top.hcode.hoj.utils.JsoupUtils;
/**
* @Author: Himit_ZH
* @Date: 2021/6/24 23:27
* @Description:
*/
public class POJProblemStrategy extends ProblemStrategy {
public static final String JUDGE_NAME = "POJ";
public static final String HOST = "http://poj.org";
public static final String PROBLEM_URL = "/problem?id=%s";
@Override
public RemoteProblemInfo getProblemInfo(String problemId, String author) throws Exception {
// 验证题号是否符合规范
Assert.isTrue(problemId.matches("[1-9]\\d*"), "POJ题号格式错误");
Problem info = new Problem();
String url = HOST + String.format(PROBLEM_URL, problemId);
Connection connection = JsoupUtils.getConnectionFromUrl(url, null, null);
Document document = JsoupUtils.getDocument(connection, null);
String html = document.html();
html = html.replaceAll("<br>", "\n");
info.setProblemId(JUDGE_NAME + "-" + problemId);
info.setTitle(ReUtil.get("<title>\\d{3,} -- ([\\s\\S]*?)</title>", html, 1).trim());
info.setTimeLimit(Integer.parseInt(ReUtil.get("<b>Time Limit:</b> (\\d{3,})MS</td>", html, 1)));
info.setMemoryLimit(Integer.parseInt(ReUtil.get("<b>Memory Limit:</b> (\\d{2,})K</td>", html, 1)) / 1024);
info.setDescription(ReUtil.get("<p class=\"pst\">Description</p><div class=.*?>([\\s\\S]*?)</div><p class=\"pst\">", html, 1)
.replaceAll("src=\"../../", "src=\"" + HOST + "/"));
info.setInput(ReUtil.get("<p class=\"pst\">Input</p><div class=.*?>([\\s\\S]*?)</div><p class=\"pst\">", html, 1));
info.setOutput(ReUtil.get("<p class=\"pst\">Output</p><div class=.*?>([\\s\\S]*?)</div><p class=\"pst\">", html, 1));
StringBuilder sb = new StringBuilder("<input>");
sb.append(ReUtil.get("<p class=\"pst\">Sample Input</p><pre class=.*?>([\\s\\S]*?)</pre><p class=\"pst\">", html, 1));
sb.append("</input><output>");
sb.append(ReUtil.get("<p class=\"pst\">Sample Output</p><pre class=.*?>([\\s\\S]*?)</pre><p class=\"pst\">", html, 1))
.append("</output>");
info.setExamples(sb.toString());
info.setHint(ReUtil.get("<p class=.*?>Hint</p><div class=.*?>([\\s\\S]*?)</div><p class=\"pst\">", html, 1));
info.setIsRemote(true);
info.setSource(String.format("<a style='color:#1A5CC8' href='http://poj.org/problem?id=%s'>%s</a>", problemId, JUDGE_NAME + "-" + problemId));
info.setType(0)
.setAuth(1)
.setAuthor(author)
.setOpenCaseResult(false)
.setIsRemoveEndBlank(false)
.setDifficulty(1); // 默认为简单
return new RemoteProblemInfo().setProblem(info).setTagList(null);
}
}

View File

@ -0,0 +1,19 @@
package top.hcode.hoj.pojo.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import top.hcode.hoj.pojo.entity.Reply;
/**
* @Author: Himit_ZH
* @Date: 2021/6/24 17:00
* @Description:
*/
@Data
@Accessors(chain = true)
public class ReplyDto {
private Reply reply;
private Long did;
}

View File

@ -119,4 +119,10 @@ public class ConfigVo {
@Value("${hoj.cf.account.password:}") @Value("${hoj.cf.account.password:}")
private List<String> cfPasswordList; private List<String> cfPasswordList;
@Value("${hoj.poj.account.username:}")
private List<String> pojUsernameList;
@Value("${hoj.poj.account.password:}")
private List<String> pojPasswordList;
} }

View File

@ -19,10 +19,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils; import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import top.hcode.hoj.crawler.problem.CFProblemStrategy; import top.hcode.hoj.crawler.problem.*;
import top.hcode.hoj.crawler.problem.HDUProblemStrategy;
import top.hcode.hoj.crawler.problem.ProblemContext;
import top.hcode.hoj.crawler.problem.ProblemStrategy;
import top.hcode.hoj.pojo.dto.ProblemDto; import top.hcode.hoj.pojo.dto.ProblemDto;
import top.hcode.hoj.pojo.entity.*; import top.hcode.hoj.pojo.entity.*;
import top.hcode.hoj.pojo.vo.ImportProblemVo; import top.hcode.hoj.pojo.vo.ImportProblemVo;
@ -544,6 +541,9 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
case "CF": case "CF":
problemStrategy = new CFProblemStrategy(); problemStrategy = new CFProblemStrategy();
break; break;
case "POJ":
problemStrategy = new POJProblemStrategy();
break;
default: default:
throw new Exception("未知的OJ的名字暂时不支持"); throw new Exception("未知的OJ的名字暂时不支持");
} }

View File

@ -72,7 +72,11 @@ public class ConfigUtils {
" cf:\n" + " cf:\n" +
" account:\n" + " account:\n" +
" username: " + listToStr(configVo.getCfUsernameList()) + "\n" + " username: " + listToStr(configVo.getCfUsernameList()) + "\n" +
" password: " + listToStr(configVo.getCfPasswordList()); " password: " + listToStr(configVo.getCfPasswordList()) + "\n" +
" poj:\n" +
" account:\n" +
" username: " + listToStr(configVo.getPojUsernameList()) + "\n" +
" password: " + listToStr(configVo.getPojPasswordList());
} }
private String listToStr(List<String> list) { private String listToStr(List<String> list) {

View File

@ -58,7 +58,8 @@ public class Constants {
public enum RemoteOJ { public enum RemoteOJ {
HDU("HDU"), HDU("HDU"),
CODEFORCES("CF"); CODEFORCES("CF"),
POJ("POJ");
private final String name; private final String name;

View File

@ -32,8 +32,7 @@ public class RemoteJudgeGetResult {
@Transactional @Transactional
public void sendTask(String remoteJudge, String username, Long submitId, String uid, public void sendTask(String remoteJudge, String username, Long submitId, String uid,
Long cid, Long pid, Long resultSubmitId, String token, Long cid, Long pid, Long resultSubmitId, String cookies) {
HashMap<String, String> cookies) {
RemoteJudgeStrategy remoteJudgeStrategy = RemoteJudgeFactory.selectJudge(remoteJudge); RemoteJudgeStrategy remoteJudgeStrategy = RemoteJudgeFactory.selectJudge(remoteJudge);
@ -47,7 +46,7 @@ public class RemoteJudgeGetResult {
public void run() { public void run() {
count.getAndIncrement(); count.getAndIncrement();
try { try {
Map<String, Object> result = remoteJudgeStrategy.result(resultSubmitId, username, token, cookies); Map<String, Object> result = remoteJudgeStrategy.result(resultSubmitId, username, cookies);
Integer status = (Integer) result.getOrDefault("status", Constants.Judge.STATUS_SYSTEM_ERROR.getStatus()); Integer status = (Integer) result.getOrDefault("status", Constants.Judge.STATUS_SYSTEM_ERROR.getStatus());
if (status.intValue() != Constants.Judge.STATUS_PENDING.getStatus() && if (status.intValue() != Constants.Judge.STATUS_PENDING.getStatus() &&
status.intValue() != Constants.Judge.STATUS_JUDGING.getStatus()) { status.intValue() != Constants.Judge.STATUS_JUDGING.getStatus()) {

View File

@ -87,17 +87,16 @@ public class RemoteJudgeToSubmit {
return; return;
} }
// 提交成功顺便更新状态为-->STATUS_JUDGING 判题... // 提交成功顺便更新状态为-->STATUS_PENDING 等待...
judgeService.updateById(new Judge() judgeService.updateById(new Judge()
.setSubmitId(submitId) .setSubmitId(submitId)
.setStatus(Constants.Judge.STATUS_JUDGING.getStatus()) .setStatus(Constants.Judge.STATUS_PENDING.getStatus())
.setJudger(name) .setJudger(name)
); );
// 调用获取远程判题结果 // 调用获取远程判题结果
remoteJudgeGetResult.sendTask(remoteJudge, username, submitId, uid, cid, pid, remoteJudgeGetResult.sendTask(remoteJudge, username, submitId, uid, cid, pid,
(Long) submitResult.get("runId"), (String) submitResult.get("token"), (Long) submitResult.get("runId"), (String) submitResult.get("cookies"));
(HashMap<String, String>) submitResult.get("cookies"));
} }
} }

View File

@ -14,7 +14,6 @@ import com.gargoylesoftware.htmlunit.SilentCssErrorHandler;
import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.*; import com.gargoylesoftware.htmlunit.html.*;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import top.hcode.hoj.remoteJudge.task.RemoteJudgeStrategy; import top.hcode.hoj.remoteJudge.task.RemoteJudgeStrategy;
import top.hcode.hoj.util.Constants; import top.hcode.hoj.util.Constants;
@ -73,16 +72,15 @@ public class CodeForcesJudge implements RemoteJudgeStrategy {
// 获取提交的题目id // 获取提交的题目id
Long maxRunId = getMaxRunId(null, username, problemId); Long maxRunId = getMaxRunId(username, problemId);
return MapUtil.builder(new HashMap<String, Object>()) return MapUtil.builder(new HashMap<String, Object>())
.put("runId", maxRunId) .put("runId", maxRunId)
.put("token", "") .put("cookies", null)
.put("cookies", new HashMap<String, String>())
.map(); .map();
} }
private Long getMaxRunId(Connection connection, String username, String problemId) throws InterruptedException { private Long getMaxRunId(String username, String problemId) throws InterruptedException {
int retryNum = 0; int retryNum = 0;
String url = String.format(SUBMISSION_RESULT_URL, username); String url = String.format(SUBMISSION_RESULT_URL, username);
HttpRequest httpRequest = HttpUtil.createGet(HOST + url); HttpRequest httpRequest = HttpUtil.createGet(HOST + url);
@ -118,7 +116,7 @@ public class CodeForcesJudge implements RemoteJudgeStrategy {
} }
@Override @Override
public Map<String, Object> result(Long submitId, String username, String token, HashMap<String, String> cookies) throws Exception { public Map<String, Object> result(Long submitId, String username, String cookies) {
String url = HOST + String.format(SUBMISSION_RESULT_URL, username); String url = HOST + String.format(SUBMISSION_RESULT_URL, username);

View File

@ -2,15 +2,13 @@ package top.hcode.hoj.remoteJudge.task.Impl;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.ReUtil;
import cn.hutool.http.HtmlUtil; import cn.hutool.http.*;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.jsoup.helper.Validate; import org.jsoup.helper.Validate;
import org.jsoup.nodes.Document;
import top.hcode.hoj.pojo.entity.Problem;
import top.hcode.hoj.remoteJudge.task.RemoteJudgeStrategy; import top.hcode.hoj.remoteJudge.task.RemoteJudgeStrategy;
import top.hcode.hoj.util.Constants; import top.hcode.hoj.util.Constants;
import top.hcode.hoj.util.JsoupUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -31,6 +29,7 @@ public class HduJudge implements RemoteJudgeStrategy {
.put("Host", "acm.hdu.edu.cn") .put("Host", "acm.hdu.edu.cn")
.put("origin", "http://acm.hdu.edu.cn") .put("origin", "http://acm.hdu.edu.cn")
.put("referer", "http://acm.hdu.edu.cn") .put("referer", "http://acm.hdu.edu.cn")
.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36")
.map(); .map();
/** /**
@ -45,39 +44,46 @@ public class HduJudge implements RemoteJudgeStrategy {
return null; return null;
} }
Map<String, Object> loginUtils = getLoginUtils(username, password); Map<String, Object> loginUtils = getLoginUtils(username, password);
Map<String, String> loginCookie = (Map<String, String>) loginUtils.get("cookie"); String cookies = (String) loginUtils.get("cookie");
Connection connection = JsoupUtils.getConnectionFromUrl(HOST + SUBMIT_URL, headers, loginCookie);
Connection.Response response = JsoupUtils.postResponse(connection, MapUtil HttpRequest request = HttpUtil.createPost(HOST + SUBMIT_URL)
.builder(new HashMap<String, String>()) .addHeaders(headers)
.cookie(cookies);
HttpResponse response = request.form(MapUtil
.builder(new HashMap<String, Object>())
.put("check", "0") .put("check", "0")
.put("language", getLanguage(language)) .put("language", getLanguage(language))
.put("problemid", problemId) .put("problemid", problemId)
.put("usercode", userCode) .put("usercode", userCode)
.map()); .map())
if (response.statusCode() != 200) { .execute();
if (response.getStatus() != 200) {
log.error("进行题目提交时发生错误:提交题目失败," + HduJudge.class.getName() + ",题号:" + problemId); log.error("进行题目提交时发生错误:提交题目失败," + HduJudge.class.getName() + ",题号:" + problemId);
return null; return null;
} }
// 下面的请求都是GET
request.setMethod(Method.GET);
// 获取提交的题目id // 获取提交的题目id
Long maxRunId = getMaxRunId(connection, username, problemId); Long maxRunId = getMaxRunId(request, username, problemId);
if (maxRunId == -1L) { // 等待2s再次查询如果还是失败则表明提交失败了 if (maxRunId == -1L) { // 等待2s再次查询如果还是失败则表明提交失败了
TimeUnit.SECONDS.sleep(2); TimeUnit.SECONDS.sleep(2);
maxRunId = getMaxRunId(connection, username, problemId); maxRunId = getMaxRunId(request, username, problemId);
} }
return MapUtil.builder(new HashMap<String, Object>()) return MapUtil.builder(new HashMap<String, Object>())
.put("token", null) .put("cookies", cookies)
.put("cookies", loginCookie)
.put("runId", maxRunId) .put("runId", maxRunId)
.map(); .map();
} }
@Override @Override
public Map<String, Object> result(Long submitId, String username, String token, HashMap<String, String> cookies) throws Exception { public Map<String, Object> result(Long submitId, String username, String cookies) throws Exception {
String url = HOST + String.format(QUERY_URL, submitId); String url = HOST + String.format(QUERY_URL, submitId);
Connection connection = JsoupUtils.getConnectionFromUrl(url, headers, null); HttpRequest request = HttpUtil.createGet(url)
Connection.Response response = JsoupUtils.getResponse(connection, null); .cookie(cookies)
.addHeaders(headers);
HttpResponse response = request.execute();
// 1提交时间 2结果 3执行时间 4执行空间 5代码长度 // 1提交时间 2结果 3执行时间 4执行空间 5代码长度
// 一般情况下 代码长度和提交时间不需要想要也行自行添加 // 一般情况下 代码长度和提交时间不需要想要也行自行添加
Pattern pattern = Pattern.compile(">" + submitId + "</td><td>([\\s\\S]*?)</td><td>([\\s\\S]*?)</td><td>[\\s\\S]*?</td><td>(\\d*?)MS</td><td>(\\d*?)K</td><td>(\\d*?)B</td>"); Pattern pattern = Pattern.compile(">" + submitId + "</td><td>([\\s\\S]*?)</td><td>([\\s\\S]*?)</td><td>[\\s\\S]*?</td><td>(\\d*?)MS</td><td>(\\d*?)K</td><td>(\\d*?)B</td>");
@ -100,9 +106,9 @@ public class HduJudge implements RemoteJudgeStrategy {
result.put("memory", Integer.parseInt(executionMemory)); result.put("memory", Integer.parseInt(executionMemory));
// 如果CE了则还需要获得错误信息 // 如果CE了则还需要获得错误信息
if (statusType == Constants.Judge.STATUS_COMPILE_ERROR) { if (statusType == Constants.Judge.STATUS_COMPILE_ERROR) {
connection.url(HOST + String.format(ERROR_URL, submitId)); request.setUrl(HOST + String.format(ERROR_URL, submitId));
response = JsoupUtils.getResponse(connection, null); String CEHtml = request.execute().body();
String compilationErrorInfo = ReUtil.get("<pre>([\\s\\S]*?)</pre>", response.body(), 1); String compilationErrorInfo = ReUtil.get("<pre>([\\s\\S]*?)</pre>", CEHtml, 1);
result.put("CEInfo", HtmlUtil.unescape(compilationErrorInfo)); result.put("CEInfo", HtmlUtil.unescape(compilationErrorInfo));
} }
return result; return result;
@ -110,14 +116,16 @@ public class HduJudge implements RemoteJudgeStrategy {
@Override @Override
public Map<String, Object> getLoginUtils(String username, String password) throws Exception { public Map<String, Object> getLoginUtils(String username, String password){
Connection connection = JsoupUtils.getConnectionFromUrl(HOST + LOGIN_URL, headers, null);
Connection.Response response = JsoupUtils.postResponse(connection, MapUtil HttpRequest request = HttpUtil.createPost(HOST + LOGIN_URL).addHeaders(headers);
.builder(new HashMap<String, String>()) HttpResponse response = request.form(MapUtil
.builder(new HashMap<String, Object>())
.put("username", username) .put("username", username)
.put("login", "Sign In") .put("login", "Sign In")
.put("userpass", password).map()); .put("userpass", password).map())
return MapUtil.builder(new HashMap<String, Object>()).put("cookie", response.cookies()).map(); .execute();
return MapUtil.builder(new HashMap<String, Object>()).put("cookie", response.getCookieStr()).map();
} }
@Override @Override
@ -144,10 +152,9 @@ public class HduJudge implements RemoteJudgeStrategy {
} }
public Long getMaxRunId(Connection connection, String userName, String problemId) throws Exception { public Long getMaxRunId(HttpRequest request, String userName, String problemId){
String url = String.format(STATUS_URL, userName, problemId); String url = String.format(STATUS_URL, userName, problemId);
connection.url(HOST + url); HttpResponse response = request.setUrl(url).execute();
Connection.Response response = JsoupUtils.getResponse(connection, null);
Matcher matcher = Pattern.compile("<td height=22px>(\\d+)").matcher(response.body()); Matcher matcher = Pattern.compile("<td height=22px>(\\d+)").matcher(response.body());
return matcher.find() ? Long.parseLong(matcher.group(1)) : -1L; return matcher.find() ? Long.parseLong(matcher.group(1)) : -1L;
} }

View File

@ -0,0 +1,178 @@
package top.hcode.hoj.remoteJudge.task.Impl;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.http.*;
import lombok.extern.slf4j.Slf4j;
import top.hcode.hoj.remoteJudge.task.RemoteJudgeStrategy;
import top.hcode.hoj.util.Constants;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author: Himit_ZH
* @Date: 2021/6/24 21:19
* @Description:
*/
@Slf4j(topic = "hoj")
public class POJJudge implements RemoteJudgeStrategy {
public static final String HOST = "http://poj.org";
public static final String LOGIN_URL = "/login";
public static final String SUBMIT_URL = "/submit";
public static final String STATUS_URL = "/status?user_id=%s&problem_id=%s";
public static final String QUERY_URL = "/showsource?solution_id=%s";
public static final String ERROR_URL = "/showcompileinfo?solution_id=%s";
public static Map<String, String> headers = MapUtil
.builder(new HashMap<String, String>())
.put("Host", "poj.org")
.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36")
.map();
/**
* @param problemId 提交的题目id
* @param language
* @param userCode 用户代码
* @return
*/
@Override
public Map<String, Object> submit(String username, String password, String problemId, String language, String userCode) throws Exception {
if (problemId == null || userCode == null) {
return null;
}
Map<String, Object> loginUtils = getLoginUtils(username, password);
String cookies = (String) loginUtils.get("cookie");
HttpRequest request = HttpUtil.createPost(HOST + SUBMIT_URL)
.addHeaders(headers)
.cookie(cookies);
HttpResponse response = request.form(MapUtil.builder(new HashMap<String, Object>())
.put("language", getLanguage(language))
.put("submit", "Submit")
.put("problem_id", problemId)
.put("source", Base64.encode(userCode))
.put("encoded", 1).map())
.execute();
if (response.getStatus() != 302) {
log.error("进行题目提交时发生错误:提交题目失败," + POJJudge.class.getName() + ",题号:" + problemId);
return null;
}
// 下面的请求都是GET
request.setMethod(Method.GET);
// 获取提交的题目id
Long maxRunId = getMaxRunId(request, username, problemId);
if (maxRunId == -1L) { // 等待2s再次查询如果还是失败则表明提交失败了
TimeUnit.SECONDS.sleep(2);
maxRunId = getMaxRunId(request, username, problemId);
}
return MapUtil.builder(new HashMap<String, Object>())
.put("cookies", cookies)
.put("runId", maxRunId)
.map();
}
@Override
public Map<String, Object> result(Long submitId, String username, String cookies) {
String url = HOST + String.format(QUERY_URL, submitId);
HttpRequest request = HttpUtil.createGet(url)
.cookie(cookies)
.addHeaders(headers);
HttpResponse response = request.execute();
String statusStr = ReUtil.get("<b>Result:</b>(.+?)</td>", response.body(), 1)
.replaceAll("<.*?>", "")
.trim();
Constants.Judge statusType = statusTypeMap.getOrDefault(statusStr, null);
if (statusType == null) {
return MapUtil.builder(new HashMap<String, Object>())
.put("status", Constants.Judge.STATUS_PENDING).build();
}
// 返回的结果map
Map<String, Object> result = MapUtil.builder(new HashMap<String, Object>())
.put("status", statusType.getStatus()).build();
// 如果CE了需要获得错误信息
if (statusType == Constants.Judge.STATUS_COMPILE_ERROR) {
request.setUrl(HOST + String.format(ERROR_URL, submitId));
String CEHtml = request.execute().body();
String compilationErrorInfo = ReUtil.get("<pre>([\\s\\S]*?)</pre>", CEHtml, 1);
result.put("CEInfo", HtmlUtil.unescape(compilationErrorInfo));
} else {
// 如果不是CE,获取其他信息
String executionTime = ReUtil.get("<b>Memory:</b> ([-\\d]+)", response.body(), 1);
result.put("time", Integer.parseInt(executionTime));
String executionMemory = ReUtil.get("<b>Time:</b> ([-\\d]+)", response.body(), 1);
result.put("memory", Integer.parseInt(executionMemory));
}
return result;
}
@Override
public Map<String, Object> getLoginUtils(String username, String password) {
HttpRequest request = HttpUtil.createPost(HOST + LOGIN_URL);
HttpResponse response = request.form(MapUtil.builder(new HashMap<String, Object>())
.put("user_id1", username)
.put("B1", "login")
.put("url", ".")
.put("password1", password).map()).execute();
return MapUtil.builder(new HashMap<String, Object>()).put("cookie", response.getCookieStr()).map();
}
@Override
public String getLanguage(String language) {
switch (language) {
case "G++":
return "0";
case "GCC":
return "1";
case "Java":
return "2";
case "Pascal":
return "3";
case "C++":
return "4";
case "C":
return "5";
case "Fortran":
return "6";
default:
// TODO 抛出没有这个语言的异常
return null;
}
}
public Long getMaxRunId(HttpRequest request, String userName, String problemId) {
String url = String.format(STATUS_URL, userName, problemId);
request.setUrl(HOST + url);
String html = request.execute().body();
Matcher matcher = Pattern.compile("<tr align=center><td>(\\d+)").matcher(html);
return matcher.find() ? Long.parseLong(matcher.group(1)) : -1L;
}
// TODO 添加结果对应的状态
private static final Map<String, Constants.Judge> statusTypeMap = new HashMap<String, Constants.Judge>() {
{
put("Compiling", Constants.Judge.STATUS_COMPILING);
put("Accepted", Constants.Judge.STATUS_ACCEPTED);
put("Presentation Error", Constants.Judge.STATUS_PRESENTATION_ERROR);
put("Time Limit Exceeded", Constants.Judge.STATUS_TIME_LIMIT_EXCEEDED);
put("Memory Limit Exceeded", Constants.Judge.STATUS_MEMORY_LIMIT_EXCEEDED);
put("Wrong Answer", Constants.Judge.STATUS_WRONG_ANSWER);
put("Runtime Error", Constants.Judge.STATUS_RUNTIME_ERROR);
put("Output Limit Exceeded", Constants.Judge.STATUS_RUNTIME_ERROR);
put("Compile Error", Constants.Judge.STATUS_COMPILE_ERROR);
}
};
}

View File

@ -2,6 +2,7 @@ package top.hcode.hoj.remoteJudge.task;
import top.hcode.hoj.remoteJudge.task.Impl.CodeForcesJudge; import top.hcode.hoj.remoteJudge.task.Impl.CodeForcesJudge;
import top.hcode.hoj.remoteJudge.task.Impl.HduJudge; import top.hcode.hoj.remoteJudge.task.Impl.HduJudge;
import top.hcode.hoj.remoteJudge.task.Impl.POJJudge;
import top.hcode.hoj.util.Constants; import top.hcode.hoj.util.Constants;
@ -14,6 +15,8 @@ public class RemoteJudgeFactory {
return new HduJudge(); return new HduJudge();
case CF_JUDGE: case CF_JUDGE:
return new CodeForcesJudge(); return new CodeForcesJudge();
case POJ_JUDGE:
return new POJJudge();
default: default:
return null; return null;
} }

View File

@ -22,7 +22,7 @@ public interface RemoteJudgeStrategy {
* @param username 题库的提交者的账号 * @param username 题库的提交者的账号
* @return 返回结果 * @return 返回结果
*/ */
Map<String, Object> result(Long submitId, String username, String token, HashMap<String,String> cookies) throws Exception; Map<String, Object> result(Long submitId, String username, String cookies) throws Exception;
Map<String, Object> getLoginUtils(String username, String password) throws Exception; Map<String, Object> getLoginUtils(String username, String password) throws Exception;

View File

@ -67,6 +67,8 @@ public class Constants {
CF_JUDGE("CF"), CF_JUDGE("CF"),
POJ_JUDGE("POJ"),
HDU_REMOTE_JUDGE_ACCOUNT("Hdu Remote Judge Account"), HDU_REMOTE_JUDGE_ACCOUNT("Hdu Remote Judge Account"),
CF_REMOTE_JUDGE_ACCOUNT("Codeforces Remote Judge Account"); CF_REMOTE_JUDGE_ACCOUNT("Codeforces Remote Judge Account");

View File

@ -88,4 +88,6 @@ public class JudgeServerApplicationTests {
"}"); "}");
System.out.println(result); System.out.println(result);
} }
} }

View File

@ -56,7 +56,10 @@ public class Discussion {
private Integer viewNum; private Integer viewNum;
@ApiModelProperty(value = "点赞数量") @ApiModelProperty(value = "点赞数量")
private String likeNum; private Integer likeNum;
@ApiModelProperty(value = "评论数量,包括其评论及其回复数")
private Integer commentNum;
@ApiModelProperty(value = "优先级,是否置顶") @ApiModelProperty(value = "优先级,是否置顶")
private Boolean topPriority; private Boolean topPriority;

View File

@ -45,7 +45,7 @@ public class Problem implements Serializable {
@ApiModelProperty(value = "单位ms") @ApiModelProperty(value = "单位ms")
private Integer timeLimit; private Integer timeLimit;
@ApiModelProperty(value = "单位kb") @ApiModelProperty(value = "单位mb")
private Integer memoryLimit; private Integer memoryLimit;
@ApiModelProperty(value = "单位mb") @ApiModelProperty(value = "单位mb")
@ -104,6 +104,9 @@ public class Problem implements Serializable {
@ApiModelProperty(value = "题目测试数据的版本号") @ApiModelProperty(value = "题目测试数据的版本号")
private String caseVersion; private String caseVersion;
@ApiModelProperty(value = "修改题目的管理员用户名")
private String modifiedUser;
@TableField(fill = FieldFill.INSERT) @TableField(fill = FieldFill.INSERT)
private Date gmtCreate; private Date gmtCreate;

View File

@ -151,7 +151,8 @@ export const PROBLEM_LEVEL_RESERVE={
export const REMOTE_OJ = [ export const REMOTE_OJ = [
{name:'HDU',key:"HDU"}, {name:'HDU',key:"HDU"},
{name:"Codeforces",key:"CF"} {name:"Codeforces",key:"CF"},
{name:"POJ",key:"POJ"},
] ]
export const CONTEST_STATUS = { export const CONTEST_STATUS = {

View File

@ -622,7 +622,10 @@ export default {
return; return;
} }
this.replyObj.content = this.replyInputComment; this.replyObj.content = this.replyInputComment;
let replyData = Object.assign({}, this.replyObj); let replyData = {
reply: this.replyObj,
did: this.did,
};
api.addReply(replyData).then((res) => { api.addReply(replyData).then((res) => {
for (let i = 0; i < this.comments.length; i++) { for (let i = 0; i < this.comments.length; i++) {
if (this.comments[i].id == this.replyObj.commentId) { if (this.comments[i].id == this.replyObj.commentId) {
@ -715,6 +718,7 @@ export default {
.then(() => { .then(() => {
let replyDeleteData = { let replyDeleteData = {
id: reply.id, id: reply.id,
did: this.did,
fromUid: reply.fromUid, fromUid: reply.fromUid,
}; };
api.deleteReply(replyDeleteData).then((res) => { api.deleteReply(replyDeleteData).then((res) => {

View File

@ -1,14 +1,14 @@
<template> <template>
<div style="margin: 0px 0px 15px 0px;font-size: 14px;"> <div style="margin: 0px 0px 15px 0px;font-size: 14px;">
<el-row class="header"> <el-row class="header">
<el-col :xs="24" :sm="14" :md="14" :lg="14"> <el-col :xs="24" :sm="15" :md="15" :lg="15">
<div class="select-row"> <div class="select-row">
<span>{{ $t('m.Lang') }}:</span> <span>{{ $t('m.Lang') }}:</span>
<span> <span>
<el-select <el-select
:value="this.language" :value="this.language"
@change="onLangChange" @change="onLangChange"
class="adjust" class="left-adjust"
size="small" size="small"
> >
<el-option v-for="item in languages" :key="item" :value="item" <el-option v-for="item in languages" :key="item" :value="item"
@ -44,13 +44,13 @@
</span> </span>
</div> </div>
</el-col> </el-col>
<el-col :xs="24" :sm="10" :md="10" :lg="10"> <el-col :xs="24" :sm="9" :md="9" :lg="9">
<div class="select-row fl-right"> <div class="select-row fl-right">
<span>{{ $t('m.Theme') }}:</span> <span>{{ $t('m.Theme') }}:</span>
<el-select <el-select
:value="this.theme" :value="this.theme"
@change="onThemeChange" @change="onThemeChange"
class="adjust" class="right-adjust"
size="small" size="small"
> >
<el-option <el-option
@ -104,6 +104,7 @@ import 'codemirror/mode/php/php.js'; //php
import 'codemirror/mode/ruby/ruby.js'; //ruby import 'codemirror/mode/ruby/ruby.js'; //ruby
import 'codemirror/mode/rust/rust.js'; //rust import 'codemirror/mode/rust/rust.js'; //rust
import 'codemirror/mode/javascript/javascript.js'; //javascript import 'codemirror/mode/javascript/javascript.js'; //javascript
import 'codemirror/mode/fortran/fortran.js'; //fortran
// active-line.js // active-line.js
import 'codemirror/addon/selection/active-line.js'; import 'codemirror/addon/selection/active-line.js';
@ -260,10 +261,15 @@ export default {
margin-right: 5px; margin-right: 5px;
margin-left: 5px; margin-left: 5px;
} }
.header .adjust { .header .left-adjust {
width: 170px; width: 170px;
margin-left: 5px; margin-left: 5px;
} }
.header .right-adjust {
width: 140px;
margin-left: 5px;
}
.select-row { .select-row {
margin-top: 4px; margin-top: 4px;
} }

View File

@ -126,6 +126,7 @@ export const m = {
Add_Rmote_OJ_Problem:'Add Remote OJ Problem', Add_Rmote_OJ_Problem:'Add Remote OJ Problem',
Add_From_Public_Problem:'Add From Public Problem', Add_From_Public_Problem:'Add From Public Problem',
Auth:'Auth', Auth:'Auth',
Modified_User:'Modified User',
Public_Problem:'Public Problem', Public_Problem:'Public Problem',
Private_Problem:'Private Problem', Private_Problem:'Private Problem',
Contest_Problem:'Contest Problem', Contest_Problem:'Contest Problem',

View File

@ -126,6 +126,7 @@ export const m = {
Add_Rmote_OJ_Problem:'添加远程OJ题目', Add_Rmote_OJ_Problem:'添加远程OJ题目',
Add_From_Public_Problem:'从公共题库添加题目', Add_From_Public_Problem:'从公共题库添加题目',
Auth:'权限', Auth:'权限',
Modified_User:'最近修改者',
Public_Problem:'公开题目', Public_Problem:'公开题目',
Private_Problem:'隐藏题目', Private_Problem:'隐藏题目',
Contest_Problem:'比赛题目', Contest_Problem:'比赛题目',

View File

@ -164,6 +164,10 @@ export const m = {
Good_luck_to_you:'Good luck to you!', Good_luck_to_you:'Good luck to you!',
// /views/oj/problem/Problem.vue // /views/oj/problem/Problem.vue
Shrink_Sidebar:'Shrink Sidebar',
View_Problem_Content:'View Problem Content',
Only_View_Problem:'Only View Problem',
Put_away_the_full_screen_and_write_the_code:'Put away the full screen and write the code',
Contest_Problem:'Contest Problem', Contest_Problem:'Contest Problem',
Show_Tags:'Show tags', Show_Tags:'Show tags',
No_tag:'No tag', No_tag:'No tag',

View File

@ -165,6 +165,10 @@ export const m = {
Good_luck_to_you:'祝你好运!', Good_luck_to_you:'祝你好运!',
// /views/oj/problem/Problem.vue // /views/oj/problem/Problem.vue
Shrink_Sidebar:'收缩侧边栏',
View_Problem_Content:'查看题目内容',
Only_View_Problem:'只看题目内容',
Put_away_the_full_screen_and_write_the_code:'收起全屏,编写代码',
Contest_Problem:'比赛题目', Contest_Problem:'比赛题目',
Show_Tags:'显示标签', Show_Tags:'显示标签',
No_tag:'暂无标签', No_tag:'暂无标签',
@ -243,7 +247,7 @@ export const m = {
OI_Ranklist: 'OI 排行榜', OI_Ranklist: 'OI 排行榜',
// /views/oj/discussion/discussionList.vue // /views/oj/discussion/discussionList.vue
Created_Time:'创建时间', Created_Time:'发布时间',
Likes:'点赞', Likes:'点赞',
Views:'浏览', Views:'浏览',
Edit:'编辑', Edit:'编辑',

View File

@ -60,7 +60,7 @@
</vxe-table-column> </vxe-table-column>
<vxe-table-column <vxe-table-column
field="author" field="author"
min-width="150" min-width="130"
:title="$t('m.Author')" :title="$t('m.Author')"
> >
</vxe-table-column> </vxe-table-column>
@ -73,6 +73,12 @@
{{ row.gmtCreate | localtime }} {{ row.gmtCreate | localtime }}
</template> </template>
</vxe-table-column> </vxe-table-column>
<vxe-table-column
field="modifiedUser"
min-width="130"
:title="$t('m.Modified_User')"
>
</vxe-table-column>
<vxe-table-column min-width="130" field="auth" :title="$t('m.Auth')"> <vxe-table-column min-width="130" field="auth" :title="$t('m.Auth')">
<template v-slot="{ row }"> <template v-slot="{ row }">
<el-select <el-select

View File

@ -66,6 +66,7 @@
{{ discussion.title }} {{ discussion.title }}
</h1> </h1>
</a> </a>
<a <a
@click="toDiscussionDetail(discussion.id)" @click="toDiscussionDetail(discussion.id)"
class="article-hlink2" class="article-hlink2"
@ -101,30 +102,30 @@
>ADM</span >ADM</span
> >
</span> </span>
<span class="pr pl hidden-xs-only"
><label class="fw"><i class="fa fa-clock-o"></i></label <span class="pr pl"
><label class="fw"><i class="el-icon-chat-round"></i></label
><span> ><span>
{{ $t('m.Created_Time') }}<el-tooltip <span class="hidden-xs-only"> {{ $t('m.Comment') }}:</span>
:content="discussion.gmtCreate | localtime" {{ discussion.commentNum }}</span
placement="top"
>
<span>{{ discussion.gmtCreate | fromNow }}</span>
</el-tooltip></span
></span ></span
> >
<span class="pr" <span class="pr"
><label class="fw"><i class="fa fa-thumbs-o-up"></i></label ><label class="fw"><i class="fa fa-thumbs-o-up"></i></label
><span> ><span>
{{ $t('m.Likes') }}{{ discussion.likeNum }}</span <span class="hidden-xs-only"> {{ $t('m.Likes') }}:</span>
{{ discussion.likeNum }}</span
></span ></span
> >
<span class="pr" <span class="pr"
><label class="fw"><i class="fa fa-eye"></i></label ><label class="fw"><i class="fa fa-eye"></i></label
><span> ><span>
{{ $t('m.Views') }}{{ discussion.viewNum }}</span <span class="hidden-xs-only"> {{ $t('m.Views') }}:</span>
{{ discussion.viewNum }}</span
></span ></span
> >
<span <span class="pr"
><label class="fw"><i class="el-icon-folder-opened"></i></label> ><label class="fw"><i class="el-icon-folder-opened"></i></label>
<a <a
@click=" @click="
@ -139,6 +140,18 @@
> >
</span> </span>
<span class="pr pl hidden-xs-only">
<label class="fw"><i class="fa fa-clock-o"></i></label
><span>
{{ $t('m.Created_Time') }}<el-tooltip
:content="discussion.gmtCreate | localtime"
placement="top"
>
<span>{{ discussion.gmtCreate | fromNow }}</span>
</el-tooltip></span
>
</span>
<el-dropdown <el-dropdown
style="float:right;" style="float:right;"
class="hidden-xs-only" class="hidden-xs-only"

View File

@ -2,8 +2,8 @@
<div> <div>
<div id="problem-main"> <div id="problem-main">
<!--problem main--> <!--problem main-->
<el-row> <el-row class="problem-box">
<el-col :sm="24" :md="24" :lg="12"> <el-col :sm="24" :md="24" :lg="12" class="problem-left">
<el-card :padding="10" shadow class="problem-detail"> <el-card :padding="10" shadow class="problem-detail">
<div slot="header" class="panel-title"> <div slot="header" class="panel-title">
<span>{{ problemData.problem.title }}</span <span>{{ problemData.problem.title }}</span
@ -172,7 +172,42 @@
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :sm="24" :md="24" :lg="12"> <div
class="problem-resize hidden-md-and-down"
:title="$t('m.Shrink_Sidebar')"
>
<span></span>
<el-tooltip
:content="
toWatchProblem
? $t('m.View_Problem_Content')
: $t('m.Only_View_Problem')
"
placement="right"
>
<el-button
icon="el-icon-caret-right"
circle
class="right-fold fold"
@click.native="onlyWatchProblem"
size="mini"
></el-button>
</el-tooltip>
<el-tooltip
:content="$t('m.Put_away_the_full_screen_and_write_the_code')"
placement="left"
>
<el-button
icon="el-icon-caret-left"
circle
class="left-fold fold"
@click.native="resetWatch(false)"
size="mini"
v-show="toResetWatch"
></el-button>
</el-tooltip>
</div>
<el-col :sm="24" :md="24" :lg="12" class="problem-right">
<el-card <el-card
:padding="10" :padding="10"
id="submit-code" id="submit-code"
@ -432,6 +467,8 @@ export default {
JUDGE_STATUS_RESERVE: {}, JUDGE_STATUS_RESERVE: {},
PROBLEM_LEVEL: {}, PROBLEM_LEVEL: {},
RULE_TYPE: {}, RULE_TYPE: {},
toResetWatch: false,
toWatchProblem: false,
}; };
}, },
// //
@ -449,6 +486,7 @@ export default {
next(); next();
} }
}, },
created() { created() {
this.JUDGE_STATUS_RESERVE = Object.assign({}, JUDGE_STATUS_RESERVE); this.JUDGE_STATUS_RESERVE = Object.assign({}, JUDGE_STATUS_RESERVE);
this.PROBLEM_LEVEL = Object.assign({}, PROBLEM_LEVEL); this.PROBLEM_LEVEL = Object.assign({}, PROBLEM_LEVEL);
@ -456,9 +494,95 @@ export default {
}, },
mounted() { mounted() {
this.init(); this.init();
this.dragControllerDiv();
}, },
methods: { methods: {
...mapActions(['changeDomTitle']), ...mapActions(['changeDomTitle']),
dragControllerDiv() {
var resize = document.getElementsByClassName('problem-resize');
var left = document.getElementsByClassName('problem-left');
var right = document.getElementsByClassName('problem-right');
var box = document.getElementsByClassName('problem-box');
const _this = this;
for (let i = 0; i < resize.length; i++) {
//
resize[i].onmousedown = function(e) {
//
resize[i].style.background = '#818181';
var startX = e.clientX;
//
document.onmousemove = function(e) {
resize[i].left = startX;
var endX = e.clientX;
var moveLen = resize[i].left + (endX - startX); // endx-startx=resize[i].left+=
var maxT = box[i].clientWidth - resize[i].offsetWidth; // - =
if (moveLen < 420) moveLen = 0; // 420px
if (moveLen > maxT - 580) moveLen = maxT - 580; //580px
resize[i].style.left = moveLen; //
for (let j = 0; j < left.length; j++) {
left[j].style.width = moveLen + 'px';
let tmp = box[i].clientWidth - moveLen - 11;
right[j].style.width = tmp + 'px';
if (tmp > 0) {
_this.toResetWatch = false;
}
if (moveLen == 0) {
_this.toWatchProblem = true;
}
}
};
//
document.onmouseup = function(evt) {
//
resize[i].style.background = '#d6d6d6';
document.onmousemove = null;
document.onmouseup = null;
resize[i].releaseCapture && resize[i].releaseCapture(); //ReleaseCapture()
};
resize[i].setCapture && resize[i].setCapture(); //线
return false;
};
}
},
onlyWatchProblem() {
if (this.toWatchProblem) {
this.resetWatch(true);
this.toWatchProblem = false;
return;
}
var resize = document.getElementsByClassName('problem-resize');
var left = document.getElementsByClassName('problem-left');
var right = document.getElementsByClassName('problem-right');
var box = document.getElementsByClassName('problem-box');
for (let i = 0; i < resize.length; i++) {
resize[i].style.left = box[i].clientWidth - 11;
for (let j = 0; j < left.length; j++) {
left[j].style.width = box[i].clientWidth - 11 + 'px';
right[j].style.width = '0px';
}
}
this.toResetWatch = true;
},
resetWatch(minLeft = false) {
var resize = document.getElementsByClassName('problem-resize');
var left = document.getElementsByClassName('problem-left');
var right = document.getElementsByClassName('problem-right');
var box = document.getElementsByClassName('problem-box');
for (let i = 0; i < resize.length; i++) {
let leftWidth = 0;
if (minLeft) {
leftWidth = 431; // 420px+11px
} else {
leftWidth = box[i].clientWidth - 580; // 580px
}
resize[i].style.left = leftWidth - 11;
for (let j = 0; j < left.length; j++) {
left[j].style.width = leftWidth - 11 + 'px';
right[j].style.width = box[i].clientWidth - leftWidth + 'px';
}
}
this.toResetWatch = false;
},
init() { init() {
if (this.$route.params.contestID) { if (this.$route.params.contestID) {
this.contestID = this.$route.params.contestID; this.contestID = this.$route.params.contestID;
@ -890,6 +1014,7 @@ export default {
#problem-main { #problem-main {
flex: auto; flex: auto;
} }
.problem-menu { .problem-menu {
float: left; float: left;
} }
@ -941,6 +1066,81 @@ a {
} }
} }
@media screen and (min-width: 1050px) {
.problem-box {
width: 100%;
height: 100%;
overflow: hidden;
}
.problem-left {
width: calc(50% - 10px); /*左侧初始化宽度*/
height: 100%;
overflow-y: auto;
overflow-x: hidden;
float: left;
}
.problem-resize {
cursor: col-resize;
float: left;
position: relative;
top: 330px;
background-color: #d6d6d6;
border-radius: 5px;
margin-top: -10px;
width: 10px;
height: 50px;
background-size: cover;
background-position: center;
/*z-index: 99999;*/
font-size: 32px;
color: white;
}
.problem-resize:hover .right-fold {
display: block;
}
.problem-resize:hover .fold:before {
content: '';
position: absolute;
display: block;
width: 6px;
height: 24px;
left: -6px;
}
.right-fold {
position: absolute;
display: none;
font-weight: bolder;
margin-left: 15px;
margin-top: -35px;
cursor: pointer;
z-index: 1000;
text-align: center;
}
.left-fold {
position: absolute;
font-weight: bolder;
margin-left: -40px;
margin-top: 10px;
cursor: pointer;
z-index: 1000;
text-align: center;
}
.fold:hover {
color: #409eff;
background: #fff;
}
/*拖拽区鼠标悬停样式*/
.problem-resize:hover {
color: #444444;
}
.problem-right {
height: 100%;
float: left;
width: 50%;
}
}
@media screen and (max-width: 1080px) { @media screen and (max-width: 1080px) {
.submit-detail { .submit-detail {
padding-top: 20px; padding-top: 20px;

File diff suppressed because one or more lines are too long

View File

@ -217,7 +217,7 @@ CREATE TABLE `permissions` (
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
); );
insert into `config_info`(`id`,`data_id`,`group_id`,`content`,`md5`,`gmt_create`,`gmt_modified`,`src_user`,`src_ip`,`app_name`,`tenant_id`,`c_desc`,`c_use`,`effect`,`type`,`c_schema`) values (1,'hoj-prod.yml','DEFAULT_GROUP','hoj:\n jwt:\n # 加密秘钥\n secret: hoj-secret-init\n # token默认为24小时 86400s\n expire: 86400\n checkRefreshExpire: 43200\n header: token\n judge:\n # 调用判题服务器的token\n token: hoj-judge-token-init\n db:\n host: 172.20.0.3\n public-host: 172.20.0.3\n port: 3306\n name: hoj\n username: root\n password: hoj123456\n mail:\n ssl: true\n username: your_email_username\n password: your_email_password\n host: smtp.qq.com\n port: 465\n background-img: https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/HCODE.png\n redis:\n host: 172.20.0.2\n port: 6379\n password: hoj123456\n web-config:\n base-url: your_web_url\n name: Hcode Online Judge\n short-name: hoj\n description: Hcode Online Judge\n register: true\n footer:\n record:\n name: 2020-2021\n url: your_record_url\n project:\n name: HOJ\n url: https://gitee.com/himitzh0730/hoj\n hdu:\n account:\n username: \n password: \n cf:\n account:\n username: \n password: ','c83735964d6e700fffa073f3798556a0','2021-05-18 11:29:38','2021-05-18 11:40:41',NULL,'14.211.16.41','','','hoj配置','','','yaml',''); insert into `config_info`(`id`,`data_id`,`group_id`,`content`,`md5`,`gmt_create`,`gmt_modified`,`src_user`,`src_ip`,`app_name`,`tenant_id`,`c_desc`,`c_use`,`effect`,`type`,`c_schema`) values (1,'hoj-prod.yml','DEFAULT_GROUP','hoj:\n jwt:\n # 加密秘钥\n secret: hoj-secret-init\n # token默认为24小时 86400s\n expire: 86400\n checkRefreshExpire: 43200\n header: token\n judge:\n # 调用判题服务器的token\n token: hoj-judge-token-init\n db:\n host: 172.20.0.3\n public-host: 172.20.0.3\n port: 3306\n name: hoj\n username: root\n password: hoj123456\n mail:\n ssl: true\n username: your_email_username\n password: your_email_password\n host: smtp.qq.com\n port: 465\n background-img: https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/HCODE.png\n redis:\n host: 172.20.0.2\n port: 6379\n password: hoj123456\n web-config:\n base-url: your_web_url\n name: Hcode Online Judge\n short-name: hoj\n description: Hcode Online Judge\n register: true\n footer:\n record:\n name: 2020-2021\n url: your_record_url\n project:\n name: HOJ\n url: https://gitee.com/himitzh0730/hoj\n hdu:\n account:\n username: \n password: \n cf:\n account:\n username: \n password: \n poj:\n account:\n username: \n password: ','c83735964d6e700fffa073f3798556a0','2021-05-18 11:29:38','2021-05-18 11:40:41',NULL,'14.211.16.41','','','hoj配置','','','yaml','');
delete from `users`; delete from `users`;