目录

一、功能梳理
- 不包含题目的竞赛的新增。(不允许发布)
1.1. 前端携带着用户输入的竞赛基本信息,向后端发起添加竞赛请求。
1.2. 后端接收到请求之后,对相关参数进行处理,将竞赛信息存入数据库。并且将添加结果返回前端(成功/失败)
1.3 前端接收到后端的响应之后,如果成功,提示用户添加成功,并且竞赛列表中能够看到新增的竞赛。如果失败,提示用户失败原因。 - 包含题目的竞赛的新增。
2.1. 新增一个不含题目的竞赛。
2.2. 点击添加题目按钮,发起获取题目列表的请请求。(请求之前实现的题目列表接口即可)
2.3. 从题目列表中选择题目,点击提交按钮。携带着竞赛id和题目id发起为竞赛添加题目的请求。
2.4.后端接收到请求后,处理相关的参数将竞赛题目信息存储数据库中。并且将添加结果返回给前端。
2.5.前端接收后端的响应之后,如果成功竞赛的题目列表中要能看到新增增加进来的竞赛。如果失败的话,提示用户失败原因。
二、controller
oj-modules/oj-system/src/main/java/com/yj/system/controller/exam/ExamController.java添加新增竞赛基本信息和添加竞赛包含题目的两个方法
java
@PostMapping("/add")
public R<String> add(@RequestBody @Validated ExamAddDTO examAddDTO) {
return R.success(examService.add(examAddDTO));
}
@PostMapping("/question/add")
public R<Boolean> questionAdd(@RequestBody @Validated ExamQuestionAddDTO examQuestionAddDTOAddDTO) {
return R.success(examService.questionAdd(examQuestionAddDTOAddDTO));
}
接收参数:
oj-modules/oj-system/src/main/java/com/yj/system/domain/exam/dto/ExamAddDTO.java,新增竞赛基本信息的参数,竞赛的标题,竞赛的开始时间与结束时间
java
package com.yj.system.domain.exam.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
public class ExamAddDTO {
/**
* 竞赛标题
*/
@NotBlank(message = "竞赛标题不能为空")
private String title;
/**
* 竞赛开始时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@NotBlank(message = "竞赛开始时间不能为空")
private LocalDateTime startTime;
/**
* 竞赛结束时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@NotBlank(message = "竞赛结束时间不能为空")
private LocalDateTime endTime;
}
oj-modules/oj-system/src/main/java/com/yj/system/domain/exam/dto/ExamQuestionAddDTO.java,新增竞赛包含题目,参数是竞赛id和题目id的有顺序不重复的集合
java
package com.yj.system.domain.exam.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
import java.util.LinkedHashSet;
import java.util.Set;
@Setter
@Getter
public class ExamQuestionAddDTO {
/**
* 竞赛id
*/
@NotBlank(message = "竞赛id不能为空")
private Long examId;
/**
* 题目id
*/
private LinkedHashSet<Long> questionIdSet;
}
三、service
oj-modules/oj-system/src/main/java/com/yj/system/service/exam/IExamService.java,接口方法
java
String add(ExamAddDTO examAddDTO);
boolean questionAdd(ExamQuestionAddDTO examQuestionAddDTOAddDTO);
oj-modules/oj-system/src/main/java/com/yj/system/service/exam/impl/ExamServiceImpl.java实现:在进行竞赛的题目添加的时候,由于后面竞赛修改接口也会使用,所以进行参数的校验的时候需要带上竞赛id.
java
@Override
public String add(ExamAddDTO examAddDTO) {
//参数校验,竞赛名称不能重复
checkExamSaveParams(examAddDTO,null);
//竞赛已发布不能操作
extracted(examDO);
// 3. 拷贝属性并插入
ExamDO examDO = new ExamDO();
BeanUtil.copyProperties(examAddDTO,examDO);
// 4. 插入数据库
examMapper.insert(examDO);
return examDO.getExamId().toString();
}
@Override
public boolean questionAdd(ExamQuestionAddDTO examQuestionAddDTOAddDTO) {
//查询竞赛是否存在
ExamDO examDO = getExamDO(examQuestionAddDTOAddDTO.getExamId());
//竞赛是否开始
checkExamStarted(examDO);
//竞赛已发布不能操作
extracted(examDO);
//查询参数校验
if(CollectionUtil.isEmpty(examQuestionAddDTOAddDTO.getQuestionIdSet())) return true;
//查询要插入的题目是否都存在
Set<Long> questionIdSet = examQuestionAddDTOAddDTO.getQuestionIdSet();
List<QuestionDO> questionDOS = questionMapper.selectBatchIds(questionIdSet);
if(CollectionUtil.isEmpty(questionDOS) || questionDOS.size() < questionIdSet.size()) {
throw new ServiceException(ResultCodeEnum.EXAM_QUESTION_NOT_EXISTS);
}
return extracted(questionIdSet, examDO);
}
private boolean extracted(Set<Long> questionIdSet, ExamDO examDO) {
//赋值
int num = 1;
List<ExamQuestionDO> examQuestionDOS = new ArrayList<>();
for(Long questionId : questionIdSet) {
ExamQuestionDO examQuestionDO = new ExamQuestionDO();
examQuestionDO.setQuestionId(questionId);
examQuestionDO.setExamId(examDO.getExamId());
examQuestionDO.setQuestionOrder(num++);
examQuestionDOS.add(examQuestionDO);
}
//批量插入题目
return saveBatch(examQuestionDOS);
}
private ExamDO getExamDO(Long examId) {
ExamDO examDO = examMapper.selectById(examId);
if(null == examDO) {
throw new ServiceException(ResultCodeEnum.EXAM_NOT_EXISTS);
}
return examDO;
}
private void checkExamSaveParams(ExamAddDTO examAddDTO, Long examId) {
// 1.构建查询条件:标题重复
List<ExamDO> examDOS = examMapper.selectList(new LambdaQueryWrapper<ExamDO>()
.eq(ExamDO::getTitle, examAddDTO.getTitle())
.ne(examId != null, ExamDO::getExamId,examId)
);
if(CollectionUtil.isNotEmpty(examDOS)) {
throw new ServiceException(ResultCodeEnum.FAILED_ALREADY_EXISTS);
}
//2. 开始时间比现在时间晚, 比结束时间早
if(examAddDTO.getStartTime().isBefore(LocalDateTime.now())) {
throw new ServiceException(ResultCodeEnum.EXAM_START_TIME_BEFORE_CURRENT_TIME);
}
if(examAddDTO.getStartTime().isAfter(examAddDTO.getEndTime())) {
throw new ServiceException(ResultCodeEnum.EXAM_START_TIME_AFTER_END_TIME);
}
}
private void checkExamStarted(ExamDO examDO) {
if(examDO.getStartTime().isBefore(LocalDateTime.now())) {
throw new ServiceException(ResultCodeEnum.EXAM_STARTED);
}
}
private static void extracted(ExamDO examDO) {
if(examDO.getStatus().equals(Constants.TRUE)) {
throw new ServiceException(ResultCodeEnum.EXAM_PUBLISHED);
}
}
实现批量添加,继承:
java
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper,ExamQuestionDO> implements IExamService {}
四、mapper
oj-modules/oj-system/src/main/java/com/yj/system/mapper/exam/ExamQuestionMapper.java
java
package com.yj.system.mapper.exam;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yj.system.domain.exam.ExamQuestionDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ExamQuestionMapper extends BaseMapper<ExamQuestionDO> {
}