|
|
@@ -0,0 +1,249 @@
|
|
|
+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.beans.BeanUtils;
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
+import space.anyi.serve.entity.contest.*;
|
|
|
+import space.anyi.serve.entity.problem.Problem;
|
|
|
+import space.anyi.serve.entity.user.User;
|
|
|
+import space.anyi.serve.mapper.ContestMapper;
|
|
|
+import space.anyi.serve.mapper.ContestProblemMapper;
|
|
|
+import space.anyi.serve.mapper.ContestRegistrationMapper;
|
|
|
+import space.anyi.serve.mapper.SubmissionMapper;
|
|
|
+import space.anyi.serve.service.ContestService;
|
|
|
+import space.anyi.serve.service.ProblemService;
|
|
|
+import space.anyi.serve.service.UserService;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Objects;
|
|
|
+
|
|
|
+@Service
|
|
|
+public class ContestServiceImpl extends ServiceImpl<ContestMapper, Contest> implements ContestService {
|
|
|
+
|
|
|
+ private final ContestProblemMapper contestProblemMapper;
|
|
|
+ private final ContestRegistrationMapper contestRegistrationMapper;
|
|
|
+ private final SubmissionMapper submissionMapper;
|
|
|
+ private final UserService userService;
|
|
|
+ private final ProblemService problemService;
|
|
|
+
|
|
|
+ public ContestServiceImpl(ContestProblemMapper contestProblemMapper,
|
|
|
+ ContestRegistrationMapper contestRegistrationMapper,
|
|
|
+ SubmissionMapper submissionMapper,
|
|
|
+ UserService userService,
|
|
|
+ ProblemService problemService) {
|
|
|
+ this.contestProblemMapper = contestProblemMapper;
|
|
|
+ this.contestRegistrationMapper = contestRegistrationMapper;
|
|
|
+ this.submissionMapper = submissionMapper;
|
|
|
+ this.userService = userService;
|
|
|
+ this.problemService = problemService;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean checkPassword(Long contestId, String password) {
|
|
|
+ Contest contest = getById(contestId);
|
|
|
+ if (contest == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!"password".equals(contest.getType())) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return contest.getPassword() != null && contest.getPassword().equals(password);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean isRegistered(Long contestId, Long userId) {
|
|
|
+ if (userId == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Long count = contestRegistrationMapper.selectCount(new LambdaQueryWrapper<ContestRegistration>()
|
|
|
+ .eq(ContestRegistration::getContestId, contestId)
|
|
|
+ .eq(ContestRegistration::getUserId, userId));
|
|
|
+ return count != null && count > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean isCreator(Long contestId, Long userId) {
|
|
|
+ if (userId == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Contest contest = getById(contestId);
|
|
|
+ return contest != null && Objects.equals(contest.getCreatedBy(), userId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ContestVo getBaseInfo(Long id) {
|
|
|
+ Contest contest = getById(id);
|
|
|
+ if (contest == null) {
|
|
|
+ throw new IllegalArgumentException("比赛不存在");
|
|
|
+ }
|
|
|
+ return ContestVo.from(contest);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ContestDetailVo getDetail(Long id, Long userId) {
|
|
|
+ Contest contest = getById(id);
|
|
|
+ if (contest == null) {
|
|
|
+ throw new IllegalArgumentException("比赛不存在");
|
|
|
+ }
|
|
|
+ ContestDetailVo vo = ContestDetailVo.from(contest);
|
|
|
+ if (userId != null) {
|
|
|
+ vo.setRegistered(isRegistered(id, userId));
|
|
|
+ }
|
|
|
+ if (contest.getCreatedBy() != null) {
|
|
|
+ User creator = userService.getById(contest.getCreatedBy());
|
|
|
+ if (creator != null) {
|
|
|
+ vo.setCreatorUsername(creator.getUsername());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ vo.setProblems(contestProblemMapper.selectByContestId(id));
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Page<ContestVo> queryByPage(String title, String status, boolean onlyPublic, Page<Contest> page) {
|
|
|
+ LambdaQueryWrapper<Contest> wrapper = new LambdaQueryWrapper<Contest>()
|
|
|
+ .like(StringUtils.hasText(title), Contest::getTitle, title)
|
|
|
+ .eq(StringUtils.hasText(status), Contest::getStatus, status)
|
|
|
+ .orderByDesc(Contest::getId);
|
|
|
+ if (onlyPublic) {
|
|
|
+ wrapper.eq(Contest::getType, "public");
|
|
|
+ }
|
|
|
+ Page<Contest> contestPage = page(page, wrapper);
|
|
|
+ Page<ContestVo> result = new Page<>(contestPage.getCurrent(), contestPage.getSize(), contestPage.getTotal());
|
|
|
+ result.setRecords(ContestVo.from(contestPage.getRecords()));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public Boolean insert(ContestDto dto, Long createdBy) {
|
|
|
+ Contest contest = new Contest();
|
|
|
+ BeanUtils.copyProperties(dto, contest);
|
|
|
+ contest.setCreatedBy(createdBy);
|
|
|
+ if (!"password".equals(contest.getType())) {
|
|
|
+ contest.setPassword(null);
|
|
|
+ }
|
|
|
+ return save(contest);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public Boolean update(ContestDto dto) {
|
|
|
+ Contest contest = getById(dto.getId());
|
|
|
+ if (contest == null) {
|
|
|
+ throw new IllegalArgumentException("比赛不存在");
|
|
|
+ }
|
|
|
+ contest.setTitle(dto.getTitle());
|
|
|
+ contest.setDescription(dto.getDescription());
|
|
|
+ contest.setType(dto.getType());
|
|
|
+ contest.setPassword("password".equals(dto.getType()) ? dto.getPassword() : null);
|
|
|
+ contest.setStartDatetime(dto.getStartDatetime());
|
|
|
+ contest.setEndDatetime(dto.getEndDatetime());
|
|
|
+ return updateById(contest);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public Boolean deleteByIds(List<Long> ids) {
|
|
|
+ if (ids == null || ids.isEmpty()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ contestProblemMapper.delete(new LambdaQueryWrapper<ContestProblem>()
|
|
|
+ .in(ContestProblem::getContestId, ids));
|
|
|
+ contestRegistrationMapper.delete(new LambdaQueryWrapper<ContestRegistration>()
|
|
|
+ .in(ContestRegistration::getContestId, ids));
|
|
|
+ return removeByIds(ids);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Boolean addProblem(Long contestId, ContestProblemDto dto) {
|
|
|
+ Contest contest = getById(contestId);
|
|
|
+ if (contest == null) {
|
|
|
+ throw new IllegalArgumentException("比赛不存在");
|
|
|
+ }
|
|
|
+ Problem problem = problemService.getById(dto.getProblemId());
|
|
|
+ if (problem == null) {
|
|
|
+ throw new IllegalArgumentException("题目不存在");
|
|
|
+ }
|
|
|
+ ContestProblem contestProblem = new ContestProblem();
|
|
|
+ contestProblem.setContestId(contestId);
|
|
|
+ contestProblem.setProblemId(dto.getProblemId());
|
|
|
+ contestProblem.setDisplayOrder(dto.getDisplayOrder());
|
|
|
+ contestProblem.setScore(dto.getScore());
|
|
|
+ return contestProblemMapper.insert(contestProblem) > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Boolean updateProblem(Long contestId, ContestProblemDto dto) {
|
|
|
+ ContestProblem contestProblem = new ContestProblem();
|
|
|
+ contestProblem.setContestId(contestId);
|
|
|
+ contestProblem.setProblemId(dto.getProblemId());
|
|
|
+ contestProblem.setDisplayOrder(dto.getDisplayOrder());
|
|
|
+ contestProblem.setScore(dto.getScore());
|
|
|
+ return contestProblemMapper.update(contestProblem, new LambdaQueryWrapper<ContestProblem>()
|
|
|
+ .eq(ContestProblem::getContestId, contestId)
|
|
|
+ .eq(ContestProblem::getProblemId, dto.getProblemId())) > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Boolean removeProblem(Long contestId, Long problemId) {
|
|
|
+ return contestProblemMapper.delete(new LambdaQueryWrapper<ContestProblem>()
|
|
|
+ .eq(ContestProblem::getContestId, contestId)
|
|
|
+ .eq(ContestProblem::getProblemId, problemId)) > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public Boolean register(Long contestId, Long userId) {
|
|
|
+ Contest contest = getById(contestId);
|
|
|
+ if (contest == null) {
|
|
|
+ throw new IllegalArgumentException("比赛不存在");
|
|
|
+ }
|
|
|
+ if (!"pending".equals(contest.getStatus()) && !"running".equals(contest.getStatus())) {
|
|
|
+ throw new IllegalArgumentException("比赛未开始或已结束,无法报名");
|
|
|
+ }
|
|
|
+ Long count = contestRegistrationMapper.selectCount(new LambdaQueryWrapper<ContestRegistration>()
|
|
|
+ .eq(ContestRegistration::getContestId, contestId)
|
|
|
+ .eq(ContestRegistration::getUserId, userId));
|
|
|
+ if (count != null && count > 0) {
|
|
|
+ throw new IllegalArgumentException("已报名该比赛");
|
|
|
+ }
|
|
|
+ return contestRegistrationMapper.upsert(contestId, userId) > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<RankVo> getRanking(Long contestId) {
|
|
|
+ return submissionMapper.selectRanking(contestId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<ContestRegistrationVo> queryRegistrations(Long contestId) {
|
|
|
+ return contestRegistrationMapper.selectByContestId(contestId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Scheduled(cron = "0 * * * * *")
|
|
|
+ public void refreshStatus() {
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ List<Contest> pendings = list(new LambdaQueryWrapper<Contest>()
|
|
|
+ .eq(Contest::getStatus, "pending")
|
|
|
+ .le(Contest::getStartDatetime, now));
|
|
|
+ for (Contest contest : pendings) {
|
|
|
+ contest.setStatus("running");
|
|
|
+ updateById(contest);
|
|
|
+ }
|
|
|
+ List<Contest> runnings = list(new LambdaQueryWrapper<Contest>()
|
|
|
+ .eq(Contest::getStatus, "running")
|
|
|
+ .le(Contest::getEndDatetime, now));
|
|
|
+ for (Contest contest : runnings) {
|
|
|
+ contest.setStatus("ended");
|
|
|
+ updateById(contest);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|