Browse Source

add:客观题标答导入导出

caozixuan 1 year ago
parent
commit
0667c21d0c

+ 4 - 1
distributed-print-business/src/main/resources/db/log/脚本-caozx.sql

@@ -1,2 +1,5 @@
 ALTER TABLE `teach_course`
-    DROP COLUMN `basic_course_id`;
+    DROP COLUMN `basic_course_id`;
+INSERT INTO `sys_privilege` (`id`, `name`, `url`, `type`, `parent_id`, `sequence`, `property`, `enable`, `default_auth`, `front_display`) VALUES ('1132', '客观题模板导出', '/api/admin/mark/question/objective/export', 'URL', '897', '15', 'AUTH', '1', '1', '1');
+INSERT INTO `sys_privilege` (`id`, `name`, `url`, `type`, `parent_id`, `sequence`, `property`, `enable`, `default_auth`, `front_display`) VALUES ('1133', '客观题标答导入', '/api/admin/mark/question/objective/import', 'URL', '897', '16', 'AUTH', '1', '1', '1');
+UPDATE `sys_privilege` SET `related` = '672,902,903,904,905,906,907,908,909,910,911,912,913,962,963,1132,1133' WHERE (`id` = '901');

+ 0 - 10
distributed-print/src/main/java/com/qmth/distributed/print/api/CourseTargetController.java

@@ -1,6 +1,5 @@
 package com.qmth.distributed.print.api;
 
-import com.qmth.boot.api.annotation.Aac;
 import com.qmth.boot.api.constant.ApiConstant;
 import com.qmth.distributed.print.business.bean.params.CourseTargetParam;
 import com.qmth.distributed.print.business.bean.result.CourseDimensionTree;
@@ -121,13 +120,4 @@ public class CourseTargetController {
         courseTargetService.deleteCourseTarget(SystemConstant.convertIdToLong(id));
         return ResultUtil.ok();
     }
-
-    @ApiOperation(value = "课程目标管理-测试解析题库试题结构")
-    @RequestMapping(value = "/analyze", method = RequestMethod.POST)
-    @ApiResponses({ @ApiResponse(code = 200, message = "查询成功", response = CourseTargetResult.class) })
-    @Aac
-    public Result analyzePaperStruct(@ApiParam(value = "考试id", required = true) @RequestParam String examId,
-            @ApiParam(value = "试卷编号", required = true) @RequestParam String paperNumber) {
-        return ResultUtil.ok(courseTargetService.findTikuPaperStruct(SystemConstant.convertIdToLong(examId), paperNumber));
-    }
 }

+ 27 - 2
distributed-print/src/main/java/com/qmth/distributed/print/api/mark/MarkQuestionController.java

@@ -1,6 +1,5 @@
 package com.qmth.distributed.print.api.mark;
 
-
 import com.qmth.boot.api.constant.ApiConstant;
 import com.qmth.distributed.print.business.entity.ExamCard;
 import com.qmth.distributed.print.business.entity.ExamTaskDetail;
@@ -9,13 +8,13 @@ import com.qmth.distributed.print.business.service.ExamTaskDetailService;
 import com.qmth.teachcloud.common.annotation.OperationLogDetail;
 import com.qmth.teachcloud.common.bean.vo.PaperInfoVo;
 import com.qmth.teachcloud.common.contant.SystemConstant;
+import com.qmth.teachcloud.common.entity.MarkQuestion;
 import com.qmth.teachcloud.common.enums.CardCreateMethodEnum;
 import com.qmth.teachcloud.common.enums.log.CustomizedOperationTypeEnum;
 import com.qmth.teachcloud.common.util.ExamTaskUtil;
 import com.qmth.teachcloud.common.util.Result;
 import com.qmth.teachcloud.common.util.ResultUtil;
 import com.qmth.teachcloud.mark.dto.mark.setting.MarkQuestionDto;
-import com.qmth.teachcloud.common.entity.MarkQuestion;
 import com.qmth.teachcloud.mark.params.MarkObjectiveQuestionParams;
 import com.qmth.teachcloud.mark.params.MarkQuestionParams;
 import com.qmth.teachcloud.mark.service.MarkQuestionService;
@@ -26,6 +25,7 @@ import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 
 /**
@@ -110,6 +110,31 @@ public class MarkQuestionController extends BaseController {
         return ResultUtil.ok(true);
     }
 
+    /**
+     * 客观题模板导出
+     */
+    @ApiOperation(value = "客观题模板导出")
+    @RequestMapping(value = "/objective/export", method = RequestMethod.POST)
+    public Result objectiveExport(@ApiParam(value = "考试ID", required = true) @RequestParam String examId,
+            @ApiParam(value = "试卷编号", required = true) @RequestParam String paperNumber, HttpServletResponse response)
+            throws Exception {
+        markQuestionService.objectiveAnswerSettingModelExport(SystemConstant.convertIdToLong(examId), paperNumber,
+                response);
+        return ResultUtil.ok();
+    }
+
+    /**
+     * 客观题标答导入
+     */
+    @ApiOperation(value = "客观题标答导入")
+    @RequestMapping(value = "/objective/import", method = RequestMethod.POST)
+    public Result objectiveImport(@ApiParam(value = "考试ID", required = true) @RequestParam String examId,
+            @ApiParam(value = "试卷编号", required = true) @RequestParam String paperNumber,
+            @ApiParam(value = "标答excel文件",required = true) @RequestParam MultipartFile file) throws Exception {
+        markQuestionService.objectiveAnswerSettingImport(SystemConstant.convertIdToLong(examId), paperNumber, file);
+        return ResultUtil.ok();
+    }
+
     /**
      * 主观题标答文件查询
      */

+ 71 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/bean/dto/mark/ObjectiveAnswerDto.java

@@ -0,0 +1,71 @@
+package com.qmth.teachcloud.common.bean.dto.mark;
+
+import com.qmth.boot.tools.excel.annotation.ExcelColumn;
+import com.qmth.teachcloud.common.annotation.ExcelProperty;
+import com.qmth.teachcloud.common.annotation.excelStyle.ExcelDataStyle;
+import io.swagger.annotations.ApiModelProperty;
+import org.apache.poi.ss.usermodel.FillPatternType;
+
+/**
+ * @Description: 评卷参数设置-客观题标答设置导入导出对象
+ * @Author: CaoZixuan
+ * @Date: 2024-04-01
+ */
+public class ObjectiveAnswerDto {
+
+    @ApiModelProperty(value = "大题号")
+    @ExcelProperty(name = "大题号", index = 1)
+    @ExcelColumn(name = "大题号", index = 1, nullable = true)
+    @ExcelDataStyle(lock = true, fillColorRed = 191, fillColorGreen = 191, fillColorBlue = 191, fillPartner = FillPatternType.SOLID_FOREGROUND)
+    private String mainNumber;
+
+    @ApiModelProperty(value = "小题号")
+    @ExcelProperty(name = "小题号", index = 2)
+    @ExcelColumn(name = "小题号", index = 2, nullable = true)
+    @ExcelDataStyle(lock = true, fillColorRed = 191, fillColorGreen = 191, fillColorBlue = 191, fillPartner = FillPatternType.SOLID_FOREGROUND)
+    private String subNumber;
+
+    @ApiModelProperty(value = "大题名称")
+    @ExcelProperty(name = "大题名称", index = 3)
+    @ExcelColumn(name = "大题名称", index = 3, nullable = true)
+    @ExcelDataStyle(lock = true, fillColorRed = 191, fillColorGreen = 191, fillColorBlue = 191, fillPartner = FillPatternType.SOLID_FOREGROUND)
+    private String mainTitle;
+
+    @ApiModelProperty(value = "正确答案")
+    @ExcelProperty(name = "正确答案", index = 4)
+    @ExcelColumn(name = "正确答案", index = 4)
+    @ExcelDataStyle
+    private String answer;
+
+    public String getMainNumber() {
+        return mainNumber;
+    }
+
+    public void setMainNumber(String mainNumber) {
+        this.mainNumber = mainNumber;
+    }
+
+    public String getSubNumber() {
+        return subNumber;
+    }
+
+    public void setSubNumber(String subNumber) {
+        this.subNumber = subNumber;
+    }
+
+    public String getMainTitle() {
+        return mainTitle;
+    }
+
+    public void setMainTitle(String mainTitle) {
+        this.mainTitle = mainTitle;
+    }
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+}

+ 94 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/enums/OptionsEnum.java

@@ -0,0 +1,94 @@
+package com.qmth.teachcloud.common.enums;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: 选项编号枚举
+ * @Author: CaoZixuan
+ * @Date: 2024-04-02
+ */
+public enum OptionsEnum {
+    A("A", 1),
+    B("B", 2),
+    C("C", 3),
+    D("D", 4),
+    E("E", 5),
+    F("F", 6),
+    G("G", 7),
+    H("H", 8),
+    I("I", 9),
+    J("J", 10),
+    K("K", 11),
+    L("L", 12),
+    M("M", 13),
+    N("N", 14),
+    O("O", 15),
+    P("P", 16),
+    Q("Q", 17),
+    R("R", 18),
+    S("S", 19),
+    T("T", 20),
+    U("U", 21),
+    V("V", 22),
+    W("W", 23),
+    X("X", 24),
+    Y("Y", 25),
+    Z("Z", 26);
+
+    private final String code;
+
+    private final int index;
+
+    OptionsEnum(String code, int index) {
+        this.code = code;
+        this.index = index;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    /**
+     * 根据编号查询枚举
+     *
+     * @param code 编号
+     * @return 枚举
+     */
+    public static OptionsEnum getByCode(String code) {
+        OptionsEnum result = null;
+        List<OptionsEnum> optionsEnumList = Arrays.stream(OptionsEnum.values()).filter(e -> e.getCode().equals(code))
+                .collect(Collectors.toList());
+
+        if (optionsEnumList.size() > 1) {
+            throw ExceptionResultEnum.ERROR.exception("code:" + code + "的枚举异常");
+        } else if (optionsEnumList.size() == 1) {
+            result = optionsEnumList.get(0);
+        }
+        return result;
+    }
+
+    /**
+     * 根据索引查询枚举
+     *
+     * @param index 索引
+     * @return 枚举
+     */
+    public static String getCodeByIndex(int index) {
+        String result = "";
+        List<OptionsEnum> optionsEnumList = Arrays.stream(OptionsEnum.values()).filter(e -> index == e.getIndex())
+                .collect(Collectors.toList());
+        int size = optionsEnumList.size();
+        if (size > 1) {
+            throw ExceptionResultEnum.ERROR.exception("index:" + index + "的编号枚举异常");
+        } else if (size == 1) {
+            result = optionsEnumList.get(0).getCode();
+        }
+        return result;
+    }
+}

+ 17 - 9
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/ExcelWriter.java

@@ -146,8 +146,8 @@ public class ExcelWriter extends ExcelExport {
                 // 利用反射,根据javabean属性的先后顺序,动态调用getXxx()方法得到属性值
                 for (short i = 0; i < columnSettings.size(); i++) {
                     cell = row.createCell(i);// 创建列
-//                    Field field = columnSettings.get(i).getField();
-                    cell.setCellStyle(style);
+                    Field field = columnSettings.get(i).getField();
+                    cell.setCellStyle(this.getCellStyle(workbook.createCellStyle(), field));
                     String methodName = columnSettings.get(i).getGetMethodName();
                     Method method = this.getDataClass().getMethod(methodName, new Class[]{});
                     Object value = method.invoke(obj, new Object[]{});
@@ -184,8 +184,9 @@ public class ExcelWriter extends ExcelExport {
                 row = sheet.createRow(index);// 创建行
                 // 利用反射,根据javabean属性的先后顺序,动态调用getXxx()方法得到属性值
                 for (short i = 0; i < columnSettings.size(); i++) {
+                    Field field = columnSettings.get(i).getField();
                     cell = row.createCell(i);// 创建列
-                    cell.setCellStyle(style);
+                    cell.setCellStyle(this.getCellStyle(workbook.createCellStyle(), field));
                     String methodName = columnSettings.get(i).getGetMethodName();
                     Method method = this.getDataClass().getMethod(methodName, new Class[]{});
                     Object value = method.invoke(obj, new Object[]{});
@@ -219,11 +220,15 @@ public class ExcelWriter extends ExcelExport {
         return workbook;
     }
 
+    /**
+     * 如果对象属性上有@ExcelDataStyle注解则导出样式按照注解设置来,否则使用默认样式
+     * @param style 样式
+     * @param field 属性
+     * @return 最终样式
+     */
     private XSSFCellStyle getCellStyle(XSSFCellStyle style, Field field) {
         ExcelDataStyle excelDataStyle = field.getAnnotation(ExcelDataStyle.class);
-        if (Objects.isNull(excelDataStyle)) {
-            return style;
-        } else {
+        if (!Objects.isNull(excelDataStyle)) {
             // 文字位置
             style.setAlignment(excelDataStyle.horizontal());
             style.setVerticalAlignment(excelDataStyle.vertical());
@@ -232,7 +237,8 @@ public class ExcelWriter extends ExcelExport {
             int borderRed = excelDataStyle.borderColorRed();
             int borderGreen = excelDataStyle.borderColorGreen();
             int borderBlue = excelDataStyle.borderColorBlue();
-            XSSFColor borderColor = new XSSFColor(new Color(borderRed, borderGreen, borderBlue), workbook.getStylesSource().getIndexedColors());
+            XSSFColor borderColor = new XSSFColor(new Color(borderRed, borderGreen, borderBlue),
+                    workbook.getStylesSource().getIndexedColors());
             // 上边框
             if (excelDataStyle.borderTop().getCode() > 0) {
                 style.setBorderTop(excelDataStyle.borderTop());
@@ -256,7 +262,9 @@ public class ExcelWriter extends ExcelExport {
 
             // 背景
             style.setFillPattern(excelDataStyle.fillPartner());
-            XSSFColor fillColor = new XSSFColor(new Color(excelDataStyle.fillColorRed(), excelDataStyle.fillColorGreen(), excelDataStyle.fillColorBlue()), workbook.getStylesSource().getIndexedColors());
+            XSSFColor fillColor = new XSSFColor(
+                    new Color(excelDataStyle.fillColorRed(), excelDataStyle.fillColorGreen(),
+                            excelDataStyle.fillColorBlue()), workbook.getStylesSource().getIndexedColors());
             style.setFillForegroundColor(fillColor);
 
             // 字体
@@ -273,7 +281,7 @@ public class ExcelWriter extends ExcelExport {
             } else {
                 style.setLocked(false);
             }
-            return style;
         }
+        return style;
     }
 }

+ 25 - 4
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/MarkQuestionService.java

@@ -7,6 +7,8 @@ import com.qmth.teachcloud.mark.params.MarkObjectiveQuestionParams;
 import com.qmth.teachcloud.mark.params.MarkQuestionParams;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.util.List;
 
 /**
@@ -32,7 +34,26 @@ public interface MarkQuestionService extends IService<MarkQuestion> {
 
     void saveQuestions(MarkQuestionParams markQuestionParams);
 
-    List<MarkQuestion> listQuestionByExamIdAndPaperNumberAndGroupNumber(Long examId, String paperNumber, Integer number, Boolean isObjective);
+    List<MarkQuestion> listQuestionByExamIdAndPaperNumberAndGroupNumber(Long examId, String paperNumber, Integer number,
+            Boolean isObjective);
+
+    /**
+     * 客观题标答模板导出
+     *
+     * @param examId      考试id
+     * @param paperNumber 试卷编号
+     */
+    void objectiveAnswerSettingModelExport(Long examId, String paperNumber, HttpServletResponse response)
+            throws Exception;
+
+    /**
+     * 客观题标答导入
+     *
+     * @param examId      考试id
+     * @param paperNumber 试卷编号
+     * @param file        excel文件
+     */
+    void objectiveAnswerSettingImport(Long examId, String paperNumber, MultipartFile file) throws Exception;
 
     void saveObjectiveQuestions(MarkObjectiveQuestionParams markObjectiveQuestionParams);
 
@@ -44,12 +65,12 @@ public interface MarkQuestionService extends IService<MarkQuestion> {
 
     MarkQuestion getByExamIdAndPaperNumberAndMainNumberAndSubNumberAndObjective(Long examId, String paperNumber, Integer mainNumber, Integer subNumber, boolean isObjective);
 
-    List<MarkQuestion> listByExamIdAndPaperNumberAndPaperIndexAndPageIndex(Long examId, String paperNumber,
-                                                                           Integer paperIndex, Integer pageIndex);
+    List<MarkQuestion> listByExamIdAndPaperNumberAndPaperIndexAndPageIndex(Long examId, String paperNumber, Integer paperIndex, Integer pageIndex);
 
     MarkQuestionDto pageQuestionsByExamIdAndPaperNumber(Long examId, String paperNumber, String paperType, boolean canCreate);
 
-    void updateGroupNumberByExamIdAndPaperNumberAndGroupNumber(Integer newGroupNumber, Long examId, String paperNumber, Integer groupNumber);
+    void updateGroupNumberByExamIdAndPaperNumberAndGroupNumber(Integer newGroupNumber, Long examId, String paperNumber,
+            Integer groupNumber);
 
     long countByExamIdAndPaperNumberAndObjectiveAndGroupNumberIsNull(Long examId, String paperNumber, boolean objective);
 

+ 151 - 18
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkQuestionServiceImpl.java

@@ -1,31 +1,20 @@
 package com.qmth.teachcloud.mark.service.impl;
 
-import java.util.*;
-import java.util.stream.Collectors;
-
-import javax.annotation.Resource;
-
-import com.qmth.teachcloud.common.entity.SysUser;
-import com.qmth.teachcloud.common.util.ServletUtil;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.MapUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.multipart.MultipartFile;
-
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.teachcloud.common.bean.dto.mark.ObjectiveAnswerDto;
+import com.qmth.teachcloud.common.bean.result.ExcelResult;
 import com.qmth.teachcloud.common.contant.SystemConstant;
 import com.qmth.teachcloud.common.entity.MarkQuestion;
-import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
-import com.qmth.teachcloud.common.enums.ObjectivePolicy;
-import com.qmth.teachcloud.common.enums.QuestionType;
-import com.qmth.teachcloud.common.enums.UploadFileEnum;
+import com.qmth.teachcloud.common.entity.SysUser;
+import com.qmth.teachcloud.common.enums.*;
 import com.qmth.teachcloud.common.service.TeachcloudCommonService;
+import com.qmth.teachcloud.common.util.ConvertUtil;
+import com.qmth.teachcloud.common.util.ExcelUtil;
 import com.qmth.teachcloud.common.util.FileStoreUtil;
+import com.qmth.teachcloud.common.util.ServletUtil;
 import com.qmth.teachcloud.mark.dto.mark.setting.MarkQuestionDto;
 import com.qmth.teachcloud.mark.entity.MarkPaper;
 import com.qmth.teachcloud.mark.mapper.MarkQuestionMapper;
@@ -34,6 +23,18 @@ import com.qmth.teachcloud.mark.params.MarkQuestionParams;
 import com.qmth.teachcloud.mark.service.MarkGroupService;
 import com.qmth.teachcloud.mark.service.MarkPaperService;
 import com.qmth.teachcloud.mark.service.MarkQuestionService;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * <p>
@@ -202,6 +203,138 @@ public class MarkQuestionServiceImpl extends ServiceImpl<MarkQuestionMapper, Mar
         return this.list(lambdaQueryWrapper);
     }
 
+    @Override
+    public void objectiveAnswerSettingModelExport(Long examId, String paperNumber, HttpServletResponse response)
+            throws Exception {
+        List<ObjectiveAnswerDto> datasource = this.listQuestionByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber,
+                null, true).stream().flatMap(e -> {
+            ObjectiveAnswerDto dto = new ObjectiveAnswerDto();
+            dto.setMainNumber(String.valueOf(e.getMainNumber()));
+            dto.setSubNumber(String.valueOf(e.getSubNumber()));
+            dto.setMainTitle(e.getMainTitle());
+            dto.setAnswer(e.getAnswer());
+            return Stream.of(dto);
+        }).collect(Collectors.toList());
+
+        // 生成excel文件
+        ExcelUtil.excelExport("客观题标答模板", ObjectiveAnswerDto.class, datasource, response);
+    }
+
+    @Transactional
+    @Override
+    public void objectiveAnswerSettingImport(Long examId, String paperNumber, MultipartFile file) throws Exception {
+        List<MarkQuestion> willEditList = new ArrayList<>();
+        // 试卷结构
+        List<MarkQuestion> datasource = this.listQuestionByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, null,
+                true);
+        ExcelResult<ObjectiveAnswerDto> excelResult = ConvertUtil.analyzeExcel(file.getInputStream(), ObjectiveAnswerDto.class,
+                false, 0);
+        if (!excelResult.isSuccess()) {
+            throw ExceptionResultEnum.ERROR.exception(excelResult.getErrorMsg());
+        }
+        // 逻辑异常
+        List<String> logicErrorList = new ArrayList<>();
+        List<ObjectiveAnswerDto> objectiveAnswerDtoList = excelResult.getDatasource();
+        if (!Objects.equals(datasource.size(), objectiveAnswerDtoList.size())) {
+            logicErrorList.add("试卷结构试题数量和导入文件中的试题数量不一致");
+        }
+        Set<String> questionNumberSet = new HashSet<>();
+        // 构建题号-试题结构map
+        Map<String, MarkQuestion> markQuestionMap = datasource.stream()
+                .collect(Collectors.toMap(k -> k.getMainNumber() + SystemConstant.HYPHEN + k.getSubNumber(), v -> v));
+
+        for (int i = 0; i < objectiveAnswerDtoList.size(); i++) {
+            int rowIndex = i + 2;
+            ObjectiveAnswerDto cell = objectiveAnswerDtoList.get(i);
+            int exMainNumber = Integer.parseInt(cell.getMainNumber());
+            int exSubNumber = Integer.parseInt(cell.getSubNumber());
+            String exMainTitle = cell.getMainTitle();
+            String exAnswer = cell.getAnswer();
+
+            String key = exMainNumber + SystemConstant.HYPHEN + exSubNumber;
+            String unifyNotice = String.format("第[%s]行,大题号[%s],小题号[%s]的试题", rowIndex, exMainNumber, exSubNumber);
+            List<String> rowException = new ArrayList<>();
+            if (questionNumberSet.contains(key)) {
+                // 题号重复检验
+                rowException.add("题号和前面重复");
+            } else {
+                questionNumberSet.add(key);
+                if (markQuestionMap.containsKey(key)) {
+                    MarkQuestion markQuestion = markQuestionMap.get(key);
+                    markQuestion.setAnswer(exAnswer);
+                    willEditList.add(markQuestion);
+                    String mainTitle = markQuestion.getMainTitle();
+                    if (!Objects.equals(exMainTitle, mainTitle)) {
+                        rowException.add(String.format("大题名称[%s]错误", exMainTitle));
+                    } else {
+                        Integer questionType = markQuestion.getQuestionType();
+                        Integer optionCount = markQuestion.getOptionCount();
+                        List<String> optionScope = new ArrayList<>();
+                        try {
+                            for (int j = 1; j <= optionCount; j++) {
+                                String code = OptionsEnum.getCodeByIndex(j);
+                                optionScope.add(code);
+                            }
+                        } catch (Exception e) {
+                            throw ExceptionResultEnum.ERROR.exception(
+                                    String.format("第[%s]行,大题号[%s],小题号[%s]的试题答案可选选项解析失败", rowIndex, exMainNumber,
+                                            exSubNumber));
+                        }
+
+                        // 答案选项越界校验
+                        List<String> answerList = Arrays.asList(exAnswer.split(""));
+                        if (CollectionUtils.isNotEmpty(answerList)) {
+                            for (String answer : answerList) {
+                                String outOfBoundsException = String.format("答案[%s]错误。答案只能从[%s]中选择", answer, String.join("", optionScope));
+                                try {
+                                    OptionsEnum optionsEnum = OptionsEnum.getByCode(answer);
+                                    int optionIndex = optionsEnum.getIndex();
+                                    if (optionIndex > optionCount) {
+                                        // 答案选项超出范围
+                                        rowException.add(outOfBoundsException);
+                                    }
+                                } catch (Exception e) {
+                                    rowException.add(outOfBoundsException);
+                                }
+                            }
+                        } else {
+                            rowException.add("答案必填");
+                        }
+
+                        // 答案和题型匹配校验
+                        if (Objects.equals(questionType, 1)) {
+                            // 单选题
+                            if (answerList.size() > 1) {
+                                rowException.add(String.format("为[单选题],答案[%s]错误", exAnswer));
+                            }
+                        } else if (Objects.equals(questionType, 2)) {
+                            // 多选题
+
+                        } else if (Objects.equals(questionType, 3)) {
+                            // 判断题
+                            if (answerList.size() > 1) {
+                                rowException.add(String.format("为[判断题],答案[%s]错误", exAnswer));
+                            }
+                        } else {
+                            rowException.add("试题题型错误");
+                        }
+                    }
+
+                } else {
+                    rowException.add("题号错误,试卷中不存在该题号");
+                }
+            }
+            if (CollectionUtils.isNotEmpty(rowException)) {
+                unifyNotice = unifyNotice + String.join(",", rowException);
+                logicErrorList.add(unifyNotice);
+            }
+        }
+        if (CollectionUtils.isNotEmpty(logicErrorList)) {
+            throw ExceptionResultEnum.ERROR.exception(String.join(";\n", logicErrorList));
+        }
+        this.saveOrUpdateBatch(willEditList);
+    }
+
     @Transactional
     @Override
     public void saveObjectiveQuestions(MarkObjectiveQuestionParams markObjectiveQuestionParams) {