Răsfoiți Sursa

# feat: 题目/比赛模块实体、Mapper与DTO/VO定义

yangyi 2 luni în urmă
părinte
comite
caa73f62ba
39 a modificat fișierele cu 2321 adăugiri și 0 ștergeri
  1. 128 0
      src/main/java/space/anyi/serve/entity/contest/Contest.java
  2. 66 0
      src/main/java/space/anyi/serve/entity/contest/ContestDetailVo.java
  3. 86 0
      src/main/java/space/anyi/serve/entity/contest/ContestDto.java
  4. 80 0
      src/main/java/space/anyi/serve/entity/contest/ContestProblem.java
  5. 42 0
      src/main/java/space/anyi/serve/entity/contest/ContestProblemDto.java
  6. 58 0
      src/main/java/space/anyi/serve/entity/contest/ContestProblemVo.java
  7. 18 0
      src/main/java/space/anyi/serve/entity/contest/ContestRegisterDto.java
  8. 73 0
      src/main/java/space/anyi/serve/entity/contest/ContestRegistration.java
  9. 50 0
      src/main/java/space/anyi/serve/entity/contest/ContestRegistrationVo.java
  10. 106 0
      src/main/java/space/anyi/serve/entity/contest/ContestVo.java
  11. 60 0
      src/main/java/space/anyi/serve/entity/contest/RankVo.java
  12. 159 0
      src/main/java/space/anyi/serve/entity/contest/Submission.java
  13. 72 0
      src/main/java/space/anyi/serve/entity/problem/Category.java
  14. 40 0
      src/main/java/space/anyi/serve/entity/problem/CategoryDto.java
  15. 61 0
      src/main/java/space/anyi/serve/entity/problem/CategoryVo.java
  16. 140 0
      src/main/java/space/anyi/serve/entity/problem/Problem.java
  17. 100 0
      src/main/java/space/anyi/serve/entity/problem/ProblemCase.java
  18. 70 0
      src/main/java/space/anyi/serve/entity/problem/ProblemCaseDto.java
  19. 94 0
      src/main/java/space/anyi/serve/entity/problem/ProblemCaseVo.java
  20. 61 0
      src/main/java/space/anyi/serve/entity/problem/ProblemCategoryRel.java
  21. 68 0
      src/main/java/space/anyi/serve/entity/problem/ProblemDetailVo.java
  22. 104 0
      src/main/java/space/anyi/serve/entity/problem/ProblemDto.java
  23. 101 0
      src/main/java/space/anyi/serve/entity/problem/ProblemTest.java
  24. 70 0
      src/main/java/space/anyi/serve/entity/problem/ProblemTestDto.java
  25. 94 0
      src/main/java/space/anyi/serve/entity/problem/ProblemTestVo.java
  26. 128 0
      src/main/java/space/anyi/serve/entity/problem/ProblemVo.java
  27. 9 0
      src/main/java/space/anyi/serve/mapper/CategoryMapper.java
  28. 9 0
      src/main/java/space/anyi/serve/mapper/ContestMapper.java
  29. 15 0
      src/main/java/space/anyi/serve/mapper/ContestProblemMapper.java
  30. 17 0
      src/main/java/space/anyi/serve/mapper/ContestRegistrationMapper.java
  31. 9 0
      src/main/java/space/anyi/serve/mapper/ProblemCaseMapper.java
  32. 17 0
      src/main/java/space/anyi/serve/mapper/ProblemCategoryRelMapper.java
  33. 9 0
      src/main/java/space/anyi/serve/mapper/ProblemMapper.java
  34. 9 0
      src/main/java/space/anyi/serve/mapper/ProblemTestMapper.java
  35. 15 0
      src/main/java/space/anyi/serve/mapper/SubmissionMapper.java
  36. 14 0
      src/main/resources/mapper/ContestProblemMapper.xml
  37. 19 0
      src/main/resources/mapper/ContestRegistrationMapper.xml
  38. 18 0
      src/main/resources/mapper/ProblemCategoryRelMapper.xml
  39. 32 0
      src/main/resources/mapper/SubmissionMapper.xml

+ 128 - 0
src/main/java/space/anyi/serve/entity/contest/Contest.java

@@ -0,0 +1,128 @@
+package space.anyi.serve.entity.contest;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.time.LocalDateTime;
+
+@TableName("dev.contest")
+public class Contest {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private String title;
+    private String description;
+    private String type;
+    private String password;
+    @TableField("start_datetime")
+    private LocalDateTime startDatetime;
+    @TableField("end_datetime")
+    private LocalDateTime endDatetime;
+    private String status;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("delete_flag")
+    private Integer deleteFlag = 0;
+    @TableField("created_at")
+    private LocalDateTime createdAt;
+    @TableField("updated_at")
+    private LocalDateTime updatedAt;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public LocalDateTime getStartDatetime() {
+        return startDatetime;
+    }
+
+    public void setStartDatetime(LocalDateTime startDatetime) {
+        this.startDatetime = startDatetime;
+    }
+
+    public LocalDateTime getEndDatetime() {
+        return endDatetime;
+    }
+
+    public void setEndDatetime(LocalDateTime endDatetime) {
+        this.endDatetime = endDatetime;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public Long getCreatedBy() {
+        return createdBy;
+    }
+
+    public void setCreatedBy(Long createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public Integer getDeleteFlag() {
+        return deleteFlag;
+    }
+
+    public void setDeleteFlag(Integer deleteFlag) {
+        this.deleteFlag = deleteFlag;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public LocalDateTime getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public void setUpdatedAt(LocalDateTime updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+}

+ 66 - 0
src/main/java/space/anyi/serve/entity/contest/ContestDetailVo.java

@@ -0,0 +1,66 @@
+package space.anyi.serve.entity.contest;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Schema(description = "比赛详情响应展示对象")
+public class ContestDetailVo extends ContestVo {
+
+    @Schema(description = "比赛描述")
+    private String description;
+    @Schema(description = "创建者用户名")
+    private String creatorUsername;
+    @Schema(description = "当前用户是否已报名")
+    private Boolean registered;
+    @Schema(description = "比赛题目列表")
+    private List<ContestProblemVo> problems;
+
+    public static ContestDetailVo from(Contest contest) {
+        ContestDetailVo vo = new ContestDetailVo();
+        vo.setId(contest.getId());
+        vo.setTitle(contest.getTitle());
+        vo.setType(contest.getType());
+        vo.setStartDatetime(contest.getStartDatetime());
+        vo.setEndDatetime(contest.getEndDatetime());
+        vo.setStatus(contest.getStatus());
+        vo.setCreatedAt(contest.getCreatedAt());
+        vo.setDescription(contest.getDescription());
+        vo.setRegistered(false);
+        vo.setProblems(new ArrayList<>());
+        return vo;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getCreatorUsername() {
+        return creatorUsername;
+    }
+
+    public void setCreatorUsername(String creatorUsername) {
+        this.creatorUsername = creatorUsername;
+    }
+
+    public Boolean getRegistered() {
+        return registered;
+    }
+
+    public void setRegistered(Boolean registered) {
+        this.registered = registered;
+    }
+
+    public List<ContestProblemVo> getProblems() {
+        return problems;
+    }
+
+    public void setProblems(List<ContestProblemVo> problems) {
+        this.problems = problems;
+    }
+}

+ 86 - 0
src/main/java/space/anyi/serve/entity/contest/ContestDto.java

@@ -0,0 +1,86 @@
+package space.anyi.serve.entity.contest;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "比赛新增和修改使用的DTO")
+public class ContestDto {
+
+    @Schema(description = "比赛ID")
+    private Long id;
+    @NotBlank(message = "比赛标题不能为空")
+    @Schema(description = "比赛标题")
+    private String title;
+    @Schema(description = "比赛描述")
+    private String description;
+    @NotBlank(message = "比赛类型不能为空")
+    @Schema(description = "比赛类型:public-公开,private-私有,password-密码访问")
+    private String type;
+    @Schema(description = "访问密码(type为password时使用)")
+    private String password;
+    @NotNull(message = "开始时间不能为空")
+    @Schema(description = "开始时间")
+    private LocalDateTime startDatetime;
+    @NotNull(message = "结束时间不能为空")
+    @Schema(description = "结束时间")
+    private LocalDateTime endDatetime;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public LocalDateTime getStartDatetime() {
+        return startDatetime;
+    }
+
+    public void setStartDatetime(LocalDateTime startDatetime) {
+        this.startDatetime = startDatetime;
+    }
+
+    public LocalDateTime getEndDatetime() {
+        return endDatetime;
+    }
+
+    public void setEndDatetime(LocalDateTime endDatetime) {
+        this.endDatetime = endDatetime;
+    }
+}

+ 80 - 0
src/main/java/space/anyi/serve/entity/contest/ContestProblem.java

@@ -0,0 +1,80 @@
+package space.anyi.serve.entity.contest;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.time.LocalDateTime;
+
+@TableName("dev.contest_problem")
+public class ContestProblem {
+
+    @TableField("contest_id")
+    private Long contestId;
+    @TableField("problem_id")
+    private Long problemId;
+    @TableField("display_order")
+    private Integer displayOrder;
+    private Integer score;
+    @TableField("delete_flag")
+    private Integer deleteFlag = 0;
+    @TableField("created_at")
+    private LocalDateTime createdAt;
+    @TableField("updated_at")
+    private LocalDateTime updatedAt;
+
+    public Long getContestId() {
+        return contestId;
+    }
+
+    public void setContestId(Long contestId) {
+        this.contestId = contestId;
+    }
+
+    public Long getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Long problemId) {
+        this.problemId = problemId;
+    }
+
+    public Integer getDisplayOrder() {
+        return displayOrder;
+    }
+
+    public void setDisplayOrder(Integer displayOrder) {
+        this.displayOrder = displayOrder;
+    }
+
+    public Integer getScore() {
+        return score;
+    }
+
+    public void setScore(Integer score) {
+        this.score = score;
+    }
+
+    public Integer getDeleteFlag() {
+        return deleteFlag;
+    }
+
+    public void setDeleteFlag(Integer deleteFlag) {
+        this.deleteFlag = deleteFlag;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public LocalDateTime getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public void setUpdatedAt(LocalDateTime updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+}

+ 42 - 0
src/main/java/space/anyi/serve/entity/contest/ContestProblemDto.java

@@ -0,0 +1,42 @@
+package space.anyi.serve.entity.contest;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+
+@Schema(description = "比赛题目新增和修改使用的DTO")
+public class ContestProblemDto {
+
+    @NotNull(message = "题目ID不能为空")
+    @Schema(description = "题目ID")
+    private Long problemId;
+    @NotNull(message = "分值不能为空")
+    @Schema(description = "本题分值")
+    private Integer score;
+    @NotNull(message = "显示序号不能为空")
+    @Schema(description = "题目在比赛中的显示序号")
+    private Integer displayOrder;
+
+    public Long getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Long problemId) {
+        this.problemId = problemId;
+    }
+
+    public Integer getScore() {
+        return score;
+    }
+
+    public void setScore(Integer score) {
+        this.score = score;
+    }
+
+    public Integer getDisplayOrder() {
+        return displayOrder;
+    }
+
+    public void setDisplayOrder(Integer displayOrder) {
+        this.displayOrder = displayOrder;
+    }
+}

+ 58 - 0
src/main/java/space/anyi/serve/entity/contest/ContestProblemVo.java

@@ -0,0 +1,58 @@
+package space.anyi.serve.entity.contest;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(description = "比赛题目响应展示对象")
+public class ContestProblemVo {
+
+    @Schema(description = "题目ID")
+    private Long problemId;
+    @Schema(description = "题目标题")
+    private String title;
+    @Schema(description = "难度:easy-简单,medium-中等,hard-困难")
+    private String difficulty;
+    @Schema(description = "题目在比赛中的显示序号")
+    private Integer displayOrder;
+    @Schema(description = "本题分值")
+    private Integer score;
+
+    public Long getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Long problemId) {
+        this.problemId = problemId;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDifficulty() {
+        return difficulty;
+    }
+
+    public void setDifficulty(String difficulty) {
+        this.difficulty = difficulty;
+    }
+
+    public Integer getDisplayOrder() {
+        return displayOrder;
+    }
+
+    public void setDisplayOrder(Integer displayOrder) {
+        this.displayOrder = displayOrder;
+    }
+
+    public Integer getScore() {
+        return score;
+    }
+
+    public void setScore(Integer score) {
+        this.score = score;
+    }
+}

+ 18 - 0
src/main/java/space/anyi/serve/entity/contest/ContestRegisterDto.java

@@ -0,0 +1,18 @@
+package space.anyi.serve.entity.contest;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(description = "比赛报名使用的DTO")
+public class ContestRegisterDto {
+
+    @Schema(description = "访问密码(密码型比赛需传入)")
+    private String password;
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+}

+ 73 - 0
src/main/java/space/anyi/serve/entity/contest/ContestRegistration.java

@@ -0,0 +1,73 @@
+package space.anyi.serve.entity.contest;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.time.LocalDateTime;
+
+@TableName("dev.contest_registration")
+public class ContestRegistration {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    @TableField("contest_id")
+    private Long contestId;
+    @TableField("user_id")
+    private Long userId;
+    @TableField("delete_flag")
+    private Integer deleteFlag = 0;
+    @TableField("created_at")
+    private LocalDateTime createdAt;
+    @TableField("updated_at")
+    private LocalDateTime updatedAt;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getContestId() {
+        return contestId;
+    }
+
+    public void setContestId(Long contestId) {
+        this.contestId = contestId;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public Integer getDeleteFlag() {
+        return deleteFlag;
+    }
+
+    public void setDeleteFlag(Integer deleteFlag) {
+        this.deleteFlag = deleteFlag;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public LocalDateTime getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public void setUpdatedAt(LocalDateTime updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+}

+ 50 - 0
src/main/java/space/anyi/serve/entity/contest/ContestRegistrationVo.java

@@ -0,0 +1,50 @@
+package space.anyi.serve.entity.contest;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "比赛报名记录响应展示对象")
+public class ContestRegistrationVo {
+
+    @Schema(description = "报名记录ID")
+    private Long id;
+    @Schema(description = "用户ID")
+    private Long userId;
+    @Schema(description = "用户名")
+    private String username;
+    @Schema(description = "报名时间")
+    private LocalDateTime createdAt;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+}

+ 106 - 0
src/main/java/space/anyi/serve/entity/contest/ContestVo.java

@@ -0,0 +1,106 @@
+package space.anyi.serve.entity.contest;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@Schema(description = "比赛列表响应展示对象")
+public class ContestVo {
+
+    @Schema(description = "比赛ID")
+    private Long id;
+    @Schema(description = "比赛标题")
+    private String title;
+    @Schema(description = "比赛类型:public-公开,private-私有,password-密码访问")
+    private String type;
+    @Schema(description = "开始时间")
+    private LocalDateTime startDatetime;
+    @Schema(description = "结束时间")
+    private LocalDateTime endDatetime;
+    @Schema(description = "状态:pending-未开始,running-进行中,ended-已结束")
+    private String status;
+    @Schema(description = "创建时间")
+    private LocalDateTime createdAt;
+
+    public static ContestVo from(Contest contest) {
+        ContestVo vo = new ContestVo();
+        if (Objects.isNull(contest)) {
+            return vo;
+        }
+        vo.setId(contest.getId());
+        vo.setTitle(contest.getTitle());
+        vo.setType(contest.getType());
+        vo.setStartDatetime(contest.getStartDatetime());
+        vo.setEndDatetime(contest.getEndDatetime());
+        vo.setStatus(contest.getStatus());
+        vo.setCreatedAt(contest.getCreatedAt());
+        return vo;
+    }
+
+    public static List<ContestVo> from(List<Contest> list) {
+        List<ContestVo> result = new ArrayList<>();
+        for (Contest contest : list) {
+            result.add(ContestVo.from(contest));
+        }
+        return result;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public LocalDateTime getStartDatetime() {
+        return startDatetime;
+    }
+
+    public void setStartDatetime(LocalDateTime startDatetime) {
+        this.startDatetime = startDatetime;
+    }
+
+    public LocalDateTime getEndDatetime() {
+        return endDatetime;
+    }
+
+    public void setEndDatetime(LocalDateTime endDatetime) {
+        this.endDatetime = endDatetime;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+}

+ 60 - 0
src/main/java/space/anyi/serve/entity/contest/RankVo.java

@@ -0,0 +1,60 @@
+package space.anyi.serve.entity.contest;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "比赛排行榜响应展示对象")
+public class RankVo {
+
+    @Schema(description = "用户ID")
+    private Long userId;
+    @Schema(description = "用户名")
+    private String username;
+    @Schema(description = "总得分")
+    private Integer totalScore;
+    @Schema(description = "AC最大耗时(分钟)")
+    private Long maxAcMinutes;
+    @Schema(description = "报名时间")
+    private LocalDateTime regTime;
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public Integer getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(Integer totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public Long getMaxAcMinutes() {
+        return maxAcMinutes;
+    }
+
+    public void setMaxAcMinutes(Long maxAcMinutes) {
+        this.maxAcMinutes = maxAcMinutes;
+    }
+
+    public LocalDateTime getRegTime() {
+        return regTime;
+    }
+
+    public void setRegTime(LocalDateTime regTime) {
+        this.regTime = regTime;
+    }
+}

+ 159 - 0
src/main/java/space/anyi/serve/entity/contest/Submission.java

@@ -0,0 +1,159 @@
+package space.anyi.serve.entity.contest;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.time.LocalDateTime;
+
+@TableName("dev.submission")
+public class Submission {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    @TableField("problem_id")
+    private Long problemId;
+    @TableField("user_id")
+    private Long userId;
+    @TableField("contest_id")
+    private Long contestId;
+    private String code;
+    private String language;
+    private String status;
+    @TableField("judge_detail")
+    private String judgeDetail;
+    @TableField("time_used_ms")
+    private Integer timeUsedMs;
+    @TableField("memory_used_kb")
+    private Integer memoryUsedKb;
+    private Integer score;
+    @TableField("total_test_cases")
+    private Integer totalTestCases;
+    @TableField("passed_test_cases")
+    private Integer passedTestCases;
+    @TableField("delete_flag")
+    private Integer deleteFlag = 0;
+    @TableField("created_at")
+    private LocalDateTime createdAt;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Long problemId) {
+        this.problemId = problemId;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public Long getContestId() {
+        return contestId;
+    }
+
+    public void setContestId(Long contestId) {
+        this.contestId = contestId;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getLanguage() {
+        return language;
+    }
+
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getJudgeDetail() {
+        return judgeDetail;
+    }
+
+    public void setJudgeDetail(String judgeDetail) {
+        this.judgeDetail = judgeDetail;
+    }
+
+    public Integer getTimeUsedMs() {
+        return timeUsedMs;
+    }
+
+    public void setTimeUsedMs(Integer timeUsedMs) {
+        this.timeUsedMs = timeUsedMs;
+    }
+
+    public Integer getMemoryUsedKb() {
+        return memoryUsedKb;
+    }
+
+    public void setMemoryUsedKb(Integer memoryUsedKb) {
+        this.memoryUsedKb = memoryUsedKb;
+    }
+
+    public Integer getScore() {
+        return score;
+    }
+
+    public void setScore(Integer score) {
+        this.score = score;
+    }
+
+    public Integer getTotalTestCases() {
+        return totalTestCases;
+    }
+
+    public void setTotalTestCases(Integer totalTestCases) {
+        this.totalTestCases = totalTestCases;
+    }
+
+    public Integer getPassedTestCases() {
+        return passedTestCases;
+    }
+
+    public void setPassedTestCases(Integer passedTestCases) {
+        this.passedTestCases = passedTestCases;
+    }
+
+    public Integer getDeleteFlag() {
+        return deleteFlag;
+    }
+
+    public void setDeleteFlag(Integer deleteFlag) {
+        this.deleteFlag = deleteFlag;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+}

+ 72 - 0
src/main/java/space/anyi/serve/entity/problem/Category.java

@@ -0,0 +1,72 @@
+package space.anyi.serve.entity.problem;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.time.LocalDateTime;
+
+@TableName("dev.category")
+public class Category {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    @TableField("category_name")
+    private String categoryName;
+    private String description;
+    @TableField("delete_flag")
+    private Integer deleteFlag = 0;
+    @TableField("created_at")
+    private LocalDateTime createdAt;
+    @TableField("updated_at")
+    private LocalDateTime updatedAt;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getCategoryName() {
+        return categoryName;
+    }
+
+    public void setCategoryName(String categoryName) {
+        this.categoryName = categoryName;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getDeleteFlag() {
+        return deleteFlag;
+    }
+
+    public void setDeleteFlag(Integer deleteFlag) {
+        this.deleteFlag = deleteFlag;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public LocalDateTime getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public void setUpdatedAt(LocalDateTime updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+}

+ 40 - 0
src/main/java/space/anyi/serve/entity/problem/CategoryDto.java

@@ -0,0 +1,40 @@
+package space.anyi.serve.entity.problem;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+
+@Schema(description = "题目分类新增和修改使用的DTO")
+public class CategoryDto {
+
+    @Schema(description = "分类ID")
+    private Long id;
+    @NotBlank(message = "分类名称不能为空")
+    @Schema(description = "分类名称")
+    private String categoryName;
+    @Schema(description = "分类描述")
+    private String description;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getCategoryName() {
+        return categoryName;
+    }
+
+    public void setCategoryName(String categoryName) {
+        this.categoryName = categoryName;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+}

+ 61 - 0
src/main/java/space/anyi/serve/entity/problem/CategoryVo.java

@@ -0,0 +1,61 @@
+package space.anyi.serve.entity.problem;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@Schema(description = "题目分类响应展示对象")
+public class CategoryVo {
+
+    @Schema(description = "分类ID")
+    private Long id;
+    @Schema(description = "分类名称")
+    private String categoryName;
+    @Schema(description = "分类描述")
+    private String description;
+
+    public static CategoryVo from(Category category) {
+        CategoryVo vo = new CategoryVo();
+        if (Objects.isNull(category)) {
+            return vo;
+        }
+        vo.setId(category.getId());
+        vo.setCategoryName(category.getCategoryName());
+        vo.setDescription(category.getDescription());
+        return vo;
+    }
+
+    public static List<CategoryVo> from(List<Category> list) {
+        List<CategoryVo> result = new ArrayList<>();
+        for (Category category : list) {
+            result.add(CategoryVo.from(category));
+        }
+        return result;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getCategoryName() {
+        return categoryName;
+    }
+
+    public void setCategoryName(String categoryName) {
+        this.categoryName = categoryName;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+}

+ 140 - 0
src/main/java/space/anyi/serve/entity/problem/Problem.java

@@ -0,0 +1,140 @@
+package space.anyi.serve.entity.problem;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.time.LocalDateTime;
+
+@TableName("dev.problem")
+public class Problem {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private String title;
+    private String description;
+    @TableField("time_limit_ms")
+    private Integer timeLimitMs;
+    @TableField("memory_limit_mb")
+    private Integer memoryLimitMb;
+    @TableField("try_count")
+    private Long tryCount;
+    @TableField("accepted_count")
+    private Long acceptedCount;
+    @TableField("is_public")
+    private Integer isPublic;
+    private String difficulty;
+    @TableField("created_by")
+    private Long createdBy;
+    @TableField("delete_flag")
+    private Integer deleteFlag = 0;
+    @TableField("created_at")
+    private LocalDateTime createdAt;
+    @TableField("updated_at")
+    private LocalDateTime updatedAt;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getTimeLimitMs() {
+        return timeLimitMs;
+    }
+
+    public void setTimeLimitMs(Integer timeLimitMs) {
+        this.timeLimitMs = timeLimitMs;
+    }
+
+    public Integer getMemoryLimitMb() {
+        return memoryLimitMb;
+    }
+
+    public void setMemoryLimitMb(Integer memoryLimitMb) {
+        this.memoryLimitMb = memoryLimitMb;
+    }
+
+    public Long getTryCount() {
+        return tryCount;
+    }
+
+    public void setTryCount(Long tryCount) {
+        this.tryCount = tryCount;
+    }
+
+    public Long getAcceptedCount() {
+        return acceptedCount;
+    }
+
+    public void setAcceptedCount(Long acceptedCount) {
+        this.acceptedCount = acceptedCount;
+    }
+
+    public Integer getIsPublic() {
+        return isPublic;
+    }
+
+    public void setIsPublic(Integer isPublic) {
+        this.isPublic = isPublic;
+    }
+
+    public String getDifficulty() {
+        return difficulty;
+    }
+
+    public void setDifficulty(String difficulty) {
+        this.difficulty = difficulty;
+    }
+
+    public Long getCreatedBy() {
+        return createdBy;
+    }
+
+    public void setCreatedBy(Long createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public Integer getDeleteFlag() {
+        return deleteFlag;
+    }
+
+    public void setDeleteFlag(Integer deleteFlag) {
+        this.deleteFlag = deleteFlag;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public LocalDateTime getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public void setUpdatedAt(LocalDateTime updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+}

+ 100 - 0
src/main/java/space/anyi/serve/entity/problem/ProblemCase.java

@@ -0,0 +1,100 @@
+package space.anyi.serve.entity.problem;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.time.LocalDateTime;
+
+@TableName("dev.problem_case")
+public class ProblemCase {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    @TableField("problem_id")
+    private Long problemId;
+    private String input;
+    private String output;
+    private String description;
+    @TableField("sort_order")
+    private Integer sortOrder;
+    @TableField("delete_flag")
+    private Integer deleteFlag = 0;
+    @TableField("created_at")
+    private LocalDateTime createdAt;
+    @TableField("updated_at")
+    private LocalDateTime updatedAt;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Long problemId) {
+        this.problemId = problemId;
+    }
+
+    public String getInput() {
+        return input;
+    }
+
+    public void setInput(String input) {
+        this.input = input;
+    }
+
+    public String getOutput() {
+        return output;
+    }
+
+    public void setOutput(String output) {
+        this.output = output;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+
+    public Integer getDeleteFlag() {
+        return deleteFlag;
+    }
+
+    public void setDeleteFlag(Integer deleteFlag) {
+        this.deleteFlag = deleteFlag;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public LocalDateTime getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public void setUpdatedAt(LocalDateTime updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+}

+ 70 - 0
src/main/java/space/anyi/serve/entity/problem/ProblemCaseDto.java

@@ -0,0 +1,70 @@
+package space.anyi.serve.entity.problem;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+
+@Schema(description = "题目样例新增和修改使用的DTO")
+public class ProblemCaseDto {
+
+    @Schema(description = "样例ID")
+    private Long id;
+    @NotNull(message = "所属题目ID不能为空")
+    @Schema(description = "所属题目ID")
+    private Long problemId;
+    @Schema(description = "样例输入")
+    private String input;
+    @Schema(description = "样例输出")
+    private String output;
+    @Schema(description = "样例说明")
+    private String description;
+    @Schema(description = "显示顺序")
+    private Integer sortOrder;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Long problemId) {
+        this.problemId = problemId;
+    }
+
+    public String getInput() {
+        return input;
+    }
+
+    public void setInput(String input) {
+        this.input = input;
+    }
+
+    public String getOutput() {
+        return output;
+    }
+
+    public void setOutput(String output) {
+        this.output = output;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+}

+ 94 - 0
src/main/java/space/anyi/serve/entity/problem/ProblemCaseVo.java

@@ -0,0 +1,94 @@
+package space.anyi.serve.entity.problem;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@Schema(description = "题目样例响应展示对象")
+public class ProblemCaseVo {
+
+    @Schema(description = "样例ID")
+    private Long id;
+    @Schema(description = "所属题目ID")
+    private Long problemId;
+    @Schema(description = "样例输入")
+    private String input;
+    @Schema(description = "样例输出")
+    private String output;
+    @Schema(description = "样例说明")
+    private String description;
+    @Schema(description = "显示顺序")
+    private Integer sortOrder;
+
+    public static ProblemCaseVo from(ProblemCase problemCase) {
+        ProblemCaseVo vo = new ProblemCaseVo();
+        if (Objects.isNull(problemCase)) {
+            return vo;
+        }
+        vo.setId(problemCase.getId());
+        vo.setProblemId(problemCase.getProblemId());
+        vo.setInput(problemCase.getInput());
+        vo.setOutput(problemCase.getOutput());
+        vo.setDescription(problemCase.getDescription());
+        vo.setSortOrder(problemCase.getSortOrder());
+        return vo;
+    }
+
+    public static List<ProblemCaseVo> from(List<ProblemCase> list) {
+        List<ProblemCaseVo> result = new ArrayList<>();
+        for (ProblemCase problemCase : list) {
+            result.add(ProblemCaseVo.from(problemCase));
+        }
+        return result;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Long problemId) {
+        this.problemId = problemId;
+    }
+
+    public String getInput() {
+        return input;
+    }
+
+    public void setInput(String input) {
+        this.input = input;
+    }
+
+    public String getOutput() {
+        return output;
+    }
+
+    public void setOutput(String output) {
+        this.output = output;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+}

+ 61 - 0
src/main/java/space/anyi/serve/entity/problem/ProblemCategoryRel.java

@@ -0,0 +1,61 @@
+package space.anyi.serve.entity.problem;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.time.LocalDateTime;
+
+@TableName("dev.problem_category_rel")
+public class ProblemCategoryRel {
+
+    @TableField("problem_id")
+    private Long problemId;
+    @TableField("category_id")
+    private Long categoryId;
+    @TableField("delete_flag")
+    private Integer deleteFlag = 0;
+    @TableField("created_at")
+    private LocalDateTime createdAt;
+    @TableField("updated_at")
+    private LocalDateTime updatedAt;
+
+    public Long getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Long problemId) {
+        this.problemId = problemId;
+    }
+
+    public Long getCategoryId() {
+        return categoryId;
+    }
+
+    public void setCategoryId(Long categoryId) {
+        this.categoryId = categoryId;
+    }
+
+    public Integer getDeleteFlag() {
+        return deleteFlag;
+    }
+
+    public void setDeleteFlag(Integer deleteFlag) {
+        this.deleteFlag = deleteFlag;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public LocalDateTime getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public void setUpdatedAt(LocalDateTime updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+}

+ 68 - 0
src/main/java/space/anyi/serve/entity/problem/ProblemDetailVo.java

@@ -0,0 +1,68 @@
+package space.anyi.serve.entity.problem;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Schema(description = "题目详情响应展示对象")
+public class ProblemDetailVo extends ProblemVo {
+
+    @Schema(description = "题目描述")
+    private String description;
+    @Schema(description = "创建者用户名")
+    private String createdByUsername;
+    @Schema(description = "题目分类列表")
+    private List<CategoryVo> categories;
+    @Schema(description = "题目样例列表")
+    private List<ProblemCaseVo> cases;
+
+    public static ProblemDetailVo from(Problem problem) {
+        ProblemDetailVo vo = new ProblemDetailVo();
+        vo.setId(problem.getId());
+        vo.setTitle(problem.getTitle());
+        vo.setDifficulty(problem.getDifficulty());
+        vo.setTimeLimitMs(problem.getTimeLimitMs());
+        vo.setMemoryLimitMb(problem.getMemoryLimitMb());
+        vo.setIsPublic(problem.getIsPublic());
+        vo.setTryCount(problem.getTryCount());
+        vo.setAcceptedCount(problem.getAcceptedCount());
+        vo.setCreatedAt(problem.getCreatedAt());
+        vo.setDescription(problem.getDescription());
+        vo.setCategories(new ArrayList<>());
+        vo.setCases(new ArrayList<>());
+        return vo;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getCreatedByUsername() {
+        return createdByUsername;
+    }
+
+    public void setCreatedByUsername(String createdByUsername) {
+        this.createdByUsername = createdByUsername;
+    }
+
+    public List<CategoryVo> getCategories() {
+        return categories;
+    }
+
+    public void setCategories(List<CategoryVo> categories) {
+        this.categories = categories;
+    }
+
+    public List<ProblemCaseVo> getCases() {
+        return cases;
+    }
+
+    public void setCases(List<ProblemCaseVo> cases) {
+        this.cases = cases;
+    }
+}

+ 104 - 0
src/main/java/space/anyi/serve/entity/problem/ProblemDto.java

@@ -0,0 +1,104 @@
+package space.anyi.serve.entity.problem;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+import java.util.List;
+
+@Schema(description = "题目新增和修改使用的DTO")
+public class ProblemDto {
+
+    @Schema(description = "题目ID")
+    private Long id;
+    @NotBlank(message = "题目标题不能为空")
+    @Schema(description = "题目标题")
+    private String title;
+    @NotBlank(message = "题目描述不能为空")
+    @Schema(description = "题目描述")
+    private String description;
+    @NotNull(message = "时间限制不能为空")
+    @Min(value = 1, message = "时间限制必须大于0")
+    @Schema(description = "时间限制(毫秒)")
+    private Integer timeLimitMs;
+    @NotNull(message = "内存限制不能为空")
+    @Min(value = 1, message = "内存限制必须大于0")
+    @Schema(description = "内存限制(MB)")
+    private Integer memoryLimitMb;
+    @NotNull(message = "是否公开不能为空")
+    @Min(value = 0, message = "是否公开取值0或1")
+    @Max(value = 1, message = "是否公开取值0或1")
+    @Schema(description = "是否公开题目,0-未公开,1-公开")
+    private Integer isPublic;
+    @Schema(description = "难度:easy-简单,medium-中等,hard-困难")
+    private String difficulty;
+    @Schema(description = "题目分类ID列表")
+    private List<Long> categoryIds;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getTimeLimitMs() {
+        return timeLimitMs;
+    }
+
+    public void setTimeLimitMs(Integer timeLimitMs) {
+        this.timeLimitMs = timeLimitMs;
+    }
+
+    public Integer getMemoryLimitMb() {
+        return memoryLimitMb;
+    }
+
+    public void setMemoryLimitMb(Integer memoryLimitMb) {
+        this.memoryLimitMb = memoryLimitMb;
+    }
+
+    public Integer getIsPublic() {
+        return isPublic;
+    }
+
+    public void setIsPublic(Integer isPublic) {
+        this.isPublic = isPublic;
+    }
+
+    public String getDifficulty() {
+        return difficulty;
+    }
+
+    public void setDifficulty(String difficulty) {
+        this.difficulty = difficulty;
+    }
+
+    public List<Long> getCategoryIds() {
+        return categoryIds;
+    }
+
+    public void setCategoryIds(List<Long> categoryIds) {
+        this.categoryIds = categoryIds;
+    }
+}

+ 101 - 0
src/main/java/space/anyi/serve/entity/problem/ProblemTest.java

@@ -0,0 +1,101 @@
+package space.anyi.serve.entity.problem;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.time.LocalDateTime;
+
+@TableName("dev.problem_test")
+public class ProblemTest {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    @TableField("problem_id")
+    private Long problemId;
+    private String input;
+    private String output;
+    @TableField("sort_order")
+    private Integer sortOrder;
+    @TableField("is_hidden")
+    private Integer isHidden;
+    @TableField("delete_flag")
+    private Integer deleteFlag = 0;
+    @TableField("created_at")
+    private LocalDateTime createdAt;
+    @TableField("updated_at")
+    private LocalDateTime updatedAt;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Long problemId) {
+        this.problemId = problemId;
+    }
+
+    public String getInput() {
+        return input;
+    }
+
+    public void setInput(String input) {
+        this.input = input;
+    }
+
+    public String getOutput() {
+        return output;
+    }
+
+    public void setOutput(String output) {
+        this.output = output;
+    }
+
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+
+    public Integer getIsHidden() {
+        return isHidden;
+    }
+
+    public void setIsHidden(Integer isHidden) {
+        this.isHidden = isHidden;
+    }
+
+    public Integer getDeleteFlag() {
+        return deleteFlag;
+    }
+
+    public void setDeleteFlag(Integer deleteFlag) {
+        this.deleteFlag = deleteFlag;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public LocalDateTime getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public void setUpdatedAt(LocalDateTime updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+}

+ 70 - 0
src/main/java/space/anyi/serve/entity/problem/ProblemTestDto.java

@@ -0,0 +1,70 @@
+package space.anyi.serve.entity.problem;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+
+@Schema(description = "题目评测用例新增和修改使用的DTO")
+public class ProblemTestDto {
+
+    @Schema(description = "评测用例ID")
+    private Long id;
+    @NotNull(message = "所属题目ID不能为空")
+    @Schema(description = "所属题目ID")
+    private Long problemId;
+    @Schema(description = "测试输入")
+    private String input;
+    @Schema(description = "测试输出")
+    private String output;
+    @Schema(description = "判题顺序")
+    private Integer sortOrder;
+    @Schema(description = "是否隐藏用例,0-不隐藏,1-隐藏")
+    private Integer isHidden;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Long problemId) {
+        this.problemId = problemId;
+    }
+
+    public String getInput() {
+        return input;
+    }
+
+    public void setInput(String input) {
+        this.input = input;
+    }
+
+    public String getOutput() {
+        return output;
+    }
+
+    public void setOutput(String output) {
+        this.output = output;
+    }
+
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+
+    public Integer getIsHidden() {
+        return isHidden;
+    }
+
+    public void setIsHidden(Integer isHidden) {
+        this.isHidden = isHidden;
+    }
+}

+ 94 - 0
src/main/java/space/anyi/serve/entity/problem/ProblemTestVo.java

@@ -0,0 +1,94 @@
+package space.anyi.serve.entity.problem;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@Schema(description = "题目评测用例响应展示对象")
+public class ProblemTestVo {
+
+    @Schema(description = "评测用例ID")
+    private Long id;
+    @Schema(description = "所属题目ID")
+    private Long problemId;
+    @Schema(description = "测试输入")
+    private String input;
+    @Schema(description = "测试输出")
+    private String output;
+    @Schema(description = "判题顺序")
+    private Integer sortOrder;
+    @Schema(description = "是否隐藏用例,0-不隐藏,1-隐藏")
+    private Integer isHidden;
+
+    public static ProblemTestVo from(ProblemTest problemTest) {
+        ProblemTestVo vo = new ProblemTestVo();
+        if (Objects.isNull(problemTest)) {
+            return vo;
+        }
+        vo.setId(problemTest.getId());
+        vo.setProblemId(problemTest.getProblemId());
+        vo.setInput(problemTest.getInput());
+        vo.setOutput(problemTest.getOutput());
+        vo.setSortOrder(problemTest.getSortOrder());
+        vo.setIsHidden(problemTest.getIsHidden());
+        return vo;
+    }
+
+    public static List<ProblemTestVo> from(List<ProblemTest> list) {
+        List<ProblemTestVo> result = new ArrayList<>();
+        for (ProblemTest problemTest : list) {
+            result.add(ProblemTestVo.from(problemTest));
+        }
+        return result;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Long problemId) {
+        this.problemId = problemId;
+    }
+
+    public String getInput() {
+        return input;
+    }
+
+    public void setInput(String input) {
+        this.input = input;
+    }
+
+    public String getOutput() {
+        return output;
+    }
+
+    public void setOutput(String output) {
+        this.output = output;
+    }
+
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+
+    public Integer getIsHidden() {
+        return isHidden;
+    }
+
+    public void setIsHidden(Integer isHidden) {
+        this.isHidden = isHidden;
+    }
+}

+ 128 - 0
src/main/java/space/anyi/serve/entity/problem/ProblemVo.java

@@ -0,0 +1,128 @@
+package space.anyi.serve.entity.problem;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@Schema(description = "题目列表响应展示对象")
+public class ProblemVo {
+
+    @Schema(description = "题目ID")
+    private Long id;
+    @Schema(description = "题目标题")
+    private String title;
+    @Schema(description = "难度:easy-简单,medium-中等,hard-困难")
+    private String difficulty;
+    @Schema(description = "时间限制(毫秒)")
+    private Integer timeLimitMs;
+    @Schema(description = "内存限制(MB)")
+    private Integer memoryLimitMb;
+    @Schema(description = "是否公开题目,0-未公开,1-公开")
+    private Integer isPublic;
+    @Schema(description = "总提交次数")
+    private Long tryCount;
+    @Schema(description = "通过次数")
+    private Long acceptedCount;
+    @Schema(description = "创建时间")
+    private LocalDateTime createdAt;
+
+    public static ProblemVo from(Problem problem) {
+        ProblemVo vo = new ProblemVo();
+        if (Objects.isNull(problem)) {
+            return vo;
+        }
+        vo.setId(problem.getId());
+        vo.setTitle(problem.getTitle());
+        vo.setDifficulty(problem.getDifficulty());
+        vo.setTimeLimitMs(problem.getTimeLimitMs());
+        vo.setMemoryLimitMb(problem.getMemoryLimitMb());
+        vo.setIsPublic(problem.getIsPublic());
+        vo.setTryCount(problem.getTryCount());
+        vo.setAcceptedCount(problem.getAcceptedCount());
+        vo.setCreatedAt(problem.getCreatedAt());
+        return vo;
+    }
+
+    public static List<ProblemVo> from(List<Problem> list) {
+        List<ProblemVo> result = new ArrayList<>();
+        for (Problem problem : list) {
+            result.add(ProblemVo.from(problem));
+        }
+        return result;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDifficulty() {
+        return difficulty;
+    }
+
+    public void setDifficulty(String difficulty) {
+        this.difficulty = difficulty;
+    }
+
+    public Integer getTimeLimitMs() {
+        return timeLimitMs;
+    }
+
+    public void setTimeLimitMs(Integer timeLimitMs) {
+        this.timeLimitMs = timeLimitMs;
+    }
+
+    public Integer getMemoryLimitMb() {
+        return memoryLimitMb;
+    }
+
+    public void setMemoryLimitMb(Integer memoryLimitMb) {
+        this.memoryLimitMb = memoryLimitMb;
+    }
+
+    public Integer getIsPublic() {
+        return isPublic;
+    }
+
+    public void setIsPublic(Integer isPublic) {
+        this.isPublic = isPublic;
+    }
+
+    public Long getTryCount() {
+        return tryCount;
+    }
+
+    public void setTryCount(Long tryCount) {
+        this.tryCount = tryCount;
+    }
+
+    public Long getAcceptedCount() {
+        return acceptedCount;
+    }
+
+    public void setAcceptedCount(Long acceptedCount) {
+        this.acceptedCount = acceptedCount;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+}

+ 9 - 0
src/main/java/space/anyi/serve/mapper/CategoryMapper.java

@@ -0,0 +1,9 @@
+package space.anyi.serve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import space.anyi.serve.entity.problem.Category;
+
+@Mapper
+public interface CategoryMapper extends BaseMapper<Category> {
+}

+ 9 - 0
src/main/java/space/anyi/serve/mapper/ContestMapper.java

@@ -0,0 +1,9 @@
+package space.anyi.serve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import space.anyi.serve.entity.contest.Contest;
+
+@Mapper
+public interface ContestMapper extends BaseMapper<Contest> {
+}

+ 15 - 0
src/main/java/space/anyi/serve/mapper/ContestProblemMapper.java

@@ -0,0 +1,15 @@
+package space.anyi.serve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import space.anyi.serve.entity.contest.ContestProblem;
+import space.anyi.serve.entity.contest.ContestProblemVo;
+
+import java.util.List;
+
+@Mapper
+public interface ContestProblemMapper extends BaseMapper<ContestProblem> {
+
+    List<ContestProblemVo> selectByContestId(@Param("contestId") Long contestId);
+}

+ 17 - 0
src/main/java/space/anyi/serve/mapper/ContestRegistrationMapper.java

@@ -0,0 +1,17 @@
+package space.anyi.serve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import space.anyi.serve.entity.contest.ContestRegistration;
+import space.anyi.serve.entity.contest.ContestRegistrationVo;
+
+import java.util.List;
+
+@Mapper
+public interface ContestRegistrationMapper extends BaseMapper<ContestRegistration> {
+
+    int upsert(@Param("contestId") Long contestId, @Param("userId") Long userId);
+
+    List<ContestRegistrationVo> selectByContestId(@Param("contestId") Long contestId);
+}

+ 9 - 0
src/main/java/space/anyi/serve/mapper/ProblemCaseMapper.java

@@ -0,0 +1,9 @@
+package space.anyi.serve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import space.anyi.serve.entity.problem.ProblemCase;
+
+@Mapper
+public interface ProblemCaseMapper extends BaseMapper<ProblemCase> {
+}

+ 17 - 0
src/main/java/space/anyi/serve/mapper/ProblemCategoryRelMapper.java

@@ -0,0 +1,17 @@
+package space.anyi.serve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import space.anyi.serve.entity.problem.CategoryVo;
+import space.anyi.serve.entity.problem.ProblemCategoryRel;
+
+import java.util.List;
+
+@Mapper
+public interface ProblemCategoryRelMapper extends BaseMapper<ProblemCategoryRel> {
+
+    int upsert(@Param("problemId") Long problemId, @Param("categoryId") Long categoryId);
+
+    List<CategoryVo> selectCategoriesByProblemId(@Param("problemId") Long problemId);
+}

+ 9 - 0
src/main/java/space/anyi/serve/mapper/ProblemMapper.java

@@ -0,0 +1,9 @@
+package space.anyi.serve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import space.anyi.serve.entity.problem.Problem;
+
+@Mapper
+public interface ProblemMapper extends BaseMapper<Problem> {
+}

+ 9 - 0
src/main/java/space/anyi/serve/mapper/ProblemTestMapper.java

@@ -0,0 +1,9 @@
+package space.anyi.serve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import space.anyi.serve.entity.problem.ProblemTest;
+
+@Mapper
+public interface ProblemTestMapper extends BaseMapper<ProblemTest> {
+}

+ 15 - 0
src/main/java/space/anyi/serve/mapper/SubmissionMapper.java

@@ -0,0 +1,15 @@
+package space.anyi.serve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import space.anyi.serve.entity.contest.RankVo;
+import space.anyi.serve.entity.contest.Submission;
+
+import java.util.List;
+
+@Mapper
+public interface SubmissionMapper extends BaseMapper<Submission> {
+
+    List<RankVo> selectRanking(@Param("contestId") Long contestId);
+}

+ 14 - 0
src/main/resources/mapper/ContestProblemMapper.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="space.anyi.serve.mapper.ContestProblemMapper">
+
+    <select id="selectByContestId" resultType="space.anyi.serve.entity.contest.ContestProblemVo">
+        SELECT cp.problem_id AS problemId, p.title, p.difficulty,
+               cp.display_order AS displayOrder, cp.score
+        FROM dev.contest_problem cp
+        JOIN dev.problem p ON p.id = cp.problem_id AND p.delete_flag = 0
+        WHERE cp.contest_id = #{contestId} AND cp.delete_flag = 0
+        ORDER BY cp.display_order
+    </select>
+
+</mapper>

+ 19 - 0
src/main/resources/mapper/ContestRegistrationMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="space.anyi.serve.mapper.ContestRegistrationMapper">
+
+    <insert id="upsert">
+        INSERT INTO dev.contest_registration (contest_id, user_id)
+        VALUES (#{contestId}, #{userId})
+        ON CONFLICT (contest_id, user_id) DO UPDATE SET delete_flag = 0
+    </insert>
+
+    <select id="selectByContestId" resultType="space.anyi.serve.entity.contest.ContestRegistrationVo">
+        SELECT r.id, r.user_id AS userId, u.username, r.created_at AS createdAt
+        FROM dev.contest_registration r
+        JOIN dev.user u ON u.id = r.user_id AND u.delete_flag = 0
+        WHERE r.contest_id = #{contestId} AND r.delete_flag = 0
+        ORDER BY r.created_at
+    </select>
+
+</mapper>

+ 18 - 0
src/main/resources/mapper/ProblemCategoryRelMapper.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="space.anyi.serve.mapper.ProblemCategoryRelMapper">
+
+    <insert id="upsert">
+        INSERT INTO dev.problem_category_rel (problem_id, category_id)
+        VALUES (#{problemId}, #{categoryId})
+        ON CONFLICT (problem_id, category_id) DO UPDATE SET delete_flag = 0
+    </insert>
+
+    <select id="selectCategoriesByProblemId" resultType="space.anyi.serve.entity.problem.CategoryVo">
+        SELECT c.id, c.category_name AS categoryName, c.description
+        FROM dev.problem_category_rel r
+        JOIN dev.category c ON c.id = r.category_id AND c.delete_flag = 0
+        WHERE r.problem_id = #{problemId} AND r.delete_flag = 0
+    </select>
+
+</mapper>

+ 32 - 0
src/main/resources/mapper/SubmissionMapper.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="space.anyi.serve.mapper.SubmissionMapper">
+
+    <select id="selectRanking" resultType="space.anyi.serve.entity.contest.RankVo">
+        SELECT u.id AS userId, u.username,
+               COALESCE(stats.totalScore, 0) AS totalScore,
+               COALESCE(stats.maxAcMinutes, 0) AS maxAcMinutes,
+               reg.created_at AS regTime
+        FROM dev.contest_registration reg
+        JOIN dev.user u ON u.id = reg.user_id AND u.delete_flag = 0
+        LEFT JOIN (
+            SELECT ac.user_id AS userId,
+                   SUM(cp.score) AS totalScore,
+                   MAX(CEIL(EXTRACT(EPOCH FROM (ac.ac_time - c.start_datetime)) / 60.0)) AS maxAcMinutes
+            FROM (SELECT contest_id, user_id, problem_id, MIN(created_at) AS ac_time
+                  FROM dev.submission
+                  WHERE status = 'AC' AND delete_flag = 0
+                  GROUP BY contest_id, user_id, problem_id) ac
+            JOIN dev.contest_problem cp
+              ON cp.contest_id = ac.contest_id AND cp.problem_id = ac.problem_id AND cp.delete_flag = 0
+            JOIN dev.contest c ON c.id = ac.contest_id AND c.delete_flag = 0
+            WHERE ac.contest_id = #{contestId}
+            GROUP BY ac.user_id
+        ) stats ON stats.userId = reg.user_id
+        WHERE reg.contest_id = #{contestId} AND reg.delete_flag = 0
+          AND EXISTS (SELECT 1 FROM dev.submission s
+                      WHERE s.contest_id = reg.contest_id AND s.user_id = reg.user_id AND s.delete_flag = 0)
+        ORDER BY totalScore DESC, maxAcMinutes ASC, reg.created_at ASC
+    </select>
+
+</mapper>