Prechádzať zdrojové kódy

Merge remote-tracking branch 'origin/dev_v5.0.3' into release_v5.0.3

deason 1 rok pred
rodič
commit
3b1c207f7b

+ 74 - 35
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/DefaultQuesionController.java

@@ -1,17 +1,23 @@
 package cn.com.qmth.examcloud.core.questions.api.controller;
 
+import cn.com.qmth.examcloud.api.commons.enums.ExamType;
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.api.request.GetDefaultQuesionReq;
 import cn.com.qmth.examcloud.core.questions.api.request.GetDefaultQuesionsReq;
 import cn.com.qmth.examcloud.core.questions.api.request.GetQuestionListReq;
 import cn.com.qmth.examcloud.core.questions.api.request.GetQuestionReq;
+import cn.com.qmth.examcloud.core.questions.dao.entity.ExtractConfig;
 import cn.com.qmth.examcloud.core.questions.service.ExtractConfigProviderService;
+import cn.com.qmth.examcloud.core.questions.service.ExtractConfigService;
 import cn.com.qmth.examcloud.core.questions.service.QuestionProviderService;
 import cn.com.qmth.examcloud.question.commons.core.question.DefaultQuestion;
+import cn.com.qmth.examcloud.question.commons.core.question.DefaultQuestionStructure;
+import cn.com.qmth.examcloud.question.commons.core.question.DefaultQuestionUnit;
 import cn.com.qmth.examcloud.support.handler.QuestionBodyHandler;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import io.swagger.annotations.ApiOperation;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,6 +49,9 @@ public class DefaultQuesionController extends ControllerSupport {
     @Autowired
     private ExtractConfigProviderService extractConfigExamService;
 
+    @Autowired
+    private ExtractConfigService extractConfigService;
+
     @ApiOperation(value = "外部接口保存单个试题", notes = "外部接口保存单个试题")
     @PostMapping("/save")
     public ResponseEntity<Object> save(@RequestBody GetDefaultQuesionReq req) {
@@ -100,59 +109,89 @@ public class DefaultQuesionController extends ControllerSupport {
         return new ResponseEntity<>(defaultQuestions, HttpStatus.OK);
     }
 
-    @ApiOperation(value = "查询试题")
+    @ApiOperation(value = "查询试题详细信息(管理端)")
+    @PostMapping("/question/detail")
+    public ResponseEntity<Object> findForWebAdmin(@RequestBody GetQuestionReq req) {
+        DefaultQuestion defaultQuestion = this.getDefaultQuestion(req, "webAdmin");
+        return new ResponseEntity<>(defaultQuestion, HttpStatus.OK);
+    }
+
+    @ApiOperation(value = "查询试题详细信息(Web版客户端)")
     @PostMapping("/question")
-    public ResponseEntity<Object> findOne(@RequestBody GetQuestionReq questionReq) {
-        Long examId = questionReq.getExamId();
-        if (null == examId) {
-            throw new StatusException("Q-010078", "examId is null");
-        }
-        String courseCode = questionReq.getCourseCode();
-        if (StringUtils.isBlank(courseCode)) {
-            throw new StatusException("Q-010082", "courseCode is null");
-        }
-        String groupCode = questionReq.getGroupCode();
-        if (StringUtils.isBlank(groupCode)) {
-            throw new StatusException("Q-010086", "groupCode is null");
-        }
-        String questionId = questionReq.getQuestionId();
-        if (StringUtils.isBlank(questionId)) {
-            throw new StatusException("Q-010090", "questionId is null");
-        }
+    public ResponseEntity<Object> findForWebClient(@RequestBody GetQuestionReq req) {
+        DefaultQuestion defaultQuestion = this.getDefaultQuestion(req, "webClient");
+        this.handeQuestionAnswer(req.getExamId(), req.getCourseCode(), defaultQuestion);
 
-        DefaultQuestion defaultQuestion = extractConfigExamService.getDefaultQuestion(examId, courseCode, groupCode,
-                questionId, "webClient");
         return new ResponseEntity<>(defaultQuestion, HttpStatus.OK);
     }
 
-    @ApiOperation(value = "查询试题(客户端)")
+    @ApiOperation(value = "查询试题详细信息(C版客户端)")
     @PostMapping("/question/for/client")
-    public ResponseEntity<Object> findOneForClient(@RequestBody GetQuestionReq questionReq) {
-        Long examId = questionReq.getExamId();
+    public ResponseEntity<Object> findForPcClient(@RequestBody GetQuestionReq req) {
+        DefaultQuestion defaultQuestion = this.getDefaultQuestion(req, "pcClient");
+        this.handeQuestionAnswer(req.getExamId(), req.getCourseCode(), defaultQuestion);
+
+        // 将题干、选项等 HTML结构转换为“富文本”JSON结构
+        QuestionBodyHandler.convertRichText(defaultQuestion);
 
-        if (null == examId) {
+        return new ResponseEntity<>(defaultQuestion, HttpStatus.OK);
+    }
+
+    private DefaultQuestion getDefaultQuestion(GetQuestionReq req, String fromBy) {
+        if (req.getExamId() == null) {
             throw new StatusException("Q-010078", "examId is null");
         }
-        String courseCode = questionReq.getCourseCode();
-        if (StringUtils.isBlank(courseCode)) {
+
+        if (StringUtils.isBlank(req.getCourseCode())) {
             throw new StatusException("Q-010082", "courseCode is null");
         }
-        String groupCode = questionReq.getGroupCode();
-        if (StringUtils.isBlank(groupCode)) {
+
+        if (StringUtils.isBlank(req.getGroupCode())) {
             throw new StatusException("Q-010086", "groupCode is null");
         }
-        String questionId = questionReq.getQuestionId();
-        if (StringUtils.isBlank(questionId)) {
+
+        if (StringUtils.isBlank(req.getQuestionId())) {
             throw new StatusException("Q-010090", "questionId is null");
         }
 
-        DefaultQuestion defaultQuestion = extractConfigExamService.getDefaultQuestion(examId, courseCode, groupCode,
-                questionId, "pcClient");
+        return extractConfigExamService.getDefaultQuestion(req.getExamId(), req.getCourseCode(), req.getGroupCode(),
+                req.getQuestionId(), fromBy);
+    }
 
-        // 将题干、选项等 HTML结构转换为“富文本”JSON结构
-        QuestionBodyHandler.convertRichText(defaultQuestion);
+    private void handeQuestionAnswer(Long examId, String courseCode, DefaultQuestion defaultQuestion) {
+        ExtractConfig extractConfig = extractConfigService.findConfig(new ExtractConfig(examId, courseCode));
+
+        boolean hideAnswer = false;
+        if (extractConfig == null) {
+            // 不存在调卷规则,则业务数据不正常,屏蔽答案
+            hideAnswer = true;
+            log.warn("$$$异常行为,获取不存在调卷规则的试题敏感数据!questionId:{}", defaultQuestion.getId());
+        } else {
+            if (ExamType.ONLINE.name().equals(extractConfig.getExamType())
+                    || ExamType.ONLINE_HOMEWORK.name().equals(extractConfig.getExamType())) {
+                // 目前除了“练习”,“在线考试、在线作业”在学生端使用中不需要显示答案
+                hideAnswer = true;
+                log.warn("$$$异常行为,获取考试类型为{}的试题敏感数据!questionId:{}", extractConfig.getExamType(), defaultQuestion.getId());
+            }
+        }
 
-        return new ResponseEntity<>(defaultQuestion, HttpStatus.OK);
+        if (!hideAnswer) {
+            return;
+        }
+
+        DefaultQuestionStructure structure = defaultQuestion.getMasterVersion();
+        if (structure == null) {
+            return;
+        }
+        List<DefaultQuestionUnit> questionUnits = structure.getQuestionUnitList();
+        if (CollectionUtils.isEmpty(questionUnits)) {
+            return;
+        }
+
+        for (DefaultQuestionUnit questionUnit : questionUnits) {
+            // 清空答案
+            questionUnit.setRightAnswer(null);
+        }
     }
 
 }

+ 643 - 685
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExtractConfigProviderServiceImpl.java

@@ -1,64 +1,38 @@
 package cn.com.qmth.examcloud.core.questions.service.impl;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-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.collections.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Example;
-import org.springframework.stereotype.Service;
-
 import cn.com.qmth.examcloud.api.commons.enums.CallType;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 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.question.enums.QuesStructType;
-import cn.com.qmth.examcloud.core.questions.dao.AudioTimeConfigRepo;
-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.QuesRepo;
-import cn.com.qmth.examcloud.core.questions.dao.entity.AudioTimeConfig;
-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.service.ExtractConfigProviderService;
-import cn.com.qmth.examcloud.core.questions.service.ExtractConfigService;
-import cn.com.qmth.examcloud.core.questions.service.PaperService;
-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.dao.*;
+import cn.com.qmth.examcloud.core.questions.dao.entity.*;
+import cn.com.qmth.examcloud.core.questions.service.*;
 import cn.com.qmth.examcloud.core.questions.service.bean.QuestionAnswerConvert;
 import cn.com.qmth.examcloud.core.questions.service.bean.extract.ExtractConfigPaper;
 import cn.com.qmth.examcloud.question.commons.core.paper.DefaultPaper;
 import cn.com.qmth.examcloud.question.commons.core.paper.DefaultQuestionGroup;
 import cn.com.qmth.examcloud.question.commons.core.paper.DefaultQuestionStructureWrapper;
 import cn.com.qmth.examcloud.question.commons.core.paper.DefaultQuestionUnitWrapper;
-import cn.com.qmth.examcloud.question.commons.core.question.DefaultQuestion;
-import cn.com.qmth.examcloud.question.commons.core.question.DefaultQuestionOption;
-import cn.com.qmth.examcloud.question.commons.core.question.DefaultQuestionStructure;
-import cn.com.qmth.examcloud.question.commons.core.question.DefaultQuestionUnit;
 import cn.com.qmth.examcloud.question.commons.core.question.QuestionType;
+import cn.com.qmth.examcloud.question.commons.core.question.*;
 import cn.com.qmth.examcloud.support.cache.CacheHelper;
 import cn.com.qmth.examcloud.support.cache.bean.ExtractConfigCacheBean;
 import cn.com.qmth.examcloud.support.cache.bean.ExtractConfigDetailCacheBean;
 import cn.com.qmth.examcloud.support.cache.bean.ExtractConfigPaperCacheBean;
 import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 /**
  * @author weiwenhai
@@ -70,648 +44,632 @@ import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
 @Service("extractConfigCloudService")
 public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderService {
 
-	private static final Logger LOG = LoggerFactory.getLogger(ExtractConfigProviderServiceImpl.class);
-
-	@Autowired
-	private ExtractConfigService extractConfigService;
-
-	@Autowired
-	private PaperRepo paperRepo;
-
-	@Autowired
-	private PaperDetailRepo paperDetailRepo;
-
-	@Autowired
-	private PaperDetailUnitRepo paperDetailUnitRepo;
-
-	@Autowired
-	private QuesRepo quesRepo;
-
-	@Autowired
-	private AudioTimeConfigRepo audioTimeConfigRepo;
-
-	@Autowired
-	private QuestionAudioService questionAudioService;
-
-	@Autowired
-	private QuesService quesService;
-
-	@Autowired
-	private PaperService paperService;
-
-	// @Autowired
-	// private SysProperty sysProperty;
-
-	@Override
-	public ExtractConfigCacheBean getExtractConfig(Long examId, String courseCode) {
-		ExtractConfig extractConfig = extractConfigService.findConfig(new ExtractConfig(examId, courseCode));
-		if (extractConfig == null) {
-			throw new StatusException("500", "该课程调卷规则未制定,请联系学校!");
-		}
-		if (CallType.WHOLE_SET.equals(extractConfig.getCallType())) {
-			if (CollectionUtils.isEmpty(extractConfig.getExamPaperList())) {
-				throw new StatusException("500", "可供抽取的试卷集合为空!");
-			}
-		}
-
-		ExtractConfigCacheBean cacheBean = new ExtractConfigCacheBean();
-		cacheBean.setId(extractConfig.getId());
-		cacheBean.setExamId(examId);
-		cacheBean.setCallType(extractConfig.getCallType());
-		cacheBean.setCourseCode(courseCode);
-		cacheBean.setRandomPaperId(extractConfig.getRandomPaperId());
-		cacheBean.setPlayTime(extractConfig.getPlayTime());
-		cacheBean.setSortQuestionOrder(getByShort(extractConfig.getScrambling_the_question_order()));
-		cacheBean.setSortOptionOrder(getByShort(extractConfig.getScrambling_the_option_order()));
-
-		if (CallType.WHOLE_SET.equals(extractConfig.getCallType())) {
-			List<ExtractConfigDetailCacheBean> details = new ArrayList<>();
-			for (ExamPaper examPaper : extractConfig.getExamPaperList()) {
-				details.add(new ExtractConfigDetailCacheBean(examPaper.getGroupCode(), examPaper.getPaper().getId(),
-						examPaper.getWeight()));
-			}
-			cacheBean.setDetails(details);
-		}
-
-		return cacheBean;
-	}
-	
-	private boolean getByShort(Short s) {
-		if(s==null) {
-			return false;
-		}else {
-			return s!=0;
-		}
-	}
-
-	@Override
-	public ExtractConfigPaperCacheBean getExtractConfigPaper(Long examId, String courseCode, String groupCode,
-			String paperId) {
-		DefaultPaper defaultPaper = this.buildDefaultByBasePaper(paperId, examId, courseCode, groupCode);
-
-		ExtractConfigPaperCacheBean cacheBean = new ExtractConfigPaperCacheBean();
-		cacheBean.setDefaultPaper(defaultPaper);
-		return cacheBean;
-	}
-
-	@Override
-	public ExtractConfigPaper getDefaultPaper(Long examId, String courseCode, String groupCode) {
-		LOG.info("调卷开始...");
-
-		ExtractConfigCacheBean extractConfigCache = CacheHelper.getExtractConfig(examId, courseCode);
-		if (extractConfigCache == null) {
-			throw new StatusException("500", "该课程调卷规则未制定,请联系学校!");
-		}
-
-		final String msg = "该考试和课程下调卷规则中试卷不存在,请重新制定调卷规则!";
-		Map<String, String> paperTypeMaps = this.getExamPaperByProbability(extractConfigCache.getDetails());
-		if (paperTypeMaps.isEmpty()) {
-			throw new StatusException("500", msg);
-		}
-
-		String basePaperId = paperTypeMaps.get(groupCode);
-		if (StringUtils.isEmpty(basePaperId)) {
-			throw new StatusException("500", msg);
-		}
-
-		LOG.info("构建试卷结构..." + basePaperId);
-		ExtractConfigPaperCacheBean extractConfigPaperCache = CacheHelper.getExtractConfigPaper(examId, courseCode,
-				groupCode, basePaperId);
-		if (extractConfigPaperCache == null) {
-			throw new StatusException("500", msg);
-		}
-
-		ExtractConfigPaper result = new ExtractConfigPaper();
-		result.setDefaultPaper(extractConfigPaperCache.getDefaultPaper());
-		result.setPaperId(basePaperId);
-		result.setSortQuestionOrder(extractConfigCache.getSortQuestionOrder());
-		result.setSortOptionOrder(extractConfigCache.getSortOptionOrder());
-		return result;
-	}
-
-	/**
-	 * 每个试卷类型取出一套试卷 {A:paperId,B:paperId} A是试卷类型,paperId是A类型下选定的试卷ID
-	 */
-	private Map<String, String> getExamPaperByProbability(List<ExtractConfigDetailCacheBean> examPapers) {
-		if (CollectionUtils.isEmpty(examPapers)) {
-			throw new StatusException("500", "可供抽取的试卷集合为空!");
-		}
-
-		Map<String, List<ExtractConfigDetailCacheBean>> examPaperMaps = new HashMap<>();
-		for (ExtractConfigDetailCacheBean examPaper : examPapers) {
-			if (examPaperMaps.containsKey(examPaper.getGroupCode())) {
-				examPaperMaps.get(examPaper.getGroupCode()).add(examPaper);
-			} else {
-				List<ExtractConfigDetailCacheBean> list = new ArrayList<>();
-				list.add(examPaper);
-				examPaperMaps.put(examPaper.getGroupCode(), list);
-			}
-		}
-
-		Map<String, String> paperTypeMaps = new HashMap<>();
-		for (Map.Entry<String, List<ExtractConfigDetailCacheBean>> entry : examPaperMaps.entrySet()) {
-			List<ExtractConfigDetailCacheBean> list = examPaperMaps.get(entry.getKey());
-
-			String paperId = this.getPaperByProbability(list);
-			if (StringUtils.isEmpty(paperId)) {
-				continue;
-			}
-
-			paperTypeMaps.put(entry.getKey(), paperId);
-		}
-
-		return paperTypeMaps;
-	}
-
-	/**
-	 * 根据设定几率取出一套试卷
-	 */
-	private String getPaperByProbability(List<ExtractConfigDetailCacheBean> examPapers) {
-		int sum = 0;
-		for (ExtractConfigDetailCacheBean examPaper : examPapers) {
-			sum += examPaper.getWeight();
-		}
-
-		// 从1开始
-		int r = new Random().nextInt(sum) + 1;
-		for (ExtractConfigDetailCacheBean examPaper : examPapers) {
-			r -= examPaper.getWeight();
-			if (r <= 0) {
-				return examPaper.getPaperId();// 选中
-			}
-		}
-
-		return null;
-	}
-
-	/**
-	 * 根据paper对象构建DefaultPaper对象
-	 */
-	public DefaultPaper buildDefaultByBasePaper(String basePaperId, Long examId, String courseCode, String groupCode) {
-		Paper basePaper = Model.of(paperRepo.findById(basePaperId));
-		return this.buildDefaultByBasePaper(basePaper, examId, courseCode, groupCode);
-	}
-
-	/**
-	 * 根据paper对象构建DefaultPaper对象
-	 */
-	public DefaultPaper buildDefaultByBasePaper(Paper basePaper, Long examId, String courseCode, String groupCode) {
-		LOG.debug("开始包装网考需要的试卷结构...");
-
-		// 获取大题
-		List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(basePaper);
-
-		// 排序大题
-		Collections.sort(paperDetails);
-
-		// 将小题全部取出来,只取一次,减少对数据库的查询
-		List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaper(basePaper);
-		boolean fullyObjective = checkIsAllQbjectiveQuestion(allPaperDetailUnits);
-
-		// 根据大题id将小题归类
-		Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
-				.collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
-
-		LOG.debug("循环大题,开始组装对象...");
-
-		// 生成新的分组集合
-		List<DefaultQuestionGroup> questionGroupList = new ArrayList<>();
-		for (int i = 0; i < paperDetails.size(); i++) {
-			PaperDetail paperDetail = paperDetails.get(i);
-			DefaultQuestionGroup defaultQuestionGroup = new DefaultQuestionGroup();
-			defaultQuestionGroup.setGroupName(paperDetail.getName());
-			defaultQuestionGroup.setGroupScore(paperDetail.getScore());
-
-			// 获取原小题的集合
-			List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());
-			if (CollectionUtils.isEmpty(paperDetailUnits)) {
-				LOG.warn("试卷大题下面没有小题!");
-				throw new StatusException("500", "考试试卷有误,请联系老师!");
-			}
-
-			// 按题号顺序排序
-			Collections.sort(paperDetailUnits);
-
-			// 生成新的题包装器集合
-			List<DefaultQuestionStructureWrapper> questionWrapperList = new ArrayList<>();
-			for (int j = 0; j < paperDetailUnits.size(); j++) {
-				PaperDetailUnit paperDetailUnit = paperDetailUnits.get(j);
-				DefaultQuestionStructureWrapper defaultQuestionStructureWrapper = new DefaultQuestionStructureWrapper();
-				defaultQuestionStructureWrapper.setQuestionId(paperDetailUnit.getQuestion().getId());
-				defaultQuestionStructureWrapper.setVersion(CommonUtils.QUESTION_VERSION);
-				defaultQuestionStructureWrapper.setQuestionScore(paperDetailUnit.getScore());
-				defaultQuestionStructureWrapper.setTimeLimit(paperDetailUnit.getTimeLimit());
-
-				if (examId != null) {
-					// 设置音频播放次数
-					if (paperDetailUnit.getQuestion().getHasAudio() != null
-							&& paperDetailUnit.getQuestion().getHasAudio()) {
-						AudioTimeConfig find = new AudioTimeConfig(examId.toString(), courseCode, groupCode,
-								paperDetailUnit.getQuestion().getId());
-						AudioTimeConfig audioTimeConfig = Model.of(audioTimeConfigRepo.findOne(Example.of(find)));
-						if (audioTimeConfig != null) {
-							defaultQuestionStructureWrapper.setLimitedPlayTimes(audioTimeConfig.getPlayTime());
-						}
-					}
-				}
-
-				// 生成新的题单元包装器
-				List<DefaultQuestionUnitWrapper> defaultQuestionUnitWrappers = new ArrayList<>();
-				if (paperDetailUnit.getQuestion().getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
-					List<Question> subQuesList = paperDetailUnit.getQuestion().getSubQuestions();
-					List<Double> scoreList = paperDetailUnit.getSubScoreList();
-					if (subQuesList != null && subQuesList.size() > 0) {
-						for (int k = 0; k < subQuesList.size(); k++) {
-							DefaultQuestionUnitWrapper defaultQuestionUnitWrapper = buildQuesUnitWrapper(
-									subQuesList.get(k), scoreList.get(k));
-							defaultQuestionUnitWrappers.add(defaultQuestionUnitWrapper);
-						}
-					}
-				} else {
-					DefaultQuestionUnitWrapper defaultQuestionUnitWrapper = buildQuesUnitWrapper(
-							paperDetailUnit.getQuestion(), paperDetailUnit.getScore());
-					defaultQuestionUnitWrappers.add(defaultQuestionUnitWrapper);
-				}
-
-				defaultQuestionStructureWrapper.setQuestionUnitWrapperList(defaultQuestionUnitWrappers);
-				questionWrapperList.add(defaultQuestionStructureWrapper);
-			}
-
-			defaultQuestionGroup.setQuestionWrapperList(questionWrapperList);
-			questionGroupList.add(defaultQuestionGroup);
-		}
-
-		DefaultPaper defaultPaper = new DefaultPaper();
-		defaultPaper.setName(basePaper.getName());
-		defaultPaper.setQuestionGroupList(questionGroupList);
-		defaultPaper.setFullyObjective(fullyObjective);
-
-		return defaultPaper;
-	}
-
-	/**
-	 * 根据question生成题单元包装器
-	 *
-	 * @param question
-	 * @param score
-	 * @return
-	 */
-	private DefaultQuestionUnitWrapper buildQuesUnitWrapper(Question question, Double score) {
-		Integer[] optionPermutation = null;
-		if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-				|| question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-			int length = question.getQuesOptions().size();
-			optionPermutation = new Integer[length];
-
-			for (int i = 0; i < length; i++) {
-				optionPermutation[i] = i;
-			}
-		}
-
-		DefaultQuestionUnitWrapper defaultQuestionUnitWrapper = new DefaultQuestionUnitWrapper();
-		defaultQuestionUnitWrapper.setOptionPermutation(optionPermutation);
-		defaultQuestionUnitWrapper.setQuestionScore(score);
-		defaultQuestionUnitWrapper.setQuestionType(getByOldType(question.getQuestionType()));
-		defaultQuestionUnitWrapper.setAnswerType(question.getAnswerType());// 作答类型
-		return defaultQuestionUnitWrapper;
-	}
-
-	/**
-	 * 题型转换方法
-	 *
-	 * @param quesStructType
-	 * @return
-	 */
-	private QuestionType getByOldType(QuesStructType quesStructType) {
-		if (quesStructType == QuesStructType.BOOL_ANSWER_QUESTION) {
-			return QuestionType.TRUE_OR_FALSE;
-		}
-		if (quesStructType == QuesStructType.FILL_BLANK_QUESTION) {
-			return QuestionType.FILL_UP;
-		}
-		if (quesStructType == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-			return QuestionType.MULTIPLE_CHOICE;
-		}
-		if (quesStructType == QuesStructType.SINGLE_ANSWER_QUESTION) {
-			return QuestionType.SINGLE_CHOICE;
-		}
-		if (quesStructType == QuesStructType.TEXT_ANSWER_QUESTION) {
-			return QuestionType.ESSAY;
-		}
-		return null;
-	}
-
-	@Override
-	public DefaultQuestion getDefaultQuestion(Long examId, String courseCode, String groupCode, String questionId, String fromBy) {
-		Question question = Model.of(quesRepo.findById(questionId));
-		if (question == null) {
-			throw new StatusException("500", "试题不存在!");
-		}
-		quesService.formatQues(question);
-
-		// 封装成新的题单元集合
-		DefaultQuestionStructure defaultQuestionStructure = new DefaultQuestionStructure();
-		if (question.getHasAudio() == null || false == question.getHasAudio()) {
-			defaultQuestionStructure.setHasAudios(false);
-		} else {
-			defaultQuestionStructure.setHasAudios(true);
-		}
-		defaultQuestionStructure.setVersion(CommonUtils.QUESTION_VERSION);
-
-		List<DefaultQuestionUnit> questionUnitList = new ArrayList<>();
-		if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
-			defaultQuestionStructure.setBody(question.getQuesBody());
-
-			// 获取套题下面所有子题
-			List<Question> subQuesList = question.getSubQuestions();
-			if (CollectionUtils.isNotEmpty(subQuesList)) {
-				for (int i = 0; i < subQuesList.size(); i++) {
-					Question subQuestion = subQuesList.get(i);
-					DefaultQuestionUnit defaultQuestionUnit = buildQuestionUnit(subQuestion);
-					questionUnitList.add(defaultQuestionUnit);
-				}
-			}
-		} else {
-			DefaultQuestionUnit defaultQuestionUnit = buildQuestionUnit(question);
-			questionUnitList.add(defaultQuestionUnit);
-		}
-
-		defaultQuestionStructure.setQuestionUnitList(questionUnitList);
-		DefaultQuestion defaultQuestion = new DefaultQuestion();
-		defaultQuestion.setId(questionId);
-		defaultQuestion.setMasterVersion(defaultQuestionStructure);
-
-		if (examId != null) {
-			appendAudioFlag(defaultQuestion, String.valueOf(examId), courseCode, groupCode, question);
-		}
-
-		// 获取试题信息带答案(敏感信息日志,用于排查调用者)
-		LOG.warn("$$$getQuestionWithAnswer fromBy:{} questionId:{} {}_{}_{} ", fromBy, questionId, examId, courseCode, groupCode);
-		return defaultQuestion;
-	}
-
-	/**
-	 * 构建试题单元
-	 *
-	 * @param question
-	 * @return
-	 */
-	private DefaultQuestionUnit buildQuestionUnit(Question question) {
-		DefaultQuestionUnit defaultQuestionUnit = new DefaultQuestionUnit();
-		defaultQuestionUnit.setAnswerType(question.getAnswerType());// 作答类型
-		defaultQuestionUnit.setBody(question.getQuesBody());
-		defaultQuestionUnit.setQuestionType(getByOldType(question.getQuestionType()));
-
-		// 如果是单选或者多选,添加选项和答案转换
-		if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-				|| question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-			List<DefaultQuestionOption> defaultQuestionOptions = new ArrayList<>();
-			List<QuesOption> quesOptions = question.getQuesOptions();
-
-			if (quesOptions != null && quesOptions.size() > 0) {
-				for (int i = 0; i < quesOptions.size(); i++) {
-					QuesOption quesOption = quesOptions.get(i);
-					DefaultQuestionOption defaultQuestionOption = new DefaultQuestionOption();
-					defaultQuestionOption.setBody(quesOption.getOptionBody());
-					defaultQuestionOptions.add(defaultQuestionOption);
-				}
-			}
-			defaultQuestionUnit.setQuestionOptionList(defaultQuestionOptions);
-			defaultQuestionUnit.setRightAnswer(QuestionAnswerConvert.parseQuestionOptionAnswers(quesOptions));
-		} else {
-			defaultQuestionUnit.setRightAnswer(QuestionAnswerConvert.parseQuestionAnswers(question));
-		}
-
-		return defaultQuestionUnit;
-	}
-
-	/**
-	 * html中添加音频标签
-	 *
-	 * @param defaultQuestion
-	 * @param examId
-	 * @param courseCode
-	 * @param groupCode
-	 * @param question
-	 */
-	private void appendAudioFlag(DefaultQuestion defaultQuestion, String examId, String courseCode, String groupCode,
-			Question question) {
-		if (question.getHasAudio() != null && question.getHasAudio() == true) {
-			// 1.判断questionDto是否含有音频,如果有添加音频播放次数
-			AudioTimeConfig audioTimeConfig = Model.of(audioTimeConfigRepo
-					.findOne(Example.of(new AudioTimeConfig(examId, courseCode, groupCode, question.getId()))));
-			if (audioTimeConfig != null) {
-				// 2.1 取到题干,给a标签添加url
-				String quesBody = null;
-				if (StringUtils.isNotBlank(defaultQuestion.getMasterVersion().getBody())) {
-					// 套题
-					quesBody = defaultQuestion.getMasterVersion().getBody();
-					defaultQuestion.getMasterVersion().setBody(buildBody(quesBody, audioTimeConfig.getPlayTime()));
-				} else {
-					DefaultQuestionUnit defaultQuestionUnit = (DefaultQuestionUnit) defaultQuestion.getMasterVersion()
-							.getQuestionUnitList().get(0);
-					quesBody = defaultQuestionUnit.getBody();
-					defaultQuestionUnit.setBody(buildBody(quesBody, audioTimeConfig.getPlayTime()));
-					if (defaultQuestionUnit.getQuestionType() == QuestionType.SINGLE_CHOICE
-							|| defaultQuestionUnit.getQuestionType() == QuestionType.MULTIPLE_CHOICE) {
-						List<DefaultQuestionOption> questionOptionList = defaultQuestionUnit.getQuestionOptionList();
-						if (questionOptionList != null && questionOptionList.size() > 0) {
-							for (int i = 0; i < questionOptionList.size(); i++) {
-								DefaultQuestionOption defaultQuestionOption = questionOptionList.get(i);
-								defaultQuestionOption.setBody(
-										buildBody(defaultQuestionOption.getBody(), audioTimeConfig.getPlayTime()));
-							}
-						}
-					}
-				}
-			} else {
-				String quesBody = null;
-				if (StringUtils.isNotBlank(defaultQuestion.getMasterVersion().getBody())) {
-					// 套题
-					quesBody = defaultQuestion.getMasterVersion().getBody();
-					defaultQuestion.getMasterVersion().setBody(buildBody(quesBody, null));
-				} else {
-					DefaultQuestionUnit defaultQuestionUnit = (DefaultQuestionUnit) defaultQuestion.getMasterVersion()
-							.getQuestionUnitList().get(0);
-					quesBody = defaultQuestionUnit.getBody();
-					defaultQuestionUnit.setBody(buildBody(quesBody, null));
-					if (defaultQuestionUnit.getQuestionType() == QuestionType.SINGLE_CHOICE
-							|| defaultQuestionUnit.getQuestionType() == QuestionType.MULTIPLE_CHOICE) {
-						List<DefaultQuestionOption> questionOptionList = defaultQuestionUnit.getQuestionOptionList();
-						if (questionOptionList != null && questionOptionList.size() > 0) {
-							for (int i = 0; i < questionOptionList.size(); i++) {
-								DefaultQuestionOption defaultQuestionOption = questionOptionList.get(i);
-								defaultQuestionOption.setBody(buildBody(defaultQuestionOption.getBody(), null));
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-
-	/**
-	 * 给题目和选项添加url
-	 *
-	 * @param body
-	 * @param playTime
-	 * @return
-	 */
-	public String buildBody(String body, Integer playTime) {
-		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 (playTime != null) {
-							containAStr += " question-audio url=\"" + url + "\" playTime=\"" + playTime + "\""
-									+ "></a>";
-						} else {
-							containAStr += " question-audio url=\"" + url + "\"" + "></a>";
-						}
-					}
-				}
-				resultBody += containAStr;
-			}
-			return resultBody;
-		} else {
-			return body;
-		}
-	}
-
-	/**
-	 * 获取一段html中某个标签的值
-	 *
-	 * @param source
-	 * @param element
-	 * @param attr
-	 * @return
-	 */
-	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 "";
-	}
-
-	/**
-	 * 检查所有题目是否为客观题
-	 *
-	 * @param paperDetailUnits
-	 * @return
-	 */
-	private boolean checkIsAllQbjectiveQuestion(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 DefaultPaper getBaseDefaultPaper(String paperId) {
-		Paper basePaper = Model.of(paperRepo.findById(paperId));
-		if (basePaper == null) {
-			LOG.error("该考试和课程下调卷规则中该类型试卷不存在,请检查调卷规则,调卷程序退出");
-			throw new StatusException("Q-020560", "该考试和课程下调卷规则中试卷不存在,请重新制定调卷规则");
-		}
-
-		// 构建试卷结构
-		DefaultPaper defaultPaper = this.buildDefaultByBasePaper(basePaper, null, null, null);
-		return defaultPaper;
-	}
-
-	@Override
-	public Map<String, DefaultQuestion> getDefaultQuestions(Set<String> questionIds) {
-		Map<String, DefaultQuestion> map = new HashMap<>();
-		List<Question> questions = quesRepo.findByIdIn(questionIds);
-		if (CollectionUtils.isEmpty(questions)) {
-			LOG.error("根据试题id的集合没有查询到试题结合");
-			throw new StatusException("Q-020572", "根据试题id的集合没有查询到试题结合");
-		}
-		for (Question question : questions) {
-			DefaultQuestion defaultQuestion = getDefaultQuestion(null, null, null,
-					question.getId(), "default");
-
-			map.put(question.getId(), defaultQuestion);
-		}
-		return map;
-	}
-
-	@Override
-	public List<String> getAnswer(String questionId) {
-		List<String> list = new ArrayList<>();
-		Question question = Model.of(quesRepo.findById(questionId));
-
-		if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
-			List<Question> subList = question.getSubQuestions();
-			if (subList != null && subList.size() > 0) {
-				String answer = "";
-				for (Question subQuestion : subList) {
-					if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-							|| subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-						String[] answers = QuestionAnswerConvert
-								.parseQuestionOptionAnswers(subQuestion.getQuesOptions());
-						answer = StringUtils.join(answers, ",");
-					} else if (subQuestion.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
-						if (subQuestion.getQuesAnswer().endsWith("正确")) {
-							answer = "true";
-						} else {
-							answer = "false";
-						}
-					} else {
-						answer = subQuestion.getQuesAnswer();
-					}
-					list.add(answer);
-				}
-			}
-		} else {
-			String answer = "";
-			if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-					|| question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-				String[] answers = QuestionAnswerConvert.parseQuestionOptionAnswers(question.getQuesOptions());
-				answer = StringUtils.join(answers, ",");
-			} else if (question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
-				if (question.getQuesAnswer().endsWith("正确")) {
-					answer = "true";
-				} else {
-					answer = "false";
-				}
-			} else {
-				answer = question.getQuesAnswer();
-			}
-			list.add(answer);
-		}
-
-		return list;
-	}
-
-	@Override
-	public String getQuestionStructure(String paperId) throws Exception {
-		String json = paperService.findQuestionStructure(paperId);
-		return json;
-	}
+    private static final Logger LOG = LoggerFactory.getLogger(ExtractConfigProviderServiceImpl.class);
+
+    @Autowired
+    private ExtractConfigService extractConfigService;
+
+    @Autowired
+    private PaperRepo paperRepo;
+
+    @Autowired
+    private PaperDetailRepo paperDetailRepo;
+
+    @Autowired
+    private PaperDetailUnitRepo paperDetailUnitRepo;
+
+    @Autowired
+    private QuesRepo quesRepo;
+
+    @Autowired
+    private AudioTimeConfigRepo audioTimeConfigRepo;
+
+    @Autowired
+    private QuestionAudioService questionAudioService;
+
+    @Autowired
+    private QuesService quesService;
+
+    @Autowired
+    private PaperService paperService;
+
+    // @Autowired
+    // private SysProperty sysProperty;
+
+    @Override
+    public ExtractConfigCacheBean getExtractConfig(Long examId, String courseCode) {
+        ExtractConfig extractConfig = extractConfigService.findConfig(new ExtractConfig(examId, courseCode));
+        if (extractConfig == null) {
+            throw new StatusException("500", "该课程调卷规则未制定,请联系学校!");
+        }
+        if (CallType.WHOLE_SET.equals(extractConfig.getCallType())) {
+            if (CollectionUtils.isEmpty(extractConfig.getExamPaperList())) {
+                throw new StatusException("500", "可供抽取的试卷集合为空!");
+            }
+        }
+
+        ExtractConfigCacheBean cacheBean = new ExtractConfigCacheBean();
+        cacheBean.setId(extractConfig.getId());
+        cacheBean.setExamId(examId);
+        cacheBean.setCallType(extractConfig.getCallType());
+        cacheBean.setCourseCode(courseCode);
+        cacheBean.setRandomPaperId(extractConfig.getRandomPaperId());
+        cacheBean.setPlayTime(extractConfig.getPlayTime());
+        cacheBean.setSortQuestionOrder(getByShort(extractConfig.getScrambling_the_question_order()));
+        cacheBean.setSortOptionOrder(getByShort(extractConfig.getScrambling_the_option_order()));
+
+        if (CallType.WHOLE_SET.equals(extractConfig.getCallType())) {
+            List<ExtractConfigDetailCacheBean> details = new ArrayList<>();
+            for (ExamPaper examPaper : extractConfig.getExamPaperList()) {
+                details.add(new ExtractConfigDetailCacheBean(examPaper.getGroupCode(), examPaper.getPaper().getId(),
+                        examPaper.getWeight()));
+            }
+            cacheBean.setDetails(details);
+        }
+
+        return cacheBean;
+    }
+
+    private boolean getByShort(Short s) {
+        if (s == null) {
+            return false;
+        } else {
+            return s != 0;
+        }
+    }
+
+    @Override
+    public ExtractConfigPaperCacheBean getExtractConfigPaper(Long examId, String courseCode, String groupCode,
+                                                             String paperId) {
+        DefaultPaper defaultPaper = this.buildDefaultByBasePaper(paperId, examId, courseCode, groupCode);
+
+        ExtractConfigPaperCacheBean cacheBean = new ExtractConfigPaperCacheBean();
+        cacheBean.setDefaultPaper(defaultPaper);
+        return cacheBean;
+    }
+
+    @Override
+    public ExtractConfigPaper getDefaultPaper(Long examId, String courseCode, String groupCode) {
+        LOG.info("调卷开始...");
+
+        ExtractConfigCacheBean extractConfigCache = CacheHelper.getExtractConfig(examId, courseCode);
+        if (extractConfigCache == null) {
+            throw new StatusException("500", "该课程调卷规则未制定,请联系学校!");
+        }
+
+        final String msg = "该考试和课程下调卷规则中试卷不存在,请重新制定调卷规则!";
+        Map<String, String> paperTypeMaps = this.getExamPaperByProbability(extractConfigCache.getDetails());
+        if (paperTypeMaps.isEmpty()) {
+            throw new StatusException("500", msg);
+        }
+
+        String basePaperId = paperTypeMaps.get(groupCode);
+        if (StringUtils.isEmpty(basePaperId)) {
+            throw new StatusException("500", msg);
+        }
+
+        LOG.info("构建试卷结构..." + basePaperId);
+        ExtractConfigPaperCacheBean extractConfigPaperCache = CacheHelper.getExtractConfigPaper(examId, courseCode,
+                groupCode, basePaperId);
+        if (extractConfigPaperCache == null) {
+            throw new StatusException("500", msg);
+        }
+
+        ExtractConfigPaper result = new ExtractConfigPaper();
+        result.setDefaultPaper(extractConfigPaperCache.getDefaultPaper());
+        result.setPaperId(basePaperId);
+        result.setSortQuestionOrder(extractConfigCache.getSortQuestionOrder());
+        result.setSortOptionOrder(extractConfigCache.getSortOptionOrder());
+        return result;
+    }
+
+    /**
+     * 每个试卷类型取出一套试卷 {A:paperId,B:paperId} A是试卷类型,paperId是A类型下选定的试卷ID
+     */
+    private Map<String, String> getExamPaperByProbability(List<ExtractConfigDetailCacheBean> examPapers) {
+        if (CollectionUtils.isEmpty(examPapers)) {
+            throw new StatusException("500", "可供抽取的试卷集合为空!");
+        }
+
+        Map<String, List<ExtractConfigDetailCacheBean>> examPaperMaps = new HashMap<>();
+        for (ExtractConfigDetailCacheBean examPaper : examPapers) {
+            if (examPaperMaps.containsKey(examPaper.getGroupCode())) {
+                examPaperMaps.get(examPaper.getGroupCode()).add(examPaper);
+            } else {
+                List<ExtractConfigDetailCacheBean> list = new ArrayList<>();
+                list.add(examPaper);
+                examPaperMaps.put(examPaper.getGroupCode(), list);
+            }
+        }
+
+        Map<String, String> paperTypeMaps = new HashMap<>();
+        for (Map.Entry<String, List<ExtractConfigDetailCacheBean>> entry : examPaperMaps.entrySet()) {
+            List<ExtractConfigDetailCacheBean> list = examPaperMaps.get(entry.getKey());
+
+            String paperId = this.getPaperByProbability(list);
+            if (StringUtils.isEmpty(paperId)) {
+                continue;
+            }
+
+            paperTypeMaps.put(entry.getKey(), paperId);
+        }
+
+        return paperTypeMaps;
+    }
+
+    /**
+     * 根据设定几率取出一套试卷
+     */
+    private String getPaperByProbability(List<ExtractConfigDetailCacheBean> examPapers) {
+        int sum = 0;
+        for (ExtractConfigDetailCacheBean examPaper : examPapers) {
+            sum += examPaper.getWeight();
+        }
+
+        // 从1开始
+        int r = new Random().nextInt(sum) + 1;
+        for (ExtractConfigDetailCacheBean examPaper : examPapers) {
+            r -= examPaper.getWeight();
+            if (r <= 0) {
+                return examPaper.getPaperId();// 选中
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * 根据paper对象构建DefaultPaper对象
+     */
+    public DefaultPaper buildDefaultByBasePaper(String basePaperId, Long examId, String courseCode, String groupCode) {
+        Paper basePaper = Model.of(paperRepo.findById(basePaperId));
+        return this.buildDefaultByBasePaper(basePaper, examId, courseCode, groupCode);
+    }
+
+    /**
+     * 根据paper对象构建DefaultPaper对象
+     */
+    public DefaultPaper buildDefaultByBasePaper(Paper basePaper, Long examId, String courseCode, String groupCode) {
+        LOG.debug("开始包装网考需要的试卷结构...");
+
+        // 获取大题
+        List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(basePaper);
+
+        // 排序大题
+        Collections.sort(paperDetails);
+
+        // 将小题全部取出来,只取一次,减少对数据库的查询
+        List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaper(basePaper);
+        boolean fullyObjective = checkIsAllQbjectiveQuestion(allPaperDetailUnits);
+
+        // 根据大题id将小题归类
+        Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
+                .collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
+
+        LOG.debug("循环大题,开始组装对象...");
+
+        // 生成新的分组集合
+        List<DefaultQuestionGroup> questionGroupList = new ArrayList<>();
+        for (int i = 0; i < paperDetails.size(); i++) {
+            PaperDetail paperDetail = paperDetails.get(i);
+            DefaultQuestionGroup defaultQuestionGroup = new DefaultQuestionGroup();
+            defaultQuestionGroup.setGroupName(paperDetail.getName());
+            defaultQuestionGroup.setGroupScore(paperDetail.getScore());
+
+            // 获取原小题的集合
+            List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());
+            if (CollectionUtils.isEmpty(paperDetailUnits)) {
+                LOG.warn("试卷大题下面没有小题!");
+                throw new StatusException("500", "考试试卷有误,请联系老师!");
+            }
+
+            // 按题号顺序排序
+            Collections.sort(paperDetailUnits);
+
+            // 生成新的题包装器集合
+            List<DefaultQuestionStructureWrapper> questionWrapperList = new ArrayList<>();
+            for (int j = 0; j < paperDetailUnits.size(); j++) {
+                PaperDetailUnit paperDetailUnit = paperDetailUnits.get(j);
+                DefaultQuestionStructureWrapper defaultQuestionStructureWrapper = new DefaultQuestionStructureWrapper();
+                defaultQuestionStructureWrapper.setQuestionId(paperDetailUnit.getQuestion().getId());
+                defaultQuestionStructureWrapper.setVersion(CommonUtils.QUESTION_VERSION);
+                defaultQuestionStructureWrapper.setQuestionScore(paperDetailUnit.getScore());
+                defaultQuestionStructureWrapper.setTimeLimit(paperDetailUnit.getTimeLimit());
+
+                if (examId != null) {
+                    // 设置音频播放次数
+                    if (paperDetailUnit.getQuestion().getHasAudio() != null
+                            && paperDetailUnit.getQuestion().getHasAudio()) {
+                        AudioTimeConfig find = new AudioTimeConfig(examId.toString(), courseCode, groupCode,
+                                paperDetailUnit.getQuestion().getId());
+                        AudioTimeConfig audioTimeConfig = Model.of(audioTimeConfigRepo.findOne(Example.of(find)));
+                        if (audioTimeConfig != null) {
+                            defaultQuestionStructureWrapper.setLimitedPlayTimes(audioTimeConfig.getPlayTime());
+                        }
+                    }
+                }
+
+                // 生成新的题单元包装器
+                List<DefaultQuestionUnitWrapper> defaultQuestionUnitWrappers = new ArrayList<>();
+                if (paperDetailUnit.getQuestion().getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+                    List<Question> subQuesList = paperDetailUnit.getQuestion().getSubQuestions();
+                    List<Double> scoreList = paperDetailUnit.getSubScoreList();
+                    if (subQuesList != null && subQuesList.size() > 0) {
+                        for (int k = 0; k < subQuesList.size(); k++) {
+                            DefaultQuestionUnitWrapper defaultQuestionUnitWrapper = buildQuesUnitWrapper(
+                                    subQuesList.get(k), scoreList.get(k));
+                            defaultQuestionUnitWrappers.add(defaultQuestionUnitWrapper);
+                        }
+                    }
+                } else {
+                    DefaultQuestionUnitWrapper defaultQuestionUnitWrapper = buildQuesUnitWrapper(
+                            paperDetailUnit.getQuestion(), paperDetailUnit.getScore());
+                    defaultQuestionUnitWrappers.add(defaultQuestionUnitWrapper);
+                }
+
+                defaultQuestionStructureWrapper.setQuestionUnitWrapperList(defaultQuestionUnitWrappers);
+                questionWrapperList.add(defaultQuestionStructureWrapper);
+            }
+
+            defaultQuestionGroup.setQuestionWrapperList(questionWrapperList);
+            questionGroupList.add(defaultQuestionGroup);
+        }
+
+        DefaultPaper defaultPaper = new DefaultPaper();
+        defaultPaper.setName(basePaper.getName());
+        defaultPaper.setQuestionGroupList(questionGroupList);
+        defaultPaper.setFullyObjective(fullyObjective);
+
+        return defaultPaper;
+    }
+
+    /**
+     * 根据question生成题单元包装器
+     *
+     * @param question
+     * @param score
+     * @return
+     */
+    private DefaultQuestionUnitWrapper buildQuesUnitWrapper(Question question, Double score) {
+        Integer[] optionPermutation = null;
+        if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+                || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+            int length = question.getQuesOptions().size();
+            optionPermutation = new Integer[length];
+
+            for (int i = 0; i < length; i++) {
+                optionPermutation[i] = i;
+            }
+        }
+
+        DefaultQuestionUnitWrapper defaultQuestionUnitWrapper = new DefaultQuestionUnitWrapper();
+        defaultQuestionUnitWrapper.setOptionPermutation(optionPermutation);
+        defaultQuestionUnitWrapper.setQuestionScore(score);
+        defaultQuestionUnitWrapper.setQuestionType(getByOldType(question.getQuestionType()));
+        defaultQuestionUnitWrapper.setAnswerType(question.getAnswerType());// 作答类型
+        return defaultQuestionUnitWrapper;
+    }
+
+    /**
+     * 题型转换方法
+     *
+     * @param quesStructType
+     * @return
+     */
+    private QuestionType getByOldType(QuesStructType quesStructType) {
+        if (quesStructType == QuesStructType.BOOL_ANSWER_QUESTION) {
+            return QuestionType.TRUE_OR_FALSE;
+        }
+        if (quesStructType == QuesStructType.FILL_BLANK_QUESTION) {
+            return QuestionType.FILL_UP;
+        }
+        if (quesStructType == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+            return QuestionType.MULTIPLE_CHOICE;
+        }
+        if (quesStructType == QuesStructType.SINGLE_ANSWER_QUESTION) {
+            return QuestionType.SINGLE_CHOICE;
+        }
+        if (quesStructType == QuesStructType.TEXT_ANSWER_QUESTION) {
+            return QuestionType.ESSAY;
+        }
+        return null;
+    }
+
+    @Override
+    public DefaultQuestion getDefaultQuestion(Long examId, String courseCode, String groupCode, String questionId, String fromBy) {
+        Question question = Model.of(quesRepo.findById(questionId));
+        if (question == null) {
+            LOG.warn("试题不存在!fromBy:{} questionId:{}", fromBy, questionId);
+            throw new StatusException("500", "试题不存在!");
+        }
+        quesService.formatQues(question);
+
+        // 封装成新的题单元集合
+        DefaultQuestionStructure defaultQuestionStructure = new DefaultQuestionStructure();
+        if (question.getHasAudio() == null || !question.getHasAudio()) {
+            defaultQuestionStructure.setHasAudios(false);
+        } else {
+            defaultQuestionStructure.setHasAudios(true);
+        }
+        defaultQuestionStructure.setVersion(CommonUtils.QUESTION_VERSION);
+
+        List<DefaultQuestionUnit> questionUnitList = new ArrayList<>();
+        if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+            defaultQuestionStructure.setBody(question.getQuesBody());
+
+            // 获取套题下面所有子题
+            List<Question> subQuesList = question.getSubQuestions();
+            if (CollectionUtils.isNotEmpty(subQuesList)) {
+                for (int i = 0; i < subQuesList.size(); i++) {
+                    Question subQuestion = subQuesList.get(i);
+                    DefaultQuestionUnit defaultQuestionUnit = buildQuestionUnit(subQuestion);
+                    questionUnitList.add(defaultQuestionUnit);
+                }
+            }
+        } else {
+            DefaultQuestionUnit defaultQuestionUnit = buildQuestionUnit(question);
+            questionUnitList.add(defaultQuestionUnit);
+        }
+
+        defaultQuestionStructure.setQuestionUnitList(questionUnitList);
+        DefaultQuestion defaultQuestion = new DefaultQuestion();
+        defaultQuestion.setId(questionId);
+        defaultQuestion.setMasterVersion(defaultQuestionStructure);
+
+        if (examId != null) {
+            appendAudioFlag(defaultQuestion, String.valueOf(examId), courseCode, groupCode, question);
+        }
+
+        // 获取试题信息带答案(敏感信息日志,用于排查调用者)
+        LOG.warn("$$$getQuestionWithAnswer fromBy:{} questionId:{} {}_{}_{} ", fromBy, questionId, examId, courseCode, groupCode);
+        return defaultQuestion;
+    }
+
+    /**
+     * 构建试题单元
+     *
+     * @param question
+     * @return
+     */
+    private DefaultQuestionUnit buildQuestionUnit(Question question) {
+        DefaultQuestionUnit defaultQuestionUnit = new DefaultQuestionUnit();
+        defaultQuestionUnit.setAnswerType(question.getAnswerType());// 作答类型
+        defaultQuestionUnit.setBody(question.getQuesBody());
+        defaultQuestionUnit.setQuestionType(getByOldType(question.getQuestionType()));
+
+        // 如果是单选或者多选,添加选项和答案转换
+        if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+                || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+            List<DefaultQuestionOption> defaultQuestionOptions = new ArrayList<>();
+            List<QuesOption> quesOptions = question.getQuesOptions();
+
+            if (quesOptions != null && quesOptions.size() > 0) {
+                for (int i = 0; i < quesOptions.size(); i++) {
+                    QuesOption quesOption = quesOptions.get(i);
+                    DefaultQuestionOption defaultQuestionOption = new DefaultQuestionOption();
+                    defaultQuestionOption.setBody(quesOption.getOptionBody());
+                    defaultQuestionOptions.add(defaultQuestionOption);
+                }
+            }
+            defaultQuestionUnit.setQuestionOptionList(defaultQuestionOptions);
+            defaultQuestionUnit.setRightAnswer(QuestionAnswerConvert.parseQuestionOptionAnswers(quesOptions));
+        } else {
+            defaultQuestionUnit.setRightAnswer(QuestionAnswerConvert.parseQuestionAnswers(question));
+        }
+
+        return defaultQuestionUnit;
+    }
+
+    /**
+     * html中添加音频标签
+     *
+     * @param defaultQuestion
+     * @param examId
+     * @param courseCode
+     * @param groupCode
+     * @param question
+     */
+    private void appendAudioFlag(DefaultQuestion defaultQuestion, String examId, String courseCode, String groupCode,
+                                 Question question) {
+        if (question.getHasAudio() == null || !question.getHasAudio()) {
+            return;
+        }
+
+        // 判断questionDto是否含有音频,如果有添加音频播放次数
+        Example<AudioTimeConfig> query = Example.of(new AudioTimeConfig(examId, courseCode, groupCode, question.getId()));
+        AudioTimeConfig audioTimeConfig = Model.of(audioTimeConfigRepo.findOne(query));
+
+        Integer playTime = null;
+        if (audioTimeConfig != null) {
+            playTime = audioTimeConfig.getPlayTime();
+        }
+
+        // 取到题干,给a标签添加url
+        DefaultQuestionStructure quesStructure = defaultQuestion.getMasterVersion();
+        if (StringUtils.isNotBlank(quesStructure.getBody())) {
+            // 套题
+            String quesBody = quesStructure.getBody();
+            quesStructure.setBody(buildBody(quesBody, playTime));
+        } else {
+            DefaultQuestionUnit defaultQuestionUnit = quesStructure.getQuestionUnitList().get(0);
+            String quesBody = defaultQuestionUnit.getBody();
+            defaultQuestionUnit.setBody(buildBody(quesBody, playTime));
+
+            if (defaultQuestionUnit.getQuestionType() == QuestionType.SINGLE_CHOICE
+                    || defaultQuestionUnit.getQuestionType() == QuestionType.MULTIPLE_CHOICE) {
+                List<DefaultQuestionOption> questionOptionList = defaultQuestionUnit.getQuestionOptionList();
+                if (questionOptionList != null && !questionOptionList.isEmpty()) {
+                    for (int i = 0; i < questionOptionList.size(); i++) {
+                        DefaultQuestionOption defaultQuestionOption = questionOptionList.get(i);
+                        defaultQuestionOption.setBody(buildBody(defaultQuestionOption.getBody(), playTime));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 给题目和选项添加url
+     *
+     * @param body
+     * @param playTime
+     * @return
+     */
+    public String buildBody(String body, Integer playTime) {
+        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 (playTime != null) {
+                            containAStr += " question-audio url=\"" + url + "\" playTime=\"" + playTime + "\""
+                                    + "></a>";
+                        } else {
+                            containAStr += " question-audio url=\"" + url + "\"" + "></a>";
+                        }
+                    }
+                }
+                resultBody += containAStr;
+            }
+            return resultBody;
+        } else {
+            return body;
+        }
+    }
+
+    /**
+     * 获取一段html中某个标签的值
+     *
+     * @param source
+     * @param element
+     * @param attr
+     * @return
+     */
+    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 "";
+    }
+
+    /**
+     * 检查所有题目是否为客观题
+     *
+     * @param paperDetailUnits
+     * @return
+     */
+    private boolean checkIsAllQbjectiveQuestion(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 DefaultPaper getBaseDefaultPaper(String paperId) {
+        Paper basePaper = Model.of(paperRepo.findById(paperId));
+        if (basePaper == null) {
+            LOG.error("该考试和课程下调卷规则中该类型试卷不存在,请检查调卷规则,调卷程序退出");
+            throw new StatusException("Q-020560", "该考试和课程下调卷规则中试卷不存在,请重新制定调卷规则");
+        }
+
+        // 构建试卷结构
+        DefaultPaper defaultPaper = this.buildDefaultByBasePaper(basePaper, null, null, null);
+        return defaultPaper;
+    }
+
+    @Override
+    public Map<String, DefaultQuestion> getDefaultQuestions(Set<String> questionIds) {
+        Map<String, DefaultQuestion> map = new HashMap<>();
+        List<Question> questions = quesRepo.findByIdIn(questionIds);
+        if (CollectionUtils.isEmpty(questions)) {
+            LOG.error("根据试题id的集合没有查询到试题结合");
+            throw new StatusException("Q-020572", "根据试题id的集合没有查询到试题结合");
+        }
+        for (Question question : questions) {
+            DefaultQuestion defaultQuestion = getDefaultQuestion(null, null, null,
+                    question.getId(), "default");
+
+            map.put(question.getId(), defaultQuestion);
+        }
+        return map;
+    }
+
+    @Override
+    public List<String> getAnswer(String questionId) {
+        List<String> list = new ArrayList<>();
+        Question question = Model.of(quesRepo.findById(questionId));
+
+        if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+            List<Question> subList = question.getSubQuestions();
+            if (subList != null && subList.size() > 0) {
+                String answer = "";
+                for (Question subQuestion : subList) {
+                    if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+                            || subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+                        String[] answers = QuestionAnswerConvert
+                                .parseQuestionOptionAnswers(subQuestion.getQuesOptions());
+                        answer = StringUtils.join(answers, ",");
+                    } else if (subQuestion.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+                        if (subQuestion.getQuesAnswer().endsWith("正确")) {
+                            answer = "true";
+                        } else {
+                            answer = "false";
+                        }
+                    } else {
+                        answer = subQuestion.getQuesAnswer();
+                    }
+                    list.add(answer);
+                }
+            }
+        } else {
+            String answer = "";
+            if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+                    || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+                String[] answers = QuestionAnswerConvert.parseQuestionOptionAnswers(question.getQuesOptions());
+                answer = StringUtils.join(answers, ",");
+            } else if (question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+                if (question.getQuesAnswer().endsWith("正确")) {
+                    answer = "true";
+                } else {
+                    answer = "false";
+                }
+            } else {
+                answer = question.getQuesAnswer();
+            }
+            list.add(answer);
+        }
+
+        return list;
+    }
+
+    @Override
+    public String getQuestionStructure(String paperId) throws Exception {
+        String json = paperService.findQuestionStructure(paperId);
+        return json;
+    }
 
 }

+ 185 - 213
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.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.collections4.CollectionUtils;
-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.transaction.annotation.Transactional;
-import org.springframework.util.Assert;
-
 import cn.com.qmth.examcloud.api.commons.enums.CallType;
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
 import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
@@ -48,39 +14,15 @@ 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.RandomPaperRepo;
-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.entity.RandomPaper;
+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.RandomPaperService;
-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.core.questions.service.bean.extract.SaveRandomPaperExtractConfigForToolDomain;
 import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.RandomPaperListVo;
 import cn.com.qmth.examcloud.core.questions.service.cache.ExtractConfigCache;
@@ -96,6 +38,32 @@ 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.collections4.CollectionUtils;
+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.transaction.annotation.Transactional;
+import org.springframework.util.Assert;
+
+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
@@ -152,11 +120,9 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
 
     @Autowired
     private RedisClient redisClient;
+
     @Autowired
     private ExtractConfigCache extractConfigCache;
-    /*
-     * @Autowired private PaperDetailUnitNativeRepo detailUnitNativeRepo;
-     */
 
     @Autowired
     private ExamCloudService examCloudService;
@@ -166,29 +132,32 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
 
     @Autowired
     private ExamRecordDataCloudService studentExamRecordCloudService;
-    
+
     @Autowired
     private RandomPaperService randomPaperService;
-    
+
     @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);
-        if(tempConfig!=null&&tempConfig.getCallType()==null) {
-        	tempConfig.setCallType(CallType.WHOLE_SET);
+        if (StringUtils.isNotBlank(condition.getOrgId())) {
+            query.addCriteria(Criteria.where("orgId").is(condition.getOrgId()));
         }
-        return tempConfig;
+
+        ExtractConfig extractConfig = this.mongoTemplate.findOne(query, ExtractConfig.class);
+        if (extractConfig != null && extractConfig.getCallType() == null) {
+            extractConfig.setCallType(CallType.WHOLE_SET);
+        }
+        return extractConfig;
     }
 
     @Override
@@ -212,7 +181,7 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
 
     @Override
     public List<ExamPaper> saveExtractConfig(ExtractConfig extractConfig, User user) {
-    	Course course = courseService.getCourse(Long.valueOf(extractConfig.getOrgId()), extractConfig.getCourseCode());
+        Course course = courseService.getCourse(Long.valueOf(extractConfig.getOrgId()), extractConfig.getCourseCode());
         extractConfig.setCourse(course);
         extractConfig.setCourseName(course.getName());
         List<ExamPaper> examPapers = extractConfig.getExamPaperList();
@@ -220,9 +189,9 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
             ExamPaper examPaper = examPapers.get(i);
             Paper paper = examPaper.getPaper();
             paper = Model.of(paperRepo.findById(paper.getId()));
-            if(!extractConfig.getCourse().getOrgId().equals(paper.getCourse().getOrgId())
-            		||!extractConfig.getCourse().getCode().equals(paper.getCourse().getCode())) {
-            	throw new StatusException("试卷的课程和当前课程不匹配");
+            if (!extractConfig.getCourse().getOrgId().equals(paper.getCourse().getOrgId())
+                    || !extractConfig.getCourse().getCode().equals(paper.getCourse().getCode())) {
+                throw new StatusException("试卷的课程和当前课程不匹配");
             }
             examPaper.setPaper(paper);
             if (ExamType.OFFLINE.name().equals(extractConfig.getExamType())) {
@@ -237,16 +206,16 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
         disposeNowPaper(extractConfig.getId());
         return examPapers;
     }
-    
+
     @Transactional
     @Override
     public void saveExtractConfigForRandomPaper(ExtractConfig extractConfig) {
-    	GetExamReq examReq=new GetExamReq();
-    	examReq.setId(extractConfig.getExamId());
-    	GetExamResp examRes=examCloudService.getExam(examReq);
-    	if(ExamType.OFFLINE.name().equals(examRes.getExamBean().getExamType())) {
-    		throw new StatusException("离线考试不能随机抽题");
-    	}
+        GetExamReq examReq = new GetExamReq();
+        examReq.setId(extractConfig.getExamId());
+        GetExamResp examRes = examCloudService.getExam(examReq);
+        if (ExamType.OFFLINE.name().equals(examRes.getExamBean().getExamType())) {
+            throw new StatusException("离线考试不能随机抽题");
+        }
         Course course = courseService.getCourse(Long.valueOf(extractConfig.getOrgId()), extractConfig.getCourseCode());
         extractConfig.setCourse(course);
         extractConfig.setCourseName(course.getName());
@@ -256,36 +225,36 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
         query.setCourseId(Long.parseLong(course.getId()));
         Example<RandomPaper> queryExample = Example.of(query);
         boolean exists = randomPaperRepo.exists(queryExample);
-        if(!exists){
+        if (!exists) {
             LOG.warn("随机抽题模板选择错误,与当前课程不匹配! courseId:{} randomPaperId:{}", course.getId(), extractConfig.getRandomPaperId());
             throw new StatusException("500", "随机抽题模板选择错误,与当前课程不匹配!");
         }
 
-		// 新增,id为空
-		if (StringUtils.isBlank(extractConfig.getId())) {
-			// 先查询
-			ExtractConfig tempConfig = this.findConfig(extractConfig);
-			if (tempConfig != null) {
-				tempConfig.setRandomPaperId(extractConfig.getRandomPaperId());
-				tempConfig.setPlayTime(extractConfig.getPlayTime());
-				tempConfig.setCourseName(course.getName());
-				tempConfig.setCourse(course);
-				tempConfig.setCallType(extractConfig.getCallType());
-				extractConfig=tempConfig;
-			}
-		}else {
-			ExtractConfig tempConfig = Model.of(extractConfigRepo.findById(extractConfig.getId()));
-			tempConfig.setRandomPaperId(extractConfig.getRandomPaperId());
-			tempConfig.setPlayTime(extractConfig.getPlayTime());
-			tempConfig.setCourseName(course.getName());
-			tempConfig.setCourse(course);
-			tempConfig.setCallType(extractConfig.getCallType());
-			extractConfig=tempConfig;
-		}
-		if (StringUtils.isNotBlank(extractConfig.getId())) {
+        // 新增,id为空
+        if (StringUtils.isBlank(extractConfig.getId())) {
+            // 先查询
+            ExtractConfig tempConfig = this.findConfig(extractConfig);
+            if (tempConfig != null) {
+                tempConfig.setRandomPaperId(extractConfig.getRandomPaperId());
+                tempConfig.setPlayTime(extractConfig.getPlayTime());
+                tempConfig.setCourseName(course.getName());
+                tempConfig.setCourse(course);
+                tempConfig.setCallType(extractConfig.getCallType());
+                extractConfig = tempConfig;
+            }
+        } else {
+            ExtractConfig tempConfig = Model.of(extractConfigRepo.findById(extractConfig.getId()));
+            tempConfig.setRandomPaperId(extractConfig.getRandomPaperId());
+            tempConfig.setPlayTime(extractConfig.getPlayTime());
+            tempConfig.setCourseName(course.getName());
+            tempConfig.setCourse(course);
+            tempConfig.setCallType(extractConfig.getCallType());
+            extractConfig = tempConfig;
+        }
+        if (StringUtils.isNotBlank(extractConfig.getId())) {
             disposeOldPaper(extractConfig.getId());
         }
-		extractConfig.setExamPaperList(null);
+        extractConfig.setExamPaperList(null);
         extractConfigRepo.save(extractConfig);
     }
 
@@ -296,8 +265,8 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
      */
     private void disposeOldPaper(String extractConfigId) {
         ExtractConfig ec = Model.of(extractConfigRepo.findById(extractConfigId));
-        if(CollectionUtils.isEmpty(ec.getExamPaperList())) {
-        	return;
+        if (CollectionUtils.isEmpty(ec.getExamPaperList())) {
+            return;
         }
         for (ExamPaper paper : ec.getExamPaperList()) {
             if (inExam(paper.getPaper().getId())) {//如果有考试记录
@@ -1413,28 +1382,28 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
 
         return extractConfigList;
     }
-    
+
     @Transactional
     @Override
     public void saveExtractConfigForRandomPaperByOuter(SaveRandomPaperExtractConfigReq param) {
-    	if(param.getRootOrgId()==null) {
-    		throw new StatusException("rootOrgId不能为空");
-    	}
-    	GetExamReq examReq=new GetExamReq();
-    	examReq.setRootOrgId(param.getRootOrgId());
-    	examReq.setName(param.getExamName());
-    	examReq.setCode(param.getExamCode());
-    	GetExamResp examRes=examCloudService.getExam(examReq);
-    	if(ExamType.OFFLINE.name().equals(examRes.getExamBean().getExamType())) {
-    		throw new StatusException("不能对离线考试创建调卷规则");
-    	}
-
-    	// 从考务查询改考试下的所有课程
+        if (param.getRootOrgId() == null) {
+            throw new StatusException("rootOrgId不能为空");
+        }
+        GetExamReq examReq = new GetExamReq();
+        examReq.setRootOrgId(param.getRootOrgId());
+        examReq.setName(param.getExamName());
+        examReq.setCode(param.getExamCode());
+        GetExamResp examRes = examCloudService.getExam(examReq);
+        if (ExamType.OFFLINE.name().equals(examRes.getExamBean().getExamType())) {
+            throw new StatusException("不能对离线考试创建调卷规则");
+        }
+
+        // 从考务查询改考试下的所有课程
         List<ExamCourseRelationBean> courseInfoList = new ArrayList<>();
         GetExamCourseListReq req = new GetExamCourseListReq();
         req.setExamId(examRes.getExamBean().getId());
         Long start = 1L;
-        for(;;) {
+        for (; ; ) {
             req.setStart(start);
             GetExamCourseListResp resp = examCloudService.getExamCourseList(req);
             courseInfoList.addAll(resp.getRelationList());
@@ -1445,57 +1414,59 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
             }
         }
         if (CollectionUtils.isEmpty(courseInfoList)) {
-            LOG.warn("==>当前考试下课程列表为空!rootOrgId:{} examId:{}",examReq.getRootOrgId(),req.getExamId());
-        	throw new StatusException("请先同步考生再创建调卷规则");
+            LOG.warn("==>当前考试下课程列表为空!rootOrgId:{} examId:{}", examReq.getRootOrgId(), req.getExamId());
+            throw new StatusException("请先同步考生再创建调卷规则");
         }
 
-        for(ExamCourseRelationBean course:courseInfoList) {
-        	createByCourse(param.getRootOrgId(), examRes.getExamBean(), course);
+        for (ExamCourseRelationBean course : courseInfoList) {
+            createByCourse(param.getRootOrgId(), examRes.getExamBean(), course);
         }
     }
-    
-    private void createByCourse(Long rootOrgId,ExamBean examBean,ExamCourseRelationBean course) {
-    	List<RandomPaperListVo> rps=randomPaperService.getList(rootOrgId,course.getCourseId());
-    	if(CollectionUtils.isEmpty(rps)) {
-            LOG.warn("==>没有启用的随机抽题模板!rootOrgId:{} examId:{} courseCode:{}",rootOrgId,examBean.getId(),course.getCourseCode());
-    		throw new StatusException(course.getCourseName()+",没有启用的随机抽题模板");
-    	}
-    	if(rps.size()>1) {
-            LOG.warn("==>存在多个启用的随机抽题模板!rootOrgId:{} examId:{} courseCode:{}",rootOrgId,examBean.getId(),course.getCourseCode());
-    		throw new StatusException("存在多个启用的随机抽题模板");
-    	}
-
-    	ExtractConfig extractConfig=findConfig(examBean.getId(), course.getCourseCode());
-    	if(extractConfig==null) {
-	    	extractConfig=new ExtractConfig();
-	    	extractConfig.setExamId(examBean.getId());
-	    	extractConfig.setOrgId(rootOrgId.toString());
-	    	extractConfig.setExamName(examBean.getName());
-	    	extractConfig.setExamType(examBean.getExamType());
-	    	extractConfig.setCourseCode(course.getCourseCode());
-	        extractConfig.setCourse(of(course,rootOrgId));
-	        extractConfig.setCourseName(course.getCourseName());
-    	}
-    	extractConfig.setRandomPaperId(rps.get(0).getId());
-    	extractConfig.setCallType(CallType.RANDOM_PAPER);
-		extractConfig.setPlayTime(1);
+
+    private void createByCourse(Long rootOrgId, ExamBean examBean, ExamCourseRelationBean course) {
+        List<RandomPaperListVo> rps = randomPaperService.getList(rootOrgId, course.getCourseId());
+        if (CollectionUtils.isEmpty(rps)) {
+            LOG.warn("==>没有启用的随机抽题模板!rootOrgId:{} examId:{} courseCode:{}", rootOrgId, examBean.getId(), course.getCourseCode());
+            throw new StatusException(course.getCourseName() + ",没有启用的随机抽题模板");
+        }
+        if (rps.size() > 1) {
+            LOG.warn("==>存在多个启用的随机抽题模板!rootOrgId:{} examId:{} courseCode:{}", rootOrgId, examBean.getId(), course.getCourseCode());
+            throw new StatusException("存在多个启用的随机抽题模板");
+        }
+
+        ExtractConfig extractConfig = findConfig(examBean.getId(), course.getCourseCode());
+        if (extractConfig == null) {
+            extractConfig = new ExtractConfig();
+            extractConfig.setExamId(examBean.getId());
+            extractConfig.setOrgId(rootOrgId.toString());
+            extractConfig.setExamName(examBean.getName());
+            extractConfig.setExamType(examBean.getExamType());
+            extractConfig.setCourseCode(course.getCourseCode());
+            extractConfig.setCourse(of(course, rootOrgId));
+            extractConfig.setCourseName(course.getCourseName());
+        }
+        extractConfig.setRandomPaperId(rps.get(0).getId());
+        extractConfig.setCallType(CallType.RANDOM_PAPER);
+        extractConfig.setPlayTime(1);
         extractConfigRepo.save(extractConfig);
-        LOG.warn("==>创建随机抽卷调卷规则成功!rootOrgId:{} examId:{} courseCode:{}",rootOrgId,examBean.getId(),course.getCourseCode());
+        LOG.warn("==>创建随机抽卷调卷规则成功!rootOrgId:{} examId:{} courseCode:{}", rootOrgId, examBean.getId(), course.getCourseCode());
 
         // 清除缓存
- 		extractConfigCache.remove(extractConfig.getExamId(), extractConfig.getCourseCode());
+        extractConfigCache.remove(extractConfig.getExamId(), extractConfig.getCourseCode());
     }
-    private Course of(ExamCourseRelationBean course,Long rootOrgId) {
-    	Course c=new Course();
-    	c.setCode(course.getCourseCode());
-    	c.setId(course.getCourseId().toString());
-    	c.setEnable(course.getCourseEnable()==null?"false":course.getCourseEnable().toString());
-    	c.setName(course.getCourseName());
-    	c.setLevel(course.getCourseLevel());
-    	c.setOrgId(rootOrgId.toString());
-    	return c;
+
+    private Course of(ExamCourseRelationBean course, Long rootOrgId) {
+        Course c = new Course();
+        c.setCode(course.getCourseCode());
+        c.setId(course.getCourseId().toString());
+        c.setEnable(course.getCourseEnable() == null ? "false" : course.getCourseEnable().toString());
+        c.setName(course.getCourseName());
+        c.setLevel(course.getCourseLevel());
+        c.setOrgId(rootOrgId.toString());
+        return c;
     }
-    private ExtractConfig findConfig(Long examId,String courseCode) {
+
+    private ExtractConfig findConfig(Long examId, String courseCode) {
         if (examId == null) {
             return null;
         }
@@ -1510,56 +1481,57 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
     }
 
     @Transactional
-	@Override
-	public void saveExtractConfigForRandomPaper(SaveRandomPaperExtractConfigForToolDomain req) {
-    	if(req.getExamId()==null) {
-    		throw new StatusException("ExamId不能为空");
-    	}
-    	GetExamReq examReq=new GetExamReq();
-    	examReq.setId(req.getExamId());
-    	GetExamResp examRes=examCloudService.getExam(examReq);
-    	if(ExamType.OFFLINE.name().equals(examRes.getExamBean().getExamType())) {
-    		throw new StatusException("不能对离线考试创建调卷规则");
-    	}
-    	Long rootOrgId=examRes.getExamBean().getRootOrgId();
-    	Course course = courseService.getCourse(rootOrgId, req.getCourseCode());
-    	GetExamCourseReq settingReq = new GetExamCourseReq();
+    @Override
+    public void saveExtractConfigForRandomPaper(SaveRandomPaperExtractConfigForToolDomain req) {
+        if (req.getExamId() == null) {
+            throw new StatusException("ExamId不能为空");
+        }
+        GetExamReq examReq = new GetExamReq();
+        examReq.setId(req.getExamId());
+        GetExamResp examRes = examCloudService.getExam(examReq);
+        if (ExamType.OFFLINE.name().equals(examRes.getExamBean().getExamType())) {
+            throw new StatusException("不能对离线考试创建调卷规则");
+        }
+        Long rootOrgId = examRes.getExamBean().getRootOrgId();
+        Course course = courseService.getCourse(rootOrgId, req.getCourseCode());
+        GetExamCourseReq settingReq = new GetExamCourseReq();
         settingReq.setExamId(examRes.getExamBean().getId());
         settingReq.setCourseId(Long.valueOf(course.getId()));
         GetExamCourseResp settingResp = examCloudService.getExamCourseSetting(settingReq);
 
-        if(settingResp.getBean()==null) {
-            LOG.warn("-->当前考试课程不存在!rootOrgId:{} examId:{} courseCode:{}",rootOrgId,req.getExamId(),req.getCourseCode());
-        	throw new StatusException("请先同步考生后再创建相应课程的调卷规则");
-        }
-    	List<RandomPaperListVo> rps=randomPaperService.getList(rootOrgId, Long.valueOf(course.getId()));
-    	if(CollectionUtils.isEmpty(rps)) {
-            LOG.warn("-->没有启用的随机抽题模板!rootOrgId:{} examId:{} courseCode:{}",rootOrgId,req.getExamId(),req.getCourseCode());
-    		throw new StatusException("没有启用的随机抽题模板");
-    	}
-    	if(rps.size()>1) {
-            LOG.warn("-->存在多个启用的随机抽题模板!rootOrgId:{} examId:{} courseCode:{}",rootOrgId,req.getExamId(),req.getCourseCode());
-    		throw new StatusException("存在多个启用的随机抽题模板");
-    	}
-
-    	ExtractConfig extractConfig=findConfig(examRes.getExamBean().getId(), req.getCourseCode());
-    	if(extractConfig==null) {
-	    	extractConfig=new ExtractConfig();
-	    	extractConfig.setExamId(examRes.getExamBean().getId());
-	    	extractConfig.setOrgId(rootOrgId.toString());
-	    	extractConfig.setExamName(examRes.getExamBean().getName());
-	    	extractConfig.setExamType(examRes.getExamBean().getExamType());
-	    	extractConfig.setCourseCode(course.getCode());
-	        extractConfig.setCourse(course);
-	        extractConfig.setCourseName(course.getName());
-    	}
-    	extractConfig.setCallType(CallType.RANDOM_PAPER);
-    	extractConfig.setRandomPaperId(rps.get(0).getId());
-		extractConfig.setPlayTime(1);
+        if (settingResp.getBean() == null) {
+            LOG.warn("-->当前考试课程不存在!rootOrgId:{} examId:{} courseCode:{}", rootOrgId, req.getExamId(), req.getCourseCode());
+            throw new StatusException("请先同步考生后再创建相应课程的调卷规则");
+        }
+        List<RandomPaperListVo> rps = randomPaperService.getList(rootOrgId, Long.valueOf(course.getId()));
+        if (CollectionUtils.isEmpty(rps)) {
+            LOG.warn("-->没有启用的随机抽题模板!rootOrgId:{} examId:{} courseCode:{}", rootOrgId, req.getExamId(), req.getCourseCode());
+            throw new StatusException("没有启用的随机抽题模板");
+        }
+        if (rps.size() > 1) {
+            LOG.warn("-->存在多个启用的随机抽题模板!rootOrgId:{} examId:{} courseCode:{}", rootOrgId, req.getExamId(), req.getCourseCode());
+            throw new StatusException("存在多个启用的随机抽题模板");
+        }
+
+        ExtractConfig extractConfig = findConfig(examRes.getExamBean().getId(), req.getCourseCode());
+        if (extractConfig == null) {
+            extractConfig = new ExtractConfig();
+            extractConfig.setExamId(examRes.getExamBean().getId());
+            extractConfig.setOrgId(rootOrgId.toString());
+            extractConfig.setExamName(examRes.getExamBean().getName());
+            extractConfig.setExamType(examRes.getExamBean().getExamType());
+            extractConfig.setCourseCode(course.getCode());
+            extractConfig.setCourse(course);
+            extractConfig.setCourseName(course.getName());
+        }
+        extractConfig.setCallType(CallType.RANDOM_PAPER);
+        extractConfig.setRandomPaperId(rps.get(0).getId());
+        extractConfig.setPlayTime(1);
         extractConfigRepo.save(extractConfig);
-        LOG.warn("-->创建随机抽卷调卷规则成功!rootOrgId:{} examId:{} courseCode:{}",rootOrgId,req.getExamId(),req.getCourseCode());
+        LOG.warn("-->创建随机抽卷调卷规则成功!rootOrgId:{} examId:{} courseCode:{}", rootOrgId, req.getExamId(), req.getCourseCode());
 
         // 清除缓存
- 		extractConfigCache.remove(extractConfig.getExamId(), extractConfig.getCourseCode());
-	}
+        extractConfigCache.remove(extractConfig.getExamId(), extractConfig.getCourseCode());
+    }
+
 }