package com.qmth.qrzk.repository.user.service.impl;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.qmth.qrzk.dao.IQuestionDao;
import com.qmth.qrzk.repository.common.base.BaseDao;
import com.qmth.qrzk.repository.common.enumeration.QuestionStatusEnum;
import com.qmth.qrzk.repository.common.mapper.QuestionSimilarityMapper;
import com.qmth.qrzk.repository.exception.BizException;
import com.qmth.qrzk.repository.pager.model.PagerConstruct;
import com.qmth.qrzk.repository.pager.model.PagerConstructDetail;
import com.qmth.qrzk.repository.pager.model.PagerConstructQtype;
import com.qmth.qrzk.repository.pager.model.PaperDifficultyEstimateDetail;
import com.qmth.qrzk.repository.pager.model.PaperSkeleton;
import com.qmth.qrzk.repository.pager.model.PaperSkeletonDetail;
import com.qmth.qrzk.repository.pager.service.PaperDifficultyEstimateService;
import com.qmth.qrzk.repository.pager.service.ZujuanService;
import com.qmth.qrzk.repository.service.query.QuestionQueryParams;
import com.qmth.qrzk.repository.service.query.QuestionSimilarityQueryParams;
import com.qmth.qrzk.repository.task.PaperTask;
import com.qmth.qrzk.repository.user.model.Attachment;
import com.qmth.qrzk.repository.user.model.AuditorJob;
import com.qmth.qrzk.repository.user.model.Book;
import com.qmth.qrzk.repository.user.model.Course;
import com.qmth.qrzk.repository.user.model.InputQuesTask;
import com.qmth.qrzk.repository.user.model.InputerJob;
import com.qmth.qrzk.repository.user.model.OperLog;
import com.qmth.qrzk.repository.user.model.Question;
import com.qmth.qrzk.repository.user.model.QuestionImportHistory;
import com.qmth.qrzk.repository.user.model.QuestionOption;
import com.qmth.qrzk.repository.user.model.QuestionType;
import com.qmth.qrzk.repository.user.model.SimilarQuestion;
import com.qmth.qrzk.repository.user.model.User;
import com.qmth.qrzk.repository.user.service.IQuestionOperationLogService;
import com.qmth.qrzk.repository.user.service.IQuestionOptionService;
import com.qmth.qrzk.repository.user.service.IQuestionService;
import com.qmth.qrzk.repository.utils.Consts;
import com.qmth.qrzk.repository.utils.DateHelper;
import com.qmth.qrzk.repository.utils.General;
import com.qmth.qrzk.repository.utils.ParamUtils;
import com.qmth.qrzk.repository.utils.StringSimilarityUtils;
import com.qmth.qrzk.repository.utils.docx.DocxReader;
import com.qmth.qrzk.repository.utils.docx.HtmlCreator;
import com.qmth.qrzk.repository.utils.docx.ResourceInfo;

@Service("questionServiceImpl")
public class QuestionServiceImpl implements IQuestionService {
	
	private final static Logger log = Logger.getLogger(QuestionServiceImpl.class);
	
	@Autowired private BaseDao dao;
	
	@Autowired
	@Qualifier("questionDaoImpl")
	private IQuestionDao questionDao;
	
	@Autowired
	@Qualifier("questionSimilarityMapper")
	private QuestionSimilarityMapper questionSimilarityMapper;
    
    @Autowired 
	private PaperSkeletonService paperSkeletonSrv;
    
    @Autowired 
	private ZujuanService zujuanService;
	
	@Autowired
	private AttachmentService attachmentService;
	
	@Autowired
	private UserServiceImpl userService;
	
	@Autowired
	private QuestionTypeServiceImpl quesTypeService;
	
	@Autowired
	private PaperDifficultyEstimateService paperDifficultyEstimateService;
	
	@Override
	public int insertQuestion(Question question) {
		return questionDao.addQuestion(question);
	}
	
	@Autowired
	private CourseServiceImpl courseService;
	
	@Autowired
	private QuestionTypeServiceImpl questionTypeService;

	@Autowired
	@Qualifier("questionOperationLogServiceImpl")
	private IQuestionOperationLogService questionOperationLogService;

	@Autowired
	@Qualifier("questionOptionServiceImpl")
	private IQuestionOptionService questionOptionServiceImpl;
	
	public List<Question> searchByCondition(QuestionQueryParams question,User curUser) {
		question.setCurUserId(curUser.getId());
		List<Question> quesList = dao.getPagerList("searchByCondition", question);
		for(Question ques:quesList){
			General.quesWordToHtmlForShow(ques);
		}
		return quesList;
	}

	public List<Question> searchToAuditByCondition(QuestionQueryParams question,User curUser) {
		question.setCurUserId(curUser.getId());
		List<Question> quesList = dao.getPagerList("searchToAuditByCondition", question);
		loadPapersIntoQuesList(quesList);
		for(Question ques:quesList){
			General.quesWordToHtmlForShow(ques);
		}
		return quesList;
	}

	public List<Question> searchByConditionForMng(QuestionQueryParams question,User curUser) {
		question.setCurUserId(curUser.getId());
		List<Question> quesList = dao.getPagerList("searchByConditionForMng", question);
		loadPapersIntoQuesList(quesList);
		for(Question ques:quesList){
			String quesTimesSTr="";
			General.quesWordToHtmlForShow(ques);
			//获取试卷历史启动时间
			List<java.util.Date> quesTimes = dao.getList("queryTimesxxLogs", ques.getId());
			for(java.util.Date t:quesTimes){
				quesTimesSTr=quesTimesSTr+DateHelper.toYearMString(t)+",";
			}
			if(quesTimesSTr.length()>0){
				quesTimesSTr=quesTimesSTr.substring(0,quesTimesSTr.length()-1);
			}
			ques.setOpenTimesStr(quesTimesSTr);
		}
		return quesList;
	}
	
	public void loadPapersIntoQuesList(List<Question> quesList){
		for(Question ques:quesList){
			ques.setCreatedByBean(Consts.UserMap.get(ques.getCreatedBy()));
			ques.setUpdatedByBean(Consts.UserMap.get(ques.getUpdatedBy()));
			List<Map<String,Object>> infos = dao.getList("getTasksByQuesId", ques.getId());
			List<PaperTask> tasks = new ArrayList<PaperTask>();
			for(Map<String,Object> info:infos){
				PaperTask task = new PaperTask();
				task.setId(General.toInteger(info.get("id"), 0));
				task.setPaperName((String)info.get("paperName"));
				task.setStatus((String)info.get("status"));
				tasks.add(task);
			}
			ques.setPaperTasks(tasks);
		}
	}

	public List<Question> searchByConditionForZj(Question question,int showCount,int currentPage) {
		Map<String,Object> param = new HashMap<String, Object>();
		param.put("question", question);
		param.put("showCount", showCount);
		param.put("currentPage", currentPage);
		List<Question> quesList = dao.getList("searchByConditionForZj", param);
		for(Question ques:quesList){
			General.quesWordToHtmlForShow(ques);
		}
		return quesList;
	}
	
	public void saveAudit(int quesId,int userId,String auditResult,String remark){
		Question ques = new Question();
		ques.setId(quesId);
		ques.setStatusCode(auditResult);
		dao.update("updateQuestion", ques);
		
		OperLog ol = new OperLog();
		ol.setObjId(quesId);
		ol.setObjType(Consts.OperLogObjType.Question);
		ol.setOperCode(QuestionStatusEnum.QUESTION_APPROVED.getKey().equals(auditResult)?"审核通过":"审核拒绝");
		ol.setRemark(remark);
		ol.setCreatedBy(userId);
		dao.insert("addOperLog", ol);
	}
	
	public List<OperLog> getAuditLogs(int quesId){
		Map<String,Object> param = new HashMap<String, Object>();
		param.put("objId", quesId);
		param.put("objType", Consts.OperLogObjType.Question);
		List<OperLog> list = dao.getList("queryOperLogsByObjIdAndType", param);
		return list;
	}

	public int searchByConditionCountForZj(Question question) {
		Integer count = dao.get("searchByConditionCountForZj", question);
		return count;
	}
	
	public boolean checkAllowAddIntoPaper(Question ques,int paperId,int quesTypeId,double score,String quesOperType,Integer paperDetailId){
		Question quesCondition = new Question();
		quesCondition.setId(ques.getId());
		quesCondition.setEstimateDifficultyCode(ques.getEstimateDifficultyCode());
		quesCondition.setAbilityCode(ques.getAbilityCode());
		quesCondition.setChapter(ques.getChapter());
		quesCondition.setTypeId(quesTypeId);
		quesCondition.setScore(score);
		return zjGetAllQuestions(quesCondition, paperId,quesOperType,paperDetailId).size() > 0;
	}
	
	public List<PaperSkeletonDetail> calcAllRestQuesDetails(int paperId){
		List<PaperSkeletonDetail> details = new ArrayList<PaperSkeletonDetail>();
		List<PagerConstructQtype> qts = dao.getList("findPagerConstructQtypesByPaperId", paperId);
		for(PagerConstructQtype qt:qts){
			for(PaperSkeletonDetail d: calcRestQuesDetails(paperId,qt.getQuestionTypeId(),qt.getScore())){
				if(d.getQuesNum() > 0){
					details.add(d);
				}
			}
		}
		return details;
	}
	
	public List<PaperSkeletonDetail> calcRestQuesDetails(int paperId,int quesTypeId,double score){
		String quesTypeName = questionTypeService.queryById(quesTypeId).getName();
		List<PaperSkeletonDetail> restQuesDetails = new ArrayList<PaperSkeletonDetail>();
		PagerConstruct paper = zujuanService.findById(paperId);
		PaperSkeleton skel = paperSkeletonSrv.queryById(paper.getSkeletonId());
    	List<QuestionType> quesTypeArr = skel.getQuesTypeArr();
    	Map<String,List<PaperSkeletonDetail>> skelDetailMap = new HashMap<String, List<PaperSkeletonDetail>>();
    	for(PaperSkeletonDetail d:skel.getSkeletonDetailArr()){
    		QuestionType qt = quesTypeArr.get(d.getQuesTypeNo() - 1);
    		String key = qt.getId() + "_" + qt.getDefaultScore();
    		List<PaperSkeletonDetail> dList = skelDetailMap.get(key);
    		if(dList == null){
    			dList = new ArrayList<PaperSkeletonDetail>();
    			skelDetailMap.put(key, dList);
    		}
    		dList.add(d);
    	}
    	List<PaperSkeletonDetail> psDetails = skelDetailMap.get(quesTypeId + "_" + score);
    	if(psDetails != null){
    		Map<String,Object> param = new HashMap<String, Object>();
    		param.put("paperId", paperId);
    		param.put("quesTypeId", quesTypeId);
    		param.put("score", score);

    		List<Map<String, Object>> quesStats = dao.getList("getQuestionCountBySkelCdt", param);
    		Map<String, Integer> quesStatMap = new HashMap<String, Integer>();
    		for(Map<String, Object> m:quesStats){
    			quesStatMap.put(m.get("skelCdt") + "", General.toInteger(m.get("count"), 0));
    		}
    		for(PaperSkeletonDetail psDetail:psDetails){
    			Integer count = quesStatMap.get(psDetail.getChapterNo() + "_" + psDetail.getEstimateDifficultyCode() + "_" + psDetail.getAbilityCode());
    			if(count == null){
    				count = 0;
    			}
				psDetail.setQuesNum(psDetail.getQuesNum() - count);
				psDetail.setQuesTypeName(quesTypeName);
				psDetail.setScore(score);
				restQuesDetails.add(psDetail);
    		}
    	}
    	return restQuesDetails;
	}
	
	/**
	 * 组卷过程中获取可用的试题列表，由于此方法中有数据库更新操作，此方法就需要数据库事务，所以此方法名就命名得比较特殊了
	 * @param quesCondition
	 * @param paperId
	 * @param quesOperType
	 * @param paperDetailId
	 * @return
	 */
	public List<Question> zjGetAllQuestions(Question quesCondition,int paperId,String quesOperType,Integer paperDetailId){
		String diffCodeCondition = quesCondition.getEstimateDifficultyCode();
		String abilityCodeCondition = quesCondition.getAbilityCode();
		String chapterCondition = quesCondition.getChapter();
		List<Question> quesList = new ArrayList<Question>();
		PagerConstruct paper = zujuanService.findById(paperId);
		PaperSkeleton skel = paperSkeletonSrv.queryById(paper.getSkeletonId());
    	List<QuestionType> quesTypeArr = skel.getQuesTypeArr();
    	Map<String,List<PaperSkeletonDetail>> skelDetailMap = new HashMap<String, List<PaperSkeletonDetail>>();
    	for(PaperSkeletonDetail d:skel.getSkeletonDetailArr()){
    		QuestionType qt = quesTypeArr.get(d.getQuesTypeNo() - 1);
    		String key = qt.getId() + "_" + qt.getDefaultScore();
    		List<PaperSkeletonDetail> dList = skelDetailMap.get(key);
    		if(dList == null){
    			dList = new ArrayList<PaperSkeletonDetail>();
    			skelDetailMap.put(key, dList);
    		}
    		dList.add(d);
    	}
    	List<PaperSkeletonDetail> psDetails = skelDetailMap.get(quesCondition.getTypeId() + "_" + quesCondition.getScore());
    	if(psDetails != null){
    		Map<String,Object> param = new HashMap<String, Object>();
    		param.put("paperId", paperId);
    		param.put("quesTypeId", quesCondition.getTypeId());
    		param.put("score", quesCondition.getScore());

    		PagerConstructDetail paperDetail = null;
    		if(Consts.QuesOperTypeForZj.CHANGE_INTO_PAPER.equals(quesOperType) || Consts.QuesOperTypeForZj.UPDATE_INTO_PAPER.equals(quesOperType)){
    			paperDetail = dao.get("getPagerConstructDetailById", paperDetailId);
    			PagerConstructDetail tmp = new PagerConstructDetail();
    			tmp.setId(paperDetailId);
    			tmp.setQuestionId(0);
    			dao.update("updatePagerConstructDetail", tmp);
    		}
    		List<Map<String, Object>> quesStats = dao.getList("getQuestionCountBySkelCdt", param);
    		if(Consts.QuesOperTypeForZj.CHANGE_INTO_PAPER.equals(quesOperType)){
    			PagerConstructDetail tmp = new PagerConstructDetail();
    			tmp.setId(paperDetail.getId());
    			tmp.setQuestionId(paperDetail.getQuestionId());
    			dao.update("updatePagerConstructDetail", tmp);
    		}
    		
    		Map<String, Integer> quesStatMap = new HashMap<String, Integer>();
    		for(Map<String, Object> m:quesStats){
    			quesStatMap.put(m.get("skelCdt") + "", General.toInteger(m.get("count"), 0));
    		}
    		for(PaperSkeletonDetail psDetail:psDetails){
    			Integer count = quesStatMap.get(psDetail.getChapterNo() + "_" + psDetail.getEstimateDifficultyCode() + "_" + psDetail.getAbilityCode());
    			if(count == null){
    				count = 0;
    			}
    			if(count < psDetail.getQuesNum()){
	    			quesCondition.setChapter(psDetail.getChapterNo() + "");
	    			quesCondition.setEstimateDifficultyCode(psDetail.getEstimateDifficultyCode());
	    			quesCondition.setAbilityCode(psDetail.getAbilityCode());
	    			quesList.addAll(searchByConditionForZj(quesCondition, Integer.MAX_VALUE, 0));
    			}
    		}
    		if(Consts.QuesOperTypeForZj.UPDATE_INTO_PAPER.equals(quesOperType)){
    			PagerConstructDetail tmp = new PagerConstructDetail();
    			tmp.setId(paperDetail.getId());
    			tmp.setQuestionId(paperDetail.getQuestionId());
    			dao.update("updatePagerConstructDetail", tmp);
    		}
    		for(int i=0;i<quesList.size();i++){
    			Question q = quesList.get(i);
    			if(General.isNotEmpty(diffCodeCondition)){
    				if(!diffCodeCondition.equals(q.getEstimateDifficultyCode())){
    					quesList.remove(i);
    					i--;
    					continue;
    				}
    			}
    			if(General.isNotEmpty(abilityCodeCondition)){
    				if(!abilityCodeCondition.equals(q.getAbilityCode())){
    					quesList.remove(i);
    					i--;
    					continue;
    				}
    			}
    			if(General.isNotEmpty(chapterCondition)){
    				if(!chapterCondition.equals(q.getChapter())){
    					quesList.remove(i);
    					i--;
    					continue;
    				}
    			}
    		}
    	}
		quesCondition.setEstimateDifficultyCode(diffCodeCondition);
		quesCondition.setAbilityCode(abilityCodeCondition);
		quesCondition.setChapter(chapterCondition);
		return quesList;
	}

	@Override
	public int importQuestion(Question question) {
		return questionDao.importQuestion(question);
	}

	@Override
	public Question searchById(int id) {
		Question ques = questionDao.searchById(id);
		if(ques != null){
			General.repairQuestionAndOptions(ques);
		}
		return ques;
	}

	@Override
	public void update(Question question) {
		dao.update("updateQuestion", question);
	}

	@Override
	public List<Question> generatePaperQuestion(Question question) {
		return questionDao.generatePaperQuestion(question);
	}

	@Override
	public int updateStatusCode(Question question) {
		// TODO Auto-generated method stub
		return questionDao.updateStatus(question);
	}

	@Override
	public int searchAuditCount(Question question, String condition1,
			String condition2) {
		// TODO Auto-generated method stub
		return questionDao.searchAuditCount(question, condition1, condition2);
	}

	@Override
	public List<Question> searchAudit(Question question, int showCount,
			int currentPage, String condition1, String condition2) {
		List<Question> quesList = questionDao.searchAudit(question, showCount, currentPage, condition1, condition2);
		for(Question ques:quesList){
			General.repairQuestionAndOptions(ques);
		}
		return quesList;
	}
	
	@Override
	public List<Question> findAll() {
		return questionDao.findAll();
	}
	
	public List<Question> findAllQuestionsWithOptions(){
		List<Question> list = dao.getList("findAllQuestionsWithOptions");
		return list;
	}
	
	public List<Integer> findAllQuestionIds(){
		List<Integer> list = dao.getList("findAllQuestionIds");
		return list;
	}
	
	public Question searchQuestionWithOptionsById(int id){
		return dao.get("searchQuestionWithOptionsById", id);
	}

	@Override
	public List<Question> getStatusCount() {
		return questionDao.getStatusCount();
	}

	@Override
	public int getMaxChapterByCourseId(int courseId) {
		return questionDao.getMaxChapterByCourseId(courseId);
	}

	@Override
	public int getCountByCondition(Question question) {
		return questionDao.getCountByCondition(question);
	}

	@Override
	public int getCountByChapter(Question question) {
		return questionDao.getCountByChapter(question);
	}

	@Override
	public List<HashMap<String, Object>> getCountByCourseIdGroupByQuesTypeAndChapter(int courseId) {
		return dao.getList("getCountByCourseIdGroupByQuesTypeAndChapter", courseId);
	}

	public void delQues(int quesId,User curUser) throws BizException {
		List<Map<String,Object>> infos = dao.getList("getTasksByQuesId", quesId);
		if(infos.size() > 0){
			throw new BizException("此题已被试卷使用，不能被删除");
		}
		questionOptionServiceImpl.deleteOptionsByQuesId(quesId);
		dao.delete("delQuesById", quesId);
		OperLog ol = new OperLog();
		ol.setObjId(quesId);
		ol.setObjType(Consts.OperLogObjType.Question);
		ol.setOperCode("删除");
		ol.setRemark("强行删除");
		ol.setCreatedBy(curUser.getId());
		dao.insert("addOperLog", ol);
	}
	
	public Question zujuanSave(Question question,User curUser,int paperId,int quesTypeId,Integer paperDetailId) throws BizException{
		List<QuestionOption> options = question.getOptions();
//		AppendLuceneIndex lucene=new AppendLuceneIndex();
		Date date=new Date();
		question.setUpdatedTime(date);
		question.setUpdatedBy(curUser.getId());
		String quesOperType = null;
		
		QuestionType qt = questionTypeService.queryById(question.getTypeId());
		String quesTypeName = qt.getName();
		if(quesTypeName.equals("单项选择题") || quesTypeName.equals("单选题") || quesTypeName.equals("单选")
				|| quesTypeName.equals("多项选择题") || quesTypeName.equals("多选题") || quesTypeName.equals("多选")){
			question.setAnswerWord(new byte[]{});
			question.setAnswer("");
			question.setAnswerSummary("");
		}else{
			question.setOptions(new ArrayList<QuestionOption>());
		}
		curUser = Consts.UserMap.get(curUser.getId());
		if(question.getId() == null || question.getId() == 0){
			quesOperType = Consts.QuesOperTypeForZj.ADD_INTO_PAPER;
			question.setCreatedTime(date);
			question.setSetTime(date);
			question.setSetBy(curUser.getId());
			question.setVersion(0);
			question.setSetTeacherName(curUser.getName());
			question.setCreatedBy(curUser.getId());
			question.setRemark(General.removeHTMLCode(question.getRemark()));//把前端传入后端的脚本过滤
			question.setStatusCode(QuestionStatusEnum.QUESTION_DRAFT.getKey()); // 组卷保存的试题都是审核通过的试题
			
			insertQuestion(question);
			
			OperLog ol = new OperLog();
			ol.setObjId(question.getId());
			ol.setObjType(Consts.OperLogObjType.Question);
			ol.setOperCode("新建");
			ol.setRemark("组卷时新建，试题状态：草稿");
			ol.setCreatedBy(curUser.getId());
			dao.insert("addOperLog", ol);
			
			question = questionDao.searchById(question.getId());
//			// 记录日志
//			QuestionOperationLog qol = new QuestionOperationLog(
//					question.getId(), "insert", question.getVersion(),
//					question.getRemark(), question.getCreatedTime(),
//					question.getCreatedBy());
//			questionOperationLogService.recordLog(qol);
		}else{
//			List<Integer> inAllPaperIdList = dao.getList("statQuestionInAllPapers",question.getId());
//			Set<Integer> inAllPaperIdSet = new HashSet<Integer>(inAllPaperIdList);
//			inAllPaperIdSet.remove(paperId);
//			if(inAllPaperIdSet.size() > 0){//如果当前试题已经被其他试卷引用了，那么就需要复制一个出来新建，不能影响其他试卷内容
//				throw new BizException("当前试题已经被其他试卷引用了，不能修改");
//			}
			quesOperType = Consts.QuesOperTypeForZj.UPDATE_INTO_PAPER;
//			question.setStatusCode(QuestionStatusEnum.QUESTION_APPROVED.getKey()); // 组卷保存的试题都是审核通过的试题
			update(question);
			
			OperLog ol = new OperLog();
			ol.setObjId(question.getId());
			ol.setObjType(Consts.OperLogObjType.Question);
			ol.setOperCode("修改");
			ol.setRemark("组卷时修改了试题内容");
			ol.setCreatedBy(curUser.getId());
			dao.insert("addOperLog", ol);
			
			question = questionDao.searchById(question.getId());
//			lucene.deleteIndex(String.valueOf(question.getId()));//先删除相应id的索引 
//			// 记录日志
//			QuestionOperationLog qol = new QuestionOperationLog(question.getId(),
//					"update", question.getVersion(), question.getRemark(),
//					question.getCreatedTime(), question.getCreatedBy());
//			questionOperationLogService.recordLog(qol);
		}
		{
//			lucene.appendSingleIndex(question);		//新增试题即创建索引文件  存在一个问题新增试题成功后需返回questionId 然后处理索引信息
		}
		{
			questionOptionServiceImpl.deleteOptionsByQuesId(question.getId());
			// 增加选择表数据
			if (options != null && options.size() > 0) {
				for(QuestionOption qo:options){
					qo.setQuestionId(question.getId());
					questionOptionServiceImpl.saveQuestionOption(qo);
				}
			}
		}
		question = searchById(question.getId());
		question.setOptions(options);
		if(!checkAllowAddIntoPaper(question, paperId, quesTypeId, question.getScore(), quesOperType, paperDetailId)){
			throw new BizException("该题不符合细目表规则");
		}
		PaperDifficultyEstimateDetail pde = new PaperDifficultyEstimateDetail();
		pde.setEstimateDifficulty(Integer.parseInt(question.getEstimateDifficultyCode()));
		pde.setQuestionId(question.getId());
		pde.setPagerConstructId(paperId);
		pde.setQuestionType(quesTypeName);
		//更新及格分数预估表
		if(paperDifficultyEstimateService.checkPaperDifficultyEstimate(paperId)){
			paperDifficultyEstimateService.updatePaperDifficultyEstimateByQuestion(pde);
		}
		General.quesWordToHtmlForShow(question);
		return question;
	}
	
	public Question save(Question question,int curUserId) throws BizException{
		Date date = new Date();
		question.setUpdatedTime(date);
		question.setUpdatedBy(curUserId);
		QuestionType qt = questionTypeService.queryById(question.getTypeId());
		String quesTypeName = qt.getName();
		
		if(quesTypeName.equals("单项选择题") || quesTypeName.equals("单选题") || quesTypeName.equals("单选")
				|| quesTypeName.equals("多项选择题") || quesTypeName.equals("多选题") || quesTypeName.equals("多选")){
			question.setAnswerWord(new byte[]{});
			question.setAnswer("");
			question.setAnswerSummary("");
		}else{
			question.setOptions(new ArrayList<QuestionOption>());
		}
		if(question.getId() == null || question.getId() == 0){
//			question.setEnableTime(date);
			if(question.getSetTime() == null){
				question.setSetTime(date);
			}
//			question.setSetBy(curUser.getId());
			question.setVersion(0);
//			question.setSetTeacherName(curUser.getName());
			question.setCreatedBy(curUserId);
			question.setCreatedTime(date);
			question.setRemark(General.removeHTMLCode(question.getRemark()));//把前端传入后端的脚本过滤
			question.setStatusCode(QuestionStatusEnum.QUESTION_DRAFT.getKey()); // 命题状态
			
			insertQuestion(question);
			
			OperLog ol = new OperLog();
			ol.setObjId(question.getId());
			ol.setObjType(Consts.OperLogObjType.Question);
			ol.setOperCode("新建");
			User curUser = Consts.UserMap.get(curUserId);
			ol.setRemark("新建试题，试题状态：草稿");
			ol.setCreatedBy(curUser.getId());
			dao.insert("addOperLog", ol);
		}else{
//			if(isUsedByPaper(question.getId())){//如果当前试题已经被其他试卷引用了，那么就需要复制一个出来新建，不能影响其他试卷内容
//				throw new BizException("当前试题已经被试卷引用了，不能修改");
//			}else{
//				update(question);
//			}
			dao.update("updateEnableTime", question);
			update(question);
			
			OperLog ol = new OperLog();
			ol.setObjId(question.getId());
			ol.setObjType(Consts.OperLogObjType.Question);
			ol.setOperCode("修改");
			User curUser = Consts.UserMap.get(curUserId);
			ol.setRemark("修改了试题内容");
			ol.setCreatedBy(curUser.getId());
			dao.insert("addOperLog", ol);
		}
		List<QuestionOption> options = question.getOptions();
//		{
//			lucene.appendSingleIndex(question);		//新增试题即创建索引文件  存在一个问题新增试题成功后需返回questionId 然后处理索引信息
//		}
		{
			questionOptionServiceImpl.deleteOptionsByQuesId(question.getId());
			// 增加选择表数据
			if (options != null && options.size() > 0) {
				for(QuestionOption qo:options){
					qo.setQuestionId(question.getId());
					questionOptionServiceImpl.saveQuestionOption(qo);
				}
			}
		}
		question = searchById(question.getId());
		question.setOptions(options);
		return question;
	}
	
	public boolean isUsedByPaper(int quesId){
		Course course = courseService.findCourseByQuesId(quesId);
		if(course.getOpenEdit() != null && course.getOpenEdit().equals(1)){
			List<Integer> inAllPaperIdList = dao.getList("statQuestionInAllPapers",quesId);
			return inAllPaperIdList.size() > 1;
		}else{
			List<Integer> inAllPaperIdList = dao.getList("statQuestionInAllPapers",quesId);
			return inAllPaperIdList.size() > 0;
		}
	}
	
	public boolean isUsedByOtherPaper(int quesId,int paperId){
		List<Integer> inAllPaperIdList = dao.getList("statQuestionInAllPapers",quesId);
		Set<Integer> paperIdSet = new HashSet<Integer>(inAllPaperIdList);
		paperIdSet.remove(paperId);
		return paperIdSet.size() > 0;
	}
	
	
	
	public List<AuditorJob> statAuditorJobByCourseId(int courseId){
		Map<Integer,AuditorJob> jobMap = new HashMap<Integer, AuditorJob>();
		List<Map<String,Object>> statInfo = dao.getList("statAuditorJobByCourseId", courseId);
		for(Map<String,Object> stat:statInfo){
			int auditorId = General.toInteger(stat.get("auditorId"), 0);
			AuditorJob job = jobMap.get(auditorId);
			if(job == null){
				job = new AuditorJob();
				job.setAuditorId(auditorId);
				job.setAuditorName(Consts.UserMap.get(auditorId).getName());
				jobMap.put(auditorId, job);
			}
			String statusCode = (String)stat.get("statusCode");
			int num = General.toInteger(stat.get("num"), 0);
			if("QUESTION_APPROVED".equals(statusCode)){
				job.setApprovedNum(num);
			}else if("QUESTION_REJECTED".equals(statusCode)){
				job.setRejectedNum(num);
			}
		}
		List<AuditorJob> jobs = new ArrayList<AuditorJob>(jobMap.values());
		Collections.sort(jobs, new Comparator<AuditorJob>() {
			@Override
			public int compare(AuditorJob o1, AuditorJob o2) {
				int compareValue = o2.getApprovedNum() - o1.getApprovedNum();
				if(compareValue == 0){
					compareValue = o2.getRejectedNum() - o1.getRejectedNum();
				}
				return compareValue;
			}
		});
		AuditorJob total = new AuditorJob();
		total.setAuditorName("总计");
		for(AuditorJob job:jobs){
			total.setApprovedNum(total.getApprovedNum() + job.getApprovedNum());
			total.setRejectedNum(total.getRejectedNum() + job.getRejectedNum());
		}
		jobs.add(total);
		return jobs;
	}
	
	public int statDraftQuesNumByCourseId(int courseId){
		return General.toInteger(dao.get("statDraftQuesNumByCourseId", courseId), 0);
	}
	
	public List<AuditorJob> statMyAuditJob(int userId){
		Map<Integer,AuditorJob> jobMap = new HashMap<Integer, AuditorJob>();
		List<Course> myCourses = courseService.findToAuditCoursesByUserId(userId);
		for(Course c:myCourses){
			AuditorJob job = new AuditorJob();
			job.setCourseId(c.getId());
			job.setCourseName(c.getCode() + "-" + c.getName());
			job.setDraftNum(statDraftQuesNumByCourseId(c.getId()));
			jobMap.put(c.getId(), job);
		}
		
		List<Map<String,Object>> statInfo = dao.getList("statAuditorJobByAuditorId", userId);
		for(Map<String,Object> stat:statInfo){
			int courseId = General.toInteger(stat.get("courseId"), 0);
			AuditorJob job = jobMap.get(courseId);
			if(job == null){
				Course c = courseService.findById(courseId);
				job = new AuditorJob();
				job.setCourseId(c.getId());
				job.setCourseName(c.getCode() + "-" + c.getName());
				job.setDraftNum(statDraftQuesNumByCourseId(c.getId()));
				jobMap.put(c.getId(), job);
			}
			String statusCode = (String)stat.get("statusCode");
			int num = General.toInteger(stat.get("num"), 0);
			if("QUESTION_APPROVED".equals(statusCode)){
				job.setApprovedNum(num);
			}else if("QUESTION_REJECTED".equals(statusCode)){
				job.setRejectedNum(num);
			}
		}
		List<AuditorJob> jobs = new ArrayList<AuditorJob>(jobMap.values());
		Collections.sort(jobs, new Comparator<AuditorJob>() {
			@Override
			public int compare(AuditorJob o1, AuditorJob o2) {
				int compareValue = o2.getApprovedNum() - o1.getApprovedNum();
				if(compareValue == 0){
					compareValue = o2.getRejectedNum() - o1.getRejectedNum();
				}
				return compareValue;
			}
		});
		AuditorJob total = new AuditorJob();
		total.setCourseName("总计");
		for(AuditorJob job:jobs){
			total.setDraftNum(total.getDraftNum() + job.getDraftNum());
			total.setApprovedNum(total.getApprovedNum() + job.getApprovedNum());
			total.setRejectedNum(total.getRejectedNum() + job.getRejectedNum());
		}
		jobs.add(total);
		return jobs;
	}
	
	public List<InputerJob> statMyInputJob(int userId){
		List<InputerJob> jobs = new ArrayList<InputerJob>();
		InputQuesTask firstTask = dao.getObject("getCurInputTasksByInputerId", userId);
		List<Course> myCourses = courseService.findToInputCoursesByUserId(userId);
		for(Course c:myCourses){
			InputerJob job = new InputerJob();
			job.setCourseId(c.getId());
			job.setCourseName(c.getCode() + "-" + c.getName());
			Map<String,Object> param = new HashMap<String, Object>();
			param.put("courseId", job.getCourseId());
			param.put("inputerId", userId);
			param.put("planStartTime", firstTask.getPlanStartTime());
			param.put("planEndTime", firstTask.getPlanEndTime());
			job.setInputNum(General.toInteger(dao.get("statInputerJobByCourseIdAndInputerId", param), 0));
			jobs.add(job);
		}
		Collections.sort(jobs, new Comparator<InputerJob>() {
			@Override
			public int compare(InputerJob o1, InputerJob o2) {
				return o2.getInputNum() - o1.getInputNum();
			}
		});
		InputerJob total = new InputerJob();
		total.setCourseName("总计");
		for(InputerJob job:jobs){
			total.setInputNum(total.getInputNum() + job.getInputNum());
		}
		jobs.add(total);
		return jobs;
	}
	
	/**
	 * 导入试题Docx文件，解析并保存到数据库中，如果解析过程中出现异常，事务回滚。
	 * @param attId
	 * @return 导入过程记录的日志
	 * @throws DocumentException 
	 * @throws IOException 
	 */
	public List<String> impQuesFromDocx(int attId,User curUser) throws IOException, DocumentException{
		Date curTime = new Date(System.currentTimeMillis());
		List<String> logs = new ArrayList<String>();
		Attachment att = attachmentService.getAttachmentById(attId);
        DocxReader docxReader = null;
        InputStream is = null;
		User auditTeacher = null;
		Date auditTime = null;
		String courseCode = null;
		Course course = null;
        List<Question> quesList = new ArrayList<Question>();
		try {
			File impFile = new File(att.getStorePath());
			docxReader = new DocxReader(impFile);
			docxReader.doRead();
			File documentFile = docxReader.getDocumentInfo().getFile();
			SAXReader sax = new SAXReader();
			is = new FileInputStream(documentFile);
			Document document = sax.read(is);
			Element root = document.getRootElement();
			Element body = root.element("body");
			@SuppressWarnings("rawtypes")
			List children = body.elements();
			Book book = null;
			String setTeacherName = null;
			User setTeacher = null;
			String setTimeStr = null;
			Date setTime = null;
			String auditTeacherName = null;
			String auditTimeStr = null;
			
			{
				Element ele = (Element)children.get(0);
				String txt = extractText(ele);
				String courseStr = null;
				if(txt.startsWith("[课程]：") || txt.startsWith("[课程]:")){
					courseStr = txt.substring("[课程]：".length()).trim();
					if(courseStr.length() > 5){
						courseCode = courseStr.substring(0, 5);
					}else{
						courseCode = courseStr;
					}
					if(courseCode.length() != 5){
						logs.add("课程信息：" + courseStr + " 格式不正确，导入失败。");
						return logs;
					}
					course = courseService.findByCode(courseCode);
					if(course == null){
						logs.add("课程信息：" + courseStr + " 不存在，导入失败。");
						return logs;
					}
				}else{
					logs.add("第1行必须是课程信息，导入失败。");
        			return logs;
				}
				List<Book> books = dao.getList("findValidBooksInCourse", course.getId());
				if(books.size() == 0){
					logs.add("课程：" + courseStr + " 没有对应的启用状态的教材，请到课程管理里面进行配置。");
					return logs;
				}
				if(books.size() > 1){
					logs.add("课程：" + courseStr + " 的配置信息里面出现了多个启用状态的教材（只允许有一个），请到课程管理里面进行配置。");
					return logs;
				}
				book = books.get(0);
			}
			{
				Element ele = (Element)children.get(1);
				String txt = extractText(ele);
				if(txt.startsWith("[命题教师]：") || txt.startsWith("[命题教师]:")){
					setTeacherName = txt.substring("[命题教师]：".length()).trim();
					if(setTeacherName.length() == 0){
						logs.add("命题教师不能为空，导入失败。");
						return logs;
					}
					setTeacher = userService.queryByName(setTeacherName);
					if(setTeacher == null){
						logs.add("命题教师：" + setTeacherName + " 不存在，导入失败。");
		    			return logs;
					}
				}else{
					logs.add("第2行必须是命题教师信息，导入失败。");
        			return logs;
				}
			}

			{
				Element ele = (Element)children.get(2);
				String txt = extractText(ele);
				if(txt.startsWith("[命题时间]：") || txt.startsWith("[命题时间]:")){
					setTimeStr = txt.substring("[命题时间]：".length()).trim();
					if(setTimeStr.length() == 0){
						logs.add("命题时间不能为空，导入失败。");
						return logs;
					}
					setTime = DateHelper.parseString(setTimeStr.replace('/', '-'));
					if(setTime == null){
						logs.add("命题时间：" + setTimeStr + " 不能识别，导入失败。");
						return logs;
					}
				}else{
					logs.add("第3行必须是命题时间信息，导入失败。");
        			return logs;
				}
			}

			{
				Element ele = (Element)children.get(3);
				String txt = extractText(ele);
				if(txt.startsWith("[审题教师]：") || txt.startsWith("[审题教师]:")){
					auditTeacherName = txt.substring("[审题教师]：".length()).trim();
				}else{
					logs.add("第4行必须是审题教师信息，导入失败。");
        			return logs;
				}
			}

			{
				Element ele = (Element)children.get(4);
				String txt = extractText(ele);
				if(auditTimeStr == null && (txt.startsWith("[审题时间]：") || txt.startsWith("[审题时间]:"))){
					auditTimeStr = txt.substring("[审题时间]：".length()).trim();
					if(auditTimeStr.length() > 0){
						auditTime = DateHelper.parseString(auditTimeStr.replace('/', '-'));
						if(auditTime == null){
							logs.add("审题时间：" + auditTimeStr + " 不能识别，导入失败。");
							return logs;
						}
					}
				}else{
					logs.add("第5行必须是审题时间信息，导入失败。");
        			return logs;
				}
			}
			if(auditTeacherName.length() == 0 && auditTimeStr.length() > 0){
				logs.add("审题教师不能为空，导入失败。");
    			return logs;
			}
			if(auditTeacherName.length() > 0 && auditTimeStr.length() == 0){
				logs.add("审题时间不能为空，导入失败。");
    			return logs;
			}
			if(auditTeacherName.length() > 0){
				auditTeacher = userService.queryByName(auditTeacherName);
				if(auditTeacher == null){
					logs.add("审题教师：" + auditTeacherName + " 不存在，导入失败。");
	    			return logs;
				}
			}
			
			QuestionType quesType = null;
			int quesIndex = 0;
			int quesTypeIndex = 0;
			for(int i=5;i<children.size();i++){
				Element ele = (Element)children.get(i);
				String eleName = ele.getName();
				if(!eleName.equals("p") && !eleName.equals("tbl")){
					continue;
				}
				if(eleName.equals("p")){
					String txt = extractText(ele);
					if(txt.startsWith("[题型]：") || txt.startsWith("[题型]:")){
						String quesTypeStr = txt.substring("[题型]：".length()).trim();
						if(quesTypeStr.length() == 0){
							logs.add("第" + (quesTypeIndex + 1) + "个题型为空，导入失败。");
			    			return logs;
						}
						QuestionType quesTypeTmp = quesTypeService.findByName(quesTypeStr);
						if(quesTypeTmp == null){
							logs.add("第" + (quesTypeIndex + 1) + "个题型：" + quesTypeStr + " 系统中不存在，导入失败。");
							return logs;
						}
						quesTypeIndex++;
						Element scoreEle = (Element)children.get(i + 1);
						String scoreStr = extractText(scoreEle);
						if(scoreStr.length() == 0 || (!scoreStr.startsWith("[分数]：") && !scoreStr.startsWith("[分数]:"))){
							logs.add(txt + "的下一行未发现[分数]，导入失败。");
			    			return logs;
						}
						scoreStr = scoreStr.substring("[分数]：".length()).trim();
						if(scoreStr.length() == 0){
							logs.add(txt + "的[分数]不能为空，导入失败。");
			    			return logs;
						}
						int score = General.toInteger(scoreStr, 0);
						if(score == 0){
							logs.add(txt + "的[分数]必须为正整数，导入失败。");
			    			return logs;
						}
						quesType = quesTypeService.findByNameWithCreate(quesTypeStr, score, course.getId(), curUser);
						
						int quesIndexInType = 0;
						List<Element> quesEles = new ArrayList<Element>();
						for(int j=i+2;j<children.size();j++){
							Element eleJ = (Element)children.get(j);
							String txtJ = extractText(eleJ);
							if(txtJ.startsWith("[题型]：") || txtJ.startsWith("[题型]:")){
								if(quesEles.size() > 0){
									logs.add("解析[" + quesTypeStr + "]里面的第" + (quesIndexInType + 1) + "道试题（全局序号：" + (quesIndex + 1) + "）时出现异常，原因：试题信息不完整，导入失败。");
					    			return logs;
								}
								i = j - 1;
								break;
							}
							if(quesEles.size() == 0){
								if(eleJ.getName().equals("p")){
									if(txtJ.length() > 0){
										removeSortNo(eleJ);
										quesEles.add(eleJ);
									}
								}
							}else{
								quesEles.add(eleJ);
								if(txtJ.startsWith("[预计难度]：") || txtJ.startsWith("[预计难度]:")){
									Question ques = null;
									try{
										log.debug("正在解析[" + quesTypeStr + "]里面的第" + (quesIndexInType + 1) + "道试题（全局序号：" + (quesIndex + 1) + "）...");
										ques = dealQuesUnit(quesEles, impFile, quesType);
									}catch(Exception e){
										logs.add("解析[" + quesTypeStr + "]里面的第" + (quesIndexInType + 1) + "道试题（全局序号：" + (quesIndex + 1) + "）时出现异常，原因：" + e.getMessage() +"，导入失败。");
						    			return logs;
									}
									quesIndex++;
									quesIndexInType++;
									ques.setCourseId(course.getId());
									ques.setBookId(book.getId());
									ques.setTypeId(quesType.getId());
									ques.setScore(score + 0.0);
									ques.setSetTime(setTime);
									ques.setSetTeacherName(setTeacherName);
									ques.setCreatedTime(curTime);
									ques.setCreatedBy(curUser.getId());
									ques.setUpdatedTime(curTime);
									ques.setUpdatedBy(curUser.getId());
									if(auditTeacher != null){
										ques.setStatusCode(QuestionStatusEnum.QUESTION_APPROVED.getKey());
									}else{
										ques.setStatusCode(QuestionStatusEnum.QUESTION_DRAFT.getKey());
									}
									ques.setVersion(0);
									ques.setRemark("word导入，时间：" + DateHelper.formatDate(curTime));
									quesList.add(ques);
									quesEles = new ArrayList<Element>();
								}
							}
						}
					}
				}
				
			}
		} finally{
			General.close(is);
			General.release(docxReader);
		}
		{
			att.setStatus(Consts.AttachmentStatus.USING);
	    	att.setUpdatedBy(curUser.getId());
	    	dao.update("updateAttachment", att);
			QuestionImportHistory impHis = new QuestionImportHistory();
			impHis.setFileName(att.getOriginalName());
			impHis.setFileId(att.getId());
			impHis.setQuestionCount(quesList.size());
			impHis.setRemark(curUser.getName() + "于" + DateHelper.formatDate(curTime) + "导入了" + quesList.size() +"道试题。");
			impHis.setImportTime(curTime);
			impHis.setImportBy(curUser.getId());
			dao.insert("insertQuestionImportHistory", impHis);
			for(Question ques:quesList){
				ques.setImportId(impHis.getId());
				ques.setQuestionNo(General.getNewQuestionNo(courseCode));
				insertQuestion(ques);
				if(auditTeacher != null){
					OperLog ol = new OperLog();
					ol.setObjId(ques.getId());
					ol.setObjType(Consts.OperLogObjType.Question);
					ol.setOperCode("审核通过");
					ol.setRemark("word导入，时间：" + DateHelper.formatDate(curTime));
					ol.setCreatedBy(auditTeacher.getId());
					ol.setCreatedTime(auditTime);
					dao.insert("addOperLog", ol);
				}
				{
					OperLog ol = new OperLog();
					ol.setObjId(ques.getId());
					ol.setObjType(Consts.OperLogObjType.Question);
					ol.setOperCode("导入");
					ol.setRemark("导入试题，试题状态：" + ((auditTeacher != null)?"审核通过":"草稿"));
					ol.setCreatedBy(curUser.getId());
					ol.setCreatedTime(curTime);
					dao.insert("addOperLog", ol);
				}
				if (ques.getOptions() != null && ques.getOptions().size() > 0) {
					for(QuestionOption qo:ques.getOptions()){
						qo.setQuestionId(ques.getId());
						questionOptionServiceImpl.saveQuestionOption(qo);
					}
				}
			}
		}
		logs.add("成功导入：" + quesList.size() + " 道试题，耗时：" + String.format("%.2f", (System.currentTimeMillis() - curTime.getTime())/1000.0) + "秒");
		return logs;
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static Question dealQuesUnit(List<Element> quesEles,File quesFile,QuestionType quesType) throws BizException{
		Question ques = new Question();
		List<QuestionOption> opts = null;
		boolean isOneChoice = quesType.getCorrectOptionsNum() == 1;
		boolean isMultipleChoice = quesType.getCorrectOptionsNum() == 2;
		boolean isXzt = isOneChoice || isMultipleChoice;//是否是选择题
        if(isXzt){
        	opts = new ArrayList<QuestionOption>();
        	ques.setOptions(opts);
        }
		int optIndex = 0;//选项的开始索引值
		int answerIndex = 0;//答案的开始索引值
		int zjmyIndex = 0;//章节目页的开始索引值
		{//获取题干Word数据
			File newDocx = null;
	        DocxReader templetDocxReader = null;
	        Map<String, ResourceInfo> needToUpdate = new HashMap<String, ResourceInfo>();
            File newQuesFile = null;
	        try {
	            {// 拷贝一份模板文件到临时文件夹里面
	                FileOutputStream fos = null;
	                InputStream is = null;
	                try {
	                    newDocx = new File(ParamUtils.TemporaryDirPath, UUID.randomUUID().toString().replace("-", ""));
	                    fos = new FileOutputStream(newDocx);
	                    is = ZujuanService.class.getResourceAsStream("/paperTmps/quesTmp.docx");
	                    IOUtils.copyLarge(is, fos);
	                    fos.flush();
	                } finally {
	                    General.close(is);
	                    General.close(fos);
	                }
	            }

	            templetDocxReader = new DocxReader(newDocx);
	            templetDocxReader.doRead();
	            needToUpdate.put(templetDocxReader.getDocumentInfo().getPathInZip(),templetDocxReader.getDocumentInfo());
	            
	            List bodyEles = new ArrayList();
	            for(int i=0;i<quesEles.size();i++){
	            	Element eleJ = quesEles.get(i);
	            	String txtJ = extractText(eleJ);
	            	if(isXzt){
	            		if(txtJ.startsWith("A.") || txtJ.startsWith("A．")){
	            			optIndex = i;
	            			break;
	            		}
	            	}else if(txtJ.startsWith("[答案]：") || txtJ.startsWith("[答案]:")){
						answerIndex = i;
						break;
					}
					bodyEles.add(eleJ.clone());
	            }
	            trimBlankLine(bodyEles);
	            if(bodyEles.size() == 0){
            		throw new BizException("题干不能为空");
            	}
	            {
	            	newQuesFile = copyFileIntoTmpDir(quesFile);
	            	DocxReader newQuesDocxReader = null;
	                InputStream newQuesDocFileIs = null;
	                OutputStream newQuesDocFileOs = null;
	                Map<String, ResourceInfo> newQuesDocxNeedToUpdate = new HashMap<String, ResourceInfo>();
	        		try {
	        			newQuesDocxReader = new DocxReader(newQuesFile);
	        			newQuesDocxReader.doRead();
	        			File documentFile = newQuesDocxReader.getDocumentInfo().getFile();
	        			SAXReader sax = new SAXReader();
	        			newQuesDocFileIs = new FileInputStream(documentFile);
	        			Document document = sax.read(newQuesDocFileIs);
	        			Element root = document.getRootElement();
	        			Element body = root.element("body");
	        			body.setContent(bodyEles);
	        			newQuesDocFileOs = new FileOutputStream(documentFile,false);
	        			IOUtils.write(document.asXML(), newQuesDocFileOs, "UTF-8");
	        			newQuesDocxNeedToUpdate.put(newQuesDocxReader.getDocumentInfo().getPathInZip(),newQuesDocxReader.getDocumentInfo());
	        			DocxReader.updateDocx(newQuesFile, newQuesDocxNeedToUpdate);
	        		}finally {
	        			General.close(newQuesDocFileIs);
	        			General.close(newQuesDocFileOs);
	    	            General.release(newQuesDocxReader);
	    	        }
	            }
	            {
	            	File document = templetDocxReader.getDocumentInfo().getFile();
	            	InputStream is = null;
	            	FileOutputStream fos = null;
	            	InputStream newQuesFileIs = null;
	            	byte[] newQuesFileBytes = null;
	            	try {
	            		newQuesFileIs = new FileInputStream(newQuesFile);
	            		newQuesFileBytes = IOUtils.toByteArray(newQuesFileIs);
	            		String quesStr = General.dealWord(newQuesFileBytes, templetDocxReader, needToUpdate);
	            		is = new FileInputStream(document);
	                    String docStr = IOUtils.toString(is, "UTF-8");
	                    docStr = docStr.replace("<w:body>", "<w:body>" + quesStr);
	                    fos = new FileOutputStream(document, false);
	                    IOUtils.write(docStr, fos, "UTF-8");
	                    fos.flush();
	                    DocxReader.updateDocx(newDocx, needToUpdate);
					} finally {
	        			General.close(is);
	        			General.close(fos);
	        			General.close(newQuesFileIs);
					}
	            }
	            {
	            	InputStream is = null;
	            	try {
	            		is = new FileInputStream(newDocx);
	            		ques.setBodyWord(IOUtils.toByteArray(is));
	            		ques.setBody(HtmlCreator.covertToHtml(ques.getBodyWord()).toString());
	            		ques.setBodySummary(General.extractText(ques.getBody()));
					} finally {
	        			General.close(is);
					}
	            }
	            
	        } catch (Exception e) {
	        	e.printStackTrace();
	            throw new BizException(e.getMessage(), e);
	        } finally {
	        	General.delete(newQuesFile);
	        	General.delete(newDocx);
	            General.release(templetDocxReader);
	        }
		}
		if(isXzt){
			if(optIndex == 0){
				throw new BizException("未发现选项信息");
			}
			char optChar = 'A';
			int i = 0;
			String anStr = null;//答案字符串
			while(true){//获取选项Word数据
				if(optIndex + i > quesEles.size() - 1){
					break;
				}
            	QuestionOption opt = new QuestionOption();
            	opt.setOptionNo(i);
            	opt.setIsCorrectOption(0);
				File newDocx = null;
		        DocxReader templetDocxReader = null;
		        Map<String, ResourceInfo> needToUpdate = new HashMap<String, ResourceInfo>();
	            File optFile = null;
		        try {
	            	List optEles = new ArrayList();
		        	{
						Element eleJ = quesEles.get(optIndex + i);
						if(isBlankLine(eleJ)){
							i++;
							continue;
						}
		            	String txtJ = extractText(eleJ);
		            	if(txtJ.startsWith("[答案]：") || txtJ.startsWith("[答案]:")){
							answerIndex = optIndex + i;
							anStr = txtJ.substring("[答案]：".length());
							break;
						}
		            	if(!txtJ.startsWith(optChar + ".") && !txtJ.startsWith(optChar + "．")){
		            		throw new BizException("无法识别选项数据里面的第" + (i + 1) + "行，预期此行应该是以“" + (optChar + ".") + "”开头");
		            	}
		            	optEles.add(dealOptPrefixWord(eleJ));
		            	trimBlankLine(optEles);
		            	if(optEles.size() == 0){
		            		throw new BizException("选项" + optChar + "的值不能为空");
		            	}
		        	}
		            {// 拷贝一份模板文件到临时文件夹里面
		                FileOutputStream fos = null;
		                InputStream is = null;
		                try {
		                    newDocx = new File(ParamUtils.TemporaryDirPath, UUID.randomUUID().toString().replace("-", ""));
		                    fos = new FileOutputStream(newDocx);
		                    is = ZujuanService.class.getResourceAsStream("/paperTmps/quesTmp.docx");
		                    IOUtils.copyLarge(is, fos);
		                    fos.flush();
		                } finally {
		                    General.close(is);
		                    General.close(fos);
		                }
		            }

		            templetDocxReader = new DocxReader(newDocx);
		            templetDocxReader.doRead();
		            needToUpdate.put(templetDocxReader.getDocumentInfo().getPathInZip(),templetDocxReader.getDocumentInfo());
		            
		            {
		            	optFile = copyFileIntoTmpDir(quesFile);
		            	DocxReader newQuesDocxReader = null;
		                InputStream newQuesDocFileIs = null;
		                OutputStream newQuesDocFileOs = null;
		                Map<String, ResourceInfo> newQuesDocxNeedToUpdate = new HashMap<String, ResourceInfo>();
		        		try {
		        			newQuesDocxReader = new DocxReader(optFile);
		        			newQuesDocxReader.doRead();
		        			File documentFile = newQuesDocxReader.getDocumentInfo().getFile();
		        			SAXReader sax = new SAXReader();
		        			newQuesDocFileIs = new FileInputStream(documentFile);
		        			Document document = sax.read(newQuesDocFileIs);
		        			Element root = document.getRootElement();
		        			Element body = root.element("body");
		        			body.setContent(optEles);
		        			newQuesDocFileOs = new FileOutputStream(documentFile,false);
		        			IOUtils.write(document.asXML(), newQuesDocFileOs, "UTF-8");
		        			newQuesDocxNeedToUpdate.put(newQuesDocxReader.getDocumentInfo().getPathInZip(),newQuesDocxReader.getDocumentInfo());
		        			DocxReader.updateDocx(optFile, newQuesDocxNeedToUpdate);
		        		}finally {
		        			General.close(newQuesDocFileIs);
		        			General.close(newQuesDocFileOs);
		    	            General.release(newQuesDocxReader);
		    	        }
		            }
		            {
		            	File document = templetDocxReader.getDocumentInfo().getFile();
		            	InputStream is = null;
		            	FileOutputStream fos = null;
		            	InputStream newQuesFileIs = null;
		            	byte[] newQuesFileBytes = null;
		            	try {
		            		newQuesFileIs = new FileInputStream(optFile);
		            		newQuesFileBytes = IOUtils.toByteArray(newQuesFileIs);
		            		String quesStr = General.dealWord(newQuesFileBytes, templetDocxReader, needToUpdate);
		            		is = new FileInputStream(document);
		                    String docStr = IOUtils.toString(is, "UTF-8");
		                    docStr = docStr.replace("<w:body>", "<w:body>" + quesStr);
		                    fos = new FileOutputStream(document, false);
		                    IOUtils.write(docStr, fos, "UTF-8");
		                    fos.flush();
		                    DocxReader.updateDocx(newDocx, needToUpdate);
						} finally {
		        			General.close(is);
		        			General.close(fos);
		        			General.close(newQuesFileIs);
						}
		            }
		            {
		            	InputStream is = null;
		            	try {
		            		is = new FileInputStream(newDocx);
		            		opt.setContentWord(IOUtils.toByteArray(is));
		            		opt.setContent(HtmlCreator.covertToHtml(opt.getContentWord()).toString());
		            		opt.setSummary(General.extractText(opt.getContent()));
		                	opts.add(opt);
						} finally {
		        			General.close(is);
						}
		            }
		            
		        } catch (BizException e) {
		            throw e;
		        } catch (Exception e) {
		            throw new BizException(e.getMessage(), e);
		        } finally {
		        	General.delete(optFile);
		        	General.delete(newDocx);
		            General.release(templetDocxReader);
		        }
		        i++;
		        optChar = (char)(optChar + 1);
			}
			if(isOneChoice){
				if(opts.size() != 4){
					throw new BizException("当前选项数量为：" + opts.size() + "，应该是4个");
				}
				if(!anStr.equals("A") && !anStr.equals("B") && !anStr.equals("C") && !anStr.equals("D")){
					throw new BizException("无法解析[答案]：" + anStr);
				}
				int corIndex = anStr.charAt(0) - 'A';
				opts.get(corIndex).setIsCorrectOption(1);
			}
			if(isMultipleChoice){
				if(opts.size() != 5){
					throw new BizException("当前选项数量为：" + opts.size() + "，应该是5个");
				}
				Pattern pattern = Pattern.compile("([A-Z]{1})");
				Matcher matcher = pattern.matcher(anStr);
				Set<String> anSet = new HashSet<String>();
				while(matcher.find()){
					String anTmp = matcher.group(1);
					if(!anTmp.equals("A") && !anTmp.equals("B") && !anTmp.equals("C") && !anTmp.equals("D") && !anTmp.equals("E")){
						throw new BizException("无法解析[答案]：" + anStr);
					}
					anSet.add(anTmp);
				}
				if(anSet.size() == 0){
					throw new BizException("无法解析[答案]：" + anStr);
				}
				for(String anTmp:anSet){
					int corIndex = anTmp.charAt(0) - 'A';
					opts.get(corIndex).setIsCorrectOption(1);
				}
			}
			try{
				for(int j=answerIndex + 1;j<quesEles.size();j++){
	            	Element eleJ = quesEles.get(j);
	            	String txtJ = extractText(eleJ);
					if(txtJ.startsWith("[章节目页]：") || txtJ.startsWith("[章节目页]:")){
						zjmyIndex = j;
						break;
					}
	            }
			} catch (Exception e) {
	            throw new BizException(e.getMessage(), e);
	        }
		}
		if(!isXzt){//获取答案Word数据
			if(answerIndex == 0){
				throw new BizException("未发现答案信息");
			}
			File newDocx = null;
	        DocxReader templetDocxReader = null;
	        Map<String, ResourceInfo> needToUpdate = new HashMap<String, ResourceInfo>();
            File anFile = null;
	        try {
	            {// 拷贝一份模板文件到临时文件夹里面
	                FileOutputStream fos = null;
	                InputStream is = null;
	                try {
	                    newDocx = new File(ParamUtils.TemporaryDirPath, UUID.randomUUID().toString().replace("-", ""));
	                    fos = new FileOutputStream(newDocx);
	                    is = ZujuanService.class.getResourceAsStream("/paperTmps/quesTmp.docx");
	                    IOUtils.copyLarge(is, fos);
	                    fos.flush();
	                } finally {
	                    General.close(is);
	                    General.close(fos);
	                }
	            }

	            templetDocxReader = new DocxReader(newDocx);
	            templetDocxReader.doRead();
	            needToUpdate.put(templetDocxReader.getDocumentInfo().getPathInZip(),templetDocxReader.getDocumentInfo());
	            
	            List answerEles = new ArrayList();
	            for(int i=answerIndex;i<quesEles.size();i++){
	            	Element eleJ = quesEles.get(i);
	            	if(i == answerIndex){
	            		Element eleJ2 = dealAnswerPrefixWord(eleJ);
	            		if(eleJ2 != null){
	            			answerEles.add(eleJ2);
	            		}
	            	}else{
		            	String txtJ = extractText(eleJ);
						if(txtJ.startsWith("[章节目页]：") || txtJ.startsWith("[章节目页]:")){
							zjmyIndex = i;
							break;
						}
						answerEles.add(eleJ.clone());
	            	}
	            }
	            trimBlankLine(answerEles);
	            if(answerEles.size() == 0){
            		throw new BizException("答案不能为空");
            	}
	            {
	            	anFile = copyFileIntoTmpDir(quesFile);
	            	DocxReader newQuesDocxReader = null;
	                InputStream newQuesDocFileIs = null;
	                OutputStream newQuesDocFileOs = null;
	                Map<String, ResourceInfo> newQuesDocxNeedToUpdate = new HashMap<String, ResourceInfo>();
	        		try {
	        			newQuesDocxReader = new DocxReader(anFile);
	        			newQuesDocxReader.doRead();
	        			File documentFile = newQuesDocxReader.getDocumentInfo().getFile();
	        			SAXReader sax = new SAXReader();
	        			newQuesDocFileIs = new FileInputStream(documentFile);
	        			Document document = sax.read(newQuesDocFileIs);
	        			Element root = document.getRootElement();
	        			Element body = root.element("body");
	        			body.setContent(answerEles);
	        			newQuesDocFileOs = new FileOutputStream(documentFile,false);
	        			IOUtils.write(document.asXML(), newQuesDocFileOs, "UTF-8");
	        			newQuesDocxNeedToUpdate.put(newQuesDocxReader.getDocumentInfo().getPathInZip(),newQuesDocxReader.getDocumentInfo());
	        			DocxReader.updateDocx(anFile, newQuesDocxNeedToUpdate);
	        		}finally {
	        			General.close(newQuesDocFileIs);
	        			General.close(newQuesDocFileOs);
	    	            General.release(newQuesDocxReader);
	    	        }
	            }
	            {
	            	File document = templetDocxReader.getDocumentInfo().getFile();
	            	InputStream is = null;
	            	FileOutputStream fos = null;
	            	InputStream newQuesFileIs = null;
	            	byte[] newQuesFileBytes = null;
	            	try {
	            		newQuesFileIs = new FileInputStream(anFile);
	            		newQuesFileBytes = IOUtils.toByteArray(newQuesFileIs);
	            		String quesStr = General.dealWord(newQuesFileBytes, templetDocxReader, needToUpdate);
	            		is = new FileInputStream(document);
	                    String docStr = IOUtils.toString(is, "UTF-8");
	                    docStr = docStr.replace("<w:body>", "<w:body>" + quesStr);
	                    fos = new FileOutputStream(document, false);
	                    IOUtils.write(docStr, fos, "UTF-8");
	                    fos.flush();
	                    DocxReader.updateDocx(newDocx, needToUpdate);
					} finally {
	        			General.close(is);
	        			General.close(fos);
	        			General.close(newQuesFileIs);
					}
	            }
	            {
	            	InputStream is = null;
	            	try {
	            		is = new FileInputStream(newDocx);
	            		ques.setAnswerWord(IOUtils.toByteArray(is));
	            		ques.setAnswer(HtmlCreator.covertToHtml(ques.getAnswerWord()).toString());
	            		ques.setAnswerSummary(General.extractText(ques.getAnswer()));
					} finally {
	        			General.close(is);
					}
	            }
	            
	        } catch (BizException e) {
	            throw e;
	        } catch (Exception e) {
	            throw new BizException(e.getMessage(), e);
	        } finally {
	        	General.delete(anFile);
	        	General.delete(newDocx);
	            General.release(templetDocxReader);
	        }
		}
		try{//处理后面属性值
			if(zjmyIndex > 0){//处理章节目页
				Element ele = (Element)quesEles.get(zjmyIndex);
				String txt = extractText(ele);
				String zjmyStr = txt.substring("[章节目页]：".length()).trim();
				if(zjmyStr.length() == 0){
					throw new BizException("[章节目页]信息为空");
				}
				String[] zjmyArr = zjmyStr.split("-");
				if(zjmyArr.length != 4){
					throw new BizException("无法解析[章节目页]：" + zjmyStr);
				}
				{
					int chapter = General.toInteger(zjmyArr[0], 0);
					if(chapter <= 0){
						throw new BizException("[章节目页]里面的章必须正整数");
					}
					ques.setChapter(chapter + "");
				}
				{
					int section = General.toInteger(zjmyArr[1], 0);
					if(section <= 0){
						throw new BizException("[章节目页]里面的节必须正整数");
					}
					ques.setSection(section + "");
				}
				{
					int item = General.toInteger(zjmyArr[2], -1);
					if(item < 0){
						throw new BizException("[章节目页]里面的目必须0或者正整数");
					}
					ques.setItem(item + "");
				}
				{
					int page = General.toInteger(zjmyArr[3], -1);
					if(page < 0){
						throw new BizException("[章节目页]里面的页必须0或者正整数");
					}
					ques.setPage(page + "");
				}
			}else{
				throw new BizException("未发现[章节目页]信息");
			}
			{//处理能力要求
				Element ele = (Element)quesEles.get(zjmyIndex + 1);
				String txt = extractText(ele);
				if(txt.startsWith("[能力要求]：") || txt.startsWith("[能力要求]:")){
					String abilityStr = txt.substring("[能力要求]：".length()).trim();
					if(abilityStr.length() == 0){
						throw new BizException("[能力要求]信息为空");
					}
					if(!abilityStr.equals("A") && !abilityStr.equals("B") && !abilityStr.equals("C") && !abilityStr.equals("D")){
						throw new BizException("无法解析[能力要求]：" + abilityStr);
					}
					ques.setAbilityCode(abilityStr);
				}else{
					throw new BizException("未在[章节目页]的下一行发现[能力要求]");
				}
			}
			{//处理考核要求
				Element ele = (Element)quesEles.get(zjmyIndex + 2);
				String txt = extractText(ele);
				if(txt.startsWith("[考核要求]：") || txt.startsWith("[考核要求]:")){
					String assessmentDemandStr = txt.substring("[考核要求]：".length()).trim();
					if(assessmentDemandStr.length() == 0){
						throw new BizException("[考核要求]信息为空");
					}
					if(!assessmentDemandStr.equals("1") && !assessmentDemandStr.equals("2") && !assessmentDemandStr.equals("3")){
						throw new BizException("无法解析[考核要求]：" + assessmentDemandStr);
					}
					ques.setAssessmentDemand((char)(General.toInteger(assessmentDemandStr, 0) - 1 + 'A') + "");
				}else{
					throw new BizException("未在[能力要求]的下一行发现[考核要求]");
				}
			}
			{//处理预计难度
				Element ele = (Element)quesEles.get(zjmyIndex + 3);
				String txt = extractText(ele);
				if(txt.startsWith("[预计难度]：") || txt.startsWith("[预计难度]:")){
					String diffStr = txt.substring("[预计难度]：".length()).trim();
					if(diffStr.length() == 0){
						throw new BizException("[预计难度]信息为空");
					}
					if(!diffStr.equals("A") && !diffStr.equals("B") && !diffStr.equals("C") && !diffStr.equals("D")){
						throw new BizException("无法解析[预计难度]：" + diffStr);
					}
					ques.setEstimateDifficultyCode((diffStr.charAt(0) - 'A' + 1)*10 + "");
				}else{
					throw new BizException("未在[考核要求]的下一行发现[预计难度]");
				}
			}
		} catch (BizException e) {
            throw e;
        } catch (Exception e) {
            throw new BizException(e.getMessage(), e);
        }
		return ques;
	}
	
	/**
	 * 截取掉选项的第一行里面的“A.”标识，并返回新的克隆节点
	 * @param ele
	 * @return
	 * @throws IOException
	 * @throws DocumentException
	 */
	private static Element dealOptPrefixWord(Element ele) throws IOException, DocumentException{
		Element eleTmp = (Element)ele.clone();
		@SuppressWarnings("rawtypes")
		List tNodes = eleTmp.selectNodes("w:r/w:t");
		String prefixWord = "A.";
		for(int i=0;i<tNodes.size();i++){
			Element tNode = (Element)tNodes.get(i);
			String tStr = tNode.getText().trim();
			if(tStr.length() >= prefixWord.length()){
				tStr = tStr.substring(prefixWord.length());
				prefixWord = "";
			}else{
				prefixWord = prefixWord.substring(tStr.length());
				tStr = "";
			}
			if(tStr.length() == 0){
				tNode.getParent().getParent().remove(tNode.getParent());
			}else{
				tNode.setText(tStr);
			}
			if(prefixWord.length() == 0){
				break;
			}
		}
		return eleTmp;
	}
	
	/**
	 * 截取掉答案的第一行里面的“[答案]：”标识，并返回新的克隆节点
	 * @param ele
	 * @return
	 * @throws IOException
	 * @throws DocumentException
	 */
	private static Element dealAnswerPrefixWord(Element ele) throws IOException, DocumentException{
		Element eleTmp = (Element)ele.clone();
		@SuppressWarnings("rawtypes")
		List tNodes = eleTmp.selectNodes("w:r/w:t");
		String prefixWord = "[答案]：";
		for(int i=0;i<tNodes.size();i++){
			Element tNode = (Element)tNodes.get(i);
			String tStr = tNode.getText().trim();
			if(tStr.length() >= prefixWord.length()){
				tStr = tStr.substring(prefixWord.length());
				prefixWord = "";
			}else{
				prefixWord = prefixWord.substring(tStr.length());
				tStr = "";
			}
			if(tStr.length() == 0){
				tNode.getParent().getParent().remove(tNode.getParent());
			}else{
				tNode.setText(tStr);
			}
			if(prefixWord.length() == 0){
				break;
			}
		}
		return eleTmp;
	}
	
	/**
	 * 删除首尾的空行
	 * @param eles
	 * @throws IOException
	 * @throws DocumentException
	 */
	@SuppressWarnings("rawtypes")
	private static void trimBlankLine(List eles) throws IOException, DocumentException{
		while(eles.size() > 0){
			Element tNode = (Element)eles.get(eles.size() - 1);
			if(isBlankLine(tNode)){
				eles.remove(eles.size() - 1);
			}else{
				break;
			}
		}
		while(eles.size() > 0){
			Element tNode = (Element)eles.get(0);
			if(isBlankLine(tNode)){
				eles.remove(0);
			}else{
				break;
			}
		}
	}
	
	/**
	 * 是否是空行
	 * @param tNode
	 * @return
	 * @throws IOException
	 * @throws DocumentException
	 */
	private static boolean isBlankLine(Element tNode) throws IOException, DocumentException{
		return tNode.getName().equals("p") && tNode.elements("r").size() == 0 && extractText(tNode).length() == 0;
	}
	
	/**
	 * 去掉一行前面的序号。必须是Word里面的“编号”
	 * @param ele
	 */
	private static void removeSortNo(Element ele){
		Element pPr = ele.element("pPr");
		if(pPr != null){
			@SuppressWarnings("rawtypes")
			List children = pPr.elements();
			for(int i=0;i<children.size();i++){
				Element child = (Element)children.get(i);
				String tagName = child.getName();
				if(tagName.equals("pStyle") || tagName.equals("numPr") || tagName.equals("ind")){
					children.remove(i);
					i--;
				}
			}
			pPr.setContent(children);
		}
	}
	
	private static File copyFileIntoTmpDir(File file) throws IOException{
		File newFile = General.newTmpFile();
		FileUtils.copyFile(file, newFile);
		return newFile;
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static String extractText(Element ele) throws IOException, DocumentException{
		String xtStrTmp = Consts.PAPERTMPBODY_START + Consts.PAPERTMPBODY_END;
        SAXReader saxTmp = new SAXReader();
        ByteArrayInputStream isTmp = new ByteArrayInputStream(xtStrTmp.getBytes("UTF-8"));
        Document documentTmp = saxTmp.read(isTmp);
        Element rootTmp = documentTmp.getRootElement();
        List cTmp = rootTmp.elements();
        cTmp.add(ele.clone());
        rootTmp.setContent(cTmp);
        xtStrTmp = documentTmp.asXML();
        int firstWtBeginIndex = xtStrTmp.indexOf("<paperTmpBody>");
    	int firstWtBeginIndex2 = xtStrTmp.indexOf("<paperTmpBody ");
    	firstWtBeginIndex = (firstWtBeginIndex < 0 || (firstWtBeginIndex2 >= 0 && firstWtBeginIndex > firstWtBeginIndex2))?firstWtBeginIndex2:firstWtBeginIndex;
    	int nearestGtIndex = xtStrTmp.indexOf('>', firstWtBeginIndex + 1);
    	
        xtStrTmp = xtStrTmp.substring(nearestGtIndex + 1, xtStrTmp.length() - Consts.PAPERTMPBODY_END.length());
        return General.extractText(xtStrTmp).trim();
	}
	
	/**
	 * 
	 * <p>查询相似题目</p>
	 * @author songyue
	 * @date 2016-05-20
	 */
	public List<SimilarQuestion> queryQuestionSimilarity(QuestionSimilarityQueryParams question) {
		return questionSimilarityMapper.queryQuestionSimilarity(question);
	}
	
	/**
	 * 
	 * <p>查询相似题目数量</p>
	 * @author songyue
	 * @date 2016-05-20
	 */
	public int queryQuestionSimilarityCount(QuestionSimilarityQueryParams question) {
		return questionSimilarityMapper.queryQuestionSimilarityCount(question);
	}
	
	/**
	 * <p>清空相似题目</p>
	 * @author songyue
	 * @date 2016-05-20
	 */
	public int delAllQuestionSimilarity(){
		return dao.delete("delAllQuestionSimilarity", null);
	}
	
	/**
	 * <p>新增相似题目</p>
	 * @author songyue
	 * @date 2016-05-20
	 */
	public int addQuestionSimilarity(List<SimilarQuestion> list){
		return dao.insert("addQuestionSimilarity", list);
	}
	
	/**
	 * <p>检查相似度</p>
	 * @author songyue
	 * @date 2016-05-20
	 */
	public List<SimilarQuestion> checkSimilarity() {
		List<Question> quesList = findAll();
		List<SimilarQuestion> similarQuestionList = new ArrayList<SimilarQuestion>();
		List<SimilarQuestion> resultList = new ArrayList<SimilarQuestion>();
		for (Question ques : quesList) {
			List<QuestionOption> quesOption = ques.getOptions();
			String option = "";
			if (quesOption != null && quesOption.size() > 0) {
				for (QuestionOption op : quesOption) {
					option += op.getSummary();
				}
			}
			if (ques != null && !StringUtils.isBlank(ques.getQuestionNo())
					&& !StringUtils.isBlank(ques.getBodySummary())) {
				SimilarQuestion similarQuestion = new SimilarQuestion();
				similarQuestion.setSimilarQuestionId(ques.getId().toString());
				similarQuestion.setSimilarQuestionNo(ques.getQuestionNo());
				similarQuestion.setSimilarQuestionBody(ques.getBodySummary() + option);
				similarQuestion.setCourseName(ques.getCourseName());
				similarQuestion.setBookName(ques.getBookName());
				similarQuestion.setTypeName(ques.getTypeName());
				similarQuestion.setCourseId(ques.getCourseId());
				similarQuestion.setCourseNo(ques.getCourseNo());
				similarQuestionList.add(similarQuestion);
			}
		}
		//及时释放对象
		quesList.removeAll(quesList);
		quesList = null;
		int size = similarQuestionList.size();
		double similarity = 0;
		for (int i = 0; i < size; i++) {
			for (int j = i + 1; j < size; j++) {
				//System.out.println("i:"+i+"j:"+j);
				SimilarQuestion similarques1 = similarQuestionList.get(i);
				SimilarQuestion similarques2 = similarQuestionList.get(j);
				// 检查同课程、同教材、同题型下的试题
				if (similarques1.getCourseName().equals(similarques2.getCourseName())
						&& similarques1.getTypeName().equals(similarques2.getTypeName())) {
					if ((similarity = StringSimilarityUtils.getSimilarityWithCosinesBySeg(
							similarques1.getSimilarQuestionBody(), similarques2.getSimilarQuestionBody())
							* 100) >= 60) {
						SimilarQuestion similarResult = new SimilarQuestion();
						similarResult.setSimilarQuestionId(
								similarques1.getSimilarQuestionId() + "," + similarques2.getSimilarQuestionId());
						similarResult.setSimilarQuestionNo(
								similarques1.getSimilarQuestionNo() + "," + similarques2.getSimilarQuestionNo());
						similarResult.setSimilarity(String.format("%.2f", similarity));
						similarResult.setSimilarQuestionBody(
								similarques1.getSimilarQuestionBody() + "||" + similarques2.getSimilarQuestionBody());
						similarResult.setCourseName(similarques1.getCourseName());
						similarResult.setBookName(similarques1.getBookName());
						similarResult.setTypeName(similarques1.getTypeName());
						similarResult.setCourseId(similarques1.getCourseId());
						similarResult.setCourseNo(similarques1.getCourseNo());
						resultList.add(similarResult);
					}
				}
			}
		}
		similarQuestionList.removeAll(similarQuestionList);
		similarQuestionList = null;
		Collections.sort(resultList);
		return resultList;
	}
	
	/**
	 * <p>检查最近3年题目相似度</p>
	 */
	public List<SimilarQuestion> specifyCheckSimilarity(Integer course_id, Integer type_id, String quesBody) {
		Question quesParam = new Question();
		quesParam.setCourseId(course_id);
		quesParam.setTypeId(type_id);
		List<Question> quesList = this.questionDao.findRecent3YearQues(quesParam);
		List<SimilarQuestion> similarQuestionList = new ArrayList<SimilarQuestion>();
		List<SimilarQuestion> resultList = new ArrayList<SimilarQuestion>();
		for (Question ques : quesList) {
			//List<QuestionOption> quesOption = ques.getOptions();
			String option = "";
			/*if (quesOption != null && quesOption.size() > 0) {
				for (QuestionOption op : quesOption) {
					option += op.getSummary();
				}
			}*/
			if (ques != null && !StringUtils.isBlank(ques.getQuestionNo())
					&& !StringUtils.isBlank(ques.getBodySummary())) {
				SimilarQuestion similarQuestion = new SimilarQuestion();
				similarQuestion.setSimilarQuestionId(ques.getId().toString());
				similarQuestion.setSimilarQuestionNo(ques.getQuestionNo());
				similarQuestion.setSimilarQuestionBody(ques.getBodySummary() + option);
				similarQuestion.setCourseName(ques.getCourseName());
				similarQuestion.setBookName(ques.getBookName());
				similarQuestion.setTypeName(ques.getTypeName());
				similarQuestion.setCourseId(ques.getCourseId());
				similarQuestion.setCourseNo(ques.getCourseNo());
				similarQuestionList.add(similarQuestion);
			}
		}
		//及时释放对象
		quesList.removeAll(quesList);
		quesList = null;
		int size = similarQuestionList.size();
		double similarity = 0;
		SimilarQuestion similarques1 = new SimilarQuestion();//要比对的题干
		similarques1.setSimilarQuestionBody(quesBody);
		for (int i = 0; i < size; i++) {
			SimilarQuestion similarques2 = similarQuestionList.get(i);
			//已经是相同课程相同题型
			if ((similarity = StringSimilarityUtils.getSimilarityWithCosinesBySeg(
					similarques1.getSimilarQuestionBody(), similarques2.getSimilarQuestionBody())
					* 100) >= 60) {
				SimilarQuestion similarResult = new SimilarQuestion();
				similarResult.setSimilarQuestionId(similarques2.getSimilarQuestionId());
				similarResult.setSimilarQuestionNo(similarques2.getSimilarQuestionNo());
				similarResult.setSimilarity(String.format("%.2f", similarity));
				similarResult.setSimilarQuestionBody(
						similarques1.getSimilarQuestionBody() + "||" + similarques2.getSimilarQuestionBody());
				similarResult.setCourseId(course_id);
				similarResult.setSimilarQuestionId(similarques2.getSimilarQuestionId());//相似的数据库题ID
				similarResult.setSimilarQuestionName(similarques2.getSimilarQuestionBody());//相似的数据库题目
				resultList.add(similarResult);
			}
		}
		similarQuestionList.removeAll(similarQuestionList);
		similarQuestionList = null;
		Collections.sort(resultList);
		return resultList;
	}

	//选择题答案选项占比
	public List<Question> queryQuestionKeyTJ(String pager_name) {
		Question question = new Question();
		question.setRemark(pager_name);
		List<Question> list = this.questionDao.queryQuestionKeyTJ(question);
		int total = 0;
		for (Question tj : list) {
			total = total + tj.getCount();
		}
		for (Question tj : list) {
			tj.setRemark(new BigDecimal(tj.getCount()).divide(new BigDecimal(total), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)).setScale(2).toPlainString() + "%");
		}
		return list;
	}
	
	public static void main(String[] args) {
		String ret = new BigDecimal(1).divide(new BigDecimal(3), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)).setScale(2).toPlainString();
		System.err.println(ret);
	}
}
