archive file
parent
1fb5839a02
commit
5004254c45
|
|
@ -73,7 +73,7 @@ public class PaperController {
|
||||||
|
|
||||||
@ApiOperation("新建试卷-导入试题")
|
@ApiOperation("新建试卷-导入试题")
|
||||||
@PostMapping("/import")
|
@PostMapping("/import")
|
||||||
public SingleResponse<PaperImportVO> importQuestions(@Validated PaperImportDTO paperImportDTO) {
|
public SingleResponse<QuestionImportVO> importQuestions(@Validated PaperImportDTO paperImportDTO) {
|
||||||
return SingleResponse.success(paperService.importQuestions(paperImportDTO));
|
return SingleResponse.success(paperService.importQuestions(paperImportDTO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import io.swagger.annotations.ApiOperation;
|
||||||
import io.swagger.annotations.ApiParam;
|
import io.swagger.annotations.ApiParam;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.qinan.cedu.model.dto.*;
|
import org.qinan.cedu.model.dto.*;
|
||||||
|
import org.qinan.cedu.model.vo.QuestionImportVO;
|
||||||
import org.qinan.cedu.model.vo.QuestionCoursewareVO;
|
import org.qinan.cedu.model.vo.QuestionCoursewareVO;
|
||||||
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;
|
||||||
|
|
@ -94,4 +95,10 @@ public class QuestionController {
|
||||||
return PageResponse.of(page.getRecords(), page.getTotal());
|
return PageResponse.of(page.getRecords(), page.getTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation("导入试题")
|
||||||
|
@PostMapping("/import")
|
||||||
|
public SingleResponse<QuestionImportVO> importQuestions(@Validated QuestionImportDTO questionImportDTO) {
|
||||||
|
return SingleResponse.success(questionService.importQuestions(questionImportDTO));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.qinan.cedu.model.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新建试卷-导入试题 请求对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value = "PaperImportDTO", description = "新建试卷-导入试题请求")
|
||||||
|
public class QuestionImportDTO {
|
||||||
|
|
||||||
|
@ApiModelProperty("关联课件管理id")
|
||||||
|
private Long coursewareManagementId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "试题Excel文件(第一个sheet单选题,第二个sheet多选题,第三个sheet判断题)", required = true)
|
||||||
|
@NotNull(message = "请上传试题Excel文件")
|
||||||
|
private MultipartFile file;
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ import lombok.NoArgsConstructor;
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class PaperImportErrorVO {
|
public class QuestionImportErrorVO {
|
||||||
|
|
||||||
@ApiModelProperty(value = "Excel行号(从1开始的物理行号)")
|
@ApiModelProperty(value = "Excel行号(从1开始的物理行号)")
|
||||||
private Integer rowIndex;
|
private Integer rowIndex;
|
||||||
|
|
@ -22,7 +22,7 @@ public class PaperImportErrorVO {
|
||||||
@ApiModelProperty(value = "异常原因")
|
@ApiModelProperty(value = "异常原因")
|
||||||
private String errorMessage;
|
private String errorMessage;
|
||||||
|
|
||||||
public PaperImportErrorVO(Integer rowIndex, String sheetName, String errorMessage) {
|
public QuestionImportErrorVO(Integer rowIndex, String sheetName, String errorMessage) {
|
||||||
this.rowIndex = rowIndex;
|
this.rowIndex = rowIndex;
|
||||||
this.sheetName = sheetName;
|
this.sheetName = sheetName;
|
||||||
this.errorMessage = errorMessage;
|
this.errorMessage = errorMessage;
|
||||||
|
|
@ -14,15 +14,15 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class PaperImportVO {
|
public class QuestionImportVO {
|
||||||
|
|
||||||
@ApiModelProperty(value = "成功导入条数")
|
@ApiModelProperty(value = "成功导入条数")
|
||||||
private Integer successCount;
|
private Integer successCount;
|
||||||
|
|
||||||
@ApiModelProperty(value = "异常明细")
|
@ApiModelProperty(value = "异常明细")
|
||||||
private List<PaperImportErrorVO> errors = new ArrayList<>();
|
private List<QuestionImportErrorVO> errors = new ArrayList<>();
|
||||||
|
|
||||||
public PaperImportVO(Integer successCount, List<PaperImportErrorVO> errors) {
|
public QuestionImportVO(Integer successCount, List<QuestionImportErrorVO> errors) {
|
||||||
this.successCount = successCount;
|
this.successCount = successCount;
|
||||||
this.errors = errors;
|
this.errors = errors;
|
||||||
}
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ import org.qinan.cedu.model.dto.PaperPageQueryDTO;
|
||||||
import org.qinan.cedu.model.dto.PaperUpdateDTO;
|
import org.qinan.cedu.model.dto.PaperUpdateDTO;
|
||||||
import org.qinan.cedu.model.entity.PaperDO;
|
import org.qinan.cedu.model.entity.PaperDO;
|
||||||
import org.qinan.cedu.model.vo.PaperBasicInfoVO;
|
import org.qinan.cedu.model.vo.PaperBasicInfoVO;
|
||||||
import org.qinan.cedu.model.vo.PaperImportVO;
|
import org.qinan.cedu.model.vo.QuestionImportVO;
|
||||||
import org.qinan.cedu.model.vo.PaperExamInfoVO;
|
import org.qinan.cedu.model.vo.PaperExamInfoVO;
|
||||||
import org.qinan.cedu.model.vo.PaperPageVO;
|
import org.qinan.cedu.model.vo.PaperPageVO;
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ public interface PaperService extends IService<PaperDO> {
|
||||||
* @param importDTO 试卷信息+试题Excel文件
|
* @param importDTO 试卷信息+试题Excel文件
|
||||||
* @return 导入结果(成功条数+异常明细)
|
* @return 导入结果(成功条数+异常明细)
|
||||||
*/
|
*/
|
||||||
PaperImportVO importQuestions(PaperImportDTO importDTO);
|
QuestionImportVO importQuestions(PaperImportDTO importDTO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* importQuestionsTemplate
|
* importQuestionsTemplate
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import org.qinan.cedu.model.dto.*;
|
import org.qinan.cedu.model.dto.*;
|
||||||
import org.qinan.cedu.model.entity.QuestionDO;
|
import org.qinan.cedu.model.entity.QuestionDO;
|
||||||
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
||||||
import org.qinan.cedu.model.vo.PaperQuestionVO;
|
import org.qinan.cedu.model.vo.*;
|
||||||
import org.qinan.cedu.model.vo.QuestionCoursewareVO;
|
|
||||||
import org.qinan.cedu.model.vo.QuestionTypeCountVO;
|
|
||||||
import org.qinan.cedu.model.vo.QuestionVO;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -122,4 +119,12 @@ public interface QuestionService extends IService<QuestionDO> {
|
||||||
* @param dto dto
|
* @param dto dto
|
||||||
*/
|
*/
|
||||||
void addQuestion(QuestionAddDTO dto);
|
void addQuestion(QuestionAddDTO dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入试题
|
||||||
|
*
|
||||||
|
* @param questionImportDTO questionImportDTO
|
||||||
|
* @return QuestionImportVO
|
||||||
|
*/
|
||||||
|
QuestionImportVO importQuestions(QuestionImportDTO questionImportDTO);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
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 com.baomidou.mybatisplus.extension.toolkit.Db;
|
||||||
import com.lowagie.text.Document;
|
import com.lowagie.text.*;
|
||||||
import com.lowagie.text.DocumentException;
|
|
||||||
import com.lowagie.text.Element;
|
|
||||||
import com.lowagie.text.Font;
|
|
||||||
import com.lowagie.text.PageSize;
|
|
||||||
import com.lowagie.text.Paragraph;
|
|
||||||
import com.lowagie.text.pdf.BaseFont;
|
import com.lowagie.text.pdf.BaseFont;
|
||||||
import com.lowagie.text.pdf.PdfWriter;
|
import com.lowagie.text.pdf.PdfWriter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
@ -28,23 +23,12 @@ 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.PaperConfigRuleDTO;
|
import org.qinan.cedu.model.dto.*;
|
||||||
import org.qinan.cedu.model.dto.PaperCopyDTO;
|
|
||||||
import org.qinan.cedu.model.dto.PaperCoursewareQuestionRuleDTO;
|
|
||||||
import org.qinan.cedu.model.dto.PaperImportDTO;
|
|
||||||
import org.qinan.cedu.model.dto.PaperOrgPageQueryDTO;
|
|
||||||
import org.qinan.cedu.model.dto.PaperPageQueryDTO;
|
|
||||||
import org.qinan.cedu.model.dto.PaperUpdateDTO;
|
|
||||||
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;
|
||||||
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
||||||
import org.qinan.cedu.model.vo.PaperBasicInfoVO;
|
import org.qinan.cedu.model.vo.*;
|
||||||
import org.qinan.cedu.model.vo.PaperExamInfoVO;
|
|
||||||
import org.qinan.cedu.model.vo.PaperImportVO;
|
|
||||||
import org.qinan.cedu.model.vo.PaperPageVO;
|
|
||||||
import org.qinan.cedu.model.vo.QuestionOptionVO;
|
|
||||||
import org.qinan.cedu.model.vo.QuestionVO;
|
|
||||||
import org.qinan.cedu.service.PaperService;
|
import org.qinan.cedu.service.PaperService;
|
||||||
import org.qinan.cedu.service.support.QuestionExcelImporter;
|
import org.qinan.cedu.service.support.QuestionExcelImporter;
|
||||||
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
|
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
|
||||||
|
|
@ -276,12 +260,13 @@ public class PaperServiceImpl extends ServiceImpl<PaperMapper, PaperDO> implemen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public PaperImportVO importQuestions(PaperImportDTO importDTO) {
|
public QuestionImportVO importQuestions(PaperImportDTO importDTO) {
|
||||||
QuestionExcelImporter.ParseResult parseResult = questionExcelImporter.parse(importDTO.getFile());
|
QuestionExcelImporter.ParseResult parseResult = questionExcelImporter.parse(
|
||||||
|
importDTO.getFile(), null);
|
||||||
|
|
||||||
if (!CollectionUtils.isEmpty(parseResult.getErrors())) {
|
if (!CollectionUtils.isEmpty(parseResult.getErrors())) {
|
||||||
// 没有任何校验通过的试题,不创建试卷,直接返回异常明细
|
// 没有任何校验通过的试题,不创建试卷,直接返回异常明细
|
||||||
return new PaperImportVO(0, parseResult.getErrors());
|
return new QuestionImportVO(0, parseResult.getErrors());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建试卷
|
// 创建试卷
|
||||||
|
|
@ -311,7 +296,7 @@ public class PaperServiceImpl extends ServiceImpl<PaperMapper, PaperDO> implemen
|
||||||
rels.add(rel);
|
rels.add(rel);
|
||||||
}
|
}
|
||||||
Db.saveBatch(rels);
|
Db.saveBatch(rels);
|
||||||
return new PaperImportVO(questions.size(), parseResult.getErrors());
|
return new QuestionImportVO(questions.size(), parseResult.getErrors());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,9 @@ import org.qinan.cedu.mapper.QuestionOptionMapper;
|
||||||
import org.qinan.cedu.model.dto.*;
|
import org.qinan.cedu.model.dto.*;
|
||||||
import org.qinan.cedu.model.entity.QuestionDO;
|
import org.qinan.cedu.model.entity.QuestionDO;
|
||||||
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
||||||
import org.qinan.cedu.model.vo.PaperQuestionVO;
|
import org.qinan.cedu.model.vo.*;
|
||||||
import org.qinan.cedu.model.vo.QuestionCoursewareVO;
|
|
||||||
import org.qinan.cedu.model.vo.QuestionTypeCountVO;
|
|
||||||
import org.qinan.cedu.model.vo.QuestionVO;
|
|
||||||
import org.qinan.cedu.service.QuestionService;
|
import org.qinan.cedu.service.QuestionService;
|
||||||
|
import org.qinan.cedu.service.support.QuestionExcelImporter;
|
||||||
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
|
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
@ -43,6 +41,7 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
|
||||||
|
|
||||||
private final QuestionOptionMapper questionOptionMapper;
|
private final QuestionOptionMapper questionOptionMapper;
|
||||||
private final QuestionConvertor questionConvertor;
|
private final QuestionConvertor questionConvertor;
|
||||||
|
private final QuestionExcelImporter questionExcelImporter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<PaperQuestionVO> pagePaperQuestions(PaperQuestionPageQueryDTO query) {
|
public IPage<PaperQuestionVO> pagePaperQuestions(PaperQuestionPageQueryDTO query) {
|
||||||
|
|
@ -274,6 +273,23 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
|
||||||
Db.saveBatch(options);
|
Db.saveBatch(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public QuestionImportVO importQuestions(QuestionImportDTO importDTO) {
|
||||||
|
QuestionExcelImporter.ParseResult parseResult = questionExcelImporter.parse(
|
||||||
|
importDTO.getFile(), importDTO.getCoursewareManagementId());
|
||||||
|
|
||||||
|
if (!CollectionUtils.isEmpty(parseResult.getErrors())) {
|
||||||
|
// 没有任何校验通过的试题,不创建试卷,直接返回异常明细
|
||||||
|
return new QuestionImportVO(0, parseResult.getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存试题和选项(id已预置雪花id)
|
||||||
|
Db.saveBatch(parseResult.getQuestions());
|
||||||
|
Db.saveBatch(parseResult.getOptions());
|
||||||
|
return new QuestionImportVO(parseResult.getQuestions().size(), parseResult.getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存试题选项(重置id并绑定试题id、统一填充时间后批量插入)
|
* 保存试题选项(重置id并绑定试题id、统一填充时间后批量插入)
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package org.qinan.cedu.service.support;
|
package org.qinan.cedu.service.support;
|
||||||
|
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.alibaba.excel.EasyExcel;
|
import com.alibaba.excel.EasyExcel;
|
||||||
import com.alibaba.excel.ExcelReader;
|
import com.alibaba.excel.ExcelReader;
|
||||||
import com.alibaba.excel.read.metadata.ReadSheet;
|
import com.alibaba.excel.read.metadata.ReadSheet;
|
||||||
|
|
@ -14,7 +15,7 @@ import org.qinan.cedu.mapper.CoursewareManagementMapper;
|
||||||
import org.qinan.cedu.model.entity.CoursewareManagementDO;
|
import org.qinan.cedu.model.entity.CoursewareManagementDO;
|
||||||
import org.qinan.cedu.model.entity.QuestionDO;
|
import org.qinan.cedu.model.entity.QuestionDO;
|
||||||
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
import org.qinan.cedu.model.entity.QuestionOptionDO;
|
||||||
import org.qinan.cedu.model.vo.PaperImportErrorVO;
|
import org.qinan.cedu.model.vo.QuestionImportErrorVO;
|
||||||
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
|
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
@ -71,54 +72,58 @@ public class QuestionExcelImporter {
|
||||||
private static final String COMMA_DELIMITER = ",";
|
private static final String COMMA_DELIMITER = ",";
|
||||||
private static final String VIOLATION_DELIMITER = ";";
|
private static final String VIOLATION_DELIMITER = ";";
|
||||||
|
|
||||||
|
public static final String DEFAULT_IMPORT_COURSEWARE_NAME = "默认使用导入试题关联的课件_id";
|
||||||
|
|
||||||
private final CoursewareManagementMapper coursewareManagementMapper;
|
private final CoursewareManagementMapper coursewareManagementMapper;
|
||||||
private final Validator validator;
|
private final Validator validator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析上传的试题Excel,返回校验通过的试题(含选项,已预置雪花id)和异常明细
|
* 解析上传的试题Excel,返回校验通过的试题(含选项,已预置雪花id)和异常明细
|
||||||
*
|
*
|
||||||
* @param file 上传的试题Excel文件
|
* @param file 上传的试题Excel文件
|
||||||
|
* @param coursewareManagementId coursewareManagementId
|
||||||
* @return 解析结果(试题、选项、异常明细)
|
* @return 解析结果(试题、选项、异常明细)
|
||||||
*/
|
*/
|
||||||
public ParseResult parse(MultipartFile file) {
|
public ParseResult parse(MultipartFile file, Long coursewareManagementId) {
|
||||||
if (file == null || file.isEmpty()) {
|
if (file == null || file.isEmpty()) {
|
||||||
throw new IllegalArgumentException(UPLOAD_EMPTY_MESSAGE);
|
throw new IllegalArgumentException(UPLOAD_EMPTY_MESSAGE);
|
||||||
}
|
}
|
||||||
byte[] bytes = readBytes(file);
|
byte[] bytes = readBytes(file);
|
||||||
Map<Integer, String> sheetNames = readSheetNames(bytes);
|
Map<Integer, String> sheetNames = readSheetNames(bytes);
|
||||||
|
|
||||||
List<PaperImportErrorVO> errors = new ArrayList<>();
|
List<QuestionImportErrorVO> errors = new ArrayList<>();
|
||||||
List<ChoiceQuestionImportRow> singleRows = new ArrayList<>();
|
List<ChoiceQuestionImportRow> singleRows = new ArrayList<>();
|
||||||
List<ChoiceQuestionImportRow> multipleRows = new ArrayList<>();
|
List<ChoiceQuestionImportRow> multipleRows = new ArrayList<>();
|
||||||
List<JudgeQuestionImportRow> judgeRows = new ArrayList<>();
|
List<JudgeQuestionImportRow> judgeRows = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
singleRows = readSheet(bytes, SINGLE_SHEET_NO, ChoiceQuestionImportRow.class);
|
singleRows = readSheet(bytes, SINGLE_SHEET_NO, ChoiceQuestionImportRow.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errors.add(new PaperImportErrorVO(null, sheetName(sheetNames, SINGLE_SHEET_NO)
|
errors.add(new QuestionImportErrorVO(null, sheetName(sheetNames, SINGLE_SHEET_NO)
|
||||||
, READ_SINGLE_SHEET_FAIL_PREFIX + e.getMessage()));
|
, READ_SINGLE_SHEET_FAIL_PREFIX + e.getMessage()));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
multipleRows = readSheet(bytes, MULTIPLE_SHEET_NO, ChoiceQuestionImportRow.class);
|
multipleRows = readSheet(bytes, MULTIPLE_SHEET_NO, ChoiceQuestionImportRow.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errors.add(new PaperImportErrorVO(null, sheetName(sheetNames, MULTIPLE_SHEET_NO)
|
errors.add(new QuestionImportErrorVO(null, sheetName(sheetNames, MULTIPLE_SHEET_NO)
|
||||||
, READ_MULTIPLE_SHEET_FAIL_PREFIX + e.getMessage()));
|
, READ_MULTIPLE_SHEET_FAIL_PREFIX + e.getMessage()));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
judgeRows = readSheet(bytes, JUDGE_SHEET_NO, JudgeQuestionImportRow.class);
|
judgeRows = readSheet(bytes, JUDGE_SHEET_NO, JudgeQuestionImportRow.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errors.add(new PaperImportErrorVO(null, sheetName(sheetNames, JUDGE_SHEET_NO)
|
errors.add(new QuestionImportErrorVO(null, sheetName(sheetNames, JUDGE_SHEET_NO)
|
||||||
, READ_JUDGE_SHEET_FAIL_PREFIX + e.getMessage()));
|
, READ_JUDGE_SHEET_FAIL_PREFIX + e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Long> coursewareManagementIdByName = loadCoursewareIdByName(singleRows, multipleRows, judgeRows);
|
Map<String, Long> coursewareManagementIdByName = loadCoursewareIdByName(
|
||||||
|
singleRows, multipleRows, judgeRows, coursewareManagementId);
|
||||||
|
|
||||||
ParseResult parseResult = new ParseResult(errors);
|
ParseResult parseResult = new ParseResult(errors);
|
||||||
convertChoiceRows(singleRows, sheetName(sheetNames, SINGLE_SHEET_NO)
|
convertChoiceRows(singleRows, sheetName(sheetNames, SINGLE_SHEET_NO)
|
||||||
, QuestionTypeEnum.SINGLE, coursewareManagementIdByName, parseResult);
|
, QuestionTypeEnum.SINGLE, coursewareManagementIdByName, parseResult, coursewareManagementId);
|
||||||
convertChoiceRows(multipleRows, sheetName(sheetNames, MULTIPLE_SHEET_NO)
|
convertChoiceRows(multipleRows, sheetName(sheetNames, MULTIPLE_SHEET_NO)
|
||||||
, QuestionTypeEnum.MULTIPLE, coursewareManagementIdByName, parseResult);
|
, QuestionTypeEnum.MULTIPLE, coursewareManagementIdByName, parseResult, coursewareManagementId);
|
||||||
convertJudgeRows(judgeRows, sheetName(sheetNames, JUDGE_SHEET_NO)
|
convertJudgeRows(judgeRows, sheetName(sheetNames, JUDGE_SHEET_NO)
|
||||||
, coursewareManagementIdByName, parseResult);
|
, coursewareManagementIdByName, parseResult, coursewareManagementId);
|
||||||
return parseResult;
|
return parseResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,20 +190,23 @@ public class QuestionExcelImporter {
|
||||||
/**
|
/**
|
||||||
* 批量查询关联课件名称对应的id(名称 -> id)
|
* 批量查询关联课件名称对应的id(名称 -> id)
|
||||||
*
|
*
|
||||||
* @param singleRows 单选题行数据
|
* @param singleRows 单选题行数据
|
||||||
* @param multipleRows 多选题行数据
|
* @param multipleRows 多选题行数据
|
||||||
* @param judgeRows 判断题行数据
|
* @param judgeRows 判断题行数据
|
||||||
|
* @param coursewareManagementId coursewareManagementId
|
||||||
* @return 课件名称到课件id的映射
|
* @return 课件名称到课件id的映射
|
||||||
*/
|
*/
|
||||||
private Map<String, Long> loadCoursewareIdByName(List<ChoiceQuestionImportRow> singleRows,
|
private Map<String, Long> loadCoursewareIdByName(List<ChoiceQuestionImportRow> singleRows
|
||||||
List<ChoiceQuestionImportRow> multipleRows,
|
, List<ChoiceQuestionImportRow> multipleRows, List<JudgeQuestionImportRow> judgeRows, Long coursewareManagementId) {
|
||||||
List<JudgeQuestionImportRow> judgeRows) {
|
|
||||||
Set<String> names = new HashSet<>();
|
Set<String> names = new HashSet<>();
|
||||||
singleRows.forEach(row -> addCoursewareName(names, row.getCoursewareName()));
|
singleRows.forEach(row -> addCoursewareName(names, row.getCoursewareName()));
|
||||||
multipleRows.forEach(row -> addCoursewareName(names, row.getCoursewareName()));
|
multipleRows.forEach(row -> addCoursewareName(names, row.getCoursewareName()));
|
||||||
judgeRows.forEach(row -> addCoursewareName(names, row.getCoursewareName()));
|
judgeRows.forEach(row -> addCoursewareName(names, row.getCoursewareName()));
|
||||||
if (names.isEmpty()) {
|
if (names.isEmpty()) {
|
||||||
return Collections.emptyMap();
|
if (coursewareManagementId == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
return Collections.singletonMap(DEFAULT_IMPORT_COURSEWARE_NAME, coursewareManagementId);
|
||||||
}
|
}
|
||||||
List<CoursewareManagementDO> coursewares = coursewareManagementMapper.selectList(
|
List<CoursewareManagementDO> coursewares = coursewareManagementMapper.selectList(
|
||||||
new LambdaQueryWrapper<CoursewareManagementDO>()
|
new LambdaQueryWrapper<CoursewareManagementDO>()
|
||||||
|
|
@ -209,6 +217,9 @@ public class QuestionExcelImporter {
|
||||||
for (CoursewareManagementDO courseware : coursewares) {
|
for (CoursewareManagementDO courseware : coursewares) {
|
||||||
idByName.putIfAbsent(courseware.getCoursewareName(), courseware.getId());
|
idByName.putIfAbsent(courseware.getCoursewareName(), courseware.getId());
|
||||||
}
|
}
|
||||||
|
if (coursewareManagementId != null) {
|
||||||
|
idByName.putIfAbsent(DEFAULT_IMPORT_COURSEWARE_NAME, coursewareManagementId);
|
||||||
|
}
|
||||||
return idByName;
|
return idByName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,10 +243,11 @@ public class QuestionExcelImporter {
|
||||||
* @param questionType 题目类型(单选/多选)
|
* @param questionType 题目类型(单选/多选)
|
||||||
* @param coursewareManagementIdByName 课件名称到课件id的映射
|
* @param coursewareManagementIdByName 课件名称到课件id的映射
|
||||||
* @param parseResult 解析结果(写入试题、选项和异常明细)
|
* @param parseResult 解析结果(写入试题、选项和异常明细)
|
||||||
|
* @param formCoursewareManagementId formCoursewareManagementId
|
||||||
*/
|
*/
|
||||||
private void convertChoiceRows(List<ChoiceQuestionImportRow> rows, String sheetName, QuestionTypeEnum questionType,
|
private void convertChoiceRows(List<ChoiceQuestionImportRow> rows, String sheetName, QuestionTypeEnum questionType,
|
||||||
Map<String, Long> coursewareManagementIdByName, ParseResult parseResult) {
|
Map<String, Long> coursewareManagementIdByName, ParseResult parseResult, Long formCoursewareManagementId) {
|
||||||
List<PaperImportErrorVO> errors = parseResult.getErrors();
|
List<QuestionImportErrorVO> errors = parseResult.getErrors();
|
||||||
boolean single = questionType == QuestionTypeEnum.SINGLE;
|
boolean single = questionType == QuestionTypeEnum.SINGLE;
|
||||||
for (int i = 0; i < rows.size(); i++) {
|
for (int i = 0; i < rows.size(); i++) {
|
||||||
ChoiceQuestionImportRow row = rows.get(i);
|
ChoiceQuestionImportRow row = rows.get(i);
|
||||||
|
|
@ -244,11 +256,15 @@ public class QuestionExcelImporter {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
row.setRowIndex(rowIndex);
|
row.setRowIndex(rowIndex);
|
||||||
|
String coursewareName = StrUtil.trim(row.getCoursewareName());
|
||||||
|
if (!StringUtils.hasText(coursewareName) && formCoursewareManagementId != null) {
|
||||||
|
row.setCoursewareName(DEFAULT_IMPORT_COURSEWARE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
// JSR-303 校验(必填、数据库长度限制),不通过则跳过该行
|
// JSR-303 校验(必填、数据库长度限制),不通过则跳过该行
|
||||||
Set<ConstraintViolation<ChoiceQuestionImportRow>> violations = validator.validate(row);
|
Set<ConstraintViolation<ChoiceQuestionImportRow>> violations = validator.validate(row);
|
||||||
if (!violations.isEmpty()) {
|
if (!violations.isEmpty()) {
|
||||||
errors.add(new PaperImportErrorVO(rowIndex, sheetName, joinViolationMessages(violations)));
|
errors.add(new QuestionImportErrorVO(rowIndex, sheetName, joinViolationMessages(violations)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,14 +278,14 @@ public class QuestionExcelImporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (options.size() < 2) {
|
if (options.size() < 2) {
|
||||||
errors.add(new PaperImportErrorVO(rowIndex, sheetName, OPTION_AT_LEAST_TWO_MESSAGE));
|
errors.add(new QuestionImportErrorVO(rowIndex, sheetName, OPTION_AT_LEAST_TWO_MESSAGE));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验答案:必须有答案且答案必须能找到对应的选项
|
// 校验答案:必须有答案且答案必须能找到对应的选项
|
||||||
String answerRaw = row.getAnswer().trim().toUpperCase();
|
String answerRaw = row.getAnswer().trim().toUpperCase();
|
||||||
if (!answerRaw.chars().allMatch(c -> c >= 'A' && c <= 'F')) {
|
if (!answerRaw.chars().allMatch(c -> c >= 'A' && c <= 'F')) {
|
||||||
errors.add(new PaperImportErrorVO(rowIndex, sheetName, CHOICE_ANSWER_LETTER_MESSAGE));
|
errors.add(new QuestionImportErrorVO(rowIndex, sheetName, CHOICE_ANSWER_LETTER_MESSAGE));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
List<String> answerLetters = answerRaw.chars()
|
List<String> answerLetters = answerRaw.chars()
|
||||||
|
|
@ -278,23 +294,22 @@ public class QuestionExcelImporter {
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (single && answerLetters.size() != 1) {
|
if (single && answerLetters.size() != 1) {
|
||||||
errors.add(new PaperImportErrorVO(rowIndex, sheetName, SINGLE_ANSWER_LETTER_MESSAGE));
|
errors.add(new QuestionImportErrorVO(rowIndex, sheetName, SINGLE_ANSWER_LETTER_MESSAGE));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
List<String> notMatched = answerLetters.stream()
|
List<String> notMatched = answerLetters.stream()
|
||||||
.filter(letter -> !options.containsKey(letter))
|
.filter(letter -> !options.containsKey(letter))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (!notMatched.isEmpty()) {
|
if (!notMatched.isEmpty()) {
|
||||||
errors.add(new PaperImportErrorVO(rowIndex, sheetName,
|
errors.add(new QuestionImportErrorVO(rowIndex, sheetName,
|
||||||
String.format(ANSWER_NOT_MATCH_OPTION_TEMPLATE, String.join("", notMatched))));
|
String.format(ANSWER_NOT_MATCH_OPTION_TEMPLATE, String.join("", notMatched))));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验关联课件名称,查询到后回填coursewareManagementId
|
// 校验关联课件名称,查询到后回填coursewareManagementId
|
||||||
String coursewareName = row.getCoursewareName().trim();
|
|
||||||
Long coursewareManagementId = coursewareManagementIdByName.get(coursewareName);
|
Long coursewareManagementId = coursewareManagementIdByName.get(coursewareName);
|
||||||
if (coursewareManagementId == null) {
|
if (coursewareManagementId == null) {
|
||||||
errors.add(new PaperImportErrorVO(rowIndex, sheetName, String.format(COURSEWARE_NOT_EXIST_TEMPLATE, coursewareName)));
|
errors.add(new QuestionImportErrorVO(rowIndex, sheetName, String.format(COURSEWARE_NOT_EXIST_TEMPLATE, coursewareName)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,10 +354,11 @@ public class QuestionExcelImporter {
|
||||||
* @param sheetName sheet名称(用于异常提示定位)
|
* @param sheetName sheet名称(用于异常提示定位)
|
||||||
* @param coursewareManagementIdByName 课件名称到课件id的映射
|
* @param coursewareManagementIdByName 课件名称到课件id的映射
|
||||||
* @param parseResult 解析结果(写入试题、选项和异常明细)
|
* @param parseResult 解析结果(写入试题、选项和异常明细)
|
||||||
|
* @param formCoursewareManagementId formCoursewareManagementId
|
||||||
*/
|
*/
|
||||||
private void convertJudgeRows(List<JudgeQuestionImportRow> rows, String sheetName,
|
private void convertJudgeRows(List<JudgeQuestionImportRow> rows, String sheetName,
|
||||||
Map<String, Long> coursewareManagementIdByName, ParseResult parseResult) {
|
Map<String, Long> coursewareManagementIdByName, ParseResult parseResult, Long formCoursewareManagementId) {
|
||||||
List<PaperImportErrorVO> errors = parseResult.getErrors();
|
List<QuestionImportErrorVO> errors = parseResult.getErrors();
|
||||||
for (int i = 0; i < rows.size(); i++) {
|
for (int i = 0; i < rows.size(); i++) {
|
||||||
JudgeQuestionImportRow row = rows.get(i);
|
JudgeQuestionImportRow row = rows.get(i);
|
||||||
int rowIndex = i + HEAD_ROW_NUMBER + 1;
|
int rowIndex = i + HEAD_ROW_NUMBER + 1;
|
||||||
|
|
@ -350,23 +366,26 @@ public class QuestionExcelImporter {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
row.setRowIndex(rowIndex);
|
row.setRowIndex(rowIndex);
|
||||||
|
String coursewareName = StrUtil.trim(row.getCoursewareName());
|
||||||
|
if (!StringUtils.hasText(coursewareName) && formCoursewareManagementId != null) {
|
||||||
|
row.setCoursewareName(DEFAULT_IMPORT_COURSEWARE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
Set<ConstraintViolation<JudgeQuestionImportRow>> violations = validator.validate(row);
|
Set<ConstraintViolation<JudgeQuestionImportRow>> violations = validator.validate(row);
|
||||||
if (!violations.isEmpty()) {
|
if (!violations.isEmpty()) {
|
||||||
errors.add(new PaperImportErrorVO(rowIndex, sheetName, joinViolationMessages(violations)));
|
errors.add(new QuestionImportErrorVO(rowIndex, sheetName, joinViolationMessages(violations)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String answerRaw = row.getAnswer().trim().toUpperCase();
|
String answerRaw = row.getAnswer().trim().toUpperCase();
|
||||||
if (!JUDGE_ANSWER_TRUE.equals(answerRaw) && !JUDGE_ANSWER_FALSE.equals(answerRaw)) {
|
if (!JUDGE_ANSWER_TRUE.equals(answerRaw) && !JUDGE_ANSWER_FALSE.equals(answerRaw)) {
|
||||||
errors.add(new PaperImportErrorVO(rowIndex, sheetName, JUDGE_ANSWER_LETTER_MESSAGE));
|
errors.add(new QuestionImportErrorVO(rowIndex, sheetName, JUDGE_ANSWER_LETTER_MESSAGE));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String coursewareName = row.getCoursewareName().trim();
|
|
||||||
Long coursewareManagementId = coursewareManagementIdByName.get(coursewareName);
|
Long coursewareManagementId = coursewareManagementIdByName.get(coursewareName);
|
||||||
if (coursewareManagementId == null) {
|
if (coursewareManagementId == null) {
|
||||||
errors.add(new PaperImportErrorVO(rowIndex, sheetName, String.format(COURSEWARE_NOT_EXIST_TEMPLATE, coursewareName)));
|
errors.add(new QuestionImportErrorVO(rowIndex, sheetName, String.format(COURSEWARE_NOT_EXIST_TEMPLATE, coursewareName)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -517,7 +536,7 @@ public class QuestionExcelImporter {
|
||||||
/**
|
/**
|
||||||
* 异常明细
|
* 异常明细
|
||||||
*/
|
*/
|
||||||
private final List<PaperImportErrorVO> errors;
|
private final List<QuestionImportErrorVO> errors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全参构造
|
* 全参构造
|
||||||
|
|
@ -526,7 +545,7 @@ public class QuestionExcelImporter {
|
||||||
* @param options 校验通过的试题选项列表
|
* @param options 校验通过的试题选项列表
|
||||||
* @param errors 异常明细列表
|
* @param errors 异常明细列表
|
||||||
*/
|
*/
|
||||||
public ParseResult(List<QuestionDO> questions, List<QuestionOptionDO> options, List<PaperImportErrorVO> errors) {
|
public ParseResult(List<QuestionDO> questions, List<QuestionOptionDO> options, List<QuestionImportErrorVO> errors) {
|
||||||
this.questions = questions;
|
this.questions = questions;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.questionNumByQuestionId = new HashMap<>();
|
this.questionNumByQuestionId = new HashMap<>();
|
||||||
|
|
@ -548,7 +567,7 @@ public class QuestionExcelImporter {
|
||||||
*
|
*
|
||||||
* @param errors 异常明细列表
|
* @param errors 异常明细列表
|
||||||
*/
|
*/
|
||||||
public ParseResult(List<PaperImportErrorVO> errors) {
|
public ParseResult(List<QuestionImportErrorVO> errors) {
|
||||||
this.questions = new ArrayList<>(64);
|
this.questions = new ArrayList<>(64);
|
||||||
this.options = new ArrayList<>(256);
|
this.options = new ArrayList<>(256);
|
||||||
this.questionNumByQuestionId = new HashMap<>();
|
this.questionNumByQuestionId = new HashMap<>();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue