Forráskód Böngészése

Merge remote-tracking branch 'remotes/origin/release_v4.1.0'

deason 3 éve
szülő
commit
7c926faa2b
77 módosított fájl, 4064 hozzáadás és 2814 törlés
  1. 41 10
      .gitignore
  2. 4 4
      examcloud-core-questions-api-provider/pom.xml
  3. 61 15
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/CoursePropertyController.java
  4. 1 1
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/ExportPaperController.java
  5. 19 15
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/ExtractConfigController.java
  6. 11 4
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/ImportPaperController.java
  7. 207 130
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/PaperController.java
  8. 68 0
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/PaperStorageController.java
  9. 17 6
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/PaperStructController.java
  10. 6 1
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/QuesController.java
  11. 4 4
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/provider/ExtractConfigCloudServiceProvider.java
  12. 5 5
      examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/provider/HandleSyncCloudServiceProvider.java
  13. 26 15
      examcloud-core-questions-base/pom.xml
  14. 14 14
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/FileDisposeUtil.java
  15. 4 4
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/Cryptogram.java
  16. 1 1
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/FileUtil.java
  17. 7 6
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/excel/ExcelWriter.java
  18. 3 3
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/excel/ImportUtils.java
  19. 7 7
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/word/DocxProcessUtil.java
  20. 4 4
      examcloud-core-questions-base/src/main/resources/export_template/origin_paper/document.ftl
  21. 17 16
      examcloud-core-questions-dao/pom.xml
  22. 7 3
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/CoursePropertyRepo.java
  23. 164 164
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/PaperDetailUnitNativeRepo.java
  24. 4 2
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/PropertyRepo.java
  25. 27 0
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/Paper.java
  26. 12 1
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/PaperSearchInfo.java
  27. 11 0
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/PaperStruct.java
  28. 13 0
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/Property.java
  29. 2 0
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/dto/PropertyConvert.java
  30. 11 0
      examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/dto/PropertyDto.java
  31. 17 16
      examcloud-core-questions-service/pom.xml
  32. 1 1
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ClonePaperService.java
  33. 18 6
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/CoursePropertyService.java
  34. 4 2
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ExtractConfigService.java
  35. 1 1
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ImportDdCollegePaperService.java
  36. 1 1
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ImportPaperFromJsonService.java
  37. 50 57
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ImportPaperService.java
  38. 23 20
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/PaperService.java
  39. 83 30
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/PaperStructService.java
  40. 5 2
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/QuesService.java
  41. 1 1
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/UpYunService.java
  42. 98 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/CoursePropertyImportInfo.java
  43. 20 1
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/dto/PaperDetailUnitExp.java
  44. 1 1
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/converter/PrintExamPaperService.java
  45. 3 1
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/export/ExportPaperAbstractService.java
  46. 461 36
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/CoursePropertyServiceImpl.java
  47. 10 10
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExportPaperServiceImpl.java
  48. 1 1
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExportStructureServiceImpl.java
  49. 4 4
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExportTemplateServiceImpl.java
  50. 7 7
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExportThemisPaperServiceImpl.java
  51. 477 477
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExtractConfigFileServiceImpl.java
  52. 14 14
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExtractConfigProviderServiceImpl.java
  53. 1229 1118
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExtractConfigServiceImpl.java
  54. 16 16
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/GenPaperService.java
  55. 6 0
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PaperDetailServiceImpl.java
  56. 49 2
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PaperDetailUnitServiceImpl.java
  57. 4 4
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PaperProviderServiceImpl.java
  58. 448 292
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PaperServiceImpl.java
  59. 8 5
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PropertyServiceImpl.java
  60. 23 7
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/QuesServiceImpl.java
  61. 1 1
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/QuestionProviderServiceImpl.java
  62. 4 2
      examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/temp/CqdxService.java
  63. 66 61
      examcloud-core-questions-starter/pom.xml
  64. 1 1
      examcloud-core-questions-starter/shell/start.args
  65. 8 24
      examcloud-core-questions-starter/shell/start.sh
  66. 4 14
      examcloud-core-questions-starter/shell/stop.sh
  67. 12 21
      examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/CoreQuestionApp.java
  68. 5 6
      examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/config/ExamCloudResourceManager.java
  69. 39 39
      examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/config/NativeMongoConfig.java
  70. 2 2
      examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/config/Swagger2.java
  71. 1 1
      examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/config/initData.java
  72. 5 5
      examcloud-core-questions-starter/src/main/resources/application.properties
  73. 0 1
      examcloud-core-questions-starter/src/main/resources/classpath.location
  74. 32 50
      examcloud-core-questions-starter/src/main/resources/log4j2.xml
  75. BIN
      examcloud-core-questions-starter/src/main/resources/templates/coursePropertyImportTemplate.xlsx
  76. 4 0
      jenkins.sh
  77. 19 18
      pom.xml

+ 41 - 10
.gitignore

@@ -1,19 +1,50 @@
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
 *.class
+*.log
 
-# Proguard folder generated by ide
-.project
+
+### Eclipse & STS ###
+.apt_generated
 .classpath
+.factorypath
+.project
 .settings
-target/
-.idea/
-*.iml
+.springBeans
+.sts4-cache
+
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
 
-# Log Files
-*.log
-*.class
 
+### VS Code ###
+.vscode
+node_modules
+package-lock.json
+yarn.lock
 
-# Package Files #
-*.jar
+
+### Package Files ###
+*.zip
 *.war
 *.ear
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+target/
+
+.flattened-pom.xml
+.DS_Store
+

+ 4 - 4
examcloud-core-questions-api-provider/pom.xml

@@ -6,16 +6,16 @@
     <packaging>jar</packaging>
 
     <parent>
+        <groupId>cn.com.qmth.examcloud</groupId>
         <artifactId>examcloud-core-questions</artifactId>
-        <groupId>cn.com.qmth.examcloud.core.questions</groupId>
-        <version>v4.0.2-RELEASE</version>
+        <version>${revision}</version>
     </parent>
 
     <dependencies>
         <dependency>
-            <groupId>cn.com.qmth.examcloud.core.questions</groupId>
+            <groupId>cn.com.qmth.examcloud</groupId>
             <artifactId>examcloud-core-questions-service</artifactId>
-            <version>${examcloud.version}</version>
+            <version>${project.version}</version>
         </dependency>
     </dependencies>
 

+ 61 - 15
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/CoursePropertyController.java

@@ -1,17 +1,20 @@
 package cn.com.qmth.examcloud.core.questions.api.controller;
 
+import java.io.File;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotNull;
+
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.domain.Sort.Direction;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -19,13 +22,26 @@ 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.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.google.common.collect.Maps;
 
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.util.PathUtil;
 import cn.com.qmth.examcloud.core.questions.base.Constants;
+import cn.com.qmth.examcloud.core.questions.base.excel.ExportUtils;
 import cn.com.qmth.examcloud.core.questions.dao.entity.CourseProperty;
 import cn.com.qmth.examcloud.core.questions.dao.entity.dto.CoursePropertyDto;
 import cn.com.qmth.examcloud.core.questions.service.CoursePropertyService;
+import cn.com.qmth.examcloud.core.questions.service.bean.CoursePropertyImportInfo;
+import cn.com.qmth.examcloud.web.security.DataRule;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import io.swagger.annotations.ApiOperation;
 
@@ -34,38 +50,36 @@ import io.swagger.annotations.ApiOperation;
  * @describle 课程属性
  * @date 2017.11.2
  */
-@Controller
+@RestController
 @RequestMapping("${api_cqb}/")
 public class CoursePropertyController extends ControllerSupport {
 
     @Autowired
     private CoursePropertyService coursePropertyService;
 
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "根据orgId查询所有课程属性")
     @GetMapping(value = "/courseProperty/all")
     public ResponseEntity<Object> findAllByOrg() {
         User user = getAccessUser();
-        if (user == null) {
-            throw new StatusException(Constants.SYS_CODE_500, "请先登录!");
-        }
+        UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
 
-        List<CourseProperty> courseProperties = coursePropertyService.findAllByOrgId(user.getRootOrgId());
+        List<CourseProperty> courseProperties = coursePropertyService.findAllByOrgId(user.getRootOrgId(),ud);
         return new ResponseEntity<>(courseProperties, HttpStatus.OK);
     }
 
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "根据orgId查询所有课程属性(分页)")
     @GetMapping(value = "/courseProperty/all/{curPage}/{pageSize}")
     public ResponseEntity<Object> findAllByOrgId(@ModelAttribute CoursePropertyDto coursePropertyDto,
-            @PathVariable Integer curPage, @PathVariable Integer pageSize) {
+                                                 @PathVariable Integer curPage, @PathVariable Integer pageSize) {
         User user = getAccessUser();
-        if (user == null) {
-            throw new StatusException(Constants.SYS_CODE_500, "请先登录!");
-        }
+        UserDataRule userDataRule = super.getUserDataRule(DataRuleType.COURSE);
 
         coursePropertyDto.setOrgId(user.getRootOrgId());
 
-        PageRequest pageRequest = PageRequest.of(curPage - 1, pageSize, new Sort(Direction.DESC, "updateTime"));
-        Page<CourseProperty> coursePropertiesPage = coursePropertyService.findList(coursePropertyDto, pageRequest);
+        PageRequest pageable = PageRequest.of(curPage - 1, pageSize);
+        Page<CourseProperty> coursePropertiesPage = coursePropertyService.findList(coursePropertyDto, pageable, userDataRule);
 
         return new ResponseEntity<>(coursePropertiesPage, HttpStatus.OK);
     }
@@ -160,16 +174,48 @@ public class CoursePropertyController extends ControllerSupport {
         return new ResponseEntity<>(courseProperties, HttpStatus.OK);
     }
 
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "根据courseCode查询所有开启的课程属性")
     @GetMapping(value = "/courseProperty/enable/{courseCode}")
     public ResponseEntity<Object> findAllEnable(@PathVariable String courseCode) {
         User user = getAccessUser();
+        UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
         if (user == null) {
             throw new StatusException(Constants.SYS_CODE_500, "请先登录!");
         }
 
-        List<CourseProperty> courseProperties = coursePropertyService.findByEnable(courseCode, true);
+        List<CourseProperty> courseProperties = coursePropertyService.findByEnable(courseCode, true,user,ud);
         return new ResponseEntity<>(courseProperties, HttpStatus.OK);
     }
+    
+    @ApiOperation(value = "下载导入模板", notes = "下载导入模板")
+	@GetMapping("/courseProperty/importTemplate")
+	public void getImportTemplate(HttpServletResponse response) {
+		String resoucePath = PathUtil.getResoucePath("templates/coursePropertyImportTemplate.xlsx");
+		exportFile("课程属性导入模板.xlsx", new File(resoucePath));
+	}
+
+	@DataRule(type = DataRuleType.COURSE)
+	@ApiOperation(value = "导入课程属性", notes = "导入")
+	@PostMapping("/courseProperty/import")
+	public Map<String, Object> importCourseProperty(@RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile file) {
+		User user = getAccessUser();
+		UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
+		List<Map<String, Object>> failRecords = coursePropertyService.importCourseProperty(ud, user, getRootOrgId(), file);
+		Map<String, Object> map = Maps.newHashMap();
+		map.put("hasError", CollectionUtils.isNotEmpty(failRecords));
+		map.put("failRecords", failRecords);
+		return map;
+	}
+	
+	@DataRule(type = DataRuleType.COURSE)
+    @ApiOperation(value = "导出课程属性")
+    @RequestMapping(value = "/courseProperty/export", method = RequestMethod.GET)
+    public void exportCourseProperty(@RequestParam(required = false) String name,@RequestParam(required = false) Long courseId, HttpServletResponse response) {
+        User user = getAccessUser();
+        UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
+        List<CoursePropertyImportInfo> dtos = coursePropertyService.exportCourseProperty(ud, user, name,courseId);
+        ExportUtils.exportEXCEL("课程属性", CoursePropertyImportInfo.class, dtos, response);
+    }
 
 }

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

@@ -33,7 +33,7 @@ import io.swagger.annotations.ApiOperation;
 @RequestMapping("${api_cqb}/")
 public class ExportPaperController extends ControllerSupport {
 
-    protected static final Logger log = LoggerFactory.getLogger(ExportPaperController.class);
+    private static final Logger log = LoggerFactory.getLogger(ExportPaperController.class);
 
     @Autowired
     ExportServiceManageRepo exportServiceManageRepo;

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

@@ -23,7 +23,9 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.base.enums.ExportWay;
 import cn.com.qmth.examcloud.core.questions.base.enums.PaperSeqMode;
@@ -38,6 +40,7 @@ import cn.com.qmth.examcloud.core.questions.service.bean.dto.ExportPaperInfoMode
 import cn.com.qmth.examcloud.core.questions.service.cache.ExtractConfigCache;
 import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
 import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
+import cn.com.qmth.examcloud.web.security.DataRule;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import cn.com.qmth.examcloud.web.support.Naked;
 import io.swagger.annotations.ApiOperation;
@@ -53,7 +56,7 @@ import io.swagger.annotations.ApiOperation;
 @Controller
 @RequestMapping("${api_cqb}/")
 public class ExtractConfigController extends ControllerSupport {
-    private static final Logger logger = LoggerFactory.getLogger(ExtractConfigController.class);
+    private static final Logger LOG = LoggerFactory.getLogger(ExtractConfigController.class);
 
     @Autowired
     private ExtractConfigService extractConfigService;
@@ -64,6 +67,7 @@ public class ExtractConfigController extends ControllerSupport {
     @Autowired
     private ExtractConfigCache extractConfigCache;
 
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "根据考试ID和课程ID获取调卷规则", notes = "根据考试ID和课程ID获取调卷规则")
     @GetMapping(value = "/findPageExtractConfig/{currentPage}/{pageSize}")
     public ResponseEntity<Object> findPageExtractConfig(@PathVariable int currentPage,
@@ -72,10 +76,11 @@ public class ExtractConfigController extends ControllerSupport {
                                                         @RequestParam("courseNo") String courseNo) {
         try {
             User user = getAccessUser();
-            Page<ExtractConfig> extractConfigPageList = extractConfigService.findPageExtractConfig(currentPage, pageSize, examId, courseNo, String.valueOf(user.getRootOrgId()));
+            UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
+            Page<ExtractConfig> extractConfigPageList = extractConfigService.findPageExtractConfig(currentPage, pageSize, examId, courseNo, String.valueOf(user.getRootOrgId()),ud);
             return new ResponseEntity<>(extractConfigPageList, HttpStatus.OK);
         } catch (Exception e) {
-            log.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
             throw new StatusException("Q-050065", e.getMessage());
         }
     }
@@ -162,14 +167,13 @@ public class ExtractConfigController extends ControllerSupport {
         return new ResponseEntity<ExtractConfig>(extractConfig, HttpStatus.OK);
     }
 
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "根据考试Id获取已经制定的调卷规则", notes = "根据考试Id获取已经制定的调卷规则")
     @GetMapping(value = "/findCourseByExtractConfig/{examId}")
     public ResponseEntity<Object> findCourseByExtractConfig(@PathVariable Long examId) {
         User user = getAccessUser();
-        if (user == null) {
-            throw new StatusException("Q-", "user is null");
-        }
-        List<CouresInfo> courseCodeList = extractConfigService.findCourseByExtractConfig(examId, user.getRootOrgId().toString());
+        UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
+        List<CouresInfo> courseCodeList = extractConfigService.findCourseByExtractConfig(examId, user.getRootOrgId().toString(),ud);
         return new ResponseEntity<>(courseCodeList, HttpStatus.OK);
     }
 
@@ -196,7 +200,7 @@ public class ExtractConfigController extends ControllerSupport {
                 return new ResponseEntity<>(returnMap.get("errorMsg") + "", HttpStatus.OK);
             }
         } catch (Exception e) {
-            logger.error("抽卷失败", e);
+            LOG.error("抽卷失败", e);
             return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
         }
     }
@@ -212,7 +216,7 @@ public class ExtractConfigController extends ControllerSupport {
             QuestionDto questionDto = extractConfigService.extractExamQuestion(examId, courseCode, groupCode, paperDetailUnitId);
             return new ResponseEntity<>(questionDto, HttpStatus.OK);
         } catch (Exception e) {
-            logger.error("抽题失败", e);
+            LOG.error("抽题失败", e);
             return new ResponseEntity<>("抽题失败:" + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
         }
 
@@ -234,7 +238,7 @@ public class ExtractConfigController extends ControllerSupport {
             quesMap.put("message", result ? "全为客观题" : "不全为客观题");
             return new ResponseEntity<>(quesMap, HttpStatus.OK);
         } catch (Exception e) {
-            logger.error("调用checkIsAllQbjectiveQuestion失败", e);
+            LOG.error("调用checkIsAllQbjectiveQuestion失败", e);
             quesMap.put("result", "error");
             quesMap.put("message", "调用失败");
             return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
@@ -265,7 +269,7 @@ public class ExtractConfigController extends ControllerSupport {
         try {
             extractConfigFileService.exportExamPaperInfo(exportModel, response, loginName, orgId, psw);
         } catch (Exception e) {
-            log.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         }
     }
 
@@ -281,7 +285,7 @@ public class ExtractConfigController extends ControllerSupport {
             extractConfigFileService.exportExamPaperInfoCheck(exportModel, response);
             return new ResponseEntity<>(HttpStatus.OK);
         } catch (Exception e) {
-            log.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
             throw new StatusException("Q-050200", e.getMessage());
         }
     }
@@ -307,7 +311,7 @@ public class ExtractConfigController extends ControllerSupport {
         try {
             extractConfigFileService.exportExamPaperInfo(exportModel, response, loginName, orgId, null);
         } catch (Exception e) {
-            log.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         }
     }
 
@@ -324,7 +328,7 @@ public class ExtractConfigController extends ControllerSupport {
                 return new ResponseEntity<>(returnMap.get("errorMsg") + "", HttpStatus.OK);
             }
         } catch (Exception e) {
-            logger.error("抽卷失败", e);
+            LOG.error("抽卷失败", e);
             return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
         }
     }
@@ -337,7 +341,7 @@ public class ExtractConfigController extends ControllerSupport {
             PaperDto paperDto = extractConfigService.getPaperDtoByPaperNew(paperId);
             return new ResponseEntity<>(paperDto, HttpStatus.OK);
         } catch (Exception e) {
-            logger.error("获取试卷Dto失败", e);
+            LOG.error("获取试卷Dto失败", e);
             return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
         }
     }

+ 11 - 4
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/ImportPaperController.java

@@ -21,7 +21,9 @@ import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.commons.CommonsMultipartFile;
 
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.base.Model;
 import cn.com.qmth.examcloud.core.questions.base.exception.PaperException;
@@ -35,6 +37,7 @@ import cn.com.qmth.examcloud.core.questions.service.impl.CourseService;
 import cn.com.qmth.examcloud.core.questions.service.temp.CqdxService;
 import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
 import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
+import cn.com.qmth.examcloud.web.security.DataRule;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import io.swagger.annotations.ApiOperation;
 
@@ -48,7 +51,8 @@ import io.swagger.annotations.ApiOperation;
 @RestController
 @RequestMapping("${api_cqb}/")
 public class ImportPaperController extends ControllerSupport {
-	protected static final Logger LOGGER = LoggerFactory.getLogger(ImportPaperController.class);
+	private static final Logger LOG = LoggerFactory.getLogger(ImportPaperController.class);
+
 	@Autowired
 	private ClonePaperService clonePaperService;
 	@Autowired
@@ -69,16 +73,18 @@ public class ImportPaperController extends ControllerSupport {
 	 * @param file
 	 * @return
 	 */
+    @DataRule(type = DataRuleType.COURSE)
 	@ApiOperation(value = "导入试卷", notes = "导入试卷")
 	@PostMapping(value = "/importPaper")
 	public ResponseEntity<Object> importPaper(@ModelAttribute Paper paper,
 			@RequestParam("file") CommonsMultipartFile file) {
 		User user = getAccessUser();
-		LOGGER.info("导入开始");
+		UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
+		LOG.info("导入开始");
 		File tempFile = null;
 		try {
 			tempFile = importPaperService.getUploadFile(file);
-			Paper newPaper = importPaperService.importPaper(paper, user, tempFile);
+			Paper newPaper = importPaperService.importPaper(paper, user, tempFile,ud);
 			StringBuilder sb=new StringBuilder();
 			sb.append("课程:"+paper.getCourse().getName()+"("+paper.getCourse().getCode()+")");
 			sb.append(" 试卷名称:"+paper.getName());
@@ -87,7 +93,8 @@ public class ImportPaperController extends ControllerSupport {
 		} catch (StatusException e) {
 			throw e;
 		} catch (Exception e) {
-			throw new StatusException("500", e.getMessage());
+			LOG.error(e.getMessage(), e);
+			throw new StatusException("500", e.getMessage(),e);
 		}
 	}
 

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

@@ -1,47 +1,8 @@
 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.enums.DataRuleType;
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.commons.util.PathUtil;
 import cn.com.qmth.examcloud.core.basic.api.OrgCloudService;
@@ -52,8 +13,10 @@ 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.PaperDetailUnitRepo;
 import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
 import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
+import cn.com.qmth.examcloud.core.questions.dao.entity.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.PaperService;
@@ -70,11 +33,36 @@ import cn.com.qmth.examcloud.core.questions.service.util.PaperUtil;
 import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
 import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
 import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.web.security.DataRule;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import cn.com.qmth.examcloud.web.support.Naked;
+import com.google.gson.Gson;
 import freemarker.template.TemplateException;
 import io.swagger.annotations.ApiOperation;
 
+import org.apache.commons.collections4.CollectionUtils;
+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 javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotNull;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
 /**
  * Created by songyue on 16/12/28.
  * updated by weiwenhai on 2018.9.28
@@ -84,7 +72,9 @@ import io.swagger.annotations.ApiOperation;
 @Controller
 @RequestMapping("${api_cqb}/")
 public class PaperController extends ControllerSupport {
-    private final static String BASE_PAGE="base_page";
+
+    private final static String BASE_PAGE = "base_page";
+
     @Autowired
     PaperService paperService;
 
@@ -102,10 +92,12 @@ public class PaperController extends ControllerSupport {
 
     @Autowired
     ExportServiceManageRepo exportServiceManageRepo;
-    
+
     @Autowired
     OrgCloudService orgCloudService;
 
+    @Autowired
+    PaperDetailUnitRepo paperDetailUnitRepo;
     /**
      * 根据Id获取试卷
      *
@@ -131,37 +123,37 @@ public class PaperController extends ControllerSupport {
     @PutMapping(value = "/paper")
     public ResponseEntity<Object> savePaperById(@RequestBody PaperExp paper) {
         User user = getAccessUser();
-        String changeInfo=getChangeInfo(paper);
+        String changeInfo = getChangeInfo(paper);
         Map<String, Object> msgMap = paperService.savePaper(paper, user);
         if ("success".equals(msgMap.get("msg"))) {
-        	if(changeInfo!=null) {
-        		if(PaperType.IMPORT.equals(paper.getPaperType())) {
-        			ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), "导入试卷管理-编辑试卷",changeInfo));
-        		}else {
-        			ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), "考试试卷管理-编辑试卷",changeInfo));
-        		}
-        	}
+            if (changeInfo != null) {
+                if (PaperType.IMPORT.equals(paper.getPaperType())) {
+                    ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), "导入试卷管理-编辑试卷", changeInfo));
+                } else {
+                    ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), "考试试卷管理-编辑试卷", changeInfo));
+                }
+            }
             return new ResponseEntity<>(msgMap, HttpStatus.OK);
         } else {
-            throw new StatusException("1000",msgMap.get("msg").toString());
+            throw new StatusException("1000", msgMap.get("msg").toString());
         }
     }
-    
+
     private String getChangeInfo(PaperExp paper) {
-    	Paper old = cn.com.qmth.examcloud.core.questions.base.Model.of(paperRepo.findById(paper.getId()));
+        Paper old = cn.com.qmth.examcloud.core.questions.base.Model.of(paperRepo.findById(paper.getId()));
         if (old == null) {
-        	return null;
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        if (!old.getName().equals(paper.getName())) {
+            sb.append("试卷名称变动(" + old.getName() + "改为" + paper.getName() + ")");
         }
-        StringBuilder sb=new StringBuilder();
-        if(!old.getName().equals(paper.getName())) {
-        	sb.append("试卷名称变动("+old.getName()+"改为"+paper.getName()+")");
+
+        if (sb.length() != 0) {
+            return sb.toString();
         }
-        
-    	if(sb.length()!=0) {
-    		return sb.toString();
-    	}
-    	return null;
-        
+        return null;
+
     }
 
     /**
@@ -172,6 +164,7 @@ public class PaperController extends ControllerSupport {
      * @param pageSize
      * @return
      */
+    @DataRule(type = DataRuleType.COURSE)
     @ResponseBody
     @ApiOperation(value = "查询所有导入试卷", notes = "查询所有导入试卷")
     @GetMapping(value = "/importPaper/{curPage}/{pageSize}")
@@ -179,8 +172,11 @@ public class PaperController extends ControllerSupport {
                                                   @PathVariable int curPage,
                                                   @PathVariable int pageSize) {
         User user = getAccessUser();
+        UserDataRule userDataRule = super.getUserDataRule(DataRuleType.COURSE);
+
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
-        return new ResponseEntity<>(paperService.getImportPapers(paperSearchInfo, curPage, pageSize), HttpStatus.OK);
+
+        return new ResponseEntity<>(paperService.getImportPapers(paperSearchInfo, curPage, pageSize, userDataRule), HttpStatus.OK);
     }
 
     @ResponseBody
@@ -219,14 +215,16 @@ public class PaperController extends ControllerSupport {
      * @param pageSize
      * @return
      */
+    @DataRule(type = DataRuleType.COURSE)
     @ResponseBody
     @ApiOperation(value = "查询所有已组试卷", notes = "查询所有已组试卷")
     @GetMapping(value = "/genPaper/{curPage}/{pageSize}")
     public ResponseEntity<Object> getGenPapers(@ModelAttribute PaperSearchInfo paperSearchInfo,
                                                @PathVariable int curPage, @PathVariable int pageSize) {
         User user = getAccessUser();
+        UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
-        return new ResponseEntity<>(paperService.getGenPapers(paperSearchInfo, curPage, pageSize), HttpStatus.OK);
+        return new ResponseEntity<>(paperService.getGenPapers(paperSearchInfo, curPage, pageSize,ud), HttpStatus.OK);
     }
 
     /**
@@ -236,24 +234,30 @@ public class PaperController extends ControllerSupport {
      * @param pageSize
      * @return
      */
+    @DataRule(type = DataRuleType.COURSE)
     @ResponseBody
     @ApiOperation(value = "查询所有导入试卷(not in paperIds)", notes = "查询所有导入试卷(not in paperIds)")
-    @GetMapping(value = "/genPaper/{ids}/{curPage}/{pageSize}")
+    @GetMapping(value = "/genPaper/huoge/{curPage}/{pageSize}")
     public ResponseEntity<Object> getGenPapersNotInIds(@ModelAttribute PaperSearchInfo paperSearchInfo,
-                                                       @PathVariable String[] ids, @PathVariable int curPage, @PathVariable int pageSize) {
-        User user = getAccessUser();
+                                                       @RequestParam String ids, @PathVariable int curPage, @PathVariable int pageSize) {
+    	User user = getAccessUser();
+    	UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
-        Page<Paper> ret;
-        if (ids != null && ids.length > 0) {
-        	ret=paperService.getPapersNotInIds(paperSearchInfo, ids, curPage, pageSize, PaperType.GENERATE);
-        	if(ret.getContent().size()==0&&ret.getTotalElements()>0&&curPage>1) {
-        		ret=paperService.getPapersNotInIds(paperSearchInfo, ids, curPage-1, pageSize, PaperType.GENERATE);
-        	}
+        Page<Paper> ret=null;
+        if (StringUtils.isNotBlank(ids)) {
+        	String[] idArr=ids.split(",");
+            ret = paperService.getPapersNotInIds(paperSearchInfo, idArr, curPage, pageSize, PaperType.GENERATE,ud);
+            if (ret.getContent().size() == 0 && ret.getTotalElements() > 0 && curPage > 1) {
+                ret = paperService.getPapersNotInIds(paperSearchInfo, idArr, curPage - 1, pageSize, PaperType.GENERATE,ud);
+            }
         } else {
-        	ret=paperService.getGenPapers(paperSearchInfo, curPage, pageSize);
-        	if(ret.getContent().size()==0&&ret.getTotalElements()>0&&curPage>1) {
-        		ret=paperService.getGenPapers(paperSearchInfo, curPage-1, pageSize);
-        	}
+            ret = paperService.getGenPapers(paperSearchInfo, curPage, pageSize,ud);
+            if (ret.getContent().size() == 0 && ret.getTotalElements() > 0 && curPage > 1) {
+                ret = paperService.getGenPapers(paperSearchInfo, curPage - 1, pageSize,ud);
+            }
+        }
+        if(ret!=null&&CollectionUtils.isNotEmpty(ret.getContent())) {
+        	setPaperQuesCountAndScore(ret.getContent());
         }
         return new ResponseEntity<>(ret, HttpStatus.OK);
     }
@@ -267,16 +271,10 @@ public class PaperController extends ControllerSupport {
     @ResponseBody
     @ApiOperation(value = "删除试卷", notes = "删除试卷")
     @DeleteMapping(value = "/paper/{paperIds}")
-    public ResponseEntity<Object> delPaper(@PathVariable String paperIds) {
-    	User user = getAccessUser();
+    public void delPaper(@PathVariable String paperIds) {
+        User user = getAccessUser();
         List<String> paperList = Stream.of(paperIds.split(",")).collect(Collectors.toList());
-        Map<String, Object> msgMap = paperService.deletePapers(paperList,user);
-        if (msgMap.get("msg").equals("success")) {
-            return new ResponseEntity<>(msgMap, HttpStatus.OK);
-        } else {
-            throw new StatusException("1001","试卷["+msgMap.get("paperName")+"]中有试题被组卷使用,不能删除!");
-        }
-
+        paperService.deletePapers(paperList, user);
     }
 
     /**
@@ -416,10 +414,10 @@ public class PaperController extends ControllerSupport {
     @DeleteMapping(value = "/paper/deleteQuestion/{questionId}")
     public ResponseEntity<Object> deletePaperQuestion(@PathVariable String questionId) {
         User user = getAccessUser();
-        List<String> paperNames = paperService.deleteImportQuestionById(null,questionId, user);
+        List<String> paperNames = paperService.deleteImportQuestionById(null, questionId, user);
         return new ResponseEntity<>(paperNames, HttpStatus.OK);
     }
-    
+
     /**
      * 删除导入试卷中的试题
      *
@@ -429,9 +427,9 @@ public class PaperController extends ControllerSupport {
     @ResponseBody
     @ApiOperation(value = "删除导入试卷中的试题", notes = "删除导入试卷中的试题")
     @DeleteMapping(value = "/paper/deleteQuestion/{detailUnitId}/{questionId}")
-    public ResponseEntity<Object> deleteImportPaperQuestion(@PathVariable String detailUnitId,@PathVariable String questionId) {
+    public ResponseEntity<Object> deleteImportPaperQuestion(@PathVariable String detailUnitId, @PathVariable String questionId) {
         User user = getAccessUser();
-        List<String> paperNames = paperService.deleteImportQuestionById(detailUnitId,questionId, user);
+        List<String> paperNames = paperService.deleteImportQuestionById(detailUnitId, questionId, user);
         return new ResponseEntity<>(paperNames, HttpStatus.OK);
     }
 
@@ -537,6 +535,7 @@ public class PaperController extends ControllerSupport {
      * @param
      * @return
      */
+    @DataRule(type = DataRuleType.COURSE)
     @ResponseBody
     @ApiOperation(value = "查询用于选题的试题列表", notes = "查询用于选题的试题列表")
     @GetMapping(value = "/paper/listQuestion/{paperId}/{curPage}/{pageSize}")
@@ -546,6 +545,7 @@ public class PaperController extends ControllerSupport {
                                                         @RequestParam(name = "quesType") String quesType,
                                                         @RequestParam(name = "quesBody") String quesBody) {
         User user = getAccessUser();
+        UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
         if (user == null) {
             return new ResponseEntity<>(new PageImpl<Question>(new ArrayList<>()), HttpStatus.OK);
         }
@@ -553,7 +553,7 @@ public class PaperController extends ControllerSupport {
         if (StringUtils.isNotEmpty(quesType)) {
             quesStructType = QuesStructType.valueOf(quesType);
         }
-        Page<Question> questionPageList = paperService.listQuestionforSelect(paperId, curPage, pageSize, quesStructType, user, quesBody);
+        Page<Question> questionPageList = paperService.listQuestionforSelect(paperId, curPage, pageSize, quesStructType, user, quesBody,ud);
         return new ResponseEntity<>(questionPageList, HttpStatus.OK);
     }
 
@@ -583,27 +583,93 @@ public class PaperController extends ControllerSupport {
      * @param pageSize
      * @return
      */
+    @DataRule(type = DataRuleType.COURSE)
     @ResponseBody
     @ApiOperation(value = "查询所有导入试卷(not in paperIds)", notes = "查询所有导入试卷(not in paperIds)")
-    @GetMapping(value = "/importPaper/{ids}/{curPage}/{pageSize}")
-    public ResponseEntity<Object> getImportPapersNotInIds(@ModelAttribute PaperSearchInfo paperSearchInfo, @PathVariable String[] ids, @PathVariable int curPage,
+    @GetMapping(value = "/importPaper/huoge/{curPage}/{pageSize}")
+    public ResponseEntity<Object> getImportPapersNotInIds(@ModelAttribute PaperSearchInfo paperSearchInfo, @RequestParam String ids, @PathVariable int curPage,
                                                           @PathVariable int pageSize) {
-        User user = getAccessUser();
+    	User user = getAccessUser();
+    	UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
-        Page<Paper> ret;
-        if (ids != null && ids.length > 0) {
-        	ret= paperService.getPapersNotInIds(paperSearchInfo, ids, curPage, pageSize, PaperType.IMPORT);
-        	if(ret.getContent().size()==0&&ret.getTotalElements()>0&&curPage>1) {
-        		ret= paperService.getPapersNotInIds(paperSearchInfo, ids, curPage-1, pageSize, PaperType.IMPORT);
-        	}
+        Page<Paper> ret=null;
+        if (StringUtils.isNotBlank(ids)) {
+        	String[] idArr=ids.split(",");
+            ret = paperService.getPapersNotInIds(paperSearchInfo, idArr, curPage, pageSize, PaperType.IMPORT,ud);
+            if (ret.getContent().size() == 0 && ret.getTotalElements() > 0 && curPage > 1) {
+                ret = paperService.getPapersNotInIds(paperSearchInfo, idArr, curPage - 1, pageSize, PaperType.IMPORT,ud);
+            }
         } else {
-            ret= paperService.getImportPapers(paperSearchInfo, curPage, pageSize);
-            if(ret.getContent().size()==0&&ret.getTotalElements()>0&&curPage>1) {
-            	ret= paperService.getImportPapers(paperSearchInfo, curPage-1, pageSize);
+            ret = paperService.getImportPapers(paperSearchInfo, curPage, pageSize,ud);
+            if (ret.getContent().size() == 0 && ret.getTotalElements() > 0 && curPage > 1) {
+                ret = paperService.getImportPapers(paperSearchInfo, curPage - 1, pageSize,ud);
             }
         }
+        if(ret!=null&&CollectionUtils.isNotEmpty(ret.getContent())) {
+        	setPaperQuesCountAndScore(ret.getContent());
+        }
         return new ResponseEntity<>(ret, HttpStatus.OK);
     }
+    
+    private void setPaperQuesCountAndScore(List<Paper> paperList) {
+        if (paperList == null || paperList.size() == 0) {
+            return;
+        }
+        for (Paper paper : paperList) {
+            initQuesCountAndScore(paper);
+            Map<String, Integer> quesCount = paper.getQuesCount();
+            Map<String, Double> quesScore = paper.getQuesScore();
+            List<PaperDetailUnit> units = paperDetailUnitRepo.findByPaperOrderByNumber(paper);
+            if (units != null && units.size() > 0) {
+                for (PaperDetailUnit unit : units) {
+                    if (unit.getQuestion().getPublicity() == null) {
+                        unit.getQuestion().setPublicity(true);
+                    }
+                    if (unit.getQuestion().getDifficulty() == null) {
+                        unit.getQuestion().setDifficulty("中");
+                    }
+                    String key = getKey(unit.getQuestion().getPublicity(), unit.getQuestion().getDifficulty());
+                    quesCount.put(key, quesCount.get(key) + 1);
+                    quesScore.put(key, BigDecimal.valueOf(quesScore.get(key)).add(BigDecimal.valueOf(unit.getScore())).doubleValue());
+                }
+            }
+        }
+    }
+
+    private String getKey(Boolean publicity, String difficultyType) {
+        String difficulty = null;
+        if ("易".equals(difficultyType)) {
+            difficulty = "1";
+        } else if ("中".equals(difficultyType)) {
+            difficulty = "2";
+        } else {
+            difficulty = "3";
+        }
+        if (publicity == null || publicity) {
+            return "1-" + difficulty;
+        } else {
+            return "0-" + difficulty;
+        }
+    }
+
+    private void initQuesCountAndScore(Paper paper) {
+        Map<String, Integer> quesCount = new HashMap<>();
+        quesCount.put("0-1", 0);
+        quesCount.put("0-2", 0);
+        quesCount.put("0-3", 0);
+        quesCount.put("1-1", 0);
+        quesCount.put("1-2", 0);
+        quesCount.put("1-3", 0);
+        Map<String, Double> quesScore = new HashMap<>();
+        quesScore.put("0-1", 0.0);
+        quesScore.put("0-2", 0.0);
+        quesScore.put("0-3", 0.0);
+        quesScore.put("1-1", 0.0);
+        quesScore.put("1-2", 0.0);
+        quesScore.put("1-3", 0.0);
+        paper.setQuesCount(quesCount);
+        paper.setQuesScore(quesScore);
+    }
 
     /**
      * @param paperSearchInfo
@@ -635,18 +701,18 @@ public class PaperController extends ControllerSupport {
         User user = getAccessUser();
         try {
             String[] paperIdArray = paperIds.split(",");
-            StringBuilder paperInfo=new StringBuilder();
+            StringBuilder paperInfo = new StringBuilder();
             for (int i = 0; i < paperIdArray.length; i++) {
                 Paper oldpaper = cn.com.qmth.examcloud.core.questions.base.Model.of(paperRepo.findById(paperIdArray[i]));
                 boolean result = paperService.checkPaperName(oldpaper.getName(), PaperType.GENERATE, user.getRootOrgId() + "");
                 if (!result) {
                     throw new StatusException("160565", "考试试卷:" + oldpaper.getName() + "已经存在");
                 }
-                paperInfo.append(" 课程:"+oldpaper.getCourse().getName()+"("+oldpaper.getCourse().getCode()+") 试卷名称:"+oldpaper.getName());
+                paperInfo.append(" 课程:" + oldpaper.getCourse().getName() + "(" + oldpaper.getCourse().getCode() + ") 试卷名称:" + oldpaper.getName());
             }
             paperService.useBasePaper(paperIds, user.getDisplayName());
-    		
-            ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), "导入试卷管理-使用原卷",paperInfo.toString()));
+
+            ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), "导入试卷管理-使用原卷", paperInfo.toString()));
             return new ResponseEntity<>(HttpStatus.OK);
         } catch (Exception e) {
             throw new StatusException("160537", e.getMessage());
@@ -698,12 +764,12 @@ public class PaperController extends ControllerSupport {
             map.put("courseName", previewPaper.getCourseName());
             map.put("courseLevel", previewPaper.getCourseLevel());
             ExportTemplateUtil.getTemplate(Long.valueOf(paper.getOrgId()), ExportTemplateType.PAPER_VIEW).process(map, result);
-            String content=result.toString();
+            String content = result.toString();
             model.addAttribute("pageContent", content);
             return BASE_PAGE;
         } catch (NumberFormatException | TemplateException | IOException e) {
             throw new StatusException("500", "解析模板出错!");
-        }finally {
+        } finally {
             try {
                 result.close();
             } catch (IOException e) {
@@ -723,7 +789,7 @@ public class PaperController extends ControllerSupport {
         ExportPaperAbstractService exportPaperService = PaperUtil.getByRootOrgId(paper.getOrgId());
         PaperExp previewPaper = exportPaperService.previewPaperForPDF(paper);
 
-        
+
         StringWriter result = new StringWriter();
         try {
             Map<String, Object> map = new HashMap<String, Object>();
@@ -734,12 +800,12 @@ public class PaperController extends ControllerSupport {
             map.put("courseName", previewPaper.getCourseName());
             map.put("courseLevel", previewPaper.getCourseLevel());
             ExportTemplateUtil.getTemplate(Long.valueOf(paper.getOrgId()), ExportTemplateType.ANWSER_VIEW).process(map, result);
-            String content=result.toString();
+            String content = result.toString();
             model.addAttribute("pageContent", content);
             return BASE_PAGE;
         } catch (NumberFormatException | TemplateException | IOException e) {
             throw new StatusException("500", "解析模板出错!");
-        }finally {
+        } finally {
             try {
                 result.close();
             } catch (IOException e) {
@@ -775,43 +841,54 @@ 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){
+    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 (paper == null) {
+            throw new StatusException("500", "未找到试卷");
         }
-        if(!isSuperAdmin()&&!paper.getOrgId().equals(String.valueOf(user.getRootOrgId()))) {
-        	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);
     }
+
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "试卷答案导入")
     @RequestMapping(value = "/paper/answer/import/{paperId}", method = RequestMethod.POST)
     @ResponseBody
-    public void answerImport(@PathVariable String paperId,@RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile){
+    public void answerImport(@PathVariable String paperId, @RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile) {
         User user = getAccessUser();
+        UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
+        if (ud.assertEmptyQueryResult()) {
+        	throw new StatusException("500", "没有数据权限");
+        }
         Paper paper = cn.com.qmth.examcloud.core.questions.base.Model.of(paperRepo.findById(paperId));
-        if(paper==null) {
-        	throw new StatusException("500", "未找到试卷");
+        if (paper == null) {
+            throw new StatusException("500", "未找到试卷");
         }
-        if(!isSuperAdmin()&&!paper.getOrgId().equals(String.valueOf(user.getRootOrgId()))) {
-        	throw new StatusException("500", "非法请求");
+        if (!isSuperAdmin() && !paper.getOrgId().equals(String.valueOf(user.getRootOrgId()))) {
+            throw new StatusException("500", "非法请求");
         }
-        paperService.answerImport(paper,dataFile);
-        StringBuilder paperInfo=new StringBuilder();
-		paperInfo.append("课程:"+paper.getCourse().getName()+"("+paper.getCourse().getCode()+")");
-		paperInfo.append(" 试卷名称:"+paper.getName());
-        ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), "导入试卷管理-试卷答案导入",paperInfo.toString()));
+        if(ud.assertNeedQueryRefIds()&&!ud.stringRefIds().contains(paper.getCourse().getId())) {
+        	throw new StatusException("500", "没有数据权限");
+        }
+        paperService.answerImport(paper, dataFile);
+        StringBuilder paperInfo = new StringBuilder();
+        paperInfo.append("课程:" + paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")");
+        paperInfo.append(" 试卷名称:" + paper.getName());
+        ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), "导入试卷管理-试卷答案导入", paperInfo.toString()));
     }
+
     @ApiOperation(value = "下载答案模板", notes = "下载答案模板")
     @GetMapping("/paper/answer/template")
     public void getImportTemplate(HttpServletResponse response) {
         String resoucePath = PathUtil.getResoucePath("templates/answerImportTemplate.xlsx");
         exportFile("答案导入模板.xlsx", new File(resoucePath));
     }
+
 }

+ 68 - 0
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/PaperStorageController.java

@@ -0,0 +1,68 @@
+package cn.com.qmth.examcloud.core.questions.api.controller;
+
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+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.PutMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperSearchInfo;
+import cn.com.qmth.examcloud.core.questions.service.PaperService;
+import cn.com.qmth.examcloud.web.security.DataRule;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@RequestMapping("${api_cqb}/paper_storage")
+public class PaperStorageController extends ControllerSupport {
+
+    @Autowired
+    PaperService paperService;
+
+    @DataRule(type = DataRuleType.COURSE)
+    @ApiOperation(value = "试卷仓库分页查询", notes = "试卷仓库分页查询")
+    @ResponseBody
+    @GetMapping(value = "/findPage/{curPage}/{pageSize}")
+    public ResponseEntity<Object> getGenPapers(@ModelAttribute PaperSearchInfo paperSearchInfo,
+                                               @PathVariable int curPage, @PathVariable int pageSize) {
+        User user = getAccessUser();
+        UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
+        paperSearchInfo.setOrgId(user.getRootOrgId().toString());
+        return new ResponseEntity<>(paperService.getStoragePaperPage(paperSearchInfo, curPage, pageSize,ud), HttpStatus.OK);
+    }
+    
+    @ResponseBody
+    @ApiOperation(value = "回收试卷", notes = "回收试卷")
+    @PutMapping(value = "/recovery/{paperIds}")
+    public ResponseEntity<Object> recoveryPaper(@PathVariable String paperIds) {
+    	User user = getAccessUser();
+        List<String> paperList = Stream.of(paperIds.split(",")).collect(Collectors.toList());
+        paperService.updatePapersStorage(paperList,1,user.getRootOrgId().toString());
+        return new ResponseEntity<>(HttpStatus.OK);
+
+    }
+    
+    @ResponseBody
+    @ApiOperation(value = "释放试卷", notes = "释放试卷")
+    @PutMapping(value = "/release/{paperIds}")
+    public ResponseEntity<Object> releasePaper(@PathVariable String paperIds) {
+    	User user = getAccessUser();
+        List<String> paperList = Stream.of(paperIds.split(",")).collect(Collectors.toList());
+        paperService.updatePapersStorage(paperList,0,user.getRootOrgId().toString());
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+}

+ 17 - 6
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/PaperStructController.java

@@ -1,12 +1,16 @@
 package cn.com.qmth.examcloud.core.questions.api.controller;
 
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
 import cn.com.qmth.examcloud.core.questions.dao.PaperStructRepo;
 import cn.com.qmth.examcloud.core.questions.dao.entity.PaperStruct;
 import cn.com.qmth.examcloud.core.questions.dao.entity.PaperStructSearchInfo;
 import cn.com.qmth.examcloud.core.questions.service.PaperStructService;
 import cn.com.qmth.examcloud.core.questions.service.bean.dto.QuesNameDto;
+import cn.com.qmth.examcloud.web.security.DataRule;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -39,16 +43,18 @@ public class PaperStructController extends ControllerSupport {
      * @param
      * @return
      */
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "获取试卷结构带分页", notes = "获取试卷结构带分页")
     @GetMapping(value = "/paperStruct/{curPage}/{pageSize}")
     public ResponseEntity<Object> getPaperStructs(@ModelAttribute PaperStructSearchInfo searchInfo,
                                                   @PathVariable int curPage,
                                                   @PathVariable int pageSize) {
         User user = getAccessUser();
-        if (user != null) {
-            searchInfo.setOrgId(user.getRootOrgId().toString());
-        }
-        Page<PaperStruct> paperStructs = paperStructService.getPaperStructs(searchInfo, curPage, pageSize);
+        UserDataRule userDataRule = super.getUserDataRule(DataRuleType.COURSE);
+
+        searchInfo.setOrgId(user.getRootOrgId().toString());
+
+        Page<PaperStruct> paperStructs = paperStructService.getPaperStructs(searchInfo, curPage, pageSize, userDataRule);
         return new ResponseEntity<>(paperStructs, HttpStatus.OK);
     }
 
@@ -58,14 +64,16 @@ public class PaperStructController extends ControllerSupport {
      * @param
      * @return
      */
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "获取试卷结构不带分页", notes = "获取试卷结构不带分页")
     @GetMapping(value = "/paperStruct")
     public ResponseEntity<Object> getPaperStructs(@ModelAttribute PaperStructSearchInfo searchInfo) {
         User user = getAccessUser();
+        UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
         if (user != null) {
             searchInfo.setOrgId(user.getRootOrgId().toString());
         }
-        List<PaperStruct> paperStructs = paperStructService.getPaperStructs(searchInfo);
+        List<PaperStruct> paperStructs = paperStructService.getPaperStructs(searchInfo,ud);
         return new ResponseEntity<>(paperStructs, HttpStatus.OK);
     }
 
@@ -91,6 +99,7 @@ public class PaperStructController extends ControllerSupport {
     @PutMapping(value = "/paperStruct")
     public ResponseEntity<Object> updatePaperStruct(@RequestBody PaperStruct ps) {
         User user = getAccessUser();
+        ps.setOrgId(user.getRootOrgId()+"");
         PaperStruct paperStruct = paperStructService.save(ps, user);
         if (paperStruct == null) {
             return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
@@ -109,9 +118,10 @@ public class PaperStructController extends ControllerSupport {
     @PostMapping(value = "/paperStruct")
     public ResponseEntity<Object> addPaperStruct(@RequestBody PaperStruct ps) {
         User user = getAccessUser();
+        ps.setOrgId(user.getRootOrgId()+"");
         PaperStruct paperStructTemp = paperStructService.checkNameUnique(ps.getName(), user.getRootOrgId().toString(), ps.getType());
         if (paperStructTemp != null) {
-            return new ResponseEntity<>("试卷结构名称重复,请重新命名!", HttpStatus.INTERNAL_SERVER_ERROR);
+            throw new  StatusException("试卷结构名称重复,请重新命名!");
         } else {
             PaperStruct paperStruct = paperStructService.save(ps, user);
             return new ResponseEntity<>(paperStruct, HttpStatus.OK);
@@ -149,4 +159,5 @@ public class PaperStructController extends ControllerSupport {
         }
         return new ResponseEntity<>(quesNameDtos, HttpStatus.OK);
     }
+
 }

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

@@ -20,13 +20,16 @@ import org.springframework.web.bind.annotation.RestController;
 
 import com.google.gson.Gson;
 
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.dao.QuesRepo;
 import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
 import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionSearchCondition;
 import cn.com.qmth.examcloud.core.questions.service.PaperDetailUnitService;
 import cn.com.qmth.examcloud.core.questions.service.QuesService;
+import cn.com.qmth.examcloud.web.security.DataRule;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import io.swagger.annotations.ApiOperation;
 
@@ -68,14 +71,16 @@ public class QuesController extends ControllerSupport {
      *
      * @return
      */
+    @DataRule(type = DataRuleType.COURSE)
     @ApiOperation(value = "分页查询试题", notes = "分页查询试题")
     @GetMapping(value = "/question/{curPage}/{pageSize}")
     public ResponseEntity<Object> getAllQuestion(@ModelAttribute QuestionSearchCondition searchCondition,
                                                  @PathVariable int curPage,
                                                  @PathVariable int pageSize) {
         User user = getAccessUser();
+        UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
         searchCondition.setOrgId(user.getRootOrgId().toString());
-        return new ResponseEntity<>(quesService.findAll(searchCondition, curPage, pageSize), HttpStatus.OK);
+        return new ResponseEntity<>(quesService.findAll(searchCondition, curPage, pageSize,ud), HttpStatus.OK);
     }
 
     /**

+ 4 - 4
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/provider/ExtractConfigCloudServiceProvider.java

@@ -1,8 +1,6 @@
 package cn.com.qmth.examcloud.core.questions.api.provider;
 
 import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
-import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
 import cn.com.qmth.examcloud.core.questions.api.ExtractConfigCloudService;
 import cn.com.qmth.examcloud.core.questions.api.request.*;
 import cn.com.qmth.examcloud.core.questions.api.response.*;
@@ -12,6 +10,8 @@ import cn.com.qmth.examcloud.question.commons.core.paper.DefaultPaper;
 import cn.com.qmth.examcloud.question.commons.core.question.DefaultQuestion;
 import io.swagger.annotations.ApiOperation;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -35,7 +35,7 @@ import java.util.Set;
 @RequestMapping("${$rmp.cloud.questions}" + "extract_config")
 public class ExtractConfigCloudServiceProvider implements ExtractConfigCloudService {
 
-    protected ExamCloudLog log = ExamCloudLogFactory.getLog(this.getClass());
+    private static final Logger LOG = LoggerFactory.getLogger(ExtractConfigCloudServiceProvider.class);
 
     private static final long serialVersionUID = 2346453260179681094L;
 
@@ -165,7 +165,7 @@ public class ExtractConfigCloudServiceProvider implements ExtractConfigCloudServ
             resp.setJson(json);
             return resp;
         } catch (Exception e) {
-            log.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         }
         return null;
     }

+ 5 - 5
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/provider/HandleSyncCloudServiceProvider.java

@@ -101,7 +101,7 @@ public class HandleSyncCloudServiceProvider extends ControllerSupport implements
                 i++;
             }
             extractConfigRepo.saveAll(extractConfigs);
-            log.debug("调卷规则同步完成,已经同步数量:" + i);
+            LOGGER.debug("调卷规则同步完成,已经同步数量:" + i);
         }
         //同步试卷
         List<Paper> papers = paperRepo.findByCourseNoAndOrgId(courseCode, rootOrgId.toString());
@@ -128,7 +128,7 @@ public class HandleSyncCloudServiceProvider extends ControllerSupport implements
                 i++;
             }
             paperRepo.saveAll(papers);
-            log.debug("试卷同步完成,已经同步数量:" + i);
+            LOGGER.debug("试卷同步完成,已经同步数量:" + i);
         }
         //同步试题
         Query query = new Query();
@@ -158,7 +158,7 @@ public class HandleSyncCloudServiceProvider extends ControllerSupport implements
                 i++;
             }
             quesRepo.saveAll(questions);
-            log.debug("试题同步完成,已经同步数量:" + i);
+            LOGGER.debug("试题同步完成,已经同步数量:" + i);
         }
         SyncCourseResp resp = new SyncCourseResp();
         return resp;
@@ -209,7 +209,7 @@ public class HandleSyncCloudServiceProvider extends ControllerSupport implements
         query.addCriteria(Criteria.where("orgId").is(rootOrgId.toString()));
         query.addCriteria(Criteria.where("examId").is(id));
         List<ExtractConfig> extractConfigs = this.mongoTemplate.find(query, ExtractConfig.class);
-        log.debug("调卷规则总数量:" + extractConfigs.size());
+        LOGGER.debug("调卷规则总数量:" + extractConfigs.size());
         if (extractConfigs != null && extractConfigs.size() > 0) {
             int i = 0;
             for (ExtractConfig extractConfig : extractConfigs) {
@@ -219,7 +219,7 @@ public class HandleSyncCloudServiceProvider extends ControllerSupport implements
                 extractConfig.setExamType(examType);
             }
             extractConfigRepo.saveAll(extractConfigs);
-            log.debug("调卷规则同步完成,已经同步数量:" + i);
+            LOGGER.debug("调卷规则同步完成,已经同步数量:" + i);
         }
         SyncExamResp resp = new SyncExamResp();
         return resp;

+ 26 - 15
examcloud-core-questions-base/pom.xml

@@ -2,13 +2,14 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
+    <artifactId>examcloud-core-questions-base</artifactId>
+    <packaging>jar</packaging>
+
     <parent>
+        <groupId>cn.com.qmth.examcloud</groupId>
         <artifactId>examcloud-core-questions</artifactId>
-        <groupId>cn.com.qmth.examcloud.core.questions</groupId>
-        <version>v4.0.2-RELEASE</version>
+        <version>${revision}</version>
     </parent>
-    <artifactId>examcloud-core-questions-base</artifactId>
-    <packaging>jar</packaging>
 
     <dependencies>
         <dependency>
@@ -24,48 +25,58 @@
         <dependency>
             <groupId>cn.com.qmth.examcloud</groupId>
             <artifactId>examcloud-web</artifactId>
-            <version>${examcloud.version}</version>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
             <groupId>cn.com.qmth.examcloud</groupId>
             <artifactId>examcloud-support</artifactId>
-            <version>${examcloud.version}</version>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
-            <groupId>cn.com.qmth.examcloud.question</groupId>
+            <groupId>cn.com.qmth.examcloud</groupId>
             <artifactId>examcloud-question-commons</artifactId>
-            <version>${examcloud.version}</version>
+            <version>${project.version}</version>
         </dependency>
 
         <dependency>
             <groupId>cn.com.qmth.examcloud.rpc</groupId>
             <artifactId>examcloud-global-api-client</artifactId>
-            <version>${examcloud.version}</version>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
             <groupId>cn.com.qmth.examcloud.rpc</groupId>
             <artifactId>examcloud-core-basic-api-client</artifactId>
-            <version>${examcloud.version}</version>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
             <groupId>cn.com.qmth.examcloud.rpc</groupId>
             <artifactId>examcloud-core-examwork-api-client</artifactId>
-            <version>${examcloud.version}</version>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
             <groupId>cn.com.qmth.examcloud.rpc</groupId>
             <artifactId>examcloud-core-print-api-client</artifactId>
-            <version>${examcloud.version}</version>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
             <groupId>cn.com.qmth.examcloud.rpc</groupId>
             <artifactId>examcloud-core-questions-api-client</artifactId>
-            <version>${examcloud.version}</version>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
-            <groupId>cn.com.qmth.examcloud.reports</groupId>
+            <groupId>cn.com.qmth.examcloud</groupId>
             <artifactId>examcloud-reports-commons</artifactId>
-            <version>${examcloud.version}</version>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-core-oe-admin-api-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-core-oe-student-api-client</artifactId>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
             <groupId>com.upyun</groupId>

+ 14 - 14
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/FileDisposeUtil.java

@@ -36,7 +36,7 @@ import org.slf4j.LoggerFactory;
  */
 public class FileDisposeUtil {
 
-    private static final Logger logger = LoggerFactory.getLogger(FileDisposeUtil.class);
+    private static final Logger LOG = LoggerFactory.getLogger(FileDisposeUtil.class);
 
     /**
      * 将网络文件保存到本地
@@ -52,7 +52,7 @@ public class FileDisposeUtil {
         try {
             url = new URL(fileUrl);
         } catch (MalformedURLException e) {
-            logger.error("fileUrl:"+fileUrl, e);
+            LOG.error("fileUrl:"+fileUrl, e);
             return false;
         }
 
@@ -60,7 +60,7 @@ public class FileDisposeUtil {
         try {
             connection = (HttpURLConnection) url.openConnection();
         } catch (IOException e) {
-            logger.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
             return false;
         }
 
@@ -77,7 +77,7 @@ public class FileDisposeUtil {
             dataOutputStream.flush();
             return true;
         } catch (Exception e) {
-            logger.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
             return false;
         } finally {
             if (connection != null) {
@@ -121,9 +121,9 @@ public class FileDisposeUtil {
 
             response.flushBuffer();
         } catch (FileNotFoundException e) {
-            logger.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         } catch (IOException e) {
-            logger.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         }
     }
 
@@ -139,7 +139,7 @@ public class FileDisposeUtil {
         try {
             type = Files.probeContentType(path);
         } catch (IOException e) {
-            logger.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         }
         return type;
     }
@@ -156,23 +156,23 @@ public class FileDisposeUtil {
      * @return
      */
     public static boolean fileToZip(String sourceFilePath, String zipFilePath, String fileName) {
-        logger.info("压缩" + sourceFilePath + "目录开始");
+        LOG.info("压缩" + sourceFilePath + "目录开始");
 
         File sourceFile = new File(sourceFilePath);
         if (!sourceFile.exists()) {
-            logger.error("待压缩的文件目录:" + sourceFilePath + "不存在.");
+            LOG.error("待压缩的文件目录:" + sourceFilePath + "不存在.");
             return false;
         }
 
         File zipFile = new File(zipFilePath + File.separator + fileName + ".zip");
         if (zipFile.exists()) {
-            logger.error(zipFilePath + "目录下存在名字为:" + fileName + ".zip" + "打包文件.");
+            LOG.error(zipFilePath + "目录下存在名字为:" + fileName + ".zip" + "打包文件.");
             return false;
         }
 
         File[] sourceFiles = sourceFile.listFiles();
         if (null == sourceFiles || sourceFiles.length < 1) {
-            logger.error("待压缩的文件目录:" + sourceFilePath + "里面不存在文件,无需压缩.");
+            LOG.error("待压缩的文件目录:" + sourceFilePath + "里面不存在文件,无需压缩.");
             return false;
         }
 
@@ -204,14 +204,14 @@ public class FileDisposeUtil {
 
                     zos.flush();
                 } catch (Exception e) {
-                    logger.error(e.getMessage(), e);
+                    LOG.error(e.getMessage(), e);
                 }
             }
 
-            logger.info("压缩" + sourceFilePath + "目录完成");
+            LOG.info("压缩" + sourceFilePath + "目录完成");
             return true;
         } catch (Exception e) {
-            logger.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         }
 
         return false;

+ 4 - 4
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/Cryptogram.java

@@ -25,7 +25,7 @@ import java.security.SecureRandom;
  * 支持DES/AES对称加密的工具类
  */
 public class Cryptogram {
-    private static Logger log = LoggerFactory.getLogger(Cryptogram.class);
+    private static final Logger log = LoggerFactory.getLogger(Cryptogram.class);
     private static final String AES = "AES";
     private static final String AES_CBC = "AES/CBC/PKCS5Padding";
     private static final String IV_SPEC = "0102030405060708";
@@ -52,7 +52,7 @@ public class Cryptogram {
             byte[] bytes = aes(strBytes, keyBytes, Cipher.ENCRYPT_MODE);
             return byteToHex(bytes);
         } catch (Exception e) {
-            log.error(e.getMessage());
+            log.error(e.getMessage(), e);
             return null;
         }
     }
@@ -76,7 +76,7 @@ public class Cryptogram {
             byte[] decryptResult = aes(strBytes, keyBytes, Cipher.DECRYPT_MODE);
             return new String(decryptResult);
         } catch (Exception e) {
-            log.error(e.getMessage());
+            log.error(e.getMessage(), e);
             return null;
         }
     }
@@ -137,7 +137,7 @@ public class Cryptogram {
             SecretKey secretKey = keyGenerator.generateKey();
             return secretKey.getEncoded();
         } catch (GeneralSecurityException e) {
-            log.error(e.getMessage());
+            log.error(e.getMessage(), e);
             return null;
         }
     }

+ 1 - 1
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/FileUtil.java

@@ -21,7 +21,7 @@ import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
 
 public class FileUtil {
-    private static Logger log = LoggerFactory.getLogger(FileUtil.class);
+    private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
 
     /**
      * 分隔文件

+ 7 - 6
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/excel/ExcelWriter.java

@@ -18,7 +18,8 @@ import java.util.regex.Pattern;
  * Created by dizhi on 2016/6/19.
  */
 public class ExcelWriter extends ExcelUtils {
-    private static final Logger log = LoggerFactory.getLogger(ExcelWriter.class);
+
+    private static final Logger LOG = LoggerFactory.getLogger(ExcelWriter.class);
 
     public ExcelWriter(Class<?> dataClass) {
         super(dataClass, false);
@@ -158,20 +159,20 @@ public class ExcelWriter extends ExcelUtils {
                             }
                         }
                     } catch (NoSuchFieldException e) {
-                        log.error(e.getMessage(), e);
+                        LOG.error(e.getMessage(), e);
                     } catch (IllegalAccessException e) {
-                        log.error(e.getMessage(), e);
+                        LOG.error(e.getMessage(), e);
                     } catch (SecurityException e) {
-                        log.error(e.getMessage(), e);
+                        LOG.error(e.getMessage(), e);
                     } catch (IllegalArgumentException e) {
-                        log.error(e.getMessage(), e);
+                        LOG.error(e.getMessage(), e);
                     }
                 }
             }
 
             workbook.write(out);
         } catch (IOException e) {
-            log.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         } finally {
             try {
                 out.close();

+ 3 - 3
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/excel/ImportUtils.java

@@ -12,7 +12,7 @@ import java.io.*;
  */
 public final class ImportUtils {
 
-    protected static final Logger logger = LoggerFactory.getLogger(ImportUtils.class);
+    private static final Logger LOG = LoggerFactory.getLogger(ImportUtils.class);
 
     public static final String TEMP_FILE_IMP = "fileImport/";
 
@@ -50,9 +50,9 @@ public final class ImportUtils {
              InputStream is = file.getInputStream();) {
             IOUtils.copyLarge(is, os);
         } catch (FileNotFoundException e) {
-            logger.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         } catch (IOException e) {
-            logger.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         }
 
         return tempFile;

+ 7 - 7
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/word/DocxProcessUtil.java

@@ -75,7 +75,7 @@ import java.util.zip.ZipOutputStream;
  */
 public final class DocxProcessUtil {
 
-    private static final Logger logger = LoggerFactory.getLogger(DocxProcessUtil.class);
+    private static final Logger LOG = LoggerFactory.getLogger(DocxProcessUtil.class);
 
     public static final LayoutContext IMG_LAYOUT = new StyleAttributeLayoutContext(
             LayoutContextImpl.getDefaultLayoutContext(), "2em", java.awt.Color.BLACK);
@@ -471,7 +471,7 @@ public final class DocxProcessUtil {
 
             return textStr.toString();
         } catch (Exception e) {
-            logger.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         }
 
         return htmlStr;
@@ -707,7 +707,7 @@ public final class DocxProcessUtil {
             template.process(dataModel, out);
             out.flush();
         } catch (Exception e) {
-            logger.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
             throw new RuntimeException("Export Word Fail.");
         }
     }
@@ -733,7 +733,7 @@ public final class DocxProcessUtil {
      * @throws Exception
      */
     public static void processImage(String fileName, List<WordprocessingMLPackage> wordMLPackages) throws Exception {
-        logger.info("试卷:“" + fileName + "”导出处理图片开始...");
+        LOG.info("试卷:“" + fileName + "”导出处理图片开始...");
         String filePath = TEMP_FILE_EXP + fileName;
         InputStream mainFile = new FileInputStream(filePath);
         // 取到当前生成的主Word
@@ -779,7 +779,7 @@ public final class DocxProcessUtil {
         OutputStream os = new FileOutputStream(filePath);
         Docx4J.save(wordMLPackage, os, Docx4J.FLAG_SAVE_ZIP_FILE);
         IOUtils.closeQuietly(os);
-        logger.info("试卷:“" + fileName + "”导出处理图片结束");
+        LOG.info("试卷:“" + fileName + "”导出处理图片结束");
     }
 
     public static void copyImage(WordprocessingMLPackage wordMLPackage, byte[] bytes, String relId) throws Exception {
@@ -847,7 +847,7 @@ public final class DocxProcessUtil {
         try {
             wordMLPackage = Docx4J.load(inputStream);
         } catch (Docx4JException e) {
-            logger.error(e.getMessage());
+            LOG.error(e.getMessage(), e);
         }
         return wordMLPackage;
     }
@@ -911,7 +911,7 @@ public final class DocxProcessUtil {
                 response.flushBuffer();
             }
         } catch (IOException e) {
-            logger.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         }
 
         //删除本地文件

+ 4 - 4
examcloud-core-questions-base/src/main/resources/export_template/origin_paper/document.ftl

@@ -330,7 +330,7 @@
 				<w:rPr>
 					<w:rFonts w:hint="eastAsia" />
 				</w:rPr>
-				<w:t>${subQuestion.quesProperties[0].firstProperty.name}</w:t>
+				<w:t>${subQuestion.quesProperties[0].firstProperty.code}</w:t>
 			</w:r>
 			</#if>
 		</w:p>
@@ -361,7 +361,7 @@
 				<w:rPr>
 					<w:rFonts w:hint="eastAsia" />
 				</w:rPr>
-				<w:t>${subQuestion.quesProperties[0].secondProperty.name}</w:t>
+				<w:t>${subQuestion.quesProperties[0].secondProperty.code}</w:t>
 			</w:r>
 			</#if>
 			</#if>
@@ -492,7 +492,7 @@
 				<w:rPr>
 					<w:rFonts w:hint="eastAsia" />
 				</w:rPr>
-				<w:t>${paperDetailUnit.firstName!}</w:t>
+				<w:t>${paperDetailUnit.firstCode!}</w:t>
 			</w:r>
 		</w:p>
 		<w:p w:rsidP="00F62967" w:rsidR="00F62967"
@@ -519,7 +519,7 @@
 				<w:rPr>
 					<w:rFonts w:hint="eastAsia" />
 				</w:rPr>
-				<w:t>${paperDetailUnit.secondName!}</w:t>
+				<w:t>${paperDetailUnit.secondCode!}</w:t>
 			</w:r>
 		</w:p>
 		<w:p w:rsidP="00F62967" w:rsidR="00F62967"

+ 17 - 16
examcloud-core-questions-dao/pom.xml

@@ -1,21 +1,22 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-	<parent>
-		<artifactId>examcloud-core-questions</artifactId>
-		<groupId>cn.com.qmth.examcloud.core.questions</groupId>
-		<version>v4.0.2-RELEASE</version>
-	</parent>
-	<artifactId>examcloud-core-questions-dao</artifactId>
-	<packaging>jar</packaging>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>examcloud-core-questions-dao</artifactId>
+    <packaging>jar</packaging>
 
+    <parent>
+        <groupId>cn.com.qmth.examcloud</groupId>
+        <artifactId>examcloud-core-questions</artifactId>
+        <version>${revision}</version>
+    </parent>
 
-	<dependencies>
-		<dependency>
-			<groupId>cn.com.qmth.examcloud.core.questions</groupId>
-			<artifactId>examcloud-core-questions-base</artifactId>
-			<version>${examcloud.version}</version>
-		</dependency>
-	</dependencies>
+    <dependencies>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud</groupId>
+            <artifactId>examcloud-core-questions-base</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
 
 </project>

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

@@ -5,6 +5,7 @@ import org.springframework.data.mongodb.repository.MongoRepository;
 import org.springframework.data.repository.query.QueryByExampleExecutor;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * @author weiwenhai
@@ -13,9 +14,6 @@ import java.util.List;
  */
 public interface CoursePropertyRepo extends MongoRepository<CourseProperty, String>, QueryByExampleExecutor<CourseProperty> {
 
-    //查询所有课程属性
-    List<CourseProperty> findByOrgId(Long orgId);
-
     //根据属性名查询
     CourseProperty findByOrgIdAndName(Long orgId, String name);
 
@@ -33,4 +31,10 @@ public interface CoursePropertyRepo extends MongoRepository<CourseProperty, Stri
     //根据id集合查询
     List<CourseProperty> findByIdIn(List<String> ids);
 
+	CourseProperty findByOrgIdAndCourseIdAndName(Long rootOrgId, Long courseId, String propertyName);
+
+	List<CourseProperty> findByOrgIdAndCourseIdIn(Long orgId, Set<Long> refIds);
+
+	List<CourseProperty> findByOrgId(Long orgId);
+
 }

+ 164 - 164
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/PaperDetailUnitNativeRepo.java

@@ -1,164 +1,164 @@
-package cn.com.qmth.examcloud.core.questions.dao;
-
-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.Paper;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
-import com.mongodb.DBRef;
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoCursor;
-import org.bson.Document;
-import org.bson.types.ObjectId;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.data.mongodb.core.MongoTemplate;
-import org.springframework.stereotype.Repository;
-
-import java.util.*;
-
-import static com.mongodb.client.model.Filters.eq;
-
-/**
- * Created by songyue on 18/1/22.
- */
-@Repository
-public class PaperDetailUnitNativeRepo {
-
-    private static final Logger log = LoggerFactory.getLogger(PaperDetailUnitNativeRepo.class);
-
-    @Autowired
-    @Qualifier("pduCollection")
-    private MongoCollection<Document> mongoCollection;
-
-    @Autowired
-    private MongoTemplate mongoTemplate;
-
-    public List<PaperDetailUnit> findByPaperId(String paperId) {
-
-        long beginTime = System.currentTimeMillis();
-
-        //初始化试卷缓存
-        Paper paperCache = mongoTemplate.findById(getObjectId(paperId), Paper.class);
-
-        //初始化大题缓存
-        Map<String, PaperDetail> pdCache = new HashMap<>();
-
-        //初始化小题列表
-        List<PaperDetailUnit> paperDetailUnits = new ArrayList<>();
-
-        //获取小题原始文档
-        MongoCursor<Document> mongoCursor = mongoCollection.find(eq("paper.$id", getObjectId(paperId))).iterator();
-        long documentEndTime = System.currentTimeMillis();
-        log.info("获取document共耗时:" + (documentEndTime - beginTime) + "ms");
-
-        //转换小题文档
-        while (mongoCursor.hasNext()) {
-            Document document = mongoCursor.next();
-            PaperDetailUnit pdu = toDomain(document, paperCache, pdCache);
-            paperDetailUnits.add(pdu);
-        }
-        long pduEndTime = System.currentTimeMillis();
-        log.info("转换document共耗时:" + (pduEndTime - documentEndTime) + "ms");
-        return paperDetailUnits;
-    }
-
-    @SuppressWarnings("unchecked")
-    private PaperDetailUnit toDomain(Document document,
-                                     Paper paperCache,
-                                     Map<String, PaperDetail> pdCache) {
-        PaperDetailUnit paperDetailUnit = new PaperDetailUnit();
-        Set<String> pduProperty = document.keySet();
-        for (String key : pduProperty) {
-            if (key.equals("_id")) {
-
-                paperDetailUnit.setId(String.valueOf(document.get(key)));
-
-            } else if (key.equals("number")) {
-
-                paperDetailUnit.setNumber(document.getInteger(key));
-
-            } else if (key.equals("score")) {
-
-                paperDetailUnit.setScore(document.getDouble(key));
-
-            } else if (key.equals("subScoreList")) {
-
-                List<Double> subScoreList = document.get(key, List.class);
-                paperDetailUnit.setSubScoreList(subScoreList);
-
-            } else if (key.equals("questionType")) {
-
-                String quesType = document.getString(key);
-                paperDetailUnit.setQuestionType(QuesStructType.valueOf(quesType));
-
-            } else if (key.equals("optionOrder")) {
-
-                paperDetailUnit.setOptionOrder(document.getString(key));
-
-            } else if (key.equals("creator")) {
-
-                paperDetailUnit.setCreator(document.getString(key));
-
-            } else if (key.equals("createTime")) {
-
-                paperDetailUnit.setCreateTime(document.getString(key));
-
-            } else if (key.equals("paperType")) {
-
-                String paperType = document.getString(key);
-                paperDetailUnit.setPaperType(PaperType.valueOf(paperType));
-
-            } else if (key.equals("paper")) {
-
-                paperDetailUnit.setPaper(paperCache);
-
-            } else if (key.equals("paperDetail")) {
-
-                paperDetailUnit.setPaperDetail(getPaperDetail(document, pdCache));
-
-            } else if (key.equals("question")) {
-
-                paperDetailUnit.setQuestion(getQuestion(document));
-
-            }
-        }
-        return paperDetailUnit;
-    }
-
-    private Object getObjectId(String id) {
-        if (id == null) {
-            return null;
-        }
-        return ObjectId.isValid(id) ? new ObjectId(id) : id;
-    }
-
-    private PaperDetail getPaperDetail(Document parentDocument, Map<String, PaperDetail> pdCache) {
-        Object value = parentDocument.get("paperDetail");
-        if (value == null || !(value instanceof DBRef)) {
-            return null;
-        }
-        DBRef paperDetailDoc = (DBRef) value;
-        String paperDetailId = String.valueOf(paperDetailDoc.getId());
-        PaperDetail paperDetail = pdCache.get(paperDetailId);
-        if (paperDetail == null) {
-            paperDetail = mongoTemplate.findById(getObjectId(paperDetailId), PaperDetail.class);
-            pdCache.put(paperDetailId, paperDetail);
-        }
-        return paperDetail;
-    }
-
-    private Question getQuestion(Document parentDocument) {
-        Object value = parentDocument.get("question");
-        if (value == null || !(value instanceof DBRef)) {
-            return null;
-        }
-        DBRef quesDoc = (DBRef) value;
-        String quesId = String.valueOf(quesDoc.getId());
-        Question question = mongoTemplate.findById(getObjectId(quesId), Question.class);
-        return question;
-    }
-}
+// package cn.com.qmth.examcloud.core.questions.dao;
+//
+// 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.Paper;
+// import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
+// import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
+// import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
+// import com.mongodb.DBRef;
+// import com.mongodb.client.MongoCollection;
+// import com.mongodb.client.MongoCursor;
+// import org.bson.Document;
+// import org.bson.types.ObjectId;
+// import org.slf4j.Logger;
+// import org.slf4j.LoggerFactory;
+// import org.springframework.beans.factory.annotation.Autowired;
+// import org.springframework.beans.factory.annotation.Qualifier;
+// import org.springframework.data.mongodb.core.MongoTemplate;
+// import org.springframework.stereotype.Repository;
+//
+// import java.util.*;
+//
+// import static com.mongodb.client.model.Filters.eq;
+//
+// /**
+//  * Created by songyue on 18/1/22.
+//  */
+// @Repository
+// public class PaperDetailUnitNativeRepo {
+//
+//     private static final Logger log = LoggerFactory.getLogger(PaperDetailUnitNativeRepo.class);
+//
+//     @Autowired
+//     @Qualifier("pduCollection")
+//     private MongoCollection<Document> mongoCollection;
+//
+//     @Autowired
+//     private MongoTemplate mongoTemplate;
+//
+//     public List<PaperDetailUnit> findByPaperId(String paperId) {
+//
+//         long beginTime = System.currentTimeMillis();
+//
+//         //初始化试卷缓存
+//         Paper paperCache = mongoTemplate.findById(getObjectId(paperId), Paper.class);
+//
+//         //初始化大题缓存
+//         Map<String, PaperDetail> pdCache = new HashMap<>();
+//
+//         //初始化小题列表
+//         List<PaperDetailUnit> paperDetailUnits = new ArrayList<>();
+//
+//         //获取小题原始文档
+//         MongoCursor<Document> mongoCursor = mongoCollection.find(eq("paper.$id", getObjectId(paperId))).iterator();
+//         long documentEndTime = System.currentTimeMillis();
+//         log.info("获取document共耗时:" + (documentEndTime - beginTime) + "ms");
+//
+//         //转换小题文档
+//         while (mongoCursor.hasNext()) {
+//             Document document = mongoCursor.next();
+//             PaperDetailUnit pdu = toDomain(document, paperCache, pdCache);
+//             paperDetailUnits.add(pdu);
+//         }
+//         long pduEndTime = System.currentTimeMillis();
+//         log.info("转换document共耗时:" + (pduEndTime - documentEndTime) + "ms");
+//         return paperDetailUnits;
+//     }
+//
+//     @SuppressWarnings("unchecked")
+//     private PaperDetailUnit toDomain(Document document,
+//                                      Paper paperCache,
+//                                      Map<String, PaperDetail> pdCache) {
+//         PaperDetailUnit paperDetailUnit = new PaperDetailUnit();
+//         Set<String> pduProperty = document.keySet();
+//         for (String key : pduProperty) {
+//             if (key.equals("_id")) {
+//
+//                 paperDetailUnit.setId(String.valueOf(document.get(key)));
+//
+//             } else if (key.equals("number")) {
+//
+//                 paperDetailUnit.setNumber(document.getInteger(key));
+//
+//             } else if (key.equals("score")) {
+//
+//                 paperDetailUnit.setScore(document.getDouble(key));
+//
+//             } else if (key.equals("subScoreList")) {
+//
+//                 List<Double> subScoreList = document.get(key, List.class);
+//                 paperDetailUnit.setSubScoreList(subScoreList);
+//
+//             } else if (key.equals("questionType")) {
+//
+//                 String quesType = document.getString(key);
+//                 paperDetailUnit.setQuestionType(QuesStructType.valueOf(quesType));
+//
+//             } else if (key.equals("optionOrder")) {
+//
+//                 paperDetailUnit.setOptionOrder(document.getString(key));
+//
+//             } else if (key.equals("creator")) {
+//
+//                 paperDetailUnit.setCreator(document.getString(key));
+//
+//             } else if (key.equals("createTime")) {
+//
+//                 paperDetailUnit.setCreateTime(document.getString(key));
+//
+//             } else if (key.equals("paperType")) {
+//
+//                 String paperType = document.getString(key);
+//                 paperDetailUnit.setPaperType(PaperType.valueOf(paperType));
+//
+//             } else if (key.equals("paper")) {
+//
+//                 paperDetailUnit.setPaper(paperCache);
+//
+//             } else if (key.equals("paperDetail")) {
+//
+//                 paperDetailUnit.setPaperDetail(getPaperDetail(document, pdCache));
+//
+//             } else if (key.equals("question")) {
+//
+//                 paperDetailUnit.setQuestion(getQuestion(document));
+//
+//             }
+//         }
+//         return paperDetailUnit;
+//     }
+//
+//     private Object getObjectId(String id) {
+//         if (id == null) {
+//             return null;
+//         }
+//         return ObjectId.isValid(id) ? new ObjectId(id) : id;
+//     }
+//
+//     private PaperDetail getPaperDetail(Document parentDocument, Map<String, PaperDetail> pdCache) {
+//         Object value = parentDocument.get("paperDetail");
+//         if (value == null || !(value instanceof DBRef)) {
+//             return null;
+//         }
+//         DBRef paperDetailDoc = (DBRef) value;
+//         String paperDetailId = String.valueOf(paperDetailDoc.getId());
+//         PaperDetail paperDetail = pdCache.get(paperDetailId);
+//         if (paperDetail == null) {
+//             paperDetail = mongoTemplate.findById(getObjectId(paperDetailId), PaperDetail.class);
+//             pdCache.put(paperDetailId, paperDetail);
+//         }
+//         return paperDetail;
+//     }
+//
+//     private Question getQuestion(Document parentDocument) {
+//         Object value = parentDocument.get("question");
+//         if (value == null || !(value instanceof DBRef)) {
+//             return null;
+//         }
+//         DBRef quesDoc = (DBRef) value;
+//         String quesId = String.valueOf(quesDoc.getId());
+//         Question question = mongoTemplate.findById(getObjectId(quesId), Question.class);
+//         return question;
+//     }
+// }

+ 4 - 2
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/PropertyRepo.java

@@ -19,7 +19,9 @@ public interface PropertyRepo extends MongoRepository<Property, String>, QueryBy
     //查询子节点
     List<Property> findByParentIdOrderByNumber(String parentId);
 
-    //根据课程属性id,一级id,属性名称查询
-    Property findByOrgIdAndCoursePropertyIdAndParentIdAndName(Long orgId, String coursePropertyId, String parentId, String name);
+	//根据课程属性id,一级id,属性名称查询
+    Property findByOrgIdAndCoursePropertyIdAndParentIdAndCode(Long orgId, String coursePropertyId, String parentId, String code);
+
+	List<Property> findByCoursePropertyIdOrderByNumber(String id);
 
 }

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

@@ -79,6 +79,16 @@ public class Paper extends MongoBaseEntity {
     @Transient
     private Map<String,Double> quesScore;
     
+    
+    /**
+     * 是否在仓库:1-在 ,空或者0不在
+     */
+    private Integer storage;
+    
+    /**
+     * 是否已调用:1-已调用,空或者0未调用
+     */
+    private Integer inUse;
 
     public String getName() {
         return name;
@@ -277,5 +287,22 @@ public class Paper extends MongoBaseEntity {
 		this.quesScore = quesScore;
 	}
 
+	public Integer getStorage() {
+		return storage;
+	}
+
+	public void setStorage(Integer storage) {
+		this.storage = storage;
+	}
+
+	public Integer getInUse() {
+		return inUse;
+	}
+
+	public void setInUse(Integer inUse) {
+		this.inUse = inUse;
+	}
+
+	
 	
 }

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

@@ -7,7 +7,7 @@ import java.io.Serializable;
 public class PaperSearchInfo implements Serializable {
 
     private static final long serialVersionUID = 1L;
-
+    
     private String name;// 试卷名称
 
     private String createTime;// 创建时间
@@ -27,6 +27,8 @@ public class PaperSearchInfo implements Serializable {
     private PaperStatus paperStatus; // 试卷审核状态
 
     private String[] paperIds;
+    
+    private Integer inUse;
 
     public String getName() {
         return name;
@@ -108,4 +110,13 @@ public class PaperSearchInfo implements Serializable {
 		this.lastModifyName = lastModifyName;
 	}
 
+	public Integer getInUse() {
+		return inUse;
+	}
+
+	public void setInUse(Integer inUse) {
+		this.inUse = inUse;
+	}
+
+	
 }

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

@@ -35,6 +35,8 @@ public class PaperStruct extends IdBase {
 
     private String orgId;// 机构ID
 
+    private Long courseId;
+    
     private String courseNo;
 
     private String courseName;
@@ -199,4 +201,13 @@ public class PaperStruct extends IdBase {
         this.courseName = courseName;
     }
 
+	public Long getCourseId() {
+		return courseId;
+	}
+
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
+
+    
 }

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

@@ -23,6 +23,9 @@ public class Property extends IdBase {
 
     @NotNull
     private String name;
+    
+    @NotNull
+    private String code;
 
     @NotNull
     private Integer number;//序号
@@ -77,4 +80,14 @@ public class Property extends IdBase {
         this.remark = remark;
     }
 
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+    
+    
+
 }

+ 2 - 0
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/dto/PropertyConvert.java

@@ -11,6 +11,7 @@ public class PropertyConvert {
         dto.setParentId(entity.getParentId());
         dto.setId(entity.getId());
         dto.setName(entity.getName());
+        dto.setCode(entity.getCode()); 
         dto.setNumber(entity.getNumber());
         dto.setRemark(entity.getRemark());
         return dto;
@@ -25,6 +26,7 @@ public class PropertyConvert {
         entity.setName(dto.getName());
         entity.setNumber(dto.getNumber());
         entity.setRemark(dto.getRemark());
+        entity.setCode(dto.getCode());
         return entity;
     }
 

+ 11 - 0
examcloud-core-questions-dao/src/main/java/cn/com/qmth/examcloud/core/questions/dao/entity/dto/PropertyDto.java

@@ -18,6 +18,8 @@ public class PropertyDto implements Serializable {
     private String parentId;
 
     private String id;
+    
+    private String code;
 
     private String name;
 
@@ -94,4 +96,13 @@ public class PropertyDto implements Serializable {
         this.remark = remark;
     }
 
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+    
 }

+ 17 - 16
examcloud-core-questions-service/pom.xml

@@ -1,21 +1,22 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-	<parent>
-		<artifactId>examcloud-core-questions</artifactId>
-		<groupId>cn.com.qmth.examcloud.core.questions</groupId>
-		<version>v4.0.2-RELEASE</version>
-	</parent>
-	<artifactId>examcloud-core-questions-service</artifactId>
-	<packaging>jar</packaging>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>examcloud-core-questions-service</artifactId>
+    <packaging>jar</packaging>
 
+    <parent>
+        <groupId>cn.com.qmth.examcloud</groupId>
+        <artifactId>examcloud-core-questions</artifactId>
+        <version>${revision}</version>
+    </parent>
 
-	<dependencies>
-		<dependency>
-			<groupId>cn.com.qmth.examcloud.core.questions</groupId>
-			<artifactId>examcloud-core-questions-dao</artifactId>
-			<version>${examcloud.version}</version>
-		</dependency>
-	</dependencies>
+    <dependencies>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud</groupId>
+            <artifactId>examcloud-core-questions-dao</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
 
 </project>

+ 1 - 1
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ClonePaperService.java

@@ -52,7 +52,7 @@ import cn.com.qmth.examcloud.web.filestorage.YunPathInfo;
  */
 @Service
 public class ClonePaperService {
-    protected static final Logger log = LoggerFactory.getLogger(ClonePaperService.class);
+    private static final Logger log = LoggerFactory.getLogger(ClonePaperService.class);
     @Autowired
     private PaperService paperService;
     @Autowired

+ 18 - 6
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/CoursePropertyService.java

@@ -1,11 +1,18 @@
 package cn.com.qmth.examcloud.core.questions.service;
 
-import cn.com.qmth.examcloud.core.questions.dao.entity.CourseProperty;
-import cn.com.qmth.examcloud.core.questions.dao.entity.dto.CoursePropertyDto;
+import java.util.List;
+import java.util.Map;
+
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
+import org.springframework.web.multipart.MultipartFile;
 
-import java.util.List;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
+import cn.com.qmth.examcloud.core.questions.dao.entity.CourseProperty;
+import cn.com.qmth.examcloud.core.questions.dao.entity.dto.CoursePropertyDto;
+import cn.com.qmth.examcloud.core.questions.service.bean.CoursePropertyImportInfo;
+import cn.com.qmth.examcloud.core.questions.service.bean.paper.PaperAnswerDomain;
 
 /**
  * @author weiwenhai
@@ -16,13 +23,14 @@ public interface CoursePropertyService {
 
     /**
      * 查询所有课程属性
+     * @param ud 
      */
-    List<CourseProperty> findAllByOrgId(Long orgId);
+    List<CourseProperty> findAllByOrgId(Long orgId, UserDataRule ud);
 
     /**
      * 查询所有课程属性带分页
      */
-    Page<CourseProperty> findList(CoursePropertyDto dto, Pageable pageable);
+    Page<CourseProperty> findList(CoursePropertyDto dto, Pageable pageable, UserDataRule userDataRule);
 
     /**
      * 保存课程属性
@@ -42,7 +50,7 @@ public interface CoursePropertyService {
     /**
      * 根据课程code查询所有开启的课程属性
      */
-    List<CourseProperty> findByEnable(String courseCode, Boolean enable);
+    List<CourseProperty> findByEnable(String courseCode, Boolean enable,User user,UserDataRule ud);
 
     /**
      * 批量启用/批量禁用
@@ -54,4 +62,8 @@ public interface CoursePropertyService {
      */
     void updateCoursePropertyStatus(String coursePropertyId, Boolean enable);
 
+	List<Map<String, Object>> importCourseProperty(UserDataRule ud, User user, Long rootOrgId, MultipartFile dataFile);
+
+	List<CoursePropertyImportInfo> exportCourseProperty(UserDataRule ud, User user, String name, Long courseId);
+
 }

+ 4 - 2
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ExtractConfigService.java

@@ -1,6 +1,7 @@
 package cn.com.qmth.examcloud.core.questions.service;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
 import cn.com.qmth.examcloud.core.questions.base.question.PaperDto;
 import cn.com.qmth.examcloud.core.questions.base.question.QuestionDto;
@@ -117,7 +118,7 @@ public interface ExtractConfigService {
      * @param courseNo
      * @return
      */
-    public Page<ExtractConfig> findPageExtractConfig(int currentPage, int pageSize, Long examId, String courseCode, String orgId);
+    public Page<ExtractConfig> findPageExtractConfig(int currentPage, int pageSize, Long examId, String courseCode, String orgId,UserDataRule ud );
 
     /**
      * 根据试卷id获取试卷结构
@@ -127,6 +128,7 @@ public interface ExtractConfigService {
     public Map<String, Object> extractPaper(String paperId);
 
 
+    @Deprecated
     public PaperDto getPaperDtoByPaperNew(String basePaperId);
 
     /**
@@ -143,7 +145,7 @@ public interface ExtractConfigService {
      * @param examId
      * @return
      */
-    public List<CouresInfo> findCourseByExtractConfig(Long examId, String orgId);
+    public List<CouresInfo> findCourseByExtractConfig(Long examId, String orgId,UserDataRule ud);
 
 	List<ExtractConfig> findExtractConfig(Long examId);
 

+ 1 - 1
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ImportDdCollegePaperService.java

@@ -60,7 +60,7 @@ import cn.com.qmth.examcloud.web.config.SystemProperties;
 @Service
 public class ImportDdCollegePaperService {
 
-    protected static final Logger log = LoggerFactory.getLogger(ImportDdCollegePaperService.class);
+    private static final Logger log = LoggerFactory.getLogger(ImportDdCollegePaperService.class);
 
     @Autowired
     private PaperRepo paperRepo;

+ 1 - 1
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ImportPaperFromJsonService.java

@@ -58,7 +58,7 @@ import cn.com.qmth.examcloud.core.questions.service.impl.CourseService;
 @Service
 public class ImportPaperFromJsonService {
 
-    protected static final Logger log = LoggerFactory.getLogger(ImportPaperFromJsonService.class);
+    private static final Logger log = LoggerFactory.getLogger(ImportPaperFromJsonService.class);
 
     @Autowired
     private PaperRepo paperRepo;

+ 50 - 57
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ImportPaperService.java

@@ -29,13 +29,13 @@ import org.docx4j.wml.P;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Example;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.commons.CommonsMultipartFile;
 
 import com.google.gson.Gson;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.base.BeanCopierUtil;
 import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
@@ -76,7 +76,7 @@ import cn.com.qmth.examcloud.web.filestorage.YunPathInfo;
 @Service
 public class ImportPaperService {
 
-    protected static final Logger log = LoggerFactory.getLogger(ImportPaperService.class);
+    private static final Logger log = LoggerFactory.getLogger(ImportPaperService.class);
 
     private static final String DOC_SUFFIX = ".docx";
 
@@ -172,10 +172,10 @@ public class ImportPaperService {
      * @throws Exception
      */
 
-    public Paper importPaper(Paper paper, User user, File file) throws Exception {
+    public Paper importPaper(Paper paper, User user, File file,UserDataRule ud) throws Exception {
         paperService.checkPaperNameNew(paper.getName(), user.getRootOrgId().toString());
         if (file.getName().endsWith(DOC_SUFFIX)) {
-            return processImportPaper(paper, user, file);
+            return processImportPaper(paper, user, file,ud);
         } else if (file.getName().endsWith(JSON_SUFFIX)) {
              return importPaperFromJsonService.processImportPaper(paper, user,
              file);
@@ -275,10 +275,20 @@ public class ImportPaperService {
      * @throws Exception
      */
 
-    public Paper processImportPaper(Paper paper, User user, File file) throws Exception {
+    public Paper processImportPaper(Paper paper, User user, File file,UserDataRule ud) throws Exception {
         try {
-			// 得到前台的课程代码
-			String courseNo = paper.getCourseNo();
+        	if (ud.assertEmptyQueryResult()) {
+    			throw new StatusException("没有数据权限");
+    		}
+
+            // 得到前台的课程代码
+            String courseNo = paper.getCourseNo();
+
+    		Course course = courseService.getCourse(user.getRootOrgId(), courseNo);
+    		if(ud.assertNeedQueryRefIds()&&!ud.stringRefIds().contains(course.getId())) {
+    			throw new StatusException("没有数据权限");
+    		}
+
 			WordprocessingMLPackage wordMLPackage;
 			WordprocessingMLPackage tmpWordMlPackage;
 			// WordprocessingMLPackage writePkg;
@@ -1135,39 +1145,28 @@ public class ImportPaperService {
             }
 
             for (CourseProperty courseProperty : courseProperties) {
-                Property propertyParent = new Property();
-                propertyParent.setCoursePropertyId(courseProperty.getId());
-                propertyParent.setParentId(Property.ROOT_PARENT_ID);
-                propertyParent.setName(firstProperty);
-
                 // 查询一级属性是否存在
-                List<Property> parentProperties = propertyRepo.findAll(Example.of(propertyParent));
+                Property proParent = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(
+                		Long.valueOf(paper.getOrgId()), courseProperty.getId(), Property.ROOT_PARENT_ID, firstProperty);
 
                 // 存在一级属性
-                if (parentProperties != null && parentProperties.size() > 0) {
+                if (proParent != null) {
                     isFirstEmpty = false;
-                    for (Property proParent : parentProperties) {
-                        Property propertySon = new Property();
-                        propertySon.setCoursePropertyId(courseProperty.getId());
-                        propertySon.setParentId(proParent.getId());
-                        propertySon.setName(secondProperty);
-
-                        // 查询二级属性
-                        List<Property> sonProperties = propertyRepo.findAll(Example.of(propertySon));
-
-                        // 存在二级属性
-                        if (sonProperties != null && sonProperties.size() > 0) {
-                            isSecondEmpty = false;
-                            for (Property proSon : sonProperties) {
-                                // 保存一级和二级属性
-                                QuesProperty quesProperty = new QuesProperty(proParent, proSon, courseProperty);
-                                String idNumber = quesProperty.getCoursePropertyName() + "-"
-                                        + quesProperty.getFirstProperty().getId() + "-"
-                                        + quesProperty.getSecondProperty().getId();
-                                quesProperty.setId(idNumber);
-                                quesProperties.add(quesProperty);
-                            }
-                        }
+
+                    // 查询二级属性
+                    Property proSon = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(
+                    		Long.valueOf(paper.getOrgId()), courseProperty.getId(), proParent.getId(), secondProperty);
+
+                    // 存在二级属性
+                    if (proSon != null) {
+                        isSecondEmpty = false;
+                        // 保存一级和二级属性
+                        QuesProperty quesProperty = new QuesProperty(proParent, proSon, courseProperty);
+                        String idNumber = quesProperty.getCoursePropertyName() + "-"
+                                + quesProperty.getFirstProperty().getId() + "-"
+                                + quesProperty.getSecondProperty().getId();
+                        quesProperty.setId(idNumber);
+                        quesProperties.add(quesProperty);
                     }
                 }
             }
@@ -1200,32 +1199,26 @@ public class ImportPaperService {
 
             for (CourseProperty courseProperty : courseProperties) {
                 // 查询一级属性
-                Property propertyParent = new Property();
-                propertyParent.setCoursePropertyId(courseProperty.getId());
-                propertyParent.setParentId(Property.ROOT_PARENT_ID);
-                propertyParent.setName(firstProperty);
-
-                List<Property> parentProperties = propertyRepo.findAll(Example.of(propertyParent));
+                Property proParent = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(
+                		Long.valueOf(paper.getOrgId()), courseProperty.getId(), Property.ROOT_PARENT_ID, firstProperty);
 
                 // 存在一级属性
-                if (parentProperties != null && parentProperties.size() > 0) {
+                if (proParent != null) {
                     isFirstEmpty = false;
-                    for (Property proParent : parentProperties) {
                         // 根据一级属性查询二级属性
-                        List<Property> sonProperties = propertyRepo.findByParentIdOrderByNumber(proParent.getId());
+                    List<Property> sonProperties = propertyRepo.findByParentIdOrderByNumber(proParent.getId());
 
-                        // 存在二级属性,跳过
-                        if (sonProperties != null && sonProperties.size() > 0) {
-                            continue;
-                        } else {
-                            isSecondEmpty = false;
-                            // 保存试题属性
-                            QuesProperty quesProperty = new QuesProperty(proParent, null, courseProperty);
-                            String idNumber = quesProperty.getCoursePropertyName() + "-"
-                                    + quesProperty.getFirstProperty().getId();
-                            quesProperty.setId(idNumber);
-                            quesProperties.add(quesProperty);
-                        }
+                    // 存在二级属性,跳过
+                    if (sonProperties != null && sonProperties.size() > 0) {
+                        continue;
+                    } else {
+                        isSecondEmpty = false;
+                        // 保存试题属性
+                        QuesProperty quesProperty = new QuesProperty(proParent, null, courseProperty);
+                        String idNumber = quesProperty.getCoursePropertyName() + "-"
+                                + quesProperty.getFirstProperty().getId();
+                        quesProperty.setId(idNumber);
+                        quesProperties.add(quesProperty);
                     }
                 }
             }

+ 23 - 20
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/PaperService.java

@@ -1,23 +1,19 @@
 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.api.commons.security.bean.UserDataRule;
 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.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.dao.entity.*;
 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 org.springframework.data.domain.Page;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 
 /**
@@ -27,6 +23,7 @@ import cn.com.qmth.examcloud.core.questions.service.bean.paper.PaperAnswerDomain
  * @description PaperService.java
  */
 public interface PaperService {
+
     /**
      * 查询所有已导入试卷
      *
@@ -35,7 +32,8 @@ public interface PaperService {
      * @param pageSize
      * @return
      */
-    public Page<Paper> getImportPapers(PaperSearchInfo paperSearchInfo, int curPage, int pageSize);
+
+    public Page<Paper> getImportPapers(PaperSearchInfo paperSearchInfo, int curPage, int pageSize, UserDataRule userDataRule);
 
     /**
      * 查询所有待审核和审核不通过的导入试卷
@@ -79,7 +77,7 @@ public interface PaperService {
      * @param pageSize
      * @return
      */
-    public Page<Paper> getGenPapers(PaperSearchInfo paperSearchInfo, int curPage, int pageSize);
+    public Page<Paper> getGenPapers(PaperSearchInfo paperSearchInfo, int curPage, int pageSize,UserDataRule ud);
 
     /**
      * 查询考试试卷
@@ -121,7 +119,7 @@ public interface PaperService {
      *
      * @param paperIds
      */
-    public Map<String, Object> deletePapers(List<String> paperIds,User user);
+    public void deletePapers(List<String> paperIds, User user);
 
     /**
      * 批量通过试卷
@@ -186,7 +184,7 @@ public interface PaperService {
      * @param questionId
      * @return
      */
-    public List<String> deleteImportQuestionById(String detailUnitId,String questionId, User user);
+    public List<String> deleteImportQuestionById(String detailUnitId, String questionId, User user);
 
     /**
      * 向试卷中插入一个试题
@@ -205,7 +203,7 @@ public interface PaperService {
      */
     public List<String> getPaperNamesByQuestionId(String questionId);
 
-    public Page<Question> listQuestionforSelect(String paperId, int curPage, int pageSize, QuesStructType quesType, User user, String quesBody);
+    public Page<Question> listQuestionforSelect(String paperId, int curPage, int pageSize, QuesStructType quesType, User user, String quesBody,UserDataRule ud);
 
     public Paper selectQuestionsToPaper(String paperId, String paperDetailId, List<Question> questions, User user);
 
@@ -213,7 +211,7 @@ public interface PaperService {
 
     public void checkPaperNameNew(String paperName, String orgId) throws Exception;
 
-    public Page<Paper> getPapersNotInIds(PaperSearchInfo paperSearchInfo, String[] ids, int curPage, int pageSize, PaperType paperType);
+    public Page<Paper> getPapersNotInIds(PaperSearchInfo paperSearchInfo, String[] ids, int curPage, int pageSize, PaperType paperType,UserDataRule ud);
 
     /**
      * 将选中的导入试卷复制为卷库试卷
@@ -310,7 +308,12 @@ public interface PaperService {
      */
     public double getQuestionTypeScore(String paperId, Integer publicityType, Integer difficultyType);
 
-	public List<PaperAnswerDomain> answerExport(Paper paper);
+    public List<PaperAnswerDomain> answerExport(Paper paper);
+
+    public void answerImport(Paper paper, MultipartFile dataFile);
+
+    Page<Paper> getStoragePaperPage(PaperSearchInfo paperSearchInfo, int curPage, int pageSize,UserDataRule ud);
+
+    void updatePapersStorage(List<String> paperIds, int storage, String orgId);
 
-	public void answerImport(Paper paper,MultipartFile dataFile);
 }

+ 83 - 30
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/PaperStructService.java

@@ -1,6 +1,8 @@
 package cn.com.qmth.examcloud.core.questions.service;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.basic.api.CourseCloudService;
 import cn.com.qmth.examcloud.core.basic.api.request.GetCourseReq;
 import cn.com.qmth.examcloud.core.basic.api.response.GetCourseResp;
@@ -14,6 +16,7 @@ import cn.com.qmth.examcloud.core.questions.dao.entity.*;
 import cn.com.qmth.examcloud.core.questions.dao.entity.dto.CoursePropertyNumberDto;
 import cn.com.qmth.examcloud.core.questions.dao.entity.dto.PaperDetailUnitStructDto;
 import cn.com.qmth.examcloud.core.questions.service.bean.dto.QuesNameDto;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
@@ -26,9 +29,7 @@ import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -57,25 +58,30 @@ public class PaperStructService {
      * @param pageSize
      * @return
      */
-    public Page<PaperStruct> getPaperStructs(PaperStructSearchInfo searchInfo, int curPage, int pageSize) {
+    public Page<PaperStruct> getPaperStructs(PaperStructSearchInfo searchInfo, int curPage, int pageSize, UserDataRule userDataRule) {
+         if (userDataRule.assertEmptyQueryResult()) {
+             return Page.empty();
+         }
+
+
         Query query = new Query();
         query.addCriteria(Criteria.where("orgId").is(searchInfo.getOrgId()));
+        if (userDataRule.assertNeedQueryRefIds()) {
+			Criteria c1 = Criteria.where("courseId").in(userDataRule.getRefIds());
+			Criteria c2 = Criteria.where("courseId").exists(false);
+			Criteria cr = new Criteria();
+			query.addCriteria(cr.orOperator(c1, c2));
+		}
         query.addCriteria(Criteria.where("type").is(searchInfo.getType()));
-        if (StringUtils.isNotBlank(searchInfo.getName())) {
-            String paperStructName = CommonUtils.escapeExprSpecialWord(searchInfo.getName());
-            query.addCriteria(Criteria.where("name").regex(".*?" + paperStructName + ".*"));
-        }
-        if (StringUtils.isNotBlank(searchInfo.getCreator())) {
-            String userName = CommonUtils.escapeExprSpecialWord(searchInfo.getCreator());
-            query.addCriteria(Criteria.where("creator").regex(".*?" + userName + ".*"));
-        }
-        //判断试卷结构
-        if (searchInfo.getType().equals("EXACT")) {
+
+        // 判断试卷结构
+        if (PaperStructType.EXACT.name().equals(searchInfo.getType())) {
             if (StringUtils.isNotBlank(searchInfo.getCourseNo())) {
-                if (!searchInfo.getCourseNo().equals("ALL")) {
+                if (!"ALL".equals(searchInfo.getCourseNo())) {
                     query.addCriteria(Criteria.where("courseNo").is(searchInfo.getCourseNo()));
                 }
             } else {
+                // “公用”情况
                 query.addCriteria(Criteria.where("courseNo").is(""));
             }
         } else {
@@ -84,27 +90,53 @@ public class PaperStructService {
             }
         }
 
-        long count = this.mongoTemplate.count(query, PaperStruct.class);
-        query.with(new Sort(new Sort.Order(Sort.Direction.DESC, "createTime")));
-        query.limit(pageSize);
-        query.skip((curPage - 1L) * pageSize);
+        if (StringUtils.isNotBlank(searchInfo.getName())) {
+            String paperStructName = CommonUtils.escapeExprSpecialWord(searchInfo.getName());
+            query.addCriteria(Criteria.where("name").regex(".*?" + paperStructName + ".*"));
+        }
 
-        List<PaperStruct> paperList = this.mongoTemplate.find(query, PaperStruct.class);
-        //远程调用rmi,查询课程名称
-        if (paperList != null && paperList.size() > 0) {
-            for (PaperStruct paperStruct : paperList) {
-                if (StringUtils.isBlank(paperStruct.getCourseNo())) {
-                    paperStruct.setCourseName("公用");
-                } else {
+        if (StringUtils.isNotBlank(searchInfo.getCreator())) {
+            String userName = CommonUtils.escapeExprSpecialWord(searchInfo.getCreator());
+            query.addCriteria(Criteria.where("creator").regex(".*?" + userName + ".*"));
+        }
+
+        long total = this.mongoTemplate.count(query, PaperStruct.class);
+        if (total == 0) {
+            return Page.empty();
+        }
+
+        PageRequest pageable = PageRequest.of(curPage - 1, pageSize);
+        query.with(Sort.by(Sort.Order.desc("createTime")));
+        query.skip(pageable.getOffset());
+        query.limit(pageable.getPageSize());
+
+        List<PaperStruct> paperStructs = this.mongoTemplate.find(query, PaperStruct.class);
+        if (CollectionUtils.isEmpty(paperStructs)) {
+            return Page.empty();
+        }
+
+        Map<String, String> courseNameMaps = new HashMap<>();
+
+        for (PaperStruct paperStruct : paperStructs) {
+            if (StringUtils.isBlank(paperStruct.getCourseNo())) {
+                paperStruct.setCourseName("公用");
+            } else {
+                String courseName = courseNameMaps.get(paperStruct.getCourseNo());
+
+                if (StringUtils.isEmpty(courseName)) {
                     GetCourseReq req = new GetCourseReq();
                     req.setRootOrgId(Long.valueOf(paperStruct.getOrgId()));
                     req.setCode(paperStruct.getCourseNo());
                     GetCourseResp resp = courseCloudService.getCourse(req);
-                    paperStruct.setCourseName(resp.getCourseBean().getName());
+                    courseName = resp.getCourseBean().getName();
+                    courseNameMaps.put(paperStruct.getCourseNo(), courseName);
                 }
+
+                paperStruct.setCourseName(courseName);
             }
         }
-        return new PageImpl<PaperStruct>(paperList, new PageRequest(curPage - 1, pageSize), count);
+
+        return new PageImpl<>(paperStructs, pageable, total);
     }
 
     /**
@@ -113,14 +145,23 @@ public class PaperStructService {
      * @param searchInfo
      * @return
      */
-    public List<PaperStruct> getPaperStructs(PaperStructSearchInfo searchInfo) {
+    public List<PaperStruct> getPaperStructs(PaperStructSearchInfo searchInfo,UserDataRule ud) {
+    	if (ud.assertEmptyQueryResult()) {
+			return new ArrayList<>();
+		}
         Query query = new Query();
         if (StringUtils.isNotBlank(searchInfo.getCourseNo())) {
             query.addCriteria(Criteria.where("courseNo")
                     .in("", searchInfo.getCourseNo()));
         }
         query.addCriteria(Criteria.where("orgId").is(searchInfo.getOrgId()));
-        query.with(new Sort(new Sort.Order(Sort.Direction.DESC, "createTime")));
+		if (ud.assertNeedQueryRefIds()) {
+			Criteria c1 = Criteria.where("courseId").in(ud.getRefIds());
+			Criteria c2 = Criteria.where("courseId").exists(false);
+			Criteria cr = new Criteria();
+			cr.orOperator(c1, c2);
+		}
+        query.with(Sort.by(Sort.Order.desc("createTime")));
         List<PaperStruct> paperList = this.mongoTemplate.find(query, PaperStruct.class);
         return paperList;
     }
@@ -148,6 +189,18 @@ public class PaperStructService {
      * @return
      */
     public PaperStruct save(PaperStruct paperStruct, User user) {
+    	if (StringUtils.isNotBlank(paperStruct.getCourseNo())) {
+    		GetCourseReq req = new GetCourseReq();
+            req.setRootOrgId(Long.valueOf(paperStruct.getOrgId()));
+            req.setCode(paperStruct.getCourseNo());
+            GetCourseResp resp = courseCloudService.getCourse(req);
+			if (resp.getCourseBean() == null) {
+				throw new StatusException("500","课程不存在");
+			}
+			paperStruct.setCourseId(resp.getCourseBean().getId());
+		} else {
+			paperStruct.setCourseId(null);
+		}
         if (StringUtils.isNotBlank(paperStruct.getId())) {
             PaperStruct oldPaperStruct = Model.of(paperStructRepo.findById(paperStruct.getId()));
             PaperStruct rps = null;

+ 5 - 2
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/QuesService.java

@@ -7,6 +7,7 @@ import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Service;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
 import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
 import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionSearchCondition;
@@ -38,9 +39,11 @@ public interface QuesService {
      * @param searchCondition
      * @param curPage
      * @param pageSize
+     * @param refIds
+     * @param userId
      * @return
      */
-    public Page<Question> findAll(QuestionSearchCondition searchCondition, int curPage, int pageSize);
+    public Page<Question> findAll(QuestionSearchCondition searchCondition, int curPage, int pageSize,UserDataRule ud);
 
     public void formatQuesCondition(QuestionSearchCondition searchCondition);
 
@@ -52,7 +55,7 @@ public interface QuesService {
      * @param pageSize
      * @return
      */
-    public Page<Question> findByIdExclude(Set<String> idSet, String courseNo, QuesStructType quesType, int curPage, int pageSize, Long orgId, String quesBody);
+    public Page<Question> findByIdExclude(Set<String> idSet, String courseNo, QuesStructType quesType, int curPage, int pageSize, Long orgId, String quesBody,UserDataRule ud);
 
     /**
      * 按ID获取试题

+ 1 - 1
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/UpYunService.java

@@ -11,7 +11,7 @@ import cn.com.qmth.examcloud.core.questions.service.config.SysProperty;
 
 @Service
 public class UpYunService {
-    protected static final Logger log = LoggerFactory.getLogger(UpYunService.class);
+    private static final Logger log = LoggerFactory.getLogger(UpYunService.class);
 
     public static final String TEMP_FILE_EXP = "docxExport/";
 

+ 98 - 0
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/CoursePropertyImportInfo.java

@@ -0,0 +1,98 @@
+package cn.com.qmth.examcloud.core.questions.service.bean;
+
+import cn.com.qmth.examcloud.core.questions.base.excel.ExcelProperty;
+
+public class CoursePropertyImportInfo {
+
+	@ExcelProperty(name = "课程代码", width = 30, index = 1)
+	private String courseCode;
+	
+	@ExcelProperty(name = "课程名称", width = 30, index = 2)
+	private String courseName;
+	
+	private Long courseId;
+	
+	@ExcelProperty(name = "属性名称", width = 30, index = 3)
+	private String propertyName;
+	
+	@ExcelProperty(name = "一级属性编号", width = 30, index = 4)
+	private String firstPropertyCode;
+	
+	@ExcelProperty(name = "一级属性内容", width = 30, index = 5)
+	private String firstPropertyName;
+	
+	@ExcelProperty(name = "二级属性编号", width = 30, index = 6)
+	private String secondPropertyCode;
+	
+	@ExcelProperty(name = "二级属性内容", width = 30, index = 7)
+	private String secondPropertyName;
+
+	
+
+	public Long getCourseId() {
+		return courseId;
+	}
+
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
+
+	public String getPropertyName() {
+		return propertyName;
+	}
+
+	public void setPropertyName(String propertyName) {
+		this.propertyName = propertyName;
+	}
+
+	public String getFirstPropertyCode() {
+		return firstPropertyCode;
+	}
+
+	public void setFirstPropertyCode(String firstPropertyCode) {
+		this.firstPropertyCode = firstPropertyCode;
+	}
+
+	public String getFirstPropertyName() {
+		return firstPropertyName;
+	}
+
+	public void setFirstPropertyName(String firstPropertyName) {
+		this.firstPropertyName = firstPropertyName;
+	}
+
+	public String getSecondPropertyCode() {
+		return secondPropertyCode;
+	}
+
+	public void setSecondPropertyCode(String secondPropertyCode) {
+		this.secondPropertyCode = secondPropertyCode;
+	}
+
+	public String getSecondPropertyName() {
+		return secondPropertyName;
+	}
+
+	public void setSecondPropertyName(String secondPropertyName) {
+		this.secondPropertyName = secondPropertyName;
+	}
+
+	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;
+	}
+	
+	
+	
+}

+ 20 - 1
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/dto/PaperDetailUnitExp.java

@@ -33,11 +33,13 @@ public class PaperDetailUnitExp implements Serializable, Comparable<PaperDetailU
      * 一级属性名称(默认取question中第一个)
      */
     private String firstName;
+    private String firstCode;
 
     /**
      * 二级属性名称(默认取question中第一个)
      */
     private String secondName;
+    private String secondCode;
 
     /**
      * 公开,非公开
@@ -155,8 +157,25 @@ public class PaperDetailUnitExp implements Serializable, Comparable<PaperDetailU
     public void setPublicity(String publicity) {
         this.publicity = publicity;
     }
+    
 
-    /**
+    public String getFirstCode() {
+		return firstCode;
+	}
+
+	public void setFirstCode(String firstCode) {
+		this.firstCode = firstCode;
+	}
+
+	public String getSecondCode() {
+		return secondCode;
+	}
+
+	public void setSecondCode(String secondCode) {
+		this.secondCode = secondCode;
+	}
+
+	/**
      * 按照number排序
      *
      * @param paperDetailUnitExp

+ 1 - 1
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/converter/PrintExamPaperService.java

@@ -54,7 +54,7 @@ import cn.com.qmth.examcloud.core.questions.service.PaperService;
 @Service
 public class PrintExamPaperService {
 
-    private static Logger log = LoggerFactory.getLogger(PrintExamPaperService.class);
+    private static final Logger log = LoggerFactory.getLogger(PrintExamPaperService.class);
 
     @Autowired
     private PaperService paperService;

+ 3 - 1
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/export/ExportPaperAbstractService.java

@@ -1176,9 +1176,11 @@ public class ExportPaperAbstractService {
                 question.setQuesAnswerWord(null);
 
                 if (CollectionUtils.isNotEmpty(subQuestions)) {
+                	int index=0;
                     for (Question subQues : subQuestions) {
+                    	index++;
                         Map<String, String> params = new HashMap<>();
-                        params.put("number", String.valueOf(subQues.getNumber()));
+                        params.put("number", String.valueOf(index));
                         subQues.setQuesParams(params);
                         subQues.setQuesBodyWord(null);
                         subQues.setQuesAnswerWord(null);

+ 461 - 36
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/CoursePropertyServiceImpl.java

@@ -1,25 +1,57 @@
 package cn.com.qmth.examcloud.core.questions.service.impl;
 
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionAspectSupport;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.helpers.poi.ExcelReader;
+import cn.com.qmth.examcloud.commons.util.PathUtil;
+import cn.com.qmth.examcloud.core.basic.api.CourseCloudService;
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.basic.api.request.GetCourseReq;
+import cn.com.qmth.examcloud.core.basic.api.response.GetCourseResp;
 import cn.com.qmth.examcloud.core.questions.base.Constants;
 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.dao.CoursePropertyRepo;
+import cn.com.qmth.examcloud.core.questions.dao.PropertyRepo;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Course;
 import cn.com.qmth.examcloud.core.questions.dao.entity.CourseProperty;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Property;
 import cn.com.qmth.examcloud.core.questions.dao.entity.dto.CoursePropertyDto;
 import cn.com.qmth.examcloud.core.questions.service.CoursePropertyService;
+import cn.com.qmth.examcloud.core.questions.service.bean.CoursePropertyImportInfo;
 import cn.com.qmth.examcloud.support.cache.CacheHelper;
 import cn.com.qmth.examcloud.support.cache.bean.CourseCacheBean;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Example;
-import org.springframework.data.domain.ExampleMatcher;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.Pageable;
-import org.springframework.stereotype.Service;
-
-import java.util.Date;
-import java.util.List;
+import cn.com.qmth.examcloud.web.config.SystemProperties;
 
 /**
  * @author weiwenhai
@@ -29,12 +61,36 @@ import java.util.List;
 @Service("coursePropertyService")
 public class CoursePropertyServiceImpl implements CoursePropertyService {
 
+	private static final Logger log = LoggerFactory.getLogger(CoursePropertyServiceImpl.class);
+	
+	private static final String[] EXCEL_HEADER = new String[] { "课程代码", "课程名称", "属性名称", "一级属性编号", "一级属性内容", "二级属性编号",
+	"二级属性内容" };
+
     @Autowired
     private CoursePropertyRepo coursePropertyRepo;
+    
+	@Autowired
+	private PropertyRepo propertyRepo;
+    
+    @Autowired
+    private CourseCloudService courseCloudService;
+
+    @Autowired
+    private MongoTemplate mongoTemplate;
+    
+    @Autowired
+    private SystemProperties systemProperties;
 
     @Override
-    public List<CourseProperty> findAllByOrgId(Long orgId) {
-        return coursePropertyRepo.findByOrgId(orgId);
+    public List<CourseProperty> findAllByOrgId(Long orgId,UserDataRule ud) {
+    	if (ud.assertEmptyQueryResult()) {
+            return Lists.newArrayList();
+        }
+    	if (ud.assertNeedQueryRefIds()) {
+    		return coursePropertyRepo.findByOrgIdAndCourseIdIn(orgId,ud.getRefIds());
+    	}else {
+    		return coursePropertyRepo.findByOrgId(orgId);
+    	}
     }
 
     @Override
@@ -73,47 +129,57 @@ public class CoursePropertyServiceImpl implements CoursePropertyService {
     }
 
     @Override
-    public Page<CourseProperty> findList(CoursePropertyDto dto, Pageable pageable) {
-        CourseProperty params = new CourseProperty();
+    public Page<CourseProperty> findList(CoursePropertyDto dto, Pageable pageable, UserDataRule userDataRule) {
+        if (userDataRule.assertEmptyQueryResult()) {
+            return Page.empty();
+        }
+
+        Query query = new Query();
 
         if (dto.getOrgId() != null) {
-            params.setOrgId(dto.getOrgId());
+            query.addCriteria(Criteria.where("orgId").is(dto.getOrgId()));
         }
 
         if (dto.getCourseId() != null) {
-            params.setCourseId(dto.getCourseId());
+            if (!userDataRule.getRefIds().contains(dto.getCourseId())) {
+                return Page.empty();
+            }
+            query.addCriteria(Criteria.where("courseId").is(dto.getCourseId()));
+        } else {
+            if (userDataRule.assertNeedQueryRefIds()) {
+                query.addCriteria(Criteria.where("courseId").in(userDataRule.getRefIds()));
+            }
         }
 
-        if (dto.getEnable() != null) {
-            params.setEnable(dto.getEnable());
+        if (StringUtils.isNotBlank(dto.getName())) {
+            query.addCriteria(Criteria.where("name").regex(dto.getName().trim()));
         }
 
-        Example<CourseProperty> query;
-        if (!StringUtils.isEmpty(dto.getName())) {
-            params.setName(dto.getName());
-
-            ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("name", match -> match.contains());
-
-            query = Example.of(params, matcher);
-        } else {
-            query = Example.of(params);
+        if (dto.getEnable() != null) {
+            query.addCriteria(Criteria.where("enable").is(dto.getEnable()));
         }
 
-        Page<CourseProperty> page = coursePropertyRepo.findAll(query, pageable);
+        long total = mongoTemplate.count(query, CourseProperty.class);
+        if (total == 0) {
+            return Page.empty();
+        }
 
-        List<CourseProperty> list = page.getContent();
-        if (CollectionUtils.isEmpty(list)) {
-            return page;
+        query.with(Sort.by(new Sort.Order(Sort.Direction.DESC, "updateTime")));
+        query.skip(pageable.getOffset());
+        query.limit(pageable.getPageSize());
+        List<CourseProperty> courseProperties = mongoTemplate.find(query, CourseProperty.class);
+        if (CollectionUtils.isEmpty(courseProperties)) {
+            return Page.empty();
         }
 
-        for (CourseProperty courseProperty : list) {
+        for (CourseProperty courseProperty : courseProperties) {
             CourseCacheBean course = CacheHelper.getCourse(courseProperty.getCourseId());
             if (course != null) {
                 courseProperty.setCourseName(course.getName());
             }
         }
 
-        return page;
+        return new PageImpl<>(courseProperties, pageable, total);
     }
 
     @Override
@@ -127,8 +193,21 @@ public class CoursePropertyServiceImpl implements CoursePropertyService {
     }
 
     @Override
-    public List<CourseProperty> findByEnable(String courseCode, Boolean enable) {
-        return coursePropertyRepo.findByCourseCodeAndEnable(courseCode, enable);
+    public List<CourseProperty> findByEnable(String courseCode, Boolean enable,User user,UserDataRule ud) {
+        
+        if (ud.assertEmptyQueryResult()) {
+			return new ArrayList<>();
+		}
+		Query query = new Query();
+		if (ud.assertNeedQueryRefIds()) {
+			query.addCriteria(Criteria.where("courseId").in(ud.getRefIds()));
+		}
+		query.addCriteria(Criteria.where("orgId").is(user.getRootOrgId()));
+		query.addCriteria(Criteria.where("courseCode").is(courseCode));
+		
+		query.addCriteria(Criteria.where("enable").is(true));
+		List<CourseProperty> paperList = this.mongoTemplate.find(query, CourseProperty.class);
+		return paperList;
     }
 
     @Override
@@ -161,5 +240,351 @@ public class CoursePropertyServiceImpl implements CoursePropertyService {
         courseProperty.setUpdateTime(new Date());
         coursePropertyRepo.save(courseProperty);
     }
+    
+    @Transactional
+	@Override
+	public List<Map<String, Object>> importCourseProperty(UserDataRule ud, User user, Long rootOrgId, MultipartFile dataFile) {
+    	File file = new File(systemProperties.getTempDataDir() + File.separator + UUID.randomUUID() + ".xlsx");
+        try {
+            file.createNewFile();
+            dataFile.transferTo(file);
+            return disposeImportCourseProperty(ud, user, rootOrgId, file);
+        } catch (Exception e) {
+            throw new StatusException("500", "导入课程属性失败:" + e.getMessage(), e);
+        } finally {
+            FileUtil.deleteFile(file.getAbsolutePath());
+        }
+    }
+    
+	private List<Map<String, Object>> disposeImportCourseProperty(UserDataRule ud, User user, Long rootOrgId, File file) {
+		List<String[]> lineList = null;
+		try {
+			lineList = ExcelReader.readSheetBySax(PathUtil.getCanonicalPath(file), 1, 7);
+		} catch (Exception e) {
+			throw new StatusException("100110", "Excel 解析失败",e );
+		}
+
+		if (CollectionUtils.isEmpty(lineList)) {
+			throw new StatusException("100111", "Excel无内容");
+		}
+
+		if (10001 < lineList.size()) {
+			throw new StatusException("100112", "数据行数不能超过10000");
+		}
+
+		List<Map<String, Object>> failRecords = Collections.synchronizedList(new ArrayList<Map<String, Object>>());
+
+		List<CoursePropertyImportInfo> courseList = Lists.newArrayList();
+		Map<String, Course> cousreMap = new HashMap<>();
+		for (int i = 0; i < lineList.size(); i++) {
+			String[] line = lineList.get(i);
+			if (0 == i) {
+				if (headerError(line)) {
+					throw new StatusException("100111", "Excel表头错误");
+				}
+				continue;
+			}
+
+			boolean hasError = false;
+			StringBuilder msg = new StringBuilder();
+
+			CoursePropertyImportInfo courseInfo = new CoursePropertyImportInfo();
+
+			String courseCode = trimAndNullIfBlank(line[0]);
+			if (StringUtils.isBlank(courseCode)) {
+				msg.append("  课程代码不能为空");
+				hasError = true;
+			} else if (!checkCourseExist(cousreMap, courseCode, rootOrgId)) {
+				msg.append("  课程代码不存在");
+				hasError = true;
+			} else if ("false".equals(cousreMap.get(courseCode).getEnable())) {
+				msg.append("  课程已禁用");
+				hasError = true;
+			}
+			
+			courseInfo.setCourseCode(courseCode);
+			
+			if(cousreMap.get(courseCode)!=null) {
+				courseInfo.setCourseId(Long.valueOf(cousreMap.get(courseCode).getId()));
+			}
+
+			String propertyName = trimAndNullIfBlank(line[2]);
+			if (StringUtils.isBlank(propertyName)) {
+				msg.append("  属性名称不能为空");
+				hasError = true;
+			} else if (propertyName.length() > 100) {
+				msg.append("  属性名称不能超过100个字符");
+				hasError = true;
+			}
+			courseInfo.setPropertyName(propertyName);
+
+			String firstCode = trimAndNullIfBlank(line[3]);
+			if (StringUtils.isBlank(firstCode)) {
+				msg.append("  一级属性编号不能为空");
+				hasError = true;
+			} else if (firstCode.length() > 100) {
+				msg.append("  一级属性编号不能超过100个字符");
+				hasError = true;
+			}
+			courseInfo.setFirstPropertyCode(firstCode);
+
+			String firstName = trimAndNullIfBlank(line[4]);
+			if (StringUtils.isNotBlank(firstName) && firstName.length() > 100) {
+				msg.append("  一级属性内容不能超过100个字符");
+				hasError = true;
+			}
+			courseInfo.setFirstPropertyName(firstName);
+
+			String secCode = trimAndNullIfBlank(line[5]);
+			if (StringUtils.isNotBlank(secCode) && secCode.length() > 100) {
+				msg.append("  二级属性编号不能超过100个字符");
+				hasError = true;
+			}
+			courseInfo.setSecondPropertyCode(secCode);
+
+			String secName = trimAndNullIfBlank(line[6]);
+			if (StringUtils.isNotBlank(secName) && secName.length() > 100) {
+				msg.append("  二级属性内容不能超过100个字符");
+				hasError = true;
+			}
+			courseInfo.setSecondPropertyName(secName);
+
+			if (hasError) {
+				failRecords.add(newError(i + 1, msg.toString()));
+			} else {
+				courseList.add(courseInfo);
+			}
+
+		}
+
+		if (CollectionUtils.isNotEmpty(failRecords)) {
+			return failRecords;
+		}
+
+		for (int i = 0; i < courseList.size(); i++) {
+			CoursePropertyImportInfo cur = courseList.get(i);
+			try {
+				saveCoursePropertyInfo(user, ud, cur);
+			} catch (StatusException e) {
+				failRecords.add(newError(i + 2, e.getDesc()));
+			} catch (Exception e) {
+				failRecords.add(newError(i + 2, "系统异常"));
+				log.error("课程属性导入系统异常", e);
+			}
+		}
+		if (CollectionUtils.isNotEmpty(failRecords)) {
+			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+		}
+
+		return failRecords;
+	}
+
+	private void saveCoursePropertyInfo(User user, UserDataRule ud, CoursePropertyImportInfo cur) {
+		if(ud.assertEmptyQueryResult()) {
+			throw new StatusException(" 课程不在该账号负责的课程中");
+		}
+		if (ud.assertNeedQueryRefIds() && !ud.getRefIds().contains(cur.getCourseId())) {
+			throw new StatusException(" 课程不在该账号负责的课程中");
+		}
+		CourseProperty c = coursePropertyRepo.findByOrgIdAndCourseIdAndName(user.getRootOrgId(),cur.getCourseId(), cur.getPropertyName());
+		if(c==null) {
+			c=new CourseProperty();
+			c.setCourseId(cur.getCourseId());
+			c.setCourseCode(cur.getCourseCode());
+			c.setName(cur.getPropertyName());
+			c.setOrgId(user.getRootOrgId());
+			c.setEnable(true);
+			coursePropertyRepo.save(c);
+		}
+		
+		Property first=propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(user.getRootOrgId(), c.getId(), Property.ROOT_PARENT_ID, cur.getFirstPropertyCode());
+		if(first!=null) {
+			first.setName(cur.getFirstPropertyName());
+		}else {
+			if(StringUtils.isBlank(cur.getFirstPropertyName())) {
+				throw new StatusException(" 一级属性内容不能为空");
+			}
+			first=new Property();
+			first.setParentId(Property.ROOT_PARENT_ID);
+			first.setOrgId(user.getRootOrgId());
+			first.setCoursePropertyId(c.getId());
+			first.setCode(cur.getFirstPropertyCode());
+			first.setName(cur.getFirstPropertyName());
+			Integer number = 0;
+            List<Property> parentProperties = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdOrderByNumber(first.getOrgId(), first.getCoursePropertyId(), first.getParentId());
+            if (parentProperties != null && parentProperties.size() > 0) {
+                number = parentProperties.get(parentProperties.size() - 1).getNumber();
+            }
+
+            first.setNumber(number + 1);
+		}
+		propertyRepo.save(first);
+		
+		if(StringUtils.isNotBlank(cur.getSecondPropertyCode())&&StringUtils.isBlank(cur.getSecondPropertyName())) {
+			throw new StatusException(" 二级属性内容不能为空");
+		}
+		if(StringUtils.isNotBlank(cur.getSecondPropertyCode())){
+			Property sec=propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(user.getRootOrgId(), c.getId(), first.getId(), cur.getSecondPropertyCode());
+			if(sec!=null) {
+				sec.setName(cur.getSecondPropertyName());
+			}else {
+				sec=new Property();
+				sec.setParentId(first.getId());
+				sec.setOrgId(user.getRootOrgId());
+				sec.setCoursePropertyId(c.getId());
+				sec.setCode(cur.getSecondPropertyCode());
+				sec.setName(cur.getSecondPropertyName());
+				Integer number = 0;
+	            List<Property> parentProperties = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdOrderByNumber(sec.getOrgId(), sec.getCoursePropertyId(), sec.getParentId());
+	            if (parentProperties != null && parentProperties.size() > 0) {
+	                number = parentProperties.get(parentProperties.size() - 1).getNumber();
+	            }
+
+	            sec.setNumber(number + 1);
+			}
+			propertyRepo.save(sec);
+		}
+		
+	}
+
+	private boolean checkCourseExist(Map<String, Course> cousre, String courseCode, Long rootOrgId) {
+		if (cousre.get(courseCode) != null) {
+			return true;
+		}
+		GetCourseReq req=new GetCourseReq();
+		req.setRootOrgId(rootOrgId);
+		req.setCode(courseCode);
+		GetCourseResp res=courseCloudService.getCourse(req);
+		if (res.getCourseBean() == null) {
+			return false;
+		} else {
+			cousre.put(courseCode, of(res.getCourseBean()));
+			return true;
+		}
+	}
+	
+	private Course of(CourseBean b) {
+		Course c=new Course();
+		c.setCode(b.getCode());
+		c.setEnable(b.getEnable()!=null?null:b.getEnable().toString());
+		c.setId(b.getId()+"");
+		c.setLevel(b.getLevel());
+		c.setName(b.getName());
+		c.setOrgId(b.getRootOrgId()+"");
+		return c;
+	}
+
+	private Map<String, Object> newError(int lineNum, String msg) {
+		Map<String, Object> map = Maps.newHashMap();
+		map.put("lineNum", lineNum);
+		map.put("msg", msg);
+		return map;
+	}
+
+	private String trimAndNullIfBlank(String s) {
+		if (StringUtils.isBlank(s)) {
+			return null;
+		}
+		return s.trim();
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @param header
+	 * @author WANGWEI
+	 */
+	private boolean headerError(String[] header) {
+		for (int i = 0; i < EXCEL_HEADER.length; i++) {
+			if (null == header[i]) {
+				return true;
+			}
+			if (!EXCEL_HEADER[i].equals(header[i].trim())) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public List<CoursePropertyImportInfo> exportCourseProperty(UserDataRule userDataRule, User user, String name, Long courseId) {
+		if (userDataRule.assertEmptyQueryResult()) {
+            return Lists.newArrayList();
+        }
+
+        Query query = new Query();
+
+        query.addCriteria(Criteria.where("orgId").is(user.getRootOrgId()));
+
+        if (courseId != null) {
+            if (!userDataRule.getRefIds().contains(courseId)) {
+                return Lists.newArrayList();
+            }
+            query.addCriteria(Criteria.where("courseId").is(courseId));
+        } else {
+            if (userDataRule.assertNeedQueryRefIds()) {
+                query.addCriteria(Criteria.where("courseId").in(userDataRule.getRefIds()));
+            }
+        }
+
+        if (StringUtils.isNotBlank(name)) {
+            query.addCriteria(Criteria.where("name").regex(name.trim()));
+        }
+
+
+        query.with(Sort.by(new Sort.Order(Sort.Direction.DESC, "updateTime")));
+        List<CourseProperty> coursePros = mongoTemplate.find(query, CourseProperty.class);
+
+		List<CoursePropertyImportInfo> ret=new ArrayList<>();
+		if(!CollectionUtils.isEmpty(coursePros)) {
+			for(CourseProperty cp:coursePros) {
+				fillProperty(ret, cp);
+			}
+		}
+		return ret;
+	}
+	
+	private void fillProperty(List<CoursePropertyImportInfo> ret,CourseProperty cp) {
+		List<Property> ps=propertyRepo.findByCoursePropertyIdOrderByNumber(cp.getId());
+		if(!CollectionUtils.isEmpty(ps)) {
+			Map<String,List<Property>> secPs=new HashMap<>();
+			for(Property p:ps) {
+				if(!Property.ROOT_PARENT_ID.equals(p.getParentId())) {
+					List<Property> list=secPs.get(p.getParentId());
+					if(list==null) {
+						list=new ArrayList<>();
+						secPs.put(p.getParentId(), list);
+					}
+					list.add(p);
+				}
+			}
+			
+			for(Property p:ps) {
+				if(Property.ROOT_PARENT_ID.equals(p.getParentId())) {
+					CoursePropertyImportInfo info=new CoursePropertyImportInfo();
+					info.setCourseCode(cp.getCourseCode());
+					info.setCourseName(CacheHelper.getCourse(cp.getCourseId()).getName());
+					info.setPropertyName(cp.getName());
+					info.setFirstPropertyCode(p.getCode());
+					info.setFirstPropertyName(p.getName());
+					ret.add(info);
+					List<Property> secPsList=secPs.get(p.getId());
+					if(!CollectionUtils.isEmpty(secPsList)) {
+						for(Property sec:secPsList) {
+							CoursePropertyImportInfo secinfo=new CoursePropertyImportInfo();
+							secinfo.setCourseCode(cp.getCourseCode());
+							secinfo.setCourseName(CacheHelper.getCourse(cp.getCourseId()).getName());
+							secinfo.setPropertyName(cp.getName());
+							secinfo.setFirstPropertyCode(p.getCode());
+							secinfo.setFirstPropertyName(p.getName());
+							secinfo.setSecondPropertyCode(sec.getCode());
+							secinfo.setSecondPropertyName(sec.getName());
+							ret.add(secinfo);
+						}
+					}
+				}
+			}
+		}
+	}
 
 }

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

@@ -25,6 +25,8 @@ import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.query.Criteria;
@@ -36,8 +38,6 @@ 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.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.FileDisposeUtil;
 import cn.com.qmth.examcloud.core.questions.base.IdUtils;
@@ -87,7 +87,7 @@ import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
 @Service("exportPaperService")
 public class ExportPaperServiceImpl implements ExportPaperService {
 
-    protected ExamCloudLog log = ExamCloudLogFactory.getLog(this.getClass());
+    private static final Logger LOG = LoggerFactory.getLogger(ExportPaperServiceImpl.class);
 
     public static final String TEMP_FILE_EXP = "docxExport/";
 
@@ -167,7 +167,7 @@ public class ExportPaperServiceImpl implements ExportPaperService {
                     exportPaperAbstractService.downloadPaperAnswer(dto);
                 }
             } catch (Exception e) {
-                log.error(e.getMessage(), e);
+                LOG.error(e.getMessage(), e);
                 throw new StatusException("100001", e.getMessage());
             }
             // 下载考试说明 2018-2-27 weiwehai
@@ -176,20 +176,20 @@ public class ExportPaperServiceImpl implements ExportPaperService {
              * StringUtils.isNotBlank(paper.getExamRemark())){
              * downExamRemark(paper,zipFileName); }
              */
-            log.debug("开始压缩成zip...");
+            LOG.debug("开始压缩成zip...");
             startTime = System.currentTimeMillis();
             FileDisposeUtil.fileToZip(TEMP_FILE_EXP + File.separator + zipFileName, TEMP_FILE_EXP, zipFileName);
             FileDisposeUtil.downloadFile(paper.getName() + "_" + paper.getCourse().getCode() + ".zip",
                     TEMP_FILE_EXP + File.separator + zipFileName + ".zip", response);
             long endTime = System.currentTimeMillis();
-            log.debug("下载zip耗时:" + (endTime - startTime) + "ms");
+            LOG.debug("下载zip耗时:" + (endTime - startTime) + "ms");
         } finally {
             long endTime = System.currentTimeMillis();
 			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");
+            LOG.debug("删除文件耗时:" + (deleteTime - endTime) + "ms");
         }
         StringBuilder paperInfo=new StringBuilder();
 		paperInfo.append("课程:"+paper.getCourse().getName()+"("+paper.getCourse().getCode()+")");
@@ -577,9 +577,9 @@ public class ExportPaperServiceImpl implements ExportPaperService {
             outputStream.write(b);
             outputStream.flush();
         } catch (FileNotFoundException e) {
-            log.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         } catch (IOException e) {
-            log.error(e.getMessage(), e);
+            LOG.error(e.getMessage(), e);
         } finally {
             IOUtils.closeQuietly(outputStream);
         }
@@ -786,7 +786,7 @@ public class ExportPaperServiceImpl implements ExportPaperService {
             }
         }
 
-        log.info("[WriteExcel] " + filePath);
+        LOG.info("[WriteExcel] " + filePath);
 
         File file = new File(filePath);
         OutputStream out = new FileOutputStream(file);

+ 1 - 1
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExportStructureServiceImpl.java

@@ -98,7 +98,7 @@ public class ExportStructureServiceImpl implements ExportStructureService {
         query.limit(pageSize);
         query.skip((curPage - 1L) * pageSize);
         List<ExportStructure> list = this.mongoTemplate.find(query, ExportStructure.class);
-        return new PageImpl<ExportStructure>(list, new PageRequest(curPage - 1, pageSize), count);
+        return new PageImpl<ExportStructure>(list, PageRequest.of(curPage - 1, pageSize), count);
     }
 
     @Override

+ 4 - 4
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExportTemplateServiceImpl.java

@@ -10,6 +10,8 @@ import java.util.List;
 import javax.persistence.criteria.Predicate;
 
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
@@ -24,8 +26,6 @@ import org.springframework.web.multipart.MultipartFile;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
-import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
 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.dto.ExportTemplateDto;
@@ -40,7 +40,7 @@ import cn.com.qmth.examcloud.web.filestorage.FileStoragePathEnvInfo;
 @Service("exportTemplateService")
 public class ExportTemplateServiceImpl implements ExportTemplateService {
 
-	protected ExamCloudLog log = ExamCloudLogFactory.getLog(this.getClass());
+	private static final Logger LOG = LoggerFactory.getLogger(ExportTemplateServiceImpl.class);
 
 	private static final String ZIP_SUFFIX = "zip";
 
@@ -146,7 +146,7 @@ public class ExportTemplateServiceImpl implements ExportTemplateService {
 				try {
 					is.close();
 				} catch (IOException e1) {
-					log.warn("InputStream close faild!");
+					LOG.warn("InputStream close faild!");
 				}
 			}
 		}

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

@@ -18,14 +18,14 @@ import org.jsoup.nodes.Document;
 import org.jsoup.nodes.Element;
 import org.jsoup.nodes.Node;
 import org.jsoup.nodes.TextNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 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;
@@ -57,7 +57,7 @@ import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
 @Service("exportThemisPaperService")
 public class ExportThemisPaperServiceImpl implements ExportThemisPaperService {
 
-	protected ExamCloudLog log = ExamCloudLogFactory.getLog(this.getClass());
+	private static final Logger LOG = LoggerFactory.getLogger(ExportThemisPaperServiceImpl.class);
 
 	@Autowired
 	private PaperService paperService;
@@ -169,9 +169,9 @@ public class ExportThemisPaperServiceImpl implements ExportThemisPaperService {
 			outputStream.write(b);
 			outputStream.flush();
 		} catch (FileNotFoundException e) {
-			log.error(e.getMessage(), e);
+			LOG.error(e.getMessage(), e);
 		} catch (IOException e) {
-			log.error(e.getMessage(), e);
+			LOG.error(e.getMessage(), e);
 		} finally {
 			IOUtils.closeQuietly(outputStream);
 		}
@@ -193,9 +193,9 @@ public class ExportThemisPaperServiceImpl implements ExportThemisPaperService {
 			outputStream.write(b);
 			outputStream.flush();
 		} catch (FileNotFoundException e) {
-			log.error(e.getMessage(), e);
+			LOG.error(e.getMessage(), e);
 		} catch (IOException e) {
-			log.error(e.getMessage(), e);
+			LOG.error(e.getMessage(), e);
 		} finally {
 			IOUtils.closeQuietly(outputStream);
 		}

+ 477 - 477
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExtractConfigFileServiceImpl.java

@@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.stereotype.Service;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
@@ -73,500 +74,499 @@ import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
 @Service("extractConfigFileService")
 public class ExtractConfigFileServiceImpl implements ExtractConfigFileService {
 
-    private static final Logger logger = LoggerFactory.getLogger(ExtractConfigFileServiceImpl.class);
-
-    public static final String TEMP_FILE_EXP = "docxExport/";
-
-    public static final String TEMP_FILE_NAME = "_考试说明";
-
-    @Autowired
-    private ExportStructureService exportStructureService;
-
-    @Autowired
-    private ExtractConfigService extractConfigService;
-
-    @Autowired
-    private ExamFileService examFileService;
-
-    @Autowired
-    private QuestionAudioService questionAudioService;
-
-    @Autowired
-    private InitPaperExpService initPaperExpService;
-
-    @Autowired
-    private PrintExamPaperService printExamPaperService;
-
-
-    @Autowired
-    private ExamCloudService examCloudService;
-
-    @Autowired
-    private SysProperty sysProperty;
-
-    @Override
-    public void saveExtractConfigAndBuildPaperFile(ExtractConfig extractConfig, Integer isbuildFile, User user,PaperSeqMode seqMode) {
-        //新增,id为空
-        if (StringUtils.isBlank(extractConfig.getId())) {
-            //先查询
-            ExtractConfig tempConfig = extractConfigService.findConfig(extractConfig);
-            if (tempConfig != null) {
-                extractConfig.setId(tempConfig.getId());
-            }
-        }
-        //查询试卷导出设置
-        ExportStructure exportStructure = null;
-        if (isbuildFile == 1) {
-            exportStructure = exportStructureService.findStructureByExamId(extractConfig.getExamId() + "");
-        }
-        //生成试卷
-        List<ExamPaper> examPaperList = extractConfigService.saveExtractConfig(extractConfig, user);
-        if (isbuildFile == 1) {
-            //删除原有试卷文件
-            ExamFile examFile = new ExamFile();
-            examFile.setExamId(extractConfig.getExamId() + "");
-            examFile.setCourseId(extractConfig.getCourseCode());
-            examFile.setOrgId(extractConfig.getOrgId());
-            examFileService.deleteExamFile(examFile);
-            //上传抽取试卷对象集合
-            for (ExamPaper examPaper : examPaperList) {
-                String paperId = examPaper.getPaper().getId();
-                uploadPaperFile(extractConfig, paperId, exportStructure, user, examPaper,seqMode);
-            }
-        }
-        /*if (isbuildFile == 1) {
-            //删除原有试卷文件
-            ExamFile examFile = new ExamFile();
-            examFile.setExamId(extractConfig.getExamId() + "");
-            examFile.setCourseId(extractConfig.getCourseCode());
-            examFile.setOrgId(extractConfig.getOrgId());
-            examFileService.deleteExamFile(examFile);
-            //生成并上传新的试卷文件
-            Set<Entry<String, String>> entrySet = finishedPaperIdMap.entrySet();
-            Iterator<Entry<String, String>> iterator = entrySet.iterator();
-            while (iterator.hasNext()) {
-                Entry<String, String> entry = iterator.next();
-                String paperId = entry.getValue();
-                uploadPaperFile(extractConfig, paperId, exportStructure, user);
-            }
-        }*/
-    }
-
-    /**
-     * 生成并上传试卷文件
-     *
-     * @param extractConfig
-     * @param paperId
-     * @param exportStructure
-     * @param user
-     * @throws Exception
-     */
-    private void uploadPaperFile(ExtractConfig extractConfig, String paperId, ExportStructure exportStructure, User user, ExamPaper examPaper,PaperSeqMode seqMode) {
-        ExportPaperAbstractService exportPaperAbstractService = PaperUtil.getByRootOrgId(String.valueOf(user.getRootOrgId()));
-        exportPaperAbstractService.uploadFile(extractConfig, paperId, exportStructure, user, examPaper,seqMode);
-    }
-
-    @Override
-    public void exportExamPaperInfoCheck(ExportPaperInfoModel exportModel, HttpServletResponse response) throws Exception {
-        ExportStructure exportStructure = exportStructureService.findStructureByExamId(exportModel.getExamId() + "");
-        //如果是批量导出
-        if (exportModel.getExportWay() == ExportWay.BATCH) {
-            if (exportStructure == null) {
-                exportStructure = new ExportStructure();
-                exportStructure.setExportType(ExportType.NORMAL);
-            }
-            //查询该考试下是否所有课程都制定了调卷规则和考试文件
-            checkAllCourseByExamId(exportModel.getExamId(), exportStructure.getExportType());
-        }
-    }
-
-    @Override
-    public void exportExamPaperInfo(ExportPaperInfoModel exportModel, HttpServletResponse response, String loginName, String orgId, String psw) throws Exception {
-        String tempDir = loginName + System.currentTimeMillis();
-        String downloadDir = sysProperty.getDownloadDirectory() + "/" + tempDir;
-        String downZipDir = sysProperty.getZipDirectory() + "/" + tempDir;
-        //创建试卷和压缩文件 文件夹
-        FileDisposeUtil.createDirectory(downloadDir);
-        //创建压缩文件的文件夹
-        FileDisposeUtil.createDirectory(downZipDir);
-        ExportStructure exportStructure = exportStructureService.findStructureByExamId(exportModel.getExamId() + "");
-        if (exportStructure == null) {
-            exportStructure = new ExportStructure();
-            exportStructure.setExportType(ExportType.NORMAL);
-        }
-
-        //导出分布式印刷的数据包
-        if (exportModel.getExportContentList().contains(ExamFileType.PRINT_EXAM_PACKAGE.name())) {
-            ExtractConfig condition = new ExtractConfig();
-            condition.setExamId(Long.valueOf(exportModel.getExamId()));
-            condition.setCourseCode(exportModel.getCourseId());
-            //获取调卷规则
-            ExtractConfig extractConfig = extractConfigService.findConfig(condition);
-            if (extractConfig != null) {
-                List<Paper> papers = new ArrayList<>();
-                List<ExamPaper> examPapers = extractConfig.getExamPaperList();
-                if (examPapers != null && examPapers.size() > 0) {
-                    for (ExamPaper examPaper : examPapers) {
-                        papers.add(examPaper.getPaper());
-                    }
-                }
-                printExamPaperService.downloadPaper(papers, downloadDir, psw);
-            } else {
-                logger.info("exportPrintExamPaper fail, extractConfig is not exist!");
-            }
-        }
-
-        //如果是普通类型的批量导出  导出试卷结构
-        if (exportModel.getExportWay() == ExportWay.BATCH) {
-            Map<String, String> paperIds = checkAllCourseByExamId(exportModel.getExamId(), exportStructure.getExportType());
-            if (exportStructure.getExportType() == ExportType.NORMAL) {
-                if (paperIds.size() > 0 && exportModel.getExportContentList().contains(ExamFileType.PAPER_STRUCTURE_OBJECTIVE.name())) {
-                    makePaperStructure(downloadDir, exportStructure.getExamName(), paperIds, exportStructure);//for export test
-                }
-            }
-        }
-        //根据条件获取到文件下载路径,下载文件到服务器的downloadDirectory文件夹
-        List<ExamFile> examFiles = examFileService.findExamFileListByExportPaperInfoModel(exportModel);
-        if (examFiles != null && examFiles.size() > 0) {
-            for (int i = 0; i < examFiles.size(); i++) {
-                ExamFile examFile = examFiles.get(i);
+	private static final Logger LOG = LoggerFactory.getLogger(ExtractConfigFileServiceImpl.class);
+
+	public static final String TEMP_FILE_EXP = "docxExport/";
+
+	public static final String TEMP_FILE_NAME = "_考试说明";
+
+	@Autowired
+	private ExportStructureService exportStructureService;
+
+	@Autowired
+	private ExtractConfigService extractConfigService;
+
+	@Autowired
+	private ExamFileService examFileService;
+
+	@Autowired
+	private QuestionAudioService questionAudioService;
+
+	@Autowired
+	private InitPaperExpService initPaperExpService;
+
+	@Autowired
+	private PrintExamPaperService printExamPaperService;
+
+	@Autowired
+	private ExamCloudService examCloudService;
+
+	@Autowired
+	private SysProperty sysProperty;
+
+	@Autowired
+	MongoTemplate mongoTemplate;
+
+	@Override
+	public void saveExtractConfigAndBuildPaperFile(ExtractConfig extractConfig, Integer isbuildFile, User user,
+			PaperSeqMode seqMode) {
+		// 新增,id为空
+		if (StringUtils.isBlank(extractConfig.getId())) {
+			// 先查询
+			ExtractConfig tempConfig = extractConfigService.findConfig(extractConfig);
+			if (tempConfig != null) {
+				extractConfig.setId(tempConfig.getId());
+			}
+		}
+		// 查询试卷导出设置
+		ExportStructure exportStructure = null;
+		if (isbuildFile == 1) {
+			exportStructure = exportStructureService.findStructureByExamId(extractConfig.getExamId() + "");
+		}
+
+		// 生成试卷
+		List<ExamPaper> examPaperList = extractConfigService.saveExtractConfig(extractConfig, user);
+		if (isbuildFile == 1) {
+			// 删除原有试卷文件
+			ExamFile examFile = new ExamFile();
+			examFile.setExamId(extractConfig.getExamId() + "");
+			examFile.setCourseId(extractConfig.getCourseCode());
+			examFile.setOrgId(extractConfig.getOrgId());
+			examFileService.deleteExamFile(examFile);
+			// 上传抽取试卷对象集合
+			for (ExamPaper examPaper : examPaperList) {
+				String paperId = examPaper.getPaper().getId();
+				uploadPaperFile(extractConfig, paperId, exportStructure, user, examPaper, seqMode);
+			}
+		}
+	}
+	
+	/**
+	 * 生成并上传试卷文件
+	 *
+	 * @param extractConfig
+	 * @param paperId
+	 * @param exportStructure
+	 * @param user
+	 * @throws Exception
+	 */
+	private void uploadPaperFile(ExtractConfig extractConfig, String paperId, ExportStructure exportStructure,
+			User user, ExamPaper examPaper, PaperSeqMode seqMode) {
+		ExportPaperAbstractService exportPaperAbstractService = PaperUtil
+				.getByRootOrgId(String.valueOf(user.getRootOrgId()));
+		exportPaperAbstractService.uploadFile(extractConfig, paperId, exportStructure, user, examPaper, seqMode);
+	}
+
+	@Override
+	public void exportExamPaperInfoCheck(ExportPaperInfoModel exportModel, HttpServletResponse response)
+			throws Exception {
+		ExportStructure exportStructure = exportStructureService.findStructureByExamId(exportModel.getExamId() + "");
+		// 如果是批量导出
+		if (exportModel.getExportWay() == ExportWay.BATCH) {
+			if (exportStructure == null) {
+				exportStructure = new ExportStructure();
+				exportStructure.setExportType(ExportType.NORMAL);
+			}
+			// 查询该考试下是否所有课程都制定了调卷规则和考试文件
+			checkAllCourseByExamId(exportModel.getExamId(), exportStructure.getExportType());
+		}
+	}
+
+	@Override
+	public void exportExamPaperInfo(ExportPaperInfoModel exportModel, HttpServletResponse response, String loginName,
+			String orgId, String psw) throws Exception {
+		String tempDir = loginName + System.currentTimeMillis();
+		String downloadDir = sysProperty.getDownloadDirectory() + "/" + tempDir;
+		String downZipDir = sysProperty.getZipDirectory() + "/" + tempDir;
+		// 创建试卷和压缩文件 文件夹
+		FileDisposeUtil.createDirectory(downloadDir);
+		// 创建压缩文件的文件夹
+		FileDisposeUtil.createDirectory(downZipDir);
+		ExportStructure exportStructure = exportStructureService.findStructureByExamId(exportModel.getExamId() + "");
+		if (exportStructure == null) {
+			exportStructure = new ExportStructure();
+			exportStructure.setExportType(ExportType.NORMAL);
+		}
+
+		// 导出分布式印刷的数据包
+		if (exportModel.getExportContentList().contains(ExamFileType.PRINT_EXAM_PACKAGE.name())) {
+			ExtractConfig condition = new ExtractConfig();
+			condition.setExamId(Long.valueOf(exportModel.getExamId()));
+			condition.setCourseCode(exportModel.getCourseId());
+			// 获取调卷规则
+			ExtractConfig extractConfig = extractConfigService.findConfig(condition);
+			if (extractConfig != null) {
+				List<Paper> papers = new ArrayList<>();
+				List<ExamPaper> examPapers = extractConfig.getExamPaperList();
+				if (examPapers != null && examPapers.size() > 0) {
+					for (ExamPaper examPaper : examPapers) {
+						papers.add(examPaper.getPaper());
+					}
+				}
+				printExamPaperService.downloadPaper(papers, downloadDir, psw);
+			} else {
+				LOG.info("exportPrintExamPaper fail, extractConfig is not exist!");
+			}
+		}
+
+		// 如果是普通类型的批量导出 导出试卷结构
+		if (exportModel.getExportWay() == ExportWay.BATCH) {
+			Map<String, String> paperIds = checkAllCourseByExamId(exportModel.getExamId(),
+					exportStructure.getExportType());
+			if (exportStructure.getExportType() == ExportType.NORMAL) {
+				if (paperIds.size() > 0
+						&& exportModel.getExportContentList().contains(ExamFileType.PAPER_STRUCTURE_OBJECTIVE.name())) {
+					makePaperStructure(downloadDir, exportStructure.getExamName(), paperIds, exportStructure);// for
+																												// export
+																												// test
+				}
+			}
+		}
+		// 根据条件获取到文件下载路径,下载文件到服务器的downloadDirectory文件夹
+		List<ExamFile> examFiles = examFileService.findExamFileListByExportPaperInfoModel(exportModel);
+		if (examFiles != null && examFiles.size() > 0) {
+			for (int i = 0; i < examFiles.size(); i++) {
+				ExamFile examFile = examFiles.get(i);
 //                UpYun upyun = new UpYun(sysProperty.getBucketName(), sysProperty.getUserName(), sysProperty.getPassword());
-                File file = new File(downloadDir + File.separator + examFile.getFileName());
+				File file = new File(downloadDir + File.separator + examFile.getFileName());
 //                upyun.readFile(examFile.getFilePath(), file);
-                //通用存储
+				// 通用存储
 				FileStorageUtil.saveUrlAs(FileStorageUtil.realPath(examFile.getFilePath()), file);
 
-                if (examFile.getExamFileType() == ExamFileType.PAPER) {
-                    Long examId = Long.parseLong(exportModel.getExamId());
-                    ExtractConfig extractConfig = extractConfigService.findConfig(new ExtractConfig(examId, examFile.getCourseId()));
-                    List<ExamPaper> list = extractConfig.getExamPaperList();
-                    if (list != null && list.size() > 0) {
-                        for (ExamPaper examPaper : list) {
-                            String groupCode = examPaper.getGroupCode();
-                            String paperId = examPaper.getPaper().getId();
-                            if (groupCode.equals(examFile.getGroupCode())) {
-                                downloadAudio(paperId, examFile, downloadDir, orgId);
-                            }
-
-                        }
-                    }
-                   /* Map<String, String> finishedPaperIdMap = extractConfig.getFinishedPaperIdMap();
-                    Set<Entry<String, String>> entrySet = finishedPaperIdMap.entrySet();
-                    Iterator<Entry<String, String>> iterator = entrySet.iterator();
-                    while (iterator.hasNext()) {
-                        Entry<String, String> entry = iterator.next();
-                        String groupCode = entry.getKey();
-                        String paperId = entry.getValue();
-                        if (groupCode.equals(examFile.getGroupCode())) {
-                            downloadAudio(paperId, examFile, downloadDir, orgName);
-                        }
-                    }*/
-                }
-            }
-        }
-        //创建压缩文件名称
-        String zipFileName = exportModel.getExamId();
-        //将downloadDirectory文件夹压缩成zip文件,存放到zipDirectory文件夹中
-        FileDisposeUtil.fileToZip(downloadDir, downZipDir, zipFileName);
-        //下载zip文件到客户端
-        FileDisposeUtil.downloadFile(zipFileName + ".zip", downZipDir + File.separator + zipFileName + ".zip", response);
-        //删除文件夹
-        FileUtils.deleteQuietly(new File(downloadDir));
-        FileUtils.deleteQuietly(new File(downZipDir));
-    }
-
-    /**
-     * 下载试卷音频文件
-     *
-     * @throws Exception
-     */
-    private void downloadAudio(String paperId, ExamFile examFile, String downloadDir, String orgId) throws Exception {
-        ExportPaperAbstractService exportPaperAbstractService = PaperUtil.getByRootOrgId(orgId);
-        DownloadPaperDto dto=new DownloadPaperDto();
-    	dto.setPaperId(paperId);
-    	dto.setSeqMode(PaperSeqMode.MODE3);
-        PaperExp paperExp = exportPaperAbstractService.initPaperExp(dto);
-        //下载考试说明
-        if (StringUtils.isNotBlank(paperExp.getExamRemark())) {
-            downExamRemark(paperExp, downloadDir, examFile);
-        }
-        //取到所有大题
-        List<PaperDetailExp> paperDetailExps = paperExp.getPaperDetails();
-        if (paperDetailExps != null && paperDetailExps.size() > 0) {
-            for (PaperDetailExp paperDetailExp : paperDetailExps) {
-                //取到所有小题
-                List<PaperDetailUnitExp> paperDetailUnitExps = paperDetailExp.getPaperDetailUnits();
-                if (paperDetailUnitExps != null && paperDetailUnitExps.size() > 0) {
-                    for (PaperDetailUnitExp unit : paperDetailUnitExps) {
-                        if (unit.getQuestion().getHasAudio() != null && unit.getQuestion().getHasAudio()) {
-                            List<QuestionAudio> questionAudios = questionAudioService.findQuestionAudiosByQuestionId(unit.getQuestion().getId());
-                            for (QuestionAudio audio : questionAudios) {
-                                String audioFileName = audio.getId() +
-                                        "_" + exportPaperAbstractService.getAudioFileName(audio, unit, paperDetailExp);
+				if (examFile.getExamFileType() == ExamFileType.PAPER) {
+					Long examId = Long.parseLong(exportModel.getExamId());
+					ExtractConfig extractConfig = extractConfigService
+							.findConfig(new ExtractConfig(examId, examFile.getCourseId()));
+					List<ExamPaper> list = extractConfig.getExamPaperList();
+					if (list != null && list.size() > 0) {
+						for (ExamPaper examPaper : list) {
+							String groupCode = examPaper.getGroupCode();
+							String paperId = examPaper.getPaper().getId();
+							if (groupCode.equals(examFile.getGroupCode())) {
+								downloadAudio(paperId, examFile, downloadDir, orgId);
+							}
+
+						}
+					}
+					/*
+					 * Map<String, String> finishedPaperIdMap =
+					 * extractConfig.getFinishedPaperIdMap(); Set<Entry<String, String>> entrySet =
+					 * finishedPaperIdMap.entrySet(); Iterator<Entry<String, String>> iterator =
+					 * entrySet.iterator(); while (iterator.hasNext()) { Entry<String, String> entry
+					 * = iterator.next(); String groupCode = entry.getKey(); String paperId =
+					 * entry.getValue(); if (groupCode.equals(examFile.getGroupCode())) {
+					 * downloadAudio(paperId, examFile, downloadDir, orgName); } }
+					 */
+				}
+			}
+		}
+		// 创建压缩文件名称
+		String zipFileName = exportModel.getExamId();
+		// 将downloadDirectory文件夹压缩成zip文件,存放到zipDirectory文件夹中
+		FileDisposeUtil.fileToZip(downloadDir, downZipDir, zipFileName);
+		// 下载zip文件到客户端
+		FileDisposeUtil.downloadFile(zipFileName + ".zip", downZipDir + File.separator + zipFileName + ".zip",
+				response);
+		// 删除文件夹
+		FileUtils.deleteQuietly(new File(downloadDir));
+		FileUtils.deleteQuietly(new File(downZipDir));
+	}
+
+	/**
+	 * 下载试卷音频文件
+	 *
+	 * @throws Exception
+	 */
+	private void downloadAudio(String paperId, ExamFile examFile, String downloadDir, String orgId) throws Exception {
+		ExportPaperAbstractService exportPaperAbstractService = PaperUtil.getByRootOrgId(orgId);
+		DownloadPaperDto dto = new DownloadPaperDto();
+		dto.setPaperId(paperId);
+		dto.setSeqMode(PaperSeqMode.MODE3);
+		PaperExp paperExp = exportPaperAbstractService.initPaperExp(dto);
+		// 下载考试说明
+		if (StringUtils.isNotBlank(paperExp.getExamRemark())) {
+			downExamRemark(paperExp, downloadDir, examFile);
+		}
+		// 取到所有大题
+		List<PaperDetailExp> paperDetailExps = paperExp.getPaperDetails();
+		if (paperDetailExps != null && paperDetailExps.size() > 0) {
+			for (PaperDetailExp paperDetailExp : paperDetailExps) {
+				// 取到所有小题
+				List<PaperDetailUnitExp> paperDetailUnitExps = paperDetailExp.getPaperDetailUnits();
+				if (paperDetailUnitExps != null && paperDetailUnitExps.size() > 0) {
+					for (PaperDetailUnitExp unit : paperDetailUnitExps) {
+						if (unit.getQuestion().getHasAudio() != null && unit.getQuestion().getHasAudio()) {
+							List<QuestionAudio> questionAudios = questionAudioService
+									.findQuestionAudiosByQuestionId(unit.getQuestion().getId());
+							for (QuestionAudio audio : questionAudios) {
+								String audioFileName = audio.getId() + "_"
+										+ exportPaperAbstractService.getAudioFileName(audio, unit, paperDetailExp);
 //                                UpYun upyun = new UpYun(sysProperty.getBucketName(), sysProperty.getUserName(), sysProperty.getPassword());
-                                File file = new File(downloadDir + File.separator + audioFileName);
+								File file = new File(downloadDir + File.separator + audioFileName);
 //                                upyun.readFile(audio.getFileUrl(), file);
-                                //通用存储
-                				FileStorageUtil.saveUrlAs(FileStorageUtil.realPath(audio.getFileUrl()), file);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * 下载考试说明
-     *
-     * @param zipFileName
-     * @throws Exception
-     */
-    public void downExamRemark(PaperExp paperExp, String zipFileName, ExamFile examFile) throws Exception {
+								// 通用存储
+								FileStorageUtil.saveUrlAs(FileStorageUtil.realPath(audio.getFileUrl()), file);
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * 下载考试说明
+	 *
+	 * @param zipFileName
+	 * @throws Exception
+	 */
+	public void downExamRemark(PaperExp paperExp, String zipFileName, ExamFile examFile) throws Exception {
 //        //1.考试说明html转成word
 //        String title = "<p style=\"text-align:center\"><span style=\"font-size:26px\"><span style=\"font-family:宋体\">考&nbsp;试&nbsp;说&nbsp;明</span></span></p>";
 //        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
 //        String html = title + ImageUtils.reSizeImg(paperExp.getExamRemark());
 //        DocxProcessUtil.html2Docx(wordMLPackage, CommonUtils.formatHtml(html));
 //        //2.导出考试说明word
-        File directory = new File(zipFileName + File.separator);
-        String fileName=paperExp.getName() + "_" + paperExp.getCourse().getName() + "_" + paperExp.getCourse().getCode() + "_" + examFile.getGroupCode() + TEMP_FILE_NAME;
+		File directory = new File(zipFileName + File.separator);
+		String fileName = paperExp.getName() + "_" + paperExp.getCourse().getName() + "_"
+				+ paperExp.getCourse().getCode() + "_" + examFile.getGroupCode() + TEMP_FILE_NAME;
 //        Docx4J.save(wordMLPackage, file);
-        ExportPaperUtil.createExamRemarkDocFile(directory, fileName, paperExp.getExamRemark());
-    }
-
-    /**
-     * 计算取得音频文件名称
-     *
-     * @param audio
-     * @param unit
-     * @return
-     */
-    @SuppressWarnings("unused")
-    private String getAudioFileName(QuestionAudio audio, PaperDetailUnit unit) {
-        String questionAudioId = audio.getId();
-        StringBuffer audioFileName = new StringBuffer(unit.getPaperDetail().getNumber() + "_" + unit.getNumber() + "_");
-        Question question = unit.getQuestion();
-        List<String> idvaluesBody = CommonUtils.getTagANames(question.getQuesBody());
-        if (idvaluesBody.contains(questionAudioId)) {
-            audioFileName.append("1");//题干
-            audioFileName.append("_");
-            int index = idvaluesBody.indexOf(questionAudioId);
-            audioFileName.append(index + 1);
-        } else {
-            List<QuesOption> options = question.getQuesOptions();
-            if (options != null && options.size() > 0) {
-                for (QuesOption option : options) {
-                    List<String> idvaluesOption = CommonUtils.getTagANames(option.getOptionBody());
-                    if (idvaluesOption.contains(questionAudioId)) {
-                        audioFileName.append("2");//选项
-                        audioFileName.append("_");
-                        audioFileName.append(CommonUtils.getOptionNum(Integer.parseInt(option.getNumber()) - 1));
-                        audioFileName.append("_");
-                        int index = idvaluesOption.indexOf(questionAudioId);
-                        audioFileName.append(index + 1);
-                        break;
-                    }
-                }
-            }
-        }
-        audioFileName.append(".");
-        audioFileName.append(audio.getFileSuffixes());
-        return audioFileName.toString();
-    }
-
-    /**
-     * 查询该考试下是否所有课程都制定了调卷规则
-     *
-     * @param examId
-     */
-    private Map<String, String> checkAllCourseByExamId(String examId, ExportType exportType) {
-        Map<String, String> paperIdMap = new HashMap<>();
-        List<ExamCourseRelationBean> examCourseDtoList = new ArrayList<>();
-        GetExamCourseListReq req = new GetExamCourseListReq();
-        req.setExamId(Long.valueOf(examId));
-        //req.setExamId(299L);//for export test
-        req.setCourseEnable(true);
-        Long start = 1l;
-        int count = 0;
-        while (true) {
-            req.setStart(start);
-            GetExamCourseListResp resp = examCloudService.getExamCourseList(req);
-            examCourseDtoList.addAll(resp.getRelationList());
-            if (start.equals(resp.getNext())) {
-                break;
-            } else {
-                start = resp.getNext();
-            }
-            count++;
-            if (count > 1000) {
-                throw new StatusException("Q-", "考试下课程的数据量过大");
-            }
-        }
-
-        for (ExamCourseRelationBean examCourseDto : examCourseDtoList) {
-            ExtractConfig condition = new ExtractConfig();
-            condition.setExamId(examCourseDto.getExamId());
-            //condition.setExamId(606L);//for export online
-            condition.setCourseCode(examCourseDto.getCourseCode());
-
-            ExtractConfig extractConfig = extractConfigService.findConfig(condition);
+		ExportPaperUtil.createExamRemarkDocFile(directory, fileName, paperExp.getExamRemark());
+	}
+
+	/**
+	 * 计算取得音频文件名称
+	 *
+	 * @param audio
+	 * @param unit
+	 * @return
+	 */
+	@SuppressWarnings("unused")
+	private String getAudioFileName(QuestionAudio audio, PaperDetailUnit unit) {
+		String questionAudioId = audio.getId();
+		StringBuffer audioFileName = new StringBuffer(unit.getPaperDetail().getNumber() + "_" + unit.getNumber() + "_");
+		Question question = unit.getQuestion();
+		List<String> idvaluesBody = CommonUtils.getTagANames(question.getQuesBody());
+		if (idvaluesBody.contains(questionAudioId)) {
+			audioFileName.append("1");// 题干
+			audioFileName.append("_");
+			int index = idvaluesBody.indexOf(questionAudioId);
+			audioFileName.append(index + 1);
+		} else {
+			List<QuesOption> options = question.getQuesOptions();
+			if (options != null && options.size() > 0) {
+				for (QuesOption option : options) {
+					List<String> idvaluesOption = CommonUtils.getTagANames(option.getOptionBody());
+					if (idvaluesOption.contains(questionAudioId)) {
+						audioFileName.append("2");// 选项
+						audioFileName.append("_");
+						audioFileName.append(CommonUtils.getOptionNum(Integer.parseInt(option.getNumber()) - 1));
+						audioFileName.append("_");
+						int index = idvaluesOption.indexOf(questionAudioId);
+						audioFileName.append(index + 1);
+						break;
+					}
+				}
+			}
+		}
+		audioFileName.append(".");
+		audioFileName.append(audio.getFileSuffixes());
+		return audioFileName.toString();
+	}
+
+	/**
+	 * 查询该考试下是否所有课程都制定了调卷规则
+	 *
+	 * @param examId
+	 */
+	private Map<String, String> checkAllCourseByExamId(String examId, ExportType exportType) {
+		Map<String, String> paperIdMap = new HashMap<>();
+		List<ExamCourseRelationBean> examCourseDtoList = new ArrayList<>();
+		GetExamCourseListReq req = new GetExamCourseListReq();
+		req.setExamId(Long.valueOf(examId));
+		// req.setExamId(299L);//for export test
+		req.setCourseEnable(true);
+		Long start = 1l;
+		int count = 0;
+		while (true) {
+			req.setStart(start);
+			GetExamCourseListResp resp = examCloudService.getExamCourseList(req);
+			examCourseDtoList.addAll(resp.getRelationList());
+			if (start.equals(resp.getNext())) {
+				break;
+			} else {
+				start = resp.getNext();
+			}
+			count++;
+			if (count > 1000) {
+				throw new StatusException("Q-", "考试下课程的数据量过大");
+			}
+		}
+
+		for (ExamCourseRelationBean examCourseDto : examCourseDtoList) {
+			ExtractConfig condition = new ExtractConfig();
+			condition.setExamId(examCourseDto.getExamId());
+			// condition.setExamId(606L);//for export online
+			condition.setCourseCode(examCourseDto.getCourseCode());
+
+			ExtractConfig extractConfig = extractConfigService.findConfig(condition);
 //            if (extractConfig == null) {
 //                logger.warn("examId:" + examId + " courseCode:" + examCourseDto.getCourseCode());
 //                continue;
 //            }
 
-            if (extractConfig == null) {
-                throw new RuntimeException("该考试下的课程“" + examCourseDto.getCourseName() + "”没有制定调卷规则,不能批量导出,请保存调卷规则生成文件");
-            }
-            if (extractConfig.getIfFinish() == null || extractConfig.getIfFinish() == 0) {
-                throw new RuntimeException("该考试下的课程“" + examCourseDto.getCourseName() + "”考试文件没有生成,不能批量导出,请保存调卷规则生成文件");
-            }
-            checkExamFileExists(examId, examCourseDto.getCourseCode(), exportType, examCourseDto.getCourseName());
-
-            List<ExamPaper> examPapers = extractConfig.getExamPaperList();
-            if (examPapers == null || examPapers.size() < 1) {
-                throw new RuntimeException("该考试下的课程“" + examCourseDto.getCourseName() + "”没有绑定试卷。");
-            }
-            for (ExamPaper examPaper : examPapers) {
-                String paperId = examPaper.getPaper().getId();
-                paperIdMap.put(paperId, examPaper.getGroupCode());
-            }
-            
-            /*Map<String, String> finishedPaperIdMap = extractConfig.getFinishedPaperIdMap();
-            Set<Entry<String, String>> entry = finishedPaperIdMap.entrySet();
-            Iterator<Entry<String, String>> iterator = entry.iterator();
-            while (iterator.hasNext()) {
-                Entry<String, String> next = iterator.next();
-                //paperId为key,paperType为value
-                paperIdMap.put(next.getValue(), next.getKey());
-            }*/
-        }
-        return paperIdMap;
-    }
-
-    /**
-     * 检查试卷文件是否存在
-     *
-     * @param examId
-     */
-    private void checkExamFileExists(String examId, String code, ExportType exportType, String name) {
-        ExamFile examFileCondition = new ExamFile();
-        examFileCondition.setExamId(examId);
-        examFileCondition.setCourseId(code);
-
-        List<ExamFile> examfiles = examFileService.findExamFileListByExamFile(examFileCondition);
-
-        //检查是否都生成了试卷文件
-        boolean paperFlag = false;
-        if (exportType == ExportType.NORMAL) {
-            for (ExamFile examFile : examfiles) {
-                if (examFile.getExamFileType() == ExamFileType.PAPER) {
-                    paperFlag = true;
-                }
-            }
-        } else {
-            for (ExamFile examFile : examfiles) {
-                if (examFile.getExamFileType() == ExamFileType.COMPUTERTEST_PACKAGE) {
-                    paperFlag = true;
-                }
-            }
-        }
-
-        if (!paperFlag) {
-            String message = exportType == ExportType.NORMAL ? "考试文件" : "机考数据包";
-            throw new RuntimeException("该考试下的课程“" + name + "”" + message + "没有生成,不能批量导出,请重新保存调卷规则生成");
-        }
-    }
-
-    /**
-     * 将该考试下的所有课程试卷的结构生成到一张Excel表中
-     *
-     * @param examName
-     * @param paperIds
-     * @param exportStructure
-     * @throws Exception
-     */
-    private void makePaperStructure(String downloadDir, String examName, Map<String, String> paperIds, ExportStructure exportStructure) throws Exception {
-        logger.info("正在批量生成试卷结构...");
-        List<QuestionTypeNum> questionTypeNums = exportStructure.getQuestionTypeNums();
-        //客观题集合
-        List<ObjectiveQuestionStructure> objectiveQuestionStructureList = new ArrayList<>();
-        //主观题集合
-        List<SubjectiveQuestionStructure> subjectiveQuestionStructureList = new ArrayList<>();
-
-        ExportPaperAbstractService exportPaperAbstractService = PaperUtil.getByRootOrgId(exportStructure.getOrgId());
-        Set<Entry<String, String>> entrySet = paperIds.entrySet();
-        Iterator<Entry<String, String>> iterator = entrySet.iterator();
-
-        int index = 1;
-        while (iterator.hasNext()) {
-            Entry<String, String> entry = iterator.next();
-            String paperId = entry.getKey();
-            String paperType = entry.getValue();
-            logger.info(index + " - 处理试卷:" + paperId);
-            
-            DownloadPaperDto dto=new DownloadPaperDto();
-            dto.setPaperId(paperId);
-        	dto.setSeqMode(PaperSeqMode.MODE3);
-            PaperExp paperExp = initPaperExpService.initPaperExp(dto);//for export test
-            logger.info("处理客观题...");
-            //添加客观题
-            List<PaperDetailExp> objectiveDetails = exportPaperAbstractService.getAllObjectiveDetails(paperExp);
-            //得到补齐后的客观题大题
-            List<PaperDetailExp> paperDetailExps = exportPaperAbstractService.fillObjectiveQuestions(objectiveDetails, questionTypeNums);
-            List<ObjectiveQuestionStructure> objectiveList = new ArrayList<>();
-            for (PaperDetailExp paperDetailExp : paperDetailExps) {
-                for (PaperDetailUnitExp unit : paperDetailExp.getPaperDetailUnits()) {
-                    objectiveList.add(new ObjectiveQuestionStructure(paperExp, paperDetailExp, unit, paperType));
-                }
-            }
-            objectiveQuestionStructureList.addAll(objectiveList);
-            logger.info("处理客观题完成");
-            //添加主观题
-            logger.info("处理主观题...");
-            List<PaperDetailExp> subjectiveDetails = exportPaperAbstractService.getAllSubjectiveDetails(paperExp);
-            List<SubjectiveQuestionStructure> subjectiveList = new ArrayList<>();
-            for (PaperDetailExp paperDetailExp : subjectiveDetails) {
-                for (PaperDetailUnitExp unit : paperDetailExp.getPaperDetailUnits()) {
-                    subjectiveList.add(new SubjectiveQuestionStructure(paperExp, paperDetailExp, unit, paperType));
-                }
-            }
-            subjectiveQuestionStructureList.addAll(subjectiveList);
-            logger.info("处理主观题完成");
-            index++;
-        }
-
-
-        ExcelWriter objectiveExcelExporter = new ExcelWriter(ObjectiveQuestionStructure.class);
-        String keguanFileName = examName + "_客观题.xlsx";
-        try (FileOutputStream out1 = getFileOutputStream(downloadDir, keguanFileName);) {
-            objectiveExcelExporter.write(keguanFileName, objectiveQuestionStructureList, out1);
-        } catch (IOException e) {
-            logger.error(e.getMessage(), e);
-        }
-
-        ExcelWriter subjectiveExcelExporter = new ExcelWriter(SubjectiveQuestionStructure.class);
-        String zhuguanFileName = examName + "_主观题.xlsx";
-        try (FileOutputStream out2 = getFileOutputStream(downloadDir, zhuguanFileName);) {
-            subjectiveExcelExporter.write(zhuguanFileName, subjectiveQuestionStructureList, out2);
-        } catch (IOException e) {
-            logger.error(e.getMessage(), e);
-        }
-
-        logger.info("批量生成试卷结构完成");
-    }
-
-    private FileOutputStream getFileOutputStream(String downloadDir, String fileName) throws IOException {
-        File directory = new File(downloadDir);
-        if (!directory.exists()) {
-            directory.mkdirs();
-        }
-        File file = new File(downloadDir + File.separator + fileName);
-        if (!file.exists()) {
-            IoUtils.createFile(file);
-        }
-        return new FileOutputStream(file);
-    }
+			if (extractConfig == null) {
+				throw new RuntimeException("该考试下的课程“" + examCourseDto.getCourseName() + "”没有制定调卷规则,不能批量导出,请保存调卷规则生成文件");
+			}
+			if (extractConfig.getIfFinish() == null || extractConfig.getIfFinish() == 0) {
+				throw new RuntimeException("该考试下的课程“" + examCourseDto.getCourseName() + "”考试文件没有生成,不能批量导出,请保存调卷规则生成文件");
+			}
+			checkExamFileExists(examId, examCourseDto.getCourseCode(), exportType, examCourseDto.getCourseName());
+
+			List<ExamPaper> examPapers = extractConfig.getExamPaperList();
+			if (examPapers == null || examPapers.size() < 1) {
+				throw new RuntimeException("该考试下的课程“" + examCourseDto.getCourseName() + "”没有绑定试卷。");
+			}
+			for (ExamPaper examPaper : examPapers) {
+				String paperId = examPaper.getPaper().getId();
+				paperIdMap.put(paperId, examPaper.getGroupCode());
+			}
+
+			/*
+			 * Map<String, String> finishedPaperIdMap =
+			 * extractConfig.getFinishedPaperIdMap(); Set<Entry<String, String>> entry =
+			 * finishedPaperIdMap.entrySet(); Iterator<Entry<String, String>> iterator =
+			 * entry.iterator(); while (iterator.hasNext()) { Entry<String, String> next =
+			 * iterator.next(); //paperId为key,paperType为value
+			 * paperIdMap.put(next.getValue(), next.getKey()); }
+			 */
+		}
+		return paperIdMap;
+	}
+
+	/**
+	 * 检查试卷文件是否存在
+	 *
+	 * @param examId
+	 */
+	private void checkExamFileExists(String examId, String code, ExportType exportType, String name) {
+		ExamFile examFileCondition = new ExamFile();
+		examFileCondition.setExamId(examId);
+		examFileCondition.setCourseId(code);
+
+		List<ExamFile> examfiles = examFileService.findExamFileListByExamFile(examFileCondition);
+
+		// 检查是否都生成了试卷文件
+		boolean paperFlag = false;
+		if (exportType == ExportType.NORMAL) {
+			for (ExamFile examFile : examfiles) {
+				if (examFile.getExamFileType() == ExamFileType.PAPER) {
+					paperFlag = true;
+				}
+			}
+		} else {
+			for (ExamFile examFile : examfiles) {
+				if (examFile.getExamFileType() == ExamFileType.COMPUTERTEST_PACKAGE) {
+					paperFlag = true;
+				}
+			}
+		}
+
+		if (!paperFlag) {
+			String message = exportType == ExportType.NORMAL ? "考试文件" : "机考数据包";
+			throw new RuntimeException("该考试下的课程“" + name + "”" + message + "没有生成,不能批量导出,请重新保存调卷规则生成");
+		}
+	}
+
+	/**
+	 * 将该考试下的所有课程试卷的结构生成到一张Excel表中
+	 *
+	 * @param examName
+	 * @param paperIds
+	 * @param exportStructure
+	 * @throws Exception
+	 */
+	private void makePaperStructure(String downloadDir, String examName, Map<String, String> paperIds,
+			ExportStructure exportStructure) throws Exception {
+		LOG.info("正在批量生成试卷结构...");
+		List<QuestionTypeNum> questionTypeNums = exportStructure.getQuestionTypeNums();
+		// 客观题集合
+		List<ObjectiveQuestionStructure> objectiveQuestionStructureList = new ArrayList<>();
+		// 主观题集合
+		List<SubjectiveQuestionStructure> subjectiveQuestionStructureList = new ArrayList<>();
+
+		ExportPaperAbstractService exportPaperAbstractService = PaperUtil.getByRootOrgId(exportStructure.getOrgId());
+		Set<Entry<String, String>> entrySet = paperIds.entrySet();
+		Iterator<Entry<String, String>> iterator = entrySet.iterator();
+
+		int index = 1;
+		while (iterator.hasNext()) {
+			Entry<String, String> entry = iterator.next();
+			String paperId = entry.getKey();
+			String paperType = entry.getValue();
+			LOG.info(index + " - 处理试卷:" + paperId);
+
+			DownloadPaperDto dto = new DownloadPaperDto();
+			dto.setPaperId(paperId);
+			dto.setSeqMode(PaperSeqMode.MODE3);
+			PaperExp paperExp = initPaperExpService.initPaperExp(dto);// for export test
+			LOG.info("处理客观题...");
+			// 添加客观题
+			List<PaperDetailExp> objectiveDetails = exportPaperAbstractService.getAllObjectiveDetails(paperExp);
+			// 得到补齐后的客观题大题
+			List<PaperDetailExp> paperDetailExps = exportPaperAbstractService.fillObjectiveQuestions(objectiveDetails,
+					questionTypeNums);
+			List<ObjectiveQuestionStructure> objectiveList = new ArrayList<>();
+			for (PaperDetailExp paperDetailExp : paperDetailExps) {
+				for (PaperDetailUnitExp unit : paperDetailExp.getPaperDetailUnits()) {
+					objectiveList.add(new ObjectiveQuestionStructure(paperExp, paperDetailExp, unit, paperType));
+				}
+			}
+			objectiveQuestionStructureList.addAll(objectiveList);
+			LOG.info("处理客观题完成");
+			// 添加主观题
+			LOG.info("处理主观题...");
+			List<PaperDetailExp> subjectiveDetails = exportPaperAbstractService.getAllSubjectiveDetails(paperExp);
+			List<SubjectiveQuestionStructure> subjectiveList = new ArrayList<>();
+			for (PaperDetailExp paperDetailExp : subjectiveDetails) {
+				for (PaperDetailUnitExp unit : paperDetailExp.getPaperDetailUnits()) {
+					subjectiveList.add(new SubjectiveQuestionStructure(paperExp, paperDetailExp, unit, paperType));
+				}
+			}
+			subjectiveQuestionStructureList.addAll(subjectiveList);
+			LOG.info("处理主观题完成");
+			index++;
+		}
+
+		ExcelWriter objectiveExcelExporter = new ExcelWriter(ObjectiveQuestionStructure.class);
+		String keguanFileName = examName + "_客观题.xlsx";
+		try (FileOutputStream out1 = getFileOutputStream(downloadDir, keguanFileName);) {
+			objectiveExcelExporter.write(keguanFileName, objectiveQuestionStructureList, out1);
+		} catch (IOException e) {
+			LOG.error(e.getMessage(), e);
+		}
+
+		ExcelWriter subjectiveExcelExporter = new ExcelWriter(SubjectiveQuestionStructure.class);
+		String zhuguanFileName = examName + "_主观题.xlsx";
+		try (FileOutputStream out2 = getFileOutputStream(downloadDir, zhuguanFileName);) {
+			subjectiveExcelExporter.write(zhuguanFileName, subjectiveQuestionStructureList, out2);
+		} catch (IOException e) {
+			LOG.error(e.getMessage(), e);
+		}
+
+		LOG.info("批量生成试卷结构完成");
+	}
+
+	private FileOutputStream getFileOutputStream(String downloadDir, String fileName) throws IOException {
+		File directory = new File(downloadDir);
+		if (!directory.exists()) {
+			directory.mkdirs();
+		}
+		File file = new File(downloadDir + File.separator + fileName);
+		if (!file.exists()) {
+			IoUtils.createFile(file);
+		}
+		return new FileOutputStream(file);
+	}
 
 }

+ 14 - 14
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExtractConfigProviderServiceImpl.java

@@ -13,13 +13,13 @@ import java.util.stream.Collectors;
 
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Example;
 import org.springframework.stereotype.Service;
 
 import cn.com.qmth.examcloud.commons.exception.StatusException;
-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.question.enums.QuesStructType;
@@ -69,7 +69,7 @@ import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
 @Service("extractConfigCloudService")
 public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderService {
 
-    protected ExamCloudLog log = ExamCloudLogFactory.getLog(this.getClass());
+    private static final Logger LOG = LoggerFactory.getLogger(ExtractConfigProviderServiceImpl.class);
 
     @Autowired
     private ExtractConfigService extractConfigService;
@@ -140,7 +140,7 @@ public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderSe
 
     @Override
     public ExtractConfigPaper getDefaultPaper(Long examId, String courseCode, String groupCode) {
-        log.info("调卷开始...");
+        LOG.info("调卷开始...");
 
         ExtractConfigCacheBean extractConfigCache = CacheHelper.getExtractConfig(examId, courseCode);
         if (extractConfigCache == null) {
@@ -158,7 +158,7 @@ public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderSe
             throw new StatusException("500", msg);
         }
 
-        log.info("构建试卷结构..." + basePaperId);
+        LOG.info("构建试卷结构..." + basePaperId);
         ExtractConfigPaperCacheBean extractConfigPaperCache = CacheHelper.getExtractConfigPaper(examId, courseCode, groupCode, basePaperId);
         if (extractConfigPaperCache == null) {
             throw new StatusException("500", msg);
@@ -240,7 +240,7 @@ public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderSe
      * 根据paper对象构建DefaultPaper对象
      */
     public DefaultPaper buildDefaultByBasePaper(Paper basePaper, Long examId, String courseCode, String groupCode) {
-        log.debug("开始包装网考需要的试卷结构...");
+        LOG.debug("开始包装网考需要的试卷结构...");
 
         //获取大题
         List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(basePaper);
@@ -255,7 +255,7 @@ public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderSe
         //根据大题id将小题归类
         Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream().collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
 
-        log.debug("循环大题,开始组装对象...");
+        LOG.debug("循环大题,开始组装对象...");
 
         //生成新的分组集合
         List<DefaultQuestionGroup> questionGroupList = new ArrayList<>();
@@ -268,7 +268,7 @@ public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderSe
             //获取原小题的集合
             List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());
             if (CollectionUtils.isEmpty(paperDetailUnits)) {
-                log.warn("试卷大题下面没有小题!");
+                LOG.warn("试卷大题下面没有小题!");
                 throw new StatusException("500", "考试试卷有误,请联系老师!");
             }
 
@@ -381,7 +381,7 @@ public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderSe
 
     @Override
     public DefaultQuestion getDefaultQuestion(Long examId, String courseCode, String groupCode, String questionId) {
-        log.debug("网考根据调卷规则中试题id:" + questionId + "获取单个试题...");
+        LOG.debug("网考根据调卷规则中试题id:" + questionId + "获取单个试题...");
         long beginTime = System.currentTimeMillis();
 
         Question question = Model.of(quesRepo.findById(questionId));
@@ -390,7 +390,7 @@ public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderSe
         }
 
         quesService.formatQues(question);
-        log.debug("查询单个试题耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
+        LOG.debug("查询单个试题耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
 
         //封装成新的题单元集合
         beginTime = System.currentTimeMillis();
@@ -426,11 +426,11 @@ public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderSe
         defaultQuestion.setMasterVersion(defaultQuestionStructure);
 
         if (examId != null) {
-            log.debug("替换试题单元中的音频路径...");
+            LOG.debug("替换试题单元中的音频路径...");
             appendAudioFlag(defaultQuestion, String.valueOf(examId), courseCode, groupCode, question);
         }
 
-        log.debug("封装成新的题单元集合耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
+        LOG.debug("封装成新的题单元集合耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
         return defaultQuestion;
     }
 
@@ -610,7 +610,7 @@ public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderSe
     public DefaultPaper getBaseDefaultPaper(String paperId) {
         Paper basePaper = Model.of(paperRepo.findById(paperId));
         if (basePaper == null) {
-            log.error("该考试和课程下调卷规则中该类型试卷不存在,请检查调卷规则,调卷程序退出");
+            LOG.error("该考试和课程下调卷规则中该类型试卷不存在,请检查调卷规则,调卷程序退出");
             throw new StatusException("Q-020560", "该考试和课程下调卷规则中试卷不存在,请重新制定调卷规则");
         }
 
@@ -624,7 +624,7 @@ public class ExtractConfigProviderServiceImpl implements ExtractConfigProviderSe
         Map<String, DefaultQuestion> map = new HashMap<>();
         List<Question> questions = quesRepo.findByIdIn(questionIds);
         if (CollectionUtils.isEmpty(questions)) {
-            log.error("根据试题id的集合没有查询到试题结合");
+            LOG.error("根据试题id的集合没有查询到试题结合");
             throw new StatusException("Q-020572", "根据试题id的集合没有查询到试题结合");
         }
         for (Question question : questions) {

+ 1229 - 1118
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExtractConfigServiceImpl.java

@@ -1,6 +1,5 @@
 package cn.com.qmth.examcloud.core.questions.service.impl;
 
-
 import static cn.com.qmth.examcloud.core.questions.service.cache.Constants.CACHE_KEY_PAPER_FOR_DTO;
 import static cn.com.qmth.examcloud.core.questions.service.cache.Constants.DEFAULT_TIME_OUT;
 
@@ -17,6 +16,7 @@ import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.StringUtils;
+import org.bson.types.ObjectId;
 import org.nlpcn.commons.lang.util.StringUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,12 +29,18 @@ import org.springframework.data.domain.PageRequest;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.stereotype.Service;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.oe.admin.api.ExamRecordCloudService;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.CheckPaperInExamReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.CheckPaperInExamResp;
+import cn.com.qmth.examcloud.core.oe.student.api.ExamRecordDataCloudService;
 import cn.com.qmth.examcloud.core.questions.base.BeanCopierUtil;
 import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
 import cn.com.qmth.examcloud.core.questions.base.Model;
@@ -50,7 +56,6 @@ import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
 import cn.com.qmth.examcloud.core.questions.dao.AudioTimeConfigRepo;
 import cn.com.qmth.examcloud.core.questions.dao.ExtractConfigRepo;
 import cn.com.qmth.examcloud.core.questions.dao.PaperDetailRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PaperDetailUnitNativeRepo;
 import cn.com.qmth.examcloud.core.questions.dao.PaperDetailUnitRepo;
 import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
 import cn.com.qmth.examcloud.core.questions.dao.entity.AudioTimeConfig;
@@ -89,1135 +94,1241 @@ import cn.com.qmth.examcloud.web.redis.RedisClient;
  */
 @Service("extractConfigService")
 public class ExtractConfigServiceImpl implements ExtractConfigService {
-    private static final Logger logger = LoggerFactory.getLogger(ExtractConfigServiceImpl.class);
+	private static final Logger LOG = LoggerFactory.getLogger(ExtractConfigServiceImpl.class);
 
-    @Autowired
-    private ExtractConfigRepo extractConfigRepo;
+	@Autowired
+	private ExtractConfigRepo extractConfigRepo;
 
-    @Autowired
-    private PaperDetailRepo paperDetailRepo;
+	@Autowired
+	private PaperDetailRepo paperDetailRepo;
 
-    @Autowired
-    private PaperDetailUnitRepo paperDetailUnitRepo;
+	@Autowired
+	private PaperDetailUnitRepo paperDetailUnitRepo;
 
-    @Autowired
-    private AudioTimeConfigRepo audioTimeConfigRepo;
+	@Autowired
+	private AudioTimeConfigRepo audioTimeConfigRepo;
 
-    @Autowired
-    private PaperRepo paperRepo;
+	@Autowired
+	private PaperRepo paperRepo;
 
-    @Autowired
-    private QuesService quesService;
+	@Autowired
+	private QuesService quesService;
 
-    @Autowired
-    private QuestionAudioService questionAudioService;
+	@Autowired
+	private QuestionAudioService questionAudioService;
 
-    @Autowired
-    private MongoTemplate mongoTemplate;
+	@Autowired
+	private MongoTemplate mongoTemplate;
 
-    @Autowired
-    private CourseService courseService;
+	@Autowired
+	private CourseService courseService;
 //
 //    @Autowired
 //    private SysProperty sysProperty;
 
-    @Autowired
-    private PaperDtoAssembler paperDtoAssembler;
-
-    @Autowired
-    private PaperDetailDtoAssembler paperDetailDtoAssembler;
-
-    @Autowired
-    private PaperDetailUnitDtoAssembler paperDetailUnitDtoAssembler;
-
-    @Autowired
-    private SubQuestionDtoAssembler subQuestionDtoAssembler;
-
-    @Autowired
-    private RedisClient redisClient;
-
-    @Autowired
-    private PaperDetailUnitNativeRepo detailUnitNativeRepo;
-
-    @Autowired
-    private ExamCloudService examCloudService;
-
-    @Override
-    public ExtractConfig findConfig(ExtractConfig condition) {
-        if (condition.getExamId() == null) {
-            return null;
-        }
-        if (StringUtils.isBlank(condition.getCourseCode())) {
-            return null;
-        }
-        Query query = new Query();
-        if (!StringUtils.isBlank(condition.getOrgId())) {
-            query.addCriteria(Criteria.where("orgId").is(condition.getOrgId()));
-        }
-        query.addCriteria(Criteria.where("examId").is(condition.getExamId()));
-        query.addCriteria(Criteria.where("courseCode").is(condition.getCourseCode()));
-        ExtractConfig tempConfig = this.mongoTemplate.findOne(query, ExtractConfig.class);
-        return tempConfig;
-    }
-
-    @Override
-    public Map<String, String> saveExtractConfig2(ExtractConfig extractConfig, User user) throws Exception {
-        List<ExamPaper> examPapers = extractConfig.getExamPaperList();
-        for (int i = 0; i < examPapers.size(); i++) {
-            ExamPaper examPaper = examPapers.get(i);
-            Paper paper = examPaper.getPaper();
-            paper = Model.of(paperRepo.findById(paper.getId()));
-            examPaper.setPaper(paper);
-        }
-        Course course = courseService.getCourse(Long.valueOf(extractConfig.getOrgId()), extractConfig.getCourseCode());
-        extractConfig.setCourse(course);
-        extractConfig.setCourseName(course.getName());
-        Map<String, String> newFinishedPaperIdMap = makePaperByConfig(extractConfig);
-        extractConfig.setFinishedPaperIdMap(newFinishedPaperIdMap);
-        extractConfig.setIfFinish((short) 1);
-        extractConfigRepo.save(extractConfig);
-        return newFinishedPaperIdMap;
-    }
-
-    @Override
-    public List<ExamPaper> saveExtractConfig(ExtractConfig extractConfig, User user) {
-        List<ExamPaper> examPapers = extractConfig.getExamPaperList();
-        for (int i = 0; i < examPapers.size(); i++) {
-            ExamPaper examPaper = examPapers.get(i);
-            Paper paper = examPaper.getPaper();
-            paper = Model.of(paperRepo.findById(paper.getId()));
-            examPaper.setPaper(paper);
-            if(ExamType.OFFLINE.name().equals(extractConfig.getExamType())) {
-            	checkOfflinePaper(paper);
-            }
-        }
-        Course course = courseService.getCourse(Long.valueOf(extractConfig.getOrgId()), extractConfig.getCourseCode());
-        extractConfig.setCourse(course);
-        extractConfig.setCourseName(course.getName());
-        extractConfig.setIfFinish((short) 1);
-        extractConfigRepo.save(extractConfig);
-        return examPapers;
-    }
-    
-    private void checkOfflinePaper(Paper paper) {
-    	List<PaperDetailUnit> pdus=paperDetailUnitRepo.findByPaper(paper);
-    	if(pdus==null||pdus.size()==0) {
-    		return;
-    	}
-    	for(PaperDetailUnit pdu:pdus) {
-    		if (pdu.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-                    || pdu.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
-                    || pdu.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION){
-    			throw new StatusException("500", "试卷包含客观题,无法保存规则");
-    		}else if(pdu.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION&&!CollectionUtils.isEmpty(pdu.getQuestion().getSubQuestions())) {
-    			for(Question q:pdu.getQuestion().getSubQuestions()) {
-    				if (q.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-    	                    || q.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
-    	                    || q.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION){
-    	    			throw new StatusException("500", "试卷包含客观题,无法保存规则");
-    	    		}
-    			}
-    		}
-    	}
-    }
-
-
-    @Override
-    public ExtractConfig findConfigById(String id) {
-        if (StringUtils.isBlank(id)) {
-            return null;
-        }
-        return Model.of(extractConfigRepo.findById(id));
-    }
-
-    @Override
-    public Map<String, Object> extractExamPaper(Long exam_id, String course_code, String group_code) {
-        Map<String, Object> returnMap = new HashMap<>();
-        logger.info("调卷开始...");
-        long beginTime = System.currentTimeMillis();
-        logger.info("开始根据examId:" + exam_id + "和courseCode:" + course_code + "获取调卷规则");
-        ExtractConfig extractConfig = this.findConfig(new ExtractConfig(exam_id, course_code));
-        if (extractConfig == null) {
-            logger.error("该考试和课程下调卷规则未制定,请先制定调卷规则,调卷程序退出");
-            returnMap.put("errorMsg", "该考试和课程下调卷规则未制定,请先制定调卷规则");
-            return returnMap;
-        }
-        long configFinishTime = System.currentTimeMillis();
-        logger.info("获取调卷规则共耗时:" + (configFinishTime - beginTime) + "ms");
-        logger.info("根据调卷规则中设置的概率获取类型为" + group_code + "的试卷");
-        Map<String, Paper> paperMap = this.getExamPaperByProbability(extractConfig.getExamPaperList());
-        if (paperMap.isEmpty()) {
-            logger.error("该考试和课程下调卷规则中试卷不存在,请检查调卷规则,调卷程序退出");
-            returnMap.put("errorMsg", "该考试和课程下调卷规则中试卷不存在,请重新制定调卷规则");
-            return returnMap;
-        }
-
-        long paperMapFinishTime = System.currentTimeMillis();
-        logger.info("获取类型为" + group_code + "的试卷共耗时:" + (paperMapFinishTime - configFinishTime) + "ms");
-
-        Paper basePaper = paperMap.get(group_code);
-        if (basePaper == null) {
-            logger.error("该考试和课程下调卷规则中该类型试卷不存在,请检查调卷规则,调卷程序退出");
-            returnMap.put("errorMsg", "该考试和课程下调卷规则中该类型试卷不存在,请重新制定调卷规则");
-            return returnMap;
-        }
-        String basePaperId = basePaper.getId();
-        logger.info("将原始试卷:" + basePaperId + "根据规则重新组卷");
-        int upSetQuestionOrder = extractConfig.getScrambling_the_question_order();
-        int upSetOptionOrder = extractConfig.getScrambling_the_option_order();
-        //不乱序直接调卷
-        if (upSetQuestionOrder == 0 && upSetOptionOrder == 0) {
-            PaperDto paperDto = getPaperDtoByPaper(basePaper, basePaperId);
-            long paperDtoFinishTime = System.currentTimeMillis();
-            logger.info("获取试卷Dto共耗时:" + (paperDtoFinishTime - paperMapFinishTime) + "ms");
-            returnMap.put("paperDto", paperDto);
-            logger.info("调卷完成");
-            logger.info("总共耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
-        } else {
-            //乱序重新生成试卷
-            Paper newPaper = this.recombinationPaper(basePaper, PaperType.STUDENT_EXAM, upSetQuestionOrder, upSetOptionOrder);
-            logger.info("根据新试卷 paperId:" + newPaper.getId() + "组装PaperDto后返回");
-
-            long genPaperFinishTime = System.currentTimeMillis();
-            logger.info("组卷共耗时:" + (genPaperFinishTime - paperMapFinishTime) + "ms");
-            PaperDto paperDto = getPaperDtoByPaper(newPaper, basePaperId);
-
-            long paperDtoFinishTime = System.currentTimeMillis();
-            logger.info("获取试卷Dto共耗时:" + (paperDtoFinishTime - genPaperFinishTime) + "ms");
-
-            returnMap.put("paperDto", paperDto);
-            logger.info("调卷完成");
-            logger.info("总共耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
-        }
-        return returnMap;
-    }
-
-    @Override
-    public boolean checkIsAllQbjectiveQuestion(String paperId) {
-        //优先从redis中获取缓存dto
-        PaperDto cachePaperDto = redisClient.get(CACHE_KEY_PAPER_FOR_DTO + paperId, PaperDto.class, DEFAULT_TIME_OUT);
-        if (cachePaperDto != null) {
-            return cachePaperDto.isAllQbjectiveQuestion();
-        }
-
-        Paper paper = Model.of(paperRepo.findById(paperId));
-        List<PaperDetailUnit> paperDetailUnits = paperDetailUnitRepo.findByPaperOrderByNumber(paper);
-        for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
-            Question question = paperDetailUnit.getQuestion();
-            //填空或问答
-            if (question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
-                    || question.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
-                return false;
-            }
-            if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
-                List<Question> subQuestions = question.getSubQuestions();
-                for (Question subQuestion : subQuestions) {
-                    if (subQuestion.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
-                            || subQuestion.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
-                        return false;
-                    }
-                }
-            }
-        }
-        return true;
-    }
-
-    public boolean checkIsAllQbjectiveByPdu(List<PaperDetailUnit> paperDetailUnits) {
-        for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
-            Question question = paperDetailUnit.getQuestion();
-            //填空或问答
-            if (question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
-                    || question.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
-                return false;
-            }
-            if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
-                List<Question> subQuestions = question.getSubQuestions();
-                for (Question subQuestion : subQuestions) {
-                    if (subQuestion.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
-                            || subQuestion.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
-                        return false;
-                    }
-                }
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public Map<String, String> makePaperByConfig(ExtractConfig extractConfig) {
-        Map<String, String> finishedPaperIdMap = new HashMap<>();
-        if (extractConfig == null) {
-            throw new RuntimeException("调卷规则不存在");
-        }
-        //获得规则中设置的试卷
-        Map<String, Paper> paperMap = this.getExamPaperByProbability(extractConfig.getExamPaperList());
-        if (paperMap.isEmpty()) {
-            throw new RuntimeException("抽取试卷失败");
-        }
-        for (Map.Entry<String, Paper> entry : paperMap.entrySet()) {
-            String key = entry.getKey();
-            //根据原有试卷重新组卷得到新试卷
-        	/*Paper newPaper = this.recombinationPaper(entry.getValue(), 
-             PaperType.PREVIEW,
-             extractConfig.getScrambling_the_question_order(),
-             extractConfig.getScrambling_the_option_order());*/
-            finishedPaperIdMap.put(key, entry.getValue().getId());
-        }
-        return finishedPaperIdMap;
-    }
-
-    /**
-     * 重组试卷,生成新的试卷
-     *
-     * @param paper              选中的试卷
-     * @param upSetQuestionOrder 客观题小题乱序		1:乱序  0:不乱序
-     * @param upSetOptionOrder   客观题选项乱序		1:乱序  0:不乱序
-     * @return
-     */
-    public Paper recombinationPaper(Paper paper, PaperType paperType, int upSetQuestionOrder, int upSetOptionOrder) {
-
-        //将小题全部取出来,只取一次
-        List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaperOrderByNumber(paper);
-        //获取大题
-        List<PaperDetail> paperDetails = paperDetailRepo.findByPaperOrderByNumber(paper);
-
-        //抽取大题号对应的小题
-        Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
-                .collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
-        //最终保存的所有小题
-        List<PaperDetailUnit> savePaperDetailUnits = new ArrayList<>();
-        //保存试卷信息
-        paper.setId(null);
-        paper.setPaperType(paperType);
-        Paper newPaper = paperRepo.insert(paper);
-
-        for (int i = 0; i < paperDetails.size(); i++) {
-            PaperDetail paperDetail = paperDetails.get(i);
-
-            List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());
-            if (paperDetailUnits == null || paperDetailUnits.size() == 0) {
-                continue;
-            }
-            Collections.sort(paperDetailUnits);
-
-            //将大题中最小的number取出
-            PaperDetailUnit topDetailUnit = paperDetailUnits.get(0);
-            int minNumber = topDetailUnit.getNumber();
-
-            //小题乱序
-            if (paperDetailUnits != null && paperDetailUnits.size() > 0) {
-                if ((topDetailUnit.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-                        || topDetailUnit.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
-                        || topDetailUnit.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION)
-                        && upSetQuestionOrder == 1) {
-                    Collections.shuffle(paperDetailUnits);//打乱小题List
-                }
-            }
-            //设置大题信息
-            paperDetail.setId(null);
-            paperDetail.setPaper(newPaper);
-
-            for (int j = 0; j < paperDetailUnits.size(); j++) {
-                //重新设置保存PaperDetailUnit
-                PaperDetailUnit paperDetailUnit = paperDetailUnits.get(j);
-                paperDetailUnit.setPaperType(paperType);
-                paperDetailUnit.setPaper(newPaper);
-                paperDetailUnit.setPaperDetail(paperDetail);
-                paperDetailUnit.setNumber(minNumber + j);            //重新设置序号
-                reSavePaperDetailUtilAndQuestion(paperDetailUnit, upSetOptionOrder);
-                savePaperDetailUnits.add(paperDetailUnit);
-            }
-
-        }
-        //保存大题信息
-        paperDetailRepo.insert(paperDetails);
-        //保存小题信息
-        paperDetailUnitRepo.insert(savePaperDetailUnits);
-
-
-        //清空所有list
-        allPaperDetailUnits.clear();
-        savePaperDetailUnits.clear();
-        paperDetails.clear();
-
-        return newPaper;
-    }
-
-    /**
-     * 每个试卷类型取出一套试卷
-     * {
-     * A:Paper,
-     * B:Paper
-     * }
-     * A是试卷类型   Paper是A类型下选定的试卷
-     *
-     * @param examPaperList
-     * @return
-     */
-    private Map<String, Paper> getExamPaperByProbability(List<ExamPaper> examPaperList) {
-        Map<String, Paper> paperByTypeMap = new HashMap<>();
-        if (examPaperList == null || examPaperList.size() == 0) {
-            throw new RuntimeException("可供抽取的试卷集合为空,无法抽取试卷");
-        }
-
-        Map<String, List<ExamPaper>> examPaperMap = new HashMap<>();
-        for (int i = 0; i < examPaperList.size(); i++) {
-            ExamPaper examPaper = examPaperList.get(i);
-            if (!examPaperMap.containsKey(examPaper.getGroupCode())) {
-                if (examPaper.getPaper() != null) {
-                    List<ExamPaper> epList = new ArrayList<>();
-                    epList.add(examPaper);
-                    examPaperMap.put(examPaper.getGroupCode(), epList);
-                }
-            } else {
-                if (examPaper.getPaper() != null) {
-                    List<ExamPaper> epList = examPaperMap.get(examPaper.getGroupCode());
-                    epList.add(examPaper);
-                }
-            }
-        }
-
-        if (examPaperMap != null) {
-            Set<String> keys = examPaperMap.keySet();
-            Iterator<String> it = keys.iterator();
-            while (it.hasNext()) {
-                String key = it.next();
-                Paper paper = this.getPaperByProbability(examPaperMap.get(key));
-
-                if (paper == null) {
-                    continue;
-                }
-
-                //不能用原来的paper对象,否则examPaperList中的paper对象会被覆盖
-                Paper newPaper = Model.of(paperRepo.findById(paper.getId()));
-                paperByTypeMap.put(key, newPaper);
-            }
-        }
-
-        return paperByTypeMap;
-    }
-
-    /**
-     * 根据设定几率取出一套试卷
-     *
-     * @param examPaperList
-     * @return
-     */
-    private Paper getPaperByProbability(List<ExamPaper> examPaperList) {
-        int sum = 0;
-        for (int i = 0; i < examPaperList.size(); i++) {
-            sum += examPaperList.get(i).getWeight();
-        }
-
-        // 从1开始  
-        Integer rand = new Random().nextInt(sum) + 1;
-        for (int i = 0; i < examPaperList.size(); i++) {
-            rand -= examPaperList.get(i).getWeight();
-            // 选中
-            if (rand <= 0) {
-                return examPaperList.get(i).getPaper();
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * 重新设置并保存paperDetailUnit和question
-     *
-     * @param paperDetailUnit
-     * @param upSetOptionOrder
-     */
-    private void reSavePaperDetailUtilAndQuestion(PaperDetailUnit paperDetailUnit, Integer upSetOptionOrder) {
-        Question question = paperDetailUnit.getQuestion();
-        //选项乱序
-        if (upSetOptionOrder == 1) {
-            //单选或多选
-            if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION ||
-                    question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-                List<String> numberList = new ArrayList<>();
-                List<QuesOption> options = question.getQuesOptions();
-                for (int k = 0; k < options.size(); k++) {
-                    QuesOption quesOption = options.get(k);
-                    numberList.add(quesOption.getNumber());
-                }
-                Collections.shuffle(numberList);                      //打乱number顺序
-                paperDetailUnit.setOptionOrder(StringUtils.join(numberList.toArray(), ","));//设置option顺序
-            }
-            //套题,套题下小题不乱序,选择题选项乱序
-            if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
-                List<Question> subQuestions = question.getSubQuestions();
-                StringBuffer optionOrder = new StringBuffer();
-                for (int m = 0; m < subQuestions.size(); m++) {
-                    Question subQuestion = subQuestions.get(m);
-                    if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION ||
-                            subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-                        List<String> numberList = new ArrayList<>();
-                        List<QuesOption> options = subQuestion.getQuesOptions();
-                        for (int n = 0; n < options.size(); n++) {
-                            QuesOption quesOption = options.get(n);
-                            numberList.add(quesOption.getNumber());
-                        }
-                        Collections.shuffle(numberList);                      //打乱number顺序
-                        optionOrder.append(StringUtils.join(numberList.toArray(), ",")).append(";");
-                    }
-                }
-                paperDetailUnit.setOptionOrder(optionOrder.toString());         //设置option顺序
-            }
-        }
-        paperDetailUnit.setId(null);
-    }
-
-    @Override
-    public List<String> getExamPaperId(String courseCode, String orgId) {
-        Assert.hasLength(courseCode, "courseCode不能为空");
-        Assert.hasLength(orgId, "orgId不能为空");
-        List<String> paperIdList = new ArrayList<>();
-        Query query = new Query();
-        query.addCriteria(Criteria.where("courseCode").is(courseCode));
-        query.addCriteria(Criteria.where("orgId").is(orgId));
-        List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
-        for (ExtractConfig extractConfig : extractConfigList) {
-            List<ExamPaper> examPaperList = extractConfig.getExamPaperList();
-            if (examPaperList != null && examPaperList.size() > 0) {
-                for (ExamPaper examPaper : examPaperList) {
-                    paperIdList.add(examPaper.getPaper().getId());
-                }
-            }
-        }
-        return paperIdList;
-    }
-
-    /**
-     * 根据paper得到PaperDto
-     *
-     * @param paper
-     * @return
-     */
-    @SuppressWarnings("unchecked")
-    private PaperDto getPaperDtoByPaper(Paper paper, String basePaperId) {
-        long beginTime = System.currentTimeMillis();
-        //没有则重新组装
-        PaperDto paperDto = paperDtoAssembler.toDto(paper);
-        paperDto.setBasePaperId(basePaperId);
-        paperDto.setAllQbjectiveQuestion(checkIsAllQbjectiveQuestion(basePaperId));
-
-        long paperDtoEndTime = System.currentTimeMillis();
-        logger.info("单独组装paperDto耗时:" + (paperDtoEndTime - beginTime) + "ms");
-
-        //将小题全部取出来,只取一次
-        List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaper(paper);
-        long pduEndTime = System.currentTimeMillis();
-        logger.info("数据库取小题耗时:" + (pduEndTime - paperDtoEndTime) + "ms");
-        Collections.sort(allPaperDetailUnits);
-        long pduSortEndTime = System.currentTimeMillis();
-        logger.info("排序小题耗时:" + (pduSortEndTime - pduEndTime) + "ms");
-
-        //获取大题
-        List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(paper);
-        long pdEndTime = System.currentTimeMillis();
-        logger.info("数据库取大题耗时:" + (pdEndTime - pduSortEndTime) + "ms");
-        Collections.sort(paperDetails);
-        long pdSortEndTime = System.currentTimeMillis();
-        logger.info("排序大题耗时:" + (pdSortEndTime - pdEndTime) + "ms");
-
-        //抽取大题Id对应的小题
-        Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
-                .collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
-        long pduMapEndTime = System.currentTimeMillis();
-        logger.info("获取大题与小题对应关系耗时:" + (pduMapEndTime - pdSortEndTime) + "ms");
-
-        // 获取大题Dto
-        List<PaperDetailDto> paperDetailDtos = paperDetailDtoAssembler.toDtoList(paperDetails);
-        paperDto.setPaperDetails(paperDetailDtos);
-        long paperDetailDtoEndTime = System.currentTimeMillis();
-        logger.info("单独组装paperDetailDto耗时:" + (paperDetailDtoEndTime - pduMapEndTime) + "ms");
-
-        // 封装小题
-        for (int i = 0; i < paperDetailDtos.size(); i++) {
-            //根据大题查出大题下面的小题
-            PaperDetail paperDetail = paperDetails.get(i);
-
-            List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());
-
-            List<PaperDetailUnitDto> paperDetailUnitDtos = new ArrayList<>();
-            for (int j = 0; j < paperDetailUnits.size(); j++) {
-                PaperDetailUnit paperDetailUnit = paperDetailUnits.get(j);
-                if (paperDetailUnit == null || paperDetailUnit.getQuestion() == null) {
-                    break;
-                }
-                //设置答案
-                setSelectQuestionAnswerUnit(paperDetailUnit);
-
-                PaperDetailUnitDto unitDto = paperDetailUnitDtoAssembler.toDto(paperDetailUnit);
-                /**
-                 * 此处不能传questionId,需要传paperDetailUnitId
-                 * 因为选项乱序在paperDetailUnit里
-                 * unitDto.setQuesId(paperDetailUnit.getQuestion().getId());
-                 */
-                unitDto.setQuesId(paperDetailUnit.getId());
-                String answer = paperDetailUnit.getQuestion().getQuesAnswer();
-                if (StringUtils.isNotEmpty(answer)) {
-                    unitDto.setAnswer(answer);
-                }
-                if (unitDto.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {// 假如是套题
-                    List<Question> subQuesList = paperDetailUnit.getQuestion().getSubQuestions();
-                    List<SubQuestionDto> subQuesDtos = subQuestionDtoAssembler.toDtoList(subQuesList);
-                    for (int m = 0; m < subQuesList.size(); m++) {
-                        List<QuesOptionDto> quesOptionDtos = subQuestionDtoAssembler.toOptionDtoList(subQuesList.get(m).getQuesOptions());
-                        subQuesDtos.get(m).setQuesOptions(quesOptionDtos);
-                        if (StringUtils.isNotEmpty(subQuesList.get(m).getQuesAnswer())) {
-                            subQuesDtos.get(m).setQuesAnswer(subQuesList.get(m).getQuesAnswer());
-                        }
-                        subQuesDtos.get(m).setNumber(m + 1);
-                        //套题分数从小题类中取值
-                        subQuesDtos.get(m).setScore(paperDetailUnit.getSubScoreList().get(m));
-                    }
-                    unitDto.setSubQuestions(subQuesDtos);
-                }
-                paperDetailUnitDtos.add(unitDto);
-            }
-            paperDetailDtos.get(i).setPaperDetailUnits(paperDetailUnitDtos);
-            paperDetailDtos.get(i).setCnNum(CommonUtils.toCHNum(paperDetailDtos.get(i).getNumber()));
-        }
-        long paperDetailUnitDtoEndTime = System.currentTimeMillis();
-        logger.info("单独组装paperDetailUnitDto耗时:" + (paperDetailUnitDtoEndTime - paperDetailDtoEndTime) + "ms");
-
-        //将重新组装的dto放进缓存
-        redisClient.set(CACHE_KEY_PAPER_FOR_DTO + paperDto.getId(), paperDto, DEFAULT_TIME_OUT);
-        return paperDto;
-    }
-
-    /**
-     * 测试获取paperDto
-     *
-     * @param basePaperId
-     * @return
-     */
-    public PaperDto getPaperDtoByPaperNew(String basePaperId) {
-
-        long beginTime = System.currentTimeMillis();
-        //没有则重新组装
-        Paper paper = Model.of(paperRepo.findById(basePaperId));
-        PaperDto paperDto = paperDtoAssembler.toDto(paper);
-        paperDto.setBasePaperId(basePaperId);
-
-        long paperDtoEndTime = System.currentTimeMillis();
-        logger.info("单独组装paperDto耗时:" + (paperDtoEndTime - beginTime) + "ms");
-
-        //将小题全部取出来,只取一次
-        List<PaperDetailUnit> allPaperDetailUnits = detailUnitNativeRepo.findByPaperId(paper.getId());
-        long pduEndTime = System.currentTimeMillis();
-        logger.info("数据库取小题耗时:" + (pduEndTime - paperDtoEndTime) + "ms");
-        Collections.sort(allPaperDetailUnits);
-        long pduSortEndTime = System.currentTimeMillis();
-        logger.info("排序小题耗时:" + (pduSortEndTime - pduEndTime) + "ms");
-
-
-        paperDto.setAllQbjectiveQuestion(checkIsAllQbjectiveByPdu(allPaperDetailUnits));
-        long isAllObjEndtime = System.currentTimeMillis();
-        logger.info("设置客观题耗时:" + (isAllObjEndtime - pduSortEndTime) + "ms");
-
-
-        //获取大题
-        List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(paper);
-        long pdEndTime = System.currentTimeMillis();
-        logger.info("数据库取大题耗时:" + (pdEndTime - isAllObjEndtime) + "ms");
-        Collections.sort(paperDetails);
-        long pdSortEndTime = System.currentTimeMillis();
-        logger.info("排序大题耗时:" + (pdSortEndTime - pdEndTime) + "ms");
-
-        //抽取大题Id对应的小题
-        Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
-                .collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
-        long pduMapEndTime = System.currentTimeMillis();
-        logger.info("获取大题与小题对应关系耗时:" + (pduMapEndTime - pdSortEndTime) + "ms");
-
-        // 获取大题Dto
-        List<PaperDetailDto> paperDetailDtos = paperDetailDtoAssembler.toDtoList(paperDetails);
-        paperDto.setPaperDetails(paperDetailDtos);
-        long paperDetailDtoEndTime = System.currentTimeMillis();
-        logger.info("单独组装paperDetailDto耗时:" + (paperDetailDtoEndTime - pduMapEndTime) + "ms");
-
-        // 封装小题
-        for (int i = 0; i < paperDetailDtos.size(); i++) {
-            //根据大题查出大题下面的小题
-            PaperDetail paperDetail = paperDetails.get(i);
-
-            List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());
-
-            List<PaperDetailUnitDto> paperDetailUnitDtos = new ArrayList<>();
-            for (int j = 0; j < paperDetailUnits.size(); j++) {
-                PaperDetailUnit paperDetailUnit = paperDetailUnits.get(j);
-                if (paperDetailUnit == null || paperDetailUnit.getQuestion() == null) {
-                    break;
-                }
-                //设置答案
-                setSelectQuestionAnswerUnit(paperDetailUnit);
-
-                PaperDetailUnitDto unitDto = paperDetailUnitDtoAssembler.toDto(paperDetailUnit);
-                /**
-                 * 此处不能传questionId,需要传paperDetailUnitId
-                 * 因为选项乱序在paperDetailUnit里
-                 * unitDto.setQuesId(paperDetailUnit.getQuestion().getId());
-                 */
-                unitDto.setQuesId(paperDetailUnit.getId());
-                String answer = paperDetailUnit.getQuestion().getQuesAnswer();
-                if (StringUtils.isNotEmpty(answer)) {
-                    unitDto.setAnswer(answer);
-                }
-                if (unitDto.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {// 假如是套题
-                    List<Question> subQuesList = paperDetailUnit.getQuestion().getSubQuestions();
-                    List<SubQuestionDto> subQuesDtos = subQuestionDtoAssembler.toDtoList(subQuesList);
-                    for (int m = 0; m < subQuesList.size(); m++) {
-                        List<QuesOptionDto> quesOptionDtos = subQuestionDtoAssembler.toOptionDtoList(subQuesList.get(m).getQuesOptions());
-                        subQuesDtos.get(m).setQuesOptions(quesOptionDtos);
-                        if (StringUtils.isNotEmpty(subQuesList.get(m).getQuesAnswer())) {
-                            subQuesDtos.get(m).setQuesAnswer(subQuesList.get(m).getQuesAnswer());
-                        }
-                        subQuesDtos.get(m).setNumber(m + 1);
-                        //套题分数从小题类中取值
-                        subQuesDtos.get(m).setScore(paperDetailUnit.getSubScoreList().get(m));
-                    }
-                    unitDto.setSubQuestions(subQuesDtos);
-                }
-                paperDetailUnitDtos.add(unitDto);
-            }
-            paperDetailDtos.get(i).setPaperDetailUnits(paperDetailUnitDtos);
-            paperDetailDtos.get(i).setCnNum(CommonUtils.toCHNum(paperDetailDtos.get(i).getNumber()));
-        }
-        long paperDetailUnitDtoEndTime = System.currentTimeMillis();
-        logger.info("单独组装paperDetailUnitDto耗时:" + (paperDetailUnitDtoEndTime - paperDetailDtoEndTime) + "ms");
-        return paperDto;
-    }
-
-    private void setSelectQuestoionAnswer(List<PaperDetailUnit> paperDetailUnits) {
-        for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
-            if (paperDetailUnit == null || paperDetailUnit.getQuestion() == null) {
-                break;
-            }
-            String optionOrder = paperDetailUnit.getOptionOrder();
-            Question question = paperDetailUnit.getQuestion();
-            quesService.setSelectQuestionAnswer(question, optionOrder);
-        }
-    }
-
-    private void setSelectQuestionAnswerUnit(PaperDetailUnit paperDetailUnit) {
-        Question question = paperDetailUnit.getQuestion();
-        if (paperDetailUnit == null || question == null) {
-            return;
-        }
-        String optionOrder = paperDetailUnit.getOptionOrder();
-        quesService.setSelectQuestionAnswer(question, optionOrder);
-    }
-
-
-    /**
-     * 根据paperDetailUnitId抽取单个试题
-     * 根据paperDetailUnitId中设置的option顺序对option排序
-     */
-    @Override
-    public QuestionDto extractExamQuestion(String examId, String courseCode, String groupCode, String paperDetailUnitId) {
-        PaperDetailUnit paperDetailUnit = Model.of(paperDetailUnitRepo.findById(paperDetailUnitId));
-        List<PaperDetailUnit> paperDetailUnits = new ArrayList<>();
-        paperDetailUnits.add(paperDetailUnit);
-        //设置答案
-        setSelectQuestoionAnswer(paperDetailUnits);
-        //重新对选择题option进行排序(多选、单选、套题下选择题)
-        reorderChoicequestionOption(paperDetailUnit);
-        Question ques = paperDetailUnit.getQuestion();
-        quesService.formatQues(ques);
-        QuestionDto dto = BeanCopierUtil.copyProperties(ques, QuestionDto.class);
-        dto.setScore(paperDetailUnit.getScore());
-        dto.setQuesOptions(buildQuestionOptionDto(ques.getQuesOptions()));
-        if (ques.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
-            List<Question> subQuesList = ques.getSubQuestions();
-            List<SubQuestionDto> subQuesDtos = new ArrayList<>();
-            for (Question question : subQuesList) {
-                SubQuestionDto subQuestionDto = new SubQuestionDto();
-                BeanUtils.copyProperties(question, subQuestionDto);
-                subQuesDtos.add(subQuestionDto);
-            }
-            for (int m = 0; m < subQuesList.size(); m++) {
-                List<QuesOptionDto> quesOptionDtos = BeanCopierUtil
-                        .copyPropertiesOfList(subQuesList.get(m).getQuesOptions(), QuesOptionDto.class);
-                subQuesDtos.get(m).setQuesOptions(quesOptionDtos);
-                subQuesDtos.get(m).setQuesAnswer(subQuesList.get(m).getQuesAnswer());
-                subQuesDtos.get(m).setNumber(m + 1);
-                //套题分数从小题类中取值
-                subQuesDtos.get(m).setScore(paperDetailUnit.getSubScoreList().get(m));
-                dto.setSubQuestions(subQuesDtos);
-            }
-        }
-        if (ques.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-                || ques.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-            dto.setQuesAnswer(ques.getQuesAnswer());
-        }
-        appendAudioFlag(dto, examId, courseCode, groupCode);
-        return dto;
-    }
-
-    private List<QuesOptionDto> buildQuestionOptionDto(List<QuesOption> quesOptions) {
-        if (quesOptions == null) {
-            return null;
-        }
-        List<QuesOptionDto> optionDtos = new ArrayList<>();
-        for (QuesOption option : quesOptions) {
-            QuesOptionDto optionDto = new QuesOptionDto();
-            optionDto.setNumber(option.getNumber());
-            optionDto.setOptionBody(option.getOptionBody());
-            optionDtos.add(optionDto);
-        }
-        return optionDtos;
-    }
-
-    /**
-     * 1.给QuestionDto添加音频播放次数
-     * 2.给试题音频中有a标签添加url
-     *
-     * @param questionDto
-     */
-    private void appendAudioFlag(QuestionDto questionDto, String examId, String courseCode, String groupCode) {
-        if (questionDto.getHasAudio() != null && questionDto.getHasAudio() == true) {
-            //1.判断questionDto是否含有音频,如果有添加音频播放次数
-            AudioTimeConfig audioTimeConfig = Model.of(audioTimeConfigRepo.findOne(Example.of(new AudioTimeConfig(examId, courseCode, groupCode, questionDto.getId()))));
-            questionDto.setPlayTime(audioTimeConfig != null ? audioTimeConfig.getPlayTime() : null);
-            //2.1 取到题干,给a标签添加url
-            String quesBody = questionDto.getQuesBody();
-            questionDto.setQuesBody(buildBody(quesBody, questionDto));
-            //2.2取到选项,给a标签添加url
-            List<QuesOptionDto> quesOptionDtoList = questionDto.getQuesOptions();
-            if (quesOptionDtoList != null && quesOptionDtoList.size() > 0) {
-                for (QuesOptionDto quesOptionDto : quesOptionDtoList) {
-                    quesOptionDto.setOptionBody(buildBody(quesOptionDto.getOptionBody(), questionDto));
-                }
-            }
-        } else {
-            questionDto.setPlayTime(null);
-        }
-    }
-
-    //给题目和选项添加url
-    public String buildBody(String body, QuestionDto questionDto) {
-        String[] bodyStrings = body.split("></a>");
-        if (bodyStrings.length > 1) {
-            String resultBody = "";
-            for (int i = 0; i < bodyStrings.length; i++) {
-                String containAStr = bodyStrings[i];
-                if (containAStr.indexOf("<a") > -1) {
-                    String questionAudioId = matchAudioName(containAStr, "a", "id");
-                    QuestionAudio questionAudio = questionAudioService.findAudioById(questionAudioId);
-                    if (questionAudio != null) {
+	@Autowired
+	private PaperDtoAssembler paperDtoAssembler;
+
+	@Autowired
+	private PaperDetailDtoAssembler paperDetailDtoAssembler;
+
+	@Autowired
+	private PaperDetailUnitDtoAssembler paperDetailUnitDtoAssembler;
+
+	@Autowired
+	private SubQuestionDtoAssembler subQuestionDtoAssembler;
+
+	@Autowired
+	private RedisClient redisClient;
+
+	/*
+	 * @Autowired private PaperDetailUnitNativeRepo detailUnitNativeRepo;
+	 */
+
+	@Autowired
+	private ExamCloudService examCloudService;
+	@Autowired
+	private ExamRecordCloudService adminExamRecordCloudService;
+	@Autowired
+	private ExamRecordDataCloudService studentExamRecordCloudService;
+
+	@Override
+	public ExtractConfig findConfig(ExtractConfig condition) {
+		if (condition.getExamId() == null) {
+			return null;
+		}
+		if (StringUtils.isBlank(condition.getCourseCode())) {
+			return null;
+		}
+		Query query = new Query();
+		if (!StringUtils.isBlank(condition.getOrgId())) {
+			query.addCriteria(Criteria.where("orgId").is(condition.getOrgId()));
+		}
+		query.addCriteria(Criteria.where("examId").is(condition.getExamId()));
+		query.addCriteria(Criteria.where("courseCode").is(condition.getCourseCode()));
+		ExtractConfig tempConfig = this.mongoTemplate.findOne(query, ExtractConfig.class);
+		return tempConfig;
+	}
+
+	@Override
+	public Map<String, String> saveExtractConfig2(ExtractConfig extractConfig, User user) throws Exception {
+		List<ExamPaper> examPapers = extractConfig.getExamPaperList();
+		for (int i = 0; i < examPapers.size(); i++) {
+			ExamPaper examPaper = examPapers.get(i);
+			Paper paper = examPaper.getPaper();
+			paper = Model.of(paperRepo.findById(paper.getId()));
+			examPaper.setPaper(paper);
+		}
+		Course course = courseService.getCourse(Long.valueOf(extractConfig.getOrgId()), extractConfig.getCourseCode());
+		extractConfig.setCourse(course);
+		extractConfig.setCourseName(course.getName());
+		Map<String, String> newFinishedPaperIdMap = makePaperByConfig(extractConfig);
+		extractConfig.setFinishedPaperIdMap(newFinishedPaperIdMap);
+		extractConfig.setIfFinish((short) 1);
+		extractConfigRepo.save(extractConfig);
+		return newFinishedPaperIdMap;
+	}
+
+	@Override
+	public List<ExamPaper> saveExtractConfig(ExtractConfig extractConfig, User user) {
+		List<ExamPaper> examPapers = extractConfig.getExamPaperList();
+		for (int i = 0; i < examPapers.size(); i++) {
+			ExamPaper examPaper = examPapers.get(i);
+			Paper paper = examPaper.getPaper();
+			paper = Model.of(paperRepo.findById(paper.getId()));
+			examPaper.setPaper(paper);
+			if (ExamType.OFFLINE.name().equals(extractConfig.getExamType())) {
+				checkOfflinePaper(paper);
+			}
+		}
+		if(StringUtils.isNotBlank(extractConfig.getId())) {
+			disposeOldPaper(extractConfig.getId());
+		}
+		Course course = courseService.getCourse(Long.valueOf(extractConfig.getOrgId()), extractConfig.getCourseCode());
+		extractConfig.setCourse(course);
+		extractConfig.setCourseName(course.getName());
+		extractConfig.setIfFinish((short) 1);
+		extractConfigRepo.save(extractConfig);
+		disposeNowPaper(extractConfig.getId());
+		return examPapers;
+	}
+
+	/**先处理原来绑定的试卷
+	 * @param extractConfigId
+	 */
+	private void disposeOldPaper(String extractConfigId) {
+		ExtractConfig ec = Model.of(extractConfigRepo.findById(extractConfigId));
+		for (ExamPaper paper : ec.getExamPaperList()) {
+			if(inExam(paper.getPaper().getId())) {//如果有考试记录
+				updatePaperInUse(paper.getPaper().getId(), 1);
+			}else {
+				if(!inOtherExtractConfig(extractConfigId, paper.getPaper().getId())) {//没有考试记录且不被其他调卷规则引用
+					updatePaperInUse(paper.getPaper().getId(), 0);
+				}
+			}
+		}
+	}
+	/**处理当前绑定试卷
+	 * @param extractConfigId
+	 */
+	private void disposeNowPaper(String extractConfigId) {
+		ExtractConfig ec = Model.of(extractConfigRepo.findById(extractConfigId));
+		for (ExamPaper paper : ec.getExamPaperList()) {
+			updatePaperInUse(paper.getPaper().getId(), 1);
+		}
+	}
+
+	private void updatePaperInUse(String paperId, int inUse) {
+		Query query = null;
+		if(paperId.length()==24) {
+			query = Query.query(Criteria.where("_id").is(new ObjectId(paperId)));
+		}else {
+			query = Query.query(Criteria.where("_id").is(paperId));
+		}
+		Update update = new Update();
+		update.set("inUse", inUse);
+		mongoTemplate.updateFirst(query, update, "paper");
+	}
+
+	private boolean inExam(String paperId) {
+		CheckPaperInExamReq req1 = new CheckPaperInExamReq();
+		req1.setBasePaperId(paperId);
+		CheckPaperInExamResp res1 = adminExamRecordCloudService.checkPaperInExam(req1);
+		if (res1.getInExam()) {
+			return res1.getInExam();
+		}
+		cn.com.qmth.examcloud.core.oe.student.api.request.CheckPaperInExamReq req2 = new cn.com.qmth.examcloud.core.oe.student.api.request.CheckPaperInExamReq();
+		req2.setBasePaperId(paperId);
+		cn.com.qmth.examcloud.core.oe.student.api.response.CheckPaperInExamResp res2 = studentExamRecordCloudService
+				.checkPaperInExam(req2);
+		return res2.getInExam();
+	}
+
+	private boolean inOtherExtractConfig(String extractConfigId, String paperId) {
+		Criteria criteria = new Criteria();
+		criteria.and("examPaperList").elemMatch(Criteria.where("paper.$id").is(new ObjectId(paperId)));
+		Query query = Query.query(criteria);
+		List<ExtractConfig> list = mongoTemplate.find(query, ExtractConfig.class, "extractConfig");
+		if (list == null || list.size() == 0) {
+			return false;
+		}
+		if (list.size() == 1 && list.get(0).getId().equals(extractConfigId)) {
+			return false;
+		}
+		return true;
+	}
+
+	private void checkOfflinePaper(Paper paper) {
+		List<PaperDetailUnit> pdus = paperDetailUnitRepo.findByPaper(paper);
+		if (pdus == null || pdus.size() == 0) {
+			return;
+		}
+		for (PaperDetailUnit pdu : pdus) {
+			if (pdu.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+					|| pdu.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
+					|| pdu.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+				throw new StatusException("500", "试卷包含客观题,无法保存规则");
+			} else if (pdu.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION
+					&& !CollectionUtils.isEmpty(pdu.getQuestion().getSubQuestions())) {
+				for (Question q : pdu.getQuestion().getSubQuestions()) {
+					if (q.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+							|| q.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
+							|| q.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+						throw new StatusException("500", "试卷包含客观题,无法保存规则");
+					}
+				}
+			}
+		}
+	}
+
+	@Override
+	public ExtractConfig findConfigById(String id) {
+		if (StringUtils.isBlank(id)) {
+			return null;
+		}
+		return Model.of(extractConfigRepo.findById(id));
+	}
+
+	@Override
+	public Map<String, Object> extractExamPaper(Long exam_id, String course_code, String group_code) {
+		Map<String, Object> returnMap = new HashMap<>();
+		LOG.info("调卷开始...");
+		long beginTime = System.currentTimeMillis();
+		LOG.info("开始根据examId:" + exam_id + "和courseCode:" + course_code + "获取调卷规则");
+		ExtractConfig extractConfig = this.findConfig(new ExtractConfig(exam_id, course_code));
+		if (extractConfig == null) {
+			LOG.error("该考试和课程下调卷规则未制定,请先制定调卷规则,调卷程序退出");
+			returnMap.put("errorMsg", "该考试和课程下调卷规则未制定,请先制定调卷规则");
+			return returnMap;
+		}
+		long configFinishTime = System.currentTimeMillis();
+		LOG.info("获取调卷规则共耗时:" + (configFinishTime - beginTime) + "ms");
+		LOG.info("根据调卷规则中设置的概率获取类型为" + group_code + "的试卷");
+		Map<String, Paper> paperMap = this.getExamPaperByProbability(extractConfig.getExamPaperList());
+		if (paperMap.isEmpty()) {
+			LOG.error("该考试和课程下调卷规则中试卷不存在,请检查调卷规则,调卷程序退出");
+			returnMap.put("errorMsg", "该考试和课程下调卷规则中试卷不存在,请重新制定调卷规则");
+			return returnMap;
+		}
+
+		long paperMapFinishTime = System.currentTimeMillis();
+		LOG.info("获取类型为" + group_code + "的试卷共耗时:" + (paperMapFinishTime - configFinishTime) + "ms");
+
+		Paper basePaper = paperMap.get(group_code);
+		if (basePaper == null) {
+			LOG.error("该考试和课程下调卷规则中该类型试卷不存在,请检查调卷规则,调卷程序退出");
+			returnMap.put("errorMsg", "该考试和课程下调卷规则中该类型试卷不存在,请重新制定调卷规则");
+			return returnMap;
+		}
+		String basePaperId = basePaper.getId();
+		LOG.info("将原始试卷:" + basePaperId + "根据规则重新组卷");
+		int upSetQuestionOrder = extractConfig.getScrambling_the_question_order();
+		int upSetOptionOrder = extractConfig.getScrambling_the_option_order();
+		// 不乱序直接调卷
+		if (upSetQuestionOrder == 0 && upSetOptionOrder == 0) {
+			PaperDto paperDto = getPaperDtoByPaper(basePaper, basePaperId);
+			long paperDtoFinishTime = System.currentTimeMillis();
+			LOG.info("获取试卷Dto共耗时:" + (paperDtoFinishTime - paperMapFinishTime) + "ms");
+			returnMap.put("paperDto", paperDto);
+			LOG.info("调卷完成");
+			LOG.info("总共耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
+		} else {
+			// 乱序重新生成试卷
+			Paper newPaper = this.recombinationPaper(basePaper, PaperType.STUDENT_EXAM, upSetQuestionOrder,
+					upSetOptionOrder);
+			LOG.info("根据新试卷 paperId:" + newPaper.getId() + "组装PaperDto后返回");
+
+			long genPaperFinishTime = System.currentTimeMillis();
+			LOG.info("组卷共耗时:" + (genPaperFinishTime - paperMapFinishTime) + "ms");
+			PaperDto paperDto = getPaperDtoByPaper(newPaper, basePaperId);
+
+			long paperDtoFinishTime = System.currentTimeMillis();
+			LOG.info("获取试卷Dto共耗时:" + (paperDtoFinishTime - genPaperFinishTime) + "ms");
+
+			returnMap.put("paperDto", paperDto);
+			LOG.info("调卷完成");
+			LOG.info("总共耗时:" + (System.currentTimeMillis() - beginTime) + "ms");
+		}
+		return returnMap;
+	}
+
+	@Override
+	public boolean checkIsAllQbjectiveQuestion(String paperId) {
+		// 优先从redis中获取缓存dto
+		PaperDto cachePaperDto = redisClient.get(CACHE_KEY_PAPER_FOR_DTO + paperId, PaperDto.class, DEFAULT_TIME_OUT);
+		if (cachePaperDto != null) {
+			return cachePaperDto.isAllQbjectiveQuestion();
+		}
+
+		Paper paper = Model.of(paperRepo.findById(paperId));
+		List<PaperDetailUnit> paperDetailUnits = paperDetailUnitRepo.findByPaperOrderByNumber(paper);
+		for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
+			Question question = paperDetailUnit.getQuestion();
+			// 填空或问答
+			if (question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
+					|| question.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
+				return false;
+			}
+			if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+				List<Question> subQuestions = question.getSubQuestions();
+				for (Question subQuestion : subQuestions) {
+					if (subQuestion.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
+							|| subQuestion.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
+						return false;
+					}
+				}
+			}
+		}
+		return true;
+	}
+
+	public boolean checkIsAllQbjectiveByPdu(List<PaperDetailUnit> paperDetailUnits) {
+		for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
+			Question question = paperDetailUnit.getQuestion();
+			// 填空或问答
+			if (question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
+					|| question.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
+				return false;
+			}
+			if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+				List<Question> subQuestions = question.getSubQuestions();
+				for (Question subQuestion : subQuestions) {
+					if (subQuestion.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION
+							|| subQuestion.getQuestionType() == QuesStructType.TEXT_ANSWER_QUESTION) {
+						return false;
+					}
+				}
+			}
+		}
+		return true;
+	}
+
+	@Override
+	public Map<String, String> makePaperByConfig(ExtractConfig extractConfig) {
+		Map<String, String> finishedPaperIdMap = new HashMap<>();
+		if (extractConfig == null) {
+			throw new RuntimeException("调卷规则不存在");
+		}
+		// 获得规则中设置的试卷
+		Map<String, Paper> paperMap = this.getExamPaperByProbability(extractConfig.getExamPaperList());
+		if (paperMap.isEmpty()) {
+			throw new RuntimeException("抽取试卷失败");
+		}
+		for (Map.Entry<String, Paper> entry : paperMap.entrySet()) {
+			String key = entry.getKey();
+			// 根据原有试卷重新组卷得到新试卷
+			/*
+			 * Paper newPaper = this.recombinationPaper(entry.getValue(), PaperType.PREVIEW,
+			 * extractConfig.getScrambling_the_question_order(),
+			 * extractConfig.getScrambling_the_option_order());
+			 */
+			finishedPaperIdMap.put(key, entry.getValue().getId());
+		}
+		return finishedPaperIdMap;
+	}
+
+	/**
+	 * 重组试卷,生成新的试卷
+	 *
+	 * @param paper              选中的试卷
+	 * @param upSetQuestionOrder 客观题小题乱序 1:乱序 0:不乱序
+	 * @param upSetOptionOrder   客观题选项乱序 1:乱序 0:不乱序
+	 * @return
+	 */
+	public Paper recombinationPaper(Paper paper, PaperType paperType, int upSetQuestionOrder, int upSetOptionOrder) {
+
+		// 将小题全部取出来,只取一次
+		List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaperOrderByNumber(paper);
+		// 获取大题
+		List<PaperDetail> paperDetails = paperDetailRepo.findByPaperOrderByNumber(paper);
+
+		// 抽取大题号对应的小题
+		Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
+				.collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
+		// 最终保存的所有小题
+		List<PaperDetailUnit> savePaperDetailUnits = new ArrayList<>();
+		// 保存试卷信息
+		paper.setId(null);
+		paper.setPaperType(paperType);
+		Paper newPaper = paperRepo.insert(paper);
+
+		for (int i = 0; i < paperDetails.size(); i++) {
+			PaperDetail paperDetail = paperDetails.get(i);
+
+			List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());
+			if (paperDetailUnits == null || paperDetailUnits.size() == 0) {
+				continue;
+			}
+			Collections.sort(paperDetailUnits);
+
+			// 将大题中最小的number取出
+			PaperDetailUnit topDetailUnit = paperDetailUnits.get(0);
+			int minNumber = topDetailUnit.getNumber();
+
+			// 小题乱序
+			if (paperDetailUnits != null && paperDetailUnits.size() > 0) {
+				if ((topDetailUnit.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+						|| topDetailUnit.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
+						|| topDetailUnit.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION)
+						&& upSetQuestionOrder == 1) {
+					Collections.shuffle(paperDetailUnits);// 打乱小题List
+				}
+			}
+			// 设置大题信息
+			paperDetail.setId(null);
+			paperDetail.setPaper(newPaper);
+
+			for (int j = 0; j < paperDetailUnits.size(); j++) {
+				// 重新设置保存PaperDetailUnit
+				PaperDetailUnit paperDetailUnit = paperDetailUnits.get(j);
+				paperDetailUnit.setPaperType(paperType);
+				paperDetailUnit.setPaper(newPaper);
+				paperDetailUnit.setPaperDetail(paperDetail);
+				paperDetailUnit.setNumber(minNumber + j); // 重新设置序号
+				reSavePaperDetailUtilAndQuestion(paperDetailUnit, upSetOptionOrder);
+				savePaperDetailUnits.add(paperDetailUnit);
+			}
+
+		}
+		// 保存大题信息
+		paperDetailRepo.insert(paperDetails);
+		// 保存小题信息
+		paperDetailUnitRepo.insert(savePaperDetailUnits);
+
+		// 清空所有list
+		allPaperDetailUnits.clear();
+		savePaperDetailUnits.clear();
+		paperDetails.clear();
+
+		return newPaper;
+	}
+
+	/**
+	 * 每个试卷类型取出一套试卷 { A:Paper, B:Paper } A是试卷类型 Paper是A类型下选定的试卷
+	 *
+	 * @param examPaperList
+	 * @return
+	 */
+	private Map<String, Paper> getExamPaperByProbability(List<ExamPaper> examPaperList) {
+		Map<String, Paper> paperByTypeMap = new HashMap<>();
+		if (examPaperList == null || examPaperList.size() == 0) {
+			throw new RuntimeException("可供抽取的试卷集合为空,无法抽取试卷");
+		}
+
+		Map<String, List<ExamPaper>> examPaperMap = new HashMap<>();
+		for (int i = 0; i < examPaperList.size(); i++) {
+			ExamPaper examPaper = examPaperList.get(i);
+			if (!examPaperMap.containsKey(examPaper.getGroupCode())) {
+				if (examPaper.getPaper() != null) {
+					List<ExamPaper> epList = new ArrayList<>();
+					epList.add(examPaper);
+					examPaperMap.put(examPaper.getGroupCode(), epList);
+				}
+			} else {
+				if (examPaper.getPaper() != null) {
+					List<ExamPaper> epList = examPaperMap.get(examPaper.getGroupCode());
+					epList.add(examPaper);
+				}
+			}
+		}
+
+		if (examPaperMap != null) {
+			Set<String> keys = examPaperMap.keySet();
+			Iterator<String> it = keys.iterator();
+			while (it.hasNext()) {
+				String key = it.next();
+				Paper paper = this.getPaperByProbability(examPaperMap.get(key));
+
+				if (paper == null) {
+					continue;
+				}
+
+				// 不能用原来的paper对象,否则examPaperList中的paper对象会被覆盖
+				Paper newPaper = Model.of(paperRepo.findById(paper.getId()));
+				paperByTypeMap.put(key, newPaper);
+			}
+		}
+
+		return paperByTypeMap;
+	}
+
+	/**
+	 * 根据设定几率取出一套试卷
+	 *
+	 * @param examPaperList
+	 * @return
+	 */
+	private Paper getPaperByProbability(List<ExamPaper> examPaperList) {
+		int sum = 0;
+		for (int i = 0; i < examPaperList.size(); i++) {
+			sum += examPaperList.get(i).getWeight();
+		}
+
+		// 从1开始
+		Integer rand = new Random().nextInt(sum) + 1;
+		for (int i = 0; i < examPaperList.size(); i++) {
+			rand -= examPaperList.get(i).getWeight();
+			// 选中
+			if (rand <= 0) {
+				return examPaperList.get(i).getPaper();
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * 重新设置并保存paperDetailUnit和question
+	 *
+	 * @param paperDetailUnit
+	 * @param upSetOptionOrder
+	 */
+	private void reSavePaperDetailUtilAndQuestion(PaperDetailUnit paperDetailUnit, Integer upSetOptionOrder) {
+		Question question = paperDetailUnit.getQuestion();
+		// 选项乱序
+		if (upSetOptionOrder == 1) {
+			// 单选或多选
+			if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+					|| question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+				List<String> numberList = new ArrayList<>();
+				List<QuesOption> options = question.getQuesOptions();
+				for (int k = 0; k < options.size(); k++) {
+					QuesOption quesOption = options.get(k);
+					numberList.add(quesOption.getNumber());
+				}
+				Collections.shuffle(numberList); // 打乱number顺序
+				paperDetailUnit.setOptionOrder(StringUtils.join(numberList.toArray(), ","));// 设置option顺序
+			}
+			// 套题,套题下小题不乱序,选择题选项乱序
+			if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+				List<Question> subQuestions = question.getSubQuestions();
+				StringBuffer optionOrder = new StringBuffer();
+				for (int m = 0; m < subQuestions.size(); m++) {
+					Question subQuestion = subQuestions.get(m);
+					if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+							|| subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+						List<String> numberList = new ArrayList<>();
+						List<QuesOption> options = subQuestion.getQuesOptions();
+						for (int n = 0; n < options.size(); n++) {
+							QuesOption quesOption = options.get(n);
+							numberList.add(quesOption.getNumber());
+						}
+						Collections.shuffle(numberList); // 打乱number顺序
+						optionOrder.append(StringUtils.join(numberList.toArray(), ",")).append(";");
+					}
+				}
+				paperDetailUnit.setOptionOrder(optionOrder.toString()); // 设置option顺序
+			}
+		}
+		paperDetailUnit.setId(null);
+	}
+
+	@Override
+	public List<String> getExamPaperId(String courseCode, String orgId) {
+		Assert.hasLength(courseCode, "courseCode不能为空");
+		Assert.hasLength(orgId, "orgId不能为空");
+		List<String> paperIdList = new ArrayList<>();
+		Query query = new Query();
+		query.addCriteria(Criteria.where("courseCode").is(courseCode));
+		query.addCriteria(Criteria.where("orgId").is(orgId));
+		List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
+		for (ExtractConfig extractConfig : extractConfigList) {
+			List<ExamPaper> examPaperList = extractConfig.getExamPaperList();
+			if (examPaperList != null && examPaperList.size() > 0) {
+				for (ExamPaper examPaper : examPaperList) {
+					paperIdList.add(examPaper.getPaper().getId());
+				}
+			}
+		}
+		return paperIdList;
+	}
+
+	/**
+	 * 根据paper得到PaperDto
+	 *
+	 * @param paper
+	 * @return
+	 */
+	@SuppressWarnings("unchecked")
+	private PaperDto getPaperDtoByPaper(Paper paper, String basePaperId) {
+		long beginTime = System.currentTimeMillis();
+		// 没有则重新组装
+		PaperDto paperDto = paperDtoAssembler.toDto(paper);
+		paperDto.setBasePaperId(basePaperId);
+		paperDto.setAllQbjectiveQuestion(checkIsAllQbjectiveQuestion(basePaperId));
+
+		long paperDtoEndTime = System.currentTimeMillis();
+		LOG.info("单独组装paperDto耗时:" + (paperDtoEndTime - beginTime) + "ms");
+
+		// 将小题全部取出来,只取一次
+		List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaper(paper);
+		long pduEndTime = System.currentTimeMillis();
+		LOG.info("数据库取小题耗时:" + (pduEndTime - paperDtoEndTime) + "ms");
+		Collections.sort(allPaperDetailUnits);
+		long pduSortEndTime = System.currentTimeMillis();
+		LOG.info("排序小题耗时:" + (pduSortEndTime - pduEndTime) + "ms");
+
+		// 获取大题
+		List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(paper);
+		long pdEndTime = System.currentTimeMillis();
+		LOG.info("数据库取大题耗时:" + (pdEndTime - pduSortEndTime) + "ms");
+		Collections.sort(paperDetails);
+		long pdSortEndTime = System.currentTimeMillis();
+		LOG.info("排序大题耗时:" + (pdSortEndTime - pdEndTime) + "ms");
+
+		// 抽取大题Id对应的小题
+		Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
+				.collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
+		long pduMapEndTime = System.currentTimeMillis();
+		LOG.info("获取大题与小题对应关系耗时:" + (pduMapEndTime - pdSortEndTime) + "ms");
+
+		// 获取大题Dto
+		List<PaperDetailDto> paperDetailDtos = paperDetailDtoAssembler.toDtoList(paperDetails);
+		paperDto.setPaperDetails(paperDetailDtos);
+		long paperDetailDtoEndTime = System.currentTimeMillis();
+		LOG.info("单独组装paperDetailDto耗时:" + (paperDetailDtoEndTime - pduMapEndTime) + "ms");
+
+		// 封装小题
+		for (int i = 0; i < paperDetailDtos.size(); i++) {
+			// 根据大题查出大题下面的小题
+			PaperDetail paperDetail = paperDetails.get(i);
+
+			List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());
+
+			List<PaperDetailUnitDto> paperDetailUnitDtos = new ArrayList<>();
+			for (int j = 0; j < paperDetailUnits.size(); j++) {
+				PaperDetailUnit paperDetailUnit = paperDetailUnits.get(j);
+				if (paperDetailUnit == null || paperDetailUnit.getQuestion() == null) {
+					break;
+				}
+				// 设置答案
+				setSelectQuestionAnswerUnit(paperDetailUnit);
+
+				PaperDetailUnitDto unitDto = paperDetailUnitDtoAssembler.toDto(paperDetailUnit);
+				/**
+				 * 此处不能传questionId,需要传paperDetailUnitId 因为选项乱序在paperDetailUnit里
+				 * unitDto.setQuesId(paperDetailUnit.getQuestion().getId());
+				 */
+				unitDto.setQuesId(paperDetailUnit.getId());
+				String answer = paperDetailUnit.getQuestion().getQuesAnswer();
+				if (StringUtils.isNotEmpty(answer)) {
+					unitDto.setAnswer(answer);
+				}
+				if (unitDto.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {// 假如是套题
+					List<Question> subQuesList = paperDetailUnit.getQuestion().getSubQuestions();
+					List<SubQuestionDto> subQuesDtos = subQuestionDtoAssembler.toDtoList(subQuesList);
+					for (int m = 0; m < subQuesList.size(); m++) {
+						List<QuesOptionDto> quesOptionDtos = subQuestionDtoAssembler
+								.toOptionDtoList(subQuesList.get(m).getQuesOptions());
+						subQuesDtos.get(m).setQuesOptions(quesOptionDtos);
+						if (StringUtils.isNotEmpty(subQuesList.get(m).getQuesAnswer())) {
+							subQuesDtos.get(m).setQuesAnswer(subQuesList.get(m).getQuesAnswer());
+						}
+						subQuesDtos.get(m).setNumber(m + 1);
+						// 套题分数从小题类中取值
+						subQuesDtos.get(m).setScore(paperDetailUnit.getSubScoreList().get(m));
+					}
+					unitDto.setSubQuestions(subQuesDtos);
+				}
+				paperDetailUnitDtos.add(unitDto);
+			}
+			paperDetailDtos.get(i).setPaperDetailUnits(paperDetailUnitDtos);
+			paperDetailDtos.get(i).setCnNum(CommonUtils.toCHNum(paperDetailDtos.get(i).getNumber()));
+		}
+		long paperDetailUnitDtoEndTime = System.currentTimeMillis();
+		LOG.info("单独组装paperDetailUnitDto耗时:" + (paperDetailUnitDtoEndTime - paperDetailDtoEndTime) + "ms");
+
+		// 将重新组装的dto放进缓存
+		redisClient.set(CACHE_KEY_PAPER_FOR_DTO + paperDto.getId(), paperDto, DEFAULT_TIME_OUT);
+		return paperDto;
+	}
+
+	/**
+	 * 测试获取paperDto
+	 *
+	 * @param basePaperId
+	 * @return
+	 */
+	@Deprecated
+	public PaperDto getPaperDtoByPaperNew(String basePaperId) {
+
+		long beginTime = System.currentTimeMillis();
+		// 没有则重新组装
+		Paper paper = Model.of(paperRepo.findById(basePaperId));
+		PaperDto paperDto = paperDtoAssembler.toDto(paper);
+		paperDto.setBasePaperId(basePaperId);
+
+		long paperDtoEndTime = System.currentTimeMillis();
+		LOG.info("单独组装paperDto耗时:" + (paperDtoEndTime - beginTime) + "ms");
+
+		// 将小题全部取出来,只取一次
+		// List<PaperDetailUnit> allPaperDetailUnits =
+		// detailUnitNativeRepo.findByPaperId(paper.getId());
+		List<PaperDetailUnit> allPaperDetailUnits = new ArrayList<>();
+
+		long pduEndTime = System.currentTimeMillis();
+		LOG.info("数据库取小题耗时:" + (pduEndTime - paperDtoEndTime) + "ms");
+		Collections.sort(allPaperDetailUnits);
+		long pduSortEndTime = System.currentTimeMillis();
+		LOG.info("排序小题耗时:" + (pduSortEndTime - pduEndTime) + "ms");
+
+		paperDto.setAllQbjectiveQuestion(checkIsAllQbjectiveByPdu(allPaperDetailUnits));
+		long isAllObjEndtime = System.currentTimeMillis();
+		LOG.info("设置客观题耗时:" + (isAllObjEndtime - pduSortEndTime) + "ms");
+
+		// 获取大题
+		List<PaperDetail> paperDetails = paperDetailRepo.findByPaper(paper);
+		long pdEndTime = System.currentTimeMillis();
+		LOG.info("数据库取大题耗时:" + (pdEndTime - isAllObjEndtime) + "ms");
+		Collections.sort(paperDetails);
+		long pdSortEndTime = System.currentTimeMillis();
+		LOG.info("排序大题耗时:" + (pdSortEndTime - pdEndTime) + "ms");
+
+		// 抽取大题Id对应的小题
+		Map<String, List<PaperDetailUnit>> pduMap = allPaperDetailUnits.stream()
+				.collect(Collectors.groupingBy(PaperDetailUnit::getDetailId));
+		long pduMapEndTime = System.currentTimeMillis();
+		LOG.info("获取大题与小题对应关系耗时:" + (pduMapEndTime - pdSortEndTime) + "ms");
+
+		// 获取大题Dto
+		List<PaperDetailDto> paperDetailDtos = paperDetailDtoAssembler.toDtoList(paperDetails);
+		paperDto.setPaperDetails(paperDetailDtos);
+		long paperDetailDtoEndTime = System.currentTimeMillis();
+		LOG.info("单独组装paperDetailDto耗时:" + (paperDetailDtoEndTime - pduMapEndTime) + "ms");
+
+		// 封装小题
+		for (int i = 0; i < paperDetailDtos.size(); i++) {
+			// 根据大题查出大题下面的小题
+			PaperDetail paperDetail = paperDetails.get(i);
+
+			List<PaperDetailUnit> paperDetailUnits = pduMap.get(paperDetail.getId());
+
+			List<PaperDetailUnitDto> paperDetailUnitDtos = new ArrayList<>();
+			for (int j = 0; j < paperDetailUnits.size(); j++) {
+				PaperDetailUnit paperDetailUnit = paperDetailUnits.get(j);
+				if (paperDetailUnit == null || paperDetailUnit.getQuestion() == null) {
+					break;
+				}
+				// 设置答案
+				setSelectQuestionAnswerUnit(paperDetailUnit);
+
+				PaperDetailUnitDto unitDto = paperDetailUnitDtoAssembler.toDto(paperDetailUnit);
+				/**
+				 * 此处不能传questionId,需要传paperDetailUnitId 因为选项乱序在paperDetailUnit里
+				 * unitDto.setQuesId(paperDetailUnit.getQuestion().getId());
+				 */
+				unitDto.setQuesId(paperDetailUnit.getId());
+				String answer = paperDetailUnit.getQuestion().getQuesAnswer();
+				if (StringUtils.isNotEmpty(answer)) {
+					unitDto.setAnswer(answer);
+				}
+				if (unitDto.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {// 假如是套题
+					List<Question> subQuesList = paperDetailUnit.getQuestion().getSubQuestions();
+					List<SubQuestionDto> subQuesDtos = subQuestionDtoAssembler.toDtoList(subQuesList);
+					for (int m = 0; m < subQuesList.size(); m++) {
+						List<QuesOptionDto> quesOptionDtos = subQuestionDtoAssembler
+								.toOptionDtoList(subQuesList.get(m).getQuesOptions());
+						subQuesDtos.get(m).setQuesOptions(quesOptionDtos);
+						if (StringUtils.isNotEmpty(subQuesList.get(m).getQuesAnswer())) {
+							subQuesDtos.get(m).setQuesAnswer(subQuesList.get(m).getQuesAnswer());
+						}
+						subQuesDtos.get(m).setNumber(m + 1);
+						// 套题分数从小题类中取值
+						subQuesDtos.get(m).setScore(paperDetailUnit.getSubScoreList().get(m));
+					}
+					unitDto.setSubQuestions(subQuesDtos);
+				}
+				paperDetailUnitDtos.add(unitDto);
+			}
+			paperDetailDtos.get(i).setPaperDetailUnits(paperDetailUnitDtos);
+			paperDetailDtos.get(i).setCnNum(CommonUtils.toCHNum(paperDetailDtos.get(i).getNumber()));
+		}
+		long paperDetailUnitDtoEndTime = System.currentTimeMillis();
+		LOG.info("单独组装paperDetailUnitDto耗时:" + (paperDetailUnitDtoEndTime - paperDetailDtoEndTime) + "ms");
+		return paperDto;
+	}
+
+	private void setSelectQuestoionAnswer(List<PaperDetailUnit> paperDetailUnits) {
+		for (PaperDetailUnit paperDetailUnit : paperDetailUnits) {
+			if (paperDetailUnit == null || paperDetailUnit.getQuestion() == null) {
+				break;
+			}
+			String optionOrder = paperDetailUnit.getOptionOrder();
+			Question question = paperDetailUnit.getQuestion();
+			quesService.setSelectQuestionAnswer(question, optionOrder);
+		}
+	}
+
+	private void setSelectQuestionAnswerUnit(PaperDetailUnit paperDetailUnit) {
+		Question question = paperDetailUnit.getQuestion();
+		if (paperDetailUnit == null || question == null) {
+			return;
+		}
+		String optionOrder = paperDetailUnit.getOptionOrder();
+		quesService.setSelectQuestionAnswer(question, optionOrder);
+	}
+
+	/**
+	 * 根据paperDetailUnitId抽取单个试题 根据paperDetailUnitId中设置的option顺序对option排序
+	 */
+	@Override
+	public QuestionDto extractExamQuestion(String examId, String courseCode, String groupCode,
+			String paperDetailUnitId) {
+		PaperDetailUnit paperDetailUnit = Model.of(paperDetailUnitRepo.findById(paperDetailUnitId));
+		List<PaperDetailUnit> paperDetailUnits = new ArrayList<>();
+		paperDetailUnits.add(paperDetailUnit);
+		// 设置答案
+		setSelectQuestoionAnswer(paperDetailUnits);
+		// 重新对选择题option进行排序(多选、单选、套题下选择题)
+		reorderChoicequestionOption(paperDetailUnit);
+		Question ques = paperDetailUnit.getQuestion();
+		quesService.formatQues(ques);
+		QuestionDto dto = BeanCopierUtil.copyProperties(ques, QuestionDto.class);
+		dto.setScore(paperDetailUnit.getScore());
+		dto.setQuesOptions(buildQuestionOptionDto(ques.getQuesOptions()));
+		if (ques.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+			List<Question> subQuesList = ques.getSubQuestions();
+			List<SubQuestionDto> subQuesDtos = new ArrayList<>();
+			for (Question question : subQuesList) {
+				SubQuestionDto subQuestionDto = new SubQuestionDto();
+				BeanUtils.copyProperties(question, subQuestionDto);
+				subQuesDtos.add(subQuestionDto);
+			}
+			for (int m = 0; m < subQuesList.size(); m++) {
+				List<QuesOptionDto> quesOptionDtos = BeanCopierUtil
+						.copyPropertiesOfList(subQuesList.get(m).getQuesOptions(), QuesOptionDto.class);
+				subQuesDtos.get(m).setQuesOptions(quesOptionDtos);
+				subQuesDtos.get(m).setQuesAnswer(subQuesList.get(m).getQuesAnswer());
+				subQuesDtos.get(m).setNumber(m + 1);
+				// 套题分数从小题类中取值
+				subQuesDtos.get(m).setScore(paperDetailUnit.getSubScoreList().get(m));
+				dto.setSubQuestions(subQuesDtos);
+			}
+		}
+		if (ques.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+				|| ques.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+			dto.setQuesAnswer(ques.getQuesAnswer());
+		}
+		appendAudioFlag(dto, examId, courseCode, groupCode);
+		return dto;
+	}
+
+	private List<QuesOptionDto> buildQuestionOptionDto(List<QuesOption> quesOptions) {
+		if (quesOptions == null) {
+			return null;
+		}
+		List<QuesOptionDto> optionDtos = new ArrayList<>();
+		for (QuesOption option : quesOptions) {
+			QuesOptionDto optionDto = new QuesOptionDto();
+			optionDto.setNumber(option.getNumber());
+			optionDto.setOptionBody(option.getOptionBody());
+			optionDtos.add(optionDto);
+		}
+		return optionDtos;
+	}
+
+	/**
+	 * 1.给QuestionDto添加音频播放次数 2.给试题音频中有a标签添加url
+	 *
+	 * @param questionDto
+	 */
+	private void appendAudioFlag(QuestionDto questionDto, String examId, String courseCode, String groupCode) {
+		if (questionDto.getHasAudio() != null && questionDto.getHasAudio() == true) {
+			// 1.判断questionDto是否含有音频,如果有添加音频播放次数
+			AudioTimeConfig audioTimeConfig = Model.of(audioTimeConfigRepo
+					.findOne(Example.of(new AudioTimeConfig(examId, courseCode, groupCode, questionDto.getId()))));
+			questionDto.setPlayTime(audioTimeConfig != null ? audioTimeConfig.getPlayTime() : null);
+			// 2.1 取到题干,给a标签添加url
+			String quesBody = questionDto.getQuesBody();
+			questionDto.setQuesBody(buildBody(quesBody, questionDto));
+			// 2.2取到选项,给a标签添加url
+			List<QuesOptionDto> quesOptionDtoList = questionDto.getQuesOptions();
+			if (quesOptionDtoList != null && quesOptionDtoList.size() > 0) {
+				for (QuesOptionDto quesOptionDto : quesOptionDtoList) {
+					quesOptionDto.setOptionBody(buildBody(quesOptionDto.getOptionBody(), questionDto));
+				}
+			}
+		} else {
+			questionDto.setPlayTime(null);
+		}
+	}
+
+	// 给题目和选项添加url
+	public String buildBody(String body, QuestionDto questionDto) {
+		String[] bodyStrings = body.split("></a>");
+		if (bodyStrings.length > 1) {
+			String resultBody = "";
+			for (int i = 0; i < bodyStrings.length; i++) {
+				String containAStr = bodyStrings[i];
+				if (containAStr.indexOf("<a") > -1) {
+					String questionAudioId = matchAudioName(containAStr, "a", "id");
+					QuestionAudio questionAudio = questionAudioService.findAudioById(questionAudioId);
+					if (questionAudio != null) {
 //                        String url = sysProperty.getDomain() + questionAudio.getFileUrl();
-                        //通用存储
-                        String url = FileStorageUtil.realPath(questionAudio.getFileUrl());
-                        if (questionDto.getPlayTime() != null) {
-                            containAStr += " question-audio url=\"" + url + "\" playTime=\"" + questionDto.getPlayTime() + "\"" + "></a>";
-                        } else {
-                            containAStr += " question-audio url=\"" + url + "\"" + "></a>";
-                        }
-                    }
-                }
-                resultBody += containAStr;
-            }
-            return resultBody;
-        } else {
-            return body;
-        }
-    }
-
-    private String matchAudioName(String source, String element, String attr) {
-        String reg = "<" + element + "[^<>]*?\\s" + attr + "=['\"]?(.*?)['\"]?(\\s.*?)";
-        Matcher m = Pattern.compile(reg).matcher(source);
-        if (m.find()) {
-            return m.group(1);
-        }
-        return "";
-    }
-
-    /**
-     * 重新对选择题option进行排序(多选、单选、套题下选择题)
-     */
-    private void reorderChoicequestionOption(PaperDetailUnit paperDetailUnit) {
-        String optionOrder = paperDetailUnit.getOptionOrder();
-        if (StringUtil.isNotBlank(optionOrder)) {
-            Question question = paperDetailUnit.getQuestion();
-            if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-                    || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-                question.setQuesOptions(reorderOptionCore(question.getQuesOptions(), optionOrder));
-            }
-            if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
-                List<Question> subQuestions = question.getSubQuestions();
-                int index = 0;
-                for (int k = 0; k < subQuestions.size(); k++) {
-                    Question subQuestion = subQuestions.get(k);
-                    if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-                            || subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-                        subQuestion.setQuesOptions(reorderOptionCore(subQuestion.getQuesOptions(), optionOrder.split(";")[index]));
-                        index++;
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * 对option排序
-     *
-     * @param quesOptions
-     * @param optionOrder
-     * @return
-     */
-    private List<QuesOption> reorderOptionCore(List<QuesOption> quesOptions, String optionOrder) {
-        List<QuesOption> newQuesOptions = new ArrayList<>();
-        if (StringUtil.isBlank(optionOrder) || quesOptions.isEmpty()) {
-            return null;
-        }
-        String[] optionOrderArr = optionOrder.split(",");
-        for (int j = 0; j < optionOrderArr.length; j++) {
-            for (int k = 0; k < quesOptions.size(); k++) {
-                if (optionOrderArr[j].equals(quesOptions.get(k).getNumber())) {
-                    newQuesOptions.add(quesOptions.get(k));
-                }
-            }
-        }
-        quesOptions = null;
-        return newQuesOptions;
-    }
-
-    @Override
-    public Page<ExtractConfig> findPageExtractConfig(int currentPage, int pageSize, Long examId, String courseCode, String orgId) {
-        if (examId == null) {
-            throw new StatusException("Q-", "examId is null");
-        }
-        Query query = new Query();
-        query.addCriteria(Criteria.where("orgId").is(orgId));
-        query.addCriteria(Criteria.where("examId").is(examId));
-        query.addCriteria(Criteria.where("course.enable").is("true"));
-        if (!StringUtils.isBlank(courseCode)) {
-            query.addCriteria(Criteria.where("course.code").is(courseCode));
-        }
-
-        long count = this.mongoTemplate.count(query, ExtractConfig.class);
-        query.limit(pageSize);
-        query.skip((currentPage - 1L) * pageSize);
-
-        List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
-        if (extractConfigList != null && extractConfigList.size() > 0) {
-            //调用考务rmi,获取考试信息
-            GetExamReq req = new GetExamReq();
-            req.setId(examId);
-            req.setRootOrgId(Long.valueOf(orgId));
-            GetExamResp resp = examCloudService.getExam(req);
-            ExamBean bean = resp.getExamBean();
-            for (ExtractConfig extractConfig : extractConfigList) {
-                extractConfig.setExamType(bean.getExamType());
-            }
-        }
-
-        return new PageImpl<>(extractConfigList, PageRequest.of(currentPage - 1, pageSize), count);
-    }
-
-    @Override
-    public Map<String, Object> extractPaper(String paperId) {
-        Map<String, Object> returnMap = new HashMap<>();
-        Paper paper = Model.of(paperRepo.findById(paperId));
-        if (paper == null) {
-            returnMap.put("errorMsg", "该试卷不存在");
-            return returnMap;
-        }
-        PaperDto paperDto = getPaperDtoByPaper(paper, paperId);
-        returnMap.put("paperDto", paperDto);
-        return returnMap;
-    }
-
-    @Override
-    public String getAnswerHtml(String paperId) {
-        //1.根据id查询试卷
-        Paper paper = Model.of(paperRepo.findById(paperId));
-        //2.定义html字符串
-        StringBuffer answerHtml = new StringBuffer("<p style=\"text-align:center;font-size:20px;font-weight:bold\">" + paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")" + "</p>");
-        //添加考试说明
-        if (paper.getExamRemark() == null) {
-            answerHtml.append("<p>考试说明:</p>");
-        } else {
-            answerHtml.append("<p>考试说明:</p>" + paper.getExamRemark());
-        }
-        //3.通过试卷获取考试端试卷结构
-        PaperDto paperDto = getPaperDtoByPaper(paper, paper.getId());
-        //4.获取试卷所有大题,并且循环
-        List<PaperDetailDto> paperDetailDtos = paperDto.getPaperDetails();
-        for (PaperDetailDto paperDetailDto : paperDetailDtos) {
-            String title = "<p>" + paperDetailDto.getCnNum() + "、" + paperDetailDto.getName() + "(共" + paperDetailDto.getScore() + "分)" + "</p>";
-            answerHtml.append(title);
-            //5.获取大题下面的小题,并循环
-            List<PaperDetailUnitDto> paperDetailUnitDtos = paperDetailDto.getPaperDetailUnits();
-            for (PaperDetailUnitDto paperDetailUnitDto : paperDetailUnitDtos) {
-                //定义题干
-                String body = paperDetailUnitDto.getQuesBody().replaceAll("<span>", "").replaceAll("</span>", "").replaceAll("###", "___");
-                ;
-                //定义选项
-                String option = "";
-                //定义答案
-                String answer = "";
-                //6.如果为单选和多选
-                if (paperDetailUnitDto.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION || paperDetailUnitDto.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-                    //获取选项
-                    List<QuesOptionDto> quesOptionDtos = paperDetailUnitDto.getQuesOptions();
-                    //添加题干
-                    body = startWithP(body, paperDetailUnitDto.getNumber(), false);
-                    answerHtml.append(body);
-                    //添加选项
-                    for (QuesOptionDto quesOptionDto : quesOptionDtos) {
-                        option = startWithP(quesOptionDto.getOptionBody(), Integer.parseInt(quesOptionDto.getNumber()), true);
-                        answerHtml.append(option);
-                    }
-                    answer = startWithP(paperDetailUnitDto.getAnswer(), -1, false);
-                    answerHtml.append(answer);
-                }
-                //7.如果为套题
-                else if (paperDetailUnitDto.getSubQuestions() != null && paperDetailUnitDto.getSubQuestions().size() > 0) {
-                    //添加主题干
-                    String bigBody = CommonUtils.relaceQuestionIdx(paperDetailUnitDto.getQuesBody(), 0);
-                    bigBody = bigBody.replaceAll("<span>", "").replaceAll("</span>", "").replaceAll("###", "___");
-                    bigBody = startWithP(bigBody, -2, false);
-                    answerHtml.append(bigBody);
-                    List<SubQuestionDto> subQuestionDtos = paperDetailUnitDto.getSubQuestions();
-                    for (SubQuestionDto subQuestionDto : subQuestionDtos) {
-                        body = subQuestionDto.getQuesBody().replaceAll("<span>", "").replaceAll("</span>", "").replaceAll("###", "___");
-                        //如果子题为单选或者多选
-                        if (subQuestionDto.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION || subQuestionDto.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-                            //获取选项
-                            List<QuesOptionDto> quesOptionDtos = subQuestionDto.getQuesOptions();
-                            //添加题干
-                            body = startWithP(body, subQuestionDto.getNumber(), false);
-                            answerHtml.append(body);
-                            //添加选项
-                            for (QuesOptionDto quesOptionDto : quesOptionDtos) {
-                                option = startWithP(quesOptionDto.getOptionBody(), Integer.parseInt(quesOptionDto.getNumber()), true);
-                                answerHtml.append(option);
-                            }
-                            answer = startWithP(subQuestionDto.getQuesAnswer(), -1, false);
-                            answerHtml.append(answer);
-                        } else {
-                            //添加题干
-                            body = startWithP(body, subQuestionDto.getNumber(), false);
-                            answerHtml.append(body);
-                            //添加答案
-                            answer = startWithP(subQuestionDto.getQuesAnswer(), -1, false);
-                            answerHtml.append(answer);
-                        }
-                    }
-                } else {
-                    //添加题干
-                    body = startWithP(body, paperDetailUnitDto.getNumber(), false);
-                    answerHtml.append(body);
-                    //添加答案
-                    answer = startWithP(paperDetailUnitDto.getAnswer(), -1, false);
-                    answerHtml.append(answer);
-                }
-            }
-        }
-        return answerHtml.toString();
-    }
-
-    public String startWithP(String s, Integer number, boolean option) {
-        if (s == null) {
-            s = "<p></p>";
-        }
-        if (number == -2) {
-            if (s.startsWith("<p>")) {
-                s = s.replaceAll("<p>", "<p style=\"margin-left:40px;\">").replaceAll("<p style=\"margin-left:40px;\">", "<p style=\"margin-left:20px;\">");
-            } else {
-                s = "<p style=\"margin-left:20px;color:red;\">" + s + "</p>";
-            }
-        } else if (number == -1) {
-            if (s.startsWith("<p>")) {
-                s = s.replaceAll("<p>", "<p style=\"margin-left:65px;\">").replaceFirst("<p style=\"margin-left:65px;\">", "<p style=\"margin-left:20px;color:red;\">" + "答案:").replace("<p style=\"margin-left:65px;\">", "<p style=\"margin-left:65px;color:red;\">");
-            } else {
-                s = "<p style=\"margin-left:20px;color:red;\">" + "答案:" + s + "</p>";
-            }
-        } else {
-            if (option) {
-                if (s.startsWith("<p>")) {
-                    s = s.replaceAll("<p>", "<p style=\"margin-left:40px;\">").replaceFirst("<p style=\"margin-left:40px;\">", "<p style=\"margin-left:20px;\">" + CommonUtils.getOptionNum(number - 1) + ".");
-                } else {
-                    s = "<p style=\"margin-left:20px;\">" + CommonUtils.getOptionNum(number - 1) + "." + s + "</p>";
-                }
-            } else {
-                if (s.startsWith("<p>")) {
-                    s = s.replaceAll("<p>", "<p style=\"margin-left:40px;\">").replaceFirst("<p style=\"margin-left:40px;\">", "<p style=\"margin-left:20px;\">" + number + ".");
-                } else {
-                    s = "<p style=\"margin-left:20px;\">" + number + "." + s + "</p>";
-                }
-            }
-        }
-        return s;
-    }
-
-
-    @Override
-    public List<CouresInfo> findCourseByExtractConfig(Long examId, String orgId) {
-        if (examId == null) {
-            throw new StatusException("Q-", "examId is null");
-        }
-        //从考务查询改考试下的所有开启课程
-        List<CouresInfo> courseInfoList = new ArrayList<>();
-        GetExamCourseListReq req = new GetExamCourseListReq();
-        req.setExamId(examId);
-        req.setCourseEnable(true);
-        Long start = 1l;
-        int count = 0;
-        while (true) {
-            req.setStart(start);
-            GetExamCourseListResp resp = examCloudService.getExamCourseList(req);
-            for (ExamCourseRelationBean bean : resp.getRelationList()) {
-                CouresInfo info = new CouresInfo(bean);
-                courseInfoList.add(info);
-            }
-            if (start.equals(resp.getNext())) {
-                break;
-            } else {
-                start = resp.getNext();
-            }
-            count++;
-            if (count > 1000) {
-                throw new StatusException("Q-", "考试下课程的数据量过大");
-            }
-        }
-        if (CollectionUtils.isEmpty(courseInfoList)) {
-            return null;
-        }
-        //查询已经改考试下已经制定的课程的调卷规则
-        Query query = new Query();
-        query.addCriteria(Criteria.where("orgId").is(orgId));
-        query.addCriteria(Criteria.where("examId").is(examId));
-        List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
-        if (extractConfigList != null && extractConfigList.size() > 0) {
-            for (ExtractConfig extractConfig : extractConfigList) {
-                CouresInfo info = new CouresInfo();
-                info.setCourseCode(extractConfig.getCourseCode());
-                if (courseInfoList.contains(info)) {
-                    courseInfoList.remove(info);
-                }
-            }
-            return courseInfoList;
-        }
-        return courseInfoList;
-    }
-    
-    @Override
-    public List<ExtractConfig> findExtractConfig(Long examId) {
-        if (examId == null) {
-            throw new StatusException("1001", "examId is null");
-        }
-        Query query = new Query();
-        query.addCriteria(Criteria.where("examId").is(examId));
-        query.addCriteria(Criteria.where("course.enable").is("true"));
-
-        List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
-
-        return extractConfigList;
-    }
+						// 通用存储
+						String url = FileStorageUtil.realPath(questionAudio.getFileUrl());
+						if (questionDto.getPlayTime() != null) {
+							containAStr += " question-audio url=\"" + url + "\" playTime=\"" + questionDto.getPlayTime()
+									+ "\"" + "></a>";
+						} else {
+							containAStr += " question-audio url=\"" + url + "\"" + "></a>";
+						}
+					}
+				}
+				resultBody += containAStr;
+			}
+			return resultBody;
+		} else {
+			return body;
+		}
+	}
+
+	private String matchAudioName(String source, String element, String attr) {
+		String reg = "<" + element + "[^<>]*?\\s" + attr + "=['\"]?(.*?)['\"]?(\\s.*?)";
+		Matcher m = Pattern.compile(reg).matcher(source);
+		if (m.find()) {
+			return m.group(1);
+		}
+		return "";
+	}
+
+	/**
+	 * 重新对选择题option进行排序(多选、单选、套题下选择题)
+	 */
+	private void reorderChoicequestionOption(PaperDetailUnit paperDetailUnit) {
+		String optionOrder = paperDetailUnit.getOptionOrder();
+		if (StringUtil.isNotBlank(optionOrder)) {
+			Question question = paperDetailUnit.getQuestion();
+			if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+					|| question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+				question.setQuesOptions(reorderOptionCore(question.getQuesOptions(), optionOrder));
+			}
+			if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+				List<Question> subQuestions = question.getSubQuestions();
+				int index = 0;
+				for (int k = 0; k < subQuestions.size(); k++) {
+					Question subQuestion = subQuestions.get(k);
+					if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+							|| subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+						subQuestion.setQuesOptions(
+								reorderOptionCore(subQuestion.getQuesOptions(), optionOrder.split(";")[index]));
+						index++;
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * 对option排序
+	 *
+	 * @param quesOptions
+	 * @param optionOrder
+	 * @return
+	 */
+	private List<QuesOption> reorderOptionCore(List<QuesOption> quesOptions, String optionOrder) {
+		List<QuesOption> newQuesOptions = new ArrayList<>();
+		if (StringUtil.isBlank(optionOrder) || quesOptions.isEmpty()) {
+			return null;
+		}
+		String[] optionOrderArr = optionOrder.split(",");
+		for (int j = 0; j < optionOrderArr.length; j++) {
+			for (int k = 0; k < quesOptions.size(); k++) {
+				if (optionOrderArr[j].equals(quesOptions.get(k).getNumber())) {
+					newQuesOptions.add(quesOptions.get(k));
+				}
+			}
+		}
+		quesOptions = null;
+		return newQuesOptions;
+	}
+
+	@Override
+	public Page<ExtractConfig> findPageExtractConfig(int currentPage, int pageSize, Long examId, String courseCode,
+			String orgId, UserDataRule ud) {
+		if (ud.assertEmptyQueryResult()) {
+			return Page.empty();
+		}
+		if (examId == null) {
+			throw new StatusException("Q-", "examId is null");
+		}
+		Query query = new Query();
+		query.addCriteria(Criteria.where("orgId").is(orgId));
+		if (ud.assertNeedQueryRefIds()) {
+			query.addCriteria(Criteria.where("course.id").in(ud.stringRefIds()));
+		}
+		query.addCriteria(Criteria.where("examId").is(examId));
+		query.addCriteria(Criteria.where("course.enable").is("true"));
+		if (!StringUtils.isBlank(courseCode)) {
+			query.addCriteria(Criteria.where("course.code").is(courseCode));
+		}
+
+		long count = this.mongoTemplate.count(query, ExtractConfig.class);
+		query.limit(pageSize);
+		query.skip((currentPage - 1L) * pageSize);
+
+		List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
+		if (extractConfigList != null && extractConfigList.size() > 0) {
+			// 调用考务rmi,获取考试信息
+			GetExamReq req = new GetExamReq();
+			req.setId(examId);
+			req.setRootOrgId(Long.valueOf(orgId));
+			GetExamResp resp = examCloudService.getExam(req);
+			ExamBean bean = resp.getExamBean();
+			for (ExtractConfig extractConfig : extractConfigList) {
+				extractConfig.setExamType(bean.getExamType());
+			}
+		}
+
+		return new PageImpl<>(extractConfigList, PageRequest.of(currentPage - 1, pageSize), count);
+	}
+
+	@Override
+	public Map<String, Object> extractPaper(String paperId) {
+		Map<String, Object> returnMap = new HashMap<>();
+		Paper paper = Model.of(paperRepo.findById(paperId));
+		if (paper == null) {
+			returnMap.put("errorMsg", "该试卷不存在");
+			return returnMap;
+		}
+		PaperDto paperDto = getPaperDtoByPaper(paper, paperId);
+		returnMap.put("paperDto", paperDto);
+		return returnMap;
+	}
+
+	@Override
+	public String getAnswerHtml(String paperId) {
+		// 1.根据id查询试卷
+		Paper paper = Model.of(paperRepo.findById(paperId));
+		// 2.定义html字符串
+		StringBuffer answerHtml = new StringBuffer("<p style=\"text-align:center;font-size:20px;font-weight:bold\">"
+				+ paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")" + "</p>");
+		// 添加考试说明
+		if (paper.getExamRemark() == null) {
+			answerHtml.append("<p>考试说明:</p>");
+		} else {
+			answerHtml.append("<p>考试说明:</p>" + paper.getExamRemark());
+		}
+		// 3.通过试卷获取考试端试卷结构
+		PaperDto paperDto = getPaperDtoByPaper(paper, paper.getId());
+		// 4.获取试卷所有大题,并且循环
+		List<PaperDetailDto> paperDetailDtos = paperDto.getPaperDetails();
+		for (PaperDetailDto paperDetailDto : paperDetailDtos) {
+			String title = "<p>" + paperDetailDto.getCnNum() + "、" + paperDetailDto.getName() + "(共"
+					+ paperDetailDto.getScore() + "分)" + "</p>";
+			answerHtml.append(title);
+			// 5.获取大题下面的小题,并循环
+			List<PaperDetailUnitDto> paperDetailUnitDtos = paperDetailDto.getPaperDetailUnits();
+			for (PaperDetailUnitDto paperDetailUnitDto : paperDetailUnitDtos) {
+				// 定义题干
+				String body = paperDetailUnitDto.getQuesBody().replaceAll("<span>", "").replaceAll("</span>", "")
+						.replaceAll("###", "___");
+				;
+				// 定义选项
+				String option = "";
+				// 定义答案
+				String answer = "";
+				// 6.如果为单选和多选
+				if (paperDetailUnitDto.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+						|| paperDetailUnitDto.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+					// 获取选项
+					List<QuesOptionDto> quesOptionDtos = paperDetailUnitDto.getQuesOptions();
+					// 添加题干
+					body = startWithP(body, paperDetailUnitDto.getNumber(), false);
+					answerHtml.append(body);
+					// 添加选项
+					for (QuesOptionDto quesOptionDto : quesOptionDtos) {
+						option = startWithP(quesOptionDto.getOptionBody(), Integer.parseInt(quesOptionDto.getNumber()),
+								true);
+						answerHtml.append(option);
+					}
+					answer = startWithP(paperDetailUnitDto.getAnswer(), -1, false);
+					answerHtml.append(answer);
+				}
+				// 7.如果为套题
+				else if (paperDetailUnitDto.getSubQuestions() != null
+						&& paperDetailUnitDto.getSubQuestions().size() > 0) {
+					// 添加主题干
+					String bigBody = CommonUtils.relaceQuestionIdx(paperDetailUnitDto.getQuesBody(), 0);
+					bigBody = bigBody.replaceAll("<span>", "").replaceAll("</span>", "").replaceAll("###", "___");
+					bigBody = startWithP(bigBody, -2, false);
+					answerHtml.append(bigBody);
+					List<SubQuestionDto> subQuestionDtos = paperDetailUnitDto.getSubQuestions();
+					for (SubQuestionDto subQuestionDto : subQuestionDtos) {
+						body = subQuestionDto.getQuesBody().replaceAll("<span>", "").replaceAll("</span>", "")
+								.replaceAll("###", "___");
+						// 如果子题为单选或者多选
+						if (subQuestionDto.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+								|| subQuestionDto.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+							// 获取选项
+							List<QuesOptionDto> quesOptionDtos = subQuestionDto.getQuesOptions();
+							// 添加题干
+							body = startWithP(body, subQuestionDto.getNumber(), false);
+							answerHtml.append(body);
+							// 添加选项
+							for (QuesOptionDto quesOptionDto : quesOptionDtos) {
+								option = startWithP(quesOptionDto.getOptionBody(),
+										Integer.parseInt(quesOptionDto.getNumber()), true);
+								answerHtml.append(option);
+							}
+							answer = startWithP(subQuestionDto.getQuesAnswer(), -1, false);
+							answerHtml.append(answer);
+						} else {
+							// 添加题干
+							body = startWithP(body, subQuestionDto.getNumber(), false);
+							answerHtml.append(body);
+							// 添加答案
+							answer = startWithP(subQuestionDto.getQuesAnswer(), -1, false);
+							answerHtml.append(answer);
+						}
+					}
+				} else {
+					// 添加题干
+					body = startWithP(body, paperDetailUnitDto.getNumber(), false);
+					answerHtml.append(body);
+					// 添加答案
+					answer = startWithP(paperDetailUnitDto.getAnswer(), -1, false);
+					answerHtml.append(answer);
+				}
+			}
+		}
+		return answerHtml.toString();
+	}
+
+	public String startWithP(String s, Integer number, boolean option) {
+		if (s == null) {
+			s = "<p></p>";
+		}
+		if (number == -2) {
+			if (s.startsWith("<p>")) {
+				s = s.replaceAll("<p>", "<p style=\"margin-left:40px;\">").replaceAll("<p style=\"margin-left:40px;\">",
+						"<p style=\"margin-left:20px;\">");
+			} else {
+				s = "<p style=\"margin-left:20px;color:red;\">" + s + "</p>";
+			}
+		} else if (number == -1) {
+			if (s.startsWith("<p>")) {
+				s = s.replaceAll("<p>", "<p style=\"margin-left:65px;\">")
+						.replaceFirst("<p style=\"margin-left:65px;\">",
+								"<p style=\"margin-left:20px;color:red;\">" + "答案:")
+						.replace("<p style=\"margin-left:65px;\">", "<p style=\"margin-left:65px;color:red;\">");
+			} else {
+				s = "<p style=\"margin-left:20px;color:red;\">" + "答案:" + s + "</p>";
+			}
+		} else {
+			if (option) {
+				if (s.startsWith("<p>")) {
+					s = s.replaceAll("<p>", "<p style=\"margin-left:40px;\">").replaceFirst(
+							"<p style=\"margin-left:40px;\">",
+							"<p style=\"margin-left:20px;\">" + CommonUtils.getOptionNum(number - 1) + ".");
+				} else {
+					s = "<p style=\"margin-left:20px;\">" + CommonUtils.getOptionNum(number - 1) + "." + s + "</p>";
+				}
+			} else {
+				if (s.startsWith("<p>")) {
+					s = s.replaceAll("<p>", "<p style=\"margin-left:40px;\">").replaceFirst(
+							"<p style=\"margin-left:40px;\">", "<p style=\"margin-left:20px;\">" + number + ".");
+				} else {
+					s = "<p style=\"margin-left:20px;\">" + number + "." + s + "</p>";
+				}
+			}
+		}
+		return s;
+	}
+
+	@Override
+	public List<CouresInfo> findCourseByExtractConfig(Long examId, String orgId, UserDataRule ud) {
+		if (ud.assertEmptyQueryResult()) {
+			return new ArrayList<>();
+		}
+		if (examId == null) {
+			throw new StatusException("500", "examId is null");
+		}
+		// 从考务查询改考试下的所有开启课程
+		List<CouresInfo> courseInfoList = new ArrayList<>();
+		GetExamCourseListReq req = new GetExamCourseListReq();
+		req.setExamId(examId);
+		req.setCourseEnable(true);
+		Long start = 1l;
+		int count = 0;
+		while (true) {
+			req.setStart(start);
+			GetExamCourseListResp resp = examCloudService.getExamCourseList(req);
+			for (ExamCourseRelationBean bean : resp.getRelationList()) {
+				CouresInfo info = new CouresInfo(bean);
+				courseInfoList.add(info);
+			}
+			if (start.equals(resp.getNext())) {
+				break;
+			} else {
+				start = resp.getNext();
+			}
+			count++;
+			if (count > 1000) {
+				throw new StatusException("Q-", "考试下课程的数据量过大");
+			}
+		}
+		if (CollectionUtils.isEmpty(courseInfoList)) {
+			return null;
+		}
+		// 查询已经改考试下已经制定的课程的调卷规则
+		Query query = new Query();
+		query.addCriteria(Criteria.where("orgId").is(orgId));
+		query.addCriteria(Criteria.where("examId").is(examId));
+		List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
+		if (extractConfigList != null && extractConfigList.size() > 0) {
+			for (ExtractConfig extractConfig : extractConfigList) {
+				CouresInfo info = new CouresInfo();
+				info.setCourseCode(extractConfig.getCourseCode());
+				if (courseInfoList.contains(info)) {
+					courseInfoList.remove(info);
+				}
+			}
+		}
+		if (ud.assertNeedQueryRefIds() && courseInfoList.size() > 0) {
+			List<CouresInfo> ret = new ArrayList<>();
+			for (CouresInfo info : courseInfoList) {
+				if (ud.getRefIds().contains(info.getCourseId())) {
+					ret.add(info);
+				}
+			}
+			return ret;
+		}
+		return courseInfoList;
+	}
+
+	@Override
+	public List<ExtractConfig> findExtractConfig(Long examId) {
+		if (examId == null) {
+			throw new StatusException("1001", "examId is null");
+		}
+		Query query = new Query();
+		query.addCriteria(Criteria.where("examId").is(examId));
+		query.addCriteria(Criteria.where("course.enable").is("true"));
+
+		List<ExtractConfig> extractConfigList = this.mongoTemplate.find(query, ExtractConfig.class);
+
+		return extractConfigList;
+	}
 
 }

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

@@ -13,12 +13,12 @@ import java.util.stream.Collectors;
 
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
-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.IdUtils;
 import cn.com.qmth.examcloud.core.questions.base.Model;
@@ -50,7 +50,7 @@ import cn.com.qmth.examcloud.core.questions.service.util.PaperUtil;
 @Service
 public class GenPaperService {
 
-    protected ExamCloudLog log = ExamCloudLogFactory.getLog(this.getClass());
+    private static final Logger LOG = LoggerFactory.getLogger(GenPaperService.class);
 
     @Autowired
     PaperRepo paperRepo;
@@ -80,7 +80,7 @@ public class GenPaperService {
      */
     @SuppressWarnings("unchecked")
     public Map<String, Object> genPaper(GenPaperDto genPaperDto) {
-        log.debug("开始组卷.....");
+        LOG.debug("开始组卷.....");
         String msg = "";
         Map<String, Object> paperMsgMap = new HashMap<>();
         LinkedList<Question> questions = new LinkedList<>();
@@ -108,7 +108,7 @@ public class GenPaperService {
         Set<PaperDetailUnit> spareUnits = new HashSet<>();
         spareUnits.addAll(unitList);
         long total = (System.currentTimeMillis() - start) / 1000;
-        log.debug("total:" + total);
+        LOG.debug("total:" + total);
     	
     	/*for (PaperDetailUnit unit : spareUnits) {
             Question question = unit.getQuestion();
@@ -129,11 +129,11 @@ public class GenPaperService {
         for (PaperDetailUnit unit : spareUnits) {
             oIds.add(unit.getQuestion().getId());
         }
-        log.debug("total2.....--------实际原题数量:" + oIds.size());
+        LOG.debug("total2.....--------实际原题数量:" + oIds.size());
         long start2 = System.currentTimeMillis();
         List<PaperDetailUnit> oDetailUnits = paperDetailUnitService.findByQuestionsAndPaperType(oIds, PaperType.IMPORT);
         long total2 = (System.currentTimeMillis() - start2) / 1000;
-        log.debug("total2....." + total2 + "--------查询原题数量:" + oDetailUnits.size());
+        LOG.debug("total2....." + total2 + "--------查询原题数量:" + oDetailUnits.size());
         long start4 = System.currentTimeMillis();
         for (PaperDetailUnit oldUnit : oDetailUnits) {
             if (oldUnit.getPaper().getPaperType() == PaperType.IMPORT) {
@@ -150,7 +150,7 @@ public class GenPaperService {
             }
         }
         long total4 = (System.currentTimeMillis() - start4) / 1000;
-        log.debug("循环耗时....." + total4 + "--------最终原题数量:" + questions.size());
+        LOG.debug("循环耗时....." + total4 + "--------最终原题数量:" + questions.size());
         Collections.shuffle(questions);
         int maxLoopCount = questions.size() * 2;
         int selectCount = 0;
@@ -204,7 +204,7 @@ public class GenPaperService {
             long start3 = System.currentTimeMillis();
             paper = this.persistentPaper(PaperStructType.EXACT, paperDetailunits, paperDetails, paper);
             long total3 = (System.currentTimeMillis() - start3) / 1000;
-            log.debug("total3....." + total3);
+            LOG.debug("total3....." + total3);
             msg = "success";
             paperMsgMap.put("paper", paper);
             paperMsgMap.put("msg", msg);
@@ -944,7 +944,7 @@ public class GenPaperService {
                                  List<PaperDetailUnit> selectedUnits,
                                  List<PaperDetail> details,
                                  Paper paper) {
-        log.debug("3.....");
+        LOG.debug("3.....");
         paper = paperRepo.save(paper);
         for (PaperDetail pd : details) {
             pd.setPaper(paper);
@@ -1023,7 +1023,7 @@ public class GenPaperService {
      */
     @SuppressWarnings("unchecked")
     public Map<String, Object> genBluePaper(GenPaperDto genPaperDto) {
-        log.debug("开始蓝图组卷.....");
+        LOG.debug("开始蓝图组卷.....");
         String msg = "";
         Map<String, Object> paperMsgMap = new HashMap<>();
         LinkedList<Question> questions = new LinkedList<>();
@@ -1051,7 +1051,7 @@ public class GenPaperService {
         Set<PaperDetailUnit> spareUnits = new HashSet<>();
         spareUnits.addAll(unitList);
         long total = (System.currentTimeMillis() - start) / 1000;
-        log.debug("total:" + total);
+        LOG.debug("total:" + total);
     	
     	/*for (PaperDetailUnit unit : spareUnits) {
             Question question = unit.getQuestion();
@@ -1072,11 +1072,11 @@ public class GenPaperService {
         for (PaperDetailUnit unit : spareUnits) {
             oIds.add(unit.getQuestion().getId());
         }
-        log.debug("total2.....--------实际原题数量:" + oIds.size());
+        LOG.debug("total2.....--------实际原题数量:" + oIds.size());
         long start2 = System.currentTimeMillis();
         List<PaperDetailUnit> oDetailUnits = paperDetailUnitService.findByQuestionsAndPaperType(oIds, PaperType.IMPORT);
         long total2 = (System.currentTimeMillis() - start2) / 1000;
-        log.debug("total2....." + total2 + "--------查询原题数量:" + oDetailUnits.size());
+        LOG.debug("total2....." + total2 + "--------查询原题数量:" + oDetailUnits.size());
         long start4 = System.currentTimeMillis();
         for (PaperDetailUnit oldUnit : oDetailUnits) {
             if (oldUnit.getPaper().getPaperType() == PaperType.IMPORT) {
@@ -1086,7 +1086,7 @@ public class GenPaperService {
             }
         }
         long total4 = (System.currentTimeMillis() - start4) / 1000;
-        log.debug("循环耗时....." + total4 + "--------最终原题数量:" + questions.size());
+        LOG.debug("循环耗时....." + total4 + "--------最终原题数量:" + questions.size());
         /*for (Paper oldPaper : papers) {
             List<PaperDetailUnit> unitList = unitRepo.findByPaperOrderByNumber(oldPaper);
             for (PaperDetailUnit unit : unitList) {
@@ -1224,7 +1224,7 @@ public class GenPaperService {
     }
 
 	public Map<String, Object> genPaperManual(GenPaperDto genPaperDto) {
-		log.debug("开始组卷.....");
+		LOG.debug("开始组卷.....");
         String msg = "";
         Map<String, Object> paperMsgMap = new HashMap<>();
         //查询试卷结构

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

@@ -114,6 +114,9 @@ public class PaperDetailServiceImpl implements PaperDetailService {
      */
     public void deletePaperDetail(String paperId,String id,User user) {
     	Paper paper = Model.of(paperRepo.findById(paperId));
+    	if(paper.getInUse()!=null&&paper.getInUse()==1) {
+			throw new StatusException("500", "试卷已调用");
+		}
     	PaperDetail oldpaperDetail = Model.of(paperDetailRepo.findById(id));
         paperDetailRepo.deleteById(id);
         StringBuilder paperInfo=new StringBuilder();
@@ -165,6 +168,9 @@ public class PaperDetailServiceImpl implements PaperDetailService {
 	@Override
 	public void movePaperDetail(String paperId, String detailId, String vc, User user) {
 		Paper paper = Model.of(paperRepo.findById(paperId));
+		if(paper.getInUse()!=null&&paper.getInUse()==1) {
+			throw new StatusException("500", "试卷已调用");
+		}
 		List<PaperDetail> pds=getPaperDetailsByPaper(paper);
 		if(pds==null||pds.size()<=1) {
 			throw new StatusException("500", "大题数量未达到两题,无法移动");

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

@@ -135,6 +135,10 @@ public class PaperDetailUnitServiceImpl implements PaperDetailUnitService {
     public PaperDetailUnit savePaperDetailUnit(PaperDetailUnitExp updateUnit, User user) {
     	StringBuilder sb=new StringBuilder();
         PaperDetailUnit baseUnit = Model.of(paperDetailUnitRepo.findById(updateUnit.getId()));
+        Paper cpaper=baseUnit.getPaper();
+        if(cpaper.getInUse()!=null&&cpaper.getInUse()==1) {
+        	checkUpdate(updateUnit, baseUnit);
+        }
         Question baseQuestion = baseUnit.getQuestion();
         Question updateQuestion = updateUnit.getQuestion();
         checkFillBlankQuestion(updateQuestion);
@@ -252,6 +256,46 @@ public class PaperDetailUnitServiceImpl implements PaperDetailUnitService {
         return baseUnit;
     }
     
+    private void checkUpdate(PaperDetailUnitExp updateUnit,PaperDetailUnit baseUnit) {
+    	Question baseQuestion = baseUnit.getQuestion();
+        Question updateQuestion = updateUnit.getQuestion();
+    	if (baseUnit.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+            if (updateQuestion.getId().equals(baseQuestion.getId())) {
+            	return;
+            } else {
+                int size = baseQuestion.getSubQuestions().size();
+                // 判断更新的对象是哪个子题
+                for (int index = 1; index <= size; index++) {
+
+                    Question sub = baseQuestion.getSubQuestions().get(index - 1);
+                    if (updateQuestion.getId().equals(sub.getId())) {
+                        checkUpdate(updateQuestion, sub);
+
+                    }
+                }
+            }
+        } else {
+        	checkUpdate(updateQuestion, baseQuestion);
+        }
+    }
+    private void checkUpdate(Question update,Question base) {
+    	if(update.getScore().doubleValue()!=base.getScore().doubleValue()) {
+    		throw new StatusException("500", "试卷已调用,不能修改分数");
+    	}
+    	if(QuesStructType.SINGLE_ANSWER_QUESTION.equals(base.getQuestionType())
+    			||QuesStructType.MULTIPLE_ANSWER_QUESTION.equals(base.getQuestionType())){
+	    	if(update.getQuesOptions().size()!=base.getQuesOptions().size()) {
+	    		throw new StatusException("500", "试卷已调用,不能修改选项数量");
+	    	}
+    	}
+    	if(QuesStructType.SINGLE_ANSWER_QUESTION.equals(base.getQuestionType())
+    			||QuesStructType.MULTIPLE_ANSWER_QUESTION.equals(base.getQuestionType())
+    			||QuesStructType.BOOL_ANSWER_QUESTION.equals(base.getQuestionType())){
+	    	if(!update.getQuesAnswer().equals(base.getQuesAnswer())) {
+	    		throw new StatusException("500", "试卷已调用,不能修改客观题答案");
+	    	}
+    	}
+    }
     
     private String getQuestionChangeInfo(Question old,Question now) {
     	StringBuilder sb=new StringBuilder();
@@ -297,6 +341,9 @@ public class PaperDetailUnitServiceImpl implements PaperDetailUnitService {
     	String changInfo=null;
         PaperDetailUnit detailUnit = Model.of(paperDetailUnitRepo.findById(id));
         Paper paper = detailUnit.getPaper();
+        if(paper.getInUse()!=null&&paper.getInUse()==1) {
+			throw new StatusException("500", "试卷已调用");
+		}
         paperDetailUnitRepo.deleteById(id);
 
         if (detailUnit.getQuestion() != null) {
@@ -372,8 +419,8 @@ public class PaperDetailUnitServiceImpl implements PaperDetailUnitService {
     public List<PaperDetailUnit> findByPaperAndSort(Paper paper) {
         Query query = new Query();
         query.addCriteria(Criteria.where("paper").is(paper));
-        query.with(new Sort(new Order(Direction.ASC, "number")));
-        query.with(new Sort(new Order(Direction.ASC, "createTime")));
+        query.with(Sort.by(new Order(Direction.ASC, "number")));
+        query.with(Sort.by(new Order(Direction.ASC, "createTime")));
         return this.mongoTemplate.find(query, PaperDetailUnit.class);
     }
 

+ 4 - 4
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PaperProviderServiceImpl.java

@@ -1,8 +1,6 @@
 package cn.com.qmth.examcloud.core.questions.service.impl;
 
 import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
-import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
 import cn.com.qmth.examcloud.core.questions.api.bean.PaperK12Bean;
 import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
 import cn.com.qmth.examcloud.core.questions.base.Model;
@@ -14,6 +12,8 @@ import cn.com.qmth.examcloud.core.questions.service.PaperProviderService;
 import cn.com.qmth.examcloud.question.commons.core.paper.DefaultPaper;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -33,7 +33,7 @@ import java.util.Set;
 @Service("paperProviderServiceImpl")
 public class PaperProviderServiceImpl implements PaperProviderService {
 
-    protected ExamCloudLog log = ExamCloudLogFactory.getLog(this.getClass());
+    private static final Logger LOG = LoggerFactory.getLogger(PaperProviderServiceImpl.class);
 
     @Autowired
     private QuesRepo quesRepo;
@@ -67,7 +67,7 @@ public class PaperProviderServiceImpl implements PaperProviderService {
         //查询所有试题集合
         List<Question> questions = quesRepo.findByIdIn(questionIds);
         if (CollectionUtils.isEmpty(questions)) {
-            log.error("根据试题id的集合没有查询到试题结合");
+            LOG.error("根据试题id的集合没有查询到试题结合");
             throw new StatusException("Q-014041", "根据试题id的集合没有查询到试题结合");
         }
         //构建试卷对象

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 448 - 292
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PaperServiceImpl.java


+ 8 - 5
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PropertyServiceImpl.java

@@ -78,6 +78,9 @@ public class PropertyServiceImpl implements PropertyService {
         if (StringUtils.isEmpty(dto.getName())) {
             throw new StatusException(Constants.SYS_CODE_500, "属性名不能为空!");
         }
+        if (StringUtils.isEmpty(dto.getCode())) {
+            throw new StatusException(Constants.SYS_CODE_500, "编码不能为空!");
+        }
 
         if (StringUtils.isBlank(dto.getId())) {
             dto.setId(null);//剔除空字符串ID(导致异常数据)
@@ -86,9 +89,9 @@ public class PropertyServiceImpl implements PropertyService {
         Property property = PropertyConvert.of(dto);
         if (property.getId() == null) {
             //新增
-            Property existProperty = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndName(property.getOrgId(), property.getCoursePropertyId(), property.getParentId(), property.getName());
+            Property existProperty = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(property.getOrgId(), property.getCoursePropertyId(), property.getParentId(), property.getCode());
             if (existProperty != null) {
-                throw new StatusException(Constants.SYS_CODE_500, "属性名已经存在!");
+                throw new StatusException(Constants.SYS_CODE_500, "编码已经存在!");
             }
 
             Integer number = 0;
@@ -105,11 +108,11 @@ public class PropertyServiceImpl implements PropertyService {
                 throw new StatusException(Constants.SYS_CODE_500, "当前属性已不存在!");
             }
 
-            if (!oldProperty.getName().equals(property.getName())) {
+            if (!oldProperty.getCode().equals(property.getCode())) {
                 //检查是否重名
-                Property existProperty = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndName(property.getOrgId(), property.getCoursePropertyId(), property.getParentId(), property.getName());
+                Property existProperty = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(property.getOrgId(), property.getCoursePropertyId(), property.getParentId(), property.getCode());
                 if (existProperty != null) {
-                    throw new StatusException(Constants.SYS_CODE_500, "属性名已经存在!");
+                    throw new StatusException(Constants.SYS_CODE_500, "编码已经存在!");
                 }
             }
         }

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

@@ -25,6 +25,7 @@ import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.stereotype.Service;
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
 import cn.com.qmth.examcloud.core.questions.base.Model;
@@ -222,15 +223,22 @@ public class QuesServiceImpl implements QuesService {
      * @param searchCondition
      * @param curPage
      * @param pageSize
+     * @param refIds
+     * @param userId
      * @return
      */
-    public Page<Question> findAll(QuestionSearchCondition searchCondition, int curPage, int pageSize) {
-        formatQuesCondition(searchCondition);
+    public Page<Question> findAll(QuestionSearchCondition searchCondition, int curPage, int pageSize,UserDataRule ud) {
+    	if (ud.assertEmptyQueryResult()) {
+			return Page.empty();
+		}
+    	formatQuesCondition(searchCondition);
 
         Query query = new Query();
         query.addCriteria(Criteria.where("orgId").is(searchCondition.getOrgId()));
         query.addCriteria(Criteria.where("course.enable").is("true"));
-
+        if (ud.assertNeedQueryRefIds()) {
+            query.addCriteria(Criteria.where("course.id").in(ud.stringRefIds()));
+        }
         if (StringUtils.isNotBlank(searchCondition.getCourseNo())) {
             query.addCriteria(Criteria.where("course.code").is(searchCondition.getCourseNo()));
         }
@@ -318,9 +326,16 @@ public class QuesServiceImpl implements QuesService {
      * @return
      */
     public Page<Question> findByIdExclude(Set<String> idSet, String courseNo, QuesStructType quesType, int curPage,
-            int pageSize, Long orgId, String quesBody) {
-        Query query = new Query();
+            int pageSize, Long orgId, String quesBody,UserDataRule ud) {
+    	if (ud.assertEmptyQueryResult()) {
+    		return Page.empty();
+    	}
+    	
+    	Query query = new Query();
         query.addCriteria(Criteria.where("orgId").is(orgId + ""));
+        if(ud.assertNeedQueryRefIds()) {
+        	query.addCriteria(Criteria.where("course.id").in(ud.stringRefIds()));
+        }
         query.addCriteria(Criteria.where("course.enable").is("true"));
         if (quesType != null) {
             query.addCriteria(Criteria.where("questionType").is(quesType));
@@ -333,11 +348,11 @@ public class QuesServiceImpl implements QuesService {
             query.addCriteria(Criteria.where("quesBody").regex(pattern));
         }
         long total = this.mongoTemplate.count(query, Question.class);
-        query.with(new Sort(new Order(Direction.DESC, "id")));
+        query.with(Sort.by(new Order(Direction.DESC, "id")));
         query.limit(pageSize);
         query.skip((curPage - 1L) * pageSize);
         List<Question> questionList = this.mongoTemplate.find(query, Question.class);
-        Page<Question> questionPageList = new PageImpl<Question>(questionList, new PageRequest(curPage - 1, pageSize),
+        Page<Question> questionPageList = new PageImpl<Question>(questionList, PageRequest.of(curPage - 1, pageSize),
                 total);
         for (Question question : questionPageList) {
             formatQues(question);
@@ -777,6 +792,7 @@ public class QuesServiceImpl implements QuesService {
     public QuestionAnswerBean findQuestionAnswer(String questionId) {
         Optional<Question> optional = quesRepo.findById(questionId);
         if (!optional.isPresent()) {
+            log.error("question is not exist, id = {}", questionId);
             throw new StatusException("500", "当前试题已不存在!");
         }
 

+ 1 - 1
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/QuestionProviderServiceImpl.java

@@ -259,7 +259,7 @@ public class QuestionProviderServiceImpl implements QuestionProviderService {
         query.skip((curPage - 1L) * pageSize);
         List<Question> questionList = this.mongoTemplate.find(query, Question.class);
         List<DefaultQuestion> defaultQuestions = buildDefaultQuestions(questionList);
-        Page<DefaultQuestion> questions = new PageImpl<DefaultQuestion>(defaultQuestions, new PageRequest(curPage - 1, pageSize), count);
+        Page<DefaultQuestion> questions = new PageImpl<DefaultQuestion>(defaultQuestions, PageRequest.of(curPage - 1, pageSize), count);
         return questions;
     }
 

+ 4 - 2
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/temp/CqdxService.java

@@ -13,6 +13,7 @@ import java.util.regex.Pattern;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 
+import cn.com.qmth.examcloud.web.config.SystemProperties;
 import org.apache.commons.collections4.map.HashedMap;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
@@ -45,7 +46,6 @@ 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.service.QuesTypeNameService;
 import cn.com.qmth.examcloud.core.questions.service.impl.CourseService;
-import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
 
 @Component
 public class CqdxService {
@@ -66,9 +66,11 @@ public class CqdxService {
 //	private QuesPkgPathRepo quesPkgPathRepo;
 	@Autowired
 	private QuesTypeNameService quesTypeNameService;
+	@Autowired
+	private SystemProperties systemProperties;
 
 	public void bulidPaper(MultipartFile dataFile, User user, Long rootOrgId, String paperNameSuffix, String impType) {
-		String tempDir = PropertyHolder.getString("examcloud.web.sys.tempDataDir");
+		String tempDir = systemProperties.getTempDataDir();
 		String dir = tempDir + File.separator + UUID.randomUUID() + File.separator;
 		try {
 			File dfile = new File(dir);

+ 66 - 61
examcloud-core-questions-starter/pom.xml

@@ -1,68 +1,73 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-	<parent>
-		<groupId>cn.com.qmth.examcloud.core.questions</groupId>
-		<artifactId>examcloud-core-questions</artifactId>
-		<version>v4.0.2-RELEASE</version>
-	</parent>
-	<artifactId>examcloud-core-questions-starter</artifactId>
-	<packaging>jar</packaging>
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>examcloud-core-questions-starter</artifactId>
+    <packaging>jar</packaging>
 
+    <parent>
+        <groupId>cn.com.qmth.examcloud</groupId>
+        <artifactId>examcloud-core-questions</artifactId>
+        <version>${revision}</version>
+    </parent>
 
-	<dependencies>
-		<dependency>
-			<groupId>cn.com.qmth.examcloud.core.questions</groupId>
-			<artifactId>examcloud-core-questions-api-provider</artifactId>
-			<version>${examcloud.version}</version>
-		</dependency>
-	</dependencies>
+    <dependencies>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud</groupId>
+            <artifactId>examcloud-core-questions-api-provider</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
 
-	<build>
-		<plugins>
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-jar-plugin</artifactId>
-				<configuration>
-					<archive>
-						<manifest>
-							<mainClass>cn.com.qmth.examcloud.core.questions.starter.CoreQuestionApp</mainClass>
-							<addClasspath>true</addClasspath>
-							<classpathPrefix>./</classpathPrefix>
-						</manifest>
-						<manifestEntries>
-							<Class-Path>../config/</Class-Path>
-						</manifestEntries>
-					</archive>
-					<excludes>
-						<exclude>templates/*</exclude>
-						<exclude>*.properties</exclude>
-						<exclude>*.xml </exclude>
-						<exclude>classpath.location</exclude>
-					</excludes>
-				</configuration>
-			</plugin>
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-assembly-plugin</artifactId>
-				<configuration>
-					<finalName>examcloud-core-questions</finalName>
-					<descriptors>
-						<descriptor>assembly.xml</descriptor>
-					</descriptors>
-				</configuration>
-				<executions>
-					<execution>
-						<id>make-assembly</id>
-						<phase>install</phase>
-						<goals>
-							<goal>assembly</goal>
-						</goals>
-					</execution>
-				</executions>
-			</plugin>
-		</plugins>
-	</build>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <mainClass>cn.com.qmth.examcloud.core.questions.starter.CoreQuestionApp</mainClass>
+                            <addClasspath>true</addClasspath>
+                            <classpathPrefix>./</classpathPrefix>
+                        </manifest>
+                        <manifestEntries>
+                            <Class-Path>../config/</Class-Path>
+                        </manifestEntries>
+                    </archive>
+                    <excludes>
+                        <exclude>templates/*</exclude>
+                        <exclude>*.properties</exclude>
+                        <exclude>*.xml</exclude>
+                        <exclude>classpath.location</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <finalName>examcloud-core-questions</finalName>
+                    <descriptors>
+                        <descriptor>assembly.xml</descriptor>
+                    </descriptors>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <phase>install</phase>
+                        <goals>
+                            <goal>assembly</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 
 </project>

+ 1 - 1
examcloud-core-questions-starter/shell/start.args

@@ -1 +1 @@
---spring.profiles.active=dev --examcloud.startup.configCenterHost=192.168.10.39
+--spring.profiles.active=dev --sys.config.center.address=192.168.10.39:8888

+ 8 - 24
examcloud-core-questions-starter/shell/start.sh

@@ -1,35 +1,19 @@
 #!/bin/bash
 
-FILE_PATH=$(cd `dirname $0`; pwd)
+PROJECT_JAR="examcloud-core-questions-starter-v4.1.0-SNAPSHOT.jar"
 
-APP_MAIN_JAR="examcloud-core-questions-starter-v4.0.2-RELEASE.jar"
+PROJECT_JVM_ARGS=`cat start.vmoptions`
 
-JAVA_OPTS=`cat $FILE_PATH/start.vmoptions`
-APP_ARGS=`cat $FILE_PATH/start.args`
+PROJECT_ARGS=`cat start.args`
 
-PID_LIST=`ps -ef|grep $APP_MAIN_JAR|grep java|awk '{print $2}'`
+PROJECT_ARGS=$PROJECT_ARGS" --sys.config.center.secretKey="$1
 
+PID_LIST=`ps -ef | grep $PROJECT_JAR | grep java | awk '{print $2}'`
 if [ ! -z "$PID_LIST" ]; then
-    echo "[ERROR] : APP is already running!"
-#    exit -1
-fi
-
-if [ "$1" ];then
-    echo "startupCode:"$1;
-else
-    echo "[ERROR] : no arguments"
+    echo "$PROJECT_JAR is already running..."
     exit -1
 fi
 
-APP_ARGS=$APP_ARGS" --examcloud.startup.startupCode="$1
-
-echo "java options:"
-echo "$JAVA_OPTS"
-echo "args:"
-echo "$APP_ARGS"
-    
-nohup java $JAVA_OPTS -jar $FILE_PATH/lib/$APP_MAIN_JAR $APP_ARGS >/dev/null 2>&1 &
-
-echo "starting......"
+nohup java $PROJECT_JVM_ARGS -jar lib/$PROJECT_JAR $PROJECT_ARGS >/dev/null 2>&1 &
 
-exit 0
+echo "$PROJECT_JAR start..."

+ 4 - 14
examcloud-core-questions-starter/shell/stop.sh

@@ -1,18 +1,8 @@
 #!/bin/bash
 
-FILE_PATH=$(cd `dirname $0`; pwd)
+PROJECT_JAR="examcloud-core-questions-starter-v4.1.0-SNAPSHOT.jar"
 
-APP_MAIN_JAR="examcloud-core-questions-starter-v4.0.2-RELEASE.jar"
+ps -ef | grep $PROJECT_JAR | grep java | awk '{printf("kill -15 %s\n",$2)}' | sh
+BUILD_ID=DONTKILLME
 
-PID_LIST=`ps -ef|grep $APP_MAIN_JAR|grep java|awk '{print $2}'`
-
-if [ ! -z "$PID_LIST" ]; then
-    echo "Runnable jar is $APP_MAIN_JAR."
-    for PID in $PID_LIST 
-    do
-        kill -9 $PID
-    done
-    echo "stopped !"
-fi
-
-exit 0
+echo "$PROJECT_JAR stop..."

+ 12 - 21
examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/CoreQuestionApp.java

@@ -1,6 +1,7 @@
 package cn.com.qmth.examcloud.core.questions.starter;
 
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
+import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.domain.EntityScan;
 import org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration;
@@ -17,42 +18,32 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 import org.springframework.web.multipart.MultipartResolver;
 import org.springframework.web.multipart.commons.CommonsMultipartResolver;
 
-import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
-import cn.com.qmth.examcloud.web.bootstrap.AppBootstrap;
-import cn.com.qmth.examcloud.web.support.SpringContextHolder;
-
-@SpringBootApplication
 @Configuration
 @EnableJpaAuditing
 @EnableMongoAuditing
 @EnableTransactionManagement
 @EnableEurekaClient
 @EnableDiscoveryClient
-@ComponentScan(basePackages = { "cn.com.qmth" })
-@EntityScan(basePackages = { "cn.com.qmth" })
-@EnableJpaRepositories(basePackages = { "cn.com.qmth" })
-@EnableMongoRepositories(basePackages = { "cn.com.qmth" })
-@EnableAutoConfiguration(exclude = { MultipartAutoConfiguration.class })
+@ComponentScan(basePackages = {"cn.com.qmth"})
+@EntityScan(basePackages = {"cn.com.qmth"})
+@EnableJpaRepositories(basePackages = {"cn.com.qmth"})
+@EnableMongoRepositories(basePackages = {"cn.com.qmth"})
+@SpringBootApplication(exclude = {MultipartAutoConfiguration.class})
 public class CoreQuestionApp {
 
     static {
-        String runtimeLevel = System.getProperty("log.commonLevel");
-        if (null == runtimeLevel) {
+        if (System.getProperty("log.commonLevel") == null) {
             System.setProperty("log.commonLevel", "INFO");
         }
         System.setProperty("hibernate.dialect.storage_engine", "innodb");
     }
 
     public static void main(String[] args) {
-        AppBootstrap.run(CoreQuestionApp.class, args);
-        FileStorageUtil.initYunSite();
-		FileStorageUtil.initYunClient();
-        test();
-    }
+        // AppBootstrap.run(CoreQuestionApp.class, args);
+        SpringApplication.run(CoreQuestionApp.class, args);
 
-    private static void test() {
-        Tester tester = SpringContextHolder.getBean(Tester.class);
-        tester.test();
+        FileStorageUtil.initYunSite();
+        FileStorageUtil.initYunClient();
     }
 
     @Bean(name = "multipartResolver")

+ 5 - 6
examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/config/ExamCloudResourceManager.java

@@ -10,8 +10,6 @@ package cn.com.qmth.examcloud.core.questions.starter.config;
 import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
 import cn.com.qmth.examcloud.api.commons.security.bean.*;
 import cn.com.qmth.examcloud.api.commons.security.enums.RoleMeta;
-import cn.com.qmth.examcloud.commons.util.PathUtil;
-import cn.com.qmth.examcloud.commons.util.PropertiesUtil;
 import cn.com.qmth.examcloud.commons.util.RegExpUtil;
 import cn.com.qmth.examcloud.core.basic.api.UserDataRuleCloudService;
 import cn.com.qmth.examcloud.core.basic.api.request.QueryUserDataRuleReq;
@@ -20,6 +18,7 @@ import cn.com.qmth.examcloud.support.cache.CacheHelper;
 import cn.com.qmth.examcloud.support.cache.bean.AppCacheBean;
 import cn.com.qmth.examcloud.web.redis.RedisClient;
 import cn.com.qmth.examcloud.web.security.ResourceManager;
+import cn.com.qmth.examcloud.web.security.SecurityProperty;
 import cn.com.qmth.examcloud.web.support.ApiInfo;
 import com.google.common.collect.Sets;
 import org.apache.commons.collections4.CollectionUtils;
@@ -40,7 +39,7 @@ public class ExamCloudResourceManager implements ResourceManager {
     private UserDataRuleCloudService userDataRuleCloudService;
 
     static {
-        PropertiesUtil.loadFromPath(PathUtil.getResoucePath("security.properties"));
+        SecurityProperty.loadProperties("security.properties");
     }
 
     @Override
@@ -80,7 +79,7 @@ public class ExamCloudResourceManager implements ResourceManager {
         // 学生鉴权
         if (user.getUserType().equals(UserType.STUDENT)) {
             String key = "[s]" + mapping;
-            return PropertiesUtil.getBoolean(key, false);
+            return SecurityProperty.getBooleanProperty(key, false);
         }
 
         List<Role> roleList = user.getRoleList();
@@ -96,8 +95,8 @@ public class ExamCloudResourceManager implements ResourceManager {
         }
 
         // 权限组集合
-        String privilegeGroups = PropertiesUtil.getString(mapping);
-        if (StringUtils.isBlank(privilegeGroups)) {
+        String privilegeGroups = SecurityProperty.getProperty(mapping);
+        if (StringUtils.isEmpty(privilegeGroups)) {
             return true;
         }
 

+ 39 - 39
examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/config/NativeMongoConfig.java

@@ -1,39 +1,39 @@
-package cn.com.qmth.examcloud.core.questions.starter.config;
-
-import com.mongodb.MongoClient;
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoDatabase;
-import org.bson.Document;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * Created by songyue on 18/1/29.
- */
-@Configuration
-public class NativeMongoConfig {
-    private static final String PDU_COLLECTION_NAME = "paperDetailUnit";
-
-    @Value("${mguri.database}")
-    private String dbName;
-
-    @Autowired
-    private MongoClient mongoClient;
-
-    @Bean
-    @Qualifier("mongoDatabase")
-    MongoDatabase getMongoDatabase() {
-        return mongoClient.getDatabase(dbName);
-    }
-
-    @Bean
-    @Qualifier("pduCollection")
-    MongoCollection<Document> getMongoPduCollection() {
-        MongoDatabase database = getMongoDatabase();
-        return database.getCollection(PDU_COLLECTION_NAME);
-    }
-
-}
+// package cn.com.qmth.examcloud.core.questions.starter.config;
+//
+// import com.mongodb.MongoClient;
+// import com.mongodb.client.MongoCollection;
+// import com.mongodb.client.MongoDatabase;
+// import org.bson.Document;
+// import org.springframework.beans.factory.annotation.Autowired;
+// import org.springframework.beans.factory.annotation.Qualifier;
+// import org.springframework.beans.factory.annotation.Value;
+// import org.springframework.context.annotation.Bean;
+// import org.springframework.context.annotation.Configuration;
+//
+// /**
+//  * Created by songyue on 18/1/29.
+//  */
+// @Configuration
+// public class NativeMongoConfig {
+//     private static final String PDU_COLLECTION_NAME = "paperDetailUnit";
+//
+//     @Value("${mguri.database}")
+//     private String dbName;
+//
+//     @Autowired
+//     private MongoClient mongoClient;
+//
+//     @Bean
+//     @Qualifier("mongoDatabase")
+//     MongoDatabase getMongoDatabase() {
+//         return mongoClient.getDatabase(dbName);
+//     }
+//
+//     @Bean
+//     @Qualifier("pduCollection")
+//     MongoCollection<Document> getMongoPduCollection() {
+//         MongoDatabase database = getMongoDatabase();
+//         return database.getCollection(PDU_COLLECTION_NAME);
+//     }
+//
+// }

+ 2 - 2
examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/config/Swagger2.java

@@ -10,10 +10,10 @@ import springfox.documentation.service.ApiInfo;
 import springfox.documentation.service.Contact;
 import springfox.documentation.spi.DocumentationType;
 import springfox.documentation.spring.web.plugins.Docket;
-import springfox.documentation.swagger2.annotations.EnableSwagger2;
+import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
 
 @Configuration
-@EnableSwagger2
+@EnableSwagger2WebMvc
 public class Swagger2 {
 
 	@Bean

+ 1 - 1
examcloud-core-questions-starter/src/main/java/cn/com/qmth/examcloud/core/questions/starter/config/initData.java

@@ -14,7 +14,7 @@ import org.springframework.stereotype.Component;
 @Component
 public class initData implements CommandLineRunner {
 
-    protected static final Logger log = LoggerFactory.getLogger(initData.class);
+    private static final Logger log = LoggerFactory.getLogger(initData.class);
 
     @Autowired
     SettingService settingService;

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

@@ -1,6 +1,6 @@
 spring.profiles.active=dev
-
-examcloud.startup.startupCode=8008
-examcloud.startup.configCenterHost=192.168.10.39
-examcloud.startup.configCenterPort=9999
-examcloud.startup.appCode=Q
+sys.config.center.enabled=true
+sys.config.center.address=192.168.10.39:8888
+sys.config.center.namespace=examcloud
+sys.config.center.appCode=Q
+sys.config.center.secretKey=

+ 0 - 1
examcloud-core-questions-starter/src/main/resources/classpath.location

@@ -1 +0,0 @@
-classpath 定位文件

+ 32 - 50
examcloud-core-questions-starter/src/main/resources/log4j2.xml

@@ -2,47 +2,31 @@
 <Configuration status="WARN" monitorInterval="30">
 
     <Properties>
-        <Property name="commonLevel" value="${sys:log.commonLevel}"/>
-        <Property name="logPattern">
+        <Property name="LOG_LEVEL" value="${sys:log.commonLevel}"/>
+        <Property name="LOG_DIR" value="../logs/examcloud-core-questions"/>
+        <Property name="LOG_PATTERN">
             %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="${logPattern}" charset="UTF-8"/>
+        <Console name="CONSOLE_APPENDER" target="SYSTEM_OUT">
+            <PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
         </Console>
 
-        <!-- 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>
+        <RollingFile name="FILE_APPENDER"
+                     fileName="${LOG_DIR}/debug.log"
+                     filePattern="${LOG_DIR}/debug-%d{yyyyMMdd}-%i.log">
+            <PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
 
-        <!-- 接口日志 -->
-        <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">
+                <Delete basePath="${LOG_DIR}" maxDepth="1">
+                    <IfFileName glob="debug-*.log">
                         <IfAccumulatedFileSize exceeds="10 GB"/>
                     </IfFileName>
                 </Delete>
@@ -51,35 +35,33 @@
     </Appenders>
 
     <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="springfox.documentation" level="WARN"/>
+        <logger name="org.springframework" level="WARN"/>
+        <logger name="org.hibernate" level="WARN"/>
+        <logger name="org.apache" level="WARN"/>
+        <logger name="org.quartz" level="WARN"/>
+        <logger name="org.docx4j" level="WARN"/>
+        <logger name="cn.afterturn" level="WARN"/>
+        <logger name="com.netflix" level="WARN"/>
+        <logger name="com.aliyun" level="WARN"/>
+        <logger name="io.lettuce" level="WARN"/>
+        <logger name="io.netty" level="WARN"/>
 
+        <!--<Logger name="org.hibernate.SQL" level="DEBUG"/>-->
+        <!--<Logger name="org.hibernate.engine.transaction" level="DEBUG"/>-->
+        <!--<Logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>-->
         <!--<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"/>-->
 
-        <Logger name="cn.com.qmth" level="${commonLevel}" additivity="false">
-            <AppenderRef ref="DEBUG_APPENDER"/>
-            <AppenderRef ref="Console"/>
-        </Logger>
-
-        <Logger name="INTERFACE_LOGGER" level="${commonLevel}" additivity="false">
-            <AppenderRef ref="INTERFACE_APPENDER"/>
-            <AppenderRef ref="Console"/>
+        <Logger name="cn.com.qmth" level="${LOG_LEVEL}" additivity="false">
+            <AppenderRef ref="CONSOLE_APPENDER"/>
+            <AppenderRef ref="FILE_APPENDER"/>
         </Logger>
 
-        <Root level="${commonLevel}">
-            <AppenderRef ref="Console"/>
-            <AppenderRef ref="DEBUG_APPENDER"/>
+        <Root level="${LOG_LEVEL}">
+            <AppenderRef ref="CONSOLE_APPENDER"/>
+            <AppenderRef ref="FILE_APPENDER"/>
         </Root>
     </Loggers>
 

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


+ 4 - 0
jenkins-prod.sh → jenkins.sh

@@ -1,4 +1,8 @@
 #!/bin/bash
+
 pwd
+mkdir -p ~/packages
 
 cp examcloud-core-questions-starter/target/examcloud-core-questions-distribution.zip ~/packages
+
+echo "finished..."

+ 19 - 18
pom.xml

@@ -1,22 +1,23 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-	<parent>
-		<groupId>cn.com.qmth.examcloud</groupId>
-		<artifactId>examcloud-parent</artifactId>
-		<version>v4.0.2-RELEASE</version>
-	</parent>
-	<groupId>cn.com.qmth.examcloud.core.questions</groupId>
-	<artifactId>examcloud-core-questions</artifactId>
-	<version>v4.0.2-RELEASE</version>
-	<packaging>pom</packaging>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>examcloud-core-questions</artifactId>
+    <packaging>pom</packaging>
 
-	<modules>
-		<module>examcloud-core-questions-base</module>
-		<module>examcloud-core-questions-dao</module>
-		<module>examcloud-core-questions-service</module>
-		<module>examcloud-core-questions-api-provider</module>
-		<module>examcloud-core-questions-starter</module>
-	</modules>
+    <parent>
+        <groupId>cn.com.qmth.examcloud</groupId>
+        <artifactId>examcloud-parent</artifactId>
+        <version>${revision}</version>
+        <relativePath>../examcloud-parent/pom.xml</relativePath>
+    </parent>
+
+    <modules>
+        <module>examcloud-core-questions-base</module>
+        <module>examcloud-core-questions-dao</module>
+        <module>examcloud-core-questions-service</module>
+        <module>examcloud-core-questions-api-provider</module>
+        <module>examcloud-core-questions-starter</module>
+    </modules>
 
 </project>

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott