Selaa lähdekoodia

rename basic、question cache keys.

deason 2 vuotta sitten
vanhempi
commit
61def54c86

+ 12 - 2
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/DemoController.java

@@ -4,6 +4,7 @@ import cn.com.qmth.examcloud.commons.util.JsonMapper;
 import cn.com.qmth.examcloud.core.questions.service.ExtractConfigProviderService;
 import cn.com.qmth.examcloud.core.questions.service.bean.extract.ExtractConfigPaper;
 import cn.com.qmth.examcloud.core.questions.service.cache.*;
+import cn.com.qmth.examcloud.support.CacheConstants;
 import cn.com.qmth.examcloud.support.cache.CacheHelper;
 import cn.com.qmth.examcloud.support.cache.bean.*;
 import cn.com.qmth.examcloud.web.redis.RedisClient;
@@ -25,21 +26,30 @@ import java.util.Set;
 @Controller
 @RequestMapping("${api_cqb}/")
 public class DemoController {
+
     private static final Logger log = LoggerFactory.getLogger(DemoController.class);
+
     @Autowired
     private RedisTemplate<String, Object> redisTemplate;
+
     @Autowired
     private RedisClient redisClient;
+
     @Autowired
     private ExtractConfigCache extractConfigCache;
+
     @Autowired
     private ExtractConfigPaperCache extractConfigPaperCache;
+
     @Autowired
     private BasePaperCache basePaperCache;
+
     @Autowired
     private QuestionCache questionCache;
+
     @Autowired
     private QuestionAnswerCache questionAnswerCache;
+
     @Autowired
     private ExtractConfigProviderService extractConfigProviderService;
 
@@ -76,11 +86,11 @@ public class DemoController {
         log.info("--->试题答案缓存: " + new JsonMapper().toJson(questionAnswer));
         //questionAnswerCache.remove(questionId);
 
-        final String pKeys = Constants.CACHE_KEY_PAPER + "*" + paperId;
+        final String pKeys = CacheConstants.CACHE_Q_EXTRACT_CONFIG_PAPER + "*" + paperId;
         Set<String> paperKeys = redisTemplate.keys(pKeys);
         log.info(StringUtils.join(paperKeys, ","));
 
-        final String qKeys = Constants.CACHE_KEY_QUESTION + "*" + questionId;
+        final String qKeys = CacheConstants.CACHE_Q_QUESTION + "*" + questionId;
         Set<String> questionKeys = redisTemplate.keys(qKeys);
         log.info(StringUtils.join(questionKeys, ","));
 

+ 3 - 1
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/cache/BasePaperCache.java

@@ -3,6 +3,7 @@ package cn.com.qmth.examcloud.core.questions.service.cache;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.service.ExtractConfigProviderService;
 import cn.com.qmth.examcloud.question.commons.core.paper.DefaultPaper;
+import cn.com.qmth.examcloud.support.CacheConstants;
 import cn.com.qmth.examcloud.support.cache.bean.BasePaperCacheBean;
 import cn.com.qmth.examcloud.web.cache.RandomObjectRedisCache;
 import org.apache.commons.lang3.StringUtils;
@@ -11,6 +12,7 @@ import org.springframework.stereotype.Component;
 
 @Component
 public class BasePaperCache extends RandomObjectRedisCache<BasePaperCacheBean> {
+
     @Autowired
     private ExtractConfigProviderService extractConfigProviderService;
 
@@ -31,7 +33,7 @@ public class BasePaperCache extends RandomObjectRedisCache<BasePaperCacheBean> {
 
     @Override
     protected String getKeyPrefix() {
-        return "Q_PAPER:BASE_";
+        return CacheConstants.CACHE_Q_BASE_PAPER;
     }
 
     @Override

+ 0 - 6
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/cache/Constants.java

@@ -2,12 +2,6 @@ package cn.com.qmth.examcloud.core.questions.service.cache;
 
 public interface Constants {
 
-    String CACHE_KEY_PAPER = "Q_PAPER:";
-
-    String CACHE_KEY_PAPER_FOR_DTO = "Q_PAPER:DTO_";
-
-    String CACHE_KEY_QUESTION = "Q_QUESTION:";
-
     int DEFAULT_TIME_OUT = 30 * 60;// N分钟
 
 }

+ 7 - 6
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/cache/ExtractConfigPaperCache.java

@@ -1,21 +1,22 @@
 package cn.com.qmth.examcloud.core.questions.service.cache;
 
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.service.ExtractConfigProviderService;
+import cn.com.qmth.examcloud.support.CacheConstants;
 import cn.com.qmth.examcloud.support.cache.bean.ExtractConfigPaperCacheBean;
 import cn.com.qmth.examcloud.web.cache.HashRedisCache;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
 
 @Component
 public class ExtractConfigPaperCache extends HashRedisCache<ExtractConfigPaperCacheBean> {
+
     @Autowired
     private ExtractConfigProviderService extractConfigProviderService;
 
     @Override
-    public ExtractConfigPaperCacheBean loadFromResource(Object[] keys,Object[] subkeys) {
+    public ExtractConfigPaperCacheBean loadFromResource(Object[] keys, Object[] subkeys) {
         Long examId = (Long) subkeys[0];
         String courseCode = String.valueOf(subkeys[1]);
         String groupCode = String.valueOf(subkeys[2]);
@@ -42,7 +43,7 @@ public class ExtractConfigPaperCache extends HashRedisCache<ExtractConfigPaperCa
 
     @Override
     protected String getKeyPrefix() {
-        return "Q_PAPER:EXTRACT_CONFIG_PAPER_";
+        return CacheConstants.CACHE_Q_EXTRACT_CONFIG_PAPER;
     }
 
     @Override

+ 3 - 1
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/cache/QuestionAnswerCache.java

@@ -3,6 +3,7 @@ package cn.com.qmth.examcloud.core.questions.service.cache;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.service.QuesService;
 import cn.com.qmth.examcloud.core.questions.service.bean.QuestionAnswerBean;
+import cn.com.qmth.examcloud.support.CacheConstants;
 import cn.com.qmth.examcloud.support.cache.bean.QuestionAnswerCacheBean;
 import cn.com.qmth.examcloud.web.cache.RandomObjectRedisCache;
 import org.apache.commons.lang3.StringUtils;
@@ -11,6 +12,7 @@ import org.springframework.stereotype.Component;
 
 @Component
 public class QuestionAnswerCache extends RandomObjectRedisCache<QuestionAnswerCacheBean> {
+
     @Autowired
     private QuesService quesService;
 
@@ -33,7 +35,7 @@ public class QuestionAnswerCache extends RandomObjectRedisCache<QuestionAnswerCa
 
     @Override
     protected String getKeyPrefix() {
-        return "Q_QUESTION:ANSWER_";
+        return CacheConstants.CACHE_Q_QUESTION_ANSWER;
     }
 
     @Override

+ 7 - 6
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/cache/QuestionCache.java

@@ -1,22 +1,23 @@
 package cn.com.qmth.examcloud.core.questions.service.cache;
 
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.service.ExtractConfigProviderService;
 import cn.com.qmth.examcloud.question.commons.core.question.DefaultQuestion;
+import cn.com.qmth.examcloud.support.CacheConstants;
 import cn.com.qmth.examcloud.support.cache.bean.QuestionCacheBean;
 import cn.com.qmth.examcloud.web.cache.HashRedisCache;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
 
 @Component
 public class QuestionCache extends HashRedisCache<QuestionCacheBean> {
+
     @Autowired
     private ExtractConfigProviderService extractConfigProviderService;
 
     @Override
-    public QuestionCacheBean loadFromResource(Object[] keys,Object[] subkeys) {
+    public QuestionCacheBean loadFromResource(Object[] keys, Object[] subkeys) {
         Long examId = (Long) subkeys[0];
         String courseCode = String.valueOf(subkeys[1]);
         String groupCode = String.valueOf(subkeys[2]);
@@ -47,7 +48,7 @@ public class QuestionCache extends HashRedisCache<QuestionCacheBean> {
 
     @Override
     protected String getKeyPrefix() {
-        return "Q_QUESTION:";
+        return CacheConstants.CACHE_Q_QUESTION;
     }
 
     @Override

+ 1274 - 1296
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExtractConfigServiceImpl.java

@@ -1,39 +1,5 @@
 package cn.com.qmth.examcloud.core.questions.service.impl;
 
-import static cn.com.qmth.examcloud.core.questions.service.cache.Constants.CACHE_KEY_PAPER_FOR_DTO;
-import static cn.com.qmth.examcloud.core.questions.service.cache.Constants.DEFAULT_TIME_OUT;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
-import org.apache.commons.lang3.StringUtils;
-import org.bson.types.ObjectId;
-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.data.mongodb.core.query.Update;
-import org.springframework.stereotype.Service;
-import org.springframework.util.Assert;
-import org.springframework.util.CollectionUtils;
-
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
 import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
@@ -46,36 +12,14 @@ 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.em.enums.ExamType;
 import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
-import cn.com.qmth.examcloud.core.questions.base.question.PaperDetailDto;
-import cn.com.qmth.examcloud.core.questions.base.question.PaperDetailUnitDto;
-import cn.com.qmth.examcloud.core.questions.base.question.PaperDto;
-import cn.com.qmth.examcloud.core.questions.base.question.QuesOptionDto;
-import cn.com.qmth.examcloud.core.questions.base.question.QuestionDto;
-import cn.com.qmth.examcloud.core.questions.base.question.SubQuestionDto;
+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.AudioTimeConfigRepo;
-import cn.com.qmth.examcloud.core.questions.dao.ExtractConfigRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PaperDetailRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PaperDetailUnitRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
-import cn.com.qmth.examcloud.core.questions.dao.entity.AudioTimeConfig;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Course;
-import cn.com.qmth.examcloud.core.questions.dao.entity.ExamPaper;
-import cn.com.qmth.examcloud.core.questions.dao.entity.ExtractConfig;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionAudio;
+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.ExtractConfigService;
 import cn.com.qmth.examcloud.core.questions.service.QuesService;
 import cn.com.qmth.examcloud.core.questions.service.QuestionAudioService;
-import cn.com.qmth.examcloud.core.questions.service.bean.CouresInfo;
-import cn.com.qmth.examcloud.core.questions.service.bean.PaperDetailDtoAssembler;
-import cn.com.qmth.examcloud.core.questions.service.bean.PaperDetailUnitDtoAssembler;
-import cn.com.qmth.examcloud.core.questions.service.bean.PaperDtoAssembler;
-import cn.com.qmth.examcloud.core.questions.service.bean.SubQuestionDtoAssembler;
+import cn.com.qmth.examcloud.core.questions.service.bean.*;
 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;
@@ -83,8 +27,34 @@ 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.support.CacheConstants;
 import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
 import cn.com.qmth.examcloud.web.redis.RedisClient;
+import org.apache.commons.lang3.StringUtils;
+import org.bson.types.ObjectId;
+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.data.mongodb.core.query.Update;
+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.DEFAULT_TIME_OUT;
 
 /**
  * @author chenken
@@ -94,1241 +64,1249 @@ import cn.com.qmth.examcloud.web.redis.RedisClient;
  */
 @Service("extractConfigService")
 public class ExtractConfigServiceImpl implements ExtractConfigService {
-	private static final Logger LOG = LoggerFactory.getLogger(ExtractConfigServiceImpl.class);
-
-	@Autowired
-	private ExtractConfigRepo extractConfigRepo;
-
-	@Autowired
-	private PaperDetailRepo paperDetailRepo;
-
-	@Autowired
-	private PaperDetailUnitRepo paperDetailUnitRepo;
-
-	@Autowired
-	private AudioTimeConfigRepo audioTimeConfigRepo;
-
-	@Autowired
-	private PaperRepo paperRepo;
-
-	@Autowired
-	private QuesService quesService;
-
-	@Autowired
-	private QuestionAudioService questionAudioService;
-
-	@Autowired
-	private MongoTemplate mongoTemplate;
-
-	@Autowired
-	private CourseService courseService;
-//
-//    @Autowired
-//    private SysProperty sysProperty;
-
-	@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;
-	@Autowired
-	private ExamRecordCloudService adminExamRecordCloudService;
-	@Autowired
-	private ExamRecordDataCloudService studentExamRecordCloudService;
-
-	@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) {
-		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);
-			if (ExamType.OFFLINE.name().equals(extractConfig.getExamType())) {
-				checkOfflinePaper(paper);
-			}
-		}
-		if(StringUtils.isNotBlank(extractConfig.getId())) {
-			disposeOldPaper(extractConfig.getId());
-		}
-		Course course = courseService.getCourse(Long.valueOf(extractConfig.getOrgId()), extractConfig.getCourseCode());
-		extractConfig.setCourse(course);
-		extractConfig.setCourseName(course.getName());
-		extractConfig.setIfFinish((short) 1);
-		extractConfigRepo.save(extractConfig);
-		disposeNowPaper(extractConfig.getId());
-		return examPapers;
-	}
-
-	/**先处理原来绑定的试卷
-	 * @param extractConfigId
-	 */
-	private void disposeOldPaper(String extractConfigId) {
-		ExtractConfig ec = Model.of(extractConfigRepo.findById(extractConfigId));
-		for (ExamPaper paper : ec.getExamPaperList()) {
-			if(inExam(paper.getPaper().getId())) {//如果有考试记录
-				updatePaperInUse(paper.getPaper().getId(), 1);
-			}else {
-				if(!inOtherExtractConfig(extractConfigId, paper.getPaper().getId())) {//没有考试记录且不被其他调卷规则引用
-					updatePaperInUse(paper.getPaper().getId(), 0);
-				}
-			}
-		}
-	}
-	/**处理当前绑定试卷
-	 * @param extractConfigId
-	 */
-	private void disposeNowPaper(String extractConfigId) {
-		ExtractConfig ec = Model.of(extractConfigRepo.findById(extractConfigId));
-		for (ExamPaper paper : ec.getExamPaperList()) {
-			updatePaperInUse(paper.getPaper().getId(), 1);
-		}
-	}
-
-	private void updatePaperInUse(String paperId, int inUse) {
-		Query query = null;
-		if(paperId.length()==24) {
-			query = Query.query(Criteria.where("_id").is(new ObjectId(paperId)));
-		}else {
-			query = Query.query(Criteria.where("_id").is(paperId));
-		}
-		Update update = new Update();
-		update.set("inUse", inUse);
-		mongoTemplate.updateFirst(query, update, "paper");
-	}
-
-	private boolean inExam(String paperId) {
-		CheckPaperInExamReq req1 = new CheckPaperInExamReq();
-		req1.setBasePaperId(paperId);
-		CheckPaperInExamResp res1 = adminExamRecordCloudService.checkPaperInExam(req1);
-		if (res1.getInExam()) {
-			return res1.getInExam();
-		}
-		cn.com.qmth.examcloud.core.oe.student.api.request.CheckPaperInExamReq req2 = new cn.com.qmth.examcloud.core.oe.student.api.request.CheckPaperInExamReq();
-		req2.setBasePaperId(paperId);
-		cn.com.qmth.examcloud.core.oe.student.api.response.CheckPaperInExamResp res2 = studentExamRecordCloudService
-				.checkPaperInExam(req2);
-		return res2.getInExam();
-	}
-
-	private boolean inOtherExtractConfig(String extractConfigId, String paperId) {
-		Criteria criteria = new Criteria();
-		criteria.and("examPaperList").elemMatch(Criteria.where("paper.$id").is(paperId));
-		Query query = Query.query(criteria);
-		List<ExtractConfig> list = mongoTemplate.find(query, ExtractConfig.class, "extractConfig");
-		if (list == null || list.size() == 0) {
-			return false;
-		}
-		if (list.size() == 1 && list.get(0).getId().equals(extractConfigId)) {
-			return false;
-		}
-		return true;
-	}
-
-	private void checkOfflinePaper(Paper paper) {
-		List<PaperDetailUnit> pdus = paperDetailUnitRepo.findByPaper(paper);
-		if (pdus == null || pdus.size() == 0) {
-			return;
-		}
-		for (PaperDetailUnit pdu : pdus) {
-			if (pdu.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-					|| pdu.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
-					|| pdu.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
-				throw new StatusException("500", "试卷包含客观题,无法保存规则");
-			} else if (pdu.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION
-					&& !CollectionUtils.isEmpty(pdu.getQuestion().getSubQuestions())) {
-				for (Question q : pdu.getQuestion().getSubQuestions()) {
-					if (q.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-							|| q.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
-							|| q.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
-						throw new StatusException("500", "试卷包含客观题,无法保存规则");
-					}
-				}
-			}
-		}
-	}
-
-	@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<>();
-		LOG.info("调卷开始...");
-		long beginTime = System.currentTimeMillis();
-		LOG.info("开始根据examId:" + exam_id + "和courseCode:" + course_code + "获取调卷规则");
-		ExtractConfig extractConfig = this.findConfig(new ExtractConfig(exam_id, course_code));
-		if (extractConfig == null) {
-			LOG.error("该考试和课程下调卷规则未制定,请先制定调卷规则,调卷程序退出");
-			returnMap.put("errorMsg", "该考试和课程下调卷规则未制定,请先制定调卷规则");
-			return returnMap;
-		}
-		long configFinishTime = System.currentTimeMillis();
-		LOG.info("获取调卷规则共耗时:" + (configFinishTime - beginTime) + "ms");
-		LOG.info("根据调卷规则中设置的概率获取类型为" + group_code + "的试卷");
-		Map<String, Paper> paperMap = this.getExamPaperByProbability(extractConfig.getExamPaperList());
-		if (paperMap.isEmpty()) {
-			LOG.error("该考试和课程下调卷规则中试卷不存在,请检查调卷规则,调卷程序退出");
-			returnMap.put("errorMsg", "该考试和课程下调卷规则中试卷不存在,请重新制定调卷规则");
-			return returnMap;
-		}
-
-		long paperMapFinishTime = System.currentTimeMillis();
-		LOG.info("获取类型为" + group_code + "的试卷共耗时:" + (paperMapFinishTime - configFinishTime) + "ms");
-
-		Paper basePaper = paperMap.get(group_code);
-		if (basePaper == null) {
-			LOG.error("该考试和课程下调卷规则中该类型试卷不存在,请检查调卷规则,调卷程序退出");
-			returnMap.put("errorMsg", "该考试和课程下调卷规则中该类型试卷不存在,请重新制定调卷规则");
-			return returnMap;
-		}
-		String basePaperId = basePaper.getId();
-		LOG.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();
-			LOG.info("获取试卷Dto共耗时:" + (paperDtoFinishTime - paperMapFinishTime) + "ms");
-			returnMap.put("paperDto", paperDto);
-			LOG.info("调卷完成");
-			LOG.info("总共耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
-		} else {
-			// 乱序重新生成试卷
-			Paper newPaper = this.recombinationPaper(basePaper, PaperType.STUDENT_EXAM, upSetQuestionOrder,
-					upSetOptionOrder);
-			LOG.info("根据新试卷 paperId:" + newPaper.getId() + "组装PaperDto后返回");
-
-			long genPaperFinishTime = System.currentTimeMillis();
-			LOG.info("组卷共耗时:" + (genPaperFinishTime - paperMapFinishTime) + "ms");
-			PaperDto paperDto = getPaperDtoByPaper(newPaper, basePaperId);
-
-			long paperDtoFinishTime = System.currentTimeMillis();
-			LOG.info("获取试卷Dto共耗时:" + (paperDtoFinishTime - genPaperFinishTime) + "ms");
-
-			returnMap.put("paperDto", paperDto);
-			LOG.info("调卷完成");
-			LOG.info("总共耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
-		}
-		return returnMap;
-	}
-
-	@Override
-	public boolean checkIsAllQbjectiveQuestion(String paperId) {
-		// 优先从redis中获取缓存dto
-		PaperDto cachePaperDto = redisClient.get(CACHE_KEY_PAPER_FOR_DTO + paperId, PaperDto.class, DEFAULT_TIME_OUT);
-		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<>();
-		if (examPaperList == null || examPaperList.size() == 0) {
-			throw new RuntimeException("可供抽取的试卷集合为空,无法抽取试卷");
-		}
-
-		Map<String, List<ExamPaper>> examPaperMap = new HashMap<>();
-		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<>();
-					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));
-
-				if (paper == null) {
-					continue;
-				}
-
-				// 不能用原来的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<>();
-				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<>();
-						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<>();
-		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();
-		LOG.info("单独组装paperDto耗时:" + (paperDtoEndTime - beginTime) + "ms");
-
-		// 将小题全部取出来,只取一次
-		List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaper(paper);
-		long pduEndTime = System.currentTimeMillis();
-		LOG.info("数据库取小题耗时:" + (pduEndTime - paperDtoEndTime) + "ms");
-		Collections.sort(allPaperDetailUnits);
-		long pduSortEndTime = System.currentTimeMillis();
-		LOG.info("排序小题耗时:" + (pduSortEndTime - pduEndTime) + "ms");
-
-		// 获取大题
-		List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(paper);
-		long pdEndTime = System.currentTimeMillis();
-		LOG.info("数据库取大题耗时:" + (pdEndTime - pduSortEndTime) + "ms");
-		Collections.sort(paperDetails);
-		long pdSortEndTime = System.currentTimeMillis();
-		LOG.info("排序大题耗时:" + (pdSortEndTime - pdEndTime) + "ms");
-
-		// 抽取大题Id对应的小题
-		Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
-				.collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
-		long pduMapEndTime = System.currentTimeMillis();
-		LOG.info("获取大题与小题对应关系耗时:" + (pduMapEndTime - pdSortEndTime) + "ms");
-
-		// 获取大题Dto
-		List<PaperDetailDto> paperDetailDtos = paperDetailDtoAssembler.toDtoList(paperDetails);
-		paperDto.setPaperDetails(paperDetailDtos);
-		long paperDetailDtoEndTime = System.currentTimeMillis();
-		LOG.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();
-		LOG.info("单独组装paperDetailUnitDto耗时:" + (paperDetailUnitDtoEndTime - paperDetailDtoEndTime) + "ms");
-
-		// 将重新组装的dto放进缓存
-		redisClient.set(CACHE_KEY_PAPER_FOR_DTO + paperDto.getId(), paperDto, DEFAULT_TIME_OUT);
-		return paperDto;
-	}
-
-	/**
-	 * 测试获取paperDto
-	 *
-	 * @param basePaperId
-	 * @return
-	 */
-	@Deprecated
-	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();
-		LOG.info("单独组装paperDto耗时:" + (paperDtoEndTime - beginTime) + "ms");
-
-		// 将小题全部取出来,只取一次
-		// List<PaperDetailUnit> allPaperDetailUnits =
-		// detailUnitNativeRepo.findByPaperId(paper.getId());
-		List<PaperDetailUnit> allPaperDetailUnits = new ArrayList<>();
-
-		long pduEndTime = System.currentTimeMillis();
-		LOG.info("数据库取小题耗时:" + (pduEndTime - paperDtoEndTime) + "ms");
-		Collections.sort(allPaperDetailUnits);
-		long pduSortEndTime = System.currentTimeMillis();
-		LOG.info("排序小题耗时:" + (pduSortEndTime - pduEndTime) + "ms");
-
-		paperDto.setAllQbjectiveQuestion(checkIsAllQbjectiveByPdu(allPaperDetailUnits));
-		long isAllObjEndtime = System.currentTimeMillis();
-		LOG.info("设置客观题耗时:" + (isAllObjEndtime - pduSortEndTime) + "ms");
-
-		// 获取大题
-		List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(paper);
-		long pdEndTime = System.currentTimeMillis();
-		LOG.info("数据库取大题耗时:" + (pdEndTime - isAllObjEndtime) + "ms");
-		Collections.sort(paperDetails);
-		long pdSortEndTime = System.currentTimeMillis();
-		LOG.info("排序大题耗时:" + (pdSortEndTime - pdEndTime) + "ms");
-
-		// 抽取大题Id对应的小题
-		Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
-				.collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
-		long pduMapEndTime = System.currentTimeMillis();
-		LOG.info("获取大题与小题对应关系耗时:" + (pduMapEndTime - pdSortEndTime) + "ms");
-
-		// 获取大题Dto
-		List<PaperDetailDto> paperDetailDtos = paperDetailDtoAssembler.toDtoList(paperDetails);
-		paperDto.setPaperDetails(paperDetailDtos);
-		long paperDetailDtoEndTime = System.currentTimeMillis();
-		LOG.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();
-		LOG.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<>();
-		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<>();
-			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<>();
-		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 = sysProperty.getDomain() + questionAudio.getFileUrl();
-						// 通用存储
-						String url = FileStorageUtil.realPath(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);
-		if (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<>();
-		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, UserDataRule ud) {
-		if (ud.assertEmptyQueryResult()) {
-			return Page.empty();
-		}
-		if (examId == null) {
-			throw new StatusException("Q-", "examId is null");
-		}
-		Query query = new Query();
-		query.addCriteria(Criteria.where("orgId").is(orgId));
-		if (ud.assertNeedQueryRefIds()) {
-			query.addCriteria(Criteria.where("course.id").in(ud.stringRefIds()));
-		}
-		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 - 1L) * 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<>(extractConfigList, PageRequest.of(currentPage - 1, pageSize), count);
-	}
-
-	@Override
-	public Map<String, Object> extractPaper(String paperId) {
-		Map<String, Object> returnMap = new HashMap<>();
-		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, UserDataRule ud) {
-		if (ud.assertEmptyQueryResult()) {
-			return new ArrayList<>();
-		}
-		if (examId == null) {
-			throw new StatusException("500", "examId is null");
-		}
-		// 从考务查询改考试下的所有开启课程
-		List<CouresInfo> courseInfoList = new ArrayList<>();
-		GetExamCourseListReq req = new GetExamCourseListReq();
-		req.setExamId(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);
-				}
-			}
-		}
-		if (ud.assertNeedQueryRefIds() && courseInfoList.size() > 0) {
-			List<CouresInfo> ret = new ArrayList<>();
-			for (CouresInfo info : courseInfoList) {
-				if (ud.getRefIds().contains(info.getCourseId())) {
-					ret.add(info);
-				}
-			}
-			return ret;
-		}
-		return courseInfoList;
-	}
-
-	@Override
-	public List<ExtractConfig> findExtractConfig(Long examId) {
-		if (examId == null) {
-			throw new StatusException("1001", "examId is null");
-		}
-		Query query = new Query();
-		query.addCriteria(Criteria.where("examId").is(examId));
-		query.addCriteria(Criteria.where("course.enable").is("true"));
-
-		List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
-
-		return extractConfigList;
-	}
+
+    private static final Logger LOG = LoggerFactory.getLogger(ExtractConfigServiceImpl.class);
+
+    @Autowired
+    private ExtractConfigRepo extractConfigRepo;
+
+    @Autowired
+    private PaperDetailRepo paperDetailRepo;
+
+    @Autowired
+    private PaperDetailUnitRepo paperDetailUnitRepo;
+
+    @Autowired
+    private AudioTimeConfigRepo audioTimeConfigRepo;
+
+    @Autowired
+    private PaperRepo paperRepo;
+
+    @Autowired
+    private QuesService quesService;
+
+    @Autowired
+    private QuestionAudioService questionAudioService;
+
+    @Autowired
+    private MongoTemplate mongoTemplate;
+
+    @Autowired
+    private CourseService courseService;
+    //
+    //    @Autowired
+    //    private SysProperty sysProperty;
+
+    @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;
+
+    @Autowired
+    private ExamRecordCloudService adminExamRecordCloudService;
+
+    @Autowired
+    private ExamRecordDataCloudService studentExamRecordCloudService;
+
+    @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) {
+        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);
+            if (ExamType.OFFLINE.name().equals(extractConfig.getExamType())) {
+                checkOfflinePaper(paper);
+            }
+        }
+        if (StringUtils.isNotBlank(extractConfig.getId())) {
+            disposeOldPaper(extractConfig.getId());
+        }
+        Course course = courseService.getCourse(Long.valueOf(extractConfig.getOrgId()), extractConfig.getCourseCode());
+        extractConfig.setCourse(course);
+        extractConfig.setCourseName(course.getName());
+        extractConfig.setIfFinish((short) 1);
+        extractConfigRepo.save(extractConfig);
+        disposeNowPaper(extractConfig.getId());
+        return examPapers;
+    }
+
+    /**
+     * 先处理原来绑定的试卷
+     *
+     * @param extractConfigId
+     */
+    private void disposeOldPaper(String extractConfigId) {
+        ExtractConfig ec = Model.of(extractConfigRepo.findById(extractConfigId));
+        for (ExamPaper paper : ec.getExamPaperList()) {
+            if (inExam(paper.getPaper().getId())) {//如果有考试记录
+                updatePaperInUse(paper.getPaper().getId(), 1);
+            } else {
+                if (!inOtherExtractConfig(extractConfigId, paper.getPaper().getId())) {//没有考试记录且不被其他调卷规则引用
+                    updatePaperInUse(paper.getPaper().getId(), 0);
+                }
+            }
+        }
+    }
+
+    /**
+     * 处理当前绑定试卷
+     *
+     * @param extractConfigId
+     */
+    private void disposeNowPaper(String extractConfigId) {
+        ExtractConfig ec = Model.of(extractConfigRepo.findById(extractConfigId));
+        for (ExamPaper paper : ec.getExamPaperList()) {
+            updatePaperInUse(paper.getPaper().getId(), 1);
+        }
+    }
+
+    private void updatePaperInUse(String paperId, int inUse) {
+        Query query = null;
+        if (paperId.length() == 24) {
+            query = Query.query(Criteria.where("_id").is(new ObjectId(paperId)));
+        } else {
+            query = Query.query(Criteria.where("_id").is(paperId));
+        }
+        Update update = new Update();
+        update.set("inUse", inUse);
+        mongoTemplate.updateFirst(query, update, "paper");
+    }
+
+    private boolean inExam(String paperId) {
+        CheckPaperInExamReq req1 = new CheckPaperInExamReq();
+        req1.setBasePaperId(paperId);
+        CheckPaperInExamResp res1 = adminExamRecordCloudService.checkPaperInExam(req1);
+        if (res1.getInExam()) {
+            return res1.getInExam();
+        }
+        cn.com.qmth.examcloud.core.oe.student.api.request.CheckPaperInExamReq req2 = new cn.com.qmth.examcloud.core.oe.student.api.request.CheckPaperInExamReq();
+        req2.setBasePaperId(paperId);
+        cn.com.qmth.examcloud.core.oe.student.api.response.CheckPaperInExamResp res2 = studentExamRecordCloudService
+                .checkPaperInExam(req2);
+        return res2.getInExam();
+    }
+
+    private boolean inOtherExtractConfig(String extractConfigId, String paperId) {
+        Criteria criteria = new Criteria();
+        criteria.and("examPaperList").elemMatch(Criteria.where("paper.$id").is(paperId));
+        Query query = Query.query(criteria);
+        List<ExtractConfig> list = mongoTemplate.find(query, ExtractConfig.class, "extractConfig");
+        if (list == null || list.size() == 0) {
+            return false;
+        }
+        if (list.size() == 1 && list.get(0).getId().equals(extractConfigId)) {
+            return false;
+        }
+        return true;
+    }
+
+    private void checkOfflinePaper(Paper paper) {
+        List<PaperDetailUnit> pdus = paperDetailUnitRepo.findByPaper(paper);
+        if (pdus == null || pdus.size() == 0) {
+            return;
+        }
+        for (PaperDetailUnit pdu : pdus) {
+            if (pdu.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+                    || pdu.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
+                    || pdu.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+                throw new StatusException("500", "试卷包含客观题,无法保存规则");
+            } else if (pdu.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION
+                    && !CollectionUtils.isEmpty(pdu.getQuestion().getSubQuestions())) {
+                for (Question q : pdu.getQuestion().getSubQuestions()) {
+                    if (q.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+                            || q.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
+                            || q.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+                        throw new StatusException("500", "试卷包含客观题,无法保存规则");
+                    }
+                }
+            }
+        }
+    }
+
+    @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<>();
+        LOG.info("调卷开始...");
+        long beginTime = System.currentTimeMillis();
+        LOG.info("开始根据examId:" + exam_id + "和courseCode:" + course_code + "获取调卷规则");
+        ExtractConfig extractConfig = this.findConfig(new ExtractConfig(exam_id, course_code));
+        if (extractConfig == null) {
+            LOG.error("该考试和课程下调卷规则未制定,请先制定调卷规则,调卷程序退出");
+            returnMap.put("errorMsg", "该考试和课程下调卷规则未制定,请先制定调卷规则");
+            return returnMap;
+        }
+        long configFinishTime = System.currentTimeMillis();
+        LOG.info("获取调卷规则共耗时:" + (configFinishTime - beginTime) + "ms");
+        LOG.info("根据调卷规则中设置的概率获取类型为" + group_code + "的试卷");
+        Map<String, Paper> paperMap = this.getExamPaperByProbability(extractConfig.getExamPaperList());
+        if (paperMap.isEmpty()) {
+            LOG.error("该考试和课程下调卷规则中试卷不存在,请检查调卷规则,调卷程序退出");
+            returnMap.put("errorMsg", "该考试和课程下调卷规则中试卷不存在,请重新制定调卷规则");
+            return returnMap;
+        }
+
+        long paperMapFinishTime = System.currentTimeMillis();
+        LOG.info("获取类型为" + group_code + "的试卷共耗时:" + (paperMapFinishTime - configFinishTime) + "ms");
+
+        Paper basePaper = paperMap.get(group_code);
+        if (basePaper == null) {
+            LOG.error("该考试和课程下调卷规则中该类型试卷不存在,请检查调卷规则,调卷程序退出");
+            returnMap.put("errorMsg", "该考试和课程下调卷规则中该类型试卷不存在,请重新制定调卷规则");
+            return returnMap;
+        }
+        String basePaperId = basePaper.getId();
+        LOG.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();
+            LOG.info("获取试卷Dto共耗时:" + (paperDtoFinishTime - paperMapFinishTime) + "ms");
+            returnMap.put("paperDto", paperDto);
+            LOG.info("调卷完成");
+            LOG.info("总共耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
+        } else {
+            // 乱序重新生成试卷
+            Paper newPaper = this.recombinationPaper(basePaper, PaperType.STUDENT_EXAM, upSetQuestionOrder,
+                    upSetOptionOrder);
+            LOG.info("根据新试卷 paperId:" + newPaper.getId() + "组装PaperDto后返回");
+
+            long genPaperFinishTime = System.currentTimeMillis();
+            LOG.info("组卷共耗时:" + (genPaperFinishTime - paperMapFinishTime) + "ms");
+            PaperDto paperDto = getPaperDtoByPaper(newPaper, basePaperId);
+
+            long paperDtoFinishTime = System.currentTimeMillis();
+            LOG.info("获取试卷Dto共耗时:" + (paperDtoFinishTime - genPaperFinishTime) + "ms");
+
+            returnMap.put("paperDto", paperDto);
+            LOG.info("调卷完成");
+            LOG.info("总共耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
+        }
+        return returnMap;
+    }
+
+    @Override
+    public boolean checkIsAllQbjectiveQuestion(String paperId) {
+        // 优先从redis中获取缓存dto
+        PaperDto cachePaperDto = redisClient.get(CacheConstants.CACHE_Q_TEMP_PAPER + paperId, PaperDto.class, DEFAULT_TIME_OUT);
+        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<>();
+        if (examPaperList == null || examPaperList.size() == 0) {
+            throw new RuntimeException("可供抽取的试卷集合为空,无法抽取试卷");
+        }
+
+        Map<String, List<ExamPaper>> examPaperMap = new HashMap<>();
+        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<>();
+                    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));
+
+                if (paper == null) {
+                    continue;
+                }
+
+                // 不能用原来的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<>();
+                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<>();
+                        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<>();
+        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();
+        LOG.info("单独组装paperDto耗时:" + (paperDtoEndTime - beginTime) + "ms");
+
+        // 将小题全部取出来,只取一次
+        List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaper(paper);
+        long pduEndTime = System.currentTimeMillis();
+        LOG.info("数据库取小题耗时:" + (pduEndTime - paperDtoEndTime) + "ms");
+        Collections.sort(allPaperDetailUnits);
+        long pduSortEndTime = System.currentTimeMillis();
+        LOG.info("排序小题耗时:" + (pduSortEndTime - pduEndTime) + "ms");
+
+        // 获取大题
+        List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(paper);
+        long pdEndTime = System.currentTimeMillis();
+        LOG.info("数据库取大题耗时:" + (pdEndTime - pduSortEndTime) + "ms");
+        Collections.sort(paperDetails);
+        long pdSortEndTime = System.currentTimeMillis();
+        LOG.info("排序大题耗时:" + (pdSortEndTime - pdEndTime) + "ms");
+
+        // 抽取大题Id对应的小题
+        Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
+                .collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
+        long pduMapEndTime = System.currentTimeMillis();
+        LOG.info("获取大题与小题对应关系耗时:" + (pduMapEndTime - pdSortEndTime) + "ms");
+
+        // 获取大题Dto
+        List<PaperDetailDto> paperDetailDtos = paperDetailDtoAssembler.toDtoList(paperDetails);
+        paperDto.setPaperDetails(paperDetailDtos);
+        long paperDetailDtoEndTime = System.currentTimeMillis();
+        LOG.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();
+        LOG.info("单独组装paperDetailUnitDto耗时:" + (paperDetailUnitDtoEndTime - paperDetailDtoEndTime) + "ms");
+
+        // 将重新组装的dto放进缓存
+        redisClient.set(CacheConstants.CACHE_Q_TEMP_PAPER + paperDto.getId(), paperDto, DEFAULT_TIME_OUT);
+        return paperDto;
+    }
+
+    /**
+     * 测试获取paperDto
+     *
+     * @param basePaperId
+     * @return
+     */
+    @Deprecated
+    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();
+        LOG.info("单独组装paperDto耗时:" + (paperDtoEndTime - beginTime) + "ms");
+
+        // 将小题全部取出来,只取一次
+        // List<PaperDetailUnit> allPaperDetailUnits =
+        // detailUnitNativeRepo.findByPaperId(paper.getId());
+        List<PaperDetailUnit> allPaperDetailUnits = new ArrayList<>();
+
+        long pduEndTime = System.currentTimeMillis();
+        LOG.info("数据库取小题耗时:" + (pduEndTime - paperDtoEndTime) + "ms");
+        Collections.sort(allPaperDetailUnits);
+        long pduSortEndTime = System.currentTimeMillis();
+        LOG.info("排序小题耗时:" + (pduSortEndTime - pduEndTime) + "ms");
+
+        paperDto.setAllQbjectiveQuestion(checkIsAllQbjectiveByPdu(allPaperDetailUnits));
+        long isAllObjEndtime = System.currentTimeMillis();
+        LOG.info("设置客观题耗时:" + (isAllObjEndtime - pduSortEndTime) + "ms");
+
+        // 获取大题
+        List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(paper);
+        long pdEndTime = System.currentTimeMillis();
+        LOG.info("数据库取大题耗时:" + (pdEndTime - isAllObjEndtime) + "ms");
+        Collections.sort(paperDetails);
+        long pdSortEndTime = System.currentTimeMillis();
+        LOG.info("排序大题耗时:" + (pdSortEndTime - pdEndTime) + "ms");
+
+        // 抽取大题Id对应的小题
+        Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
+                .collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
+        long pduMapEndTime = System.currentTimeMillis();
+        LOG.info("获取大题与小题对应关系耗时:" + (pduMapEndTime - pdSortEndTime) + "ms");
+
+        // 获取大题Dto
+        List<PaperDetailDto> paperDetailDtos = paperDetailDtoAssembler.toDtoList(paperDetails);
+        paperDto.setPaperDetails(paperDetailDtos);
+        long paperDetailDtoEndTime = System.currentTimeMillis();
+        LOG.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();
+        LOG.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<>();
+        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<>();
+            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<>();
+        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 = sysProperty.getDomain() + questionAudio.getFileUrl();
+                        // 通用存储
+                        String url = FileStorageUtil.realPath(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);
+        if (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<>();
+        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, UserDataRule ud) {
+        if (ud.assertEmptyQueryResult()) {
+            return Page.empty();
+        }
+        if (examId == null) {
+            throw new StatusException("Q-", "examId is null");
+        }
+        Query query = new Query();
+        query.addCriteria(Criteria.where("orgId").is(orgId));
+        if (ud.assertNeedQueryRefIds()) {
+            query.addCriteria(Criteria.where("course.id").in(ud.stringRefIds()));
+        }
+        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 - 1L) * 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<>(extractConfigList, PageRequest.of(currentPage - 1, pageSize), count);
+    }
+
+    @Override
+    public Map<String, Object> extractPaper(String paperId) {
+        Map<String, Object> returnMap = new HashMap<>();
+        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, UserDataRule ud) {
+        if (ud.assertEmptyQueryResult()) {
+            return new ArrayList<>();
+        }
+        if (examId == null) {
+            throw new StatusException("500", "examId is null");
+        }
+        // 从考务查询改考试下的所有开启课程
+        List<CouresInfo> courseInfoList = new ArrayList<>();
+        GetExamCourseListReq req = new GetExamCourseListReq();
+        req.setExamId(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);
+                }
+            }
+        }
+        if (ud.assertNeedQueryRefIds() && courseInfoList.size() > 0) {
+            List<CouresInfo> ret = new ArrayList<>();
+            for (CouresInfo info : courseInfoList) {
+                if (ud.getRefIds().contains(info.getCourseId())) {
+                    ret.add(info);
+                }
+            }
+            return ret;
+        }
+        return courseInfoList;
+    }
+
+    @Override
+    public List<ExtractConfig> findExtractConfig(Long examId) {
+        if (examId == null) {
+            throw new StatusException("1001", "examId is null");
+        }
+        Query query = new Query();
+        query.addCriteria(Criteria.where("examId").is(examId));
+        query.addCriteria(Criteria.where("course.enable").is("true"));
+
+        List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
+
+        return extractConfigList;
+    }
 
 }