archive file

dev-tmp1
luotaiqian 2026-08-27 10:58:11 +08:00
parent 1fb5839a02
commit 5004254c45
10 changed files with 130 additions and 75 deletions

View File

@ -73,7 +73,7 @@ public class PaperController {
@ApiOperation("新建试卷-导入试题")
@PostMapping("/import")
public SingleResponse<PaperImportVO> importQuestions(@Validated PaperImportDTO paperImportDTO) {
public SingleResponse<QuestionImportVO> importQuestions(@Validated PaperImportDTO paperImportDTO) {
return SingleResponse.success(paperService.importQuestions(paperImportDTO));
}

View File

@ -6,6 +6,7 @@ import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
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.QuestionTypeCountVO;
import org.qinan.cedu.model.vo.QuestionVO;
@ -94,4 +95,10 @@ public class QuestionController {
return PageResponse.of(page.getRecords(), page.getTotal());
}
@ApiOperation("导入试题")
@PostMapping("/import")
public SingleResponse<QuestionImportVO> importQuestions(@Validated QuestionImportDTO questionImportDTO) {
return SingleResponse.success(questionService.importQuestions(questionImportDTO));
}
}

View File

@ -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;
}

View File

@ -11,7 +11,7 @@ import lombok.NoArgsConstructor;
*/
@Data
@NoArgsConstructor
public class PaperImportErrorVO {
public class QuestionImportErrorVO {
@ApiModelProperty(value = "Excel行号从1开始的物理行号")
private Integer rowIndex;
@ -22,7 +22,7 @@ public class PaperImportErrorVO {
@ApiModelProperty(value = "异常原因")
private String errorMessage;
public PaperImportErrorVO(Integer rowIndex, String sheetName, String errorMessage) {
public QuestionImportErrorVO(Integer rowIndex, String sheetName, String errorMessage) {
this.rowIndex = rowIndex;
this.sheetName = sheetName;
this.errorMessage = errorMessage;

View File

@ -14,15 +14,15 @@ import java.util.List;
*/
@Data
@NoArgsConstructor
public class PaperImportVO {
public class QuestionImportVO {
@ApiModelProperty(value = "成功导入条数")
private Integer successCount;
@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.errors = errors;
}

View File

@ -10,7 +10,7 @@ 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.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.PaperPageVO;
@ -91,7 +91,7 @@ public interface PaperService extends IService<PaperDO> {
* @param importDTO +Excel
* @return +
*/
PaperImportVO importQuestions(PaperImportDTO importDTO);
QuestionImportVO importQuestions(PaperImportDTO importDTO);
/**
* importQuestionsTemplate

View File

@ -5,10 +5,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import org.qinan.cedu.model.dto.*;
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.QuestionCoursewareVO;
import org.qinan.cedu.model.vo.QuestionTypeCountVO;
import org.qinan.cedu.model.vo.QuestionVO;
import org.qinan.cedu.model.vo.*;
import java.util.List;
@ -122,4 +119,12 @@ public interface QuestionService extends IService<QuestionDO> {
* @param dto dto
*/
void addQuestion(QuestionAddDTO dto);
/**
*
*
* @param questionImportDTO questionImportDTO
* @return QuestionImportVO
*/
QuestionImportVO importQuestions(QuestionImportDTO questionImportDTO);
}

View File

@ -8,12 +8,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.lowagie.text.Document;
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.*;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfWriter;
import lombok.RequiredArgsConstructor;
@ -28,23 +23,12 @@ import org.qinan.cedu.mapper.PaperMapper;
import org.qinan.cedu.mapper.PaperQuestionRelMapper;
import org.qinan.cedu.mapper.QuestionMapper;
import org.qinan.cedu.mapper.QuestionOptionMapper;
import org.qinan.cedu.model.dto.PaperConfigRuleDTO;
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.dto.*;
import org.qinan.cedu.model.entity.PaperDO;
import org.qinan.cedu.model.entity.PaperQuestionRelDO;
import org.qinan.cedu.model.entity.QuestionDO;
import org.qinan.cedu.model.entity.QuestionOptionDO;
import org.qinan.cedu.model.vo.PaperBasicInfoVO;
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.model.vo.*;
import org.qinan.cedu.service.PaperService;
import org.qinan.cedu.service.support.QuestionExcelImporter;
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
@ -276,12 +260,13 @@ public class PaperServiceImpl extends ServiceImpl<PaperMapper, PaperDO> implemen
@Override
@Transactional(rollbackFor = Exception.class)
public PaperImportVO importQuestions(PaperImportDTO importDTO) {
QuestionExcelImporter.ParseResult parseResult = questionExcelImporter.parse(importDTO.getFile());
public QuestionImportVO importQuestions(PaperImportDTO importDTO) {
QuestionExcelImporter.ParseResult parseResult = questionExcelImporter.parse(
importDTO.getFile(), null);
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);
}
Db.saveBatch(rels);
return new PaperImportVO(questions.size(), parseResult.getErrors());
return new QuestionImportVO(questions.size(), parseResult.getErrors());
}
@Override

View File

@ -16,11 +16,9 @@ import org.qinan.cedu.mapper.QuestionOptionMapper;
import org.qinan.cedu.model.dto.*;
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.QuestionCoursewareVO;
import org.qinan.cedu.model.vo.QuestionTypeCountVO;
import org.qinan.cedu.model.vo.QuestionVO;
import org.qinan.cedu.model.vo.*;
import org.qinan.cedu.service.QuestionService;
import org.qinan.cedu.service.support.QuestionExcelImporter;
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -43,6 +41,7 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
private final QuestionOptionMapper questionOptionMapper;
private final QuestionConvertor questionConvertor;
private final QuestionExcelImporter questionExcelImporter;
@Override
public IPage<PaperQuestionVO> pagePaperQuestions(PaperQuestionPageQueryDTO query) {
@ -274,6 +273,23 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
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());
}
/**
* idid
*

View File

@ -1,6 +1,7 @@
package org.qinan.cedu.service.support;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
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.QuestionDO;
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.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@ -71,54 +72,58 @@ public class QuestionExcelImporter {
private static final String COMMA_DELIMITER = ",";
private static final String VIOLATION_DELIMITER = "";
public static final String DEFAULT_IMPORT_COURSEWARE_NAME = "默认使用导入试题关联的课件_id";
private final CoursewareManagementMapper coursewareManagementMapper;
private final Validator validator;
/**
* Excelid
*
* @param file Excel
* @param file Excel
* @param coursewareManagementId coursewareManagementId
* @return
*/
public ParseResult parse(MultipartFile file) {
public ParseResult parse(MultipartFile file, Long coursewareManagementId) {
if (file == null || file.isEmpty()) {
throw new IllegalArgumentException(UPLOAD_EMPTY_MESSAGE);
}
byte[] bytes = readBytes(file);
Map<Integer, String> sheetNames = readSheetNames(bytes);
List<PaperImportErrorVO> errors = new ArrayList<>();
List<QuestionImportErrorVO> errors = new ArrayList<>();
List<ChoiceQuestionImportRow> singleRows = new ArrayList<>();
List<ChoiceQuestionImportRow> multipleRows = new ArrayList<>();
List<JudgeQuestionImportRow> judgeRows = new ArrayList<>();
try {
singleRows = readSheet(bytes, SINGLE_SHEET_NO, ChoiceQuestionImportRow.class);
} 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()));
}
try {
multipleRows = readSheet(bytes, MULTIPLE_SHEET_NO, ChoiceQuestionImportRow.class);
} 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()));
}
try {
judgeRows = readSheet(bytes, JUDGE_SHEET_NO, JudgeQuestionImportRow.class);
} 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()));
}
Map<String, Long> coursewareManagementIdByName = loadCoursewareIdByName(singleRows, multipleRows, judgeRows);
Map<String, Long> coursewareManagementIdByName = loadCoursewareIdByName(
singleRows, multipleRows, judgeRows, coursewareManagementId);
ParseResult parseResult = new ParseResult(errors);
convertChoiceRows(singleRows, sheetName(sheetNames, SINGLE_SHEET_NO)
, QuestionTypeEnum.SINGLE, coursewareManagementIdByName, parseResult);
, QuestionTypeEnum.SINGLE, coursewareManagementIdByName, parseResult, coursewareManagementId);
convertChoiceRows(multipleRows, sheetName(sheetNames, MULTIPLE_SHEET_NO)
, QuestionTypeEnum.MULTIPLE, coursewareManagementIdByName, parseResult);
, QuestionTypeEnum.MULTIPLE, coursewareManagementIdByName, parseResult, coursewareManagementId);
convertJudgeRows(judgeRows, sheetName(sheetNames, JUDGE_SHEET_NO)
, coursewareManagementIdByName, parseResult);
, coursewareManagementIdByName, parseResult, coursewareManagementId);
return parseResult;
}
@ -185,20 +190,23 @@ public class QuestionExcelImporter {
/**
* id -> id
*
* @param singleRows
* @param multipleRows
* @param judgeRows
* @param singleRows
* @param multipleRows
* @param judgeRows
* @param coursewareManagementId coursewareManagementId
* @return id
*/
private Map<String, Long> loadCoursewareIdByName(List<ChoiceQuestionImportRow> singleRows,
List<ChoiceQuestionImportRow> multipleRows,
List<JudgeQuestionImportRow> judgeRows) {
private Map<String, Long> loadCoursewareIdByName(List<ChoiceQuestionImportRow> singleRows
, List<ChoiceQuestionImportRow> multipleRows, List<JudgeQuestionImportRow> judgeRows, Long coursewareManagementId) {
Set<String> names = new HashSet<>();
singleRows.forEach(row -> addCoursewareName(names, row.getCoursewareName()));
multipleRows.forEach(row -> addCoursewareName(names, row.getCoursewareName()));
judgeRows.forEach(row -> addCoursewareName(names, row.getCoursewareName()));
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(
new LambdaQueryWrapper<CoursewareManagementDO>()
@ -209,6 +217,9 @@ public class QuestionExcelImporter {
for (CoursewareManagementDO courseware : coursewares) {
idByName.putIfAbsent(courseware.getCoursewareName(), courseware.getId());
}
if (coursewareManagementId != null) {
idByName.putIfAbsent(DEFAULT_IMPORT_COURSEWARE_NAME, coursewareManagementId);
}
return idByName;
}
@ -232,10 +243,11 @@ public class QuestionExcelImporter {
* @param questionType /
* @param coursewareManagementIdByName id
* @param parseResult
* @param formCoursewareManagementId formCoursewareManagementId
*/
private void convertChoiceRows(List<ChoiceQuestionImportRow> rows, String sheetName, QuestionTypeEnum questionType,
Map<String, Long> coursewareManagementIdByName, ParseResult parseResult) {
List<PaperImportErrorVO> errors = parseResult.getErrors();
Map<String, Long> coursewareManagementIdByName, ParseResult parseResult, Long formCoursewareManagementId) {
List<QuestionImportErrorVO> errors = parseResult.getErrors();
boolean single = questionType == QuestionTypeEnum.SINGLE;
for (int i = 0; i < rows.size(); i++) {
ChoiceQuestionImportRow row = rows.get(i);
@ -244,11 +256,15 @@ public class QuestionExcelImporter {
continue;
}
row.setRowIndex(rowIndex);
String coursewareName = StrUtil.trim(row.getCoursewareName());
if (!StringUtils.hasText(coursewareName) && formCoursewareManagementId != null) {
row.setCoursewareName(DEFAULT_IMPORT_COURSEWARE_NAME);
}
// JSR-303 校验(必填、数据库长度限制),不通过则跳过该行
Set<ConstraintViolation<ChoiceQuestionImportRow>> violations = validator.validate(row);
if (!violations.isEmpty()) {
errors.add(new PaperImportErrorVO(rowIndex, sheetName, joinViolationMessages(violations)));
errors.add(new QuestionImportErrorVO(rowIndex, sheetName, joinViolationMessages(violations)));
continue;
}
@ -262,14 +278,14 @@ public class QuestionExcelImporter {
}
}
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;
}
// 校验答案:必须有答案且答案必须能找到对应的选项
String answerRaw = row.getAnswer().trim().toUpperCase();
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;
}
List<String> answerLetters = answerRaw.chars()
@ -278,23 +294,22 @@ public class QuestionExcelImporter {
.sorted()
.collect(Collectors.toList());
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;
}
List<String> notMatched = answerLetters.stream()
.filter(letter -> !options.containsKey(letter))
.collect(Collectors.toList());
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))));
continue;
}
// 校验关联课件名称查询到后回填coursewareManagementId
String coursewareName = row.getCoursewareName().trim();
Long coursewareManagementId = coursewareManagementIdByName.get(coursewareName);
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;
}
@ -339,10 +354,11 @@ public class QuestionExcelImporter {
* @param sheetName sheet
* @param coursewareManagementIdByName id
* @param parseResult
* @param formCoursewareManagementId formCoursewareManagementId
*/
private void convertJudgeRows(List<JudgeQuestionImportRow> rows, String sheetName,
Map<String, Long> coursewareManagementIdByName, ParseResult parseResult) {
List<PaperImportErrorVO> errors = parseResult.getErrors();
Map<String, Long> coursewareManagementIdByName, ParseResult parseResult, Long formCoursewareManagementId) {
List<QuestionImportErrorVO> errors = parseResult.getErrors();
for (int i = 0; i < rows.size(); i++) {
JudgeQuestionImportRow row = rows.get(i);
int rowIndex = i + HEAD_ROW_NUMBER + 1;
@ -350,23 +366,26 @@ public class QuestionExcelImporter {
continue;
}
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);
if (!violations.isEmpty()) {
errors.add(new PaperImportErrorVO(rowIndex, sheetName, joinViolationMessages(violations)));
errors.add(new QuestionImportErrorVO(rowIndex, sheetName, joinViolationMessages(violations)));
continue;
}
String answerRaw = row.getAnswer().trim().toUpperCase();
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;
}
String coursewareName = row.getCoursewareName().trim();
Long coursewareManagementId = coursewareManagementIdByName.get(coursewareName);
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;
}
@ -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 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.options = options;
this.questionNumByQuestionId = new HashMap<>();
@ -548,7 +567,7 @@ public class QuestionExcelImporter {
*
* @param errors
*/
public ParseResult(List<PaperImportErrorVO> errors) {
public ParseResult(List<QuestionImportErrorVO> errors) {
this.questions = new ArrayList<>(64);
this.options = new ArrayList<>(256);
this.questionNumByQuestionId = new HashMap<>();