package cn.com.qmth.examcloud.core.questions.service.impl;


import cn.com.qmth.examcloud.api.commons.security.bean.User;
import cn.com.qmth.examcloud.commons.exception.StatusException;
import cn.com.qmth.examcloud.core.questions.base.BeanCopierUtil;
import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
import cn.com.qmth.examcloud.core.questions.base.Model;
import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
import cn.com.qmth.examcloud.core.questions.base.question.*;
import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
import cn.com.qmth.examcloud.core.questions.dao.*;
import cn.com.qmth.examcloud.core.questions.dao.entity.*;
import cn.com.qmth.examcloud.core.questions.service.*;
import cn.com.qmth.examcloud.core.questions.service.bean.*;
import cn.com.qmth.examcloud.core.questions.service.config.UpYunProperty;
import cn.com.qmth.examcloud.examwork.api.ExamCloudService;
import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
import cn.com.qmth.examcloud.examwork.api.bean.ExamCourseRelationBean;
import cn.com.qmth.examcloud.examwork.api.request.GetExamCourseListReq;
import cn.com.qmth.examcloud.examwork.api.request.GetExamReq;
import cn.com.qmth.examcloud.examwork.api.response.GetExamCourseListResp;
import cn.com.qmth.examcloud.examwork.api.response.GetExamResp;
import cn.com.qmth.examcloud.web.redis.RedisClient;
import org.apache.commons.lang3.StringUtils;
import org.nlpcn.commons.lang.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static cn.com.qmth.examcloud.core.questions.service.cache.Constants.CACHE_PREFIX_EXTRACT_EXAM_PAPER;

/**
 * @author chenken
 * @date 2017年4月14日 下午6:07:55
 * @company QMTH
 * @description 调卷规则service
 */
@Service("extractConfigService")
public class ExtractConfigServiceImpl implements ExtractConfigService {

    private static final Logger logger = LoggerFactory.getLogger(ExtractConfigServiceImpl.class);

    @Autowired
    private ExtractConfigRepo extractConfigRepo;

    @Autowired
    private PaperDetailRepo paperDetailRepo;

    @Autowired
    private PaperDetailUnitRepo paperDetailUnitRepo;

    @Autowired
    private AudioTimeConfigRepo audioTimeConfigRepo;

    @Autowired
    private PaperDetailUnitService paperDetailUnitService;

    @Autowired
    private PaperService paperService;

    @Autowired
    private PaperRepo paperRepo;

    @Autowired
    private QuesService quesService;

    @Autowired
    private QuesRepo quesRepo;

    @Autowired
    private QuestionAudioService questionAudioService;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private ExportServiceManageRepo exportServiceManageRepo;

    @Autowired
    private ExportStructureService exportStructureService;

    @Autowired
    private CourseService courseService;

    @Autowired
    private UpYunProperty upYunProperty;

    @Autowired
    private PaperDtoAssembler paperDtoAssembler;

    @Autowired
    private PaperDetailDtoAssembler paperDetailDtoAssembler;

    @Autowired
    private PaperDetailUnitDtoAssembler paperDetailUnitDtoAssembler;

    @Autowired
    private SubQuestionDtoAssembler subQuestionDtoAssembler;

    @Autowired
    private RedisClient redisClient;

    @Autowired
    private PaperDetailUnitNativeRepo detailUnitNativeRepo;

    @Autowired
    private ExamCloudService examCloudService;

    @Override
    public ExtractConfig findConfig(ExtractConfig condition) {
        if (condition.getExamId() == null) {
            return null;
        }
        if (StringUtils.isBlank(condition.getCourseCode())) {
            return null;
        }
        Query query = new Query();
        if (!StringUtils.isBlank(condition.getOrgId())) {
            query.addCriteria(Criteria.where("orgId").is(condition.getOrgId()));
        }
        query.addCriteria(Criteria.where("examId").is(condition.getExamId()));
        query.addCriteria(Criteria.where("courseCode").is(condition.getCourseCode()));
        ExtractConfig tempConfig = this.mongoTemplate.findOne(query, ExtractConfig.class);
        return tempConfig;
    }

    @Override
    public Map<String, String> saveExtractConfig2(ExtractConfig extractConfig, User user) throws Exception {
        List<ExamPaper> examPapers = extractConfig.getExamPaperList();
        for (int i = 0; i < examPapers.size(); i++) {
            ExamPaper examPaper = examPapers.get(i);
            Paper paper = examPaper.getPaper();
            paper = Model.of(paperRepo.findById(paper.getId()));
            examPaper.setPaper(paper);
        }
        Course course = courseService.getCourse(Long.valueOf(extractConfig.getOrgId()), extractConfig.getCourseCode());
        extractConfig.setCourse(course);
        extractConfig.setCourseName(course.getName());
        Map<String, String> newFinishedPaperIdMap = makePaperByConfig(extractConfig);
        extractConfig.setFinishedPaperIdMap(newFinishedPaperIdMap);
        extractConfig.setIfFinish((short) 1);
        extractConfigRepo.save(extractConfig);
        return newFinishedPaperIdMap;
    }

    @Override
    public List<ExamPaper> saveExtractConfig(ExtractConfig extractConfig, User user) throws Exception {
        List<ExamPaper> examPapers = extractConfig.getExamPaperList();
        for (int i = 0; i < examPapers.size(); i++) {
            ExamPaper examPaper = examPapers.get(i);
            Paper paper = examPaper.getPaper();
            paper = Model.of(paperRepo.findById(paper.getId()));
            examPaper.setPaper(paper);
        }
        Course course = courseService.getCourse(Long.valueOf(extractConfig.getOrgId()), extractConfig.getCourseCode());
        extractConfig.setCourse(course);
        extractConfig.setCourseName(course.getName());
        extractConfig.setIfFinish((short) 1);
        extractConfigRepo.save(extractConfig);
        return examPapers;
    }


    @Override
    public ExtractConfig findConfigById(String id) {
        if (StringUtils.isBlank(id)) {
            return null;
        }
        return Model.of(extractConfigRepo.findById(id));
    }

    @Override
    public Map<String, Object> extractExamPaper(Long exam_id, String course_code, String group_code) {
        Map<String, Object> returnMap = new HashMap<String, Object>();
        logger.info("调卷开始...");
        long beginTime = System.currentTimeMillis();
        logger.info("开始根据examId:" + exam_id + "和courseCode:" + course_code + "获取调卷规则");
        ExtractConfig extractConfig = this.findConfig(new ExtractConfig(exam_id, course_code));
        if (extractConfig == null) {
            logger.error("该考试和课程下调卷规则未制定,请先制定调卷规则,调卷程序退出");
            returnMap.put("errorMsg", "该考试和课程下调卷规则未制定,请先制定调卷规则");
            return returnMap;
        }
        long configFinishTime = System.currentTimeMillis();
        logger.info("获取调卷规则共耗时:" + (configFinishTime - beginTime) + "ms");
        logger.info("根据调卷规则中设置的概率获取类型为" + group_code + "的试卷");
        Map<String, Paper> paperMap = this.getExamPaperByProbability(extractConfig.getExamPaperList());
        if (paperMap.isEmpty()) {
            logger.error("该考试和课程下调卷规则中试卷不存在,请检查调卷规则,调卷程序退出");
            returnMap.put("errorMsg", "该考试和课程下调卷规则中试卷不存在,请重新制定调卷规则");
            return returnMap;
        }

        long paperMapFinishTime = System.currentTimeMillis();
        logger.info("获取类型为" + group_code + "的试卷共耗时:" + (paperMapFinishTime - configFinishTime) + "ms");

        Paper basePaper = paperMap.get(group_code);
        if (basePaper == null) {
            logger.error("该考试和课程下调卷规则中该类型试卷不存在,请检查调卷规则,调卷程序退出");
            returnMap.put("errorMsg", "该考试和课程下调卷规则中该类型试卷不存在,请重新制定调卷规则");
            return returnMap;
        }
        String basePaperId = basePaper.getId();
        logger.info("将原始试卷:" + basePaperId + "根据规则重新组卷");
        int upSetQuestionOrder = extractConfig.getScrambling_the_question_order();
        int upSetOptionOrder = extractConfig.getScrambling_the_option_order();
        //不乱序直接调卷
        if (upSetQuestionOrder == 0 && upSetOptionOrder == 0) {
            PaperDto paperDto = getPaperDtoByPaper(basePaper, basePaperId);
            long paperDtoFinishTime = System.currentTimeMillis();
            logger.info("获取试卷Dto共耗时:" + (paperDtoFinishTime - paperMapFinishTime) + "ms");
            returnMap.put("paperDto", paperDto);
            logger.info("调卷完成");
            logger.info("总共耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
        } else {
            //乱序重新生成试卷
            Paper newPaper = this.recombinationPaper(basePaper, PaperType.STUDENT_EXAM, upSetQuestionOrder, upSetOptionOrder);
            logger.info("根据新试卷 paperId:" + newPaper.getId() + "组装PaperDto后返回");

            long genPaperFinishTime = System.currentTimeMillis();
            logger.info("组卷共耗时:" + (genPaperFinishTime - paperMapFinishTime) + "ms");
            PaperDto paperDto = getPaperDtoByPaper(newPaper, basePaperId);

            long paperDtoFinishTime = System.currentTimeMillis();
            logger.info("获取试卷Dto共耗时:" + (paperDtoFinishTime - genPaperFinishTime) + "ms");

            returnMap.put("paperDto", paperDto);
            logger.info("调卷完成");
            logger.info("总共耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
        }
        return returnMap;
    }

    @Override
    public boolean checkIsAllQbjectiveQuestion(String paperId) {
        //优先从redis中获取缓存dto
        PaperDto cachePaperDto = redisClient.get(CACHE_PREFIX_EXTRACT_EXAM_PAPER + paperId, PaperDto.class, 10 * 60);
        if (cachePaperDto != null) {
            return cachePaperDto.isAllQbjectiveQuestion();
        }

        Paper paper = Model.of(paperRepo.findById(paperId));
        List<PaperDetailUnit> paperDetailUnits = paperDetailUnitRepo.findByPaperOrderByNumber(paper);
        for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
            Question question = paperDetailUnit.getQuestion();
            //填空或问答
            if (question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
                    || question.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
                return false;
            }
            if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
                List<Question> subQuestions = question.getSubQuestions();
                for (Question subQuestion : subQuestions) {
                    if (subQuestion.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
                            || subQuestion.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    public boolean checkIsAllQbjectiveByPdu(List<PaperDetailUnit> paperDetailUnits) {
        for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
            Question question = paperDetailUnit.getQuestion();
            //填空或问答
            if (question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
                    || question.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
                return false;
            }
            if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
                List<Question> subQuestions = question.getSubQuestions();
                for (Question subQuestion : subQuestions) {
                    if (subQuestion.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
                            || subQuestion.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    @Override
    public Map<String, String> makePaperByConfig(ExtractConfig extractConfig) {
        Map<String, String> finishedPaperIdMap = new HashMap<>();
        if (extractConfig == null) {
            throw new RuntimeException("调卷规则不存在");
        }
        //获得规则中设置的试卷
        Map<String, Paper> paperMap = this.getExamPaperByProbability(extractConfig.getExamPaperList());
        if (paperMap.isEmpty()) {
            throw new RuntimeException("抽取试卷失败");
        }
        for (Map.Entry<String, Paper> entry : paperMap.entrySet()) {
            String key = entry.getKey();
            //根据原有试卷重新组卷得到新试卷
        	/*Paper newPaper = this.recombinationPaper(entry.getValue(), 
             PaperType.PREVIEW,
             extractConfig.getScrambling_the_question_order(),
             extractConfig.getScrambling_the_option_order());*/
            finishedPaperIdMap.put(key, entry.getValue().getId());
        }
        return finishedPaperIdMap;
    }

    /**
     * 重组试卷，生成新的试卷
     *
     * @param paper              选中的试卷
     * @param upSetQuestionOrder 客观题小题乱序		1:乱序  0：不乱序
     * @param upSetOptionOrder   客观题选项乱序		1:乱序  0：不乱序
     * @return
     */
    public Paper recombinationPaper(Paper paper, PaperType paperType, int upSetQuestionOrder, int upSetOptionOrder) {

        //将小题全部取出来,只取一次
        List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaperOrderByNumber(paper);
        //获取大题
        List<PaperDetail> paperDetails = paperDetailRepo.findByPaperOrderByNumber(paper);

        //抽取大题号对应的小题
        Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
                .collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
        //最终保存的所有小题
        List<PaperDetailUnit> savePaperDetailUnits = new ArrayList<>();
        //保存试卷信息
        paper.setId(null);
        paper.setPaperType(paperType);
        Paper newPaper = paperRepo.insert(paper);

        for (int i = 0; i < paperDetails.size(); i++) {
            PaperDetail paperDetail = paperDetails.get(i);

            List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());
            if (paperDetailUnits == null || paperDetailUnits.size() == 0) {
                continue;
            }
            Collections.sort(paperDetailUnits);

            //将大题中最小的number取出
            PaperDetailUnit topDetailUnit = paperDetailUnits.get(0);
            int minNumber = topDetailUnit.getNumber();

            //小题乱序
            if (paperDetailUnits != null && paperDetailUnits.size() > 0) {
                if ((topDetailUnit.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                        || topDetailUnit.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
                        || topDetailUnit.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION)
                        && upSetQuestionOrder == 1) {
                    Collections.shuffle(paperDetailUnits);//打乱小题List
                }
            }
            //设置大题信息
            paperDetail.setId(null);
            paperDetail.setPaper(newPaper);

            for (int j = 0; j < paperDetailUnits.size(); j++) {
                //重新设置保存PaperDetailUnit
                PaperDetailUnit paperDetailUnit = paperDetailUnits.get(j);
                paperDetailUnit.setPaperType(paperType);
                paperDetailUnit.setPaper(newPaper);
                paperDetailUnit.setPaperDetail(paperDetail);
                paperDetailUnit.setNumber(minNumber + j);            //重新设置序号
                reSavePaperDetailUtilAndQuestion(paperDetailUnit, upSetOptionOrder);
                savePaperDetailUnits.add(paperDetailUnit);
            }

        }
        //保存大题信息
        paperDetailRepo.insert(paperDetails);
        //保存小题信息
        paperDetailUnitRepo.insert(savePaperDetailUnits);


        //清空所有list
        allPaperDetailUnits.clear();
        savePaperDetailUnits.clear();
        paperDetails.clear();

        return newPaper;
    }

    /**
     * 每个试卷类型取出一套试卷
     * {
     * A:Paper,
     * B:Paper
     * }
     * A是试卷类型   Paper是A类型下选定的试卷
     *
     * @param examPaperList
     * @return
     */
    private Map<String, Paper> getExamPaperByProbability(List<ExamPaper> examPaperList) {
        Map<String, Paper> paperByTypeMap = new HashMap<String, Paper>();
        if (examPaperList == null || examPaperList.size() == 0) {
            throw new RuntimeException("可供抽取的试卷集合为空,无法抽取试卷");
        }
        Map<String, List<ExamPaper>> examPaperMap = new HashMap<String, List<ExamPaper>>();
        for (int i = 0; i < examPaperList.size(); i++) {
            ExamPaper examPaper = examPaperList.get(i);
            if (!examPaperMap.containsKey(examPaper.getGroupCode())) {
                if (examPaper.getPaper() != null) {
                    List<ExamPaper> epList = new ArrayList<ExamPaper>();
                    epList.add(examPaper);
                    examPaperMap.put(examPaper.getGroupCode(), epList);
                }
            } else {
                if (examPaper.getPaper() != null) {
                    List<ExamPaper> epList = examPaperMap.get(examPaper.getGroupCode());
                    epList.add(examPaper);
                }
            }
        }
        if (examPaperMap != null) {
            Set<String> keys = examPaperMap.keySet();
            Iterator<String> it = keys.iterator();
            while (it.hasNext()) {
                String key = it.next();
                Paper paper = this.getPaperByProbability(examPaperMap.get(key));
                //不能用原来的paper对象，否则examPaperList中的paper对象会被覆盖
                Paper newPaper = Model.of(paperRepo.findById(paper.getId()));
                paperByTypeMap.put(key, newPaper);
            }
        }
        return paperByTypeMap;
    }

    /**
     * 根据设定几率取出一套试卷
     *
     * @param examPaperList
     * @return
     */
    private Paper getPaperByProbability(List<ExamPaper> examPaperList) {
        int sum = 0;
        for (int i = 0; i < examPaperList.size(); i++) {
            sum += examPaperList.get(i).getWeight();
        }
        // 从1开始  
        Integer rand = new Random().nextInt(sum) + 1;
        for (int i = 0; i < examPaperList.size(); i++) {
            rand -= examPaperList.get(i).getWeight();
            // 选中
            if (rand <= 0) {
                return examPaperList.get(i).getPaper();
            }
        }
        return null;
    }

    /**
     * 重新设置并保存paperDetailUnit和question
     *
     * @param paperDetailUnit
     * @param upSetOptionOrder
     */
    private void reSavePaperDetailUtilAndQuestion(PaperDetailUnit paperDetailUnit, Integer upSetOptionOrder) {
        Question question = paperDetailUnit.getQuestion();
        //选项乱序
        if (upSetOptionOrder == 1) {
            //单选或多选
            if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION ||
                    question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
                List<String> numberList = new ArrayList<String>();
                List<QuesOption> options = question.getQuesOptions();
                for (int k = 0; k < options.size(); k++) {
                    QuesOption quesOption = options.get(k);
                    numberList.add(quesOption.getNumber());
                }
                Collections.shuffle(numberList);                      //打乱number顺序
                paperDetailUnit.setOptionOrder(StringUtils.join(numberList.toArray(), ","));//设置option顺序
            }
            //套题，套题下小题不乱序，选择题选项乱序
            if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
                List<Question> subQuestions = question.getSubQuestions();
                StringBuffer optionOrder = new StringBuffer();
                for (int m = 0; m < subQuestions.size(); m++) {
                    Question subQuestion = subQuestions.get(m);
                    if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION ||
                            subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
                        List<String> numberList = new ArrayList<String>();
                        List<QuesOption> options = subQuestion.getQuesOptions();
                        for (int n = 0; n < options.size(); n++) {
                            QuesOption quesOption = options.get(n);
                            numberList.add(quesOption.getNumber());
                        }
                        Collections.shuffle(numberList);                      //打乱number顺序
                        optionOrder.append(StringUtils.join(numberList.toArray(), ",")).append(";");
                    }
                }
                paperDetailUnit.setOptionOrder(optionOrder.toString());         //设置option顺序
            }
        }
        paperDetailUnit.setId(null);
    }

    @Override
    public List<String> getExamPaperId(String courseCode, String orgId) {
        Assert.hasLength(courseCode, "courseCode不能为空");
        Assert.hasLength(orgId, "orgId不能为空");
        List<String> paperIdList = new ArrayList<String>();
        Query query = new Query();
        query.addCriteria(Criteria.where("courseCode").is(courseCode));
        query.addCriteria(Criteria.where("orgId").is(orgId));
        List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
        for (ExtractConfig extractConfig : extractConfigList) {
            List<ExamPaper> examPaperList = extractConfig.getExamPaperList();
            if (examPaperList != null && examPaperList.size() > 0) {
                for (ExamPaper examPaper : examPaperList) {
                    paperIdList.add(examPaper.getPaper().getId());
                }
            }
        }
        return paperIdList;
    }

    /**
     * 根据paper得到PaperDto
     *
     * @param paper
     * @return
     */
    @SuppressWarnings("unchecked")
    private PaperDto getPaperDtoByPaper(Paper paper, String basePaperId) {
        long beginTime = System.currentTimeMillis();
        //没有则重新组装
        PaperDto paperDto = paperDtoAssembler.toDto(paper);
        paperDto.setBasePaperId(basePaperId);
        paperDto.setAllQbjectiveQuestion(checkIsAllQbjectiveQuestion(basePaperId));

        long paperDtoEndTime = System.currentTimeMillis();
        logger.info("单独组装paperDto耗时:" + (paperDtoEndTime - beginTime) + "ms");

        //将小题全部取出来,只取一次
        List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaper(paper);
        long pduEndTime = System.currentTimeMillis();
        logger.info("数据库取小题耗时:" + (pduEndTime - paperDtoEndTime) + "ms");
        Collections.sort(allPaperDetailUnits);
        long pduSortEndTime = System.currentTimeMillis();
        logger.info("排序小题耗时:" + (pduSortEndTime - pduEndTime) + "ms");

        //获取大题
        List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(paper);
        long pdEndTime = System.currentTimeMillis();
        logger.info("数据库取大题耗时:" + (pdEndTime - pduSortEndTime) + "ms");
        Collections.sort(paperDetails);
        long pdSortEndTime = System.currentTimeMillis();
        logger.info("排序大题耗时:" + (pdSortEndTime - pdEndTime) + "ms");

        //抽取大题Id对应的小题
        Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
                .collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
        long pduMapEndTime = System.currentTimeMillis();
        logger.info("获取大题与小题对应关系耗时:" + (pduMapEndTime - pdSortEndTime) + "ms");

        // 获取大题Dto
        List<PaperDetailDto> paperDetailDtos = paperDetailDtoAssembler.toDtoList(paperDetails);
        paperDto.setPaperDetails(paperDetailDtos);
        long paperDetailDtoEndTime = System.currentTimeMillis();
        logger.info("单独组装paperDetailDto耗时:" + (paperDetailDtoEndTime - pduMapEndTime) + "ms");

        // 封装小题
        for (int i = 0; i < paperDetailDtos.size(); i++) {
            //根据大题查出大题下面的小题
            PaperDetail paperDetail = paperDetails.get(i);

            List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());

            List<PaperDetailUnitDto> paperDetailUnitDtos = new ArrayList<>();
            for (int j = 0; j < paperDetailUnits.size(); j++) {
                PaperDetailUnit paperDetailUnit = paperDetailUnits.get(j);
                if (paperDetailUnit == null || paperDetailUnit.getQuestion() == null) {
                    break;
                }
                //设置答案
                setSelectQuestionAnswerUnit(paperDetailUnit);

                PaperDetailUnitDto unitDto = paperDetailUnitDtoAssembler.toDto(paperDetailUnit);
                /**
                 * 此处不能传questionId，需要传paperDetailUnitId
                 * 因为选项乱序在paperDetailUnit里
                 * unitDto.setQuesId(paperDetailUnit.getQuestion().getId());
                 */
                unitDto.setQuesId(paperDetailUnit.getId());
                String answer = paperDetailUnit.getQuestion().getQuesAnswer();
                if (StringUtils.isNotEmpty(answer)) {
                    unitDto.setAnswer(answer);
                }
                if (unitDto.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {// 假如是套题
                    List<Question> subQuesList = paperDetailUnit.getQuestion().getSubQuestions();
                    List<SubQuestionDto> subQuesDtos = subQuestionDtoAssembler.toDtoList(subQuesList);
                    for (int m = 0; m < subQuesList.size(); m++) {
                        List<QuesOptionDto> quesOptionDtos = subQuestionDtoAssembler.toOptionDtoList(subQuesList.get(m).getQuesOptions());
                        subQuesDtos.get(m).setQuesOptions(quesOptionDtos);
                        if (StringUtils.isNotEmpty(subQuesList.get(m).getQuesAnswer())) {
                            subQuesDtos.get(m).setQuesAnswer(subQuesList.get(m).getQuesAnswer());
                        }
                        subQuesDtos.get(m).setNumber(m + 1);
                        //套题分数从小题类中取值
                        subQuesDtos.get(m).setScore(paperDetailUnit.getSubScoreList().get(m));
                    }
                    unitDto.setSubQuestions(subQuesDtos);
                }
                paperDetailUnitDtos.add(unitDto);
            }
            paperDetailDtos.get(i).setPaperDetailUnits(paperDetailUnitDtos);
            paperDetailDtos.get(i).setCnNum(CommonUtils.toCHNum(paperDetailDtos.get(i).getNumber()));
        }
        long paperDetailUnitDtoEndTime = System.currentTimeMillis();
        logger.info("单独组装paperDetailUnitDto耗时:" + (paperDetailUnitDtoEndTime - paperDetailDtoEndTime) + "ms");

        //将重新组装的dto放进缓存
        redisClient.set(CACHE_PREFIX_EXTRACT_EXAM_PAPER + paperDto.getId(), paperDto, 10 * 60);
        return paperDto;
    }

    /**
     * 测试获取paperDto
     *
     * @param basePaperId
     * @return
     */
    public PaperDto getPaperDtoByPaperNew(String basePaperId) {

        long beginTime = System.currentTimeMillis();
        //没有则重新组装
        Paper paper = Model.of(paperRepo.findById(basePaperId));
        PaperDto paperDto = paperDtoAssembler.toDto(paper);
        paperDto.setBasePaperId(basePaperId);

        long paperDtoEndTime = System.currentTimeMillis();
        logger.info("单独组装paperDto耗时:" + (paperDtoEndTime - beginTime) + "ms");

        //将小题全部取出来,只取一次
        List<PaperDetailUnit> allPaperDetailUnits = detailUnitNativeRepo.findByPaperId(paper.getId());
        long pduEndTime = System.currentTimeMillis();
        logger.info("数据库取小题耗时:" + (pduEndTime - paperDtoEndTime) + "ms");
        Collections.sort(allPaperDetailUnits);
        long pduSortEndTime = System.currentTimeMillis();
        logger.info("排序小题耗时:" + (pduSortEndTime - pduEndTime) + "ms");


        paperDto.setAllQbjectiveQuestion(checkIsAllQbjectiveByPdu(allPaperDetailUnits));
        long isAllObjEndtime = System.currentTimeMillis();
        logger.info("设置客观题耗时:" + (isAllObjEndtime - pduSortEndTime) + "ms");


        //获取大题
        List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(paper);
        long pdEndTime = System.currentTimeMillis();
        logger.info("数据库取大题耗时:" + (pdEndTime - isAllObjEndtime) + "ms");
        Collections.sort(paperDetails);
        long pdSortEndTime = System.currentTimeMillis();
        logger.info("排序大题耗时:" + (pdSortEndTime - pdEndTime) + "ms");

        //抽取大题Id对应的小题
        Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
                .collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
        long pduMapEndTime = System.currentTimeMillis();
        logger.info("获取大题与小题对应关系耗时:" + (pduMapEndTime - pdSortEndTime) + "ms");

        // 获取大题Dto
        List<PaperDetailDto> paperDetailDtos = paperDetailDtoAssembler.toDtoList(paperDetails);
        paperDto.setPaperDetails(paperDetailDtos);
        long paperDetailDtoEndTime = System.currentTimeMillis();
        logger.info("单独组装paperDetailDto耗时:" + (paperDetailDtoEndTime - pduMapEndTime) + "ms");

        // 封装小题
        for (int i = 0; i < paperDetailDtos.size(); i++) {
            //根据大题查出大题下面的小题
            PaperDetail paperDetail = paperDetails.get(i);

            List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());

            List<PaperDetailUnitDto> paperDetailUnitDtos = new ArrayList<>();
            for (int j = 0; j < paperDetailUnits.size(); j++) {
                PaperDetailUnit paperDetailUnit = paperDetailUnits.get(j);
                if (paperDetailUnit == null || paperDetailUnit.getQuestion() == null) {
                    break;
                }
                //设置答案
                setSelectQuestionAnswerUnit(paperDetailUnit);

                PaperDetailUnitDto unitDto = paperDetailUnitDtoAssembler.toDto(paperDetailUnit);
                /**
                 * 此处不能传questionId，需要传paperDetailUnitId
                 * 因为选项乱序在paperDetailUnit里
                 * unitDto.setQuesId(paperDetailUnit.getQuestion().getId());
                 */
                unitDto.setQuesId(paperDetailUnit.getId());
                String answer = paperDetailUnit.getQuestion().getQuesAnswer();
                if (StringUtils.isNotEmpty(answer)) {
                    unitDto.setAnswer(answer);
                }
                if (unitDto.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {// 假如是套题
                    List<Question> subQuesList = paperDetailUnit.getQuestion().getSubQuestions();
                    List<SubQuestionDto> subQuesDtos = subQuestionDtoAssembler.toDtoList(subQuesList);
                    for (int m = 0; m < subQuesList.size(); m++) {
                        List<QuesOptionDto> quesOptionDtos = subQuestionDtoAssembler.toOptionDtoList(subQuesList.get(m).getQuesOptions());
                        subQuesDtos.get(m).setQuesOptions(quesOptionDtos);
                        if (StringUtils.isNotEmpty(subQuesList.get(m).getQuesAnswer())) {
                            subQuesDtos.get(m).setQuesAnswer(subQuesList.get(m).getQuesAnswer());
                        }
                        subQuesDtos.get(m).setNumber(m + 1);
                        //套题分数从小题类中取值
                        subQuesDtos.get(m).setScore(paperDetailUnit.getSubScoreList().get(m));
                    }
                    unitDto.setSubQuestions(subQuesDtos);
                }
                paperDetailUnitDtos.add(unitDto);
            }
            paperDetailDtos.get(i).setPaperDetailUnits(paperDetailUnitDtos);
            paperDetailDtos.get(i).setCnNum(CommonUtils.toCHNum(paperDetailDtos.get(i).getNumber()));
        }
        long paperDetailUnitDtoEndTime = System.currentTimeMillis();
        logger.info("单独组装paperDetailUnitDto耗时:" + (paperDetailUnitDtoEndTime - paperDetailDtoEndTime) + "ms");
        return paperDto;
    }

    private void setSelectQuestoionAnswer(List<PaperDetailUnit> paperDetailUnits) {
        for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
            if (paperDetailUnit == null || paperDetailUnit.getQuestion() == null) {
                break;
            }
            String optionOrder = paperDetailUnit.getOptionOrder();
            Question question = paperDetailUnit.getQuestion();
            quesService.setSelectQuestionAnswer(question, optionOrder);
        }
    }

    private void setSelectQuestionAnswerUnit(PaperDetailUnit paperDetailUnit) {
        Question question = paperDetailUnit.getQuestion();
        if (paperDetailUnit == null || question == null) {
            return;
        }
        String optionOrder = paperDetailUnit.getOptionOrder();
        quesService.setSelectQuestionAnswer(question, optionOrder);
    }


    /**
     * 根据paperDetailUnitId抽取单个试题
     * 根据paperDetailUnitId中设置的option顺序对option排序
     */
    @Override
    public QuestionDto extractExamQuestion(String examId, String courseCode, String groupCode, String paperDetailUnitId) {
        PaperDetailUnit paperDetailUnit = Model.of(paperDetailUnitRepo.findById(paperDetailUnitId));
        List<PaperDetailUnit> paperDetailUnits = new ArrayList<PaperDetailUnit>();
        paperDetailUnits.add(paperDetailUnit);
        //设置答案
        setSelectQuestoionAnswer(paperDetailUnits);
        //重新对选择题option进行排序(多选、单选、套题下选择题)
        reorderChoicequestionOption(paperDetailUnit);
        Question ques = paperDetailUnit.getQuestion();
        quesService.formatQues(ques);
        QuestionDto dto = BeanCopierUtil.copyProperties(ques, QuestionDto.class);
        dto.setScore(paperDetailUnit.getScore());
        dto.setQuesOptions(buildQuestionOptionDto(ques.getQuesOptions()));
        if (ques.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
            List<Question> subQuesList = ques.getSubQuestions();
            List<SubQuestionDto> subQuesDtos = new ArrayList<SubQuestionDto>();
            for (Question question : subQuesList) {
                SubQuestionDto subQuestionDto = new SubQuestionDto();
                BeanUtils.copyProperties(question, subQuestionDto);
                subQuesDtos.add(subQuestionDto);
            }
            for (int m = 0; m < subQuesList.size(); m++) {
                List<QuesOptionDto> quesOptionDtos = BeanCopierUtil
                        .copyPropertiesOfList(subQuesList.get(m).getQuesOptions(), QuesOptionDto.class);
                subQuesDtos.get(m).setQuesOptions(quesOptionDtos);
                subQuesDtos.get(m).setQuesAnswer(subQuesList.get(m).getQuesAnswer());
                subQuesDtos.get(m).setNumber(m + 1);
                //套题分数从小题类中取值
                subQuesDtos.get(m).setScore(paperDetailUnit.getSubScoreList().get(m));
                dto.setSubQuestions(subQuesDtos);
            }
        }
        if (ques.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                || ques.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
            dto.setQuesAnswer(ques.getQuesAnswer());
        }
        appendAudioFlag(dto, examId, courseCode, groupCode);
        return dto;
    }

    private List<QuesOptionDto> buildQuestionOptionDto(List<QuesOption> quesOptions) {
        if (quesOptions == null) {
            return null;
        }
        List<QuesOptionDto> optionDtos = new ArrayList<QuesOptionDto>();
        for (QuesOption option : quesOptions) {
            QuesOptionDto optionDto = new QuesOptionDto();
            optionDto.setNumber(option.getNumber());
            optionDto.setOptionBody(option.getOptionBody());
            optionDtos.add(optionDto);
        }
        return optionDtos;
    }

    /**
     * 1.给QuestionDto添加音频播放次数
     * 2.给试题音频中有a标签添加url
     *
     * @param questionDto
     */
    private void appendAudioFlag(QuestionDto questionDto, String examId, String courseCode, String groupCode) {
        if (questionDto.getHasAudio() != null && questionDto.getHasAudio() == true) {
            //1.判断questionDto是否含有音频，如果有添加音频播放次数
            AudioTimeConfig audioTimeConfig = Model.of(audioTimeConfigRepo.findOne(Example.of(new AudioTimeConfig(examId, courseCode, groupCode, questionDto.getId()))));
            questionDto.setPlayTime(audioTimeConfig != null ? audioTimeConfig.getPlayTime() : null);
            //2.1 取到题干，给a标签添加url
            String quesBody = questionDto.getQuesBody();
            questionDto.setQuesBody(buildBody(quesBody, questionDto));
            //2.2取到选项，给a标签添加url
            List<QuesOptionDto> quesOptionDtoList = questionDto.getQuesOptions();
            if (quesOptionDtoList != null && quesOptionDtoList.size() > 0) {
                for (QuesOptionDto quesOptionDto : quesOptionDtoList) {
                    quesOptionDto.setOptionBody(buildBody(quesOptionDto.getOptionBody(), questionDto));
                }
            }
        } else {
            questionDto.setPlayTime(null);
        }
    }

    //给题目和选项添加url
    public String buildBody(String body, QuestionDto questionDto) {
        String[] bodyStrings = body.split("></a>");
        if (bodyStrings.length > 1) {
            String resultBody = "";
            for (int i = 0; i < bodyStrings.length; i++) {
                String containAStr = bodyStrings[i];
                if (containAStr.indexOf("<a") > -1) {
                    String questionAudioId = matchAudioName(containAStr, "a", "id");
                    QuestionAudio questionAudio = questionAudioService.findAudioById(questionAudioId);
                    if (questionAudio != null) {
                        String url = upYunProperty.getDownloadUrl() + questionAudio.getFileUrl();
                        if (questionDto.getPlayTime() != null) {
                            containAStr += " question-audio url=\"" + url + "\" playTime=\"" + questionDto.getPlayTime() + "\"" + "></a>";
                        } else {
                            containAStr += " question-audio url=\"" + url + "\"" + "></a>";
                        }
                    }
                }
                resultBody += containAStr;
            }
            return resultBody;
        } else {
            return body;
        }
    }

    private String matchAudioName(String source, String element, String attr) {
        String reg = "<" + element + "[^<>]*?\\s" + attr + "=['\"]?(.*?)['\"]?(\\s.*?)";
        Matcher m = Pattern.compile(reg).matcher(source);
        while (m.find()) {
            return m.group(1);
        }
        return "";
    }

    /**
     * 重新对选择题option进行排序(多选、单选、套题下选择题)
     */
    private void reorderChoicequestionOption(PaperDetailUnit paperDetailUnit) {
        String optionOrder = paperDetailUnit.getOptionOrder();
        if (StringUtil.isNotBlank(optionOrder)) {
            Question question = paperDetailUnit.getQuestion();
            if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                    || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
                question.setQuesOptions(reorderOptionCore(question.getQuesOptions(), optionOrder));
            }
            if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
                List<Question> subQuestions = question.getSubQuestions();
                int index = 0;
                for (int k = 0; k < subQuestions.size(); k++) {
                    Question subQuestion = subQuestions.get(k);
                    if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                            || subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
                        subQuestion.setQuesOptions(reorderOptionCore(subQuestion.getQuesOptions(), optionOrder.split(";")[index]));
                        index++;
                    }
                }
            }
        }
    }

    /**
     * 对option排序
     *
     * @param quesOptions
     * @param optionOrder
     * @return
     */
    private List<QuesOption> reorderOptionCore(List<QuesOption> quesOptions, String optionOrder) {
        List<QuesOption> newQuesOptions = new ArrayList<QuesOption>();
        if (StringUtil.isBlank(optionOrder) || quesOptions.isEmpty()) {
            return null;
        }
        String[] optionOrderArr = optionOrder.split(",");
        for (int j = 0; j < optionOrderArr.length; j++) {
            for (int k = 0; k < quesOptions.size(); k++) {
                if (optionOrderArr[j].equals(quesOptions.get(k).getNumber())) {
                    newQuesOptions.add(quesOptions.get(k));
                }
            }
        }
        quesOptions = null;
        return newQuesOptions;
    }

    @Override
    public Page<ExtractConfig> findPageExtractConfig(int currentPage, int pageSize, Long examId, String courseCode, String orgId) {
        if (examId == null) {
            throw new StatusException("Q-", "examId is null");
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("orgId").is(orgId));
        query.addCriteria(Criteria.where("examId").is(examId));
        query.addCriteria(Criteria.where("course.enable").is("true"));
        if (!StringUtils.isBlank(courseCode)) {
            query.addCriteria(Criteria.where("course.code").is(courseCode));
        }
        long count = this.mongoTemplate.count(query, ExtractConfig.class);
        query.limit(pageSize);
        query.skip((currentPage - 1) * pageSize);
        List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
        if (extractConfigList != null && extractConfigList.size() > 0) {
            //调用考务rmi，获取考试信息
            GetExamReq req = new GetExamReq();
            req.setId(examId);
            req.setRootOrgId(Long.valueOf(orgId));
            GetExamResp resp = examCloudService.getExam(req);
            ExamBean bean = resp.getExamBean();
            for (ExtractConfig extractConfig : extractConfigList) {
                extractConfig.setExamType(bean.getExamType());
            }
        }
        return new PageImpl<ExtractConfig>(extractConfigList, new PageRequest(currentPage - 1, pageSize), count);
    }

    @Override
    public Map<String, Object> extractPaper(String paperId) {
        Map<String, Object> returnMap = new HashMap<String, Object>();
        Paper paper = Model.of(paperRepo.findById(paperId));
        if (paper == null) {
            returnMap.put("errorMsg", "该试卷不存在");
            return returnMap;
        }
        PaperDto paperDto = getPaperDtoByPaper(paper, paperId);
        returnMap.put("paperDto", paperDto);
        return returnMap;
    }

    @Override
    public String getAnswerHtml(String paperId) {
        //1.根据id查询试卷
        Paper paper = Model.of(paperRepo.findById(paperId));
        //2.定义html字符串
        StringBuffer answerHtml = new StringBuffer("<p style=\"text-align:center;font-size:20px;font-weight:bold\">" + paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")" + "</p>");
        //添加考试说明
        if (paper.getExamRemark() == null) {
            answerHtml.append("<p>考试说明：</p>");
        } else {
            answerHtml.append("<p>考试说明：</p>" + paper.getExamRemark());
        }
        //3.通过试卷获取考试端试卷结构
        PaperDto paperDto = getPaperDtoByPaper(paper, paper.getId());
        //4.获取试卷所有大题,并且循环
        List<PaperDetailDto> paperDetailDtos = paperDto.getPaperDetails();
        for (PaperDetailDto paperDetailDto : paperDetailDtos) {
            String title = "<p>" + paperDetailDto.getCnNum() + "、" + paperDetailDto.getName() + "(共" + paperDetailDto.getScore() + "分)" + "</p>";
            answerHtml.append(title);
            //5.获取大题下面的小题，并循环
            List<PaperDetailUnitDto> paperDetailUnitDtos = paperDetailDto.getPaperDetailUnits();
            for (PaperDetailUnitDto paperDetailUnitDto : paperDetailUnitDtos) {
                //定义题干
                String body = paperDetailUnitDto.getQuesBody().replaceAll("<span>", "").replaceAll("</span>", "").replaceAll("###", "___");
                ;
                //定义选项
                String option = "";
                //定义答案
                String answer = "";
                //6.如果为单选和多选
                if (paperDetailUnitDto.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION || paperDetailUnitDto.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
                    //获取选项
                    List<QuesOptionDto> quesOptionDtos = paperDetailUnitDto.getQuesOptions();
                    //添加题干
                    body = startWithP(body, paperDetailUnitDto.getNumber(), false);
                    answerHtml.append(body);
                    //添加选项
                    for (QuesOptionDto quesOptionDto : quesOptionDtos) {
                        option = startWithP(quesOptionDto.getOptionBody(), Integer.parseInt(quesOptionDto.getNumber()), true);
                        answerHtml.append(option);
                    }
                    answer = startWithP(paperDetailUnitDto.getAnswer(), -1, false);
                    answerHtml.append(answer);
                }
                //7.如果为套题
                else if (paperDetailUnitDto.getSubQuestions() != null && paperDetailUnitDto.getSubQuestions().size() > 0) {
                    //添加主题干
                    String bigBody = CommonUtils.relaceQuestionIdx(paperDetailUnitDto.getQuesBody(), 0);
                    bigBody = bigBody.replaceAll("<span>", "").replaceAll("</span>", "").replaceAll("###", "___");
                    bigBody = startWithP(bigBody, -2, false);
                    answerHtml.append(bigBody);
                    List<SubQuestionDto> subQuestionDtos = paperDetailUnitDto.getSubQuestions();
                    for (SubQuestionDto subQuestionDto : subQuestionDtos) {
                        body = subQuestionDto.getQuesBody().replaceAll("<span>", "").replaceAll("</span>", "").replaceAll("###", "___");
                        //如果子题为单选或者多选
                        if (subQuestionDto.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION || subQuestionDto.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
                            //获取选项
                            List<QuesOptionDto> quesOptionDtos = subQuestionDto.getQuesOptions();
                            //添加题干
                            body = startWithP(body, subQuestionDto.getNumber(), false);
                            answerHtml.append(body);
                            //添加选项
                            for (QuesOptionDto quesOptionDto : quesOptionDtos) {
                                option = startWithP(quesOptionDto.getOptionBody(), Integer.parseInt(quesOptionDto.getNumber()), true);
                                answerHtml.append(option);
                            }
                            answer = startWithP(subQuestionDto.getQuesAnswer(), -1, false);
                            answerHtml.append(answer);
                        } else {
                            //添加题干
                            body = startWithP(body, subQuestionDto.getNumber(), false);
                            answerHtml.append(body);
                            //添加答案
                            answer = startWithP(subQuestionDto.getQuesAnswer(), -1, false);
                            answerHtml.append(answer);
                        }
                    }
                } else {
                    //添加题干
                    body = startWithP(body, paperDetailUnitDto.getNumber(), false);
                    answerHtml.append(body);
                    //添加答案
                    answer = startWithP(paperDetailUnitDto.getAnswer(), -1, false);
                    answerHtml.append(answer);
                }
            }
        }
        return answerHtml.toString();
    }

    public String startWithP(String s, Integer number, boolean option) {
        if (s == null) {
            s = "<p></p>";
        }
        if (number == -2) {
            if (s.startsWith("<p>")) {
                s = s.replaceAll("<p>", "<p style=\"margin-left:40px;\">").replaceAll("<p style=\"margin-left:40px;\">", "<p style=\"margin-left:20px;\">");
            } else {
                s = "<p style=\"margin-left:20px;color:red;\">" + s + "</p>";
            }
        } else if (number == -1) {
            if (s.startsWith("<p>")) {
                s = s.replaceAll("<p>", "<p style=\"margin-left:65px;\">").replaceFirst("<p style=\"margin-left:65px;\">", "<p style=\"margin-left:20px;color:red;\">" + "答案：").replace("<p style=\"margin-left:65px;\">", "<p style=\"margin-left:65px;color:red;\">");
            } else {
                s = "<p style=\"margin-left:20px;color:red;\">" + "答案：" + s + "</p>";
            }
        } else {
            if (option) {
                if (s.startsWith("<p>")) {
                    s = s.replaceAll("<p>", "<p style=\"margin-left:40px;\">").replaceFirst("<p style=\"margin-left:40px;\">", "<p style=\"margin-left:20px;\">" + CommonUtils.getOptionNum(number - 1) + ".");
                } else {
                    s = "<p style=\"margin-left:20px;\">" + CommonUtils.getOptionNum(number - 1) + "." + s + "</p>";
                }
            } else {
                if (s.startsWith("<p>")) {
                    s = s.replaceAll("<p>", "<p style=\"margin-left:40px;\">").replaceFirst("<p style=\"margin-left:40px;\">", "<p style=\"margin-left:20px;\">" + number + ".");
                } else {
                    s = "<p style=\"margin-left:20px;\">" + number + "." + s + "</p>";
                }
            }
        }
        return s;
    }


    @Override
    public List<CouresInfo> findCourseByExtractConfig(Long examId, String orgId) {
        if (examId == null) {
            throw new StatusException("Q-", "examId is null");
        }
        //从考务查询改考试下的所有开启课程
        List<CouresInfo> courseInfoList = new ArrayList<CouresInfo>();
        GetExamCourseListReq req = new GetExamCourseListReq();
        req.setExamId(Long.valueOf(examId));
        req.setCourseEnable(true);
        Long start = 1l;
        int count = 0;
        while (true) {
            req.setStart(start);
            GetExamCourseListResp resp = examCloudService.getExamCourseList(req);
            for (ExamCourseRelationBean bean : resp.getRelationList()) {
                CouresInfo info = new CouresInfo(bean);
                courseInfoList.add(info);
            }
            if (start.equals(resp.getNext())) {
                break;
            } else {
                start = resp.getNext();
            }
            count++;
            if (count > 1000) {
                throw new StatusException("Q-", "考试下课程的数据量过大");
            }
        }
        if (CollectionUtils.isEmpty(courseInfoList)) {
            return null;
        }
        //查询已经改考试下已经制定的课程的调卷规则
        Query query = new Query();
        query.addCriteria(Criteria.where("orgId").is(orgId));
        query.addCriteria(Criteria.where("examId").is(examId));
        List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
        if (extractConfigList != null && extractConfigList.size() > 0) {
            for (ExtractConfig extractConfig : extractConfigList) {
                CouresInfo info = new CouresInfo();
                info.setCourseCode(extractConfig.getCourseCode());
                if (courseInfoList.contains(info)) {
                    courseInfoList.remove(info);
                }
            }
            return courseInfoList;
        }
        return courseInfoList;
    }

}
