|
@@ -0,0 +1,177 @@
|
|
|
|
|
+package space.anyi.serve.service.impl;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+import space.anyi.codesandbox.CodeContext;
|
|
|
|
|
+import space.anyi.codesandbox.CodeResponse;
|
|
|
|
|
+import space.anyi.codesandbox.CodeSandBox;
|
|
|
|
|
+import space.anyi.codesandbox.TestCase;
|
|
|
|
|
+import space.anyi.serve.entity.contest.Contest;
|
|
|
|
|
+import space.anyi.serve.entity.contest.ContestProblem;
|
|
|
|
|
+import space.anyi.serve.entity.contest.Submission;
|
|
|
|
|
+import space.anyi.serve.entity.contest.SubmissionVo;
|
|
|
|
|
+import space.anyi.serve.entity.contest.SubmitDto;
|
|
|
|
|
+import space.anyi.serve.entity.problem.Problem;
|
|
|
|
|
+import space.anyi.serve.entity.problem.ProblemTest;
|
|
|
|
|
+import space.anyi.serve.entity.user.User;
|
|
|
|
|
+import space.anyi.serve.mapper.ContestProblemMapper;
|
|
|
|
|
+import space.anyi.serve.mapper.ProblemTestMapper;
|
|
|
|
|
+import space.anyi.serve.mapper.SubmissionMapper;
|
|
|
|
|
+import space.anyi.serve.service.ContestService;
|
|
|
|
|
+import space.anyi.serve.service.ProblemService;
|
|
|
|
|
+import space.anyi.serve.service.SubmissionService;
|
|
|
|
|
+import space.anyi.serve.util.SecurityUtils;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Objects;
|
|
|
|
|
+
|
|
|
|
|
+@Service
|
|
|
|
|
+public class SubmissionServiceImpl extends ServiceImpl<SubmissionMapper, Submission> implements SubmissionService {
|
|
|
|
|
+
|
|
|
|
|
+ private final SubmissionMapper submissionMapper;
|
|
|
|
|
+ private final ProblemTestMapper problemTestMapper;
|
|
|
|
|
+ private final ContestProblemMapper contestProblemMapper;
|
|
|
|
|
+ private final ProblemService problemService;
|
|
|
|
|
+ private final ContestService contestService;
|
|
|
|
|
+ private final CodeSandBox codeSandBox;
|
|
|
|
|
+
|
|
|
|
|
+ public SubmissionServiceImpl(SubmissionMapper submissionMapper,
|
|
|
|
|
+ ProblemTestMapper problemTestMapper,
|
|
|
|
|
+ ContestProblemMapper contestProblemMapper,
|
|
|
|
|
+ ProblemService problemService,
|
|
|
|
|
+ ContestService contestService,
|
|
|
|
|
+ CodeSandBox codeSandBox) {
|
|
|
|
|
+ this.submissionMapper = submissionMapper;
|
|
|
|
|
+ this.problemTestMapper = problemTestMapper;
|
|
|
|
|
+ this.contestProblemMapper = contestProblemMapper;
|
|
|
|
|
+ this.problemService = problemService;
|
|
|
|
|
+ this.contestService = contestService;
|
|
|
|
|
+ this.codeSandBox = codeSandBox;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional
|
|
|
|
|
+ public SubmissionVo submit(SubmitDto dto, Long userId) {
|
|
|
|
|
+ Problem problem = problemService.getById(dto.getProblemId());
|
|
|
|
|
+ if (problem == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("题目不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dto.getContestId() != null) {
|
|
|
|
|
+ validateContestSubmission(dto.getContestId(), userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ List<ProblemTest> problemTests = problemTestMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<ProblemTest>()
|
|
|
|
|
+ .eq(ProblemTest::getProblemId, dto.getProblemId())
|
|
|
|
|
+ .orderByAsc(ProblemTest::getSortOrder));
|
|
|
|
|
+ if (problemTests.isEmpty()) {
|
|
|
|
|
+ throw new IllegalArgumentException("该题目还没有评测用例,无法提交");
|
|
|
|
|
+ }
|
|
|
|
|
+ Submission submission = new Submission();
|
|
|
|
|
+ submission.setProblemId(dto.getProblemId());
|
|
|
|
|
+ submission.setUserId(userId);
|
|
|
|
|
+ submission.setContestId(dto.getContestId());
|
|
|
|
|
+ submission.setCode(dto.getCode());
|
|
|
|
|
+ submission.setLanguage(dto.getLanguage());
|
|
|
|
|
+ submission.setStatus(Submission.STATUS_JUDGING);
|
|
|
|
|
+ submission.setTotalTestCases(problemTests.size());
|
|
|
|
|
+ submission.setPassedTestCases(0);
|
|
|
|
|
+ submissionMapper.insert(submission);
|
|
|
|
|
+
|
|
|
|
|
+ List<TestCase> testCases = new ArrayList<>();
|
|
|
|
|
+ for (ProblemTest problemTest : problemTests) {
|
|
|
|
|
+ testCases.add(new TestCase(problemTest.getId(), problemTest.getInput(), problemTest.getOutput()));
|
|
|
|
|
+ }
|
|
|
|
|
+ CodeContext context = new CodeContext(submission.getId(), dto.getCode(), dto.getLanguage(),
|
|
|
|
|
+ problem.getTimeLimitMs().longValue(), problem.getMemoryLimitMb() * 1024L, testCases);
|
|
|
|
|
+ CodeResponse response = codeSandBox.process(context);
|
|
|
|
|
+
|
|
|
|
|
+ submission.setStatus(response.status());
|
|
|
|
|
+ submission.setTimeUsedMs(response.usedTime() == null ? 0 : response.usedTime().intValue());
|
|
|
|
|
+ submission.setMemoryUsedKb(response.usedMemory() == null ? 0 : response.usedMemory().intValue());
|
|
|
|
|
+ submission.setJudgeDetail(response.message());
|
|
|
|
|
+ if (response.testCaseResponses() != null) {
|
|
|
|
|
+ long passed = response.testCaseResponses().stream()
|
|
|
|
|
+ .filter(c -> Submission.STATUS_AC.equals(c.status()))
|
|
|
|
|
+ .count();
|
|
|
|
|
+ submission.setPassedTestCases((int) passed);
|
|
|
|
|
+ }
|
|
|
|
|
+ submission.setScore(resolveScore(dto.getContestId(), submission, problem));
|
|
|
|
|
+ submissionMapper.updateById(submission);
|
|
|
|
|
+
|
|
|
|
|
+ problem.setTryCount(problem.getTryCount() == null ? 1 : problem.getTryCount() + 1);
|
|
|
|
|
+ if (Submission.STATUS_AC.equals(submission.getStatus())) {
|
|
|
|
|
+ problem.setAcceptedCount(problem.getAcceptedCount() == null ? 1 : problem.getAcceptedCount() + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ problemService.updateById(problem);
|
|
|
|
|
+
|
|
|
|
|
+ return SubmissionVo.from(submission);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public SubmissionVo getDetail(Long submissionId, Long userId) {
|
|
|
|
|
+ Submission submission = submissionMapper.selectById(submissionId);
|
|
|
|
|
+ if (submission == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("提交记录不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ String role = SecurityUtils.getCurrentRole();
|
|
|
|
|
+ if (!User.ROLE_ADMIN.equals(role) && !User.ROLE_COACH.equals(role)
|
|
|
|
|
+ && !Objects.equals(submission.getUserId(), userId)) {
|
|
|
|
|
+ throw new IllegalArgumentException("无权限查看该提交记录");
|
|
|
|
|
+ }
|
|
|
|
|
+ return SubmissionVo.from(submission);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Page<SubmissionVo> queryMySubmissions(Long userId, Integer pageNum, Integer pageSize) {
|
|
|
|
|
+ Page<Submission> page = page(new Page<>(pageNum, pageSize),
|
|
|
|
|
+ new LambdaQueryWrapper<Submission>()
|
|
|
|
|
+ .eq(Submission::getUserId, userId)
|
|
|
|
|
+ .orderByDesc(Submission::getCreatedAt));
|
|
|
|
|
+ return convertToVoPage(page);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Page<SubmissionVo> queryByProblemId(Long problemId, Integer pageNum, Integer pageSize) {
|
|
|
|
|
+ Page<Submission> page = page(new Page<>(pageNum, pageSize),
|
|
|
|
|
+ new LambdaQueryWrapper<Submission>()
|
|
|
|
|
+ .eq(Submission::getProblemId, problemId)
|
|
|
|
|
+ .orderByDesc(Submission::getCreatedAt));
|
|
|
|
|
+ return convertToVoPage(page);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void validateContestSubmission(Long contestId, Long userId) {
|
|
|
|
|
+ Contest contest = contestService.getById(contestId);
|
|
|
|
|
+ if (contest == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("比赛不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!Contest.STATUS_RUNNING.equals(contest.getStatus())) {
|
|
|
|
|
+ throw new IllegalArgumentException("比赛未在进行中");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!contestService.isRegistered(contestId, userId)) {
|
|
|
|
|
+ throw new IllegalArgumentException("未报名该比赛");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Integer resolveScore(Long contestId, Submission submission, Problem problem) {
|
|
|
|
|
+ if (!Submission.STATUS_AC.equals(submission.getStatus())) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (contestId == null) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ ContestProblem contestProblem = contestProblemMapper.selectOne(new LambdaQueryWrapper<ContestProblem>()
|
|
|
|
|
+ .eq(ContestProblem::getContestId, contestId)
|
|
|
|
|
+ .eq(ContestProblem::getProblemId, submission.getProblemId()));
|
|
|
|
|
+ return contestProblem == null || contestProblem.getScore() == null ? 0 : contestProblem.getScore();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Page<SubmissionVo> convertToVoPage(Page<Submission> page) {
|
|
|
|
|
+ Page<SubmissionVo> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
|
|
|
|
|
+ result.setRecords(SubmissionVo.from(page.getRecords()));
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|