dev-tmp1
luotaiqian 2026-08-19 16:02:19 +08:00
parent 7a1545f7b5
commit 85e96a07b6
5 changed files with 84 additions and 97 deletions

View File

@ -1,6 +1,7 @@
package org.qinan.cedu.convertor; package org.qinan.cedu.convertor;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.qinan.cedu.model.dto.PaperQuestionSaveDTO;
import org.qinan.cedu.model.dto.QuestionOptionUpdateDTO; import org.qinan.cedu.model.dto.QuestionOptionUpdateDTO;
import org.qinan.cedu.model.dto.QuestionUpdateDTO; import org.qinan.cedu.model.dto.QuestionUpdateDTO;
import org.qinan.cedu.model.entity.QuestionDO; import org.qinan.cedu.model.entity.QuestionDO;
@ -25,6 +26,14 @@ public interface QuestionConvertor {
*/ */
QuestionDO convertDtoToE(QuestionUpdateDTO dto); QuestionDO convertDtoToE(QuestionUpdateDTO dto);
/**
*
*
* @param dto
* @return
*/
QuestionDO convertPaperDtoToE(PaperQuestionSaveDTO dto);
/** /**
* DTO * DTO
* *

View File

@ -4,18 +4,14 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.DecimalMax; import javax.validation.Valid;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
/** /**
* * QuestionUpdateDTOid
*/ */
@Data @Data
@ApiModel(value = "PaperQuestionSaveDTO", description = "新增试卷试题请求") @ApiModel(value = "PaperQuestionSaveDTO", description = "新增试卷试题请求")
@ -25,45 +21,34 @@ public class PaperQuestionSaveDTO {
@NotNull(message = "试卷id不能为空") @NotNull(message = "试卷id不能为空")
private Long paperId; private Long paperId;
@ApiModelProperty(value = "类型single单选/multiple多选", required = true) @ApiModelProperty(value = "题类型single单选/multiple多选", required = true)
@NotBlank(message = "类型不能为空") @NotBlank(message = "题类型不能为空")
@Pattern(regexp = "single|multiple", message = "类型只能填写single单选或multiple多选") @Pattern(regexp = "single|multiple", message = "题类型只能填写single单选或multiple多选")
private String questionType; private String questionType;
@ApiModelProperty(value = "题", required = true) @ApiModelProperty(value = "题目内容", required = true)
@NotBlank(message = "题干不能为空") @NotBlank(message = "题干不能为空")
private String title; private String title;
@ApiModelProperty(value = "分值", required = true) @ApiModelProperty(value = "分值", required = true)
@NotNull(message = "分值不能为空") @NotNull(message = "分值不能为空")
@DecimalMin(value = "0.01", message = "分值必须大于0") @DecimalMin(value = "0.1", message = "分值必须大于0")
@DecimalMax(value = "999.99", message = "分值不能超过999.99") @DecimalMax(value = "999.9", message = "分值不能超过999.9")
@Digits(integer = 4, fraction = 1)
private BigDecimal score; 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 = "答案解析") @ApiModelProperty(value = "答案解析")
private String analysis; 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;
} }

View File

@ -12,9 +12,9 @@ import javax.validation.constraints.NotNull;
public interface PaperQuestionRelService extends IService<PaperQuestionRelDO> { public interface PaperQuestionRelService extends IService<PaperQuestionRelDO> {
/** /**
* + * +isCorrect
* *
* @param dto id+ * @param dto id+++
*/ */
void savePaperQuestion(PaperQuestionSaveDTO dto); void savePaperQuestion(PaperQuestionSaveDTO dto);

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db; import com.baomidou.mybatisplus.extension.toolkit.Db;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.qinan.cedu.convertor.QuestionConvertor;
import org.qinan.cedu.enums.DeleteEnum; import org.qinan.cedu.enums.DeleteEnum;
import org.qinan.cedu.enums.QuestionTypeEnum; import org.qinan.cedu.enums.QuestionTypeEnum;
import org.qinan.cedu.mapper.PaperMapper; 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.QuestionMapper;
import org.qinan.cedu.mapper.QuestionOptionMapper; import org.qinan.cedu.mapper.QuestionOptionMapper;
import org.qinan.cedu.model.dto.PaperQuestionSaveDTO; 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.PaperDO;
import org.qinan.cedu.model.entity.PaperQuestionRelDO; import org.qinan.cedu.model.entity.PaperQuestionRelDO;
import org.qinan.cedu.model.entity.QuestionDO; 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.qinan.cedu.service.PaperQuestionRelService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -40,78 +38,52 @@ public class PaperQuestionRelServiceImpl extends ServiceImpl<PaperQuestionRelMap
private final PaperMapper paperMapper; private final PaperMapper paperMapper;
private final QuestionMapper questionMapper; private final QuestionMapper questionMapper;
private final QuestionOptionMapper questionOptionMapper; private final QuestionOptionMapper questionOptionMapper;
private final QuestionConvertor questionConvertor;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void savePaperQuestion(PaperQuestionSaveDTO dto) { public void savePaperQuestion(PaperQuestionSaveDTO dto) {
// 校验试卷存在 // 校验试卷存在且未删除
boolean exists = paperMapper.exists(new LambdaQueryWrapper<PaperDO>() boolean exists = paperMapper.exists(new LambdaQueryWrapper<PaperDO>()
.eq(PaperDO::getId, dto.getPaperId()) .eq(PaperDO::getId, dto.getPaperId())
.eq(PaperDO::getDeleteEnum, DeleteEnum.FALSE.getCode())); .eq(PaperDO::getDeleteEnum, DeleteEnum.FALSE.getCode()));
if (exists) { if (!exists) {
throw new IllegalArgumentException("试卷不存在id=" + dto.getPaperId()); throw new IllegalArgumentException("试卷不存在id=" + dto.getPaperId());
} }
// 组装选项按A-D列空白列不生成选项 // 校验正确答案:至少一个;单选题只能有一个
Map<String, String> optionTexts = new LinkedHashMap<>(); List<QuestionOptionUpdateDTO> optionDTOs = dto.getQuestionOptionList();
optionTexts.put("A", dto.getOptionA().trim()); long correctCount = optionDTOs.stream()
optionTexts.put("B", dto.getOptionB().trim()); .filter(option -> Boolean.TRUE.equals(option.getIsCorrect()))
putIfHasText(optionTexts, "C", dto.getOptionC()); .count();
putIfHasText(optionTexts, "D", dto.getOptionD()); if (correctCount == 0) {
throw new IllegalArgumentException("正确答案不能为空");
// 校验答案必须能找到对应的选项
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(dto.getQuestionType()); boolean single = QuestionTypeEnum.SINGLE.getCode().equals(dto.getQuestionType());
if (single && answers.size() != 1) { if (single && correctCount != 1) {
throw new IllegalArgumentException("单选题答案只能有一个"); throw new IllegalArgumentException("单选题答案只能有一个");
} }
// 保存试题和选项id用雪花id // 保存试题和选项id用雪花id
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
QuestionDO question = new QuestionDO(); QuestionDO question = questionConvertor.convertPaperDtoToE(dto);
question.setId(IdUtil.getSnowflakeNextId()); question.setId(IdUtil.getSnowflakeNextId());
question.setTitle(dto.getTitle().trim()); question.setAnswer(optionDTOs.stream()
question.setQuestionType(dto.getQuestionType()); .filter(option -> Boolean.TRUE.equals(option.getIsCorrect()))
question.setAnswer(String.join(",", answers)); .map(QuestionOptionUpdateDTO::getOptionKey)
question.setScore(dto.getScore()); .map(String::trim)
question.setAnalysis(StringUtils.hasText(dto.getAnalysis()) ? dto.getAnalysis().trim() : null); .map(String::toUpperCase)
question.setCoursewareManagementId(dto.getCoursewareManagementId()); .collect(Collectors.joining(",")));
question.setCreateTime(now);
question.setUpdateTime(now);
question.setVersion(0);
List<QuestionOptionDO> options = new ArrayList<>(); List<QuestionOptionDO> options = buildOptions(question.getId(), optionDTOs, now);
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);
}
question.setAnswerQuestionOptionIds(options.stream() question.setAnswerQuestionOptionIds(options.stream()
.filter(option -> Boolean.TRUE.equals(option.getIsCorrect())) .filter(option -> Boolean.TRUE.equals(option.getIsCorrect()))
.map(option -> String.valueOf(option.getId())) .map(option -> String.valueOf(option.getId()))
.collect(Collectors.joining(","))); .collect(Collectors.joining(",")));
question.setCreateTime(now);
question.setUpdateTime(now);
question.setVersion(0);
questionMapper.insert(question); questionMapper.insert(question);
Db.saveBatch(options); Db.saveBatch(options);
@ -124,6 +96,31 @@ public class PaperQuestionRelServiceImpl extends ServiceImpl<PaperQuestionRelMap
this.save(rel); 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 @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void deletePaperQuestion(Long paperId, Long questionId) { 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());
}
}
} }

View File

@ -181,6 +181,7 @@ public class OrgPersonnelExecutor implements OrgPersonnelApi {
cmd.setJoinWorkDate(resolveJoinWorkDate(cmd.getWorkDateAge(), cmd.getJoinWorkDate())); cmd.setJoinWorkDate(resolveJoinWorkDate(cmd.getWorkDateAge(), cmd.getJoinWorkDate()));
OrgPersonnelEntity entity = orgPersonnelConvertor.toEntity(cmd); OrgPersonnelEntity entity = orgPersonnelConvertor.toEntity(cmd);
entity.setId(cmd.getId()); entity.setId(cmd.getId());
entity.setIsCertComplete(true);
OrgPersonnelEntity result = orgPersonnelDomainService.modify(entity); OrgPersonnelEntity result = orgPersonnelDomainService.modify(entity);
// 组织信息变更,清理该用户机构缓存 // 组织信息变更,清理该用户机构缓存
if (userOrgIdResolver != null) { if (userOrgIdResolver != null) {