deason 4 лет назад
Родитель
Сommit
a99c33721f
55 измененных файлов с 2558 добавлено и 303 удалено
  1. 0 1
      .gitignore
  2. 1 1
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/ExportPaperController.java
  3. 27 1
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/GenPaperController.java
  4. 79 19
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/PaperController.java
  5. 5 1
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/enums/ExamFileType.java
  6. 1 1
      examcloud-core-questions-base/src/main/resources/export_template/origin_paper/doc_section.ftl
  7. 1 1
      examcloud-core-questions-base/src/main/resources/export_template/origin_paper/exam_reamark_document.ftl
  8. 1 1
      examcloud-core-questions-base/src/main/resources/export_template/origin_paper/question_options.ftl
  9. 1 1
      examcloud-core-questions-base/src/main/resources/export_template/origin_paper/question_section.ftl
  10. 8 3
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/AudioTimeConfig.java
  11. 10 6
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/ExamFile.java
  12. 8 11
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/ExportServiceManage.java
  13. 9 5
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/ExportStructure.java
  14. 2 15
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/ExportTemplateEntity.java
  15. 9 5
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/ExtractConfig.java
  16. 12 6
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/Paper.java
  17. 9 3
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/PaperDetail.java
  18. 12 6
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/PaperDetailUnit.java
  19. 10 0
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/PaperSearchInfo.java
  20. 10 5
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/QuesTypeName.java
  21. 8 3
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/Question.java
  22. 9 3
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/QuestionAudio.java
  23. 8 3
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/QuestionBak.java
  24. 7 3
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/QuestionPkgPath.java
  25. 105 0
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/base/MongoBaseEntity.java
  26. 113 0
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/base/MysqlBaseEntity.java
  27. 15 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ExportThemisPaperService.java
  28. 17 7
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/PaperService.java
  29. 81 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/paper/PaperAnswerDomain.java
  30. 101 69
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExportPaperServiceImpl.java
  31. 566 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExportThemisPaperServiceImpl.java
  32. 64 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/GenPaperService.java
  33. 41 3
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PaperDetailUnitServiceImpl.java
  34. 336 2
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PaperServiceImpl.java
  35. 23 15
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/QuesServiceImpl.java
  36. 32 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisAnswer.java
  37. 69 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisAnswerContent.java
  38. 47 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisAnswerDetail.java
  39. 49 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisBlock.java
  40. 32 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisOption.java
  41. 141 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisPaper.java
  42. 30 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisPaperAndAnswer.java
  43. 85 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisPaperDetail.java
  44. 154 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisQuestion.java
  45. 25 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisSection.java
  46. 25 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisSections.java
  47. 35 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisSubjectiveAnswer.java
  48. 23 9
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/util/ExportPaperUtil.java
  49. 2 0
      examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/CoreQuestionApp.java
  50. 23 0
      examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/config/UserIDAuditorBean.java
  51. 1 1
      examcloud-core-questions-starter/src/main/resources/application.properties
  52. 76 56
      examcloud-core-questions-starter/src/main/resources/log4j2.xml
  53. BIN
      examcloud-core-questions-starter/src/main/resources/templates/answerImportTemplate.xlsx
  54. 0 19
      jenkins-dev.sh
  55. 0 18
      jenkins-test.sh

+ 0 - 1
.gitignore

@@ -17,4 +17,3 @@ target/
 *.jar
 *.war
 *.ear
-/cqb-paper/src/main/java/com/qmth/cqb/paper/service/.gitignore

+ 1 - 1
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/ExportPaperController.java

@@ -95,7 +95,7 @@ public class ExportPaperController extends ControllerSupport {
             exportPaperService.exportPaperFiles(paperList, esm.getExportServiceName(), exportContentList, response,
                     loginName, examType, user.getRootOrgId());
         } catch (Exception e) {
-            log.error("导出异常:" + e.getMessage());
+            log.error("导出异常",e);
         }
         log.info("导出结束");
     }

+ 27 - 1
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/GenPaperController.java

@@ -187,5 +187,31 @@ public class GenPaperController extends ControllerSupport {
             return new ResponseEntity<>(paperMap, HttpStatus.INTERNAL_SERVER_ERROR);
         }
     }
-
+    
+    @ApiOperation(value = "手动组卷", notes = "手动组卷")
+    @PostMapping("/genPaper/manual")
+    public ResponseEntity<Object> genPaperManual(@RequestBody GenPaperDto genPaperDto) {
+        User user = getAccessUser();
+        genPaperDto.setOrgId(user.getRootOrgId().toString());
+        genPaperDto.setCreator(user.getDisplayName());
+        Map<String, Object> paperMap = new HashMap<>();
+        //判断试卷名称是否一样
+        try {
+            //判断试卷名称是否一样
+            boolean result = false;
+            result = paperService.checkPaperName(genPaperDto.getPaperName(), PaperType.GENERATE, user.getRootOrgId() + "");
+            if (!result) {
+                paperMap.put("msg", "考试试卷:" + genPaperDto.getPaperName() + "已经存在");
+                return new ResponseEntity<>(paperMap, HttpStatus.INTERNAL_SERVER_ERROR);
+            }
+        } catch (Exception e) {
+            return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+        paperMap = genPaperService.genPaperManual(genPaperDto);
+        if (paperMap.get("msg").equals("success")) {
+            return new ResponseEntity<>(paperMap, HttpStatus.OK);
+        } else {
+            return new ResponseEntity<>(paperMap, HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
 }

+ 79 - 19
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/PaperController.java

@@ -1,12 +1,55 @@
 package cn.com.qmth.examcloud.core.questions.api.controller;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotNull;
+
+import org.apache.commons.lang3.StringUtils;
+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.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.google.gson.Gson;
+
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.util.PathUtil;
 import cn.com.qmth.examcloud.core.questions.base.BeanCopierUtil;
 import cn.com.qmth.examcloud.core.questions.base.SpringContextUtils;
 import cn.com.qmth.examcloud.core.questions.base.StringSimilarityUtils;
 import cn.com.qmth.examcloud.core.questions.base.enums.ExportTemplateType;
 import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
+import cn.com.qmth.examcloud.core.questions.base.excel.ExportUtils;
 import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
 import cn.com.qmth.examcloud.core.questions.dao.ExportServiceManageRepo;
 import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
@@ -19,6 +62,7 @@ import cn.com.qmth.examcloud.core.questions.service.QuesService;
 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.paper.PaperAnswerDomain;
 import cn.com.qmth.examcloud.core.questions.service.bean.paper.PreviewPaperHandler;
 import cn.com.qmth.examcloud.core.questions.service.config.SysProperty;
 import cn.com.qmth.examcloud.core.questions.service.export.ExportPaperAbstractService;
@@ -26,26 +70,7 @@ import cn.com.qmth.examcloud.core.questions.service.util.ExportTemplateUtil;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import cn.com.qmth.examcloud.web.support.Naked;
 import freemarker.template.TemplateException;
-
-import com.google.gson.Gson;
 import io.swagger.annotations.ApiOperation;
-import org.apache.commons.lang3.StringUtils;
-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.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 /**
  * Created by songyue on 16/12/28.
@@ -724,4 +749,39 @@ public class PaperController extends ControllerSupport {
         double total = paperService.getQuestionTypeScore(paperId, publicityType, difficultyType);
         return new ResponseEntity<>(total, HttpStatus.OK);
     }
+    
+    @ApiOperation(value = "试卷答案导出")
+    @RequestMapping(value = "/paper/answer/export/{paperId}", method = RequestMethod.GET)
+    public void answerExport(@PathVariable String paperId,HttpServletResponse response){
+        User user = getAccessUser();
+        Paper paper = cn.com.qmth.examcloud.core.questions.base.Model.of(paperRepo.findById(paperId));
+        if(paper==null) {
+        	throw new StatusException("500", "未找到试卷");
+        }
+        if(!isSuperAdmin()&&!paper.getOrgId().equals(String.valueOf(user.getRootOrgId()))) {
+        	throw new StatusException("500", "非法请求");
+        }
+        List<PaperAnswerDomain> dtos = paperService.answerExport(paper);
+        ExportUtils.exportEXCEL("试卷答案", PaperAnswerDomain.class, dtos, response);
+    }
+    @ApiOperation(value = "试卷答案导入")
+    @RequestMapping(value = "/paper/answer/import/{paperId}", method = RequestMethod.POST)
+    @ResponseBody
+    public void answerImport(@PathVariable String paperId,@RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile){
+        User user = getAccessUser();
+        Paper paper = cn.com.qmth.examcloud.core.questions.base.Model.of(paperRepo.findById(paperId));
+        if(paper==null) {
+        	throw new StatusException("500", "未找到试卷");
+        }
+        if(!isSuperAdmin()&&!paper.getOrgId().equals(String.valueOf(user.getRootOrgId()))) {
+        	throw new StatusException("500", "非法请求");
+        }
+        paperService.answerImport(paper,dataFile);
+    }
+    @ApiOperation(value = "下载答案模板", notes = "下载答案模板")
+    @GetMapping("/paper/answer/template")
+    public void getImportTemplate(HttpServletResponse response) {
+        String resoucePath = PathUtil.getResoucePath("templates/answerImportTemplate.xlsx");
+        exportFile("答案导入模板.xlsx", new File(resoucePath));
+    }
 }

+ 5 - 1
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/enums/ExamFileType.java

@@ -38,7 +38,11 @@ public enum ExamFileType {
     /**
      * 分布式印刷数据包
      */
-    PRINT_EXAM_PACKAGE("分布式印刷数据包");
+    PRINT_EXAM_PACKAGE("分布式印刷数据包"),
+    /**
+     *在线考试数据包 
+     */
+    THEMIS_PACKAGE("在线考试数据包 ");
 
     private String name;
 

+ 1 - 1
examcloud-core-questions-base/src/main/resources/export_template/origin_paper/doc_section.ftl

@@ -60,7 +60,7 @@
 		<w:rPr>
 			<w:rFonts w:hint="eastAsia" />
 		</w:rPr>
-		<w:t>${element.value}</w:t>
+		<w:t xml:space="preserve">${element.value}</w:t>
 	</w:r>
 	</#if>
 	</#list>

+ 1 - 1
examcloud-core-questions-base/src/main/resources/export_template/origin_paper/exam_reamark_document.ftl

@@ -96,7 +96,7 @@
 		<w:rPr>
 			<w:rFonts w:hint="eastAsia" />
 		</w:rPr>
-		<w:t>${element.value}</w:t>
+		<w:t xml:space="preserve">${element.value}</w:t>
 	</w:r>
 	</#if>
 	</#list>

+ 1 - 1
examcloud-core-questions-base/src/main/resources/export_template/origin_paper/question_options.ftl

@@ -90,7 +90,7 @@
 		<w:rPr>
 			<w:rFonts w:hint="eastAsia" />
 		</w:rPr>
-		<w:t>${element.value}</w:t>
+		<w:t xml:space="preserve">${element.value}</w:t>
 	</w:r>
 	</#if>
 	</#list>

+ 1 - 1
examcloud-core-questions-base/src/main/resources/export_template/origin_paper/question_section.ftl

@@ -69,7 +69,7 @@
 		<w:rPr>
 			<w:rFonts w:hint="eastAsia" />
 		</w:rPr>
-		<w:t>${element.value}</w:t>
+		<w:t xml:space="preserve">${element.value}</w:t>
 	</w:r>
 	</#if>
 	</#list>

+ 8 - 3
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/AudioTimeConfig.java

@@ -1,9 +1,10 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
+import java.util.Date;
+
 import org.springframework.data.mongodb.core.mapping.DBRef;
 
-import java.util.Date;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
 
 
 /**
@@ -12,8 +13,12 @@ import java.util.Date;
  * @company QMTH
  * @description 音频播放次数设置
  */
-public class AudioTimeConfig extends IdBase {
+public class AudioTimeConfig extends MongoBaseEntity {
     /**
+	 * 
+	 */
+	private static final long serialVersionUID = 7379774910493107448L;
+	/**
      * 考试ID
      */
     private String examId;

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

@@ -1,25 +1,29 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
-import cn.com.qmth.examcloud.core.questions.base.em.enums.ExamType;
-import cn.com.qmth.examcloud.core.questions.base.enums.ExamFileType;
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
-import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.ComputerTestPaper;
-
 import java.util.Date;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 
+import cn.com.qmth.examcloud.core.questions.base.em.enums.ExamType;
+import cn.com.qmth.examcloud.core.questions.base.enums.ExamFileType;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
+import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.ComputerTestPaper;
+
 /**
  * @author chenken
  * @date 2017年7月13日 下午4:22:24
  * @company QMTH
  * @description 考试文件
  */
-public class ExamFile extends IdBase {
+public class ExamFile extends MongoBaseEntity {
 
     /**
+	 * 
+	 */
+	private static final long serialVersionUID = -6244613661379455829L;
+	/**
      * 机构ID
      */
     private String orgId;

+ 8 - 11
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/ExportServiceManage.java

@@ -1,23 +1,20 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
 
-public class ExportServiceManage extends IdBase {
+public class ExportServiceManage extends MongoBaseEntity {
 
-    private String orgId;
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 3666813945343263621L;
+
+	private String orgId;
 
     private String orgName;
 
     private String exportServiceName;
 
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
     public String getOrgName() {
         return orgName;
     }

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

@@ -1,20 +1,24 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
-import cn.com.qmth.examcloud.core.questions.base.em.enums.ExamType;
-import cn.com.qmth.examcloud.core.questions.base.enums.ExportType;
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
-
 import java.util.Date;
 import java.util.List;
 
+import cn.com.qmth.examcloud.core.questions.base.em.enums.ExamType;
+import cn.com.qmth.examcloud.core.questions.base.enums.ExportType;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
+
 /**
  * @author chenken
  * @date 2017年7月12日 下午3:45:08
  * @company QMTH
  * @description 导出结构设置
  */
-public class ExportStructure extends IdBase {
+public class ExportStructure extends MongoBaseEntity {
     /**
+	 * 
+	 */
+	private static final long serialVersionUID = 6035380391499981884L;
+	/**
      * 考试类型
      */
     private ExamType examType;

+ 2 - 15
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/ExportTemplateEntity.java

@@ -1,18 +1,16 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
-import java.util.Date;
-
 import javax.persistence.Entity;
 import javax.persistence.Index;
 import javax.persistence.Table;
 import javax.validation.constraints.NotNull;
 
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdEntity;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MysqlBaseEntity;
 
 @Entity
 @Table(name = "ec_q_export_template", indexes = {
         @Index(name = "IDX_Q_EXPORT_TEMPLATE_01", columnList = "rootOrgId", unique = false) })
-public class ExportTemplateEntity extends IdEntity {
+public class ExportTemplateEntity extends MysqlBaseEntity {
 
     /**
      * 
@@ -40,9 +38,6 @@ public class ExportTemplateEntity extends IdEntity {
     @NotNull
     private Boolean enable;
 
-    @NotNull
-    private Date creationTime;
-
     @NotNull
     private String createUser;
 
@@ -94,14 +89,6 @@ public class ExportTemplateEntity extends IdEntity {
         this.enable = enable;
     }
 
-    public Date getCreationTime() {
-        return creationTime;
-    }
-
-    public void setCreationTime(Date creationTime) {
-        this.creationTime = creationTime;
-    }
-
     public String getCreateUser() {
         return createUser;
     }

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

@@ -1,19 +1,23 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
-import cn.com.qmth.examcloud.core.questions.base.core.ExamCourseDto;
-import cn.com.qmth.examcloud.core.questions.base.enums.ExtractPolicy;
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
-
 import java.util.List;
 import java.util.Map;
 
+import cn.com.qmth.examcloud.core.questions.base.core.ExamCourseDto;
+import cn.com.qmth.examcloud.core.questions.base.enums.ExtractPolicy;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
+
 /**
  * 调卷规则
  *
  * @author chenken
  */
-public class ExtractConfig extends IdBase {
+public class ExtractConfig extends MongoBaseEntity {
     /**
+	 * 
+	 */
+	private static final long serialVersionUID = -2723715817328472562L;
+	/**
      * 考试ID
      */
     private Long examId;

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

@@ -1,19 +1,25 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
+import java.util.Map;
+
+import org.springframework.data.mongodb.core.index.Indexed;
+
 import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
 import cn.com.qmth.examcloud.core.questions.base.core.enums.CourseLevel;
 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.dao.entity.base.IdBase;
-import org.springframework.data.mongodb.core.index.Indexed;
-
-import java.util.Map;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
 
 /**
  * @author songyue
  */
-public class Paper extends IdBase {
-    @Indexed(unique = true)
+public class Paper extends MongoBaseEntity {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -8681575737341326402L;
+
+	@Indexed(unique = true)
     private String name;// 试卷名称
 
     private String title;// 试卷标题

+ 9 - 3
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/PaperDetail.java

@@ -1,10 +1,16 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
 import org.springframework.data.mongodb.core.mapping.DBRef;
 
-public class PaperDetail extends IdBase implements Comparable<PaperDetail> {
-    @DBRef(lazy = false)
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
+
+public class PaperDetail extends MongoBaseEntity implements Comparable<PaperDetail> {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -8021835042074266092L;
+
+	@DBRef(lazy = false)
     private Paper paper;// 关联的试卷
 
     private Integer number;// 大题序号

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

@@ -1,17 +1,23 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.data.mongodb.core.mapping.DBRef;
+
 import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
 import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
 import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
-import org.springframework.data.mongodb.core.mapping.DBRef;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
 
-import java.util.ArrayList;
-import java.util.List;
+public class PaperDetailUnit extends MongoBaseEntity implements Comparable<PaperDetailUnit> {
 
-public class PaperDetailUnit extends IdBase implements Comparable<PaperDetailUnit> {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 266448283780973578L;
 
-    @DBRef(lazy = false)
+	@DBRef(lazy = false)
     private Paper paper;// 关联的试卷
 
     private Integer number;// 小题序号

+ 10 - 0
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/PaperSearchInfo.java

@@ -13,6 +13,8 @@ public class PaperSearchInfo implements Serializable {
     private String createTime;// 创建时间
 
     private String creator;// 录入员
+    
+    private String lastModifyName;//修改人
 
     private String courseNo;// 课程代码
 
@@ -98,4 +100,12 @@ public class PaperSearchInfo implements Serializable {
         this.level = level;
     }
 
+	public String getLastModifyName() {
+		return lastModifyName;
+	}
+
+	public void setLastModifyName(String lastModifyName) {
+		this.lastModifyName = lastModifyName;
+	}
+
 }

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

@@ -1,16 +1,21 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
-import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
-
 import java.util.List;
 
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
+
 /**
  * Created by songyue on 17/8/22.
  */
-public class QuesTypeName extends IdBase {
+public class QuesTypeName extends MongoBaseEntity {
+
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 1435121079364833256L;
 
-    private String orgId;
+	private String orgId;
 
     private String courseNo;
 

+ 8 - 3
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/Question.java

@@ -7,15 +7,20 @@ import java.util.Map;
 import org.springframework.data.annotation.Transient;
 
 import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
 import cn.com.qmth.examcloud.question.commons.core.question.AnswerType;
 
 /**
  * Created by songyue on 16/12/27.
  */
-public class Question extends IdBase {
+public class Question extends MongoBaseEntity {
 
-    private String quesBody;// 题干,默认为html
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 448493904015571457L;
+
+	private String quesBody;// 题干,默认为html
 
     private String quesBodyWord;// 题干word
 

+ 9 - 3
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/QuestionAudio.java

@@ -1,9 +1,10 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
+import java.util.Date;
+
 import org.apache.commons.lang3.StringUtils;
 
-import java.util.Date;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
 
 /**
  * @author chenken
@@ -11,8 +12,13 @@ import java.util.Date;
  * @company QMTH
  * @description QuestionAudio.java
  */
-public class QuestionAudio extends IdBase {
+public class QuestionAudio extends MongoBaseEntity {
     /**
+	 * 
+	 */
+	private static final long serialVersionUID = 6995367410867753147L;
+
+	/**
      * 试题ID
      */
     private String questionId;

+ 8 - 3
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/QuestionBak.java

@@ -7,11 +7,16 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
 
 import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
 import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
 
-public class QuestionBak extends IdBase {
+public class QuestionBak extends MongoBaseEntity {
 
-    private String quesBody;// 题干,默认为html
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -2952550503209263454L;
+
+	private String quesBody;// 题干,默认为html
 
     private String quesBodyWord;// 题干word
 

+ 7 - 3
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/QuestionPkgPath.java

@@ -1,6 +1,6 @@
 package cn.com.qmth.examcloud.core.questions.dao.entity;
 
-import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
+import cn.com.qmth.examcloud.core.questions.dao.entity.base.MongoBaseEntity;
 
 /**
  * Created by songyue on 17/11/19.
@@ -8,9 +8,13 @@ import cn.com.qmth.examcloud.core.questions.dao.entity.base.IdBase;
  */
 //新方案导出不存word字节信息
 @Deprecated
-public class QuestionPkgPath extends IdBase {
+public class QuestionPkgPath extends MongoBaseEntity {
 
-    private byte[] quesPkg;
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 6939709727710460908L;
+	private byte[] quesPkg;
 
     public byte[] getQuesPkg() {
         return quesPkg;

+ 105 - 0
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/base/MongoBaseEntity.java

@@ -0,0 +1,105 @@
+package cn.com.qmth.examcloud.core.questions.dao.entity.base;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.EntityListeners;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedBy;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+@MappedSuperclass
+@EntityListeners(AuditingEntityListener.class)
+public abstract class MongoBaseEntity implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3471392332225060352L;
+	
+    @Id
+    protected String id;
+
+	/**
+	 * 更新时间
+	 */
+	@LastModifiedDate
+	@Column(nullable = true)
+	@Temporal(TemporalType.TIMESTAMP)
+	private Date updateDate;
+
+	/**
+	 * 创建时间
+	 */
+	@CreatedDate
+	@Column(nullable = true, updatable = false)
+	@Temporal(TemporalType.TIMESTAMP)
+	private Date creationDate;
+	
+	/**
+	 * 创建人id
+	 */
+	@CreatedBy
+	@Column(nullable = true, updatable = false)
+	private Long creationBy;
+	
+	
+	/**
+	 * 更新人id
+	 */
+	@LastModifiedBy
+	@Column(nullable = true)
+	private Long updateBy;
+
+	public Date getUpdateDate() {
+		return updateDate;
+	}
+
+	public void setUpdateDate(Date updateDate) {
+		this.updateDate = updateDate;
+	}
+
+	public Date getCreationDate() {
+		return creationDate;
+	}
+
+	public void setCreationDate(Date creationDate) {
+		this.creationDate = creationDate;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public Long getCreationBy() {
+		return creationBy;
+	}
+
+	public void setCreationBy(Long creationBy) {
+		this.creationBy = creationBy;
+	}
+
+	public Long getUpdateBy() {
+		return updateBy;
+	}
+
+	public void setUpdateBy(Long updateBy) {
+		this.updateBy = updateBy;
+	}
+
+
+	
+}

+ 113 - 0
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/base/MysqlBaseEntity.java

@@ -0,0 +1,113 @@
+package cn.com.qmth.examcloud.core.questions.dao.entity.base;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.EntityListeners;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedBy;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+@MappedSuperclass
+@EntityListeners(AuditingEntityListener.class)
+public abstract class MysqlBaseEntity implements JsonSerializable {
+
+	
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 1774213746500092636L;
+
+	@Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private long id;
+
+	/**
+	 * 更新时间
+	 */
+	@LastModifiedDate
+	@Column(nullable = true)
+	@Temporal(TemporalType.TIMESTAMP)
+	private Date updateTime;
+
+	/**
+	 * 创建时间
+	 */
+	@CreatedDate
+	@Column(nullable = true, updatable = false)
+	@Temporal(TemporalType.TIMESTAMP)
+	private Date creationTime;
+	
+	/**
+	 * 创建人id
+	 */
+	@CreatedBy
+	@Column(nullable = true, updatable = false)
+	private Long creationBy;
+	
+	
+	/**
+	 * 更新人id
+	 */
+	@LastModifiedBy
+	@Column(nullable = true)
+	private Long updateBy;
+
+	
+
+
+
+	public Date getUpdateTime() {
+		return updateTime;
+	}
+
+	public void setUpdateTime(Date updateTime) {
+		this.updateTime = updateTime;
+	}
+
+	public Date getCreationTime() {
+		return creationTime;
+	}
+
+	public void setCreationTime(Date creationTime) {
+		this.creationTime = creationTime;
+	}
+
+	public long getId() {
+		return id;
+	}
+
+	public void setId(long id) {
+		this.id = id;
+	}
+
+	public Long getCreationBy() {
+		return creationBy;
+	}
+
+	public void setCreationBy(Long creationBy) {
+		this.creationBy = creationBy;
+	}
+
+	public Long getUpdateBy() {
+		return updateBy;
+	}
+
+	public void setUpdateBy(Long updateBy) {
+		this.updateBy = updateBy;
+	}
+
+
+	
+}

+ 15 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ExportThemisPaperService.java

@@ -0,0 +1,15 @@
+package cn.com.qmth.examcloud.core.questions.service;
+
+import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaper;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaperAndAnswer;
+
+public interface ExportThemisPaperService {
+
+	ThemisPaperAndAnswer buildPaperAndAnswer(Paper paper);
+
+	void makePaperAnswerToJsonFile(ThemisPaperAndAnswer pa, String jsonDirectoryPath);
+
+	void downloadAudio(ThemisPaper pa, String paperDirectory);
+
+}

+ 17 - 7
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/PaperService.java

@@ -1,17 +1,23 @@
 package cn.com.qmth.examcloud.core.questions.service;
 
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.data.domain.Page;
+import org.springframework.web.multipart.MultipartFile;
+
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
 import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
 import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
-import cn.com.qmth.examcloud.core.questions.dao.entity.*;
+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.Question;
 import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitExp;
 import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperExp;
-import org.springframework.data.domain.Page;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import cn.com.qmth.examcloud.core.questions.service.bean.paper.PaperAnswerDomain;
 
 
 /**
@@ -303,4 +309,8 @@ public interface PaperService {
      * @return
      */
     public double getQuestionTypeScore(String paperId, Integer publicityType, Integer difficultyType);
+
+	public List<PaperAnswerDomain> answerExport(Paper paper);
+
+	public void answerImport(Paper paper,MultipartFile dataFile);
 }

+ 81 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/paper/PaperAnswerDomain.java

@@ -0,0 +1,81 @@
+package cn.com.qmth.examcloud.core.questions.service.bean.paper;
+
+import cn.com.qmth.examcloud.core.questions.base.excel.ExcelProperty;
+
+public class PaperAnswerDomain {
+
+	@ExcelProperty(name = "大题号", width = 30, index = 1)
+	private Integer number;
+	@ExcelProperty(name = "大题名称", width = 30, index = 2)
+	private String name;
+	@ExcelProperty(name = "小题号", width = 30, index = 3)
+	private Integer subNumber;
+	@ExcelProperty(name = "套题子题号", width = 30, index = 4)
+	private Integer childNumber;
+	@ExcelProperty(name = "题型", width = 30, index = 5)
+	private String subType;
+	@ExcelProperty(name = "答案", width = 30, index = 6)
+	private String answer;
+	
+	public PaperAnswerDomain() {
+	}
+
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+
+	public String getSubType() {
+		return subType;
+	}
+
+	public void setSubType(String subType) {
+		this.subType = subType;
+	}
+
+	public String getAnswer() {
+		return answer;
+	}
+
+	public void setAnswer(String answer) {
+		this.answer = answer;
+	}
+
+
+	public Integer getNumber() {
+		return number;
+	}
+
+
+	public void setNumber(Integer number) {
+		this.number = number;
+	}
+
+
+	public Integer getSubNumber() {
+		return subNumber;
+	}
+
+
+	public void setSubNumber(Integer subNumber) {
+		this.subNumber = subNumber;
+	}
+
+
+	public Integer getChildNumber() {
+		return childNumber;
+	}
+
+
+	public void setChildNumber(Integer childNumber) {
+		this.childNumber = childNumber;
+	}
+
+    
+    
+}

+ 101 - 69
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExportPaperServiceImpl.java

@@ -42,6 +42,7 @@ import cn.com.qmth.examcloud.core.questions.base.FileDisposeUtil;
 import cn.com.qmth.examcloud.core.questions.base.IdUtils;
 import cn.com.qmth.examcloud.core.questions.base.Model;
 import cn.com.qmth.examcloud.core.questions.base.SpringContextUtils;
+import cn.com.qmth.examcloud.core.questions.base.converter.utils.FileUtil;
 import cn.com.qmth.examcloud.core.questions.base.enums.ExamFileType;
 import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
 import cn.com.qmth.examcloud.core.questions.dao.CoursePropertyRepo;
@@ -66,6 +67,7 @@ import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.Compute
 import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.Section;
 import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.Sections;
 import cn.com.qmth.examcloud.core.questions.service.ExportPaperService;
+import cn.com.qmth.examcloud.core.questions.service.ExportThemisPaperService;
 import cn.com.qmth.examcloud.core.questions.service.PaperDetailService;
 import cn.com.qmth.examcloud.core.questions.service.PaperService;
 import cn.com.qmth.examcloud.core.questions.service.PropertyService;
@@ -73,6 +75,7 @@ import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperExp;
 import cn.com.qmth.examcloud.core.questions.service.bean.dto.QuestionDistributeDto;
 import cn.com.qmth.examcloud.core.questions.service.converter.PrintExamPaperService;
 import cn.com.qmth.examcloud.core.questions.service.export.ExportPaperAbstractService;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaperAndAnswer;
 import cn.com.qmth.examcloud.core.questions.service.util.ExportPaperUtil;
 import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
 
@@ -115,6 +118,9 @@ public class ExportPaperServiceImpl implements ExportPaperService {
 
     @Autowired
     private PaperDetailUnitRepo paperDetailUnitRepo;
+    
+    @Autowired
+    private ExportThemisPaperService exportThemisPaperService;
 
 //    @Autowired
 //    protected QuesPkgPathRepo quesPkgPathRepo;
@@ -142,6 +148,11 @@ public class ExportPaperServiceImpl implements ExportPaperService {
             if (exportContentList.indexOf(ExamFileType.COMPUTERTEST_PACKAGE.name()) > -1) {
                 downJson(paper, zipFileName);
             }
+            if (exportContentList.indexOf(ExamFileType.THEMIS_PACKAGE.name()) > -1) {
+            	List<Paper> papers=new ArrayList<>();
+            	papers.add(paper);
+                downThemisPackage(papers, zipFileName);
+            }
             try {
                 if (exportContentList.indexOf(ExamFileType.PAPER.name()) > -1) {
                     exportPaperAbstractService.downloadPaper(rootOrgId, paperId, zipFileName, examType);
@@ -168,7 +179,9 @@ public class ExportPaperServiceImpl implements ExportPaperService {
             log.debug("下载zip耗时:" + (endTime - startTime) + "ms");
         } finally {
             long endTime = System.currentTimeMillis();
-            deteleFolder(TEMP_FILE_EXP, zipFileName);
+			FileUtil.deleteFolder(TEMP_FILE_EXP+ File.separator + zipFileName);
+			FileUtil.deleteFolder(TEMP_FILE_EXP+ File.separator + zipFileName+ ".zip");
+//            deteleFolder(TEMP_FILE_EXP, zipFileName);
             long deleteTime = System.currentTimeMillis();
             log.debug("删除文件耗时:" + (deleteTime - endTime) + "ms");
         }
@@ -192,34 +205,65 @@ public class ExportPaperServiceImpl implements ExportPaperService {
 //        Docx4J.save(wordMLPackage, file);
 //    }
 
-    private void deteleFolder(String sourceFilePath, String zipFileName) {
-        File ComputerTestPaperfoler = new File(sourceFilePath + File.separator + "json");
-        if (ComputerTestPaperfoler.exists()) {
-            FileUtils.deleteQuietly(ComputerTestPaperfoler);
-        }
-        File zipFolder = new File(sourceFilePath + File.separator + zipFileName);
-        if (zipFolder.exists()) {
-            FileUtils.deleteQuietly(zipFolder);
-        }
-        File zipFile = new File(sourceFilePath + File.separator + zipFileName + ".zip");
-        if (zipFile.exists()) {
-            FileUtils.deleteQuietly(zipFile);
-        }
-    }
+//    private void deteleFolder(String sourceFilePath, String zipFileName) {
+//        File ComputerTestPaperfoler = new File(sourceFilePath + File.separator + "json");
+//        if (ComputerTestPaperfoler.exists()) {
+//            FileUtils.deleteQuietly(ComputerTestPaperfoler);
+//        }
+//        File zipFolder = new File(sourceFilePath + File.separator + zipFileName);
+//        if (zipFolder.exists()) {
+//            FileUtils.deleteQuietly(zipFolder);
+//        }
+//        File zipFile = new File(sourceFilePath + File.separator + zipFileName + ".zip");
+//        if (zipFile.exists()) {
+//            FileUtils.deleteQuietly(zipFile);
+//        }
+//    }
 
     private void downJson(Paper paper, String zipFileName) {
         ComputerTestPaper computerTestPaper = buildComputerTestPapers(paper);
-        String jsonDirectory = TEMP_FILE_EXP + File.separator + "json";
+        String jsonDirectory = TEMP_FILE_EXP + File.separator+zipFileName+File.separator + "json";
+        try {
+	        // 新建文件夹
+	        File dirFile = new File(jsonDirectory);
+	        if (!dirFile.exists()) {
+	            dirFile.mkdirs();
+	        }
+	        makeComputerTestPaperToJsonFile(paper.getCourse().getCode(), computerTestPaper, jsonDirectory);
+	        downloadAudio(computerTestPaper, jsonDirectory);
+	        // 将文件夹打包成zip压缩包放在docxExport下
+	        FileDisposeUtil.fileToZip(jsonDirectory, TEMP_FILE_EXP + File.separator + zipFileName,
+	                paper.getCourse().getCode() + "_" + paper.getName());
+		} finally {
+			FileUtil.deleteDirectory(jsonDirectory);
+		}
+    }
+    
+    private void downThemisPackage(List<Paper> papers, String zipFileName) {
+        String zipDirectory = TEMP_FILE_EXP + File.separator +zipFileName+ File.separator+ "themis_package";
         // 新建文件夹
-        File dirFile = new File(jsonDirectory);
+        File dirFile = new File(zipDirectory);
         if (!dirFile.exists()) {
-            dirFile.mkdirs();
+        	dirFile.mkdirs();
         }
-        makeComputerTestPaperToJsonFile(paper.getCourse().getCode(), computerTestPaper, jsonDirectory);
-        downloadAudio(computerTestPaper, jsonDirectory);
-        // 将文件夹打包成zip压缩包放在docxExport下
-        FileDisposeUtil.fileToZip(jsonDirectory, TEMP_FILE_EXP + File.separator + zipFileName,
-                paper.getCourse().getCode() + "_" + paper.getName());
+        try {
+        	for(Paper paper:papers) {
+        		ThemisPaperAndAnswer pa = exportThemisPaperService.buildPaperAndAnswer(paper);
+				String paperDirectory = zipDirectory + File.separator + paper.getCourseNo() + File.separator
+						+ paper.getId();
+				File paperDir = new File(paperDirectory);
+				if (!paperDir.exists()) {
+					paperDir.mkdirs();
+				}
+				exportThemisPaperService.makePaperAnswerToJsonFile(pa, paperDirectory);
+				exportThemisPaperService.downloadAudio(pa.getPaper(), paperDirectory);
+        	}
+			// 将文件夹打包成zip压缩包放在docxExport下
+			File zipFile=new File(TEMP_FILE_EXP + File.separator + zipFileName+ File.separator + "在线考试数据包.zip");
+			FileUtil.doZip(dirFile, zipFile);
+		} finally {
+			FileUtil.deleteDirectory(zipDirectory);
+		}
     }
 
     private void downloadAudio(ComputerTestPaper computerTestPaper, String jsonDirectory) {
@@ -528,52 +572,40 @@ public class ExportPaperServiceImpl implements ExportPaperService {
                 .getBeanById(serviceName);
         // 根据试卷id查询所有试卷
         List<Paper> papers = CommonUtils.toList(paperRepo.findByIdIn(paperIds));
-        String zipFileName = loginName;
-        // 创建压缩文件夹
-        File directory = new File(TEMP_FILE_EXP + File.separator + zipFileName);
-        if (directory.exists()) {
-            deteleFolder(TEMP_FILE_EXP, zipFileName);
-        }
-        directory.mkdirs();
-        // 下载试卷
-        if (exportContentList.indexOf(ExamFileType.PAPER.name()) > -1) {
-            for (Paper paper : papers) {
-                exportPaperAbstractService.downloadPaper(rootOrgId, paper.getId(), zipFileName, examType);
-            }
-        }
-        // 下载答案
-        if (exportContentList.indexOf(ExamFileType.ANSWER.name()) > -1) {
-            for (Paper paper : papers) {
-                exportPaperAbstractService.downloadPaperAnswer(rootOrgId, paper.getId(), zipFileName);
-            }
-        }
-        // 下载机考数据包
-        if (exportContentList.indexOf(ExamFileType.COMPUTERTEST_PACKAGE.name()) > -1) {
-            int i = 1;
-            for (Paper paper : papers) {
-                // 创建json文件夹
-                String jsonDir = TEMP_FILE_EXP + File.separator + zipFileName + File.separator + "json";
-                File jsonDirectory = new File(jsonDir);
-                if (!jsonDirectory.exists()) {
-                    jsonDirectory.mkdirs();
-                }
-                ComputerTestPaper computerTestPaper = buildComputerTestPapers(paper);
-                makeComputerTestPaperToJsonFile(paper.getCourse().getCode(), computerTestPaper, jsonDir);
-                // 将文件夹打包成zip压缩包放在docxExport下
-                FileDisposeUtil.fileToZip(jsonDir, TEMP_FILE_EXP + File.separator + zipFileName,
-                        paper.getCourse().getCode() + "_" + paper.getName());
-                // 删除json文件夹
-                File ComputerTestPaperfoler = new File(jsonDir);
-                if (ComputerTestPaperfoler.exists()) {
-                    FileUtils.deleteQuietly(ComputerTestPaperfoler);
-                }
-            }
-        }
-        String nameString = System.currentTimeMillis() + "";
-        FileDisposeUtil.fileToZip(TEMP_FILE_EXP + File.separator + zipFileName, TEMP_FILE_EXP, nameString);
-        FileDisposeUtil.downloadFile(nameString + ".zip", TEMP_FILE_EXP + File.separator + nameString + ".zip",
-                response);
-        deteleFolder(TEMP_FILE_EXP, zipFileName);
+        String zipFileName = IdUtils.uuid();
+        try {
+			// 创建压缩文件夹
+			File directory = new File(TEMP_FILE_EXP + File.separator + zipFileName);
+			directory.mkdirs();
+			// 下载试卷
+			if (exportContentList.indexOf(ExamFileType.PAPER.name()) > -1) {
+				for (Paper paper : papers) {
+					exportPaperAbstractService.downloadPaper(rootOrgId, paper.getId(), zipFileName, examType);
+				}
+			}
+			// 下载答案
+			if (exportContentList.indexOf(ExamFileType.ANSWER.name()) > -1) {
+				for (Paper paper : papers) {
+					exportPaperAbstractService.downloadPaperAnswer(rootOrgId, paper.getId(), zipFileName);
+				}
+			}
+			// 下载机考数据包
+			if (exportContentList.indexOf(ExamFileType.COMPUTERTEST_PACKAGE.name()) > -1) {
+				for (Paper paper : papers) {
+					downJson(paper, zipFileName);
+				}
+			}
+			// 下载在线考试数据包
+			if (exportContentList.indexOf(ExamFileType.THEMIS_PACKAGE.name()) > -1) {
+				downThemisPackage(papers, zipFileName);
+			}
+			FileDisposeUtil.fileToZip(TEMP_FILE_EXP + File.separator + zipFileName, TEMP_FILE_EXP, zipFileName);
+			FileDisposeUtil.downloadFile(zipFileName + ".zip", TEMP_FILE_EXP + File.separator + zipFileName + ".zip",
+					response);
+		} finally {
+			FileUtil.deleteFolder(TEMP_FILE_EXP+ File.separator + zipFileName);
+			FileUtil.deleteFolder(TEMP_FILE_EXP+ File.separator + zipFileName+ ".zip");
+		}
     }
 
     @Override

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

@@ -0,0 +1,566 @@
+package cn.com.qmth.examcloud.core.questions.service.impl;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.nodes.Node;
+import org.jsoup.nodes.TextNode;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
+import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
+import cn.com.qmth.examcloud.core.questions.base.Model;
+import cn.com.qmth.examcloud.core.questions.base.converter.utils.FileUtil;
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+import cn.com.qmth.examcloud.core.questions.dao.QuestionAudioRepo;
+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.QuesOption;
+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.service.ExportThemisPaperService;
+import cn.com.qmth.examcloud.core.questions.service.PaperDetailService;
+import cn.com.qmth.examcloud.core.questions.service.PaperService;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisAnswer;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisAnswerContent;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisAnswerDetail;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisBlock;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisSubjectiveAnswer;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisOption;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaper;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaperAndAnswer;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaperDetail;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisQuestion;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisSection;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisSections;
+import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
+
+@Service("exportThemisPaperService")
+public class ExportThemisPaperServiceImpl implements ExportThemisPaperService {
+
+	protected ExamCloudLog log = ExamCloudLogFactory.getLog(this.getClass());
+
+	@Autowired
+	private PaperService paperService;
+
+	@Autowired
+	private PaperDetailService paperDetailService;
+	@Autowired
+	private QuestionAudioRepo questionAudioRepo;
+
+	@Override
+	public void downloadAudio(ThemisPaper pa, String paperDirectory) {
+		String attDirectory = paperDirectory + File.separator + "attachment" + File.separator;
+		File attDir = new File(attDirectory);
+		if (!attDir.exists()) {
+			attDir.mkdirs();
+		}
+		int count = 0;
+		// 取到所有大题
+		List<ThemisPaperDetail> details = pa.getDetails();
+		if (details != null && details.size() > 0) {
+			for (ThemisPaperDetail detail : details) {
+				// 取到所有小题集合
+				List<ThemisQuestion> questions = detail.getQuestions();
+				if (questions != null && questions.size() > 0) {
+					for (ThemisQuestion question : questions) {
+//						int bodyNum = 1;
+						// 取到题干
+						ThemisSections body = question.getBody();
+						List<ThemisSection> sections = body.getSections();
+						for (ThemisSection section : sections) {
+							List<ThemisBlock> blocks = section.getBlocks();
+							if (blocks != null && blocks.size() > 0) {
+								for (ThemisBlock block : blocks) {
+									if (block.getType().equals("audio")) {
+										String id = block.getValue();
+										QuestionAudio questionAudio = Model.of(questionAudioRepo.findById(id));
+										String audioFileName = questionAudio.getId() + "."
+												+ questionAudio.getFileSuffixes();
+										File file = new File(attDirectory + audioFileName);
+										// 通用存储
+										FileStorageUtil.saveUrlAs(FileStorageUtil.realPath(questionAudio.getFileUrl()),
+												file);
+										count++;
+//										bodyNum++;
+									}
+								}
+							}
+						}
+						// 取到选项
+						List<ThemisOption> options = question.getOptions();
+						if (options != null && options.size() > 0) {
+							for (ThemisOption computerTestOption : options) {
+//								int optionNum = 1;
+								// 获取选项主体
+								ThemisSections optionBody = computerTestOption.getBody();
+								List<ThemisSection> optionSections = optionBody.getSections();
+								if (optionSections != null && optionSections.size() > 0) {
+									for (ThemisSection optionSection : optionSections) {
+										List<ThemisBlock> blocks = optionSection.getBlocks();
+										if (blocks != null && blocks.size() > 0) {
+											for (ThemisBlock block : blocks) {
+												if (block.getType().equals("audio")) {
+													String id = block.getValue();
+													QuestionAudio questionAudio = Model
+															.of(questionAudioRepo.findById(id));
+													String audioFileName = questionAudio.getId() + "."
+															+ questionAudio.getFileSuffixes();
+													File file = new File(attDirectory + audioFileName);
+													// 通用存储
+													FileStorageUtil.saveUrlAs(
+															FileStorageUtil.realPath(questionAudio.getFileUrl()), file);
+													count++;
+//													optionNum++;
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+		if (count == 0) {
+			FileUtil.deleteFolder(attDirectory);
+		}
+	}
+
+	@Override
+	public void makePaperAnswerToJsonFile(ThemisPaperAndAnswer pa, String jsonDirectoryPath) {
+		makeAnswerToJsonFile(pa.getAnswer(), jsonDirectoryPath);
+		makePaperToJsonFile(pa.getPaper(), jsonDirectoryPath);
+	}
+
+	private void makeAnswerToJsonFile(ThemisAnswer pa, String jsonDirectoryPath) {
+		// 创建新的JSON文件
+		File file = new File(jsonDirectoryPath + File.separator + "answer.json");
+		// 将对象转成 json对象
+		Gson gson = new Gson();
+		String strJSON = gson.toJson(pa);
+
+		strJSON = CommonUtils.replaceUnicodeStr(strJSON);
+		// 生成文件流写入JSON文件
+		FileOutputStream outputStream = null;
+		try {
+			outputStream = new FileOutputStream(file);
+			byte b[] = strJSON.getBytes();
+			outputStream.write(b);
+			outputStream.flush();
+		} catch (FileNotFoundException e) {
+			log.error(e.getMessage(), e);
+		} catch (IOException e) {
+			log.error(e.getMessage(), e);
+		} finally {
+			IOUtils.closeQuietly(outputStream);
+		}
+	}
+
+	private void makePaperToJsonFile(ThemisPaper pa, String jsonDirectoryPath) {
+		// 创建新的JSON文件
+		File file = new File(jsonDirectoryPath + File.separator + "paper.json");
+		// 将对象转成 json对象
+		Gson gson = new Gson();
+		String strJSON = gson.toJson(pa);
+
+		strJSON = CommonUtils.replaceUnicodeStr(strJSON);
+		// 生成文件流写入JSON文件
+		FileOutputStream outputStream = null;
+		try {
+			outputStream = new FileOutputStream(file);
+			byte b[] = strJSON.getBytes();
+			outputStream.write(b);
+			outputStream.flush();
+		} catch (FileNotFoundException e) {
+			log.error(e.getMessage(), e);
+		} catch (IOException e) {
+			log.error(e.getMessage(), e);
+		} finally {
+			IOUtils.closeQuietly(outputStream);
+		}
+	}
+
+	@Override
+	public ThemisPaperAndAnswer buildPaperAndAnswer(Paper paper) {
+		// 得到所有旧对象的大题对象
+		List<PaperDetail> paperDetails = paperService.findPaperDetailsById(paper.getId());
+		// 通过 paper 对象 ,生成新的 ComputerTestPaper 对象
+		ThemisPaper computerTestPaper = new ThemisPaper(paper, "");
+		ThemisAnswer themisAnswer = new ThemisAnswer();
+		List<ThemisPaperDetail> details = new ArrayList<>();
+		List<ThemisAnswerDetail> answerdetails = new ArrayList<>();
+		// 遍历所有旧大题对象,得到小题对象的集合
+		for (PaperDetail paperDetail : paperDetails) {
+			List<PaperDetailUnit> paperDetailUnits = paperDetailService.getUnitsByPaperDetailId(paperDetail.getId());
+			ThemisPaperDetail computerTestPaperDetail = new ThemisPaperDetail(paperDetail);
+			ThemisAnswerDetail answerDetail = new ThemisAnswerDetail(paperDetail);
+			List<ThemisQuestion> questions = new ArrayList<>();
+			List<ThemisAnswerContent> answers = new ArrayList<>();
+			// 遍历所有的小题对象
+			for (int i = 0; i < paperDetailUnits.size(); i++) {
+				PaperDetailUnit paperDetailUnit = paperDetailUnits.get(i);
+				// 根据旧的小题对象,生成新的小题对象
+				ThemisQuestion computerTestQuestion = new ThemisQuestion(paperDetailUnit);
+				ThemisAnswerContent themisAnswerContent = new ThemisAnswerContent(paperDetailUnit);
+				// 设置小题题号
+				computerTestQuestion.setNumber(i + 1);
+				themisAnswerContent.setNumber(i + 1);
+				// 得到小题题干
+				computerTestQuestion.setBody(getBody(paperDetailUnit.getQuestion().getQuesBody(), computerTestPaper));
+				// 得到小题所有选项
+				computerTestQuestion.setOptions(getOption(paperDetailUnit.getQuestion(), computerTestPaper));
+				// 得到小题的答案
+				if (paperDetailUnit.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+						|| paperDetailUnit.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+					themisAnswerContent.setAnswer(getSelectAnswer(paperDetailUnit.getQuestion()));
+				} else if (paperDetailUnit.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+					themisAnswerContent.setAnswer(getBoolAnswer(paperDetailUnit.getQuestion()));
+				} else if (paperDetailUnit.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
+					themisAnswerContent.setAnswer(getFillBlankAnswer(paperDetailUnit.getQuestion()));
+				} else {
+					themisAnswerContent
+							.setAnswer(getAnswer(paperDetailUnit.getQuestion().getQuesAnswer(), computerTestPaper));
+				}
+				// 查询小题中的 套题
+				List<Question> subQuestionsList = paperDetailUnit.getQuestion().getSubQuestions();
+				// 判断这个小题中是否有套题
+				if (subQuestionsList != null && subQuestionsList.size() > 0) {
+					List<ThemisQuestion> subQuestions = new ArrayList<>();
+					List<ThemisAnswerContent> subAnswers = new ArrayList<>();
+					// 遍历每个套题
+					for (int j = 0; j < subQuestionsList.size(); j++) {
+						Question subQuestion = subQuestionsList.get(j);
+						ThemisQuestion subcomputerTestQuestion = new ThemisQuestion(subQuestion);
+						ThemisAnswerContent subthemisAnswerContent = new ThemisAnswerContent(subQuestion);
+						// 设置套题中小题题号
+						subcomputerTestQuestion.setNumber(j + 1);
+						subthemisAnswerContent.setNumber(j + 1);
+						subcomputerTestQuestion.setBody(getBody(subQuestion.getQuesBody(), computerTestPaper));
+						subcomputerTestQuestion.setOptions(getOption(subQuestion, computerTestPaper));
+						if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+								|| subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+							subthemisAnswerContent.setAnswer(getSelectAnswer(subQuestion));
+						} else if (subQuestion.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+							subthemisAnswerContent.setAnswer(getBoolAnswer(subQuestion));
+						} else if (subQuestion.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
+							subthemisAnswerContent.setAnswer(getFillBlankAnswer(subQuestion));
+						} else {
+							subthemisAnswerContent.setAnswer(getAnswer(subQuestion.getQuesAnswer(), computerTestPaper));
+						}
+						subQuestions.add(subcomputerTestQuestion);
+						subAnswers.add(subthemisAnswerContent);
+					}
+					computerTestQuestion.setSubQuestions(subQuestions);
+					themisAnswerContent.setSubQuestions(subAnswers);
+				}
+				questions.add(computerTestQuestion);
+				answers.add(themisAnswerContent);
+			}
+			computerTestPaperDetail.setQuestions(questions);
+			answerDetail.setQuestions(answers);
+			// paperDetail中的题数(unitCount)可能不准确,这里以questions的实际size为准
+			computerTestPaperDetail.setQuestionCount(questions.size());
+			details.add(computerTestPaperDetail);
+			answerdetails.add(answerDetail);
+		}
+		computerTestPaper.setDetails(details);
+		themisAnswer.setDetails(answerdetails);
+
+		ThemisPaperAndAnswer ret = new ThemisPaperAndAnswer();
+		computerTestPaper.setId(null);
+		ret.setPaper(computerTestPaper);
+		ret.setAnswer(themisAnswer);
+		return ret;
+	}
+
+	private ThemisSections getBody(String str, ThemisPaper computerTestPaper) {
+		ThemisSections body = new ThemisSections();
+		List<ThemisSection> sections = new ArrayList<>();
+		// 得到小题题干或者答案行数
+		if (StringUtils.isBlank(str)) {
+			return body;
+		}
+		String[] questionRowStrings = str.split("</p>");
+		for (int i = 0; i < questionRowStrings.length; i++) {
+			List<ThemisBlock> blocks = disposeQuestionBodyOrOption(questionRowStrings[i], computerTestPaper);
+			if (blocks != null && blocks.size() > 0) {
+				ThemisSection section = new ThemisSection();
+				// 将小题题干拆分为Block集合
+				section.setBlocks(blocks);
+				sections.add(section);
+			}
+		}
+		body.setSections(sections);
+		return body;
+	}
+
+	private List<ThemisSubjectiveAnswer> getAnswer(String str, ThemisPaper computerTestPaper) {
+		List<ThemisSection> sections = new ArrayList<>();
+		// 得到小题题干或者答案行数
+		if (StringUtils.isBlank(str)) {
+			return null;
+		}
+		String[] questionRowStrings = str.split("</p>");
+		for (int i = 0; i < questionRowStrings.length; i++) {
+			List<ThemisBlock> blocks = disposeQuestionBodyOrOption(questionRowStrings[i], computerTestPaper);
+			if (blocks != null && blocks.size() > 0) {
+				ThemisSection section = new ThemisSection();
+				// 将小题题干拆分为Block集合
+				section.setBlocks(blocks);
+				sections.add(section);
+			}
+		}
+		List<ThemisSubjectiveAnswer> ans=new ArrayList<>();
+		ThemisSubjectiveAnswer an=new ThemisSubjectiveAnswer();
+		ans.add(an);
+		an.setIndex(1);
+		an.setSections(sections);
+		return ans;
+	}
+
+	private List<ThemisBlock> disposeQuestionBodyOrOption(String questionRow, ThemisPaper computerTestPaper) {
+		List<ThemisBlock> blocks = new ArrayList<>();
+		// 去掉每行里面的<p>,<span>,</span>标签
+		questionRow = questionRow.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<span>", "")
+				.replaceAll("</span>", "").replaceAll("</a>", "");
+		String[] questionRowStrings = questionRow.split("<|/>|>");
+		boolean hasAudio = false;
+		for (int i = 0; i < questionRowStrings.length; i++) {
+			ThemisBlock block = new ThemisBlock();
+			String rowStr = questionRowStrings[i];
+			// 判断是否有图片
+			if (rowStr.startsWith("img")) {
+				rowStr = "<" + rowStr + ">";
+				Map<String, Object> param = new HashMap<>();
+				// 需要继续做截取,取到Parma
+				block.setType("image");
+				// 获取图片的路径
+				List<String> strSrcList = getImg(rowStr, "src");
+				if (strSrcList.size() > 0) {
+					String strSrc = strSrcList.get(0).replaceAll("src=\"", "").replaceAll("\"", "");
+					block.setValue(strSrc);
+				}
+				// 获取图片的高度
+				List<String> strHeightList = getImg(rowStr, "height");
+				if (strHeightList.size() > 0) {
+					String strHeight = strHeightList.get(0).replaceAll("height=\"", "").replaceAll("\"", "");
+					param.put("height", strHeight);
+				}
+				// 获取图片的宽度
+				List<String> strWidthList = getImg(rowStr, "width");
+				if (strHeightList.size() > 0) {
+					String strWidth = strWidthList.get(0).replaceAll("width=\"", "").replaceAll("\"", "");
+					param.put("width", strWidth);
+				}
+				block.setParam(param);
+				blocks.add(block);
+			} else if (rowStr.startsWith("a") && rowStr.contains("id") && rowStr.contains("name")) { // 处理音频
+				rowStr = "<" + rowStr + ">";
+				block.setType("audio");
+				block.setValue(CommonUtils.getAttrValue(rowStr, "id"));
+				blocks.add(block);
+				hasAudio = true;
+			} else {
+				block.setType("text");
+				rowStr = rowStr.replace("&nbsp;", "");// 消除空格
+				rowStr = rowStr.replace("&quot;", "\"");// 将&quot;转换成\"
+				rowStr = rowStr.replace("&lt;", "<");// 将&lt;转换成<
+				rowStr = rowStr.replace("&gt;", ">");// 将&gt;转换成>
+				rowStr = rowStr.replace("&amp;", "&");// 将&amp;转换成&
+				if (StringUtils.isNotBlank(rowStr)) {
+					block.setValue(rowStr);
+					blocks.add(block);
+				}
+			}
+		}
+		if (hasAudio) {
+			computerTestPaper.setHasAudio(hasAudio);
+		}
+		return blocks;
+	}
+
+	/**
+	 * 获取图片里面的路径,长度,宽度
+	 */
+	private List<String> getImg(String s, String str) {
+		String regex;
+		List<String> list = new ArrayList<>();
+		regex = str + "=\"(.*?)\"";
+		Pattern pa = Pattern.compile(regex, Pattern.DOTALL);
+		Matcher ma = pa.matcher(s);
+		while (ma.find()) {
+			list.add(ma.group());
+		}
+		return list;
+	}
+
+	private List<ThemisOption> getOption(Question question, ThemisPaper computerTestPaper) {
+		// 得到小题选项
+		List<QuesOption> quesOptions = question.getQuesOptions();
+		List<ThemisOption> options = new ArrayList<>();
+		// 遍历小题选项
+		if (quesOptions != null && quesOptions.size() > 0) {
+			for (QuesOption quesOption : quesOptions) {
+				ThemisOption option = new ThemisOption();
+				option.setNumber(new Integer(quesOption.getNumber()));
+				ThemisSections body = new ThemisSections();
+
+				List<ThemisSection> sections = new ArrayList<>();
+				// 得到小题选项
+				String optionString = quesOption.getOptionBody();
+				String[] optionStrings = optionString.split("</p>");
+				for (int i = 0; i < optionStrings.length; i++) {
+					List<ThemisBlock> blocks = disposeQuestionBodyOrOption(optionStrings[i], computerTestPaper);
+					if (blocks != null && blocks.size() > 0) {
+						ThemisSection section = new ThemisSection();
+						section.setBlocks(blocks);
+						sections.add(section);
+					}
+				}
+				body.setSections(sections);
+				option.setBody(body);
+				options.add(option);
+			}
+		}
+		return options;
+	}
+
+	private JsonArray getSelectAnswer(Question question) {
+		// 得到小题选项
+		List<QuesOption> quesOptions = question.getQuesOptions();
+		int index = 0;
+		// 遍历小题选项
+		if (quesOptions != null && quesOptions.size() > 0) {
+			JsonArray ja = new JsonArray();
+			for (QuesOption quesOption : quesOptions) {
+				index++;
+				if (quesOption.getIsCorrect() == 1) {
+					ja.add(index);
+				}
+			}
+			if (ja.size() > 0) {
+				return ja;
+			} else {
+				return null;
+			}
+		}
+		return null;
+	}
+
+	private Boolean getBoolAnswer(Question question) {
+		String as = question.getQuesAnswer();
+		if (StringUtils.isBlank(as)) {
+			return null;
+		}
+		if (as.contains("正确")) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	private void getSectionElement(Node ce, StringBuilder sb) {
+		if (("span".equals(ce.nodeName()) || "p".equals(ce.nodeName())) && ce.childNodeSize() > 0) {
+			for (Node e : ce.childNodes()) {
+				getSectionElement(e, sb);
+			}
+		} else {
+			if (ce instanceof TextNode) {
+				TextNode tn = (TextNode) ce;
+				String text = tn.text();
+				sb.append(text);
+			} else if (ce instanceof Element) {
+				if ("img".equals(ce.nodeName())) {
+					Element el = (Element) ce;
+					sb.append(el.outerHtml());
+				} else {
+					Element el = (Element) ce;
+					sb.append(el.text());
+				}
+			}
+		}
+	}
+
+	private List<ThemisSubjectiveAnswer> getFillBlankAnswer(Question question) {
+		String html = question.getQuesAnswer();
+		if (StringUtils.isBlank(html)) {
+			return null;
+		}
+		StringBuilder sb = new StringBuilder();
+		Document doc = Jsoup.parse(html);
+		Element b = doc.body();
+		for (Node ce : b.childNodes()) {
+			getSectionElement(ce, sb);
+		}
+
+		if (StringUtils.isBlank(sb.toString())) {
+			return null;
+		}
+		List<ThemisSubjectiveAnswer> tas = new ArrayList<>();
+		String[] sbs = sb.toString().split("##");
+		int index = 0;
+		for (String an : sbs) {
+			index++;
+			ThemisSubjectiveAnswer ta = new ThemisSubjectiveAnswer();
+			ta.setIndex(index);
+			List<ThemisSection> sections = new ArrayList<>();
+			ThemisSection sec = new ThemisSection();
+			List<ThemisBlock> blocks = new ArrayList<>();
+			Document subdoc = Jsoup.parse(an);
+			for (Node ce : subdoc.body().childNodes()) {
+				ThemisBlock block = new ThemisBlock();
+				if (ce instanceof TextNode) {
+					TextNode tn = (TextNode) ce;
+					String text = tn.text();
+					block.setType("text");
+					block.setValue(text);
+				} else if (ce instanceof Element) {
+					if ("img".equals(ce.nodeName())) {
+						Element el = (Element) ce;
+						block.setType("image");
+						block.setValue(el.attr("src"));
+						String h = el.attr("height");
+						String w = el.attr("width");
+						if (StringUtils.isNotBlank(w) && StringUtils.isNotBlank(h)) {
+							Map<String, Object> param = new HashMap<String, Object>();
+							param.put("height", h);
+							param.put("width", w);
+							block.setParam(param);
+						}
+					} else {
+						Element el = (Element) ce;
+						block.setType("text");
+						block.setValue(el.text());
+					}
+				}
+				blocks.add(block);
+			}
+			sec.setBlocks(blocks);
+			sections.add(sec);
+			ta.setSections(sections);
+			tas.add(ta);
+		}
+		return tas;
+	}
+}

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

@@ -1091,4 +1091,68 @@ public class GenPaperService {
         return false;
     }
 
+	public Map<String, Object> genPaperManual(GenPaperDto genPaperDto) {
+		log.debug("开始组卷.....");
+        String msg = "";
+        Map<String, Object> paperMsgMap = new HashMap<>();
+        //查询试卷结构
+        long start = System.currentTimeMillis();
+        PaperStruct paperStruct = Model.of(paperStructRepo.findById(genPaperDto.getPaperStructId()));
+        if (paperStruct == null) {
+            paperMsgMap.put("msg", "试卷结构不存在");
+            return paperMsgMap;
+        }
+        PaperContext paperContext = new PaperContext(paperStruct, genPaperDto.getConditions());
+        List<UnitContext> unitContexts = new LinkedList<>();
+        List<DetailContext> detailContexts = paperContext.getDetails();
+        for (DetailContext dc : detailContexts) {
+            unitContexts.addAll(dc.getUnits());
+        }
+
+	 	Paper paper = new Paper();
+        paper.setPaperDetailCount(paperStruct.getDetailCount());
+        paper.setTotalScore(0.0);
+        paper.setPaperType(PaperType.GENERATE);
+        // 补全paper属性
+        paper.setExamRemark(paperStruct.getExamRemark());
+        paper.setName(genPaperDto.getPaperName());
+        paper.setCourseNo(genPaperDto.getCourseNo());
+        paper.setCourseName(genPaperDto.getCourseName());
+        Course course = courseService.getCourse(Long.valueOf(genPaperDto.getOrgId()), genPaperDto.getCourseNo());
+        paper.setCourse(course);
+        paper.setCreator(genPaperDto.getCreator());
+        paper.setOrgId(genPaperDto.getOrgId());
+        paper.setLevel(genPaperDto.getLevel());
+        paper.setCreateTime(CommonUtils.getCurDateTime());
+        paper.setUnitCount(0);
+        paper.setPaperType(PaperType.GENERATE);
+        paper.setDifficultyDegree(0.5);
+        // 数据入库
+        paperRepo.save(paper);
+        List<PaperDetail> paperDetails = buildPaperDetail(paper, paperStruct);
+        paperDetailRepo.saveAll(paperDetails);
+        
+        msg = "success";
+        paperMsgMap.put("paper", paper);
+        paperMsgMap.put("msg", msg);
+        
+        
+        return paperMsgMap;
+	}
+	
+	private List<PaperDetail> buildPaperDetail(Paper paper,PaperStruct paperStruct) {
+        List<PaperDetail> detailList = new ArrayList<>();
+        for (PaperDetailStruct pds : paperStruct.getPaperDetailStructs()) {
+            PaperDetail paperDetail = new PaperDetail();
+            paperDetail.setPaper(paper);
+            paperDetail.setNumber(pds.getNumber());
+            paperDetail.setName(pds.getName());
+            paperDetail.setCreator(paperStruct.getCreator());
+            paperDetail.setScore(0.0);
+            paperDetail.setUnitCount(0);
+            detailList.add(paperDetail);
+        }
+        return detailList;
+	}
+
 }

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

@@ -4,7 +4,11 @@ import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.commons.lang3.StringUtils;
 import org.bson.types.ObjectId;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort.Direction;
@@ -16,6 +20,7 @@ import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.base.Model;
 import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
 import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
@@ -84,7 +89,41 @@ public class PaperDetailUnitServiceImpl implements PaperDetailUnitService {
     public PaperDetailUnit findById(String id) {
         return Model.of(paperDetailUnitRepo.findById(id));
     }
-
+    private void checkFillBlankQuestion(Question question) {
+    	if(question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
+    		String body=question.getQuesBody();
+    		Document bodyDoc = Jsoup.parse(body);
+    		if(bodyDoc.body().childrenSize()==0) {
+    			throw new StatusException("1000", "题干不能为空");
+    		}
+	        String bodyText=bodyDoc.body().html();
+	        if(StringUtils.isBlank(bodyText)) {
+	        	throw new StatusException("1001", "题干不能为空");
+	        }
+	        if(bodyText.indexOf("###")==-1) {
+	        	throw new StatusException("1002", "题干不能没有空格(###)");
+	        }
+	        String answer=question.getQuesAnswer();
+	        Document answerDoc = Jsoup.parse(answer);
+	        if(answerDoc.body().childrenSize()!=0) {
+		        String answerText=answerDoc.body().html();
+		        if(StringUtils.isNotBlank(answerText)) {
+		        	if(getSubStringCount(bodyText, "###")!=answerText.split("##").length) {
+		        		throw new StatusException("1003", "题干空格(###)数量和答案数量不一致");
+		        	}
+		        }
+	        }
+    	}
+    }
+    private int getSubStringCount(String src,String find){
+    	int o = 0;
+    	int index=-1;
+    	while((index=src.indexOf(find,index))>-1){
+	    	++index;
+	    	++o;
+    	}
+		return o;
+	}
     /**
      * 保存小题
      */
@@ -92,7 +131,7 @@ public class PaperDetailUnitServiceImpl implements PaperDetailUnitService {
         PaperDetailUnit baseUnit = Model.of(paperDetailUnitRepo.findById(updateUnit.getId()));
         Question baseQuestion = baseUnit.getQuestion();
         Question updateQuestion = updateUnit.getQuestion();
-
+        checkFillBlankQuestion(updateQuestion);
         if (baseUnit.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
             if (updateQuestion.getId().equals(baseQuestion.getId())) {
                 // 更新对象为套题本身
@@ -176,7 +215,6 @@ public class PaperDetailUnitServiceImpl implements PaperDetailUnitService {
 
         return baseUnit;
     }
-
     /**
      * 删除小题
      *

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

@@ -12,16 +12,25 @@ 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 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.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
 import org.nlpcn.commons.lang.util.StringUtil;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -55,6 +64,7 @@ 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.SpringContextUtils;
+import cn.com.qmth.examcloud.core.questions.base.converter.utils.FileUtil;
 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;
@@ -87,6 +97,7 @@ 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.cache.BasePaperCache;
 import cn.com.qmth.examcloud.core.questions.service.cache.ExtractConfigPaperCache;
 import cn.com.qmth.examcloud.core.questions.service.cache.QuestionAnswerCache;
@@ -94,6 +105,7 @@ 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.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;
 
@@ -164,6 +176,8 @@ public class PaperServiceImpl implements PaperService {
     private QuestionCache questionCache;
     @Autowired
     private QuestionAnswerCache questionAnswerCache;
+    @Autowired
+    private SystemProperties systemProperties;
 
     private Random random = new Random();
 
@@ -195,6 +209,14 @@ public class PaperServiceImpl implements PaperService {
             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 + ".*"));
+        }
         long count = this.mongoTemplate.count(query, Paper.class);
         query.with(new Sort(new Order(Direction.DESC, "createTime")));
         query.limit(pageSize);
@@ -341,6 +363,14 @@ public class PaperServiceImpl implements PaperService {
         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 count = this.mongoTemplate.count(query, Paper.class);
         query.with(new Sort(new Order(Direction.DESC, "createTime")));
         query.limit(pageSize);
@@ -1646,8 +1676,12 @@ public class PaperServiceImpl implements PaperService {
                     List<QuesProperty> quesProperties = question.getQuesProperties();
                     if (quesProperties != null && quesProperties.size() > 0) {
                         QuesProperty quesProperty = quesProperties.get(0);
-                        paperDetailUnit.setFirstName(quesProperty.getFirstProperty().getName());
-                        paperDetailUnit.setSecondName(quesProperty.getSecondProperty().getName());
+                        if(quesProperty.getFirstProperty()!=null) {
+                        	paperDetailUnit.setFirstName(quesProperty.getFirstProperty().getName());
+                        }
+                        if(quesProperty.getSecondProperty()!=null) {
+                        	paperDetailUnit.setSecondName(quesProperty.getSecondProperty().getName());
+                        }
                     }
                 }
             }
@@ -1869,4 +1903,304 @@ public class PaperServiceImpl implements PaperService {
         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());
+		}
+		
+		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);
+						}
+					}
+				}else {
+					if(answerMap.get(unit.getNumber().toString())!=null) {
+						checkAndSetAnswer(qu, answerMap.get(unit.getNumber().toString()).getAnswer(), unit.getNumber(), null);
+					}
+				}
+			}
+			ques.add(qu);
+		}
+		quesRepo.saveAll(ques);
+	}
+	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,"答案选项不存在");
+                }
+            }
+            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());
+	}
 }

+ 23 - 15
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/QuesServiceImpl.java

@@ -180,23 +180,23 @@ public class QuesServiceImpl implements QuesService {
 //        updateQuesWord(question);
         // 更新QuesBody,把 下划线 转成 ##
         updateQuesBody(question);
-        //多选题答案逗号分隔
-        formatSelectQuestionAnswer(question);
+//        //多选题答案逗号分隔
+//        formatSelectQuestionAnswer(question);
         return quesRepo.save(question);
     }
-    
-    private void formatSelectQuestionAnswer(Question question) {
-        if(question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-            if(StringUtils.isNotBlank(question.getQuesAnswer())&&question.getQuesAnswer().indexOf(",")==-1) {
-                String[] sarr=new String[question.getQuesAnswer().length()];
-                for(int i=0;i<sarr.length;i++) {
-                    sarr[i]=String.valueOf(question.getQuesAnswer().charAt(i));
-                }
-                question.setQuesAnswer(StringUtils.join(sarr, ","));
-            }
-        }
-        
-    }
+   
+//    private void formatSelectQuestionAnswer(Question question) {
+//        if(question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+//            if(StringUtils.isNotBlank(question.getQuesAnswer())&&question.getQuesAnswer().indexOf(",")==-1) {
+//                String[] sarr=new String[question.getQuesAnswer().length()];
+//                for(int i=0;i<sarr.length;i++) {
+//                    sarr[i]=String.valueOf(question.getQuesAnswer().charAt(i));
+//                }
+//                question.setQuesAnswer(StringUtils.join(sarr, ","));
+//            }
+//        }
+//        
+//    }
 
     private void updateQuesBody(Question question) {
         String newQuesBody = question.getQuesBody().replaceAll("______", "###");
@@ -597,6 +597,14 @@ public class QuesServiceImpl implements QuesService {
                 }
             }
         }
+        //逗号分隔
+        if(StringUtils.isNotBlank(answer)) {
+        	String[] sarr=new String[answer.length()];
+        	for(int i=0;i<sarr.length;i++) {
+                sarr[i]=String.valueOf(answer.charAt(i));
+            }
+        	answer=StringUtils.join(sarr, ",");
+        }
         return answer;
     }
 

+ 32 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisAnswer.java

@@ -0,0 +1,32 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+import java.util.List;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午9:39:11
+ * @company QMTH
+ * @description ComputerTestPaper.java
+ */
+public class ThemisAnswer {
+
+    /**
+     * 大题集合
+     */
+    private List<ThemisAnswerDetail> details;
+
+    public ThemisAnswer() {
+
+    }
+
+
+    public List<ThemisAnswerDetail> getDetails() {
+        return details;
+    }
+
+    public void setDetails(List<ThemisAnswerDetail> details) {
+        this.details = details;
+    }
+
+}
+

+ 69 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisAnswerContent.java

@@ -0,0 +1,69 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午9:51:37
+ * @company QMTH
+ * @description ComputerTestQuestion.java
+ */
+public class ThemisAnswerContent {
+
+    /**
+     * 小题号或套题内子题序号
+     */
+    private Integer number;
+    /**
+     * 子题
+     */
+    private List<ThemisAnswerContent> subQuestions;
+
+    /**
+     * 答案
+     */
+    private Object answer;
+
+    public ThemisAnswerContent() {
+    }
+
+    public ThemisAnswerContent(PaperDetailUnit paperDetailUnit) {
+        this.number = paperDetailUnit.getNumber();
+//        this.score = paperDetailUnit.getScore();
+//        this.structType = paperDetailUnit.getQuestionType().getId();
+    }
+    public ThemisAnswerContent(Question question) {
+//        this.score = question.getScore();
+//        this.structType = question.getQuestionType().getId();
+    }
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+
+    public List<ThemisAnswerContent> getSubQuestions() {
+        return subQuestions;
+    }
+
+    public void setSubQuestions(List<ThemisAnswerContent> subQuestions) {
+        this.subQuestions = subQuestions;
+    }
+
+
+    public Object getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(Object answer) {
+        this.answer = answer;
+    }
+
+}
+

+ 47 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisAnswerDetail.java

@@ -0,0 +1,47 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午9:43:00
+ * @company QMTH
+ * @description ComputerTestPaperDetail.java
+ */
+public class ThemisAnswerDetail {
+    /**
+     * 大题号
+     */
+    private Integer number;
+    /**
+     * 小题集合
+     */
+    private List<ThemisAnswerContent> questions;
+
+    public ThemisAnswerDetail() {
+    }
+    public ThemisAnswerDetail(PaperDetail paperDetail) {
+        this.number = paperDetail.getNumber();
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+
+    public List<ThemisAnswerContent> getQuestions() {
+        return questions;
+    }
+
+    public void setQuestions(List<ThemisAnswerContent> questions) {
+        this.questions = questions;
+    }
+
+}
+

+ 49 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisBlock.java

@@ -0,0 +1,49 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+import java.util.Map;
+
+/**
+ * @author chenken
+ *
+ */
+public class ThemisBlock {
+    /**
+     * text:文字
+     * image:图片
+     * audio:音频
+     * video:视频
+     */
+    private String type;
+    /**
+     * 资源相对路径
+     */
+    private String value;
+
+    private Map<String, Object> param;
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public Map<String, Object> getParam() {
+        return param;
+    }
+
+    public void setParam(Map<String, Object> param) {
+        this.param = param;
+    }
+
+}
+

+ 32 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisOption.java

@@ -0,0 +1,32 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午10:22:56
+ * @company QMTH
+ * @description 选项
+ */
+public class ThemisOption {
+    private Integer number;
+
+    private ThemisSections body;
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+
+    public ThemisSections getBody() {
+        return body;
+    }
+
+    public void setBody(ThemisSections body) {
+        this.body = body;
+    }
+
+}
+

+ 141 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisPaper.java

@@ -0,0 +1,141 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
+
+import java.util.List;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午9:39:11
+ * @company QMTH
+ * @description ComputerTestPaper.java
+ */
+public class ThemisPaper {
+
+    /**
+     * 试卷编号
+     */
+    private String id;
+    /**
+     * 试卷名称
+     */
+    private String name;
+    /**
+     * 课程code
+     */
+    private String courseCode;
+    /**
+     * 试卷类型   A、B卷
+     */
+    private transient String groupCode;
+    /**
+     * 课程名称
+     */
+    private String courseName;
+    /**
+     * 总分
+     */
+    private double totalScore;
+    /**
+     * 1:含有音视频
+     * 0:不含有音视频
+     */
+    private Boolean hasAudio;
+    /**
+     * 大题数量
+     */
+    private int detailCount;
+    /**
+     * 大题集合
+     */
+    private List<ThemisPaperDetail> details;
+
+    public ThemisPaper() {
+
+    }
+
+    public ThemisPaper(Paper paper, String groupCode) {
+        this.id = paper.getId();
+        this.name = paper.getName();
+        this.courseCode = paper.getCourse().getCode();
+        this.courseName = paper.getCourse().getName();
+        this.totalScore = paper.getTotalScore();
+        this.detailCount = paper.getPaperDetailCount();
+        this.hasAudio = false;
+        this.groupCode = groupCode;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public int getDetailCount() {
+        return detailCount;
+    }
+
+    public void setDetailCount(int detailCount) {
+        this.detailCount = detailCount;
+    }
+
+    public List<ThemisPaperDetail> getDetails() {
+        return details;
+    }
+
+    public void setDetails(List<ThemisPaperDetail> details) {
+        this.details = details;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getGroupCode() {
+        return groupCode;
+    }
+
+    public void setGroupCode(String groupCode) {
+        this.groupCode = groupCode;
+    }
+
+	public Boolean getHasAudio() {
+		return hasAudio;
+	}
+
+	public void setHasAudio(Boolean hasAudio) {
+		this.hasAudio = hasAudio;
+	}
+
+}
+

+ 30 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisPaperAndAnswer.java

@@ -0,0 +1,30 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午9:39:11
+ * @company QMTH
+ * @description ComputerTestPaper.java
+ */
+public class ThemisPaperAndAnswer {
+
+	private ThemisAnswer answer;
+	private ThemisPaper paper;
+
+	public ThemisAnswer getAnswer() {
+		return answer;
+	}
+
+	public void setAnswer(ThemisAnswer answer) {
+		this.answer = answer;
+	}
+
+	public ThemisPaper getPaper() {
+		return paper;
+	}
+
+	public void setPaper(ThemisPaper paper) {
+		this.paper = paper;
+	}
+
+}

+ 85 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisPaperDetail.java

@@ -0,0 +1,85 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
+
+import java.util.List;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午9:43:00
+ * @company QMTH
+ * @description ComputerTestPaperDetail.java
+ */
+public class ThemisPaperDetail {
+    /**
+     * 大题号
+     */
+    private Integer number;
+    /**
+     * 大题名称
+     */
+    private String name;
+    /**
+     * 大题总分
+     */
+    private Double totalScore;
+    /**
+     * 小题数量
+     */
+    private Integer questionCount;
+    /**
+     * 小题集合
+     */
+    private List<ThemisQuestion> questions;
+
+    public ThemisPaperDetail() {
+    }
+
+    public ThemisPaperDetail(PaperDetail paperDetail) {
+        this.number = paperDetail.getNumber();
+        this.name = paperDetail.getName();
+        this.totalScore = paperDetail.getScore();
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(Double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public Integer getQuestionCount() {
+        return questionCount;
+    }
+
+    public void setQuestionCount(Integer questionCount) {
+        this.questionCount = questionCount;
+    }
+
+    public List<ThemisQuestion> getQuestions() {
+        return questions;
+    }
+
+    public void setQuestions(List<ThemisQuestion> questions) {
+        this.questions = questions;
+    }
+
+}
+

+ 154 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisQuestion.java

@@ -0,0 +1,154 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午9:51:37
+ * @company QMTH
+ * @description ComputerTestQuestion.java
+ */
+public class ThemisQuestion {
+
+    private String id;
+    /**
+     * 小题号或套题内子题序号
+     */
+    private Integer number;
+
+    private Double score;// 小题分数
+    /**
+     * 1-单选,2-多选,3-判断,4-填空,5-问答,6-套题
+     */
+    private Long structType;
+    /**
+     * 是否客观题
+     */
+    private Boolean objective;
+    /**
+     * 子题
+     */
+    private List<ThemisQuestion> subQuestions;
+
+    /**
+     * 题干
+     */
+    private ThemisSections body;
+
+    /**
+     * 选项
+     */
+    private List<ThemisOption> options;
+
+    private Map<String, Object> param;
+
+
+    public ThemisQuestion() {
+    }
+
+    public ThemisQuestion(PaperDetailUnit paperDetailUnit) {
+        this.id = paperDetailUnit.getId();
+        this.number = paperDetailUnit.getNumber();
+        this.score = paperDetailUnit.getScore();
+        this.structType = paperDetailUnit.getQuestionType().getId();
+        if (paperDetailUnit.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION ||
+                paperDetailUnit.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION ||
+                paperDetailUnit.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+            this.objective = true;
+        } else {
+            this.objective = false;
+        }
+    }
+
+    public ThemisQuestion(Question question) {
+        this.id = question.getId();
+        this.score = question.getScore();
+        this.structType = question.getQuestionType().getId();
+        if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION ||
+                question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION ||
+                question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+            this.objective = true;
+        } else {
+            this.objective = false;
+        }
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public Double getScore() {
+        return score;
+    }
+
+    public void setScore(Double score) {
+        this.score = score;
+    }
+
+    public Long getStructType() {
+        return structType;
+    }
+
+    public void setStructType(Long structType) {
+        this.structType = structType;
+    }
+
+    public Boolean getObjective() {
+        return objective;
+    }
+
+    public void setObjective(Boolean objective) {
+        this.objective = objective;
+    }
+
+    public List<ThemisQuestion> getSubQuestions() {
+        return subQuestions;
+    }
+
+    public void setSubQuestions(List<ThemisQuestion> subQuestions) {
+        this.subQuestions = subQuestions;
+    }
+
+    public ThemisSections getBody() {
+        return body;
+    }
+
+    public void setBody(ThemisSections body) {
+        this.body = body;
+    }
+
+    public List<ThemisOption> getOptions() {
+        return options;
+    }
+
+    public void setOptions(List<ThemisOption> options) {
+        this.options = options;
+    }
+
+    public Map<String, Object> getParam() {
+        return param;
+    }
+
+    public void setParam(Map<String, Object> param) {
+        this.param = param;
+    }
+
+}
+

+ 25 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisSection.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+import java.util.List;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午10:19:03
+ * @company QMTH
+ * @description Section.java
+ */
+public class ThemisSection {
+
+    private List<ThemisBlock> blocks;
+
+    public List<ThemisBlock> getBlocks() {
+        return blocks;
+    }
+
+    public void setBlocks(List<ThemisBlock> blocks) {
+        this.blocks = blocks;
+    }
+
+
+}
+

+ 25 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisSections.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+import java.util.List;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午10:13:10
+ * @company QMTH
+ * @description Sections.java
+ */
+public class ThemisSections {
+
+    private List<ThemisSection> sections;
+
+    public List<ThemisSection> getSections() {
+        return sections;
+    }
+
+    public void setSections(List<ThemisSection> sections) {
+        this.sections = sections;
+    }
+
+
+}
+

+ 35 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisSubjectiveAnswer.java

@@ -0,0 +1,35 @@
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+import java.util.List;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午10:13:10
+ * @company QMTH
+ * @description Sections.java
+ */
+public class ThemisSubjectiveAnswer {
+	
+	private Integer index;
+
+    private List<ThemisSection> sections;
+
+    public List<ThemisSection> getSections() {
+        return sections;
+    }
+
+    public void setSections(List<ThemisSection> sections) {
+        this.sections = sections;
+    }
+
+	public Integer getIndex() {
+		return index;
+	}
+
+	public void setIndex(Integer index) {
+		this.index = index;
+	}
+
+
+}
+

+ 23 - 9
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/util/ExportPaperUtil.java

@@ -6,6 +6,8 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -27,6 +29,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import cn.com.qmth.examcloud.commons.exception.ExamCloudRuntimeException;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.base.FileDisposeUtil;
 import cn.com.qmth.examcloud.core.questions.base.dto.ExportTempDataDto;
 import cn.com.qmth.examcloud.core.questions.base.enums.ExamFileType;
@@ -41,6 +44,7 @@ import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
 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.support.filestorage.FileStorageUtil;
 import freemarker.template.Configuration;
 import freemarker.template.Template;
 import sun.misc.BASE64Decoder;
@@ -474,13 +478,15 @@ public class ExportPaperUtil {
         for (SectionElement se : dto.getImages()) {
             File file = new File(docxTargetDir.getAbsolutePath() + "/word/media/image" + se.getParam().getIndex() + "."
                     + se.getParam().getType());
-            String base64 = se.getValue();
-            if (base64.contains("data:image")) {
-                base64 = base64.substring(base64.indexOf(",") + 1);
+            String src = se.getValue();
+            if (src.contains("data:image")) {//base64图片
+            	src = src.substring(src.indexOf(",") + 1);
+                BASE64Decoder decoder = new BASE64Decoder();
+                byte[] bytes = decoder.decodeBuffer(src);
+                FileUtils.writeByteArrayToFile(file, bytes);
+            }else {//网络图片
+            	FileStorageUtil.saveUrlAs(src, file);
             }
-            BASE64Decoder decoder = new BASE64Decoder();
-            byte[] bytes = decoder.decodeBuffer(base64);
-            FileUtils.writeByteArrayToFile(file, bytes);
         }
     }
 
@@ -753,9 +759,17 @@ public class ExportPaperUtil {
             }
         }
     }
-
-    private static String getImageType(String base64) {
-        return base64.substring(11, base64.indexOf(";"));
+    private static String getImageType(String src) {
+    	if(src.contains("data:image")) {
+    		return src.substring(11, src.indexOf(";"));
+    	}else {
+    		try {
+				URL url=new URL(src);
+				return url.getPath().substring(url.getPath().lastIndexOf(".")+1);
+			} catch (MalformedURLException e) {
+				throw new StatusException("1000", "图片链接格式错误:"+src);
+			}
+    	}
     }
     private static String getSectionDoc(List<JSection> sections) {
         Map<String, Object> map = new HashMap<String, Object>();

+ 2 - 0
examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/CoreQuestionApp.java

@@ -11,6 +11,7 @@ import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
 import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.data.mongodb.config.EnableMongoAuditing;
 import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
 import org.springframework.transaction.annotation.EnableTransactionManagement;
 import org.springframework.web.multipart.MultipartResolver;
@@ -23,6 +24,7 @@ import cn.com.qmth.examcloud.web.support.SpringContextHolder;
 @SpringBootApplication
 @Configuration
 @EnableJpaAuditing
+@EnableMongoAuditing
 @EnableTransactionManagement
 @EnableEurekaClient
 @EnableDiscoveryClient

+ 23 - 0
examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/config/UserIDAuditorBean.java

@@ -0,0 +1,23 @@
+package cn.com.qmth.examcloud.core.questions.starter.config;
+
+import java.util.Optional;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.domain.AuditorAware;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.web.enums.HttpServletRequestAttribute;
+import cn.com.qmth.examcloud.web.support.ServletUtil;
+
+@Configuration
+public class UserIDAuditorBean implements AuditorAware<Long> {
+    @Override
+    public Optional<Long> getCurrentAuditor() {
+    	User accessUser = (User) ServletUtil.getRequest()
+                .getAttribute(HttpServletRequestAttribute.$_ACCESS_USER.name());
+        if (null == accessUser) {
+        	return Optional.of(-999L);
+        }
+        return Optional.of(accessUser.getUserId());
+    }
+}

+ 1 - 1
examcloud-core-questions-starter/src/main/resources/application.properties

@@ -1,6 +1,6 @@
 spring.profiles.active=dev
 
 examcloud.startup.startupCode=8008
-examcloud.startup.configCenterHost=127.0.0.1
+examcloud.startup.configCenterHost=192.168.10.39
 examcloud.startup.configCenterPort=9999
 examcloud.startup.appCode=Q

+ 76 - 56
examcloud-core-questions-starter/src/main/resources/log4j2.xml

@@ -1,66 +1,86 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Configuration status="WARN" monitorInterval="30">
 
-	<Properties>
-		<Property name="commonLevel" value="${sys:log.commonLevel}" />
-	</Properties>
+    <Properties>
+        <Property name="commonLevel" value="${sys:log.commonLevel}"/>
+        <Property name="logPattern">
+            %d{yyyy-MM-dd HH:mm:ss.SSS} | %clr{%level} | %X{TRACE_ID} %X{CALLER} | %clr{%c{1.1}:%L}{cyan} | %m%n
+        </Property>
+    </Properties>
 
-	<Appenders>
-		<!-- 控制台 日志 -->
-		<Console name="Console" target="SYSTEM_OUT">
-			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m | %l%n" />
-		</Console>
-		<!-- debug 日志 -->
-		<RollingFile name="DEBUG_APPENDER" fileName="./logs/debug/debug.log" filePattern="./logs/debug/debug-%d{yyyy.MM.dd.HH}-%i.log">
-			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m | %l%n" />
-			<Policies>
-				<TimeBasedTriggeringPolicy interval="1" />
-				<SizeBasedTriggeringPolicy size="100 MB" />
-			</Policies>
-			<DefaultRolloverStrategy max="10000">
-				<Delete basePath="./logs/debug" maxDepth="1">
-					<IfFileName glob="debug-*.log">
-						<IfAccumulatedFileSize exceeds="2 GB" />
-					</IfFileName>
-				</Delete>
-			</DefaultRolloverStrategy>
-		</RollingFile>
-		<!-- 接口日志 -->
-		<RollingFile name="INTERFACE_APPENDER" fileName="./logs/interface/interface.log" filePattern="./logs/interface/interface-%d{yyyy.MM.dd.HH}-%i.log">
-			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m%n" />
-			<Policies>
-				<TimeBasedTriggeringPolicy interval="1" />
-				<SizeBasedTriggeringPolicy size="100 MB" />
-			</Policies>
-			<DefaultRolloverStrategy max="10000">
-				<Delete basePath="./logs/interface" maxDepth="1">
-					<IfFileName glob="interface-*.log">
-						<IfAccumulatedFileSize exceeds="10 GB" />
-					</IfFileName>
-				</Delete>
-			</DefaultRolloverStrategy>
-		</RollingFile>
-	</Appenders>
+    <Appenders>
+        <!-- 控制台 日志 -->
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="${logPattern}" charset="UTF-8"/>
+        </Console>
 
-	<Loggers>
-		<Logger name="cn.com.qmth" level="${commonLevel}" additivity="false">
-			<AppenderRef ref="DEBUG_APPENDER" />
-			<AppenderRef ref="Console" />
-		</Logger>
+        <!-- debug 日志 -->
+        <RollingFile name="DEBUG_APPENDER"
+                     fileName="./logs/debug/debug.log"
+                     filePattern="./logs/debug/debug-%d{yyyy.MM.dd.HH}-%i.log">
+            <PatternLayout pattern="${logPattern}" charset="UTF-8"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy interval="1" modulate="false"/>
+                <SizeBasedTriggeringPolicy size="100 MB"/>
+            </Policies>
+            <DefaultRolloverStrategy max="1000">
+                <Delete basePath="./logs/debug" maxDepth="1">
+                    <IfFileName glob="debug-*.log">
+                        <IfAccumulatedFileSize exceeds="2 GB"/>
+                    </IfFileName>
+                </Delete>
+            </DefaultRolloverStrategy>
+        </RollingFile>
 
-		<Logger name="INTERFACE_LOGGER" level="INFO" additivity="false">
-			<AppenderRef ref="INTERFACE_APPENDER" />
-			<AppenderRef ref="Console" />
-		</Logger>
+        <!-- 接口日志 -->
+        <RollingFile name="INTERFACE_APPENDER" fileName="./logs/interface/interface.log"
+                     filePattern="./logs/interface/interface-%d{yyyy.MM.dd.HH}-%i.log">
+            <PatternLayout pattern="${logPattern}" charset="UTF-8"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy interval="1" modulate="false"/>
+                <SizeBasedTriggeringPolicy size="100 MB"/>
+            </Policies>
+            <DefaultRolloverStrategy max="1000">
+                <Delete basePath="./logs/interface" maxDepth="1">
+                    <IfFileName glob="interface-*.log">
+                        <IfAccumulatedFileSize exceeds="10 GB"/>
+                    </IfFileName>
+                </Delete>
+            </DefaultRolloverStrategy>
+        </RollingFile>
+    </Appenders>
 
-		<Logger name="org.hibernate.SQL" level="${commonLevel}" />
+    <Loggers>
+        <logger name="springfox.documentation" level="ERROR"/>
+        <logger name="org.springframework" level="ERROR"/>
+        <logger name="org.hibernate" level="ERROR"/>
+        <logger name="org.apache" level="ERROR"/>
+        <logger name="org.quartz" level="ERROR"/>
+        <logger name="org.docx4j" level="ERROR"/>
+        <logger name="cn.afterturn" level="ERROR"/>
+        <logger name="com.netflix" level="ERROR"/>
+        <logger name="com.aliyun" level="ERROR"/>
+        <logger name="io.lettuce" level="ERROR"/>
+        <logger name="io.netty" level="ERROR"/>
 
-		<logger name="cn.com.qmth.examcloud.web.actuator" level="WARN" />
+        <!--<logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>-->
+        <!--<logger name="org.springframework.data.mongodb" level="DEBUG"/>-->
+        <!--<logger name="org.springframework.data.redis" level="DEBUG"/>-->
 
-		<Root level="INFO">
-			<AppenderRef ref="Console" />
-			<AppenderRef ref="DEBUG_APPENDER" />
-		</Root>
-	</Loggers>
+        <Logger name="cn.com.qmth" level="${commonLevel}" additivity="false">
+            <AppenderRef ref="DEBUG_APPENDER"/>
+            <AppenderRef ref="Console"/>
+        </Logger>
 
-</Configuration>
+        <Logger name="INTERFACE_LOGGER" level="${commonLevel}" additivity="false">
+            <AppenderRef ref="INTERFACE_APPENDER"/>
+            <AppenderRef ref="Console"/>
+        </Logger>
+
+        <Root level="${commonLevel}">
+            <AppenderRef ref="Console"/>
+            <AppenderRef ref="DEBUG_APPENDER"/>
+        </Root>
+    </Loggers>
+
+</Configuration>

BIN
examcloud-core-questions-starter/src/main/resources/templates/answerImportTemplate.xlsx


+ 0 - 19
jenkins-dev.sh

@@ -1,19 +0,0 @@
-#!/bin/bash
-pwd
-
-rm -rf ~/project/examcloud/examcloud-core-questions-distribution.zip
-rm -rf ~/project/examcloud/examcloud-core-questions/lib/
-rm -rf ~/project/examcloud/examcloud-core-questions/config/
-
-cp examcloud-core-questions-starter/target/examcloud-core-questions-distribution.zip ~/project/examcloud/
-
-cd  ~/project/examcloud/
-unzip -o examcloud-core-questions-distribution.zip
-
-cd examcloud-core-questions
-echo "--spring.profiles.active=dev --examcloud.startup.configCenterHost=localhost" > start.args
-echo "-server -Xms512m -Xmx512m  -XX:-UseGCOverheadLimit" > start.vmoptions
-
-bash stop.sh
-BUILD_ID=DONTKILLME
-bash start.sh jenkins

+ 0 - 18
jenkins-test.sh

@@ -1,18 +0,0 @@
-#!/bin/bash
-pwd
-
-rm -rf ~/project/examcloud/examcloud-core-questions-distribution.zip
-rm -rf ~/project/examcloud/examcloud-core-questions/lib/
-
-cp examcloud-core-questions-starter/target/examcloud-core-questions-distribution.zip ~/project/examcloud/
-
-cd  ~/project/examcloud/
-unzip -o examcloud-core-questions-distribution.zip
-
-cd examcloud-core-questions
-echo "--spring.profiles.active=test --examcloud.startup.configCenterHost=localhost" > start.args
-echo "-server -Xms512m -Xmx512m  -XX:-UseGCOverheadLimit" > start.vmoptions
-
-bash stop.sh
-BUILD_ID=DONTKILLME
-bash start.sh jenkins