添加编辑器文件上传 修改题目数据、排行榜统计方式

This commit is contained in:
Himit_ZH 2021-06-02 22:38:31 +08:00
parent 86777c86f2
commit 458d3f1a24
42 changed files with 366 additions and 539 deletions

View File

@ -27,8 +27,9 @@ public class CorsConfig implements WebMvcConfigurer {
// 前端直接通过/public/img/图片名称即可拿到
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(Constants.File.IMG_API.getPath() + "**") // /api/public/img/**
// /api/public/img/** /api/public/file/**
registry.addResourceHandler(Constants.File.IMG_API.getPath() + "**",Constants.File.FILE_API.getPath() + "**")
.addResourceLocations("file:" + Constants.File.USER_AVATAR_FOLDER.getPath() + File.separator,
"file:" + Constants.File.MARKDOWN_IMG_FOLDER.getPath() + File.separator);
"file:" + Constants.File.MARKDOWN_FILE_FOLDER.getPath() + File.separator);
}
}

View File

@ -1,5 +1,6 @@
package top.hcode.hoj.controller.admin;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -17,8 +18,10 @@ import top.hcode.hoj.pojo.dto.ProblemDto;
import top.hcode.hoj.pojo.entity.*;
import top.hcode.hoj.pojo.vo.AnnouncementVo;
import top.hcode.hoj.service.impl.*;
import top.hcode.hoj.utils.Constants;
import javax.validation.Valid;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@ -226,6 +229,7 @@ public class AdminContestController {
problem的id为其他表的外键的表中的对应数据都会被一起删除
*/
if (result) { // 删除成功
FileUtil.del(Constants.File.TESTCASE_BASE_FOLDER.getPath() + File.separator + "problem_" + pid);
return CommonResult.successResponse(null, "删除成功!");
} else {
return CommonResult.errorResponse("删除失败!", CommonResult.STATUS_FAIL);

View File

@ -38,9 +38,6 @@ public class AdminJudgeController {
@Autowired
private JudgeServiceImpl judgeService;
@Autowired
private ProblemCountServiceImpl problemCountService;
@Autowired
private UserAcproblemServiceImpl userAcproblemService;
@ -73,15 +70,6 @@ public class AdminJudgeController {
// 如果是非比赛题目
if (judge.getCid() == 0 && judge.getCpid() == 0) {
// 重判前需要将该题目对应记录表一并更新
// 更新该题目的提交统计表problem_count
String columnName = Constants.Judge.getTableColumnNameByStatus(judge.getStatus()); // 获取要修改的字段名
if (columnName != null) {
UpdateWrapper<ProblemCount> problemCountUpdateWrapper = new UpdateWrapper<>();
// 重判需要减掉之前的判题结果
problemCountUpdateWrapper.setSql(columnName + "=" + columnName + "-1,total=total-1")
.eq("pid", judge.getPid());
problemCountService.update(problemCountUpdateWrapper);
}
// 如果该题已经是AC通过状态更新该题目的用户ac做题表 user_acproblem
if (judge.getStatus().intValue() == Constants.Judge.STATUS_ACCEPTED.getStatus().intValue()) {
QueryWrapper<UserAcproblem> userAcproblemQueryWrapper = new QueryWrapper<>();

View File

@ -172,7 +172,7 @@ public class AdminProblemController {
if (problemCases != null && problemCases.size() > 0) {
return CommonResult.successResponse(problemCases, "获取该题目的评测样例列表成功!");
} else {
return CommonResult.errorResponse("获取该题目的评测样例列表失败可能该题目测试数据是zip上传的");
return CommonResult.successResponse(null,"获取该题目的评测样例列表为空");
}
}

View File

@ -226,7 +226,11 @@ public class FileController {
// 遍历读取与检查是否in和out文件一一对应否则报错
for (File tmp : files) {
String tmpPreName = tmp.getName().substring(0, tmp.getName().lastIndexOf("."));
String tmpPreName = null;
try {
tmpPreName = tmp.getName().substring(0, tmp.getName().lastIndexOf("."));
} catch (Exception ignored) {
}
if (tmp.getName().endsWith("in")) {
inputData.put(tmpPreName, tmp.getName());
} else if (tmp.getName().endsWith("out") || tmp.getName().endsWith("ans")) {
@ -577,11 +581,9 @@ public class FileController {
@RequestMapping(value = "/upload-md-img", method = RequestMethod.POST)
@RequiresAuthentication
@ResponseBody
@Transactional
@RequiresRoles(value = {"root", "admin"}, logical = Logical.OR)
public CommonResult uploadMDImg(@RequestParam("image") MultipartFile image) {
if (image == null) {
return CommonResult.errorResponse("图片为空!");
return CommonResult.errorResponse("上传的图片不能为空!");
}
if (image.getSize() > 1024 * 1024 * 4) {
return CommonResult.errorResponse("上传的图片文件大小不能大于4M");
@ -593,13 +595,13 @@ public class FileController {
}
//若不存在该目录则创建目录
FileUtil.mkdir(Constants.File.MARKDOWN_IMG_FOLDER.getPath());
FileUtil.mkdir(Constants.File.MARKDOWN_FILE_FOLDER.getPath());
//通过UUID生成唯一文件名
String filename = IdUtil.simpleUUID() + "." + suffix;
try {
//将文件保存指定目录
image.transferTo(FileUtil.file(Constants.File.MARKDOWN_IMG_FOLDER.getPath() + File.separator + filename));
image.transferTo(FileUtil.file(Constants.File.MARKDOWN_FILE_FOLDER.getPath() + File.separator + filename));
} catch (Exception e) {
log.error("图片文件上传异常-------------->{}", e.getMessage());
return CommonResult.errorResponse("服务器异常:图片文件上传失败!", CommonResult.STATUS_ERROR);
@ -607,17 +609,53 @@ public class FileController {
return CommonResult.successResponse(MapUtil.builder()
.put("link", Constants.File.IMG_API.getPath() + filename)
.put("filePath", Constants.File.MARKDOWN_IMG_FOLDER.getPath() + File.separator + filename).map(),
.put("filePath", Constants.File.MARKDOWN_FILE_FOLDER.getPath() + File.separator + filename).map(),
"上传图片成功!");
}
@RequestMapping(value = "/upload-md-file", method = RequestMethod.POST)
@RequiresAuthentication
@ResponseBody
@RequiresRoles(value = {"root", "admin"}, logical = Logical.OR)
public CommonResult uploadMd(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
if (file == null) {
return CommonResult.errorResponse("上传的文件不能为空!");
}
if (file.getSize() >= 1024 * 1024 * 128) {
return CommonResult.errorResponse("上传的文件大小不能大于128M");
}
//获取文件后缀
String suffix = "";
String filename = "";
if (file.getOriginalFilename() != null && file.getOriginalFilename().contains(".")) {
suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
//通过UUID生成唯一文件名
filename = IdUtil.simpleUUID() + "." + suffix;
} else {
filename = IdUtil.simpleUUID();
}
//若不存在该目录则创建目录
FileUtil.mkdir(Constants.File.MARKDOWN_FILE_FOLDER.getPath());
try {
//将文件保存指定目录
file.transferTo(FileUtil.file(Constants.File.MARKDOWN_FILE_FOLDER.getPath() + File.separator + filename));
} catch (Exception e) {
log.error("文件上传异常-------------->{}", e.getMessage());
return CommonResult.errorResponse("服务器异常:文件上传失败!", CommonResult.STATUS_ERROR);
}
return CommonResult.successResponse(MapUtil.builder()
.put("link", Constants.File.FILE_API.getPath() + filename).map(),
"上传文件成功!");
}
@RequestMapping(value = "/delete-md-img", method = RequestMethod.GET)
@RequiresAuthentication
@ResponseBody
@Transactional
@RequiresRoles(value = {"root", "admin"}, logical = Logical.OR)
public CommonResult uploadMDImg(@RequestParam("filePath") String filePath) {
boolean result = FileUtil.del(filePath);
if (result) {
@ -928,6 +966,7 @@ public class FileController {
qdojProblemDto.setIsSpj(spj != null);
Problem problem = new Problem();
problem.setAuth(1)
.setIsUploadCase(true)
.setSource(problemJson.getStr("source", null))
.setDifficulty(1)
.setProblemId(problemJson.getStr("display_id"))

View File

@ -60,8 +60,6 @@ public class ContestController {
@Autowired
private ProblemLanguageServiceImpl problemLanguageService;
@Autowired
private ProblemCountServiceImpl problemCountService;
@Autowired
private JudgeServiceImpl judgeService;
@ -298,7 +296,7 @@ public class ContestController {
});
// 获取题目的提交记录
ProblemCount problemCount = problemCountService.getContestProblemCount(contestProblem.getPid(), contestProblem.getId(), contestProblem.getCid());
ProblemCountVo problemCount = judgeService.getContestProblemCount(contestProblem.getPid(), contestProblem.getId(), contestProblem.getCid());
// 获取题目的代码模板
QueryWrapper<CodeTemplate> codeTemplateQueryWrapper = new QueryWrapper<>();

View File

@ -59,9 +59,6 @@ public class JudgeController {
@Autowired
private ProblemService problemService;
@Autowired
private UserRecordServiceImpl userRecordService;
@Autowired
private ContestProblemServiceImpl contestProblemService;
@ -71,9 +68,6 @@ public class JudgeController {
@Autowired
private ContestRecordServiceImpl contestRecordService;
@Autowired
private ProblemCountServiceImpl problemCountService;
@Autowired
private UserAcproblemServiceImpl userAcproblemService;
@ -116,8 +110,6 @@ public class JudgeController {
.setSubmitTime(new Date())
.setVersion(0)
.setIp(IpUtils.getUserIpAddr(request));
boolean updateUserRecord = true;
boolean updateContestRecord = true;
int result = 0;
// 如果比赛id不等于0则说明为比赛提交需要查询对应的contest_problem的主键
@ -197,15 +189,11 @@ public class JudgeController {
Problem problem = problemService.getOne(problemQueryWrapper);
judge.setCpid(0L).setPid(problem.getId()).setDisplayPid(problem.getProblemId());
// 更新一下user_record表
UpdateWrapper<UserRecord> userRecordUpdateWrapper = new UpdateWrapper<>();
userRecordUpdateWrapper.setSql("submissions=submissions+1").eq("uid", judge.getUid());
updateUserRecord = userRecordService.update(userRecordUpdateWrapper);
// 将新提交数据插入数据库
result = judgeMapper.insert(judge);
}
if (result != 1 || !updateContestRecord || !updateUserRecord) {
if (result != 1 || !updateContestRecord) {
return CommonResult.errorResponse("代码提交失败!", CommonResult.STATUS_ERROR);
}
@ -248,15 +236,6 @@ public class JudgeController {
// 如果是非比赛题目
if (judge.getCid() == 0) {
// 重判前需要将该题目对应记录表一并更新
// 更新该题目的提交统计表problem_count
String columnName = Constants.Judge.getTableColumnNameByStatus(judge.getStatus()); // 获取要修改的字段名
if (columnName != null) {
UpdateWrapper<ProblemCount> problemCountUpdateWrapper = new UpdateWrapper<>();
// 重判需要减掉之前的判题结果
problemCountUpdateWrapper.setSql(columnName + "=" + columnName + "-1,total=total-1")
.eq("pid", judge.getPid());
problemCountService.update(problemCountUpdateWrapper);
}
// 如果该题已经是AC通过状态更新该题目的用户ac做题表 user_acproblem
if (judge.getStatus().intValue() == Constants.Judge.STATUS_ACCEPTED.getStatus().intValue()) {
QueryWrapper<UserAcproblem> userAcproblemQueryWrapper = new QueryWrapper<>();

View File

@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.*;
import top.hcode.hoj.common.result.CommonResult;
import top.hcode.hoj.pojo.dto.PidListDto;
import top.hcode.hoj.pojo.entity.*;
import top.hcode.hoj.pojo.vo.ProblemCountVo;
import top.hcode.hoj.pojo.vo.ProblemInfoVo;
import top.hcode.hoj.pojo.vo.ProblemVo;
import top.hcode.hoj.pojo.vo.UserRolesVo;
@ -54,8 +55,6 @@ public class ProblemController {
@Autowired
private ProblemLanguageServiceImpl problemLanguageService;
@Autowired
private ProblemCountServiceImpl problemCountService;
@Autowired
private CodeTemplateServiceImpl codeTemplateService;
@ -255,9 +254,7 @@ public class ProblemController {
});
// 获取题目的提交记录
QueryWrapper<ProblemCount> problemCountQueryWrapper = new QueryWrapper<>();
problemCountQueryWrapper.eq("pid", problem.getId());
ProblemCount problemCount = problemCountService.getOne(problemCountQueryWrapper);
ProblemCountVo problemCount = judgeService.getProblemCount(problem.getId());
// 获取题目的代码模板
QueryWrapper<CodeTemplate> codeTemplateQueryWrapper = new QueryWrapper<>();

View File

@ -8,8 +8,8 @@ import org.springframework.stereotype.Repository;
import top.hcode.hoj.pojo.entity.Judge;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import top.hcode.hoj.pojo.vo.JudgeVo;
import top.hcode.hoj.pojo.vo.ProblemCountVo;
import java.util.List;
/**
* <p>
@ -29,4 +29,8 @@ public interface JudgeMapper extends BaseMapper<Judge> {
@Param("username") String username, @Param("uid") String uid, @Param("beforeContestSubmit") Boolean beforeContestSubmit);
int getTodayJudgeNum();
ProblemCountVo getContestProblemCount(@Param("pid") Long pid, @Param("cpid") Long cpid, @Param("cid") Long cid);
ProblemCountVo getProblemCount(@Param("pid") Long pid);
}

View File

@ -1,21 +0,0 @@
package top.hcode.hoj.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import top.hcode.hoj.pojo.entity.ProblemCount;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author Himit_ZH
* @since 2020-10-23
*/
@Mapper
@Repository
public interface ProblemCountMapper extends BaseMapper<ProblemCount> {
ProblemCount getContestProblemCount(@Param("pid") Long pid, @Param("cpid") Long cpid, @Param("cid") Long cid);
}

View File

@ -2,8 +2,9 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="top.hcode.hoj.dao.AnnouncementMapper">
<select id="getAnnouncementList" resultMap="map_AnnouncementVo">
<select id="getAnnouncementList" resultMap="map_AnnouncementVo" useCache="true">
SELECT a.*,u.username FROM user_info u,announcement a where a.uid = u.uuid
and (SELECT COUNT(*) FROM contest_announcement ca WHERE ca.aid=a.id) = 0
<if test="notAdmin">
and a.status = 0
</if>

View File

@ -62,4 +62,32 @@
<select id="getTodayJudgeNum" resultType="int">
SELECT count(*) FROM judge WHERE DATE(gmt_create) = CURDATE();
</select>
<select id="getContestProblemCount" resultType="top.hcode.hoj.pojo.vo.ProblemCountVo">
SELECT COUNT(IF(status=-3,status,NULL)) AS pe,
COUNT(IF(status=-2,status,NULL)) AS ce,
COUNT(IF(status=-1,status,NULL)) AS wa,
COUNT(IF(status=0,status,NULL)) AS ac,
COUNT(IF(status=1,status,NULL)) AS tle,
COUNT(IF(status=2,status,NULL)) AS mle,
COUNT(IF(status=3,status,NULL)) AS re,
COUNT(IF(status=4,status,NULL)) AS se,
COUNT(IF(status=8,status,NULL)) AS pa,
COUNT(*) AS total
FROM judge where pid=#{pid} and cpid = #{cpid} and cid = #{cid}
</select>
<select id="getProblemCount" resultType="top.hcode.hoj.pojo.vo.ProblemCountVo">
SELECT COUNT(IF(status=-3,status,NULL)) AS pe,
COUNT(IF(status=-2,status,NULL)) AS ce,
COUNT(IF(status=-1,status,NULL)) AS wa,
COUNT(IF(status=0,status,NULL)) AS ac,
COUNT(IF(status=1,status,NULL)) AS tle,
COUNT(IF(status=2,status,NULL)) AS mle,
COUNT(IF(status=3,status,NULL)) AS re,
COUNT(IF(status=4,status,NULL)) AS se,
COUNT(IF(status=8,status,NULL)) AS pa,
COUNT(*) AS total
FROM judge where pid=#{pid}
</select>
</mapper>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="top.hcode.hoj.dao.ProblemCountMapper">
<select id="getContestProblemCount" resultType="top.hcode.hoj.pojo.entity.ProblemCount">
SELECT COUNT(IF(status=-3,status,NULL)) AS pe,
COUNT(IF(status=-2,status,NULL)) AS ce,
COUNT(IF(status=-1,status,NULL)) AS wa,
COUNT(IF(status=0,status,NULL)) AS ac,
COUNT(IF(status=1,status,NULL)) AS tle,
COUNT(IF(status=2,status,NULL)) AS mle,
COUNT(IF(status=3,status,NULL)) AS re,
COUNT(IF(status=4,status,NULL)) AS se,
COUNT(IF(status=8,status,NULL)) AS pa,
COUNT(*) AS total
FROM judge where pid=#{pid} and cpid = #{cpid} and cid = #{cid}
</select>
</mapper>

View File

@ -24,9 +24,35 @@
<!-- 主查询 -->
<select id="getProblemList" resultMap="map_ProblemList">
SELECT distinct p.id as pid,p.problem_id,p.title,p.difficulty,p.type,pc.total,pc.ac,pc.mle,pc.tle,pc.re,
pc.pe,pc.ce,pc.wa,pc.se,pc.pa FROM problem p
LEFT JOIN problem_count pc ON p.id = pc.pid
SELECT DISTINCT p.id AS pid, p.problem_id, p.title, p.difficulty, p.type
, COALESCE(j.pe, 0) AS pe
, COALESCE(j.ce, 0) AS ce
, COALESCE(j.wa, 0) AS wa
, COALESCE(j.ac, 0) AS ac
, COALESCE(j.tle, 0) AS tle
, COALESCE(j.mle, 0) AS mle
, COALESCE(j.re, 0) AS re
, COALESCE(j.se, 0) AS se
, COALESCE(j.pa, 0) AS pa
, COALESCE(j.total, 0) AS total
FROM problem p
LEFT JOIN (
SELECT j.pid AS pid
, COUNT(IF(j.status = -3, STATUS, NULL)) AS pe
, COUNT(IF(j.status = -2, STATUS, NULL)) AS ce
, COUNT(IF(j.status = -1, STATUS, NULL)) AS wa
, COUNT(IF(j.status = 0, STATUS, NULL)) AS ac
, COUNT(IF(j.status = 1, STATUS, NULL)) AS tle
, COUNT(IF(j.status = 2, STATUS, NULL)) AS mle
, COUNT(IF(j.status = 3, STATUS, NULL)) AS re
, COUNT(IF(j.status = 4, STATUS, NULL)) AS se
, COUNT(IF(j.status = 8, STATUS, NULL)) AS pa
, COUNT(*) AS total
FROM judge j
WHERE j.pid
GROUP BY j.pid
) j
ON j.pid = p.id
LEFT JOIN problem_tag pt ON p.id = pt.pid
<where>
p.auth = 1

View File

@ -2,37 +2,39 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="top.hcode.hoj.dao.UserRecordMapper">
<select id="getACMRankList" resultType="top.hcode.hoj.pojo.vo.ACMRankVo" useCache="true">
SELECT u.uuid as uid,u.nickname,u.username,u.signature,u.avatar,ur.submissions as total,ur.rating,
SELECT u.uuid as uid,u.nickname,u.username,u.signature,u.avatar,
(SELECT COUNT( DISTINCT pid ) FROM user_acproblem WHERE uid =u.uuid) AS solved,
(SELECT COUNT(pid) FROM user_acproblem WHERE uid =u.uuid) AS ac
FROM user_info u,user_record ur WHERE u.uuid = ur.uid AND u.status = 0
(SELECT COUNT(pid) FROM user_acproblem WHERE uid =u.uuid) AS ac,
(SELECT COUNT(uid) FROM judge WHERE uid=u.uuid) AS total
FROM user_info u WHERE u.status = 0
ORDER BY solved DESC,ac DESC
</select>
<select id="getRecent7ACRank" resultType="top.hcode.hoj.pojo.vo.ACMRankVo">
SELECT u.uuid as uid,u.nickname,u.username,u.signature,u.avatar,ur.submissions as total,ur.rating,
SELECT u.uuid as uid,u.nickname,u.username,u.signature,u.avatar,
(SELECT COUNT( DISTINCT pid ) FROM user_acproblem WHERE uid =u.uuid
and DATE(gmt_create) >= DATE_SUB(CURDATE(),INTERVAL 7 DAY) ) AS solved,
(SELECT COUNT(pid) FROM user_acproblem WHERE uid =u.uuid
and DATE(gmt_create) >= DATE_SUB(CURDATE(),INTERVAL 7 DAY)) AS ac
FROM user_info u,user_record ur WHERE u.uuid = ur.uid AND u.status = 0
ORDER BY solved DESC,ac DESC LIMIT 10
(SELECT COUNT( DISTINCT pid ) FROM user_acproblem WHERE uid =u.uuid
and DATE(gmt_create) >= DATE_SUB(CURDATE(),INTERVAL 7 DAY) ) AS solved,
(SELECT COUNT(pid) FROM user_acproblem WHERE uid =u.uuid
and DATE(gmt_create) >= DATE_SUB(CURDATE(),INTERVAL 7 DAY)) AS ac,
(SELECT COUNT(uid) FROM judge WHERE uid=u.uuid) AS total
FROM user_info u WHERE u.status = 0
ORDER BY solved DESC,ac DESC LIMIT 10
</select>
<select id="getOIRankList" resultType="top.hcode.hoj.pojo.vo.OIRankVo" useCache="true">
SELECT u.uuid as uid,u.nickname,u.username,u.signature,u.avatar,ur.submissions as total,ur.rating,
ur.total_score as score,
(SELECT COUNT(pid) FROM user_acproblem WHERE uid =u.uuid) AS ac
FROM user_info u,user_record ur WHERE u.uuid = ur.uid AND u.status = 0
ORDER BY score DESC,ac DESC
SELECT u.uuid as uid,u.nickname,u.username,u.signature,u.avatar,
(SELECT IFNULL(SUM(score),0) FROM judge WHERE uid=u.uuid) AS score,
(SELECT COUNT(pid) FROM user_acproblem WHERE uid =u.uuid) AS ac,
(SELECT COUNT(uid) FROM judge WHERE uid=u.uuid) AS total
FROM user_info u WHERE u.status = 0
ORDER BY score DESC,ac DESC
</select>
<select id="getUserHomeInfo" resultType="top.hcode.hoj.pojo.vo.UserHomeVo">
SELECT u.username,u.signature,u.school,u.github,u.blog,u.avatar,ur.submissions as total,ur.rating,
ur.total_score as score
SELECT u.username,u.signature,u.school,u.github,u.blog,u.avatar,ur.rating,
(SELECT IFNULL(SUM(score),0) FROM judge WHERE uid=u.uuid) AS score,
(SELECT COUNT(uid) FROM judge WHERE uid=u.uuid) AS total
FROM user_info u,user_record ur WHERE u.uuid = ur.uid AND u.status = 0 AND u.uuid = #{uid}
</select>

View File

@ -1,4 +1,4 @@
package top.hcode.hoj.pojo.entity;
package top.hcode.hoj.pojo.vo;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
@ -19,16 +19,11 @@ import java.util.Date;
* @since 2020-10-23
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="ProblemCount对象", description="")
public class ProblemCount implements Serializable {
public class ProblemCountVo implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "pid", type = IdType.ID_WORKER)
private Long pid;
private Integer total;
private Integer ac;
@ -57,14 +52,4 @@ public class ProblemCount implements Serializable {
@ApiModelProperty(value = "部分通过OI题目")
private Integer pa;
@Version
private Long version;
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
}

View File

@ -3,7 +3,6 @@ package top.hcode.hoj.pojo.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import top.hcode.hoj.pojo.entity.Problem;
import top.hcode.hoj.pojo.entity.ProblemCount;
import java.util.HashMap;
import java.util.List;
@ -19,6 +18,6 @@ public class ProblemInfoVo {
private Problem problem;
private List<String> tags;
private List<String> languages;
private ProblemCount problemCount;
private ProblemCountVo problemCount;
private HashMap<String, String> codeTemplate;
}

View File

@ -2,16 +2,11 @@ package top.hcode.hoj.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import top.hcode.hoj.pojo.entity.Judge;
import com.baomidou.mybatisplus.extension.service.IService;
import top.hcode.hoj.pojo.entity.ToJudge;
import top.hcode.hoj.pojo.vo.JudgeVo;
import top.hcode.hoj.pojo.vo.ProblemCountVo;
import java.util.List;
/**
@ -33,4 +28,7 @@ public interface JudgeService extends IService<Judge> {
void failToUseRedisPublishJudge(Long submitId, Long pid, Boolean isContest);
ProblemCountVo getContestProblemCount(Long pid, Long cpid, Long cid);
ProblemCountVo getProblemCount(Long pid);
}

View File

@ -1,16 +0,0 @@
package top.hcode.hoj.service;
import top.hcode.hoj.pojo.entity.ProblemCount;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author Himit_ZH
* @since 2020-10-23
*/
public interface ProblemCountService extends IService<ProblemCount> {
ProblemCount getContestProblemCount(Long pid, Long cpid, Long cid);
}

View File

@ -1,6 +1,5 @@
package top.hcode.hoj.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -8,8 +7,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import top.hcode.hoj.pojo.entity.ContestRecord;
import top.hcode.hoj.pojo.entity.Judge;
import top.hcode.hoj.dao.JudgeMapper;
import top.hcode.hoj.pojo.entity.ProblemCount;
import top.hcode.hoj.pojo.vo.JudgeVo;
import top.hcode.hoj.pojo.vo.ProblemCountVo;
import top.hcode.hoj.service.JudgeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@ -30,9 +29,6 @@ public class JudgeServiceImpl extends ServiceImpl<JudgeMapper, Judge> implements
@Autowired
private JudgeMapper judgeMapper;
@Autowired
private ProblemCountServiceImpl problemCountService;
@Autowired
private ContestRecordServiceImpl contestRecordService;
@ -61,15 +57,8 @@ public class JudgeServiceImpl extends ServiceImpl<JudgeMapper, Judge> implements
.set("error_message", "The something has gone wrong with the data Backup server. Please report this to administrator.")
.set("status", Constants.Judge.STATUS_SYSTEM_ERROR.getStatus());
judgeMapper.update(null, judgeUpdateWrapper);
// 更新problem_count
if (!isContest) {
QueryWrapper<ProblemCount> problemCountQueryWrapper = new QueryWrapper<ProblemCount>();
problemCountQueryWrapper.eq("pid", pid);
ProblemCount problemCount = problemCountService.getOne(problemCountQueryWrapper);
problemCount.setSe(problemCount.getSe() + 1);
problemCountService.saveOrUpdate(problemCount);
} else {
// 更新contest_record表
// 更新contest_record表
if (isContest) {
UpdateWrapper<ContestRecord> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("submit_id", submitId) // submit_id一定只有一个
.set("first_blood", false)
@ -78,4 +67,14 @@ public class JudgeServiceImpl extends ServiceImpl<JudgeMapper, Judge> implements
}
}
@Override
public ProblemCountVo getContestProblemCount(Long pid, Long cpid, Long cid) {
return judgeMapper.getContestProblemCount(pid, cpid, cid);
}
@Override
public ProblemCountVo getProblemCount(Long pid) {
return judgeMapper.getProblemCount(pid);
}
}

View File

@ -1,28 +0,0 @@
package top.hcode.hoj.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import top.hcode.hoj.pojo.entity.ProblemCount;
import top.hcode.hoj.dao.ProblemCountMapper;
import top.hcode.hoj.service.ProblemCountService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author Himit_ZH
* @since 2020-10-23
*/
@Service
public class ProblemCountServiceImpl extends ServiceImpl<ProblemCountMapper, ProblemCount> implements ProblemCountService {
@Autowired
private ProblemCountMapper problemCountMapper;
@Override
public ProblemCount getContestProblemCount(Long pid, Long cpid, Long cid) {
return problemCountMapper.getContestProblemCount(pid,cpid, cid);
}
}

View File

@ -17,6 +17,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.yaml.snakeyaml.Yaml;
import top.hcode.hoj.crawler.problem.CFProblemStrategy;
import top.hcode.hoj.crawler.problem.HDUProblemStrategy;
@ -70,9 +71,6 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
@Autowired
private ProblemTagServiceImpl problemTagService;
@Autowired
private ProblemCountServiceImpl problemCountService;
@Autowired
private ApplicationContext applicationContext;
@ -236,87 +234,68 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
boolean checkProblemCase = true;
if (!problem.getIsRemote()) { // 如果是自家的题目才有测试数据
// 如果是选择上传测试文件的则需要遍历对应文件夹读取数据写入数据库,先前的题目数据一并清空
if (problemDto.getIsUploadTestCase()) {
// 如果是选择了上传测试文件的模式但是数据为空那就是修改题目但是没修改测试数据需要判断
if (problemDto.getSamples().size() > 0) {
int sumScore = 0;
String testcaseDir = problemDto.getUploadTestcaseDir();
if (!problem.getIsRemote() && problemDto.getSamples().size() > 0) { // 如果是自家的题目才有测试数据
int sumScore = 0;
// 新增加的case列表
List<ProblemCase> newProblemCaseList = new LinkedList<>();
// 需要修改的case列表
List<ProblemCase> needUpdateProblemCaseList = new LinkedList<>();
// 遍历上传的case列表如果还存在则从需要删除的测试样例列表移除该id
for (ProblemCase problemCase : problemDto.getSamples()) {
if (problemCase.getId() != null) { // 已存在的case
needDeleteProblemCases.remove(problemCase.getId());
// 跟原先的数据做对比如果变动 则加入需要修改的case列表
ProblemCase oldProblemCase = oldProblemMap.get(problemCase.getId());
if (!oldProblemCase.getInput().equals(problemCase.getInput()) ||
!oldProblemCase.getOutput().equals(problemCase.getOutput())) {
needUpdateProblemCaseList.add(problemCase);
} else if (problem.getType().intValue() == Constants.Contest.TYPE_OI.getCode()) {
if (oldProblemCase.getScore().intValue() != problemCase.getScore()) {
needUpdateProblemCaseList.add(problemCase);
}
}
} else {
newProblemCaseList.add(problemCase);
}
if (problemCase.getScore() != null) {
sumScore += problemCase.getScore();
}
}
// 设置oi总分数根据每个测试点的加和
if (problem.getType().intValue() == Constants.Contest.TYPE_OI.getCode()) {
problem.setIoScore(sumScore);
}
// 执行批量删除操作
boolean deleteCasesFromProblemResult = true;
if (needDeleteProblemCases.size() > 0) {
deleteCasesFromProblemResult = problemCaseService.removeByIds(needDeleteProblemCases);
}
// 执行批量添加操作
boolean addCasesToProblemResult = true;
if (newProblemCaseList.size() > 0) {
addCasesToProblemResult = problemCaseService.saveBatch(newProblemCaseList);
}
// 执行批量修改操作
boolean updateCasesToProblemResult = true;
if (needUpdateProblemCaseList.size() > 0) {
updateCasesToProblemResult = problemCaseService.saveOrUpdateBatch(needUpdateProblemCaseList);
}
checkProblemCase = addCasesToProblemResult && deleteCasesFromProblemResult && updateCasesToProblemResult;
// 只要有新添加修改删除都需要更新版本号 同时更新测试数据
String caseVersion = String.valueOf(System.currentTimeMillis());
if (needDeleteProblemCases.size() > 0 || newProblemCaseList.size() > 0 || needUpdateProblemCaseList.size() > 0) {
problem.setCaseVersion(caseVersion);
// 如果是选择上传测试文件的临时文件路径不为空则需要遍历对应文件夹读取数据写入数据库,先前的题目数据一并清空
String testcaseDir = problemDto.getUploadTestcaseDir();
if (problemDto.getIsUploadTestCase() && !StringUtils.isEmpty(testcaseDir)) {
// 将之前的临时文件夹里面的评测文件全部复制到指定文件夹(覆盖)
FileUtil.copyFilesFromDir(new File(testcaseDir), new File(Constants.File.TESTCASE_BASE_FOLDER.getPath() + File.separator + "problem_" + pid), true);
// 如果是io题目统计总分
for (ProblemCase problemCase : problemDto.getSamples()) {
if (problemCase.getScore() != null) {
sumScore += problemCase.getScore();
}
}
// 设置oi总分数根据每个测试点的加和
if (problem.getType().intValue() == Constants.Contest.TYPE_OI.getCode()) {
problem.setIoScore(sumScore);
}
// 设置新的测试样例版本号
String caseVersion = String.valueOf(System.currentTimeMillis());
problem.setCaseVersion(caseVersion);
// 获取代理bean对象执行异步方法===根据测试文件初始info
applicationContext.getBean(ProblemServiceImpl.class).initUploadTestCase(problem.getSpjLanguage() != null, caseVersion, pid, problemDto.getSamples());
}else{
applicationContext.getBean(ProblemServiceImpl.class).initHandTestCase(problem.getSpjLanguage() != null, problem.getCaseVersion(), pid, problemDto.getSamples());
}
} else if (problemDto.getSamples().size() > 0) {
int sumScore = 0;
// 新增加的case列表
List<ProblemCase> newProblemCaseList = new LinkedList<>();
// 需要修改的case列表
List<ProblemCase> needUpdateProblemCaseList = new LinkedList<>();
// 遍历上传的case列表如果还存在则从需要删除的测试样例列表移除该id
for (ProblemCase problemCase : problemDto.getSamples()) {
if (problemCase.getId() != null) { // 已存在的case
needDeleteProblemCases.remove(problemCase.getId());
// 跟原先的数据做对比如果变动 则加入需要修改的case列表
ProblemCase oldProblemCase = oldProblemMap.get(problemCase.getId());
if (!oldProblemCase.getInput().equals(problemCase.getInput()) ||
!oldProblemCase.getOutput().equals(problemCase.getOutput())) {
needUpdateProblemCaseList.add(problemCase);
} else if (problem.getType().intValue() == Constants.Contest.TYPE_OI.getCode()) {
if (oldProblemCase.getScore().intValue() != problemCase.getScore()) {
needUpdateProblemCaseList.add(problemCase);
}
}
} else {
newProblemCaseList.add(problemCase);
}
if (problemCase.getScore() != null) {
sumScore += problemCase.getScore();
}
}
// 设置oi总分数根据每个测试点的加和
if (problem.getType().intValue() == Constants.Contest.TYPE_OI.getCode()) {
problem.setIoScore(sumScore);
}
// 执行批量删除操作
boolean deleteCasesFromProblemResult = true;
if (needDeleteProblemCases.size() > 0) {
deleteCasesFromProblemResult = problemCaseService.removeByIds(needDeleteProblemCases);
}
// 执行批量添加操作
boolean addCasesToProblemResult = true;
if (newProblemCaseList.size() > 0) {
addCasesToProblemResult = problemCaseService.saveBatch(newProblemCaseList);
}
// 执行批量修改操作
boolean updateCasesToProblemResult = true;
if (needUpdateProblemCaseList.size() > 0) {
updateCasesToProblemResult = problemCaseService.saveOrUpdateBatch(needUpdateProblemCaseList);
}
checkProblemCase = addCasesToProblemResult && deleteCasesFromProblemResult && updateCasesToProblemResult;
// 只要有新添加修改删除都需要更新版本号 同时更新测试数据
if (needDeleteProblemCases.size() > 0 || newProblemCaseList.size() > 0 || needUpdateProblemCaseList.size() > 0) {
problem.setCaseVersion(String.valueOf(System.currentTimeMillis()));
initHandTestCase(problem.getSpjLanguage() != null, problem.getCaseVersion(), pid, problemDto.getSamples());
}
}
}
@ -333,6 +312,7 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
} else {
return false;
}
}
@Override
@ -375,15 +355,18 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
// 将之前的临时文件夹里面的评测文件全部复制到指定文件夹(覆盖)
FileUtil.copyFilesFromDir(new File(testcaseDir), new File(Constants.File.TESTCASE_BASE_FOLDER.getPath() + File.separator + "problem_" + pid), true);
// 如果是io题目统计总分
for (ProblemCase problemCase : problemDto.getSamples()) {
List<ProblemCase> problemCases = problemDto.getSamples();
for (ProblemCase problemCase : problemCases) {
if (problemCase.getScore() != null) {
sumScore += problemCase.getScore();
}
problemCase.setPid(pid);
}
// 设置oi总分数根据每个测试点的加和
if (problem.getType().intValue() == Constants.Contest.TYPE_OI.getCode()) {
problem.setIoScore(sumScore);
}
addCasesToProblemResult = problemCaseService.saveOrUpdateBatch(problemCases);
// 获取代理bean对象执行异步方法===根据测试文件初始info
applicationContext.getBean(ProblemServiceImpl.class).initUploadTestCase(problem.getSpjLanguage() != null, problem.getCaseVersion(), pid, problemDto.getSamples());
} else {
@ -414,12 +397,9 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
}
boolean addTagsToProblemResult = problemTagService.saveOrUpdateBatch(problemTagList);
// 为新的题目初始化problem_count表
boolean initProblemCountResult = problemCountService.save(new ProblemCount().setPid(pid));
if (addProblemResult && addCasesToProblemResult && addLangToProblemResult
&& addTagsToProblemResult && initProblemCountResult && addProblemCodeTemplate) {
&& addTagsToProblemResult && addProblemCodeTemplate) {
// 修改数据库成功后如果有进行文件上传操作则进行删除
if (problemDto.getIsUploadTestCase()) {
FileUtil.del(problemDto.getUploadTestcaseDir());
@ -454,7 +434,7 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
jsonObject.set("outputName", problemCase.getOutput());
// 读取输出文件
FileReader readFile = new FileReader(testCasesDir + "/" + problemCase.getOutput(), CharsetUtil.UTF_8);
String output = readFile.readString().replaceAll("\r\n","\n");
String output = readFile.readString().replaceAll("\r\n", "\n");
// spj是根据特判程序输出判断结果所以无需初始化测试数据
if (!isSpj) {
@ -478,6 +458,7 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
// 初始化手动输入上传的测试数据写成json文件
@Async
public void initHandTestCase(Boolean isSpj,
String version,
Long problemId,
@ -505,7 +486,7 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
String outputName = (index + 1) + ".out";
jsonObject.set("outputName", outputName);
// 生成对应文件
String outputData = problemCaseList.get(index).getOutput().replaceAll("\r\n","\n");
String outputData = problemCaseList.get(index).getOutput().replaceAll("\r\n", "\n");
FileWriter outFile = new FileWriter(testCasesDir + "/" + outputName, CharsetUtil.UTF_8);
outFile.write(outputData);
@ -571,9 +552,6 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
boolean addProblemLanguageResult = problemLanguageService.saveOrUpdateBatch(problemLanguageList);
// 为新的题目初始化problem_count表
boolean initProblemCountResult = problemCountService.save(new ProblemCount().setPid(problem.getId()));
boolean addProblemTagResult = true;
List<Tag> addTagList = remoteProblemInfo.getTagList();
@ -613,7 +591,7 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
.setPid(problem.getId()));
}
return addProblemResult && addProblemTagResult && addProblemLanguageResult && initProblemCountResult;
return addProblemResult && addProblemTagResult && addProblemLanguageResult;
}
@Override
@ -626,6 +604,7 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
.setGmtCreate(null)
.setId(null)
.setAuth(1)
.setIsUploadCase(true)
.setAuthor(null)
.setGmtModified(null);
HashMap<String, Object> problemMap = new HashMap<>();

View File

@ -53,15 +53,6 @@ public class Constants {
public String getColumnName() {
return columnName;
}
public static String getTableColumnNameByStatus(int status) {
for (Judge judge : Judge.values()) {
if (judge.getStatus() == status) {
return judge.getColumnName();
}
}
return null;
}
}
public enum RemoteOJ {
@ -160,10 +151,12 @@ public class Constants {
USER_AVATAR_FOLDER("/hoj/file/avatar"),
MARKDOWN_IMG_FOLDER("/hoj/file/md"),
MARKDOWN_FILE_FOLDER("/hoj/file/md"),
IMG_API("/api/public/img/"),
FILE_API("/api/public/file/"),
TESTCASE_TMP_FOLDER("/hoj/file/zip"),
TESTCASE_BASE_FOLDER("/hoj/testcase"),

View File

@ -15,6 +15,7 @@ spring:
multipart:
max-file-size: 128MB
max-request-size: 128MB
redis:
host: ${hoj.redis.host:${REDIS_HOST:172.20.0.2}}
port: ${hoj.redis.port:${REDIS_PORT:6379}}

View File

@ -0,0 +1,16 @@
${AnsiColor.BRIGHT_YELLOW}
,--, ,----..
,--.'| ,----.. / / \ ,---, ,---,.
,--, | : / / \ / . : .' .' `\ ,' .' |
,---.'| : '| : : . / ;. \,---.' \ ,---.' |
| | : _' |. | ;. /. ; / ` ;| | .`\ || | .'
: : |.' |. ; /--` ; | ; \ ; |: : | ' |: : |-,
| ' ' ; :; | ; | : | ; | '| ' ' ; :: | ;/|
' | .'. || : | . | ' ' ' :' | ; . || : .'
| | : | '. | '___ ' ; \; / || | : | '| | |-,
' : | : ;' ; : .'| \ \ ', / ' : | / ; ' : ;/|
| | ' ,/ ' | '/ : ; : / | | '` ,/ | | \
; : ;--' | : / \ \ .' ; : .' | : .'
| ,/ \ \ .' `---` | ,.' | | ,' @Author Himit_ZH
'---' `---` '---' `----'

View File

@ -3,6 +3,9 @@ hoj-backstage:
nacos-url: ${NACOS_URL:172.20.0.4:8848} # nacos的地址
server:
port: ${hoj-backstage.port}
servlet:
encoding:
force: true
spring:
profiles:
active: prod

View File

@ -16,7 +16,7 @@ public class RestTemplateConfig {
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(10000);//单位为ms
factory.setReadTimeout(90000);//单位为ms
factory.setConnectTimeout(10000);//单位为ms
return factory;
}

View File

@ -1,20 +0,0 @@
package top.hcode.hoj.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import top.hcode.hoj.pojo.entity.ProblemCount;
/**
* <p>
* Mapper 接口
* </p>
*
* @author Himit_ZH
* @since 2020-10-23
*/
@Mapper
@Repository
public interface ProblemCountMapper extends BaseMapper<ProblemCount> {
}

View File

@ -83,7 +83,7 @@ public class SandboxRun {
static {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(20000);
requestFactory.setReadTimeout(20000);
requestFactory.setReadTimeout(180000);
restTemplate = new RestTemplate(requestFactory);
}

View File

@ -1,19 +0,0 @@
package top.hcode.hoj.service;
import com.baomidou.mybatisplus.extension.service.IService;
import top.hcode.hoj.pojo.entity.Judge;
import top.hcode.hoj.pojo.entity.ProblemCount;
/**
* <p>
* 服务类
* </p>
*
* @author Himit_ZH
* @since 2020-10-23
*/
public interface ProblemCountService extends IService<ProblemCount> {
void updateCount(int status, Long pid);
}

View File

@ -36,14 +36,9 @@ public class JudgeServiceImpl extends ServiceImpl<JudgeMapper, Judge> implements
@Autowired
private JudgeStrategy judgeStrategy;
@Autowired
private ProblemCountServiceImpl problemCountService;
@Autowired
private UserAcproblemServiceImpl userAcproblemService;
@Autowired
private UserRecordServiceImpl userRecordService;
@Autowired
private ContestRecordServiceImpl contestRecordService;
@ -99,14 +94,6 @@ public class JudgeServiceImpl extends ServiceImpl<JudgeMapper, Judge> implements
.setSubmitId(submitId)
);
}
// 比赛的提交不纳入更新该提交对应题目的数据
problemCountService.updateCount(status, pid);
// 如果是非比赛提交且为OI题目的提交需要判断是否更新用户得分
if (score != null) {
userRecordService.updateRecord(uid, submitId, pid, score);
}
} else { //如果是比赛提交
contestRecordService.UpdateContestRecord(uid, score, status, submitId, cid);

View File

@ -1,134 +0,0 @@
package top.hcode.hoj.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import top.hcode.hoj.dao.ProblemCountMapper;
import top.hcode.hoj.pojo.entity.Judge;
import top.hcode.hoj.pojo.entity.ProblemCount;
import top.hcode.hoj.pojo.entity.UserRecord;
import top.hcode.hoj.service.ProblemCountService;
import top.hcode.hoj.util.Constants;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* <p>
* 服务实现类
* </p>
*
* @author Himit_ZH
* @since 2020-10-23
*/
@Service
public class ProblemCountServiceImpl extends ServiceImpl<ProblemCountMapper, ProblemCount> implements ProblemCountService {
@Autowired
private ProblemCountMapper problemCountMapper;
// 默认的事务隔离等级可重复读会产生幻读读不到新的version数据所以需要更换等级为读已提交
@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
@Async
public void updateCount(int status, Long pid) {
// 更新problem_count
QueryWrapper<ProblemCount> problemCountQueryWrapper = new QueryWrapper<ProblemCount>();
problemCountQueryWrapper.eq("pid", pid);
ProblemCount problemCount = problemCountMapper.selectOne(problemCountQueryWrapper);
ProblemCount newProblemCount = getNewProblemCount(status, problemCount);
newProblemCount.setVersion(problemCount.getVersion());
int num = problemCountMapper.updateById(newProblemCount);
if (num == 1) {
return;
} else {
// 进行重试操作
tryAgainUpdate(status, pid);
}
}
@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
public boolean tryAgainUpdate(int status, Long pid) {
boolean retryable;
int attemptNumber = 0;
do {
// 查询最新版本号
QueryWrapper<ProblemCount> problemCountQueryWrapper = new QueryWrapper<ProblemCount>();
problemCountQueryWrapper.eq("pid", pid);
ProblemCount problemCount = problemCountMapper.selectOne(problemCountQueryWrapper);
// 更新
ProblemCount newProblemCount = getNewProblemCount(status, problemCount);
newProblemCount.setVersion(problemCount.getVersion());
boolean success = problemCountMapper.updateById(newProblemCount) == 1;
if (success) {
return true;
} else {
attemptNumber++;
retryable = attemptNumber < 8;
if (attemptNumber == 8) {
log.error("超过最大重试次数");
break;
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
log.error(e.getMessage());
}
}
} while (retryable);
return false;
}
public ProblemCount getNewProblemCount(int status, ProblemCount oldProblemCount) {
ProblemCount newProblemCount = new ProblemCount();
newProblemCount.setPid(oldProblemCount.getPid()).setTotal(oldProblemCount.getTotal() + 1);
Constants.Judge type = Constants.Judge.getTypeByStatus(status);
switch (type) {
case STATUS_ACCEPTED:
newProblemCount.setAc(oldProblemCount.getAc() + 1);
break;
case STATUS_MEMORY_LIMIT_EXCEEDED:
newProblemCount.setMle(oldProblemCount.getMle() + 1);
break;
case STATUS_TIME_LIMIT_EXCEEDED:
newProblemCount.setTle(oldProblemCount.getTle() + 1);
break;
case STATUS_RUNTIME_ERROR:
newProblemCount.setRe(oldProblemCount.getRe() + 1);
break;
case STATUS_PRESENTATION_ERROR:
newProblemCount.setPe(oldProblemCount.getPe() + 1);
break;
case STATUS_COMPILE_ERROR:
newProblemCount.setCe(oldProblemCount.getCe() + 1);
break;
case STATUS_WRONG_ANSWER:
newProblemCount.setWa(oldProblemCount.getWa() + 1);
break;
case STATUS_SYSTEM_ERROR:
newProblemCount.setSe(oldProblemCount.getSe() + 1);
break;
case STATUS_PARTIAL_ACCEPTED:
newProblemCount.setPa(oldProblemCount.getPa() + 1);
break;
default:
break;
}
return newProblemCount;
}
}

View File

@ -31,10 +31,17 @@ public class UserRecordServiceImpl extends ServiceImpl<UserRecordMapper, UserRec
@Autowired
private JudgeServiceImpl judgeService;
// 默认的事务隔离等级可重复读会产生幻读读不到新的version数据所以需要更换等级为读已提交
/**
* @MethodNameupdateRecord
* @Params * @param null
* @Description 本方法启用不适合数据一致性
* @Return
* @Since 2021/6/2
*/
@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
@Async
@Override
@Deprecated
public void updateRecord(String uid, Long submitId, Long pid, Integer score) {
QueryWrapper<Judge> judgeQueryWrapper = new QueryWrapper<>();
judgeQueryWrapper.isNotNull("score")

View File

@ -0,0 +1,16 @@
${AnsiColor.BRIGHT_YELLOW}
,--, ,----..
,--.'| ,----.. / / \ ,---, ,---,.
,--, | : / / \ / . : .' .' `\ ,' .' |
,---.'| : '| : : . / ;. \,---.' \ ,---.' |
| | : _' |. | ;. /. ; / ` ;| | .`\ || | .'
: : |.' |. ; /--` ; | ; \ ; |: : | ' |: : |-,
| ' ' ; :; | ; | : | ; | '| ' ' ; :: | ;/|
' | .'. || : | . | ' ' ' :' | ; . || : .'
| | : | '. | '___ ' ; \; / || | : | '| | |-,
' : | : ;' ; : .'| \ \ ', / ' : | / ; ' : ;/|
| | ' ,/ ' | '/ : ; : / | | '` ,/ | | \
; : ;--' | : / \ \ .' ; : .' | : .'
| ,/ \ \ .' `---` | ,.' | | ,' @Author Himit_ZH
'---' `---` '---' `----'

View File

@ -95,6 +95,9 @@ public class Problem implements Serializable {
@ApiModelProperty(value = "是否默认开启该题目的测试样例结果查看")
private Boolean openCaseResult;
@ApiModelProperty(value = "题目测试数据是否是上传的")
private Boolean isUploadCase;
@ApiModelProperty(value = "题目测试数据的版本号")
private String caseVersion;

View File

@ -32,20 +32,10 @@ public class UserRecord implements Serializable {
@ApiModelProperty(value = "用户id")
private String uid;
@ApiModelProperty(value = "总提交数")
private Integer submissions;
@ApiModelProperty(value = "cf得分")
@TableField("rating")
private Integer rating;
@ApiModelProperty(value = "IO题目总得分")
private Integer totalScore;
@ApiModelProperty(value = "乐观锁")
@Version
private Integer version;
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;

View File

@ -30,7 +30,7 @@ export default {
},
data() {
return {
isOpen: true,
isOpen: false,
};
},
};

View File

@ -42,7 +42,7 @@ export default {
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
indentUnit: 4, //
styleActiveLine: true,
autofocus: true,
autofocus: false,
matchBrackets: true, //
styleActiveLine: true,
autoCloseBrackets: true,

View File

@ -6,6 +6,7 @@
type="search"
size="medium"
@search-click="filterByKeyword"
@keyup.enter.native="filterByKeyword"
style="margin-bottom:10px"
></vxe-input>
<vxe-table

View File

@ -8,10 +8,31 @@
:autofocus="false"
v-model="currentValue"
codeStyle="arduino-light"
></mavon-editor>
>
<template v-slot:left-toolbar-after v-if="isAdminRole">
<button
type="button"
title="文件上传"
class="op-icon fa markdown-upload"
aria-hidden="true"
@click="uploadFile"
>
<!-- 这里用的是element-ui给出的图标 -->
<i class="el-icon-upload" />
</button>
</template>
</mavon-editor>
<!-- 在这里放一个隐藏的input用来选择文件 -->
<input
ref="uploadInput"
style="display: none"
type="file"
@change="uploadFileChange"
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import { addCodeBtn } from '@/common/codeblock';
export default {
name: 'Editor',
@ -53,6 +74,34 @@ export default {
},
});
},
uploadFile() {
// refinput
this.$refs.uploadInput.click();
},
// input
uploadFileChange(e) {
// input
const file = e.target.files[0];
// formform
const formdata = new FormData();
formdata.append('file', file);
this.$http({
url: '/api/file/upload-md-file',
method: 'post',
data: formdata,
headers: { 'Content-Type': 'multipart/form-data' },
}).then((res) => {
// mavon
const $vm = this.$refs.md;
// mavon-editor
$vm.insertText($vm.getTextareaDom(), {
prefix: `[${file.name}](${res.data.data.link})`,
subfix: '',
str: '',
});
});
},
},
watch: {
value(val) {
@ -69,6 +118,9 @@ export default {
}
},
},
computed: {
...mapGetters(['isAdminRole']),
},
};
</script>
<style>

View File

@ -366,15 +366,14 @@
</div>
<el-switch
v-model="isUploadTestCase"
@change="changeSampleUploadMethod"
v-model="problem.isUploadCase"
active-text="Use Upload File"
inactive-text="Use Manual Input"
style="margin: 10px 0"
>
</el-switch>
<div v-show="isUploadTestCase">
<div v-show="problem.isUploadCase">
<el-col :span="24">
<el-form-item :error="error.testcase">
<el-upload
@ -416,7 +415,7 @@
</el-col>
</div>
<div v-show="!isUploadTestCase">
<div v-show="!problem.isUploadCase">
<el-form-item
v-for="(sample, index) in problemSamples"
:key="'sample' + index"
@ -590,6 +589,7 @@ export default {
uploadTestcaseDir: '',
testCaseScore: [],
isRemote: false,
isUploadCase: true,
type: 0,
hint: '',
source: '',
@ -610,7 +610,6 @@ export default {
spjMode: '',
disableRuleType: false,
routeName: '',
isUploadTestCase: true,
uploadTestcaseDir: '',
uploadFileUrl: '',
error: {
@ -660,6 +659,7 @@ export default {
spjLanguage: '',
spjCode: '',
spjCompileOk: false,
isUploadCase: true,
uploadTestcaseDir: '',
testCaseScore: [],
contestProblem: {},
@ -699,6 +699,8 @@ export default {
this.problemTags = []; //
this.problemLanguages = []; //
this.problemSamples = [];
this.problemCodeTemplate = [];
this.codeTemplate = [];
this.init();
},
@ -771,6 +773,14 @@ export default {
this.problem = data;
this.problem['examples'] = utils.stringToExamples(data.examples);
this.testCaseUploaded = true;
api.admin_getProblemCases(this.pid).then((res) => {
if (this.problem.isUploadCase) {
this.problem.testCaseScore = res.data.data;
} else {
this.problemSamples = res.data.data;
}
});
});
if (funcName === 'admin_getContestProblem') {
api
@ -781,11 +791,6 @@ export default {
}
this.getProblemCodeTemplateAndLanguage();
if (!this.isUploadTestCase) {
api.admin_getProblemCases(this.pid).then((res) => {
this.problemSamples = res.data.data;
});
}
api.admin_getProblemTags(this.pid).then((res) => {
this.problemTags = res.data.data;
});
@ -810,30 +815,6 @@ export default {
});
},
changeSampleUploadMethod() {
if (
!this.isUploadTestCase &&
this.problemSamples.length == 0 &&
this.mode == 'edit'
) {
this.$confirm(
'你确定要获取显示该题目的评测数据可能该题目是使用zip上传那么数据库默认无该测试数据',
'注意',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
api.admin_getProblemCases(this.pid).then((res) => {
this.problemSamples = res.data.data;
myMessage.success(res.data.msg);
});
})
.catch(() => {});
}
},
switchSpj() {
if (this.testCaseUploaded) {
this.$confirm(
@ -1024,7 +1005,7 @@ export default {
}
if (!this.problem.isRemote) {
//
if (!this.isUploadTestCase) {
if (!this.problem.isUploadCase) {
if (!this.problemSamples.length) {
myMessage.error('评测数据不能为空!请手动输入评测数据!');
return;
@ -1159,11 +1140,11 @@ export default {
problemDto['codeTemplates'] = this.problemCodeTemplate;
problemDto['tags'] = problemTagList;
problemDto['languages'] = problemLanguageList;
problemDto['isUploadTestCase'] = this.isUploadTestCase;
problemDto['isUploadTestCase'] = this.problem.isUploadCase;
problemDto['uploadTestcaseDir'] = this.problem.uploadTestcaseDir;
problemDto['isSpj'] = this.problem.spj;
// 使
if (this.isUploadTestCase) {
if (this.problem.isUploadCase) {
problemDto['samples'] = this.problem.testCaseScore;
} else {
problemDto['samples'] = this.problemSamples;

View File

@ -153,7 +153,7 @@
<p class="title">Hint</p>
<el-card dis-hover>
<div
class="content"
class="hint-content"
v-html="problemData.problem.hint"
v-katex
v-highlight
@ -928,6 +928,10 @@ a {
padding-left: 8px;
}
.hint-content {
font-size: 15px !important;
}
p.content {
margin-left: 25px;
margin-right: 20px;
@ -941,6 +945,7 @@ p.content {
align-items: flex-start;
flex-flow: row nowrap;
}
.example {
align-items: stretch;
}