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

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.common.collect.Lists;
import com.itextpdf.text.DocumentException;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.distributed.print.business.bean.dto.*;
import com.qmth.teachcloud.common.bean.dto.excel.BasicClazzImportDto;
import com.qmth.teachcloud.common.bean.dto.excel.BasicCourseImportDto;
import com.qmth.teachcloud.common.bean.dto.excel.BasicStudentImportDto;
import com.qmth.distributed.print.business.bean.params.SerialNumberParams;
import com.qmth.distributed.print.business.cache.CreatePdfCacheUtil;
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.HtmlToPdfUtil;
import com.qmth.teachcloud.common.annotation.ExcelDBFieldDesc;
import com.qmth.teachcloud.common.bean.dto.excel.DescribeImportDto;
import com.qmth.teachcloud.common.bean.dto.excel.SysUserImportDto;
import com.qmth.teachcloud.common.bean.params.ArraysParams;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.BasicAttachment;
import com.qmth.teachcloud.common.entity.BasicSchool;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.entity.TBTask;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.FlowStatusEnum;
import com.qmth.teachcloud.common.enums.PageSizeEnum;
import com.qmth.teachcloud.common.enums.UploadFileEnum;
import com.qmth.teachcloud.common.service.*;
import com.qmth.teachcloud.common.util.*;
import com.qmth.teachcloud.common.util.excel.ExcelError;
import org.apache.commons.codec.digest.DigestUtils;
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.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;

import javax.annotation.Resource;
import java.io.*;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @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
    ExamDetailService detailService;

    @Resource
    ExamDetailCourseService detailCourseService;

    @Resource
    ExamPrintPlanService examPrintPlanService;

    @Resource
    ExamTaskService examTaskService;

    @Resource
    ExamTaskDetailService examTaskDetailService;

    @Resource
    ExamCardService examCardService;

    @Resource
    ExamCardDetailService examCardDetailService;

    @Resource
    ExamStudentService examStudentService;

    @Resource
    BasicAttachmentService basicAttachmentService;

    @Resource
    ExamDetailService examDetailService;

//    @Resource
//    OssUtil ossUtil;

    @Resource
    CommonCacheService commonCacheService;

    @Resource
    BasicCourseService basicCourseService;

    @Resource
    CreatePdfUtil createPdfUtil;

    @Resource
    BasicTemplateService basicTemplateService;

    @Resource
    RedisTemplate<String, Object> redisTemplate;

    @Resource
    BasicExamRuleService basicExamRuleService;

    @Resource
    TBTaskService tbTaskService;

    @Resource
    BasicCardRuleService basicCardRuleService;

    @Resource
    TeachcloudCommonService teachcloudCommonService;

    @Resource
    ConvertUtil convertUtil;

    @Autowired
    ExamDetailCourseService examDetailCourseService;

    @Autowired
    BasicStudentService basicStudentService;

    @Autowired
    ExamTaskPrintService examTaskPrintService;

    @Resource
    SysUserService sysUserService;

    @Resource
    FileStoreUtil fileStoreUtil;

    @Resource
    BasicClazzService basicClazzService;


    /**
     * 创建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();
        List<Long> examDetailIds = Objects.nonNull(map.get("examDetailIds")) ? (List<Long>) map.get("examDetailIds") : null;
        //查询printPlan
        ExamPrintPlan examPrintPlan = examPrintPlanService.getById(tbTask.getPrintPlanId());
        if (Objects.isNull(examPrintPlan)) {
            throw ExceptionResultEnum.EXAM_PRINT_IS_NULL.exception();
        }
        UpdateWrapper<ExamDetail> examDetailQueryWrapper = new UpdateWrapper<>();
        examDetailQueryWrapper.lambda().eq(ExamDetail::getSchoolId, sysUser.getSchoolId())
                .eq(ExamDetail::getPrintPlanId, tbTask.getPrintPlanId());
        if (Objects.nonNull(examDetailId)) {
            examDetailQueryWrapper.lambda().eq(ExamDetail::getId, examDetailId);
        } else if (Objects.nonNull(examDetailIds)) {
            examDetailQueryWrapper.lambda().in(ExamDetail::getId, examDetailIds);
        }
        examDetailQueryWrapper.lambda().set(ExamDetail::getStatus, ExamDetailStatusEnum.NEW)
                .set(ExamDetail::getAttachmentPath, null);
        detailService.update(examDetailQueryWrapper);

        //所有考场都撤回，印刷任务状态改为就绪
        QueryWrapper<ExamDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamDetail::getPrintPlanId, tbTask.getPrintPlanId())
                .notIn(ExamDetail::getStatus, ExamDetailStatusEnum.NEW, ExamDetailStatusEnum.READY);
        List<ExamDetail> examDetails = detailService.list(queryWrapper);
        if (CollectionUtils.isEmpty(examDetails)) {
            UpdateWrapper<ExamPrintPlan> printPlanUpdateWrapper = new UpdateWrapper<>();
            printPlanUpdateWrapper.lambda().set(ExamPrintPlan::getStatus, PrintPlanStatusEnum.READY).eq(ExamPrintPlan::getId, tbTask.getPrintPlanId());
            examPrintPlanService.update(printPlanUpdateWrapper);
        }
        return map;
    }

    /**
     * 创建pdf核心逻辑
     *
     * @param examDetailCourseList
     * @param examPrintPlan
     * @param examDetail
     * @param sysUser
     * @param paperTypeParam
     * @param basicAttachmentList
     * @param attachmentIds
     * @param list
     * @return
     * @throws IOException
     * @throws DocumentException
     */
    @Transactional
    public List<BasicAttachment> createPdfCoreLogic(List<ExamDetailCourse> examDetailCourseList,
                                                    ExamPrintPlan examPrintPlan,
                                                    ExamDetail examDetail,
                                                    SysUser sysUser,
                                                    String paperTypeParam,
                                                    List<BasicAttachment> basicAttachmentList,
                                                    Set<Long> attachmentIds,
                                                    List<PdfDto>... list
    ) throws Exception {
        if (Objects.nonNull(examDetailCourseList) && examDetailCourseList.size() > 0) {
            String printContent = examPrintPlan.getPrintContent();
            if (StringUtils.isBlank(printContent)) {
                return null;
            }

            Map<Long, BasicCardRule> basicCardRuleMap = new HashMap<>();
            for (ExamDetailCourse examDetailCourse : examDetailCourseList) {
                List<PdfDto> paperPdfTempList = new ArrayList<>();//所有试卷
                List<PdfDto> examStudentTempPdfList = new ArrayList<>();//所有题卡
                List<PdfDto> backupPaperTempPdfList = new ArrayList<>();//备份试卷
                List<PdfDto> cardPdfTempList = new ArrayList<>();//备份题卡

                //查询试卷
                List<ExamTask> examTaskList = examTaskService.findByFlowStatus(sysUser.getSchoolId()
                        , examDetailCourse.getCourseCode()
                        , examDetailCourse.getCourseName()
                        , examDetailCourse.getPaperNumber()
                        , FlowStatusEnum.FINISH);
                if (Objects.isNull(examTaskList) || examTaskList.size() == 0) {
                    throw ExceptionResultEnum.EXAM_TASK_IS_NULL.exception();
                }
                Set<Long> examTaskIds = examTaskList.stream().map(s -> s.getId()).collect(Collectors.toSet());
                Map<Long, ExamTask> examTaskMap = examTaskList.stream().collect(
                        Collectors.toMap(ExamTask::getId, Function.identity(), (dto1, dto2) -> dto1));

                QueryWrapper<ExamTaskDetail> examTaskDetailQueryWrapper = new QueryWrapper<>();
                examTaskDetailQueryWrapper.lambda().in(ExamTaskDetail::getExamTaskId, examTaskIds)
                        .eq(ExamTaskDetail::getEnable, true);
                List<ExamTaskDetail> examTaskDetailList = examTaskDetailService.list(examTaskDetailQueryWrapper);
                JSONObject jsonObject = new JSONObject();
                JSONArray jsonArray = new JSONArray();
                for (ExamTaskDetail examTaskDetail : examTaskDetailList) {
                    //查询题卡
                    ExamCard examCard = examCardService.getById(examTaskDetail.getCardId());
                    if (Objects.isNull(examCard)) {
                        throw ExceptionResultEnum.EXAM_CARD_IS_NULL.exception();
                    }

                    //查询题卡规则
                    ExamTask examTask = examTaskMap.get(examTaskDetail.getExamTaskId());
                    BasicCardRule basicCardRule = null;
                    if (Objects.nonNull(examTask)) {
                        if (!basicCardRuleMap.containsKey(examTask.getCardRuleId())) {
                            basicCardRule = basicCardRuleService.getById(examTask.getCardRuleId());
                            BasicCardRule finalBasicCardRule = basicCardRule;
                            basicCardRuleMap.computeIfAbsent(examTask.getCardRuleId(), v -> finalBasicCardRule);
                        } else {
                            basicCardRule = basicCardRuleMap.get(examTask.getCardRuleId());
                        }
                    }
                    QueryWrapper<ExamCardDetail> examCardDetailQueryWrapper = new QueryWrapper<>();
                    examCardDetailQueryWrapper.lambda().eq(ExamCardDetail::getCardId, examCard.getId());
                    List<ExamCardDetail> examCardDetailList = examCardDetailService.list(examCardDetailQueryWrapper);

                    //拼装key
                    String key = examDetail.getSchoolId()
                            + "_" + examDetail.getExamStartTime()
                            + "_" + examDetail.getExamEndTime()
                            + "_" + examDetailCourse.getCourseCode()
                            + "_" + examDetailCourse.getPaperNumber();
                    String paperType = null;
                    if (Objects.nonNull(paperTypeParam)) {
                        paperType = paperTypeParam;
                        CreatePdfCacheUtil.setPaperType(key, paperType);
                    } else {
                        paperType = createPdfUtil.getPaperType(examPrintPlan, examTaskDetail, key);//抽取卷型
                    }
                    examTaskDetail.setRelatePaperType(paperType);
                    examDetailCourse.setPaperType(paperType);

                    List<PaperPdfDto> paperPdfDto = null;
                    if (printContent.contains("PAPER")) {
                        paperPdfDto = createPdfUtil.getPaperPdfFile(paperType, examTaskDetail);
                    }

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

                    basicAttachmentList = Objects.isNull(basicAttachmentList) ? basicAttachmentList = new ArrayList<>() : basicAttachmentList;
                    if (printContent.contains("CARD")) {
                        for (ExamCardDetail examCardDetail : examCardDetailList) {
                            createPdfUtil.getCardAttachmentId(examCardDetail, attachmentIds);

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

                            for (int i = 1; i <= examPrintPlan.getBackupCount(); i++) {
                                BasicAttachment basicAttachment = createPdfUtil.cardHtml(String.format("%02d", i), cardContent, examDetail, examDetailCourse, examCard, jsonArray, sysUser.getId(), cardPdfTempList, basicCardRule);
                                examDetailCourse.setCardPagesA3(basicAttachment.getPages());
                                basicAttachmentList.add(basicAttachment);
                            }
                            list[1].addAll(cardPdfTempList);

                            //查询考生
                            QueryWrapper<ExamStudent> examStudentQueryWrapper = new QueryWrapper<>();
                            examStudentQueryWrapper.lambda().eq(ExamStudent::getSchoolId, sysUser.getSchoolId())
                                    .eq(ExamStudent::getExamDetailCourseId, examDetailCourse.getId());
                            List<ExamStudent> examStudentList = examStudentService.list(examStudentQueryWrapper);

                            List<String> paperTypes = Arrays.asList(paperType.split(",")).stream().sorted(Comparator.comparing(String::valueOf)).collect(Collectors.toList());
                            int i = 0;
                            for (ExamStudent t : examStudentList) {
                                int mod = i % examTaskDetail.getDrawCount().intValue();
                                String stuPaperType = paperTypes.get(mod);
                                if (Objects.nonNull(pdfDto)) {
                                    createPdfUtil.getExamStudentPaperPdf(stuPaperType, paperPdfDto, paperPdfTempList);
                                }
                                t.setPaperType(stuPaperType);
                                basicAttachmentList.add(createPdfUtil.examStudentHtml(attachmentIds, studentContent, t, examDetail, examDetailCourse, sysUser.getId(), examStudentTempPdfList));
                                i++;
                            }
                            list[2].addAll(paperPdfTempList);
                            list[3].addAll(examStudentTempPdfList);
                            examStudentService.saveOrUpdateBatch(examStudentList);
                            jsonObject.put("card", jsonArray);
                            examDetailCourse.setAttachmentId(jsonObject.toJSONString());
                        }
//                        examCardDetailService.saveOrUpdateBatch(examCardDetailList);
                    }
                }
                examDetailCourse.setAttachmentId(jsonObject.toJSONString());
                examTaskDetailService.saveOrUpdateBatch(examTaskDetailList);
            }
            detailCourseService.saveOrUpdateBatch(examDetailCourseList);
        }
        return basicAttachmentList;
    }

    /**
     * 创建A4文件
     *
     * @param examPrintPlan
     * @param examDetail
     * @param basicSchool
     * @param ftlList
     * @param examDetailCourseList
     * @param list
     * @throws IOException
     * @throws DocumentException
     */
    @Transactional
    public void createA4File(ExamPrintPlan examPrintPlan, ExamDetail examDetail, BasicSchool basicSchool, Set<File> ftlList, List<ExamDetailCourse> examDetailCourseList, List<PdfDto>... list) throws Exception {
        List<Long> examDetailCourseListIds = examDetailCourseList.stream().map(s -> s.getId()).collect(Collectors.toList());
        if (Objects.nonNull(examPrintPlan.getOrdinaryContent())) {
            //获取普通印品
            JSONArray jsonArrayOrdinary = JSONArray.parseArray(examPrintPlan.getOrdinaryContent());
            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"));
                }
            }
        }

        if (Objects.nonNull(examPrintPlan.getVariableContent())) {
            List<ExamStudentCourseDto> examStudentCourseDtoList = examStudentService.queryBySchoolIdAndExamDetailCourseIds(basicSchool.getId(), examDetailCourseListIds);
            int count = (int) examStudentCourseDtoList.stream().filter(s -> s.getPaperPageA3() != null && s.getPaperPageA3() > 2).count();
            boolean tag = count > 0 ? true : false;

            //获取变量印品
            JSONArray jsonArrayVariable = JSONArray.parseArray(examPrintPlan.getVariableContent());
            for (int i = 0; i < jsonArrayVariable.size(); i++) {
                JSONObject jsonObjectVariable = jsonArrayVariable.getJSONObject(i);
                String type = (String) jsonObjectVariable.get("type");
                if (Objects.nonNull(jsonObjectVariable.get("attachmentId")) && !Objects.equals("", jsonObjectVariable.get("attachmentId"))) {
                    Long attachmentId = Long.parseLong((String) jsonObjectVariable.get("attachmentId"));
                    BasicAttachment basicAttachment = basicAttachmentService.getById(attachmentId);
                    ftlList.add(teachcloudCommonService.getFile(basicAttachment.getPath(), false));
                    if (Objects.nonNull(type) && Objects.equals(type.toUpperCase(), "SIGN")) {//签到表
                        createPdfUtil.createSignBook(basicAttachment, basicSchool.getName(), examDetail, examStudentCourseDtoList, list[1], (Integer) jsonObjectVariable.get("backupCount"), examDetailCourseList);
                    } else if (Objects.nonNull(type) && Objects.equals(type.toUpperCase(), "PACKAGE")) {//卷袋贴
                        createPdfUtil.createPaperPackage(tag, basicAttachment, basicSchool.getName(), examDetail, examStudentCourseDtoList, list[1], (Integer) jsonObjectVariable.get("backupCount"), examDetailCourseList);
                    }
                }
            }
        }

        Collections.sort(list[1], new Comparator<PdfDto>() {
            @Override
            public int compare(PdfDto o1, PdfDto o2) {
                return o1.getSequence() > o2.getSequence() ? 1 : -1;
            }
        });
    }

    /**
     * 创建pdf逻辑
     *
     * @param map
     * @return
     */
    @Override
//    @Transactional
    public Map<String, Object> executeCreatePdfLogic(Map<String, Object> map) throws IOException {
        List<BasicAttachment> basicAttachmentList = null;
        try {
            TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
            SysUser sysUser = (SysUser) map.get(SystemConstant.USER);
            Long examDetailId = tbTask.getEntityId();
            List<Long> examDetailIds = Objects.nonNull(map.get("examDetailIds")) ? (List<Long>) map.get("examDetailIds") : null;
            Long schoolId = tbTask.getSchoolId();
            List<Long> examDetailCourseIds = Objects.nonNull(map.get("examDetailCourseIds")) ? (List<Long>) map.get("examDetailCourseIds") : null;
            String paperTypeParam = Objects.nonNull(map.get("paperType")) ? (String) map.get("paperType") : null;

            //查询printPlan
            ExamPrintPlan examPrintPlan = examPrintPlanService.getById(tbTask.getPrintPlanId());
            if (Objects.isNull(examPrintPlan)) {
                throw ExceptionResultEnum.EXAM_PRINT_IS_NULL.exception();
            }

            BasicSchool basicSchool = commonCacheService.schoolCache(examPrintPlan.getSchoolId());
            BasicExamRule basicExamRule = basicExamRuleService.getBySchoolId(schoolId);
            if (Objects.isNull(basicExamRule)) {
                throw ExceptionResultEnum.EXAM_RULE_IS_NULL.exception();
            }

            //查询examDetail
            QueryWrapper<ExamDetail> examDetailQueryWrapper = new QueryWrapper<>();
            examDetailQueryWrapper.lambda().eq(ExamDetail::getSchoolId, sysUser.getSchoolId())
                    .eq(ExamDetail::getPrintPlanId, tbTask.getPrintPlanId());
            if (Objects.nonNull(examDetailId)) {
                examDetailQueryWrapper.lambda().eq(ExamDetail::getId, examDetailId);
            } else if (Objects.nonNull(examDetailIds)) {
                examDetailQueryWrapper.lambda().in(ExamDetail::getId, examDetailIds);
            }
            List<ExamDetail> examDetailList = detailService.list(examDetailQueryWrapper);
            if (Objects.isNull(examDetailList) || examDetailList.size() == 0) {
                throw ExceptionResultEnum.EXAM_DETAIL_IS_NULL.exception();
            }
            Set<Long> attachmentIds = new HashSet<>();
            Set<File> ftlList = new HashSet<>();
            for (ExamDetail examDetail : examDetailList) {
                tbTask.setObjName(examDetail.getExamRoom() + "-" + examDetail.getExamPlace());
                //查询examDetailCourse
                QueryWrapper<ExamDetailCourse> examDetailCourseQueryWrapper = new QueryWrapper<>();
                if (Objects.nonNull(examDetailCourseIds) && examDetailCourseIds.size() > 0) {
                    examDetailCourseQueryWrapper.lambda().in(ExamDetailCourse::getId, examDetailCourseIds);
                } else {
                    examDetailCourseQueryWrapper.lambda().eq(ExamDetailCourse::getExamDetailId, examDetail.getId());
                }
                List<ExamDetailCourse> examDetailCourseList = detailCourseService.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> cardPdfList = new ArrayList<>();//备份题卡

                //创建pdf核心逻辑
                basicAttachmentList = createPdfCoreLogic(examDetailCourseList,
                        examPrintPlan,
                        examDetail,
                        sysUser,
                        paperTypeParam,
                        basicAttachmentList,
                        attachmentIds,
                        backupPaperPdfList,
                        cardPdfList,
                        paperPdfList,
                        examStudentPdfList);

                createA4File(examPrintPlan, examDetail, basicSchool, ftlList, examDetailCourseList, ordinaryPdfList, variablePdfList);

                //合并pdf
                String dirNameA3 = createPdfUtil.mergeA3Pdf(paperPdfList, examStudentPdfList, backupPaperPdfList, cardPdfList);
                String dirNameA4 = createPdfUtil.mergeA4Pdf(variablePdfList, ordinaryPdfList);

                List<PdfDto> mergePdfDeleteList = new ArrayList<>();
                mergePdfDeleteList.addAll(examStudentPdfList);
                mergePdfDeleteList.addAll(cardPdfList);
                BasicAttachment attachment = createPdfUtil.mergePdfSaveDb(dirNameA4, dirNameA3, tbTask, sysUser.getId(), examDetail, basicExamRule, mergePdfDeleteList);
                if (basicAttachmentList != null) {
                    basicAttachmentList.add(attachment);
                }
            }
            updateExamPrintPlan(basicExamRule, examPrintPlan);
            map.computeIfAbsent("size", v -> examDetailList.size());
            //最后一步删除附件
//            createPdfUtil.deleteAttachment(attachmentIds, ftlList);
        } catch (Exception e) {
            log.error("请求出错", e);
            basicAttachmentService.batchDeleteAttachment(basicAttachmentList);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return map;
    }

    /**
     * 更新考试计划
     *
     * @param basicExamRule
     * @param examPrintPlan
     */
    @Transactional
    public void updateExamPrintPlan(BasicExamRule basicExamRule, ExamPrintPlan examPrintPlan) {
        if (PrintMethodEnum.AUTO == basicExamRule.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());

//        UploadFileEnum.FILE/
        LocalDateTime nowTime = LocalDateTime.now();
        StringJoiner stringJoiner = new StringJoiner("");
        stringJoiner.add(UploadFileEnum.FILE.getTitle()).add(File.separator);

        stringJoiner.add(String.valueOf(nowTime.getYear())).add(File.separator)
                .add(String.format("%02d", nowTime.getMonthValue())).add(File.separator)
                .add(String.format("%02d", nowTime.getDayOfMonth()));

        JSONObject jsonObject = new JSONObject();
        stringJoiner.add(File.separator).add(SystemConstant.getUuid()).add(".").add(SystemConstant.XLSX);

        String dirName = stringJoiner.toString().replaceAll("\\\\", "/");

//        ossUtil.ossUpload(dirName, in, null);
        fileStoreUtil.ossUpload(dirName, in, DigestUtils.md5Hex(new ByteArrayInputStream(outputStream.toByteArray())), fileStoreUtil.getUploadEnumByPath(dirName).getFssType());
        jsonObject.put(SystemConstant.TYPE, SystemConstant.OSS);
        jsonObject.put(SystemConstant.PATH, dirName);
        jsonObject.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.FILE);
        map.put(SystemConstant.PATH, jsonObject.toString());
        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();
        if (!tbTaskService.countByPrintPlanIdAndEntityId(schoolId, printPlanId, null)) {
            throw ExceptionResultEnum.ERROR.exception("当前印刷计划正在生成pdf,无法导入考务数据");
        }
        Long userId = tbTask.getCreateId();
        ExamPrintPlan examPrintPlan = examPrintPlanService.getById(printPlanId);

        if (examPrintPlan == null) {
            throw ExceptionResultEnum.ERROR.exception("[印刷计划]不存在");
        }

        PrintPlanStatusEnum printPlanStatus = examPrintPlan.getStatus();
        if (PrintPlanStatusEnum.NEW != printPlanStatus && PrintPlanStatusEnum.READY != printPlanStatus) {
            throw ExceptionResultEnum.ERROR.exception(printPlanStatus + "状态下的印刷计划不允许导入");
        }

        String printPlanName = examPrintPlan.getName();

        // 该学校有效考务数据
        List<FieldsDto> fieldsDtoList = examDetailService.findExaminationFields(schoolId);
        if (fieldsDtoList.size() == 0) {
            throw ExceptionResultEnum.ERROR.exception("该学校没有设置考务数据");
        }
        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("文件格式异常");
        }
        // 读取第一个工作表
        Sheet sheet = workbook.getSheetAt(0);
        if (sheet.getLastRowNum() == 0 && sheet.getPhysicalNumberOfRows() == 0) {
            throw ExceptionResultEnum.ERROR.exception("第一个sheet为空,考务数据必须放在第一个sheet（工作表）内");
        }
        // 获取sheet行数
        int totalRows = sheet.getPhysicalNumberOfRows();
        // 获取sheet列数
        int totalCells = 0;
        if (totalRows > 1 && sheet.getRow(0) != null) {
            totalCells = sheet.getRow(0).getPhysicalNumberOfCells();
        }
        Row head = sheet.getRow(0);
        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);
                }
            }
            headList.add(cellValue);
        }

        // 搜索所有有效字段 excel中的表头是否包含
        for (FieldsDto fieldsDto : fieldsDtoList) {
            if (!headList.contains(fieldsDto.getName())) {
                throw ExceptionResultEnum.ERROR.exception("学校考务必填字段 ：'" + fieldsDto.getName() + "' 不存在");
            }
        }
        List<ExaminationImportDto> examinationImportDtoList = new ArrayList<>();
        for (int r = 1; r < totalRows; r++) {
            Row row = sheet.getRow(r);

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

            ExaminationImportDto examinationImportDto = new ExaminationImportDto();
            Field[] examinationImportDtoFields = examinationImportDto.getClass().getDeclaredFields();
            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);
                    if (cell == null) {
                        throw ExceptionResultEnum.ERROR.exception("excel中第[" + (r + 1) + "]行,第[" + (index + 1) + "]列,字段[" + name + "]必填");
                    }
                    String cellValue = String.valueOf(ExcelUtil.convert(cell));
                    if (cellValue == null || cellValue.length() < 1 || cellValue.equals("null")) {
                        throw ExceptionResultEnum.ERROR.exception("excel中第[" + (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;
                            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);
                    }
                }
            }
            // 校验试卷编号
            examTaskService.verifyCourseCodeByPaperNumber(schoolId, examinationImportDto.getPaperNumber(), examinationImportDto.getCourseCode());

            // 校验课程信息
            basicCourseService.verifyCourseInfo(schoolId, examinationImportDto.getCourseCode(), examinationImportDto.getCourseName(), userId);

            // 解析时间
            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.setSecondaryFieldList(secondaryFieldList);
            examinationImportDto.setExamStartTime(examStartTime);
            examinationImportDto.setExamEndTime(examEndTime);
            examinationImportDto.setSchoolId(schoolId);
            examinationImportDto.setPrintPlanId(printPlanId);
            examinationImportDto.setPrintPlanName(printPlanName);
            examinationImportDtoList.add(examinationImportDto);
        }

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

        // 校验课程代码和试卷编号在印刷计划下是1对1的关系
        List<String> paperNumberList = examinationImportDtoList.stream().map(ExaminationImportDto::getPaperNumber).distinct().collect(Collectors.toList());
        for (String paperNumber : paperNumberList) {
            List<String> tmp = examinationImportDtoList.stream().filter(e -> paperNumber.equals(e.getPaperNumber()))
                    .map(ExaminationImportDto::getCourseCode).distinct().collect(Collectors.toList());
            if (tmp.size() != 1) {
                throw ExceptionResultEnum.ERROR.exception("试卷编号为 " + paperNumber + ",对应多个课程代码 : " + tmp);
            }
        }

        // 删除印刷计划下的考务数据
        examDetailService.deleteExaminationData(printPlanId, ExamDataSourceEnum.FILE_IMPORT);

        // 卷袋号生成规则
        SerialNumberParams serialNumberParams = new SerialNumberParams("packageCode-" + schoolId, "1", 6);
        String key = serialNumberParams.getModel() + serialNumberParams.getPrefix();
        RedisAtomicLong counter = new RedisAtomicLong(key, Objects.requireNonNull(redisTemplate.getConnectionFactory()));
        Long value = counter.get();
        try {
            // 组装exam_detail数据
            List<Long> examDetailIdList = examDetailService.disposeExamDetailByExaminationExcel(examinationImportDtoList, userId, serialNumberParams, sysUser);
            // 组装exam_detail_course数据
            examDetailService.disposeExamDetailCourseByExaminationExcel(examinationImportDtoList, userId, sysUser);
            // 组装exam_student数据
            examDetailService.disposeExamStudentByExaminationExcel(examinationImportDtoList, userId, sysUser);

            // 更改印刷计划状态
            examPrintPlan.setStatus(PrintPlanStatusEnum.READY);
            examPrintPlanService.updateById(examPrintPlan);

            map.put("examDetailIdList", examDetailIdList);
            map.put("dataCount", examinationImportDtoList.size());
        } catch (Exception e) {
            redisTemplate.opsForValue().set(key, value);
            throw new RuntimeException(e);
        }

        return map;
    }

    /**
     * 下载pdf
     *
     * @param map
     * @return
     * @throws IOException
     */
    @Override
    @Transactional
    public Map<String, Object> executeDownloadPdfLogic(Map<String, Object> map) throws Exception {
        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("数组id为空");
            }

            //查询examDetail
            List<ExamDetail> examDetailList = examDetailService.listByIds(Arrays.asList(arraysParams.getIds()));
            if (Objects.nonNull(examDetailList)) {
                Set<Long> attachmentIds = examDetailList.stream().map(ExamDetail::getAttachmentId).collect(Collectors.toSet());
                List<BasicAttachment> basicAttachmentList = basicAttachmentService.listByIds(attachmentIds);

                if (Objects.nonNull(basicAttachmentList) && basicAttachmentList.size() > 0) {
                    LocalDateTime nowTime = LocalDateTime.now();
                    StringJoiner stringJoiner = new StringJoiner("")
                            .add(SystemConstant.TEMP_FILES_DIR).add(File.separator);
                    StringJoiner dirName = new StringJoiner("")
                            .add(UploadFileEnum.FILE.getTitle()).add(File.separator)
                            .add(String.valueOf(nowTime.getYear())).add(File.separator)
                            .add(String.format("%02d", nowTime.getMonthValue())).add(File.separator)
                            .add(String.format("%02d", nowTime.getDayOfMonth()))
                            .add(File.separator).add(SystemConstant.getUuid()).add(SystemConstant.ZIP_PREFIX);
                    File zipFile = new File(stringJoiner.toString() + dirName.toString());
                    if (!zipFile.getParentFile().exists()) {
                        zipFile.getParentFile().mkdirs();
                        zipFile.createNewFile();
                    }
                    List<File> sourceFiles = new ArrayList<>();
                    for (BasicAttachment basicAttachment : basicAttachmentList) {
                        JSONObject jsonObject = JSONObject.parseObject(basicAttachment.getPath());
                        UploadFileEnum uploadFileEnum = UploadFileEnum.valueOf((String) jsonObject.get(SystemConstant.UPLOAD_TYPE));

                        StringJoiner stringJoinerPdf = new StringJoiner("")
                                .add(SystemConstant.TEMP_FILES_DIR).add(File.separator)
                                .add((String) jsonObject.get(SystemConstant.PATH));
//                        sourceFiles.add(ossUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), stringJoinerPdf.toString()));
                        sourceFiles.add(fileStoreUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), stringJoinerPdf.toString(), uploadFileEnum.getFssType()));
                    }
                    FileUtil.doZip(zipFile, sourceFiles);
//                    ossUtil.ossUpload(dirName.toString(), zipFile, BinaryUtil.toBase64String(HexUtils.decodeHex(DigestUtils.md5Hex(new FileInputStream(zipFile)))));
                    fileStoreUtil.ossUpload(dirName.toString(), zipFile, BinaryUtil.toBase64String(HexUtils.decodeHex(DigestUtils.md5Hex(new FileInputStream(zipFile)))), fileStoreUtil.getUploadEnumByPath(dirName.toString()).getFssType());
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put(SystemConstant.PATH, dirName.toString());
                    jsonObject.put(SystemConstant.TYPE, SystemConstant.OSS);
                    jsonObject.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.FILE);
                    tbTask.setResultFilePath(jsonObject.toJSONString());
                    for (File file : sourceFiles) {
                        file.delete();
                    }
                    zipFile.delete();
                }
                map.computeIfAbsent(SystemConstant.SIZE, v -> attachmentIds.size());
            }
        } catch (Exception e) {
            log.error("请求出错", e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return map;
    }

    @Override
    @Transactional
    public Map<String, Object> executeExportSampleLogic(Map<String, Object> map) throws Exception {
        int count = 0;
        TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
        LocalDateTime nowTime = LocalDateTime.now();
        StringJoiner zipJoiner = new StringJoiner("")
                .add(SystemConstant.TEMP_FILES_DIR).add(File.separator);
        StringJoiner dirName = new StringJoiner("")
                .add(UploadFileEnum.FILE.getTitle()).add(File.separator)
                .add(String.valueOf(nowTime.getYear())).add(File.separator)
                .add(String.format("%02d", nowTime.getMonthValue())).add(File.separator)
                .add(String.format("%02d", nowTime.getDayOfMonth()))
                .add(File.separator).add(SystemConstant.getUuid()).add(SystemConstant.ZIP_PREFIX);
        String dirNameTmp = dirName.toString().replaceAll("\\\\", "/");
        File zipFile = new File(zipJoiner.toString() + dirNameTmp);
        if (!zipFile.getParentFile().exists()) {
            zipFile.getParentFile().mkdirs();
            zipFile.createNewFile();
        }

        // 跟文件路径
        String zipLocalRootPath = SystemConstant.TEMP_FILES_DIR + File.separator + System.currentTimeMillis();
        Long[] ids = (Long[]) map.get("ids");
        for (Long id : ids) {
            ExamTask examTask = examTaskService.getById(id);
            if (Objects.isNull(examTask)) {
                throw ExceptionResultEnum.ERROR.exception("未找到命题任务");
            }
            String examTaskSign = examTask.getCourseName() + "-" + examTask.getPaperNumber();
            String firstPath = zipLocalRootPath + File.separator + examTaskSign;
//            List<ExamTaskDetail> examTaskDetailList = examTaskDetailService.list(new QueryWrapper<ExamTaskDetail>().lambda().eq(ExamTaskDetail::getExamTaskId, id));
//            if (examTaskDetailList.size() != 1) {
//                throw ExceptionResultEnum.ERROR.exception("获取命题任务详情失败");
//            }
//            ExamTaskDetail examTaskDetail = examTaskDetailList.get(0);
            ReviewSampleDto reviewSampleDto = examTaskService.findReviewSampleInfoByExamTaskId(id);


            // 处理试卷样品
            List<Map> paperInfo = JSONObject.parseArray(reviewSampleDto.getPaperAttachmentIds(), Map.class);
            if (Objects.isNull(paperInfo)) {
                throw ExceptionResultEnum.ERROR.exception("试卷信息不存在");
            }
            System.out.println("paperInfo = " + JSON.toJSONString(paperInfo));
            Set<Long> attPaperIds = new HashSet<>();
            for (Map paperMap : paperInfo) {
                if (Objects.nonNull(paperMap.get("attachmentId")) && String.valueOf(paperMap.get("attachmentId")).length() > 0 && !String.valueOf(paperMap.get("attachmentId")).equals("null")) {
                    Long attachmentId = Long.valueOf(String.valueOf(paperMap.get("attachmentId")));
                    attPaperIds.add(attachmentId);
                }
            }
            if (attPaperIds.size() > 0) {
                List<BasicAttachment> paperAttachmentList = basicAttachmentService.listByIds(attPaperIds);
                for (BasicAttachment paperAttachment : paperAttachmentList) {
                    JSONObject jsonObject = JSONObject.parseObject(paperAttachment.getPath());
                    UploadFileEnum uploadFileEnum = UploadFileEnum.valueOf((String) jsonObject.get(SystemConstant.UPLOAD_TYPE));
                    String paperPath = firstPath + File.separator + "试卷" + File.separator + paperAttachment.getName() + paperAttachment.getType();
//                    ossUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), paperPath);
                    fileStoreUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), paperPath, uploadFileEnum.getFssType());
                    count++;
                }
            }

            // 处理审核样品
            List<Map> confirmInfo = JSONObject.parseArray(reviewSampleDto.getPaperConfirmAttachmentIds(), Map.class);
            System.out.println("confirmInfo = " + JSON.toJSONString(confirmInfo));
            Set<Long> attConfirmIds = new HashSet<>();
            for (Map confirmMap : confirmInfo) {
                if (Objects.isNull(confirmMap.get("attachmentId"))) {
                    throw ExceptionResultEnum.ERROR.exception("未找到附件id");
                }
                Long attachmentId = Long.valueOf(String.valueOf(confirmMap.get("attachmentId")));
                attConfirmIds.add(attachmentId);
            }
            if (attConfirmIds.size() > 0) {
                List<BasicAttachment> confirmAttachmentList = basicAttachmentService.listByIds(attConfirmIds);
                for (BasicAttachment confirmAttachment : confirmAttachmentList) {
                    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();
//                    ossUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), confirmPath);
                    fileStoreUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), confirmPath, uploadFileEnum.getFssType());
                    count++;
                }
            }

            // 处理题卡
            Long cardId = reviewSampleDto.getCardId();
            ExamCard examCard = examCardService.getById(cardId);
            if (Objects.isNull(examCard)) {
                throw ExceptionResultEnum.ERROR.exception("找不到答题卡");
            }
            MakeMethodEnum makeMethodEnum = examCard.getMakeMethod();
            if (MakeMethodEnum.SELECT.equals(makeMethodEnum)) {
                Long templateId = examCard.getTemplateId();
                if (templateId == null || templateId == 0) {
                    throw ExceptionResultEnum.ERROR.exception("找不到题卡对应的模板");
                }
                BasicTemplate basicTemplate = basicTemplateService.getById(templateId);
                if (Objects.isNull(basicTemplate)) {
                    throw ExceptionResultEnum.ERROR.exception("找不到模板信息");
                }
                Long attachmentId = basicTemplate.getAttachmentId();
                if (attachmentId == null || attachmentId == 0) {
                    throw ExceptionResultEnum.ERROR.exception("找不到模板对应的附件");
                }
                BasicAttachment cardAttachment = basicAttachmentService.getById(attachmentId);
                if (Objects.isNull(cardAttachment)) {
                    throw ExceptionResultEnum.ERROR.exception("找不到附件");
                }
                JSONObject jsonObject = JSONObject.parseObject(cardAttachment.getPath());
                String cardPath = firstPath + File.separator + "题卡" + File.separator + cardAttachment.getName() + cardAttachment.getType();
                UploadFileEnum uploadFileEnum = UploadFileEnum.valueOf((String) jsonObject.get(SystemConstant.UPLOAD_TYPE));
//                ossUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), cardPath);
                fileStoreUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), cardPath, uploadFileEnum.getFssType());
            } else {
                List<ExamCardDetail> examCardDetailList = examCardDetailService.list(new QueryWrapper<ExamCardDetail>().lambda().eq(ExamCardDetail::getCardId, cardId));
                if (examCardDetailList.size() != 1) {
                    throw ExceptionResultEnum.ERROR.exception("题卡明细信息异常");
                }
                ExamCardDetail examCardDetail = examCardDetailService.getByCardId(cardId);
                String htmlContent = examCardDetail.getHtmlContent();
                byte[] bytes = htmlContent.getBytes();
                String cardPath = firstPath + File.separator + "题卡" + File.separator + examCard.getTitle() + SystemConstant.HTML_PREFIX;
                File localFile = new File(cardPath);
                if (!localFile.getParentFile().exists()) {
                    localFile.getParentFile().mkdirs();
                }
                FileCopyUtils.copy(bytes, localFile);
            }
            count++;
        }
        ZipUtil.zip(zipLocalRootPath, zipFile.getPath(), false);
//        ossUtil.ossUpload(dirNameTmp, zipFile, BinaryUtil.toBase64String(HexUtils.decodeHex(DigestUtils.md5Hex(new FileInputStream(zipFile)))));
        fileStoreUtil.ossUpload(dirNameTmp, zipFile, BinaryUtil.toBase64String(HexUtils.decodeHex(DigestUtils.md5Hex(new FileInputStream(zipFile)))), fileStoreUtil.getUploadEnumByPath(dirNameTmp).getFssType());
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(SystemConstant.PATH, dirNameTmp);
        jsonObject.put(SystemConstant.TYPE, SystemConstant.OSS);
        jsonObject.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.FILE);
        tbTask.setResultFilePath(jsonObject.toJSONString());
        ConvertUtil.delFolder(zipLocalRootPath);
        zipFile.delete();
        map.put("count", count);
        return map;
    }

    @Override
    public Map<String, Object> executeExportPaperAndCardLogic(Map<String, Object> map) throws Exception {
        TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
        String yyyyMMddHH24mmss = DateUtil.format(new Date(), "yyyyMMddHHmmss");
        BasicSchool basicSchool = commonCacheService.schoolCache(tbTask.getSchoolId());
        LocalDateTime nowTime = LocalDateTime.now();
        StringJoiner zipJoiner = new StringJoiner("")
                .add(SystemConstant.TEMP_FILES_DIR).add(File.separator);
        StringJoiner dirName = new StringJoiner("")
                .add(UploadFileEnum.FILE.getTitle()).add(File.separator)
                .add(String.valueOf(nowTime.getYear())).add(File.separator)
                .add(String.format("%02d", nowTime.getMonthValue())).add(File.separator)
                .add(String.format("%02d", nowTime.getDayOfMonth()))
                .add(File.separator)
                .add(yyyyMMddHH24mmss).add("_").add(basicSchool.getName()).add("_").add("试卷导出文件").add(SystemConstant.ZIP_PREFIX);
        String dirNameTmp = dirName.toString().replaceAll("\\\\", "/");

        // 根文件路径
        String zipLocalRootPath = SystemConstant.TEMP_FILES_DIR + File.separator + System.currentTimeMillis();

        List<ExamTaskDetailDto> examTasks = (List<ExamTaskDetailDto>) map.get("examTasks");
        for (ExamTaskDetailDto examTask : examTasks) {
            ExamTaskDetail examTaskDetail = examTaskDetailService.getByExamTaskId(Long.valueOf(examTask.getId()));

            // 试卷
            String paperAttachmentIds = examTaskDetail.getPaperAttachmentIds();
            if (StringUtils.isNotBlank(paperAttachmentIds)) {
                List<Map> paperInfo = JSONObject.parseArray(paperAttachmentIds, Map.class);
                if (Objects.isNull(paperInfo)) {
                    throw ExceptionResultEnum.ERROR.exception("试卷信息不存在");
                }
                for (Map paperMap : paperInfo) {
                    if (Objects.nonNull(paperMap.get("attachmentId")) && String.valueOf(paperMap.get("attachmentId")).length() > 0 && !String.valueOf(paperMap.get("attachmentId")).equals("null")) {
                        String name = paperMap.get("name").toString();
                        Long attachmentId = Long.valueOf(String.valueOf(paperMap.get("attachmentId")));
                        BasicAttachment attachment = basicAttachmentService.getById(attachmentId);
                        if (Objects.nonNull(attachment)) {
                            JSONObject jsonObject = JSONObject.parseObject(attachment.getPath());
                            String paperPath = zipLocalRootPath + File.separator + examTask.getPaperNumber() + File.separator + "试卷" + "_" + examTask.getPaperNumber() + "_" + name + attachment.getType();
                            UploadFileEnum uploadFileEnum = UploadFileEnum.valueOf((String) jsonObject.get(SystemConstant.UPLOAD_TYPE));

//                            ossUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), paperPath);
                            fileStoreUtil.ossDownload((String) jsonObject.get(SystemConstant.PATH), paperPath, uploadFileEnum.getFssType());
                        }
                    }
                }
            }

            // 题卡
            // 处理题卡
            ExamCard examCard = examCardService.getById(examTaskDetail.getCardId());
            if (Objects.isNull(examCard)) {
                throw ExceptionResultEnum.ERROR.exception("找不到答题卡");
            }
            MakeMethodEnum makeMethodEnum = examCard.getMakeMethod();

            String cardPath = zipLocalRootPath + File.separator + examTask.getPaperNumber();
            String cardHtmlPath = cardPath + File.separator + "题卡" + "_" + examTask.getPaperNumber() + SystemConstant.HTML_PREFIX;
            String cardPdfPath = cardPath + File.separator + "题卡" + "_" + examTask.getPaperNumber() + SystemConstant.PDF_PREFIX;
            // 通用题卡
            ExamCardDetail examCardDetail = examCardDetailService.getByCardId(examCard.getId());
            String htmlContent;
            if (MakeMethodEnum.SELECT.equals(makeMethodEnum)) {
                htmlContent = createPdfUtil.replaceHtmlTemplete(examCardDetail);
            } else {
                BasicCardRule basicCardRule = basicCardRuleService.getById(examTask.getCardRuleId());
                htmlContent = createPdfUtil.replaceHtmlCard(examCardDetail, basicCardRule);
            }
            // html
            File localFile = new File(cardHtmlPath);
            if (!localFile.getParentFile().exists()) {
                localFile.getParentFile().mkdirs();
            }
            // 生成html文件
            FileCopyUtils.copy(htmlContent.getBytes(), localFile);
            // 转pdf文件
            File file = new File(cardPdfPath);
            if (!file.exists()) {
                file.createNewFile();
            }
            HtmlToPdfUtil.convert(cardHtmlPath, cardPdfPath, PageSizeEnum.A3);
        }

        File zipFile = new File(zipJoiner.toString() + dirNameTmp);
        if (!zipFile.getParentFile().exists()) {
            zipFile.getParentFile().mkdirs();
        }
        if (!zipFile.exists()) {
            zipFile.createNewFile();
        }

        ZipUtil.zip(zipLocalRootPath, zipFile.getPath(), false);
//        ossUtil.ossUpload(dirNameTmp, zipFile, BinaryUtil.toBase64String(HexUtils.decodeHex(DigestUtils.md5Hex(new FileInputStream(zipFile)))));
        fileStoreUtil.ossUpload(dirNameTmp, zipFile, BinaryUtil.toBase64String(HexUtils.decodeHex(DigestUtils.md5Hex(new FileInputStream(zipFile)))), fileStoreUtil.getUploadEnumByPath(dirNameTmp).getFssType());
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(SystemConstant.PATH, dirNameTmp);
        jsonObject.put(SystemConstant.TYPE, SystemConstant.OSS);
        jsonObject.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.FILE);
        tbTask.setResultFilePath(jsonObject.toJSONString());
        ConvertUtil.delFolder(zipLocalRootPath);
        zipFile.delete();
        map.put("count", 1);
        return map;
    }


    @Transactional
    @Override
    public Map<String, Object> executeImportBasicStudentLogic(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(BasicStudentImportDto.class,DescribeImportDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            List<ExcelError> excelErrorTemp = new ArrayList<>();
            Map<String, String> checkCodeMap = new HashMap<>();
            Map<String, String> checkPhoneMap = new HashMap<>();
            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> basicStudentImportDtoList = excelMap.get(i);
                assert basicStudentImportDtoList != null;
                if (basicStudentImportDtoList.get(0) instanceof DescribeImportDto){
                    continue;
                }
                for (int y = 0; y < Objects.requireNonNull(basicStudentImportDtoList).size(); y++) {
                    BasicStudentImportDto basicStudentImportDto = (BasicStudentImportDto) basicStudentImportDtoList.get(y);
                    String studentName = basicStudentImportDto.getStudentName();
                    String studentCode = basicStudentImportDto.getStudentCode();
                    String phoneNumber = basicStudentImportDto.getPhoneNumber();
                    String clazz = basicStudentImportDto.getClazz();

                    // 检验excel中
                    // 检验学号
                    if (checkCodeMap.containsKey(studentCode)) {
                        throw ExceptionResultEnum.ERROR.exception("导入的excel中包含在重复的【学生编号】：" + studentCode);
                    } else {
                        checkCodeMap.put(studentCode, studentName);
                    }
                    // 如果电话有值则检验电话excel中唯一性
                    if (SystemConstant.strNotNull(phoneNumber)){
                        if (checkPhoneMap.containsKey(phoneNumber)) {
                            throw ExceptionResultEnum.ERROR.exception("导入的excel中包含在重复的【电话号码】：" + phoneNumber);
                        } else {
                            checkPhoneMap.put(phoneNumber, studentCode);
                        }
                    }


                    excelErrorTemp.addAll(ExcelUtil.checkExcelField(basicStudentImportDto, y, i));
                    if (Objects.isNull(studentCode) || studentCode.length() > 30 || !studentCode.matches(SystemConstant.REGULAR_EXPRESSION_OF_CODE)) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[学号]不符合输入规范"));
                    }
                    if (Objects.isNull(studentName) || studentName.length() > 30) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[学生名称]不符合输入规范"));
                    }
                    if (Objects.nonNull(phoneNumber) && !phoneNumber.matches(SystemConstant.REGULAR_EXPRESSION_OF_PHONE)) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[电话号码]不符合输入规范"));
                    }
                }
            }
            if (excelErrorTemp.size() > 0) {
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(excelErrorTemp));
            }
            return finalExcelList;
        });
        return basicStudentService.executeBasicStudentImportLogic(finalList, map);
    }

    @Transactional
    @Override
    public Map<String, Object> executeImportBasicCourseLogic(Map<String, Object> map) throws Exception {

        InputStream inputStream = (InputStream) map.get("inputStream");
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(inputStream, Lists.newArrayList(BasicCourseImportDto.class, DescribeImportDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            List<ExcelError> excelErrorTemp = new ArrayList<>();
            Map<String, String> checkMap = new HashMap<>();
            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> basicCourseImportDtoList = excelMap.get(i);
                assert basicCourseImportDtoList != null;
                if (basicCourseImportDtoList.get(0) instanceof DescribeImportDto) {
                    continue;
                }
                for (int y = 0; y < Objects.requireNonNull(basicCourseImportDtoList).size(); y++) {
                    BasicCourseImportDto basicCourseImportDto = (BasicCourseImportDto) basicCourseImportDtoList.get(y);
                    String courseName = basicCourseImportDto.getCourseName();
                    String courseCode = basicCourseImportDto.getCourseCode();
                    String teachingRoomName = basicCourseImportDto.getTeachingRoomName();
                    String clazz = basicCourseImportDto.getClazz();

                    // 检验excel中
                    if (checkMap.containsKey(courseCode)) {
                        throw ExceptionResultEnum.ERROR.exception("导入的excel中包含在重复的【课程编号】：" + courseCode);
                    } else {
                        checkMap.put(courseCode, courseName);
                    }

                    excelErrorTemp.addAll(ExcelUtil.checkExcelField(basicCourseImportDto, y, i));
                    if (Objects.isNull(courseCode) || courseCode.length() > 30 || !courseCode.matches(SystemConstant.REGULAR_EXPRESSION_OF_CODE)) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程编号]不符合输入规范"));
                    }
                    if (Objects.isNull(courseName) || courseName.length() > 30) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程名称]不符合输入规范"));
                    }
                }
            }
            if (excelErrorTemp.size() > 0) {
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(excelErrorTemp));
            }
            return finalExcelList;
        });
        return basicCourseService.executeBasicCourseImportLogic(finalList, map);
    }

    @Transactional
    @Override
    public Map<String, Object> executeImportSysUserLogic(Map<String, Object> map) throws Exception {
        InputStream inputStream = (InputStream) map.get("inputStream");
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(inputStream, Lists.newArrayList(SysUserImportDto.class, DescribeImportDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            List<ExcelError> excelErrorTemp = new ArrayList<>();
            Map<String, String> checkName = new HashMap<>();
            Map<String, String> checkCode = new HashMap<>();
            Map<String, String> checkPhoneMap = new HashMap<>();
            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> sysUserImportDtoList = excelMap.get(i);
                assert sysUserImportDtoList != null;
                if (sysUserImportDtoList.get(0) instanceof DescribeImportDto) {
                    continue;
                }
                for (int y = 0; y < Objects.requireNonNull(sysUserImportDtoList).size(); y++) {
                    SysUserImportDto sysUserImportDto = (SysUserImportDto) sysUserImportDtoList.get(y);
                    String name = sysUserImportDto.getName();
                    String code = sysUserImportDto.getCode();
                    String phoneNumber = sysUserImportDto.getPhoneNumber();
                    String orgName = sysUserImportDto.getOrgName();
                    String roleName = sysUserImportDto.getRoleName();

                    // 检验excel中
                    if (checkName.containsKey(name)) {
                        throw ExceptionResultEnum.ERROR.exception("导入的excel中包含在重复的【姓名】：" + name);
                    } else {
                        checkName.put(name, code);
                    }
                    if (checkCode.containsKey(code)) {
                        throw ExceptionResultEnum.ERROR.exception("导入的excel中包含在重复的【工号】：" + code);
                    } else {
                        checkCode.put(code, name);
                    }
                    // 如果电话有值则检验电话excel中唯一性
                    if (checkPhoneMap.containsKey(phoneNumber)) {
                        throw ExceptionResultEnum.ERROR.exception("导入的excel中包含在重复的【电话号码】：" + phoneNumber);
                    } else {
                        checkPhoneMap.put(phoneNumber, code);
                    }


                    excelErrorTemp.addAll(ExcelUtil.checkExcelField(sysUserImportDto, y, i));
                    if (Objects.isNull(code) || code.length() > 30 || !code.matches(SystemConstant.REGULAR_EXPRESSION_OF_CODE)) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程编号]不符合输入规范"));
                    }
                    if (Objects.isNull(name) || name.length() > 30) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程名称]不符合输入规范"));
                    }
                    if (Objects.isNull(phoneNumber) || !phoneNumber.matches(SystemConstant.REGULAR_EXPRESSION_OF_PHONE)) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[电话号码]不符合输入规范"));
                    }
                }
            }
            if (excelErrorTemp.size() > 0) {
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(excelErrorTemp));
            }
            return finalExcelList;
        });
        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();
                    String campusName = basicClazzImportDto.getCampusName();

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

                    excelErrorTemp.addAll(ExcelUtil.checkExcelField(basicClazzImportDto, y, i));
                    if (Objects.isNull(clazzName) || clazzName.length() > 50) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[学号]不符合输入规范"));
                    }
                }
            }
            if (excelErrorTemp.size() > 0) {
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(excelErrorTemp));
            }
            return finalExcelList;
        });
        return basicClazzService.executeBasicClazzImportLogic(finalList, map);
    }


    public String createTempNumber(SerialNumberParams serialNumberParams) {
        return convertUtil.getIncre(serialNumberParams.getPrefix(), serialNumberParams.getModel(), serialNumberParams.getDigit());
    }
}
