refactor: improve question service entity types and security config

- Change Question entity time fields from Date to LocalDateTime for Java 8+ time API consistency
- Add auto-fill annotation for updateTime field in Question and QuestionSubmit entities
- Simplify Serializable import in QuestionQueryRequestDTO
- Temporarily set SecurityConfiguration to permit all requests for development
- Remove generated .flattened-pom.xml build artifacts from version control

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-01-26 21:57:08 +08:00
parent 17f58a7b45
commit be709efa2e
25 changed files with 166 additions and 843 deletions

View File

@@ -5,7 +5,7 @@ import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 题目实体类
@@ -90,12 +90,13 @@ public class Question implements Serializable {
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
private LocalDateTime createTime;
/**
* 更新时间
*/
private Date updateTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 是否删除

View File

@@ -66,5 +66,6 @@ public class QuestionSubmit implements Serializable {
/**
* 是否删除
*/
@TableLogic
private Integer isDelete;
}

View File

@@ -7,6 +7,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
@@ -15,7 +16,7 @@ import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "题目查询请求")
public class QuestionQueryRequestDTO extends Page<Question> implements java.io.Serializable {
public class QuestionQueryRequestDTO extends Page<Question> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;

View File

@@ -1,10 +1,12 @@
package cn.meowrain.aioj.backend.question.dto.resp;
import cn.meowrain.aioj.backend.question.dto.req.JudgeCase;
import cn.meowrain.aioj.backend.question.dto.req.JudgeConfig;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
/**
@@ -49,6 +51,12 @@ public class QuestionResponseDTO implements Serializable {
@Schema(description = "标签列表", example = "[\"\", \"数组\", \"算法\"]")
private List<String> tags;
/**
* 判题用例列表
*/
@Schema(description = "判题用例列表")
private List<JudgeCase> judgeCase;
/**
* 通过数
*/
@@ -62,11 +70,11 @@ public class QuestionResponseDTO implements Serializable {
private Integer submitCount;
/**
* 判题配置JSON格式字符串
* 判题配置
* 包含 timeLimit, memoryLimit, stackLimit 等
*/
@Schema(description = "判题配置 (JSON字符串包含时间限制、内存限制等)", example = "{\"timeLimit\": 1000, \"memoryLimit\": 256}")
private String judgeConfig;
@Schema(description = "判题配置", example = "{\"timeLimit\": 1000, \"memoryLimit\": 256}")
private JudgeConfig judgeConfig;
/**
* 点赞数
@@ -89,12 +97,12 @@ public class QuestionResponseDTO implements Serializable {
/**
* 创建时间
*/
@Schema(description = "创建时间", example = "2024-01-20 12:00:00")
private Date createTime;
@Schema(description = "创建时间", example = "2024-01-20T12:00:00")
private LocalDateTime createTime;
/**
* 更新时间
*/
@Schema(description = "更新时间", example = "2024-01-21 10:30:00")
private Date updateTime;
@Schema(description = "更新时间", example = "2024-01-21T10:30:00")
private LocalDateTime updateTime;
}

View File

@@ -1,7 +1,10 @@
package cn.meowrain.aioj.backend.question.service;
import cn.meowrain.aioj.backend.question.dao.entity.Question;
import cn.meowrain.aioj.backend.question.dto.req.QuestionCreateRequestDTO;
import cn.meowrain.aioj.backend.question.dto.req.QuestionQueryRequestDTO;
import cn.meowrain.aioj.backend.question.dto.req.QuestionUpdateRequestDTO;
import cn.meowrain.aioj.backend.question.dto.resp.QuestionResponseDTO;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
@@ -10,6 +13,13 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface QuestionService extends IService<Question> {
/**
* 创建题目(使用责任链校验)
* @param requestDTO 题目创建请求DTO
* @return 题目ID
*/
Long createQuestionWithChain(QuestionCreateRequestDTO requestDTO);
/**
* 创建题目
* @param question 题目信息
@@ -36,7 +46,7 @@ public interface QuestionService extends IService<Question> {
* @param questionId 题目ID
* @return 题目详情
*/
Question getQuestionById(Long questionId);
QuestionResponseDTO getQuestionById(Long questionId);
/**
* 根据ID获取题目详情内部接口
@@ -50,5 +60,5 @@ public interface QuestionService extends IService<Question> {
* @param request 查询条件
* @return 题目分页列表
*/
Page<Question> listQuestions(QuestionQueryRequestDTO request);
Page<QuestionResponseDTO> listQuestions(QuestionQueryRequestDTO request);
}

View File

@@ -1,23 +1,83 @@
package cn.meowrain.aioj.backend.question.service.impl;
import cn.meowrain.aioj.backend.question.common.enums.ChainMarkEnums;
import cn.meowrain.aioj.backend.question.dao.entity.Question;
import cn.meowrain.aioj.backend.question.dao.mapper.QuestionMapper;
import cn.meowrain.aioj.backend.question.dto.req.QuestionQueryRequestDTO;
import cn.meowrain.aioj.backend.question.dto.chains.context.QuestionCreateRequestParamVerifyContext;
import cn.meowrain.aioj.backend.question.dto.req.*;
import cn.meowrain.aioj.backend.question.dto.resp.QuestionResponseDTO;
import cn.meowrain.aioj.backend.question.service.QuestionService;
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 com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.core.type.TypeReference;
import java.util.List;
/**
* 题目服务实现
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {
private final QuestionCreateRequestParamVerifyContext questionCreateChainContext;
@Override
public Long createQuestionWithChain(QuestionCreateRequestDTO requestDTO) {
// 执行责任链校验
log.info("开始执行题目创建责任链校验");
questionCreateChainContext.handler(
ChainMarkEnums.QUESTION_CREATE_PARAM_VERIFY_CHAIN.getMark(),
requestDTO
);
log.info("题目创建责任链校验通过");
// 校验通过,转换为实体并保存
Question question = new Question();
BeanUtils.copyProperties(requestDTO, question);
// 处理 tags 字段List<String> -> JSON 字符串
if (requestDTO.getTags() != null && !requestDTO.getTags().isEmpty()) {
try {
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
question.setTags(mapper.writeValueAsString(requestDTO.getTags()));
} catch (Exception e) {
log.error("序列化 tags 失败", e);
}
}
// 处理 judgeConfig 字段JudgeConfig 对象 -> JSON 字符串
if (requestDTO.getJudgeConfig() != null) {
try {
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
question.setJudgeConfig(mapper.writeValueAsString(requestDTO.getJudgeConfig()));
} catch (Exception e) {
log.error("序列化 judgeConfig 失败", e);
}
}
// 处理 judgeCase 字段List<JudgeCase> -> JSON 字符串
if (requestDTO.getJudgeCase() != null && !requestDTO.getJudgeCase().isEmpty()) {
try {
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
question.setJudgeCase(mapper.writeValueAsString(requestDTO.getJudgeCase()));
} catch (Exception e) {
log.error("序列化 judgeCase 失败", e);
}
}
question.setUserId(1L);
this.save(question);
return question.getId();
}
@Override
public Long createQuestion(Question question) {
this.save(question);
@@ -35,8 +95,9 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> i
}
@Override
public Question getQuestionById(Long questionId) {
return this.getById(questionId);
public QuestionResponseDTO getQuestionById(Long questionId) {
Question question = this.getById(questionId);
return convertToDTO(question);
}
@Override
@@ -45,7 +106,7 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> i
}
@Override
public Page<Question> listQuestions(QuestionQueryRequestDTO request) {
public Page<QuestionResponseDTO> listQuestions(QuestionQueryRequestDTO request) {
LambdaQueryWrapper<Question> wrapper = new LambdaQueryWrapper<>();
// ID 精确查询
@@ -80,6 +141,64 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> i
}
// 直接使用 request 作为分页对象
return this.page(request, wrapper);
Page<Question> page = this.page(request, wrapper);
// 转换为 DTO
Page<QuestionResponseDTO> dtoPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
dtoPage.setRecords(page.getRecords().stream()
.map(this::convertToDTO)
.toList());
return dtoPage;
}
/**
* 将 Question 实体转换为 QuestionResponseDTO
*/
private QuestionResponseDTO convertToDTO(Question question) {
if (question == null) {
return null;
}
QuestionResponseDTO dto = new QuestionResponseDTO();
BeanUtils.copyProperties(question, dto);
// 处理 tags 字段JSON 字符串 -> List<String>
if (question.getTags() != null && !question.getTags().isEmpty()) {
try {
ObjectMapper mapper = new ObjectMapper();
List<String> tagList = mapper.readValue(question.getTags(),
new TypeReference<List<String>>() {});
dto.setTags(tagList);
} catch (Exception e) {
log.error("解析 tags JSON 失败: {}", question.getTags(), e);
}
}
// 处理 judgeConfig 字段JSON 字符串 -> JudgeConfig 对象
if (question.getJudgeConfig() != null && !question.getJudgeConfig().isEmpty()) {
try {
ObjectMapper mapper = new ObjectMapper();
JudgeConfig judgeConfig =
mapper.readValue(question.getJudgeConfig(),
JudgeConfig.class);
dto.setJudgeConfig(judgeConfig);
} catch (Exception e) {
log.error("解析 judgeConfig JSON 失败: {}", question.getJudgeConfig(), e);
}
}
// 处理 judgeCase 字段JSON 字符串 -> List<JudgeCase>
if (question.getJudgeCase() != null && !question.getJudgeCase().isEmpty()) {
try {
ObjectMapper mapper = new ObjectMapper();
List<JudgeCase> judgeCaseList =
mapper.readValue(question.getJudgeCase(),
new TypeReference<List<JudgeCase>>() {});
dto.setJudgeCase(judgeCaseList);
} catch (Exception e) {
log.error("解析 judgeCase JSON 失败: {}", question.getJudgeCase(), e);
}
}
return dto;
}
}

View File

@@ -20,8 +20,8 @@ springdoc:
operations-sorter: alpha
group-configs:
- group: 'default'
paths-to-match: '/api/**'
packages-to-scan: cn.meowrain.aioj.backend.questionservice.controller
paths-to-match: '/**'
packages-to-scan: cn.meowrain.aioj.backend.question
knife4j:
basic:
enable: true