test
parent
8833a1d575
commit
e71a2b774b
|
|
@ -1,22 +1,15 @@
|
|||
package org.qinan.cedu.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.qinan.cedu.model.dto.PaperQuestionPageQueryDTO;
|
||||
import org.qinan.cedu.model.dto.QuestionUpdateDTO;
|
||||
import org.qinan.cedu.model.vo.PaperQuestionVO;
|
||||
import org.qinan.cedu.model.vo.QuestionVO;
|
||||
import org.qinan.cedu.service.QuestionService;
|
||||
import org.qinan.safetyeval.client.dto.PageResponse;
|
||||
import org.qinan.safetyeval.client.dto.SingleResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 试题管理
|
||||
|
|
@ -36,4 +29,11 @@ public class QuestionController {
|
|||
return SingleResponse.success();
|
||||
}
|
||||
|
||||
@ApiOperation("查询习题")
|
||||
@GetMapping("/find")
|
||||
public SingleResponse<QuestionVO> find(@RequestParam @ApiParam(value = "试题id", readOnly = true) Long questionId) {
|
||||
QuestionVO questionVO = questionService.find(questionId);
|
||||
return SingleResponse.success(questionVO);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
package org.qinan.cedu.convertor;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.qinan.cedu.model.dto.QuestionOptionUpdateDTO;
|
||||
import org.qinan.cedu.model.dto.QuestionUpdateDTO;
|
||||
import org.qinan.cedu.model.entity.QuestionDO;
|
||||
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
||||
import org.qinan.cedu.model.vo.QuestionOptionVO;
|
||||
import org.qinan.cedu.model.vo.QuestionVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface QuestionConvertor {
|
||||
|
||||
QuestionVO convertEToVo(QuestionDO entity);
|
||||
|
||||
List<QuestionVO> convertEListToVoList(List<QuestionDO> list);
|
||||
|
||||
/**
|
||||
* 编辑习题请求转试题实体
|
||||
*
|
||||
* @param dto 编辑内容
|
||||
* @return 试题实体
|
||||
*/
|
||||
QuestionDO convertDtoToE(QuestionUpdateDTO dto);
|
||||
|
||||
/**
|
||||
* 选项编辑DTO列表转选项实体列表
|
||||
*
|
||||
* @param list 选项编辑DTO列表
|
||||
* @return 选项实体列表
|
||||
*/
|
||||
List<QuestionOptionDO> convertOptionDtoListToEList(List<QuestionOptionUpdateDTO> list);
|
||||
|
||||
QuestionOptionVO convertOptionEToVo(QuestionOptionDO entity);
|
||||
|
||||
List<QuestionOptionVO> convertOptionEListToVoList(List<QuestionOptionDO> list);
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import org.apache.ibatis.annotations.Param;
|
|||
import org.qinan.cedu.model.dto.PaperQuestionPageQueryDTO;
|
||||
import org.qinan.cedu.model.entity.QuestionDO;
|
||||
import org.qinan.cedu.model.vo.PaperQuestionVO;
|
||||
import org.qinan.cedu.model.vo.QuestionVO;
|
||||
|
||||
/**
|
||||
* 试题 Mapper
|
||||
|
|
@ -23,4 +24,12 @@ public interface QuestionMapper extends BaseMapper<QuestionDO> {
|
|||
* @return 试题分页
|
||||
*/
|
||||
IPage<PaperQuestionVO> pagePaperQuestions(Page<PaperQuestionVO> page, @Param("query") PaperQuestionPageQueryDTO query);
|
||||
|
||||
/**
|
||||
* 查询习题详情(关联课件名称)
|
||||
*
|
||||
* @param questionId 试题id
|
||||
* @return 习题详情
|
||||
*/
|
||||
QuestionVO findQuestion(@Param("questionId") Long questionId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package org.qinan.cedu.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 试题选项编辑内容(id为空新增、id在库中更新,不在请求中的库内选项将被逻辑删除)
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "QuestionOptionUpdateDTO", description = "试题选项编辑内容")
|
||||
public class QuestionOptionUpdateDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty(value = "选项ID(为空则新增)")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "是否正确答案 true 是,false 不是", required = true)
|
||||
@NotBlank(message = "是否正确答案不能为空")
|
||||
private Boolean isCorrect;
|
||||
|
||||
@ApiModelProperty(value = "选项标识:A,B,C,D", required = true)
|
||||
@NotBlank(message = "选项标识不能为空")
|
||||
private String optionKey;
|
||||
|
||||
@ApiModelProperty(value = "选项内容", required = true)
|
||||
@NotBlank(message = "选项内容不能为空")
|
||||
private String optionText;
|
||||
}
|
||||
|
|
@ -4,17 +4,14 @@ import io.swagger.annotations.ApiModel;
|
|||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.DecimalMax;
|
||||
import javax.validation.constraints.DecimalMin;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 编辑习题请求
|
||||
* 编辑习题请求(字段参考 QuestionVO)
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "QuestionUpdateDTO", description = "编辑习题请求")
|
||||
|
|
@ -24,40 +21,29 @@ public class QuestionUpdateDTO {
|
|||
@NotNull(message = "试题id不能为空")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "题干", required = true)
|
||||
@ApiModelProperty(value = "题目内容", required = true)
|
||||
@NotBlank(message = "题干不能为空")
|
||||
private String title;
|
||||
|
||||
@ApiModelProperty(value = "分值", required = true)
|
||||
@NotNull(message = "分值不能为空")
|
||||
@DecimalMin(value = "0.01", message = "分值必须大于0")
|
||||
@DecimalMax(value = "999.99", message = "分值不能超过999.99")
|
||||
@DecimalMin(value = "0.1", message = "分值必须大于0")
|
||||
@DecimalMax(value = "999.9", message = "分值不能超过999.9")
|
||||
@Digits(integer = 4, fraction = 1)
|
||||
private BigDecimal score;
|
||||
|
||||
@ApiModelProperty(value = "选项A内容", required = true)
|
||||
@NotBlank(message = "选项A不能为空")
|
||||
private String optionA;
|
||||
|
||||
@ApiModelProperty(value = "选项B内容", required = true)
|
||||
@NotBlank(message = "选项B不能为空")
|
||||
private String optionB;
|
||||
|
||||
@ApiModelProperty(value = "选项C内容")
|
||||
@Size(max = 64, message = "选项C长度不能超过64个字符")
|
||||
private String optionC;
|
||||
|
||||
@ApiModelProperty(value = "选项D内容")
|
||||
@Size(max = 64, message = "选项D长度不能超过64个字符")
|
||||
private String optionD;
|
||||
|
||||
@ApiModelProperty(value = "答案:单选填写A,多选填写A,B", required = true)
|
||||
@NotEmpty(message = "答案不能为空")
|
||||
private List<String> answer;
|
||||
|
||||
@ApiModelProperty(value = "关联课件id courseware_management_id", required = true)
|
||||
@NotNull(message = "关联课件不能为空")
|
||||
private Long coursewareManagementId;
|
||||
|
||||
@ApiModelProperty(value = "答案解析")
|
||||
private String analysis;
|
||||
|
||||
@ApiModelProperty(value = "标签类型")
|
||||
private String tagType;
|
||||
|
||||
@ApiModelProperty(value = "关联课件管理id", required = true)
|
||||
@NotNull(message = "关联课件不能为空")
|
||||
private Long coursewareManagementId;
|
||||
|
||||
@ApiModelProperty(value = "试题选项信息", required = true)
|
||||
@NotEmpty(message = "试题选项不能为空")
|
||||
@Valid
|
||||
private List<QuestionOptionUpdateDTO> questionOptionList;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package org.qinan.cedu.model.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 试题选项
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "QuestionOption 试题选项", description = "试题选项")
|
||||
public class QuestionOptionVO {
|
||||
|
||||
@ApiModelProperty("选项ID")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("所属试题ID")
|
||||
private Long questionId;
|
||||
|
||||
@ApiModelProperty("选项标识:A,B,C,D,E,F")
|
||||
private String optionKey;
|
||||
|
||||
@ApiModelProperty("选项标志名称")
|
||||
private String optionName;
|
||||
|
||||
@ApiModelProperty("选项内容")
|
||||
private String optionText;
|
||||
|
||||
@ApiModelProperty("是否正确答案:1是,0否")
|
||||
private Boolean isCorrect;
|
||||
|
||||
@ApiModelProperty("选项显示顺序")
|
||||
private Integer sortOrder;
|
||||
}
|
||||
|
|
@ -1,18 +1,30 @@
|
|||
|
||||
package org.qinan.cedu.model.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 试题
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class QuestionVO extends BaseVO {
|
||||
public class QuestionVO {
|
||||
|
||||
@ApiModelProperty("主键")
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty("创建时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@ApiModelProperty("题目内容")
|
||||
private String title;
|
||||
|
|
@ -40,4 +52,10 @@ public class QuestionVO extends BaseVO {
|
|||
|
||||
@ApiModelProperty("关联课件管理id")
|
||||
private Long coursewareManagementId;
|
||||
|
||||
@ApiModelProperty("关联课件管理名称")
|
||||
private String coursewareManagementName;
|
||||
|
||||
@ApiModelProperty("试题选项信息")
|
||||
private List<QuestionOptionVO> questionOptionVOList;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import org.qinan.cedu.model.dto.QuestionUpdateDTO;
|
|||
import org.qinan.cedu.model.entity.QuestionDO;
|
||||
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
||||
import org.qinan.cedu.model.vo.PaperQuestionVO;
|
||||
import org.qinan.cedu.model.vo.QuestionVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -22,6 +23,14 @@ public interface QuestionService extends IService<QuestionDO> {
|
|||
*/
|
||||
void updateQuestion(QuestionUpdateDTO dto);
|
||||
|
||||
/**
|
||||
* 查询习题详情(关联课件名称,含选项信息)
|
||||
*
|
||||
* @param questionId 试题id
|
||||
* @return 习题详情
|
||||
*/
|
||||
QuestionVO find(Long questionId);
|
||||
|
||||
/**
|
||||
* 试卷试题分页查询(XML关联查询,含课件名称)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -7,16 +7,19 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.qinan.cedu.convertor.QuestionConvertor;
|
||||
import org.qinan.cedu.enums.DeleteEnum;
|
||||
import org.qinan.cedu.enums.QuestionTypeEnum;
|
||||
import org.qinan.cedu.mapper.QuestionMapper;
|
||||
import org.qinan.cedu.model.dto.PaperQuestionPageQueryDTO;
|
||||
import org.qinan.cedu.model.dto.QuestionOptionUpdateDTO;
|
||||
import org.qinan.cedu.model.dto.QuestionUpdateDTO;
|
||||
import org.qinan.cedu.model.entity.QuestionDO;
|
||||
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
||||
import org.qinan.cedu.model.vo.PaperQuestionVO;
|
||||
import org.qinan.cedu.model.vo.QuestionVO;
|
||||
import org.qinan.cedu.service.QuestionOptionService;
|
||||
import org.qinan.cedu.service.QuestionService;
|
||||
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
|
@ -25,9 +28,8 @@ import org.springframework.util.StringUtils;
|
|||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -41,11 +43,33 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
|
|||
@Autowired
|
||||
private QuestionOptionService questionOptionService;
|
||||
|
||||
private final QuestionConvertor questionConvertor;
|
||||
|
||||
@Override
|
||||
public IPage<PaperQuestionVO> pagePaperQuestions(PaperQuestionPageQueryDTO query) {
|
||||
return this.baseMapper.pagePaperQuestions(new Page<>(query.getCurrent(), query.getSize()), query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuestionVO find(Long questionId) {
|
||||
QuestionVO questionVO = this.baseMapper.findQuestion(questionId);
|
||||
if (questionVO == null) {
|
||||
throw new IllegalArgumentException("试题不存在:id=" + questionId);
|
||||
}
|
||||
// 查询选项信息(仅查询VO需要的字段)
|
||||
List<QuestionOptionDO> options = questionOptionService.lambdaQuery()
|
||||
.select(QuestionOptionDO::getId, QuestionOptionDO::getQuestionId,
|
||||
QuestionOptionDO::getOptionKey, QuestionOptionDO::getOptionName,
|
||||
QuestionOptionDO::getOptionText, QuestionOptionDO::getIsCorrect,
|
||||
QuestionOptionDO::getSortOrder)
|
||||
.eq(QuestionOptionDO::getQuestionId, questionId)
|
||||
.eq(QuestionOptionDO::getDeleteEnum, DeleteEnum.FALSE.getCode())
|
||||
.orderByAsc(QuestionOptionDO::getSortOrder)
|
||||
.list();
|
||||
questionVO.setQuestionOptionVOList(questionConvertor.convertOptionEListToVoList(options));
|
||||
return questionVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateQuestion(QuestionUpdateDTO dto) {
|
||||
|
|
@ -58,75 +82,84 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
|
|||
throw new IllegalArgumentException("试题不存在:id=" + dto.getId());
|
||||
}
|
||||
|
||||
// 组装选项(按A-D,空白列不生成选项)
|
||||
Map<String, String> optionTexts = new LinkedHashMap<>();
|
||||
optionTexts.put("A", dto.getOptionA().trim());
|
||||
optionTexts.put("B", dto.getOptionB().trim());
|
||||
putIfHasText(optionTexts, "C", dto.getOptionC());
|
||||
putIfHasText(optionTexts, "D", dto.getOptionD());
|
||||
// 更新试题基本信息(MapStruct转换,答案归一化后回填)
|
||||
QuestionDO question = questionConvertor.convertDtoToE(dto);
|
||||
question.setAnswer(dto.getQuestionOptionList().stream()
|
||||
.filter(option -> Boolean.TRUE.equals(option.getIsCorrect()))
|
||||
.map(QuestionOptionUpdateDTO::getOptionKey)
|
||||
.collect(Collectors.joining(",")));
|
||||
question.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
// 校验答案必须能找到对应的选项
|
||||
List<String> answers = dto.getAnswer().stream()
|
||||
.map(String::trim)
|
||||
.map(String::toUpperCase)
|
||||
.distinct()
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
List<String> notMatched = answers.stream()
|
||||
.filter(answer -> !optionTexts.containsKey(answer))
|
||||
.collect(Collectors.toList());
|
||||
if (!notMatched.isEmpty()) {
|
||||
throw new IllegalArgumentException("答案[" + String.join(",", notMatched) + "]找不到对应的选项");
|
||||
}
|
||||
boolean single = QuestionTypeEnum.SINGLE.getCode().equals(exist.getQuestionType());
|
||||
if (single && answers.size() != 1) {
|
||||
throw new IllegalArgumentException("单选题答案只能有一个");
|
||||
}
|
||||
|
||||
// 更新试题(选项全量覆盖重建,id预置雪花id)
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
QuestionDO question = new QuestionDO();
|
||||
question.setId(dto.getId());
|
||||
question.setTitle(dto.getTitle().trim());
|
||||
question.setAnswer(String.join(",", answers));
|
||||
question.setScore(dto.getScore());
|
||||
question.setAnalysis(StringUtils.hasText(dto.getAnalysis()) ? dto.getAnalysis().trim() : null);
|
||||
question.setCoursewareManagementId(dto.getCoursewareManagementId());
|
||||
question.setUpdateTime(now);
|
||||
|
||||
List<QuestionOptionDO> options = new ArrayList<>();
|
||||
int sortOrder = 1;
|
||||
for (Map.Entry<String, String> entry : optionTexts.entrySet()) {
|
||||
QuestionOptionDO option = new QuestionOptionDO();
|
||||
option.setId(IdUtil.getSnowflakeNextId());
|
||||
option.setQuestionId(dto.getId());
|
||||
option.setOptionKey(entry.getKey());
|
||||
option.setOptionName(entry.getKey());
|
||||
option.setOptionText(entry.getValue());
|
||||
option.setIsCorrect(answers.contains(entry.getKey()));
|
||||
option.setSortOrder(sortOrder++);
|
||||
option.setCreateTime(now);
|
||||
option.setUpdateTime(now);
|
||||
options.add(option);
|
||||
}
|
||||
// 同步选项:id在库的更新、无id的新增、多余的逻辑删除
|
||||
List<QuestionOptionDO> options = syncOptions(dto.getId(), dto.getQuestionOptionList());
|
||||
question.setAnswerQuestionOptionIds(options.stream()
|
||||
.filter(option -> Boolean.TRUE.equals(option.getIsCorrect()))
|
||||
.map(option -> String.valueOf(option.getId()))
|
||||
.collect(Collectors.joining(",")));
|
||||
|
||||
InsertFieldDefaults.applyForUpdate(question);
|
||||
boolean updated = this.updateById(question);
|
||||
if (!updated) {
|
||||
throw new IllegalArgumentException("编辑习题失败:id=" + dto.getId());
|
||||
}
|
||||
questionOptionService.remove(Wrappers.<QuestionOptionDO>lambdaQuery()
|
||||
.eq(QuestionOptionDO::getQuestionId, dto.getId()));
|
||||
questionOptionService.saveBatch(options);
|
||||
}
|
||||
|
||||
private void putIfHasText(Map<String, String> optionTexts, String key, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
optionTexts.put(key, value.trim());
|
||||
|
||||
/**
|
||||
* 同步试题选项:id在库中的更新、无id(或id不在库中)的新增、库中多余的选项逻辑删除
|
||||
*
|
||||
* @param questionId 所属试题id
|
||||
* @param optionDTOs 选项编辑内容
|
||||
* @return 处理后的选项实体列表(更新与新增的合集,含最终id)
|
||||
*/
|
||||
private List<QuestionOptionDO> syncOptions(Long questionId, List<QuestionOptionUpdateDTO> optionDTOs) {
|
||||
// 查询该试题现存未删除的选项id
|
||||
Set<Long> existingIds = questionOptionService.lambdaQuery()
|
||||
.select(QuestionOptionDO::getId)
|
||||
.eq(QuestionOptionDO::getQuestionId, questionId)
|
||||
.eq(QuestionOptionDO::getDeleteEnum, DeleteEnum.FALSE.getCode())
|
||||
.list()
|
||||
.stream()
|
||||
.map(QuestionOptionDO::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 组装选项:归一化标识、标记正确答案,按id是否在库分为更新与新增两组
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
List<QuestionOptionDO> options = questionConvertor.convertOptionDtoListToEList(optionDTOs);
|
||||
List<QuestionOptionDO> toUpdate = new ArrayList<>();
|
||||
List<QuestionOptionDO> toInsert = new ArrayList<>();
|
||||
int sortOrder = 1;
|
||||
for (QuestionOptionDO option : options) {
|
||||
String key = option.getOptionKey().trim().toUpperCase();
|
||||
option.setQuestionId(questionId);
|
||||
option.setOptionKey(key);
|
||||
option.setOptionName(key);
|
||||
option.setSortOrder(sortOrder++);
|
||||
if (option.getId() != null && existingIds.contains(option.getId())) {
|
||||
toUpdate.add(option);
|
||||
InsertFieldDefaults.applyForUpdate(option);
|
||||
} else {
|
||||
option.setId(IdUtil.getSnowflakeNextId());
|
||||
InsertFieldDefaults.applyIgnoreOrgId(option);
|
||||
toInsert.add(option);
|
||||
}
|
||||
}
|
||||
|
||||
// 库中多余(未出现在本次请求中)的选项逻辑删除
|
||||
Set<Long> keepIds = toUpdate.stream().map(QuestionOptionDO::getId).collect(Collectors.toSet());
|
||||
List<Long> toDeleteIds = existingIds.stream()
|
||||
.filter(id -> !keepIds.contains(id))
|
||||
.collect(Collectors.toList());
|
||||
if (!toDeleteIds.isEmpty()) {
|
||||
questionOptionService.lambdaUpdate()
|
||||
.set(QuestionOptionDO::getDeleteEnum, DeleteEnum.TRUE.getCode())
|
||||
.set(QuestionOptionDO::getUpdateTime, now)
|
||||
.in(QuestionOptionDO::getId, toDeleteIds)
|
||||
.update();
|
||||
}
|
||||
questionOptionService.updateBatchById(toUpdate);
|
||||
questionOptionService.saveBatch(toInsert);
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -200,7 +233,11 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
|
|||
}
|
||||
|
||||
/**
|
||||
* 保存试题选项
|
||||
* 保存试题选项(重置id并绑定试题id、统一填充时间后批量插入)
|
||||
*
|
||||
* @param questionId 所属试题id
|
||||
* @param options 选项列表,为空则不处理
|
||||
* @param now 统一的创建/修改时间
|
||||
*/
|
||||
private void saveOptions(Long questionId, List<QuestionOptionDO> options, LocalDateTime now) {
|
||||
if (CollectionUtils.isEmpty(options)) {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
JOIN question q ON pqr.question_id = q.id
|
||||
LEFT JOIN courseware_management cm ON cm.id = q.courseware_management_id
|
||||
WHERE pqr.paper_id = #{query.paperId}
|
||||
AND pqr.delete_enum = 0
|
||||
AND q.delete_enum = 0
|
||||
AND pqr.delete_enum = 'false'
|
||||
AND q.delete_enum = 'false'
|
||||
<if test="query.title != null and query.title != ''">
|
||||
AND q.title LIKE CONCAT('%', #{query.title}, '%')
|
||||
</if>
|
||||
|
|
@ -20,4 +20,15 @@
|
|||
</if>
|
||||
ORDER BY pqr.question_num ASC
|
||||
</select>
|
||||
|
||||
<select id="findQuestion" resultType="org.qinan.cedu.model.vo.QuestionVO">
|
||||
SELECT q.id, q.create_time, q.title, q.question_type, q.answer
|
||||
, q.answer_question_option_ids, q.children_question_ids, q.score
|
||||
, q.analysis, q.tag_type, q.courseware_management_id
|
||||
, cm.courseware_name AS coursewareManagementName
|
||||
FROM question q
|
||||
JOIN courseware_management cm ON q.courseware_management_id = cm.id
|
||||
WHERE q.id = #{questionId}
|
||||
AND q.delete_enum = 'false'
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
|
|||
Loading…
Reference in New Issue