【OJ项目】深入剖析题目接口控制器:功能、实现与应用
《深入剖析题目接口控制器:功能、实现与应用》
一、引言
在在线编程平台或竞赛系统中,题目管理和提交是核心功能之一。QuestionController
类作为控制器层,承担着处理与题目相关的各种请求的重要职责,包括题目的增删改查、题目提交等操作。本文将详细剖析该类的代码,深入理解其功能和实现细节。
二、类概述
QuestionController
类使用 Spring 的 @RestController
注解,表明它是一个 RESTful 风格的控制器,用于处理 HTTP 请求。它位于 /
根路径下,提供了一系列与题目相关的接口。该类依赖于 QuestionService
、UserFeignClient
和 QuestionSubmitService
等服务,通过这些服务来完成具体的业务逻辑。
@RestController
@RequestMapping("/")
@Slf4j
public class QuestionController {
@Resource
private QuestionService questionService;
@Resource
private UserFeignClient userFeignClient;
@Resource
private QuestionSubmitService questionSubmitService;
private final static Gson GSON = new Gson();
// ... 具体方法 ...
}
三、增删改查功能实现
3.1 创建题目
@PostMapping("/add")
public BaseResponse<Long> addQuestion(@RequestBody QuestionAddRequest questionAddRequest, HttpServletRequest request) {
if (questionAddRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Question question = new Question();
BeanUtils.copyProperties(questionAddRequest, question);
List<String> tags = questionAddRequest.getTags();
if (tags != null) {
question.setTags(GSON.toJson(tags));
}
List<JudgeCase> judgeCase = questionAddRequest.getJudgeCase();
if (judgeCase != null) {
question.setJudgeCase(GSON.toJson(judgeCase));
}
JudgeConfig judgeConfig = questionAddRequest.getJudgeConfig();
if (judgeConfig != null) {
question.setJudgeConfig(GSON.toJson(judgeConfig));
}
questionService.validQuestion(question, true);
User loginUser = userFeignClient.getLoginUser(request);
question.setUserId(loginUser.getId());
question.setFavourNum(0);
question.setThumbNum(0);
boolean result = questionService.save(question);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
long newQuestionId = question.getId();
return ResultUtils.success(newQuestionId);
}
- 功能:处理创建题目的请求,将用户提交的题目信息保存到数据库中。
- 实现步骤:
-
- 检查请求参数是否为空,若为空则抛出参数错误异常。
- 将
QuestionAddRequest
对象的属性复制到Question
对象中。 - 对题目标签、测试用例和判题配置进行 JSON 序列化处理。
- 调用
questionService
的validQuestion
方法对题目信息进行校验。 - 获取当前登录用户的信息,并将用户 ID 关联到题目中。
- 初始化题目点赞数和收藏数为 0。
- 调用
questionService
的save
方法保存题目信息,若保存失败则抛出操作错误异常。 - 返回新创建题目的 ID。
3.2 删除题目
@PostMapping("/delete")
public BaseResponse<Boolean> deleteQuestion(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
if (deleteRequest == null || deleteRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = userFeignClient.getLoginUser(request);
long id = deleteRequest.getId();
// 判断是否存在
Question oldQuestion = questionService.getById(id);
ThrowUtils.throwIf(oldQuestion == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可删除
if (!oldQuestion.getUserId().equals(user.getId()) && !userFeignClient.isAdmin(user)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
boolean b = questionService.removeById(id);
return ResultUtils.success(b);
}
- 功能:处理删除题目的请求,从数据库中删除指定 ID 的题目。
- 实现步骤:
-
- 检查请求参数是否合法,若不合法则抛出参数错误异常。
- 获取当前登录用户的信息。
- 根据题目 ID 查询题目信息,若题目不存在则抛出未找到错误异常。
- 检查当前用户是否有删除权限,只有题目创建者或管理员才能删除题目,若没有权限则抛出无权限错误异常。
- 调用
questionService
的removeById
方法删除题目信息,并返回删除结果。
3.3 更新题目(仅管理员)
@PostMapping("/update")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> updateQuestion(@RequestBody QuestionUpdateRequest questionUpdateRequest) {
if (questionUpdateRequest == null || questionUpdateRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Question question = new Question();
BeanUtils.copyProperties(questionUpdateRequest, question);
List<String> tags = questionUpdateRequest.getTags();
if (tags != null) {
question.setTags(GSON.toJson(tags));
}
List<JudgeCase> judgeCase = questionUpdateRequest.getJudgeCase();
if (judgeCase != null) {
question.setJudgeCase(GSON.toJson(judgeCase));
}
JudgeConfig judgeConfig = questionUpdateRequest.getJudgeConfig();
if (judgeConfig != null) {
question.setJudgeConfig(GSON.toJson(judgeConfig));
}
// 参数校验
questionService.validQuestion(question, false);
long id = questionUpdateRequest.getId();
// 判断是否存在
Question oldQuestion = questionService.getById(id);
ThrowUtils.throwIf(oldQuestion == null, ErrorCode.NOT_FOUND_ERROR);
boolean result = questionService.updateById(question);
return ResultUtils.success(result);
}
- 功能:处理更新题目的请求,只有管理员才能执行此操作。
- 实现步骤:
-
- 检查请求参数是否合法,若不合法则抛出参数错误异常。
- 将
QuestionUpdateRequest
对象的属性复制到Question
对象中。 - 对题目标签、测试用例和判题配置进行 JSON 序列化处理。
- 调用
questionService
的validQuestion
方法对题目信息进行校验。 - 根据题目 ID 查询题目信息,若题目不存在则抛出未找到错误异常。
- 调用
questionService
的updateById
方法更新题目信息,并返回更新结果。
3.4 根据 ID 获取题目
@GetMapping("/get")
public BaseResponse<Question> getQuestionById(long id, HttpServletRequest request) {
if (id <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Question question = questionService.getById(id);
if (question == null) {
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
}
User loginUser = userFeignClient.getLoginUser(request);
// 不是本人或管理员,不能直接获取所有信息
if (!question.getUserId().equals(loginUser.getId()) && !userFeignClient.isAdmin(loginUser)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
return ResultUtils.success(question);
}
- 功能:根据题目 ID 获取题目信息,只有题目创建者或管理员才能获取完整信息。
- 实现步骤:
-
- 检查题目 ID 是否合法,若不合法则抛出参数错误异常。
- 根据题目 ID 查询题目信息,若题目不存在则抛出未找到错误异常。
- 获取当前登录用户的信息。
- 检查当前用户是否有获取完整信息的权限,若没有权限则抛出无权限错误异常。
- 返回题目信息。
3.5 根据 ID 获取脱敏后的题目信息
@GetMapping("/get/vo")
public BaseResponse<QuestionVO> getQuestionVOById(long id, HttpServletRequest request) {
if (id <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Question question = questionService.getById(id);
if (question == null) {
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
}
return ResultUtils.success(questionService.getQuestionVO(question, request));
}
- 功能:根据题目 ID 获取脱敏后的题目信息,适用于普通用户查看。
- 实现步骤:
-
- 检查题目 ID 是否合法,若不合法则抛出参数错误异常。
- 根据题目 ID 查询题目信息,若题目不存在则抛出未找到错误异常。
- 调用
questionService
的getQuestionVO
方法获取脱敏后的题目信息并返回。
3.6 分页获取题目列表(封装类)
@PostMapping("/list/page/vo")
public BaseResponse<Page<QuestionVO>> listQuestionVOByPage(@RequestBody QuestionQueryRequest questionQueryRequest,
HttpServletRequest request) {
long current = questionQueryRequest.getCurrent();
long size = questionQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
Page<Question> questionPage = questionService.page(new Page<>(current, size),
questionService.getQueryWrapper(questionQueryRequest));
return ResultUtils.success(questionService.getQuestionVOPage(questionPage, request));
}
- 功能:分页获取题目列表,并返回封装后的题目信息(
QuestionVO
)。 - 实现步骤:
-
- 从请求参数中获取当前页码和每页数量。
- 检查每页数量是否超过限制,若超过则抛出参数错误异常,防止爬虫过度请求。
- 调用
questionService
的page
方法进行分页查询。 - 调用
questionService
的getQuestionVOPage
方法将查询结果封装为QuestionVO
并返回。
3.7 分页获取当前用户创建的题目列表
@PostMapping("/my/list/page/vo")
public BaseResponse<Page<QuestionVO>> listMyQuestionVOByPage(@RequestBody QuestionQueryRequest questionQueryRequest,
HttpServletRequest request) {
if (questionQueryRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User loginUser = userFeignClient.getLoginUser(request);
questionQueryRequest.setUserId(loginUser.getId());
long current = questionQueryRequest.getCurrent();
long size = questionQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
Page<Question> questionPage = questionService.page(new Page<>(current, size),
questionService.getQueryWrapper(questionQueryRequest));
return ResultUtils.success(questionService.getQuestionVOPage(questionPage, request));
}
- 功能:分页获取当前用户创建的题目列表,并返回封装后的题目信息(
QuestionVO
)。 - 实现步骤:
-
- 检查请求参数是否为空,若为空则抛出参数错误异常。
- 获取当前登录用户的信息,并将用户 ID 设置到查询请求中。
- 从请求参数中获取当前页码和每页数量。
- 检查每页数量是否超过限制,若超过则抛出参数错误异常,防止爬虫过度请求。
- 调用
questionService
的page
方法进行分页查询。 - 调用
questionService
的getQuestionVOPage
方法将查询结果封装为QuestionVO
并返回。
3.8 分页获取题目列表(仅管理员)
@PostMapping("/list/page")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Page<Question>> listQuestionByPage(@RequestBody QuestionQueryRequest questionQueryRequest,
HttpServletRequest request) {
long current = questionQueryRequest.getCurrent();
long size = questionQueryRequest.getPageSize();
Page<Question> questionPage = questionService.page(new Page<>(current, size),
questionService.getQueryWrapper(questionQueryRequest));
return ResultUtils.success(questionPage);
}
- 功能:分页获取题目列表,只有管理员才能执行此操作。
- 实现步骤:
-
- 从请求参数中获取当前页码和每页数量。
- 调用
questionService
的page
方法进行分页查询。 - 返回查询结果。
四、其他功能实现
4.1 编辑题目(用户)
@PostMapping("/edit")
public BaseResponse<Boolean> editQuestion(@RequestBody QuestionEditRequest questionEditRequest, HttpServletRequest request) {
if (questionEditRequest == null || questionEditRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Question question = new Question();
BeanUtils.copyProperties(questionEditRequest, question);
List<String> tags = questionEditRequest.getTags();
if (tags != null) {
question.setTags(GSON.toJson(tags));
}
List<JudgeCase> judgeCase = questionEditRequest.getJudgeCase();
if (judgeCase != null) {
question.setJudgeCase(GSON.toJson(judgeCase));
}
JudgeConfig judgeConfig = questionEditRequest.getJudgeConfig();
if (judgeConfig != null) {
question.setJudgeConfig(GSON.toJson(judgeConfig));
}
// 参数校验
questionService.validQuestion(question, false);
User loginUser = userFeignClient.getLoginUser(request);
long id = questionEditRequest.getId();
// 判断是否存在
Question oldQuestion = questionService.getById(id);
ThrowUtils.throwIf(oldQuestion == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可编辑
if (!oldQuestion.getUserId().equals(loginUser.getId()) && !userFeignClient.isAdmin(loginUser)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
boolean result = questionService.updateById(question);
return ResultUtils.success(result);
}
- 功能:处理编辑题目的请求,只有题目创建者或管理员才能执行此操作。
- 实现步骤:
-
- 检查请求参数是否合法,若不合法则抛出参数错误异常。
- 将
QuestionEditRequest
对象的属性复制到Question
对象中。 - 对题目标签、测试用例和判题配置进行 JSON 序列化处理。
- 调用
questionService
的validQuestion
方法对题目信息进行校验。 - 获取当前登录用户的信息。
- 根据题目 ID 查询题目信息,若题目不存在则抛出未找到错误异常。
- 检查当前用户是否有编辑权限,若没有权限则抛出无权限错误异常。
- 调用
questionService
的updateById
方法更新题目信息,并返回更新结果。
4.2 提交题目
@PostMapping("/question_submit/do")
public BaseResponse<Long> doQuestionSubmit(@RequestBody QuestionSubmitAddRequest questionSubmitAddRequest,
HttpServletRequest request) {
if (questionSubmitAddRequest == null || questionSubmitAddRequest.getQuestionId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 登录才能提交
final User loginUser = userFeignClient.getLoginUser(request);
long questionSubmitId = questionSubmitService.doQuestionSubmit(questionSubmitAddRequest, loginUser);
return ResultUtils.success(questionSubmitId);
}
- 功能:处理提交题目的请求,用户必须登录才能提交。
- 实现步骤:
-
- 检查请求参数是否合法,若不合法则抛出参数错误异常。
- 获取当前登录用户的信息。
- 调用
questionSubmitService
的doQuestionSubmit
方法处理题目提交,并返回提交记录的 ID。
4.3 分页获取题目提交列表
@PostMapping("/question_submit/list/page")
public BaseResponse<Page<QuestionSubmitVO>> listQuestionSubmitByPage(@RequestBody QuestionSubmitQueryRequest questionSubmitQueryRequest,
HttpServletRequest request) {
long current = questionSubmitQueryRequest.getCurrent();
long size = questionSubmitQueryRequest.getPageSize();
// 从数据库中查询原始的题目提交分页信息
Page<QuestionSubmit> questionSubmitPage = questionSubmitService.page(new Page<>(current, size),
questionSubmitService.getQueryWrapper(questionSubmitQueryRequest));
final User loginUser = userFeignClient.getLoginUser(request);
// 返回脱敏信息
return ResultUtils.success(questionSubmitService.getQuestionSubmitVOPage(questionSubmitPage, loginUser));
}
- 功能:分页获取题目提交列表