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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.distributed.print.business.bean.dto.ExamPaperObjectiveStructureDto;
import com.qmth.distributed.print.business.bean.dto.ExamPaperStructureDto;
import com.qmth.distributed.print.business.bean.dto.ExamPaperSubjectiveStructureDto;
import com.qmth.distributed.print.business.bean.marking.*;
import com.qmth.distributed.print.business.entity.*;
import com.qmth.distributed.print.business.enums.ExamPaperStructureStatusEnum;
import com.qmth.distributed.print.business.enums.ExamPrintPlanSyncStatusEnum;
import com.qmth.distributed.print.business.mapper.ExamPaperStructureMapper;
import com.qmth.distributed.print.business.service.*;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.BasicAttachment;
import com.qmth.teachcloud.common.entity.BasicSemester;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.PushTypeEnum;
import com.qmth.teachcloud.common.enums.UploadFileEnum;
import com.qmth.teachcloud.common.service.BasicAttachmentService;
import com.qmth.teachcloud.common.service.BasicSemesterService;
import com.qmth.teachcloud.common.service.TeachcloudCommonService;
import com.qmth.teachcloud.common.sync.CloudMarkingTaskUtils;
import com.qmth.teachcloud.common.util.ExcelUtil;
import com.qmth.teachcloud.common.util.ResultUtil;
import com.qmth.teachcloud.common.util.ServletUtil;
import com.qmth.teachcloud.common.util.excel.ExcelError;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 试卷结构 服务实现类
 * </p>
 */
@Service
public class ExamPaperStructureServiceImpl extends ServiceImpl<ExamPaperStructureMapper, ExamPaperStructure> implements ExamPaperStructureService {

    @Resource
    ExamTaskService examTaskService;

    @Resource
    ExamTaskDetailService examTaskDetailService;

    @Resource
    @Lazy
    PrintCommonService printCommonService;

    @Resource
    BasicAttachmentService basicAttachmentService;

    @Resource
    CloudMarkingTaskUtils stmmsUtils;

    @Resource
    BasicSemesterService basicSemesterService;

    @Resource
    BasicExamService basicExamService;

    @Resource
    ExamPaperGroupService examPaperGroupService;

    @Resource
    ExamCardDetailService examCardDetailService;

    @Resource
    TeachcloudCommonService teachcloudCommonService;

    @Resource
    TBSyncTaskService tbSyncTaskService;

    @Override
    public IPage<ExamPaperStructure> listByPropositionTeacherId(Long semesterId, Long examId, Integer pageNumber, Integer pageSize, String[] structureStatusEnums) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        Page<ExamPaperStructure> page = new Page<>(pageNumber, pageSize);
        IPage<ExamPaperStructure> examPaperStructureIPage = this.baseMapper.listByPropositionTeacherId(page, schoolId, semesterId, examId, sysUser.getId(), ExamPrintPlanSyncStatusEnum.FINISH.name(), structureStatusEnums);
        examPaperStructureIPage.getRecords().forEach(m->{
            m.setStatusStr(ExamPaperStructureStatusEnum.INIT.equals(m.getStatus()) ? "未提交" : "已提交");

            // 试卷结构同步任务状态
            if(m.getId() != null) {
                QueryWrapper<TBSyncTask> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().eq(TBSyncTask::getObjectId, m.getId()).eq(TBSyncTask::getType, PushTypeEnum.STRUCTURE_GROUP_PUSH);
                TBSyncTask tbSyncTask = tbSyncTaskService.getOne(queryWrapper);
                m.setTaskStatus(tbSyncTask == null ? null : tbSyncTask.getStatus().name());
            }
        });
        return examPaperStructureIPage;
    }

    @Transactional
    @Override
    public ExamPaperStructure upload(String examPaperStructure, String md5, String keys, MultipartFile[] files) {

        ExamPaperStructure examPaperStructureTemp = JSONObject.parseObject(examPaperStructure, ExamPaperStructure.class);
        if (Objects.isNull(examPaperStructureTemp.getId())) {
            // 保存
            examPaperStructureTemp.setId(SystemConstant.getDbUuid());
            this.save(examPaperStructureTemp);
        } else {
            examPaperStructureTemp = this.getById(examPaperStructureTemp.getId());
        }

        // 组装ExamPaperStructureDto
        List<ExamPaperStructureDto> examPaperStructureDtos = createExamPaperStructure(md5, keys, examPaperStructureTemp.getPaperType(), files);
        List<BasicAttachment> basicAttachmentList = new ArrayList<>();

        List<Map<String, Object>> paperAnswerList = new ArrayList<>();
        List<Map<String, Object>> subjectiveStructureList = new ArrayList<>();
        List<Map<String, Object>> objectiveStructureList = new ArrayList<>();

        // 试卷卷型及原卷附件ID
        Map<String, String> paperMap = createExamTaskAttachmentIds(examPaperStructureTemp);

        // 上传试卷结构、标答文件
        for (ExamPaperStructureDto examPaperStructureDto : examPaperStructureDtos) {
            Map<String, Object> paperAnswerMap = new HashMap<>();
            paperAnswerMap.put("paperType", examPaperStructureDto.getPaperType());
            Map<String, Object> subjectiveStructureMap = new HashMap<>();
            subjectiveStructureMap.put("paperType", examPaperStructureDto.getPaperType());
            Map<String, Object> objectiveStructureMap = new HashMap<>();
            objectiveStructureMap.put("paperType", examPaperStructureDto.getPaperType());
            try {
                // 客观题附件
                List<Object> objectiveStructure = null;
                if (examPaperStructureDto.getObjectiveQuestionFile() != null) {
                    BasicAttachment objectiveBasicAttachment = printCommonService.saveAttachment(examPaperStructureDto.getObjectiveQuestionFile(), examPaperStructureDto.getObjectiveQuestionMd5(), UploadFileEnum.UPLOAD);
                    if (Objects.isNull(objectiveBasicAttachment)) {
                        throw ExceptionResultEnum.ATTACHMENT_ERROR.exception();
                    }
                    basicAttachmentList.add(objectiveBasicAttachment);
                    // 解析试卷结构
                    objectiveStructure = analyzPaperObjectiveStructure(examPaperStructureDto.getObjectiveQuestionFile());
                }
                objectiveStructureMap.put("content", CollectionUtils.isEmpty(objectiveStructure) ? null : objectiveStructure);

                // 主观题附件
                List<Object> subjectiveStructrue = null;
                if (examPaperStructureDto.getSubjectiveQuestionFile() != null) {
                    BasicAttachment subjectiveBasicAttachment = printCommonService.saveAttachment(examPaperStructureDto.getSubjectiveQuestionFile(), examPaperStructureDto.getSubjectiveQuestionMd5(), UploadFileEnum.UPLOAD);
                    if (Objects.isNull(subjectiveBasicAttachment)) {
                        throw ExceptionResultEnum.ATTACHMENT_ERROR.exception();
                    }
                    basicAttachmentList.add(subjectiveBasicAttachment);
                    // 解析试卷结构
                    subjectiveStructrue = analyzPaperSubjectiveStructure(examPaperStructureDto.getSubjectiveQuestionFile());
                }
                subjectiveStructureMap.put("content", CollectionUtils.isEmpty(subjectiveStructrue) ? null : subjectiveStructrue);

                // 标答附件
                Long answerId = null;
                if (examPaperStructureDto.getStandardAnswerFile() != null) {
                    BasicAttachment answerBasicAttachment = printCommonService.saveAttachment(examPaperStructureDto.getStandardAnswerFile(), examPaperStructureDto.getStandardAnswerMd5(), UploadFileEnum.UPLOAD);
                    if (Objects.isNull(answerBasicAttachment)) {
                        throw ExceptionResultEnum.ATTACHMENT_ERROR.exception();
                    }
                    basicAttachmentList.add(answerBasicAttachment);
                    answerId = answerBasicAttachment.getId();
                }
                paperAnswerMap.put("answer", answerId);

                // 试卷原卷
                paperAnswerMap.put("paper", paperMap.get(examPaperStructureDto.getPaperType()));
                paperAnswerList.add(paperAnswerMap);
                subjectiveStructureList.add(subjectiveStructureMap);
                objectiveStructureList.add(objectiveStructureMap);
            } catch (Exception e) {
                log.error(SystemConstant.LOG_ERROR, e);
                for (BasicAttachment basicAttachment : basicAttachmentList) {
                    basicAttachmentService.deleteAttachment(basicAttachment);
                }
                if (e instanceof ApiException) {
                    ResultUtil.error((ApiException) e, e.getMessage());
                } else {
                    ResultUtil.error(e.getMessage());
                }
            }
        }

        // 保存
        examPaperStructureTemp.setPaperAnswer(JSONObject.toJSONString(paperAnswerList));
        examPaperStructureTemp.setSubjectiveStructure(JSONObject.toJSONString(subjectiveStructureList));
        examPaperStructureTemp.setObjectiveStructure(JSONObject.toJSONString(objectiveStructureList));
        // 上传成功
        this.updateById(examPaperStructureTemp);

        ExamTask examTask = examTaskService.getByCourseCodeAndPaperNumber(examPaperStructureTemp.getSchoolId(), examPaperStructureTemp.getCourseCode(), examPaperStructureTemp.getPaperNumber());
        examPaperStructureTemp.setPaperNumberId(String.valueOf(examTask.getPaperNumberId()));
        return examPaperStructureTemp;
    }

    @Transactional
    @Override
    public ExamPaperStructure uploadAnswer(String id, String md5, MultipartFile file) {
        if (id == null || "null".equals(id)) {
            throw ExceptionResultEnum.ERROR.exception("请先设置试卷结构和分组");
        }
        ExamPaperStructure examPaperStructure = this.getById(id);
        // 组装ExamPaperStructureDto
        List<BasicAttachment> basicAttachmentList = new ArrayList<>();
        List<Map<String, Object>> paperAnswerList = new ArrayList<>();

        // 试卷卷型及原卷附件ID
        Map<String, String> paperMap = createExamTaskAttachmentIds(examPaperStructure);
        // 上传试卷结构、标答文件
        Map<String, Object> paperAnswerMap = new HashMap<>();
        paperAnswerMap.put("paperType", examPaperStructure.getPaperType());
        try {
            // 标答附件
            BasicAttachment answerBasicAttachment = printCommonService.saveAttachment(file, md5, UploadFileEnum.UPLOAD);
            if (Objects.isNull(answerBasicAttachment)) {
                throw ExceptionResultEnum.ATTACHMENT_ERROR.exception();
            }
            basicAttachmentList.add(answerBasicAttachment);
            paperAnswerMap.put("answer", answerBasicAttachment.getId());
            // 试卷原卷
            paperAnswerMap.put("paper", paperMap.get(examPaperStructure.getPaperType()));
            paperAnswerList.add(paperAnswerMap);
        } catch (Exception e) {
            for (BasicAttachment basicAttachment : basicAttachmentList) {
                basicAttachmentService.deleteAttachment(basicAttachment);
            }
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }

        // 保存
        examPaperStructure.setPaperAnswer(JSONObject.toJSONString(paperAnswerList));
        this.updateById(examPaperStructure);

        return examPaperStructure;
    }

    @Override
    public void updateStatusById(Long id, ExamPaperStructureStatusEnum status) {
        UpdateWrapper<ExamPaperStructure> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(ExamPaperStructure::getStatus, status).eq(ExamPaperStructure::getId, id);
        this.update(updateWrapper);
    }

    @Override
    public List<Map> preStructure(Long id) {
        if (Objects.isNull(id)) {
            throw ExceptionResultEnum.ERROR.exception("参数有误");
        }
        ExamPaperStructure examPaperStructure = this.getById(id);
        if (!ExamPaperStructureStatusEnum.FINISH.equals(examPaperStructure.getStatus())) {
            throw ExceptionResultEnum.ERROR.exception("试卷结构没有同步成功");
        }
        ExamTask examTask = examTaskService.getByCourseCodeAndPaperNumber(examPaperStructure.getSchoolId(), examPaperStructure.getCourseCode(), examPaperStructure.getPaperNumber());
        String paperType = examPaperStructure.getPaperType();
        List<Map> list = new ArrayList<>();
        for (String str : paperType.split(",")) {
            Map<String, Object> map = new HashMap<>();
            map.put("paperType", str);
            List<Map> paperStructure = stmmsUtils.queryPaperStructure(examPaperStructure.getSchoolId(), String.valueOf(examPaperStructure.getThirdRelateId()), examTask.getPaperNumberId() + str, null);
            map.put("content", paperStructure);
            list.add(map);
        }
        return list;
    }

    @Override
    public long countByPropositionTeacherId() {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        String[] structureStatusEnums = new String[]{ExamPaperStructureStatusEnum.FINISH.name()};
        List<ExamPaperStructure> examPaperStructureList = this.baseMapper.listByPropositionTeacherId(schoolId,null, null, sysUser.getId(), ExamPrintPlanSyncStatusEnum.FINISH.name(), structureStatusEnums);
        return CollectionUtils.isEmpty(examPaperStructureList) ? 0 : examPaperStructureList.size();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ExamPaperStructure submitExamPaperParams(String evaluationParameterStr, SysUser requestUser) {
        // 1.判断试卷中主观题是否全部完成分组
        EvaluationParameters evaluationParameters = JSON.parseObject(evaluationParameterStr, EvaluationParameters.class);
        boolean matchSubAndGroup = Question.matchTwoQuestionList(evaluationParameters.getPaperStructureInfo().getSubjectiveQuestionList(),
                evaluationParameters.getGroupInfo().stream().flatMap(e -> {
                    List<Question> questionList = e.getQuestions();
                    return questionList.stream();
                }).collect(Collectors.toList()));
        if (!matchSubAndGroup) {
            throw ExceptionResultEnum.ERROR.exception("题目没有全部完成分组或者试卷机构中的主观题结构和分组中主观题结构不一致");
        }
        // 2.保存试卷结构
        ExamPaperStructure examPaperStructure = this.saveExamPaperStructure(evaluationParameterStr, requestUser);
        // 3.保存分组和分组关系信息
        examPaperGroupService.saveExamPaperGroupInfo(examPaperStructure.getId(), evaluationParameters.getGroupInfo());
        // 返回试卷结构信息
        return examPaperStructure;
    }

    @Override
    public ExamPaperStructure updateObjectiveAnswer(Long id, String objectiveStructure) {
        ExamPaperStructure examPaperStructure = this.getById(id);
        if (examPaperStructure == null) {
            throw ExceptionResultEnum.ERROR.exception("试卷结构数据不存在");
        }
        // todo xf 20220414 校验所有题卡都有答案
        examPaperStructure.setObjectiveStructure(objectiveStructure);
        this.updateById(examPaperStructure);
        return examPaperStructure;
    }

    @Override
    public List<CardJpgResult> findCardJpgFileByPaperNumber(Long examId, String courseCode, String paperNumber) {
        Long examTaskId = examTaskService.getOne(new QueryWrapper<ExamTask>().lambda()
                .eq(ExamTask::getExamId, examId)
                .eq(ExamTask::getCourseCode, courseCode)
                .eq(ExamTask::getPaperNumber, paperNumber)).getId();
        Long cardId = examTaskDetailService.getOne(new QueryWrapper<ExamTaskDetail>().lambda()
                .eq(ExamTaskDetail::getExamTaskId, examTaskId)).getCardId();
        String jpgAttachmentInfo = examCardDetailService.getOne(new QueryWrapper<ExamCardDetail>().lambda()
                .eq(ExamCardDetail::getCardId, cardId)).getJpgAttachmentInfo();

        List<ConvertJpgStorage> convertJpgStorageList = JSONObject.parseArray(jpgAttachmentInfo, ConvertJpgStorage.class);

        return convertJpgStorageList.stream().flatMap(e -> {
            BasicAttachment basicAttachment = basicAttachmentService.getById(e.getAttachmentId());

            CardJpgResult cardJpgResult = new CardJpgResult();
            cardJpgResult.setName(basicAttachment.getName());
            cardJpgResult.setIndex(e.getIndex());
            cardJpgResult.setPath(teachcloudCommonService.filePreview(basicAttachment.getPath()));
            return Stream.of(cardJpgResult);
        }).collect(Collectors.toList());
    }

    /**
     * 保存试卷结构
     *
     * @param evaluationParameterStr 评卷参数
     * @return 评卷参数id
     */
    private ExamPaperStructure saveExamPaperStructure(String evaluationParameterStr, SysUser requestUser) {
        EvaluationParameters evaluationParameters = JSON.parseObject(evaluationParameterStr, EvaluationParameters.class);
        BasicPaperInfo basicPaperInfo = evaluationParameters.getBasicPaperInfo();
        PaperStructureInfo paperStructureInfo = evaluationParameters.getPaperStructureInfo();

        Long examId = basicPaperInfo.getExamId();
        BasicSemester basicSemester = basicSemesterService.getById(SystemConstant.convertIdToLong(basicExamService.getById(examId).getSemesterId()));
        Long semesterId = basicSemester.getId();
        String semesterName = basicSemester.getName();
        Long thirdRelateId = basicPaperInfo.getThirdRelateId();
        String thirdRelateName = basicPaperInfo.getThirdRelateName();
        String paperNumber = basicPaperInfo.getPaperNumber();
        String courseCode = basicPaperInfo.getCourseCode();
        String courseName = basicPaperInfo.getCourseName();
        String paperType = basicPaperInfo.getPaperType();
        if (!SystemConstant.strNotNull(paperType)) {
            throw ExceptionResultEnum.ERROR.exception("试卷类型不存在");
        }
        ExamPaperStructureStatusEnum status = basicPaperInfo.getStatus();
        String objectiveStructure = JSON.toJSONString(paperStructureInfo.getObjectiveQuestionList());
        String subjectiveStructure = JSON.toJSONString(paperStructureInfo.getSubjectiveQuestionList());
        Long propositionTeacherId = requestUser.getId();
        Long examPaperStructureId = basicPaperInfo.getExamPaperStructureId();
        String sequence = basicPaperInfo.getSequence();

        ExamPaperStructure examPaperStructure = new ExamPaperStructure();
        examPaperStructure.setSchoolId(requestUser.getSchoolId());
        examPaperStructure.setSemesterId(semesterId);
        examPaperStructure.setSemesterName(semesterName);
        examPaperStructure.setExamId(examId);
        examPaperStructure.setThirdRelateId(thirdRelateId);
        examPaperStructure.setThirdRelateName(thirdRelateName);
        examPaperStructure.setPaperNumber(paperNumber);
        examPaperStructure.setCourseCode(courseCode);
        examPaperStructure.setCourseName(courseName);
        examPaperStructure.setSequence(sequence);
        examPaperStructure.setPaperType(paperType);
        examPaperStructure.setStatus(status);
        examPaperStructure.setObjectiveStructure(objectiveStructure);
        examPaperStructure.setSubjectiveStructure(subjectiveStructure);
        examPaperStructure.setPaperInfoJson(evaluationParameterStr);
        examPaperStructure.setPropositionTeacherId(propositionTeacherId);
        examPaperStructure.setEnable(true);
        if (SystemConstant.longNotNull(examPaperStructureId)) {
            // 如果是编辑试卷机构，当客观题信息（大题名称、大题号、小题号、分数）全都没变的情况下不更新客观题机构（因为答案可能已经上传）
            examPaperStructure.setId(examPaperStructureId);
            ExamPaperStructure old = this.getById(examPaperStructureId);
            // 更新paperAnswer不清
            examPaperStructure.setPaperAnswer(old.getPaperAnswer());
            // 数据库中的原客观题结构
            String oldObjectiveQuestion = old.getObjectiveStructure();
            if (this.matchObjectiveQuestion(objectiveStructure, oldObjectiveQuestion)) {
                // 如果本次编辑试卷结构 客观题结构和之前数据库中存储的确实一致，则不更新客观题结构（把原数据库的客观题结构作为更新的）
                examPaperStructure.setObjectiveStructure(oldObjectiveQuestion);
            }
        }
        this.saveOrUpdate(examPaperStructure);
        return examPaperStructure;
    }


    private Map<String, String> createExamTaskAttachmentIds(ExamPaperStructure examPaperStructure) {
        ExamTask examTask = examTaskService.getByCourseCodeAndPaperNumber(examPaperStructure.getSchoolId(), examPaperStructure.getCourseCode(), examPaperStructure.getPaperNumber());

        QueryWrapper<ExamTaskDetail> examTaskDetailQueryWrapper = new QueryWrapper<>();
        examTaskDetailQueryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, examTask.getId());
        ExamTaskDetail examTaskDetail = examTaskDetailService.getOne(examTaskDetailQueryWrapper);

        String paperAttachmentIds = examTaskDetail.getPaperAttachmentIds();
        List<Map> list = JSONObject.parseArray(paperAttachmentIds, Map.class);
        if (CollectionUtils.isEmpty(list)) {
            throw ExceptionResultEnum.ERROR.exception("未找到关联的试卷文件");
        }

        return list.stream().collect(Collectors.toMap(m -> m.get("name").toString(), m -> m.get("attachmentId").toString()));
    }

    /**
     * 解析excel文件内容
     *
     * @param file
     * @return
     */
    private List<Object> analyzPaperSubjectiveStructure(MultipartFile file) throws
            IOException, NoSuchFieldException {
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(ExamPaperSubjectiveStructureDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            List<ExcelError> excelErrorTemp = new ArrayList<>();
            // 只允许导入一个sheet
            if (finalExcelList.size() > 1) {
                throw ExceptionResultEnum.ERROR.exception("excel中只允许有一个sheet");
            }
            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> examTaskTempList = excelMap.get(i);
                for (int y = 0; y < examTaskTempList.size(); y++) {
                    ExamPaperSubjectiveStructureDto subjectiveStructureDto = (ExamPaperSubjectiveStructureDto) examTaskTempList.get(y);
                    if (StringUtils.isBlank(subjectiveStructureDto.getCourseCode())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程代码]必填"));
                    }
                    if (StringUtils.isBlank(subjectiveStructureDto.getCourseName())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程名称]必填"));
                    }
                    if (StringUtils.isBlank(subjectiveStructureDto.getMainName())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[大题名称]必填"));
                    }
                    if (subjectiveStructureDto.getMainNumber() == null) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[大题号]必填"));
                    }
                    if (StringUtils.isBlank(subjectiveStructureDto.getSubNumber())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[小题号]必填"));
                    }
                    if (StringUtils.isBlank(subjectiveStructureDto.getScore())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[小题满分]必填"));
                    }
                }
            }
            if (excelErrorTemp.size() > 0) {
                List<String> errors = excelErrorTemp.stream().map(ExcelError::getExcelErrorType).collect(Collectors.toList());
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(errors));
            }
            return finalExcelList;
        });
        List<Object> list = new ArrayList<>();
        for (LinkedMultiValueMap<Integer, Object> map : finalList) {
            for (Map.Entry<Integer, List<Object>> entry : map.entrySet()) {
                list.addAll(entry.getValue());
            }
        }
        return list;
    }

    /**
     * 解析excel文件内容
     *
     * @param file
     * @return
     */
    private List<Object> analyzPaperObjectiveStructure(MultipartFile file) throws IOException, NoSuchFieldException {
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(ExamPaperObjectiveStructureDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            List<ExcelError> excelErrorTemp = new ArrayList<>();
            // 只允许导入一个sheet
            if (finalExcelList.size() > 1) {
                throw ExceptionResultEnum.ERROR.exception("excel中只允许有一个sheet");
            }
            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> examTaskTempList = excelMap.get(i);
                for (int y = 0; y < examTaskTempList.size(); y++) {
                    ExamPaperObjectiveStructureDto objectiveStructureDto = (ExamPaperObjectiveStructureDto) examTaskTempList.get(y);
                    if (StringUtils.isBlank(objectiveStructureDto.getCourseCode())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程代码]必填"));
                    }
                    if (StringUtils.isBlank(objectiveStructureDto.getCourseName())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程名称]必填"));
                    }
                    if (StringUtils.isBlank(objectiveStructureDto.getPaperType())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[试卷类型]必填"));
                    }
                    if (StringUtils.isBlank(objectiveStructureDto.getMainName())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[大题名称]必填"));
                    }
                    if (objectiveStructureDto.getMainNumber() == null) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[大题号]必填"));
                    }
                    if (StringUtils.isBlank(objectiveStructureDto.getSubNumber())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[小题号]必填"));
                    }
                    if (StringUtils.isBlank(objectiveStructureDto.getAnswer())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[标准答案]必填"));
                    }
                    if (StringUtils.isBlank(objectiveStructureDto.getScore())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[小题满分]必填"));
                    }
                }
            }
            if (excelErrorTemp.size() > 0) {
                List<String> errors = excelErrorTemp.stream().map(ExcelError::getExcelErrorType).collect(Collectors.toList());
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(errors));
            }
            return finalExcelList;
        });
        List<Object> list = new ArrayList<>();
        for (LinkedMultiValueMap<Integer, Object> map : finalList) {
            for (Map.Entry<Integer, List<Object>> entry : map.entrySet()) {
                list.addAll(entry.getValue());
            }
        }
        return list;
    }

    /**
     * 组装ExamPaperStructureDto
     *
     * @param md5
     * @param paperType
     * @param files
     * @return
     */
    private List<ExamPaperStructureDto> createExamPaperStructure(String md5, String keys, String paperType, MultipartFile[] files) {
        if (StringUtils.isBlank(paperType)) {
            throw ExceptionResultEnum.ERROR.exception("没有可上传文件的试卷卷型");
        }
        if (files == null || files.length == 0) {
            throw ExceptionResultEnum.ERROR.exception("请上传文件");
        }
        if (StringUtils.isBlank(keys)) {
            throw ExceptionResultEnum.ERROR.exception("上传文件有误");
        }
        if (StringUtils.isBlank(md5)) {
            throw ExceptionResultEnum.ERROR.exception("上传文件有误");
        }
        String[] paperTypesAttr = paperType.split(",");
        String[] md5sAttr = md5.split(",");
        String[] keysAttr = keys.split(",");
        String[] fileTypeAttr = {"objectiveQuestion", "subjectiveQuestion", "standardAnswer"};
        List<ExamPaperStructureDto> list = new ArrayList<>();

        for (String pType : paperTypesAttr) {
            ExamPaperStructureDto examPaperStructureDto = new ExamPaperStructureDto();
            examPaperStructureDto.setPaperType(pType);
            int oi = Arrays.asList(keysAttr).indexOf(pType + "-" + fileTypeAttr[0]);
            examPaperStructureDto.setObjectiveQuestionFile(oi < 0 ? null : files[oi]);
            examPaperStructureDto.setObjectiveQuestionMd5(oi < 0 ? null : md5sAttr[oi]);
            int si = Arrays.asList(keysAttr).indexOf(pType + "-" + fileTypeAttr[1]);
            examPaperStructureDto.setSubjectiveQuestionFile(si < 0 ? null : files[si]);
            examPaperStructureDto.setSubjectiveQuestionMd5(si < 0 ? null : md5sAttr[si]);
            int ai = Arrays.asList(keysAttr).indexOf(pType + "-" + fileTypeAttr[2]);
            examPaperStructureDto.setStandardAnswerFile(ai < 0 ? null : files[ai]);
            examPaperStructureDto.setStandardAnswerMd5(ai < 0 ? null : md5sAttr[ai]);
            list.add(examPaperStructureDto);
        }
        return list;
    }

    /**
     * 匹配客观题结构是否更改（若未上传答案则直接返回true）
     *
     * @param newQuestions 新的结构
     * @param dbQuestions  旧的结构
     * @return 结果
     */
    private boolean matchObjectiveQuestion(String newQuestions, String dbQuestions) {
        List<Question> dbList = JSON.parseArray(dbQuestions, Question.class);
        if (dbList.size() == 0) {
            return false;
        }
        // 旧结构没有答案，直接更新
        if (!SystemConstant.strNotNull(dbList.get(0).getAnswer())) {
            return false;
        }

        List<Question> freshList = JSON.parseArray(newQuestions, Question.class);

        return Question.matchTwoQuestionList(dbList, freshList);
    }
}
