deason 1 năm trước cách đây
mục cha
commit
50a88686fc

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

@@ -100,59 +100,50 @@ public class DefaultQuesionController extends ControllerSupport {
         return new ResponseEntity<>(defaultQuestions, HttpStatus.OK);
     }
 
-    @ApiOperation(value = "查询试题")
-    @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");
-        }
+    @ApiOperation(value = "查询试题详细信息(管理端)")
+    @PostMapping("/question/detail")
+    public ResponseEntity<Object> findForWebAdmin(@RequestBody GetQuestionReq questionReq) {
+        DefaultQuestion defaultQuestion = this.getDefaultQuestion(questionReq, "webAdmin");
+        return new ResponseEntity<>(defaultQuestion, HttpStatus.OK);
+    }
 
-        DefaultQuestion defaultQuestion = extractConfigExamService.getDefaultQuestion(examId, courseCode, groupCode,
-                questionId, "webClient");
+    @ApiOperation(value = "查询试题详细信息(Web版客户端)")
+    @PostMapping("/question")
+    public ResponseEntity<Object> findForWebClient(@RequestBody GetQuestionReq questionReq) {
+        DefaultQuestion defaultQuestion = this.getDefaultQuestion(questionReq, "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> findForClient(@RequestBody GetQuestionReq questionReq) {
+        DefaultQuestion defaultQuestion = this.getDefaultQuestion(questionReq, "pcClient");
 
-        if (null == examId) {
+        // 将题干、选项等 HTML结构转换为“富文本”JSON结构
+        QuestionBodyHandler.convertRichText(defaultQuestion);
+
+        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");
-
-        // 将题干、选项等 HTML结构转换为“富文本”JSON结构
-        QuestionBodyHandler.convertRichText(defaultQuestion);
-
-        return new ResponseEntity<>(defaultQuestion, HttpStatus.OK);
+        return extractConfigExamService.getDefaultQuestion(req.getExamId(), req.getCourseCode(), req.getGroupCode(),
+                req.getQuestionId(), fromBy);
     }
 
 }

+ 659 - 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,648 @@ 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) {
+            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() == 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;
+    }
 
 }