Kaynağa Gözat

提交导出试卷代码

chenken 7 yıl önce
ebeveyn
işleme
d92e2c7de3

+ 621 - 0
cqb-paper/src/main/java/com/qmth/cqb/paper/service/export/ExportPaperAbstractService.java

@@ -0,0 +1,621 @@
+package com.qmth.cqb.paper.service.export;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import main.java.com.UpYun;
+
+import org.apache.commons.io.FileUtils;
+import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.domain.Example;
+
+import cn.com.qmth.examcloud.common.dto.question.enums.QuesStructType;
+import cn.com.qmth.examcloud.common.uac.entity.AccessUser;
+import cn.com.qmth.examcloud.common.util.excel.ExcelWriter;
+
+import com.google.gson.Gson;
+import com.qmth.cqb.paper.dao.AudioTimeConfigRepo;
+import com.qmth.cqb.paper.dao.PaperDetailRepo;
+import com.qmth.cqb.paper.dao.PaperDetailUnitRepo;
+import com.qmth.cqb.paper.dao.PaperRepo;
+import com.qmth.cqb.paper.dto.ObjectiveQuestionStructure;
+import com.qmth.cqb.paper.dto.PaperDetailExp;
+import com.qmth.cqb.paper.dto.PaperDetailUnitExp;
+import com.qmth.cqb.paper.dto.PaperExp;
+import com.qmth.cqb.paper.dto.SubjectiveQuestionStructure;
+import com.qmth.cqb.paper.model.AudioTimeConfig;
+import com.qmth.cqb.paper.model.ExamFile;
+import com.qmth.cqb.paper.model.ExportStructure;
+import com.qmth.cqb.paper.model.ExtractConfig;
+import com.qmth.cqb.paper.model.Paper;
+import com.qmth.cqb.paper.model.PaperDetailUnit;
+import com.qmth.cqb.paper.model.QuestionTypeNum;
+import com.qmth.cqb.paper.model.computerTestModel.Block;
+import com.qmth.cqb.paper.model.computerTestModel.ComputerTestOption;
+import com.qmth.cqb.paper.model.computerTestModel.ComputerTestPaper;
+import com.qmth.cqb.paper.model.computerTestModel.ComputerTestPaperDetail;
+import com.qmth.cqb.paper.model.computerTestModel.ComputerTestQuestion;
+import com.qmth.cqb.paper.model.computerTestModel.Section;
+import com.qmth.cqb.paper.model.computerTestModel.Sections;
+import com.qmth.cqb.paper.service.ExamFileService;
+import com.qmth.cqb.paper.service.PaperDetailService;
+import com.qmth.cqb.paper.service.PaperService;
+import com.qmth.cqb.question.dao.QuesRepo;
+import com.qmth.cqb.question.model.Question;
+import com.qmth.cqb.question.model.QuestionAudio;
+import com.qmth.cqb.question.service.QuestionAudioService;
+import com.qmth.cqb.utils.CommonUtils;
+import com.qmth.cqb.utils.FileDisposeUtil;
+import com.qmth.cqb.utils.enums.ExamFileType;
+import com.qmth.cqb.utils.word.DocxProcessUtil;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2017年7月15日 下午4:00:24
+ * @company 	QMTH
+ * @description 导出、上传文件service 父类
+ */
+public abstract class ExportPaperAbstractService {
+	
+	@Autowired
+	protected PaperRepo paperRepo;
+
+	@Autowired
+	protected PaperService paperService;
+
+	@Autowired
+	protected PaperDetailRepo paperDetailRepo;
+
+	@Autowired
+	protected PaperDetailUnitRepo paperDetailUnitRepo;
+	
+	@Autowired
+	protected ExamFileService examFileService;
+
+	@Autowired
+	protected PaperDetailService paperDetailService;
+	
+	@Autowired
+	protected QuestionAudioService questionAudioService;
+	
+	@Autowired
+	protected BuildComputerTestJsonService buildComputerTestJsonService;
+	
+	@Autowired
+	protected InitPaperExpService initPaperExpService;
+	
+	@Autowired
+	protected QuesRepo quesRepo;
+	
+	@Autowired
+	protected AudioTimeConfigRepo audioTimeConfigRepo;
+
+	public static final String FILL_BLANK_QUESTION_FLAG = "###";
+	
+	public static final String TEMP_FILE_EXP = "docxExport/";
+	
+	/**
+	 * Word文件后缀
+	 */
+	public static final String DOCX_SUFFIX = ".docx";
+	/**
+	 * Excel文件后缀
+	 */
+	public static final String EXCEL_SUFFIX = ".xlsx";
+	/**
+	 * ZIP文件后缀
+	 */
+	public static final String ZIP_SUFFIX = ".zip";
+	
+	public static final String ENCODING = "UTF-8";
+	/**
+	 * 下划线
+	 */
+	private static final String UNDERSCORE = "_";
+
+	//陕西师范模板
+	protected static Template SXSF_TEMPLATE_PAPER;
+	protected static Template SXSF_TEMPLATE_ANSWER;
+	
+	//电子科大模板
+	protected static Template DZKD_TEMPLATE_PAPER;
+	protected static Template DZKD_TEMPLATE_ANSWER;
+	
+	//华中科技大学模板
+	protected static Template HZKJ_TEMPLATE_PAPER;
+	protected static Template HZKJ_TEMPLATE_ANSWER;
+	
+	//山东大学
+	protected static Template SDDX_TEMPLATE_PAPER;
+	protected static Template SDDX_TEMPLATE_ANSWER;
+	
+	//天津大学
+	protected static Template TJDX_TEMPLATE_PAPER;
+	protected static Template TJDX_TEMPLATE_ANSWER;
+	
+	//石油大学模板
+	protected static Template SYDX_TEMPLATE_PAPER;
+	protected static Template SYDX_TEMPLATE_ANSWER;
+	
+	//西安交大模板
+	protected static Template XAJD_TEMPLATE_PAPER;
+	protected static Template XAJD_TEMPLATE_ANSWER;
+
+	@Value("${upyun.bucketName}")
+	protected String bucketName;
+	
+	@Value("${upyun.userName}")
+	protected String userName;
+	
+	@Value("${upyun.password}")
+	protected String password;
+	
+	@Value("${upyun.uploadUrl}")
+	protected String uploadUrl;
+	
+	@Value("${upyun.downloadUrl}")
+	protected String downloadUrl;
+	
+	public static Configuration CONFIGURATION;
+	
+	static {
+		try {
+			CONFIGURATION = new Configuration(Configuration.VERSION_2_3_25);
+			// 设置编码
+			CONFIGURATION.setDefaultEncoding(ENCODING);
+			// 设置ftl模板路径
+			CONFIGURATION.setClassForTemplateLoading(DocxProcessUtil.class, "/");
+			
+			CONFIGURATION = new Configuration(Configuration.VERSION_2_3_25);
+			// 设置编码
+			CONFIGURATION.setDefaultEncoding(ENCODING);
+			// 设置ftl模板路径
+			CONFIGURATION.setClassForTemplateLoading(DocxProcessUtil.class, "/");
+			
+			SXSF_TEMPLATE_PAPER = CONFIGURATION.getTemplate("sxsf_paper_template.ftl", ENCODING);
+			SXSF_TEMPLATE_ANSWER = CONFIGURATION.getTemplate("sxsf_answer_template.ftl", ENCODING);
+			
+			DZKD_TEMPLATE_PAPER = CONFIGURATION.getTemplate("dzkd_paper_template.ftl", ENCODING);
+			DZKD_TEMPLATE_ANSWER = CONFIGURATION.getTemplate("dzkd_answer_template.ftl", ENCODING);
+			
+			HZKJ_TEMPLATE_PAPER = CONFIGURATION.getTemplate("hzkj_paper_template.ftl", ENCODING);
+			HZKJ_TEMPLATE_ANSWER = CONFIGURATION.getTemplate("hzkj_answer_template.ftl", ENCODING);
+			
+			SDDX_TEMPLATE_PAPER = CONFIGURATION.getTemplate("sddx_paper_template.ftl", ENCODING);
+			SDDX_TEMPLATE_ANSWER = CONFIGURATION.getTemplate("sddx_answer_template.ftl", ENCODING);
+			
+			TJDX_TEMPLATE_PAPER = CONFIGURATION.getTemplate("tjdx_paper_template.ftl", ENCODING);
+			TJDX_TEMPLATE_ANSWER = CONFIGURATION.getTemplate("tjdx_answer_template.ftl", ENCODING);
+			
+			SYDX_TEMPLATE_PAPER = CONFIGURATION.getTemplate("sydx_paper_template.ftl", ENCODING);
+			SYDX_TEMPLATE_ANSWER = CONFIGURATION.getTemplate("sydx_answer_template.ftl", ENCODING);
+			
+			XAJD_TEMPLATE_PAPER = CONFIGURATION.getTemplate("xajd_paper_template.ftl", ENCODING);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	/**
+	 * 下载试卷
+	 * @param id
+	 * @param response
+	 * @throws Exception
+	 */
+	public abstract void downloadPaper(String paperId,String zipFileName)throws Exception;
+	/**
+	 * 下载答案
+	 * @param paperId
+	 * @param response
+	 * @throws Exception
+	 */
+	public abstract void downloadPaperAnswer(String paperId,String zipFileName)throws Exception;
+	/**
+	 * 上传试卷相关文件
+	 * @param orgName
+	 * @param examName
+	 * @param paperId
+	 * @throws Exception
+	 */
+    public abstract void uploadFile(ExtractConfig extractConfig,String paperId,ExportStructure exportStructure,AccessUser accessUser)  throws Exception;
+
+    /**
+     * 创建机考文件,并打包上传至又拍云
+     * @param paperId
+     * @param currNum
+     * ObjectId("591ead1290994d57cd453464")
+     * @throws IOException 
+     */
+    protected void uploadComputerTestFile(ExtractConfig extractConfig,AccessUser accessUser) throws IOException{
+    	List<ComputerTestPaper>  computerTestPaperList = buildComputerTestJsonService.buildComputerTestPapers(extractConfig);
+    	String currentTimeStr = CommonUtils.getCurNum();
+    	for(int i = 0;i<computerTestPaperList.size();i++){
+    		ComputerTestPaper computerTestPaper = computerTestPaperList.get(i);
+    		String jsonDirectoryName = currentTimeStr+UNDERSCORE+ExamFileType.COMPUTERTEST_PACKAGE.name()+UNDERSCORE+i;
+    		String jsonDirectoryPath = TEMP_FILE_EXP + File.separator + jsonDirectoryName;
+    		//新建文件夹
+    		File dirFile = new File(jsonDirectoryPath);
+    		if(!dirFile.exists()){
+    			dirFile.mkdirs();
+    		}
+    		//下载试卷中的音频文件到jsonDirectoryPath文件夹中
+			downloadQuestionAudio(computerTestPaper,jsonDirectoryPath,extractConfig.getExamId()+"");
+    		//将computerTestPaper对象生成JSON文件存放在jsonDirectoryPath中
+    		makeComputerTestPaperToJsonFile(extractConfig,computerTestPaper,jsonDirectoryPath);
+			//将文件夹打包成zip压缩包放在docxExport下
+			FileDisposeUtil.fileToZip(jsonDirectoryPath,"docxExport",jsonDirectoryName);
+			//上传zip压缩包到又拍云
+			File zipFile = new File(TEMP_FILE_EXP+jsonDirectoryName+ZIP_SUFFIX);
+			String zipUpyunFilePath = uploadUrl+extractConfig.getOrgId()+"/"+jsonDirectoryName+ZIP_SUFFIX;
+			UpYun upyun = new UpYun(bucketName,userName,password);
+			upyun.writeFile(zipUpyunFilePath,zipFile,true);
+			//保存数据库记录
+			examFileService.saveExamFile(new ExamFile(computerTestPaper,extractConfig,zipUpyunFilePath,ExamFileType.COMPUTERTEST_PACKAGE,ZIP_SUFFIX),accessUser);
+			//删除本地生成的文件
+			zipFile.delete();
+			FileUtils.deleteQuietly(dirFile);
+    	}
+    }
+    
+    /**
+     * 将computerTestPaper对象生成JSON文件存放在jsonDirectoryPath中
+     * @param extractConfig
+     * @param computerTestPaper
+     * @param jsonDirectoryPath
+     */
+    private void makeComputerTestPaperToJsonFile(ExtractConfig extractConfig,ComputerTestPaper computerTestPaper,String jsonDirectoryPath){
+		//创建新的JSON文件
+		File file = new File(jsonDirectoryPath+File.separator+extractConfig.getCourseCode()+".json");
+		//将对象转成 json对象
+		Gson gson = new Gson();
+		String strJSON = gson.toJson(computerTestPaper);
+		//生成文件流写入JSON文件
+		FileOutputStream outputStream;
+		try {
+			outputStream = new FileOutputStream(file);
+			byte b[] = strJSON.getBytes();
+			outputStream.write(b);
+			outputStream.close();
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+    }
+    
+    /**
+     * 下载题目音频文件
+     * @param computerTestPaper
+     */
+    private void downloadQuestionAudio(ComputerTestPaper computerTestPaper,String jsonDirectoryPath,String examId){
+    	List<ComputerTestPaperDetail> details = computerTestPaper.getDetails();
+		for(ComputerTestPaperDetail detail:details){
+			List<ComputerTestQuestion> questions = detail.getQuestions();
+			for(ComputerTestQuestion computerTestQuestion:questions){
+				getBodyAndOptionAudioFile(computerTestQuestion,computerTestQuestion.getBody(),jsonDirectoryPath,computerTestPaper,examId);
+				List<ComputerTestOption> options = computerTestQuestion.getOptions();
+				for(ComputerTestOption option:options){
+					getBodyAndOptionAudioFile(computerTestQuestion,option.getBody(),jsonDirectoryPath,computerTestPaper,examId);
+				}
+				//子题音频处理
+				//List<ComputerTestQuestion> subQuestions = question.getSubQuestions();
+			}
+		}
+    }
+    
+    private void getBodyAndOptionAudioFile(ComputerTestQuestion computerTestQuestion,Sections bodySections,String jsonDirectoryPath,ComputerTestPaper computerTestPaper,String examId){
+    	List<Section> sectionList = bodySections.getSections();
+		for(Section section:sectionList){
+			List<Block> blocks = section.getBlocks();
+			for(Block block:blocks){
+				if("audio".equals(block.getType())){
+					computerTestPaper.setHasVideo(1);
+					PaperDetailUnit paperDetailUnit = paperDetailUnitRepo.findById(computerTestQuestion.getId());
+					QuestionAudio audio = questionAudioService.findAudioById(block.getValue());
+					block.setValue(block.getValue()+"."+audio.getFileSuffixes());
+					if(audio!=null){
+						AudioTimeConfig audioTime = new AudioTimeConfig();
+						audioTime.setCourseCode(computerTestPaper.getCourseCode());
+						audioTime.setExamId(examId);
+						audioTime.setGroupCode(computerTestPaper.getGroupCode());
+						audioTime.setPaperDetailUnit(paperDetailUnit);
+						AudioTimeConfig audioTimeConfig = audioTimeConfigRepo.findOne(Example.of(audioTime));
+						if(audioTimeConfig!=null){
+							block.setPlayTime(audioTimeConfig.getPlayTime());
+						}
+						FileDisposeUtil.saveUrlAs(downloadUrl+audio.getFileUrl(),jsonDirectoryPath+File.separator+block.getValue());
+					}
+				}
+			}
+		}
+    }
+    
+	/**
+     * 生成试卷或答案Word,上传至又拍云
+     * @param orgName
+     * @param dataMap
+     * @param extractConfig
+     * @param paperId
+     * @param accessUser
+     */
+    protected void uploadPaperOrAnswerFile(PaperExp paperExp,ExtractConfig extractConfig,AccessUser accessUser,String currNum,Template template,ExamFileType examFileType){
+    	String paperfileName = currNum+examFileType.name()+DOCX_SUFFIX;
+    	try {
+			DocxProcessUtil.exportWord(paperExp,paperfileName,template);
+			DocxProcessUtil.processImage(paperfileName,getPkgList(paperExp.getId()));
+			File file  = new File(TEMP_FILE_EXP+paperfileName);
+			String paperFilePath = uploadUrl+extractConfig.getOrgId()+"/"+paperfileName;
+            UpYun upyun = new UpYun(bucketName,userName,password);
+			upyun.writeFile(paperFilePath,file,true);
+			examFileService.saveExamFile(new ExamFile(paperExp.getId(),extractConfig,paperFilePath,examFileType,DOCX_SUFFIX),accessUser);
+			file.delete();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+    }
+    
+    /**
+     * 生成试卷结构,上传至又拍云
+     */
+    protected void uploadPaperStructure(PaperExp paperExp,ExtractConfig extractConfig,AccessUser accessUser,String currNum,List<QuestionTypeNum> questionTypeNums) {
+    	exportObjectiveQuestionStructures(paperExp,extractConfig,accessUser,currNum,questionTypeNums);
+    	exportSubjectiveQuestionStructures(paperExp,extractConfig,accessUser,currNum);
+	}
+    
+    /**
+     * 获得客观题导出结构,并上传至又拍云
+     * @param paperExp
+     * @return
+     */
+    protected void exportObjectiveQuestionStructures(PaperExp paperExp,ExtractConfig extractConfig,AccessUser accessUser,String currNum,List<QuestionTypeNum> questionTypeNums){
+    	String objectiveFilename = currNum+ExamFileType.PAPER_STRUCTURE_OBJECTIVE.name()+EXCEL_SUFFIX;
+    	//得到试卷中的客观大题
+    	List<PaperDetailExp> objectiveDetails = getAllObjectiveDetails(paperExp);
+    	//根据试卷结构导出设置中的数量补齐客观题
+    	List<PaperDetailExp> paperDetailExps = fillObjectiveQuestions(objectiveDetails,questionTypeNums);
+    	List<ObjectiveQuestionStructure> objectiveQuestionStructureList = new ArrayList<ObjectiveQuestionStructure>();
+    	for(PaperDetailExp paperDetailExp:paperDetailExps){
+    		for(PaperDetailUnitExp unit:paperDetailExp.getPaperDetailUnits()){
+    			if(unit.getQuestionType() != QuesStructType.NESTED_ANSWER_QUESTION){
+    				objectiveQuestionStructureList.add(new ObjectiveQuestionStructure(paperExp,paperDetailExp,unit));
+    			}else{
+    				List<Question> subQuestions = unit.getQuestion().getSubQuestions();
+    				for(Question question:subQuestions){
+    					objectiveQuestionStructureList.add(new ObjectiveQuestionStructure(paperExp,paperDetailExp,question));
+    				}
+    			}
+    		}
+    	}
+    	ExcelWriter objectiveExcelExporter = new ExcelWriter(ObjectiveQuestionStructure.class); 
+		try {
+			File file = new File(TEMP_FILE_EXP+objectiveFilename);
+			FileOutputStream out = new FileOutputStream(file);
+			objectiveExcelExporter.write(objectiveFilename,objectiveQuestionStructureList,out);
+			String objectiveFilePath = uploadUrl+extractConfig.getOrgId()+"/"+objectiveFilename;
+            UpYun upyun = new UpYun(bucketName,userName,password);
+			upyun.writeFile(objectiveFilePath,file,true);
+			examFileService.saveExamFile(new ExamFile(paperExp.getId(),extractConfig,objectiveFilePath,ExamFileType.PAPER_STRUCTURE_OBJECTIVE,EXCEL_SUFFIX),accessUser);
+			file.delete();
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+    }
+    
+
+   	/**
+     * 获得主观题导出结构,并上传至又拍云
+     * @param paperExp
+     * @return
+     */
+    protected void exportSubjectiveQuestionStructures(PaperExp paperExp,ExtractConfig extractConfig,AccessUser accessUser,String currNum){
+    	String subjectiveFileName = currNum+ExamFileType.PAPER_STRUCTURE_SUBJECTIVE.name()+EXCEL_SUFFIX;
+    	List<PaperDetailExp> subjectiveDetails = getAllSubjectiveDetails(paperExp);
+    	List<SubjectiveQuestionStructure> subjectiveQuestionStructureList = new ArrayList<SubjectiveQuestionStructure>();
+    	for(PaperDetailExp paperDetailExp:subjectiveDetails){
+    		for(PaperDetailUnitExp unit:paperDetailExp.getPaperDetailUnits()){
+    			subjectiveQuestionStructureList.add(new SubjectiveQuestionStructure(paperExp,paperDetailExp,unit));
+    		}
+    	}
+    	ExcelWriter subjectiveExcelExporter = new ExcelWriter(SubjectiveQuestionStructure.class); 
+		try {
+			File file = new File(TEMP_FILE_EXP+subjectiveFileName);
+			FileOutputStream out = new FileOutputStream(file);
+			subjectiveExcelExporter.write(subjectiveFileName,subjectiveQuestionStructureList,out);
+			String subjectiveFilePath = uploadUrl+extractConfig.getOrgId()+"/"+subjectiveFileName;
+            UpYun upyun = new UpYun(bucketName,userName,password);
+			upyun.writeFile(subjectiveFilePath,file,true);
+			examFileService.saveExamFile(new ExamFile(paperExp.getId(),extractConfig,subjectiveFilePath,ExamFileType.PAPER_STRUCTURE_SUBJECTIVE,EXCEL_SUFFIX),accessUser);
+			file.delete();
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+    }
+    
+    /**
+     * 根据试卷结构导出设置中的数量补齐客观题
+     * @param objectiveDetails
+     * @param questionTypeNums
+     */
+    public List<PaperDetailExp> fillObjectiveQuestions(List<PaperDetailExp> objectiveDetails,List<QuestionTypeNum> questionTypeNums) {
+    	//1.得到所有客观题的类型
+    	Set<QuesStructType> types = new HashSet<QuesStructType>();
+    	for(PaperDetailExp paperDetailExp:objectiveDetails){
+    		List<PaperDetailUnitExp> paperDetailUnits = paperDetailExp.getPaperDetailUnits();
+    		types.add(paperDetailUnits.get(0).getQuestionType());
+    	}
+    	List<PaperDetailExp> paperDetailExps = new ArrayList<PaperDetailExp>();
+    	//如果没有单选
+    	if(!types.contains(QuesStructType.SINGLE_ANSWER_QUESTION)){
+    		buildObjectiveQuestion(paperDetailExps,questionTypeNums,QuesStructType.SINGLE_ANSWER_QUESTION);
+    	}else{
+    		addObjectiveQuestion(paperDetailExps,objectiveDetails, questionTypeNums, QuesStructType.SINGLE_ANSWER_QUESTION);
+    	}
+    	//如果没有多选
+    	if(!types.contains(QuesStructType.MULTIPLE_ANSWER_QUESTION)){
+    		buildObjectiveQuestion(paperDetailExps,questionTypeNums,QuesStructType.MULTIPLE_ANSWER_QUESTION);
+    	}else{
+    		addObjectiveQuestion(paperDetailExps, objectiveDetails, questionTypeNums, QuesStructType.MULTIPLE_ANSWER_QUESTION);
+    	}
+    	//如果没有判断
+    	if(!types.contains(QuesStructType.BOOL_ANSWER_QUESTION)){
+    		buildObjectiveQuestion(paperDetailExps,questionTypeNums,QuesStructType.BOOL_ANSWER_QUESTION);
+    	}else{
+    		addObjectiveQuestion(paperDetailExps, objectiveDetails, questionTypeNums, QuesStructType.BOOL_ANSWER_QUESTION);
+    	}
+    	return paperDetailExps;
+	}
+    
+    /**
+     * 根据数量和类型构建客观题
+     * @param questionTypeNums
+     * @param quesStructType
+     * @return
+     */
+    private void buildObjectiveQuestion(List<PaperDetailExp> paperDetailExps,List<QuestionTypeNum> questionTypeNums,QuesStructType quesStructType){
+    	//1.得到需要的题量
+    	int quantity = getQuantity(questionTypeNums,quesStructType);
+		//2.构建题目
+		PaperDetailExp paperDetailExp = new PaperDetailExp();
+    	List<PaperDetailUnitExp> paperDetailUnits  = new ArrayList<PaperDetailUnitExp>();
+		for(int i = 0;i<quantity;i++){
+			paperDetailUnits.add(new PaperDetailUnitExp(quesStructType,i+1));
+		}
+		paperDetailExp.setPaperDetailUnits(paperDetailUnits);
+		paperDetailExp.setNumber(Integer.parseInt(quesStructType.getId()+""));
+		paperDetailExps.add(paperDetailExp);
+    }
+    
+    /**
+     * 补齐需要的客观题
+     * @param objectiveDetails
+     * @param questionTypeNums
+     */
+    private void addObjectiveQuestion(List<PaperDetailExp> paperDetailExps,List<PaperDetailExp> objectiveDetails,List<QuestionTypeNum> questionTypeNums,QuesStructType quesStructType){
+    	//得到当前题量
+    	int currentSize = 0;
+		for(PaperDetailExp paperDetailExp:objectiveDetails){
+    		List<PaperDetailUnitExp> paperDetailUnits = paperDetailExp.getPaperDetailUnits();
+    		if(paperDetailUnits.get(0).getQuestionType() == quesStructType){
+    			paperDetailExps.add(paperDetailExp);
+    			for(PaperDetailUnitExp paperDetailUnitExp:paperDetailUnits){
+    				if(paperDetailUnitExp.getQuestionType() != QuesStructType.NESTED_ANSWER_QUESTION){
+    					currentSize += 1;
+    				}else{
+    					currentSize += paperDetailUnitExp.getQuestion().getSubQuestions().size();
+    				}
+    			}
+    		}
+    	}
+		//计算差额、补齐试题
+		int quantity = getQuantity(questionTypeNums,quesStructType)-currentSize;
+		PaperDetailExp paperDetailExp = new PaperDetailExp();
+		List<PaperDetailUnitExp> paperDetailUnits = new ArrayList<PaperDetailUnitExp>();
+		for(int i = 0;i<quantity;i++){
+			int number = currentSize+(i+1);
+			paperDetailUnits.add(new PaperDetailUnitExp(quesStructType,number));
+		}
+		paperDetailExp.setPaperDetailUnits(paperDetailUnits);
+		//设置大题题号
+		paperDetailExp.setNumber(Integer.parseInt(quesStructType.getId()+""));
+		paperDetailExps.add(paperDetailExp);
+    }
+    
+    //根据类型得到需要的题量
+    private Integer getQuantity(List<QuestionTypeNum> questionTypeNums,QuesStructType quesStructType){
+    	int quantity = 0;
+		for(QuestionTypeNum typeNum:questionTypeNums){
+			if(typeNum.getQuestionType() == quesStructType){
+				quantity = typeNum.getQuantity();
+			}
+		}
+		return quantity;
+    }
+    /**
+     * 得到试卷中所有客观大题
+     * 与试题类型枚举:QuesStructType中的ID对应
+     * @param paperExp
+     * @return
+     */
+    protected List<PaperDetailExp> getAllObjectiveDetails(PaperExp paperExp){
+    	List<PaperDetailExp> objectiveDetails = new ArrayList<PaperDetailExp>();
+    	for(PaperDetailExp paperDetailExp:paperExp.getPaperDetails()){
+    		if(paperDetailExp.getSortNumber()==1L
+    				||paperDetailExp.getSortNumber()==2L
+    					||paperDetailExp.getSortNumber()==3L){
+    			objectiveDetails.add(paperDetailExp);
+    		}
+    	}
+    	return objectiveDetails;
+    }
+    /**
+     * 得到所有主观大题
+     * 与试题类型枚举:QuesStructType中的ID对应
+     * @param paperExp
+     * @return
+     */
+    protected List<PaperDetailExp> getAllSubjectiveDetails(PaperExp paperExp){
+    	List<PaperDetailExp> subjectiveDetails = new ArrayList<PaperDetailExp>();
+    	for(PaperDetailExp paperDetailExp:paperExp.getPaperDetails()){
+    		if(paperDetailExp.getSortNumber()==4L
+    				||paperDetailExp.getSortNumber()==5L
+    					||paperDetailExp.getSortNumber()==6L){
+    			subjectiveDetails.add(paperDetailExp);
+    		}
+    	}
+    	return subjectiveDetails;
+    }
+	/**
+     * 检查客观题数量是否小于试卷结构导出设置的数量
+     * @param paperExp
+     * @param objectiveDetails
+     * @param questionTypeNums
+     * @return
+     */
+    protected void checkObjectiveDetailsNum(PaperExp paperExp,List<QuestionTypeNum> questionTypeNums) {
+       List<PaperDetailExp> objectiveDetails = getAllObjectiveDetails(paperExp);
+	   for(QuestionTypeNum typeNum:questionTypeNums){
+		   int currentSize = 0;
+		   for(PaperDetailExp paperDetailExp:objectiveDetails){
+		   		List<PaperDetailUnitExp> paperDetailUnits = paperDetailExp.getPaperDetailUnits();
+		   		if(paperDetailUnits.get(0).getQuestionType() == typeNum.getQuestionType());{
+		   			currentSize += paperDetailUnits.size();
+		   			break;
+		   		}
+		   }
+		   if(currentSize>typeNum.getQuantity()){
+			   throw new RuntimeException("课程最大题量超出设定范围,请核实后重新设置导出规则!");
+		   }
+	   }
+    }
+    /**
+     * 获取当前试卷下所有试题WordPkg
+     * @param id
+     * @return
+     */
+    protected List<WordprocessingMLPackage> getPkgList(String id){
+        Paper paper = paperRepo.findOne(id);
+        List<WordprocessingMLPackage> wordMLPackages = paperDetailUnitRepo.findByPaperOrderByNumber(paper)
+                .stream().map(PaperDetailUnit::getQuestion).collect(Collectors.toList())
+                .stream().map(Question::getPkgObj).collect(Collectors.toList());
+        return wordMLPackages;
+    }
+    
+}
+