package cn.com.qmth.examcloud.core.questions.service.impl;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.bson.types.ObjectId;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.nlpcn.commons.lang.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.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.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import com.google.gson.Gson;

import cn.com.qmth.examcloud.api.commons.enums.AdminOperateType;
import cn.com.qmth.examcloud.api.commons.security.bean.User;
import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
import cn.com.qmth.examcloud.commons.exception.StatusException;
import cn.com.qmth.examcloud.commons.util.JsonUtil;
import cn.com.qmth.examcloud.core.print.api.CoursePaperCloudService;
import cn.com.qmth.examcloud.core.print.api.bean.SyncCoursePaperBean;
import cn.com.qmth.examcloud.core.print.api.request.SyncCoursePaperReq;
import cn.com.qmth.examcloud.core.print.api.response.SyncCoursePaperResp;
import cn.com.qmth.examcloud.core.questions.base.BeanCopierUtil;
import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
import cn.com.qmth.examcloud.core.questions.base.IoUtils;
import cn.com.qmth.examcloud.core.questions.base.Model;
import cn.com.qmth.examcloud.core.questions.base.converter.utils.FileUtil;
import cn.com.qmth.examcloud.core.questions.base.enums.PaperSeqMode;
import cn.com.qmth.examcloud.core.questions.base.enums.PaperStatus;
import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
import cn.com.qmth.examcloud.core.questions.base.exception.PaperException;
import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
import cn.com.qmth.examcloud.core.questions.dao.ExamPaperRepo;
import cn.com.qmth.examcloud.core.questions.dao.PaperDetailRepo;
import cn.com.qmth.examcloud.core.questions.dao.PaperDetailUnitRepo;
import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
import cn.com.qmth.examcloud.core.questions.dao.PropertyRepo;
import cn.com.qmth.examcloud.core.questions.dao.QuesBakRepo;
import cn.com.qmth.examcloud.core.questions.dao.QuesRepo;
import cn.com.qmth.examcloud.core.questions.dao.entity.Course;
import cn.com.qmth.examcloud.core.questions.dao.entity.ExamPaper;
import cn.com.qmth.examcloud.core.questions.dao.entity.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.PaperSearchInfo;
import cn.com.qmth.examcloud.core.questions.dao.entity.Property;
import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
import cn.com.qmth.examcloud.core.questions.dao.entity.QuesProperty;
import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionAudio;
import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionBak;
import cn.com.qmth.examcloud.core.questions.service.ExtractConfigService;
import cn.com.qmth.examcloud.core.questions.service.PaperDetailService;
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.RandomPaperService;
import cn.com.qmth.examcloud.core.questions.service.bean.PaperId;
import cn.com.qmth.examcloud.core.questions.service.bean.dto.DownloadPaperDto;
import cn.com.qmth.examcloud.core.questions.service.bean.dto.ObjectiveQuestionStructure;
import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailExp;
import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitExp;
import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperExp;
import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperQuestionStructureInfo;
import cn.com.qmth.examcloud.core.questions.service.bean.dto.SubjectiveQuestionStructure;
import cn.com.qmth.examcloud.core.questions.service.bean.paper.PaperAnswerDomain;
import cn.com.qmth.examcloud.core.questions.service.bean.randompaper.PaperDetailUnitDto;
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.config.SysProperty;
import cn.com.qmth.examcloud.core.questions.service.export.ExportPaperAbstractService;
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;
import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
import cn.com.qmth.examcloud.web.config.SystemProperties;
import cn.com.qmth.examcloud.web.filestorage.FileStoragePathEnvInfo;
import cn.com.qmth.examcloud.web.filestorage.YunPathInfo;
import cn.com.qmth.examcloud.web.support.SpringContextHolder;

/**
 * @author chenken
 * @date 2017年9月12日 上午11:20:44
 * @company QMTH
 * @description PaperServiceImpl.java
 */
@Service("paperService")
public class PaperServiceImpl implements PaperService {

	private static final Logger LOG = LoggerFactory.getLogger(PaperServiceImpl.class);
	
	@Autowired
	private PropertyRepo propertyRepo;
	@Autowired
	PaperRepo paperRepo;

	@Autowired
	PaperDetailService paperDetailService;

	@Autowired
	ExamPaperRepo examPaperRepo;

	@Autowired
	PaperDetailRepo paperDetailRepo;

	@Autowired
	PaperDetailUnitRepo paperDetailUnitRepo;

	@Autowired
	QuesRepo quesRepo;

	@Autowired
	QuesBakRepo quesBakRepo;

	@Autowired
	Gson gson;

	@Autowired
	PaperDetailUnitService paperDetailUnitService;

	@Autowired
	QuesService quesService;

	@Autowired
	ExtractConfigService extractConfigService;

	@Autowired
	private MongoTemplate mongoTemplate;
	@Resource(name="mongoTemplate2")
	private MongoTemplate mongoTemplate2;
	@Autowired
	private QuestionAudioServiceImpl questionAudioService;

	@Autowired
	private SysProperty sysProperty;

	@Autowired
	private CoursePaperCloudService coursePaperCloudService;

	@Autowired
	private CourseService courseService;

	@Autowired
	private ExtractConfigPaperCache extractConfigPaperCache;

	@Autowired
	private BasePaperCache basePaperCache;
	@Autowired
	private RandomPaperQuestionService randomPaperQuestionService;
	@Autowired
	private RandomPaperService randomPaperService;
	
	@Autowired
	private QuestionCache questionCache;

	@Autowired
	private QuestionAnswerCache questionAnswerCache;

	@Autowired
	private SystemProperties systemProperties;

	private Random random = new Random();

	public static final String TEMP_FILE_EXP = "docxExport/";

	/**
	 * 查询所有已导入试卷
	 */
	@Override
	public Page<Paper> getImportPapers(PaperSearchInfo paperSearchInfo, int curPage, int pageSize, UserDataRule ud) {

		if (ud.assertEmptyQueryResult()) {
			return Page.empty();
		}
		Query query = new Query();

		query.addCriteria(Criteria.where("orgId").is(paperSearchInfo.getOrgId()));

		if (ud.assertNeedQueryRefIds()) {
			query.addCriteria(Criteria.where("course.id").in(ud.stringRefIds()));
		}

		query.addCriteria(Criteria.where("paperType").is(PaperType.IMPORT));

		query.addCriteria(Criteria.where("course.enable").is("true"));

		if (StringUtils.isNotBlank(paperSearchInfo.getCourseNo())) {
			query.addCriteria(Criteria.where("course.code").is(paperSearchInfo.getCourseNo()));
		}

		if (StringUtils.isNotBlank(paperSearchInfo.getLevel())) {
			query.addCriteria(Criteria.where("course.level").is(paperSearchInfo.getLevel()));
		}

		if (StringUtils.isNoneBlank(paperSearchInfo.getName())) {
			String paperName = CommonUtils.escapeExprSpecialWord(paperSearchInfo.getName());
			query.addCriteria(Criteria.where("name").regex(".*?\\.*" + paperName + ".*"));
		}

		if (StringUtils.isNoneBlank(paperSearchInfo.getCreator())) {
			String creator = CommonUtils.escapeExprSpecialWord(paperSearchInfo.getCreator());
			query.addCriteria(Criteria.where("creator").regex(".*?\\.*" + creator + ".*"));
		}

		if (StringUtils.isNoneBlank(paperSearchInfo.getLastModifyName())) {
			String lastModifyName = CommonUtils.escapeExprSpecialWord(paperSearchInfo.getLastModifyName());
			query.addCriteria(Criteria.where("lastModifyName").regex(".*?\\.*" + lastModifyName + ".*"));
		}

		if (StringUtils.isNotBlank(paperSearchInfo.getSpecialtyNo())) {
			query.addCriteria(Criteria.where("specialty.code").is(paperSearchInfo.getSpecialtyNo()));
		}

		long total = this.mongoTemplate.count(query, Paper.class);
		if (total == 0) {
			return Page.empty();
		}

		PageRequest pageable = PageRequest.of(curPage - 1, pageSize);
		query.with(Sort.by(Sort.Order.desc("createTime")));
		query.skip(pageable.getOffset());
		query.limit(pageable.getPageSize());

		List<Paper> paperList = this.mongoTemplate.find(query, Paper.class);
		if (CollectionUtils.isEmpty(paperList)) {
			return Page.empty();
		}

		return new PageImpl<>(paperList, pageable, total);
	}

	/**
	 * 查询所有待审核和审核不通过的导入试卷
	 *
	 * @param paperSearchInfo
	 * @param curPage
	 * @param pageSize
	 * @return
	 */
	public Page<Paper> getImportPapersNotSuccess(PaperSearchInfo paperSearchInfo, int curPage, int pageSize) {
		Query query = new Query();
		query.addCriteria(Criteria.where("orgId").is(paperSearchInfo.getOrgId()));
		query.addCriteria(Criteria.where("paperType").is(PaperType.IMPORT));
		query.addCriteria(Criteria.where("course.enable").is("true"));
		if (paperSearchInfo.getPaperStatus() != null) {
			query.addCriteria(Criteria.where("paperStatus").is(paperSearchInfo.getPaperStatus()));
		} else {
			query.addCriteria(Criteria.where("paperStatus").ne(PaperStatus.PASS));
		}
		if (StringUtil.isNotBlank(paperSearchInfo.getCourseNo())) {
			query.addCriteria(Criteria.where("course.code").is(paperSearchInfo.getCourseNo()));
		}
		long total = this.mongoTemplate.count(query, Paper.class);
		if (total == 0) {
			return Page.empty();
		}

		query.limit(pageSize);
		query.skip((curPage - 1L) * pageSize);
		List<Paper> paperList = this.mongoTemplate.find(query, Paper.class);
		return new PageImpl<Paper>(paperList, PageRequest.of(curPage - 1, pageSize), total);
	}

	/**
	 * 根据条件查询
	 *
	 * @param paperSearchInfo
	 * @return
	 */
	public List<Paper> getImportPapersBySearch(PaperSearchInfo paperSearchInfo) {
		Query query = new Query();
		query.addCriteria(Criteria.where("orgId").is(paperSearchInfo.getOrgId()));
		query.addCriteria(Criteria.where("paperType").is(PaperType.IMPORT.name()));
		query.addCriteria(Criteria.where("course.enable").is("true"));
		if (StringUtils.isNotBlank(paperSearchInfo.getCourseNo())) {
			query.addCriteria(Criteria.where("course.code").is(paperSearchInfo.getCourseNo()));
		}
		if (StringUtils.isNotBlank(paperSearchInfo.getLevel())) {
			query.addCriteria(Criteria.where("course.level").is(paperSearchInfo.getLevel()));
		}
		List<Paper> paperList = this.mongoTemplate.find(query, Paper.class);
		return paperList;
	}

	/**
	 * 根据条件查询
	 *
	 * @param paperSearchInfo
	 * @return
	 */
	public List<Paper> getGenPapersBySearch(PaperSearchInfo paperSearchInfo) {
		Query query = new Query();
		query.addCriteria(Criteria.where("orgId").is(paperSearchInfo.getOrgId()));
		query.addCriteria(Criteria.where("paperType").is(PaperType.GENERATE.name()));
		query.addCriteria(Criteria.where("course.enable").is("true"));
		query.addCriteria(Criteria.where("storage").ne(1));
		if (StringUtils.isNotBlank(paperSearchInfo.getCourseNo())) {
			query.addCriteria(Criteria.where("course.code").is(paperSearchInfo.getCourseNo()));
		}

		if (StringUtils.isNotBlank(paperSearchInfo.getLevel())) {
			query.addCriteria(Criteria.where("course.level").is(paperSearchInfo.getLevel()));
		}

		query.with(Sort.by(Sort.Direction.DESC, "createTime"));

		List<Paper> paperList = this.mongoTemplate.find(query, Paper.class);
		return paperList;
	}

	/**
	 * 保存试卷
	 *
	 * @param paperExp
	 * @return
	 */
	public Map<String, Object> savePaper(PaperExp paperExp, User user) {
		Map<String, Object> msgMap = new HashMap<>();

		Paper oldPaper = Model.of(paperRepo.findById(paperExp.getId()));
		if (oldPaper != null) {
			if (PaperType.GENERATE.equals(oldPaper.getPaperType())) {
				oldPaper.setAuditStatus(false);
			}
			String oldName = oldPaper.getName().trim();
			oldPaper.setTitle(paperExp.getTitle());
			oldPaper.setName(paperExp.getName().trim());
			oldPaper.setLastModifyName(user.getDisplayName());
			oldPaper.setExamRemark(paperExp.getExamRemark());
			if (!oldName.equals(paperExp.getName().trim())) {// 假如改变了试卷名称
				// 则要效验试卷名称唯一性
				boolean existName = this.checkPaperName(paperExp.getName().trim(), user.getRootOrgId().toString());
				if (existName) {
					msgMap.put("msg", "试卷名称重复，请重新命名！");
				} else {
					formatPaper(oldPaper, user);
					paperRepo.save(oldPaper);
					msgMap.put("msg", "success");
				}
			} else {
				formatPaper(oldPaper, user);
				paperRepo.save(oldPaper);
				msgMap.put("msg", "success");
			}
		}

		// 清除缓存
		this.clearPaperCache(paperExp.getId());

		return msgMap;
	}

	/**
	 * 查询所有已组试卷
	 *
	 * @param paperSearchInfo
	 * @param curPage
	 * @param pageSize
	 * @return
	 */
	public Page<Paper> getGenPapers(PaperSearchInfo paperSearchInfo, int curPage, int pageSize, UserDataRule ud) {
		if (ud.assertEmptyQueryResult()) {
			return Page.empty();
		}
		Query query = new Query();
		query.addCriteria(Criteria.where("orgId").is(paperSearchInfo.getOrgId()));
		if (ud.assertNeedQueryRefIds()) {
			query.addCriteria(Criteria.where("course.id").in(ud.stringRefIds()));
		}
		if (paperSearchInfo.getAuditStatus() != null) {
			if (paperSearchInfo.getAuditStatus()) {
				query.addCriteria(Criteria.where("auditStatus").ne(false));
			} else {
				query.addCriteria(Criteria.where("auditStatus").is(false));
			}
		}
		query.addCriteria(Criteria.where("paperType").is(PaperType.GENERATE.name()));
		query.addCriteria(Criteria.where("storage").ne(1));
		query.addCriteria(Criteria.where("course.enable").is("true"));
		if (paperSearchInfo.getInUse() != null) {
			if (paperSearchInfo.getInUse() == 0) {
				query.addCriteria(Criteria.where("inUse").ne(1));
			} else if (paperSearchInfo.getInUse() == 1) {
				query.addCriteria(Criteria.where("inUse").is(1));
			}
		}
		if (StringUtils.isNotBlank(paperSearchInfo.getCourseNo())) {
			query.addCriteria(Criteria.where("course.code").is(paperSearchInfo.getCourseNo()));
		}
		if (StringUtils.isNotBlank(paperSearchInfo.getName())) {
			String paperName = CommonUtils.escapeExprSpecialWord(paperSearchInfo.getName());
			query.addCriteria(Criteria.where("name").regex(".*?\\.*" + paperName + ".*"));
		}
		if (StringUtils.isNotBlank(paperSearchInfo.getLevel())) {
			query.addCriteria(Criteria.where("course.level").is(paperSearchInfo.getLevel()));
		}
		if (StringUtils.isNotBlank(paperSearchInfo.getCreator())) {
			String creator = CommonUtils.escapeExprSpecialWord(paperSearchInfo.getCreator());
			query.addCriteria(Criteria.where("creator").regex(".*?\\.*" + creator + ".*"));
		}
		if (StringUtils.isNotBlank(paperSearchInfo.getLastModifyName())) {
			String lastModifyName = CommonUtils.escapeExprSpecialWord(paperSearchInfo.getLastModifyName());
			query.addCriteria(Criteria.where("lastModifyName").regex(".*?\\.*" + lastModifyName + ".*"));
		}

		long total = this.mongoTemplate.count(query, Paper.class);
		if (total == 0) {
			return Page.empty();
		}

		query.with(Sort.by(new Order(Direction.DESC, "createTime")));
		query.limit(pageSize);
		query.skip((curPage - 1L) * pageSize);
		List<Paper> paperList = this.mongoTemplate.find(query, Paper.class);
		return new PageImpl<Paper>(paperList, PageRequest.of(curPage - 1, pageSize), total);
	}

	@Override
	public Page<Paper> getStoragePaperPage(PaperSearchInfo paperSearchInfo, int curPage, int pageSize,
			UserDataRule ud) {
		if (ud.assertEmptyQueryResult()) {
			return Page.empty();
		}
		Query query = new Query();
		query.addCriteria(Criteria.where("orgId").is(paperSearchInfo.getOrgId()));
		if (ud.assertNeedQueryRefIds()) {
			query.addCriteria(Criteria.where("course.id").in(ud.stringRefIds()));
		}
		query.addCriteria(Criteria.where("paperType").is(PaperType.GENERATE.name()));
		query.addCriteria(Criteria.where("storage").is(1));
		query.addCriteria(Criteria.where("course.enable").is("true"));
		if (StringUtils.isNotBlank(paperSearchInfo.getCourseNo())) {
			query.addCriteria(Criteria.where("course.code").is(paperSearchInfo.getCourseNo()));
		}
		if (StringUtils.isNoneBlank(paperSearchInfo.getName())) {
			String paperName = CommonUtils.escapeExprSpecialWord(paperSearchInfo.getName());
			query.addCriteria(Criteria.where("name").regex(".*?\\.*" + paperName + ".*"));
		}
		if (StringUtils.isNotBlank(paperSearchInfo.getLevel())) {
			query.addCriteria(Criteria.where("course.level").is(paperSearchInfo.getLevel()));
		}
		if (StringUtils.isNoneBlank(paperSearchInfo.getCreator())) {
			String creator = CommonUtils.escapeExprSpecialWord(paperSearchInfo.getCreator());
			query.addCriteria(Criteria.where("creator").regex(".*?\\.*" + creator + ".*"));
		}
		if (StringUtils.isNoneBlank(paperSearchInfo.getLastModifyName())) {
			String lastModifyName = CommonUtils.escapeExprSpecialWord(paperSearchInfo.getLastModifyName());
			query.addCriteria(Criteria.where("lastModifyName").regex(".*?\\.*" + lastModifyName + ".*"));
		}

		long total = this.mongoTemplate.count(query, Paper.class);
		if (total == 0) {
			return Page.empty();
		}

		query.with(Sort.by(new Order(Direction.DESC, "createTime")));
		query.limit(pageSize);
		query.skip((curPage - 1L) * pageSize);
		List<Paper> paperList = this.mongoTemplate.find(query, Paper.class);
		return new PageImpl<Paper>(paperList, PageRequest.of(curPage - 1, pageSize), total);
	}

	/**
	 * 查询考试试卷
	 *
	 * @param id
	 * @param courseCode
	 * @param groupCode
	 * @return
	 */
	public List<Paper> listExamPapers(long id, String courseCode, String groupCode) {
		List<Paper> papers = new ArrayList<>();
		ExamPaper examPaper = new ExamPaper();
		examPaper.setExamId(id);
		examPaper.setCourseCode(courseCode);
		examPaper.setGroupCode(groupCode);
		Example<ExamPaper> example = Example.of(examPaper);
		List<ExamPaper> examPapers = examPaperRepo.findAll(example);
		for (ExamPaper ePaper : examPapers) {
			Paper paper = Model.of(paperRepo.findById(ePaper.getPaper().getId()));
			papers.add(paper);
		}
		return papers;
	}

	/**
	 * 设置考试试卷
	 *
	 * @param examId
	 * @param courseCode
	 * @param groupCode
	 * @param paperId
	 * @return
	 */
	public void joinToExamPaper(long examId, String courseCode, String groupCode, String paperId) {
		ExamPaper examPaper = new ExamPaper();
		examPaper.setExamId(examId);
		examPaper.setGroupCode(groupCode);
		examPaper.setCourseCode(courseCode);
		// examPaper.setPaperId(paperId);
		examPaperRepo.save(examPaper);
	}

	public void releaseExamPaper(long examId, String courseCode, String groupCode, String paperId) {
		ExamPaper examPaper = new ExamPaper();
		examPaper.setExamId(examId);
		examPaper.setGroupCode(groupCode);
		examPaper.setCourseCode(courseCode);
		// examPaper.setPaperId(paperId);
		examPaperRepo.delete(examPaper);
	}

	public Set<String> listGroupCodes(long examId, String courseCode) {
		Set<String> groupSet = new HashSet<>();
		ExamPaper examPaper = new ExamPaper();
		examPaper.setExamId(examId);
		examPaper.setCourseCode(courseCode);
		List<ExamPaper> examPapers = examPaperRepo.findAll(Example.of(examPaper));
		for (ExamPaper expaper : examPapers) {
			groupSet.add(expaper.getGroupCode());
		}
		return groupSet;
	}

	public void deletGroupCode(long examId, String courseCode, String groupCode) {
		ExamPaper examPaper = new ExamPaper();
		examPaper.setExamId(examId);
		examPaper.setCourseCode(courseCode);
		examPaper.setCourseCode(courseCode);
		examPaperRepo.delete(examPaper);
	}

	/**
	 * 根据试卷ID获取试卷下面的大题
	 *
	 * @param id
	 * @return
	 */
	public List<PaperDetail> findPaperDetailsById(String id) {
		return paperDetailService.getPaperDetailsByPaper(Model.of(paperRepo.findById(id)));
	}

	/**
	 * 批量删除试卷
	 *
	 * @param paperIds
	 */
	@Override
	public void deletePapers(List<String> paperIds, User user) {
		List<Paper> papers = CommonUtils.toList(paperRepo.findByIdIn(paperIds));
		if (papers.get(0).getPaperType() == PaperType.IMPORT) {
			List<Question> quesList = new ArrayList<>();
			for (Paper paper : papers) {
				List<PaperDetailUnit> paperUnits = paperDetailUnitRepo.findByPaperOrderByNumber(paper);
				List<String> questionIds=paperUnits.stream().map(e->e.getQuestion().getId()).collect(Collectors.toList());
				if(randomPaperQuestionService.existQuestion(questionIds)) {
					throw new StatusException("试卷[" + paper.getName() + "]中有试题被抽题模板使用，不能删除");
				}
				for (PaperDetailUnit pdu : paperUnits) {
					if (pdu.getQuestion() != null) {
						quesList.add(pdu.getQuestion());
					}
				}
			}
			List<PaperDetailUnit> allUnits = paperDetailUnitRepo.findByQuestionIn(quesList);
			for (PaperDetailUnit pdu : allUnits) {
				if (pdu.getPaper() != null && pdu.getPaper().getPaperType() == PaperType.GENERATE) {
//                    msg = "待删除试卷中有试题被组卷使用，不能删除!";
//                    msgMap.put("msg", msg);
//                    msgMap.put("paperName", pdu.getPaper().getName());
//                    return msgMap;
					throw new StatusException("试卷[" + pdu.getPaper().getName() + "]中有试题被组卷使用，不能删除");
				}
			}
			// 删除音频
			questionAudioService.deleteAudio(quesList);
			quesRepo.deleteAll(quesList);
		} else if (papers.get(0).getPaperType() == PaperType.GENERATE) {
			for (Paper paper : papers) {
//                List<String> examPaperIds = extractConfigService.getExamPaperId(paper.getCourseNo(), paper.getOrgId());
//                if (examPaperIds != null && examPaperIds.contains(paper.getId())) {
//                    msg = "待删除试卷有被调卷规则使用，不能删除!";
//                    msgMap.put("msg", msg);
//                    msgMap.put("paperName", paper.getName());
//                    return msgMap;
//                }
				if (paper.getInUse() != null && paper.getInUse() == 1) {
					throw new StatusException("试卷[" + paper.getName() + "]已调用，不能删除");
				}
				if(randomPaperService.existPaper(Long.valueOf(paper.getCourse().getId()),paper.getId())) {
					throw new StatusException("试卷[" + paper.getName() + "]被抽题模板使用，不能删除");
				}
			}
		}
		paperDetailService.deletePaperDetailsByPapers(papers);
		paperRepo.deleteAll(papers);

		for (String paperId : paperIds) {
			// 清除缓存
			this.clearPaperCache(paperId);
		}

		for (Paper paper : papers) {
			StringBuilder paperInfo = new StringBuilder();
			paperInfo.append("课程:" + paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")");
			paperInfo.append(" 试卷名称:" + paper.getName());
			if (PaperType.IMPORT.equals(paper.getPaperType())) {
				ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
						AdminOperateType.TYPE46.getDesc(), paperInfo.toString()));
			} else {
				ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
						AdminOperateType.TYPE47.getDesc(), paperInfo.toString()));

			}
		}
	}
	private List<PaperDetailUnitDto> findUnitByPaperId(String paperId) {
		Object id ;
		if (paperId.length() > 24) {
			id=paperId;
		} else {
			id=new ObjectId(paperId);
		}
		Query query = new Query();
		query.addCriteria(Criteria.where("paper.$id").is(id));
		List<PaperDetailUnitDto> units = this.mongoTemplate2.find(query, PaperDetailUnitDto.class, "paperDetailUnit");
		return units;
	}
	private boolean existGenerateQuestion(List<String> questionIds) {
		List<Object> ids = new ArrayList<>();
		for (String pid : questionIds) {
			if (pid.length() > 24) {
				ids.add(pid);
			} else {
				ids.add(new ObjectId(pid));
			}
		}
		Query query = new Query();
		query.addCriteria(Criteria.where("question.$id").in(ids));
		query.addCriteria(Criteria.where("paperType").is(PaperType.GENERATE.name()));
		long count=mongoTemplate.count(query,"paperDetailUnit");
		return count>0;
	}
	private void removeByIds(List<String> stringIds,String collectionName) {
		List<Object> ids = new ArrayList<>();
		for (String pid : stringIds) {
			if (pid.length() > 24) {
				ids.add(pid);
			} else {
				ids.add(new ObjectId(pid));
			}
		}
		Query query = new Query();
		query.addCriteria(Criteria.where("_id").in(ids));
		mongoTemplate.remove(query,collectionName);
	}
	private void removeDetailByPaperIds(List<String> paperIds) {
		List<Object> ids = new ArrayList<>();
		for (String pid : paperIds) {
			if (pid.length() > 24) {
				ids.add(pid);
			} else {
				ids.add(new ObjectId(pid));
			}
		}
		Query query = new Query();
		query.addCriteria(Criteria.where("paper.$id").in(ids));
		mongoTemplate.remove(query,"paperDetail");
	}
	@Override
	public void deletePapersPlus(List<String> paperIds, User user) {
		List<Paper> papers = paperRepo.findByIdIn(paperIds);
		List<PaperDetailUnitDto> unitList=new ArrayList<>();
		if (papers.get(0).getPaperType() == PaperType.IMPORT) {
			List<String> quesList = new ArrayList<>();
			for (Paper paper : papers) {
				List<PaperDetailUnitDto> paperUnits = findUnitByPaperId(paper.getId());
				if(CollectionUtils.isEmpty(paperUnits)) {
					continue;
				}
				List<String> questionIds=paperUnits.stream().map(e->e.getQuestion().getId()).collect(Collectors.toList());
				if(existGenerateQuestion(questionIds)) {
					throw new StatusException("试卷[" + paper.getName() + "]中有试题被组卷使用，不能删除");
				}
				if(randomPaperQuestionService.existQuestion(questionIds)) {
					throw new StatusException("试卷[" + paper.getName() + "]中有试题被抽题模板使用，不能删除");
				}
				unitList.addAll(paperUnits);
				quesList.addAll(questionIds);
			}
			if(CollectionUtils.isNotEmpty(quesList)) {
				questionAudioService.deleteAudioByQuestionId(quesList);
				removeByIds(quesList, "question");
			}
		} else if (papers.get(0).getPaperType() == PaperType.GENERATE) {
			for (Paper paper : papers) {
				if (paper.getInUse() != null && paper.getInUse() == 1) {
					throw new StatusException("试卷[" + paper.getName() + "]已调用，不能删除");
				}
				if(randomPaperService.existPaper(Long.valueOf(paper.getCourse().getId()),paper.getId())) {
					throw new StatusException("试卷[" + paper.getName() + "]被抽题模板使用，不能删除");
				}
				List<PaperDetailUnitDto> paperUnits = findUnitByPaperId(paper.getId());
				if(CollectionUtils.isEmpty(paperUnits)) {
					continue;
				}
				unitList.addAll(paperUnits);
			}
		}
		List<String> unitids=unitList.stream().map(e->e.getId()).collect(Collectors.toList());
		if(CollectionUtils.isNotEmpty(unitids)) {
			removeByIds(unitids, "paperDetailUnit");
		}
		removeDetailByPaperIds(paperIds);
		removeByIds(paperIds, "paper");

		for (String paperId : paperIds) {
			// 清除缓存
			this.clearPaperCache(paperId);
		}

		for (Paper paper : papers) {
			StringBuilder paperInfo = new StringBuilder();
			paperInfo.append("课程:" + paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")");
			paperInfo.append(" 试卷名称:" + paper.getName());
			if (PaperType.IMPORT.equals(paper.getPaperType())) {
				ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
						AdminOperateType.TYPE46.getDesc(), paperInfo.toString()));
			} else {
				ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
						AdminOperateType.TYPE47.getDesc(), paperInfo.toString()));

			}
		}
	}

	/**
	 * 批量通过试卷
	 *
	 * @param paperIds
	 */
	public void passPapers(List<String> paperIds) {
		List<Paper> papers = CommonUtils.toList(paperRepo.findByIdIn(paperIds));
		papers.stream().forEach(paper -> {
			paper.setPaperStatus(PaperStatus.PASS);
		});
		paperRepo.saveAll(papers);
	}

	@Override
	public void updatePapersStorage(List<String> paperIds, int storage, String orgId) {
		List<Paper> papers = CommonUtils.toList(paperRepo.findByIdIn(paperIds));
		List<Paper> papersList = papers.stream().filter(p -> orgId.equals(p.getOrgId())).collect(Collectors.toList());
		papersList.stream().forEach(paper -> {
			paper.setStorage(storage);
		});
		if (papersList.size() > 0) {
			paperRepo.saveAll(papersList);
		}
	}

	/**
	 * 批量不通过试卷
	 *
	 * @param paperIds
	 */
	public void noPassPapers(List<String> paperIds) {
		List<Paper> papers = CommonUtils.toList(paperRepo.findByIdIn(paperIds));
		papers.stream().forEach(paper -> {
			paper.setPaperStatus(PaperStatus.NOPASS);
		});
		paperRepo.saveAll(papers);
	}

	/**
	 * 批量待审核试卷
	 *
	 * @param paperIds
	 */
	public void backPapers(List<String> paperIds) {
		List<Paper> papers = CommonUtils.toList(paperRepo.findByIdIn(paperIds));
		papers.stream().forEach(paper -> {
			paper.setPaperStatus(PaperStatus.DRAFT);
		});
		paperRepo.saveAll(papers);
	}

	/**
	 * 初始化导出试卷DTO
	 *
	 * @param id
	 * @return
	 */
	public PaperExp getPaperDto(String id) {
		Paper paper = Model.of(paperRepo.findById(id));
		// 创建paperDto
		PaperExp paperExp = new PaperExp();
		BeanUtils.copyProperties(paper, paperExp);
		paperExp.setCourse(paper.getCourse());
		// 获取大题
		List<PaperDetail> paperDetails = paperDetailRepo.findByPaperOrderByNumber(paper);
		List<PaperDetailExp> paperDetailExps = new ArrayList<>();
		for (PaperDetail paperDetail : paperDetails) {
			PaperDetailExp paperDetailExp = new PaperDetailExp();
			BeanUtils.copyProperties(paperDetail, paperDetailExp);
			paperDetailExps.add(paperDetailExp);
		}
		// 封装小题
		for (int i = 0; i < paperDetailExps.size(); i++) {
			List<PaperDetailUnit> paperDetailUnits = paperDetailUnitRepo
					.findByPaperDetailOrderByNumber(paperDetails.get(i));
			if (paperDetailUnits != null && paperDetailUnits.size() > 0) {
				List<PaperDetailUnitExp> paperDetailUnitExps = new ArrayList<>();
				for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
					PaperDetailUnitExp paperDetailUnitExp = new PaperDetailUnitExp();
					BeanUtils.copyProperties(paperDetailUnit, paperDetailUnitExp);
					fillProperty(paperDetailUnitExp.getQuestion());
					if (!CollectionUtils.isEmpty(paperDetailUnitExp.getQuestion().getSubQuestions())) {
						for (Question subque : paperDetailUnitExp.getQuestion().getSubQuestions()) {
							fillProperty(subque);
						}
					}
					paperDetailUnitExps.add(paperDetailUnitExp);
				}
				// 选择题，套题下选择题 选项顺序重新排列
				reorderChoicequestionOption(paperDetailUnitExps);
				paperDetailExps.get(i).setPaperDetailUnits(paperDetailUnitExps);
			} else {
				paperDetailExps.get(i).setUnitCount(0);
			}
		}
		paperExp.setPaperDetails(paperDetailExps);
		// 初始化试卷内容
		initPaper(paperExp);
		return paperExp;
	}

	/**
	 * 重新对选择题option进行排序(多选、单选、套题下选择题)
	 */
	public void reorderChoicequestionOption(List<PaperDetailUnitExp> paperDetailUnitExps) {
		for (PaperDetailUnitExp paperDetailUnitExp : paperDetailUnitExps) {
			String optionOrder = paperDetailUnitExp.getOptionOrder();
			if (StringUtil.isNotBlank(optionOrder)) {
				Question question = paperDetailUnitExp.getQuestion();
				if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
						|| question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
					question.setQuesOptions(reorderOptionCore(question.getQuesOptions(), optionOrder));
				}
				if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
					List<Question> subQuestions = question.getSubQuestions();
					int index = 0;
					for (int k = 0; k < subQuestions.size(); k++) {
						Question subQuestion = subQuestions.get(k);
						if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
								|| subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
							subQuestion.setQuesOptions(
									reorderOptionCore(subQuestion.getQuesOptions(), optionOrder.split(";")[index]));
							index++;
						}
					}
				}
			}
		}
	}

	private List<QuesOption> reorderOptionCore(List<QuesOption> quesOptions, String optionOrder) {
		List<QuesOption> newQuesOptions = new ArrayList<>();
		if (StringUtil.isBlank(optionOrder) || quesOptions.isEmpty()) {
			return null;
		}
		String[] optionOrderArr = optionOrder.split(",");
		for (int j = 0; j < optionOrderArr.length; j++) {
			for (int k = 0; k < quesOptions.size(); k++) {
				if (optionOrderArr[j].equals(quesOptions.get(k).getNumber())) {
					newQuesOptions.add(quesOptions.get(k));
				}
			}
		}
		quesOptions = null;
		return newQuesOptions;
	}

	/**
	 * 初始化试卷内容(增加序号)
	 *
	 * @param paperExp
	 */
	public void initPaper(PaperExp paperExp) {
		if (paperExp.getPaperDetails() == null || paperExp.getPaperDetails().size() == 0) {
			return;
		}
		int mainNum = 0;
		List<PaperDetailExp> paperDetailExpList = paperExp.getPaperDetails();
		for (PaperDetailExp paperDetail : paperDetailExpList) {
			// 大题序号
			paperDetail.setNumber(++mainNum);
			paperDetail.setCnNum(CommonUtils.toCHNum(paperDetail.getNumber()));
			if (paperDetail != null && paperDetail.getPaperDetailUnits() != null
					&& paperDetail.getPaperDetailUnits().size() > 0) {
				for (PaperDetailUnitExp paperDetailUnit : paperDetail.getPaperDetailUnits()) {
					Question question = paperDetailUnit.getQuestion();
					if (question != null) {
						if (question.getHasAudio() != null && question.getHasAudio()) {
							paperExp.setHasAudio(true); // 设置试卷含有音频
						}
						quesService.formatQuesUnit(question);
						List<Question> subQuesList = question.getSubQuestions();
						// 套题序号
						if (subQuesList != null && subQuesList.size() > 0) {
							int index = 0;
							for (Question subQues : subQuesList) {
								Map<String, String> params = new HashMap<>();
								params.put("number", String.valueOf(++index));
								subQues.setQuesParams(params);
								quesService.formatQuesUnit(subQues);
							}
							String quesBodyHtml = CommonUtils.relaceQuestionIdx(question.getQuesBody(), 0);
							question.setQuesBody(quesBodyHtml);
						}
					}
				}
			}
		}
	}

	/**
	 * 格式化查询条件
	 *
	 * @param paperSearchInfo
	 */
	public void formatPaperSearchInfo(PaperSearchInfo paperSearchInfo) {
		if (StringUtils.isEmpty(paperSearchInfo.getCourseNo())) {
			paperSearchInfo.setCourseNo(null);
		}
		if (StringUtils.isEmpty(paperSearchInfo.getCreateTime())) {
			paperSearchInfo.setCreateTime(null);
		}
		if (StringUtils.isEmpty(paperSearchInfo.getCreator())) {
			paperSearchInfo.setCreator(null);
		}
		if (StringUtils.isEmpty(paperSearchInfo.getName())) {
			paperSearchInfo.setName(null);
		}
	}

	/**
	 * 1.重新设置小题number，并返回试卷总分 2.重新计算大题的总分和小题总数 3.重新保存试卷大题数量、小题数量、试卷总分等信息
	 *
	 * @param paper
	 */
	public void formatPaper(Paper paper, User user) {
		double paperTotalScore = reSetPaperDetailUnit(paper);
		paper.setDifficultyDegree(reSetDifficulty(paper, paperTotalScore));
		Map<String, Object> paperInfoMap = reSetPaperDetail(paper);
		reSetPaper(paper, user, paperInfoMap, paperTotalScore);
	}

	/**
	 * 计算试卷难度
	 *
	 * @param paper
	 * @param paperTotalScore
	 * @return
	 */

	@SuppressWarnings("unused")
	public Double reSetDifficulty(Paper paper, double paperTotalScore) {
		Double sum = 0.0;
		List<PaperDetail> paperDetails = paperDetailRepo.findByPaperOrderByNumber(paper);
		if (paperDetails != null && paperDetails.size() > 0) {
			for (PaperDetail paperDetail : paperDetails) {
				// 获取每个大题下面的所有小题
				List<PaperDetailUnit> paperDetailUnits = paperDetailUnitRepo
						.findByPaperDetailOrderByNumber(paperDetail);
				if (paperDetailUnits != null && paperDetailUnits.size() > 0) {
					for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
						// 如果为套题，重新计算难度
						if (paperDetailUnit.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
							// 重新计算套题的难度，公开度
							boolean publicity = false;
							double totalSum = 0d;
							double totalDou = 0d;
							for (int i = 0; i < paperDetailUnit.getQuestion().getSubQuestions().size(); i++) {
								Question subQuestion = paperDetailUnit.getQuestion().getSubQuestions().get(i);
								// 设置公开度
								if (subQuestion.getPublicity() == null) {
									publicity = true;
								} else {
									if (subQuestion.getPublicity()) {
										publicity = true;
									}
								}
								if (subQuestion.getDifficultyDegree() == null) {
									subQuestion.setDifficultyDegree(0.5);
								}
								totalSum = subQuestion.getDifficultyDegree() * paperDetailUnit.getSubScoreList().get(i)
										+ totalSum;
								totalDou = paperDetailUnit.getSubScoreList().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();
							paperDetailUnit.getQuestion().setDifficultyDegree(difficulty);
							paperDetailUnit.setScore(totalDou);
						}
						// 旧题没有难度值，需要重新赋值
						if (paperDetailUnit.getQuestion().getDifficultyDegree() == null) {
							paperDetailUnit.getQuestion().setDifficultyDegree(0.5);
						}
						if (paperDetailUnit.getScore() == null) {
							paperDetailUnit.setScore(0d);
						}
						sum = paperDetailUnit.getScore() * paperDetailUnit.getQuestion().getDifficultyDegree() + sum;
					}
				}
			}
			if (paperTotalScore < 0.1) {
				return 0.0;
			}
			BigDecimal b = BigDecimal.valueOf(sum / paperTotalScore);
			Double difficulty = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
			return difficulty;
		}
		return 0.0;
	}

	/**
	 * 重新设置小题number，并返回试卷总分
	 *
	 * @param paper
	 * @return
	 */
	private double reSetPaperDetailUnit(Paper paper) {
		List<PaperDetailUnit> paperDetailUnitAll = paperDetailUnitService.findByPaperAndSort(paper);
		double totalScore = 0;
		for (int i = 0; i < paperDetailUnitAll.size(); i++) {
			PaperDetailUnit unit = paperDetailUnitAll.get(i);
			if (unit.getScore() != null) {
				totalScore += unit.getScore();
			}
			unit.setNumber(i + 1);
		}
		paperDetailUnitRepo.saveAll(paperDetailUnitAll);
		totalScore = CommonUtils.formatDouble(totalScore);
		return totalScore;
	}

	/**
	 * 重新计算大题的总分和小题总数
	 *
	 * @param paper
	 */
	private Map<String, Object> reSetPaperDetail(Paper paper) {
		Map<String, Object> paperInfoMap = new HashMap<>();
		int allQuesCount = 0;
		List<PaperDetail> paperDetails = paperDetailRepo.findByPaperOrderByNumber(paper);
		// 计算各大题总分和小题数量
		for (PaperDetail paperDetail : paperDetails) {
			List<PaperDetailUnit> paperDetailUnits = paperDetailUnitRepo.findByPaperDetailOrderByNumber(paperDetail);
			if (paperDetailUnits.size() > 0) {
				int count = 0;
				double score = 0;
				int nestQusNum = 0;
				for (PaperDetailUnit unit : paperDetailUnits) {
					if (unit.getScore() != null) {
						score += unit.getScore();
					}
					if (unit.getQuestion() != null && unit.getQuestion().getSubQuestions() != null
							&& unit.getQuestion().getSubQuestions().size() > 0) {
						nestQusNum += unit.getQuestion().getSubQuestions().size() - 1;
					}
				}
				count = paperDetailUnits.size() + nestQusNum;
				score = CommonUtils.formatDouble(score);
				paperDetail.setScore(score);
				paperDetail.setUnitCount(count);
				allQuesCount += count;
			} else {
				paperDetail.setScore(0d);
			}
		}
		paperDetailRepo.saveAll(paperDetails);
		paperInfoMap.put("allQuesCount", allQuesCount);
		paperInfoMap.put("paperDetails", paperDetails);
		return paperInfoMap;
	}

	/**
	 * 重新设置试卷大题数量、小题数量、试卷总分等属性
	 *
	 * @param paper
	 * @param user
	 * @param paperInfoMap
	 * @param paperTotalScore
	 */
	@SuppressWarnings("unchecked")
	private void reSetPaper(Paper paper, User user, Map<String, Object> paperInfoMap, double paperTotalScore) {
		List<PaperDetail> paperDetails = (List<PaperDetail>) paperInfoMap.get("paperDetails");
		paper.setPaperDetailCount(paperDetails.size());// 设置大题数量
		paper.setUnitCount(Integer.parseInt(paperInfoMap.get("allQuesCount") + ""));// 设置小题数量
		paper.setTotalScore(paperTotalScore);// 设置试卷总分
		if (user != null) {
			paper.setLastModifyName(user.getDisplayName());
		}
		paperRepo.save(paper);

		// 清除缓存
		this.clearPaperCache(paper.getId());
	}

	/**
	 * 先备份准备删掉的试题，然后再删掉
	 *
	 * @param questionId
	 * @return
	 */
	public void deleteImportQuestionById(String detailUnitId, String questionId, User user) {
		if(randomPaperQuestionService.existQuestion(questionId)) {
        	throw new StatusException("该试题已被抽题模板使用，不能删除");
        }
		PaperDetailUnit paperDetailUnit = null;
		if (detailUnitId != null) {
			paperDetailUnit = Model.of(paperDetailUnitRepo.findById(detailUnitId));
			Paper paper = paperDetailUnit.getPaper();
			if (paper.getInUse() != null && paper.getInUse() == 1) {
				throw new StatusException("500", "试卷已调用");
			}
		}
		Question ques = Model.of(quesRepo.findById(questionId));

		List<PaperDetailUnit> pdus = CommonUtils.toList(paperDetailUnitRepo.findByQuestion(ques));
		List<String> paperNames = new ArrayList<>();

		// 需要删除的小题
		List<PaperDetailUnit> needPdus = new ArrayList<>();

		List<Paper> papers = new ArrayList<>();
		for (PaperDetailUnit pdu : pdus) {
			if (pdu.getPaper() != null) {
				if (!papers.contains(pdu.getPaper())) {
					papers.add(pdu.getPaper());
				}
				if (PaperType.GENERATE == pdu.getPaper().getPaperType()) {
					paperNames.add(pdu.getPaper().getName());
				}
			}
		}
		String changInfo = null;
		if (paperNames.size() == 0) {
			needPdus.addAll(pdus);// 此试题没有被组卷调用，则可以删除此试题
			paperDetailUnitRepo.deleteAll(needPdus);
			quesBakRepo.save(BeanCopierUtil.copyProperties(ques, QuestionBak.class));
			quesRepo.delete(ques);

			for (Paper paper : papers) {
				if (paperDetailUnit != null && paperDetailUnit.getPaper().getId().equals(paper.getId())) {
					Double total = paper.getTotalScore();
					Integer dc = paper.getPaperDetailCount();
					Integer uc = paper.getUnitCount();
					formatPaper(paper, user);
					changInfo = PaperUtil.getPaperChangeInfo(total, dc, uc, paper);
				} else {
					formatPaper(paper, user);
				}
			}
		}

		if (paperDetailUnit != null) {
			StringBuilder sb = new StringBuilder();
			sb.append("课程:" + paperDetailUnit.getPaper().getCourse().getName() + "("
					+ paperDetailUnit.getPaper().getCourse().getCode() + ")");
			sb.append(" 试卷名称:" + paperDetailUnit.getPaper().getName());
			sb.append(" 第" + paperDetailUnit.getNumber() + "小题 ");
			sb.append(changInfo);
			ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
					AdminOperateType.TYPE48.getDesc(), sb.toString()));
		}
		if (CollectionUtils.isNotEmpty(paperNames)) {
			throw new StatusException("该试题被试卷：" + StringUtils.join(paperNames, ",") + "使用,不能删除");
		}
	}

	/**
	 * 向试卷中插入一个试题
	 *
	 * @param paperId
	 * @param paperDetailId
	 * @return
	 */
	public Paper insertQuestionToPaper(String paperId, String paperDetailId, Question question, User user) {
		Paper paper = Model.of(paperRepo.findById(paperId));
		question.setOrgId(user.getRootOrgId().toString());
		question.setCourse(paper.getCourse());
		question.setCourseNo(paper.getCourse().getCode());// 必须设置，因为部分方法需要courseNo作为查询条件
		question = quesService.saveQues(question);

		PaperDetail paperDetail = Model.of(paperDetailRepo.findById(paperDetailId));
		PaperDetailUnit paperDetailUnit = new PaperDetailUnit(paper, paperDetail, question);
		paperDetailUnit.setPaperType(PaperType.IMPORT);
		PaperDetailUnit paperDetailUnit2 = paperDetailUnitService.findTopOrderByNumber(paperDetail, "DESC");
		if (paperDetailUnit2 == null) {
			paperDetailUnit.setNumber(1);
		} else {
			paperDetailUnit.setNumber(paperDetailUnit2.getNumber());// 设置number为大题的最大题号
		}
		paperDetailUnitRepo.save(paperDetailUnit);
		formatPaper(paper, user);
		return paper;
	}

	/**
	 * 获取试题所在的试卷名称
	 *
	 * @param questionId
	 * @return
	 */
	public List<String> getPaperNamesByQuestionId(String questionId) {
		List<String> paperNames = new ArrayList<>();
		List<PaperDetailUnit> pdus = paperDetailUnitRepo.findByQuestion(Model.of(quesRepo.findById(questionId)));
		for (PaperDetailUnit pdu : pdus) {
			paperNames.add(pdu.getPaper().getName());
		}
		return paperNames;

	}

	public Page<Question> listQuestionforSelect(String paperId, int curPage, int pageSize, QuesStructType quesType,
			User user, String quesBody, UserDataRule ud) {
		Set<String> selectedIds = new HashSet<>();
		Paper paper = Model.of(paperRepo.findById(paperId));
		List<PaperDetailUnit> pdus = paperDetailUnitRepo.findByPaperOrderByNumber(paper);
		for (PaperDetailUnit pdu : pdus) {
			selectedIds.add(pdu.getQuestion().getId());
		}
		return quesService.findByIdExclude(selectedIds, paper.getCourseNo(), quesType, curPage, pageSize,
				user.getRootOrgId(), quesBody, ud);
	}

	@SuppressWarnings("unused")
	public Paper selectQuestionsToPaper(String paperId, String paperDetailId, List<Question> questions, User user) {
		Paper paper = Model.of(paperRepo.findById(paperId));
		if (paper.getInUse() != null && paper.getInUse() == 1) {
			throw new StatusException("500", "试卷已调用");
		}
		PaperDetail paperDetail = Model.of(paperDetailRepo.findById(paperDetailId));
		PaperDetailUnit paperDetailUnit = paperDetailUnitRepo.findTopByPaperDetailOrderByNumberDesc(paperDetail);

		List<PaperDetailUnit> saveUnits = new ArrayList<>();
		for (Question ques : questions) {
			PaperDetailUnit pdu = new PaperDetailUnit(paper, paperDetail, ques);

			// 如果大题没有小题，取导入的试题分数
			if (paperDetailUnit == null) {
				pdu.setNumber(1);
			} else {
				pdu.setNumber(paperDetailUnit.getNumber());// 设置为大题中最大的number
			}

			pdu.setScore(ques.getScore() == null ? 0d : ques.getScore());

			// 处理套题
			if (pdu.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
				List<Question> subQuestions = ques.getSubQuestions();
				List<Double> subScoreList = new ArrayList<>();
				if (subQuestions != null && subQuestions.size() > 0) {
					for (Question subQuestion : subQuestions) {
						subScoreList.add(CommonUtils.formatDouble(pdu.getScore() / subQuestions.size()));
					}
				}
				pdu.setSubScoreListNew(subScoreList);
			}

			saveUnits.add(pdu);
		}

		paperDetailUnitRepo.saveAll(saveUnits);

		// 清除缓存
		this.clearPaperCache(paper.getId());
		paper.setAuditStatus(false);
		Double total = paper.getTotalScore();
		Integer dc = paper.getPaperDetailCount();
		Integer uc = paper.getUnitCount();
		formatPaper(paper, user);

		StringBuilder paperInfo = new StringBuilder();
		paperInfo.append("课程:" + paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")");
		paperInfo.append(" 试卷名称:" + paper.getName());
		paperInfo.append(" 第" + paperDetail.getNumber() + "大题选题题数:" + questions.size());
		String changInfo = PaperUtil.getPaperChangeInfo(total, dc, uc, paper);
		if (changInfo != null) {
			paperInfo.append(changInfo);
		}
		ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
				AdminOperateType.TYPE49.getDesc(), paperInfo.toString()));
		return paper;
	}

	public boolean checkPaperName(String paperName, String orgId) {
		List<Paper> paperList = paperRepo.findByNameAndOrgId(paperName, orgId);
		if (CollectionUtils.isNotEmpty(paperList)) {
			return true;
		}
		return false;
	}

	public void checkPaperNameNew(String paperName, String orgId) throws Exception {
		List<Paper> paperList = paperRepo.findByNameAndOrgId(paperName, orgId);
		if (paperList != null && paperList.size() > 0) {
			throw new PaperException("试卷名称重复，请重新命名");
		}
	}

	public Page<Paper> getPapersNotInIds(PaperSearchInfo paperSearchInfo, String[] ids, int curPage, int pageSize,
			PaperType paperType, UserDataRule ud) {
		Set<String> selectedIds = new HashSet<>();
		for (String id : ids) {
			selectedIds.add(id);
		}
		if (ud.assertEmptyQueryResult()) {
			return Page.empty();
		}
		Query query = new Query();
		query.addCriteria(Criteria.where("orgId").is(paperSearchInfo.getOrgId()));
		if (ud.assertNeedQueryRefIds()) {
			query.addCriteria(Criteria.where("course.id").in(ud.stringRefIds()));
		}
		query.addCriteria(Criteria.where("paperType").is(paperType));
		query.addCriteria(Criteria.where("course.code").is(paperSearchInfo.getCourseNo()));
		query.addCriteria(Criteria.where("id").nin(selectedIds));
		if (PaperType.GENERATE.equals(paperType)) {
			query.addCriteria(Criteria.where("storage").ne(1));
		}
		long total = this.mongoTemplate.count(query, Paper.class);
		if (total == 0) {
			return Page.empty();
		}

		query.with(Sort.by(new Order(Direction.DESC, "createTime")));
		query.limit(pageSize);
		query.skip((curPage - 1L) * pageSize);
		List<Paper> paperList = this.mongoTemplate.find(query, Paper.class);
		Page<Paper> paperPageList = new PageImpl<Paper>(paperList, PageRequest.of(curPage - 1, pageSize), total);
		return paperPageList;
	}

	/**
	 * 使用原卷
	 */
	public void useBasePaper(String selectedPaperIds, String userId) {
		Assert.hasLength(selectedPaperIds, "试卷id不能为空!");
		String[] paperIds = selectedPaperIds.split(",");
		for (int i = 0; i < paperIds.length; i++) {
			Paper oldpaper = Model.of(paperRepo.findById(paperIds[i]));
			List<PaperDetail> paperDetails = paperDetailRepo.findByPaperOrderByNumber(oldpaper);
			for (PaperDetail paperDetail : paperDetails) {
				List<PaperDetailUnit> paperDetailUnits = paperDetailUnitService.getUnitsByPaperDetail(paperDetail);
				paperDetail.setPaperDetailUnits(paperDetailUnits);
			}
			// PaperUtil.sortDetails(paperDetails);
			oldpaper.setId(null);
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String createTime = sdf.format(new Date());
			oldpaper.setCreateTime(createTime);
			oldpaper.setCreator(userId);
			oldpaper.setLastModifyName(userId);
			oldpaper.setPaperType(PaperType.GENERATE);// 试卷类型 修改为组卷
			oldpaper.setAuditStatus(false);
			Paper newPaper = paperRepo.save(oldpaper);
			for (int j = 0; j < paperDetails.size(); j++) {
				PaperDetail paperDetail = paperDetails.get(j);
				paperDetail.setNumber(j + 1);
				List<PaperDetailUnit> paperDetailUnits = paperDetail.getPaperDetailUnits();
				paperDetail.setPaper(newPaper);// 关联新Paper
				paperDetail.setId(null);
				paperDetail.setCreateTime(createTime);
				paperDetail.setCreator(userId);
				PaperDetail newPaperDetail = paperDetailRepo.save(paperDetail);// 保存新的paperDetail
				for (int k = 0; k < paperDetailUnits.size(); k++) {
					// 重新设置保存PaperDetailUnit
					PaperDetailUnit paperDetailUnit = paperDetailUnits.get(k);
					paperDetailUnit.setPaper(newPaper); // 关联新Paper
					paperDetailUnit.setPaperDetail(newPaperDetail); // 关联新paperDetail
					paperDetailUnit.setId(null);
					paperDetailUnit.setCreateTime(createTime);
					paperDetailUnit.setCreator(userId);
					paperDetailUnit.setPaperType(PaperType.GENERATE);
					paperDetailUnitRepo.save(paperDetailUnit);// 保存新的paperDetailUnit
				}
			}
		}
	}

	/**
	 * 根据试卷名称、试卷类型检查名称是否存在
	 *
	 * @param paperName
	 * @param paperType
	 * @param orgId
	 * @return
	 * @throws Exception
	 */
	public boolean checkPaperName(String paperName, PaperType paperType, String orgId) throws Exception {
		Query query = new Query();
		query.addCriteria(Criteria.where("orgId").is(orgId));
		query.addCriteria(Criteria.where("name").is(paperName.trim()));
		query.addCriteria(Criteria.where("paperType").is(paperType));
		List<Paper> papers = this.mongoTemplate.find(query, Paper.class);
		if (papers != null && papers.size() > 0) {
			return false;
		}
		return true;
	}

	/**
	 * 上传音频文件检查
	 *
	 * @param paperId
	 * @param filesName
	 * @return
	 */
	public Map<String, String> checkRadioFile(String paperId, List<String> filesName) {
		Map<String, String> messageMap = new HashMap<>();
		// 判断文件名中格式是否正确
		for (String fileName : filesName) {
			String[] fileNames = fileName.split("\\.");
			String fileType = fileNames[fileNames.length - 1];
			if (sysProperty.getRadioType().indexOf(fileType) < 0) {
				messageMap.put("errorMsg", fileName + ",文件格式不正确,当前支持格式:" + sysProperty.getRadioType());
				return messageMap;
			}

			String pattern_01 = "\\d+_1_\\d{1,2}"; // 题干正则
			String pattern_02 = "\\d+_2_[A-Z|a-z]_\\d{1,2}"; // 选项正则
			if (!(Pattern.matches(pattern_01, fileNames[0]) || Pattern.matches(pattern_02, fileNames[0]))) {
				messageMap.put("errorMsg", fileName + "文件名格式不正确,请检查");
				return messageMap;
			}
		}

		// 根据试卷id，查询该试卷
		Paper paper = Model.of(paperRepo.findById(paperId));

		// 根据试卷查询所有的小题
		List<PaperDetailUnit> paperDetailUnits = paperDetailUnitRepo.findByPaperOrderByNumber(paper);

		String names = "";
		for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
			names = names + paperDetailUnit.getNumber().toString() + ",";
		}

		for (String fileName : filesName) {
			String fileNames[] = fileName.split("_");

			// 先判断小题号是否正确
			String fileNameFirst = fileNames[0];
			if (!names.contains(fileNameFirst)) {
				messageMap.put("errorMsg", fileName + "文件，试卷中没有对应的小题");
				return messageMap;
			}

			// 再判断题干中是否存在ABCD
			String fileNameSecond = fileNames[1];
			if (fileNameSecond.equals("1")) {
				if (fileNames.length > 3) {
					Matcher m = Pattern.compile(".*[a-zA-Z]+.*").matcher(fileNames[2]);
					if (m.matches()) {
						messageMap.put("errorMsg", fileName + "文件名称不对，文件名为题干，但存在选项");
						return messageMap;
					}
				}
			}

			// 判断选项
			else if (fileNameSecond.equals("2")) {
				messageMap = checkOptions(paperDetailUnits, fileNames, fileName);
				if (messageMap != null) {
					return messageMap;
				}
				messageMap = new HashMap<>();
			} else {
				messageMap.put("errorMsg", fileName + "文件名称不对，无法识别为题干或选项 ");
				return messageMap;
			}
		}

		messageMap.put("errorMsg", "OK");
		return messageMap;
	}

	// 判断选项
	public Map<String, String> checkOptions(List<PaperDetailUnit> paperDetailUnits, String fileNames[],
			String fileName) {
		Map<String, String> messageMap = new HashMap<>();
		for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
			if (paperDetailUnit.getNumber().toString().equals(fileNames[0])) {
				// 判断是否为选择题
				if (paperDetailUnit.getQuestionType() != QuesStructType.SINGLE_ANSWER_QUESTION
						&& paperDetailUnit.getQuestionType() != QuesStructType.MULTIPLE_ANSWER_QUESTION) {
					Matcher m = Pattern.compile(".*[a-zA-Z]+.*").matcher(fileNames[2]);
					if (m.matches()) {
						messageMap.put("errorMsg", fileName + "文件名称有误,题目不是选择题");
						return messageMap;
					}
					break;
				} else {
					List<QuesOption> options = paperDetailUnit.getQuestion().getQuesOptions();
					String option = "";
					for (QuesOption quesOption : options) {
						option = option + quesOption.getNumber() + ",";
					}
					Integer integer = CommonUtils.characterToNumber(fileNames[2]);
					if (!option.contains(integer.toString())) {
						messageMap.put("errorMsg", fileName + "文件名称有误，题目中没有对应的" + fileNames[2] + "选项");
						return messageMap;
					}
					break;
				}
			}
		}
		return null;
	}

	/**
	 * 上传音频文件到又拍云
	 *
	 * @param files
	 * @param paperId
	 * @throws IOException
	 */
	public void uploadRadio(List<MultipartFile> files, String paperId, User user) {
		// 根据试卷id，查询该试卷
		Paper paper = Model.of(paperRepo.findById(paperId));

		for (MultipartFile file : files) {
			// 判断文件大小
			long fileSize = file.getSize();
			int size = Integer.parseInt(sysProperty.getAudioMaxsize());
			if (fileSize > (size * 1048576L)) {
				throw new StatusException("400", "音频文件过大，限制" + size + "M内！");
			}

			// 根据试卷查询所有的小题，根据文件名匹配出当前小题ID
			String numbers[] = file.getOriginalFilename().split("_");
			if (numbers.length < 3) {
				throw new StatusException("400", "音频文件命名格式错误！");
			}

			PaperDetailUnit unit = paperDetailUnitRepo.findByPaperAndNumber(paper, Integer.valueOf(numbers[0]));
			if (unit == null || unit.getQuestion() == null) {
				throw new StatusException("400", "对应试题不存在！");
			}

			Question question = unit.getQuestion();
			uploadAudioFile(paperId, question.getId(), file, user);
			appendAudioTag(file.getOriginalFilename(), question.getId());

			// 清除缓存
			this.clearQuestionCache(question.getId());
		}

		// 删除服务器文件夹
		String mp3DirectoryPath = TEMP_FILE_EXP + File.separator + paperId;
		try {
			File mp3Directory = new File(mp3DirectoryPath);
			FileUtils.deleteDirectory(mp3Directory);
		} catch (IOException e) {
			LOG.error(e.getMessage(), e);
		}
	}

	/**
	 * 上传音频文件至又拍云
	 */
	private void uploadAudioFile(String paperId, String questionId, MultipartFile file, User user) {
		// 文件夹不存在则新建
		final String mp3DirectoryPath = TEMP_FILE_EXP + File.separator + paperId;
		File mp3Directory = new File(mp3DirectoryPath);
		if (!mp3Directory.exists()) {
			mp3Directory.mkdirs();
		}

		// 文件名包含随机数，防止缓存
		int randomNumber = random.nextInt(1000);
		final String mp3FileName = questionId + "_" + randomNumber + "_" + file.getOriginalFilename();
		File mp3File = new File(mp3DirectoryPath + File.separator + mp3FileName);

		FileOutputStream outputStream;
		try {
			outputStream = new FileOutputStream(mp3File);
		} catch (FileNotFoundException e) {
			LOG.error(e.getMessage(), e);
			throw new StatusException("500", "音频文件处理失败！");
		}

		try (InputStream is = file.getInputStream();
				BufferedInputStream bis = new BufferedInputStream(is, 1024 * 10);) {
			int read;
			byte[] bytes = new byte[1024 * 4];
			while ((read = bis.read(bytes, 0, 1024 * 4)) != -1) {
				outputStream.write(bytes, 0, read);
			}

			// 上传到又拍云
			// UpYun upYun = new UpYun(sysProperty.getBucketName(),
			// sysProperty.getUserName(), sysProperty.getPassword());
			// upYun.writeFile(sysProperty.getRadioUploadPath() + mp3FileName, mp3File,
			// true);
			// 通用存储
			FileStoragePathEnvInfo env = new FileStoragePathEnvInfo();
			env.setRelativePath(sysProperty.getRadioUploadPath() + mp3FileName);
			YunPathInfo pi = FileStorageUtil.saveFile("audioFile", env, mp3File, null);
			IoUtils.removeFile(mp3File);

			// 保存记录
			QuestionAudio audio = new QuestionAudio(questionId, file.getOriginalFilename(), pi.getRelativePath());
			questionAudioService.saveQuestionAudio(audio, user);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			throw new StatusException("500", "音频文件保存失败！");
		} finally {
			try {
				if (outputStream != null) {
					outputStream.close();
				}
			} catch (IOException e) {
				// ignore
			}
		}
	}

	/**
	 * 音频文件插入到标签
	 */
	private void appendAudioTag(String fileName, String questionId) {
		QuestionAudio questionAudio = questionAudioService.findByQuestionIdAndFileName(questionId, fileName);
		if (questionAudio == null) {
			return;
		}

		Question question = Model.of(quesRepo.findById(questionAudio.getQuestionId()));

		// 正则匹配音频标签
		Pattern audioPattern = Pattern.compile(String.format("<a id=\"[\\d,\\w]+\" name=\"%s\"></a>", fileName));
		final String audioTag = String.format("<a id=\"%s\" name=\"%s\"></a>", questionAudio.getId(), fileName);

		String numbers[] = fileName.split("_");
		if (numbers[1].equals("1")) {
			// 处理题干
			String quesBody = question.getQuesBody();
			if (StringUtils.isBlank(quesBody)) {
				question.setQuesBody("<p>" + audioTag + "</p>");
			} else {
				Matcher matcher = audioPattern.matcher(quesBody);
				if (matcher.find()) {
					// 已存在音频标签则直接替换
					question.setQuesBody(matcher.replaceAll(audioTag));
				} else {
					// 不存在音频标签则添加
					question.setQuesBody(quesBody + "<p>" + audioTag + "</p>");
				}
			}
		} else {
			// 处理选项
			for (QuesOption quesOption : question.getQuesOptions()) {
				Integer optNumber = CommonUtils.characterToNumber(numbers[2]);
				if (!quesOption.getNumber().equals(optNumber.toString())) {
					continue;
				}

				String optionBody = quesOption.getOptionBody();
				if (StringUtils.isBlank(optionBody)) {
					quesOption.setOptionBody("<p>" + audioTag + "</p>");
					continue;
				}

				Matcher matcher = audioPattern.matcher(optionBody);
				if (matcher.find()) {
					// 已存在音频标签则直接替换
					quesOption.setOptionBody(matcher.replaceAll(audioTag));
				} else {
					// 不存在音频标签则添加
					quesOption.setOptionBody(optionBody + "<p>" + audioTag + "</p>");
				}
			}
		}

		question.setHasAudio(true);
		quesRepo.save(question);
	}

	@Override
	public Map<String, Object> getPaperPDF(String paperId, String type) {
		PaperExp paperExp = getPaperDto(paperId);
		Map<String, Object> map = new HashMap<>();
		map.put("courseName", paperExp.getCourseName());
		map.put("courseNo", paperExp.getCourseNo());
		List<String> htmlList = new ArrayList<>();
		if (paperExp.getPaperDetails() != null && paperExp.getPaperDetails().size() > 0) {
			for (PaperDetailExp paperDetail : paperExp.getPaperDetails()) {
				// 添加大题标题
				if (StringUtils.isBlank(paperDetail.getTitle())) {
					// htmlList.add(("<p>"+paperDetail.getCnNum()+"、"+paperDetail.getName()+"</p>").replaceAll("'",
					// "&apos"));
					htmlList.add(("<p class='paperDetailTitle'>" + paperDetail.getCnNum() + "、" + paperDetail.getName()
							+ "</p>").replaceAll("'", "&apos"));
				} else {
					htmlList.add("<p class='paperDetailTitle'>" + paperDetail.getCnNum() + "、" + paperDetail.getName()
							+ paperDetail.getTitle() + "</p>");
				}
				if (paperDetail.getPaperDetailUnits() != null && paperDetail.getPaperDetailUnits().size() > 0) {
					for (PaperDetailUnitExp paperDetailUnitExp : paperDetail.getPaperDetailUnits()) {
						Question question = paperDetailUnitExp.getQuestion();
						if (type.equals("paper")) {
							// 添加题干
							String questionBody = CommonUtils.deleteHtmlP(paperDetailUnitExp.getNumber() + "."
									+ question.getQuesBody() + "(" + paperDetailUnitExp.getScore() + "分)");
							htmlList.add("<p class='questionBody'>" + questionBody + "</p>");
							// 判断是否为选择题
							if (paperDetailUnitExp.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
									|| paperDetailUnitExp
											.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
								for (QuesOption quesOption : question.getQuesOptions()) {
									// 添加选项
									String questionOption = CommonUtils.deleteHtmlP(
											CommonUtils.getOptionNum(Integer.parseInt(quesOption.getNumber()) - 1) + "."
													+ quesOption.getOptionBody());
									htmlList.add("<p class='selectOption'>" + questionOption + "</p>");
								}
							}
							// 判断是否为套题
							if (paperDetailUnitExp.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
								// 得到子题
								List<Question> subQuestions = question.getSubQuestions();
								for (Question subQuestion : subQuestions) {
									// 添加子题题干
									String subQuestionBody = CommonUtils
											.deleteHtmlP(subQuestion.getQuesParams().get("number") + "."
													+ subQuestion.getQuesBody() + "(" + subQuestion.getScore() + "分)");
									htmlList.add("<p class='questionBody'>" + subQuestionBody + "</p>");
									// 判断是否为选择题
									if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
											|| subQuestion
													.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
										for (QuesOption quesOption : subQuestion.getQuesOptions()) {
											// 添加选项
											String questionOption = CommonUtils.deleteHtmlP(CommonUtils
													.getOptionNum(Integer.parseInt(quesOption.getNumber()) - 1) + "."
													+ quesOption.getOptionBody());
											htmlList.add("<p class='selectOption'>" + questionOption + "</p>");
										}
									}
								}
							}
						} else {
							// 判断是否为套题
							if (paperDetailUnitExp.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
								// 得到子题
								List<Question> subQuestions = question.getSubQuestions();
								for (Question subQuestion : subQuestions) {
									// 添加答案
									String questionAnswer = CommonUtils
											.deleteHtmlP(subQuestion.getQuesParams().get("number") + "."
													+ subQuestion.getQuesAnswer() + "(" + subQuestion.getScore()
													+ "分)");
									htmlList.add("<p class='questionAnswer'>" + questionAnswer + "</p>");
								}
							} else {
								// 添加答案
								String questionAnswer = CommonUtils.deleteHtmlP(paperDetailUnitExp.getNumber() + "."
										+ question.getQuesAnswer() + "(" + paperDetailUnitExp.getScore() + "分)");
								htmlList.add("<p class='questionAnswer'>" + questionAnswer + "</p>");
							}
						}

					}
				}
			}
		}
		map.put("htmlList", htmlList);
		return map;
	}

	@SuppressWarnings("unused")
	@Override
	public String sendPrint(String paperId, String orgId, String examId) {
		// 查询原paper对象
		Paper paper = Model.of(paperRepo.findById(paperId));
		SyncCoursePaperBean bean = new SyncCoursePaperBean();
		bean.setOrgId(Long.valueOf(orgId));
		bean.setExamId(Long.valueOf(examId));
		Course course = courseService.getCourse(Long.valueOf(orgId), paper.getCourse().getCode());
		bean.setCourseId(Long.valueOf(course.getId()));
		bean.setCourseCode(paper.getCourse().getCode());
		bean.setCourseName(paper.getCourse().getName());
		bean.setPaperId(paper.getId());
		bean.setPaperName(paper.getName());
		SyncCoursePaperReq req = new SyncCoursePaperReq();
		req.setBean(bean);
		SyncCoursePaperResp resp = coursePaperCloudService.syncCoursePaper(req);
		return "success";
	}

	@Override
	public String findQuestionStructure(String paperId) throws Exception {
		// 根据paperId查询新的对象
		ExportPaperAbstractService exportPaperAbstractService = (ExportPaperAbstractService) SpringContextHolder
				.getBean("exportPaperAbstractService");
		DownloadPaperDto dto = new DownloadPaperDto();
		dto.setPaperId(paperId);
		dto.setSeqMode(PaperSeqMode.MODE3);
		PaperExp paperExp = exportPaperAbstractService.initPaperExp(dto);
		LOG.info("已经获取试卷");
		// 得到试卷中的客观大题
		List<PaperDetailExp> objectiveDetails = exportPaperAbstractService.getAllObjectiveDetails(paperExp);
		// 客观题中如果有套题，要拆开
		List<PaperDetailExp> newObjectiveDetails = buildPaperDetailExp(objectiveDetails);
		// 生成客观题数集合
		List<ObjectiveQuestionStructure> objectiveQuestionStructureList = new ArrayList<>();
		for (PaperDetailExp paperDetailExp : newObjectiveDetails) {
			for (PaperDetailUnitExp unit : paperDetailExp.getPaperDetailUnits()) {
				objectiveQuestionStructureList
						.add(new ObjectiveQuestionStructure(paperExp, paperDetailExp, unit, null));
			}
		}
		LOG.info("生成客观题数集合");
		// 得到试卷中的主观大题
		List<PaperDetailExp> subjectiveDetails = exportPaperAbstractService.getAllSubjectiveDetails(paperExp);
		// 生成主观题数集合
		List<SubjectiveQuestionStructure> subjectiveQuestionStructureList = new ArrayList<>();
		for (PaperDetailExp paperDetailExp : subjectiveDetails) {
			for (PaperDetailUnitExp unit : paperDetailExp.getPaperDetailUnits()) {
				subjectiveQuestionStructureList
						.add(new SubjectiveQuestionStructure(paperExp, paperDetailExp, unit, null));
			}
		}
		LOG.info("生成主观题数集合");
		PaperQuestionStructureInfo info = new PaperQuestionStructureInfo();
		info.setObjectives(objectiveQuestionStructureList);
		info.setSubjectives(subjectiveQuestionStructureList);
		String jsonResp = JsonUtil.toJson(info);
		LOG.info("jsonResp:" + jsonResp);
		return jsonResp;
	}

	public List<PaperDetailExp> buildPaperDetailExp(List<PaperDetailExp> objectiveDetails) {
		// 1.得到所有客观题大题的类型
		Set<QuesStructType> types = new HashSet<>();
		for (PaperDetailExp paperDetailExp : objectiveDetails) {
			types.add(QuesStructType.getQuesStructTypeById(paperDetailExp.getSortNumber()));
		}
		// 2.生成新的大题集合
		List<PaperDetailExp> newObjs = new ArrayList<>();
		for (QuesStructType type : types) {
			for (PaperDetailExp paperDetailExp : objectiveDetails) {
				if (paperDetailExp.getSortNumber().equals(type.getId())) {
					// 生成新的大题
					PaperDetailExp newPaperDetailExp = new PaperDetailExp();
					// 生成新的小题
					List<PaperDetailUnitExp> newPaperDetailUnits = new ArrayList<>();
					// 得到旧大题下的所有小题
					List<PaperDetailUnitExp> paperDetailUnits = paperDetailExp.getPaperDetailUnits();
					for (PaperDetailUnitExp paperDetailUnitExp : paperDetailUnits) {
						if (paperDetailUnitExp.getQuestionType() != QuesStructType.NESTED_ANSWER_QUESTION) {
							newPaperDetailUnits.add(paperDetailUnitExp);
						} else {
							List<Question> subQuestions = paperDetailUnitExp.getQuestion().getSubQuestions();
							if (subQuestions != null && subQuestions.size() > 0) {
								for (int i = 0; i < subQuestions.size(); i++) {
									newPaperDetailUnits.add(new PaperDetailUnitExp(type, subQuestions.get(i)));
								}
							}
						}
					}
					// 设置小题
					newPaperDetailExp.setPaperDetailUnits(newPaperDetailUnits);
					// 设置大题题号
					newPaperDetailExp.setNumber(Integer.parseInt(type.getId() + ""));
					newPaperDetailExp.setSortNumber(paperDetailExp.getSortNumber());
					newObjs.add(newPaperDetailExp);
				}
			}
		}
		// 刷一遍number
		int num = 0;
		for (QuesStructType type : types) {
			for (int i = 0; i < newObjs.size(); i++) {
				if (newObjs.get(i).getSortNumber().equals(type.getId())) {
					List<PaperDetailUnitExp> exps = newObjs.get(i).getPaperDetailUnits();
					for (int j = 0; j < exps.size(); j++) {
						num++;
						exps.get(j).setNumber(num);
					}
				}
			}
		}
		return newObjs;
	}

	@Override
	public PaperExp getDownPaperExp(String paperId) {
		Paper paper = Model.of(paperRepo.findById(paperId));
		// 创建paperDto
		PaperExp paperExp = BeanCopierUtil.copyProperties(paper, PaperExp.class);
		paperExp.setCourseName(paper.getCourse().getName());
		paperExp.setCourseNo(paper.getCourse().getCode());
		paperExp.setCourse(paper.getCourse());
		// 获取大题
		List<PaperDetail> paperDetails = paperDetailRepo.findByPaperOrderByNumber(paper);
		List<PaperDetailExp> paperDetailExps = new ArrayList<>();
		for (PaperDetail paperDetail : paperDetails) {
			PaperDetailExp paperDetailExp = new PaperDetailExp();
			BeanUtils.copyProperties(paperDetail, paperDetailExp);
			if (StringUtils.isBlank(paperDetailExp.getName())) {
				paperDetailExp.setName("默认大题");
			}
			paperDetailExps.add(paperDetailExp);
		}
		// 封装小题
		for (int i = 0; i < paperDetailExps.size(); i++) {
			List<PaperDetailUnit> paperDetailUnits = paperDetailUnitRepo
					.findByPaperDetailOrderByNumber(paperDetails.get(i));
			if (paperDetailUnits != null && paperDetailUnits.size() > 0) {
				List<PaperDetailUnitExp> paperDetailUnitExps = new ArrayList<>();
				for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
					PaperDetailUnitExp paperDetailUnitExp = new PaperDetailUnitExp();
					BeanUtils.copyProperties(paperDetailUnit, paperDetailUnitExp);
					paperDetailUnitExps.add(paperDetailUnitExp);
				}
				paperDetailExps.get(i).setPaperDetailUnits(paperDetailUnitExps);
				paperDetailExps.get(i).setUnitCount(paperDetailUnitExps.size());
			} else {
				paperDetailExps.get(i).setUnitCount(0);
			}
		}
		paperExp.setPaperDetails(paperDetailExps);
		if (paperExp.getPaperDetails() == null || paperExp.getPaperDetails().size() == 0) {
			return paperExp;
		}
		int mainNum = 0;
		List<PaperDetailExp> paperDetailExpList = paperExp.getPaperDetails();
		for (PaperDetailExp paperDetail : paperDetailExpList) {
			// 大题序号
			paperDetail.setNumber(++mainNum);
			paperDetail.setCnNum(CommonUtils.toCHNum(paperDetail.getNumber()));
			if (paperDetail.getPaperDetailUnits() == null || paperDetail.getPaperDetailUnits().size() == 0) {
				throw new StatusException("500", "第" + mainNum + "大题没有小题");
			}
			if (paperDetail != null && paperDetail.getPaperDetailUnits() != null
					&& paperDetail.getPaperDetailUnits().size() > 0) {
				for (PaperDetailUnitExp paperDetailUnit : paperDetail.getPaperDetailUnits()) {
					Question question = paperDetailUnit.getQuestion();
					question.setDifficultyDegree(question.getDifficultyDegree() * 10);
					if (question.getPublicity() == null || question.getPublicity() == true) {
						paperDetailUnit.setPublicity("公开");
					} else {
						paperDetailUnit.setPublicity("非公开");
					}
					if (StringUtils.isBlank(paperDetail.getQuesType())) {
						paperDetail.setQuesType(question.getQuestionType().getName());
					}
					// 设置选项
					List<QuesOption> optionList = paperDetailUnit.getQuestion().getQuesOptions();
					if (optionList != null && optionList.size() > 0) {
						// int index = 0;
						// for (QuesOption quesOption : optionList) {
						// quesOption.setOptionBodyWord(setOptionNum(quesOption.getOptionBodyWord(),
						// getOptionNum(index)));
						// index++;
						// }
					}
					if (question.getQuestionType() != QuesStructType.NESTED_ANSWER_QUESTION) {
						// 给小题设置序号
						// question.setQuesBodyWord(setSubQuesNum(question.getQuesBodyWord(),
						// minNum++));
						// question.setQuesAnswerWord(setAnswerNum(question.getQuesAnswerWord()));
						fillProperty(question);
						if (paperDetail.getFirstScore() == null) {
							if (question.getScore() == null) {
								paperDetail.setFirstScore(0d);
							} else {
								paperDetail.setFirstScore(question.getScore());
							}
						}
					} else {
						List<Question> subQuesList = question.getSubQuestions();
						// 套题小题设置序号
						if (subQuesList != null && subQuesList.size() > 0) {
							for (Question subQues : subQuesList) {
								subQues.setDifficultyDegree(subQues.getDifficultyDegree() * 10);
								// subQues.setQuesBodyWord(setSubQuesNum(subQues.getQuesBodyWord(), index++));
								// subQues.setQuesAnswerWord(setAnswerNum(subQues.getQuesAnswerWord()));
								if (paperDetail.getFirstScore() == null) {
									if (subQues.getScore() == null) {
										paperDetail.setFirstScore(0d);
									} else {
										paperDetail.setFirstScore(subQues.getScore());
									}
								}
								fillProperty(subQues);
							}
						}
					}
					List<QuesProperty> quesProperties = question.getQuesProperties();
					if (quesProperties != null && quesProperties.size() > 0) {
						QuesProperty quesProperty = quesProperties.get(0);
						if (quesProperty.getFirstProperty() != null) {
							paperDetailUnit.setFirstName(quesProperty.getFirstProperty().getName());
							paperDetailUnit.setFirstCode(quesProperty.getFirstProperty().getCode());
						}
						if (quesProperty.getSecondProperty() != null) {
							paperDetailUnit.setSecondName(quesProperty.getSecondProperty().getName());
							paperDetailUnit.setSecondCode(quesProperty.getSecondProperty().getCode());
						}
					}
				}
			}
		}
		return paperExp;
	}

	private void fillProperty(Question question) {
		List<QuesProperty> ret = new ArrayList<>();
		List<QuesProperty> quesProperties = question.getQuesProperties();
		if (quesProperties != null && quesProperties.size() > 0) {
			for (QuesProperty quesProperty : quesProperties) {
				if (quesProperty.getFirstProperty() != null && quesProperty.getFirstProperty().getId() != null) {
					Property curProperty = Model.of(propertyRepo.findById(quesProperty.getFirstProperty().getId()));
					if (curProperty == null) {
						continue;
					}
					quesProperty.setFirstProperty(curProperty);
				}
				if (quesProperty.getSecondProperty() != null && quesProperty.getSecondProperty().getId() != null) {
					Property curProperty = Model.of(propertyRepo.findById(quesProperty.getSecondProperty().getId()));
					if (curProperty == null) {
						continue;
					}
					quesProperty.setSecondProperty(curProperty);
				}
				ret.add(quesProperty);
			}
		}
		question.setQuesProperties(ret);
	}

	/**
	 * 设置题号
	 *
	 * @param quesBodyWordMl
	 * @param num
	 * @return
	 * @throws Exception
	 */
	// public String setSubQuesNum(String quesBodyWordMl, int num) throws Exception
	// {
	// String tmpStr = DocxProcessUtil.BODY_HEADER + quesBodyWordMl +
	// DocxProcessUtil.BODY_TAIL;
	// Body body = (Body) XmlUtils.unmarshalString(tmpStr, Context.jc, Body.class);
	// List<Object> pList = body.getContent();
	// int index = 0;
	// for (Object pObj : pList) {
	// if (index > 0) {
	// break;
	// }
	// P p = (P) pObj;
	// List<Object> pContent = p.getContent();
	// R run = new R();
	// Text text = new Text();
	// text.setValue(num + ". ");
	// run.getContent().add(text);
	// pContent.add(0, run);
	// index++;
	// }
	// StringBuffer pWordMl = new StringBuffer();
	// for (Object pObj : pList) {
	// if (pObj instanceof P) {
	// pWordMl.append(DocxProcessUtil.formatPWordMl(XmlUtils.marshaltoString(pObj)));
	// }
	// }
	// return pWordMl.toString();
	// }

	/**
	 * 将数字1,2,3,4转化成A,B,C,D
	 *
	 * @param number
	 * @return
	 */
	public String getOptionNum(int number) {
		char optionNum = (char) (65 + number);
		return String.valueOf(optionNum);
	}

	/**
	 * 设置选项号
	 *
	 * @param optionWordMl
	 * @param num
	 * @return
	 * @throws Exception
	 */
	// public String setOptionNum(String optionWordMl, String num) throws Exception
	// {
	// String tmpStr = DocxProcessUtil.BODY_HEADER + optionWordMl +
	// DocxProcessUtil.BODY_TAIL;
	// Body body = (Body) XmlUtils.unmarshalString(tmpStr, Context.jc, Body.class);
	// List<Object> pList = body.getContent();
	// int index = 0;
	// for (Object pObj : pList) {
	// if (index > 0) {
	// break;
	// }
	// P p = (P) pObj;
	// List<Object> pContent = p.getContent();
	// R run = new R();
	// Text text = new Text();
	// text.setValue(num + ". ");
	// run.getContent().add(text);
	// pContent.add(0, run);
	// index++;
	// }
	// StringBuffer pWordMl = new StringBuffer();
	// for (Object pObj : pList) {
	// if (pObj instanceof P) {
	// pWordMl.append(DocxProcessUtil.formatPWordMl(XmlUtils.marshaltoString(pObj)));
	// }
	// }
	// return pWordMl.toString();
	// }

	// public String setAnswerNum(String quesBodyWordMl) throws Exception {
	// String tmpStr = DocxProcessUtil.BODY_HEADER + quesBodyWordMl +
	// DocxProcessUtil.BODY_TAIL;
	// Body body = (Body) XmlUtils.unmarshalString(tmpStr, Context.jc, Body.class);
	// List<Object> pList = body.getContent();
	// int index = 0;
	// for (Object pObj : pList) {
	// if (index > 0) {
	// break;
	// }
	// P p = (P) pObj;
	// List<Object> pContent = p.getContent();
	// R run = new R();
	// Text text = new Text();
	// text.setValue("[答案]:");
	// run.getContent().add(text);
	// pContent.add(0, run);
	// index++;
	// }
	// StringBuffer pWordMl = new StringBuffer();
	// for (Object pObj : pList) {
	// if (pObj instanceof P) {
	// pWordMl.append(DocxProcessUtil.formatPWordMl(XmlUtils.marshaltoString(pObj)));
	// }
	// }
	// return pWordMl.toString();
	// }
	@Override
	public int getQuestionTypeNumbers(String paperId, Integer publicityType, Integer difficultyType) {
		if (publicityType == null || difficultyType == null) {
			return 0;
		}
		Boolean publicity = null;
		String difficulty = null;
		if (publicityType == 1) {
			publicity = true;
		} else {
			publicity = false;
		}
		if (difficultyType == 1) {
			difficulty = "易";
		} else if (difficultyType == 2) {
			difficulty = "中";
		} else {
			difficulty = "难";
		}
		String needType = publicity + "-" + difficulty;
		Paper paper = Model.of(paperRepo.findById(paperId));
		List<PaperDetailUnit> units = paperDetailUnitRepo.findByPaperOrderByNumber(paper);
		if (units != null && units.size() > 0) {
			int total = 0;
			for (PaperDetailUnit unit : units) {
				if (unit.getQuestion().getPublicity() == null) {
					unit.getQuestion().setPublicity(true);
				}
				if (unit.getQuestion().getDifficulty() == null) {
					unit.getQuestion().setDifficulty("中");
				}
				String factType = unit.getQuestion().getPublicity() + "-" + unit.getQuestion().getDifficulty();
				if (needType.equals(factType)) {
					total += 1;
				}
			}
			return total;
		}
		return 0;
	}

	@Override
	public double getQuestionTypeScore(String paperId, Integer publicityType, Integer difficultyType) {
		if (publicityType == null || difficultyType == null) {
			return 0;
		}
		Boolean publicity = null;
		String difficulty = null;
		if (publicityType == 1) {
			publicity = true;
		} else {
			publicity = false;
		}
		if (difficultyType == 1) {
			difficulty = "易";
		} else if (difficultyType == 2) {
			difficulty = "中";
		} else {
			difficulty = "难";
		}
		String needType = publicity + "-" + difficulty;
		Paper paper = Model.of(paperRepo.findById(paperId));
		List<PaperDetailUnit> units = paperDetailUnitRepo.findByPaperOrderByNumber(paper);
		if (units != null && units.size() > 0) {
			double total = 0;
			BigDecimal b1 = BigDecimal.valueOf(total);
			for (PaperDetailUnit unit : units) {
				if (unit.getQuestion().getPublicity() == null) {
					unit.getQuestion().setPublicity(true);
				}
				if (unit.getQuestion().getDifficulty() == null) {
					unit.getQuestion().setDifficulty("中");
				}
				String factType = unit.getQuestion().getPublicity() + "-" + unit.getQuestion().getDifficulty();
				if (needType.equals(factType)) {
					BigDecimal b2 = BigDecimal.valueOf(unit.getScore());
					b1 = b1.add(b2);
				}
			}
			return b1.doubleValue();
		}
		return 0;
	}

	private void clearPaperCache(String paperId) {
		// 清理与当前试卷相关的缓存
		// final String patternKey = CACHE_KEY_PAPER + "*" + paperId;
		// Set<String> keys = redisTemplate.keys(patternKey);
		// if (CollectionUtils.isNotEmpty(keys)) {
		// redisTemplate.delete(keys);
		// }
		extractConfigPaperCache.remove(paperId);
		basePaperCache.remove(paperId);
	}

	private 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);
	}

	@Override
	public List<PaperAnswerDomain> answerExport(Paper paper) {
		List<PaperAnswerDomain> ret = new ArrayList<PaperAnswerDomain>();
		List<PaperDetail> paperDetails = paperDetailRepo.findByPaperOrderByNumber(paper);
		if (paperDetails != null && paperDetails.size() > 0) {
			for (PaperDetail paperDetail : paperDetails) {
				List<PaperDetailUnit> paperDetailUnits = paperDetailUnitRepo
						.findByPaperDetailOrderByNumber(paperDetail);
				if (paperDetailUnits != null && paperDetailUnits.size() > 0) {
					for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
						Question ques = paperDetailUnit.getQuestion();
						if (QuesStructType.NESTED_ANSWER_QUESTION.equals(ques.getQuestionType())) {
							List<Question> subques = ques.getSubQuestions();
							if (subques != null && subques.size() > 0) {
								for (int i = 0; i < subques.size(); i++) {
									Question subq = subques.get(i);
									PaperAnswerDomain domain = new PaperAnswerDomain();
									domain.setName(paperDetail.getName());
									domain.setNumber(paperDetail.getNumber());
									domain.setSubNumber(paperDetailUnit.getNumber());
									domain.setSubType(subq.getQuestionType().getName());
									domain.setChildNumber(i + 1);
									domain.setAnswer(subq.getQuesAnswer());
									ret.add(domain);
								}
							}
						} else {
							PaperAnswerDomain domain = new PaperAnswerDomain();
							domain.setName(paperDetail.getName());
							domain.setNumber(paperDetail.getNumber());
							domain.setSubNumber(paperDetailUnit.getNumber());
							domain.setSubType(paperDetailUnit.getQuestionType().getName());
							domain.setAnswer(ques.getQuesAnswer());
							ret.add(domain);
						}
					}
				}
			}
		}
		return ret;
	}

	@Override
	public void answerImport(Paper paper, MultipartFile dataFile) {
		File file = new File(systemProperties.getTempDataDir() + File.separator + UUID.randomUUID() + ".xlsx");
		try {
			file.createNewFile();
			dataFile.transferTo(file);
			List<String[]> lineList = getData(file);
			if (lineList == null || lineList.size() == 0) {
				throw new StatusException("500", "没有导入的数据");
			}
			List<PaperAnswerDomain> ret = new ArrayList<PaperAnswerDomain>();
			for (int i = 0; i < lineList.size(); i++) {
				String[] line = lineList.get(i);
				PaperAnswerDomain domain = new PaperAnswerDomain();
				domain.setNumber(getNumber(trimAndNullIfBlank(line[0])));
				domain.setName(trimAndNullIfBlank(line[1]));
				domain.setSubNumber(getNumber(trimAndNullIfBlank(line[2])));
				domain.setChildNumber(getNumber(trimAndNullIfBlank(line[3])));
				domain.setSubType(trimAndNullIfBlank(line[4]));
				domain.setAnswer(trimAndNullIfBlank(line[5]));
				ret.add(domain);
			}
			if (ret == null || ret.size() == 0) {
				throw new StatusException("500", "没有导入的数据");
			}
			diposeAnswer(paper, ret);
		} catch (StatusException e) {
			throw e;
		} catch (Exception e) {
			throw new StatusException("500", "导入答案失败:" + e.getMessage(), e);
		} finally {
			FileUtil.deleteFile(file.getAbsolutePath());
		}
	}

	private List<String[]> getData(File file) {
		XSSFWorkbook wb = null;
		try {
			try {
				wb = new XSSFWorkbook(file);
			} catch (Exception e) {
				throw new StatusException("500", "文件类型错误");
			}
			List<String[]> outerList = new ArrayList<String[]>();
			XSSFSheet sheet = wb.getSheetAt(0);
			for (int i = 1; i <= sheet.getLastRowNum(); i++) {
				String[] innerList = new String[6];
				XSSFRow row = sheet.getRow(i);
				for (int j = 0; j < 6; j++) {
					XSSFCell cell = row.getCell(j);
					if (cell != null) {
						String cellinfo = cell.getStringCellValue();
						innerList[j] = cellinfo;
					} else {
						innerList[j] = "";
					}
				}
				if (!isEmpty(innerList)) {
					if (StringUtils.isEmpty(innerList[0])) {
						throw new StatusException("500", "文件类型错误" + (i + 1) + "行大题号不能为空");
					}
					if (StringUtils.isEmpty(innerList[2])) {
						throw new StatusException("500", "文件类型错误" + (i + 1) + "行小题号不能为空");
					}
					outerList.add(innerList);
				}
			}
			return outerList;
		} finally {
			if (wb != null) {
				try {
					wb.close();
				} catch (IOException e) {
					LOG.debug("wb.close() error " + e);
				}
			}
		}

	}

	private boolean isEmpty(String[] ss) {
		for (String s : ss) {
			if (StringUtils.isNotEmpty(s)) {
				return false;
			}
		}
		return true;
	}

	private void diposeAnswer(Paper paper, List<PaperAnswerDomain> domains) {
		Map<String, PaperAnswerDomain> answerMap = new LinkedHashMap<String, PaperAnswerDomain>();
		for (PaperAnswerDomain domain : domains) {
			String key = null;
			if (domain.getChildNumber() != null) {
				key = domain.getSubNumber() + "-" + domain.getChildNumber();
			} else {
				key = domain.getSubNumber().toString();
			}
			if (answerMap.get(key) == null) {
				answerMap.put(key, domain);
			} else {
				throw new StatusException("500", "导入数据中第" + domain.getSubNumber() + "小题"
						+ (domain.getChildNumber() != null ? "第" + domain.getChildNumber() + "子题" : "") + "重复");
			}
		}

		Map<String, Question> quesMap = new LinkedHashMap<String, Question>();
		List<PaperDetailUnit> paperDetailUnits = paperDetailUnitRepo.findByPaperIdOrderByNumber(paper.getId());
		for (PaperDetailUnit unit : paperDetailUnits) {
			Question qu = unit.getQuestion();
			if (QuesStructType.NESTED_ANSWER_QUESTION.equals(qu.getQuestionType())) {
				for (int i = 0; i < unit.getQuestion().getSubQuestions().size(); i++) {
					quesMap.put(unit.getNumber() + "-" + (i + 1), unit.getQuestion().getSubQuestions().get(i));
				}
			} else {
				quesMap.put(unit.getNumber().toString(), qu);
			}
		}

		Set<String> saveQuestion = new HashSet<String>();
		for (String key : answerMap.keySet()) {
			PaperAnswerDomain domain = answerMap.get(key);
			Question qu = quesMap.get(key);
			if (qu == null) {
				answerFomatErr(domain.getSubNumber(), domain.getChildNumber(), "试题信息不存在");
			}
			saveQuestion.add(domain.getSubNumber().toString());
		}

		Set<String> updateQuesIds = new HashSet<String>();
		List<Question> ques = new ArrayList<Question>();
		for (PaperDetailUnit unit : paperDetailUnits) {
			Question qu = unit.getQuestion();
			if (saveQuestion.contains(unit.getNumber().toString())) {
				if (QuesStructType.NESTED_ANSWER_QUESTION.equals(qu.getQuestionType())) {
					for (int i = 0; i < unit.getQuestion().getSubQuestions().size(); i++) {
						if (answerMap.get(unit.getNumber() + "-" + (i + 1)) != null) {
							checkAndSetAnswer(unit.getQuestion().getSubQuestions().get(i),
									answerMap.get(unit.getNumber() + "-" + (i + 1)).getAnswer(), unit.getNumber(),
									i + 1);
							updateQuesIds.add(qu.getId());
						}
					}
				} else {
					if (answerMap.get(unit.getNumber().toString()) != null) {
						checkAndSetAnswer(qu, answerMap.get(unit.getNumber().toString()).getAnswer(), unit.getNumber(),
								null);
						updateQuesIds.add(qu.getId());
					}
				}
			}
			ques.add(qu);
		}
		quesRepo.saveAll(ques);
		// 清除缓存
		for (String qid : updateQuesIds) {
			paperDetailUnitService.clearQuestionCache(qid);
		}
	}

	private void checkAndSetAnswer(Question question, String answer, Integer subNum, Integer childNum) {
		String fomatErrMsg = "答案格式错误";
		if (answer != null) {
			answer = answer.trim();
		}
		if (StringUtils.isBlank(answer)) {
			return;
		}
		if (QuesStructType.SINGLE_ANSWER_QUESTION.equals(question.getQuestionType())
				|| QuesStructType.MULTIPLE_ANSWER_QUESTION.equals(question.getQuestionType())) {
			answer = answer.replaceAll("\\s*", "");
			if (QuesStructType.SINGLE_ANSWER_QUESTION.equals(question.getQuestionType())) {
				if (answer.length() > 1) {
					answerFomatErr(subNum, childNum, fomatErrMsg);
				}
			}
			String[] pAnswerArray;
			if (answer.indexOf(",") != -1) {
				pAnswerArray = answer.split(",");
			} else {
				pAnswerArray = answer.split("");
			}
			List<QuesOption> options = question.getQuesOptions();
			List<String> optionNumList = new ArrayList<>();
			for (QuesOption quesOption : options) {
				Integer numInteger = Integer.parseInt(quesOption.getNumber());
				char word = (char) (numInteger + 64);
				optionNumList.add(word + "");
			}
			for (String option : pAnswerArray) {
				String pattern = "[A-Z]";
				if (!Pattern.matches(pattern, option)) {
					answerFomatErr(subNum, childNum, fomatErrMsg);
				}
				if (!optionNumList.contains(option)) {
					answerFomatErr(subNum, childNum, "答案选项不存在");
				}
			}
			if (pAnswerArray.length > 1) {
				question.setQuesAnswer(StringUtils.join(pAnswerArray, ","));
			} else {
				question.setQuesAnswer(answer);
			}
			processSelectOption(question);
		} else if (QuesStructType.BOOL_ANSWER_QUESTION.equals(question.getQuestionType())) {
			if (!answer.equals("正确") && !answer.equals("错误")) {
				answerFomatErr(subNum, childNum, fomatErrMsg);
			}
			question.setQuesAnswer(answer);
		} else if (QuesStructType.FILL_BLANK_QUESTION.equals(question.getQuestionType())) {
			String body = question.getQuesBody();
			Document bodyDoc = Jsoup.parse(body);
			String bodyText = bodyDoc.body().html();
			Document answerDoc = null;
			try {
				answerDoc = Jsoup.parse(answer);
			} catch (Exception e) {
				answerFomatErr(subNum, childNum, "答案格式html解析错误");
			}
			if (answerDoc.body().childrenSize() != 0) {
				String answerText = answerDoc.body().html();
				if (StringUtils.isNotBlank(answerText)) {
					if (getSubStringCount(bodyText, "###") != answerText.split("##").length) {
						answerFomatErr(subNum, childNum, "题干空格数量和答案数量不一致");
					}
				}
			}
			question.setQuesAnswer(answer);
		} else if (QuesStructType.TEXT_ANSWER_QUESTION.equals(question.getQuestionType())) {
			try {
				Jsoup.parse(answer);
			} catch (Exception e) {
				answerFomatErr(subNum, childNum, "答案格式html解析错误");
			}
			question.setQuesAnswer(answer);
		}

	}

	private int getSubStringCount(String src, String find) {
		int o = 0;
		int index = -1;
		while ((index = src.indexOf(find, index)) > -1) {
			++index;
			++o;
		}
		return o;
	}

	private void processSelectOption(Question question) {
		String answer = question.getQuesAnswer();
		if (StringUtils.isNotBlank(answer)) {
			String[] answerArray = answer.split(",");
			for (int i = 0; i < question.getQuesOptions().size(); i++) {
				QuesOption quesOption = question.getQuesOptions().get(i);
				char number = (char) (Integer.parseInt(quesOption.getNumber()) + 64);
				if (ArrayUtils.contains(answerArray, String.valueOf(number))) {
					quesOption.setIsCorrect((short) 1);
				} else {
					quesOption.setIsCorrect((short) 0);
				}
			}
		}
	}

	private void answerFomatErr(Integer subNum, Integer childNum, String msg) {
		throw new StatusException("500",
				"导入数据中第" + subNum + "小题" + (childNum != null ? "第" + childNum + "子题" : "") + msg);
	}

	private String trimAndNullIfBlank(String s) {
		if (StringUtils.isBlank(s)) {
			return null;
		}
		return s.trim();
	}

	private Integer getNumber(String s) {
		if (StringUtils.isBlank(s)) {
			return null;
		}
		return Integer.valueOf(s.trim());
	}

	@Override
	public List<String> findPaperId(Long fromRootOrgId) {
		Query query = new Query();
		query.addCriteria(Criteria.where("orgId").is(fromRootOrgId.toString()));
		query.addCriteria(Criteria.where("paperType").is(PaperType.IMPORT));
		List<PaperId> paperList = this.mongoTemplate.find(query, PaperId.class, "paper");
		if (CollectionUtils.isEmpty(paperList)) {
			return null;
		}
		List<String> ids = paperList.stream().map(e -> e.getId()).collect(Collectors.toList());
		return ids;
	}

}