PaperBasicInfo

dev-tmp1
luotaiqian 2026-08-21 10:16:15 +08:00
parent 7775928024
commit 1e8386f7c4
7 changed files with 188 additions and 29 deletions

View File

@ -4,7 +4,9 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.qinan.cedu.model.dto.PaperQuestionDeleteDTO; import org.qinan.cedu.model.dto.PaperQuestionDeleteDTO;
import org.qinan.cedu.model.dto.PaperQuestionRelUpdateDTO;
import org.qinan.cedu.model.dto.PaperQuestionSaveDTO; import org.qinan.cedu.model.dto.PaperQuestionSaveDTO;
import org.qinan.cedu.model.dto.QuestionUpdateDTO;
import org.qinan.cedu.service.PaperQuestionRelService; import org.qinan.cedu.service.PaperQuestionRelService;
import org.qinan.safetyeval.client.dto.SingleResponse; import org.qinan.safetyeval.client.dto.SingleResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -38,4 +40,11 @@ public class PaperQuestionRelController {
paperQuestionRelService.deletePaperQuestion(dto.getPaperId(), dto.getQuestionId()); paperQuestionRelService.deletePaperQuestion(dto.getPaperId(), dto.getQuestionId());
return SingleResponse.success(); return SingleResponse.success();
} }
@ApiOperation("编辑试卷习题")
@PostMapping("/modify")
public SingleResponse<Void> modify(@Validated @RequestBody PaperQuestionRelUpdateDTO dto) {
paperQuestionRelService.modify(dto);
return SingleResponse.success();
}
} }

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.PaperQuestionRelUpdateDTO;
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.dto.QuestionOptionUpdateDTO;
import org.qinan.cedu.model.dto.QuestionUpdateDTO; import org.qinan.cedu.model.dto.QuestionUpdateDTO;
@ -45,4 +46,6 @@ public interface QuestionConvertor {
QuestionOptionVO convertOptionEToVo(QuestionOptionDO entity); QuestionOptionVO convertOptionEToVo(QuestionOptionDO entity);
List<QuestionOptionVO> convertOptionEListToVoList(List<QuestionOptionDO> list); List<QuestionOptionVO> convertOptionEListToVoList(List<QuestionOptionDO> list);
QuestionDO convertPaperQuestionRelUpdateDTOToE(PaperQuestionRelUpdateDTO dto);
} }

View File

@ -0,0 +1,53 @@
package org.qinan.cedu.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.math.BigDecimal;
import java.util.List;
/**
* QuestionVO
*/
@Data
@ApiModel(value = "QuestionUpdateDTO", description = "编辑习题请求")
public class PaperQuestionRelUpdateDTO {
@ApiModelProperty(value = "试卷id", required = true)
@NotNull(message = "试卷id不能为空")
private Long paperId;
@ApiModelProperty(value = "试题id", required = true)
@NotNull(message = "试题id不能为空")
private Long id;
@ApiModelProperty(value = "题目内容", required = true)
@NotBlank(message = "题干不能为空")
private String title;
@ApiModelProperty(value = "分值", required = true)
@NotNull(message = "分值不能为空")
@DecimalMin(value = "0.1", message = "分值必须大于0")
@DecimalMax(value = "999.9", message = "分值不能超过999.9")
@Digits(integer = 4, fraction = 1)
private BigDecimal score;
@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;
}

View File

@ -1,6 +1,7 @@
package org.qinan.cedu.service; package org.qinan.cedu.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import org.qinan.cedu.model.dto.PaperQuestionRelUpdateDTO;
import org.qinan.cedu.model.dto.PaperQuestionSaveDTO; import org.qinan.cedu.model.dto.PaperQuestionSaveDTO;
import org.qinan.cedu.model.entity.PaperQuestionRelDO; import org.qinan.cedu.model.entity.PaperQuestionRelDO;
@ -25,4 +26,10 @@ public interface PaperQuestionRelService extends IService<PaperQuestionRelDO> {
* @param questionId id * @param questionId id
*/ */
void deletePaperQuestion(@NotNull Long paperId, @NotNull Long questionId); void deletePaperQuestion(@NotNull Long paperId, @NotNull Long questionId);
/**
* modify
* @param dto dto
*/
void modify(PaperQuestionRelUpdateDTO dto);
} }

View File

@ -2,6 +2,7 @@ package org.qinan.cedu.service.impl;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 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;
@ -13,6 +14,7 @@ import org.qinan.cedu.mapper.PaperMapper;
import org.qinan.cedu.mapper.PaperQuestionRelMapper; 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.PaperQuestionRelUpdateDTO;
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.dto.QuestionOptionUpdateDTO;
import org.qinan.cedu.model.entity.PaperDO; import org.qinan.cedu.model.entity.PaperDO;
@ -25,7 +27,9 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -130,4 +134,95 @@ public class PaperQuestionRelServiceImpl extends ServiceImpl<PaperQuestionRelMap
} }
} }
@Override
public void modify(PaperQuestionRelUpdateDTO dto) {
// 校验试题存在且未删除
QuestionDO exist = questionMapper.selectOne(new LambdaQueryWrapper<QuestionDO>().eq(QuestionDO::getId, dto.getId())
.eq(QuestionDO::getDeleteEnum, DeleteEnum.FALSE.getCode()));
if (exist == null) {
throw new IllegalArgumentException("试题不存在");
}
this.update(new LambdaUpdateWrapper<PaperQuestionRelDO>()
.set(PaperQuestionRelDO::getScore, dto.getScore())
.eq(PaperQuestionRelDO::getPaperId, dto.getPaperId())
.eq(PaperQuestionRelDO::getQuestionId, dto.getId()));
// 更新试题基本信息MapStruct转换答案归一化后回填
QuestionDO question = questionConvertor.convertPaperQuestionRelUpdateDTOToE(dto);
question.setAnswer(dto.getQuestionOptionList().stream()
.filter(option -> Boolean.TRUE.equals(option.getIsCorrect()))
.map(QuestionOptionUpdateDTO::getOptionKey)
.collect(Collectors.joining(",")));
question.setUpdateTime(LocalDateTime.now());
// 同步选项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 = questionMapper.updateById(question) != 0;
if (!updated) {
throw new IllegalArgumentException("编辑习题失败");
}
}
/**
* ididid
*
* @param questionId id
* @param optionDTOs
* @return id
*/
private List<QuestionOptionDO> syncOptions(Long questionId, List<QuestionOptionUpdateDTO> optionDTOs) {
// 查询该试题现存未删除的选项id
Set<Long> existingIds = questionOptionMapper.selectList(new LambdaQueryWrapper<QuestionOptionDO>()
.select(QuestionOptionDO::getId)
.eq(QuestionOptionDO::getQuestionId, questionId)
.eq(QuestionOptionDO::getDeleteEnum, DeleteEnum.FALSE.getCode()))
.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()) {
questionOptionMapper.update(new LambdaUpdateWrapper<QuestionOptionDO>()
.set(QuestionOptionDO::getDeleteEnum, DeleteEnum.TRUE.getCode())
.set(QuestionOptionDO::getUpdateTime, now)
.in(QuestionOptionDO::getId, toDeleteIds));
}
Db.updateBatchById(toUpdate);
Db.saveBatch(toInsert);
return options;
}
} }

View File

@ -160,6 +160,8 @@ public class PaperServiceImpl extends ServiceImpl<PaperMapper, PaperDO> implemen
target.setStatus(source.getStatus()); target.setStatus(source.getStatus());
target.setPaperType(source.getPaperType()); target.setPaperType(source.getPaperType());
target.setConfigRule(source.getConfigRule()); target.setConfigRule(source.getConfigRule());
target.setExamTime(source.getExamTime());
InsertFieldDefaults.applyIgnoreOrgId(target);
this.savePaper(target); this.savePaper(target);
// 复制试题+选项+试卷试题关系id全部换新雪花id // 复制试题+选项+试卷试题关系id全部换新雪花id

View File

@ -1,15 +1,18 @@
package org.qinan.cedu.service.impl; package org.qinan.cedu.service.impl;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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.convertor.QuestionConvertor;
import org.qinan.cedu.enums.DeleteEnum; import org.qinan.cedu.enums.DeleteEnum;
import org.qinan.cedu.mapper.QuestionMapper; import org.qinan.cedu.mapper.QuestionMapper;
import org.qinan.cedu.mapper.QuestionOptionMapper;
import org.qinan.cedu.model.dto.PaperQuestionPageQueryDTO; import org.qinan.cedu.model.dto.PaperQuestionPageQueryDTO;
import org.qinan.cedu.model.dto.QuestionOptionUpdateDTO; import org.qinan.cedu.model.dto.QuestionOptionUpdateDTO;
import org.qinan.cedu.model.dto.QuestionTypeCountQueryDTO; import org.qinan.cedu.model.dto.QuestionTypeCountQueryDTO;
@ -19,10 +22,8 @@ import org.qinan.cedu.model.entity.QuestionOptionDO;
import org.qinan.cedu.model.vo.PaperQuestionVO; import org.qinan.cedu.model.vo.PaperQuestionVO;
import org.qinan.cedu.model.vo.QuestionTypeCountVO; import org.qinan.cedu.model.vo.QuestionTypeCountVO;
import org.qinan.cedu.model.vo.QuestionVO; import org.qinan.cedu.model.vo.QuestionVO;
import org.qinan.cedu.service.QuestionOptionService;
import org.qinan.cedu.service.QuestionService; import org.qinan.cedu.service.QuestionService;
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults; import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
import org.springframework.beans.factory.annotation.Autowired;
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.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -42,9 +43,7 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor @RequiredArgsConstructor
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO> implements QuestionService { public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO> implements QuestionService {
@Autowired private final QuestionOptionMapper questionOptionMapper;
private QuestionOptionService questionOptionService;
private final QuestionConvertor questionConvertor; private final QuestionConvertor questionConvertor;
@Override @Override
@ -59,15 +58,14 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
throw new IllegalArgumentException("试题不存在"); throw new IllegalArgumentException("试题不存在");
} }
// 查询选项信息仅查询VO需要的字段 // 查询选项信息仅查询VO需要的字段
List<QuestionOptionDO> options = questionOptionService.lambdaQuery() List<QuestionOptionDO> options = questionOptionMapper.selectList(new LambdaQueryWrapper<QuestionOptionDO>()
.select(QuestionOptionDO::getId, QuestionOptionDO::getQuestionId, .select(QuestionOptionDO::getId, QuestionOptionDO::getQuestionId,
QuestionOptionDO::getOptionKey, QuestionOptionDO::getOptionName, QuestionOptionDO::getOptionKey, QuestionOptionDO::getOptionName,
QuestionOptionDO::getOptionText, QuestionOptionDO::getIsCorrect, QuestionOptionDO::getOptionText, QuestionOptionDO::getIsCorrect,
QuestionOptionDO::getSortOrder) QuestionOptionDO::getSortOrder)
.eq(QuestionOptionDO::getQuestionId, questionId) .eq(QuestionOptionDO::getQuestionId, questionId)
.eq(QuestionOptionDO::getDeleteEnum, DeleteEnum.FALSE.getCode()) .eq(QuestionOptionDO::getDeleteEnum, DeleteEnum.FALSE.getCode())
.orderByAsc(QuestionOptionDO::getSortOrder) .orderByAsc(QuestionOptionDO::getSortOrder));
.list();
questionVO.setQuestionOptionVOList(questionConvertor.convertOptionEListToVoList(options)); questionVO.setQuestionOptionVOList(questionConvertor.convertOptionEListToVoList(options));
return questionVO; return questionVO;
} }
@ -121,11 +119,10 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
*/ */
private List<QuestionOptionDO> syncOptions(Long questionId, List<QuestionOptionUpdateDTO> optionDTOs) { private List<QuestionOptionDO> syncOptions(Long questionId, List<QuestionOptionUpdateDTO> optionDTOs) {
// 查询该试题现存未删除的选项id // 查询该试题现存未删除的选项id
Set<Long> existingIds = questionOptionService.lambdaQuery() Set<Long> existingIds = questionOptionMapper.selectList(new LambdaQueryWrapper<QuestionOptionDO>()
.select(QuestionOptionDO::getId) .select(QuestionOptionDO::getId)
.eq(QuestionOptionDO::getQuestionId, questionId) .eq(QuestionOptionDO::getQuestionId, questionId)
.eq(QuestionOptionDO::getDeleteEnum, DeleteEnum.FALSE.getCode()) .eq(QuestionOptionDO::getDeleteEnum, DeleteEnum.FALSE.getCode()))
.list()
.stream() .stream()
.map(QuestionOptionDO::getId) .map(QuestionOptionDO::getId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@ -158,14 +155,13 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
.filter(id -> !keepIds.contains(id)) .filter(id -> !keepIds.contains(id))
.collect(Collectors.toList()); .collect(Collectors.toList());
if (!toDeleteIds.isEmpty()) { if (!toDeleteIds.isEmpty()) {
questionOptionService.lambdaUpdate() questionOptionMapper.update(new LambdaUpdateWrapper<QuestionOptionDO>()
.set(QuestionOptionDO::getDeleteEnum, DeleteEnum.TRUE.getCode()) .set(QuestionOptionDO::getDeleteEnum, DeleteEnum.TRUE.getCode())
.set(QuestionOptionDO::getUpdateTime, now) .set(QuestionOptionDO::getUpdateTime, now)
.in(QuestionOptionDO::getId, toDeleteIds) .in(QuestionOptionDO::getId, toDeleteIds));
.update();
} }
questionOptionService.updateBatchById(toUpdate); Db.updateBatchById(toUpdate);
questionOptionService.saveBatch(toInsert); Db.saveBatch(toInsert);
return options; return options;
} }
@ -185,11 +181,6 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
if (questionDO == null) { if (questionDO == null) {
return null; return null;
} }
List<QuestionOptionDO> options = questionOptionService.lambdaQuery()
.eq(QuestionOptionDO::getQuestionId, id)
.eq(QuestionOptionDO::getDeleteEnum, DeleteEnum.FALSE.getCode())
.orderByAsc(QuestionOptionDO::getSortOrder)
.list();
return questionDO; return questionDO;
} }
@ -213,7 +204,7 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
questionDO.setUpdateTime(now); questionDO.setUpdateTime(now);
boolean updated = this.updateById(questionDO); boolean updated = this.updateById(questionDO);
if (updated) { if (updated) {
questionOptionService.remove(Wrappers.<QuestionOptionDO>lambdaQuery() questionOptionMapper.delete(new LambdaUpdateWrapper<QuestionOptionDO>()
.eq(QuestionOptionDO::getQuestionId, questionDO.getId())); .eq(QuestionOptionDO::getQuestionId, questionDO.getId()));
saveOptions(questionDO.getId(), options, now); saveOptions(questionDO.getId(), options, now);
} }
@ -230,11 +221,10 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
.eq(QuestionDO::getDeleteEnum, DeleteEnum.FALSE.getCode()) .eq(QuestionDO::getDeleteEnum, DeleteEnum.FALSE.getCode())
.update(); .update();
if (deleted) { if (deleted) {
questionOptionService.lambdaUpdate() questionOptionMapper.update(new LambdaUpdateWrapper<QuestionOptionDO>()
.set(QuestionOptionDO::getDeleteEnum, DeleteEnum.TRUE.getCode()) .set(QuestionOptionDO::getDeleteEnum, DeleteEnum.TRUE.getCode())
.set(QuestionOptionDO::getUpdateTime, LocalDateTime.now()) .set(QuestionOptionDO::getUpdateTime, LocalDateTime.now())
.eq(QuestionOptionDO::getQuestionId, id) .eq(QuestionOptionDO::getQuestionId, id));
.update();
} }
return deleted; return deleted;
} }
@ -256,6 +246,6 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
option.setCreateTime(now); option.setCreateTime(now);
option.setUpdateTime(now); option.setUpdateTime(now);
}); });
questionOptionService.saveBatch(options); Db.saveBatch(options);
} }
} }