Parcourir la source

# feat: 比赛模块Service实现与状态定时刷新

yangyi il y a 2 mois
Parent
commit
5dd2f9d931

+ 42 - 0
src/main/java/space/anyi/serve/service/ContestService.java

@@ -0,0 +1,42 @@
+package space.anyi.serve.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import space.anyi.serve.entity.contest.*;
+
+import java.util.List;
+
+public interface ContestService extends IService<Contest> {
+
+    boolean checkPassword(Long contestId, String password);
+
+    boolean isRegistered(Long contestId, Long userId);
+
+    boolean isCreator(Long contestId, Long userId);
+
+    ContestVo getBaseInfo(Long id);
+
+    ContestDetailVo getDetail(Long id, Long userId);
+
+    Page<ContestVo> queryByPage(String title, String status, boolean onlyPublic, Page<Contest> page);
+
+    Boolean insert(ContestDto dto, Long createdBy);
+
+    Boolean update(ContestDto dto);
+
+    Boolean deleteByIds(List<Long> ids);
+
+    Boolean addProblem(Long contestId, ContestProblemDto dto);
+
+    Boolean updateProblem(Long contestId, ContestProblemDto dto);
+
+    Boolean removeProblem(Long contestId, Long problemId);
+
+    Boolean register(Long contestId, Long userId);
+
+    List<RankVo> getRanking(Long contestId);
+
+    List<ContestRegistrationVo> queryRegistrations(Long contestId);
+
+    void refreshStatus();
+}

+ 249 - 0
src/main/java/space/anyi/serve/service/impl/ContestServiceImpl.java

@@ -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);
+        }
+    }
+}