Przeglądaj źródła

接口建调卷规则

xiatian 1 rok temu
rodzic
commit
e4d22c09a2

+ 13 - 0
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/provider/ExtractConfigCloudServiceProvider.java

@@ -5,6 +5,7 @@ import cn.com.qmth.examcloud.core.questions.api.ExtractConfigCloudService;
 import cn.com.qmth.examcloud.core.questions.api.request.*;
 import cn.com.qmth.examcloud.core.questions.api.response.*;
 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.RandomPaperService;
 import cn.com.qmth.examcloud.core.questions.service.bean.extract.ExtractConfigPaper;
 import cn.com.qmth.examcloud.question.commons.core.paper.DefaultPaper;
@@ -42,6 +43,8 @@ public class ExtractConfigCloudServiceProvider implements ExtractConfigCloudServ
 
     @Autowired
     private ExtractConfigProviderService extractConfigExamService;
+    @Autowired
+    private ExtractConfigService extractConfigService;
     
     @Autowired
     private RandomPaperService randomPaperService;
@@ -190,4 +193,14 @@ public class ExtractConfigCloudServiceProvider implements ExtractConfigCloudServ
 		return res;
 	}
 
+    @ApiOperation("随机抽卷调卷规则创建")
+    @PostMapping("randompaper/save")
+    @Override
+	public SaveRandomPaperExtractConfigResp saveRandomPaperExtractConfig(@RequestBody SaveRandomPaperExtractConfigReq req) {
+    	String id=extractConfigService.saveExtractConfigForRandomPaperByOuter(req);
+    	SaveRandomPaperExtractConfigResp res=new SaveRandomPaperExtractConfigResp();
+    	res.setExtractConfigId(id);
+		return res;
+	}
+
 }

+ 3 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ExtractConfigService.java

@@ -2,6 +2,7 @@ package cn.com.qmth.examcloud.core.questions.service;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
 import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
+import cn.com.qmth.examcloud.core.questions.api.request.SaveRandomPaperExtractConfigReq;
 import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
 import cn.com.qmth.examcloud.core.questions.base.question.PaperDto;
 import cn.com.qmth.examcloud.core.questions.base.question.QuestionDto;
@@ -151,4 +152,6 @@ public interface ExtractConfigService {
 
 	void saveExtractConfigForRandomPaper(ExtractConfig extractConfig);
 
+	String saveExtractConfigForRandomPaperByOuter(SaveRandomPaperExtractConfigReq savereq);
+
 }

+ 4 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/RandomPaperService.java

@@ -1,5 +1,7 @@
 package cn.com.qmth.examcloud.core.questions.service;
 
+import java.util.List;
+
 import org.springframework.data.domain.Page;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
@@ -30,5 +32,7 @@ public interface RandomPaperService {
 
 	boolean existPaper(Long courseId,String id);
 
+	List<RandomPaperListVo> getList(Long rootOrgId, Long courseId);
+
 
 }

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

@@ -1,5 +1,40 @@
 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.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 org.springframework.util.CollectionUtils;
+
+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;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
@@ -7,55 +42,58 @@ import cn.com.qmth.examcloud.core.oe.admin.api.ExamRecordCloudService;
 import cn.com.qmth.examcloud.core.oe.admin.api.request.CheckPaperInExamReq;
 import cn.com.qmth.examcloud.core.oe.admin.api.response.CheckPaperInExamResp;
 import cn.com.qmth.examcloud.core.oe.student.api.ExamRecordDataCloudService;
+import cn.com.qmth.examcloud.core.questions.api.request.SaveRandomPaperExtractConfigReq;
 import cn.com.qmth.examcloud.core.questions.base.BeanCopierUtil;
 import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
 import cn.com.qmth.examcloud.core.questions.base.Model;
 import cn.com.qmth.examcloud.core.questions.base.em.enums.ExamType;
 import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
-import cn.com.qmth.examcloud.core.questions.base.question.*;
+import cn.com.qmth.examcloud.core.questions.base.question.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.enums.QuesStructType;
-import cn.com.qmth.examcloud.core.questions.dao.*;
-import cn.com.qmth.examcloud.core.questions.dao.entity.*;
+import cn.com.qmth.examcloud.core.questions.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.service.ExtractConfigService;
 import cn.com.qmth.examcloud.core.questions.service.QuesService;
 import cn.com.qmth.examcloud.core.questions.service.QuestionAudioService;
-import cn.com.qmth.examcloud.core.questions.service.bean.*;
+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.randompaper.RandomPaperListVo;
 import cn.com.qmth.examcloud.examwork.api.ExamCloudService;
 import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
 import cn.com.qmth.examcloud.examwork.api.bean.ExamCourseRelationBean;
 import cn.com.qmth.examcloud.examwork.api.request.GetExamCourseListReq;
+import cn.com.qmth.examcloud.examwork.api.request.GetExamCourseReq;
 import cn.com.qmth.examcloud.examwork.api.request.GetExamReq;
 import cn.com.qmth.examcloud.examwork.api.response.GetExamCourseListResp;
+import cn.com.qmth.examcloud.examwork.api.response.GetExamCourseResp;
 import cn.com.qmth.examcloud.examwork.api.response.GetExamResp;
 import cn.com.qmth.examcloud.support.CacheConstants;
 import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
 import cn.com.qmth.examcloud.web.redis.RedisClient;
-import org.apache.commons.lang3.StringUtils;
-import org.bson.types.ObjectId;
-import org.nlpcn.commons.lang.util.StringUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeanUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Example;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageImpl;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.mongodb.core.MongoTemplate;
-import org.springframework.data.mongodb.core.query.Criteria;
-import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.data.mongodb.core.query.Update;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.Assert;
-import org.springframework.util.CollectionUtils;
-
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
-import static cn.com.qmth.examcloud.core.questions.service.cache.Constants.DEFAULT_TIME_OUT;
 
 /**
  * @author chenken
@@ -125,7 +163,10 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
 
     @Autowired
     private ExamRecordDataCloudService studentExamRecordCloudService;
-
+    
+    @Autowired
+    private RandomPaperService randomPaperService;
+    
     @Override
     public ExtractConfig findConfig(ExtractConfig condition) {
         if (condition.getExamId() == null) {
@@ -1351,5 +1392,65 @@ public class ExtractConfigServiceImpl implements ExtractConfigService {
 
         return extractConfigList;
     }
-
+    
+    @Transactional
+    @Override
+    public String saveExtractConfigForRandomPaperByOuter(SaveRandomPaperExtractConfigReq req) {
+    	if(req.getRootOrgId()==null) {
+    		throw new StatusException("rootOrgId不能为空");
+    	}
+    	GetExamReq examReq=new GetExamReq();
+    	examReq.setRootOrgId(req.getRootOrgId());
+    	examReq.setName(req.getExamName());
+    	examReq.setCode(req.getExamCode());
+    	GetExamResp examRes=examCloudService.getExam(examReq);
+    	if(!CallType.RANDOM_PAPER.equals(examRes.getExamBean().getCallType())) {
+    		throw new StatusException("只能对随机抽题的考试创建调卷规则");
+    	}
+    	Course course = courseService.getCourse(req.getRootOrgId(), 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) {
+        	throw new StatusException("请先同步考生后再创建相应课程的调卷规则");
+        }
+    	List<RandomPaperListVo> rps=randomPaperService.getList(req.getRootOrgId(), Long.valueOf(course.getId()));
+    	if(CollectionUtils.isEmpty(rps)) {
+    		throw new StatusException("没有启用的随机抽题模板");
+    	}
+    	if(rps.size()>1) {
+    		throw new StatusException("存在多个启用的随机抽题模板");
+    	}
+    	ExtractConfig extractConfig=findConfig(examRes.getExamBean().getId(), req.getCourseCode());
+    	if(extractConfig!=null) {
+    		return extractConfig.getId();
+    	}
+    	extractConfig=new ExtractConfig();
+    	extractConfig.setExamId(examRes.getExamBean().getId());
+    	extractConfig.setCallType(CallType.RANDOM_PAPER);
+    	extractConfig.setOrgId(req.getRootOrgId().toString());
+    	extractConfig.setExamName(examRes.getExamBean().getName());
+    	extractConfig.setExamType(examRes.getExamBean().getExamType());
+    	extractConfig.setCourseCode(course.getCode());
+        extractConfig.setCourse(course);
+        extractConfig.setCourseName(course.getName());
+		extractConfig.setRandomPaperId(rps.get(0).getId());
+		extractConfig.setPlayTime(1);
+        extractConfigRepo.save(extractConfig);
+        return extractConfig.getId();
+    }
+    private ExtractConfig findConfig(Long examId,String courseCode) {
+        if (examId == null) {
+            return null;
+        }
+        if (StringUtils.isBlank(courseCode)) {
+            return null;
+        }
+        Query query = new Query();
+        query.addCriteria(Criteria.where("examId").is(examId));
+        query.addCriteria(Criteria.where("courseCode").is(courseCode));
+        ExtractConfig tempConfig = this.mongoTemplate.findOne(query, ExtractConfig.class);
+        return tempConfig;
+    }
 }

+ 1159 - 1140
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/RandomPaperServiceImpl.java

@@ -65,1144 +65,1163 @@ import java.util.stream.Collectors;
 @Service
 public class RandomPaperServiceImpl implements RandomPaperService {
 
-    private static final Logger log = LoggerFactory.getLogger(RandomPaperServiceImpl.class);
-
-    private static Cache<String, RandomPaperCache> localRandomPaperCache = CacheBuilder.newBuilder().expireAfterWrite(3, TimeUnit.MINUTES).build();
-
-    private static int cacheTimeOut = 2 * 60 * 60;
-
-    @Autowired
-    private MongoTemplate mongoTemplate;
-
-    @Resource(name = "mongoTemplate2")
-    private MongoTemplate mongoTemplate2;
-
-    @Autowired
-    private PaperStructService paperStructService;
-
-    @Autowired
-    private PaperStructRepo paperStructRepo;
-
-    @Autowired
-    private RedisClient redisClient;
-
-    @Autowired
-    private PropertyRepo propertyRepo;
-
-    @Autowired
-    private RandomPaperRepo randomPaperRepo;
-
-    @Autowired
-    private RandomPaperQuestionRepo randomPaperQuestionRepo;
-
-    @Autowired
-    private UserCloudService userCloudService;
-
-    @Override
-    public Page<RandomPaperListVo> getPage(RandomPaperQuery req) {
-        if (req.getUd().assertEmptyQueryResult()) {
-            return Page.empty();
-        }
-        Query query;
-        List<Criteria> cs = new ArrayList<>();
-        cs.add(Criteria.where("rootOrgId").is(req.getRootOrgId()));
-
-        if (req.getUd().assertNeedQueryRefIds()) {
-            cs.add(Criteria.where("courseId").in(req.getUd().getRefIds()));
-        }
-
-        if (req.getEnable() != null) {
-            cs.add(Criteria.where("enable").is(req.getEnable()));
-        }
-
-        if (req.getCourseId() != null) {
-            cs.add(Criteria.where("courseId").is(req.getCourseId()));
-        }
-        if (StringUtils.isNotBlank(req.getName())) {
-            String paperName = CommonUtils.escapeExprSpecialWord(req.getName());
-            cs.add(Criteria.where("name").regex(".*?\\.*" + paperName + ".*"));
-        }
-        Criteria and = new Criteria();
-        Criteria[] cas = new Criteria[cs.size()];
-        if (StringUtils.isNotBlank(req.getId())) {
-            and.andOperator(cs.toArray(cas));
-            query = Query.query(new Criteria().orOperator(and, Criteria.where("id").is(req.getId())));
-        } else {
-            and.andOperator(cs.toArray(cas));
-            query = Query.query(and);
-        }
-
-        long total = this.mongoTemplate.count(query, RandomPaper.class);
-        if (total == 0) {
-            return Page.empty();
-        }
-
-        PageRequest pageable = PageRequest.of(req.getPageNumber() - 1, req.getPageSize());
-        query.with(Sort.by(Sort.Order.desc("creationDate")));
-        query.skip(pageable.getOffset());
-        query.limit(pageable.getPageSize());
-
-        List<RandomPaperListVo> paperList = this.mongoTemplate.find(query, RandomPaperListVo.class, "randomPaper");
-        if (CollectionUtils.isEmpty(paperList)) {
-            return Page.empty();
-        }
-
-        for (RandomPaperListVo vo : paperList) {
-            CourseCacheBean course = CacheHelper.getCourse(vo.getCourseId());
-            vo.setCourseCode(course.getCode());
-            vo.setCourseName(course.getName());
-            vo.setPaperStructTypeStr(vo.getPaperStructType().getName());
-            PaperStruct paperStruct = Model.of(paperStructRepo.findById(vo.getPaperStructId()));
-            vo.setPaperStructName(paperStruct.getName());
-            vo.setEnableStr(vo.getEnable() ? "启用" : "禁用");
-        }
-        fillUserName(paperList, req.getRootOrgId());
-
-        return new PageImpl<>(paperList, pageable, total);
-    }
-
-    private void fillUserName(List<RandomPaperListVo> dtos, Long rootOrgId) {
-        if (dtos != null && dtos.size() > 0) {
-            List<Long> ids = dtos.stream().map(dto -> dto.getUpdateBy()).distinct().collect(Collectors.toList());
-            List<UserBean> userList = new ArrayList<UserBean>();
-            GetUserListByIdsReq req = new GetUserListByIdsReq();
-            BatchGetDataUtil<UserBean, Long> tool = new BatchGetDataUtil<UserBean, Long>() {
-                @Override
-                public List<UserBean> getData(List<Long> paramList) {
-                    req.setRootOrgId(rootOrgId);
-                    req.setUserIdList(paramList);
-                    GetUserListByIdsResp resp = userCloudService.getUserListByIds(req);
-                    return resp.getUserBeanList();
-                }
-
-            };
-            tool.getDataForBatch(userList, ids, 100);
-            Map<Long, UserBean> map = userList.stream()
-                    .collect(Collectors.toMap(UserBean::getUserId, account -> account, (key1, key2) -> key2));
-            for (RandomPaperListVo markerBean : dtos) {
-                UserBean userBean = map.get(markerBean.getUpdateBy());
-                markerBean.setUpdateByName(userBean.getDisplayName());
-            }
-        }
-    }
-
-    @Transactional
-    @Override
-    public void toggle(String id, Boolean enable, User user) {
-        RandomPaper paperStruct = Model.of(randomPaperRepo.findById(id));
-        if (paperStruct == null) {
-            throw new StatusException("未找到模板");
-        }
-        if (!paperStruct.getRootOrgId().equals(user.getRootOrgId())) {
-            throw new StatusException("非法操作");
-        }
-        paperStruct.setEnable(enable);
-        randomPaperRepo.save(paperStruct);
-    }
-
-    @Override
-    public StructInfo getStructQuestionInfo(String structId) {
-        StructInfo ret = new StructInfo();
-        PaperStruct ps = Model.of(paperStructRepo.findById(structId));
-        ret.setTotalScore(ps.getTotalScore());
-        if (PaperStructType.BLUEPRINT.equals(ps.getPaperStrucType())) {
-            ret.setDifficultyDegree(ps.getDifficulty());
-            if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
-                List<StructQuestionInfo> sqinfos = new ArrayList<>();
-                ret.setStructQuestionInfo(sqinfos);
-                for (PaperDetailStruct paperDetailStruct : ps.getPaperDetailStructs()) {
-                    StructQuestionInfo sqinfo = new StructQuestionInfo();
-                    sqinfos.add(sqinfo);
-                    sqinfo.setDetailName(paperDetailStruct.getName());
-                    sqinfo.setTotalCount(paperDetailStruct.getDetailCount());
-                    sqinfo.setTotalScore(paperDetailStruct.getTotalScore());
-                    Integer simpleCount = 0;
-                    Integer mediumCount = 0;
-                    Integer difficultyCount = 0;
-                    simpleCount = paperDetailStruct.getPublicSimpleCount() + paperDetailStruct.getNoPublicSimpleCount();
-                    mediumCount = paperDetailStruct.getPublicMediumCount() + paperDetailStruct.getNoPublicMediumCount();
-                    difficultyCount = paperDetailStruct.getPublicDifficultyCount()
-                            + paperDetailStruct.getNoPublicDifficultyCount();
-                    sqinfo.setHardInfo(new StructQuestionCountInfo(difficultyCount, true));
-                    sqinfo.setMediumInfo(new StructQuestionCountInfo(mediumCount, true));
-                    sqinfo.setEasyInfo(new StructQuestionCountInfo(simpleCount, true));
-                }
-            }
-        } else if (PaperStructType.EXACT.equals(ps.getPaperStrucType())) {
-            ret.setDifficultyDegree(getExactDifficulty(ps));
-            if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
-                List<StructQuestionInfo> sqinfos = new ArrayList<>();
-                ret.setStructQuestionInfo(sqinfos);
-                for (PaperDetailStruct paperDetailStruct : ps.getPaperDetailStructs()) {
-                    StructQuestionInfo sqinfo = new StructQuestionInfo();
-                    sqinfos.add(sqinfo);
-                    sqinfo.setDetailName(paperDetailStruct.getName());
-                    sqinfo.setTotalCount(paperDetailStruct.getDetailCount());
-                    sqinfo.setTotalScore(paperDetailStruct.getTotalScore());
-                    Integer simpleCount = 0;
-                    Integer mediumCount = 0;
-                    Integer difficultyCount = 0;
-                    if (CollectionUtils.isNotEmpty(paperDetailStruct.getUnitStructs())) {
-                        for (PaperDetailUnitStructDto unitStruct : paperDetailStruct.getUnitStructs()) {
-                            simpleCount = simpleCount + unitStruct.getPublicSimple() + unitStruct.getNoPublicSimple();
-                            mediumCount = mediumCount + unitStruct.getPublicMedium() + unitStruct.getNoPublicMedium();
-                            difficultyCount = difficultyCount + unitStruct.getPublicDifficulty()
-                                    + unitStruct.getNoPublicDifficulty();
-                        }
-                    }
-                    sqinfo.setHardInfo(new StructQuestionCountInfo(difficultyCount, true));
-                    sqinfo.setMediumInfo(new StructQuestionCountInfo(mediumCount, true));
-                    sqinfo.setEasyInfo(new StructQuestionCountInfo(simpleCount, true));
-                }
-            }
-        }
-        return ret;
-    }
-
-    private Double getExactDifficulty(PaperStruct ps) {
-        Double sum = 0.0;
-        Double totalScore = ps.getTotalScore();
-        if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
-            Integer simpleCount = 0;
-            Integer mediumCount = 0;
-            Integer difficultyCount = 0;
-            for (PaperDetailStruct paperDetailStruct : ps.getPaperDetailStructs()) {
-                if (CollectionUtils.isNotEmpty(paperDetailStruct.getUnitStructs())) {
-                    for (PaperDetailUnitStructDto unitStruct : paperDetailStruct.getUnitStructs()) {
-                        simpleCount = unitStruct.getPublicSimple() + unitStruct.getNoPublicSimple();
-                        mediumCount = unitStruct.getPublicMedium() + unitStruct.getNoPublicMedium();
-                        difficultyCount = unitStruct.getPublicDifficulty() + unitStruct.getNoPublicDifficulty();
-                        sum = simpleCount * unitStruct.getScore() * 0.8 + mediumCount * unitStruct.getScore() * 0.5
-                                + difficultyCount * unitStruct.getScore() * 0.2 + sum;
-                    }
-                }
-            }
-
-            Double dif = sum / totalScore;
-            BigDecimal b = BigDecimal.valueOf(dif);
-            Double difficulty = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
-            return difficulty;
-        }
-        return (double) 0;
-    }
-
-    @Override
-    public StructInfo getPaperQuestionViewInfo(PaperQuestionViewQuery query) {
-        String structId = query.getStructId();
-        List<String> paperIds = query.getPaperIds();
-        if (StringUtils.isBlank(structId)) {
-            throw new StatusException("structId不能为空");
-        }
-        if (CollectionUtils.isEmpty(paperIds)) {
-            throw new StatusException("paperIds不能为空");
-        }
-        StructInfo ret = getPaperQuestionInfo(structId, paperIds);
-        clearQuestionIds(ret);
-        return ret;
-    }
-
-    private StructInfo getPaperQuestionInfo(String structId, List<String> paperIds) {
-        StructInfo ret = new StructInfo();
-        ret.setValid(true);
-        PaperStruct ps = Model.of(paperStructRepo.findById(structId));
-        List<QuestionDto> questionList = new ArrayList<>();
-        List<PaperDetailUnitDto> unitList = findUnitByPaperIds(paperIds);
-        fillQuestionAndDetail(unitList);
-        StructQuestionCheckDto cd = new StructQuestionCheckDto();
-        cd.setQuestionList(questionList);
-        if (PaperStructType.BLUEPRINT.equals(ps.getPaperStrucType())) {
-            for (PaperDetailUnitDto unit : unitList) {
-                unit.getQuestion().setQuesName(unit.getPaperDetail().getName());
-                unit.getQuestion().setPropertyGroup(bulidPropertyGroup(unit.getQuestion()));
-                questionList.add(unit.getQuestion());
-            }
-            if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
-                List<StructQuestionInfo> sqinfos = new ArrayList<>();
-                ret.setStructQuestionInfo(sqinfos);
-                int detailNumber = 0;
-                for (PaperDetailStruct ds : ps.getPaperDetailStructs()) {
-                    detailNumber++;
-                    StructQuestionInfo sqinfo = new StructQuestionInfo();
-                    sqinfos.add(sqinfo);
-                    sqinfo.setDetailName(ds.getName());
-                    cd.setDetailNumber(detailNumber);
-                    cd.setDs(ds);
-                    cd.setSqinfo(sqinfo);
-                    cd.setUnitScore(ds.getScore());
-                    for (CoursePropertyNumberDto cp : ds.getCoursePropertyNumberDtos()) {
-                        if (!cp.getDisable()) {
-                            cd.setCp(cp);
-                            setQuestionInfoByBlue(cd);
-                        }
-                    }
-                    sqinfo.setTotalCount(sqinfo.getHardInfo().getCount() + sqinfo.getMediumInfo().getCount()
-                            + sqinfo.getEasyInfo().getCount());
-                }
-            }
-        } else if (PaperStructType.EXACT.equals(ps.getPaperStrucType())) {
-            for (PaperDetailUnitDto unit : unitList) {
-                unit.getQuestion().setQuesName(unit.getPaperDetail().getName());
-                questionList.add(unit.getQuestion());
-            }
-            if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
-                List<StructQuestionInfo> sqinfos = new ArrayList<>();
-                ret.setStructQuestionInfo(sqinfos);
-                int detailNumber = 0;
-                for (PaperDetailStruct ds : ps.getPaperDetailStructs()) {
-                    detailNumber++;
-                    StructQuestionInfo sqinfo = new StructQuestionInfo();
-                    sqinfos.add(sqinfo);
-                    sqinfo.setDetailName(ds.getName());
-                    cd.setDetailNumber(detailNumber);
-                    cd.setSqinfo(sqinfo);
-                    int index = 0;
-                    for (PaperDetailUnitStructDto us : ds.getUnitStructs()) {
-                        index++;
-                        cd.setUnitScore(us.getScore());
-                        cd.setIndex(index);
-                        cd.setUs(us);
-                        setQuestionInfoByExact(cd);
-                    }
-                    sqinfo.setTotalCount(sqinfo.getHardInfo().getCount() + sqinfo.getMediumInfo().getCount()
-                            + sqinfo.getEasyInfo().getCount());
-                }
-            }
-        }
-        fillValid(ret);
-        return ret;
-    }
-
-    private void fillQuestionAndDetail(List<PaperDetailUnitDto> units) {
-        if (CollectionUtils.isNotEmpty(units)) {
-            new BatchSetDataUtil<PaperDetailUnitDto>() {
-
-                @Override
-                protected void setData(List<PaperDetailUnitDto> dataList) {
-                    List<String> ids = dataList.stream().map(p -> p.getQuestion().getId()).collect(Collectors.toList());
-                    List<QuestionDto> temList = findQuestionByIds(ids);
-                    if (CollectionUtils.isNotEmpty(temList)) {
-                        Map<String, QuestionDto> map = new HashMap<>();
-                        for (QuestionDto vo : temList) {
-                            map.put(vo.getId(), vo);
-                        }
-                        for (PaperDetailUnitDto dto : dataList) {
-                            dto.setQuestion(map.get(dto.getQuestion().getId()));
-                        }
-                    }
-                    ids = dataList.stream().map(p -> p.getPaperDetail().getId()).collect(Collectors.toList());
-                    List<PaperDetailDto> details = findDetailByIds(ids);
-                    if (CollectionUtils.isNotEmpty(temList)) {
-                        Map<String, PaperDetailDto> map = new HashMap<>();
-                        for (PaperDetailDto vo : details) {
-                            map.put(vo.getId(), vo);
-                        }
-                        for (PaperDetailUnitDto dto : dataList) {
-                            dto.setPaperDetail(map.get(dto.getPaperDetail().getId()));
-                        }
-                    }
-                }
-            }.setDataForBatch(units, 1000);
-        }
-    }
-
-    private List<QuestionDto> findQuestionByIds(List<String> questionIds) {
-        List<Object> ids = new ArrayList<>();
-        for (String pid : questionIds) {
-            if (pid.length() > 24) {
-                ids.add(pid);
-            } else {
-                ids.add(new ObjectId(pid));
-            }
-        }
-        Query query = new Query();
-        query.addCriteria(Criteria.where("id").in(ids));
-        List<QuestionDto> units = this.mongoTemplate.find(query, QuestionDto.class, "question");
-        return units;
-    }
-
-    private List<PaperDetailDto> findDetailByIds(List<String> detailIds) {
-        List<Object> ids = new ArrayList<>();
-        for (String pid : detailIds) {
-            if (pid.length() > 24) {
-                ids.add(pid);
-            } else {
-                ids.add(new ObjectId(pid));
-            }
-        }
-        Query query = new Query();
-        query.addCriteria(Criteria.where("id").in(ids));
-        List<PaperDetailDto> units = this.mongoTemplate.find(query, PaperDetailDto.class, "paperDetail");
-        return units;
-    }
-
-    private void fillValid(StructInfo ret) {
-        if (CollectionUtils.isEmpty(ret.getStructQuestionInfo())) {
-            ret.setValid(false);
-            return;
-        }
-        for (StructQuestionInfo si : ret.getStructQuestionInfo()) {
-            if (!si.getHardInfo().getValid()) {
-                ret.setValid(false);
-            }
-            if (!si.getMediumInfo().getValid()) {
-                ret.setValid(false);
-            }
-            if (!si.getEasyInfo().getValid()) {
-                ret.setValid(false);
-            }
-        }
-    }
-
-    private void clearQuestionIds(StructInfo ret) {
-        if (CollectionUtils.isEmpty(ret.getStructQuestionInfo())) {
-            ret.setValid(false);
-            return;
-        }
-        for (StructQuestionInfo si : ret.getStructQuestionInfo()) {
-            if (!si.getHardInfo().getValid()) {
-                ret.setValid(false);
-            }
-            if (!si.getMediumInfo().getValid()) {
-                ret.setValid(false);
-            }
-            if (!si.getEasyInfo().getValid()) {
-                ret.setValid(false);
-            }
-            for (RandomPaperQuestionDto dto : si.getHardInfo().getQuestionInfo()) {
-                dto.setQuestionDtos(new ArrayList<>());
-            }
-            for (RandomPaperQuestionDto dto : si.getMediumInfo().getQuestionInfo()) {
-                dto.setQuestionDtos(new ArrayList<>());
-            }
-            for (RandomPaperQuestionDto dto : si.getEasyInfo().getQuestionInfo()) {
-                dto.setQuestionDtos(new ArrayList<>());
-            }
-        }
-    }
-
-    private void setQuestionInfoByExact(StructQuestionCheckDto cd) {
-        PaperDetailUnitStructDto us = cd.getUs();
-        StructQuestionInfo sqinfo = cd.getSqinfo();
-        if (us.getNoPublicDifficulty() > 0) {
-            cd.setSi(sqinfo.getHardInfo());
-            cd.setPub(false);
-            cd.setDifficulty(QuestionDifficulty.HARD.getName());
-            cd.setNeedCount(us.getNoPublicDifficulty());
-            setQuestionInfoByExactItem(cd);
-        }
-        if (us.getPublicDifficulty() > 0) {
-            cd.setSi(sqinfo.getHardInfo());
-            cd.setPub(true);
-            cd.setDifficulty(QuestionDifficulty.HARD.getName());
-            cd.setNeedCount(us.getPublicDifficulty());
-            setQuestionInfoByExactItem(cd);
-        }
-
-        if (us.getNoPublicMedium() > 0) {
-            cd.setSi(sqinfo.getMediumInfo());
-            cd.setPub(false);
-            cd.setDifficulty(QuestionDifficulty.MEDIUM.getName());
-            cd.setNeedCount(us.getNoPublicMedium());
-            setQuestionInfoByExactItem(cd);
-        }
-        if (us.getPublicMedium() > 0) {
-            cd.setSi(sqinfo.getMediumInfo());
-            cd.setPub(true);
-            cd.setDifficulty(QuestionDifficulty.MEDIUM.getName());
-            cd.setNeedCount(us.getPublicMedium());
-            setQuestionInfoByExactItem(cd);
-        }
-        if (us.getNoPublicSimple() > 0) {
-            cd.setSi(sqinfo.getEasyInfo());
-            cd.setPub(false);
-            cd.setDifficulty(QuestionDifficulty.EASY.getName());
-            cd.setNeedCount(us.getNoPublicSimple());
-            setQuestionInfoByExactItem(cd);
-        }
-        if (us.getPublicSimple() > 0) {
-            cd.setSi(sqinfo.getEasyInfo());
-            cd.setPub(true);
-            cd.setDifficulty(QuestionDifficulty.EASY.getName());
-            cd.setNeedCount(us.getPublicSimple());
-            setQuestionInfoByExactItem(cd);
-        }
-    }
-
-    private void setQuestionInfoByExactItem(StructQuestionCheckDto cd) {
-        StructQuestionCountInfo si = cd.getSi();
-        RandomPaperQuestionDto rq = new RandomPaperQuestionDto();
-        rq.setUnitScore(cd.getUnitScore());
-        rq.setDetailNumber(cd.getDetailNumber());
-        rq.setKey(cd.getIndex() + "-" + cd.getPub() + "-" + cd.getDifficulty());
-        si.getQuestionInfo().add(rq);
-        if (CollectionUtils.isNotEmpty(cd.getQuestionList())) {
-            Iterator<QuestionDto> it = cd.getQuestionList().iterator();
-            while (it.hasNext()) {
-                QuestionDto q = it.next();
-                if (cd.getUsedQuesIds().contains(q.getId())) {
-                    it.remove();
-                } else {
-                    if (checkExactQuesType(cd.getUs().getQuesNames(), cd.getUs().getQuestionType(), cd.getPub(),
-                            cd.getDifficulty(), q)) {
-                        rq.getQuestionDtos().add(q);
-                        cd.getUsedQuesIds().add(q.getId());
-                        it.remove();
-                    }
-                }
-            }
-        }
-        si.setCount(si.getCount() + rq.getQuestionDtos().size());
-        if (si.getValid() && cd.getNeedCount() > rq.getQuestionDtos().size()) {
-            si.setValid(false);
-            si.setInvalidMsg(getExactErrmsg(cd.getIndex(), cd.getDetailNumber(), cd.getPub(), cd.getDifficulty()));
-        }
-    }
-
-    private void setQuestionInfoByBlue(StructQuestionCheckDto cd) {
-        StructQuestionInfo sqinfo = cd.getSqinfo();
-        CoursePropertyNumberDto cp = cd.getCp();
-        if (cp.getNoPublicDifficulty() > 0) {
-            cd.setSi(sqinfo.getHardInfo());
-            cd.setPub(false);
-            cd.setDifficulty(QuestionDifficulty.HARD.getName());
-            cd.setNeedCount(cp.getNoPublicDifficulty());
-            setQuestionInfoByBlueProp(cd);
-        }
-        if (cp.getPublicDifficulty() > 0) {
-            cd.setSi(sqinfo.getHardInfo());
-            cd.setPub(true);
-            cd.setDifficulty(QuestionDifficulty.HARD.getName());
-            cd.setNeedCount(cp.getPublicDifficulty());
-            setQuestionInfoByBlueProp(cd);
-        }
-
-        if (cp.getNoPublicMedium() > 0) {
-            cd.setSi(sqinfo.getMediumInfo());
-            cd.setPub(false);
-            cd.setDifficulty(QuestionDifficulty.MEDIUM.getName());
-            cd.setNeedCount(cp.getNoPublicMedium());
-            setQuestionInfoByBlueProp(cd);
-        }
-        if (cp.getPublicMedium() > 0) {
-            cd.setSi(sqinfo.getMediumInfo());
-            cd.setPub(true);
-            cd.setDifficulty(QuestionDifficulty.MEDIUM.getName());
-            cd.setNeedCount(cp.getPublicMedium());
-            setQuestionInfoByBlueProp(cd);
-        }
-        if (cp.getNoPublicSimple() > 0) {
-            cd.setSi(sqinfo.getEasyInfo());
-            cd.setPub(false);
-            cd.setDifficulty(QuestionDifficulty.EASY.getName());
-            cd.setNeedCount(cp.getNoPublicSimple());
-            setQuestionInfoByBlueProp(cd);
-        }
-        if (cp.getPublicSimple() > 0) {
-            cd.setSi(sqinfo.getEasyInfo());
-            cd.setPub(true);
-            cd.setDifficulty(QuestionDifficulty.EASY.getName());
-            cd.setNeedCount(cp.getPublicSimple());
-            setQuestionInfoByBlueProp(cd);
-        }
-    }
-
-    private void setQuestionInfoByBlueProp(StructQuestionCheckDto cd) {
-        StructQuestionCountInfo si = cd.getSi();
-        RandomPaperQuestionDto rq = new RandomPaperQuestionDto();
-        rq.setUnitScore(cd.getUnitScore());
-        rq.setDetailNumber(cd.getDetailNumber());
-        rq.setKey(bulidPropertyGroupByBlueStruct(cd.getCp().getPropertyParentId(), cd.getCp().getPropertyId(),
-                cd.getPub(), cd.getDifficulty()));
-        si.getQuestionInfo().add(rq);
-        if (CollectionUtils.isNotEmpty(cd.getQuestionList())) {
-            Iterator<QuestionDto> it = cd.getQuestionList().iterator();
-            while (it.hasNext()) {
-                QuestionDto q = it.next();
-                if (cd.getUsedQuesIds().contains(q.getId())) {
-                    it.remove();
-                } else {
-                    if (checkBlueQuesType(cd.getDs().getQuesNames(), cd.getDs().getQuestionType(), rq.getKey(), q)) {
-                        rq.getQuestionDtos().add(q);
-                        cd.getUsedQuesIds().add(q.getId());
-                        it.remove();
-                    }
-                }
-            }
-        }
-        si.setCount(si.getCount() + rq.getQuestionDtos().size());
-        if (si.getValid() && cd.getNeedCount() > rq.getQuestionDtos().size()) {
-            si.setValid(false);
-            si.setInvalidMsg(getBlueErrmsg(cd.getDetailNumber(), cd.getCp().getPropertyParentId(),
-                    cd.getCp().getPropertyId(), cd.getPub(), cd.getDifficulty()));
-        }
-    }
-
-    private String getExactErrmsg(Integer index, Integer detailNumber, Boolean pub, String difficulty) {
-        String pubstr;
-        if (pub) {
-            pubstr = "公开";
-        } else {
-            pubstr = "非公开";
-        }
-        return "第" + detailNumber + "大题 " + "第" + index + "题型结构 " + pubstr + "-" + difficulty + "题源数量不满足";
-    }
-
-    private String getBlueErrmsg(Integer detailNumber, String pproid, String proid, Boolean pub, String difficulty) {
-        String pubstr;
-        if (pub) {
-            pubstr = "公开";
-        } else {
-            pubstr = "非公开";
-        }
-        if (StringUtils.isNotBlank(pproid) && !"0".equals(pproid)) {
-            // 有一级 和 二级
-            Property fp = Model.of(propertyRepo.findById(pproid));
-            Property sp = Model.of(propertyRepo.findById(proid));
-            return "第" + detailNumber + "大题 " + fp.getName() + "-" + sp.getName() + "-" + pubstr + "-" + difficulty
-                    + "题源数量不满足";
-        } else {
-            // 有一级 无 二级
-            Property fp = Model.of(propertyRepo.findById(proid));
-            return "第" + detailNumber + "大题 " + fp.getName() + "-" + pubstr + "-" + difficulty + "题源数量不满足";
-        }
-    }
-
-    private boolean checkExactQuesType(List<String> quesNames, QuesStructType st, Boolean pub, String difficulty,
-                                       QuestionDto question) {
-        if (CollectionUtils.isNotEmpty(quesNames)) {
-            if (quesNames.contains(question.getQuesName()) && st.equals(question.getQuestionType())) {
-                if (question.getPublicity().equals(pub) && question.getDifficulty().equals(difficulty)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private boolean checkBlueQuesType(List<String> quesNames, QuesStructType st, String propertyGroup,
-                                      QuestionDto question) {
-        if (CollectionUtils.isNotEmpty(quesNames)) {
-            if (quesNames.contains(question.getQuesName()) && st.equals(question.getQuestionType())) {
-                if (question.getPropertyGroup() != null) {
-                    if (question.getPropertyGroup().contains(propertyGroup)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    private List<PaperDetailUnitDto> findUnitByPaperIds(List<String> paperIds) {
-        List<Object> ids = new ArrayList<>();
-        for (String pid : paperIds) {
-            if (pid.length() > 24) {
-                ids.add(pid);
-            } else {
-                ids.add(new ObjectId(pid));
-            }
-        }
-        Query query = new Query();
-        query.addCriteria(Criteria.where("paper.$id").in(ids));
-        List<PaperDetailUnitDto> units = this.mongoTemplate2.find(query, PaperDetailUnitDto.class, "paperDetailUnit");
-        return units;
-    }
-
-    private String bulidPropertyGroupByBlueStruct(String pproid, String proid, Boolean pub, String difficulty) {
-        String propertyGroup = null;
-        // 获取试题关联的多组属性
-        if (StringUtils.isNotBlank(pproid) && !"0".equals(pproid)) {
-            // 有一级 和 二级
-            propertyGroup = pproid + "-" + proid + "-" + pub + "-" + difficulty;
-        } else {
-            // 有一级 无 二级
-            propertyGroup = proid + "-" + pub + "-" + difficulty;
-        }
-        return propertyGroup;
-    }
-
-    private List<String> bulidPropertyGroup(QuestionDto question) {
-        String propertyGroup = null;
-        List<String> propertyGroups = new ArrayList<>();
-        // 获取试题关联的多组属性
-        List<QuesProperty> quesProperties = question.getQuesProperties();
-        if (quesProperties != null && quesProperties.size() > 0) {
-            for (QuesProperty quesProperty : quesProperties) {
-                if (quesProperty.getSecondProperty() != null) {
-                    // 有一级 和 二级
-                    if (quesProperty.getSecondProperty().getId() == null
-                            || StringUtils.isBlank(String.valueOf(quesProperty.getSecondProperty().getId()))) {
-                        propertyGroup = String.valueOf(quesProperty.getFirstProperty().getId()) + "-"
-                                + String.valueOf(question.getPublicity()) + "-" + question.getDifficulty();
-                    } else {
-                        propertyGroup = String.valueOf(quesProperty.getFirstProperty().getId()) + "-"
-                                + String.valueOf(quesProperty.getSecondProperty().getId()) + "-"
-                                + String.valueOf(question.getPublicity()) + "-" + question.getDifficulty();
-                    }
-                    propertyGroups.add(propertyGroup);
-                } else {
-                    // 有一级 无 二级
-                    propertyGroup = String.valueOf(quesProperty.getFirstProperty().getId()) + "-"
-                            + String.valueOf(question.getPublicity()) + "-" + question.getDifficulty();
-                    propertyGroups.add(propertyGroup);
-                }
-            }
-            return propertyGroups;
-        }
-        return null;
-    }
-
-    @Transactional
-    @Override
-    public StructInfo saveRandomPaper(RandomPaperDomain domain) {
-        if (domain.getCourseId() == null) {
-            throw new StatusException("课程id不能为空");
-        }
-        if (StringUtils.isBlank(domain.getName())) {
-            throw new StatusException("模板名称不能为空");
-        }
-        if (domain.getPaperStructType() == null) {
-            throw new StatusException("组卷模式不能为空");
-        }
-        if (domain.getPaperStructId() == null) {
-            throw new StatusException("组卷结构不能为空");
-        }
-        if (domain.getPaperType() == null) {
-            throw new StatusException("题源范围不能为空");
-        }
-        if (CollectionUtils.isEmpty(domain.getPaperIds())) {
-            throw new StatusException("试卷id不能为空");
-        }
-        RandomPaper rp = randomPaperRepo.findByRootOrgIdAndName(domain.getRootOrgId(), domain.getName());
-        if (rp != null && !rp.getId().equals(domain.getId())) {
-            throw new StatusException("模板名称已存在");
-        }
-        StructInfo ret = getPaperQuestionInfo(domain.getPaperStructId(), domain.getPaperIds());
-        if (ret.getValid()) {
-            RandomPaper e;
-            if (StringUtils.isNotBlank(domain.getId())) {
-                e = Model.of(randomPaperRepo.findById(domain.getId()));
-            } else {
-                e = new RandomPaper();
-                e.setCourseId(domain.getCourseId());
-                e.setEnable(true);
-                e.setRootOrgId(domain.getRootOrgId());
-            }
-            e.setName(domain.getName());
-            e.setPaperIds(domain.getPaperIds());
-            e.setPaperStructType(domain.getPaperStructType());
-            e.setPaperStructId(domain.getPaperStructId());
-            e.setPaperType(domain.getPaperType());
-            randomPaperRepo.save(e);
-            randomPaperQuestionRepo.deleteByRandomPaperId(e.getId());
-            List<RandomPaperQuestion> rqs = new ArrayList<>();
-            for (StructQuestionInfo si : ret.getStructQuestionInfo()) {
-                for (RandomPaperQuestionDto dto : si.getHardInfo().getQuestionInfo()) {
-                    addRqs(rqs, dto, e);
-                }
-                for (RandomPaperQuestionDto dto : si.getMediumInfo().getQuestionInfo()) {
-                    addRqs(rqs, dto, e);
-                }
-                for (RandomPaperQuestionDto dto : si.getEasyInfo().getQuestionInfo()) {
-                    addRqs(rqs, dto, e);
-                }
-            }
-            randomPaperQuestionRepo.saveAll(rqs);
-            String key = CacheConstants.CACHE_Q_RANDOM_PAPER + e.getId();
-            redisClient.delete(key);
-        }
-        clearQuestionIds(ret);
-        return ret;
-    }
-
-    private void addRqs(List<RandomPaperQuestion> rqs, RandomPaperQuestionDto dto, RandomPaper e) {
-        if (CollectionUtils.isNotEmpty(dto.getQuestionDtos())) {
-            for (QuestionDto qdto : dto.getQuestionDtos()) {
-                RandomPaperQuestion rq = new RandomPaperQuestion();
-                rqs.add(rq);
-                rq.setCourseId(e.getCourseId());
-                rq.setKey(dto.getDetailNumber() + "-" + dto.getKey());
-                rq.setQuestionId(qdto.getId());
-                rq.setRandomPaperId(e.getId());
-                rq.setRootOrgId(e.getRootOrgId());
-                rq.setScore(dto.getUnitScore());
-                rq.setQuestionType(qdto.getQuestionType());
-                rq.setAnswerType(qdto.getAnswerType());
-                if (CollectionUtils.isNotEmpty(qdto.getQuesOptions())) {
-                    rq.setOptionCount(qdto.getQuesOptions().size());
-                }
-                if (QuesStructType.NESTED_ANSWER_QUESTION.equals(rq.getQuestionType())
-                        && CollectionUtils.isNotEmpty(qdto.getSubQuestions())) {
-                    List<RandomPaperQuestion> subQuestion = new ArrayList<>();
-                    rq.setSubQuestions(subQuestion);
-                    List<Double> subScores = getSubScoreList(rq.getScore(), qdto.getSubQuestions().size());
-                    int i = 0;
-                    for (QuestionDto subQd : qdto.getSubQuestions()) {
-                        RandomPaperQuestion subrq = new RandomPaperQuestion();
-                        subQuestion.add(subrq);
-                        subrq.setCourseId(e.getCourseId());
-                        subrq.setScore(subScores.get(i));
-                        i++;
-                        subrq.setQuestionType(subQd.getQuestionType());
-                        subrq.setAnswerType(subQd.getAnswerType());
-                        if (CollectionUtils.isNotEmpty(subQd.getQuesOptions())) {
-                            subrq.setOptionCount(subQd.getQuesOptions().size());
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private List<Double> getSubScoreList(double totalScore, int count) {
-        List<Double> scoreList = new ArrayList<>();
-        if (count > 0) {
-            int baseScore = (int) (totalScore / count);
-            double leftScore = totalScore;
-            for (int i = 0; i < count; i++) {
-                scoreList.add((double) baseScore);
-                leftScore -= baseScore;
-            }
-            if (leftScore > 0) {
-                scoreList.set(count - 1, baseScore + leftScore);
-            }
-            return scoreList;
-        }
-        return null;
-    }
-
-    @Override
-    public RandomPaperListVo getInfo(String id) {
-        RandomPaperListVo vo = this.mongoTemplate.findById(id, RandomPaperListVo.class, "randomPaper");
-        CourseCacheBean course = CacheHelper.getCourse(vo.getCourseId());
-        vo.setCourseCode(course.getCode());
-        vo.setCourseName(course.getName());
-        vo.setPaperStructTypeStr(vo.getPaperStructType().getName());
-        PaperStruct paperStruct = Model.of(paperStructRepo.findById(vo.getPaperStructId()));
-        vo.setPaperStructName(paperStruct.getName());
-        vo.setEnableStr(vo.getEnable() ? "启用" : "禁用");
-        GetUserReq ureq = new GetUserReq();
-        ureq.setUserId(vo.getUpdateBy());
-        GetUserResp ures = userCloudService.getUser(ureq);
-        vo.setUpdateByName(ures.getUserBean().getDisplayName());
-        Query query = new Query();
-        List<Object> ids = new ArrayList<>();
-        for (String pid : vo.getPaperIds()) {
-            if (pid.length() > 24) {
-                ids.add(pid);
-            } else {
-                ids.add(new ObjectId(pid));
-            }
-        }
-        query.addCriteria(Criteria.where("id").in(ids));
-        List<PaperVo> papers = this.mongoTemplate.find(query, PaperVo.class, "paper");
-        vo.setPapers(papers);
-        return vo;
-    }
-
-    @Override
-    public DefaultPaper getRandomPaper(String randomPaperId, Integer playTime) {
-        long start = System.currentTimeMillis();
-        RandomPaperCache rp = this.getRandomPaperTemplateCacheById(randomPaperId);
-        long start2 = System.currentTimeMillis();
-        log.warn("获取抽卷模板! 耗时:{}ms ID:{} 题数量:{}", start2 - start, randomPaperId, rp.getQuestionMap().size());
-
-        PaperStructCache ps = paperStructService.getPaperStructCacheById(rp.getPaperStructId());
-        log.warn("获取组卷结构! 耗时:{}ms 结构类型:{} ID:{}", System.currentTimeMillis() - start2, ps.getPaperStrucType(), rp.getPaperStructId());
-
-        CreateDefaultPaperParam param = new CreateDefaultPaperParam();
-        param.setFullyObjective(true);
-        param.setRp(rp);
-        param.setPlayTime(playTime);
-        DefaultPaper paper = new DefaultPaper();
-        paper.setName(rp.getName());
-        List<DefaultQuestionGroup> details = new ArrayList<>();
-        paper.setQuestionGroupList(details);
-
-        if (PaperStructType.BLUEPRINT.equals(ps.getPaperStrucType())) {
-            if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
-                int detailNumber = 0;
-                for (PaperDetailStructCache ds : ps.getPaperDetailStructs()) {
-                    DefaultQuestionGroup detail = new DefaultQuestionGroup();
-                    details.add(detail);
-                    detail.setGroupName(ds.getName());
-                    detail.setGroupScore(ds.getTotalScore());
-                    List<DefaultQuestionStructureWrapper> units = new ArrayList<>();
-                    detail.setQuestionWrapperList(units);
-                    detailNumber++;
-                    param.setUnits(units);
-                    param.setDetailNumber(detailNumber);
-                    for (CoursePropertyNumberDto cp : ds.getCoursePropertyNumberDtos()) {
-                        if (!cp.getDisable()) {
-                            param.setCp(cp);
-                            createUnitByBlueProp(param);
-                        }
-                    }
-                }
-            }
-        } else if (PaperStructType.EXACT.equals(ps.getPaperStrucType())) {
-            if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
-                int detailNumber = 0;
-                for (PaperDetailStructCache ds : ps.getPaperDetailStructs()) {
-                    DefaultQuestionGroup detail = new DefaultQuestionGroup();
-                    details.add(detail);
-                    detail.setGroupName(ds.getName());
-                    detail.setGroupScore(ds.getTotalScore());
-                    List<DefaultQuestionStructureWrapper> units = new ArrayList<>();
-                    detail.setQuestionWrapperList(units);
-                    detailNumber++;
-                    param.setUnits(units);
-                    param.setDetailNumber(detailNumber);
-                    int index = 0;
-                    for (PaperDetailUnitStructDto us : ds.getUnitStructs()) {
-                        index++;
-                        param.setIndex(index);
-                        param.setUs(us);
-                        createUnitByExact(param);
-                    }
-                }
-            }
-        }
-        paper.setFullyObjective(param.getFullyObjective());
-
-        log.warn("抽卷完成! 总耗时:{}ms ID:{}", System.currentTimeMillis() - start, randomPaperId);
-        return paper;
-    }
-
-    private void createUnitByExact(CreateDefaultPaperParam param) {
-        PaperDetailUnitStructDto us = param.getUs();
-        if (us.getNoPublicDifficulty() > 0) {
-            param.setUnitCount(us.getNoPublicDifficulty());
-            String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + false + "-"
-                    + QuestionDifficulty.HARD.getName();
-            param.setKey(key);
-            createUnit(param);
-        }
-        if (us.getPublicDifficulty() > 0) {
-            param.setUnitCount(us.getPublicDifficulty());
-            String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + true + "-"
-                    + QuestionDifficulty.HARD.getName();
-            param.setKey(key);
-            createUnit(param);
-        }
-
-        if (us.getNoPublicMedium() > 0) {
-            param.setUnitCount(us.getNoPublicMedium());
-            String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + false + "-"
-                    + QuestionDifficulty.MEDIUM.getName();
-            param.setKey(key);
-            createUnit(param);
-        }
-        if (us.getPublicMedium() > 0) {
-            param.setUnitCount(us.getPublicMedium());
-            String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + true + "-"
-                    + QuestionDifficulty.MEDIUM.getName();
-            param.setKey(key);
-            createUnit(param);
-        }
-        if (us.getNoPublicSimple() > 0) {
-            param.setUnitCount(us.getNoPublicSimple());
-            String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + false + "-"
-                    + QuestionDifficulty.EASY.getName();
-            param.setKey(key);
-            createUnit(param);
-        }
-        if (us.getPublicSimple() > 0) {
-            param.setUnitCount(us.getPublicSimple());
-            String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + true + "-"
-                    + QuestionDifficulty.EASY.getName();
-            param.setKey(key);
-            createUnit(param);
-        }
-    }
-
-    private void createUnitByBlueProp(CreateDefaultPaperParam param) {
-        CoursePropertyNumberDto cp = param.getCp();
-        if (cp.getNoPublicDifficulty() > 0) {
-            String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
-                    cp.getPropertyId(), false, QuestionDifficulty.HARD.getName());
-            param.setKey(key);
-            param.setUnitCount(cp.getNoPublicDifficulty());
-            createUnit(param);
-        }
-        if (cp.getPublicDifficulty() > 0) {
-            String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
-                    cp.getPropertyId(), true, QuestionDifficulty.HARD.getName());
-            param.setKey(key);
-            param.setUnitCount(cp.getPublicDifficulty());
-            createUnit(param);
-        }
-
-        if (cp.getNoPublicMedium() > 0) {
-            String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
-                    cp.getPropertyId(), false, QuestionDifficulty.MEDIUM.getName());
-            param.setKey(key);
-            param.setUnitCount(cp.getNoPublicMedium());
-            createUnit(param);
-        }
-        if (cp.getPublicMedium() > 0) {
-            String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
-                    cp.getPropertyId(), true, QuestionDifficulty.MEDIUM.getName());
-            param.setKey(key);
-            param.setUnitCount(cp.getPublicMedium());
-            createUnit(param);
-        }
-        if (cp.getNoPublicSimple() > 0) {
-            String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
-                    cp.getPropertyId(), false, QuestionDifficulty.EASY.getName());
-            param.setKey(key);
-            param.setUnitCount(cp.getNoPublicSimple());
-            createUnit(param);
-        }
-        if (cp.getPublicSimple() > 0) {
-            String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
-                    cp.getPropertyId(), true, QuestionDifficulty.EASY.getName());
-            param.setKey(key);
-            param.setUnitCount(cp.getPublicSimple());
-            createUnit(param);
-        }
-    }
-
-    private void createUnit(CreateDefaultPaperParam param) {
-        List<RandomPaperQuestion> rpqs = param.getRp().getQuestionMap().get(param.getKey());
-        Collections.shuffle(rpqs);
-        for (int i = 0; i < param.getUnitCount(); i++) {
-            RandomPaperQuestion rpq = rpqs.get(i);
-            DefaultQuestionStructureWrapper qw = new DefaultQuestionStructureWrapper();
-            param.getUnits().add(qw);
-            qw.setLimitedPlayTimes(param.getPlayTime());
-            qw.setPlayedTimes(0);
-            qw.setQuestionId(rpq.getQuestionId());
-            qw.setQuestionScore(rpq.getScore());
-            List<DefaultQuestionUnitWrapper> qList = new ArrayList<>();
-            qw.setQuestionUnitWrapperList(qList);
-            if (QuesStructType.NESTED_ANSWER_QUESTION.equals(rpq.getQuestionType())) {
-                for (RandomPaperQuestion sub : rpq.getSubQuestions()) {
-                    DefaultQuestionUnitWrapper q = new DefaultQuestionUnitWrapper();
-                    qList.add(q);
-                    q.setAnswerType(sub.getAnswerType());
-                    q.setOptionPermutation(getOption(sub.getOptionCount()));
-                    q.setQuestionScore(sub.getScore());
-                    q.setQuestionType(getByOldType(sub.getQuestionType()));
-                    if (!PaperUtil.isObjecttive(sub.getQuestionType())) {
-                        param.setFullyObjective(false);
-                    }
-                }
-            } else {
-                DefaultQuestionUnitWrapper q = new DefaultQuestionUnitWrapper();
-                qList.add(q);
-                q.setAnswerType(rpq.getAnswerType());
-                q.setOptionPermutation(getOption(rpq.getOptionCount()));
-                q.setQuestionScore(rpq.getScore());
-                q.setQuestionType(getByOldType(rpq.getQuestionType()));
-                if (!PaperUtil.isObjecttive(rpq.getQuestionType())) {
-                    param.setFullyObjective(false);
-                }
-            }
-        }
-    }
-
-    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;
-    }
-
-    private Integer[] getOption(Integer count) {
-        if (count == null) {
-            return null;
-        }
-        Integer[] ret = new Integer[count];
-        for (int i = 0; i < count; i++) {
-            ret[i] = i;
-        }
-        return ret;
-    }
-
-    private RandomPaperCache getRandomPaperTemplateCacheById(String randomPaperId) {
-        // 抽卷模板 优先从本地缓存中获取
-        String key = CacheConstants.CACHE_Q_RANDOM_PAPER + randomPaperId;
-        RandomPaperCache rp = localRandomPaperCache.getIfPresent(key);
-        if (rp != null) {
-            log.warn("从【本地缓存】中获取抽卷模板! key:{}", key);
-            return rp;
-        }
-
-        // 从redis缓存中获取
-        rp = redisClient.get(key, RandomPaperCache.class, cacheTimeOut);
-        if (rp != null) {
-            localRandomPaperCache.put(key, rp);
-            log.warn("从【Redis缓存】中获取抽卷模板! key:{}", key);
-            return rp;
-        }
-
-        // 从数据库中获取
-        RandomPaper entity = Model.of(randomPaperRepo.findById(randomPaperId));
-        if (entity == null) {
-            throw new StatusException("未找到随机模板:" + randomPaperId);
-        }
-
-        List<RandomPaperQuestion> rpQuestions = randomPaperQuestionRepo.findByRandomPaperId(randomPaperId);
-        if (CollectionUtils.isEmpty(rpQuestions)) {
-            throw new StatusException("随机模板试题库为空:" + randomPaperId);
-        }
-
-        log.warn("从【数据库】中获取抽卷模板! key:{}", key);
-        rp = new RandomPaperCache();
-        rp.setName(entity.getName());
-        rp.setPaperStructId(entity.getPaperStructId());
-
-        Map<String, List<RandomPaperQuestion>> map = new HashMap<>();
-        for (RandomPaperQuestion rpq : rpQuestions) {
-            List<RandomPaperQuestion> list = map.get(rpq.getKey());
-            if (list == null) {
-                list = new ArrayList<>();
-                map.put(rpq.getKey(), list);
-            }
-            list.add(rpq);
-        }
-        rp.setQuestionMap(map);
-
-        redisClient.set(key, rp, cacheTimeOut);
-        localRandomPaperCache.put(key, rp);
-        return rp;
-    }
-
-    @Override
-    public boolean existStruct(String paperStructId) {
-        Query query = new Query();
-        query.addCriteria(Criteria.where("paperStructId").is(paperStructId));
-        RandomPaper rp = mongoTemplate.findOne(query, RandomPaper.class);
-        return rp != null;
-    }
-
-    @Override
-    public boolean existPaper(Long courseId, String paperId) {
-        Query query = new Query();
-        query.addCriteria(Criteria.where("courseId").is(courseId));
-        List<RandomPaper> rps = mongoTemplate.find(query, RandomPaper.class);
-        if (CollectionUtils.isEmpty(rps)) {
-            return false;
-        }
-        for (RandomPaper rp : rps) {
-            if (rp.getPaperIds().contains(paperId)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
+	private static final Logger log = LoggerFactory.getLogger(RandomPaperServiceImpl.class);
+
+	private static Cache<String, RandomPaperCache> localRandomPaperCache = CacheBuilder.newBuilder()
+			.expireAfterWrite(3, TimeUnit.MINUTES).build();
+
+	private static int cacheTimeOut = 2 * 60 * 60;
+
+	@Autowired
+	private MongoTemplate mongoTemplate;
+
+	@Resource(name = "mongoTemplate2")
+	private MongoTemplate mongoTemplate2;
+
+	@Autowired
+	private PaperStructService paperStructService;
+
+	@Autowired
+	private PaperStructRepo paperStructRepo;
+
+	@Autowired
+	private RedisClient redisClient;
+
+	@Autowired
+	private PropertyRepo propertyRepo;
+
+	@Autowired
+	private RandomPaperRepo randomPaperRepo;
+
+	@Autowired
+	private RandomPaperQuestionRepo randomPaperQuestionRepo;
+
+	@Autowired
+	private UserCloudService userCloudService;
+
+	@Override
+	public Page<RandomPaperListVo> getPage(RandomPaperQuery req) {
+		if (req.getUd().assertEmptyQueryResult()) {
+			return Page.empty();
+		}
+		Query query;
+		List<Criteria> cs = new ArrayList<>();
+		cs.add(Criteria.where("rootOrgId").is(req.getRootOrgId()));
+
+		if (req.getUd().assertNeedQueryRefIds()) {
+			cs.add(Criteria.where("courseId").in(req.getUd().getRefIds()));
+		}
+
+		if (req.getEnable() != null) {
+			cs.add(Criteria.where("enable").is(req.getEnable()));
+		}
+
+		if (req.getCourseId() != null) {
+			cs.add(Criteria.where("courseId").is(req.getCourseId()));
+		}
+		if (StringUtils.isNotBlank(req.getName())) {
+			String paperName = CommonUtils.escapeExprSpecialWord(req.getName());
+			cs.add(Criteria.where("name").regex(".*?\\.*" + paperName + ".*"));
+		}
+		Criteria and = new Criteria();
+		Criteria[] cas = new Criteria[cs.size()];
+		if (StringUtils.isNotBlank(req.getId())) {
+			and.andOperator(cs.toArray(cas));
+			query = Query.query(new Criteria().orOperator(and, Criteria.where("id").is(req.getId())));
+		} else {
+			and.andOperator(cs.toArray(cas));
+			query = Query.query(and);
+		}
+
+		long total = this.mongoTemplate.count(query, RandomPaper.class);
+		if (total == 0) {
+			return Page.empty();
+		}
+
+		PageRequest pageable = PageRequest.of(req.getPageNumber() - 1, req.getPageSize());
+		query.with(Sort.by(Sort.Order.desc("creationDate")));
+		query.skip(pageable.getOffset());
+		query.limit(pageable.getPageSize());
+
+		List<RandomPaperListVo> paperList = this.mongoTemplate.find(query, RandomPaperListVo.class, "randomPaper");
+		if (CollectionUtils.isEmpty(paperList)) {
+			return Page.empty();
+		}
+
+		for (RandomPaperListVo vo : paperList) {
+			CourseCacheBean course = CacheHelper.getCourse(vo.getCourseId());
+			vo.setCourseCode(course.getCode());
+			vo.setCourseName(course.getName());
+			vo.setPaperStructTypeStr(vo.getPaperStructType().getName());
+			PaperStruct paperStruct = Model.of(paperStructRepo.findById(vo.getPaperStructId()));
+			vo.setPaperStructName(paperStruct.getName());
+			vo.setEnableStr(vo.getEnable() ? "启用" : "禁用");
+		}
+		fillUserName(paperList, req.getRootOrgId());
+
+		return new PageImpl<>(paperList, pageable, total);
+	}
+
+	private void fillUserName(List<RandomPaperListVo> dtos, Long rootOrgId) {
+		if (dtos != null && dtos.size() > 0) {
+			List<Long> ids = dtos.stream().map(dto -> dto.getUpdateBy()).distinct().collect(Collectors.toList());
+			List<UserBean> userList = new ArrayList<UserBean>();
+			GetUserListByIdsReq req = new GetUserListByIdsReq();
+			BatchGetDataUtil<UserBean, Long> tool = new BatchGetDataUtil<UserBean, Long>() {
+				@Override
+				public List<UserBean> getData(List<Long> paramList) {
+					req.setRootOrgId(rootOrgId);
+					req.setUserIdList(paramList);
+					GetUserListByIdsResp resp = userCloudService.getUserListByIds(req);
+					return resp.getUserBeanList();
+				}
+
+			};
+			tool.getDataForBatch(userList, ids, 100);
+			Map<Long, UserBean> map = userList.stream()
+					.collect(Collectors.toMap(UserBean::getUserId, account -> account, (key1, key2) -> key2));
+			for (RandomPaperListVo markerBean : dtos) {
+				UserBean userBean = map.get(markerBean.getUpdateBy());
+				markerBean.setUpdateByName(userBean.getDisplayName());
+			}
+		}
+	}
+
+	@Transactional
+	@Override
+	public void toggle(String id, Boolean enable, User user) {
+		RandomPaper paperStruct = Model.of(randomPaperRepo.findById(id));
+		if (paperStruct == null) {
+			throw new StatusException("未找到模板");
+		}
+		if (!paperStruct.getRootOrgId().equals(user.getRootOrgId())) {
+			throw new StatusException("非法操作");
+		}
+		paperStruct.setEnable(enable);
+		randomPaperRepo.save(paperStruct);
+	}
+
+	@Override
+	public StructInfo getStructQuestionInfo(String structId) {
+		StructInfo ret = new StructInfo();
+		PaperStruct ps = Model.of(paperStructRepo.findById(structId));
+		ret.setTotalScore(ps.getTotalScore());
+		if (PaperStructType.BLUEPRINT.equals(ps.getPaperStrucType())) {
+			ret.setDifficultyDegree(ps.getDifficulty());
+			if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
+				List<StructQuestionInfo> sqinfos = new ArrayList<>();
+				ret.setStructQuestionInfo(sqinfos);
+				for (PaperDetailStruct paperDetailStruct : ps.getPaperDetailStructs()) {
+					StructQuestionInfo sqinfo = new StructQuestionInfo();
+					sqinfos.add(sqinfo);
+					sqinfo.setDetailName(paperDetailStruct.getName());
+					sqinfo.setTotalCount(paperDetailStruct.getDetailCount());
+					sqinfo.setTotalScore(paperDetailStruct.getTotalScore());
+					Integer simpleCount = 0;
+					Integer mediumCount = 0;
+					Integer difficultyCount = 0;
+					simpleCount = paperDetailStruct.getPublicSimpleCount() + paperDetailStruct.getNoPublicSimpleCount();
+					mediumCount = paperDetailStruct.getPublicMediumCount() + paperDetailStruct.getNoPublicMediumCount();
+					difficultyCount = paperDetailStruct.getPublicDifficultyCount()
+							+ paperDetailStruct.getNoPublicDifficultyCount();
+					sqinfo.setHardInfo(new StructQuestionCountInfo(difficultyCount, true));
+					sqinfo.setMediumInfo(new StructQuestionCountInfo(mediumCount, true));
+					sqinfo.setEasyInfo(new StructQuestionCountInfo(simpleCount, true));
+				}
+			}
+		} else if (PaperStructType.EXACT.equals(ps.getPaperStrucType())) {
+			ret.setDifficultyDegree(getExactDifficulty(ps));
+			if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
+				List<StructQuestionInfo> sqinfos = new ArrayList<>();
+				ret.setStructQuestionInfo(sqinfos);
+				for (PaperDetailStruct paperDetailStruct : ps.getPaperDetailStructs()) {
+					StructQuestionInfo sqinfo = new StructQuestionInfo();
+					sqinfos.add(sqinfo);
+					sqinfo.setDetailName(paperDetailStruct.getName());
+					sqinfo.setTotalCount(paperDetailStruct.getDetailCount());
+					sqinfo.setTotalScore(paperDetailStruct.getTotalScore());
+					Integer simpleCount = 0;
+					Integer mediumCount = 0;
+					Integer difficultyCount = 0;
+					if (CollectionUtils.isNotEmpty(paperDetailStruct.getUnitStructs())) {
+						for (PaperDetailUnitStructDto unitStruct : paperDetailStruct.getUnitStructs()) {
+							simpleCount = simpleCount + unitStruct.getPublicSimple() + unitStruct.getNoPublicSimple();
+							mediumCount = mediumCount + unitStruct.getPublicMedium() + unitStruct.getNoPublicMedium();
+							difficultyCount = difficultyCount + unitStruct.getPublicDifficulty()
+									+ unitStruct.getNoPublicDifficulty();
+						}
+					}
+					sqinfo.setHardInfo(new StructQuestionCountInfo(difficultyCount, true));
+					sqinfo.setMediumInfo(new StructQuestionCountInfo(mediumCount, true));
+					sqinfo.setEasyInfo(new StructQuestionCountInfo(simpleCount, true));
+				}
+			}
+		}
+		return ret;
+	}
+
+	private Double getExactDifficulty(PaperStruct ps) {
+		Double sum = 0.0;
+		Double totalScore = ps.getTotalScore();
+		if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
+			Integer simpleCount = 0;
+			Integer mediumCount = 0;
+			Integer difficultyCount = 0;
+			for (PaperDetailStruct paperDetailStruct : ps.getPaperDetailStructs()) {
+				if (CollectionUtils.isNotEmpty(paperDetailStruct.getUnitStructs())) {
+					for (PaperDetailUnitStructDto unitStruct : paperDetailStruct.getUnitStructs()) {
+						simpleCount = unitStruct.getPublicSimple() + unitStruct.getNoPublicSimple();
+						mediumCount = unitStruct.getPublicMedium() + unitStruct.getNoPublicMedium();
+						difficultyCount = unitStruct.getPublicDifficulty() + unitStruct.getNoPublicDifficulty();
+						sum = simpleCount * unitStruct.getScore() * 0.8 + mediumCount * unitStruct.getScore() * 0.5
+								+ difficultyCount * unitStruct.getScore() * 0.2 + sum;
+					}
+				}
+			}
+
+			Double dif = sum / totalScore;
+			BigDecimal b = BigDecimal.valueOf(dif);
+			Double difficulty = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
+			return difficulty;
+		}
+		return (double) 0;
+	}
+
+	@Override
+	public StructInfo getPaperQuestionViewInfo(PaperQuestionViewQuery query) {
+		String structId = query.getStructId();
+		List<String> paperIds = query.getPaperIds();
+		if (StringUtils.isBlank(structId)) {
+			throw new StatusException("structId不能为空");
+		}
+		if (CollectionUtils.isEmpty(paperIds)) {
+			throw new StatusException("paperIds不能为空");
+		}
+		StructInfo ret = getPaperQuestionInfo(structId, paperIds);
+		clearQuestionIds(ret);
+		return ret;
+	}
+
+	private StructInfo getPaperQuestionInfo(String structId, List<String> paperIds) {
+		StructInfo ret = new StructInfo();
+		ret.setValid(true);
+		PaperStruct ps = Model.of(paperStructRepo.findById(structId));
+		List<QuestionDto> questionList = new ArrayList<>();
+		List<PaperDetailUnitDto> unitList = findUnitByPaperIds(paperIds);
+		fillQuestionAndDetail(unitList);
+		StructQuestionCheckDto cd = new StructQuestionCheckDto();
+		cd.setQuestionList(questionList);
+		if (PaperStructType.BLUEPRINT.equals(ps.getPaperStrucType())) {
+			for (PaperDetailUnitDto unit : unitList) {
+				unit.getQuestion().setQuesName(unit.getPaperDetail().getName());
+				unit.getQuestion().setPropertyGroup(bulidPropertyGroup(unit.getQuestion()));
+				questionList.add(unit.getQuestion());
+			}
+			if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
+				List<StructQuestionInfo> sqinfos = new ArrayList<>();
+				ret.setStructQuestionInfo(sqinfos);
+				int detailNumber = 0;
+				for (PaperDetailStruct ds : ps.getPaperDetailStructs()) {
+					detailNumber++;
+					StructQuestionInfo sqinfo = new StructQuestionInfo();
+					sqinfos.add(sqinfo);
+					sqinfo.setDetailName(ds.getName());
+					cd.setDetailNumber(detailNumber);
+					cd.setDs(ds);
+					cd.setSqinfo(sqinfo);
+					cd.setUnitScore(ds.getScore());
+					for (CoursePropertyNumberDto cp : ds.getCoursePropertyNumberDtos()) {
+						if (!cp.getDisable()) {
+							cd.setCp(cp);
+							setQuestionInfoByBlue(cd);
+						}
+					}
+					sqinfo.setTotalCount(sqinfo.getHardInfo().getCount() + sqinfo.getMediumInfo().getCount()
+							+ sqinfo.getEasyInfo().getCount());
+				}
+			}
+		} else if (PaperStructType.EXACT.equals(ps.getPaperStrucType())) {
+			for (PaperDetailUnitDto unit : unitList) {
+				unit.getQuestion().setQuesName(unit.getPaperDetail().getName());
+				questionList.add(unit.getQuestion());
+			}
+			if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
+				List<StructQuestionInfo> sqinfos = new ArrayList<>();
+				ret.setStructQuestionInfo(sqinfos);
+				int detailNumber = 0;
+				for (PaperDetailStruct ds : ps.getPaperDetailStructs()) {
+					detailNumber++;
+					StructQuestionInfo sqinfo = new StructQuestionInfo();
+					sqinfos.add(sqinfo);
+					sqinfo.setDetailName(ds.getName());
+					cd.setDetailNumber(detailNumber);
+					cd.setSqinfo(sqinfo);
+					int index = 0;
+					for (PaperDetailUnitStructDto us : ds.getUnitStructs()) {
+						index++;
+						cd.setUnitScore(us.getScore());
+						cd.setIndex(index);
+						cd.setUs(us);
+						setQuestionInfoByExact(cd);
+					}
+					sqinfo.setTotalCount(sqinfo.getHardInfo().getCount() + sqinfo.getMediumInfo().getCount()
+							+ sqinfo.getEasyInfo().getCount());
+				}
+			}
+		}
+		fillValid(ret);
+		return ret;
+	}
+
+	private void fillQuestionAndDetail(List<PaperDetailUnitDto> units) {
+		if (CollectionUtils.isNotEmpty(units)) {
+			new BatchSetDataUtil<PaperDetailUnitDto>() {
+
+				@Override
+				protected void setData(List<PaperDetailUnitDto> dataList) {
+					List<String> ids = dataList.stream().map(p -> p.getQuestion().getId()).collect(Collectors.toList());
+					List<QuestionDto> temList = findQuestionByIds(ids);
+					if (CollectionUtils.isNotEmpty(temList)) {
+						Map<String, QuestionDto> map = new HashMap<>();
+						for (QuestionDto vo : temList) {
+							map.put(vo.getId(), vo);
+						}
+						for (PaperDetailUnitDto dto : dataList) {
+							dto.setQuestion(map.get(dto.getQuestion().getId()));
+						}
+					}
+					ids = dataList.stream().map(p -> p.getPaperDetail().getId()).collect(Collectors.toList());
+					List<PaperDetailDto> details = findDetailByIds(ids);
+					if (CollectionUtils.isNotEmpty(temList)) {
+						Map<String, PaperDetailDto> map = new HashMap<>();
+						for (PaperDetailDto vo : details) {
+							map.put(vo.getId(), vo);
+						}
+						for (PaperDetailUnitDto dto : dataList) {
+							dto.setPaperDetail(map.get(dto.getPaperDetail().getId()));
+						}
+					}
+				}
+			}.setDataForBatch(units, 1000);
+		}
+	}
+
+	private List<QuestionDto> findQuestionByIds(List<String> questionIds) {
+		List<Object> ids = new ArrayList<>();
+		for (String pid : questionIds) {
+			if (pid.length() > 24) {
+				ids.add(pid);
+			} else {
+				ids.add(new ObjectId(pid));
+			}
+		}
+		Query query = new Query();
+		query.addCriteria(Criteria.where("id").in(ids));
+		List<QuestionDto> units = this.mongoTemplate.find(query, QuestionDto.class, "question");
+		return units;
+	}
+
+	private List<PaperDetailDto> findDetailByIds(List<String> detailIds) {
+		List<Object> ids = new ArrayList<>();
+		for (String pid : detailIds) {
+			if (pid.length() > 24) {
+				ids.add(pid);
+			} else {
+				ids.add(new ObjectId(pid));
+			}
+		}
+		Query query = new Query();
+		query.addCriteria(Criteria.where("id").in(ids));
+		List<PaperDetailDto> units = this.mongoTemplate.find(query, PaperDetailDto.class, "paperDetail");
+		return units;
+	}
+
+	private void fillValid(StructInfo ret) {
+		if (CollectionUtils.isEmpty(ret.getStructQuestionInfo())) {
+			ret.setValid(false);
+			return;
+		}
+		for (StructQuestionInfo si : ret.getStructQuestionInfo()) {
+			if (!si.getHardInfo().getValid()) {
+				ret.setValid(false);
+			}
+			if (!si.getMediumInfo().getValid()) {
+				ret.setValid(false);
+			}
+			if (!si.getEasyInfo().getValid()) {
+				ret.setValid(false);
+			}
+		}
+	}
+
+	private void clearQuestionIds(StructInfo ret) {
+		if (CollectionUtils.isEmpty(ret.getStructQuestionInfo())) {
+			ret.setValid(false);
+			return;
+		}
+		for (StructQuestionInfo si : ret.getStructQuestionInfo()) {
+			if (!si.getHardInfo().getValid()) {
+				ret.setValid(false);
+			}
+			if (!si.getMediumInfo().getValid()) {
+				ret.setValid(false);
+			}
+			if (!si.getEasyInfo().getValid()) {
+				ret.setValid(false);
+			}
+			for (RandomPaperQuestionDto dto : si.getHardInfo().getQuestionInfo()) {
+				dto.setQuestionDtos(new ArrayList<>());
+			}
+			for (RandomPaperQuestionDto dto : si.getMediumInfo().getQuestionInfo()) {
+				dto.setQuestionDtos(new ArrayList<>());
+			}
+			for (RandomPaperQuestionDto dto : si.getEasyInfo().getQuestionInfo()) {
+				dto.setQuestionDtos(new ArrayList<>());
+			}
+		}
+	}
+
+	private void setQuestionInfoByExact(StructQuestionCheckDto cd) {
+		PaperDetailUnitStructDto us = cd.getUs();
+		StructQuestionInfo sqinfo = cd.getSqinfo();
+		if (us.getNoPublicDifficulty() > 0) {
+			cd.setSi(sqinfo.getHardInfo());
+			cd.setPub(false);
+			cd.setDifficulty(QuestionDifficulty.HARD.getName());
+			cd.setNeedCount(us.getNoPublicDifficulty());
+			setQuestionInfoByExactItem(cd);
+		}
+		if (us.getPublicDifficulty() > 0) {
+			cd.setSi(sqinfo.getHardInfo());
+			cd.setPub(true);
+			cd.setDifficulty(QuestionDifficulty.HARD.getName());
+			cd.setNeedCount(us.getPublicDifficulty());
+			setQuestionInfoByExactItem(cd);
+		}
+
+		if (us.getNoPublicMedium() > 0) {
+			cd.setSi(sqinfo.getMediumInfo());
+			cd.setPub(false);
+			cd.setDifficulty(QuestionDifficulty.MEDIUM.getName());
+			cd.setNeedCount(us.getNoPublicMedium());
+			setQuestionInfoByExactItem(cd);
+		}
+		if (us.getPublicMedium() > 0) {
+			cd.setSi(sqinfo.getMediumInfo());
+			cd.setPub(true);
+			cd.setDifficulty(QuestionDifficulty.MEDIUM.getName());
+			cd.setNeedCount(us.getPublicMedium());
+			setQuestionInfoByExactItem(cd);
+		}
+		if (us.getNoPublicSimple() > 0) {
+			cd.setSi(sqinfo.getEasyInfo());
+			cd.setPub(false);
+			cd.setDifficulty(QuestionDifficulty.EASY.getName());
+			cd.setNeedCount(us.getNoPublicSimple());
+			setQuestionInfoByExactItem(cd);
+		}
+		if (us.getPublicSimple() > 0) {
+			cd.setSi(sqinfo.getEasyInfo());
+			cd.setPub(true);
+			cd.setDifficulty(QuestionDifficulty.EASY.getName());
+			cd.setNeedCount(us.getPublicSimple());
+			setQuestionInfoByExactItem(cd);
+		}
+	}
+
+	private void setQuestionInfoByExactItem(StructQuestionCheckDto cd) {
+		StructQuestionCountInfo si = cd.getSi();
+		RandomPaperQuestionDto rq = new RandomPaperQuestionDto();
+		rq.setUnitScore(cd.getUnitScore());
+		rq.setDetailNumber(cd.getDetailNumber());
+		rq.setKey(cd.getIndex() + "-" + cd.getPub() + "-" + cd.getDifficulty());
+		si.getQuestionInfo().add(rq);
+		if (CollectionUtils.isNotEmpty(cd.getQuestionList())) {
+			Iterator<QuestionDto> it = cd.getQuestionList().iterator();
+			while (it.hasNext()) {
+				QuestionDto q = it.next();
+				if (cd.getUsedQuesIds().contains(q.getId())) {
+					it.remove();
+				} else {
+					if (checkExactQuesType(cd.getUs().getQuesNames(), cd.getUs().getQuestionType(), cd.getPub(),
+							cd.getDifficulty(), q)) {
+						rq.getQuestionDtos().add(q);
+						cd.getUsedQuesIds().add(q.getId());
+						it.remove();
+					}
+				}
+			}
+		}
+		si.setCount(si.getCount() + rq.getQuestionDtos().size());
+		if (si.getValid() && cd.getNeedCount() > rq.getQuestionDtos().size()) {
+			si.setValid(false);
+			si.setInvalidMsg(getExactErrmsg(cd.getIndex(), cd.getDetailNumber(), cd.getPub(), cd.getDifficulty()));
+		}
+	}
+
+	private void setQuestionInfoByBlue(StructQuestionCheckDto cd) {
+		StructQuestionInfo sqinfo = cd.getSqinfo();
+		CoursePropertyNumberDto cp = cd.getCp();
+		if (cp.getNoPublicDifficulty() > 0) {
+			cd.setSi(sqinfo.getHardInfo());
+			cd.setPub(false);
+			cd.setDifficulty(QuestionDifficulty.HARD.getName());
+			cd.setNeedCount(cp.getNoPublicDifficulty());
+			setQuestionInfoByBlueProp(cd);
+		}
+		if (cp.getPublicDifficulty() > 0) {
+			cd.setSi(sqinfo.getHardInfo());
+			cd.setPub(true);
+			cd.setDifficulty(QuestionDifficulty.HARD.getName());
+			cd.setNeedCount(cp.getPublicDifficulty());
+			setQuestionInfoByBlueProp(cd);
+		}
+
+		if (cp.getNoPublicMedium() > 0) {
+			cd.setSi(sqinfo.getMediumInfo());
+			cd.setPub(false);
+			cd.setDifficulty(QuestionDifficulty.MEDIUM.getName());
+			cd.setNeedCount(cp.getNoPublicMedium());
+			setQuestionInfoByBlueProp(cd);
+		}
+		if (cp.getPublicMedium() > 0) {
+			cd.setSi(sqinfo.getMediumInfo());
+			cd.setPub(true);
+			cd.setDifficulty(QuestionDifficulty.MEDIUM.getName());
+			cd.setNeedCount(cp.getPublicMedium());
+			setQuestionInfoByBlueProp(cd);
+		}
+		if (cp.getNoPublicSimple() > 0) {
+			cd.setSi(sqinfo.getEasyInfo());
+			cd.setPub(false);
+			cd.setDifficulty(QuestionDifficulty.EASY.getName());
+			cd.setNeedCount(cp.getNoPublicSimple());
+			setQuestionInfoByBlueProp(cd);
+		}
+		if (cp.getPublicSimple() > 0) {
+			cd.setSi(sqinfo.getEasyInfo());
+			cd.setPub(true);
+			cd.setDifficulty(QuestionDifficulty.EASY.getName());
+			cd.setNeedCount(cp.getPublicSimple());
+			setQuestionInfoByBlueProp(cd);
+		}
+	}
+
+	private void setQuestionInfoByBlueProp(StructQuestionCheckDto cd) {
+		StructQuestionCountInfo si = cd.getSi();
+		RandomPaperQuestionDto rq = new RandomPaperQuestionDto();
+		rq.setUnitScore(cd.getUnitScore());
+		rq.setDetailNumber(cd.getDetailNumber());
+		rq.setKey(bulidPropertyGroupByBlueStruct(cd.getCp().getPropertyParentId(), cd.getCp().getPropertyId(),
+				cd.getPub(), cd.getDifficulty()));
+		si.getQuestionInfo().add(rq);
+		if (CollectionUtils.isNotEmpty(cd.getQuestionList())) {
+			Iterator<QuestionDto> it = cd.getQuestionList().iterator();
+			while (it.hasNext()) {
+				QuestionDto q = it.next();
+				if (cd.getUsedQuesIds().contains(q.getId())) {
+					it.remove();
+				} else {
+					if (checkBlueQuesType(cd.getDs().getQuesNames(), cd.getDs().getQuestionType(), rq.getKey(), q)) {
+						rq.getQuestionDtos().add(q);
+						cd.getUsedQuesIds().add(q.getId());
+						it.remove();
+					}
+				}
+			}
+		}
+		si.setCount(si.getCount() + rq.getQuestionDtos().size());
+		if (si.getValid() && cd.getNeedCount() > rq.getQuestionDtos().size()) {
+			si.setValid(false);
+			si.setInvalidMsg(getBlueErrmsg(cd.getDetailNumber(), cd.getCp().getPropertyParentId(),
+					cd.getCp().getPropertyId(), cd.getPub(), cd.getDifficulty()));
+		}
+	}
+
+	private String getExactErrmsg(Integer index, Integer detailNumber, Boolean pub, String difficulty) {
+		String pubstr;
+		if (pub) {
+			pubstr = "公开";
+		} else {
+			pubstr = "非公开";
+		}
+		return "第" + detailNumber + "大题 " + "第" + index + "题型结构 " + pubstr + "-" + difficulty + "题源数量不满足";
+	}
+
+	private String getBlueErrmsg(Integer detailNumber, String pproid, String proid, Boolean pub, String difficulty) {
+		String pubstr;
+		if (pub) {
+			pubstr = "公开";
+		} else {
+			pubstr = "非公开";
+		}
+		if (StringUtils.isNotBlank(pproid) && !"0".equals(pproid)) {
+			// 有一级 和 二级
+			Property fp = Model.of(propertyRepo.findById(pproid));
+			Property sp = Model.of(propertyRepo.findById(proid));
+			return "第" + detailNumber + "大题 " + fp.getName() + "-" + sp.getName() + "-" + pubstr + "-" + difficulty
+					+ "题源数量不满足";
+		} else {
+			// 有一级 无 二级
+			Property fp = Model.of(propertyRepo.findById(proid));
+			return "第" + detailNumber + "大题 " + fp.getName() + "-" + pubstr + "-" + difficulty + "题源数量不满足";
+		}
+	}
+
+	private boolean checkExactQuesType(List<String> quesNames, QuesStructType st, Boolean pub, String difficulty,
+			QuestionDto question) {
+		if (CollectionUtils.isNotEmpty(quesNames)) {
+			if (quesNames.contains(question.getQuesName()) && st.equals(question.getQuestionType())) {
+				if (question.getPublicity().equals(pub) && question.getDifficulty().equals(difficulty)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	private boolean checkBlueQuesType(List<String> quesNames, QuesStructType st, String propertyGroup,
+			QuestionDto question) {
+		if (CollectionUtils.isNotEmpty(quesNames)) {
+			if (quesNames.contains(question.getQuesName()) && st.equals(question.getQuestionType())) {
+				if (question.getPropertyGroup() != null) {
+					if (question.getPropertyGroup().contains(propertyGroup)) {
+						return true;
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	private List<PaperDetailUnitDto> findUnitByPaperIds(List<String> paperIds) {
+		List<Object> ids = new ArrayList<>();
+		for (String pid : paperIds) {
+			if (pid.length() > 24) {
+				ids.add(pid);
+			} else {
+				ids.add(new ObjectId(pid));
+			}
+		}
+		Query query = new Query();
+		query.addCriteria(Criteria.where("paper.$id").in(ids));
+		List<PaperDetailUnitDto> units = this.mongoTemplate2.find(query, PaperDetailUnitDto.class, "paperDetailUnit");
+		return units;
+	}
+
+	private String bulidPropertyGroupByBlueStruct(String pproid, String proid, Boolean pub, String difficulty) {
+		String propertyGroup = null;
+		// 获取试题关联的多组属性
+		if (StringUtils.isNotBlank(pproid) && !"0".equals(pproid)) {
+			// 有一级 和 二级
+			propertyGroup = pproid + "-" + proid + "-" + pub + "-" + difficulty;
+		} else {
+			// 有一级 无 二级
+			propertyGroup = proid + "-" + pub + "-" + difficulty;
+		}
+		return propertyGroup;
+	}
+
+	private List<String> bulidPropertyGroup(QuestionDto question) {
+		String propertyGroup = null;
+		List<String> propertyGroups = new ArrayList<>();
+		// 获取试题关联的多组属性
+		List<QuesProperty> quesProperties = question.getQuesProperties();
+		if (quesProperties != null && quesProperties.size() > 0) {
+			for (QuesProperty quesProperty : quesProperties) {
+				if (quesProperty.getSecondProperty() != null) {
+					// 有一级 和 二级
+					if (quesProperty.getSecondProperty().getId() == null
+							|| StringUtils.isBlank(String.valueOf(quesProperty.getSecondProperty().getId()))) {
+						propertyGroup = String.valueOf(quesProperty.getFirstProperty().getId()) + "-"
+								+ String.valueOf(question.getPublicity()) + "-" + question.getDifficulty();
+					} else {
+						propertyGroup = String.valueOf(quesProperty.getFirstProperty().getId()) + "-"
+								+ String.valueOf(quesProperty.getSecondProperty().getId()) + "-"
+								+ String.valueOf(question.getPublicity()) + "-" + question.getDifficulty();
+					}
+					propertyGroups.add(propertyGroup);
+				} else {
+					// 有一级 无 二级
+					propertyGroup = String.valueOf(quesProperty.getFirstProperty().getId()) + "-"
+							+ String.valueOf(question.getPublicity()) + "-" + question.getDifficulty();
+					propertyGroups.add(propertyGroup);
+				}
+			}
+			return propertyGroups;
+		}
+		return null;
+	}
+
+	@Transactional
+	@Override
+	public StructInfo saveRandomPaper(RandomPaperDomain domain) {
+		if (domain.getCourseId() == null) {
+			throw new StatusException("课程id不能为空");
+		}
+		if (StringUtils.isBlank(domain.getName())) {
+			throw new StatusException("模板名称不能为空");
+		}
+		if (domain.getPaperStructType() == null) {
+			throw new StatusException("组卷模式不能为空");
+		}
+		if (domain.getPaperStructId() == null) {
+			throw new StatusException("组卷结构不能为空");
+		}
+		if (domain.getPaperType() == null) {
+			throw new StatusException("题源范围不能为空");
+		}
+		if (CollectionUtils.isEmpty(domain.getPaperIds())) {
+			throw new StatusException("试卷id不能为空");
+		}
+		RandomPaper rp = randomPaperRepo.findByRootOrgIdAndName(domain.getRootOrgId(), domain.getName());
+		if (rp != null && !rp.getId().equals(domain.getId())) {
+			throw new StatusException("模板名称已存在");
+		}
+		StructInfo ret = getPaperQuestionInfo(domain.getPaperStructId(), domain.getPaperIds());
+		if (ret.getValid()) {
+			RandomPaper e;
+			if (StringUtils.isNotBlank(domain.getId())) {
+				e = Model.of(randomPaperRepo.findById(domain.getId()));
+			} else {
+				e = new RandomPaper();
+				e.setCourseId(domain.getCourseId());
+				e.setEnable(true);
+				e.setRootOrgId(domain.getRootOrgId());
+			}
+			e.setName(domain.getName());
+			e.setPaperIds(domain.getPaperIds());
+			e.setPaperStructType(domain.getPaperStructType());
+			e.setPaperStructId(domain.getPaperStructId());
+			e.setPaperType(domain.getPaperType());
+			randomPaperRepo.save(e);
+			randomPaperQuestionRepo.deleteByRandomPaperId(e.getId());
+			List<RandomPaperQuestion> rqs = new ArrayList<>();
+			for (StructQuestionInfo si : ret.getStructQuestionInfo()) {
+				for (RandomPaperQuestionDto dto : si.getHardInfo().getQuestionInfo()) {
+					addRqs(rqs, dto, e);
+				}
+				for (RandomPaperQuestionDto dto : si.getMediumInfo().getQuestionInfo()) {
+					addRqs(rqs, dto, e);
+				}
+				for (RandomPaperQuestionDto dto : si.getEasyInfo().getQuestionInfo()) {
+					addRqs(rqs, dto, e);
+				}
+			}
+			randomPaperQuestionRepo.saveAll(rqs);
+			String key = CacheConstants.CACHE_Q_RANDOM_PAPER + e.getId();
+			redisClient.delete(key);
+		}
+		clearQuestionIds(ret);
+		return ret;
+	}
+
+	private void addRqs(List<RandomPaperQuestion> rqs, RandomPaperQuestionDto dto, RandomPaper e) {
+		if (CollectionUtils.isNotEmpty(dto.getQuestionDtos())) {
+			for (QuestionDto qdto : dto.getQuestionDtos()) {
+				RandomPaperQuestion rq = new RandomPaperQuestion();
+				rqs.add(rq);
+				rq.setCourseId(e.getCourseId());
+				rq.setKey(dto.getDetailNumber() + "-" + dto.getKey());
+				rq.setQuestionId(qdto.getId());
+				rq.setRandomPaperId(e.getId());
+				rq.setRootOrgId(e.getRootOrgId());
+				rq.setScore(dto.getUnitScore());
+				rq.setQuestionType(qdto.getQuestionType());
+				rq.setAnswerType(qdto.getAnswerType());
+				if (CollectionUtils.isNotEmpty(qdto.getQuesOptions())) {
+					rq.setOptionCount(qdto.getQuesOptions().size());
+				}
+				if (QuesStructType.NESTED_ANSWER_QUESTION.equals(rq.getQuestionType())
+						&& CollectionUtils.isNotEmpty(qdto.getSubQuestions())) {
+					List<RandomPaperQuestion> subQuestion = new ArrayList<>();
+					rq.setSubQuestions(subQuestion);
+					List<Double> subScores = getSubScoreList(rq.getScore(), qdto.getSubQuestions().size());
+					int i = 0;
+					for (QuestionDto subQd : qdto.getSubQuestions()) {
+						RandomPaperQuestion subrq = new RandomPaperQuestion();
+						subQuestion.add(subrq);
+						subrq.setCourseId(e.getCourseId());
+						subrq.setScore(subScores.get(i));
+						i++;
+						subrq.setQuestionType(subQd.getQuestionType());
+						subrq.setAnswerType(subQd.getAnswerType());
+						if (CollectionUtils.isNotEmpty(subQd.getQuesOptions())) {
+							subrq.setOptionCount(subQd.getQuesOptions().size());
+						}
+					}
+				}
+			}
+		}
+	}
+
+	private List<Double> getSubScoreList(double totalScore, int count) {
+		List<Double> scoreList = new ArrayList<>();
+		if (count > 0) {
+			int baseScore = (int) (totalScore / count);
+			double leftScore = totalScore;
+			for (int i = 0; i < count; i++) {
+				scoreList.add((double) baseScore);
+				leftScore -= baseScore;
+			}
+			if (leftScore > 0) {
+				scoreList.set(count - 1, baseScore + leftScore);
+			}
+			return scoreList;
+		}
+		return null;
+	}
+
+	@Override
+	public RandomPaperListVo getInfo(String id) {
+		RandomPaperListVo vo = this.mongoTemplate.findById(id, RandomPaperListVo.class, "randomPaper");
+		CourseCacheBean course = CacheHelper.getCourse(vo.getCourseId());
+		vo.setCourseCode(course.getCode());
+		vo.setCourseName(course.getName());
+		vo.setPaperStructTypeStr(vo.getPaperStructType().getName());
+		PaperStruct paperStruct = Model.of(paperStructRepo.findById(vo.getPaperStructId()));
+		vo.setPaperStructName(paperStruct.getName());
+		vo.setEnableStr(vo.getEnable() ? "启用" : "禁用");
+		GetUserReq ureq = new GetUserReq();
+		ureq.setUserId(vo.getUpdateBy());
+		GetUserResp ures = userCloudService.getUser(ureq);
+		vo.setUpdateByName(ures.getUserBean().getDisplayName());
+		Query query = new Query();
+		List<Object> ids = new ArrayList<>();
+		for (String pid : vo.getPaperIds()) {
+			if (pid.length() > 24) {
+				ids.add(pid);
+			} else {
+				ids.add(new ObjectId(pid));
+			}
+		}
+		query.addCriteria(Criteria.where("id").in(ids));
+		List<PaperVo> papers = this.mongoTemplate.find(query, PaperVo.class, "paper");
+		vo.setPapers(papers);
+		return vo;
+	}
+
+	@Override
+	public DefaultPaper getRandomPaper(String randomPaperId, Integer playTime) {
+		long start = System.currentTimeMillis();
+		RandomPaperCache rp = this.getRandomPaperTemplateCacheById(randomPaperId);
+		long start2 = System.currentTimeMillis();
+		log.warn("获取抽卷模板! 耗时:{}ms ID:{} 题数量:{}", start2 - start, randomPaperId, rp.getQuestionMap().size());
+
+		PaperStructCache ps = paperStructService.getPaperStructCacheById(rp.getPaperStructId());
+		log.warn("获取组卷结构! 耗时:{}ms 结构类型:{} ID:{}", System.currentTimeMillis() - start2, ps.getPaperStrucType(),
+				rp.getPaperStructId());
+
+		CreateDefaultPaperParam param = new CreateDefaultPaperParam();
+		param.setFullyObjective(true);
+		param.setRp(rp);
+		param.setPlayTime(playTime);
+		DefaultPaper paper = new DefaultPaper();
+		paper.setName(rp.getName());
+		List<DefaultQuestionGroup> details = new ArrayList<>();
+		paper.setQuestionGroupList(details);
+
+		if (PaperStructType.BLUEPRINT.equals(ps.getPaperStrucType())) {
+			if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
+				int detailNumber = 0;
+				for (PaperDetailStructCache ds : ps.getPaperDetailStructs()) {
+					DefaultQuestionGroup detail = new DefaultQuestionGroup();
+					details.add(detail);
+					detail.setGroupName(ds.getName());
+					detail.setGroupScore(ds.getTotalScore());
+					List<DefaultQuestionStructureWrapper> units = new ArrayList<>();
+					detail.setQuestionWrapperList(units);
+					detailNumber++;
+					param.setUnits(units);
+					param.setDetailNumber(detailNumber);
+					for (CoursePropertyNumberDto cp : ds.getCoursePropertyNumberDtos()) {
+						if (!cp.getDisable()) {
+							param.setCp(cp);
+							createUnitByBlueProp(param);
+						}
+					}
+				}
+			}
+		} else if (PaperStructType.EXACT.equals(ps.getPaperStrucType())) {
+			if (CollectionUtils.isNotEmpty(ps.getPaperDetailStructs())) {
+				int detailNumber = 0;
+				for (PaperDetailStructCache ds : ps.getPaperDetailStructs()) {
+					DefaultQuestionGroup detail = new DefaultQuestionGroup();
+					details.add(detail);
+					detail.setGroupName(ds.getName());
+					detail.setGroupScore(ds.getTotalScore());
+					List<DefaultQuestionStructureWrapper> units = new ArrayList<>();
+					detail.setQuestionWrapperList(units);
+					detailNumber++;
+					param.setUnits(units);
+					param.setDetailNumber(detailNumber);
+					int index = 0;
+					for (PaperDetailUnitStructDto us : ds.getUnitStructs()) {
+						index++;
+						param.setIndex(index);
+						param.setUs(us);
+						createUnitByExact(param);
+					}
+				}
+			}
+		}
+		paper.setFullyObjective(param.getFullyObjective());
+
+		log.warn("抽卷完成! 总耗时:{}ms ID:{}", System.currentTimeMillis() - start, randomPaperId);
+		return paper;
+	}
+
+	private void createUnitByExact(CreateDefaultPaperParam param) {
+		PaperDetailUnitStructDto us = param.getUs();
+		if (us.getNoPublicDifficulty() > 0) {
+			param.setUnitCount(us.getNoPublicDifficulty());
+			String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + false + "-"
+					+ QuestionDifficulty.HARD.getName();
+			param.setKey(key);
+			createUnit(param);
+		}
+		if (us.getPublicDifficulty() > 0) {
+			param.setUnitCount(us.getPublicDifficulty());
+			String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + true + "-"
+					+ QuestionDifficulty.HARD.getName();
+			param.setKey(key);
+			createUnit(param);
+		}
+
+		if (us.getNoPublicMedium() > 0) {
+			param.setUnitCount(us.getNoPublicMedium());
+			String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + false + "-"
+					+ QuestionDifficulty.MEDIUM.getName();
+			param.setKey(key);
+			createUnit(param);
+		}
+		if (us.getPublicMedium() > 0) {
+			param.setUnitCount(us.getPublicMedium());
+			String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + true + "-"
+					+ QuestionDifficulty.MEDIUM.getName();
+			param.setKey(key);
+			createUnit(param);
+		}
+		if (us.getNoPublicSimple() > 0) {
+			param.setUnitCount(us.getNoPublicSimple());
+			String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + false + "-"
+					+ QuestionDifficulty.EASY.getName();
+			param.setKey(key);
+			createUnit(param);
+		}
+		if (us.getPublicSimple() > 0) {
+			param.setUnitCount(us.getPublicSimple());
+			String key = param.getDetailNumber() + "-" + param.getIndex() + "-" + true + "-"
+					+ QuestionDifficulty.EASY.getName();
+			param.setKey(key);
+			createUnit(param);
+		}
+	}
+
+	private void createUnitByBlueProp(CreateDefaultPaperParam param) {
+		CoursePropertyNumberDto cp = param.getCp();
+		if (cp.getNoPublicDifficulty() > 0) {
+			String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
+					cp.getPropertyId(), false, QuestionDifficulty.HARD.getName());
+			param.setKey(key);
+			param.setUnitCount(cp.getNoPublicDifficulty());
+			createUnit(param);
+		}
+		if (cp.getPublicDifficulty() > 0) {
+			String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
+					cp.getPropertyId(), true, QuestionDifficulty.HARD.getName());
+			param.setKey(key);
+			param.setUnitCount(cp.getPublicDifficulty());
+			createUnit(param);
+		}
+
+		if (cp.getNoPublicMedium() > 0) {
+			String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
+					cp.getPropertyId(), false, QuestionDifficulty.MEDIUM.getName());
+			param.setKey(key);
+			param.setUnitCount(cp.getNoPublicMedium());
+			createUnit(param);
+		}
+		if (cp.getPublicMedium() > 0) {
+			String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
+					cp.getPropertyId(), true, QuestionDifficulty.MEDIUM.getName());
+			param.setKey(key);
+			param.setUnitCount(cp.getPublicMedium());
+			createUnit(param);
+		}
+		if (cp.getNoPublicSimple() > 0) {
+			String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
+					cp.getPropertyId(), false, QuestionDifficulty.EASY.getName());
+			param.setKey(key);
+			param.setUnitCount(cp.getNoPublicSimple());
+			createUnit(param);
+		}
+		if (cp.getPublicSimple() > 0) {
+			String key = param.getDetailNumber() + "-" + bulidPropertyGroupByBlueStruct(cp.getPropertyParentId(),
+					cp.getPropertyId(), true, QuestionDifficulty.EASY.getName());
+			param.setKey(key);
+			param.setUnitCount(cp.getPublicSimple());
+			createUnit(param);
+		}
+	}
+
+	private void createUnit(CreateDefaultPaperParam param) {
+		List<RandomPaperQuestion> rpqs = param.getRp().getQuestionMap().get(param.getKey());
+		Collections.shuffle(rpqs);
+		for (int i = 0; i < param.getUnitCount(); i++) {
+			RandomPaperQuestion rpq = rpqs.get(i);
+			DefaultQuestionStructureWrapper qw = new DefaultQuestionStructureWrapper();
+			param.getUnits().add(qw);
+			qw.setLimitedPlayTimes(param.getPlayTime());
+			qw.setPlayedTimes(0);
+			qw.setQuestionId(rpq.getQuestionId());
+			qw.setQuestionScore(rpq.getScore());
+			List<DefaultQuestionUnitWrapper> qList = new ArrayList<>();
+			qw.setQuestionUnitWrapperList(qList);
+			if (QuesStructType.NESTED_ANSWER_QUESTION.equals(rpq.getQuestionType())) {
+				for (RandomPaperQuestion sub : rpq.getSubQuestions()) {
+					DefaultQuestionUnitWrapper q = new DefaultQuestionUnitWrapper();
+					qList.add(q);
+					q.setAnswerType(sub.getAnswerType());
+					q.setOptionPermutation(getOption(sub.getOptionCount()));
+					q.setQuestionScore(sub.getScore());
+					q.setQuestionType(getByOldType(sub.getQuestionType()));
+					if (!PaperUtil.isObjecttive(sub.getQuestionType())) {
+						param.setFullyObjective(false);
+					}
+				}
+			} else {
+				DefaultQuestionUnitWrapper q = new DefaultQuestionUnitWrapper();
+				qList.add(q);
+				q.setAnswerType(rpq.getAnswerType());
+				q.setOptionPermutation(getOption(rpq.getOptionCount()));
+				q.setQuestionScore(rpq.getScore());
+				q.setQuestionType(getByOldType(rpq.getQuestionType()));
+				if (!PaperUtil.isObjecttive(rpq.getQuestionType())) {
+					param.setFullyObjective(false);
+				}
+			}
+		}
+	}
+
+	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;
+	}
+
+	private Integer[] getOption(Integer count) {
+		if (count == null) {
+			return null;
+		}
+		Integer[] ret = new Integer[count];
+		for (int i = 0; i < count; i++) {
+			ret[i] = i;
+		}
+		return ret;
+	}
+
+	private RandomPaperCache getRandomPaperTemplateCacheById(String randomPaperId) {
+		// 抽卷模板 优先从本地缓存中获取
+		String key = CacheConstants.CACHE_Q_RANDOM_PAPER + randomPaperId;
+		RandomPaperCache rp = localRandomPaperCache.getIfPresent(key);
+		if (rp != null) {
+			log.warn("从【本地缓存】中获取抽卷模板! key:{}", key);
+			return rp;
+		}
+
+		// 从redis缓存中获取
+		rp = redisClient.get(key, RandomPaperCache.class, cacheTimeOut);
+		if (rp != null) {
+			localRandomPaperCache.put(key, rp);
+			log.warn("从【Redis缓存】中获取抽卷模板! key:{}", key);
+			return rp;
+		}
+
+		// 从数据库中获取
+		RandomPaper entity = Model.of(randomPaperRepo.findById(randomPaperId));
+		if (entity == null) {
+			throw new StatusException("未找到随机模板:" + randomPaperId);
+		}
+
+		List<RandomPaperQuestion> rpQuestions = randomPaperQuestionRepo.findByRandomPaperId(randomPaperId);
+		if (CollectionUtils.isEmpty(rpQuestions)) {
+			throw new StatusException("随机模板试题库为空:" + randomPaperId);
+		}
+
+		log.warn("从【数据库】中获取抽卷模板! key:{}", key);
+		rp = new RandomPaperCache();
+		rp.setName(entity.getName());
+		rp.setPaperStructId(entity.getPaperStructId());
+
+		Map<String, List<RandomPaperQuestion>> map = new HashMap<>();
+		for (RandomPaperQuestion rpq : rpQuestions) {
+			List<RandomPaperQuestion> list = map.get(rpq.getKey());
+			if (list == null) {
+				list = new ArrayList<>();
+				map.put(rpq.getKey(), list);
+			}
+			list.add(rpq);
+		}
+		rp.setQuestionMap(map);
+
+		redisClient.set(key, rp, cacheTimeOut);
+		localRandomPaperCache.put(key, rp);
+		return rp;
+	}
+
+	@Override
+	public boolean existStruct(String paperStructId) {
+		Query query = new Query();
+		query.addCriteria(Criteria.where("paperStructId").is(paperStructId));
+		RandomPaper rp = mongoTemplate.findOne(query, RandomPaper.class);
+		return rp != null;
+	}
+
+	@Override
+	public boolean existPaper(Long courseId, String paperId) {
+		Query query = new Query();
+		query.addCriteria(Criteria.where("courseId").is(courseId));
+		List<RandomPaper> rps = mongoTemplate.find(query, RandomPaper.class);
+		if (CollectionUtils.isEmpty(rps)) {
+			return false;
+		}
+		for (RandomPaper rp : rps) {
+			if (rp.getPaperIds().contains(paperId)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public List<RandomPaperListVo> getList(Long rootOrgId, Long courseId) {
+		List<Criteria> cs = new ArrayList<>();
+		cs.add(Criteria.where("rootOrgId").is(rootOrgId));
+
+		cs.add(Criteria.where("enable").is(true));
+
+		cs.add(Criteria.where("courseId").is(courseId));
+		Criteria and = new Criteria();
+		Criteria[] cas = new Criteria[cs.size()];
+		and.andOperator(cs.toArray(cas));
+		Query query = Query.query(and);
+
+		List<RandomPaperListVo> paperList = this.mongoTemplate.find(query, RandomPaperListVo.class, "randomPaper");
+
+		return paperList;
+	}
 }