package com.qmth.themis.business.templete.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.qmth.themis.business.cache.bean.ExamCacheBean;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.entity.TEExam;
import com.qmth.themis.business.entity.TEExamCourse;
import com.qmth.themis.business.entity.TEExamPaper;
import com.qmth.themis.business.entity.TOeExamRecord;
import com.qmth.themis.business.enums.EncryptModeEnum;
import com.qmth.themis.business.enums.ScoreStatusEnum;
import com.qmth.themis.business.enums.TaskStatusEnum;
import com.qmth.themis.business.service.TEExamCourseService;
import com.qmth.themis.business.service.TEExamPaperService;
import com.qmth.themis.business.service.TEExamService;
import com.qmth.themis.business.service.TOeExamRecordService;
import com.qmth.themis.business.templete.TaskImportCommon;
import com.qmth.themis.business.templete.TaskImportTemplete;
import com.qmth.themis.business.util.OssUtil;
import com.qmth.themis.business.util.RedisUtil;
import com.qmth.themis.business.util.UidUtil;
import com.qmth.themis.common.contanst.Constants;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.FileUtil;
import com.qmth.themis.common.util.Result;
import com.qmth.themis.common.util.ResultUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @Description: 试卷导入
 * @Author: xiatian
 * @Date: 2020-07-21
 */
@Service("taskExamPaperImportTemplete")
public class TaskExamPaperImportTemplete implements TaskImportTemplete {
    private final static Logger log = LoggerFactory.getLogger(TaskExamPaperImportTemplete.class);

    @Resource
    TEExamService teExamService;

    @Resource
    OssUtil ossUtil;

    @Resource
    TEExamCourseService teExamCourseService;

    @Resource
    TEExamPaperService teExamPaperService;

    @Resource
    TOeExamRecordService examRecordService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    UidUtil uidUtil;

    @Override
    public Result importTask(Map<String, Object> map) throws IOException {
        long start = System.currentTimeMillis();
        TaskImportCommon taskImportCommon = new TaskImportCommon(map);
        taskImportCommon.init();
        StringBuilder result = new StringBuilder();
        addResult(result, DateUtil.format(new Date(), Constants.DEFAULT_DATE_PATTERN) + "处理开始");
        File file = null;
        try {
            file = taskImportCommon.getUploadFile();
            dispose(file, map, result);
            taskImportCommon.getTbTaskHistory().setSummary("处理成功");
        } catch (Exception e) {
            taskImportCommon.setException(true);
            log.error("处理出错 ", e);
            addResult(result, "处理出错 " + e.getMessage());
            taskImportCommon.getTbTaskHistory().setSummary("处理出错");
        } finally {
            if (file != null) {
                file.delete();
            }
            addResult(result, DateUtil.format(new Date(), Constants.DEFAULT_DATE_PATTERN) + "处理结束");
            long end = System.currentTimeMillis();
            addResult(result, "耗时(秒):" + ((end - start) / 1000));
            taskImportCommon.getTbTaskHistory().setFinishTime(System.currentTimeMillis());
            taskImportCommon.getTbTaskHistory().setStatus(TaskStatusEnum.FINISH);
            taskImportCommon.getTbTaskHistory().setProgress(100.0d);
            taskImportCommon.writeImportResultTxt(result.toString());
        }
        return ResultUtil.ok(true);
    }

    @Transactional
    public void dispose(File file, Map<String, Object> map, StringBuilder result) throws IOException {

        Long examId = (Long) map.get(SystemConstant.EXAM_ID);
        ExamCacheBean examCacheBean = teExamService.getExamCacheBean(examId);
        Optional.ofNullable(examCacheBean).orElseThrow(() -> new BusinessException(ExceptionResultEnum.EXAM_NO));

        TEExam teExam = teExamService.cacheConvert(examCacheBean);
        String tempDir = SystemConstant.TEMP_FILES_DIR;
        String dir = tempDir + "/" + SystemConstant.getNanoId() + "/";
        File dfile = new File(dir);

        String lockKey = SystemConstant.REDIS_LOCK_PAPER_IMPORT_PREFIX + examId;

        String multLockKey = SystemConstant.REDIS_LOCK_CALCULATE_SCORE_PAPER_IMPORT_PREFIX + examId;
        try {
            int timeOutSecond = 60 * 60;
            //试卷导入锁
            Boolean lock = redisUtil.lock(lockKey, timeOutSecond);
            if (!lock) {
                throw new BusinessException("该考试批次有正在进行的试卷导入");
            }

            //重新算分和试卷导入互斥锁
            Boolean multLock = redisUtil.lock(multLockKey, timeOutSecond);
            if (!multLock) {
                throw new BusinessException("该考试批次有正在进行的重新算分");
            }

            dfile.mkdirs();
            FileUtil.unZip(dfile, file);
            if (!hasErr(map, dir, examId, dfile, result)) {
                for (File courseDir : dfile.listFiles()) {
                    if (courseDir.isDirectory()) {
                        disposeCourseDir(dir, teExam, courseDir, map);
                    }
                }
            } else {
                throw new BusinessException("校验失败");
            }
            TOeExamRecord er = examRecordService.findOneByExamId(examId, null);
            Boolean processAnswer = (Boolean) map.get("processAnswer");
            if (er != null && processAnswer) {
                teExamService.updateScoreStatus(ScoreStatusEnum.NEED_CALCULATE, examId);
            }
        } finally {
            redisUtil.releaseLock(lockKey);
            redisUtil.releaseLock(multLockKey);
            FileUtil.deleteFolder(dir);
        }
    }

    private void disposeCourseDir(String rootDir, TEExam teExam, File courseDir, Map<String, Object> map)
            throws IOException {
        String courseCode = courseDir.getName();
        TEExamCourse course = teExamCourseService.findByExamIdAndCourseCode(teExam.getId(), courseCode);
        if (course == null) {
            throw new BusinessException("科目编码不存在 " + courseCode);
        }
        List<Long> paperIds = new ArrayList<Long>();
        File[] childs = courseDir.listFiles();
        for (File paperDir : childs) {
            if (paperDir.isDirectory()) {
                disposePaperDir(rootDir, teExam, course, paperDir, map, paperIds);
            }
        }
        Boolean objectiveShuffle = (Boolean) map.get("objectiveShuffle");
        Boolean optionShuffle = (Boolean) map.get("optionShuffle");
        Integer audioPlayCount = (Integer) map.get("audioPlayCount");
        Integer mobilePhotoUpload = (Integer) map.get("mobilePhotoUpload");
        if (objectiveShuffle != null) {
            course.setObjectiveShuffle(objectiveShuffle ? 1 : 0);
        }
        if (optionShuffle != null) {
            course.setOptionShuffle(optionShuffle ? 1 : 0);
        }
        if (audioPlayCount != null) {
            course.setAudioPlayCount(audioPlayCount);
        }
        if (Objects.nonNull(mobilePhotoUpload)) {
            course.setMobilePhotoUpload(mobilePhotoUpload);
        }
        List<TEExamPaper> list = teExamPaperService.findByExamIdAndCourseCode(teExam.getId(), courseCode);
        int hasAnswer = 1;
        for (TEExamPaper p : list) {
            if (StringUtils.isBlank(p.getAnswerPath())) {
                hasAnswer = 0;
                break;
            }
        }
        course.setPaperCount(list.size());
        course.setHasAnswer(hasAnswer);
        int hasAudio = 0;
        for (TEExamPaper p : list) {
            if (p.getHasAudio() != null && p.getHasAudio().intValue() == 1) {
                hasAudio = 1;
                break;
            }
        }
        course.setHasAudio(hasAudio);
        teExamCourseService.saveOrUpdate(course);
        // 设置调卷比例
        teExamPaperService.savePaperWeight(paperWeight(list));

        //处理试卷缓存
        teExamPaperService.disposePaperCacheOnPaperImport(teExam.getId(), paperIds);

        //清除考试科目缓存
        teExamCourseService.deleteExamCourseCacheBean(teExam.getId(), courseCode);
    }

    private Map<Long, Double> paperWeight(List<TEExamPaper> list) {
        BigDecimal b1 = BigDecimal.valueOf(100.0);// 基数总数
        BigDecimal b2 = BigDecimal.valueOf(list.size());// 份数
        BigDecimal b3 = b1.divide(b2, 0, BigDecimal.ROUND_FLOOR);// 平均数
        BigDecimal b4 = b1.subtract(b3.multiply(b2).setScale(0, BigDecimal.ROUND_FLOOR));// 余数
        Map<Long, Double> map = new HashMap<Long, Double>();
        for (int i = 0; i < list.size(); i++) {
            TEExamPaper p = list.get(i);
            if (i == list.size() - 1) {
                map.put(p.getId(), b3.add(b4).doubleValue());
            } else {
                map.put(p.getId(), b3.doubleValue());
            }
        }
        return map;
    }

    private void disposePaperDir(String rootDir, TEExam teExam, TEExamCourse course, File paperDir,
                                 Map<String, Object> map, List<Long> paperIds) throws IOException {
        String paperCode = paperDir.getName();
        File[] childs = paperDir.listFiles();

        File paperFile = null;
        File answerFile = null;
        File attachmentDir = null;
        for (File cfile : childs) {
            if ("paper.json".equals(cfile.getName()) && cfile.isFile()) {
                paperFile = cfile;
            }
            if ("answer.json".equals(cfile.getName()) && cfile.isFile()) {
                answerFile = cfile;
            }
            if ("attachment".equals(cfile.getName()) && cfile.isDirectory()) {
                if (cfile.listFiles() != null && cfile.listFiles().length != 0) {
                    attachmentDir = cfile;
                }
            }
        }
        TEExamPaper paper = teExamPaperService
                .findByExamIdAndCourseCodeAndPaperCode(teExam.getId(), course.getCourseCode(), paperCode);
        if (paper == null) {
            paper = new TEExamPaper();
            String createId = map.get(SystemConstant.CREATE_ID).toString();
            JSONObject paperJson = JSONObject.parseObject(FileUtil.readFileContent(paperFile));
            paper.setId(uidUtil.getId());
            paper.setName(paperJson.getString(SystemConstant.NAME));
            paper.setTotalScore(paperJson.getDouble("totalScore"));
            paper.setDecryptSecret(RandomStringUtils.randomAlphanumeric(16));
            paper.setEncryptMode(EncryptModeEnum.AUTO);
            paper.setExamId(teExam.getId());
            paper.setCode(paperCode);
            paper.setHasAudio(paperJson.getInteger("hasAudio"));
            paper.setCreateId(Long.parseLong(createId));
            paper.setCourseCode(course.getCourseCode());
            paper.setDecryptVector(RandomStringUtils.randomAlphanumeric(16));
        }
        Boolean processPaper = (Boolean) map.get("processPaper");
        Boolean processAnswer = (Boolean) map.get("processAnswer");
        if (processPaper) {
            disposePaper(rootDir, paper, paperFile, attachmentDir);
            disposeStruct(rootDir, paper, paperFile);
            disposeViewPaper(rootDir, paper, paperFile, attachmentDir);
        }
        if (processAnswer) {
            File structFile;
            if (processPaper) {
                structFile = paperFile;
            } else {
                if (Objects.isNull(paper.getStructPath())) {
                    throw new BusinessException("试卷编码不匹配");
                }
                String structPath = paper.getStructPath();
                structFile = new File(rootDir + SystemConstant.getNanoId() + SystemConstant.JSON_PREFIX);
                ossUtil.download(false, structPath, structFile.getAbsolutePath());
            }
            disposeAnswer(rootDir, paper, answerFile, structFile, attachmentDir);
        }
        Integer audioPlayCount = (Integer) map.get("audioPlayCount");
        paper.setAudioPlayCount(audioPlayCount);
        teExamPaperService.saveOrUpdate(paper);
        paperIds.add(paper.getId());
    }

    private void disposeViewPaper(String rootDir, TEExamPaper paper, File paperFile, File attachmentDir)
            throws IOException {
        JSONObject structJson = JSONObject.parseObject(FileUtil.readFileContent(paperFile));
        JSONArray structdetails = structJson.getJSONArray(SystemConstant.DETAILS);
        if (structdetails == null || structdetails.size() == 0) {
            return;
        }
        for (int i = 0; i < structdetails.size(); i++) {
            JSONArray structdetailquestions = structdetails.getJSONObject(i).getJSONArray(SystemConstant.QUESTIONS);
            if (structdetailquestions != null && structdetailquestions.size() >= 0) {
                for (int j = 0; j < structdetailquestions.size(); j++) {
                    JSONObject structquestion = structdetailquestions.getJSONObject(j);
                    JSONObject body = structquestion.getJSONObject(SystemConstant.BODY);
                    disposeQuestionBodyUrl(rootDir, body, paper.getId(), attachmentDir);
                    JSONArray options = structquestion.getJSONArray(SystemConstant.OPTIONS);
                    disposeQuestionOptionsUrl(rootDir, options, paper.getId(), attachmentDir);
                    if (structquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue() == 6 || structquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue() == 8) {
                        JSONArray structsubQuestions = structquestion.getJSONArray(SystemConstant.SUB_QUESTIONS);
                        for (int k = 0; k < structsubQuestions.size(); k++) {
                            JSONObject structsubquestion = structsubQuestions.getJSONObject(k);
                            JSONObject subbody = structsubquestion.getJSONObject(SystemConstant.BODY);
                            disposeQuestionBodyUrl(rootDir, subbody, paper.getId(), attachmentDir);
                            JSONArray suboptions = structsubquestion.getJSONArray(SystemConstant.OPTIONS);
                            disposeQuestionOptionsUrl(rootDir, suboptions, paper.getId(), attachmentDir);
                        }
                    }
                }
            }
        }
        String filePath = DateUtil.format(new Date(), Constants.DATE_PATTERN) + "/" + SystemConstant.getNanoId() + SystemConstant.JSON_PREFIX;
        paper.setPaperViewPath(filePath);
        ossUtil.upload(false, filePath, structJson.toJSONString());
    }

    private void disposeQuestionBodyUrl(String rootDir, JSONObject body, Long paperId, File attachmentDir)
            throws IOException {
        if (body == null) {
            return;
        }
        JSONArray sections = body.getJSONArray("sections");
        if (sections == null || sections.size() == 0) {
            return;
        }
        for (int j = 0; j < sections.size(); j++) {
            JSONObject block = sections.getJSONObject(j);
            if (block != null) {
                JSONArray blocks = block.getJSONArray("blocks");
                if (blocks != null && blocks.size() > 0) {
                    for (int m = 0; m < blocks.size(); m++) {
                        JSONObject blockInfo = blocks.getJSONObject(m);
                        uploadImageAndAudio(rootDir, blockInfo, paperId, attachmentDir);
                    }
                }
            }
        }
    }

    private void disposeQuestionOptionsUrl(String rootDir, JSONArray options, Long paperId, File attachmentDir)
            throws IOException {
        if (options == null || options.size() == 0) {
            return;
        }
        for (int i = 0; i < options.size(); i++) {
            JSONObject option = options.getJSONObject(i);
            if (option != null) {
                JSONObject body = option.getJSONObject(SystemConstant.BODY);
                if (body != null) {
                    JSONArray sections = body.getJSONArray("sections");
                    if (sections != null && sections.size() > 0) {
                        for (int j = 0; j < sections.size(); j++) {
                            JSONObject block = sections.getJSONObject(j);
                            if (block != null) {
                                JSONArray blocks = block.getJSONArray("blocks");
                                if (blocks != null && blocks.size() > 0) {
                                    for (int m = 0; m < blocks.size(); m++) {
                                        JSONObject blockInfo = blocks.getJSONObject(m);
                                        uploadImageAndAudio(rootDir, blockInfo, paperId, attachmentDir);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private void disposePaper(String rootDir, TEExamPaper paper, File paperFile, File attachmentDir)
            throws IOException {
        if (paperFile == null) {
            return;
        }
        File zip = new File(rootDir + SystemConstant.getNanoId() + ".zip");
        List<File> files = new ArrayList<File>();
        files.add(paperFile);
        if (attachmentDir != null) {
            files.add(attachmentDir);
        }
        FileUtil.doZip(zip, files);
        File encryptZip = new File(rootDir + SystemConstant.getNanoId() + ".zip");
        FileUtil.encryptFile(zip, encryptZip, paper.getDecryptSecret(), paper.getDecryptVector());
        String filePath = DateUtil.format(new Date(), Constants.DATE_PATTERN) + "/" + SystemConstant.getNanoId() + ".zip";
        paper.setPaperPath(filePath);
        paper.setPaperMd5(BinaryUtil.encodeMD5(FileUtil.fileConvertToByteArray(encryptZip)));
        ossUtil.upload(false, filePath, encryptZip);
    }

    private void disposeStruct(String rootDir, TEExamPaper paper, File paperFile) throws IOException {
        BigDecimal obTotal = BigDecimal.valueOf(0);
        BigDecimal subTotal = BigDecimal.valueOf(0);
        if (paperFile == null) {
            return;
        }
        JSONObject paperJson = JSONObject.parseObject(FileUtil.readFileContent(paperFile));
        JSONArray details = paperJson.getJSONArray(SystemConstant.DETAILS);
        for (int i = 0; i < details.size(); i++) {
            JSONArray questions = details.getJSONObject(i).getJSONArray(SystemConstant.QUESTIONS);
            for (int j = 0; j < questions.size(); j++) {
                JSONObject question = questions.getJSONObject(j);
                question.remove(SystemConstant.BODY);
                question.remove(SystemConstant.OPTIONS);
                Double score = question.getDouble(SystemConstant.SCORE);
                if (score == null) {
                    score = 0.0;
                }
                Integer structType = question.getInteger(SystemConstant.STRUCT_TYPE);
                if (structType.intValue() != 1 && structType.intValue() != 2 && structType.intValue() != 3
                        && structType.intValue() != 6 && structType.intValue() != 8) {//主观题
                    subTotal = subTotal.add(BigDecimal.valueOf(score));
                } else {//客观题
                    obTotal = obTotal.add(BigDecimal.valueOf(score));
                }
                if (structType.intValue() == 6 || structType.intValue() == 8) {
                    JSONArray subQuestions = question.getJSONArray(SystemConstant.SUB_QUESTIONS);
                    for (int k = 0; k < subQuestions.size(); k++) {
                        JSONObject subquestion = subQuestions.getJSONObject(k);
                        subquestion.remove(SystemConstant.BODY);
                        subquestion.remove(SystemConstant.OPTIONS);
                        Double subScore = subquestion.getDouble(SystemConstant.SCORE);
                        if (subScore == null) {
                            subScore = 0.0;
                        }
                        Integer subStructType = subquestion.getInteger(SystemConstant.STRUCT_TYPE);
                        if (subStructType.intValue() != 1 && subStructType.intValue() != 2
                                && subStructType.intValue() != 3) {//主观题
                            subTotal = subTotal.add(BigDecimal.valueOf(subScore));
                        } else {//客观题
                            obTotal = obTotal.add(BigDecimal.valueOf(subScore));
                        }
                    }
                }
            }
        }
        String filePath = DateUtil.format(new Date(), Constants.DATE_PATTERN) + "/" + SystemConstant.getNanoId() + SystemConstant.JSON_PREFIX;
        paper.setStructPath(filePath);
        ByteArrayOutputStream out = null;
        try {
            out = new ByteArrayOutputStream();
            out.write(paperJson.toJSONString().getBytes(StandardCharsets.UTF_8));
            byte[] bookByteAry = out.toByteArray();
            paper.setStructMd5(BinaryUtil.encodeMD5(bookByteAry));
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
        } finally {
            if (Objects.nonNull(out)) {
                out.flush();
                out.close();
            }
        }
        paper.setTotalObjectiveScore(obTotal.doubleValue());
        paper.setTotalSubjectiveScore(subTotal.doubleValue());
        ossUtil.upload(false, filePath, paperJson.toJSONString());
    }

    private void disposeAnswer(String rootDir, TEExamPaper paper, File answerFile, File structFile, File attachmentDir)
            throws IOException {
        if (answerFile == null) {
            return;
        }
        JSONObject answerJson = JSONObject.parseObject(FileUtil.readFileContent(answerFile));
        JSONArray answerdetails = answerJson.getJSONArray(SystemConstant.DETAILS);
        JSONObject structJson = JSONObject.parseObject(FileUtil.readFileContent(structFile));
        JSONArray structdetails = structJson.getJSONArray(SystemConstant.DETAILS);
        for (int i = 0; i < answerdetails.size(); i++) {
            JSONArray answerdetailquestions = answerdetails.getJSONObject(i).getJSONArray(SystemConstant.QUESTIONS);
            JSONArray structdetailquestions = structdetails.getJSONObject(i).getJSONArray(SystemConstant.QUESTIONS);
            for (int j = 0; j < answerdetailquestions.size(); j++) {
                JSONObject answerquestion = answerdetailquestions.getJSONObject(j);
                JSONObject structquestion = structdetailquestions.getJSONObject(j);
                int structType = structquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue();
                answerquestion.put(SystemConstant.STRUCT_TYPE, structType);
                answerquestion.put(SystemConstant.SCORE, structquestion.getDouble(SystemConstant.SCORE));
                if (structType != 1 && structType != 2 && structType != 3 && structType != 6 && structType != 8) {
                    JSONArray answer = answerquestion.getJSONArray(SystemConstant.ANSWER);
                    disposeAnswerUrl(rootDir, answer, paper.getId(), attachmentDir);
                }
                if (structquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue() == 6 || structquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue() == 8) {
                    JSONArray answersubQuestions = answerquestion.getJSONArray(SystemConstant.SUB_QUESTIONS);
                    JSONArray structsubQuestions = structquestion.getJSONArray(SystemConstant.SUB_QUESTIONS);
                    for (int k = 0; k < answersubQuestions.size(); k++) {
                        JSONObject answersubquestion = answersubQuestions.getJSONObject(k);
                        JSONObject structsubquestion = structsubQuestions.getJSONObject(k);
                        int subStructType = structsubquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue();
                        answersubquestion.put(SystemConstant.STRUCT_TYPE, subStructType);
                        answersubquestion.put(SystemConstant.SCORE, structsubquestion.getDouble(SystemConstant.SCORE));
                        if (subStructType != 1 && subStructType != 2 && subStructType != 3) {
                            JSONArray answer = answersubquestion.getJSONArray(SystemConstant.ANSWER);
                            disposeAnswerUrl(rootDir, answer, paper.getId(), attachmentDir);
                        }
                    }
                }
            }
        }
        String filePath = DateUtil.format(new Date(), Constants.DATE_PATTERN) + "/" + SystemConstant.getNanoId() + SystemConstant.JSON_PREFIX;
        paper.setAnswerPath(filePath);
        ossUtil.upload(false, filePath, answerJson.toJSONString());
    }

    private void disposeAnswerUrl(String rootDir, JSONArray answer, Long paperId, File attachmentDir)
            throws IOException {
        if (answer != null && answer.size() > 0) {
            for (int i = 0; i < answer.size(); i++) {
                JSONObject section = answer.getJSONObject(i);
                if (section != null) {
                    JSONArray sections = section.getJSONArray("sections");
                    if (sections != null && sections.size() > 0) {
                        for (int j = 0; j < sections.size(); j++) {
                            JSONObject block = sections.getJSONObject(j);
                            if (block != null) {
                                JSONArray blocks = block.getJSONArray("blocks");
                                if (blocks != null && blocks.size() > 0) {
                                    for (int m = 0; m < blocks.size(); m++) {
                                        JSONObject blockInfo = blocks.getJSONObject(m);
                                        uploadImageAndAudio(rootDir, blockInfo, paperId, attachmentDir);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private void uploadImageAndAudio(String rootDir, JSONObject blockInfo, Long paperId, File attachmentDir)
            throws IOException {
        String type = blockInfo.getString(SystemConstant.TYPE);
        String value = blockInfo.getString("value");
        if (("image".equals(type) || "audio".equals(type)) && StringUtils.isNotBlank(value) && !value.toLowerCase()
                .startsWith("https://") && !value.toLowerCase().startsWith("http://")) {
            if ("image".equals(type) && value.contains("data:image")) {
//                String suff = value.substring(11, value.indexOf(";"));
//                byte[] bytes = Base64.decodeBase64(value.substring(value.indexOf(",") + 1));
//                String filePath =
//                        "upload" + File.separator + DateUtil.format(new Date(), Constants.DATE_PATTERN) + File.separator + SystemConstant.getNanoId() + "." + suff;
//                String md5 = BinaryUtil.toBase64String(BinaryUtil.calculateMd5(bytes));
//                ossUtil.upload(true, filePath, new ByteArrayInputStream(bytes), md5);
//                blockInfo.put("value", ossUtil.getAliYunOssPublicDomain().getPublicUrl() + "/" + filePath);
                blockInfo.put("value", value);
            } else {
                String suff = value.substring(value.indexOf("."));
                String filePath =
                        "upload" + File.separator + DateUtil.format(new Date(), Constants.DATE_PATTERN) + File.separator + SystemConstant.getNanoId() + "." + suff;
                File audioFile = new File(attachmentDir.getAbsolutePath() + "/" + value);
                ossUtil.upload(true, filePath, audioFile);
                blockInfo.put("value", ossUtil.getAliYunOssPublicDomain().getPublicUrl() + "/" + filePath);
            }
        }
    }

    private void addResult(StringBuilder result, String msg) {
        result.append(msg).append("\r\n");
    }

    private boolean hasErr(Map<String, Object> map, String rootDir, Long examId, File dfile, StringBuilder result) {
        Boolean processPaper = (Boolean) map.get("processPaper");
        Boolean processAnswer = (Boolean) map.get("processAnswer");
        boolean hasErr = false;
        for (File courseDir : dfile.listFiles()) {// 校验每一个科目
            if (courseDir.isDirectory()) {
                String courseCode = courseDir.getName();
                TEExamCourse course = teExamCourseService.findByExamIdAndCourseCode(examId, courseCode);
                if (course == null || !Objects.equals(course.getCourseCode(), courseCode)) {
                    addResult(result, "科目编码不存在 " + courseCode);
                    hasErr = true;
                    continue;
                }
                File[] childs = courseDir.listFiles();
                if (childs == null || childs.length == 0) {
                    addResult(result, "科目 " + courseCode + " 没有需要导入的文件");
                    hasErr = true;
                    continue;
                }
                for (File paperDir : childs) {// 校验每一套试卷
                    if (paperDir.isDirectory()) {
                        String paperCode = paperDir.getName();
                        File[] paperDirChilds = paperDir.listFiles();
                        if (paperDirChilds == null || paperDirChilds.length == 0) {
                            addResult(result, "科目 " + courseCode + " 试卷 " + paperCode + " 没有需要导入的文件");
                            hasErr = true;
                            continue;
                        }
                        if (processPaper) {
                            if (checkPaperHasExamRecord(examId, courseCode, paperCode)) {
                                addResult(result, "科目 " + courseCode + " 试卷 " + paperCode + " 已有考试记录，不能再上传试卷");
                                hasErr = true;
                                continue;
                            }
                            String[] ps = paperDir.list(new FilenameFilter() {

                                @Override
                                public boolean accept(File dir, String name) {
                                    return name.equals("paper.json");
                                }
                            });
                            if (ps == null || ps.length == 0) {
                                addResult(result, "科目 " + courseCode + " 试卷 " + paperCode + " 没有试卷文件");
                                hasErr = true;
                                continue;
                            }
                        }
                        if (processAnswer) {
                            String[] ps = paperDir.list(new FilenameFilter() {

                                @Override
                                public boolean accept(File dir, String name) {
                                    return name.equals("answer.json");
                                }
                            });
                            if (ps == null || ps.length == 0) {
                                addResult(result, "科目 " + courseCode + " 试卷 " + paperCode + " 没有答案文件");
                                hasErr = true;
                                continue;
                            }
                            try {
                                // 校验答案
                                checkAnswerFile(rootDir, paperDirChilds, examId, courseCode, paperCode);
                            } catch (Exception e) {
                                addResult(result, e.getMessage());
                                hasErr = true;
                                continue;
                            }
                        }
                    }
                }
            }
        }
        return hasErr;
    }

    private boolean checkPaperHasExamRecord(Long examId, String courseCode, String paperCode) {
        TEExamPaper paper = teExamPaperService.findByExamIdAndCourseCodeAndPaperCode(examId, courseCode, paperCode);
        if (paper == null) {
            return false;
        }
        TOeExamRecord rd = examRecordService.findOneByPaperId(paper.getId());
        if (rd != null) {
            return true;
        }
        return false;
    }

    private void checkAnswerFile(String rootDir, File[] paperDirChilds, Long examId, String courseCode,
                                 String paperCode) {
        File paperFile = null;
        File answerFile = null;
        for (File cfile : paperDirChilds) {// 校验试卷下的答案
            if ("paper.json".equals(cfile.getName()) && cfile.isFile()) {
                paperFile = cfile;
            }
            if ("answer.json".equals(cfile.getName()) && cfile.isFile()) {
                answerFile = cfile;
            }
        }
        if (answerFile != null) {
            if (paperFile == null) {// 没有试卷信息，不能先导入答案
                TEExamPaper paper = teExamPaperService
                        .findByExamIdAndCourseCodeAndPaperCode(examId, courseCode, paperCode);
                if (paper == null) {
                    throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 没有试卷信息，不能先导入答案");
                }
                String structPath = paper.getStructPath();
                String name = structPath.substring(structPath.lastIndexOf("/") + 1);
                File structFile = new File(rootDir + name);
                try {
                    ossUtil.download(false, structPath, structFile.getAbsolutePath());
                    compareAnswerAndPaper(courseCode, paperCode, answerFile, structFile);
                } finally {
                    if (structFile.exists()) {
                        structFile.delete();
                    }
                }
            } else {
                compareAnswerAndPaper(courseCode, paperCode, answerFile, paperFile);
            }
        }
    }

    private void compareAnswerAndPaper(String courseCode, String paperCode, File answerFile, File structFile) {
        JSONObject answerJson = JSONObject.parseObject(FileUtil.readFileContent(answerFile));
        JSONArray answerdetails = answerJson.getJSONArray(SystemConstant.DETAILS);
        JSONObject structJson = JSONObject.parseObject(FileUtil.readFileContent(structFile));
        JSONArray structdetails = structJson.getJSONArray(SystemConstant.DETAILS);
        if (answerdetails.size() != structdetails.size()) {
            throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷大题数量不一致");
        }
        for (int i = 0; i < answerdetails.size(); i++) {
            JSONArray answerdetailquestions = answerdetails.getJSONObject(i).getJSONArray(SystemConstant.QUESTIONS);
            JSONArray structdetailquestions = structdetails.getJSONObject(i).getJSONArray(SystemConstant.QUESTIONS);
            if (answerdetailquestions.size() != structdetailquestions.size()) {
                throw new BusinessException(
                        "科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1) + "大题的小题数量不一致");
            }
            for (int j = 0; j < answerdetailquestions.size(); j++) {
                JSONObject answerquestion = answerdetailquestions.getJSONObject(j);
                JSONObject structquestion = structdetailquestions.getJSONObject(j);
                if (structquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue() == 1
                        || structquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue() == 2) {
                    JSONArray answer = answerquestion.getJSONArray(SystemConstant.ANSWER);
                    if (answer == null || answer.size() == 0) {
                        throw new BusinessException(
                                "科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1) + "大题第" + (j + 1)
                                        + "小题没有答案");
                    }
                }
                if (structquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue() == 3) {
                    Boolean answer = answerquestion.getBoolean(SystemConstant.ANSWER);
                    if (answer == null) {
                        throw new BusinessException(
                                "科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1) + "大题第" + (j + 1)
                                        + "小题没有答案");
                    }
                }
                if (structquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue() == 6) {
                    JSONArray answersubQuestions = answerquestion.getJSONArray(SystemConstant.SUB_QUESTIONS);
                    JSONArray structsubQuestions = structquestion.getJSONArray(SystemConstant.SUB_QUESTIONS);
                    if (answersubQuestions.size() != structsubQuestions.size()) {
                        throw new BusinessException(
                                "科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1) + "大题第" + (j + 1)
                                        + "小题子题数量不一致");
                    }
                    for (int k = 0; k < answersubQuestions.size(); k++) {
                        JSONObject answersubquestion = answersubQuestions.getJSONObject(k);
                        JSONObject structsubquestion = structsubQuestions.getJSONObject(k);
                        if (structsubquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue() == 1
                                || structsubquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue() == 2) {
                            JSONArray answer = answersubquestion.getJSONArray(SystemConstant.ANSWER);
                            if (answer == null || answer.size() == 0) {
                                throw new BusinessException(
                                        "科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1) + "大题第" + (j + 1)
                                                + "小题第" + (k + 1) + "子题没有答案");
                            }
                        }
                        if (structsubquestion.getInteger(SystemConstant.STRUCT_TYPE).intValue() == 3) {
                            Boolean answer = answersubquestion.getBoolean(SystemConstant.ANSWER);
                            if (answer == null) {
                                throw new BusinessException(
                                        "科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1) + "大题第" + (j + 1)
                                                + "小题第" + (k + 1) + "子题没有答案");
                            }
                        }
                    }
                }
            }
        }
    }
}