test
parent
7a1545f7b5
commit
85e96a07b6
|
|
@ -1,6 +1,7 @@
|
|||
package org.qinan.cedu.convertor;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.qinan.cedu.model.dto.PaperQuestionSaveDTO;
|
||||
import org.qinan.cedu.model.dto.QuestionOptionUpdateDTO;
|
||||
import org.qinan.cedu.model.dto.QuestionUpdateDTO;
|
||||
import org.qinan.cedu.model.entity.QuestionDO;
|
||||
|
|
@ -25,6 +26,14 @@ public interface QuestionConvertor {
|
|||
*/
|
||||
QuestionDO convertDtoToE(QuestionUpdateDTO dto);
|
||||
|
||||
/**
|
||||
* 新增试卷试题请求转试题实体
|
||||
*
|
||||
* @param dto 新增内容
|
||||
* @return 试题实体
|
||||
*/
|
||||
QuestionDO convertPaperDtoToE(PaperQuestionSaveDTO dto);
|
||||
|
||||
/**
|
||||
* 选项编辑DTO列表转选项实体列表
|
||||
*
|
||||
|
|
|
|||
|
|
@ -4,18 +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.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 新增试卷试题(习题)请求
|
||||
* 新增试卷试题(习题)请求(字段参考 QuestionUpdateDTO,另含试卷id和试题类型)
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "PaperQuestionSaveDTO", description = "新增试卷试题请求")
|
||||
|
|
@ -25,45 +21,34 @@ public class PaperQuestionSaveDTO {
|
|||
@NotNull(message = "试卷id不能为空")
|
||||
private Long paperId;
|
||||
|
||||
@ApiModelProperty(value = "题目类型:single单选/multiple多选", required = true)
|
||||
@NotBlank(message = "题目类型不能为空")
|
||||
@Pattern(regexp = "single|multiple", message = "题目类型只能填写single(单选)或multiple(多选)")
|
||||
@ApiModelProperty(value = "试题类型:single单选/multiple多选", required = true)
|
||||
@NotBlank(message = "试题类型不能为空")
|
||||
@Pattern(regexp = "single|multiple", message = "试题类型只能填写single(单选)或multiple(多选)")
|
||||
private String questionType;
|
||||
|
||||
@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 = "试题选项信息(isCorrect标记正确答案)", required = true)
|
||||
@NotEmpty(message = "试题选项不能为空")
|
||||
@Valid
|
||||
private List<QuestionOptionUpdateDTO> questionOptionList;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import javax.validation.constraints.NotNull;
|
|||
public interface PaperQuestionRelService extends IService<PaperQuestionRelDO> {
|
||||
|
||||
/**
|
||||
* 新增试卷试题(习题):保存试题+选项,并绑定试卷
|
||||
* 新增试卷试题(习题):保存试题+选项(isCorrect标记正确答案),并绑定试卷
|
||||
*
|
||||
* @param dto 试卷id+题目内容
|
||||
* @param dto 试卷id+试题类型+题目内容+选项列表
|
||||
*/
|
||||
void savePaperQuestion(PaperQuestionSaveDTO dto);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|||
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
||||
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.PaperMapper;
|
||||
|
|
@ -13,6 +14,7 @@ import org.qinan.cedu.mapper.PaperQuestionRelMapper;
|
|||
import org.qinan.cedu.mapper.QuestionMapper;
|
||||
import org.qinan.cedu.mapper.QuestionOptionMapper;
|
||||
import org.qinan.cedu.model.dto.PaperQuestionSaveDTO;
|
||||
import org.qinan.cedu.model.dto.QuestionOptionUpdateDTO;
|
||||
import org.qinan.cedu.model.entity.PaperDO;
|
||||
import org.qinan.cedu.model.entity.PaperQuestionRelDO;
|
||||
import org.qinan.cedu.model.entity.QuestionDO;
|
||||
|
|
@ -20,13 +22,9 @@ import org.qinan.cedu.model.entity.QuestionOptionDO;
|
|||
import org.qinan.cedu.service.PaperQuestionRelService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
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.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -40,78 +38,52 @@ public class PaperQuestionRelServiceImpl extends ServiceImpl<PaperQuestionRelMap
|
|||
private final PaperMapper paperMapper;
|
||||
private final QuestionMapper questionMapper;
|
||||
private final QuestionOptionMapper questionOptionMapper;
|
||||
private final QuestionConvertor questionConvertor;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void savePaperQuestion(PaperQuestionSaveDTO dto) {
|
||||
// 校验试卷存在
|
||||
// 校验试卷存在且未删除
|
||||
boolean exists = paperMapper.exists(new LambdaQueryWrapper<PaperDO>()
|
||||
.eq(PaperDO::getId, dto.getPaperId())
|
||||
.eq(PaperDO::getDeleteEnum, DeleteEnum.FALSE.getCode()));
|
||||
if (exists) {
|
||||
if (!exists) {
|
||||
throw new IllegalArgumentException("试卷不存在:id=" + dto.getPaperId());
|
||||
}
|
||||
|
||||
// 组装选项(按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());
|
||||
|
||||
// 校验答案必须能找到对应的选项
|
||||
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) + "]找不到对应的选项");
|
||||
// 校验正确答案:至少一个;单选题只能有一个
|
||||
List<QuestionOptionUpdateDTO> optionDTOs = dto.getQuestionOptionList();
|
||||
long correctCount = optionDTOs.stream()
|
||||
.filter(option -> Boolean.TRUE.equals(option.getIsCorrect()))
|
||||
.count();
|
||||
if (correctCount == 0) {
|
||||
throw new IllegalArgumentException("正确答案不能为空");
|
||||
}
|
||||
boolean single = QuestionTypeEnum.SINGLE.getCode().equals(dto.getQuestionType());
|
||||
if (single && answers.size() != 1) {
|
||||
if (single && correctCount != 1) {
|
||||
throw new IllegalArgumentException("单选题答案只能有一个");
|
||||
}
|
||||
|
||||
// 保存试题和选项(id用雪花id)
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
QuestionDO question = new QuestionDO();
|
||||
QuestionDO question = questionConvertor.convertPaperDtoToE(dto);
|
||||
question.setId(IdUtil.getSnowflakeNextId());
|
||||
question.setTitle(dto.getTitle().trim());
|
||||
question.setQuestionType(dto.getQuestionType());
|
||||
question.setAnswer(String.join(",", answers));
|
||||
question.setScore(dto.getScore());
|
||||
question.setAnalysis(StringUtils.hasText(dto.getAnalysis()) ? dto.getAnalysis().trim() : null);
|
||||
question.setCoursewareManagementId(dto.getCoursewareManagementId());
|
||||
question.setCreateTime(now);
|
||||
question.setUpdateTime(now);
|
||||
question.setVersion(0);
|
||||
question.setAnswer(optionDTOs.stream()
|
||||
.filter(option -> Boolean.TRUE.equals(option.getIsCorrect()))
|
||||
.map(QuestionOptionUpdateDTO::getOptionKey)
|
||||
.map(String::trim)
|
||||
.map(String::toUpperCase)
|
||||
.collect(Collectors.joining(",")));
|
||||
|
||||
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(question.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);
|
||||
option.setVersion(0);
|
||||
options.add(option);
|
||||
}
|
||||
List<QuestionOptionDO> options = buildOptions(question.getId(), optionDTOs, now);
|
||||
question.setAnswerQuestionOptionIds(options.stream()
|
||||
.filter(option -> Boolean.TRUE.equals(option.getIsCorrect()))
|
||||
.map(option -> String.valueOf(option.getId()))
|
||||
.collect(Collectors.joining(",")));
|
||||
|
||||
question.setCreateTime(now);
|
||||
question.setUpdateTime(now);
|
||||
question.setVersion(0);
|
||||
questionMapper.insert(question);
|
||||
Db.saveBatch(options);
|
||||
|
||||
|
|
@ -124,6 +96,31 @@ public class PaperQuestionRelServiceImpl extends ServiceImpl<PaperQuestionRelMap
|
|||
this.save(rel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装试题选项列表(归一化选项标识、填充雪花id与排序)
|
||||
*
|
||||
* @param questionId 所属试题id
|
||||
* @param optionDTOs 选项内容
|
||||
* @param now 统一的创建/修改时间
|
||||
* @return 选项实体列表
|
||||
*/
|
||||
private List<QuestionOptionDO> buildOptions(Long questionId, List<QuestionOptionUpdateDTO> optionDTOs, LocalDateTime now) {
|
||||
List<QuestionOptionDO> options = questionConvertor.convertOptionDtoListToEList(optionDTOs);
|
||||
int sortOrder = 1;
|
||||
for (QuestionOptionDO option : options) {
|
||||
String key = option.getOptionKey().trim().toUpperCase();
|
||||
option.setId(IdUtil.getSnowflakeNextId());
|
||||
option.setQuestionId(questionId);
|
||||
option.setOptionKey(key);
|
||||
option.setOptionName(key);
|
||||
option.setSortOrder(sortOrder++);
|
||||
option.setCreateTime(now);
|
||||
option.setUpdateTime(now);
|
||||
option.setVersion(0);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deletePaperQuestion(Long paperId, Long questionId) {
|
||||
|
|
@ -143,9 +140,4 @@ public class PaperQuestionRelServiceImpl extends ServiceImpl<PaperQuestionRelMap
|
|||
}
|
||||
}
|
||||
|
||||
private void putIfHasText(Map<String, String> optionTexts, String key, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
optionTexts.put(key, value.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@ public class OrgPersonnelExecutor implements OrgPersonnelApi {
|
|||
cmd.setJoinWorkDate(resolveJoinWorkDate(cmd.getWorkDateAge(), cmd.getJoinWorkDate()));
|
||||
OrgPersonnelEntity entity = orgPersonnelConvertor.toEntity(cmd);
|
||||
entity.setId(cmd.getId());
|
||||
entity.setIsCertComplete(true);
|
||||
OrgPersonnelEntity result = orgPersonnelDomainService.modify(entity);
|
||||
// 组织信息变更,清理该用户机构缓存
|
||||
if (userOrgIdResolver != null) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue