SubmissionServiceImpl.java 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package space.anyi.serve.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  5. import org.springframework.stereotype.Service;
  6. import org.springframework.transaction.annotation.Transactional;
  7. import space.anyi.codesandbox.CodeContext;
  8. import space.anyi.codesandbox.CodeResponse;
  9. import space.anyi.codesandbox.CodeSandBox;
  10. import space.anyi.codesandbox.TestCase;
  11. import space.anyi.serve.entity.contest.Contest;
  12. import space.anyi.serve.entity.contest.ContestProblem;
  13. import space.anyi.serve.entity.contest.Submission;
  14. import space.anyi.serve.entity.contest.SubmissionVo;
  15. import space.anyi.serve.entity.contest.SubmitDto;
  16. import space.anyi.serve.entity.problem.Problem;
  17. import space.anyi.serve.entity.problem.ProblemTest;
  18. import space.anyi.serve.entity.user.User;
  19. import space.anyi.serve.mapper.ContestProblemMapper;
  20. import space.anyi.serve.mapper.ProblemTestMapper;
  21. import space.anyi.serve.mapper.SubmissionMapper;
  22. import space.anyi.serve.service.ContestService;
  23. import space.anyi.serve.service.ProblemService;
  24. import space.anyi.serve.service.SubmissionService;
  25. import space.anyi.serve.util.SecurityUtils;
  26. import java.util.ArrayList;
  27. import java.util.List;
  28. import java.util.Objects;
  29. @Service
  30. public class SubmissionServiceImpl extends ServiceImpl<SubmissionMapper, Submission> implements SubmissionService {
  31. private final SubmissionMapper submissionMapper;
  32. private final ProblemTestMapper problemTestMapper;
  33. private final ContestProblemMapper contestProblemMapper;
  34. private final ProblemService problemService;
  35. private final ContestService contestService;
  36. private final CodeSandBox codeSandBox;
  37. public SubmissionServiceImpl(SubmissionMapper submissionMapper,
  38. ProblemTestMapper problemTestMapper,
  39. ContestProblemMapper contestProblemMapper,
  40. ProblemService problemService,
  41. ContestService contestService,
  42. CodeSandBox codeSandBox) {
  43. this.submissionMapper = submissionMapper;
  44. this.problemTestMapper = problemTestMapper;
  45. this.contestProblemMapper = contestProblemMapper;
  46. this.problemService = problemService;
  47. this.contestService = contestService;
  48. this.codeSandBox = codeSandBox;
  49. }
  50. @Override
  51. @Transactional
  52. public SubmissionVo submit(SubmitDto dto, Long userId) {
  53. Problem problem = problemService.getById(dto.getProblemId());
  54. if (problem == null) {
  55. throw new IllegalArgumentException("题目不存在");
  56. }
  57. if (dto.getContestId() != null) {
  58. validateContestSubmission(dto.getContestId(), userId);
  59. }
  60. List<ProblemTest> problemTests = problemTestMapper.selectList(
  61. new LambdaQueryWrapper<ProblemTest>()
  62. .eq(ProblemTest::getProblemId, dto.getProblemId())
  63. .orderByAsc(ProblemTest::getSortOrder));
  64. if (problemTests.isEmpty()) {
  65. throw new IllegalArgumentException("该题目还没有评测用例,无法提交");
  66. }
  67. Submission submission = new Submission();
  68. submission.setProblemId(dto.getProblemId());
  69. submission.setUserId(userId);
  70. submission.setContestId(dto.getContestId());
  71. submission.setCode(dto.getCode());
  72. submission.setLanguage(dto.getLanguage());
  73. submission.setStatus(Submission.STATUS_JUDGING);
  74. submission.setTotalTestCases(problemTests.size());
  75. submission.setPassedTestCases(0);
  76. submissionMapper.insert(submission);
  77. List<TestCase> testCases = new ArrayList<>();
  78. for (ProblemTest problemTest : problemTests) {
  79. testCases.add(new TestCase(problemTest.getId(), problemTest.getInput(), problemTest.getOutput()));
  80. }
  81. CodeContext context = new CodeContext(submission.getId(), dto.getCode(), dto.getLanguage(),
  82. problem.getTimeLimitMs().longValue(), problem.getMemoryLimitMb() * 1024L, testCases);
  83. CodeResponse response = codeSandBox.process(context);
  84. submission.setStatus(response.status());
  85. submission.setTimeUsedMs(response.usedTime() == null ? 0 : response.usedTime().intValue());
  86. submission.setMemoryUsedKb(response.usedMemory() == null ? 0 : response.usedMemory().intValue());
  87. submission.setJudgeDetail(response.message());
  88. if (response.testCaseResponses() != null) {
  89. long passed = response.testCaseResponses().stream()
  90. .filter(c -> Submission.STATUS_AC.equals(c.status()))
  91. .count();
  92. submission.setPassedTestCases((int) passed);
  93. }
  94. submission.setScore(resolveScore(dto.getContestId(), submission, problem));
  95. submissionMapper.updateById(submission);
  96. problem.setTryCount(problem.getTryCount() == null ? 1 : problem.getTryCount() + 1);
  97. if (Submission.STATUS_AC.equals(submission.getStatus())) {
  98. problem.setAcceptedCount(problem.getAcceptedCount() == null ? 1 : problem.getAcceptedCount() + 1);
  99. }
  100. problemService.updateById(problem);
  101. return SubmissionVo.from(submission);
  102. }
  103. @Override
  104. public SubmissionVo getDetail(Long submissionId, Long userId) {
  105. Submission submission = submissionMapper.selectById(submissionId);
  106. if (submission == null) {
  107. throw new IllegalArgumentException("提交记录不存在");
  108. }
  109. String role = SecurityUtils.getCurrentRole();
  110. if (!User.ROLE_ADMIN.equals(role) && !User.ROLE_COACH.equals(role)
  111. && !Objects.equals(submission.getUserId(), userId)) {
  112. throw new IllegalArgumentException("无权限查看该提交记录");
  113. }
  114. return SubmissionVo.from(submission);
  115. }
  116. @Override
  117. public Page<SubmissionVo> queryMySubmissions(Long userId, Integer pageNum, Integer pageSize) {
  118. Page<Submission> page = page(new Page<>(pageNum, pageSize),
  119. new LambdaQueryWrapper<Submission>()
  120. .eq(Submission::getUserId, userId)
  121. .orderByDesc(Submission::getCreatedAt));
  122. return convertToVoPage(page);
  123. }
  124. @Override
  125. public Page<SubmissionVo> queryByProblemId(Long problemId, Integer pageNum, Integer pageSize) {
  126. Page<Submission> page = page(new Page<>(pageNum, pageSize),
  127. new LambdaQueryWrapper<Submission>()
  128. .eq(Submission::getProblemId, problemId)
  129. .orderByDesc(Submission::getCreatedAt));
  130. return convertToVoPage(page);
  131. }
  132. private void validateContestSubmission(Long contestId, Long userId) {
  133. Contest contest = contestService.getById(contestId);
  134. if (contest == null) {
  135. throw new IllegalArgumentException("比赛不存在");
  136. }
  137. if (!Contest.STATUS_RUNNING.equals(contest.getStatus())) {
  138. throw new IllegalArgumentException("比赛未在进行中");
  139. }
  140. if (!contestService.isRegistered(contestId, userId)) {
  141. throw new IllegalArgumentException("未报名该比赛");
  142. }
  143. }
  144. private Integer resolveScore(Long contestId, Submission submission, Problem problem) {
  145. if (!Submission.STATUS_AC.equals(submission.getStatus())) {
  146. return 0;
  147. }
  148. if (contestId == null) {
  149. return 0;
  150. }
  151. ContestProblem contestProblem = contestProblemMapper.selectOne(new LambdaQueryWrapper<ContestProblem>()
  152. .eq(ContestProblem::getContestId, contestId)
  153. .eq(ContestProblem::getProblemId, submission.getProblemId()));
  154. return contestProblem == null || contestProblem.getScore() == null ? 0 : contestProblem.getScore();
  155. }
  156. private Page<SubmissionVo> convertToVoPage(Page<Submission> page) {
  157. Page<SubmissionVo> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
  158. result.setRecords(SubmissionVo.from(page.getRecords()));
  159. return result;
  160. }
  161. }