修复前端bug,添加OI比赛排行榜耗时计算

This commit is contained in:
Himit_ZH 2021-07-07 14:25:28 +08:00
parent 112320d424
commit 48259fd38d
26 changed files with 143 additions and 29 deletions

View File

@ -452,7 +452,6 @@ public class ContestController {
// 如果已经开启了封榜模式
.between(isOpenSealRank, "submit_time", contest.getStartTime(), contest.getSealRankTime())
.between(!isOpenSealRank, "submit_time", contest.getStartTime(), contest.getEndTime())
.ne("uid", "1")
.ne("username", contest.getAuthor())
.orderByAsc("time");

View File

@ -313,7 +313,7 @@ public class JudgeController {
}
}
// 超级管理员与管理员有权限查看代码
// 超级管理员与题目管理员有权限查看代码
// 如果不是本人或者并未分享代码则不可查看
// 当此次提交代码不共享
// 比赛提交只有比赛创建者和root账号可看代码
@ -332,8 +332,7 @@ public class JudgeController {
judge.setCode(null);
}
}else {
boolean admin = SecurityUtils.getSubject().hasRole("admin")
|| SecurityUtils.getSubject().hasRole("problem_admin");// 是否为管理员
boolean admin = SecurityUtils.getSubject().hasRole("problem_admin");// 是否为题目管理员
if (!judge.getShare() && !root && !admin) {
if (userRolesVo != null) { // 当前是登陆状态
// 需要判断是否为当前登陆用户自己的提交代码

View File

@ -6,6 +6,8 @@ import top.hcode.hoj.pojo.dto.RegisterDto;
import top.hcode.hoj.pojo.entity.UserInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
/**
* <p>
@ -19,4 +21,6 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@Repository
public interface UserInfoMapper extends BaseMapper<UserInfo> {
int addUser(RegisterDto registerDto);
List<UserInfo> getSuperAdminList();
}

View File

@ -31,7 +31,7 @@
SELECT cr.* FROM
(SELECT uid,pid,cpid,MAX(time) AS time FROM contest_record
<where>
cid=#{cid} AND status IS NOT NULL AND uid!='1'
cid=#{cid} AND status IS NOT NULL
<if test="contestAuthor!=null">
AND username!=#{contestAuthor}
</if>

View File

@ -6,4 +6,7 @@
#{uuid}, #{username},#{password},#{email}
)
</insert>
<select id="getSuperAdminList" resultType="top.hcode.hoj.pojo.entity.UserInfo" useCache="true">
select * from user_info u,user_role ur where u.uuid = ur.uid and ur.role_id = 1000
</select>
</mapper>

View File

@ -27,6 +27,12 @@ public class OIContestRankVo {
@ApiModelProperty(value = "提交总得分")
private Integer totalScore;
@ApiModelProperty(value = "提交总耗时,只有满分的提交才会统计")
private Integer totalTime;
@ApiModelProperty(value = "OI的题对应提交得分")
private HashMap<String, Integer> submissionInfo;
@ApiModelProperty(value = "OI的题得满分后对应提交最优耗时")
private HashMap<String, Integer> timeInfo;
}

View File

@ -1,6 +1,7 @@
package top.hcode.hoj.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import top.hcode.hoj.pojo.entity.UserInfo;
import top.hcode.hoj.pojo.vo.ACMContestRankVo;
import top.hcode.hoj.pojo.entity.ContestRecord;
import com.baomidou.mybatisplus.extension.service.IService;
@ -27,4 +28,7 @@ public interface ContestRecordService extends IService<ContestRecord> {
List<ContestRecord> getOIContestRecord(Long cid, String contestAuthor, Boolean isOpenSealRank, Date sealTime, Date startTime, Date endTime);
List<UserInfo> getSuperAdminList();
}

View File

@ -1,8 +1,11 @@
package top.hcode.hoj.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import top.hcode.hoj.dao.UserInfoMapper;
import top.hcode.hoj.pojo.entity.UserInfo;
import top.hcode.hoj.pojo.vo.ACMContestRankVo;
import top.hcode.hoj.pojo.entity.ContestRecord;
import top.hcode.hoj.dao.ContestRecordMapper;
@ -30,6 +33,9 @@ public class ContestRecordServiceImpl extends ServiceImpl<ContestRecordMapper, C
@Autowired
private ContestRecordMapper contestRecordMapper;
@Autowired
private UserInfoMapper userInfoMapper;
@Override
public IPage<ContestRecord> getACInfo(Integer currentPage, Integer limit, Integer status, Long cid) {
@ -37,6 +43,11 @@ public class ContestRecordServiceImpl extends ServiceImpl<ContestRecordMapper, C
return page.setRecords(contestRecordMapper.getACInfo(page, status, cid));
}
@Override
public List<UserInfo> getSuperAdminList() {
return userInfoMapper.getSuperAdminList();
}
@Override
public IPage<ACMContestRankVo> getContestACMRank(List<ContestRecord> contestRecordList, int currentPage, int limit) {
@ -96,7 +107,13 @@ public class ContestRecordServiceImpl extends ServiceImpl<ContestRecordMapper, C
return contestRecordMapper.getOIContestRecord(cid, contestAuthor, isOpenSealRank, sealTime, startTime, endTime);
}
public List<ACMContestRankVo> calcACMRank(List<ContestRecord> contestRecordList) {
List<UserInfo> superAdminList = getSuperAdminList();
List<String> superAdminUidList = superAdminList.stream().map(UserInfo::getUuid).collect(Collectors.toList());
List<ACMContestRankVo> result = new ArrayList<>();
HashMap<String, Integer> uidMapIndex = new HashMap<>();
@ -106,6 +123,11 @@ public class ContestRecordServiceImpl extends ServiceImpl<ContestRecordMapper, C
HashMap<String, Long> firstACMap = new HashMap<>();
for (ContestRecord contestRecord : contestRecordList) {
if (superAdminUidList.contains(contestRecord.getUid())) { // 超级管理员的提交不入排行榜
continue;
}
ACMContestRankVo ACMContestRankVo;
if (!uidMapIndex.containsKey(contestRecord.getUid())) { // 如果该用户信息没还记录
@ -182,15 +204,44 @@ public class ContestRecordServiceImpl extends ServiceImpl<ContestRecordMapper, C
}
public List<OIContestRankVo> calcOIRank(List<ContestRecord> oiContestRecord) {
List<UserInfo> superAdminList = getSuperAdminList();
List<String> superAdminUidList = superAdminList.stream().map(UserInfo::getUuid).collect(Collectors.toList());
List<OIContestRankVo> result = new ArrayList<>();
HashMap<String, Integer> uidMapIndex = new HashMap<>();
HashMap<String, HashMap<String, Integer>> uidMapTime = new HashMap<>();
int index = 0;
for (ContestRecord contestRecord : oiContestRecord) {
if (superAdminUidList.contains(contestRecord.getUid())) { // 超级管理员的提交不入排行榜
continue;
}
if (contestRecord.getStatus() == 1) { // AC
HashMap<String, Integer> pidMapTime = uidMapTime.get(contestRecord.getUid());
if (pidMapTime != null) {
Integer useTime = pidMapTime.get(contestRecord.getDisplayId());
if (useTime != null) {
if (useTime > contestRecord.getUseTime()) { // 如果时间消耗比原来的少
pidMapTime.put(contestRecord.getDisplayId(), contestRecord.getUseTime());
}
} else {
pidMapTime.put(contestRecord.getDisplayId(), contestRecord.getUseTime());
}
} else {
HashMap<String, Integer> tmp = new HashMap<>();
tmp.put(contestRecord.getDisplayId(), contestRecord.getUseTime());
uidMapTime.put(contestRecord.getUid(), tmp);
}
}
OIContestRankVo oiContestRankVo;
if (!uidMapIndex.containsKey(contestRecord.getUid())) { // 如果该用户信息没还记录
// 初始化参数
oiContestRankVo = new OIContestRankVo();
oiContestRankVo.setRealname(contestRecord.getRealname())
@ -218,9 +269,24 @@ public class ContestRecordServiceImpl extends ServiceImpl<ContestRecordMapper, C
}
// 根据总得分进行降序排序
for (OIContestRankVo oiContestRankVo : result) {
HashMap<String, Integer> pidMapTime = uidMapTime.get(oiContestRankVo.getUid());
int sumTime = 0;
if (pidMapTime != null) {
for (String key : pidMapTime.keySet()) {
Integer time = pidMapTime.get(key);
sumTime += time == null ? 0 : time;
}
}
oiContestRankVo.setTotalTime(sumTime);
oiContestRankVo.setTimeInfo(pidMapTime);
}
// 根据总得分进行降序,再根据总时耗升序排序
List<OIContestRankVo> orderResultList = result.stream()
.sorted(Comparator.comparing(OIContestRankVo::getTotalScore, Comparator.reverseOrder()))
.sorted(Comparator.comparing(OIContestRankVo::getTotalScore, Comparator.reverseOrder())
.thenComparing(OIContestRankVo::getTotalTime,Comparator.naturalOrder()))
.collect(Collectors.toList());
return orderResultList;
}

View File

@ -136,7 +136,8 @@ public class JudgeController {
finalJudge.getCid(),
finalJudge.getUid(),
finalJudge.getPid(),
finalJudge.getScore());
finalJudge.getScore(),
finalJudge.getTime());
}
@ -165,7 +166,7 @@ public class JudgeController {
public CommonResult remoteJudge(@RequestBody ToJudge toJudge) {
if (!openRemoteJudge) {
return CommonResult.errorResponse("对不起!该判题服务器未开启远程虚拟判题功能!",CommonResult.STATUS_ACCESS_DENIED);
return CommonResult.errorResponse("对不起!该判题服务器未开启远程虚拟判题功能!", CommonResult.STATUS_ACCESS_DENIED);
}
if (!toJudge.getToken().equals(judgeToken)) {

View File

@ -78,13 +78,13 @@ public class RemoteJudgeGetResult {
// 写回数据库
judgeService.updateById(judge);
// 同步其它表
judgeService.updateOtherTable(submitId, status, cid, uid, pid, score);
judgeService.updateOtherTable(submitId, status, cid, uid, pid, score, judge.getTime());
} else {
judgeService.updateById(judge);
// 同步其它表
judgeService.updateOtherTable(submitId, status, cid, uid, pid, null);
judgeService.updateOtherTable(submitId, status, cid, uid, pid, null, null);
}
scheduler.shutdown();

View File

@ -83,6 +83,7 @@ public class RemoteJudgeToSubmit {
cid,
uid,
pid,
null,
null);
log.error("网络错误---------------->获取不到提交ID");
return;

View File

@ -71,6 +71,7 @@ public class POJJudge implements RemoteJudgeStrategy {
TimeUnit.SECONDS.sleep(2);
maxRunId = getMaxRunId(request, username, problemId);
}
return MapUtil.builder(new HashMap<String, Object>())
.put("cookies", cookies)
.put("runId", maxRunId)
@ -85,6 +86,10 @@ public class POJJudge implements RemoteJudgeStrategy {
.addHeaders(headers);
HttpResponse response = request.execute();
if (response.getStatus() != 200) {
log.error(submitId + " error:{}", response.body());
}
String statusStr = ReUtil.get("<b>Result:</b>(.+?)</td>", response.body(), 1)
.replaceAll("<.*?>", "")
.trim();

View File

@ -13,5 +13,5 @@ import top.hcode.hoj.pojo.entity.Judge;
* @since 2020-10-23
*/
public interface ContestRecordService extends IService<ContestRecord> {
void UpdateContestRecord(String uid, Integer score, Integer status, Long submitId, Long cid);
void UpdateContestRecord(String uid, Integer score, Integer status, Long submitId, Long cid, Integer useTime);
}

View File

@ -21,5 +21,5 @@ public interface JudgeService extends IService<Judge> {
Boolean compileSpj(String code, Long pid, String spjLanguage) throws CompileError, SystemError;
void updateOtherTable(Long submitId, Integer status, Long cid, String uid, Long pid, Integer score);
void updateOtherTable(Long submitId, Integer status, Long cid, String uid, Long pid, Integer score,Integer useTime);
}

View File

@ -42,7 +42,7 @@ public class ContestRecordServiceImpl extends ServiceImpl<ContestRecordMapper, C
@Override
@Async
@Transactional
public void UpdateContestRecord(String uid, Integer score, Integer status, Long submitId, Long cid) {
public void UpdateContestRecord(String uid, Integer score, Integer status, Long submitId, Long cid, Integer useTime) {
UpdateWrapper<ContestRecord> updateWrapper = new UpdateWrapper<>();
// 如果是AC
if (status.intValue() == Constants.Judge.STATUS_ACCEPTED.getStatus()) {
@ -64,6 +64,8 @@ public class ContestRecordServiceImpl extends ServiceImpl<ContestRecordMapper, C
updateWrapper.set("score", score);
}
updateWrapper.set("use_time", useTime);
updateWrapper.eq("submit_id", submitId) // submit_id一定只有一个
.eq("uid", uid);
boolean result = contestRecordMapper.update(null, updateWrapper) > 0;

View File

@ -48,7 +48,7 @@ public class JudgeServiceImpl extends ServiceImpl<JudgeMapper, Judge> implements
public Judge Judge(Problem problem, Judge judge) {
// c和c++为一倍时间和空间其它语言为2倍时间和空间
if (!judge.getLanguage().equals("C++") && !judge.getLanguage().equals("C")&&
if (!judge.getLanguage().equals("C++") && !judge.getLanguage().equals("C") &&
!judge.getLanguage().equals("C++ With O2") && !judge.getLanguage().equals("C With O2")) {
problem.setTimeLimit(problem.getTimeLimit() * 2);
problem.setMemoryLimit(problem.getMemoryLimit() * 2);
@ -84,7 +84,7 @@ public class JudgeServiceImpl extends ServiceImpl<JudgeMapper, Judge> implements
@Override
@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
public void updateOtherTable(Long submitId, Integer status, Long cid, String uid, Long pid, Integer score) {
public void updateOtherTable(Long submitId, Integer status, Long cid, String uid, Long pid, Integer score, Integer useTime) {
if (cid == 0) { // 非比赛提交
// 如果是AC,就更新user_acproblem表,
@ -98,7 +98,7 @@ public class JudgeServiceImpl extends ServiceImpl<JudgeMapper, Judge> implements
} else { //如果是比赛提交
contestRecordService.UpdateContestRecord(uid, score, status, submitId, cid);
contestRecordService.UpdateContestRecord(uid, score, status, submitId, cid, useTime);
}
}

View File

@ -68,6 +68,9 @@ public class ContestRecord implements Serializable {
@ApiModelProperty(value = "OI比赛的得分")
private Integer score;
@ApiModelProperty(value = "提交耗时")
private Integer useTime;
@ApiModelProperty(value = "是否为一血AC")
private Boolean firstBlood;

View File

@ -304,9 +304,6 @@ a:hover {
margin-top: 80px;
padding: 0 4%;
}
.vxe-table--body-wrapper {
overflow-x: hidden !important;
}
}
.markdown-body img {
max-width: 100%;

View File

@ -85,8 +85,8 @@ export default {
.admin_getContestProblemList(params)
.then((res) => {
this.loading = false;
this.total = res.data.data.total;
this.problems = res.data.data.records;
this.total = res.data.data.problemList.total;
this.problems = res.data.data.problemList.records;
})
.catch(() => {
this.loading = false;

View File

@ -61,6 +61,7 @@
></vxe-table-column>
<vxe-table-column
field="username"
fixed="left"
min-width="150"
:title="$t('m.User')"
>
@ -76,6 +77,7 @@
</vxe-table-column>
<vxe-table-column
field="realname"
fixed="left"
min-width="100"
:title="$t('m.RealName')"
v-if="isContestAdmin"
@ -83,6 +85,7 @@
</vxe-table-column>
<vxe-table-column
field="rating"
fixed="left"
:title="$t('m.AC') + ' / ' + $t('m.Total')"
min-width="80"
>
@ -99,6 +102,7 @@
</vxe-table-column>
<vxe-table-column
field="totalTime"
fixed="left"
:title="$t('m.TotalTime')"
min-width="80"
>

View File

@ -10,7 +10,7 @@
align="center"
:data="contestProblems"
>
<vxe-table-column field="pid" min-width="50" :title="$t('m.ID')">
<vxe-table-column field="pid" width="60" :title="$t('m.ID')">
</vxe-table-column>
<vxe-table-column
field="displayId"
@ -20,7 +20,8 @@
<vxe-table-column
field="displayTitle"
:title="$t('m.Title')"
min-width="200"
min-width="150"
show-overflow
>
</vxe-table-column>
<vxe-table-column field="ac" :title="$t('m.AC')" min-width="80">

View File

@ -50,6 +50,7 @@
auto-resize
size="small"
align="center"
ref="OIContestRank"
:data="dataRank"
:cell-class-name="cellClassName"
:seq-config="{ startIndex: (this.page - 1) * this.limit }"
@ -63,6 +64,7 @@
<vxe-table-column
field="username"
min-width="150"
fixed="left"
:title="$t('m.User')"
>
<template v-slot="{ row }">
@ -78,6 +80,7 @@
<vxe-table-column
field="realname"
min-width="100"
fixed="left"
:title="$t('m.RealName')"
v-if="isContestAdmin"
>
@ -85,6 +88,7 @@
<vxe-table-column
field="totalScore"
:title="$t('m.Total_Score')"
fixed="left"
min-width="80"
>
<template v-slot="{ row }">
@ -94,6 +98,8 @@
style="color:rgb(87, 163, 243);"
>{{ row.totalScore }}</a
>
<br />
<span class="problem-time">({{ row.totalTime }}ms)</span>
</span>
</template>
</vxe-table-column>
@ -112,9 +118,15 @@
>
</template>
<template v-slot="{ row }">
<span v-if="row.submissionInfo[problem.displayId]">{{
row.submissionInfo[problem.displayId]
}}</span>
<div v-if="row.submissionInfo[problem.displayId]">
<span>{{ row.submissionInfo[problem.displayId] }}</span>
<br />
<span
v-if="row.timeInfo && row.timeInfo[problem.displayId]"
style="font-size:12px;"
>({{ row.timeInfo[problem.displayId] }}ms)</span
>
</div>
</template>
</vxe-table-column>
</vxe-table>
@ -339,4 +351,8 @@ a.emphasis {
a.emphasis:hover {
color: #2d8cf0;
}
.problem-time {
color: rgba(0, 0, 0, 0.45);
font-size: 12px;
}
</style>

View File

@ -150,6 +150,7 @@
field="title"
:title="$t('m.Problem')"
min-width="180"
show-overflow
>
<template v-slot="{ row }">
<a @click="getProblemUri(row.problemId)" class="title-a">{{

View File

@ -52,7 +52,7 @@
<vxe-table-column
field="submitId"
:title="$t('m.Run_ID')"
min-width="100"
width="100"
></vxe-table-column>
<vxe-table-column :title="$t('m.Submit_Time')" min-width="150">
<template v-slot="{ row }">

View File

@ -219,6 +219,7 @@
<vxe-table-column
field="language"
:title="$t('m.Language')"
show-overflow
min-width="130"
>
<template v-slot="{ row }">

View File

@ -224,6 +224,7 @@ CREATE TABLE `contest_record` (
`submit_time` datetime NOT NULL COMMENT '具体提交时间',
`time` bigint(20) unsigned DEFAULT NULL COMMENT '提交时间,为提交时间减去比赛时间',
`score` int(11) DEFAULT NULL COMMENT 'OI比赛的得分',
`use_time` int(11) DEFAULT NULL COMMENT '运行耗时',
`first_blood` tinyint(1) DEFAULT '0' COMMENT '是否为一血AC',
`checked` tinyint(1) DEFAULT '0' COMMENT 'AC是否已校验',
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,