xiatian пре 2 година
родитељ
комит
d3094835a9
17 измењених фајлова са 1362 додато и 26 уклоњено
  1. 43 6
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/RandomPaperController.java
  2. 0 3
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/enums/PaperType.java
  3. 2 0
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/RandomPaperQuestionRepo.java
  4. 2 0
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/RandomPaperRepo.java
  5. 5 6
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/RandomPaperQuestion.java
  6. 15 2
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/RandomPaperService.java
  7. 289 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/PaperDetailUnitDto.java
  8. 129 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/QuestionDto.java
  9. 99 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/RandomPaperDomain.java
  10. 1 1
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/RandomPaperListVo.java
  11. 1 1
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/RandomPaperQuery.java
  12. 38 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/RandomPaperQuestionDto.java
  13. 49 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/StructInfo.java
  14. 60 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/StructQuestionCountInfo.java
  15. 67 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/StructQuestionInfo.java
  16. 2 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/RandomPaperQuestionServiceImpl.java
  17. 560 7
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/RandomPaperServiceImpl.java

+ 43 - 6
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/RandomPaperController.java

@@ -1,25 +1,33 @@
 package cn.com.qmth.examcloud.core.questions.api.controller;
 
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 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.base.bean.randompaper.RandomPaperListVo;
-import cn.com.qmth.examcloud.core.questions.base.bean.randompaper.RandomPaperQuery;
 import cn.com.qmth.examcloud.core.questions.service.RandomPaperService;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.RandomPaperDomain;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.RandomPaperListVo;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.RandomPaperQuery;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.StructInfo;
 import cn.com.qmth.examcloud.web.security.DataRule;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 
 @RestController
+@Api(tags = "随机组卷模板")
 @RequestMapping("${api_cqb}/randompaper")
 public class RandomPaperController extends ControllerSupport {
-	
+
 	@Autowired
 	private RandomPaperService randomPaperService;
 
@@ -28,9 +36,38 @@ public class RandomPaperController extends ControllerSupport {
 	@PostMapping("page")
 	public Page<RandomPaperListVo> getPage(RandomPaperQuery req) {
 		User user = getAccessUser();
-        UserDataRule ud = super.getUserDataRule(DataRuleType.COURSE);
-        req.setUd(ud);
-        req.setRootOrgId(user.getRootOrgId());
+		UserDataRule ud = super.getUserDataRule(DataRuleType.COURSE);
+		req.setUd(ud);
+		req.setRootOrgId(user.getRootOrgId());
 		return randomPaperService.getPage(req);
 	}
+
+	@ApiOperation(value = "启用、禁用")
+	@PostMapping("toggle")
+	public void toggle(@RequestParam String id, @RequestParam Boolean enable) {
+		randomPaperService.toggle(id, enable, getAccessUser());
+	}
+
+	@ApiOperation(value = "获取组卷结构试题信息")
+	@PostMapping("struct/question/info")
+	public StructInfo getStructQuestionInfo(@RequestParam String structId) {
+		return randomPaperService.getStructQuestionInfo(structId);
+	}
+
+	@ApiOperation(value = "选中试卷范围预览")
+	@PostMapping("struct/question/view/info")
+	public StructInfo getPaperQuestionViewInfo(@RequestParam String structId, @RequestParam List<String> paperIds) {
+		return randomPaperService.getPaperQuestionViewInfo(structId,paperIds);
+	}
+	
+	@ApiOperation(value = "保存模板")
+	@PostMapping("save")
+	public StructInfo save(RandomPaperDomain domain) {
+		if (StringUtils.isBlank(domain.getId())) {
+			domain.setRootOrgId(getAccessUser().getRootOrgId());
+		}
+		return randomPaperService.saveRandomPaper(domain);
+	}
+	
+	
 }

+ 0 - 3
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/enums/PaperType.java

@@ -37,8 +37,5 @@ public enum PaperType {
         return name;
     }
 
-    public String toString() {
-        return getName();
-    }
 
 }

+ 2 - 0
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/RandomPaperQuestionRepo.java

@@ -8,4 +8,6 @@ import cn.com.qmth.examcloud.core.questions.dao.entity.RandomPaperQuestion;
 public interface RandomPaperQuestionRepo
 		extends MongoRepository<RandomPaperQuestion, String>, QueryByExampleExecutor<RandomPaperQuestion> {
 
+	void deleteByRandomPaperId(String id);
+
 }

+ 2 - 0
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/RandomPaperRepo.java

@@ -8,4 +8,6 @@ import cn.com.qmth.examcloud.core.questions.dao.entity.RandomPaper;
 public interface RandomPaperRepo
 		extends MongoRepository<RandomPaper, String>, QueryByExampleExecutor<RandomPaper> {
 
+	RandomPaper findByRootOrgIdAndName(Long rootOrgId, String name);
+
 }

+ 5 - 6
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/RandomPaperQuestion.java

@@ -2,7 +2,6 @@ package cn.com.qmth.examcloud.core.questions.dao.entity;
 
 import java.util.List;
 
-import cn.com.qmth.examcloud.core.questions.base.enums.QuestionDifficulty;
 import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
 
 public class RandomPaperQuestion extends MongoBaseEntity {
@@ -18,7 +17,7 @@ public class RandomPaperQuestion extends MongoBaseEntity {
 
 	private Long courseId;
 
-	private QuestionDifficulty questionDifficulty;
+	private Integer detailNumber;
 
 	private String key;
 
@@ -64,12 +63,12 @@ public class RandomPaperQuestion extends MongoBaseEntity {
 		this.courseId = courseId;
 	}
 
-	public QuestionDifficulty getQuestionDifficulty() {
-		return questionDifficulty;
+	public Integer getDetailNumber() {
+		return detailNumber;
 	}
 
-	public void setQuestionDifficulty(QuestionDifficulty questionDifficulty) {
-		this.questionDifficulty = questionDifficulty;
+	public void setDetailNumber(Integer detailNumber) {
+		this.detailNumber = detailNumber;
 	}
 
 }

+ 15 - 2
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/RandomPaperService.java

@@ -1,13 +1,26 @@
 package cn.com.qmth.examcloud.core.questions.service;
 
+import java.util.List;
+
 import org.springframework.data.domain.Page;
 
-import cn.com.qmth.examcloud.core.questions.base.bean.randompaper.RandomPaperListVo;
-import cn.com.qmth.examcloud.core.questions.base.bean.randompaper.RandomPaperQuery;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.RandomPaperDomain;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.RandomPaperListVo;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.RandomPaperQuery;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.StructInfo;
 
 public interface RandomPaperService {
 
 	Page<RandomPaperListVo> getPage(RandomPaperQuery query);
 
+	void toggle(String id, Boolean enable, User accessUser);
+
+	StructInfo getStructQuestionInfo(String structId);
+
+	StructInfo saveRandomPaper(RandomPaperDomain domain);
+
+	StructInfo getPaperQuestionViewInfo(String structId, List<String> paperIds);
+
 
 }

+ 289 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/PaperDetailUnitDto.java

@@ -0,0 +1,289 @@
+package cn.com.qmth.examcloud.core.questions.service.bean.randompaper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.data.mongodb.core.mapping.DBRef;
+
+import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
+import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+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.base.MongoBaseEntity;
+
+public class PaperDetailUnitDto extends MongoBaseEntity {
+
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 266448283780973578L;
+
+	@DBRef(lazy = false)
+    private Paper paper;// 关联的试卷
+
+    private Integer number;// 小题序号
+
+    private Double score;// 小题分数
+
+    private List<Double> subScoreList;// 对应套题的情况下,各子题的分数分布
+
+    @DBRef(lazy = false)
+    private PaperDetail paperDetail;// 关联的大题
+
+    private QuesStructType questionType;// 小题类型
+
+    @DBRef(lazy = false)
+    private QuestionDto question;// 关联试题
+
+    private String creator;// 创建人id
+
+    private String createTime;// 创建时间
+
+    private PaperType paperType;
+    //复制数据用
+    private Long orgId;
+    /**
+     * 作答限时 K12
+     */
+    private Long timeLimit;
+
+    public PaperDetailUnitDto() {
+    }
+
+    public PaperDetailUnitDto(Paper paper, PaperDetail paperDetail, QuestionDto question) {
+        this.paper = paper;
+        this.questionType = question.getQuestionType();
+        this.question = question;
+        this.createTime = CommonUtils.getCurDateTime();
+        this.paperDetail = paperDetail;
+        this.score = 0d;
+    }
+
+    public Paper getPaper() {
+        return paper;
+    }
+
+    public void setPaper(Paper paper) {
+        this.paper = paper;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public Double getScore() {
+        return score;
+    }
+
+    public void setScore(Double score) {
+        this.score = score;
+        // 非套题或试题未设置时,不做额外处理
+        if (questionType != QuesStructType.NESTED_ANSWER_QUESTION || question == null) {
+            return;
+        }
+        // 当前分数设置为空时,同时设置子题分数为空
+        if (score == null) {
+            subScoreList = null;
+            return;
+        }
+        // 总分与子题分数之和相同,则不做额外处理
+        if (subScoreList != null && subScoreList.size() > 0 && getSubScoreSum() == score) {
+            return;
+        }
+        // 根据试题与总分设置情况重置子题分数
+        if (question.getSubQuestions() != null) {
+            initSubScoreList(score, question.getSubQuestions());
+        }
+    }
+
+    public PaperDetail getPaperDetail() {
+        return paperDetail;
+    }
+
+    public void setPaperDetail(PaperDetail paperDetail) {
+        this.paperDetail = paperDetail;
+    }
+
+    public QuesStructType getQuestionType() {
+        return questionType;
+    }
+
+    public void setQuestionType(QuesStructType questionType) {
+        this.questionType = questionType;
+    }
+
+    public QuestionDto getQuestion() {
+        return question;
+    }
+
+    public void setQuestion(QuestionDto question) {
+        this.question = question;
+        // 非套题或总分未设置时,不做额外处理
+        if (questionType != QuesStructType.NESTED_ANSWER_QUESTION || score == null) {
+            return;
+        }
+        // 当前试题或子题列表为空时,同时设置子题分数为空
+        if (question == null || question.getSubQuestions() == null) {
+            subScoreList = null;
+            return;
+        }
+        // 当前试题子题数量与子题分数数量相同,不做额外处理
+        if (subScoreList != null && subScoreList.size() == question.getSubQuestions().size()) {
+            return;
+        }
+        // 根据试题与总分设置情况重置子题分数
+        initSubScoreList(score, question.getSubQuestions());
+    }
+
+    public String getCreator() {
+        return creator;
+    }
+
+    public void setCreator(String creator) {
+        this.creator = creator;
+    }
+
+    public String getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(String createTime) {
+        this.createTime = createTime;
+    }
+
+    public List<Double> getSubScoreList() {
+        return subScoreList;
+    }
+
+
+    public PaperType getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(PaperType paperType) {
+        if (paper != null) {
+            this.paperType = paper.getPaperType();
+        } else {
+            this.paperType = paperType;
+        }
+    }
+
+    public void setSubScoreList(List<Double> subScoreList) {
+        this.subScoreList = subScoreList;
+        if (subScoreList != null) {
+            double totalScore = 0;
+            for (Double score : subScoreList) {
+                if (score == null) {
+                    score = 0d;
+                }
+                totalScore += score;
+            }
+            this.score = CommonUtils.formatDouble(totalScore);
+        }
+    }
+
+    public void setSubScoreListNew(List<Double> subScoreList) {
+        this.subScoreList = subScoreList;
+    }
+
+    public Long getTimeLimit() {
+        return timeLimit;
+    }
+
+    public void setTimeLimit(Long timeLimit) {
+        this.timeLimit = timeLimit;
+    }
+
+
+    /**
+     * 根据预设总分自动拆解套题子题的分数分布 目前采取均分后累加到最后一题的做法
+     *
+     * @param totalScore
+     * @param questionList
+     */
+    private void initSubScoreList(double totalScore, List<QuestionDto> questionList) {
+        List<Double> scoreList = new ArrayList<>();
+        int count = questionList.size();
+        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);
+            }
+            this.subScoreList = scoreList;
+        }
+    }
+
+    private double getSubScoreSum() {
+        double totalScore = 0;
+        if (subScoreList != null) {
+            for (Double score : subScoreList) {
+                totalScore += score;
+            }
+        }
+        return CommonUtils.formatDouble(totalScore);
+    }
+
+    public int getDetailNumber() {
+    	if(this.paperDetail==null) {
+    		return -1;
+    	}
+        return this.paperDetail.getNumber();
+    }
+
+    public String getDetailId() {
+    	if(this.paperDetail==null) {
+    		return null;
+    	}
+        return this.paperDetail.getId();
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((question == null) ? 0 : question.getId().hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        PaperDetailUnitDto other = (PaperDetailUnitDto) obj;
+        if (question == null) {
+            if (other.question != null) {
+                return false;
+            }
+        } else if (!question.getId().equals(other.question.getId())) {
+            return false;
+        }
+        return true;
+    }
+
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+
+}

+ 129 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/QuestionDto.java

@@ -0,0 +1,129 @@
+package cn.com.qmth.examcloud.core.questions.service.bean.randompaper;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Course;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuesProperty;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
+
+public class QuestionDto extends MongoBaseEntity {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 448493904015571457L;
+
+	private QuesStructType questionType;// 试题结构类型
+
+	private Course course;
+
+	private String orgId; // 机构ID
+
+	private String quesName;// 来源大题
+
+	private Integer number;
+
+	private List<QuestionDto> subQuestions;// 子题目,用于套题
+	/**
+	 * 试题属性
+	 */
+	private List<QuesProperty> quesProperties;// 关联多组属性
+
+	private Double difficultyDegree; // 难度系数
+
+	private Boolean publicity; // 公开度
+
+	private String difficulty;// 难度
+
+	private List<String> propertyGroup;// 试题属性组合(蓝图组卷使用)
+
+	public QuesStructType getQuestionType() {
+		return questionType;
+	}
+
+	public void setQuestionType(QuesStructType questionType) {
+		this.questionType = questionType;
+	}
+
+	public Course getCourse() {
+		return course;
+	}
+
+	public void setCourse(Course course) {
+		this.course = course;
+	}
+
+	public String getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(String orgId) {
+		this.orgId = orgId;
+	}
+
+	public String getQuesName() {
+		return quesName;
+	}
+
+	public void setQuesName(String quesName) {
+		this.quesName = quesName;
+	}
+
+	public Integer getNumber() {
+		return number;
+	}
+
+	public void setNumber(Integer number) {
+		this.number = number;
+	}
+
+	public List<QuesProperty> getQuesProperties() {
+		return quesProperties;
+	}
+
+	public void setQuesProperties(List<QuesProperty> quesProperties) {
+		this.quesProperties = quesProperties;
+	}
+
+	public Double getDifficultyDegree() {
+		return difficultyDegree;
+	}
+
+	public void setDifficultyDegree(Double difficultyDegree) {
+		this.difficultyDegree = difficultyDegree;
+	}
+
+	public Boolean getPublicity() {
+		return publicity;
+	}
+
+	public void setPublicity(Boolean publicity) {
+		this.publicity = publicity;
+	}
+
+	public String getDifficulty() {
+		return difficulty;
+	}
+
+	public void setDifficulty(String difficulty) {
+		this.difficulty = difficulty;
+	}
+
+	public List<String> getPropertyGroup() {
+		return propertyGroup;
+	}
+
+	public void setPropertyGroup(List<String> propertyGroup) {
+		this.propertyGroup = propertyGroup;
+	}
+
+	public List<QuestionDto> getSubQuestions() {
+		return subQuestions;
+	}
+
+	public void setSubQuestions(List<QuestionDto> subQuestions) {
+		this.subQuestions = subQuestions;
+	}
+
+}

+ 99 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/RandomPaperDomain.java

@@ -0,0 +1,99 @@
+package cn.com.qmth.examcloud.core.questions.service.bean.randompaper;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.questions.base.enums.PaperStructType;
+import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
+
+public class RandomPaperDomain {
+	private String id;
+
+	private Long rootOrgId;
+
+	private Long courseId;
+
+	private String name;
+
+	private PaperStructType paperStructType;
+
+	private String paperStructId;
+
+	private PaperType paperType;
+
+	private List<String> paperIds;
+
+	private Boolean enable;
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Long getCourseId() {
+		return courseId;
+	}
+
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public PaperStructType getPaperStructType() {
+		return paperStructType;
+	}
+
+	public void setPaperStructType(PaperStructType paperStructType) {
+		this.paperStructType = paperStructType;
+	}
+
+	public String getPaperStructId() {
+		return paperStructId;
+	}
+
+	public void setPaperStructId(String paperStructId) {
+		this.paperStructId = paperStructId;
+	}
+
+	public PaperType getPaperType() {
+		return paperType;
+	}
+
+	public void setPaperType(PaperType paperType) {
+		this.paperType = paperType;
+	}
+
+	public List<String> getPaperIds() {
+		return paperIds;
+	}
+
+	public void setPaperIds(List<String> paperIds) {
+		this.paperIds = paperIds;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+}

+ 1 - 1
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/bean/randompaper/RandomPaperListVo.java → examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/RandomPaperListVo.java

@@ -1,4 +1,4 @@
-package cn.com.qmth.examcloud.core.questions.base.bean.randompaper;
+package cn.com.qmth.examcloud.core.questions.service.bean.randompaper;
 
 import java.util.Date;
 

+ 1 - 1
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/bean/randompaper/RandomPaperQuery.java → examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/RandomPaperQuery.java

@@ -1,4 +1,4 @@
-package cn.com.qmth.examcloud.core.questions.base.bean.randompaper;
+package cn.com.qmth.examcloud.core.questions.service.bean.randompaper;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.core.questions.base.bean.PagerQuery;

+ 38 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/RandomPaperQuestionDto.java

@@ -0,0 +1,38 @@
+package cn.com.qmth.examcloud.core.questions.service.bean.randompaper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RandomPaperQuestionDto {
+
+	private Integer detailNumber;
+
+	private String key;
+
+	private List<String> questionIds = new ArrayList<>();
+
+	public String getKey() {
+		return key;
+	}
+
+	public void setKey(String key) {
+		this.key = key;
+	}
+
+	public List<String> getQuestionIds() {
+		return questionIds;
+	}
+
+	public void setQuestionIds(List<String> questionIds) {
+		this.questionIds = questionIds;
+	}
+
+	public Integer getDetailNumber() {
+		return detailNumber;
+	}
+
+	public void setDetailNumber(Integer detailNumber) {
+		this.detailNumber = detailNumber;
+	}
+
+}

+ 49 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/StructInfo.java

@@ -0,0 +1,49 @@
+package cn.com.qmth.examcloud.core.questions.service.bean.randompaper;
+
+import java.util.List;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class StructInfo {
+	@ApiModelProperty("是否有效")
+	private Boolean valid;
+	@ApiModelProperty("满分")
+	private Double totalScore;
+	@ApiModelProperty("难度系数")
+	private Double difficultyDegree;
+	@ApiModelProperty("题数量")
+	private List<StructQuestionInfo> structQuestionInfo;
+
+	public Double getDifficultyDegree() {
+		return difficultyDegree;
+	}
+
+	public void setDifficultyDegree(Double difficultyDegree) {
+		this.difficultyDegree = difficultyDegree;
+	}
+
+	public List<StructQuestionInfo> getStructQuestionInfo() {
+		return structQuestionInfo;
+	}
+
+	public void setStructQuestionInfo(List<StructQuestionInfo> structQuestionInfo) {
+		this.structQuestionInfo = structQuestionInfo;
+	}
+
+	public Double getTotalScore() {
+		return totalScore;
+	}
+
+	public void setTotalScore(Double totalScore) {
+		this.totalScore = totalScore;
+	}
+
+	public Boolean getValid() {
+		return valid;
+	}
+
+	public void setValid(Boolean valid) {
+		this.valid = valid;
+	}
+
+}

+ 60 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/StructQuestionCountInfo.java

@@ -0,0 +1,60 @@
+package cn.com.qmth.examcloud.core.questions.service.bean.randompaper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class StructQuestionCountInfo {
+	@ApiModelProperty("数量")
+	private Integer count=0;
+	@ApiModelProperty("是否有效")
+	private Boolean valid=true;
+	@ApiModelProperty("无效信息")
+	private String invalidMsg;
+	@ApiModelProperty(hidden = true)
+	private List<RandomPaperQuestionDto> questionInfo=new ArrayList<>();
+
+	public Integer getCount() {
+		return count;
+	}
+
+	public void setCount(Integer count) {
+		this.count = count;
+	}
+
+	public Boolean getValid() {
+		return valid;
+	}
+
+	public void setValid(Boolean valid) {
+		this.valid = valid;
+	}
+
+	public String getInvalidMsg() {
+		return invalidMsg;
+	}
+
+	public void setInvalidMsg(String invalidMsg) {
+		this.invalidMsg = invalidMsg;
+	}
+
+	public StructQuestionCountInfo() {
+		super();
+	}
+
+	public StructQuestionCountInfo(Integer count, Boolean valid) {
+		super();
+		this.count = count;
+		this.valid = valid;
+	}
+
+	public List<RandomPaperQuestionDto> getQuestionInfo() {
+		return questionInfo;
+	}
+
+	public void setQuestionInfo(List<RandomPaperQuestionDto> questionInfo) {
+		this.questionInfo = questionInfo;
+	}
+
+}

+ 67 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/randompaper/StructQuestionInfo.java

@@ -0,0 +1,67 @@
+package cn.com.qmth.examcloud.core.questions.service.bean.randompaper;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class StructQuestionInfo {
+	@ApiModelProperty("大题名称")
+	private String detailName;
+	@ApiModelProperty("总分")
+	private Double totalScore;
+	@ApiModelProperty("数量")
+	private Integer totalCount;
+	@ApiModelProperty("难数量")
+	private StructQuestionCountInfo hardInfo=new StructQuestionCountInfo();
+	@ApiModelProperty("中数量")
+	private StructQuestionCountInfo mediumInfo=new StructQuestionCountInfo();
+	@ApiModelProperty("易数量")
+	private StructQuestionCountInfo easyInfo=new StructQuestionCountInfo();
+
+	public String getDetailName() {
+		return detailName;
+	}
+
+	public void setDetailName(String detailName) {
+		this.detailName = detailName;
+	}
+
+	public Double getTotalScore() {
+		return totalScore;
+	}
+
+	public void setTotalScore(Double totalScore) {
+		this.totalScore = totalScore;
+	}
+
+	public Integer getTotalCount() {
+		return totalCount;
+	}
+
+	public void setTotalCount(Integer totalCount) {
+		this.totalCount = totalCount;
+	}
+
+	public StructQuestionCountInfo getHardInfo() {
+		return hardInfo;
+	}
+
+	public void setHardInfo(StructQuestionCountInfo hardInfo) {
+		this.hardInfo = hardInfo;
+	}
+
+	public StructQuestionCountInfo getMediumInfo() {
+		return mediumInfo;
+	}
+
+	public void setMediumInfo(StructQuestionCountInfo mediumInfo) {
+		this.mediumInfo = mediumInfo;
+	}
+
+	public StructQuestionCountInfo getEasyInfo() {
+		return easyInfo;
+	}
+
+	public void setEasyInfo(StructQuestionCountInfo easyInfo) {
+		this.easyInfo = easyInfo;
+	}
+
+}

+ 2 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/RandomPaperQuestionServiceImpl.java

@@ -2,9 +2,11 @@ package cn.com.qmth.examcloud.core.questions.service.impl;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
 
 import cn.com.qmth.examcloud.core.questions.service.RandomPaperQuestionService;
 
+@Service
 public class RandomPaperQuestionServiceImpl implements RandomPaperQuestionService {
 
 	private static final Logger log = LoggerFactory.getLogger(RandomPaperQuestionServiceImpl.class);

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

@@ -1,9 +1,13 @@
 package cn.com.qmth.examcloud.core.questions.service.impl;
 
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
-import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.bson.types.ObjectId;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageImpl;
@@ -12,29 +16,66 @@ import org.springframework.data.domain.Sort;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.basic.api.UserCloudService;
 import cn.com.qmth.examcloud.core.basic.api.request.GetUserReq;
 import cn.com.qmth.examcloud.core.basic.api.response.GetUserResp;
 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.bean.randompaper.RandomPaperListVo;
-import cn.com.qmth.examcloud.core.questions.base.bean.randompaper.RandomPaperQuery;
+import cn.com.qmth.examcloud.core.questions.base.enums.PaperStructType;
 import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
+import cn.com.qmth.examcloud.core.questions.base.enums.QuestionDifficulty;
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+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.PaperStructRepo;
+import cn.com.qmth.examcloud.core.questions.dao.PropertyRepo;
+import cn.com.qmth.examcloud.core.questions.dao.RandomPaperQuestionRepo;
+import cn.com.qmth.examcloud.core.questions.dao.RandomPaperRepo;
 import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailStruct;
 import cn.com.qmth.examcloud.core.questions.dao.entity.PaperStruct;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Property;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuesProperty;
+import cn.com.qmth.examcloud.core.questions.dao.entity.RandomPaper;
+import cn.com.qmth.examcloud.core.questions.dao.entity.RandomPaperQuestion;
+import cn.com.qmth.examcloud.core.questions.dao.entity.dto.CoursePropertyNumberDto;
+import cn.com.qmth.examcloud.core.questions.dao.entity.dto.PaperDetailUnitStructDto;
 import cn.com.qmth.examcloud.core.questions.service.RandomPaperService;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.PaperDetailUnitDto;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.QuestionDto;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.RandomPaperDomain;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.RandomPaperListVo;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.RandomPaperQuery;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.RandomPaperQuestionDto;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.StructInfo;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.StructQuestionCountInfo;
+import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.StructQuestionInfo;
 import cn.com.qmth.examcloud.support.cache.CacheHelper;
 import cn.com.qmth.examcloud.support.cache.bean.CourseCacheBean;
 
+@Service
 public class RandomPaperServiceImpl implements RandomPaperService {
 
 	@Autowired
 	private MongoTemplate mongoTemplate;
 	@Autowired
 	private PaperStructRepo paperStructRepo;
-	
+
+	@Autowired
+	private PropertyRepo propertyRepo;
+	@Autowired
+	private PaperRepo paperRepo;
+	@Autowired
+	private PaperDetailUnitRepo unitRepo;
+	@Autowired
+	private RandomPaperRepo randomPaperRepo;
+	@Autowired
+	private RandomPaperQuestionRepo randomPaperQuestionRepo;
 	@Autowired
 	private UserCloudService userCloudService;
 
@@ -88,14 +129,526 @@ public class RandomPaperServiceImpl implements RandomPaperService {
 			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();
+			vo.setEnableStr(vo.getEnable() ? "启用" : "禁用");
+			GetUserReq ureq = new GetUserReq();
 			ureq.setUserId(vo.getUpdateBy());
-			GetUserResp ures=userCloudService.getUser(ureq);
+			GetUserResp ures = userCloudService.getUser(ureq);
 			vo.setUpdateByName(ures.getUserBean().getDisplayName());
 		}
 
 		return new PageImpl<>(paperList, pageable, total);
 	}
 
+	@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 = unitStruct.getPublicSimple() + unitStruct.getNoPublicSimple();
+							mediumCount = unitStruct.getPublicMedium() + unitStruct.getNoPublicMedium();
+							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 * paperDetailStruct.getScore() * 0.8
+								+ mediumCount * paperDetailStruct.getScore() * 0.5
+								+ difficultyCount * paperDetailStruct.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(String structId, List<String> 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<PaperDetailUnitDto> unitList = new ArrayList<>();
+		List<QuestionDto> questionList = new ArrayList<>();
+		for (String paperId : paperIds) {
+			unitList.addAll(findUnitPaperId(paperId));
+		}
+		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());
+					for (CoursePropertyNumberDto cp : ds.getCoursePropertyNumberDtos()) {
+						if (!cp.getDisable()) {
+							setQuestionInfoByBlue(detailNumber, ds.getQuestionType(), ds.getQuesNames(), sqinfo, cp,
+									questionList);
+						}
+					}
+					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());
+					int index = 0;
+					for (PaperDetailUnitStructDto us : ds.getUnitStructs()) {
+						index++;
+						setQuestionInfoByExact(index, detailNumber, us, sqinfo, questionList);
+					}
+					sqinfo.setTotalCount(sqinfo.getHardInfo().getCount() + sqinfo.getMediumInfo().getCount()
+							+ sqinfo.getEasyInfo().getCount());
+				}
+			}
+		}
+		fillValid(ret);
+		return ret;
+	}
+
+	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.setQuestionIds(new ArrayList<>());
+			}
+			for (RandomPaperQuestionDto dto : si.getMediumInfo().getQuestionInfo()) {
+				dto.setQuestionIds(new ArrayList<>());
+			}
+			for (RandomPaperQuestionDto dto : si.getEasyInfo().getQuestionInfo()) {
+				dto.setQuestionIds(new ArrayList<>());
+			}
+		}
+	}
+
+	private void setQuestionInfoByExact(Integer index, Integer detailNumber, PaperDetailUnitStructDto us,
+			StructQuestionInfo sqinfo, List<QuestionDto> questionList) {
+		StructQuestionCountInfo si = null;
+		if (us.getNoPublicDifficulty() > 0) {
+			si = sqinfo.getHardInfo();
+			setQuestionInfoByExact(index, detailNumber, us.getQuestionType(), us.getQuesNames(), si, false,
+					QuestionDifficulty.HARD.getName(), us.getNoPublicDifficulty(), questionList);
+		}
+		if (us.getPublicDifficulty() > 0) {
+			si = sqinfo.getHardInfo();
+			setQuestionInfoByExact(index, detailNumber, us.getQuestionType(), us.getQuesNames(), si, true,
+					QuestionDifficulty.HARD.getName(), us.getPublicDifficulty(), questionList);
+		}
+
+		if (us.getNoPublicMedium() > 0) {
+			si = sqinfo.getMediumInfo();
+			setQuestionInfoByExact(index, detailNumber, us.getQuestionType(), us.getQuesNames(), si, false,
+					QuestionDifficulty.MEDIUM.getName(), us.getNoPublicMedium(), questionList);
+		}
+		if (us.getPublicMedium() > 0) {
+			si = sqinfo.getMediumInfo();
+			setQuestionInfoByExact(index, detailNumber, us.getQuestionType(), us.getQuesNames(), si, true,
+					QuestionDifficulty.MEDIUM.getName(), us.getPublicMedium(), questionList);
+		}
+		if (us.getNoPublicSimple() > 0) {
+			si = sqinfo.getEasyInfo();
+			setQuestionInfoByExact(index, detailNumber, us.getQuestionType(), us.getQuesNames(), si, false,
+					QuestionDifficulty.EASY.getName(), us.getNoPublicSimple(), questionList);
+		}
+		if (us.getPublicSimple() > 0) {
+			si = sqinfo.getEasyInfo();
+			setQuestionInfoByExact(index, detailNumber, us.getQuestionType(), us.getQuesNames(), si, true,
+					QuestionDifficulty.EASY.getName(), us.getPublicSimple(), questionList);
+		}
+	}
+
+	private void setQuestionInfoByExact(Integer index, Integer detailNumber, QuesStructType st, List<String> quesNames,
+			StructQuestionCountInfo si, Boolean pub, String difficulty, Integer count, List<QuestionDto> questionList) {
+		RandomPaperQuestionDto rq = new RandomPaperQuestionDto();
+		rq.setDetailNumber(detailNumber);
+		rq.setKey(index + "-" + pub + "-" + difficulty);
+		si.getQuestionInfo().add(rq);
+		if (CollectionUtils.isNotEmpty(questionList)) {
+			Iterator<QuestionDto> it = questionList.iterator();
+			while (it.hasNext()) {
+				QuestionDto q = it.next();
+				if (checkExactQuesType(quesNames, st, pub, difficulty, q)) {
+					rq.getQuestionIds().add(q.getId());
+					it.remove();
+				}
+			}
+		}
+		si.setCount(si.getCount() + rq.getQuestionIds().size());
+		if (si.getValid() && count > rq.getQuestionIds().size()) {
+			si.setValid(false);
+			si.setInvalidMsg(getExactErrmsg(index, detailNumber, pub, difficulty));
+		}
+	}
+
+	private void setQuestionInfoByBlue(Integer detailNumber, QuesStructType st, List<String> quesNames,
+			StructQuestionInfo sqinfo, CoursePropertyNumberDto cp, List<QuestionDto> questionList) {
+		StructQuestionCountInfo si = null;
+		if (cp.getNoPublicDifficulty() > 0) {
+			si = sqinfo.getHardInfo();
+			setQuestionInfoByBlueProp(detailNumber, st, quesNames, si, cp.getPropertyParentId(), cp.getPropertyId(),
+					false, QuestionDifficulty.HARD.getName(), cp.getNoPublicDifficulty(), questionList);
+		}
+		if (cp.getPublicDifficulty() > 0) {
+			si = sqinfo.getHardInfo();
+			setQuestionInfoByBlueProp(detailNumber, st, quesNames, si, cp.getPropertyParentId(), cp.getPropertyId(),
+					true, QuestionDifficulty.HARD.getName(), cp.getPublicDifficulty(), questionList);
+		}
+
+		if (cp.getNoPublicMedium() > 0) {
+			si = sqinfo.getMediumInfo();
+			setQuestionInfoByBlueProp(detailNumber, st, quesNames, si, cp.getPropertyParentId(), cp.getPropertyId(),
+					false, QuestionDifficulty.MEDIUM.getName(), cp.getNoPublicMedium(), questionList);
+		}
+		if (cp.getPublicMedium() > 0) {
+			si = sqinfo.getMediumInfo();
+			setQuestionInfoByBlueProp(detailNumber, st, quesNames, si, cp.getPropertyParentId(), cp.getPropertyId(),
+					true, QuestionDifficulty.MEDIUM.getName(), cp.getPublicMedium(), questionList);
+		}
+		if (cp.getNoPublicSimple() > 0) {
+			si = sqinfo.getEasyInfo();
+			setQuestionInfoByBlueProp(detailNumber, st, quesNames, si, cp.getPropertyParentId(), cp.getPropertyId(),
+					false, QuestionDifficulty.EASY.getName(), cp.getNoPublicSimple(), questionList);
+		}
+		if (cp.getPublicSimple() > 0) {
+			si = sqinfo.getEasyInfo();
+			setQuestionInfoByBlueProp(detailNumber, st, quesNames, si, cp.getPropertyParentId(), cp.getPropertyId(),
+					true, QuestionDifficulty.EASY.getName(), cp.getPublicSimple(), questionList);
+		}
+	}
+
+	private void setQuestionInfoByBlueProp(Integer detailNumber, QuesStructType st, List<String> quesNames,
+			StructQuestionCountInfo si, String pproid, String proid, Boolean pub, String difficulty, Integer count,
+			List<QuestionDto> questionList) {
+		RandomPaperQuestionDto rq = new RandomPaperQuestionDto();
+		rq.setDetailNumber(detailNumber);
+		rq.setKey(bulidPropertyGroupByBlueStruct(pproid, proid, pub, difficulty));
+		si.getQuestionInfo().add(rq);
+		if (CollectionUtils.isNotEmpty(questionList)) {
+			Iterator<QuestionDto> it = questionList.iterator();
+			while (it.hasNext()) {
+				QuestionDto q = it.next();
+				if (checkBlueQuesType(quesNames, st, rq.getKey(), q)) {
+					rq.getQuestionIds().add(q.getId());
+					it.remove();
+				}
+			}
+		}
+		si.setCount(si.getCount() + rq.getQuestionIds().size());
+		if (si.getValid() && count > rq.getQuestionIds().size()) {
+			si.setValid(false);
+			si.setInvalidMsg(getBlueErrmsg(detailNumber, pproid, proid, pub, difficulty));
+		}
+	}
+
+	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> findUnitPaperId(String paperId) {
+		Query query = new Query();
+		query.addCriteria(Criteria.where("paper.$id").is(new ObjectId(paperId)));
+		List<PaperDetailUnitDto> units = this.mongoTemplate.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.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);
+		}
+		clearQuestionIds(ret);
+		return ret;
+	}
+
+	private void addRqs(List<RandomPaperQuestion> rqs, RandomPaperQuestionDto dto, RandomPaper e) {
+		if (CollectionUtils.isNotEmpty(dto.getQuestionIds())) {
+			RandomPaperQuestion rq = new RandomPaperQuestion();
+			rqs.add(rq);
+			rq.setCourseId(e.getCourseId());
+			rq.setDetailNumber(dto.getDetailNumber());
+			rq.setKey(dto.getKey());
+			rq.setQuestionIds(dto.getQuestionIds());
+			rq.setRandomPaperId(e.getId());
+			rq.setRootOrgId(e.getRootOrgId());
+		}
+	}
+
 }