소스 검색

手动组卷顺序bug

xiatian 4 달 전
부모
커밋
01a8b01e55

+ 563 - 544
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PaperDetailUnitServiceImpl.java

@@ -1,545 +1,564 @@
-package cn.com.qmth.examcloud.core.questions.service.impl;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.bson.types.ObjectId;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.domain.Sort.Direction;
-import org.springframework.data.domain.Sort.Order;
-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.redis.core.RedisTemplate;
-import org.springframework.stereotype.Service;
-
-import cn.com.qmth.examcloud.api.commons.enums.AdminOperateType;
-import cn.com.qmth.examcloud.api.commons.security.bean.User;
-import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.core.questions.base.Model;
-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.PaperDetailUnitRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
-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.Question;
-import cn.com.qmth.examcloud.core.questions.service.PaperDetailUnitService;
-import cn.com.qmth.examcloud.core.questions.service.PaperService;
-import cn.com.qmth.examcloud.core.questions.service.QuesService;
-import cn.com.qmth.examcloud.core.questions.service.RandomPaperQuestionService;
-import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitDto;
-import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitExp;
-import cn.com.qmth.examcloud.core.questions.service.cache.BasePaperCache;
-import cn.com.qmth.examcloud.core.questions.service.cache.ExtractConfigPaperCache;
-import cn.com.qmth.examcloud.core.questions.service.cache.QuestionAnswerCache;
-import cn.com.qmth.examcloud.core.questions.service.cache.QuestionCache;
-import cn.com.qmth.examcloud.core.questions.service.util.PaperUtil;
-import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
-import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
-
-/**
- * @author chenken
- * @date 2017年9月13日 上午11:29:56
- * @company QMTH
- */
-@Service("paperDetailUnitService")
-public class PaperDetailUnitServiceImpl implements PaperDetailUnitService {
-    @Autowired
-    PaperRepo paperRepo;
-    @Autowired
-    private ExtractConfigPaperCache extractConfigPaperCache;
-    @Autowired
-    private BasePaperCache basePaperCache;
-    @Autowired
-    private QuestionCache questionCache;
-    @Autowired
-    private QuestionAnswerCache questionAnswerCache;
-    
-    
-    @Autowired
-    PaperDetailUnitRepo paperDetailUnitRepo;
-
-    @Autowired
-    QuesService quesService;
-
-    @Autowired
-    PaperService paperService;
-
-    @Autowired
-    MongoTemplate mongoTemplate;
-
-    @Autowired
-    RedisTemplate<String, Object> redisTemplate;
-    @Autowired
-    private RandomPaperQuestionService randomPaperQuestionService;
-    
-    /**
-     * 根据Id获得对应的试题对象
-     *
-     * @param id
-     * @return
-     */
-    public Question getQuestionByPaperDetailUnitId(String id) {
-        return Model.of(paperDetailUnitRepo.findById(id)).getQuestion();
-    }
-
-    /**
-     * 按ID查询小题
-     *
-     * @param id
-     * @return
-     */
-    public PaperDetailUnit findById(String id) {
-        return Model.of(paperDetailUnitRepo.findById(id));
-    }
-    private void checkFillBlankQuestion(Question question) {
-    	if(question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
-    		String body=question.getQuesBody();
-    		Document bodyDoc = Jsoup.parse(body);
-    		if(bodyDoc.body().childrenSize()==0) {
-    			throw new StatusException("1000", "题干不能为空");
-    		}
-	        String bodyText=bodyDoc.body().html();
-	        if(StringUtils.isBlank(bodyText)) {
-	        	throw new StatusException("1001", "题干不能为空");
-	        }
-	        if(bodyText.indexOf("###")==-1) {
-	        	throw new StatusException("1002", "题干不能没有空格(###)");
-	        }
-	        String answer=question.getQuesAnswer();
-	        Document answerDoc = Jsoup.parse(answer);
-	        if(answerDoc.body().childrenSize()!=0) {
-		        String answerText=answerDoc.body().html();
-		        if(StringUtils.isNotBlank(answerText)) {
-		        	if(getSubStringCount(bodyText, "###")!=answerText.split("##").length) {
-		        		throw new StatusException("1003", "题干空格(###)数量和答案数量不一致");
-		        	}
-		        }
-	        }
-    	}
-    }
-    private int getSubStringCount(String src,String find){
-    	int o = 0;
-    	int index=-1;
-    	while((index=src.indexOf(find,index))>-1){
-	    	++index;
-	    	++o;
-    	}
-		return o;
-	}
-    
-    @Override
-    public boolean paperInUse(String questionId) {
-    	 Query query = new Query();
-         query.addCriteria(Criteria.where("question.$id").is(new ObjectId(questionId)));
-         query.addCriteria(Criteria.where("paperType").is(PaperType.GENERATE.name()));
-         List<PaperDetailUnitDto> units=this.mongoTemplate.find(query, PaperDetailUnitDto.class,"paperDetailUnit");
-         if(CollectionUtils.isEmpty(units)) {
-        	 return false;
-         }
-         for(PaperDetailUnitDto dto:units) {
-        	 if(dto.getPaper().getInUse()!=null&&dto.getPaper().getInUse()==1) {
-        		 return true;
-        	 }
-         }
-         return false;
-    }
-    /**
-     * 保存小题
-     */
-    public PaperDetailUnit savePaperDetailUnit(PaperDetailUnitExp updateUnit, User user) {
-    	StringBuilder sb=new StringBuilder();
-        PaperDetailUnit baseUnit = Model.of(paperDetailUnitRepo.findById(updateUnit.getId()));
-        if(paperInUse(baseUnit.getQuestion().getId())) {
-        	PaperUtil.checkUpdate(updateUnit, baseUnit,"试卷已调用,");
-        }
-        if(randomPaperQuestionService.existQuestion(baseUnit.getQuestion().getId())) {
-        	PaperUtil.checkUpdateOption(updateUnit, baseUnit,"小题已被抽题模板使用,");
-        }
-        Question baseQuestion = baseUnit.getQuestion();
-        Question updateQuestion = updateUnit.getQuestion();
-        checkFillBlankQuestion(updateQuestion);
-        if (baseUnit.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
-            if (updateQuestion.getId().equals(baseQuestion.getId())) {
-            	
-            	if(!StringUtils.equals(baseQuestion.getQuesBody(), updateQuestion.getQuesBody())) {
-            		sb.append(" 第"+baseUnit.getNumber()+"小题(套题)题干变动");
-            	}
-                // 更新对象为套题本身
-                quesService.saveQues(updateQuestion);
-                baseUnit.setQuestion(updateQuestion);
-            } else {
-            	if(updateQuestion.getQuesOptions()!=null&&updateQuestion.getQuesOptions().size()>26) {
-            		throw new StatusException("选项数量不能超过26");
-            	}
-                List<Double> subScoreList = baseUnit.getSubScoreList();
-                int size = baseQuestion.getSubQuestions().size();
-                boolean match = false;
-
-                // 判断更新的对象是哪个子题
-                for (int index = 1; index <= size; index++) {
-                    // 检查子题分数,确保没有错误数据
-                    if (subScoreList.size() < index) {
-                        subScoreList.add(0d);
-                    }
-
-                    Question sub = baseQuestion.getSubQuestions().get(index - 1);
-                    if (updateQuestion.getId().equals(sub.getId())) {
-                    	String changInfo=getQuestionChangeInfo(sub, updateQuestion);
-                    	if(changInfo!=null) {
-                    		sb.append("第"+baseUnit.getNumber()+"小题第"+index+"子题变动:"+changInfo);
-                    	}
-                        // 匹配到子题
-                        subScoreList.set(index - 1, updateUnit.getScore());
-                        baseQuestion.getSubQuestions().set(index - 1, updateQuestion);
-
-                        //重新计算套题的难度,公开度
-                        boolean publicity = false;
-                        double totalSum = 0d;
-                        double totalDou = 0d;
-                        for (int i = 0; i < baseQuestion.getSubQuestions().size(); i++) {
-                            Question subQuestion = baseQuestion.getSubQuestions().get(i);
-                            //设置公开度
-                            if (subQuestion.getPublicity()) {
-                                publicity = true;
-                            }
-
-                            if (subQuestion.getDifficultyDegree() == null) {
-                                subQuestion.setDifficultyDegree(0.5);
-                            }
-
-                            totalSum = subQuestion.getDifficultyDegree() * subScoreList.get(i) + totalSum;
-                            totalDou = subScoreList.get(i) + totalDou;
-                        }
-
-                        BigDecimal b;
-                        if (totalDou != 0d) {
-                            b = BigDecimal.valueOf(totalSum / totalDou);
-                        } else {
-                            b = BigDecimal.valueOf(0d);
-                        }
-
-                        Double difficulty = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
-                        baseQuestion.setDifficultyDegree(difficulty);
-                        baseQuestion.setPublicity(publicity);
-                        match = true;
-                    }
-                }
-
-                if (match) {
-                    quesService.saveQues(baseQuestion);
-                    baseUnit.setQuestion(baseQuestion);
-                }
-
-                // 更新套题unit当前总分与子题分数
-                baseUnit.setSubScoreList(subScoreList);
-            }
-        } else {
-        	if(updateQuestion.getQuesOptions()!=null&&updateQuestion.getQuesOptions().size()>26) {
-        		throw new StatusException("选项数量不能超过26");
-        	}
-        	String changInfo=getQuestionChangeInfo(baseQuestion, updateQuestion);
-        	if(changInfo!=null) {
-        		sb.append("第"+baseUnit.getNumber()+"小题变动:"+changInfo);
-        	}
-            quesService.saveQues(updateQuestion);
-            baseUnit.setQuestion(updateQuestion);
-            baseUnit.setScore(updateUnit.getScore());
-        }
-
-        // 同时要跟新小题里面的Question对象
-        paperDetailUnitRepo.save(baseUnit);
-
-        Paper paper = baseUnit.getPaper();
-        Double total=paper.getTotalScore();
-        Integer dc=paper.getPaperDetailCount();
-        Integer uc=paper.getUnitCount();
-        if(PaperType.GENERATE.equals(paper.getPaperType())) {
-        	paper.setAuditStatus(false);
-        }
-        paperService.formatPaper(paper, user);
-
-        String changInfo=PaperUtil.getPaperChangeInfo(total, dc, uc, paper);
-    	if(changInfo!=null) {
-    		sb.append(changInfo);
-    	}
-    	if(sb.length()!=0) {
-    		StringBuilder paperInfo=new StringBuilder();
-    		paperInfo.append(" 课程:"+paper.getCourse().getName()+"("+paper.getCourse().getCode()+")");
-    		paperInfo.append(" 试卷名称:"+paper.getName());
-    		sb.append(paperInfo);
-    		if(PaperType.IMPORT.equals(paper.getPaperType())) {
-    			ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), AdminOperateType.TYPE43.getDesc(),sb.toString()));
-    		}else {
-    			ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), AdminOperateType.TYPE44.getDesc(),sb.toString()));
-    		}
-    	}
-        if (baseUnit.getQuestion() != null) {
-            //清除缓存
-            this.clearQuestionCache(baseUnit.getQuestion().getId());
-        }
-
-        return baseUnit;
-    }
-    
-    
-    
-    private String getQuestionChangeInfo(Question old,Question now) {
-    	StringBuilder sb=new StringBuilder();
-    	if(old.getScore().doubleValue() !=now.getScore().doubleValue()) {
-    		sb.append(" 分数变动("+old.getScore()+"变为"+now.getScore()+")");
-    	}
-    	if(!StringUtils.equals(old.getQuesBody(), now.getQuesBody())) {
-    		sb.append(" 题干变动");
-    	}
-    	if(optionChange(old, now)) {
-    		sb.append(" 选项变动");
-    	}
-    	if(!StringUtils.equals(old.getQuesAnswer(), now.getQuesAnswer())) {
-    		sb.append(" 答案变动");
-    	}
-    	if(sb.length()==0) {
-    		return null;
-    	}
-    	return sb.toString();
-    }
-    
-    private boolean optionChange(Question old,Question now) {
-    	if(now.getQuesOptions()==null) {
-    		return false;
-    	}
-    	if(old.getQuesOptions().size()!=now.getQuesOptions().size()) {
-    		return true;
-    	}
-    	for(int i=0;i<now.getQuesOptions().size();i++) {
-    		if(!StringUtils.equals(old.getQuesOptions().get(i).getOptionBody(), now.getQuesOptions().get(i).getOptionBody())) {
-    			return true;
-        	}
-    	}
-    	return false;
-    }
-    /**
-     * 删除小题
-     *
-     * @param id
-     * @return
-     */
-    public void deletePaperDetailUnit(String id, User user) {
-    	String changInfo=null;
-        PaperDetailUnit detailUnit = Model.of(paperDetailUnitRepo.findById(id));
-        Paper paper = detailUnit.getPaper();
-        if(paper.getInUse()!=null&&paper.getInUse()==1) {
-			throw new StatusException("500", "试卷已调用");
-		}
-        paperDetailUnitRepo.deleteById(id);
-
-        if (detailUnit.getQuestion() != null) {
-            //清除缓存
-            this.clearQuestionCache(detailUnit.getQuestion().getId());
-        }
-        Double total=paper.getTotalScore();
-        Integer dc=paper.getPaperDetailCount();
-        Integer uc=paper.getUnitCount();
-        paper.setAuditStatus(false);
-        paperService.formatPaper(paper, user);
-        changInfo=PaperUtil.getPaperChangeInfo(total, dc, uc, paper);
-        if(detailUnit!=null) {
-        	StringBuilder sb=new StringBuilder();
-			sb.append("课程:"+detailUnit.getPaper().getCourse().getName()+"("+detailUnit.getPaper().getCourse().getCode()+")");
-			sb.append(" 试卷名称:"+detailUnit.getPaper().getName()); 
-			sb.append(" 第"+detailUnit.getNumber()+"小题 ");
-			sb.append(changInfo);
-        	ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), AdminOperateType.TYPE45.getDesc(),sb.toString()));
-        }
-    }
-
-    /**
-     * 根据大题查小题
-     *
-     * @param paperDetail
-     * @return
-     */
-    public List<PaperDetailUnit> getUnitsByPaperDetail(PaperDetail paperDetail) {
-        return paperDetailUnitRepo.findByPaperDetailOrderByNumber(paperDetail);
-    }
-
-    /**
-     * 根据大题集合删除小题
-     *
-     * @param paperDetails
-     */
-    public void deleteUnitsByPaperDetails(List<PaperDetail> paperDetails) {
-        List<PaperDetailUnit> units = new ArrayList<>();
-        for (PaperDetail pd : paperDetails) {
-            units.addAll(getUnitsByPaperDetail(pd));
-        }
-        paperDetailUnitRepo.deleteAll(units);
-    }
-
-    public List<PaperDetailUnit> findByQuestionAndPaperTypes(Question question, List<PaperType> paperTypes) {
-        Query query = new Query();
-        query.addCriteria(Criteria.where("paperType").in(paperTypes));
-        query.addCriteria(Criteria.where("question").is(question));
-        return this.mongoTemplate.find(query, PaperDetailUnit.class);
-    }
-
-    /**
-     * 查询大题下的小题
-     * 按number升序或降序排列,取第一个
-     *
-     * @return
-     */
-    public PaperDetailUnit findTopOrderByNumber(PaperDetail paperDetail, String orderType) {
-        if ("ASC".equals(orderType)) {
-            return paperDetailUnitRepo.findTopByPaperDetailOrderByNumberAsc(paperDetail);
-        } else if ("DESC".equals(orderType)) {
-            return paperDetailUnitRepo.findTopByPaperDetailOrderByNumberDesc(paperDetail);
-        }
-        return null;
-    }
-
-    /**
-     * 按试卷 查询 小题,并将小题按number,createTime排序
-     *
-     * @param paper
-     * @return
-     */
-    public List<PaperDetailUnit> findByPaperAndSort(Paper paper) {
-        Query query = new Query();
-        query.addCriteria(Criteria.where("paper").is(paper));
-        query.with(Sort.by(new Order(Direction.ASC, "number")));
-        query.with(Sort.by(new Order(Direction.ASC, "createTime")));
-        return this.mongoTemplate.find(query, PaperDetailUnit.class);
-    }
-
-
-    public List<PaperDetailUnit> findByQuestionsAndPaperTypeOnline(List<String> ids, PaperType paperType) {
-        Query query = new Query();
-        query.addCriteria(Criteria.where("question.id").in(ids));
-        query.addCriteria(Criteria.where("paperType").is(paperType));
-        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
-        return paperDetailUnits;
-    }
-
-    @Override
-    public List<PaperDetailUnit> findByQuestionsAndPaperType(List<String> ids, PaperType paperType) {
-        List<ObjectId> objectIds = new ArrayList<>();
-        for (String id : ids) {
-            objectIds.add(new ObjectId(id));
-        }
-        Query query = new Query();
-        query.addCriteria(Criteria.where("question.$id").in(objectIds));
-        query.addCriteria(Criteria.where("paperType").is(paperType));
-        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
-        return paperDetailUnits;
-    }
-
-    @Override
-    public List<PaperDetailUnit> findByDDQuestionsAndPaperType(List<String> ids, PaperType paperType, List<Question> questionList) {
-        List<ObjectId> objectIds = new ArrayList<>();
-        for (String id : ids) {
-            objectIds.add(new ObjectId(id));
-        }
-        Query query = new Query();
-        query.addCriteria(Criteria.where("question.$id").in(objectIds));
-        query.addCriteria(Criteria.where("paperType").is("IMPORT"));
-        List<PaperDetailUnit> paperDetailUnits = mongoTemplate.find(query, PaperDetailUnit.class);
-        return paperDetailUnits;
-    }
-
-	/*@Override
-	public List<PaperDetailUnit> testFind(List<String> ids,PaperType paperType, List<Question> questionList) {
-		//第一种查询,quesion.id  id
-		DBObject query1 = new BasicDBObject();
-		BasicDBList values = new BasicDBList();
-		for(String id:ids){
-			values.add(id);
-		}
-		query1.put("quesion.id", new BasicDBObject("$in", values));
-		DBCursor dbCursor = mongoTemplate.getCollection("paperDetailUnit").find(query1);
-		while (dbCursor.hasNext()){
-			DBObject object=dbCursor.next();
-			log.debug("打印游标---------------"+object);
-		}
-
-		//第二种查询,quesion.$id  id
-		//DBObject query2 = new BasicDBObject();
-		BasicDBList values2 = new BasicDBList();
-		for(String id:ids){
-			values2.add(id);
-		}
-		query1.put("quesion.$id", new BasicDBObject("$in", values2));
-		DBCursor dbCursor2 = mongoTemplate.getCollection("paperDetailUnit").find(query1);
-		while (dbCursor2.hasNext()){
-			DBObject object=dbCursor2.next();
-			log.debug("打印游标2---------------"+object);
-		}
-
-		//第三种查询,quesion.$id  ObjectId
-		//DBObject query3 = new BasicDBObject();
-		BasicDBList values3 = new BasicDBList();
-		for(String id:ids){
-			values3.add(new ObjectId(id));
-		}
-		query1.put("quesion.$id", new BasicDBObject("$in", values3));
-		DBCursor dbCursor3 = mongoTemplate.getCollection("paperDetailUnit").find(query1);
-		while (dbCursor3.hasNext()){
-			DBObject object=dbCursor3.next();
-			log.debug("打印游标3---------------"+object);
-		}
-		return null;
-	}*/
-
-    @Override
-    public List<PaperDetailUnit> findByPaperIds(List<String> ids) {
-        List<ObjectId> objectIds = new ArrayList<>();
-        for (String id : ids) {
-            objectIds.add(new ObjectId(id));
-        }
-        Query query = new Query();
-        query.addCriteria(Criteria.where("paper.$id").in(objectIds));
-        List<PaperDetailUnit> paperDetailUnits = mongoTemplate.find(query, PaperDetailUnit.class);
-        return paperDetailUnits;
-    }
-
-    @Override
-    public void clearQuestionCache(String questionId) {
-        //清理与当前试题相关的缓存
-//        final String patternKey = CACHE_KEY_QUESTION + "*" + questionId;
-//        Set<String> keys = redisTemplate.keys(patternKey);
-//        if (CollectionUtils.isNotEmpty(keys)) {
-//            redisTemplate.delete(keys);
-//        }
-        questionCache.remove(questionId);
-        questionAnswerCache.remove(questionId);
-        //根据questionId清空相关联的paper缓存
-        List<PaperDetailUnit> list=findByQuestionId(questionId);
-        if(list!=null&&list.size()>0) {
-        	for(PaperDetailUnit pd:list) {
-//        		String paperKey = CACHE_KEY_PAPER + "*" + pd.getPaper().getId();
-//                Set<String> paperKeys = redisTemplate.keys(paperKey);
-//                if (CollectionUtils.isNotEmpty(paperKeys)) {
-//                    redisTemplate.delete(paperKeys);
-//                }
-        	    extractConfigPaperCache.remove(pd.getPaper().getId());
-        	    basePaperCache.remove(pd.getPaper().getId());
-        	}
-        }
-    }
-    
-    private List<PaperDetailUnit> findByQuestionId(String questionId) {
-        Query query = new Query();
-        query.addCriteria(Criteria.where("question.id").is(questionId));
-        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
-        return paperDetailUnits;
-    }
-
+package cn.com.qmth.examcloud.core.questions.service.impl;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.bson.types.ObjectId;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.domain.Sort.Order;
+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.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.api.commons.enums.AdminOperateType;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.questions.base.Model;
+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.PaperDetailUnitRepo;
+import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
+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.Question;
+import cn.com.qmth.examcloud.core.questions.service.PaperDetailUnitService;
+import cn.com.qmth.examcloud.core.questions.service.PaperService;
+import cn.com.qmth.examcloud.core.questions.service.QuesService;
+import cn.com.qmth.examcloud.core.questions.service.RandomPaperQuestionService;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitDto;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitExp;
+import cn.com.qmth.examcloud.core.questions.service.cache.BasePaperCache;
+import cn.com.qmth.examcloud.core.questions.service.cache.ExtractConfigPaperCache;
+import cn.com.qmth.examcloud.core.questions.service.cache.QuestionAnswerCache;
+import cn.com.qmth.examcloud.core.questions.service.cache.QuestionCache;
+import cn.com.qmth.examcloud.core.questions.service.util.PaperUtil;
+import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
+import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
+
+/**
+ * @author chenken
+ * @date 2017年9月13日 上午11:29:56
+ * @company QMTH
+ */
+@Service("paperDetailUnitService")
+public class PaperDetailUnitServiceImpl implements PaperDetailUnitService {
+
+    @Autowired
+    PaperRepo paperRepo;
+
+    @Autowired
+    private ExtractConfigPaperCache extractConfigPaperCache;
+
+    @Autowired
+    private BasePaperCache basePaperCache;
+
+    @Autowired
+    private QuestionCache questionCache;
+
+    @Autowired
+    private QuestionAnswerCache questionAnswerCache;
+
+    @Autowired
+    PaperDetailUnitRepo paperDetailUnitRepo;
+
+    @Autowired
+    QuesService quesService;
+
+    @Autowired
+    PaperService paperService;
+
+    @Autowired
+    MongoTemplate mongoTemplate;
+
+    @Autowired
+    RedisTemplate<String, Object> redisTemplate;
+
+    @Autowired
+    private RandomPaperQuestionService randomPaperQuestionService;
+
+    /**
+     * 根据Id获得对应的试题对象
+     *
+     * @param id
+     * @return
+     */
+    public Question getQuestionByPaperDetailUnitId(String id) {
+        return Model.of(paperDetailUnitRepo.findById(id)).getQuestion();
+    }
+
+    /**
+     * 按ID查询小题
+     *
+     * @param id
+     * @return
+     */
+    public PaperDetailUnit findById(String id) {
+        return Model.of(paperDetailUnitRepo.findById(id));
+    }
+
+    private void checkFillBlankQuestion(Question question) {
+        if (question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
+            String body = question.getQuesBody();
+            Document bodyDoc = Jsoup.parse(body);
+            if (bodyDoc.body().childrenSize() == 0) {
+                throw new StatusException("1000", "题干不能为空");
+            }
+            String bodyText = bodyDoc.body().html();
+            if (StringUtils.isBlank(bodyText)) {
+                throw new StatusException("1001", "题干不能为空");
+            }
+            if (bodyText.indexOf("###") == -1) {
+                throw new StatusException("1002", "题干不能没有空格(###)");
+            }
+            String answer = question.getQuesAnswer();
+            Document answerDoc = Jsoup.parse(answer);
+            if (answerDoc.body().childrenSize() != 0) {
+                String answerText = answerDoc.body().html();
+                if (StringUtils.isNotBlank(answerText)) {
+                    if (getSubStringCount(bodyText, "###") != answerText.split("##").length) {
+                        throw new StatusException("1003", "题干空格(###)数量和答案数量不一致");
+                    }
+                }
+            }
+        }
+    }
+
+    private int getSubStringCount(String src, String find) {
+        int o = 0;
+        int index = -1;
+        while ((index = src.indexOf(find, index)) > -1) {
+            ++index;
+            ++o;
+        }
+        return o;
+    }
+
+    @Override
+    public boolean paperInUse(String questionId) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("question.$id").is(new ObjectId(questionId)));
+        query.addCriteria(Criteria.where("paperType").is(PaperType.GENERATE.name()));
+        List<PaperDetailUnitDto> units = this.mongoTemplate.find(query, PaperDetailUnitDto.class, "paperDetailUnit");
+        if (CollectionUtils.isEmpty(units)) {
+            return false;
+        }
+        for (PaperDetailUnitDto dto : units) {
+            if (dto.getPaper().getInUse() != null && dto.getPaper().getInUse() == 1) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 保存小题
+     */
+    public PaperDetailUnit savePaperDetailUnit(PaperDetailUnitExp updateUnit, User user) {
+        StringBuilder sb = new StringBuilder();
+        PaperDetailUnit baseUnit = Model.of(paperDetailUnitRepo.findById(updateUnit.getId()));
+        if (paperInUse(baseUnit.getQuestion().getId())) {
+            PaperUtil.checkUpdate(updateUnit, baseUnit, "试卷已调用,");
+        }
+        if (randomPaperQuestionService.existQuestion(baseUnit.getQuestion().getId())) {
+            PaperUtil.checkUpdateOption(updateUnit, baseUnit, "小题已被抽题模板使用,");
+        }
+        Question baseQuestion = baseUnit.getQuestion();
+        Question updateQuestion = updateUnit.getQuestion();
+        checkFillBlankQuestion(updateQuestion);
+        if (baseUnit.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+            if (updateQuestion.getId().equals(baseQuestion.getId())) {
+
+                if (!StringUtils.equals(baseQuestion.getQuesBody(), updateQuestion.getQuesBody())) {
+                    sb.append(" 第" + baseUnit.getNumber() + "小题(套题)题干变动");
+                }
+                // 更新对象为套题本身
+                quesService.saveQues(updateQuestion);
+                baseUnit.setQuestion(updateQuestion);
+            } else {
+                if (updateQuestion.getQuesOptions() != null && updateQuestion.getQuesOptions().size() > 26) {
+                    throw new StatusException("选项数量不能超过26");
+                }
+                List<Double> subScoreList = baseUnit.getSubScoreList();
+                int size = baseQuestion.getSubQuestions().size();
+                boolean match = false;
+
+                // 判断更新的对象是哪个子题
+                for (int index = 1; index <= size; index++) {
+                    // 检查子题分数,确保没有错误数据
+                    if (subScoreList.size() < index) {
+                        subScoreList.add(0d);
+                    }
+
+                    Question sub = baseQuestion.getSubQuestions().get(index - 1);
+                    if (updateQuestion.getId().equals(sub.getId())) {
+                        String changInfo = getQuestionChangeInfo(sub, updateQuestion);
+                        if (changInfo != null) {
+                            sb.append("第" + baseUnit.getNumber() + "小题第" + index + "子题变动:" + changInfo);
+                        }
+                        // 匹配到子题
+                        subScoreList.set(index - 1, updateUnit.getScore());
+                        baseQuestion.getSubQuestions().set(index - 1, updateQuestion);
+
+                        // 重新计算套题的难度,公开度
+                        boolean publicity = false;
+                        double totalSum = 0d;
+                        double totalDou = 0d;
+                        for (int i = 0; i < baseQuestion.getSubQuestions().size(); i++) {
+                            Question subQuestion = baseQuestion.getSubQuestions().get(i);
+                            // 设置公开度
+                            if (subQuestion.getPublicity()) {
+                                publicity = true;
+                            }
+
+                            if (subQuestion.getDifficultyDegree() == null) {
+                                subQuestion.setDifficultyDegree(0.5);
+                            }
+
+                            totalSum = subQuestion.getDifficultyDegree() * subScoreList.get(i) + totalSum;
+                            totalDou = subScoreList.get(i) + totalDou;
+                        }
+
+                        BigDecimal b;
+                        if (totalDou != 0d) {
+                            b = BigDecimal.valueOf(totalSum / totalDou);
+                        } else {
+                            b = BigDecimal.valueOf(0d);
+                        }
+
+                        Double difficulty = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
+                        baseQuestion.setDifficultyDegree(difficulty);
+                        baseQuestion.setPublicity(publicity);
+                        match = true;
+                    }
+                }
+
+                if (match) {
+                    quesService.saveQues(baseQuestion);
+                    baseUnit.setQuestion(baseQuestion);
+                }
+
+                // 更新套题unit当前总分与子题分数
+                baseUnit.setSubScoreList(subScoreList);
+            }
+        } else {
+            if (updateQuestion.getQuesOptions() != null && updateQuestion.getQuesOptions().size() > 26) {
+                throw new StatusException("选项数量不能超过26");
+            }
+            String changInfo = getQuestionChangeInfo(baseQuestion, updateQuestion);
+            if (changInfo != null) {
+                sb.append("第" + baseUnit.getNumber() + "小题变动:" + changInfo);
+            }
+            quesService.saveQues(updateQuestion);
+            baseUnit.setQuestion(updateQuestion);
+            baseUnit.setScore(updateUnit.getScore());
+        }
+
+        // 同时要跟新小题里面的Question对象
+        paperDetailUnitRepo.save(baseUnit);
+
+        Paper paper = baseUnit.getPaper();
+        Double total = paper.getTotalScore();
+        Integer dc = paper.getPaperDetailCount();
+        Integer uc = paper.getUnitCount();
+        if (PaperType.GENERATE.equals(paper.getPaperType())) {
+            paper.setAuditStatus(false);
+        }
+        paperService.formatPaper(paper, user);
+
+        String changInfo = PaperUtil.getPaperChangeInfo(total, dc, uc, paper);
+        if (changInfo != null) {
+            sb.append(changInfo);
+        }
+        if (sb.length() != 0) {
+            StringBuilder paperInfo = new StringBuilder();
+            paperInfo.append(" 课程:" + paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")");
+            paperInfo.append(" 试卷名称:" + paper.getName());
+            sb.append(paperInfo);
+            if (PaperType.IMPORT.equals(paper.getPaperType())) {
+                ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
+                        AdminOperateType.TYPE43.getDesc(), sb.toString()));
+            } else {
+                ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
+                        AdminOperateType.TYPE44.getDesc(), sb.toString()));
+            }
+        }
+        if (baseUnit.getQuestion() != null) {
+            // 清除缓存
+            this.clearQuestionCache(baseUnit.getQuestion().getId());
+        }
+
+        return baseUnit;
+    }
+
+    private String getQuestionChangeInfo(Question old, Question now) {
+        StringBuilder sb = new StringBuilder();
+        if (old.getScore().doubleValue() != now.getScore().doubleValue()) {
+            sb.append(" 分数变动(" + old.getScore() + "变为" + now.getScore() + ")");
+        }
+        if (!StringUtils.equals(old.getQuesBody(), now.getQuesBody())) {
+            sb.append(" 题干变动");
+        }
+        if (optionChange(old, now)) {
+            sb.append(" 选项变动");
+        }
+        if (!StringUtils.equals(old.getQuesAnswer(), now.getQuesAnswer())) {
+            sb.append(" 答案变动");
+        }
+        if (sb.length() == 0) {
+            return null;
+        }
+        return sb.toString();
+    }
+
+    private boolean optionChange(Question old, Question now) {
+        if (now.getQuesOptions() == null) {
+            return false;
+        }
+        if (old.getQuesOptions().size() != now.getQuesOptions().size()) {
+            return true;
+        }
+        for (int i = 0; i < now.getQuesOptions().size(); i++) {
+            if (!StringUtils.equals(old.getQuesOptions().get(i).getOptionBody(),
+                    now.getQuesOptions().get(i).getOptionBody())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 删除小题
+     *
+     * @param id
+     * @return
+     */
+    public void deletePaperDetailUnit(String id, User user) {
+        String changInfo = null;
+        PaperDetailUnit detailUnit = Model.of(paperDetailUnitRepo.findById(id));
+        Paper paper = detailUnit.getPaper();
+        if (paper.getInUse() != null && paper.getInUse() == 1) {
+            throw new StatusException("500", "试卷已调用");
+        }
+        paperDetailUnitRepo.deleteById(id);
+
+        if (detailUnit.getQuestion() != null) {
+            // 清除缓存
+            this.clearQuestionCache(detailUnit.getQuestion().getId());
+        }
+        Double total = paper.getTotalScore();
+        Integer dc = paper.getPaperDetailCount();
+        Integer uc = paper.getUnitCount();
+        paper.setAuditStatus(false);
+        paperService.formatPaper(paper, user);
+        changInfo = PaperUtil.getPaperChangeInfo(total, dc, uc, paper);
+        if (detailUnit != null) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("课程:" + detailUnit.getPaper().getCourse().getName() + "("
+                    + detailUnit.getPaper().getCourse().getCode() + ")");
+            sb.append(" 试卷名称:" + detailUnit.getPaper().getName());
+            sb.append(" 第" + detailUnit.getNumber() + "小题 ");
+            sb.append(changInfo);
+            ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
+                    AdminOperateType.TYPE45.getDesc(), sb.toString()));
+        }
+    }
+
+    /**
+     * 根据大题查小题
+     *
+     * @param paperDetail
+     * @return
+     */
+    public List<PaperDetailUnit> getUnitsByPaperDetail(PaperDetail paperDetail) {
+        return paperDetailUnitRepo.findByPaperDetailOrderByNumber(paperDetail);
+    }
+
+    /**
+     * 根据大题集合删除小题
+     *
+     * @param paperDetails
+     */
+    public void deleteUnitsByPaperDetails(List<PaperDetail> paperDetails) {
+        List<PaperDetailUnit> units = new ArrayList<>();
+        for (PaperDetail pd : paperDetails) {
+            units.addAll(getUnitsByPaperDetail(pd));
+        }
+        paperDetailUnitRepo.deleteAll(units);
+    }
+
+    public List<PaperDetailUnit> findByQuestionAndPaperTypes(Question question, List<PaperType> paperTypes) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("paperType").in(paperTypes));
+        query.addCriteria(Criteria.where("question").is(question));
+        return this.mongoTemplate.find(query, PaperDetailUnit.class);
+    }
+
+    /**
+     * 查询大题下的小题 按number升序或降序排列,取第一个
+     *
+     * @return
+     */
+    public PaperDetailUnit findTopOrderByNumber(PaperDetail paperDetail, String orderType) {
+        if ("ASC".equals(orderType)) {
+            return paperDetailUnitRepo.findTopByPaperDetailOrderByNumberAsc(paperDetail);
+        } else if ("DESC".equals(orderType)) {
+            return paperDetailUnitRepo.findTopByPaperDetailOrderByNumberDesc(paperDetail);
+        }
+        return null;
+    }
+
+    /**
+     * 按试卷 查询 小题,并将小题按number,createTime排序
+     *
+     * @param paper
+     * @return
+     */
+    public List<PaperDetailUnit> findByPaperAndSort(Paper paper) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("paper").is(paper));
+        // query.with(Sort.by(new Order(Direction.ASC, "number")));
+        // query.with(Sort.by(new Order(Direction.ASC, "createTime")));
+        List<PaperDetailUnit> list = this.mongoTemplate.find(query, PaperDetailUnit.class);
+        Collections.sort(list, new Comparator<PaperDetailUnit>() {
+
+            @Override
+            public int compare(PaperDetailUnit o1, PaperDetailUnit o2) {
+                if (o1.getPaperDetail().getNumber() > o2.getPaperDetail().getNumber()) {
+                    return 1;
+                } else if (o1.getPaperDetail().getNumber() < o2.getPaperDetail().getNumber()) {
+                    return -1;
+                } else {
+                    if (o1.getNumber() > o2.getNumber()) {
+                        return 1;
+                    } else if (o1.getNumber() < o2.getNumber()) {
+                        return -1;
+                    } else {
+                        return 0;
+                    }
+                }
+            }
+
+        });
+        return list;
+    }
+
+    public List<PaperDetailUnit> findByQuestionsAndPaperTypeOnline(List<String> ids, PaperType paperType) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("question.id").in(ids));
+        query.addCriteria(Criteria.where("paperType").is(paperType));
+        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
+        return paperDetailUnits;
+    }
+
+    @Override
+    public List<PaperDetailUnit> findByQuestionsAndPaperType(List<String> ids, PaperType paperType) {
+        List<ObjectId> objectIds = new ArrayList<>();
+        for (String id : ids) {
+            objectIds.add(new ObjectId(id));
+        }
+        Query query = new Query();
+        query.addCriteria(Criteria.where("question.$id").in(objectIds));
+        query.addCriteria(Criteria.where("paperType").is(paperType));
+        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
+        return paperDetailUnits;
+    }
+
+    @Override
+    public List<PaperDetailUnit> findByDDQuestionsAndPaperType(List<String> ids, PaperType paperType,
+            List<Question> questionList) {
+        List<ObjectId> objectIds = new ArrayList<>();
+        for (String id : ids) {
+            objectIds.add(new ObjectId(id));
+        }
+        Query query = new Query();
+        query.addCriteria(Criteria.where("question.$id").in(objectIds));
+        query.addCriteria(Criteria.where("paperType").is("IMPORT"));
+        List<PaperDetailUnit> paperDetailUnits = mongoTemplate.find(query, PaperDetailUnit.class);
+        return paperDetailUnits;
+    }
+
+    /*
+     * @Override public List<PaperDetailUnit> testFind(List<String>
+     * ids,PaperType paperType, List<Question> questionList) {
+     * //第一种查询,quesion.id id DBObject query1 = new BasicDBObject(); BasicDBList
+     * values = new BasicDBList(); for(String id:ids){ values.add(id); }
+     * query1.put("quesion.id", new BasicDBObject("$in", values)); DBCursor
+     * dbCursor = mongoTemplate.getCollection("paperDetailUnit").find(query1);
+     * while (dbCursor.hasNext()){ DBObject object=dbCursor.next();
+     * log.debug("打印游标---------------"+object); }
+     * 
+     * //第二种查询,quesion.$id id //DBObject query2 = new BasicDBObject();
+     * BasicDBList values2 = new BasicDBList(); for(String id:ids){
+     * values2.add(id); } query1.put("quesion.$id", new BasicDBObject("$in",
+     * values2)); DBCursor dbCursor2 =
+     * mongoTemplate.getCollection("paperDetailUnit").find(query1); while
+     * (dbCursor2.hasNext()){ DBObject object=dbCursor2.next();
+     * log.debug("打印游标2---------------"+object); }
+     * 
+     * //第三种查询,quesion.$id ObjectId //DBObject query3 = new BasicDBObject();
+     * BasicDBList values3 = new BasicDBList(); for(String id:ids){
+     * values3.add(new ObjectId(id)); } query1.put("quesion.$id", new
+     * BasicDBObject("$in", values3)); DBCursor dbCursor3 =
+     * mongoTemplate.getCollection("paperDetailUnit").find(query1); while
+     * (dbCursor3.hasNext()){ DBObject object=dbCursor3.next();
+     * log.debug("打印游标3---------------"+object); } return null; }
+     */
+
+    @Override
+    public List<PaperDetailUnit> findByPaperIds(List<String> ids) {
+        List<ObjectId> objectIds = new ArrayList<>();
+        for (String id : ids) {
+            objectIds.add(new ObjectId(id));
+        }
+        Query query = new Query();
+        query.addCriteria(Criteria.where("paper.$id").in(objectIds));
+        List<PaperDetailUnit> paperDetailUnits = mongoTemplate.find(query, PaperDetailUnit.class);
+        return paperDetailUnits;
+    }
+
+    @Override
+    public void clearQuestionCache(String questionId) {
+        // 清理与当前试题相关的缓存
+        // final String patternKey = CACHE_KEY_QUESTION + "*" + questionId;
+        // Set<String> keys = redisTemplate.keys(patternKey);
+        // if (CollectionUtils.isNotEmpty(keys)) {
+        // redisTemplate.delete(keys);
+        // }
+        questionCache.remove(questionId);
+        questionAnswerCache.remove(questionId);
+        // 根据questionId清空相关联的paper缓存
+        List<PaperDetailUnit> list = findByQuestionId(questionId);
+        if (list != null && list.size() > 0) {
+            for (PaperDetailUnit pd : list) {
+                // String paperKey = CACHE_KEY_PAPER + "*" +
+                // pd.getPaper().getId();
+                // Set<String> paperKeys = redisTemplate.keys(paperKey);
+                // if (CollectionUtils.isNotEmpty(paperKeys)) {
+                // redisTemplate.delete(paperKeys);
+                // }
+                extractConfigPaperCache.remove(pd.getPaper().getId());
+                basePaperCache.remove(pd.getPaper().getId());
+            }
+        }
+    }
+
+    private List<PaperDetailUnit> findByQuestionId(String questionId) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("question.id").is(questionId));
+        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
+        return paperDetailUnits;
+    }
+
 }