package com.qmth.distributed.print.business.templete.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.itextpdf.text.DocumentException;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.distributed.print.business.bean.dto.*;
import com.qmth.distributed.print.business.bean.examRule.CodeNameEnableValue;
import com.qmth.distributed.print.business.bean.examRule.FieldsDto;
import com.qmth.distributed.print.business.bean.params.BasicExamStudentParam;
import com.qmth.distributed.print.business.bean.params.DownloadPaperFileParam;
import com.qmth.distributed.print.business.bean.result.DictionaryResult;
import com.qmth.distributed.print.business.bean.result.TSyncExamStudentScoreResult;
import com.qmth.distributed.print.business.entity.*;
import com.qmth.distributed.print.business.enums.*;
import com.qmth.distributed.print.business.service.*;
import com.qmth.distributed.print.business.templete.service.TaskLogicService;
import com.qmth.distributed.print.business.util.CreatePdfUtil;
import com.qmth.distributed.print.business.util.pdf.CreatePdfNewUtil;
import com.qmth.teachcloud.common.annotation.ExcelDBFieldDesc;
import com.qmth.teachcloud.common.base.BaseEntity;
import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
import com.qmth.teachcloud.common.bean.dto.excel.*;
import com.qmth.teachcloud.common.bean.dto.excel.export.BasicStudentErrorExportDto;
import com.qmth.teachcloud.common.bean.dto.excel.export.SysUserErrorExportDto;
import com.qmth.teachcloud.common.bean.dto.mark.MarkUser;
import com.qmth.teachcloud.common.bean.dto.mark.PictureConfig;
import com.qmth.teachcloud.common.bean.params.ArraysParams;
import com.qmth.teachcloud.common.bean.params.UserSaveParams;
import com.qmth.teachcloud.common.bean.result.ExcelResult;
import com.qmth.teachcloud.common.bean.vo.PaperInfoVo;
import com.qmth.teachcloud.common.bean.vo.PrintPathVo;
import com.qmth.teachcloud.common.config.DictionaryConfig;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.*;
import com.qmth.teachcloud.common.enums.*;
import com.qmth.teachcloud.common.service.*;
import com.qmth.teachcloud.common.util.*;
import com.qmth.teachcloud.common.util.excel.ExcelError;
import com.qmth.teachcloud.mark.dto.mark.setting.MarkGroupDto;
import com.qmth.teachcloud.mark.dto.mark.setting.MarkGroupSingleDto;
import com.qmth.teachcloud.mark.entity.MarkPaper;
import com.qmth.teachcloud.mark.enums.ScorePolicy;
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 com.qmth.teachcloud.mark.service.MarkStudentService;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;

import javax.annotation.Resource;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Description: 任务处理逻辑impl
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2021/3/29
 */
@Service
public class TaskLogicServiceImpl implements TaskLogicService {
    private final static Logger log = LoggerFactory.getLogger(TaskLogicServiceImpl.class);

    @Resource
    ExamPrintPlanService examPrintPlanService;
    @Resource
    ExamTaskService examTaskService;
    @Resource
    ExamTaskDetailService examTaskDetailService;
    @Resource
    ExamCardService examCardService;
    @Resource
    ExamStudentService examStudentService;
    @Resource
    BasicAttachmentService basicAttachmentService;
    @Resource
    ExamDetailService examDetailService;
    @Resource
    CommonCacheService commonCacheService;
    @Resource
    BasicCourseService basicCourseService;
    @Resource
    CreatePdfUtil createPdfUtil;
    @Resource
    CreatePdfNewUtil createPdfNewUtil;
    @Resource
    TBTaskPdfService tbTaskPdfService;
    @Resource
    BasicCardRuleService basicCardRuleService;
    @Resource
    ExamDetailCourseService examDetailCourseService;
    @Resource
    BasicStudentService basicStudentService;
    @Resource
    BasicRoleDataPermissionService basicRoleDataPermissionService;
    @Resource
    BasicExamService basicExamService;
    @Resource
    SysUserService sysUserService;
    @Resource
    FileStoreUtil fileStoreUtil;
    @Resource
    BasicClazzService basicClazzService;
    @Resource
    SysOrgService sysOrgService;
    @Resource
    TCStatisticsService tcStatisticsService;
    @Resource
    TCStatisticsTempService tcStatisticsTempService;
    @Resource
    TSyncExamStudentScoreService tSyncExamStudentScoreService;
    @Resource
    @Lazy
    PrintCommonService printCommonService;
    @Resource
    DownloadService downloadService;
    @Resource
    TeachClazzService teachClazzService;
    @Resource
    DictionaryConfig dictionaryConfig;
    @Resource
    BasicPrintConfigService basicPrintConfigService;
    @Resource
    FileUploadService fileUploadService;
    @Resource
    BasicExamStudentService basicExamStudentService;
    @Resource
    MarkPaperService markPaperService;
    @Resource
    MarkStudentService markStudentService;
    @Resource
    MarkQuestionService markQuestionService;
    @Resource
    MarkGroupService markGroupService;
    @Resource
    SysRoleService sysRoleService;

    /**
     * 创建pdf前置条件
     *
     * @param map
     * @return
     */
    @Override
    @Transactional
    public Map<String, Object> createPdfPrepose(Map<String, Object> map) {
        TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
        SysUser sysUser = (SysUser) map.get(SystemConstant.USER);
        Long examDetailId = tbTask.getEntityId();
        //查询printPlan
        ExamPrintPlan examPrintPlan = examPrintPlanService.getById(tbTask.getPrintPlanId());
        if (Objects.isNull(examPrintPlan)) {
            throw ExceptionResultEnum.EXAM_PRINT_IS_NULL.exception();
        }
        UpdateWrapper<ExamDetail> examDetailQueryWrapper = new UpdateWrapper<>();
        if (Objects.isNull(examDetailId)) {
            throw ExceptionResultEnum.ERROR.exception("生成pdf考场ID异常");
        }
        examDetailQueryWrapper.lambda().set(ExamDetail::getStatus, ExamDetailStatusEnum.CREATING)
                .set(ExamDetail::getTaskId, tbTask.getId())
                .eq(ExamDetail::getId, examDetailId);
        examDetailService.update(examDetailQueryWrapper);

        //所有考场都撤回，印刷任务状态改为就绪
        examPrintPlanService.updateStatusById(tbTask.getPrintPlanId(), PrintPlanStatusEnum.READY);
        return map;
    }

    /**
     * 创建pdf核心逻辑
     *
     * @param examDetailCourseList
     * @param examPrintPlan        印刷计划
     * @param examDetail           考场对象
     * @param sysUser              当前用户
     * @param specifyPaperType     试卷类型
     * @param basicAttachmentList  附件集合
     * @param attachmentIds
     * @param fileTempList
     * @param list
     */
    @Transactional
    public List<BasicAttachment> createPdfCoreLogic(List<ExamDetailCourse> examDetailCourseList,
                                                    ExamPrintPlan examPrintPlan,
                                                    ExamDetail examDetail,
                                                    SysUser sysUser,
                                                    String specifyPaperType,
                                                    CreatePdfTypeEnum createPdfType,
                                                    List<BasicAttachment> basicAttachmentList,
                                                    Set<Long> attachmentIds,
                                                    List<File> fileTempList,
                                                    List<PdfDto>... list
    ) throws Exception {
        if (CreatePdfTypeEnum.SIGN.equals(createPdfType) || CreatePdfTypeEnum.PACKAGE.equals(createPdfType) || CreatePdfTypeEnum.CHECK_IN.equals(createPdfType)) {
            return null;
        }

        for (ExamDetailCourse examDetailCourse : examDetailCourseList) {
            List<PdfDto> studentPaperPdfList = new ArrayList<>();//所有试卷
            List<PdfDto> studentCardPdfList = new ArrayList<>();//所有题卡
            List<PdfDto> backupPaperPdfList = new ArrayList<>();//备份试卷
            List<PdfDto> backupCardPdfList = new ArrayList<>();//备份题卡


            String printContent = examPrintPlan.getPrintContent();
            if (StringUtils.isBlank(printContent)) {
                return null;
            }
            //查询命题任务绑定的试卷和题卡
            ExamTask examTask = examTaskService.getByExamIdAndCourseCodeAndPaperNumber(examDetail.getExamId(), examDetailCourse.getCourseCode(), examDetailCourse.getPaperNumber());
            if (examTask == null) {
                throw ExceptionResultEnum.EXAM_TASK_IS_NULL.exception();
            } else if (examTask.getReview()) {
                examTask = examTaskService.findExamTaskByFlowStatus(sysUser.getSchoolId(), examDetail.getExamId(), examDetailCourse.getCourseCode(), examDetailCourse.getPaperNumber(), FlowStatusEnum.FINISH);
            } else {
                if (!ExamStatusEnum.SUBMIT.equals(examTask.getStatus())) {
                    throw ExceptionResultEnum.EXAM_TASK_NOT_SUBMIT.exception();
                } else if (!examTask.getEnable()) {
                    throw ExceptionResultEnum.EXAM_TASK_ENABLE.exception();
                }
            }
            ExamTaskDetail examTaskDetail = examTaskDetailService.getByExamTaskId(examTask.getId());

            JSONObject jsonObject = new JSONObject();
            JSONArray jsonArray = new JSONArray();
            JSONArray stuJsonArray = new JSONArray();
            Map<String, ExamCard> examCardMap = new HashMap<>();
            List<PaperInfoVo> paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetail.getPaperAttachmentIds());
            ExamCard examCard;
            for (PaperInfoVo paperInfoVo : paperInfoVoList) {
                Long cardId = Long.valueOf(paperInfoVo.getCardId());
                if (Objects.nonNull(cardId)) {
                    examCard = examCardService.getById(cardId);
                    if (Objects.isNull(examCard)) {
                        throw ExceptionResultEnum.EXAM_CARD_IS_NULL.exception();
                    }
                    examCardMap.put(paperInfoVo.getName(), examCard);
                }
            }

            //查询题卡规则
            BasicCardRule basicCardRule = basicCardRuleService.getById(examTask.getCardRuleId());

            //试卷编号本考场使用卷型key
            String key = getPaperTypeKey(examDetail.getExamId(), examDetailCourse.getPaperNumber(), examDetail.getExamStartTime(), examDetail.getExamEndTime());
            //抽取卷型
            String paperType = createPdfUtil.getPaperType(examPrintPlan.getDrawRule(), examTaskDetail, key, specifyPaperType);
            //查询考生
            List<ExamStudent> examStudentList = examStudentService.listByExamDetailCourseId(examDetailCourse.getId());

            List<String> paperTypes = Arrays.stream(paperType.split(",")).sorted(Comparator.comparing(String::valueOf)).collect(Collectors.toList());
            // 计算备份数量，默认最小为1份。
            int backupCount = SystemConstant.calcBackupCount(examDetail.getBackupCount(), examDetail.getTotalSubjects(), 1);

            // 当前关联试卷类型
            List<String> relatePaperTypes = new ArrayList<>();
            // 考生实际关联试卷类型
            if (!CollectionUtils.isEmpty(examStudentList)) {
                AtomicInteger atomicInteger = new AtomicInteger(0);
                for (ExamStudent t : examStudentList) {
                    int i1 = atomicInteger.getAndIncrement();
                    int mod = i1 % examTaskDetail.getDrawCount();
                    t.setPaperType(paperTypes.get(mod));
                    if (!relatePaperTypes.contains(t.getPaperType())) {
                        relatePaperTypes.add(t.getPaperType());
                    }
                }
            } else {
                relatePaperTypes.addAll(paperTypes);
            }

            examTaskDetail.setRelatePaperType(String.join(",", relatePaperTypes));
            examDetailCourse.setPaperType(String.join(",", relatePaperTypes));

            // 试卷数据组装
            if ((CreatePdfTypeEnum.ALL.equals(createPdfType) || CreatePdfTypeEnum.PAPER.equals(createPdfType)) && printContent.contains("PAPER")) {
                List<PaperPdfDto> paperPdfDto = createPdfUtil.getPaperPdfFile(examDetailCourse.getPaperType(), examTaskDetail, fileTempList);

                //获取试卷pdf
                PdfDto pdfDto = createPdfUtil.getPaperPdf(paperPdfDto, backupCount, backupPaperPdfList);
                if (Objects.nonNull(pdfDto)) {
                    examDetailCourse.setPaperPagesA3(pdfDto.getPageCount());
                } else {
                    examDetailCourse.setPaperPagesA3(examDetailCourse.getPaperPagesA3());
                }
                // 备用试卷
                list[0].addAll(backupPaperPdfList);

                if (examStudentList != null && examStudentList.size() > 0) {
                    for (ExamStudent t : examStudentList) {
                        if (Objects.nonNull(pdfDto)) {
                            String[] waterMarkNames = {t.getStudentName(), t.getTicketNumber()};
                            createPdfUtil.getExamStudentPaperPdf(t.getPaperType(), paperPdfDto, studentPaperPdfList, waterMarkNames);
                        }
                    }
                } else if (examDetail.getTotalSubjects() != null) {
                    AtomicInteger atomicInteger = new AtomicInteger(0);
                    int i = 0;
                    while (i < examDetail.getTotalSubjects()) {
                        int seq = atomicInteger.getAndIncrement();
                        int mod = seq % examTaskDetail.getDrawCount();
                        if (Objects.nonNull(pdfDto)) {
                            createPdfUtil.getExamStudentPaperPdf(paperTypes.get(mod), paperPdfDto, studentPaperPdfList, null);
                        }
                        i++;
                    }
                } else {
                    throw ExceptionResultEnum.ERROR.exception("数据错误：未找到考生或者印刷数量");
                }
                // 考生试卷
                list[2].addAll(studentPaperPdfList);
            }

            // 题卡数据组装
            basicAttachmentList = Objects.isNull(basicAttachmentList) ? new ArrayList<>() : basicAttachmentList;
            if ((CreatePdfTypeEnum.ALL.equals(createPdfType) || CreatePdfTypeEnum.CARD_A3.equals(createPdfType)) && printContent.contains("CARD")) {
                Map<String, ExamCard> examCardDetailMap = new HashMap<>();
                Map<String, String> cardContentMap = new HashMap<>();
                for (String s : examDetailCourse.getPaperType().split(",")) {
                    examCard = examCardMap.get(s);
                    Optional.ofNullable(examCard).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("卷型" + s + "题卡不存在"));

                    createPdfUtil.getCardAttachmentId(examCard, attachmentIds);

                    //把模板页面上的 ${} 替换成实际内容
                    String cardContent = createPdfUtil.resetHtmlTemplateBar(examCard.getHtmlContent());

                    for (int i = 1; i <= backupCount; i++) {
                        BasicAttachment basicAttachment = createPdfUtil.cardHtml(String.format(SystemConstant.DATE_TIME_FORMAT, i), cardContent, examDetail, examDetailCourse, s, jsonArray, sysUser.getId(), backupCardPdfList, basicCardRule, fileTempList);
                        examDetailCourse.setCardPagesA3(basicAttachment.getPages());
                        basicAttachmentList.add(basicAttachment);
                    }
                    examCardDetailMap.put(s, examCard);
                    cardContentMap.put(s, cardContent);
                }
                // 备用题卡
                list[1].addAll(backupCardPdfList);

                if (examStudentList != null && examStudentList.size() > 0) {
                    for (ExamStudent t : examStudentList) {
                        // 用带条码的模板
                        basicAttachmentList.add(createPdfUtil.examStudentHtml(examCardDetailMap.get(t.getPaperType()).getHtmlContent(), t, paperType, examDetail, examDetailCourse, sysUser.getId(), studentCardPdfList, basicCardRule, fileTempList));
                    }
                } else if (examDetail.getTotalSubjects() != null) {
                    AtomicInteger atomicInteger = new AtomicInteger(0);
                    Map<String, BasicAttachment> stringBasicAttachmentMap = new HashMap<>();
                    int i = 0;
                    while (i < examDetail.getTotalSubjects()) {
                        int seq = atomicInteger.getAndIncrement();
                        int mod = seq % examTaskDetail.getDrawCount();
                        String tempPaperType = paperTypes.get(mod);
                        BasicAttachment basicAttachment = createPdfUtil.examStudentHtml(cardContentMap.get(tempPaperType), null, tempPaperType, examDetail, examDetailCourse, sysUser.getId(), studentCardPdfList, basicCardRule, fileTempList);

                        if (!stringBasicAttachmentMap.containsKey(tempPaperType)) {
                            stringBasicAttachmentMap.put(tempPaperType, basicAttachment);
                            JSONObject object = new JSONObject();
                            object.put("name", tempPaperType);
                            object.put("attachmentId", basicAttachment.getId());
                            stuJsonArray.add(object);

                            // 用不带条码的模板
                            basicAttachmentList.add(basicAttachment);
                        }
                        i++;
                    }
                } else {
                    throw ExceptionResultEnum.ERROR.exception("数据错误：未找到考生或者印刷数量");
                }
                // 题卡
                list[3].addAll(studentCardPdfList);
                jsonObject.put("card", jsonArray);
                examDetailCourse.setAttachmentId(jsonObject.toJSONString());
                examDetailCourse.setCommonAttachmentId(stuJsonArray.toJSONString());
            }
            examStudentService.saveOrUpdateBatch(examStudentList);

            examDetailCourse.setAttachmentId(jsonObject.toJSONString());
            examTaskDetailService.saveOrUpdate(examTaskDetail);

        }
        examDetailCourseService.saveOrUpdateBatch(examDetailCourseList);

        return basicAttachmentList;
    }

    private static String getPaperTypeKey(Long examId, String paperNumber, Long examStartTime, Long examEndTime) {
        //试卷编号本考场使用卷型key
        StringJoiner stringJoiner = new StringJoiner("_");
        stringJoiner.add(String.valueOf(examId)).add(paperNumber).add(String.valueOf(examStartTime)).add(String.valueOf(examEndTime));
        return stringJoiner.toString();
    }

    /**
     * 创建A4文件
     *
     * @param createPdfType
     * @param examPrintPlan
     * @param examDetail
     * @param basicSchool
     * @param examDetailCourseList
     * @param fileTempList
     * @param list
     * @throws IOException
     * @throws DocumentException
     */
    @Transactional
    public void createA4File(CreatePdfTypeEnum createPdfType, ExamPrintPlan examPrintPlan, ExamDetail examDetail, BasicSchool basicSchool, List<ExamDetailCourse> examDetailCourseList, List<File> fileTempList, List<PdfDto>... list) throws Exception {
        // 印品
        String ordinaryContent = examPrintPlan.getOrdinaryContent();
        if ((CreatePdfTypeEnum.ALL.equals(createPdfType) || CreatePdfTypeEnum.CHECK_IN.equals(createPdfType)) && StringUtils.isNotBlank(ordinaryContent)) {
            //获取普通印品
            JSONArray jsonArrayOrdinary = JSONArray.parseArray(ordinaryContent);
            for (int i = 0; i < jsonArrayOrdinary.size(); i++) {
                JSONObject jsonObjectOrdinary = jsonArrayOrdinary.getJSONObject(i);
                if (Objects.nonNull(jsonObjectOrdinary.get("attachmentId")) && !Objects.equals("", jsonObjectOrdinary.get("attachmentId"))) {
                    Long attachmentId = Long.parseLong((String) jsonObjectOrdinary.get("attachmentId"));
                    BasicAttachment basicAttachment = basicAttachmentService.getById(attachmentId);
                    createPdfUtil.createCheckIn(examDetail, basicAttachment, list[0], (Integer) jsonObjectOrdinary.get("backupCount"), fileTempList);
                }
            }
        }
        BasicExam basicExam = basicExamService.getById(examPrintPlan.getExamId());
        if (basicExam.getCategory().equals(ExamCategoryEnum.FORMAL)) {
            String variableContent = examPrintPlan.getVariableContent();
            if (StringUtils.isNotBlank(variableContent)) {
                List<Long> examDetailCourseListIds = examDetailCourseList.stream().map(BaseEntity::getId).collect(Collectors.toList());
                List<ExamStudentCourseDto> examStudentCourseDtoList = examStudentService.queryBySchoolIdAndExamDetailCourseIds(basicSchool.getId(), examDetailCourseListIds);
                //获取变量印品
                JSONArray jsonArrayVariable = JSONArray.parseArray(variableContent);
                for (int i = 0; i < jsonArrayVariable.size(); i++) {
                    JSONObject jsonObjectVariable = jsonArrayVariable.getJSONObject(i);
                    String type = (String) jsonObjectVariable.get("type");
                    if (Objects.nonNull(jsonObjectVariable.get("templateId")) && !Objects.equals("", jsonObjectVariable.get("templateId"))) {
                        Long templateId = Long.parseLong((String) jsonObjectVariable.get("templateId"));
                        if ((CreatePdfTypeEnum.ALL.equals(createPdfType) || CreatePdfTypeEnum.SIGN.equals(createPdfType)) && Objects.nonNull(type) && Objects.equals(type.toUpperCase(), "SIGN")) {//签到表
                            createPdfUtil.createSignBook(templateId, basicSchool.getName(), examDetail, examStudentCourseDtoList, list[1], (Integer) jsonObjectVariable.get("backupCount"), examDetailCourseList, fileTempList);
                        } else if ((CreatePdfTypeEnum.ALL.equals(createPdfType) || CreatePdfTypeEnum.PACKAGE.equals(createPdfType)) && Objects.nonNull(type) && Objects.equals(type.toUpperCase(), "PACKAGE")) {//卷袋贴
                            Integer backupCount = SystemConstant.calcBackupCount(examDetail.getBackupCount(), examDetail.getTotalSubjects(), 1);
                            createPdfUtil.createPaperPackage(templateId, basicSchool.getName(), examDetail, examStudentCourseDtoList, list[1], backupCount, (Integer) jsonObjectVariable.get("backupCount"), examDetailCourseList, examPrintPlan.getExamId(), fileTempList);
                        }
                    }
                }
            }
        }

        list[1].sort((o1, o2) -> o1.getSequence() > o2.getSequence() ? 1 : -1);
    }

    /**
     * 创建pdf逻辑
     *
     * @param map
     * @return
     */
    @Override
    public Map<String, Object> executeCreatePdfLogic(Map<String, Object> map) {
        List<BasicAttachment> basicAttachmentList = null;
        List<File> fileTempList = new ArrayList<>();
        TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
        SysUser sysUser = (SysUser) map.get(SystemConstant.USER);
        Long examDetailId = tbTask.getEntityId();
        try {
            String specifyPaperType = Objects.nonNull(map.get("paperType")) ? (String) map.get("paperType") : null;
            CreatePdfTypeEnum createPdfType = Objects.nonNull(map.get(SystemConstant.CREATE_PDF_TYPE)) ? CreatePdfTypeEnum.valueOf((String) map.get(SystemConstant.CREATE_PDF_TYPE)) : CreatePdfTypeEnum.ALL;

            //查询printPlan
            ExamPrintPlan examPrintPlan = examPrintPlanService.getById(tbTask.getPrintPlanId());

            BasicSchool basicSchool = commonCacheService.schoolCache(examPrintPlan.getSchoolId());
            BasicPrintConfig basicPrintConfig = basicPrintConfigService.getByExamId(examPrintPlan.getExamId());

            //查询examDetail
            Set<Long> attachmentIds = new HashSet<>();
            ExamDetail examDetail = examDetailService.getById(examDetailId);
            if (examDetail == null) {
                throw ExceptionResultEnum.EXAM_DETAIL_IS_NULL.exception();
            }

            tbTask.setObjName(examDetail.getExamRoom() + SystemConstant.HYPHEN + examDetail.getExamPlace());
            //查询examDetailCourse
            QueryWrapper<ExamDetailCourse> examDetailCourseQueryWrapper = new QueryWrapper<>();
            examDetailCourseQueryWrapper.lambda().eq(ExamDetailCourse::getExamDetailId, examDetail.getId());
            List<ExamDetailCourse> examDetailCourseList = examDetailCourseService.list(examDetailCourseQueryWrapper);


            List<PdfDto> variablePdfList = new ArrayList<>();//变量印品(签到表、卷袋贴)
            List<PdfDto> ordinaryPdfList = new ArrayList<>();//普通印品(登记表)
            List<PdfDto> paperPdfList = new ArrayList<>();//所有试卷
            List<PdfDto> examStudentPdfList = new ArrayList<>();//所有题卡
            List<PdfDto> backupPaperPdfList = new ArrayList<>();//备份试卷
            List<PdfDto> backupCardPdfList = new ArrayList<>();//备份题卡

            //创建pdf核心逻辑
            basicAttachmentList = createPdfCoreLogic(examDetailCourseList,
                    examPrintPlan,
                    examDetail,
                    sysUser,
                    specifyPaperType,
                    createPdfType,
                    basicAttachmentList,
                    attachmentIds,
                    fileTempList,
                    backupPaperPdfList,
                    backupCardPdfList,
                    paperPdfList,
                    examStudentPdfList);

            createA4File(createPdfType, examPrintPlan, examDetail, basicSchool, examDetailCourseList, fileTempList, ordinaryPdfList, variablePdfList);

            //合并A3（试卷+题卡+备用试卷+备用题卡）
//                String dirNameA3 = createPdfUtil.mergeA3Pdf(paperPdfList, examStudentPdfList, backupPaperPdfList, backupCardPdfList);
            //合并（试卷+备用试卷）
            String dirNamePaper = createPdfUtil.mergeA3Pdf(fileTempList, paperPdfList, backupPaperPdfList);
            //合并A4（签到表+卷袋贴+登记表）
//            String dirNameA4 = createPdfUtil.mergeA4Pdf(fileTempList, variablePdfList, ordinaryPdfList);
            //合并A3（题卡+备用题卡）
            String dirNameCardA3 = createPdfUtil.mergeA3Pdf(fileTempList, examStudentPdfList, backupCardPdfList);

            BasicAttachment attachment = createPdfUtil.mergePdfSaveDb(dirNamePaper, dirNameCardA3, tbTask, sysUser.getId(), examDetail, basicPrintConfig, fileTempList);
            if (basicAttachmentList != null) {
                basicAttachmentList.add(attachment);
            }

            examDetailService.updateById(examDetail);

            updateExamPrintPlan(basicPrintConfig, examPrintPlan);
            map.putIfAbsent("size", 1);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (Objects.nonNull(dictionaryConfig.sysDomain()) && dictionaryConfig.sysDomain().isOss()) {
                basicAttachmentService.batchDeleteAttachment(basicAttachmentList);
            }
            // 生成失败，状态改为新建
            examDetailService.updateStatusById(examDetailId, ExamDetailStatusEnum.NEW);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            if (!CollectionUtils.isEmpty(fileTempList)) {
                for (File file : fileTempList) {
                    file.delete();
                }
            }
        }
        return map;
    }

    /**
     * 更新考试计划
     *
     * @param basicPrintConfig
     * @param examPrintPlan
     */
    @Transactional
    public void updateExamPrintPlan(BasicPrintConfig basicPrintConfig, ExamPrintPlan examPrintPlan) {
        if (PrintMethodEnum.AUTO == basicPrintConfig.getPrintMethod()) {
            examPrintPlan.setStatus(PrintPlanStatusEnum.PRINTING);
            examPrintPlanService.updateById(examPrintPlan);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, Object> executeExaminationLogic(Map<String, Object> map) throws Exception {
        List<ExaminationExportDto> examinationExportDtoList = examDetailService.findExaminationExportDtoDatasource(map);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ExcelUtil.excelMake(ExaminationExportDto.class, examinationExportDtoList, outputStream);
        InputStream in = new ByteArrayInputStream(outputStream.toByteArray());

        StringJoiner stringJoiner = SystemConstant.getDirName(UploadFileEnum.FILE, true);
        stringJoiner.add(SystemConstant.getNanoId()).add(".").add(SystemConstant.XLSX);

        JSONObject jsonObject = new JSONObject();
        String dirName = FileUtil.replaceSplit(stringJoiner.toString());
        boolean oss = dictionaryConfig.sysDomain().isOss();
        if (oss || (!oss && dictionaryConfig.fssPrivateDomain().getConfig().startsWith(SystemConstant.START_PARENT))) {//上传至oss
            fileStoreUtil.ossUpload(dirName, in, DigestUtils.md5Hex(new ByteArrayInputStream(outputStream.toByteArray())), fileStoreUtil.getUploadEnumByPath(dirName).getFssType());
            jsonObject.put(SystemConstant.TYPE, oss ? SystemConstant.OSS : SystemConstant.LOCAL);
            jsonObject.put(SystemConstant.PATH, dirName);
        } else {
            dirName = FileUtil.replaceSplit((dictionaryConfig.fssPublicDomain().getConfig() + File.separator + dirName));
            fileStoreUtil.copyInputStreamToFile(in, new File(stringJoiner.toString()), DigestUtils.md5Hex(new ByteArrayInputStream(outputStream.toByteArray())), LocalCatalogEnum.LOCAL_FILE);
            jsonObject.put(SystemConstant.TYPE, SystemConstant.LOCAL);
            jsonObject.put(SystemConstant.PATH, dirName);
        }
        jsonObject.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.FILE);
        map.put(SystemConstant.PATH, jsonObject.toString());
        map.put(SystemConstant.DATA_COUNT, examinationExportDtoList.size());
        return map;
    }

    @Transactional
    @Override
    public Map<String, Object> executeImportExaminationLogic(Map<String, Object> map) throws Exception {
        SysUser sysUser = (SysUser) map.get(SystemConstant.USER);
        TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
        InputStream inputStream = (InputStream) map.get("inputStream");
        Long printPlanId = tbTask.getPrintPlanId();
        Long schoolId = tbTask.getSchoolId();
        ExamPrintPlan examPrintPlan = examPrintPlanService.getById(printPlanId);
        if (examPrintPlan == null) {
            throw ExceptionResultEnum.ERROR.exception("印刷计划不存在");
        }

        String printPlanName = examPrintPlan.getName();
        if (!tbTaskPdfService.countByPrintPlanIdAndEntityId(schoolId, printPlanId)) {
            throw ExceptionResultEnum.ERROR.exception("印刷计划[" + printPlanName + "]有考场正在生成PDF文件，无法导入考务数据");
        }
        PrintPlanStatusEnum printPlanStatus = examPrintPlan.getStatus();
        if (PrintPlanStatusEnum.NEW != printPlanStatus && PrintPlanStatusEnum.READY != printPlanStatus) {
            throw ExceptionResultEnum.ERROR.exception("印刷计划状态为[" + PrintPlanStatusEnum.NEW.getName() + "、" + PrintPlanStatusEnum.READY.getName() + "]时才可导入，当前状态[" + printPlanStatus.getName() + "]");
        }
        // 该学校有效考务规则字段
        List<FieldsDto> fieldsDtoList = examDetailService.findExaminationFields(schoolId);

        String importFilePath = tbTask.getImportFilePath();
        Map importFilePathMap = JSONObject.parseObject(importFilePath);
        String path = String.valueOf(importFilePathMap.get(SystemConstant.PATH));
        Workbook workbook;
        if (path.endsWith(SystemConstant.XLSX)) {
            workbook = new XSSFWorkbook(inputStream);
        } else if (path.endsWith(SystemConstant.XLS)) {
            workbook = new HSSFWorkbook(inputStream);
        } else {
            throw ExceptionResultEnum.ERROR.exception("不支持的文件格式。只允许上传后缀为" + SystemConstant.XLSX + "、" + SystemConstant.XLS + "的文件");
        }
        // 读取第一个工作表
        Sheet sheet = workbook.getSheetAt(0);
        if (sheet.getLastRowNum() == 0 && sheet.getPhysicalNumberOfRows() == 0) {
            throw ExceptionResultEnum.ERROR.exception("第一个sheet为空,考务数据必须放在第一个sheet（工作表）内");
        }
        // 第一行为说明内容，不能为空
        if (sheet.getRow(0) == null || !sheet.getRow(0).getCell(0).getStringCellValue().startsWith("说明")) {
            throw ExceptionResultEnum.ERROR.exception("表格第一行为说明内容，不能为空，且必须以\"说明\"开头");
        }
        // 第二行为表头，不能为空，且校验第一列为学号
//        if (sheet.getRow(1) == null || !"学号".equals(sheet.getRow(1).getCell(0).getStringCellValue())) {
//            throw ExceptionResultEnum.ERROR.exception("表格第二行为表头，不能为空，且第一列必须为\"学号\"");
//        }
        // 获取sheet行数
        int totalRows = sheet.getLastRowNum() + 1;
        // 获取sheet列数（列数从第二行开始记录，因为第一行是说明内容）
        int totalCells = 0;
        if (totalRows > 2 && sheet.getRow(1) != null) {
            totalCells = sheet.getRow(1).getPhysicalNumberOfCells();
        }
        // 获取sheet的title（第二行为表头）
        Row head = sheet.getRow(1);
        List<String> headList = new ArrayList<>();
        // 将必填字段匹配excel解析的表头索引
        for (int i = 0; i < totalCells; i++) {
            String cellValue = String.valueOf(ExcelUtil.convert(head.getCell(i)));
            for (FieldsDto fieldsDto : fieldsDtoList) {
                if (cellValue.equals(fieldsDto.getName())) {
                    // 如果通用规则必填字段和excel表头匹配上了，则为该必选字段设置其在excel中的索引，并跳出循环体
                    fieldsDto.setIndex(i);
                    break;
                }
            }
            headList.add(cellValue);
        }

        // 搜索所有有效字段 excel中的表头是否包含
        for (FieldsDto fieldsDto : fieldsDtoList) {
            if (!headList.contains(fieldsDto.getName())) {
                throw ExceptionResultEnum.ERROR.exception("考务数据必填字段[" + fieldsDto.getName() + "]在表头中不存在");
            }
        }

        List<ExaminationImportDto> examinationImportDtoList = new ArrayList<>();

        // 文件中课程代码对应的课程名称Map
        Map<String, String> courseCodeNameInExcelMap = new HashMap<>();
        // 课程管理中课程代码-课程名
        Map<String, String> courseCodeNameInBasicCourseMap = new HashMap<>();
        List<BasicCourse> basicCourseList = basicCourseService.list(new QueryWrapper<BasicCourse>().lambda().eq(BasicCourse::getSchoolId, schoolId));
        basicCourseList.forEach(e -> {
            courseCodeNameInBasicCourseMap.put(e.getCode(), e.getName());
        });
        // 文件中试卷编号对应多个课程代码
        Map<String, String> paperNumberWithCourseCodeInExcelMap = new HashMap<>();
        // 试卷编号在考试下有多个命题任务map
        Map<String, Integer> paperNumberWithExamTaskMap = new HashMap<>();
        Map<String, String> paperNumberCoureseSequenceMap = new HashMap<>();
        // 命题任务中试卷编号对应的课程代码map
        Map<String, String> paperNumberWithCourseCodeInExamTaskMap = new HashMap<>();

        Long examId = examPrintPlan.getExamId();
        List<ExamTask> examTaskList = examTaskService.list(new QueryWrapper<ExamTask>()
                .lambda()
                .eq(ExamTask::getSchoolId, schoolId)
                .eq(ExamTask::getExamId, examId));
        for (ExamTask examTask : examTaskList) {
            String paperNumber = examTask.getPaperNumber();
            String courseCode = examTask.getCourseCode();
            paperNumberWithCourseCodeInExamTaskMap.put(paperNumber, courseCode);
            if (paperNumberWithExamTaskMap.containsKey(paperNumber)) {
                int count = paperNumberWithExamTaskMap.get(paperNumber);
                paperNumberWithExamTaskMap.put(paperNumber, count);
            } else {
                paperNumberWithExamTaskMap.put(paperNumber, 1);
            }

            paperNumberCoureseSequenceMap.put(paperNumber, String.valueOf(examTask.getId()));
        }

        // 从第三行开始为数据（第一行说明，第二行表头，第三行往后为数据）
        for (int r = 2; r < totalRows; r++) {
            // excel中的数据错误
            StringJoiner errorRowDate = new StringJoiner("；\r\n");
            Row row = sheet.getRow(r);
            if (row == null || ExcelUtil.isRowAllCellEmpty(row, head)) {
                // excel中整行为空，直接跳过
                continue;
            }

            // 备选字段
            List<FieldsDto> secondaryFieldList = new ArrayList<>();

            ExaminationImportDto examinationImportDto = new ExaminationImportDto();
            Field[] examinationImportDtoFields = examinationImportDto.getClass().getDeclaredFields();
            List<CodeNameEnableValue> requiredFields = new ArrayList<>();
            for (FieldsDto fieldsDto : fieldsDtoList) {
                boolean match = false;
                String name = fieldsDto.getName();
                String code = fieldsDto.getCode();
                int index = fieldsDto.getIndex();
                String level = fieldsDto.getLevel();

                if (Objects.nonNull(row)) {
                    Cell cell = row.getCell(index);
                    String cellValue = cell == null ? null : String.valueOf(ExcelUtil.convert(cell));
                    if (cellValue == null || cellValue.length() < 1 || cellValue.equals("null")) {
                        cellValue = "";
                        errorRowDate.add("第" + (r + 1) + "行,第" + (index + 1) + "列,字段[" + name + "]必填");
                    }

                    for (Field examinationImportDtoField : examinationImportDtoFields) {
                        ExcelDBFieldDesc excelDBFieldDesc = examinationImportDtoField.getAnnotation(ExcelDBFieldDesc.class);
                        if (excelDBFieldDesc == null) {
                            // 如果没有注解，说明该数据库必须字段不是从excel中获得的，直接跳过
                            continue;
                        }
                        // 如果数据库字段中文名和必填字段中文名称对应，则通过反射为数据库必选字段赋值
                        String dbName = excelDBFieldDesc.name();
                        int dbLength = excelDBFieldDesc.length();
                        if (dbName.equals(name)) {
                            if (dbLength > 0) {
                                SystemConstant.verifyLength(cellValue, dbLength, dbName);
                            }
                            examinationImportDtoField.setAccessible(true);
                            examinationImportDtoField.set(examinationImportDto, cellValue);
                            match = true;

                            // 匹配上为必选字段
                            CodeNameEnableValue codeNameEnableValue = new CodeNameEnableValue();
                            codeNameEnableValue.setCode(code);
                            codeNameEnableValue.setName(name);
                            codeNameEnableValue.setEnable(true);
                            codeNameEnableValue.setValue(cellValue);
                            requiredFields.add(codeNameEnableValue);
                            break;
                        }
                    }
                    if (!match || "secondary".equals(level)) {
                        // 必选+扩展字段不能匹配到数据库中的字段存放 或者 该字段为扩展字段，则添加到扩展字段中
                        FieldsDto secondaryField = new FieldsDto();
                        secondaryField.setCode(code);
                        secondaryField.setName(name);
                        secondaryField.setEnable(true);
                        secondaryField.setValue(cellValue);
                        secondaryFieldList.add(secondaryField);
                    }
                }
            }
            examinationImportDto.setRequiredFieldList(requiredFields);
            examinationImportDto.setSecondaryFieldList(secondaryFieldList);
            examinationImportDto.setSchoolId(schoolId);
            // 解析时间
            try {
                Map<String, Object> timeMap = ConvertUtil.analyzeStartAndEndTime(examinationImportDto.getExamDate(), examinationImportDto.getExamTime());
                String examStartTime = String.valueOf(timeMap.get("startTime"));
                String examEndTime = String.valueOf(timeMap.get("endTime"));
                examinationImportDto.setExamStartTime(examStartTime);
                examinationImportDto.setExamEndTime(examEndTime);
            } catch (Exception e) {
                errorRowDate.add(e.getMessage());
            }

            // 唯一关系校验
            String courseCode = examinationImportDto.getCourseCode();
            String courseName = examinationImportDto.getCourseName();
            String paperNumber = examinationImportDto.getPaperNumber();
            String studentCode = examinationImportDto.getStudentCode();
            String teacherName = examinationImportDto.getTeacherName();
            if (StringUtils.isNotBlank(teacherName)) {
                SysUser user = sysUserService.getByLoginName(schoolId, teacherName);
                if (user == null) {
                    errorRowDate.add("任课老师[" + teacherName + "]在用户管理中不存在");
                } else {
                    examinationImportDto.setTeacherId(user.getId());
                }
            }

            if (!SystemConstant.isOneNull(courseCode, courseName)) {
                // 校验1 - 文件中课程代码对应多个不同的课程名称
                if (courseCodeNameInExcelMap.containsKey(courseCode)) {
                    String name = courseCodeNameInExcelMap.get(courseCode);
                    if (!courseName.equals(name)) {
                        errorRowDate.add("文件中课程代码[" + courseCode + "]对应多个不同的课程名称");
                    }
                } else {
                    courseCodeNameInExcelMap.put(courseCode, courseName);
                }
                // 校验2 - 文件中课程代码对应的课程名称和课程管理中对应课程名称不一致
                String basicName = courseCodeNameInBasicCourseMap.get(courseCode);
                if (StringUtils.isBlank(basicName)) {
                    errorRowDate.add(String.format("文件中课程代码[%s]对应课程名称为[%s]，课程管理中课程不存在", courseCode, courseName));
                } else if (!courseName.equals(basicName)) {
                    errorRowDate.add(String.format("文件中课程代码[%s]对应课程名称为[%s]，课程管理中对应课程名称为[%s]", courseCode, courseName, basicName));
                }
            }
            if (!RegexUtil.validStudentCode(studentCode)) {
                errorRowDate.add("文件中学号[" + studentCode + "]不对，学号只能由数字、字母组成");
            }
            if (!SystemConstant.isOneNull(paperNumber, courseCode)) {
                // 检验4 - 文件中试卷编号[%s]对应多个不同的课程代码[%s]
                if (paperNumberWithCourseCodeInExcelMap.containsKey(paperNumber)) {
                    String code = paperNumberWithCourseCodeInExcelMap.get(paperNumber);
                    if (!courseCode.equals(code)) {
                        errorRowDate.add(String.format("文件中试卷编号[%s]对应多个不同的课程代码", paperNumber));
                    }
                } else {
                    paperNumberWithCourseCodeInExcelMap.put(paperNumber, courseCode);
                }
            }
            if (SystemConstant.strNotNull(paperNumber)) {
                //检验5 - 试卷编号[%s]在考试[%s]下有多条命题任务，请联系管理员处理
                if (paperNumberWithExamTaskMap.containsKey(paperNumber)) {
                    int count = paperNumberWithExamTaskMap.get(paperNumber);
                    if (count > 1) {
                        errorRowDate.add(String.format("试卷编号[%s]在考试[%s]下有多条命题任务，请联系管理员处理", paperNumber, count));
                    }
                }
            }
            if (!SystemConstant.isOneNull(paperNumber, courseCode)) {
                //检验6 - 文件中试卷编号[%s]对应课程代码为[%s]，命题任务中对应课程代码为[%s]
                if (paperNumberWithCourseCodeInExamTaskMap.containsKey(paperNumber)) {
                    String code = paperNumberWithCourseCodeInExamTaskMap.get(paperNumber);
                    if (!courseCode.equals(code)) {
                        errorRowDate.add(String.format("文件中试卷编号[%s]对应课程代码为[%s]，命题任务中对应课程代码为[%s]", paperNumber, courseCode, code));
                    }
                }
            }
            examinationImportDto.setCoursePaperId(paperNumberCoureseSequenceMap.get(paperNumber));
            examinationImportDto.setErrorMessage(errorRowDate.toString());
            examinationImportDtoList.add(examinationImportDto);
        }
        List<String> errorList = examinationImportDtoList.stream()
                .map(ExaminationImportDto::getErrorMessage)
                .filter(SystemConstant::strNotNull)
                .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(errorList)) {
            // 优先处理excel每个独立行的数据异常
            map.put(SystemConstant.ERROR_DATA_LIST, examinationImportDtoList);
            map.put("errorList", errorList);
            map.put("fieldsDtoList", fieldsDtoList);
            return map;
        }

        // 按考点+开始时间+课程代码+考场排序
        examinationImportDtoList.sort(Comparator.comparing(ExaminationImportDto::getExamPlace)
                .thenComparing(ExaminationImportDto::getExamStartTime)
                .thenComparing(ExaminationImportDto::getCourseCode)
                .thenComparing(ExaminationImportDto::getExamRoom)
        );

        // 卷袋号生成规则
        try {
            // 组装exam_detail数据
            List<Long> examDetailIdList = examDetailService.disposeExamDataByExaminationExcel(examPrintPlan, examinationImportDtoList, sysUser);
            // 更改印刷计划状态
            examPrintPlan.setStatus(PrintPlanStatusEnum.READY);
            examPrintPlanService.updateById(examPrintPlan);

            map.put("examDetailIdList", examDetailIdList);
            map.put("dataCount", examinationImportDtoList.size());
        } catch (Exception e) {
            if (e instanceof DuplicateKeyException) {
                String errorColumn = e.getCause().toString();
                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3, errorColumn.length()).replaceAll("'", "");
                String[] data = errorColumn.split("-");
                data[1] = data[1].substring(0, data[1].indexOf("'"));
                throw ExceptionResultEnum.SQL_ERROR.exception("[" + FieldUniqueEnum.convertToTitle(columnStr) + "'" + data[1] + "']" + "数据不允许重复插入");
            } else if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return map;
    }

    /**
     * 下载pdf
     *
     * @param map
     * @return
     */
    @Override
    @Transactional
    public Map<String, Object> executeDownloadPdfLogic(Map<String, Object> map) {
        List<File> sourceFileList = new ArrayList<>();
        File zipFile = null;
        String zipLocalRootPath = null;
        try {
            TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
            JSONArray jsonArray = JSONArray.parseArray(tbTask.getRemark());
            ArraysParams arraysParams = new ArraysParams(jsonArray.toArray(new Long[jsonArray.size()]));
            if (Objects.isNull(arraysParams) || arraysParams.getIds().length == 0) {
                throw ExceptionResultEnum.ERROR.exception("请选择要下载的数据");
            }

            List<ExamDetailPdfDownloadDto> examDetailList = examDetailService.findPdfDownload(Arrays.asList(arraysParams.getIds()));
            if (CollectionUtils.isNotEmpty(examDetailList)) {
                Long time = System.currentTimeMillis();

                zipFile = SystemConstant.getFileTempDirVar(SystemConstant.ZIP_PREFIX);
                zipLocalRootPath = zipFile.getParent() + File.separator + time;

                for (ExamDetailPdfDownloadDto e : examDetailList) {
                    StringJoiner dirPath = new StringJoiner("")
                            .add(zipLocalRootPath).add(File.separator)
                            .add(e.getSemesterName()).add(File.separator)
                            .add(e.getExamName()).add(File.separator)
                            .add(e.getCourseNameCode()).add(File.separator)
                            .add(e.getPackageCode()).add(File.separator)
                            .add(e.getCourseNameCode()).add(SystemConstant.HYPHEN)
                            .add(e.getPaperNumber()).add(SystemConstant.HYPHEN);
                    //试卷合并文件
                    if (Objects.nonNull(e.getAttachmentId())) {
                        BasicAttachment attachment = basicAttachmentService.getById(e.getAttachmentId());
                        if (attachment != null) {
                            String fileName = dirPath + PdfTypeEnum.PAPER.getTitle() + SystemConstant.PDF_PREFIX;
                            File paperFile = fileUploadService.downloadFile(attachment, fileName);
                            if (Objects.nonNull(paperFile)) {
                                sourceFileList.add(paperFile);
                            }
                        }
                    }

                    //题卡合并文件
                    if (Objects.nonNull(e.getCardAttachmentId())) {
                        BasicAttachment cardAttachment = basicAttachmentService.getById(e.getCardAttachmentId());
                        if (cardAttachment != null) {
                            String fileName = dirPath + PdfTypeEnum.CARD_A3.getTitle() + SystemConstant.PDF_PREFIX;
                            File cardFile = fileUploadService.downloadFile(cardAttachment, fileName);
                            if (Objects.nonNull(cardFile)) {
                                sourceFileList.add(cardFile);
                            }
                        }
                    }

                    //卷袋贴/签到表/登记表文件
                    if (Objects.nonNull(e.getAttachmentPath())) {
                        JSONObject js = JSONObject.parseObject(e.getAttachmentPath());
                        List<PrintPathVo> printPathVoList = JSON.parseArray(js.getString(SystemConstant.PATH), PrintPathVo.class);
                        for (PrintPathVo pathVo : printPathVoList) {
                            String fileName = dirPath + pathVo.getPrintType().getName() + SystemConstant.PDF_PREFIX;
                            File cardFile = fileUploadService.downloadFile(pathVo.getType(), pathVo.getUploadType(), pathVo.getPdfPath(), fileName);
                            if (Objects.nonNull(cardFile)) {
                                sourceFileList.add(cardFile);
                            }
                        }
                    }
                }
                if (!CollectionUtils.isEmpty(sourceFileList)) {
                    boolean oss = dictionaryConfig.sysDomain().isOss();
                    StringJoiner stringJoiner = new StringJoiner("");
                    if (!oss && Objects.nonNull(dictionaryConfig.fssPublicDomain()) && !StringUtils.isBlank(dictionaryConfig.fssPublicDomain().getConfig()) && !dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT)) {
                        stringJoiner.add(dictionaryConfig.fssPublicDomain().getConfig()).add(File.separator);
                    }
                    stringJoiner = SystemConstant.getDirName(stringJoiner, UploadFileEnum.FILE, true);
                    stringJoiner.add("印刷任务管理_批量下载PDF_" + time).add(SystemConstant.ZIP_PREFIX);
                    String zipDirName = FileUtil.replaceSplit(stringJoiner.toString());
                    JSONObject jsonObject = SystemConstant.createZip(zipFile, zipLocalRootPath, zipDirName);
                    tbTask.setResultFilePath(jsonObject.toJSONString());
                }
            }
            map.put(SystemConstant.SIZE, Objects.nonNull(sourceFileList) ? sourceFileList.size() : 0);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            if (Objects.nonNull(zipFile)) {
                zipFile.delete();
            }
            if (Objects.nonNull(zipLocalRootPath)) {
                ConvertUtil.delFolder(zipLocalRootPath);
            }
            if (!CollectionUtils.isEmpty(sourceFileList)) {
                for (File file : sourceFileList) {
                    file.delete();
                }
            }
        }
        return map;
    }

    /**
     * 获取附件文件
     *
     * @param jsonObject
     * @param pathName
     * @return
     * @throws Exception
     */
    private File getJsonFile(JSONObject jsonObject, String pathName) throws Exception {
        File file = new File(pathName);
        if (!file.exists()) {
            file.getParentFile().mkdirs();
            file.createNewFile();
        }
        String attachmentType = jsonObject.getString(SystemConstant.TYPE);
        if (Objects.nonNull(attachmentType) && !Objects.equals(attachmentType, SystemConstant.LOCAL)) {
            file = fileStoreUtil.ossDownload(jsonObject.getString(SystemConstant.PDF_PATH), file, UploadFileEnum.PDF.getFssType());
        } else {
            File sourceFile = null;
            if (dictionaryConfig.fssPrivateDomain().getConfig().startsWith(SystemConstant.START_PARENT) || dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT)) {
                sourceFile = new File(dictionaryConfig.fssPrivateDomain().getConfig(), jsonObject.getString(SystemConstant.PDF_PATH));
            } else {
                sourceFile = new File(jsonObject.getString(SystemConstant.PDF_PATH));
            }
            FileUtils.copyFile(sourceFile, file);
        }
        return file;
    }

    /**
     * 获取附件文件
     *
     * @param attachment
     * @param pathName
     * @return
     * @throws Exception
     */
    private File getAttachmentFile(BasicAttachment attachment, String pathName) throws Exception {
        File file = null;
        if (Objects.nonNull(attachment) && Objects.nonNull(attachment.getPath())) {
            file = new File(pathName);
            if (!file.exists()) {
                file.getParentFile().mkdirs();
                file.createNewFile();
            }
            JSONObject jsonObject = JSONObject.parseObject(attachment.getPath());
            String attachmentType = jsonObject.getString(SystemConstant.TYPE);
            UploadFileEnum uploadFileEnum = UploadFileEnum.valueOf(jsonObject.getString(SystemConstant.UPLOAD_TYPE));
            if (Objects.nonNull(attachmentType) && !Objects.equals(attachmentType, SystemConstant.LOCAL)) {
                file = fileStoreUtil.ossDownload(jsonObject.getString(SystemConstant.PATH), file, uploadFileEnum.getFssType());
            } else {
                File sourceFile = null;
                if (dictionaryConfig.fssPrivateDomain().getConfig().startsWith(SystemConstant.START_PARENT) || dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT)) {
                    sourceFile = new File(dictionaryConfig.fssPrivateDomain().getConfig(), jsonObject.getString(SystemConstant.PATH));
                } else {
                    sourceFile = new File(jsonObject.getString(SystemConstant.PATH));
                }
                FileUtils.copyFile(sourceFile, file);
            }
        }
        return file;
    }

    @Override
    @Transactional
    public Map<String, Object> executeExportSampleLogic(Map<String, Object> map) throws Exception {
        File zipFile = null;
        String zipLocalRootPath = null;
        try {
            TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
            Long time = System.currentTimeMillis();
            boolean oss = dictionaryConfig.sysDomain().isOss();
            StringJoiner stringJoiner = new StringJoiner("");
            if (!oss && Objects.nonNull(dictionaryConfig.fssPublicDomain()) && !StringUtils.isBlank(dictionaryConfig.fssPublicDomain().getConfig()) && !dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT)) {
                stringJoiner.add(dictionaryConfig.fssPublicDomain().getConfig()).add(File.separator);
            }
            stringJoiner = SystemConstant.getDirName(stringJoiner, UploadFileEnum.FILE, true);
            stringJoiner.add("导出审核样本_" + time).add(SystemConstant.ZIP_PREFIX);

            String zipDirName = FileUtil.replaceSplit(stringJoiner.toString());
            zipFile = SystemConstant.getFileTempDirVar(SystemConstant.ZIP_PREFIX);
            zipLocalRootPath = zipFile.getParent() + File.separator + time;

            // 跟文件路径
            Long[] ids = (Long[]) map.get("ids");
            int count = 0;
            for (Long id : ids) {
                ExamTask examTask = examTaskService.getById(id);
                if (Objects.isNull(examTask)) {
                    throw ExceptionResultEnum.ERROR.exception(String.format("课程[%s（%s）]，试卷编号[%s]的命题任务不存在", examTask.getCourseName(), examTask.getCourseCode(), examTask.getPaperNumber()));
                }
                String examTaskSign = examTask.getCourseName() + SystemConstant.HYPHEN + examTask.getPaperNumber();
                String firstPath = zipLocalRootPath + File.separator + examTaskSign;
                ExamTaskDetail examTaskDetail = examTaskDetailService.getByExamTaskId(id);
                if (examTaskDetail == null) {
                    throw ExceptionResultEnum.ERROR.exception(String.format("课程[%s（%s）]，试卷编号[%s]的" + ExceptionResultEnum.EXAM_TASK_NOT_SUBMIT.getMessage(), examTask.getCourseName(), examTask.getCourseCode(), examTask.getPaperNumber()));
                }

                // 收集处理试卷和题卡
                List<PaperInfoVo> paperInfoVos = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetail.getPaperAttachmentIds());
                if (CollectionUtils.isEmpty(paperInfoVos)) {
                    throw ExceptionResultEnum.ERROR.exception(String.format("课程[%s（%s）]，试卷编号[%s]的命题任务未上传试卷或题卡", examTask.getCourseName(), examTask.getCourseCode(), examTask.getPaperNumber()));
                }

                for (PaperInfoVo paperInfoVo : paperInfoVos) {
                    // 下载试卷
                    Long attachmentId = Long.valueOf(paperInfoVo.getAttachmentId());
                    if (Objects.nonNull(attachmentId)) {
                        BasicAttachment paperAttachment = basicAttachmentService.getById(attachmentId);
                        JSONObject jsonObject = JSONObject.parseObject(paperAttachment.getPath());
                        UploadFileEnum uploadFileEnum = UploadFileEnum.valueOf((String) jsonObject.get(SystemConstant.UPLOAD_TYPE));
                        String paperPath = firstPath + File.separator + "试卷" + File.separator + paperInfoVo.getName() + "-" + paperAttachment.getName() + paperAttachment.getType();
                        if (oss || (!oss && dictionaryConfig.fssPrivateDomain().getConfig().startsWith(SystemConstant.START_PARENT))) {
                            fileStoreUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), paperPath, uploadFileEnum.getFssType());
                        } else {
                            FileUtil.copyFile(jsonObject.getString(SystemConstant.PATH), paperPath);
                        }
                    }

                    // 下载题卡
                    Long cardId = Long.valueOf(paperInfoVo.getCardId());
                    if (cardId != null) {
                        String paperType = paperInfoVo.getName();
                        ExamCard examCard = examCardService.getById(cardId);
                        if (Objects.isNull(examCard)) {
                            throw ExceptionResultEnum.ERROR.exception(String.format("课程[%s（%s）]，试卷编号[%s]，卷型[%s]的题卡不存在", examTask.getCourseName(), examTask.getCourseCode(), examTask.getPaperNumber(), paperType));
                        }
                        MakeMethodEnum makeMethodEnum = examCard.getMakeMethod();
                        if (MakeMethodEnum.SELECT.equals(makeMethodEnum) && CardCreateMethodEnum.UPLOAD.equals(examCard.getCreateMethod())) {
                            Long cardAttachmentId = examCard.getAttachmentId();
                            if (cardAttachmentId == null) {
                                throw ExceptionResultEnum.ERROR.exception(String.format("课程[%s（%s）]，试卷编号[%s]，卷型[%s]的题卡未上传", examTask.getCourseName(), examTask.getCourseCode(), examTask.getPaperNumber(), paperType));
                            }
                            BasicAttachment cardAttachment = basicAttachmentService.getById(cardAttachmentId);
                            if (Objects.isNull(cardAttachment)) {
                                throw ExceptionResultEnum.ERROR.exception(String.format("课程[%s（%s）]，试卷编号[%s]，卷型[%s]的题卡未找到上传附件数据", examTask.getCourseName(), examTask.getCourseCode(), examTask.getPaperNumber(), paperType));
                            }
                            JSONObject jsonObject = JSONObject.parseObject(cardAttachment.getPath());
                            // 题卡路径
                            String cardPath = firstPath + File.separator + "题卡" + File.separator + paperType + "-" + cardAttachment.getName() + cardAttachment.getType();
                            UploadFileEnum uploadFileEnum = UploadFileEnum.valueOf((String) jsonObject.get(SystemConstant.UPLOAD_TYPE));

                            if (oss || (!oss && dictionaryConfig.fssPrivateDomain().getConfig().startsWith(SystemConstant.START_PARENT))) {
                                fileStoreUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), cardPath, uploadFileEnum.getFssType());
                            } else {
                                FileUtil.copyFile(jsonObject.getString(SystemConstant.PATH), cardPath);
                            }
                        } else {
                            String htmlContent = examCard.getHtmlContent();
                            byte[] bytes = htmlContent.getBytes(StandardCharsets.UTF_8);
                            // 题卡路径
                            String cardPath = firstPath + File.separator + "题卡" + File.separator + paperType + "-" + examCard.getTitle() + SystemConstant.HTML_PREFIX;
                            File localFile = new File(cardPath);
                            if (!localFile.getParentFile().exists()) {
                                localFile.getParentFile().mkdirs();
                            }
                            FileCopyUtils.copy(bytes, localFile);
                        }
                    }

                    // 处理审核样品
                    List<Map> confirmInfo = JSONObject.parseArray(examTaskDetail.getPaperConfirmAttachmentIds(), Map.class);
                    for (Map confirmMap : confirmInfo) {
                        if (Objects.isNull(confirmMap.get("attachmentId"))) {
                            throw ExceptionResultEnum.ERROR.exception(String.format("课程[%s（%s）]，试卷编号[%s]的附件未上传", examTask.getCourseName(), examTask.getCourseCode(), examTask.getPaperNumber()));
                        }
                        Long confirmAttachmentId = Long.valueOf(String.valueOf(confirmMap.get("attachmentId")));
                        BasicAttachment confirmAttachment = basicAttachmentService.getById(confirmAttachmentId);
                        if (confirmAttachment == null) {
                            throw ExceptionResultEnum.ERROR.exception(String.format("课程[%s（%s）]，试卷编号[%s]的附件未找到上传数据", examTask.getCourseName(), examTask.getCourseCode(), examTask.getPaperNumber()));
                        }
                        JSONObject jsonObject = JSONObject.parseObject(confirmAttachment.getPath());
                        UploadFileEnum uploadFileEnum = UploadFileEnum.valueOf((String) jsonObject.get(SystemConstant.UPLOAD_TYPE));
                        String confirmPath = firstPath + File.separator + "审核样本" + File.separator + confirmAttachment.getName() + confirmAttachment.getType();
                        if (oss || (!oss && dictionaryConfig.fssPrivateDomain().getConfig().startsWith(SystemConstant.START_PARENT))) {
                            fileStoreUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), confirmPath, uploadFileEnum.getFssType());
                        } else {
                            FileUtil.copyFile(jsonObject.getString(SystemConstant.PATH), confirmPath);
                        }
                    }
                }
                count++;
            }
            JSONObject jsonObject = SystemConstant.createZip(zipFile, zipLocalRootPath, zipDirName);
            tbTask.setResultFilePath(jsonObject.toJSONString());
            map.put("count", count);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            if (Objects.nonNull(zipFile)) {
                zipFile.delete();
            }
            if (Objects.nonNull(zipLocalRootPath)) {
                ConvertUtil.delFolder(zipLocalRootPath);
            }
        }
        return map;
    }

    @Override
    public Map<String, Object> executeExportPaperAndCardLogic(Map<String, Object> map) {
        String zipLocalRootPath = null;
        File zipFile = null;
        List<File> sourceFileList = null;
        try {
            sourceFileList = new ArrayList<>();
            TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);

            Long time = System.currentTimeMillis();
            boolean oss = dictionaryConfig.sysDomain().isOss();
            StringJoiner stringJoiner = new StringJoiner("");
            if (!oss && Objects.nonNull(dictionaryConfig.fssPublicDomain()) && !StringUtils.isBlank(dictionaryConfig.fssPublicDomain().getConfig()) && !dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT)) {
                stringJoiner.add(dictionaryConfig.fssPublicDomain().getConfig()).add(File.separator);
            }
            stringJoiner = SystemConstant.getDirName(stringJoiner, UploadFileEnum.FILE, true);
            stringJoiner.add("卷库查询管理试卷、空白题卡批量下载_" + time).add(SystemConstant.ZIP_PREFIX);

            String zipDirName = FileUtil.replaceSplit(stringJoiner.toString());
            zipFile = SystemConstant.getFileTempDirVar(SystemConstant.ZIP_PREFIX);
            zipLocalRootPath = zipFile.getParent() + File.separator + time;

            List<ExamTaskDetailDto> examTasks = (List<ExamTaskDetailDto>) map.get("examTasks");
            for (ExamTaskDetailDto examTask : examTasks) {
                ExamTaskDetailPdfDownloadDto examTaskDetailPdfDownloadDto = examTaskDetailService.findPdfDownload(Long.valueOf(examTask.getId()));

                StringJoiner dirPath = new StringJoiner("");
                dirPath = dirPath.add(zipLocalRootPath).add(File.separator)
                        .add(examTaskDetailPdfDownloadDto.getSemesterName()).add(File.separator)
                        .add(examTaskDetailPdfDownloadDto.getExamName()).add(File.separator)
                        .add(examTaskDetailPdfDownloadDto.getCourseNameCode().replaceAll(" ", "")).add(File.separator)
                        .add(examTaskDetailPdfDownloadDto.getPaperNumber()).add(File.separator)
                        .add(examTaskDetailPdfDownloadDto.getCourseNameCode().replaceAll(" ", "")).add(SystemConstant.HYPHEN)
                        .add(examTaskDetailPdfDownloadDto.getPaperNumber()).add(SystemConstant.HYPHEN);
                // 试卷
                String paperAttachmentIds = examTaskDetailPdfDownloadDto.getPaperAttachmentIds();
                if (StringUtils.isNotBlank(paperAttachmentIds)) {
                    List<PaperInfoVo> paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(paperAttachmentIds);
                    if (CollectionUtils.isEmpty(paperInfoVoList)) {
                        throw ExceptionResultEnum.ERROR.exception("试卷信息不存在");
                    }
                    for (PaperInfoVo paperInfoVo : paperInfoVoList) {
                        Long attachmentId = Long.valueOf(paperInfoVo.getAttachmentId());
                        String name = paperInfoVo.getName();
                        if (Objects.nonNull(attachmentId)) {
                            BasicAttachment attachment = basicAttachmentService.getById(attachmentId);
                            if (Objects.nonNull(attachment)) {
                                String fileName = dirPath + "试卷" + SystemConstant.HYPHEN + name + attachment.getType();
                                File paperFile = fileUploadService.downloadFile(attachment, fileName);
                                sourceFileList.add(paperFile);
                            }
                        }
                        Long cardId = Long.valueOf(paperInfoVo.getCardId());
                        ExamCard examCard = examCardService.getById(cardId);
                        Optional.ofNullable(examCard).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("找不到答题卡"));

                        String cardHtmlPath = dirPath + "题卡" + SystemConstant.HYPHEN + name + SystemConstant.HTML_PREFIX;
                        String cardPdfPath = dirPath + "题卡" + SystemConstant.HYPHEN + name + SystemConstant.PDF_PREFIX;
                        // 通用题卡
                        String htmlContent = createPdfNewUtil.replaceBlankHtmlContent(examCard.getHtmlContent());
                        // html
                        File localFile = new File(cardHtmlPath);
                        if (!localFile.exists()) {
                            localFile.getParentFile().mkdirs();
                            localFile.createNewFile();
                        }
                        // 生成html文件
                        FileCopyUtils.copy(htmlContent.getBytes(StandardCharsets.UTF_8), localFile);
                        sourceFileList.add(localFile);
                        // 转pdf文件
                        File file = new File(cardPdfPath);
                        if (!file.exists()) {
                            file.getParentFile().mkdirs();
                            file.createNewFile();
                        }
                        HtmlToPdfUtil.convert(cardHtmlPath, cardPdfPath, PageSizeEnum.A3);
                        sourceFileList.add(file);
                    }
                }
            }
            JSONObject jsonObject = SystemConstant.createZip(zipFile, zipLocalRootPath, zipDirName);
            tbTask.setResultFilePath(jsonObject.toJSONString());
            map.put(SystemConstant.DATA_COUNT, examTasks.size());
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            if (Objects.nonNull(zipFile)) {
                zipFile.delete();
            }
            if (Objects.nonNull(zipLocalRootPath)) {
                ConvertUtil.delFolder(zipLocalRootPath);
            }
            if (!CollectionUtils.isEmpty(sourceFileList)) {
                for (File file : sourceFileList) {
                    file.delete();
                }
            }
        }
        return map;
    }


    @Transactional
    @Override
    public Map<String, Object> executeImportBasicStudentLogic(Map<String, Object> map) throws Exception {
        InputStream inputStream = (InputStream) map.get("inputStream");
        List<String> studentCodeList = new ArrayList<>();
        List<BasicStudentErrorExportDto> errorDataList = new ArrayList<>();
        AtomicInteger totalInteger = new AtomicInteger(0);
        AtomicInteger successInteger = new AtomicInteger(0);
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(inputStream, Lists.newArrayList(BasicStudentImportDto.class, DescribeImportDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            Map<String, String> checkCodeMap = new HashMap<>();
            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> basicStudentImportDtoList = excelMap.get(i);
                // 将要删除的异常数据
                List<BasicStudentImportDto> willRemoveList = new ArrayList<>();
                for (int y = 0; y < Objects.requireNonNull(basicStudentImportDtoList).size(); y++) {
                    // 行索引
                    Integer rowIndex = y + 1;
                    BasicStudentImportDto basicStudentImportDto = (BasicStudentImportDto) basicStudentImportDtoList.get(y);
                    basicStudentImportDto.setRowIndex(rowIndex);
                    totalInteger.getAndIncrement();

                    BasicStudentErrorExportDto basicStudentErrorExportDto = new BasicStudentErrorExportDto();
                    BeanUtils.copyProperties(basicStudentImportDto, basicStudentErrorExportDto);


                    String studentName = StringUtils.isNotBlank(basicStudentImportDto.getStudentName()) ? basicStudentImportDto.getStudentName().trim() : null;
                    String studentCode = StringUtils.isNotBlank(basicStudentImportDto.getStudentCode()) ? basicStudentImportDto.getStudentCode().trim() : null;
                    studentCodeList.add(studentCode);

                    StringJoiner errorStringJoiner = new StringJoiner(";");
                    // 必填字段校验
                    String errorStringEmpty = ExcelUtil.checkExcelField(basicStudentImportDto);
                    if (errorStringEmpty.length() > 0) {
                        errorStringJoiner.add(errorStringEmpty);
                    }

                    // 检验学号
                    if (SystemConstant.strNotNull(studentCode)) {
                        if (checkCodeMap.containsKey(studentCode)) {
                            errorStringJoiner.add("学号[" + studentCode + "]查询到多条数据");
                        } else {
                            checkCodeMap.put(studentCode, studentName);
                        }
                    }

                    if (Objects.nonNull(studentCode) && !studentCode.matches(SystemConstant.REGULAR_EXPRESSION_OF_CODE1)) {
                        errorStringJoiner.add("学号[" + studentCode + "]不符合规范");
                    }
                    String errorString = errorStringJoiner.toString();
                    if (errorString.length() > 0) {
                        basicStudentErrorExportDto.setErrorMsg(errorString);
                        errorDataList.add(basicStudentErrorExportDto);
                        willRemoveList.add(basicStudentImportDto);
                    } else {
                        // 校验通过的数据
                        successInteger.getAndIncrement();
                    }
                }
                basicStudentImportDtoList.removeAll(willRemoveList);
            }
            if (!errorDataList.isEmpty()) {
                map.put(SystemConstant.ERROR_DATA_LIST, errorDataList);
            }
            map.put("studentCodeList", studentCodeList);
            map.put(SystemConstant.DATA_COUNT, totalInteger.get());
            map.put(SystemConstant.SUCCESS_DATA_COUNT, successInteger.get());
            map.put(SystemConstant.ERROR_DATA_COUNT, errorDataList.size());
            return finalExcelList;
        }, 2);
        return basicStudentService.executeBasicStudentImportLogic(finalList, map);
    }

    @Transactional
    @Override
    public Map<String, Object> executeImportSysUserLogic(Map<String, Object> map) throws Exception {
        InputStream inputStream = (InputStream) map.get("inputStream");

        List<SysUserErrorExportDto> errorDataList = new ArrayList<>();
        AtomicInteger totalInteger = new AtomicInteger(0);
        AtomicInteger successInteger = new AtomicInteger(0);
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(inputStream, Lists.newArrayList(SysUserImportDto.class, DescribeImportDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            Map<String, SysUserImportDto> checkDtoMap = new HashMap<>();

            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> sysUserImportDtoList = excelMap.get(i);
                if (CollectionUtils.isEmpty(sysUserImportDtoList)) {
                    continue;
                }
                map.put(SystemConstant.DATA_COUNT, sysUserImportDtoList.size());
                List<SysUserImportDto> removeDataList = new ArrayList<>();
                Map<String, Integer> phoneNumberMap = new HashMap<>();
                for (int y = 0; y < Objects.requireNonNull(sysUserImportDtoList).size(); y++) {
                    SysUserImportDto sysUserImportDto = (SysUserImportDto) sysUserImportDtoList.get(y);
                    totalInteger.getAndIncrement();
                    SysUserErrorExportDto sysUserErrorExportDto = new SysUserErrorExportDto();
                    BeanUtils.copyProperties(sysUserImportDto, sysUserErrorExportDto);

                    String name = sysUserImportDto.getName();
                    String loginName = sysUserImportDto.getLoginName();
                    String phoneNumber = sysUserImportDto.getPhoneNumber();
                    String orgName = sysUserImportDto.getOrgName();
                    String roleName = sysUserImportDto.getRoleName();

                    StringJoiner errorStringJoiner = new StringJoiner(";");
                    String errorStringEmpty = ExcelUtil.checkExcelField(sysUserImportDto);
                    if (errorStringEmpty.length() > 0) {
                        errorStringJoiner.add(errorStringEmpty);
                    }
                    if (checkDtoMap.containsKey(loginName)) {
                        SysUserImportDto primaryCell = checkDtoMap.get(loginName);
                        String priName = primaryCell.getName();
                        String priPhoneNumber = primaryCell.getPhoneNumber();
                        String priOrgName = primaryCell.getOrgName();
                        if (!Objects.equals(priName, name)) {
                            errorStringJoiner.add("用户名/工号[" + loginName + "]的用户存在不同的姓名");
                        }
                        if (!Objects.equals(priPhoneNumber, phoneNumber)) {
                            errorStringJoiner.add("用户名/工号[" + loginName + "]的用户存在不同的手机号");
                        }
                        if (!Objects.equals(priOrgName, orgName)) {
                            errorStringJoiner.add("用户名/工号[" + loginName + "]的用户存在不同的组织架构");
                        }
                        String priRoleName = primaryCell.getRoleName();
                        if (SystemConstant.strNotNull(roleName)) {
                            priRoleName = priRoleName + SystemConstant.COMMA + roleName;
                            primaryCell.setRoleName(priRoleName);
                        }
                    } else {
                        checkDtoMap.put(loginName, sysUserImportDto);
                    }

                    if (StringUtils.isNotBlank(loginName) && !loginName.matches(SystemConstant.REGULAR_EXPRESSION_OF_CODE)) {
                        errorStringJoiner.add("用户名/工号[" + loginName + "]不符合规范");
                    }
                    if (StringUtils.isNotBlank(phoneNumber) && !phoneNumber.matches(SystemConstant.REGULAR_EXPRESSION_OF_PHONE)) {
                        errorStringJoiner.add("手机号[" + phoneNumber + "]不符合输入规范");
                    }
                    if (StringUtils.isNotBlank(phoneNumber)) {
                        if (phoneNumberMap.containsKey(phoneNumber)) {
                            errorStringJoiner.add("手机号[" + phoneNumber + "]在文件中重复");
                        } else {
                            phoneNumberMap.put(phoneNumber, y);
                        }
                    }
                    String errorString = errorStringJoiner.toString();
                    if (errorString.length() > 0) {
                        sysUserErrorExportDto.setErrorMsg(errorString);
                        errorDataList.add(sysUserErrorExportDto);
                        removeDataList.add(sysUserImportDto);
                    } else {
                        // 校验通过的数据
                        successInteger.getAndIncrement();
                    }
                }
                sysUserImportDtoList.removeAll(removeDataList);
            }
            if (!errorDataList.isEmpty()) {
                map.put(SystemConstant.ERROR_DATA_LIST, errorDataList);
            }
            map.put(SystemConstant.DATA_COUNT, totalInteger.get());
            map.put(SystemConstant.SUCCESS_DATA_COUNT, successInteger.get());
            map.put(SystemConstant.ERROR_DATA_COUNT, errorDataList.size());
            return finalExcelList;
        }, 2);
        return sysUserService.executeSysUserImportLogic(finalList, map);
    }

    @Transactional
    @Override
    public Map<String, Object> executeImportBasicClazzLogic(Map<String, Object> map) throws Exception {
        InputStream inputStream = (InputStream) map.get("inputStream");
        System.out.println(inputStream);
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(inputStream, Lists.newArrayList(BasicClazzImportDto.class, DescribeImportDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            List<ExcelError> excelErrorTemp = new ArrayList<>();
            Map<String, String> checkNameMap = new HashMap<>();
            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> basicClazzImportDtoList = excelMap.get(i);
                assert basicClazzImportDtoList != null;
                if (basicClazzImportDtoList.get(0) instanceof DescribeImportDto) {
                    continue;
                }
                for (int y = 0; y < Objects.requireNonNull(basicClazzImportDtoList).size(); y++) {
                    BasicClazzImportDto basicClazzImportDto = (BasicClazzImportDto) basicClazzImportDtoList.get(y);
                    String clazzName = basicClazzImportDto.getClazzName();

                    // 检验excel中
                    // 检验学号
                    if (checkNameMap.containsKey(clazzName)) {
                        throw ExceptionResultEnum.ERROR.exception("导入的excel中包含在重复的班级名称[" + clazzName + "]");
                    } else {
                        checkNameMap.put(clazzName, clazzName);
                    }

                    excelErrorTemp.addAll(ExcelUtil.checkExcelField(basicClazzImportDto, y, i));
                }
            }
            if (excelErrorTemp.size() > 0) {
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(excelErrorTemp));
            }
            return finalExcelList;
        }, 2);
        return basicClazzService.executeBasicClazzImportLogic(finalList, map);
    }


    @Override
    @Transactional
    public Map<String, Object> executeImportStatisticsLogic(Map<String, Object> map) throws Exception {
        InputStream inputStream = (InputStream) map.get("inputStream");
        Long examId = SystemConstant.convertIdToLong(String.valueOf(map.get("examId")));
        SysUser sysUser = (SysUser) map.get(SystemConstant.USER);

        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(inputStream, Lists.newArrayList(StatisticsImportDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            // 只允许导入一个sheet
            if (finalExcelList.size() > 1) {
                throw ExceptionResultEnum.ERROR.exception("excel中只允许有一个sheet");
            }
            if (finalExcelErrorList.size() > 0) {
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(finalExcelErrorList));
            }
            List<ExcelError> excelErrorTemp = new ArrayList<>();
            // 校验系统中是否存在
            Map<String, SysOrg> collegeOrgMap = new HashMap<>();
            Map<String, SysOrg> teachingRoomMap = new HashMap<>();
            Map<String, BasicCourse> courseMap = new HashMap<>();
            Map<String, DictionaryResult> clazzMap = new HashMap<>();
            String batchNo = SystemConstant.getNanoId();
            List<TCStatisticsTemp> tcStatisticsImportTempList = new ArrayList<>();

            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> statisticsTempList = excelMap.get(i);
                for (int y = 0; y < Objects.requireNonNull(statisticsTempList).size(); y++) {
                    StatisticsImportDto statisticsImportDto = (StatisticsImportDto) statisticsTempList.get(y);
                    SysOrg collegeOrg = this.validSysOrgExists(collegeOrgMap, statisticsImportDto.getCollegeName(), sysUser.getSchoolId(), excelErrorTemp, (y + 1), (i + 1), "开课学院");
                    SysOrg teachingRoomOrg = this.validSysOrgExists(teachingRoomMap, statisticsImportDto.getTeachingRoomName(), sysUser.getSchoolId(), excelErrorTemp, (y + 1), (i + 1), "开课学院");
                    BasicCourse basicCourse = this.validBasicCourseExists(courseMap, statisticsImportDto.getCourseName(), sysUser.getSchoolId(), statisticsImportDto.getCourseCode(), excelErrorTemp, (y + 1), (i + 1));
                    String teacherName = statisticsImportDto.getTeacherName();
                    String clazzNames = statisticsImportDto.getClazzName();
                    List<DictionaryResult> dictionaryResultList = new ArrayList<>();
                    for (String clazzName : clazzNames.split(",")) {
                        DictionaryResult clazz = this.validBasicClazzExists(clazzMap, clazzName, sysUser.getSchoolId(), excelErrorTemp, (y + 1), (i + 1));
                        dictionaryResultList.add(clazz);
                    }
                    String clazzIds = dictionaryResultList.stream().map(e -> String.valueOf(e.getId())).collect(Collectors.joining(","));


                    if (excelErrorTemp.size() == 0) {

                        TCStatisticsTemp tcStatistics = new TCStatisticsTemp(
                                examId,
                                collegeOrg.getId(),
                                collegeOrg.getName(),
                                teachingRoomOrg.getId(),
                                teachingRoomOrg.getName(),
                                basicCourse.getName(),
                                basicCourse.getCode(),
                                teacherName,
                                clazzIds,
                                clazzNames,
                                batchNo,
                                sysUser.getId());
                        tcStatisticsImportTempList.add(tcStatistics);
                    }
                }
            }
            if (excelErrorTemp.size() > 0) {
                List<String> errors = excelErrorTemp.stream().map(ExcelError::getExcelErrorType).collect(Collectors.toList());
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(errors));
            }
            //加入删除(根据考试id和当前登录人)
            tcStatisticsService.removeImportData(examId, sysUser.getId());
            tcStatisticsTempService.saveBatch(tcStatisticsImportTempList);
            map.put("dataCount", tcStatisticsImportTempList.size());
            tcStatisticsService.importJoinData(tcStatisticsImportTempList);
            tcStatisticsTempService.removeByIds(tcStatisticsImportTempList.stream().map(BaseEntity::getId).collect(Collectors.toSet()));
            return finalExcelList;
        }, 2);
        return map;
    }

    /**
     * 成绩导出
     *
     * @param map
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, Object> executeExportScoreLogic(Map<String, Object> map) throws Exception {
        ByteArrayOutputStream fos = null;
        try {
            SysUser sysUser = (SysUser) map.get(SystemConstant.USER);
            Long semesterId = null, examId = null, clazzId = null;
            String courseCode = null;
            semesterId = Objects.nonNull(map.get("semesterId")) ? Long.valueOf(map.get("semesterId").toString()) : null;
            examId = Objects.nonNull(map.get("examId")) ? Long.valueOf(map.get("examId").toString()) : null;
            clazzId = Objects.nonNull(map.get("clazzId")) ? Long.valueOf(map.get("clazzId").toString()) : null;
            courseCode = Objects.nonNull(map.get("courseCode")) ? (String) map.get("courseCode") : null;
            String servletPath = Objects.nonNull(map.get("servletPath")) ? (String) map.get("servletPath") : null;

            fos = new ByteArrayOutputStream();
            List<TSyncExamStudentScoreResult> tSyncExamStudentScoreResultList = tSyncExamStudentScoreService.export(sysUser, semesterId, examId, clazzId, courseCode, servletPath);
            ExcelUtil.excelMake(TSyncExamStudentScoreResult.class, tSyncExamStudentScoreResultList, fos);
            String excelExportFilePath = printCommonService.saveTaskAttachment(fos, (boolean) map.get(SystemConstant.OSS));
            map.computeIfAbsent("count", v -> Objects.isNull(tSyncExamStudentScoreResultList) ? 0 : tSyncExamStudentScoreResultList.size());
            TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
            tbTask.setResultFilePath(excelExportFilePath);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
        } finally {
            if (Objects.nonNull(fos)) {
                fos.flush();
                fos.close();
            }
        }
        return map;
    }

    /**
     * 成绩轨迹下载
     *
     * @param map
     * @return
     * @throws Exception
     */
    @Override
    @Transactional
    public Map<String, Object> executeDownloadScoreLogic(Map<String, Object> map) throws Exception {
        File zipFile = null;
        List<File> sourceFiles = null;
        List<TSyncExamStudentScore> errorTSyncExamStudentScoreList = null;
        boolean oss = dictionaryConfig.sysDomain().isOss();
        try {
            SysUser sysUser = (SysUser) map.get(SystemConstant.USER);
            Long semesterId = null, examId = null, clazzId = null;
            String courseCode = null;
            semesterId = Objects.nonNull(map.get("semesterId")) ? (Long) map.get("semesterId") : null;
            examId = Objects.nonNull(map.get("examId")) ? (Long) map.get("examId") : null;
            clazzId = Objects.nonNull(map.get("clazzId")) ? (Long) map.get("clazzId") : null;
            courseCode = Objects.nonNull(map.get("courseCode")) ? (String) map.get("courseCode") : null;

            String servletPath = Objects.nonNull(map.get("servletPath")) ? (String) map.get("servletPath") : null;

            List<TSyncExamStudentScoreResult> tSyncExamStudentScoreResultList = tSyncExamStudentScoreService.export(sysUser, semesterId, examId, clazzId, courseCode, servletPath);
            if (Objects.nonNull(tSyncExamStudentScoreResultList) && tSyncExamStudentScoreResultList.size() > 0) {
                List<TSyncExamStudentScore> tSyncExamStudentScoreList = new Gson().fromJson(JacksonUtil.parseJson(tSyncExamStudentScoreResultList), new TypeToken<List<TSyncExamStudentScore>>() {
                }.getType());
                Long time = System.currentTimeMillis();
                StringJoiner stringJoiner = new StringJoiner("");
                if (!oss && Objects.nonNull(dictionaryConfig.fssPublicDomain()) && !StringUtils.isBlank(dictionaryConfig.fssPublicDomain().getConfig()) && !dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT)) {
                    stringJoiner.add(dictionaryConfig.fssPublicDomain().getConfig()).add(File.separator);
                }
                stringJoiner = SystemConstant.getDirName(stringJoiner, UploadFileEnum.FILE, true);
                stringJoiner.add("成绩轨迹下载_" + time).add(SystemConstant.ZIP_PREFIX);

                String zipDirName = FileUtil.replaceSplit(stringJoiner.toString());
                zipFile = SystemConstant.getFileTempDirVar(SystemConstant.ZIP_PREFIX);

                sourceFiles = new LinkedList<>();
                List<TSyncExamStudentScore> updateTSyncExamStudentScoreList = new ArrayList<>();
                errorTSyncExamStudentScoreList = new ArrayList<>();
                for (TSyncExamStudentScore t : tSyncExamStudentScoreList) {
                    try {
                        boolean update = Objects.isNull(t.getTrajectoryUrls()) ? true : false;
                        t = tSyncExamStudentScoreService.createImageTrajectory(t, ImageTrajectoryEnum.DOWNLOAD, false, sysUser.getId());
                        if (Objects.nonNull(t.getTrajectoryFileList())) {
                            sourceFiles.addAll(t.getTrajectoryFileList());
                        } else {
                            errorTSyncExamStudentScoreList.add(t);
                        }
                        if (update || Objects.isNull(t.getTrajectoryUrls())) {
                            updateTSyncExamStudentScoreList.add(t);
                        }
                    } catch (Exception e) {
                        log.error(SystemConstant.LOG_ERROR, e);
                        errorTSyncExamStudentScoreList.add(t);
                    }
                }
                tSyncExamStudentScoreService.saveOrUpdateBatch(updateTSyncExamStudentScoreList);
                if (!sourceFiles.isEmpty()) {
                    JSONObject jsonObject = SystemConstant.createZip(zipFile, sourceFiles, zipDirName);
                    TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
                    tbTask.setResultFilePath(jsonObject.toJSONString());
                } else {
                    throw ExceptionResultEnum.ERROR.exception("阅卷方式未使用轨迹阅卷，无轨迹图");
                }
            } else {
                throw ExceptionResultEnum.ERROR.exception("没有可下载数据");
            }
            map.computeIfAbsent("count", v -> tSyncExamStudentScoreResultList.size());
            if (Objects.nonNull(errorTSyncExamStudentScoreList) && errorTSyncExamStudentScoreList.size() > 0) {
                StringJoiner stringJoiner = new StringJoiner("").add("\r\n");
                for (TSyncExamStudentScore t : errorTSyncExamStudentScoreList) {
                    stringJoiner.add("[").add(t.getName()).add(",").add(t.getExamNumber()).add("]\r\n");
                }
                List<TSyncExamStudentScore> finalErrorTSyncExamStudentScoreList = errorTSyncExamStudentScoreList;
                map.computeIfAbsent("error", v -> "其中未下载成功数据" + finalErrorTSyncExamStudentScoreList.size() + "条:" + stringJoiner.toString());
            }
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        } finally {
            if (Objects.nonNull(zipFile)) {
                zipFile.delete();
            }
            if (oss && !CollectionUtils.isEmpty(sourceFiles)) {
                for (File f : sourceFiles) {
                    f.delete();
                }
            }
        }
        return map;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, Object> executeDownloadPaperFileBatch(Map<String, Object> map) {
        TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
        DownloadPaperFileParam downloadPaperFileParam = (DownloadPaperFileParam) map.get("downloadPaperFileParam");
        Long semesterId = downloadPaperFileParam.getSemesterId();
        Long examId = downloadPaperFileParam.getExamId();
        Long orgId = downloadPaperFileParam.getOrgId();
        PaperFileDownloadContentEnum paperFileDownloadContent = downloadPaperFileParam.getPaperFileDownloadContentType();
        if (paperFileDownloadContent == null) {
            throw ExceptionResultEnum.ERROR.exception("请选择下载文件");
        }
        PaperFileDownloadExposureStatusEnum paperFileDownloadExposureStatus = downloadPaperFileParam.getPaperFileDownloadExposureStatus();
        if ((PaperFileDownloadContentEnum.ONLY_PAPER.equals(paperFileDownloadContent) || PaperFileDownloadContentEnum.PAPER_AND_CARD.equals(paperFileDownloadContent)) && Objects.isNull(paperFileDownloadExposureStatus)) {
            throw ExceptionResultEnum.ERROR.exception("请选择试卷状态");
        }
        Boolean namedByCourseInfo = downloadPaperFileParam.getNamedByCourseInfo();
        Boolean namedByPaperNumber = downloadPaperFileParam.getNamedByPaperNumber();
        Boolean namedByOriginalFile = downloadPaperFileParam.getNamedByOriginalFile();
        if (!namedByCourseInfo && !namedByPaperNumber && !namedByOriginalFile) {
            throw ExceptionResultEnum.ERROR.exception("请选择至少一种命名规则");
        }

        String courseName = downloadPaperFileParam.getCourseName();
        Set<Long> idSet = downloadPaperFileParam.getIdSet();

        File zipFile = null;
        String zipLocalRootPath = null;
        try {
            boolean oss = dictionaryConfig.sysDomain().isOss();
            StringJoiner stringJoiner = new StringJoiner("");
            if (!oss && Objects.nonNull(dictionaryConfig.fssPublicDomain()) && !StringUtils.isBlank(dictionaryConfig.fssPublicDomain().getConfig()) && !dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT)) {
                stringJoiner.add(dictionaryConfig.fssPublicDomain().getConfig()).add(File.separator);
            }
            stringJoiner = SystemConstant.getDirName(stringJoiner, UploadFileEnum.FILE, true);
            stringJoiner.add("下载管理_批量下载_" + System.currentTimeMillis()).add(SystemConstant.ZIP_PREFIX);

            String zipDirName = FileUtil.replaceSplit(stringJoiner.toString());
            zipFile = SystemConstant.getFileTempDirVarForZip(SystemConstant.getNanoId(), SystemConstant.ZIP_PREFIX);
            zipLocalRootPath = zipFile.getParent() + File.separator + System.currentTimeMillis();

            boolean downloadPaper = paperFileDownloadContent != null && (PaperFileDownloadContentEnum.ONLY_PAPER.equals(paperFileDownloadContent) || PaperFileDownloadContentEnum.PAPER_AND_CARD.equals(paperFileDownloadContent));
            boolean downloadCard = paperFileDownloadContent != null && (PaperFileDownloadContentEnum.ONLY_CARD.equals(paperFileDownloadContent) || PaperFileDownloadContentEnum.PAPER_AND_CARD.equals(paperFileDownloadContent));

            SysUser requestUser = (SysUser) map.get(SystemConstant.USER);
            DataPermissionRule dpr = null;
            if (CollectionUtils.isEmpty(idSet)) {
                dpr = basicRoleDataPermissionService.findDataPermission(653L, requestUser);
            }
            // 查询待下载数据
            List<ExamTaskDetailDto> examTaskDetailDtoList = downloadService.listExamQuery(semesterId, examId, orgId, courseName, idSet, dpr);
            List<ExamTaskPaperExportDto> examTaskPaperExportDtoList = new ArrayList<>();
            for (ExamTaskDetailDto examTaskDetailDto : examTaskDetailDtoList) {
                ExamTaskPaperExportDto examTaskPaperExportDto = new ExamTaskPaperExportDto(examTaskDetailDto);
                examTaskPaperExportDtoList.add(examTaskPaperExportDto);
                List<PaperInfoVo> paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetailDto.getPaperAttachmentIds());
                // 选择曝光卷型
                if (PaperFileDownloadExposureStatusEnum.EXPOSED_PAPER.equals(paperFileDownloadExposureStatus)) {
                    if (StringUtils.isBlank(examTaskDetailDto.getExposedPaperType())) {
                        examTaskPaperExportDto.setResult("没有曝光卷型，下载失败");
                        continue;
                    } else {
                        paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetailDto.getPaperAttachmentIds(), examTaskDetailDto.getExposedPaperType());
                    }
                }
                // 选择未曝光卷型
                if (PaperFileDownloadExposureStatusEnum.UNEXPOSED_PAPER.equals(paperFileDownloadExposureStatus)) {
                    if (StringUtils.isBlank(examTaskDetailDto.getUnexposedPaperType())) {
                        examTaskPaperExportDto.setResult("没有未曝光卷型，下载失败");
                        continue;
                    } else {
                        paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetailDto.getPaperAttachmentIds(), examTaskDetailDto.getUnexposedPaperType());
                    }
                }

                if (CollectionUtils.isEmpty(paperInfoVoList)) {
                    examTaskPaperExportDto.setResult("所选试卷状态未查询到上传的试卷信息，下载失败");
                    continue;
                }

                // 命题规则
                StringJoiner sj = new StringJoiner(SystemConstant.HYPHEN);
                if (namedByCourseInfo) {
                    sj.add(examTaskDetailDto.getCourseName()).add(examTaskDetailDto.getCourseCode());
                }
                if (namedByPaperNumber) {
                    sj.add(examTaskDetailDto.getPaperNumber());
                }
                String fileNamePath = sj.toString();

                // 目录规则（课程名称（课程代码）/试卷编号）
                String secondPath = examTaskDetailDto.getCourseName() + SystemConstant.HYPHEN + examTaskDetailDto.getCourseCode() + File.separator + examTaskDetailDto.getPaperNumber();
                for (PaperInfoVo paperInfoVo : paperInfoVoList) {
                    // 下载试卷
                    if (downloadPaper) {
                        // 不管什么命名规则，默认都加上卷型前缀
                        fileNamePath = "试卷" + SystemConstant.HYPHEN + paperInfoVo.getName() + SystemConstant.HYPHEN + fileNamePath;
                        // 原文件名
                        if (namedByOriginalFile) {
                            fileNamePath = fileNamePath + File.separator + paperInfoVo.getFilename();
                        } else {
                            fileNamePath = fileNamePath + "." + FilenameUtils.getExtension(paperInfoVo.getFilename());
                        }
                        Long attachmentId = Long.valueOf(paperInfoVo.getAttachmentId());
                        if (Objects.nonNull(attachmentId)) {
                            BasicAttachment attachment = basicAttachmentService.getById(attachmentId);
                            if (Objects.nonNull(attachment)) {
                                String fileName = StringUtils.deleteWhitespace(zipLocalRootPath + File.separator + secondPath + File.separator + fileNamePath);
                                fileUploadService.downloadFile(attachment, fileName);
                            }
                        }
                    }
                    // 下载题卡
                    if (downloadCard) {
                        Long cardId = Long.valueOf(paperInfoVo.getCardId());
                        ExamCard examCard = examCardService.getById(cardId);
                        Optional.ofNullable(examCard).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("找不到答题卡"));

                        String cardHtmlPath = zipLocalRootPath + File.separator + secondPath + File.separator + "题卡" + SystemConstant.HYPHEN + paperInfoVo.getName() + SystemConstant.HYPHEN + fileNamePath;
                        String cardPdfPath = zipLocalRootPath + File.separator + secondPath + File.separator + "题卡" + SystemConstant.HYPHEN + paperInfoVo.getName() + SystemConstant.HYPHEN + fileNamePath;
                        // 原文件名
                        if (namedByOriginalFile) {
                            cardHtmlPath = StringUtils.deleteWhitespace(cardHtmlPath + SystemConstant.HYPHEN + examCard.getTitle() + SystemConstant.HTML_PREFIX);
                            cardPdfPath = StringUtils.deleteWhitespace(cardPdfPath + SystemConstant.HYPHEN + examCard.getTitle() + SystemConstant.PDF_PREFIX);
                        } else {
                            cardHtmlPath = StringUtils.deleteWhitespace(cardHtmlPath + SystemConstant.HTML_PREFIX);
                            cardPdfPath = StringUtils.deleteWhitespace(cardPdfPath + SystemConstant.PDF_PREFIX);
                        }

                        // html
                        File localFile = new File(cardHtmlPath);
                        if (!localFile.exists()) {
                            localFile.getParentFile().mkdirs();
                            localFile.createNewFile();
                        }
                        // 通用题卡
                        String htmlContent = createPdfNewUtil.replaceBlankHtmlContent(examCard.getHtmlContent());
                        // 生成html文件
                        FileCopyUtils.copy(htmlContent.getBytes(StandardCharsets.UTF_8), localFile);
                        // 转pdf文件
                        File file = new File(cardPdfPath);
                        if (!file.exists()) {
                            file.getParentFile().mkdirs();
                            file.createNewFile();
                        }
                        HtmlToPdfUtil.convert(cardHtmlPath, cardPdfPath, PageSizeEnum.A3);
                    }
                    examTaskPaperExportDto.setResult("下载成功");
                }
            }

            // 导出文件excel
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ExcelUtil.excelMake(ExamTaskPaperExportDto.class, examTaskPaperExportDtoList, outputStream);
            InputStream in = new ByteArrayInputStream(outputStream.toByteArray());

            String excelLocalPath = zipLocalRootPath + File.separator + "下载清单" + "." + SystemConstant.XLSX;
            FileUtils.copyInputStreamToFile(in, new File(excelLocalPath));
            // 所有试卷
            JSONObject jsonObject = SystemConstant.createZip(zipFile, zipLocalRootPath, zipDirName);
            tbTask.setResultFilePath(jsonObject.toJSONString());
            map.put("count", examTaskDetailDtoList.size());
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
        } finally {
            if (Objects.nonNull(zipFile)) {
                zipFile.delete();
            }
            if (Objects.nonNull(zipLocalRootPath)) {
                ConvertUtil.delFolder(zipLocalRootPath);
            }
        }
        return map;
    }

    @Transactional
    @Override
    public Map<String, Object> executeImportBasicExamStudentLogic(Map<String, Object> map) throws Exception {
        SysUser requestUser = (SysUser) map.get(SystemConstant.USER);
        InputStream inputStream = (InputStream) map.get("inputStream");
        Long examId = SystemConstant.convertIdToLong(String.valueOf(map.get("examId")));
        Long schoolId = requestUser.getSchoolId();
        BasicExam basicExam = basicExamService.getById(examId);
        if (Objects.isNull(basicExam)) {
            throw ExceptionResultEnum.ERROR.exception("考试不存在");
        }
        Long semesterId = SystemConstant.convertIdToLong(basicExam.getSemesterId());

        List<BasicExamStudent> dbData = basicExamStudentService.list(new QueryWrapper<BasicExamStudent>().lambda()
                .select(BasicExamStudent::getId, BasicExamStudent::getExamId, BasicExamStudent::getCourseCode,
                        BasicExamStudent::getStudentCode, BasicExamStudent::getPaperNumber).eq(BasicExamStudent::getSchoolId, schoolId).eq(BasicExamStudent::getExamId, examId));

        // 数据库中学生学号和id对应关系
        Map<String, Long> studentCodeIdMap = dbData.stream().collect(Collectors.toMap(
                k -> k.getExamId() + SystemConstant.HYPHEN + k.getCourseCode() + SystemConstant.HYPHEN + k.getStudentCode(),
                BaseEntity::getId));
        Map<String, String> paperNumberCourseCodeMap = dbData.stream().filter(e -> SystemConstant.strNotNull(e.getPaperNumber())).collect(
                Collectors.toMap(BasicExamStudent::getPaperNumber, BasicExamStudent::getCourseCode, (o1, o2) -> o1));

        // excel中学生学号和学生字典对象关系
        Map<String, BasicExamStudent> studentCodeObjMap = new HashMap<>();
        AtomicInteger successInteger = new AtomicInteger(0);
        AtomicInteger exceptionInteger = new AtomicInteger(0);

        ExcelResult<BasicExamStudentDto> excelResult = ConvertUtil.analyzeExcel(inputStream, BasicExamStudentDto.class,
                true, 1);
        List<BasicExamStudentDto> basicExamStudentDtoList = excelResult.getDatasource();
        List<String> oldPaperNumberList = new ArrayList<>();
        for (BasicExamStudentDto basicExamStudentDto : basicExamStudentDtoList) {
            List<String> errorMsgList = new ArrayList<>();
            // 行索引
            String courseCode = basicExamStudentDto.getCourseCode();
            String studentName = basicExamStudentDto.getStudentName();
            String studentCode = basicExamStudentDto.getStudentCode();
            String college = basicExamStudentDto.getCollege();
            String major = basicExamStudentDto.getMajor();
            String teachClazz = basicExamStudentDto.getTeachClazz();
            String paperNumber = basicExamStudentDto.getPaperNumber();
            String teacherName = basicExamStudentDto.getTeacherName();
            String teacherCode = basicExamStudentDto.getTeacherCode();
            String examDate = basicExamStudentDto.getExamDate();
            String examTime = basicExamStudentDto.getExamTime();
            String examPlace = basicExamStudentDto.getExamPlace();
            String examRoom = basicExamStudentDto.getExamRoom();

            Long examStartTime = null;
            Long examEndTime = null;
            try {
                if (SystemConstant.strNotNull(examDate) && SystemConstant.strNotNull(examTime)) {
                    Map<String, Object> timeMap = ConvertUtil.analyzeStartAndEndTime(examDate, examTime);
                    examStartTime = SystemConstant.convertIdToLong(String.valueOf(timeMap.get("startTime")));
                    examEndTime = SystemConstant.convertIdToLong(String.valueOf(timeMap.get("endTime")));
                }
            } catch (Exception e) {
                errorMsgList.add(e.getMessage());
            }

            if (SystemConstant.strNotNull(paperNumber) && SystemConstant.strNotNull(courseCode)) {
                if (paperNumberCourseCodeMap.containsKey(paperNumber)) {
                    if (!Objects.equals(paperNumberCourseCodeMap.get(paperNumber), courseCode)) {
                        errorMsgList.add(String.format("试卷编号和课程编号不匹配:试卷编号[%s]已经存在于课程[%s]下", paperNumber,
                                paperNumberCourseCodeMap.get(paperNumber)));
                    }
                } else {
                    paperNumberCourseCodeMap.put(paperNumber, courseCode);
                }
            }

            String key = examId + SystemConstant.HYPHEN + courseCode + SystemConstant.HYPHEN + studentCode;
            Long id = null;
            if (studentCodeIdMap.containsKey(key)) {
                id = studentCodeIdMap.get(key);
            }
            BasicExamStudentParam basicExamStudentParam = new BasicExamStudentParam();
            basicExamStudentParam.setId(id);
            basicExamStudentParam.setSemesterId(semesterId);
            basicExamStudentParam.setExamId(examId);
            basicExamStudentParam.setCourseCode(courseCode);
            basicExamStudentParam.setStudentName(studentName);
            basicExamStudentParam.setStudentCode(studentCode);
            basicExamStudentParam.setCollege(college);
            basicExamStudentParam.setMajor(major);
            basicExamStudentParam.setTeachClazz(teachClazz);
            basicExamStudentParam.setPaperNumber(paperNumber);
            basicExamStudentParam.setTeacherName(teacherName);
            basicExamStudentParam.setTeacherCode(teacherCode);
            basicExamStudentParam.setExamStartTime(examStartTime);
            basicExamStudentParam.setExamEndTime(examEndTime);
            basicExamStudentParam.setExamPlace(examPlace);
            basicExamStudentParam.setExamRoom(examRoom);
            try {
                BasicExamStudent basicExamStudent = basicExamStudentService.editEntityHelp(basicExamStudentParam,
                        basicExam, oldPaperNumberList, requestUser);
                // 考试课程下学号重复 后面的替换前面的
                studentCodeObjMap.put(key, basicExamStudent);
            } catch (Exception e) {
                errorMsgList.add(e.getMessage());
            }
            String excelAnalyzeError = basicExamStudentDto.getErrorMsg();
            if (SystemConstant.strNotNull(excelAnalyzeError)) {
                exceptionInteger.getAndIncrement();
                if (CollectionUtils.isNotEmpty(errorMsgList)) {
                    basicExamStudentDto.setErrorMsg(excelAnalyzeError + ";" + String.join(";", errorMsgList));
                }
            } else if (CollectionUtils.isNotEmpty(errorMsgList)) {
                exceptionInteger.getAndIncrement();
                basicExamStudentDto.setErrorMsg(String.join(";", errorMsgList));
            } else {
                successInteger.getAndIncrement();
            }
        }

        if (basicExamStudentDtoList.stream().anyMatch(e -> SystemConstant.strNotNull(e.getErrorMsg()))) {
            map.put(SystemConstant.ERROR_DATA_LIST, basicExamStudentDtoList);
        } else {
            List<BasicExamStudent> basicExamStudentList = new ArrayList<>(studentCodeObjMap.values());
            map.put(SystemConstant.DATASOURCE, basicExamStudentList);
        }
        map.put(SystemConstant.DATA_COUNT, basicExamStudentDtoList.size());
        map.put(SystemConstant.SUCCESS_DATA_COUNT, successInteger.get());
        map.put(SystemConstant.ERROR_DATA_COUNT, exceptionInteger.get());
        map.put("oldPaperNumberList", oldPaperNumberList);
        return map;
    }

    @Transactional
    @Override
    public Map<String, Object> executeImportMarkStudentLogic(Map<String, Object> map) throws Exception {
        InputStream inputStream = (InputStream) map.get("inputStream");
        // 检验课程编号和课程名称对应
        Map<String, String> courseCodeNameCheckMap = new HashMap<>();
        // 检验学号在课程下唯一
        Map<String, List<String>> checkStudentCodeMap = new HashMap<>();
        // 检验同一个课程、试卷下的班级信息要么全填要么全不填
        Map<String, Boolean> checkClazzMap = new HashMap<>();

        List<MarkStudentImportDto> datasource = new ArrayList<>();
        AtomicInteger successInteger = new AtomicInteger(0);
        AtomicInteger exceptionInteger = new AtomicInteger(0);

        ExcelUtil.excelReader(inputStream, Lists.newArrayList(MarkStudentImportDto.class),
                (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {

                    for (int i = 0; i < finalExcelList.size(); i++) {
                        LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                        List<Object> MarkStudentImportDtoList = excelMap.get(i);
                        // 无数据，跳过
                        if (CollectionUtils.isEmpty(MarkStudentImportDtoList)) {
                            continue;
                        }
                        for (int y = 0; y < Objects.requireNonNull(MarkStudentImportDtoList).size(); y++) {
                            List<String> errorMsgList = new ArrayList<>();
                            // 行索引
                            MarkStudentImportDto markStudentImportDto = (MarkStudentImportDto) MarkStudentImportDtoList.get(
                                    y);
                            String courseName = StringUtils.trimToNull(markStudentImportDto.getCourseName());
                            String courseCode = StringUtils.trimToNull(markStudentImportDto.getCourseCode());
                            String paperNumber = StringUtils.trimToNull(markStudentImportDto.getPaperNumber());
                            String studentName = StringUtils.trimToNull(markStudentImportDto.getStudentName());
                            String studentCode = StringUtils.trimToNull(markStudentImportDto.getStudentCode());
                            String college = StringUtils.trimToNull(markStudentImportDto.getCollege());
                            String major = StringUtils.trimToNull(markStudentImportDto.getMajor());
                            String clazz = StringUtils.trimToNull(markStudentImportDto.getClazz());
                            String examDate = StringUtils.trimToNull(markStudentImportDto.getExamDate());
                            String examTime = StringUtils.trimToNull(markStudentImportDto.getExamTime());
                            String examPlace = StringUtils.trimToNull(markStudentImportDto.getExamPlace());
                            String examRoom = StringUtils.trimToNull(markStudentImportDto.getExamRoom());

                            // 本行全部列为空，跳过
                            if (StringUtils.isAllBlank(courseName, courseCode, paperNumber, studentName, studentCode,
                                    college, major, clazz, examDate, examTime, examDate, examPlace, examRoom)) {
                                continue;
                            }
                            // 课程编号和课程名称一致
                            if (courseCodeNameCheckMap.containsKey(courseCode)) {
                                if (!courseCodeNameCheckMap.get(courseCode).equals(courseName)) {
                                    errorMsgList.add("同一个课程编号对应了多个课程名称");
                                }
                            } else {
                                courseCodeNameCheckMap.put(courseCode, courseName);
                            }

                            // 学号在课程下唯一
                            if (checkStudentCodeMap.containsKey(courseCode)) {
                                List<String> studentCodeList = checkStudentCodeMap.get(courseCode);
                                if (studentCodeList.contains(studentCode)) {
                                    errorMsgList.add("学号重复（学号需要在课程下唯一）");
                                } else {
                                    studentCodeList.add(studentCode);
                                }
                            } else {
                                List<String> studentCodeList = new ArrayList<>();
                                studentCodeList.add(studentCode);
                                checkStudentCodeMap.put(courseCode, studentCodeList);
                            }

                            // 同一个课程、试卷下的班级信息要么全填要么全不填
                            String clazzCheckKey = courseCode + SystemConstant.HYPHEN + paperNumber;
                            if (checkClazzMap.containsKey(clazzCheckKey)) {
                                Boolean hasData = checkClazzMap.get(clazzCheckKey);
                                if (SystemConstant.strNotNull(clazz) != hasData) {
                                    errorMsgList.add("班级信息异常（同一个课程、试卷下的班级信息要么全填要么全不填）");
                                }
                            } else {
                                checkClazzMap.put(clazzCheckKey, SystemConstant.strNotNull(clazz));
                            }

                            try {
                                if (SystemConstant.strNotNull(examDate) && SystemConstant.strNotNull(examTime)) {
                                    ConvertUtil.analyzeStartAndEndTime(examDate, examTime);
                                }
                            } catch (Exception e) {
                                errorMsgList.add(e.getMessage());
                            }

                            if (CollectionUtils.isNotEmpty(errorMsgList)) {
                                markStudentImportDto.setErrorMsg(String.join(";", errorMsgList));
                                exceptionInteger.getAndIncrement();
                            } else {
                                successInteger.getAndIncrement();
                            }
                            datasource.add(markStudentImportDto);
                        }
                    }

                    if (datasource.stream().anyMatch(e -> SystemConstant.strNotNull(e.getErrorMsg()))) {
                        map.put(SystemConstant.ERROR_DATA_LIST, datasource);
                    } else {
                        map.put(SystemConstant.DATASOURCE, datasource);
                    }
                    map.put(SystemConstant.DATA_COUNT, datasource.size());
                    map.put(SystemConstant.SUCCESS_DATA_COUNT, successInteger.get());
                    map.put(SystemConstant.ERROR_DATA_COUNT, exceptionInteger.get());
                    return finalExcelList;
                }, 2);
        return map;
    }

    @Transactional
    @Override
    public void saveMarkStudent(Long examId, List<BasicExamStudent> basicExamStudentList) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        Long requestUserId = requestUser.getId();

        Map<String, List<BasicExamStudent>> stringMap = basicExamStudentList.stream().collect(Collectors.groupingBy(BasicExamStudent::getPaperNumber));

        for (Map.Entry<String, List<BasicExamStudent>> entry : stringMap.entrySet()) {
            BasicExamStudent basicExamStudent = entry.getValue().get(0);
            MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, entry.getKey());
            if (markPaper == null) {
                BasicCourse basicCourse = basicCourseService.getByCode(basicExamStudent.getCourseCode());
                markPaper = new MarkPaper(examId, basicExamStudent.getCourseCode(), basicCourse.getName(), entry.getKey(), String.valueOf(System.currentTimeMillis()), requestUserId, SystemConstant.DEFAULT_PAPER_TYPE_A);
                markPaperService.save(markPaper);
            }
            Set<String> secretNumberSet = new HashSet<>();
            MarkPaper finalMarkPaper = markPaper;
            entry.getValue().forEach(m -> markStudentService.updateByBasicExamStudent(m, finalMarkPaper, secretNumberSet));
            markPaperService.updateStudentCountByExamIdAndPaperNumberAndPaperType(examId, entry.getKey(), SystemConstant.DEFAULT_PAPER_TYPE_A);
        }
    }

    @Transactional
    @Override
    public Map<String, Object> executeImportObjectiveStructLogic(Map<String, Object> map) throws Exception {
        SysUser requestUser = (SysUser) map.get(SystemConstant.USER);
        InputStream inputStream = (InputStream) map.get("inputStream");
        Long examId = SystemConstant.convertIdToLong(String.valueOf(map.get("examId")));
        Long schoolId = requestUser.getSchoolId();
        BasicExam basicExam = basicExamService.getById(examId);
        if (Objects.isNull(basicExam)) {
            throw ExceptionResultEnum.ERROR.exception("考试不存在");
        }
        if (!ExamModelEnum.MODEL4.equals(basicExam.getExamModel())) {
            throw ExceptionResultEnum.ERROR.exception("仅针对模式4的考试可以导入");
        }
        // 课程编号名称校验
        Map<String, String> courseCheckMap = basicCourseService.list(
                        new QueryWrapper<BasicCourse>().lambda().eq(BasicCourse::getSchoolId, schoolId)).stream()
                .collect(Collectors.toMap(BasicCourse::getCode, BasicCourse::getName));
        List<MarkPaper> markPaperList = markPaperService.list(
                new QueryWrapper<MarkPaper>().lambda().eq(MarkPaper::getExamId, examId));
        // 题号重复校验
        // 导入的客观题题号集合
        Set<String> questionNumberCheckSet = new HashSet<>();
        // 客观题选项个数在大题下一致性校验
        Map<String, Integer> questionOptionCountMap = new HashMap<>();
        // 该试卷已存在的主观题题号集合
        Set<String> dbSubjectiveQuestionNumberSet = markQuestionService.list(
                        new QueryWrapper<MarkQuestion>().lambda().eq(MarkQuestion::getExamId, examId)
                                .eq(MarkQuestion::getObjective, false)).stream()
                .map(e -> e.getPaperNumber() + SystemConstant.HYPHEN + e.getMainNumber() + SystemConstant.HYPHEN + e.getSubNumber()).collect(Collectors.toSet());

        ExcelResult<ObjectiveStructDto> excelResult = ConvertUtil.analyzeExcel(inputStream, ObjectiveStructDto.class,
                true, 0);
        List<ObjectiveStructDto> objectiveStructDtoList = excelResult.getDatasource();
        List<MarkQuestion> markQuestionList = new ArrayList<>();
        for (ObjectiveStructDto objectiveStructDto : objectiveStructDtoList) {
            List<String> logicErrorList = new ArrayList<>();
            // 公共异常提示
            String logicErrorCommonNotice = "";

            String courseCode = objectiveStructDto.getCourseCode();
            String courseName = objectiveStructDto.getCourseName();
            String paperNumber = objectiveStructDto.getPaperNumber();
            String mainTitle = objectiveStructDto.getMainTitle();
            Integer mainNumber = objectiveStructDto.getMainNumber();
            Integer subNumber = objectiveStructDto.getSubNumber();
            String answer = objectiveStructDto.getAnswer();
            Integer optionCount = objectiveStructDto.getOptionCount();
            Double totalScore = objectiveStructDto.getTotalScore();
            Integer questionType = objectiveStructDto.getQuestionType();
            String errorMsg = objectiveStructDto.getErrorMsg();

            // 1.题号重复校验
            if (SystemConstant.strNotNull(paperNumber) && mainNumber != null && subNumber != null) {
                logicErrorCommonNotice = String.format("课程代码为[%s],试卷编号为[%s],大题号为[%s],小题号为[%s]的数据异常 : ", courseCode, paperNumber, mainNumber,
                        subNumber);
                String questionNumberKey = courseCode + SystemConstant.HYPHEN + paperNumber + SystemConstant.HYPHEN + mainNumber + SystemConstant.HYPHEN + subNumber;
                if (questionNumberCheckSet.contains(questionNumberKey)) {
                    logicErrorList.add("excel中题号重复");
                } else {
                    questionNumberCheckSet.add(questionNumberKey);
                }
                if (dbSubjectiveQuestionNumberSet.contains(questionNumberKey)) {
                    logicErrorList.add("该题号已存在主观题中");
                }
                String mainQuestionKey = paperNumber + SystemConstant.HYPHEN + mainNumber;
                if (questionOptionCountMap.containsKey(mainQuestionKey)) {
                    if (!Objects.equals(questionOptionCountMap.get(mainQuestionKey), optionCount)) {
                        logicErrorList.add("选项个数和该大题中的其他小题不一致");
                    }
                } else {
                    questionOptionCountMap.put(mainQuestionKey, optionCount);
                }
            }

            // 2.校验课程编号,课程名称,试卷在考试下是否存在
            if (SystemConstant.strNotNull(courseCode) && SystemConstant.strNotNull(courseName) && SystemConstant.strNotNull(paperNumber)) {
                if (!courseCheckMap.containsKey(courseCode)) {
                    logicErrorList.add("课程编号不存在");
                } else if (!courseCheckMap.get(courseCode).equals(courseName)) {
                    logicErrorList.add("课程编号名称不匹配");
                }
                if (markPaperList.stream().noneMatch(m -> m.getPaperNumber().equals(paperNumber) && m.getCourseCode().equals(courseCode))) {
                    logicErrorList.add("考试课程中不存在该试卷，请先创建试卷");
                }
            }
            // 3.选项越界校验
            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(e.getMessage());
            }

            // 答案选项越界校验
            List<String> answerList = Arrays.asList(answer.split(""));
            if (CollectionUtils.isNotEmpty(answerList)) {
                for (String cell : answerList) {
                    String outOfBoundsException = String.format("答案[%s]错误。答案只能从[%s]中选择", cell, String.join("", optionScope));
                    try {
                        OptionsEnum optionsEnum = OptionsEnum.getByCode(cell);
                        int optionIndex = optionsEnum.getIndex();
                        if (optionIndex > optionCount) {
                            // 答案选项超出范围
                            logicErrorList.add(outOfBoundsException);
                        }
                    } catch (Exception e) {
                        logicErrorList.add(outOfBoundsException);
                    }
                }
            } else {
                logicErrorList.add("答案必填");
            }

            // 4.判断答案是否符合题型
            if (questionType != null && SystemConstant.strNotNull(answer)) {
                switch (questionType) {
                    case 1:
                        if (answer.length() > 1) {
                            logicErrorList.add("单选题答案只能有一个");
                        }
                        break;
                    case 2:
                        break;
                    case 3:
                        if (optionCount != 2) {
                            logicErrorList.add("判断题[选项个数]只能为2");
                        }
                        break;
                    default:
                        logicErrorList.add("[题型(1-单选，2-多选，3-判断)]必须从1、2、3中填写");
                }
            }

            // 整理异常信息
            if (CollectionUtils.isNotEmpty(logicErrorList)) {
                if (SystemConstant.strNotNull(errorMsg)) {
                    errorMsg = errorMsg + "、" + logicErrorCommonNotice + String.join("、", logicErrorList);
                } else {
                    errorMsg = logicErrorCommonNotice + String.join("、", logicErrorList);
                }
                objectiveStructDto.setErrorMsg(errorMsg);
            } else {
                MarkQuestion markQuestion = new MarkQuestion();
                markQuestion.setExamId(examId);
                markQuestion.setPaperNumber(paperNumber);
                markQuestion.setPaperType(OptionsEnum.A.getCode());
                markQuestion.setObjective(true);
                markQuestion.setMainNumber(mainNumber);
                markQuestion.setSubNumber(subNumber);
                markQuestion.setMainTitle(mainTitle);
                markQuestion.setAnswer(answer);
                markQuestion.setOptionCount(optionCount);
                markQuestion.setTotalScore(totalScore);
                markQuestion.setObjectivePolicy(ObjectivePolicy.NONE);
                markQuestion.setQuestionType(questionType);
                markQuestionList.add(markQuestion);
            }
        }

        if (objectiveStructDtoList.stream().anyMatch(e -> SystemConstant.strNotNull(e.getErrorMsg()))) {
            // 抛出excel解析异常
            throw ExceptionResultEnum.ERROR.exception(
                    objectiveStructDtoList.stream().map(ObjectiveStructDto::getErrorMsg).filter(SystemConstant::strNotNull).collect(Collectors.joining(System.lineSeparator())));
        } else {
            // 保存客观题结构
            Map<String, List<MarkQuestion>> markQuestionMap = markQuestionList.stream().collect(Collectors.groupingBy(MarkQuestion::getPaperNumber));
            markQuestionMap.forEach((k, v) -> {
                // 优先删除该试卷编号下的客观题
                markQuestionService.remove(new QueryWrapper<MarkQuestion>().lambda().eq(MarkQuestion::getExamId, examId)
                        .eq(MarkQuestion::getPaperNumber, k).eq(MarkQuestion::getObjective, true));
                // 保存客观题
                markQuestionService.saveBatch(v);
                markQuestionService.updateMarkPaperScore(examId, k, null);
            });
        }
        return map;
    }

    @Transactional
    @Override
    public Map<String, Object> executeImportSubjectiveStructLogic(Map<String, Object> map) throws Exception {
        SysUser requestUser = (SysUser) map.get(SystemConstant.USER);
        InputStream inputStream = (InputStream) map.get("inputStream");
        Long examId = SystemConstant.convertIdToLong(String.valueOf(map.get("examId")));
        Long schoolId = requestUser.getSchoolId();
        BasicExam basicExam = basicExamService.getById(examId);
        if (Objects.isNull(basicExam)) {
            throw ExceptionResultEnum.ERROR.exception("考试不存在");
        }
        if (!ExamModelEnum.MODEL4.equals(basicExam.getExamModel())) {
            throw ExceptionResultEnum.ERROR.exception("仅针对模式4的考试可以导入");
        }
        SysRole markerRole = sysRoleService.getOne(
                new QueryWrapper<SysRole>().lambda().eq(SysRole::getType, RoleTypeEnum.MARKER));
        if (Objects.isNull(markerRole)) {
            throw ExceptionResultEnum.ERROR.exception("缺少评卷员角色");
        }

        // 课程编号名称校验
        Map<String, String> courseCheckMap = basicCourseService.list(
                        new QueryWrapper<BasicCourse>().lambda().eq(BasicCourse::getSchoolId, schoolId)).stream()
                .collect(Collectors.toMap(BasicCourse::getCode, BasicCourse::getName));
        List<MarkPaper> markPaperList = markPaperService.list(
                new QueryWrapper<MarkPaper>().lambda().eq(MarkPaper::getExamId, examId));
        // 题号重复校验
        // 导入的客观题题号集合
        Set<String> questionNumberCheckSet = new HashSet<>();
        // 该试卷已存在的客观题题号集合
        Set<String> dbObjectiveQuestionNumberSet = markQuestionService.list(
                        new QueryWrapper<MarkQuestion>().lambda().eq(MarkQuestion::getExamId, examId)
                                .eq(MarkQuestion::getObjective, true)).stream()
                .map(e -> e.getPaperNumber() + SystemConstant.HYPHEN + e.getMainNumber() + SystemConstant.HYPHEN + e.getSubNumber()).collect(Collectors.toSet());

        ExcelResult<SubjectiveStructDto> excelResult = ConvertUtil.analyzeExcel(inputStream, SubjectiveStructDto.class,
                true, 0);
        List<SubjectiveStructDto> subjectiveStructDtoList = excelResult.getDatasource();
        for (SubjectiveStructDto subjectiveStructDto : subjectiveStructDtoList) {
            List<String> excelErrorList = new ArrayList<>();
            // 公共异常提示
            String excelErrorCommonNotice = "";

            String courseCode = subjectiveStructDto.getCourseCode();
            String courseName = subjectiveStructDto.getCourseName();
            String paperNumber = subjectiveStructDto.getPaperNumber();
            Integer mainNumber = subjectiveStructDto.getMainNumber();
            Integer subNumber = subjectiveStructDto.getSubNumber();
            Integer markerCount = subjectiveStructDto.getMarkerCount();
            String markerCodes = subjectiveStructDto.getMarkerCodes();
            String errorMsg = subjectiveStructDto.getErrorMsg();

            // 1.题号重复校验
            if (SystemConstant.strNotNull(paperNumber) && mainNumber != null && subNumber != null) {
                excelErrorCommonNotice = String.format("课程代码为[%s],试卷编号为[%s],大题号为[%s],小题号为[%s]的数据异常 : ", courseCode, paperNumber, mainNumber,
                        subNumber);
                String questionNumberKey = courseCode + SystemConstant.HYPHEN + paperNumber + SystemConstant.HYPHEN + mainNumber + SystemConstant.HYPHEN + subNumber;
                if (questionNumberCheckSet.contains(questionNumberKey)) {
                    excelErrorList.add("excel中题号重复");
                } else {
                    questionNumberCheckSet.add(questionNumberKey);
                }
                if (dbObjectiveQuestionNumberSet.contains(questionNumberKey)) {
                    excelErrorList.add("该题号已存在客观题中");
                }
            }

            // 2.校验课程代码,课程名称,试卷在考试下是否存在
            if (SystemConstant.strNotNull(courseCode) && SystemConstant.strNotNull(courseName) && SystemConstant.strNotNull(paperNumber)) {
                if (!courseCheckMap.containsKey(courseCode)) {
                    excelErrorList.add("课程代码不存在");
                } else if (!courseCheckMap.get(courseCode).equals(courseName)) {
                    excelErrorList.add("课程代码名称不匹配");
                }
                if (markPaperList.stream().noneMatch(m -> m.getPaperNumber().equals(paperNumber) && m.getCourseCode().equals(courseCode))) {
                    excelErrorList.add("考试课程中不存在该试卷，请先创建试卷");
                }
            }

            // 3.评卷员校验
            if (Objects.isNull(markerCount) && Objects.isNull(markerCodes)) {
                excelErrorList.add("[评卷员数量（系统生成）]和[绑定工号（英文逗号分隔）]不能同时为空");
            }

            // 整理异常信息
            if (CollectionUtils.isNotEmpty(excelErrorList)) {
                if (SystemConstant.strNotNull(errorMsg)) {
                    errorMsg = errorMsg + "、" + excelErrorCommonNotice + String.join("、", excelErrorList);
                } else {
                    errorMsg = excelErrorCommonNotice + String.join("、", excelErrorList);
                }
                subjectiveStructDto.setErrorMsg(errorMsg);
            }
        }
        if (subjectiveStructDtoList.stream().anyMatch(e -> SystemConstant.strNotNull(e.getErrorMsg()))) {
            // 抛出excel解析异常
            throw ExceptionResultEnum.ERROR.exception(
                    subjectiveStructDtoList.stream().map(SubjectiveStructDto::getErrorMsg).filter(SystemConstant::strNotNull).collect(Collectors.joining(System.lineSeparator())));
        } else {
            List<String> paperErrorList = new ArrayList<>();
            // 试卷编号-> 题目集合 Map
            Map<String, List<SubjectiveStructDto>> paperQuestionsMap = subjectiveStructDtoList.stream().collect(Collectors.groupingBy(SubjectiveStructDto::getPaperNumber));
            paperQuestionsMap.forEach((paperNumber, paperQuestionList) -> {
                if (markGroupService.countByExamIdAndPaperNumber(examId, paperNumber) > 0) {
                    paperErrorList.add(String.format("[试卷编号]%s,已存在分组,请先删除该试卷的所有分组再导入试题", paperNumber));
                } else {
                    // 优先删除该试卷的主观题
                    markQuestionService.remove(
                            new QueryWrapper<MarkQuestion>().lambda().eq(MarkQuestion::getExamId, examId)
                                    .eq(MarkQuestion::getPaperNumber, paperNumber).eq(MarkQuestion::getObjective, false));

                    // 试卷下分组集合
                    List<Integer> groupNumberList = paperQuestionList.stream().map(SubjectiveStructDto::getGroupNumber)
                            .distinct().sorted().collect(Collectors.toList());

                    // 评卷员序号
                    AtomicInteger markerNumber = new AtomicInteger(0);
                    for (Integer groupNumber : groupNumberList) {
                        List<String> groupErrorList = new ArrayList<>();
                        // 分组下题目集合
                        List<SubjectiveStructDto> groupQuestionList = paperQuestionList.stream()
                                .filter(e -> e.getGroupNumber().equals(groupNumber)).collect(Collectors.toList());

                        String paperNumberGroupNumberNotice = String.format("[试卷编号]%s,[评卷分组(只能用小写数字)]%s,的", paperNumber,
                                groupNumber);

                        String orgName = null;
                        Long orgId = null;
                        BasicCourse basicCourse = basicCourseService.getOne(
                                new QueryWrapper<BasicCourse>().lambda().eq(BasicCourse::getSchoolId, schoolId)
                                        .eq(BasicCourse::getCode, groupQuestionList.get(0).getCourseCode()));
                        if (Objects.isNull(basicCourse)) {
                            groupErrorList.add("[课程代码]不存在");
                        } else {
                            Long teachingRoomId = basicCourse.getTeachingRoomId();
                            SysOrg sysOrg = sysOrgService.getById(teachingRoomId);
                            if (Objects.isNull(sysOrg)) {
                                throw ExceptionResultEnum.ERROR.exception("无法找到机构");
                            }
                            orgName = sysOrg.getName();
                            orgId = sysOrg.getId();
                        }
                        Integer markerCount = null;
                        String markerCodes = null;
                        List<Integer> markerCountList = groupQuestionList.stream().map(SubjectiveStructDto::getMarkerCount).distinct().collect(Collectors.toList());
                        if (CollectionUtils.isNotEmpty(markerCountList)) {
                            if (markerCountList.size() > 1) {
                                groupErrorList.add("[评卷员数量（系统生成）]不一致");
                            } else if (markerCountList.size() == 1) {
                                markerCount = markerCountList.get(0);
                            }
                        }

                        List<String> markerCodesList = groupQuestionList.stream().map(SubjectiveStructDto::getMarkerCodes).distinct().collect(Collectors.toList());
                        if (CollectionUtils.isNotEmpty(markerCodesList)) {
                            if (markerCodesList.size() > 1) {
                                groupErrorList.add("[绑定工号（英文逗号分隔）]不一致");
                            } else if (markerCodesList.size() == 1) {
                                markerCodes = markerCodesList.get(0);
                            }
                        }

                        // 组装评卷员信息
                        List<MarkUser> markUsers = new ArrayList<>();
                        if (SystemConstant.strNotNull(markerCodes)) {
                            // 优先以绑定工号为准
                            String[] markerCodeArray = markerCodes.split(SystemConstant.COMMA);
                            for (String markerCode : markerCodeArray) {
                                SysUser sysUser = sysUserService.getOne(
                                        new QueryWrapper<SysUser>().lambda().eq(SysUser::getSchoolId, schoolId).eq(SysUser::getLoginName, markerCode).last(SystemConstant.LIMIT1));
                                if (Objects.isNull(sysUser)) {
                                    groupErrorList.add(paperNumberGroupNumberNotice + String.format(
                                            "[绑定工号（英文逗号分隔）]中的%s,在系统中不存在请先创建用户", markerCode));
                                } else {
                                    // 添加评卷员信息
                                    MarkUser markUser = new MarkUser();
                                    markUser.setUserId(sysUser.getId());
                                    markUser.setLoginName(sysUser.getLoginName());
                                    markUser.setName(sysUser.getRealName());
                                    markUser.setOrgName(orgName);
                                    markUsers.add(markUser);
                                }
                            }
                        } else if (Objects.nonNull(markerCount) && markerCount > 0) {
                            // 没有绑定工号且评卷员生成数量大于0,生成评卷员（或使用之前工号相同的评卷员）
                            for (int i = 0; i < markerCount; i++) {
                                int num = markerNumber.incrementAndGet();
                                if (num > 99) {
                                    paperErrorList.add("[试卷编号]%s的[评卷员数量（系统生成）]总有效数量不能超过99");
                                    break;
                                }
                                String markerCode = paperNumber + String.format("%02d", num);

                                SysUser sysUser = sysUserService.getOne(
                                        new QueryWrapper<SysUser>().lambda().eq(SysUser::getSchoolId, schoolId).eq(SysUser::getLoginName, markerCode).last(SystemConstant.LIMIT1));

                                MarkUser markUser = new MarkUser();
                                markUser.setLoginName(markerCode);
                                markUser.setOrgName(orgName);
                                markUsers.add(markUser);
                                if (Objects.nonNull(sysUser)) {
                                    markUser.setUserId(sysUser.getId());
                                    markUser.setName(sysUser.getRealName());
                                } else {
                                    // 创建用户
                                    UserSaveParams userSaveParams = new UserSaveParams();
                                    userSaveParams.setSchoolId(schoolId);
                                    userSaveParams.setLoginName(markerCode);
                                    userSaveParams.setRealName(markerCode);
                                    userSaveParams.setCode(markerCode);
                                    userSaveParams.setPassword(SystemConstant.DEFAULT_PASSWORD);
                                    userSaveParams.setOrgId(orgId);
                                    userSaveParams.setEnable(true);
                                    userSaveParams.setRoleIds(new Long[]{markerRole.getId()});
                                    try {
                                        markUser.setUserId(sysUserService.saveUser(userSaveParams));
                                        markUser.setName(markerCode);
                                    } catch (IllegalAccessException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                        if (CollectionUtils.isEmpty(groupErrorList) && CollectionUtils.isEmpty(paperErrorList)) {
                            // 创建评卷试题
                            List<MarkQuestion> markQuestionList = paperQuestionList.stream().filter(e -> groupNumber.equals(e.getGroupNumber())).flatMap(e -> {
                                MarkQuestion markQuestion = new MarkQuestion();
                                markQuestion.setExamId(examId);
                                markQuestion.setPaperNumber(paperNumber);
                                markQuestion.setPaperType(OptionsEnum.A.getCode());
                                markQuestion.setObjective(false);
                                markQuestion.setMainNumber(e.getMainNumber());
                                markQuestion.setSubNumber(e.getSubNumber());
                                markQuestion.setMainTitle(e.getMainTitle());
                                markQuestion.setTotalScore(e.getTotalScore());
                                markQuestion.setIntervalScore(e.getIntervalScore());
                                markQuestion.setQuestionType(QuestionType.QUESTION_ANSWER.getValue());
                                return Stream.of(markQuestion);
                            }).collect(Collectors.toList());
                            markQuestionService.saveBatch(markQuestionList);

                            MarkGroupDto markGroupDto = new MarkGroupDto();
                            markGroupDto.setDoubleEnable(false);
                            markGroupDto.setGroupNumber(groupNumber);
                            markGroupDto.setMarkers(markUsers);
                            markGroupDto.setQuestions(markQuestionList);
                            markGroupDto.setPictureConfigs(new ArrayList<>());
                            MarkGroupSingleDto markGroupSingleDto = new MarkGroupSingleDto();
                            markGroupSingleDto.setExamId(examId);
                            markGroupSingleDto.setPaperNumber(paperNumber);
                            markGroupSingleDto.setGroupInfo(markGroupDto);
                            markGroupService.saveGroup(markGroupSingleDto);
                        }
                        if (CollectionUtils.isNotEmpty(groupErrorList)) {
                            String groupErrorMsg = paperNumberGroupNumberNotice + String.join(",", groupErrorList);
                            paperErrorList.add(groupErrorMsg);
                        }
                    }
                }
            });
            if (CollectionUtils.isNotEmpty(paperErrorList)) {
                throw ExceptionResultEnum.ERROR.exception(String.join(System.lineSeparator(), paperErrorList));
            }
        }
        return map;
    }

    /**
     * 验证机构是否存在
     *
     * @param orgMap         机构map
     * @param key            机构名称
     * @param schoolId       学校id
     * @param excelErrorTemp 错误信息
     * @param row            行
     * @param sheet          区
     * @param cloumnName     cloumnName
     * @return 机构
     */
    private SysOrg validSysOrgExists(Map<String, SysOrg> orgMap,
                                     String key,
                                     Long schoolId,
                                     List<ExcelError> excelErrorTemp,
                                     int row,
                                     int sheet,
                                     String cloumnName) {
        SysOrg sysOrg = null;
        if (!orgMap.containsKey(key)) {//不存在查询
            QueryWrapper<SysOrg> sysOrgQueryWrapper = new QueryWrapper<>();
            sysOrgQueryWrapper.lambda().eq(SysOrg::getSchoolId, schoolId)
                    .eq(SysOrg::getName, key)
                    .eq(SysOrg::getEnable, true);
            sysOrg = sysOrgService.getOne(sysOrgQueryWrapper);
        } else {
            sysOrg = orgMap.get(key);
        }
        if (Objects.isNull(sysOrg)) {
            excelErrorTemp.add(new ExcelError(row, "excel第" + sheet + "个sheet第" + row + "行[" + cloumnName + "]不存在"));
        } else {
            SysOrg finalSysOrg = sysOrg;
            orgMap.computeIfAbsent(key, v -> finalSysOrg);
        }
        return sysOrg;
    }

    /**
     * 验证课程是否存在
     *
     * @param courseMap      courseMap
     * @param key            课程名称
     * @param schoolId       学校id
     * @param value          课程编号
     * @param excelErrorTemp 错误信息
     * @param row            行
     * @param sheet          区
     * @return 基础课程
     */
    private BasicCourse validBasicCourseExists(Map<String, BasicCourse> courseMap,
                                               String key,
                                               Long schoolId,
                                               String value,
                                               List<ExcelError> excelErrorTemp,
                                               int row,
                                               int sheet) {
        BasicCourse basicCourse = null;
        if (!courseMap.containsKey(key)) {//不存在查询
            QueryWrapper<BasicCourse> basicCourseQueryWrapper = new QueryWrapper<>();
            basicCourseQueryWrapper.lambda().eq(BasicCourse::getSchoolId, schoolId)
                    .eq(BasicCourse::getName, key)
                    .eq(BasicCourse::getCode, value)
                    .eq(BasicCourse::getEnable, true);
            basicCourse = basicCourseService.getOne(basicCourseQueryWrapper);
        } else {
            basicCourse = courseMap.get(key);
        }
        if (Objects.isNull(basicCourse)) {
            excelErrorTemp.add(new ExcelError(row, "excel第" + sheet + "个sheet第" + row + "行[" + "课程代码" + "]不存在"));
        } else {
            BasicCourse finalBasicCourse = basicCourse;
            courseMap.computeIfAbsent(key, v -> finalBasicCourse);
        }
        return basicCourse;
    }

    /**
     * 验证用户是否存在
     *
     * @param userMap
     * @param key
     * @param schoolId
     * @param excelErrorTemp
     * @param row
     * @param sheet
     * @param cloumnName
     * @return
     */
    private SysUser validSysUserExists(Map<String, SysUser> userMap,
                                       String key,
                                       Long schoolId,
                                       List<ExcelError> excelErrorTemp,
                                       int row,
                                       int sheet,
                                       String cloumnName) {
        SysUser sysUser = null;
        if (!userMap.containsKey(key)) {//不存在查询
            QueryWrapper<SysUser> sysUserQueryWrapper = new QueryWrapper<>();
            sysUserQueryWrapper.lambda().eq(SysUser::getSchoolId, schoolId)
                    .eq(SysUser::getRealName, key)
                    .eq(SysUser::getEnable, true);
            sysUser = sysUserService.getOne(sysUserQueryWrapper);
        } else {
            sysUser = userMap.get(key);
        }
        if (Objects.isNull(sysUser)) {
            excelErrorTemp.add(new ExcelError(row, "excel第" + sheet + "个sheet第" + row + "行[" + cloumnName + "]不存在"));
        } else {
            SysUser finalSysUser = sysUser;
            userMap.computeIfAbsent(key, v -> finalSysUser);
        }
        return sysUser;
    }

    /**
     * 验证班级是否存在
     *
     * @param clazzMap       班级map
     * @param key            班级名称
     * @param schoolId       学校id
     * @param excelErrorTemp 错误信息
     * @param row            行
     * @param sheet          区
     * @return 班级信息
     */
    private DictionaryResult validBasicClazzExists(Map<String, DictionaryResult> clazzMap,
                                                   String key,
                                                   Long schoolId,
                                                   List<ExcelError> excelErrorTemp,
                                                   int row,
                                                   int sheet) {
        DictionaryResult clazz = null;
        if (!clazzMap.containsKey(key)) {//不存在查询
            TeachClazz teachClazz = teachClazzService.getOne(new QueryWrapper<TeachClazz>().lambda().eq(TeachClazz::getSchoolId, schoolId).eq(TeachClazz::getClazzName, key));
            if (Objects.nonNull(teachClazz)) {
                clazz = new DictionaryResult();
                clazz.setId(teachClazz.getId());
                clazz.setName(teachClazz.getClazzName());
            } else {
                QueryWrapper<BasicClazz> basicClazzQueryWrapper = new QueryWrapper<>();
                basicClazzQueryWrapper.lambda().eq(BasicClazz::getSchoolId, schoolId)
                        .eq(BasicClazz::getClazzName, key)
                        .eq(BasicClazz::getEnable, true);
                BasicClazz basicClazz = basicClazzService.getOne(basicClazzQueryWrapper);
                if (Objects.nonNull(basicClazz)) {
                    clazz = new DictionaryResult();
                    clazz.setId(basicClazz.getId());
                    clazz.setName(basicClazz.getClazzName());
                }
            }
        } else {
            clazz = clazzMap.get(key);
        }
        if (Objects.isNull(clazz)) {
            excelErrorTemp.add(new ExcelError(row, "excel第" + sheet + "个sheet第" + row + "行[" + "班级名称" + "]不存在"));
        } else {
            DictionaryResult finalClazz = clazz;
            clazzMap.computeIfAbsent(key, v -> finalClazz);
        }
        return clazz;
    }
}
