Browse Source

merge from release_v4.0.1

deason 4 năm trước cách đây
mục cha
commit
ba3705af02
56 tập tin đã thay đổi với 6689 bổ sung5130 xóa
  1. 1 4
      .gitignore
  2. 1447 1481
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/ExamController.java
  3. 564 0
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/ExamStageController.java
  4. 62 3
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/ExamStudentController.java
  5. 3 4
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/NoticeController.java
  6. 1 0
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/bean/ExamDomain.java
  7. 170 0
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/bean/ExamStageDomain.java
  8. 70 0
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/bean/ExamStageSettingDomain.java
  9. 25 0
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/bean/ExamStudentDomain.java
  10. 734 772
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/provider/ExamCloudServiceProvider.java
  11. 116 0
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/provider/ExamStageCloudServiceProvider.java
  12. 25 0
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/provider/ExamStudentCloudServiceProvider.java
  13. 114 125
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/provider/HandleSyncCloudServiceProvider.java
  14. 73 72
      examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/provider/NoticeCloudServiceProvider.java
  15. 33 0
      examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/ExamStageRepo.java
  16. 16 0
      examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/ExamStageSettingRepo.java
  17. 5 0
      examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/ExamStudentRepo.java
  18. 166 0
      examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/entity/ExamStageEntity.java
  19. 66 0
      examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/entity/ExamStageSettingEntity.java
  20. 25 0
      examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/entity/ExamStudentEntity.java
  21. 61 58
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/ExamService.java
  22. 11 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/ExamStageService.java
  23. 11 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/ExamStageSettingService.java
  24. 13 6
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/ExamStudentService.java
  25. 71 71
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/NoticeService.java
  26. 14 14
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/OnGoingExamService.java
  27. 5 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/AddNoticeInfo.java
  28. 187 188
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/ExamInfo.java
  29. 103 103
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/ExamSpecialSettingsInfo.java
  30. 371 346
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/ExamStudentInfo.java
  31. 3 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/GetLimitUserIdResp.java
  32. 113 113
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/NoticeInfo.java
  33. 3 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/NoticeInfoQuery.java
  34. 6 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/UpdateNoticeInfo.java
  35. 6 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/UserNoticeInfo.java
  36. 3 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/UserNoticeInfoQuery.java
  37. 43 44
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamOrgPropertyCache.java
  38. 44 45
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamOrgSettingsCache.java
  39. 31 32
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamPropertyCache.java
  40. 48 49
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamSettingsCache.java
  41. 95 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamStageCache.java
  42. 101 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamStagesCache.java
  43. 29 30
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamStudentPropertyCache.java
  44. 44 45
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamStudentSettingsCache.java
  45. 771 758
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/ExamServiceImpl.java
  46. 15 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/ExamStageServiceImpl.java
  47. 15 0
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/ExamStageSettingServiceImpl.java
  48. 479 462
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/ExamStudentServiceImpl.java
  49. 17 9
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/NoticeServiceImpl.java
  50. 182 183
      examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/OnGoingExamServiceImpl.java
  51. 0 16
      examcloud-core-examwork-starter/.springBeans
  52. 1 1
      examcloud-core-examwork-starter/src/main/resources/application.properties
  53. 77 58
      examcloud-core-examwork-starter/src/main/resources/log4j2.xml
  54. BIN
      examcloud-core-examwork-starter/src/main/resources/templates/studentImportTemplate.xlsx
  55. 0 19
      jenkins-dev.sh
  56. 0 19
      jenkins-test.sh

+ 1 - 4
.gitignore

@@ -3,6 +3,7 @@
 # Proguard folder generated by ide
 .project
 .classpath
+*.springBeans
 .settings
 target/
 .idea/
@@ -17,7 +18,3 @@ target/
 *.jar
 *.war
 *.ear
-/exam-work-main/src/test/
-logs/
-fileImport/
-fileExport/

+ 1447 - 1481
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/ExamController.java

@@ -1,49 +1,5 @@
 package cn.com.qmth.examcloud.core.examwork.api.controller;
 
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import javax.persistence.criteria.Predicate;
-import javax.persistence.criteria.Root;
-import javax.persistence.criteria.Subquery;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.fileupload.disk.DiskFileItem;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.domain.Sort.Direction;
-import org.springframework.data.jpa.domain.Specification;
-import org.springframework.transaction.annotation.Transactional;
-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.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.multipart.commons.CommonsMultipartFile;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
 import cn.com.qmth.examcloud.api.commons.enums.CURD;
 import cn.com.qmth.examcloud.api.commons.enums.ExamSpecialSettingsType;
 import cn.com.qmth.examcloud.api.commons.enums.ExamType;
@@ -54,12 +10,8 @@ import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.commons.helpers.DynamicEnum;
 import cn.com.qmth.examcloud.commons.helpers.DynamicEnumManager;
 import cn.com.qmth.examcloud.commons.helpers.poi.ExcelWriter;
-import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.commons.util.*;
 import cn.com.qmth.examcloud.commons.util.DateUtil.DatePatterns;
-import cn.com.qmth.examcloud.commons.util.JsonUtil;
-import cn.com.qmth.examcloud.commons.util.RegExpUtil;
-import cn.com.qmth.examcloud.commons.util.StringUtil;
-import cn.com.qmth.examcloud.commons.util.Util;
 import cn.com.qmth.examcloud.core.basic.api.OrgCloudService;
 import cn.com.qmth.examcloud.core.basic.api.StudentCloudService;
 import cn.com.qmth.examcloud.core.basic.api.bean.OrgBean;
@@ -72,20 +24,8 @@ import cn.com.qmth.examcloud.core.examwork.api.controller.bean.ExamDomain;
 import cn.com.qmth.examcloud.core.examwork.api.controller.bean.ExamOrgSettingsDomain;
 import cn.com.qmth.examcloud.core.examwork.api.controller.bean.StudentSpecialSettingsDomain;
 import cn.com.qmth.examcloud.core.examwork.base.enums.ExamProperty;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamCourseRelationRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamOrgPropertyRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamPaperTypeRelationRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamPropertyRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamSpecialSettingsRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamStudentRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamCourseRelationEntity;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamEntity;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamOrgPropertyEntity;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamPaperTypeRelationEntity;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamPropertyEntity;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamSpecialSettingsEntity;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.examwork.dao.*;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.*;
 import cn.com.qmth.examcloud.core.examwork.service.ExamService;
 import cn.com.qmth.examcloud.core.examwork.service.bean.ExamInfo;
 import cn.com.qmth.examcloud.core.examwork.service.bean.ExamSpecialSettingsInfo;
@@ -95,14 +35,7 @@ import cn.com.qmth.examcloud.core.oe.admin.api.ExamRecordCloudService;
 import cn.com.qmth.examcloud.core.oe.admin.api.request.CheckExamIsStartedReq;
 import cn.com.qmth.examcloud.core.oe.admin.api.response.CheckExamIsStartedResp;
 import cn.com.qmth.examcloud.support.cache.CacheHelper;
-import cn.com.qmth.examcloud.support.cache.bean.ExamOrgPropertyCacheBean;
-import cn.com.qmth.examcloud.support.cache.bean.ExamOrgSettingsCacheBean;
-import cn.com.qmth.examcloud.support.cache.bean.ExamPropertyCacheBean;
-import cn.com.qmth.examcloud.support.cache.bean.ExamSettingsCacheBean;
-import cn.com.qmth.examcloud.support.cache.bean.ExamStudentPropertyCacheBean;
-import cn.com.qmth.examcloud.support.cache.bean.ExamStudentSettingsCacheBean;
-import cn.com.qmth.examcloud.support.cache.bean.OrgPropertyCacheBean;
-import cn.com.qmth.examcloud.support.cache.bean.StudentCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.*;
 import cn.com.qmth.examcloud.support.privilege.PrivilegeDefine;
 import cn.com.qmth.examcloud.support.privilege.PrivilegeManager;
 import cn.com.qmth.examcloud.task.api.DataSyncCloudService;
@@ -111,7 +44,36 @@ import cn.com.qmth.examcloud.web.config.SystemProperties;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
 import cn.com.qmth.examcloud.web.redis.RedisClient;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.fileupload.disk.DiskFileItem;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.commons.CommonsMultipartFile;
+
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * 重构
@@ -121,1442 +83,1446 @@ import io.swagger.annotations.ApiOperation;
  * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  */
 @RestController
+@Api(tags = "考试相关接口")
 @RequestMapping("${$rmp.ctr.examwork}/exam")
 public class ExamController extends ControllerSupport {
 
-	@Autowired
-	SystemProperties systemConfig;
+    @Autowired
+    SystemProperties systemConfig;
 
-	@Autowired
-	ExamPropertyRepo examPropertyRepo;
+    @Autowired
+    ExamPropertyRepo examPropertyRepo;
 
-	@Autowired
-	RedisClient redisClient;
+    @Autowired
+    RedisClient redisClient;
 
-	@Autowired
-	ExamRepo examRepo;
+    @Autowired
+    ExamRepo examRepo;
 
-	@Autowired
-	ExamService examService;
+    @Autowired
+    ExamService examService;
 
-	@Autowired
-	ExamStudentRepo examStudentRepo;
+    @Autowired
+    ExamStudentRepo examStudentRepo;
 
-	@Autowired
-	ExamStudentServiceImpl examStudentService;
+    @Autowired
+    ExamStudentServiceImpl examStudentService;
 
-	@Autowired
-	ExamSpecialSettingsRepo examSpecialSettingsRepo;
+    @Autowired
+    ExamSpecialSettingsRepo examSpecialSettingsRepo;
 
-	@Autowired
-	OrgCloudService orgCloudService;
+    @Autowired
+    OrgCloudService orgCloudService;
 
-	@Autowired
-	ExamOrgPropertyRepo examOrgPropertyRepo;
+    @Autowired
+    ExamOrgPropertyRepo examOrgPropertyRepo;
 
-	@Autowired
-	StudentCloudService studentCloudService;
+    @Autowired
+    StudentCloudService studentCloudService;
 
-	@Autowired
-	ExamCourseRelationRepo examCourseRelationRepo;
+    @Autowired
+    ExamCourseRelationRepo examCourseRelationRepo;
 
-	@Autowired
-	ExamPaperTypeRelationRepo examPaperTypeRelationRepo;
+    @Autowired
+    ExamPaperTypeRelationRepo examPaperTypeRelationRepo;
 
-	@Autowired
-	ExamRecordCloudService examRecordCloudService;
+    @Autowired
+    ExamRecordCloudService examRecordCloudService;
 
-	@Autowired
-	DataSyncCloudService dataSyncCloudService;
+    @Autowired
+    DataSyncCloudService dataSyncCloudService;
 
-	@Autowired
-	ExamSettingsCache examSettingsCache;
+    @Autowired
+    ExamSettingsCache examSettingsCache;
 
-	private static final String[] EXAM_ORG_SETTINGS_EXCEL_HEADER = new String[]{"学习中心ID", "学习中心代码",
-			"学习中心名称", "是否可以考试(是/否)", "开始考试时间 yyyy-MM-dd hh:mm:ss", "结束考试时间 yyyy-MM-dd hh:mm:ss"};
+    private static final String[] EXAM_ORG_SETTINGS_EXCEL_HEADER = new String[]{"学习中心ID", "学习中心代码",
+            "学习中心名称", "是否可以考试(是/否)", "开始考试时间 yyyy-MM-dd hh:mm:ss", "结束考试时间 yyyy-MM-dd hh:mm:ss"};
 
-	@ApiOperation(value = "查询考试课程的试卷类型集合")
-	@GetMapping("queryExamCoursePaperTypeList")
-	public List<ExamPaperTypeRelationEntity> queryExamCoursePaperTypeList(
-			@RequestParam(required = true) Long examId,
-			@RequestParam(required = true) Long courseId) {
+    @ApiOperation(value = "查询考试课程的试卷类型集合")
+    @GetMapping("queryExamCoursePaperTypeList")
+    public List<ExamPaperTypeRelationEntity> queryExamCoursePaperTypeList(
+            @RequestParam(required = true) Long examId,
+            @RequestParam(required = true) Long courseId) {
 
-		if (null == examId) {
-			throw new StatusException("001251", "examId is null");
-		}
-		if (null == courseId) {
-			throw new StatusException("001252", "courseId is null");
-		}
-		ExamEntity one = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		if (null == one) {
-			throw new StatusException("001253", "examId is wrong");
-		}
+        if (null == examId) {
+            throw new StatusException("001251", "examId is null");
+        }
+        if (null == courseId) {
+            throw new StatusException("001252", "courseId is null");
+        }
+        ExamEntity one = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        if (null == one) {
+            throw new StatusException("001253", "examId is wrong");
+        }
 
-		validateRootOrgIsolation(one.getRootOrgId());
+        validateRootOrgIsolation(one.getRootOrgId());
 
-		List<ExamPaperTypeRelationEntity> list = examPaperTypeRelationRepo
-				.findByExamIdAndCourseId(examId, courseId);
+        List<ExamPaperTypeRelationEntity> list = examPaperTypeRelationRepo
+                .findByExamIdAndCourseId(examId, courseId);
 
-		return list;
-	}
+        return list;
+    }
 
-	@ApiOperation(value = "查询考试的课程集合")
-	@GetMapping("queryExamCourseList")
-	public List<ExamCourseRelationEntity> getExamCourseList(
-			@RequestParam(required = true) Long examId, @RequestParam(required = false) String name,
-			@RequestParam(required = false) String level,
-			@RequestParam(required = false) Boolean enable) {
+    @ApiOperation(value = "查询考试的课程集合")
+    @GetMapping("queryExamCourseList")
+    public List<ExamCourseRelationEntity> getExamCourseList(
+            @RequestParam(required = true) Long examId, @RequestParam(required = false) String name,
+            @RequestParam(required = false) String level,
+            @RequestParam(required = false) Boolean enable) {
 
-		ExamEntity one = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		if (null == one) {
-			throw new StatusException("001250", "examId is wrong");
-		}
-		validateRootOrgIsolation(one.getRootOrgId());
+        ExamEntity one = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        if (null == one) {
+            throw new StatusException("001250", "examId is wrong");
+        }
+        validateRootOrgIsolation(one.getRootOrgId());
 
-		Specification<ExamCourseRelationEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-
-			predicates.add(cb.equal(root.get("examId"), examId));
-
-			Predicate pr1 = cb.like(root.get("courseName"), toSqlSearchPattern(name));
-			Predicate pr2 = cb.like(root.get("courseCode"), toSqlSearchPattern(name));
-
-			predicates.add(cb.or(pr1, pr2));
-
-			if (StringUtils.isNotBlank(level)) {
-				predicates.add(cb.equal(root.get("level"), toSqlSearchPattern(level)));
-			}
-
-			if (null != enable) {
-				predicates.add(cb.equal(root.get("courseEnable"), enable));
-			}
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		PageRequest pageRequest = PageRequest.of(0, 50, new Sort(Direction.DESC, "updateTime"));
-
-		Page<ExamCourseRelationEntity> page = examCourseRelationRepo.findAll(specification,
-				pageRequest);
-
-		Iterator<ExamCourseRelationEntity> iterator = page.iterator();
-		List<ExamCourseRelationEntity> list = Lists.newArrayList();
-
-		while (iterator.hasNext()) {
-			ExamCourseRelationEntity next = iterator.next();
-			list.add(next);
-		}
-
-		return list;
-
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param curPage
-	 * @param pageSize
-	 * @param name
-	 * @param examType
-	 * @param enable
-	 * @return
-	 */
-	@ApiOperation(value = "分页查询考试批次")
-	@GetMapping("queryPage/{curPage}/{pageSize}")
-	public PageInfo<ExamDomain> queryPage(@PathVariable Integer curPage,
-			@PathVariable Integer pageSize, @RequestParam(required = false) String name,
-			@RequestParam(required = false) String examType,
-			@RequestParam(required = false) Boolean enable,
-			@RequestParam(required = false) String propertyKeys) {
-
-		User accessUser = getAccessUser();
-
-		Specification<ExamEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-			predicates.add(cb.equal(root.get("rootOrgId"), accessUser.getRootOrgId()));
-			if (StringUtils.isNotBlank(name)) {
-				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
-			}
-			if (null != enable) {
-				predicates.add(cb.equal(root.get("enable"), enable));
-			}
-			if (StringUtils.isNotBlank(examType)) {
-				predicates.add(cb.equal(root.get("examType"), ExamType.valueOf(examType)));
-			}
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		PageRequest pageRequest = PageRequest.of(curPage, pageSize,
-				new Sort(Direction.DESC, "updateTime", "id"));
-
-		Page<ExamEntity> page = examRepo.findAll(specification, pageRequest);
-
-		Iterator<ExamEntity> iterator = page.iterator();
-		List<ExamDomain> list = Lists.newArrayList();
-
-		List<String> propertyKeyList = null;
-		if (StringUtils.isNotBlank(propertyKeys)) {
-			propertyKeyList = RegExpUtil.findAll(propertyKeys, "\\w+");
-		}
-
-		while (iterator.hasNext()) {
-			ExamEntity next = iterator.next();
-			ExamDomain bean = new ExamDomain();
-			list.add(bean);
-
-			bean.setId(next.getId());
-			bean.setCode(next.getCode());
-			bean.setName(next.getName());
-			bean.setEnable(next.getEnable());
-			bean.setRootOrgId(next.getRootOrgId());
-			bean.setBeginTime(next.getBeginTime());
-			bean.setEndTime(next.getEndTime());
-			bean.setExamType(next.getExamType());
-			bean.setDuration(next.getDuration());
-			bean.setEnable(next.getEnable());
-			bean.setRemark(next.getRemark());
-			bean.setExamTimes(next.getExamTimes());
-			bean.setCreationTime(next.getCreationTime());
-			bean.setUpdateTime(next.getUpdateTime());
-			bean.setExamLimit(next.getExamLimit());
-			bean.setSpecialSettingsEnabled(next.getSpecialSettingsEnabled());
-			bean.setSpecialSettingsType(next.getSpecialSettingsType());
-
-			if (CollectionUtils.isNotEmpty(propertyKeyList)) {
-				Map<String, String> properties = getProperties(bean.getId(), propertyKeyList);
-				bean.setProperties(properties);
-			}
-		}
-
-		PageInfo<ExamDomain> ret = new PageInfo<ExamDomain>(page, list);
-		return ret;
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param name
-	 * @param examTypes
-	 * @param enable
-	 * @param studentId
-	 *            筛选学生关联的考试
-	 * @return
-	 */
-	@ApiOperation(value = "查询考试批次")
-	@GetMapping("queryByNameLike")
-	public List<ExamDomain> query(@RequestParam(required = true) String name,
-			@RequestParam(required = false) String examTypes,
-			@RequestParam(required = false) Boolean enable,
-			@RequestParam(required = false) String propertyKeys,
-			@RequestParam(required = false) Long studentId,
-			@RequestParam(required = false) Long rootOrgId) {
-
-		if (null == rootOrgId) {
-			rootOrgId = getRootOrgId();
-		}
-		final Long finalRootOrgId = rootOrgId;
-
-		Specification<ExamEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-			predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
-			if (StringUtils.isNotBlank(name)) {
-				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
-			}
-			if (null != enable) {
-				predicates.add(cb.equal(root.get("enable"), enable));
-			}
-			if (StringUtils.isNotBlank(examTypes)) {
-				List<String> examTypeList = RegExpUtil.findAll(examTypes, "\\w+");
-
-				List<ExamType> etList = Lists.newArrayList();
-				for (String cur : examTypeList) {
-					etList.add(ExamType.valueOf(cur));
-				}
-
-				if (CollectionUtils.isNotEmpty(etList)) {
-					if (10 < examTypeList.size()) {
-						throw new StatusException("001120", "too many examTypes");
-					}
-					if (1 == examTypeList.size()) {
-						predicates.add(cb.equal(root.get("examType"), etList.get(0)));
-					} else {
-						predicates.add(root.get("examType").in(etList));
-					}
-				}
-			}
-
-			if (null != studentId) {
-				Subquery<ExamStudentEntity> subquery = query.subquery(ExamStudentEntity.class);
-				Root<ExamStudentEntity> subRoot = subquery.from(ExamStudentEntity.class);
-				subquery.select(subRoot.get("id"));
-				Predicate p1 = cb.equal(subRoot.get("studentId"), studentId);
-				Predicate p2 = cb.equal(subRoot.get("examId"), root.get("id"));
-				subquery.where(cb.and(p1, p2));
-				predicates.add(cb.exists(subquery));
-			}
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		PageRequest pageRequest = PageRequest.of(0, 100,
-				new Sort(Direction.DESC, "updateTime", "id"));
-		Page<ExamEntity> page = examRepo.findAll(specification, pageRequest);
-
-		Iterator<ExamEntity> iterator = page.iterator();
-		List<ExamDomain> list = Lists.newArrayList();
-
-		List<String> propertyKeyList = null;
-		if (StringUtils.isNotBlank(propertyKeys)) {
-			propertyKeyList = RegExpUtil.findAll(propertyKeys, "\\w+");
-		}
-
-		while (iterator.hasNext()) {
-			ExamEntity next = iterator.next();
-			ExamDomain bean = new ExamDomain();
-			list.add(bean);
-
-			bean.setId(next.getId());
-			bean.setName(next.getName());
-			bean.setEnable(next.getEnable());
-			bean.setRootOrgId(next.getRootOrgId());
-			bean.setBeginTime(next.getBeginTime());
-			bean.setEndTime(next.getEndTime());
-			bean.setExamType(next.getExamType());
-
-			if (CollectionUtils.isNotEmpty(propertyKeyList)) {
-				Map<String, String> properties = getProperties(bean.getId(), propertyKeyList);
-				bean.setProperties(properties);
-			}
-		}
-
-		return list;
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @param propertyKeys
-	 * @return
-	 */
-	private Map<String, String> getProperties(Long examId, List<String> propertyKeys) {
-		Map<String, String> map = Maps.newHashMap();
-		DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-
-		for (String key : propertyKeys) {
-			DynamicEnum de = manager.getByName(key);
-			ExamPropertyEntity one = examPropertyRepo.findByExamIdAndKeyId(examId, de.getId());
-			if (null != one) {
-				map.put(key, one.getValue());
-			}
-		}
-
-		return map;
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @return
-	 */
-	@ApiOperation(value = "按ID查询考试批次", notes = "ID查询")
-	@GetMapping("{examId}")
-	public ExamDomain getExamById(@PathVariable Long examId) {
-
-		ExamSettingsCacheBean cache = examSettingsCache.get(examId);
-		validateRootOrgIsolation(cache.getRootOrgId());
-
-		ExamDomain domain = new ExamDomain();
-		domain.setBeginTime(cache.getBeginTime());
-		domain.setDuration(cache.getDuration());
-		domain.setEnable(cache.getEnable());
-		domain.setEndTime(cache.getEndTime());
-		domain.setExamTimes(cache.getExamTimes());
-		domain.setExamType(ExamType.valueOf(cache.getExamType()));
-		domain.setId(cache.getId());
-		domain.setName(cache.getName());
-		domain.setCode(cache.getCode());
-		domain.setRemark(cache.getRemark());
-		domain.setRootOrgId(cache.getRootOrgId());
-		domain.setStarted(isStarted(cache.getId()));
-		domain.setExamLimit(cache.getExamLimit());
-		domain.setSpecialSettingsEnabled(cache.getSpecialSettingsEnabled());
-		domain.setSpecialSettingsType(cache.getSpecialSettingsType());
-
-		return domain;
-	}
-
-	/**
-	 * 是否开考
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @return
-	 */
-	private boolean isStarted(Long examId) {
-		CheckExamIsStartedReq checkExamIsStartedReq = new CheckExamIsStartedReq();
-		checkExamIsStartedReq.setExamId(examId);
-		try {
-			CheckExamIsStartedResp checkExamIsStartedResp = examRecordCloudService
-					.checkExamIsStarted(checkExamIsStartedReq);
-			return checkExamIsStartedResp.getIsStarted();
-		} catch (Exception e) {
-			log.error("fail to rmi[oe.examRecordCloudService.checkExamIsStarted]", e);
-			return true;
-		}
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param domain
-	 * @return
-	 */
-	@ApiOperation(value = "新增考试批次", notes = "新增")
-	@PostMapping()
-	@Transactional
-	public ExamEntity addExam(@RequestBody ExamDomain domain) {
-		return saveExam(domain, CURD.CREATION);
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param domain
-	 * @return
-	 */
-	@ApiOperation(value = "更新考试批次", notes = "更新")
-	@PutMapping()
-	@Transactional
-	public ExamEntity updateExam(@RequestBody ExamDomain domain) {
-		return saveExam(domain, CURD.UPDATE);
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param domain
-	 * @return
-	 */
-	private ExamEntity saveExam(ExamDomain domain, CURD es) {
-		trim(domain, false);
-
-		User accessUser = getAccessUser();
-
-		ExamInfo examInfo = new ExamInfo();
-		examInfo.setBeginTime(domain.getBeginTime());
-		examInfo.setDuration(domain.getDuration());
-		examInfo.setEnable(domain.getEnable());
-		examInfo.setEndTime(domain.getEndTime());
-		examInfo.setExamTimes(domain.getExamTimes());
-		examInfo.setExamType(domain.getExamType());
-		examInfo.setCode(domain.getCode());
-		examInfo.setName(domain.getName());
-		examInfo.setRemark(domain.getRemark());
-		examInfo.setRootOrgId(accessUser.getRootOrgId());
-		examInfo.setExamLimit(domain.getExamLimit());
-		examInfo.setSpecialSettingsEnabled(domain.getSpecialSettingsEnabled());
-		examInfo.setSpecialSettingsType(domain.getSpecialSettingsType());
-
-		Map<String, String> properties = domain.getProperties();
-		if (null == properties) {
-			properties = Maps.newHashMap();
-		}
-		examInfo.setProperties(properties);
-
-		ExamEntity saved = examService.saveExam(examInfo, es);
-
-		examSettingsCache.remove(saved.getId());
-
-		return saved;
-	}
-
-	@ApiOperation(value = "复制考试批次", notes = "")
-	@PostMapping("copyExam")
-	@Transactional
-	public ExamEntity copyExam(@RequestBody CopyExamDomain domain) {
-
-		Long srcExamId = domain.getSrcExamId();
-
-		ExamEntity srcExam = GlobalHelper.getEntity(examRepo, srcExamId, ExamEntity.class);
-		if (null == srcExam) {
-			throw new StatusException("001259", "examId is wrong");
-		}
-
-		validateRootOrgIsolation(srcExam.getRootOrgId());
-
-		ExamDomain ed = new ExamDomain();
-		ed.setBeginTime(srcExam.getBeginTime());
-		ed.setDuration(srcExam.getDuration());
-		ed.setEnable(srcExam.getEnable());
-		ed.setEndTime(srcExam.getEndTime());
-		ed.setExamTimes(srcExam.getExamTimes());
-		ed.setExamType(srcExam.getExamType());
-		ed.setRemark(srcExam.getRemark());
-		ed.setRootOrgId(srcExam.getRootOrgId());
-		ed.setStarted(isStarted(srcExam.getId()));
-		ed.setExamLimit(srcExam.getExamLimit());
-		ed.setSpecialSettingsEnabled(srcExam.getSpecialSettingsEnabled());
-		ed.setSpecialSettingsType(srcExam.getSpecialSettingsType());
-
-		ed.setName(domain.getDestExamName());
-		ed.setCode(domain.getDestExamCode());
-
-		Map<String, String> map = Maps.newHashMap();
-		List<ExamPropertyEntity> list = examPropertyRepo.findByExamId(srcExam.getId());
-		DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-
-		List<String> excludedProps = Util.buildList("MARKING_TASK_BUILDED");
-		for (ExamPropertyEntity cur : list) {
-			DynamicEnum de = manager.getById(cur.getKeyId());
-			if (excludedProps.contains(de.getName())) {
-				continue;
-			}
-			map.put(de.getName(), cur.getValue());
-		}
-
-		ed.setProperties(map);
-
-		ExamEntity savedExam = saveExam(ed, CURD.CREATION);
-		return savedExam;
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @return
-	 */
-	@ApiOperation(value = "查询考试批次所有属性")
-	@GetMapping("allProperties/{examId}")
-	public Map<String, String> getAllExamProperties(@PathVariable Long examId) {
-
-		ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		if (null == examEntity) {
-			throw new StatusException("001250", "examId is wrong");
-		}
-		validateRootOrgIsolation(examEntity.getRootOrgId());
-
-		Map<String, String> map = Maps.newHashMap();
-		List<ExamPropertyEntity> list = examPropertyRepo.findByExamId(examId);
-		DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-		for (ExamPropertyEntity cur : list) {
-			DynamicEnum de = manager.getById(cur.getKeyId());
-			map.put(de.getName(), cur.getValue());
-		}
-
-		return map;
-	}
-
-	@ApiOperation(value = "查询考生的考试批次配置")
-	@GetMapping("getExamSettingsFromCacheByStudentSession/{examId}")
-	public ExamDomain getExamSettingsFromCacheByStudentSession(@PathVariable Long examId) {
-
-		User accessUser = getAccessUser();
-		if (!accessUser.getUserType().equals(UserType.STUDENT)) {
-			throw new StatusException("001259", "just allowed by student");
-		}
-
-		Long studentId = accessUser.getUserId();
-
-		StudentCacheBean student = CacheHelper.getStudent(studentId);
-		Long orgId = student.getOrgId();
-
-		ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
-
-		Boolean specialSettingsEnabled = examSettings.getSpecialSettingsEnabled();
-		ExamSpecialSettingsType specialSettingsType = examSettings.getSpecialSettingsType();
-
-		ExamDomain domain = new ExamDomain();
-
-		domain.setBeginTime(examSettings.getBeginTime());
-		domain.setDuration(examSettings.getDuration());
-		domain.setEnable(examSettings.getEnable());
-		domain.setEndTime(examSettings.getEndTime());
-		domain.setExamTimes(examSettings.getExamTimes());
-		domain.setExamType(ExamType.valueOf(examSettings.getExamType()));
-		domain.setId(examSettings.getId());
-		domain.setName(examSettings.getName());
-		domain.setCode(examSettings.getCode());
-		domain.setRemark(examSettings.getRemark());
-		domain.setRootOrgId(examSettings.getRootOrgId());
-		domain.setSpecialSettingsEnabled(examSettings.getSpecialSettingsEnabled());
-		domain.setSpecialSettingsType(examSettings.getSpecialSettingsType());
-		domain.setExamLimit(examSettings.getExamLimit());
-		domain.setStarted(examSettings.getBeginTime().before(new Date()));
-
-		if (specialSettingsEnabled && null != specialSettingsType) {
-
-			if (specialSettingsType.equals(ExamSpecialSettingsType.ORG_BASED)) {
-
-				ExamOrgSettingsCacheBean examOrgSettings = CacheHelper.getExamOrgSettings(examId,
-						orgId);
-
-				if (null != examOrgSettings.getBeginTime()) {
-					domain.setBeginTime(examOrgSettings.getBeginTime());
-					domain.setStarted(examOrgSettings.getBeginTime().before(new Date()));
-				}
-				if (null != examOrgSettings.getEndTime()) {
-					domain.setEndTime(examOrgSettings.getEndTime());
-				}
-				if (null != examOrgSettings.getDuration()) {
-					domain.setDuration(examOrgSettings.getDuration());
-				}
-				if (null != examOrgSettings.getExamTimes()) {
-					domain.setExamTimes(examOrgSettings.getExamTimes());
-				}
-				if (null != examOrgSettings.getExamLimit()) {
-					domain.setExamLimit(examOrgSettings.getExamLimit());
-				}
-
-			} else if (specialSettingsType.equals(ExamSpecialSettingsType.STUDENT_BASED)) {
-
-				ExamStudentSettingsCacheBean examStudentSettings = CacheHelper
-						.getExamStudentSettings(examId, studentId);
-
-				if (null != examStudentSettings.getBeginTime()) {
-					domain.setBeginTime(examStudentSettings.getBeginTime());
-					domain.setStarted(examStudentSettings.getBeginTime().before(new Date()));
-				}
-				if (null != examStudentSettings.getEndTime()) {
-					domain.setEndTime(examStudentSettings.getEndTime());
-				}
-				if (null != examStudentSettings.getDuration()) {
-					domain.setDuration(examStudentSettings.getDuration());
-				}
-				if (null != examStudentSettings.getExamTimes()) {
-					domain.setExamTimes(examStudentSettings.getExamTimes());
-				}
-				if (null != examStudentSettings.getExamLimit()) {
-					domain.setExamLimit(examStudentSettings.getExamLimit());
-				}
-
-			}
-		}
-
-		return domain;
-	}
-
-	@ApiOperation(value = "查询考生的考试批次属性集")
-	@GetMapping("getExamPropertyFromCacheByStudentSession/{examId}/{keys}")
-	public Map<String, String> getExamPropertyFromCacheByStudentSession(@PathVariable Long examId,
-			@PathVariable String keys) {
-
-		String[] keyArray = StringUtils.splitByWholeSeparator(keys, ",");
-
-		User accessUser = getAccessUser();
-		if (!accessUser.getUserType().equals(UserType.STUDENT)) {
-			throw new StatusException("001258", "just allowed by student");
-		}
-
-		Long studentId = accessUser.getUserId();
-
-		StudentCacheBean student = CacheHelper.getStudent(studentId);
-		Long orgId = student.getOrgId();
-
-		Map<String, String> map = Maps.newHashMap();
-
-		ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
-
-		Boolean specialSettingsEnabled = examSettings.getSpecialSettingsEnabled();
-		ExamSpecialSettingsType specialSettingsType = examSettings.getSpecialSettingsType();
-
-		for (String key : keyArray) {
-			ExamPropertyCacheBean propCache = CacheHelper.getExamProperty(examId, key);
-			map.put(key, propCache.getValue());
-		}
-
-		if (specialSettingsEnabled || null != specialSettingsType) {
-			if (specialSettingsType.equals(ExamSpecialSettingsType.ORG_BASED)) {
-				for (String key : keyArray) {
-					ExamOrgPropertyCacheBean propCache = CacheHelper.getExamOrgProperty(examId,
-							orgId, key);
-					if (StringUtils.isNotBlank(propCache.getValue())) {
-						map.put(key, propCache.getValue());
-					}
-				}
-			} else if (specialSettingsType.equals(ExamSpecialSettingsType.STUDENT_BASED)) {
-				for (String key : keyArray) {
-					ExamStudentPropertyCacheBean propCache = CacheHelper
-							.getExamStudentProperty(examId, studentId, key);
-					if (StringUtils.isNotBlank(propCache.getValue())) {
-						map.put(key, propCache.getValue());
-					}
-				}
-			}
-		}
-
-		return map;
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @return
-	 */
-	@ApiOperation(value = "查询考试批次单个属性")
-	@GetMapping("property/{examId}/{key}")
-	public String getExamProperty(@PathVariable Long examId, @PathVariable String key) {
-		ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		if (null == examEntity) {
-			throw new StatusException("001250", "examId is wrong");
-		}
-		validateRootOrgIsolation(examEntity.getRootOrgId());
-		DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-		DynamicEnum de = manager.getByName(key);
-		ExamPropertyEntity one = examPropertyRepo.findByExamIdAndKeyId(examId, de.getId());
-		if (null == one) {
-			return null;
-		}
-		return one.getValue();
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @return
-	 */
-	@ApiOperation(value = "查询考试批次多个属性")
-	@GetMapping("propertys/{examId}/{keys}")
-	public Map<String, String> getExamPropertyList(@PathVariable Long examId,
-			@PathVariable String keys) {
-		String[] keyArray = StringUtils.splitByWholeSeparator(keys, ",");
-		ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		if (null == examEntity) {
-			throw new StatusException("001250", "examId is wrong");
-		}
-		validateRootOrgIsolation(examEntity.getRootOrgId());
-
-		Map<String, String> properties = Maps.newHashMap();
-		for (String key : keyArray) {
-			DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-			DynamicEnum de = manager.getByName(key);
-			ExamPropertyEntity one = examPropertyRepo.findByExamIdAndKeyId(examId, de.getId());
-			if (null != one) {
-				properties.put(key, one.getValue());
-			}
-		}
-		return properties;
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @return
-	 */
-	@ApiOperation(value = "查询考试批次机构属性")
-	@GetMapping("examOrgProperty/{examId}/{orgId}/{key}")
-	public String getExamOrgProperty(@PathVariable Long examId, @PathVariable Long orgId,
-			@PathVariable String key) {
-		ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		if (null == examEntity) {
-			throw new StatusException("001250", "examId is wrong");
-		}
-		validateRootOrgIsolation(examEntity.getRootOrgId());
-
-		if (null == orgId) {
-			throw new StatusException("001251", "orgId is null");
-		}
-
-		GetOrgReq getOrgReq = new GetOrgReq();
-		getOrgReq.setOrgId(orgId);
-		GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
-		validateRootOrgIsolation(getOrgResp.getOrg().getRootId());
-
-		return examService.getExamOrgProperty(examId, orgId, key);
-	}
-
-	@ApiOperation(value = "启用考试", notes = "启用考试")
-	@PutMapping("enable/{ids}")
-	@Transactional
-	public void enableExam(@PathVariable String ids) {
-		List<Long> examIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
-				.collect(Collectors.toList());
-		for (Long examId : examIds) {
-			ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-			exam.setEnable(true);
-			examRepo.saveAndFlush(exam);
-		}
-
-		for (Long examId : examIds) {
-			ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-			SyncExamReq req = new SyncExamReq();
-			req.setId(exam.getId());
-			req.setBeginTime(exam.getBeginTime());
-			req.setDuration(exam.getDuration());
-			req.setEnable(exam.getEnable());
-			req.setEndTime(exam.getEndTime());
-			req.setExamTimes(exam.getExamTimes());
-			req.setExamType(exam.getExamType().name());
-			req.setName(exam.getName());
-			req.setRemark(exam.getRemark());
-			req.setRootOrgId(exam.getRootOrgId());
-
-			GetOrgReq getOrgReq = new GetOrgReq();
-			getOrgReq.setOrgId(exam.getRootOrgId());
-			GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
-			OrgBean rootOrg = getOrgResp.getOrg();
-			req.setRootOrgName(rootOrg.getName());
-
-			req.setSyncType("update");
-			dataSyncCloudService.syncExam(req);
-		}
-
-		for (Long examId : examIds) {
-			examSettingsCache.remove(examId);
-		}
-
-	}
-
-	@ApiOperation(value = "禁用考试", notes = "禁用考试")
-	@PutMapping("disable/{ids}")
-	@Transactional
-	public void disableExam(@PathVariable String ids) {
-		List<Long> examIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
-				.collect(Collectors.toList());
-		for (Long examId : examIds) {
-			ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-			exam.setEnable(false);
-			examRepo.saveAndFlush(exam);
-		}
-
-		for (Long examId : examIds) {
-			ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-			SyncExamReq req = new SyncExamReq();
-			req.setId(exam.getId());
-			req.setBeginTime(exam.getBeginTime());
-			req.setDuration(exam.getDuration());
-			req.setEnable(exam.getEnable());
-			req.setEndTime(exam.getEndTime());
-			req.setExamTimes(exam.getExamTimes());
-			req.setExamType(exam.getExamType().name());
-			req.setName(exam.getName());
-			req.setRemark(exam.getRemark());
-			req.setRootOrgId(exam.getRootOrgId());
-
-			GetOrgReq getOrgReq = new GetOrgReq();
-			getOrgReq.setOrgId(exam.getRootOrgId());
-			GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
-			OrgBean rootOrg = getOrgResp.getOrg();
-			req.setRootOrgName(rootOrg.getName());
-
-			req.setSyncType("update");
-			dataSyncCloudService.syncExam(req);
-		}
-
-		for (Long examId : examIds) {
-			examSettingsCache.remove(examId);
-		}
-
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param curPage
-	 * @param pageSize
-	 * @param examOrgDomain
-	 * @return
-	 */
-	@ApiOperation(value = "查询考试相关的学习中心设置", notes = "")
-	@GetMapping("getExamOrgSettingsList/{curPage}/{pageSize}")
-	public PageInfo<ExamOrgSettingsDomain> getExamOrgSettingsList(@PathVariable Integer curPage,
-			@PathVariable Integer pageSize, @ModelAttribute ExamOrgSettingsDomain examOrgDomain) {
-
-		Long examId = examOrgDomain.getExamId();
-		if (null == examOrgDomain.getExamId()) {
-			throw new StatusException("001210", "examId is null");
-		}
-		ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		if (null == examEntity) {
-			throw new StatusException("001250", "examId is wrong");
-		}
-		validateRootOrgIsolation(examEntity.getRootOrgId());
-
-		Specification<ExamSpecialSettingsEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-			predicates.add(cb.equal(root.get("examId"), examOrgDomain.getExamId()));
-			predicates.add(cb.isNull(root.get("courseId")));
-			predicates.add(cb.isNull(root.get("studentId")));
-
-			if (null != examOrgDomain.getOrgId()) {
-				predicates.add(cb.equal(root.get("orgId"), examOrgDomain.getOrgId()));
-			} else {
-				predicates.add(cb.isNotNull(root.get("orgId")));
-			}
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		Pageable pageable = PageRequest.of(curPage, pageSize, Sort.Direction.DESC, "updateTime",
-				"id");
-		Page<ExamSpecialSettingsEntity> page = examSpecialSettingsRepo.findAll(specification,
-				pageable);
-
-		Iterator<ExamSpecialSettingsEntity> iterator = page.iterator();
-		List<ExamOrgSettingsDomain> domainList = Lists.newArrayList();
-
-		while (iterator.hasNext()) {
-			ExamSpecialSettingsEntity next = iterator.next();
-			ExamOrgSettingsDomain bean = new ExamOrgSettingsDomain();
-			domainList.add(bean);
-
-			bean.setBeginTime(next.getBeginTime());
-			bean.setEndTime(next.getEndTime());
-			bean.setExamId(next.getExamId());
-			bean.setId(next.getId());
-			bean.setOrgId(next.getOrgId());
-			bean.setRootOrgId(next.getRootOrgId());
-			bean.setUpdateTime(next.getUpdateTime());
-			bean.setExamLimit(next.getExamLimit());
-
-			GetOrgReq getOrgReq = new GetOrgReq();
-			getOrgReq.setOrgId(bean.getOrgId());
-			GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
-			OrgBean org = getOrgResp.getOrg();
-			bean.setOrgName(org.getName());
-			bean.setOrgCode(org.getCode());
-
-			List<ExamOrgPropertyEntity> propList = examOrgPropertyRepo
-					.findByExamIdAndOrgId(next.getExamId(), next.getOrgId());
-
-			Map<String, String> map = Maps.newHashMap();
-			DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-			for (ExamOrgPropertyEntity cur : propList) {
-				DynamicEnum de = manager.getById(cur.getKeyId());
-				map.put(de.getName(), cur.getValue());
-			}
-
-			bean.setProperties(map);
-		}
-
-		PageInfo<ExamOrgSettingsDomain> ret = new PageInfo<ExamOrgSettingsDomain>();
-		ret.setList(domainList);
-		ret.setTotal(page.getTotalElements());
-		return ret;
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param examOrg
-	 * @return
-	 */
-	@ApiOperation(value = "新增考试相关的学习中心设置", notes = "")
-	@PostMapping("examOrgSettings")
-	@Transactional
-	public ExamSpecialSettingsEntity addExamOrgSettings(@RequestBody ExamOrgSettingsDomain domain) {
-		return saveExamOrgSettings(domain);
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param examOrg
-	 * @return
-	 */
-	@ApiOperation(value = "更新考试相关的学习中心设置", notes = "")
-	@PutMapping("examOrgSettings")
-	@Transactional
-	public ExamSpecialSettingsEntity updateExamOrgSettings(
-			@RequestBody ExamOrgSettingsDomain domain) {
-		return saveExamOrgSettings(domain);
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param domain
-	 * @return
-	 */
-	private ExamSpecialSettingsEntity saveExamOrgSettings(ExamOrgSettingsDomain domain) {
-		Long examId = domain.getExamId();
-
-		ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		if (null == examEntity) {
-			throw new StatusException("001250", "examId is wrong");
-		}
-		validateRootOrgIsolation(examEntity.getRootOrgId());
-
-		Long orgId = domain.getOrgId();
-
-		GetOrgReq getOrgReq = new GetOrgReq();
-		getOrgReq.setOrgId(orgId);
-		GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
-		OrgBean org = getOrgResp.getOrg();
-		org.getRootId();
-		validateRootOrgIsolation(org.getRootId());
-
-		User accessUser = getAccessUser();
-		ExamSpecialSettingsInfo info = new ExamSpecialSettingsInfo();
-		info.setId(domain.getId());
-		info.setBeginTime(domain.getBeginTime());
-		info.setEndTime(domain.getEndTime());
-		info.setExamId(domain.getExamId());
-		info.setOrgId(domain.getOrgId());
-		info.setExamLimit(domain.getExamLimit());
-		info.setRootOrgId(accessUser.getRootOrgId());
-
-		Map<String, String> properties = domain.getProperties();
-		if (null == properties) {
-			properties = Maps.newHashMap();
-		}
-		info.setProperties(domain.getProperties());
-
-		ExamSpecialSettingsEntity ret = examService.saveExamSpecialSettings(info);
-		return ret;
-	}
-
-	@ApiOperation(value = "下载学习中心特殊设置", notes = "")
-	@GetMapping("exportExamOrgSettings/{examId}")
-	public void exportExamOrgSettings(@PathVariable Long examId, HttpServletResponse response) {
-		User accessUser = getAccessUser();
-		Long rootOrgId = accessUser.getRootOrgId();
-
-		Map<Long, OrgBean> orgMap = Maps.newHashMap();
-		GetOrgsReq getOrgsReq = new GetOrgsReq();
-		getOrgsReq.setRootOrgId(rootOrgId);
-		Long start = null;
-		while (true) {
-			getOrgsReq.setStart(start);
-			GetOrgsResp getOrgsResp = orgCloudService.getOrgs(getOrgsReq);
-			Long next = getOrgsResp.getNext();
-			List<OrgBean> orgBeanList = getOrgsResp.getOrgBeanList();
-
-			if (next.equals(start)) {
-				break;
-			} else {
-				start = next;
-			}
-			for (OrgBean cur : orgBeanList) {
-				if (null == cur.getParentId()) {
-					continue;
-				}
-				orgMap.put(cur.getId(), cur);
-			}
-		}
-
-		List<Object[]> datas = Lists.newArrayList();
-
-		List<ExamSpecialSettingsEntity> orgSettingsList = examSpecialSettingsRepo
-				.findAllByExamIdAndCourseIdIsNullAndStudentIdIsNullAndOrgIdIsNotNull(examId);
-		Set<Long> orgIdSet = Sets.newHashSet();
-		for (ExamSpecialSettingsEntity cur : orgSettingsList) {
-			orgIdSet.add(cur.getOrgId());
-			OrgBean orgBean = orgMap.get(cur.getOrgId());
-			String examLimit = null == cur.getExamLimit() ? "否" : cur.getExamLimit() ? "否" : "是";
-			String beginTime = null == cur.getBeginTime()
-					? null
-					: DateUtil.format(cur.getBeginTime(), DatePatterns.CHINA_DEFAULT);
-			String endTime = null == cur.getEndTime()
-					? null
-					: DateUtil.format(cur.getEndTime(), DatePatterns.CHINA_DEFAULT);
-			datas.add(new Object[]{String.valueOf(orgBean.getId()), orgBean.getCode(),
-					orgBean.getName(), examLimit, beginTime, endTime});
-		}
-
-		for (Entry<Long, OrgBean> entry : orgMap.entrySet()) {
-			Long key = entry.getKey();
-			if (orgIdSet.contains(key)) {
-				continue;
-			}
-			OrgBean orgBean = entry.getValue();
-			if (!orgBean.getEnable()) {
-				continue;
-			}
-
-			datas.add(new Object[]{String.valueOf(orgBean.getId()), orgBean.getCode(),
-					orgBean.getName(), null, null, null});
-		}
-
-		String filePath = systemConfig.getTempDataDir() + File.separator
-				+ System.currentTimeMillis() + ".xlsx";
-		File file = new File(filePath);
-
-		ExcelWriter.write(
-				EXAM_ORG_SETTINGS_EXCEL_HEADER, new Class[]{String.class, String.class,
-						String.class, String.class, String.class, String.class},
-				datas, new File(filePath));
-
-		exportFile("学习中心特殊设置-" + getRootOrgId() + ".xlsx", file);
-
-		FileUtils.deleteQuietly(file);
-	}
-
-	@ApiOperation(value = "导入学习中心设置", notes = "导入")
-	@PostMapping("importExamOrgSettings/{examId}")
-	@Transactional
-	public Map<String, Object> importExamOrgSettings(@PathVariable Long examId,
-			@RequestParam CommonsMultipartFile file) {
-
-		ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		if (null == exam) {
-			throw new StatusException("001010", "考试不存在");
-		}
-
-		validateRootOrgIsolation(exam.getRootOrgId());
-
-		DiskFileItem item = (DiskFileItem) file.getFileItem();
-		File storeLocation = item.getStoreLocation();
-		List<Map<String, Object>> failRecords = examService.importExamOrgSettings(examId,
-				storeLocation);
-		Map<String, Object> map = Maps.newHashMap();
-		map.put("hasError", CollectionUtils.isNotEmpty(failRecords));
-		map.put("failRecords", failRecords);
-		return map;
-	}
-
-	@ApiOperation(value = "学习中心禁考", notes = "")
-	@PutMapping("setOrgExamLimited/{ids}")
-	@Transactional
-	public List<String> setOrgExamLimited(@PathVariable String ids) {
-		List<Long> orgSettingsIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
-				.collect(Collectors.toList());
-		List<String> ret = Lists.newArrayList();
-		for (Long cur : orgSettingsIds) {
-			ExamSpecialSettingsEntity entity = GlobalHelper.getEntity(examSpecialSettingsRepo, cur,
-					ExamSpecialSettingsEntity.class);
-			entity.setExamLimit(true);
-			examSpecialSettingsRepo.save(entity);
-			ret.add(entity.getExamId() + ":" + entity.getOrgId());
-		}
-		return ret;
-	}
-
-	@ApiOperation(value = "学习中心开考", notes = "")
-	@PutMapping("setOrgExamNotLimited/{ids}")
-	@Transactional
-	public List<String> setOrgExamNotLimited(@PathVariable String ids) {
-		List<Long> orgSettingsIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
-				.collect(Collectors.toList());
-		List<String> ret = Lists.newArrayList();
-		for (Long cur : orgSettingsIds) {
-			ExamSpecialSettingsEntity entity = GlobalHelper.getEntity(examSpecialSettingsRepo, cur,
-					ExamSpecialSettingsEntity.class);
-			entity.setExamLimit(false);
-			examSpecialSettingsRepo.save(entity);
-			ret.add(entity.getExamId() + ":" + entity.getOrgId());
-		}
-		return ret;
-	}
-
-	@ApiOperation(value = "删除学习中心特殊设置", notes = "")
-	@PutMapping("deleteExamOrgSettings/{ids}")
-	@Transactional
-	public List<String> deleteExamOrgSettings(@PathVariable String ids) {
-		List<Long> orgSettingsIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
-				.collect(Collectors.toList());
-		List<String> ret = Lists.newArrayList();
-		for (Long cur : orgSettingsIds) {
-			ExamSpecialSettingsEntity entity = GlobalHelper.getEntity(examSpecialSettingsRepo, cur,
-					ExamSpecialSettingsEntity.class);
-
-			examOrgPropertyRepo.deleteByExamIdAndOrgId(entity.getExamId(), entity.getOrgId());
-
-			examSpecialSettingsRepo.delete(entity);
-			ret.add(entity.getExamId() + ":" + entity.getOrgId());
-		}
-		return ret;
-	}
-
-	@ApiOperation(value = "删除所有学习中心特殊设置", notes = "")
-	@PutMapping("deleteAllExamOrgSettings/{examId}")
-	@Transactional
-	public void deleteAllExamOrgSettings(@PathVariable Long examId) {
-
-		ExamEntity exam = GlobalHelper.getPresentEntity(examRepo, examId, ExamEntity.class);
-
-		validateRootOrgIsolation(exam.getRootOrgId());
-
-		examOrgPropertyRepo.deleteByExamId(examId);
-
-		examSpecialSettingsRepo
-				.deleteByExamIdAndOrgIdIsNotNullAndCourseIdIsNullAndStudentIdIsNull(examId);
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param request
-	 * @param examId
-	 * @return
-	 */
-	@ApiOperation(value = "考试IP限制", notes = "")
-	@GetMapping("ipLimit/{examId}")
-	public Map<String, Object> ipLimit(HttpServletRequest request, @PathVariable Long examId) {
-		User accessUser = getAccessUser();
-		StudentCacheBean studentCache = CacheHelper.getStudent(accessUser.getUserId());
-		Long orgId = studentCache.getOrgId();
-		ExamPropertyCacheBean ipLimitProperty = CacheHelper.getExamProperty(examId, "IP_LIMIT");
-
-		Map<String, Object> map = Maps.newHashMap();
-
-		Boolean ipLimit = null;
-		if (null != ipLimitProperty) {
-			ipLimit = StringUtil.isTrue(ipLimitProperty.getValue());
-		}
-
-		if (null == ipLimit || !ipLimit) {
-			map.put("limited", false);
-			map.put("desc", "未配置IP限制");
-			return map;
-		}
-
-		String realIp = request.getHeader("x-forwarded-for");
-		if (StringUtils.isBlank(realIp)) {
-			realIp = request.getHeader("x-real-ip");
-		}
-		if (StringUtils.isBlank(realIp)) {
-			map.put("limited", true);
-			map.put("desc", "网络受限");
-			return map;
-		}
-		realIp = realIp.trim();
-
-		ExamPropertyCacheBean ipAddressesProperty = CacheHelper.getExamProperty(examId,
-				"IP_ADDRESSES");
-
-		String ipAddresses = null;
-		if (null != ipAddressesProperty) {
-			ipAddresses = ipAddressesProperty.getValue();
-		}
-
-		boolean limited = true;
-		if (StringUtils.isNotBlank(ipAddresses)) {
-			String[] arr = StringUtils.split(ipAddresses, ';');
-
-			for (String cur : arr) {
-				String ip = StringUtils.replace(cur.trim(), ".", "\\.");
-				ip = StringUtils.replace(ip, "*", "\\w+");
-				if (realIp.matches(ip)) {
-					limited = false;
-					map.put("desc", "IP段配置放行");
-					break;
-				}
-			}
-		}
-
-		if (limited) {
-			String key = "IP_" + orgId;
-			String value = redisClient.get(key, String.class);
-			if (null == value) {
-				map.put("desc", "无机构管理员登录");
-			} else {
-				@SuppressWarnings("unchecked")
-				Set<String> userKeyList = JsonUtil.fromJson(value, Set.class);
-
-				for (String userKey : userKeyList) {
-					User curUser = redisClient.get(userKey, User.class);
-					if (null != curUser) {
-						String clientIp = curUser.getClientIp();
-						if (null != clientIp) {
-							// IP取前三段
-							clientIp = clientIp.substring(0, clientIp.lastIndexOf(".") + 1);
-							if (realIp.startsWith(clientIp)) {
-								limited = false;
-								map.put("desc", "机构管理员[key=" + userKey + "]登录放行");
-								break;
-							}
-						}
-					}
-
-				}
-			}
-		}
-
-		map.put("limited", limited);
-
-		return map;
-	}
-
-	@ApiOperation(value = "是否可以微信作答", notes = "")
-	@GetMapping("weixinAnswerEnabled/{examId}")
-	public Boolean weixinAnswerEnabled(@PathVariable Long examId) {
-
-		ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
-
-		OrgPropertyCacheBean orgConf = CacheHelper.getOrgProperty(examSettings.getRootOrgId(),
-				"WEIXIN_ANSWER_ENABLED");
-		ExamPropertyCacheBean examConf = CacheHelper.getExamProperty(examId,
-				"WEIXIN_ANSWER_ENABLED");
-
-		String orgValue = orgConf.getValue();
-		String examValue = examConf.getValue();
-
-		if (!orgConf.getHasValue()) {
-			return false;
-		}
-		if (StringUtils.isBlank(orgValue)) {
-			return false;
-		}
-
-		if (StringUtils.isBlank(examValue)) {
-			return false;
-		}
-
-		if (StringUtil.isTrue(orgValue.toString()) && StringUtil.isTrue(examValue)) {
-			return true;
-		}
-
-		return false;
-	}
-
-	@ApiOperation(value = "是否支持人脸识别", notes = "")
-	@GetMapping("faceCheckEnabled/{examId}")
-	public Boolean faceCheckEnabled(@PathVariable Long examId) {
-		ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
-
-		return faceCheckEnabled(examSettings.getRootOrgId(), examId);
-	}
-
-	/**
-	 * 判断是否有人脸识别权限
-	 *
-	 * @author WANGWEI
-	 * @param rootOrgId
-	 * @param examId
-	 * @return
-	 */
-	private Boolean faceCheckEnabled(Long rootOrgId, Long examId) {
-		String faceCheck = PrivilegeDefine.RootOrgFunctions.OnlineExamFunctions.FaceCheck.CODE;
-		Boolean hasFaceCheckFunction = PrivilegeManager.judge(rootOrgId, faceCheck);
-
-		ExamPropertyCacheBean examConf = CacheHelper.getExamProperty(examId, "IS_FACE_ENABLE");
-		String examValue = examConf.getValue();
-
-		if (!hasFaceCheckFunction) {
-			return false;
-		}
-
-		if (StringUtils.isBlank(examValue)) {
-			return false;
-		}
-
-		if (StringUtil.isTrue(examValue)) {
-			return true;
-		}
-
-		return false;
-	}
-
-	@ApiOperation(value = "是否支持活体检测", notes = "")
-	@GetMapping("identificationOfLivingEnabled/{examId}")
-	public Boolean identificationOfLivingEnabled(@PathVariable Long examId) {
-		ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
-
-		Boolean faceCheckEnabled = faceCheckEnabled(examSettings.getRootOrgId(), examId);
-		if (!faceCheckEnabled) {
-			return false;
-		}
-
-		String IdentificationOfLivingBody = PrivilegeDefine.RootOrgFunctions.OnlineExamFunctions.IdentificationOfLivingBody.CODE;
-		Boolean hasIdentificationOfLivingBodyFunction = PrivilegeManager
-				.judge(examSettings.getRootOrgId(), IdentificationOfLivingBody);
-
-		ExamPropertyCacheBean examConf = CacheHelper.getExamProperty(examId, "IS_FACE_VERIFY");
-		String examValue = examConf.getValue();
-
-		if (!hasIdentificationOfLivingBodyFunction) {
-			return false;
-		}
-
-		if (StringUtils.isBlank(examValue)) {
-			return false;
-		}
-
-		if (StringUtil.isTrue(examValue)) {
-			return true;
-		}
-
-		return false;
-	}
-
-	@ApiOperation(value = "查询考试相关的学生设置", notes = "")
-	@GetMapping("getStudentSpecialSettingsList/{curPage}/{pageSize}")
-	public PageInfo<StudentSpecialSettingsDomain> getStudentSpecialSettingsList(
-			@PathVariable Integer curPage, @PathVariable Integer pageSize,
-			@ModelAttribute StudentSpecialSettingsDomain domain) {
-
-		Long examId = domain.getExamId();
-		if (null == domain.getExamId()) {
-			throw new StatusException("001210", "examId is null");
-		}
-		ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		if (null == examEntity) {
-			throw new StatusException("001250", "examId is wrong");
-		}
-		validateRootOrgIsolation(examEntity.getRootOrgId());
-
-		Specification<ExamSpecialSettingsEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-			predicates.add(cb.equal(root.get("examId"), domain.getExamId()));
-			predicates.add(cb.isNull(root.get("courseId")));
-			predicates.add(cb.isNull(root.get("orgId")));
-
-			if (null != domain.getStudentId()) {
-				predicates.add(cb.equal(root.get("studentId"), domain.getStudentId()));
-			} else {
-				predicates.add(cb.isNotNull(root.get("studentId")));
-			}
-
-			if (null != domain.getIdentityNumber()) {
-				predicates.add(cb.equal(root.get("ext1"), domain.getIdentityNumber()));
-			} else {
-				predicates.add(cb.isNotNull(root.get("ext1")));
-			}
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		Pageable pageable = PageRequest.of(curPage, pageSize, Sort.Direction.DESC, "updateTime",
-				"id");
-		Page<ExamSpecialSettingsEntity> page = examSpecialSettingsRepo.findAll(specification,
-				pageable);
-
-		Iterator<ExamSpecialSettingsEntity> iterator = page.iterator();
-		List<StudentSpecialSettingsDomain> domainList = Lists.newArrayList();
-
-		while (iterator.hasNext()) {
-			ExamSpecialSettingsEntity next = iterator.next();
-			StudentSpecialSettingsDomain bean = new StudentSpecialSettingsDomain();
-			domainList.add(bean);
-
-			bean.setBeginTime(next.getBeginTime());
-			bean.setEndTime(next.getEndTime());
-			bean.setExamId(next.getExamId());
-			bean.setId(next.getId());
-			bean.setRootOrgId(next.getRootOrgId());
-			bean.setUpdateTime(next.getUpdateTime());
-			bean.setExamLimit(next.getExamLimit());
-			bean.setStudentId(next.getStudentId());
-			bean.setIdentityNumber(next.getExt1());
-		}
-
-		PageInfo<StudentSpecialSettingsDomain> ret = new PageInfo<StudentSpecialSettingsDomain>();
-		ret.setList(domainList);
-		ret.setTotal(page.getTotalElements());
-		return ret;
-	}
+        Specification<ExamCourseRelationEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+
+            predicates.add(cb.equal(root.get("examId"), examId));
+
+            Predicate pr1 = cb.like(root.get("courseName"), toSqlSearchPattern(name));
+            Predicate pr2 = cb.like(root.get("courseCode"), toSqlSearchPattern(name));
+
+            predicates.add(cb.or(pr1, pr2));
+
+            if (StringUtils.isNotBlank(level)) {
+                predicates.add(cb.equal(root.get("level"), toSqlSearchPattern(level)));
+            }
+
+            if (null != enable) {
+                predicates.add(cb.equal(root.get("courseEnable"), enable));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        PageRequest pageRequest = PageRequest.of(0, 50, new Sort(Direction.DESC, "updateTime"));
+
+        Page<ExamCourseRelationEntity> page = examCourseRelationRepo.findAll(specification,
+                pageRequest);
+
+        Iterator<ExamCourseRelationEntity> iterator = page.iterator();
+        List<ExamCourseRelationEntity> list = Lists.newArrayList();
+
+        while (iterator.hasNext()) {
+            ExamCourseRelationEntity next = iterator.next();
+            list.add(next);
+        }
+
+        return list;
+
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param curPage
+     * @param pageSize
+     * @param name
+     * @param examType
+     * @param enable
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "分页查询考试批次")
+    @GetMapping("queryPage/{curPage}/{pageSize}")
+    public PageInfo<ExamDomain> queryPage(@PathVariable Integer curPage,
+                                          @PathVariable Integer pageSize, @RequestParam(required = false) String name,
+                                          @RequestParam(required = false) String examType,
+                                          @RequestParam(required = false) Boolean enable,
+                                          @RequestParam(required = false) String propertyKeys) {
+
+        User accessUser = getAccessUser();
+
+        Specification<ExamEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("rootOrgId"), accessUser.getRootOrgId()));
+            if (StringUtils.isNotBlank(name)) {
+                predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+            }
+            if (null != enable) {
+                predicates.add(cb.equal(root.get("enable"), enable));
+            }
+            if (StringUtils.isNotBlank(examType)) {
+                predicates.add(cb.equal(root.get("examType"), ExamType.valueOf(examType)));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        PageRequest pageRequest = PageRequest.of(curPage, pageSize,
+                new Sort(Direction.DESC, "updateTime", "id"));
+
+        Page<ExamEntity> page = examRepo.findAll(specification, pageRequest);
+
+        Iterator<ExamEntity> iterator = page.iterator();
+        List<ExamDomain> list = Lists.newArrayList();
+
+        List<String> propertyKeyList = null;
+        if (StringUtils.isNotBlank(propertyKeys)) {
+            propertyKeyList = RegExpUtil.findAll(propertyKeys, "\\w+");
+        }
+
+        while (iterator.hasNext()) {
+            ExamEntity next = iterator.next();
+            ExamDomain bean = new ExamDomain();
+            list.add(bean);
+
+            bean.setId(next.getId());
+            bean.setCode(next.getCode());
+            bean.setName(next.getName());
+            bean.setEnable(next.getEnable());
+            bean.setRootOrgId(next.getRootOrgId());
+            bean.setBeginTime(next.getBeginTime());
+            bean.setEndTime(next.getEndTime());
+            bean.setExamType(next.getExamType());
+            bean.setDuration(next.getDuration());
+            bean.setEnable(next.getEnable());
+            bean.setRemark(next.getRemark());
+            bean.setExamTimes(next.getExamTimes());
+            bean.setCreationTime(next.getCreationTime());
+            bean.setUpdateTime(next.getUpdateTime());
+            bean.setExamLimit(next.getExamLimit());
+            bean.setSpecialSettingsEnabled(next.getSpecialSettingsEnabled());
+            bean.setSpecialSettingsType(next.getSpecialSettingsType());
+
+            if (CollectionUtils.isNotEmpty(propertyKeyList)) {
+                Map<String, String> properties = getProperties(bean.getId(), propertyKeyList);
+                bean.setProperties(properties);
+            }
+        }
+
+        PageInfo<ExamDomain> ret = new PageInfo<ExamDomain>(page, list);
+        return ret;
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param name
+     * @param examTypes
+     * @param enable
+     * @param studentId 筛选学生关联的考试
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "查询考试批次")
+    @GetMapping("queryByNameLike")
+    public List<ExamDomain> query(@RequestParam(required = true) String name,
+                                  @RequestParam(required = false) String examTypes,
+                                  @RequestParam(required = false) Boolean enable,
+                                  @RequestParam(required = false) String propertyKeys,
+                                  @RequestParam(required = false) Long studentId,
+                                  @RequestParam(required = false) Long rootOrgId) {
+
+        if (null == rootOrgId) {
+            rootOrgId = getRootOrgId();
+        }
+        final Long finalRootOrgId = rootOrgId;
+
+        Specification<ExamEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
+            if (StringUtils.isNotBlank(name)) {
+                predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+            }
+            if (null != enable) {
+                predicates.add(cb.equal(root.get("enable"), enable));
+            }
+            if (StringUtils.isNotBlank(examTypes)) {
+                List<String> examTypeList = RegExpUtil.findAll(examTypes, "\\w+");
+
+                List<ExamType> etList = Lists.newArrayList();
+                for (String cur : examTypeList) {
+                    etList.add(ExamType.valueOf(cur));
+                }
+
+                if (CollectionUtils.isNotEmpty(etList)) {
+                    if (10 < examTypeList.size()) {
+                        throw new StatusException("001120", "too many examTypes");
+                    }
+                    if (1 == examTypeList.size()) {
+                        predicates.add(cb.equal(root.get("examType"), etList.get(0)));
+                    } else {
+                        predicates.add(root.get("examType").in(etList));
+                    }
+                }
+            }
+
+            if (null != studentId) {
+                Subquery<ExamStudentEntity> subquery = query.subquery(ExamStudentEntity.class);
+                Root<ExamStudentEntity> subRoot = subquery.from(ExamStudentEntity.class);
+                subquery.select(subRoot.get("id"));
+                Predicate p1 = cb.equal(subRoot.get("studentId"), studentId);
+                Predicate p2 = cb.equal(subRoot.get("examId"), root.get("id"));
+                subquery.where(cb.and(p1, p2));
+                predicates.add(cb.exists(subquery));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        PageRequest pageRequest = PageRequest.of(0, 100,
+                new Sort(Direction.DESC, "updateTime", "id"));
+        Page<ExamEntity> page = examRepo.findAll(specification, pageRequest);
+
+        Iterator<ExamEntity> iterator = page.iterator();
+        List<ExamDomain> list = Lists.newArrayList();
+
+        List<String> propertyKeyList = null;
+        if (StringUtils.isNotBlank(propertyKeys)) {
+            propertyKeyList = RegExpUtil.findAll(propertyKeys, "\\w+");
+        }
+
+        while (iterator.hasNext()) {
+            ExamEntity next = iterator.next();
+            ExamDomain bean = new ExamDomain();
+            list.add(bean);
+
+            bean.setId(next.getId());
+            bean.setName(next.getName());
+            bean.setEnable(next.getEnable());
+            bean.setRootOrgId(next.getRootOrgId());
+            bean.setBeginTime(next.getBeginTime());
+            bean.setEndTime(next.getEndTime());
+            bean.setExamType(next.getExamType());
+            bean.setSpecialSettingsEnabled(next.getSpecialSettingsEnabled());
+            bean.setSpecialSettingsType(next.getSpecialSettingsType());
+
+            if (CollectionUtils.isNotEmpty(propertyKeyList)) {
+                Map<String, String> properties = getProperties(bean.getId(), propertyKeyList);
+                bean.setProperties(properties);
+            }
+        }
+
+        return list;
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param examId
+     * @param propertyKeys
+     * @return
+     * @author WANGWEI
+     */
+    private Map<String, String> getProperties(Long examId, List<String> propertyKeys) {
+        Map<String, String> map = Maps.newHashMap();
+        DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+
+        for (String key : propertyKeys) {
+            DynamicEnum de = manager.getByName(key);
+            ExamPropertyEntity one = examPropertyRepo.findByExamIdAndKeyId(examId, de.getId());
+            if (null != one) {
+                map.put(key, one.getValue());
+            }
+        }
+
+        return map;
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param examId
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "按ID查询考试批次", notes = "ID查询")
+    @GetMapping("{examId}")
+    public ExamDomain getExamById(@PathVariable Long examId) {
+
+        ExamSettingsCacheBean cache = examSettingsCache.get(examId);
+        validateRootOrgIsolation(cache.getRootOrgId());
+
+        ExamDomain domain = new ExamDomain();
+        domain.setBeginTime(cache.getBeginTime());
+        domain.setDuration(cache.getDuration());
+        domain.setEnable(cache.getEnable());
+        domain.setEndTime(cache.getEndTime());
+        domain.setExamTimes(cache.getExamTimes());
+        domain.setExamType(ExamType.valueOf(cache.getExamType()));
+        domain.setId(cache.getId());
+        domain.setName(cache.getName());
+        domain.setCode(cache.getCode());
+        domain.setRemark(cache.getRemark());
+        domain.setRootOrgId(cache.getRootOrgId());
+        domain.setStarted(isStarted(cache.getId()));
+        domain.setExamLimit(cache.getExamLimit());
+        domain.setSpecialSettingsEnabled(cache.getSpecialSettingsEnabled());
+        domain.setSpecialSettingsType(cache.getSpecialSettingsType());
+
+        return domain;
+    }
+
+    /**
+     * 是否开考
+     *
+     * @param examId
+     * @return
+     * @author WANGWEI
+     */
+    private boolean isStarted(Long examId) {
+        CheckExamIsStartedReq checkExamIsStartedReq = new CheckExamIsStartedReq();
+        checkExamIsStartedReq.setExamId(examId);
+        try {
+            CheckExamIsStartedResp checkExamIsStartedResp = examRecordCloudService
+                    .checkExamIsStarted(checkExamIsStartedReq);
+            return checkExamIsStartedResp.getIsStarted();
+        } catch (Exception e) {
+            log.error("fail to rmi[oe.examRecordCloudService.checkExamIsStarted]", e);
+            return true;
+        }
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param domain
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "新增考试批次", notes = "新增")
+    @PostMapping()
+    @Transactional
+    public ExamEntity addExam(@RequestBody ExamDomain domain) {
+        return saveExam(domain, CURD.CREATION);
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param domain
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "更新考试批次", notes = "更新")
+    @PutMapping()
+    @Transactional
+    public ExamEntity updateExam(@RequestBody ExamDomain domain) {
+        return saveExam(domain, CURD.UPDATE);
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param domain
+     * @return
+     * @author WANGWEI
+     */
+    private ExamEntity saveExam(ExamDomain domain, CURD es) {
+        trim(domain, false);
+
+        User accessUser = getAccessUser();
+
+        ExamInfo examInfo = new ExamInfo();
+        examInfo.setBeginTime(domain.getBeginTime());
+        examInfo.setDuration(domain.getDuration());
+        examInfo.setEnable(domain.getEnable());
+        examInfo.setEndTime(domain.getEndTime());
+        examInfo.setExamTimes(domain.getExamTimes());
+        examInfo.setExamType(domain.getExamType());
+        examInfo.setCode(domain.getCode());
+        examInfo.setName(domain.getName());
+        examInfo.setRemark(domain.getRemark());
+        examInfo.setRootOrgId(accessUser.getRootOrgId());
+        examInfo.setExamLimit(domain.getExamLimit());
+        examInfo.setSpecialSettingsEnabled(domain.getSpecialSettingsEnabled());
+        examInfo.setSpecialSettingsType(domain.getSpecialSettingsType());
+
+        Map<String, String> properties = domain.getProperties();
+        if (null == properties) {
+            properties = Maps.newHashMap();
+        }
+        examInfo.setProperties(properties);
+
+        ExamEntity saved = examService.saveExam(examInfo, es);
+
+        examSettingsCache.remove(saved.getId());
+
+        return saved;
+    }
+
+    @ApiOperation(value = "复制考试批次", notes = "")
+    @PostMapping("copyExam")
+    @Transactional
+    public ExamEntity copyExam(@RequestBody CopyExamDomain domain) {
+
+        Long srcExamId = domain.getSrcExamId();
+
+        ExamEntity srcExam = GlobalHelper.getEntity(examRepo, srcExamId, ExamEntity.class);
+        if (null == srcExam) {
+            throw new StatusException("001259", "examId is wrong");
+        }
+
+        validateRootOrgIsolation(srcExam.getRootOrgId());
+
+        ExamDomain ed = new ExamDomain();
+        ed.setBeginTime(srcExam.getBeginTime());
+        ed.setDuration(srcExam.getDuration());
+        ed.setEnable(srcExam.getEnable());
+        ed.setEndTime(srcExam.getEndTime());
+        ed.setExamTimes(srcExam.getExamTimes());
+        ed.setExamType(srcExam.getExamType());
+        ed.setRemark(srcExam.getRemark());
+        ed.setRootOrgId(srcExam.getRootOrgId());
+        ed.setStarted(isStarted(srcExam.getId()));
+        ed.setExamLimit(srcExam.getExamLimit());
+        ed.setSpecialSettingsEnabled(srcExam.getSpecialSettingsEnabled());
+        ed.setSpecialSettingsType(srcExam.getSpecialSettingsType());
+
+        ed.setName(domain.getDestExamName());
+        ed.setCode(domain.getDestExamCode());
+
+        Map<String, String> map = Maps.newHashMap();
+        List<ExamPropertyEntity> list = examPropertyRepo.findByExamId(srcExam.getId());
+        DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+
+        List<String> excludedProps = Util.buildList("MARKING_TASK_BUILDED");
+        for (ExamPropertyEntity cur : list) {
+            DynamicEnum de = manager.getById(cur.getKeyId());
+            if (excludedProps.contains(de.getName())) {
+                continue;
+            }
+            map.put(de.getName(), cur.getValue());
+        }
+
+        ed.setProperties(map);
+
+        ExamEntity savedExam = saveExam(ed, CURD.CREATION);
+        return savedExam;
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param examId
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "查询考试批次所有属性")
+    @GetMapping("allProperties/{examId}")
+    public Map<String, String> getAllExamProperties(@PathVariable Long examId) {
+
+        ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        if (null == examEntity) {
+            throw new StatusException("001250", "examId is wrong");
+        }
+        validateRootOrgIsolation(examEntity.getRootOrgId());
+
+        Map<String, String> map = Maps.newHashMap();
+        List<ExamPropertyEntity> list = examPropertyRepo.findByExamId(examId);
+        DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+        for (ExamPropertyEntity cur : list) {
+            DynamicEnum de = manager.getById(cur.getKeyId());
+            map.put(de.getName(), cur.getValue());
+        }
+
+        return map;
+    }
+
+    @ApiOperation(value = "查询考生的考试批次配置")
+    @GetMapping("getExamSettingsFromCacheByStudentSession/{examId}")
+    public ExamDomain getExamSettingsFromCacheByStudentSession(@PathVariable Long examId) {
+
+        User accessUser = getAccessUser();
+        if (!accessUser.getUserType().equals(UserType.STUDENT)) {
+            throw new StatusException("001259", "just allowed by student");
+        }
+
+        Long studentId = accessUser.getUserId();
+
+        StudentCacheBean student = CacheHelper.getStudent(studentId);
+        Long orgId = student.getOrgId();
+
+        ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
+
+        Boolean specialSettingsEnabled = examSettings.getSpecialSettingsEnabled();
+        ExamSpecialSettingsType specialSettingsType = examSettings.getSpecialSettingsType();
+
+        ExamDomain domain = new ExamDomain();
+
+        domain.setBeginTime(examSettings.getBeginTime());
+        domain.setDuration(examSettings.getDuration());
+        domain.setEnable(examSettings.getEnable());
+        domain.setEndTime(examSettings.getEndTime());
+        domain.setExamTimes(examSettings.getExamTimes());
+        domain.setExamType(ExamType.valueOf(examSettings.getExamType()));
+        domain.setId(examSettings.getId());
+        domain.setName(examSettings.getName());
+        domain.setCode(examSettings.getCode());
+        domain.setRemark(examSettings.getRemark());
+        domain.setRootOrgId(examSettings.getRootOrgId());
+        domain.setSpecialSettingsEnabled(examSettings.getSpecialSettingsEnabled());
+        domain.setSpecialSettingsType(examSettings.getSpecialSettingsType());
+        domain.setExamLimit(examSettings.getExamLimit());
+        domain.setStarted(examSettings.getBeginTime().before(new Date()));
+
+        if (specialSettingsEnabled && null != specialSettingsType) {
+
+            if (specialSettingsType.equals(ExamSpecialSettingsType.ORG_BASED)) {
+
+                ExamOrgSettingsCacheBean examOrgSettings = CacheHelper.getExamOrgSettings(examId,
+                        orgId);
+
+                if (null != examOrgSettings.getBeginTime()) {
+                    domain.setBeginTime(examOrgSettings.getBeginTime());
+                    domain.setStarted(examOrgSettings.getBeginTime().before(new Date()));
+                }
+                if (null != examOrgSettings.getEndTime()) {
+                    domain.setEndTime(examOrgSettings.getEndTime());
+                }
+                if (null != examOrgSettings.getDuration()) {
+                    domain.setDuration(examOrgSettings.getDuration());
+                }
+                if (null != examOrgSettings.getExamTimes()) {
+                    domain.setExamTimes(examOrgSettings.getExamTimes());
+                }
+                if (null != examOrgSettings.getExamLimit()) {
+                    domain.setExamLimit(examOrgSettings.getExamLimit());
+                }
+
+            } else if (specialSettingsType.equals(ExamSpecialSettingsType.STUDENT_BASED)) {
+
+                ExamStudentSettingsCacheBean examStudentSettings = CacheHelper
+                        .getExamStudentSettings(examId, studentId);
+
+                if (null != examStudentSettings.getBeginTime()) {
+                    domain.setBeginTime(examStudentSettings.getBeginTime());
+                    domain.setStarted(examStudentSettings.getBeginTime().before(new Date()));
+                }
+                if (null != examStudentSettings.getEndTime()) {
+                    domain.setEndTime(examStudentSettings.getEndTime());
+                }
+                if (null != examStudentSettings.getDuration()) {
+                    domain.setDuration(examStudentSettings.getDuration());
+                }
+                if (null != examStudentSettings.getExamTimes()) {
+                    domain.setExamTimes(examStudentSettings.getExamTimes());
+                }
+                if (null != examStudentSettings.getExamLimit()) {
+                    domain.setExamLimit(examStudentSettings.getExamLimit());
+                }
+
+            }
+        }
+
+        return domain;
+    }
+
+    @ApiOperation(value = "查询考生的考试批次属性集")
+    @GetMapping("getExamPropertyFromCacheByStudentSession/{examId}/{keys}")
+    public Map<String, String> getExamPropertyFromCacheByStudentSession(@PathVariable Long examId,
+                                                                        @PathVariable String keys) {
+
+        String[] keyArray = StringUtils.splitByWholeSeparator(keys, ",");
+
+        User accessUser = getAccessUser();
+        if (!accessUser.getUserType().equals(UserType.STUDENT)) {
+            throw new StatusException("001258", "just allowed by student");
+        }
+
+        Long studentId = accessUser.getUserId();
+
+        StudentCacheBean student = CacheHelper.getStudent(studentId);
+        Long orgId = student.getOrgId();
+
+        Map<String, String> map = Maps.newHashMap();
+
+        ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
+
+        Boolean specialSettingsEnabled = examSettings.getSpecialSettingsEnabled();
+        ExamSpecialSettingsType specialSettingsType = examSettings.getSpecialSettingsType();
+
+        for (String key : keyArray) {
+            ExamPropertyCacheBean propCache = CacheHelper.getExamProperty(examId, key);
+            map.put(key, propCache.getValue());
+        }
+
+        if (specialSettingsEnabled || null != specialSettingsType) {
+            if (specialSettingsType.equals(ExamSpecialSettingsType.ORG_BASED)) {
+                for (String key : keyArray) {
+                    ExamOrgPropertyCacheBean propCache = CacheHelper.getExamOrgProperty(examId,
+                            orgId, key);
+                    if (StringUtils.isNotBlank(propCache.getValue())) {
+                        map.put(key, propCache.getValue());
+                    }
+                }
+            } else if (specialSettingsType.equals(ExamSpecialSettingsType.STUDENT_BASED)) {
+                for (String key : keyArray) {
+                    ExamStudentPropertyCacheBean propCache = CacheHelper
+                            .getExamStudentProperty(examId, studentId, key);
+                    if (StringUtils.isNotBlank(propCache.getValue())) {
+                        map.put(key, propCache.getValue());
+                    }
+                }
+            }
+        }
+
+        return map;
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param examId
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "查询考试批次单个属性")
+    @GetMapping("property/{examId}/{key}")
+    public String getExamProperty(@PathVariable Long examId, @PathVariable String key) {
+        ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        if (null == examEntity) {
+            throw new StatusException("001250", "examId is wrong");
+        }
+        validateRootOrgIsolation(examEntity.getRootOrgId());
+        DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+        DynamicEnum de = manager.getByName(key);
+        ExamPropertyEntity one = examPropertyRepo.findByExamIdAndKeyId(examId, de.getId());
+        if (null == one) {
+            return null;
+        }
+        return one.getValue();
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param examId
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "查询考试批次多个属性")
+    @GetMapping("propertys/{examId}/{keys}")
+    public Map<String, String> getExamPropertyList(@PathVariable Long examId,
+                                                   @PathVariable String keys) {
+        String[] keyArray = StringUtils.splitByWholeSeparator(keys, ",");
+        ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        if (null == examEntity) {
+            throw new StatusException("001250", "examId is wrong");
+        }
+        validateRootOrgIsolation(examEntity.getRootOrgId());
+
+        Map<String, String> properties = Maps.newHashMap();
+        for (String key : keyArray) {
+            DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+            DynamicEnum de = manager.getByName(key);
+            ExamPropertyEntity one = examPropertyRepo.findByExamIdAndKeyId(examId, de.getId());
+            if (null != one) {
+                properties.put(key, one.getValue());
+            }
+        }
+        return properties;
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param examId
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "查询考试批次机构属性")
+    @GetMapping("examOrgProperty/{examId}/{orgId}/{key}")
+    public String getExamOrgProperty(@PathVariable Long examId, @PathVariable Long orgId,
+                                     @PathVariable String key) {
+        ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        if (null == examEntity) {
+            throw new StatusException("001250", "examId is wrong");
+        }
+        validateRootOrgIsolation(examEntity.getRootOrgId());
+
+        if (null == orgId) {
+            throw new StatusException("001251", "orgId is null");
+        }
+
+        GetOrgReq getOrgReq = new GetOrgReq();
+        getOrgReq.setOrgId(orgId);
+        GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
+        validateRootOrgIsolation(getOrgResp.getOrg().getRootId());
+
+        return examService.getExamOrgProperty(examId, orgId, key);
+    }
+
+    @ApiOperation(value = "启用考试", notes = "启用考试")
+    @PutMapping("enable/{ids}")
+    @Transactional
+    public void enableExam(@PathVariable String ids) {
+        List<Long> examIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+                .collect(Collectors.toList());
+        for (Long examId : examIds) {
+            ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+            exam.setEnable(true);
+            examRepo.saveAndFlush(exam);
+        }
+
+        for (Long examId : examIds) {
+            ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+            SyncExamReq req = new SyncExamReq();
+            req.setId(exam.getId());
+            req.setBeginTime(exam.getBeginTime());
+            req.setDuration(exam.getDuration());
+            req.setEnable(exam.getEnable());
+            req.setEndTime(exam.getEndTime());
+            req.setExamTimes(exam.getExamTimes());
+            req.setExamType(exam.getExamType().name());
+            req.setName(exam.getName());
+            req.setRemark(exam.getRemark());
+            req.setRootOrgId(exam.getRootOrgId());
+
+            GetOrgReq getOrgReq = new GetOrgReq();
+            getOrgReq.setOrgId(exam.getRootOrgId());
+            GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
+            OrgBean rootOrg = getOrgResp.getOrg();
+            req.setRootOrgName(rootOrg.getName());
+
+            req.setSyncType("update");
+            dataSyncCloudService.syncExam(req);
+        }
+
+        for (Long examId : examIds) {
+            examSettingsCache.remove(examId);
+        }
+
+    }
+
+    @ApiOperation(value = "禁用考试", notes = "禁用考试")
+    @PutMapping("disable/{ids}")
+    @Transactional
+    public void disableExam(@PathVariable String ids) {
+        List<Long> examIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+                .collect(Collectors.toList());
+        for (Long examId : examIds) {
+            ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+            exam.setEnable(false);
+            examRepo.saveAndFlush(exam);
+        }
+
+        for (Long examId : examIds) {
+            ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+            SyncExamReq req = new SyncExamReq();
+            req.setId(exam.getId());
+            req.setBeginTime(exam.getBeginTime());
+            req.setDuration(exam.getDuration());
+            req.setEnable(exam.getEnable());
+            req.setEndTime(exam.getEndTime());
+            req.setExamTimes(exam.getExamTimes());
+            req.setExamType(exam.getExamType().name());
+            req.setName(exam.getName());
+            req.setRemark(exam.getRemark());
+            req.setRootOrgId(exam.getRootOrgId());
+
+            GetOrgReq getOrgReq = new GetOrgReq();
+            getOrgReq.setOrgId(exam.getRootOrgId());
+            GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
+            OrgBean rootOrg = getOrgResp.getOrg();
+            req.setRootOrgName(rootOrg.getName());
+
+            req.setSyncType("update");
+            dataSyncCloudService.syncExam(req);
+        }
+
+        for (Long examId : examIds) {
+            examSettingsCache.remove(examId);
+        }
+
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param curPage
+     * @param pageSize
+     * @param examOrgDomain
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "查询考试相关的学习中心设置", notes = "")
+    @GetMapping("getExamOrgSettingsList/{curPage}/{pageSize}")
+    public PageInfo<ExamOrgSettingsDomain> getExamOrgSettingsList(@PathVariable Integer curPage,
+                                                                  @PathVariable Integer pageSize,
+                                                                  @ModelAttribute ExamOrgSettingsDomain examOrgDomain) {
+
+        Long examId = examOrgDomain.getExamId();
+        if (null == examOrgDomain.getExamId()) {
+            throw new StatusException("001210", "examId is null");
+        }
+        ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        if (null == examEntity) {
+            throw new StatusException("001250", "examId is wrong");
+        }
+        validateRootOrgIsolation(examEntity.getRootOrgId());
+
+        Specification<ExamSpecialSettingsEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("examId"), examOrgDomain.getExamId()));
+            predicates.add(cb.isNull(root.get("courseId")));
+            predicates.add(cb.isNull(root.get("studentId")));
+
+            if (null != examOrgDomain.getOrgId()) {
+                predicates.add(cb.equal(root.get("orgId"), examOrgDomain.getOrgId()));
+            } else {
+                predicates.add(cb.isNotNull(root.get("orgId")));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        Pageable pageable = PageRequest.of(curPage, pageSize, Sort.Direction.DESC, "updateTime",
+                "id");
+        Page<ExamSpecialSettingsEntity> page = examSpecialSettingsRepo.findAll(specification,
+                pageable);
+
+        Iterator<ExamSpecialSettingsEntity> iterator = page.iterator();
+        List<ExamOrgSettingsDomain> domainList = Lists.newArrayList();
+
+        while (iterator.hasNext()) {
+            ExamSpecialSettingsEntity next = iterator.next();
+            ExamOrgSettingsDomain bean = new ExamOrgSettingsDomain();
+            domainList.add(bean);
+
+            bean.setBeginTime(next.getBeginTime());
+            bean.setEndTime(next.getEndTime());
+            bean.setExamId(next.getExamId());
+            bean.setId(next.getId());
+            bean.setOrgId(next.getOrgId());
+            bean.setRootOrgId(next.getRootOrgId());
+            bean.setUpdateTime(next.getUpdateTime());
+            bean.setExamLimit(next.getExamLimit());
+
+            GetOrgReq getOrgReq = new GetOrgReq();
+            getOrgReq.setOrgId(bean.getOrgId());
+            GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
+            OrgBean org = getOrgResp.getOrg();
+            bean.setOrgName(org.getName());
+            bean.setOrgCode(org.getCode());
+
+            List<ExamOrgPropertyEntity> propList = examOrgPropertyRepo
+                    .findByExamIdAndOrgId(next.getExamId(), next.getOrgId());
+
+            Map<String, String> map = Maps.newHashMap();
+            DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+            for (ExamOrgPropertyEntity cur : propList) {
+                DynamicEnum de = manager.getById(cur.getKeyId());
+                map.put(de.getName(), cur.getValue());
+            }
+
+            bean.setProperties(map);
+        }
+
+        PageInfo<ExamOrgSettingsDomain> ret = new PageInfo<ExamOrgSettingsDomain>();
+        ret.setList(domainList);
+        ret.setTotal(page.getTotalElements());
+        return ret;
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param examOrg
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "新增考试相关的学习中心设置", notes = "")
+    @PostMapping("examOrgSettings")
+    @Transactional
+    public ExamSpecialSettingsEntity addExamOrgSettings(@RequestBody ExamOrgSettingsDomain domain) {
+        return saveExamOrgSettings(domain);
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param examOrg
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "更新考试相关的学习中心设置", notes = "")
+    @PutMapping("examOrgSettings")
+    @Transactional
+    public ExamSpecialSettingsEntity updateExamOrgSettings(
+            @RequestBody ExamOrgSettingsDomain domain) {
+        return saveExamOrgSettings(domain);
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param domain
+     * @return
+     * @author WANGWEI
+     */
+    private ExamSpecialSettingsEntity saveExamOrgSettings(ExamOrgSettingsDomain domain) {
+        Long examId = domain.getExamId();
+
+        ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        if (null == examEntity) {
+            throw new StatusException("001250", "examId is wrong");
+        }
+        validateRootOrgIsolation(examEntity.getRootOrgId());
+
+        Long orgId = domain.getOrgId();
+
+        GetOrgReq getOrgReq = new GetOrgReq();
+        getOrgReq.setOrgId(orgId);
+        GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
+        OrgBean org = getOrgResp.getOrg();
+        org.getRootId();
+        validateRootOrgIsolation(org.getRootId());
+
+        User accessUser = getAccessUser();
+        ExamSpecialSettingsInfo info = new ExamSpecialSettingsInfo();
+        info.setId(domain.getId());
+        info.setBeginTime(domain.getBeginTime());
+        info.setEndTime(domain.getEndTime());
+        info.setExamId(domain.getExamId());
+        info.setOrgId(domain.getOrgId());
+        info.setExamLimit(domain.getExamLimit());
+        info.setRootOrgId(accessUser.getRootOrgId());
+
+        Map<String, String> properties = domain.getProperties();
+        if (null == properties) {
+            properties = Maps.newHashMap();
+        }
+        info.setProperties(domain.getProperties());
+
+        ExamSpecialSettingsEntity ret = examService.saveExamSpecialSettings(info);
+        return ret;
+    }
+
+    @ApiOperation(value = "下载学习中心特殊设置", notes = "")
+    @GetMapping("exportExamOrgSettings/{examId}")
+    public void exportExamOrgSettings(@PathVariable Long examId, HttpServletResponse response) {
+        User accessUser = getAccessUser();
+        Long rootOrgId = accessUser.getRootOrgId();
+
+        Map<Long, OrgBean> orgMap = Maps.newHashMap();
+        GetOrgsReq getOrgsReq = new GetOrgsReq();
+        getOrgsReq.setRootOrgId(rootOrgId);
+        Long start = null;
+        while (true) {
+            getOrgsReq.setStart(start);
+            GetOrgsResp getOrgsResp = orgCloudService.getOrgs(getOrgsReq);
+            Long next = getOrgsResp.getNext();
+            List<OrgBean> orgBeanList = getOrgsResp.getOrgBeanList();
+
+            if (next.equals(start)) {
+                break;
+            } else {
+                start = next;
+            }
+            for (OrgBean cur : orgBeanList) {
+                if (null == cur.getParentId()) {
+                    continue;
+                }
+                orgMap.put(cur.getId(), cur);
+            }
+        }
+
+        List<Object[]> datas = Lists.newArrayList();
+
+        List<ExamSpecialSettingsEntity> orgSettingsList = examSpecialSettingsRepo
+                .findAllByExamIdAndCourseIdIsNullAndStudentIdIsNullAndOrgIdIsNotNull(examId);
+        Set<Long> orgIdSet = Sets.newHashSet();
+        for (ExamSpecialSettingsEntity cur : orgSettingsList) {
+            orgIdSet.add(cur.getOrgId());
+            OrgBean orgBean = orgMap.get(cur.getOrgId());
+            String examLimit = null == cur.getExamLimit() ? "否" : cur.getExamLimit() ? "否" : "是";
+            String beginTime = null == cur.getBeginTime()
+                    ? null
+                    : DateUtil.format(cur.getBeginTime(), DatePatterns.CHINA_DEFAULT);
+            String endTime = null == cur.getEndTime()
+                    ? null
+                    : DateUtil.format(cur.getEndTime(), DatePatterns.CHINA_DEFAULT);
+            datas.add(new Object[]{String.valueOf(orgBean.getId()), orgBean.getCode(),
+                    orgBean.getName(), examLimit, beginTime, endTime});
+        }
+
+        for (Entry<Long, OrgBean> entry : orgMap.entrySet()) {
+            Long key = entry.getKey();
+            if (orgIdSet.contains(key)) {
+                continue;
+            }
+            OrgBean orgBean = entry.getValue();
+            if (!orgBean.getEnable()) {
+                continue;
+            }
+
+            datas.add(new Object[]{String.valueOf(orgBean.getId()), orgBean.getCode(),
+                    orgBean.getName(), null, null, null});
+        }
+
+        String filePath = systemConfig.getTempDataDir() + File.separator
+                + System.currentTimeMillis() + ".xlsx";
+        File file = new File(filePath);
+
+        ExcelWriter.write(
+                EXAM_ORG_SETTINGS_EXCEL_HEADER, new Class[]{String.class, String.class,
+                        String.class, String.class, String.class, String.class},
+                datas, new File(filePath));
+
+        exportFile("学习中心特殊设置-" + getRootOrgId() + ".xlsx", file);
+
+        FileUtils.deleteQuietly(file);
+    }
+
+    @ApiOperation(value = "导入学习中心设置", notes = "导入")
+    @PostMapping("importExamOrgSettings/{examId}")
+    @Transactional
+    public Map<String, Object> importExamOrgSettings(@PathVariable Long examId,
+                                                     @RequestParam CommonsMultipartFile file) {
+
+        ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        if (null == exam) {
+            throw new StatusException("001010", "考试不存在");
+        }
+
+        validateRootOrgIsolation(exam.getRootOrgId());
+
+        DiskFileItem item = (DiskFileItem) file.getFileItem();
+        File storeLocation = item.getStoreLocation();
+        List<Map<String, Object>> failRecords = examService.importExamOrgSettings(examId,
+                storeLocation);
+        Map<String, Object> map = Maps.newHashMap();
+        map.put("hasError", CollectionUtils.isNotEmpty(failRecords));
+        map.put("failRecords", failRecords);
+        return map;
+    }
+
+    @ApiOperation(value = "学习中心禁考", notes = "")
+    @PutMapping("setOrgExamLimited/{ids}")
+    @Transactional
+    public List<String> setOrgExamLimited(@PathVariable String ids) {
+        List<Long> orgSettingsIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+                .collect(Collectors.toList());
+        List<String> ret = Lists.newArrayList();
+        for (Long cur : orgSettingsIds) {
+            ExamSpecialSettingsEntity entity = GlobalHelper.getEntity(examSpecialSettingsRepo, cur,
+                    ExamSpecialSettingsEntity.class);
+            entity.setExamLimit(true);
+            examSpecialSettingsRepo.save(entity);
+            ret.add(entity.getExamId() + ":" + entity.getOrgId());
+        }
+        return ret;
+    }
+
+    @ApiOperation(value = "学习中心开考", notes = "")
+    @PutMapping("setOrgExamNotLimited/{ids}")
+    @Transactional
+    public List<String> setOrgExamNotLimited(@PathVariable String ids) {
+        List<Long> orgSettingsIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+                .collect(Collectors.toList());
+        List<String> ret = Lists.newArrayList();
+        for (Long cur : orgSettingsIds) {
+            ExamSpecialSettingsEntity entity = GlobalHelper.getEntity(examSpecialSettingsRepo, cur,
+                    ExamSpecialSettingsEntity.class);
+            entity.setExamLimit(false);
+            examSpecialSettingsRepo.save(entity);
+            ret.add(entity.getExamId() + ":" + entity.getOrgId());
+        }
+        return ret;
+    }
+
+    @ApiOperation(value = "删除学习中心特殊设置", notes = "")
+    @PutMapping("deleteExamOrgSettings/{ids}")
+    @Transactional
+    public List<String> deleteExamOrgSettings(@PathVariable String ids) {
+        List<Long> orgSettingsIds = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+                .collect(Collectors.toList());
+        List<String> ret = Lists.newArrayList();
+        for (Long cur : orgSettingsIds) {
+            ExamSpecialSettingsEntity entity = GlobalHelper.getEntity(examSpecialSettingsRepo, cur,
+                    ExamSpecialSettingsEntity.class);
+
+            examOrgPropertyRepo.deleteByExamIdAndOrgId(entity.getExamId(), entity.getOrgId());
+
+            examSpecialSettingsRepo.delete(entity);
+            ret.add(entity.getExamId() + ":" + entity.getOrgId());
+        }
+        return ret;
+    }
+
+    @ApiOperation(value = "删除所有学习中心特殊设置", notes = "")
+    @PutMapping("deleteAllExamOrgSettings/{examId}")
+    @Transactional
+    public void deleteAllExamOrgSettings(@PathVariable Long examId) {
+
+        ExamEntity exam = GlobalHelper.getPresentEntity(examRepo, examId, ExamEntity.class);
+
+        validateRootOrgIsolation(exam.getRootOrgId());
+
+        examOrgPropertyRepo.deleteByExamId(examId);
+
+        examSpecialSettingsRepo
+                .deleteByExamIdAndOrgIdIsNotNullAndCourseIdIsNullAndStudentIdIsNull(examId);
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param request
+     * @param examId
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "考试IP限制", notes = "")
+    @GetMapping("ipLimit/{examId}")
+    public Map<String, Object> ipLimit(HttpServletRequest request, @PathVariable Long examId) {
+        User accessUser = getAccessUser();
+        StudentCacheBean studentCache = CacheHelper.getStudent(accessUser.getUserId());
+        Long orgId = studentCache.getOrgId();
+        ExamPropertyCacheBean ipLimitProperty = CacheHelper.getExamProperty(examId, "IP_LIMIT");
+
+        Map<String, Object> map = Maps.newHashMap();
+
+        Boolean ipLimit = null;
+        if (null != ipLimitProperty) {
+            ipLimit = StringUtil.isTrue(ipLimitProperty.getValue());
+        }
+
+        if (null == ipLimit || !ipLimit) {
+            map.put("limited", false);
+            map.put("desc", "未配置IP限制");
+            return map;
+        }
+
+        String realIp = request.getHeader("x-forwarded-for");
+        if (StringUtils.isBlank(realIp)) {
+            realIp = request.getHeader("x-real-ip");
+        }
+        if (StringUtils.isBlank(realIp)) {
+            map.put("limited", true);
+            map.put("desc", "网络受限");
+            return map;
+        }
+        realIp = realIp.trim();
+
+        ExamPropertyCacheBean ipAddressesProperty = CacheHelper.getExamProperty(examId,
+                "IP_ADDRESSES");
+
+        String ipAddresses = null;
+        if (null != ipAddressesProperty) {
+            ipAddresses = ipAddressesProperty.getValue();
+        }
+
+        boolean limited = true;
+        if (StringUtils.isNotBlank(ipAddresses)) {
+            String[] arr = StringUtils.split(ipAddresses, ';');
+
+            for (String cur : arr) {
+                String ip = StringUtils.replace(cur.trim(), ".", "\\.");
+                ip = StringUtils.replace(ip, "*", "\\w+");
+                if (realIp.matches(ip)) {
+                    limited = false;
+                    map.put("desc", "IP段配置放行");
+                    break;
+                }
+            }
+        }
+
+        if (limited) {
+            String key = "IP_" + orgId;
+            String value = redisClient.get(key, String.class);
+            if (null == value) {
+                map.put("desc", "无机构管理员登录");
+            } else {
+                @SuppressWarnings("unchecked")
+                Set<String> userKeyList = JsonUtil.fromJson(value, Set.class);
+
+                for (String userKey : userKeyList) {
+                    User curUser = redisClient.get(userKey, User.class);
+                    if (null != curUser) {
+                        String clientIp = curUser.getClientIp();
+                        if (null != clientIp) {
+                            // IP取前三段
+                            clientIp = clientIp.substring(0, clientIp.lastIndexOf(".") + 1);
+                            if (realIp.startsWith(clientIp)) {
+                                limited = false;
+                                map.put("desc", "机构管理员[key=" + userKey + "]登录放行");
+                                break;
+                            }
+                        }
+                    }
+
+                }
+            }
+        }
+
+        map.put("limited", limited);
+
+        return map;
+    }
+
+    @ApiOperation(value = "是否可以微信作答", notes = "")
+    @GetMapping("weixinAnswerEnabled/{examId}")
+    public Boolean weixinAnswerEnabled(@PathVariable Long examId) {
+
+        ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
+
+        OrgPropertyCacheBean orgConf = CacheHelper.getOrgProperty(examSettings.getRootOrgId(),
+                "WEIXIN_ANSWER_ENABLED");
+        ExamPropertyCacheBean examConf = CacheHelper.getExamProperty(examId,
+                "WEIXIN_ANSWER_ENABLED");
+
+        String orgValue = orgConf.getValue();
+        String examValue = examConf.getValue();
+
+        if (!orgConf.getHasValue()) {
+            return false;
+        }
+        if (StringUtils.isBlank(orgValue)) {
+            return false;
+        }
+
+        if (StringUtils.isBlank(examValue)) {
+            return false;
+        }
+
+        if (StringUtil.isTrue(orgValue.toString()) && StringUtil.isTrue(examValue)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @ApiOperation(value = "是否支持人脸识别", notes = "")
+    @GetMapping("faceCheckEnabled/{examId}")
+    public Boolean faceCheckEnabled(@PathVariable Long examId) {
+        ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
+
+        return faceCheckEnabled(examSettings.getRootOrgId(), examId);
+    }
+
+    /**
+     * 判断是否有人脸识别权限
+     *
+     * @param rootOrgId
+     * @param examId
+     * @return
+     * @author WANGWEI
+     */
+    private Boolean faceCheckEnabled(Long rootOrgId, Long examId) {
+        String faceCheck = PrivilegeDefine.RootOrgFunctions.OnlineExamFunctions.FaceCheck.CODE;
+        Boolean hasFaceCheckFunction = PrivilegeManager.judge(rootOrgId, faceCheck);
+
+        ExamPropertyCacheBean examConf = CacheHelper.getExamProperty(examId, "IS_FACE_ENABLE");
+        String examValue = examConf.getValue();
+
+        if (!hasFaceCheckFunction) {
+            return false;
+        }
+
+        if (StringUtils.isBlank(examValue)) {
+            return false;
+        }
+
+        if (StringUtil.isTrue(examValue)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @ApiOperation(value = "是否支持活体检测", notes = "")
+    @GetMapping("identificationOfLivingEnabled/{examId}")
+    public Boolean identificationOfLivingEnabled(@PathVariable Long examId) {
+        ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
+
+        Boolean faceCheckEnabled = faceCheckEnabled(examSettings.getRootOrgId(), examId);
+        if (!faceCheckEnabled) {
+            return false;
+        }
+
+        String IdentificationOfLivingBody =
+                PrivilegeDefine.RootOrgFunctions.OnlineExamFunctions.IdentificationOfLivingBody.CODE;
+        Boolean hasIdentificationOfLivingBodyFunction = PrivilegeManager
+                .judge(examSettings.getRootOrgId(), IdentificationOfLivingBody);
+
+        ExamPropertyCacheBean examConf = CacheHelper.getExamProperty(examId, "IS_FACE_VERIFY");
+        String examValue = examConf.getValue();
+
+        if (!hasIdentificationOfLivingBodyFunction) {
+            return false;
+        }
+
+        if (StringUtils.isBlank(examValue)) {
+            return false;
+        }
+
+        if (StringUtil.isTrue(examValue)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @ApiOperation(value = "查询考试相关的学生设置", notes = "")
+    @GetMapping("getStudentSpecialSettingsList/{curPage}/{pageSize}")
+    public PageInfo<StudentSpecialSettingsDomain> getStudentSpecialSettingsList(
+            @PathVariable Integer curPage, @PathVariable Integer pageSize,
+            @ModelAttribute StudentSpecialSettingsDomain domain) {
+
+        Long examId = domain.getExamId();
+        if (null == domain.getExamId()) {
+            throw new StatusException("001210", "examId is null");
+        }
+        ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        if (null == examEntity) {
+            throw new StatusException("001250", "examId is wrong");
+        }
+        validateRootOrgIsolation(examEntity.getRootOrgId());
+
+        Specification<ExamSpecialSettingsEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("examId"), domain.getExamId()));
+            predicates.add(cb.isNull(root.get("courseId")));
+            predicates.add(cb.isNull(root.get("orgId")));
+
+            if (null != domain.getStudentId()) {
+                predicates.add(cb.equal(root.get("studentId"), domain.getStudentId()));
+            } else {
+                predicates.add(cb.isNotNull(root.get("studentId")));
+            }
+
+            if (null != domain.getIdentityNumber()) {
+                predicates.add(cb.equal(root.get("ext1"), domain.getIdentityNumber()));
+            } else {
+                predicates.add(cb.isNotNull(root.get("ext1")));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        Pageable pageable = PageRequest.of(curPage, pageSize, Sort.Direction.DESC, "updateTime",
+                "id");
+        Page<ExamSpecialSettingsEntity> page = examSpecialSettingsRepo.findAll(specification,
+                pageable);
+
+        Iterator<ExamSpecialSettingsEntity> iterator = page.iterator();
+        List<StudentSpecialSettingsDomain> domainList = Lists.newArrayList();
+
+        while (iterator.hasNext()) {
+            ExamSpecialSettingsEntity next = iterator.next();
+            StudentSpecialSettingsDomain bean = new StudentSpecialSettingsDomain();
+            domainList.add(bean);
+
+            bean.setBeginTime(next.getBeginTime());
+            bean.setEndTime(next.getEndTime());
+            bean.setExamId(next.getExamId());
+            bean.setId(next.getId());
+            bean.setRootOrgId(next.getRootOrgId());
+            bean.setUpdateTime(next.getUpdateTime());
+            bean.setExamLimit(next.getExamLimit());
+            bean.setStudentId(next.getStudentId());
+            bean.setIdentityNumber(next.getExt1());
+        }
+
+        PageInfo<StudentSpecialSettingsDomain> ret = new PageInfo<StudentSpecialSettingsDomain>();
+        ret.setList(domainList);
+        ret.setTotal(page.getTotalElements());
+        return ret;
+    }
 
 }

+ 564 - 0
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/ExamStageController.java

@@ -0,0 +1,564 @@
+package cn.com.qmth.examcloud.core.examwork.api.controller;
+
+import cn.com.qmth.examcloud.api.commons.enums.ExamStageStartExamStatus;
+import cn.com.qmth.examcloud.api.commons.enums.ExamType;
+import cn.com.qmth.examcloud.api.commons.enums.SubmitType;
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.helpers.poi.ExcelWriter;
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.commons.util.RegExpUtil;
+import cn.com.qmth.examcloud.core.examwork.api.controller.bean.ExamDomain;
+import cn.com.qmth.examcloud.core.examwork.api.controller.bean.ExamStageDomain;
+import cn.com.qmth.examcloud.core.examwork.api.controller.bean.ExamStageSettingDomain;
+import cn.com.qmth.examcloud.core.examwork.dao.ExamStageRepo;
+import cn.com.qmth.examcloud.core.examwork.dao.ExamStageSettingRepo;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamEntity;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStageEntity;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStageSettingEntity;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.examwork.service.ExamStageService;
+import cn.com.qmth.examcloud.core.examwork.service.ExamStageSettingService;
+import cn.com.qmth.examcloud.core.examwork.service.ExamStudentService;
+import cn.com.qmth.examcloud.core.examwork.service.cache.ExamStageCache;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.ExamSettingsCacheBean;
+import cn.com.qmth.examcloud.web.config.SystemProperties;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import com.google.common.collect.Lists;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.io.FileUtils;
+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.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * @Description 场次
+ * @Author lideyin
+ * @Date 2020/7/22 15:00
+ * @Version 1.0
+ */
+@RestController
+@Api(tags = "场次相关接口")
+@RequestMapping("${$rmp.ctr.examwork}/examStage")
+public class ExamStageController extends ControllerSupport {
+
+    private static final String[] EXAM_STAGE_EXCEL_HEADER = new String[]{"场次ID", "场次号",
+            "开始时间", "结束考试时间", "控制设置", "状态"};
+
+    @Autowired
+    ExamStageService examStageService;
+
+    @Autowired
+    ExamStageSettingService examStageSettingService;
+
+    @Autowired
+    ExamStageRepo examStageRepo;
+
+    @Autowired
+    ExamStageSettingRepo examStageSettingRepo;
+
+    @Autowired
+    ExamStudentService examStudentService;
+
+    @Autowired
+    SystemProperties systemConfig;
+    @Autowired
+    ExamStageCache examStageCache;
+
+    /**
+     * 分页查询场次列表
+     *
+     * @param curPage    页索引
+     * @param pageSize   页大小
+     * @param examId     考试id
+     * @param stageOrder 场次号
+     * @param startTime  允许考试开始时间
+     * @param endTime    允许考试结束时间
+     * @param enable     是否启用
+     * @return
+     */
+    @ApiOperation(value = "分页查询场次")
+    @GetMapping("queryPage/{curPage}/{pageSize}")
+    public PageInfo<ExamStageDomain> queryPage(@PathVariable Integer curPage,
+                                               @PathVariable Integer pageSize,
+                                               @RequestParam Long examId,
+                                               @RequestParam(required = false) Integer stageOrder,
+                                               @RequestParam(required = false) String startTime,
+                                               @RequestParam(required = false) String endTime,
+                                               @RequestParam(required = false) Boolean enable) {
+        User accessUser = getAccessUser();
+
+        Specification<ExamStageEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("rootOrgId"), accessUser.getRootOrgId()));
+            predicates.add(cb.equal(root.get("examId"), examId));
+            if (null != stageOrder) {
+                predicates.add(cb.equal(root.get("stageOrder"), stageOrder));
+            }
+            if (StringUtils.isNotBlank(startTime)) {
+                predicates.add(cb.greaterThanOrEqualTo(root.get("startTime"),
+                        DateUtil.parse(startTime, DateUtil.DatePatterns.CHINA_DEFAULT)));
+            }
+            if (StringUtils.isNotBlank(endTime)) {
+                predicates.add(cb.lessThanOrEqualTo(root.get("endTime"),
+                        DateUtil.parse(endTime, DateUtil.DatePatterns.CHINA_DEFAULT)));
+            }
+            if (null != enable) {
+                predicates.add(cb.equal(root.get("enable"), enable));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        PageRequest pageRequest = PageRequest.of(curPage, pageSize,
+                new Sort(Sort.Direction.DESC, "stageOrder"));
+
+        Page<ExamStageEntity> page = examStageRepo.findAll(specification, pageRequest);
+
+        Iterator<ExamStageEntity> iterator = page.iterator();
+        List<ExamStageDomain> list = Lists.newArrayList();
+
+        while (iterator.hasNext()) {
+            ExamStageEntity next = iterator.next();
+            ExamStageDomain domain = new ExamStageDomain();
+            domain.setId(next.getId());
+            domain.setRootOrgId(next.getRootOrgId());
+            domain.setExamId(next.getExamId());
+            domain.setStageOrder(next.getStageOrder());
+            domain.setStartTime(next.getStartTime());
+            domain.setEndTime(next.getEndTime());
+            domain.setEnable(next.getEnable());
+            domain.setSpecialSetting(next.getSpecialSetting());
+            domain.setSubmitType(next.getSubmitType());
+            domain.setSubmitDuration(next.getSubmitDuration() == null ? 0 : next.getSubmitDuration());
+            domain.setUpdateTime(next.getUpdateTime());
+            domain.setCreationTime(next.getCreationTime());
+
+            list.add(domain);
+
+        }
+
+        PageInfo<ExamStageDomain> ret = new PageInfo<>(page, list);
+        return ret;
+    }
+
+    /**
+     * 查询场次
+     *
+     * @param name
+     * @param enable
+     * @param examId
+     * @param rootOrgId
+     * @return
+     * @author WANGWEI
+     */
+    @ApiOperation(value = "查询考试批次")
+    @GetMapping("queryByNameLike")
+    public List<ExamStageDomain> query(@RequestParam(required = true) String name,
+                                       @RequestParam(required = true) Long examId,
+                                       @RequestParam(required = false) Boolean enable,
+                                       @RequestParam(required = false) Long rootOrgId) {
+
+        if (null == rootOrgId) {
+            rootOrgId = getRootOrgId();
+        }
+        final Long finalRootOrgId = rootOrgId;
+
+        //如果场次号不为数字,则直接返回空集合
+        if (StringUtils.isNotEmpty(name) && !StringUtils.isNumeric(name)) {
+            return Lists.newArrayList();
+        }
+
+        Specification<ExamStageEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("rootOrgId"), finalRootOrgId));
+            predicates.add(cb.equal(root.get("examId"), examId));
+            if (StringUtils.isNotBlank(name)) {
+                predicates.add(cb.equal(root.get("stageOrder"), Long.valueOf(name)));
+            }
+            if (null != enable) {
+                predicates.add(cb.equal(root.get("enable"), enable));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        PageRequest pageRequest = PageRequest.of(0, 100,
+                new Sort(Sort.Direction.ASC, "stageOrder"));
+        Page<ExamStageEntity> page = examStageRepo.findAll(specification, pageRequest);
+
+        Iterator<ExamStageEntity> iterator = page.iterator();
+        List<ExamStageDomain> resultList = Lists.newArrayList();
+
+        while (iterator.hasNext()) {
+            ExamStageEntity next = iterator.next();
+            ExamStageDomain bean = new ExamStageDomain();
+            bean.setId(next.getId());
+            bean.setRootOrgId(next.getRootOrgId());
+            bean.setExamId(next.getExamId());
+            bean.setStageOrder(next.getStageOrder());
+            bean.setStartTime(next.getStartTime());
+            bean.setEndTime(next.getEndTime());
+            bean.setEnable(next.getEnable());
+            bean.setSpecialSetting(next.getSpecialSetting());
+            bean.setSubmitType(next.getSubmitType());
+            bean.setSubmitDuration(next.getSubmitDuration());
+            bean.setUpdateTime(next.getUpdateTime());
+            bean.setCreationTime(next.getCreationTime());
+
+            resultList.add(bean);
+        }
+
+        return resultList;
+    }
+
+    @PostMapping(value = "batchAddStage")
+    @ApiOperation(value = "添加场次")
+    @Transactional
+    public void batchAddStage(@RequestBody List<ExamStageDomain> examStages) {
+        if (examStages.size() == 0) {
+            throw new StatusException("10001", "examStages参数不允许为空");
+        }
+
+        Long examId = examStages.get(0).getExamId();
+        if (null == examId) {
+            throw new StatusException("100002", "考试id不允许为空");
+        }
+
+        User accessUser = getAccessUser();
+        ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
+        if (!examSettings.getHasValue()) {
+            throw new StatusException("100003", "考试id不正确");
+        }
+        Date examStartTime = examSettings.getBeginTime();
+        Date examEndTime = examSettings.getEndTime();
+
+        //起始场次号
+        int startStageOrder = 0;
+        ExamStageEntity maxStageOrderEntity = examStageRepo.findFirstByExamIdOrderByStageOrderDesc(examId);
+        if (null != maxStageOrderEntity) {
+            startStageOrder = maxStageOrderEntity.getStageOrder();
+        }
+        for (ExamStageDomain esd : examStages) {
+            startStageOrder++;
+
+            if (null == esd.getStartTime()) {
+                throw new StatusException("100005", "进入考试开始时间不允许为空");
+            }
+
+            if (null == esd.getEndTime()) {
+                throw new StatusException("100006", "进入考试结束时间不允许为空");
+            }
+
+            if (esd.getStartTime().after(esd.getEndTime())) {
+                throw new StatusException("100007", "进入考试开始时间不得晚于结束时间");
+            }
+
+            if (esd.getStartTime().before(examStartTime) || esd.getStartTime().after(examEndTime)) {
+                throw new StatusException("100008", "进入考试开始时间不得超过考试批次中设置的时间范围");
+            }
+
+            if (esd.getEndTime().before(examStartTime) || esd.getEndTime().after(examEndTime)) {
+                throw new StatusException("100009", "进入考试开始时间不得超过考试批次中设置的时间范围");
+            }
+
+            ExamStageEntity esEntity = new ExamStageEntity();
+            esEntity.setRootOrgId(accessUser.getRootOrgId());
+            esEntity.setExamId(examId);
+            esEntity.setStageOrder(startStageOrder);
+            esEntity.setStartTime(esd.getStartTime());
+            esEntity.setEndTime(esd.getEndTime());
+
+            if (null == esd.getEnable()) {
+                esEntity.setEnable(true);
+            } else {
+                esEntity.setEnable(esd.getEnable());
+            }
+
+            if (null == esd.getSpecialSetting()) {
+                esEntity.setSpecialSetting(false);
+            } else {
+                esEntity.setSpecialSetting(esd.getSpecialSetting());
+            }
+
+            if (null == esd.getSubmitType()) {
+                esEntity.setSubmitType(SubmitType.NORMAL);
+            } else {
+                esEntity.setSubmitType(esd.getSubmitType());
+
+                if (SubmitType.TIMING_END == esd.getSubmitType()) {
+                    if (null == esd.getSubmitDuration()) {
+                        throw new StatusException("100007", "定点交卷时长不允许为空");
+                    }
+                }
+            }
+
+            esEntity.setSubmitDuration(esd.getSubmitDuration());
+
+            esEntity.setStartExamStatus(ExamStageStartExamStatus.NOT_START);
+            examStageRepo.save(esEntity);
+        }
+    }
+
+    @PutMapping(value = "modifyStage")
+    @ApiOperation(value = "修改场次信息")
+    @Transactional
+    public void modifyStage(@RequestParam Long id,
+                            @RequestParam(required = false) String startTime,
+                            @RequestParam(required = false) String endTime,
+                            @RequestParam Boolean specialSetting,
+                            @RequestParam(required = false) String submitType,
+                            @RequestParam(required = false) Integer submitDuration) {
+        if (null == id) {
+            throw new StatusException("10001", "场次id不允许为空");
+        }
+
+        ExamStageEntity esEntity = GlobalHelper.getEntity(examStageRepo, id, ExamStageEntity.class);
+        if (null == esEntity) {
+            throw new StatusException("10002", "场次id不正确");
+        }
+
+        Long examId = esEntity.getExamId();
+        ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
+        Date examStartTime = examSettings.getBeginTime();
+        Date examEndTime = examSettings.getEndTime();
+
+        if ((null != startTime && null == endTime) || (null == startTime && null != endTime)) {
+            throw new StatusException("100003", "进入考试开始时间和结束时间必须同时有值");
+        }
+
+        if (StringUtils.isNotEmpty(startTime) && StringUtils.isNotEmpty(endTime)) {
+            Date st = DateUtil.parse(startTime, DateUtil.DatePatterns.CHINA_DEFAULT);
+            Date et = DateUtil.parse(endTime, DateUtil.DatePatterns.CHINA_DEFAULT);
+            if (st.after(et)) {
+                throw new StatusException("100004", "进入考试开始时间不得晚于结束时间");
+            }
+
+            if (st.before(examStartTime) || st.after(examEndTime)) {
+                throw new StatusException("100005", "进入考试开始时间不得超过考试批次中设置的时间范围");
+            }
+
+            if (et.before(examStartTime) || et.after(examEndTime)) {
+                throw new StatusException("100006", "进入考试开始时间不得超过考试批次中设置的时间范围");
+            }
+            esEntity.setStartTime(st);
+            esEntity.setEndTime(et);
+        }
+
+        //未进行特殊设置,默认不使用特殊设置
+        if (null == specialSetting) {
+            specialSetting = false;
+        }
+        esEntity.setSpecialSetting(specialSetting);
+
+        if (StringUtils.isEmpty(submitType)) {
+            submitType = SubmitType.NORMAL.name();
+        }
+        esEntity.setSubmitType(SubmitType.valueOf(submitType));
+
+        if (specialSetting == true && submitType.equals(SubmitType.TIMING_END.name())) {
+            if (null == submitDuration) {
+                throw new StatusException("100007", "统一收卷时长不允许为空");
+            }
+
+            if (submitDuration.intValue() <= 0) {
+                throw new StatusException("100006", "统一收卷时长必须大于0");
+            }
+            esEntity.setSubmitDuration(submitDuration);
+        }
+
+        examStageRepo.save(esEntity);
+
+        //刷新缓存
+        examStageCache.refresh(examId, esEntity.getId());
+    }
+
+    @PutMapping(value = "modifyStageEnable")
+    @ApiOperation(value = "启用/禁用场次")
+    @Transactional
+    public void modifyStageEnable(@RequestParam List<Long> examStageIds,
+                                  @RequestParam Boolean enable) {
+        if (examStageIds.size() == 0) {
+            throw new StatusException("10001", "examStageIds参数不允许为空");
+        }
+        for (Long esId : examStageIds) {
+            ExamStageEntity entity = GlobalHelper.getEntity(examStageRepo, esId, ExamStageEntity.class);
+            entity.setEnable(enable);
+            examStageRepo.save(entity);
+
+            //刷新缓存
+            examStageCache.refresh(entity.getExamId(), entity.getId());
+        }
+    }
+
+    @PostMapping(value = "saveStageSetting")
+    @ApiOperation(value = "保存通用场次设置")
+    @Transactional
+    public void saveStageSetting(@RequestBody ExamStageSettingDomain domain) {
+        ExamStageSettingEntity essEntity;
+        //新增
+        if (null == domain.getId()) {
+            if (null == domain.getExamId()) {
+                throw new StatusException("10001", "考试id不允许为空");
+            }
+
+            SubmitType submitType = null == domain.getSubmitType() ? SubmitType.NORMAL : domain.getSubmitType();
+
+            if (SubmitType.TIMING_END == submitType) {
+                if (null == domain.getSubmitDuration()) {
+                    throw new StatusException("100002", "统一收卷时长不允许为空");
+                }
+
+                if (domain.getSubmitDuration().intValue() <= 0) {
+                    throw new StatusException("100003", "统一收卷时长必须大于0");
+                }
+            }
+
+            essEntity = new ExamStageSettingEntity();
+            essEntity.setExamId(domain.getExamId());
+            essEntity.setSubmitType(submitType);
+            essEntity.setSubmitDuration(domain.getSubmitDuration());
+
+            examStageSettingRepo.save(essEntity);
+        }
+        //修改
+        else {
+            if (null == domain.getExamId()) {
+                throw new StatusException("10004", "考试id不允许为空");
+            }
+
+            SubmitType submitType = null == domain.getSubmitType() ? SubmitType.NORMAL : domain.getSubmitType();
+
+            if (SubmitType.TIMING_END == submitType) {
+                if (null == domain.getSubmitDuration()) {
+                    throw new StatusException("100005", "统一收卷时长不允许为空");
+                }
+
+                if (domain.getSubmitDuration().intValue() <= 0) {
+                    throw new StatusException("100006", "统一收卷时长必须大于0");
+                }
+            }
+
+
+            essEntity = examStageSettingRepo.findByExamId(domain.getExamId());
+            essEntity.setSubmitType(domain.getSubmitType());
+            essEntity.setSubmitDuration(domain.getSubmitDuration());
+
+            examStageSettingRepo.save(essEntity);
+        }
+
+        //刷新缓存
+        List<ExamStageEntity> existExamStageList = examStageRepo.findByExamId(domain.getExamId());
+        if (null != existExamStageList && !existExamStageList.isEmpty()) {
+            existExamStageList.forEach(p -> {
+                examStageCache.refresh(domain.getExamId(), p.getId());
+            });
+        }
+    }
+
+    @ApiOperation(value = "获取场次通用设置")
+    @GetMapping("getExamStageSetting")
+    public ExamStageSettingDomain getExamStageSetting(@RequestParam Long examId) {
+        ExamStageSettingEntity essEntity = examStageSettingRepo.findByExamId(examId);
+        if (null == essEntity) {
+            return null;
+        }
+
+        ExamStageSettingDomain result = new ExamStageSettingDomain();
+        result.setId(essEntity.getId());
+        result.setExamId(essEntity.getExamId());
+        result.setSubmitType(essEntity.getSubmitType());
+        result.setSubmitDuration(essEntity.getSubmitDuration());
+
+        return result;
+    }
+
+    @ApiOperation(value = "清空考试下所有场次")
+    @DeleteMapping("clearStages")
+    @Transactional
+    public void clearStages(@RequestParam Long examId) {
+        if (null == examId) {
+            throw new StatusException("10001", "examId参数不允许为空");
+        }
+
+        List<ExamStageEntity> existExamStageList = examStageRepo.findByExamId(examId);
+        if (null == existExamStageList || existExamStageList.isEmpty()) {
+            return;
+        }
+
+        //如果存在已经开考的场次,则不允许清空
+        ExamStageEntity query = new ExamStageEntity();
+        query.setExamId(examId);
+        query.setStartExamStatus(ExamStageStartExamStatus.STARTED);
+        Example<ExamStageEntity> example = Example.of(query);
+        boolean existStartedExamData = examStageRepo.exists(example);
+        if (existStartedExamData) {
+            throw new StatusException("10002", "该考试下已存在已经开考的场次,不允许清空");
+        }
+
+        //解绑考生与场次的对应关系
+        examStudentService.unbindExamStudentExamStage(examId);
+        examStageRepo.deleteByExamId(examId);
+        //删除相关缓存
+        existExamStageList.forEach(p -> examStageCache.remove(p.getExamId(), p.getId()));
+    }
+
+    @ApiOperation(value = "导出场次")
+    @GetMapping("exportExamStage/{examId}")
+    public void exportExamStage(@PathVariable Long examId, HttpServletResponse response) {
+        List<ExamStageEntity> examStageList = examStageRepo.findByExamId(examId);
+
+        if (null == examStageList) {
+            examStageList = new ArrayList<>();
+        }
+
+        List<Object[]> datas = Lists.newArrayList();
+
+        for (ExamStageEntity cur : examStageList) {
+            String startTime = null == cur.getStartTime()
+                    ? null
+                    : DateUtil.format(cur.getStartTime(), DateUtil.DatePatterns.CHINA_DEFAULT);
+            String endTime = null == cur.getEndTime()
+                    ? null
+                    : DateUtil.format(cur.getEndTime(), DateUtil.DatePatterns.CHINA_DEFAULT);
+            String specialSetting = cur.getSpecialSetting() ? "自定义" : "通用";
+            String enable = cur.getEnable() ? "启用" : "禁用";
+            datas.add(new Object[]{String.valueOf(cur.getId()), String.valueOf(cur.getStageOrder()),
+                    startTime, endTime, specialSetting, enable});
+        }
+
+        String filePath = systemConfig.getTempDataDir() + File.separator + "examStage_" + examId + "_"
+                + System.currentTimeMillis() + ".xlsx";
+        File file = new File(filePath);
+
+        ExcelWriter.write(
+                EXAM_STAGE_EXCEL_HEADER, new Class[]{String.class, String.class,
+                        String.class, String.class, String.class, String.class},
+                datas, new File(filePath));
+
+        exportFile("场交设置-" + examId + ".xlsx", file);
+
+        FileUtils.deleteQuietly(file);
+    }
+
+}

+ 62 - 3
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/ExamStudentController.java

@@ -1,5 +1,6 @@
 package cn.com.qmth.examcloud.core.examwork.api.controller;
 
+import cn.com.qmth.examcloud.api.commons.enums.BooleanSelect;
 import cn.com.qmth.examcloud.api.commons.enums.CourseLevel;
 import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
@@ -19,18 +20,24 @@ import cn.com.qmth.examcloud.core.basic.api.response.GetStudentResp;
 import cn.com.qmth.examcloud.core.basic.api.response.SaveStudentResp;
 import cn.com.qmth.examcloud.core.examwork.api.controller.bean.ExamStudentDomain;
 import cn.com.qmth.examcloud.core.examwork.dao.ExamRepo;
+import cn.com.qmth.examcloud.core.examwork.dao.ExamStageRepo;
 import cn.com.qmth.examcloud.core.examwork.dao.ExamStudentRepo;
 import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamEntity;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStageEntity;
 import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStudentEntity;
 import cn.com.qmth.examcloud.core.examwork.service.ExamStudentService;
 import cn.com.qmth.examcloud.core.examwork.service.bean.ExamStudentInfo;
 import cn.com.qmth.examcloud.core.oe.admin.api.ExamRecordCloudService;
 import cn.com.qmth.examcloud.core.oe.admin.api.request.CheckExamIsStartedReq;
 import cn.com.qmth.examcloud.core.oe.admin.api.response.CheckExamIsStartedResp;
+import cn.com.qmth.examcloud.reports.commons.bean.OperateReport;
+import cn.com.qmth.examcloud.reports.commons.enums.OperateContent;
+import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
 import cn.com.qmth.examcloud.support.helper.IdentityNumberHelper;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import com.google.common.collect.Lists;
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -56,6 +63,7 @@ import java.util.stream.Stream;
  * 考生服务API Created by songyue on 17/1/13.
  */
 @RestController
+@Api(tags = "考生相关接口")
 @RequestMapping("${$rmp.ctr.examwork}/exam_student")
 public class ExamStudentController extends ControllerSupport {
 
@@ -80,6 +88,9 @@ public class ExamStudentController extends ControllerSupport {
     @Autowired
     ExamRecordCloudService examRecordCloudService;
 
+    @Autowired
+    ExamStageRepo examStageRepo;
+
     /**
      * 方法注释
      *
@@ -146,6 +157,7 @@ public class ExamStudentController extends ControllerSupport {
             @PathVariable Integer pageSize,
             @RequestParam(required = false) Long orgId,
             @RequestParam(required = false) Long examId,
+            @RequestParam(required = false) Long examStageId,
             @RequestParam(required = false) String studentName,
             @RequestParam(required = false) String studentCode,
             @RequestParam(required = false) Long courseId,
@@ -157,6 +169,7 @@ public class ExamStudentController extends ControllerSupport {
             @RequestParam(required = false) Boolean identityNumberLike,
             @RequestParam(required = false) String specialtyName,
             @RequestParam(required = false) String infoCollector,
+            @RequestParam(required = false) BooleanSelect enable,
             @RequestParam(required = false) Boolean withStarted) {
 
         User accessUser = getAccessUser();
@@ -170,6 +183,9 @@ public class ExamStudentController extends ControllerSupport {
             if (null != examId) {
                 predicates.add(cb.equal(root.get("examId"), examId));
             }
+            if (null != examStageId) {
+                predicates.add(cb.equal(root.get("examStageId"), examStageId));
+            }
             if (StringUtils.isNotEmpty(studentName)) {
                 predicates.add(cb.like(root.get("name"), toSqlSearchPattern(studentName)));
             }
@@ -201,6 +217,13 @@ public class ExamStudentController extends ControllerSupport {
                 }
             }
 
+            if (null != enable) {
+                Boolean enableBoolean = enable.getBoolean();
+                if (null != enableBoolean) {
+                    predicates.add(cb.equal(root.get("enable"), enableBoolean));
+                }
+            }
+
             if (StringUtils.isNotEmpty(specialtyName)) {
                 predicates.add(cb.like(root.get("specialtyName"), toSqlSearchPattern(specialtyName)));
             }
@@ -281,10 +304,13 @@ public class ExamStudentController extends ControllerSupport {
             bean.setPhotoPath(studentBean.getPhotoPath());
 
             bean.setExt1(cur.getExt1());
-            bean.setExt2(IdentityNumberHelper.conceal(cur.getRootOrgId(), cur.getIdentityNumber()));//特殊处理:把ext2当作隐私模式下的身份证号
+            bean.setExt2(IdentityNumberHelper.conceal(cur.getRootOrgId(), cur.getIdentityNumber()));//特殊处理:把ext2
+            // 当作隐私模式下的身份证号
             bean.setExt3(cur.getExt3());
             bean.setExt4(cur.getExt4());
             bean.setExt5(cur.getExt5());
+            bean.setExamStageOrder(cur.getExamStageOrder());
+            bean.setExamStageId(cur.getExamStageId());
             ret.add(bean);
         }
         return new PageInfo<ExamStudentDomain>(examStudents, ret);
@@ -322,7 +348,11 @@ public class ExamStudentController extends ControllerSupport {
         Long rootOrgId = getRootOrgId();
         examStudent.setRootOrgId(rootOrgId);
         examStudent.setInfoCollector(examStudent.getInfoCollector());
-        return saveExamStudent(examStudent);
+        ExamStudentDomain ret = saveExamStudent(examStudent);
+        //操作日志
+        ReportsUtil.report(new OperateReport(examStudent.getRootOrgId(), getAccessUser().getUserId(), ret.getStudentId(), ret.getId(), UserType.COMMON,
+                OperateContent.EXAM_STUDENT_ADD.getDesc()));
+        return ret;
     }
 
     /**
@@ -340,7 +370,16 @@ public class ExamStudentController extends ControllerSupport {
         Long rootOrgId = getRootOrgId();
         examStudent.setRootOrgId(rootOrgId);
         examStudent.setInfoCollector(examStudent.getInfoCollector());
-        return saveExamStudent(examStudent);
+        ExamStudentDomain ret = saveExamStudent(examStudent);
+        //操作日志
+        if (examStudent.getId() != null) {
+            ReportsUtil.report(new OperateReport(examStudent.getRootOrgId(), getAccessUser().getUserId(), ret.getStudentId(), ret.getId(), UserType.COMMON,
+                    OperateContent.EXAM_STUDENT_UPDATE.getDesc()));
+        } else {
+            ReportsUtil.report(new OperateReport(examStudent.getRootOrgId(), getAccessUser().getUserId(), ret.getStudentId(), ret.getId(), UserType.COMMON,
+                    OperateContent.EXAM_STUDENT_ADD.getDesc()));
+        }
+        return ret;
     }
 
     /**
@@ -388,6 +427,20 @@ public class ExamStudentController extends ControllerSupport {
         info.setExt4(examStudent.getExt4());
         info.setExt5(examStudent.getExt5());
 
+        info.setExamStageId(examStudent.getExamStageId());
+        if (null != examStudent.getExamStageId()) {
+            ExamStageEntity examStageEntity = GlobalHelper.getEntity(examStageRepo, examStudent.getExamStageId(), ExamStageEntity.class);
+            if (null == examStageEntity) {
+                throw new StatusException("005001", "场次id不正确");
+            }
+
+            if (examStageEntity.getEnable() == false) {
+                throw new StatusException("005002", "场次被禁用,请重新选择场次");
+            }
+
+            info.setExamStageOrder(examStageEntity.getStageOrder());
+        }
+
         ExamStudentInfo savedExamStudent = examStudentService.saveExamStudent(info);
 
         ExamStudentDomain ret = new ExamStudentDomain();
@@ -451,6 +504,9 @@ public class ExamStudentController extends ControllerSupport {
                     ExamStudentEntity.class);
             s.setEnable(true);
             examStudentRepo.save(s);
+            //操作日志
+            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getStudentId(), s.getId(), UserType.COMMON,
+                    OperateContent.EXAM_STUDENT_ENABLE.getDesc()));
             ret.add(s.getId() + ":" + s.getName());
         }
 
@@ -480,6 +536,9 @@ public class ExamStudentController extends ControllerSupport {
                     ExamStudentEntity.class);
             s.setEnable(false);
             examStudentRepo.save(s);
+            //操作日志
+            ReportsUtil.report(new OperateReport(s.getRootOrgId(), getAccessUser().getUserId(), s.getStudentId(), s.getId(), UserType.COMMON,
+                    OperateContent.EXAM_STUDENT_DISABLE.getDesc()));
             ret.add(s.getId() + ":" + s.getName());
         }
 

+ 3 - 4
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/NoticeController.java

@@ -46,14 +46,13 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
-/**
- * 公告类
- */
 @RestController
-@Api(tags = "公告")
+@Api(tags = "公告相关接口")
 @RequestMapping("${$rmp.ctr.examwork}/notice")
 public class NoticeController extends ControllerSupport {
+
     private static Pattern pattern = Pattern.compile("\\s*");
+
     @Autowired
     private NoticeService noticeService;
 

+ 1 - 0
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/bean/ExamDomain.java

@@ -104,6 +104,7 @@ public class ExamDomain implements JsonSerializable {
 	/**
 	 * 特殊设置类型
 	 */
+	@Enumerated(EnumType.STRING)
 	private ExamSpecialSettingsType specialSettingsType;
 
 	private Map<String, String> properties;

+ 170 - 0
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/bean/ExamStageDomain.java

@@ -0,0 +1,170 @@
+package cn.com.qmth.examcloud.core.examwork.api.controller.bean;
+
+import cn.com.qmth.examcloud.api.commons.enums.SubmitType;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.persistence.Column;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import java.util.Date;
+
+/**
+ * @Description 场次
+ * @Author lideyin
+ * @Date 2020/7/22 17:21
+ * @Version 1.0
+ */
+@SuppressWarnings("ALL")
+public class ExamStageDomain implements JsonSerializable {
+
+    private static final long serialVersionUID = 5181451704042537371L;
+    private Long id;
+
+    /**
+     * 顶级机构Id
+     */
+    private Long rootOrgId;
+
+    /**
+     * 考试id
+     */
+    private Long examId;
+
+    /**
+     * 场次号
+     */
+    private Integer stageOrder;
+
+    /**
+     * 开始进入考试时间
+     */
+    private Date startTime;
+
+    /**
+     * 考试批次结束时间
+     */
+    private Date endTime;
+
+    private Boolean enable;
+
+    /**
+     * 是否特殊设置
+     */
+    private Boolean specialSetting;
+
+    /**
+     * 回收试卷类型
+     */
+    @Column(nullable = false, length = 50)
+    @Enumerated(EnumType.STRING)
+    private SubmitType submitType;
+
+    /**
+     * 统一收卷考试时长
+     */
+    private Integer submitDuration;
+
+    private Date updateTime;
+
+    /**
+     * 创建时间
+     */
+    private Date creationTime;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public Integer getStageOrder() {
+        return stageOrder;
+    }
+
+    public void setStageOrder(Integer stageOrder) {
+        this.stageOrder = stageOrder;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Boolean enable) {
+        this.enable = enable;
+    }
+
+    public Boolean getSpecialSetting() {
+        return specialSetting;
+    }
+
+    public void setSpecialSetting(Boolean specialSetting) {
+        this.specialSetting = specialSetting;
+    }
+
+    public SubmitType getSubmitType() {
+        return submitType;
+    }
+
+    public void setSubmitType(SubmitType submitType) {
+        this.submitType = submitType;
+    }
+
+    public Integer getSubmitDuration() {
+        return submitDuration;
+    }
+
+    public void setSubmitDuration(Integer submitDuration) {
+        this.submitDuration = submitDuration;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public Date getCreationTime() {
+        return creationTime;
+    }
+
+    public void setCreationTime(Date creationTime) {
+        this.creationTime = creationTime;
+    }
+}

+ 70 - 0
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/bean/ExamStageSettingDomain.java

@@ -0,0 +1,70 @@
+package cn.com.qmth.examcloud.core.examwork.api.controller.bean;
+
+import cn.com.qmth.examcloud.api.commons.enums.SubmitType;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+import javax.persistence.Column;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+
+/**
+ * @Description 场次设置
+ * @Author lideyin
+ * @Date 2020/7/22 17:21
+ * @Version 1.0
+ */
+public class ExamStageSettingDomain implements JsonSerializable {
+
+    private static final long serialVersionUID = 5181451704042537371L;
+    private Long id;
+
+
+    /**
+     * 考试id
+     */
+    private Long examId;
+
+    /**
+     * 回收试卷类型
+     */
+    @Column(nullable = false, length = 50)
+    @Enumerated(EnumType.STRING)
+    private SubmitType submitType;
+
+    /**
+     * 统一收卷考试时长
+     */
+    private Integer submitDuration;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public SubmitType getSubmitType() {
+        return submitType;
+    }
+
+    public void setSubmitType(SubmitType submitType) {
+        this.submitType = submitType;
+    }
+
+    public Integer getSubmitDuration() {
+        return submitDuration;
+    }
+
+    public void setSubmitDuration(Integer submitDuration) {
+        this.submitDuration = submitDuration;
+    }
+}

+ 25 - 0
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/controller/bean/ExamStudentDomain.java

@@ -75,6 +75,16 @@ public class ExamStudentDomain implements JsonSerializable {
 
 	private String photoPath;
 
+	/**
+	 * 场次id
+	 */
+	private Long examStageId;
+
+	/**
+	 * 场次号
+	 */
+	private Integer examStageOrder;
+
 	/**
 	 * 扩展属性1
 	 */
@@ -380,4 +390,19 @@ public class ExamStudentDomain implements JsonSerializable {
 		this.photoPath = photoPath;
 	}
 
+	public Integer getExamStageOrder() {
+		return examStageOrder;
+	}
+
+	public void setExamStageOrder(Integer examStageOrder) {
+		this.examStageOrder = examStageOrder;
+	}
+
+	public Long getExamStageId() {
+		return examStageId;
+	}
+
+	public void setExamStageId(Long examStageId) {
+		this.examStageId = examStageId;
+	}
 }

+ 734 - 772
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/provider/ExamCloudServiceProvider.java

@@ -1,48 +1,13 @@
 package cn.com.qmth.examcloud.core.examwork.api.provider;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import javax.persistence.criteria.Predicate;
-
-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;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.jpa.domain.Specification;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-
 import cn.com.qmth.examcloud.api.commons.enums.CURD;
 import cn.com.qmth.examcloud.api.commons.enums.ExamType;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.commons.helpers.DynamicEnum;
 import cn.com.qmth.examcloud.commons.helpers.DynamicEnumManager;
 import cn.com.qmth.examcloud.core.examwork.base.enums.ExamProperty;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamCourseRelationRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamPaperTypeRelationRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamPropertyRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamSpecialSettingsRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamStudentRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamCourseRelationEntity;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamEntity;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamPaperTypeRelationEntity;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamPropertyEntity;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamSpecialSettingsEntity;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.examwork.dao.*;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.*;
 import cn.com.qmth.examcloud.core.examwork.service.ExamService;
 import cn.com.qmth.examcloud.core.examwork.service.OnGoingExamService;
 import cn.com.qmth.examcloud.core.examwork.service.bean.ExamInfo;
@@ -52,44 +17,31 @@ import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
 import cn.com.qmth.examcloud.examwork.api.bean.ExamCourseRelationBean;
 import cn.com.qmth.examcloud.examwork.api.bean.ExamPaperTypeRelation;
 import cn.com.qmth.examcloud.examwork.api.bean.ExamSpecialSettingsBean;
-import cn.com.qmth.examcloud.examwork.api.request.CountExamStudentReq;
-import cn.com.qmth.examcloud.examwork.api.request.GetExamCourseListReq;
-import cn.com.qmth.examcloud.examwork.api.request.GetExamCoursePaperTypeListReq;
-import cn.com.qmth.examcloud.examwork.api.request.GetExamListReq;
-import cn.com.qmth.examcloud.examwork.api.request.GetExamOrgListReq;
-import cn.com.qmth.examcloud.examwork.api.request.GetExamPropertyListReq;
-import cn.com.qmth.examcloud.examwork.api.request.GetExamPropertyReq;
-import cn.com.qmth.examcloud.examwork.api.request.GetExamReq;
-import cn.com.qmth.examcloud.examwork.api.request.GetExamStudentPropertyValueListReq;
-import cn.com.qmth.examcloud.examwork.api.request.GetExamsByIdListReq;
-import cn.com.qmth.examcloud.examwork.api.request.GetExamsReq;
-import cn.com.qmth.examcloud.examwork.api.request.GetOngoingExamListReq;
-import cn.com.qmth.examcloud.examwork.api.request.LockExamStudentsReq;
-import cn.com.qmth.examcloud.examwork.api.request.SaveExamReq;
-import cn.com.qmth.examcloud.examwork.api.request.SaveStudentSpecialSettingsReq;
-import cn.com.qmth.examcloud.examwork.api.request.SetExamPropertyReq;
-import cn.com.qmth.examcloud.examwork.api.request.UnlockExamStudentsReq;
-import cn.com.qmth.examcloud.examwork.api.response.CountExamStudentResp;
-import cn.com.qmth.examcloud.examwork.api.response.GetExamCourseListResp;
-import cn.com.qmth.examcloud.examwork.api.response.GetExamCoursePaperTypeListResp;
-import cn.com.qmth.examcloud.examwork.api.response.GetExamListResp;
-import cn.com.qmth.examcloud.examwork.api.response.GetExamOrgListResp;
-import cn.com.qmth.examcloud.examwork.api.response.GetExamPropertyListResp;
-import cn.com.qmth.examcloud.examwork.api.response.GetExamPropertyResp;
-import cn.com.qmth.examcloud.examwork.api.response.GetExamResp;
-import cn.com.qmth.examcloud.examwork.api.response.GetExamStudentPropertyValueListResp;
-import cn.com.qmth.examcloud.examwork.api.response.GetExamsByIdListResp;
-import cn.com.qmth.examcloud.examwork.api.response.GetExamsResp;
-import cn.com.qmth.examcloud.examwork.api.response.GetOngoingExamListResp;
-import cn.com.qmth.examcloud.examwork.api.response.LockExamStudentsResp;
-import cn.com.qmth.examcloud.examwork.api.response.SaveExamResp;
-import cn.com.qmth.examcloud.examwork.api.response.SaveStudentSpecialSettingsResp;
-import cn.com.qmth.examcloud.examwork.api.response.SetExamPropertyResp;
-import cn.com.qmth.examcloud.examwork.api.response.UnlockExamStudentsResp;
+import cn.com.qmth.examcloud.examwork.api.request.*;
+import cn.com.qmth.examcloud.examwork.api.response.*;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
 import cn.com.qmth.examcloud.web.redis.RedisClient;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+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;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.persistence.criteria.Predicate;
+import java.util.*;
 
 /**
  * {@link StatusException} 状态码范围:012XXX<br>
@@ -100,709 +52,719 @@ import io.swagger.annotations.ApiOperation;
  * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  */
 @RestController
+@Api(tags = "考试相关接口")
 @RequestMapping("${$rmp.cloud.examwork}" + "exam")
 public class ExamCloudServiceProvider extends ControllerSupport implements ExamCloudService {
 
-	private static final long serialVersionUID = 6850508158980856785L;
-
-	@Autowired
-	RedisClient redisClient;
-
-	@Autowired
-	private ExamService examService;
-
-	@Autowired
-	ExamSpecialSettingsRepo examSpecialSettingsRepo;
-
-	@Autowired
-	ExamRepo examRepo;
-
-	@Autowired
-	ExamStudentRepo examStudentRepo;
-
-	@Autowired
-	ExamPropertyRepo examPropertyRepo;
-
-	@Autowired
-	ExamCourseRelationRepo examCourseRelationRepo;
-
-	@Autowired
-	ExamPaperTypeRelationRepo examPaperTypeRelationRepo;
-
-	@Autowired
-	OnGoingExamService onGoingExamService;
-
-	@ApiOperation(value = "保存考试批次", notes = "保存")
-	@PostMapping("saveExam")
-	@Transactional
-	@Override
-	public SaveExamResp saveExam(@RequestBody SaveExamReq req) {
-		ExamInfo examInfo = new ExamInfo();
-
-		examInfo.setBeginTime(req.getBeginTime());
-		examInfo.setDuration(req.getDuration());
-		examInfo.setEnable(req.getEnable());
-		examInfo.setEndTime(req.getEndTime());
-		examInfo.setExamTimes(req.getExamTimes());
-
-		examInfo.setExamType(req.getExamType());
-		examInfo.setCode(req.getCode());
-		examInfo.setName(req.getName());
-		examInfo.setRemark(req.getRemark());
-		examInfo.setRootOrgId(req.getRootOrgId());
-		examInfo.setExamLimit(req.getExamLimit());
-
-		Map<String, String> properties = req.getProperties();
-		examInfo.setProperties(properties);
-
-		ExamEntity saved = examService.saveExam(examInfo, CURD.CREATION_OR_UPDATE);
-
-		SaveExamResp resp = new SaveExamResp();
-		resp.setExamId(saved.getId());
-
-		ExamBean bean = new ExamBean();
-		resp.setExamBean(bean);
-
-		bean.setId(saved.getId());
-		bean.setBeginTime(saved.getBeginTime());
-		bean.setDuration(saved.getDuration());
-		bean.setEnable(saved.getEnable());
-		bean.setEndTime(saved.getEndTime());
-		bean.setExamTimes(saved.getExamTimes());
-		bean.setExamType(saved.getExamType().name());
-		bean.setName(saved.getName());
-		bean.setCode(saved.getCode());
-		bean.setRemark(saved.getRemark());
-		bean.setRootOrgId(saved.getRootOrgId());
-		bean.setExamLimit(saved.getExamLimit());
-
-		return resp;
-	}
-
-	@ApiOperation(value = "查询考试")
-	@PostMapping("getExam")
-	@Override
-	public GetExamResp getExam(@RequestBody GetExamReq req) {
-		Long id = req.getId();
-		Long rootOrgId = req.getRootOrgId();
-		String name = req.getName();
-		String code = req.getCode();
-
-		ExamEntity exam = null;
-
-		if (null != id) {
-			exam = GlobalHelper.getEntity(examRepo, id, ExamEntity.class);
-		} else if (StringUtils.isNotBlank(name)) {
-			if (null == rootOrgId) {
-				throw new StatusException("002004", "rootOrgId is null");
-			}
-			exam = examRepo.findByNameAndRootOrgId(name, rootOrgId);
-		} else if (StringUtils.isNotBlank(code)) {
-			if (null == rootOrgId) {
-				throw new StatusException("002003", "rootOrgId is null");
-			}
-			exam = examRepo.findByCodeAndRootOrgId(code, rootOrgId);
-		} else {
-			throw new StatusException("002002", "id,name,code are all null");
-		}
-
-		if (null == exam) {
-			throw new StatusException("002005", "考试不存在");
-		}
-		GetExamResp examResp = new GetExamResp();
-		examResp.setId(exam.getId());
-		ExamBean bean = new ExamBean();
-		examResp.setExamBean(bean);
-
-		bean.setId(exam.getId());
-		bean.setBeginTime(exam.getBeginTime());
-		bean.setDuration(exam.getDuration());
-		bean.setEnable(exam.getEnable());
-		bean.setEndTime(exam.getEndTime());
-		bean.setExamTimes(exam.getExamTimes());
-		bean.setExamType(exam.getExamType().name());
-		bean.setName(exam.getName());
-		bean.setCode(exam.getCode());
-		bean.setRemark(exam.getRemark());
-		bean.setRootOrgId(exam.getRootOrgId());
-		bean.setExamLimit(exam.getExamLimit());
-
-		return examResp;
-	}
-
-	@ApiOperation(value = "设置考试属性")
-	@PostMapping("setExamProperty")
-	@Transactional
-	@Override
-	public SetExamPropertyResp setExamProperty(@RequestBody SetExamPropertyReq req) {
-		Long examId = req.getExamId();
-		String key = req.getKey();
-		String value = req.getValue();
-
-		DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-		DynamicEnum de = manager.getByName(key);
-
-		ExamPropertyEntity entity = examPropertyRepo.findByExamIdAndKeyId(examId, de.getId());
-		if (null == entity) {
-			entity = new ExamPropertyEntity();
-			entity.setExamId(examId);
-			entity.setKeyId(de.getId());
-		}
-		entity.setValue(value);
-
-		ExamPropertyEntity saved = examPropertyRepo.save(entity);
-
-		SetExamPropertyResp resp = new SetExamPropertyResp();
-		resp.setPropertyId(saved.getId());
-		return resp;
-	}
-
-	@ApiOperation(value = "查询考试属性")
-	@PostMapping("getExamProperty")
-	@Override
-	public GetExamPropertyResp getExamProperty(@RequestBody GetExamPropertyReq req) {
-		Long examId = req.getExamId();
-		Long orgId = req.getOrgId();
-		String key = req.getKey();
-
-		String value = examService.getExamOrgProperty(examId, orgId, key);
-
-		GetExamPropertyResp resp = new GetExamPropertyResp();
-		resp.setValue(value);
-		return resp;
-	}
-
-	@ApiOperation(value = "查询考试属性集合")
-	@PostMapping("getExamPropertyList")
-	@Override
-	public GetExamPropertyListResp getExamPropertyList(@RequestBody GetExamPropertyListReq req) {
-
-		Long examId = req.getExamId();
-		Long orgId = req.getOrgId();
-		List<String> keys = req.getKeys();
-		Map<String, String> properties = Maps.newHashMap();
-		for (String key : keys) {
-			String value = examService.getExamOrgProperty(examId, orgId, key);
-			properties.put(key, value);
-		}
-
-		GetExamPropertyListResp resp = new GetExamPropertyListResp();
-		resp.setProperties(properties);
-		return resp;
-	}
-
-	@ApiOperation(value = "查询正在考试的考试集合")
-	@PostMapping("getOngoingExamList")
-	@Override
-	public GetOngoingExamListResp getOngoingExamList(@RequestBody GetOngoingExamListReq req) {
-		Long rootOrgId = req.getRootOrgId();
-		String examType = req.getExamType();
-		Long orgId = req.getOrgId();
-		Long studentId = req.getStudentId();
-
-		List<ExamSpecialSettingsEntity> resultList = onGoingExamService
-				.getOngoingExamList(rootOrgId, examType, orgId, studentId);
-
-		List<ExamSpecialSettingsBean> list = Lists.newArrayList();
-		for (ExamSpecialSettingsEntity cur : resultList) {
-			ExamSpecialSettingsBean bean = new ExamSpecialSettingsBean();
-			list.add(bean);
-			bean.setId(cur.getId());
-			bean.setOrgId(cur.getOrgId());
-			bean.setCourseId(cur.getCourseId());
-			bean.setStudentId(cur.getStudentId());
-			cur.setRootOrgId(cur.getRootOrgId());
-			cur.setUpdateTime(cur.getUpdateTime());
-			bean.setBeginTime(cur.getBeginTime());
-			bean.setEndTime(cur.getEndTime());
-			bean.setExamEnable(cur.getExamEnable());
-			bean.setExamId(cur.getExamId());
-			bean.setExamLimit(cur.getExamLimit());
-			bean.setExamType(cur.getExamType().name());
-			bean.setSpecialSettingsEnabled(cur.getSpecialSettingsEnabled());
-			bean.setSpecialSettingsType(cur.getSpecialSettingsType());
-		}
-
-		GetOngoingExamListResp resp = new GetOngoingExamListResp();
-		resp.setExamSpecialSettingsList(list);
-
-		return resp;
-	}
-
-	@ApiOperation(value = "锁定考试")
-	@PostMapping("lockExamStudents")
-	@Transactional
-	@Override
-	public LockExamStudentsResp lockExamStudents(@RequestBody LockExamStudentsReq req) {
-
-		List<Long> examIdList = req.getExamIdList();
-
-		if (CollectionUtils.isEmpty(examIdList)) {
-			throw new StatusException("002003", "examIdList is empty");
-		}
-		for (Long examId : examIdList) {
-			if (null == examId) {
-				throw new StatusException("002001", "examId is null");
-			}
-			ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-			if (null == exam) {
-				throw new StatusException("002002", "ExamEntity is null");
-			}
-			exam.setExamStudentLocked(true);
-			examRepo.save(exam);
-		}
-
-		LockExamStudentsResp resp = new LockExamStudentsResp();
-		resp.setExamIdList(examIdList);
-		return resp;
-	}
-
-	@ApiOperation(value = "解锁考试")
-	@PostMapping("unlockExamStudents")
-	@Transactional
-	@Override
-	public UnlockExamStudentsResp unlockExamStudents(@RequestBody UnlockExamStudentsReq req) {
-
-		List<Long> examIdList = req.getExamIdList();
-
-		if (CollectionUtils.isEmpty(examIdList)) {
-			throw new StatusException("002003", "examIdList is empty");
-		}
-
-		for (Long examId : examIdList) {
-			if (null == examId) {
-				throw new StatusException("002001", "examId is null");
-			}
-			ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-			if (null == exam) {
-				throw new StatusException("002002", "ExamEntity is null");
-			}
-			exam.setExamStudentLocked(false);
-			examRepo.save(exam);
-		}
-
-		UnlockExamStudentsResp resp = new UnlockExamStudentsResp();
-		resp.setExamIdList(examIdList);
-		return resp;
-	}
-
-	@ApiOperation(value = "查询考试集合")
-	@PostMapping("getExamList")
-	@Override
-	public GetExamListResp getExamList(@RequestBody GetExamListReq req) {
-
-		Long rootOrgId = req.getRootOrgId();
-		Boolean enable = req.getEnable();
-		List<String> examTypeList = req.getExamTypeList();
-
-		List<ExamType> examTypes = Lists.newArrayList();
-		if (CollectionUtils.isNotEmpty(examTypeList)) {
-			for (String cur : examTypeList) {
-				examTypes.add(ExamType.valueOf(cur));
-			}
-		}
-
-		final long start = null == req.getStart() ? 1 : req.getStart();
-
-		Pageable pageable = PageRequest.of(0, 100, Sort.Direction.ASC, "id");
-
-		Specification<ExamEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-			predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
-
-			predicates.add(cb.greaterThanOrEqualTo(root.get("id"), start));
-
-			if (null != enable) {
-				predicates.add(cb.equal(root.get("enable"), enable));
-			}
-			if (CollectionUtils.isNotEmpty(examTypes)) {
-				if (1 == examTypeList.size()) {
-					predicates.add(cb.equal(root.get("examType"), examTypes.get(0)));
-				} else {
-					predicates.add(root.get("examType").in(examTypes));
-				}
-			}
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		Page<ExamEntity> page = examRepo.findAll(specification, pageable);
-
-		Iterator<ExamEntity> iterator = page.iterator();
-
-		List<ExamBean> list = Lists.newArrayList();
-		long next = start;
-		boolean has = false;
-		while (iterator.hasNext()) {
-			ExamEntity cur = iterator.next();
-			ExamBean bean = new ExamBean();
-			list.add(bean);
-
-			bean.setId(cur.getId());
-			bean.setBeginTime(cur.getBeginTime());
-			bean.setDuration(cur.getDuration());
-			bean.setEnable(cur.getEnable());
-			bean.setEndTime(cur.getEndTime());
-			bean.setExamTimes(cur.getExamTimes());
-			bean.setExamType(cur.getExamType().name());
-			bean.setName(cur.getName());
-			bean.setRemark(cur.getRemark());
-			bean.setRootOrgId(cur.getRootOrgId());
-
-			next = cur.getId();
-			has = true;
-		}
-
-		GetExamListResp resp = new GetExamListResp();
-		if (has) {
-			next++;
-		}
-		resp.setNext(next);
-		resp.setExamList(list);
-		return resp;
-	}
-
-	@ApiOperation(value = "查询考试的课程集合")
-	@PostMapping("getExamCourseList")
-	@Override
-	public GetExamCourseListResp getExamCourseList(@RequestBody GetExamCourseListReq req) {
-		Long examId = req.getExamId();
-
-		final long start = null == req.getStart() ? 1 : req.getStart();
-
-		Pageable pageable = PageRequest.of(0, 100, Sort.Direction.ASC, "courseId");
-
-		Specification<ExamCourseRelationEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-			predicates.add(cb.equal(root.get("examId"), examId));
-
-			predicates.add(cb.greaterThanOrEqualTo(root.get("courseId"), start));
-
-			if (null != req.getCourseEnable()) {
-				predicates.add(cb.equal(root.get("courseEnable"), req.getCourseEnable()));
-			}
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		Page<ExamCourseRelationEntity> page = examCourseRelationRepo.findAll(specification,
-				pageable);
-
-		Iterator<ExamCourseRelationEntity> iterator = page.iterator();
-
-		List<ExamCourseRelationBean> list = Lists.newArrayList();
-		long next = start;
-		boolean has = false;
-		while (iterator.hasNext()) {
-			ExamCourseRelationEntity e = iterator.next();
-			ExamCourseRelationBean b = new ExamCourseRelationBean();
-			b.setCourseCode(e.getCourseCode());
-			b.setCourseId(e.getCourseId());
-			b.setCourseLevel(e.getCourseLevel());
-			b.setCourseName(e.getCourseName());
-			b.setExamId(examId);
-			b.setCourseEnable(e.getCourseEnable());
-
-			next = e.getCourseId();
-			list.add(b);
-			has = true;
-		}
-
-		GetExamCourseListResp resp = new GetExamCourseListResp();
-		if (has) {
-			next++;
-		}
-		resp.setNext(next);
-		resp.setRelationList(list);
-
-		return resp;
-	}
-
-	@ApiOperation(value = "查询考试课程的试卷类型集")
-	@PostMapping("getExamCoursePaperTypeList")
-	@Override
-	public GetExamCoursePaperTypeListResp getExamCoursePaperTypeList(
-			@RequestBody GetExamCoursePaperTypeListReq req) {
-
-		Long examId = req.getExamId();
-
-		final long start = null == req.getStart() ? 1 : req.getStart();
-
-		Pageable pageable = PageRequest.of(0, 100, Sort.Direction.ASC, "courseId");
-
-		Specification<ExamPaperTypeRelationEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-			predicates.add(cb.equal(root.get("examId"), examId));
-
-			predicates.add(cb.greaterThanOrEqualTo(root.get("courseId"), start));
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		Page<ExamPaperTypeRelationEntity> page = examPaperTypeRelationRepo.findAll(specification,
-				pageable);
-
-		Iterator<ExamPaperTypeRelationEntity> iterator = page.iterator();
-
-		List<ExamPaperTypeRelation> list = Lists.newArrayList();
-		long next = start;
-		boolean has = false;
-		while (iterator.hasNext()) {
-			ExamPaperTypeRelationEntity e = iterator.next();
-			ExamPaperTypeRelation b = new ExamPaperTypeRelation();
-			b.setCourseId(e.getCourseId());
-			b.setExamId(e.getExamId());
-			b.setPaperType(e.getPaperType());
-
-			next = e.getCourseId();
-			list.add(b);
-			has = true;
-		}
-
-		GetExamCoursePaperTypeListResp resp = new GetExamCoursePaperTypeListResp();
-		if (has) {
-			next++;
-		}
-		resp.setNext(next);
-		resp.setRelationList(list);
-
-		return resp;
-	}
-
-	@ApiOperation(value = "统计考生数量")
-	@PostMapping("countExamStudent")
-	@Override
-	public CountExamStudentResp countExamStudent(@RequestBody CountExamStudentReq req) {
-
-		Specification<ExamStudentEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-
-			if (null != req.getExamId()) {
-				predicates.add(cb.equal(root.get("examId"), req.getExamId()));
-			}
-			if (null != req.getCourseId()) {
-				predicates.add(cb.equal(root.get("courseId"), req.getCourseId()));
-			}
-			if (StringUtils.isNotEmpty(req.getPaperType())) {
-				predicates.add(cb.equal(root.get("paperType"), req.getPaperType()));
-			}
-			if (StringUtils.isNotEmpty(req.getExamSite())) {
-				predicates.add(cb.equal(root.get("examSite"), req.getExamSite()));
-			}
-			if (null != req.getOrgId()) {
-				predicates.add(cb.equal(root.get("orgId"), req.getOrgId()));
-			}
-			if (null != req.getExt1()) {
-				predicates.add(cb.equal(root.get("ext1"), req.getExt1()));
-			}
-			if (null != req.getExt2()) {
-				predicates.add(cb.equal(root.get("ext2"), req.getExt2()));
-			}
-			if (null != req.getExt3()) {
-				predicates.add(cb.equal(root.get("ext3"), req.getExt3()));
-			}
-			if (null != req.getExt4()) {
-				predicates.add(cb.equal(root.get("ext4"), req.getExt4()));
-			}
-			if (null != req.getExt5()) {
-				predicates.add(cb.equal(root.get("ext5"), req.getExt5()));
-			}
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		long count = examStudentRepo.count(specification);
-		CountExamStudentResp resp = new CountExamStudentResp();
-		resp.setCount(count);
-		return resp;
-	}
-
-	@ApiOperation(value = "获取考试机构列表")
-	@PostMapping("getExamOrgList")
-	@Override
-	public GetExamOrgListResp getExamOrgList(@RequestBody GetExamOrgListReq req) {
-
-		long start = null == req.getStart() ? 1 : req.getStart();
-		Long examId = req.getExamId();
-		long next = start;
-
-		List<Long> bigIntegerList = examStudentRepo.queryOrgIdList(examId, start);
-		List<Long> orgIdList = Lists.newArrayList();
-		for (Object bigInteger : bigIntegerList) {
-			orgIdList.add(Long.parseLong(String.valueOf(bigInteger)));
-		}
-		if (CollectionUtils.isNotEmpty(orgIdList)) {
-			next = orgIdList.get(orgIdList.size() - 1);
-			next++;
-		}
-
-		GetExamOrgListResp resp = new GetExamOrgListResp();
-		resp.setNext(next);
-		resp.setOrgIdList(orgIdList);
-
-		return resp;
-	}
-
-	@ApiOperation(value = " 获取考生属性值集合")
-	@PostMapping("getExamStudentPropertyValueList")
-	@Override
-	public GetExamStudentPropertyValueListResp getExamStudentPropertyValueList(
-			@RequestBody GetExamStudentPropertyValueListReq req) {
-
-		Long examId = req.getExamId();
-		String start = null == req.getStart() ? "" : req.getStart();
-
-		List<String> valueList = null;
-		String name = req.getPropertyName();
-
-		if (StringUtils.isBlank(name)) {
-			throw new StatusException("003005", "propertyName is blank");
-		}
-
-		if (name.equals("examSite")) {
-			valueList = examStudentRepo.queryExamSiteList(examId, start);
-		} else if (name.equals("ext1")) {
-			valueList = examStudentRepo.queryExt1List(examId, start);
-		} else if (name.equals("ext2")) {
-			valueList = examStudentRepo.queryExt2List(examId, start);
-		} else if (name.equals("ext3")) {
-			valueList = examStudentRepo.queryExt3List(examId, start);
-		} else if (name.equals("ext4")) {
-			valueList = examStudentRepo.queryExt4List(examId, start);
-		} else if (name.equals("ext5")) {
-			valueList = examStudentRepo.queryExt5List(examId, start);
-		} else {
-			throw new StatusException("003006", "propertyName is wrong");
-		}
-
-		GetExamStudentPropertyValueListResp resp = new GetExamStudentPropertyValueListResp();
-		resp.setValueList(valueList);
-
-		return resp;
-	}
-
-	@ApiOperation(value = "按ID查询考试", notes = "")
-	@PostMapping("getExamsByIdList")
-	@Override
-	public GetExamsByIdListResp getExamsByIdList(@RequestBody GetExamsByIdListReq req) {
-		List<Long> examIdList = req.getExamIdList();
-		List<ExamBean> list = Lists.newArrayList();
-		for (Long cur : examIdList) {
-			ExamEntity exam = GlobalHelper.getEntity(examRepo, cur, ExamEntity.class);
-			if (null == exam) {
-				throw new StatusException("012001", "no exam [id=" + cur + "]");
-			}
-
-			ExamBean bean = new ExamBean();
-
-			bean.setId(exam.getId());
-			bean.setBeginTime(exam.getBeginTime());
-			bean.setDuration(exam.getDuration());
-			bean.setEnable(exam.getEnable());
-			bean.setEndTime(exam.getEndTime());
-			bean.setExamTimes(exam.getExamTimes());
-			bean.setExamType(exam.getExamType().name());
-			bean.setName(exam.getName());
-			bean.setCode(exam.getCode());
-			bean.setRemark(exam.getRemark());
-			bean.setRootOrgId(exam.getRootOrgId());
-			bean.setExamLimit(exam.getExamLimit());
-
-			list.add(bean);
-		}
-
-		GetExamsByIdListResp resp = new GetExamsByIdListResp();
-		resp.setExamList(list);
-		return resp;
-	}
-
-	@ApiOperation(value = "遍历考试", notes = "")
-	@PostMapping("getExams")
-	@Override
-	public GetExamsResp getExams(@RequestBody GetExamsReq req) {
-		Long rootOrgId = req.getRootOrgId();
-		Boolean enable = req.getEnable();
-
-		final long start = null == req.getStart() ? 1 : req.getStart();
-
-		Pageable pageable = PageRequest.of(0, 100, Sort.Direction.ASC, "id");
-
-		Specification<ExamEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-			predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
-
-			predicates.add(cb.greaterThanOrEqualTo(root.get("id"), start));
-
-			if (null != enable) {
-				predicates.add(cb.equal(root.get("enable"), enable));
-			}
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		Page<ExamEntity> page = examRepo.findAll(specification, pageable);
-
-		Iterator<ExamEntity> iterator = page.iterator();
-
-		List<ExamBean> list = Lists.newArrayList();
-		long next = start;
-		boolean has = false;
-		while (iterator.hasNext()) {
-			ExamEntity exam = iterator.next();
-			ExamBean bean = new ExamBean();
-			bean.setId(exam.getId());
-			bean.setBeginTime(exam.getBeginTime());
-			bean.setDuration(exam.getDuration());
-			bean.setEnable(exam.getEnable());
-			bean.setEndTime(exam.getEndTime());
-			bean.setExamTimes(exam.getExamTimes());
-			bean.setExamType(exam.getExamType().name());
-			bean.setName(exam.getName());
-			bean.setCode(exam.getCode());
-			bean.setRemark(exam.getRemark());
-			bean.setRootOrgId(exam.getRootOrgId());
-			bean.setExamLimit(exam.getExamLimit());
-
-			next = exam.getId();
-			list.add(bean);
-			has = true;
-		}
-
-		GetExamsResp resp = new GetExamsResp();
-		if (has) {
-			next++;
-		}
-		resp.setNext(next);
-		resp.setExamBeanList(list);
-
-		return resp;
-	}
-
-	@ApiOperation(value = "保存学生特殊设置", notes = "")
-	@PostMapping("saveStudentSpecialSettings")
-	@Override
-	public SaveStudentSpecialSettingsResp saveStudentSpecialSettings(
-			@RequestBody SaveStudentSpecialSettingsReq req) {
-
-		Long rootOrgId = req.getRootOrgId();
-		Long studentId = req.getStudentId();
-		Long examId = req.getExamId();
-		Date beginTime = req.getBeginTime();
-		Date endTime = req.getEndTime();
-
-		ExamSpecialSettingsInfo examSpecialInfo = new ExamSpecialSettingsInfo();
-		examSpecialInfo.setBeginTime(beginTime);
-		examSpecialInfo.setEndTime(endTime);
-		examSpecialInfo.setExamId(examId);
-		examSpecialInfo.setExamLimit(false);
-		examSpecialInfo.setRootOrgId(rootOrgId);
-		examSpecialInfo.setStudentId(studentId);
-
-		ExamSpecialSettingsEntity saved = examService.saveExamSpecialSettings(examSpecialInfo);
-
-		SaveStudentSpecialSettingsResp resp = new SaveStudentSpecialSettingsResp();
-		resp.setSpecialSettingsId(saved.getId());
-		return resp;
-	}
+    private static final long serialVersionUID = 6850508158980856785L;
+
+    @Autowired
+    RedisClient redisClient;
+
+    @Autowired
+    private ExamService examService;
+
+    @Autowired
+    ExamSpecialSettingsRepo examSpecialSettingsRepo;
+
+    @Autowired
+    ExamRepo examRepo;
+
+    @Autowired
+    ExamStudentRepo examStudentRepo;
+
+    @Autowired
+    ExamPropertyRepo examPropertyRepo;
+
+    @Autowired
+    ExamCourseRelationRepo examCourseRelationRepo;
+
+    @Autowired
+    ExamPaperTypeRelationRepo examPaperTypeRelationRepo;
+
+    @Autowired
+    OnGoingExamService onGoingExamService;
+
+    @ApiOperation(value = "保存考试批次", notes = "保存")
+    @PostMapping("saveExam")
+    @Transactional
+    @Override
+    public SaveExamResp saveExam(@RequestBody SaveExamReq req) {
+        ExamInfo examInfo = new ExamInfo();
+
+        examInfo.setBeginTime(req.getBeginTime());
+        examInfo.setDuration(req.getDuration());
+        examInfo.setEnable(req.getEnable());
+        examInfo.setEndTime(req.getEndTime());
+        examInfo.setExamTimes(req.getExamTimes());
+
+        examInfo.setExamType(req.getExamType());
+        examInfo.setCode(req.getCode());
+        examInfo.setName(req.getName());
+        examInfo.setRemark(req.getRemark());
+        examInfo.setRootOrgId(req.getRootOrgId());
+        examInfo.setExamLimit(req.getExamLimit());
+
+        Map<String, String> properties = req.getProperties();
+        examInfo.setProperties(properties);
+
+        ExamEntity saved = examService.saveExam(examInfo, CURD.CREATION_OR_UPDATE);
+
+        SaveExamResp resp = new SaveExamResp();
+        resp.setExamId(saved.getId());
+
+        ExamBean bean = new ExamBean();
+        resp.setExamBean(bean);
+
+        bean.setId(saved.getId());
+        bean.setBeginTime(saved.getBeginTime());
+        bean.setDuration(saved.getDuration());
+        bean.setEnable(saved.getEnable());
+        bean.setEndTime(saved.getEndTime());
+        bean.setExamTimes(saved.getExamTimes());
+        bean.setExamType(saved.getExamType().name());
+        bean.setName(saved.getName());
+        bean.setCode(saved.getCode());
+        bean.setRemark(saved.getRemark());
+        bean.setRootOrgId(saved.getRootOrgId());
+        bean.setExamLimit(saved.getExamLimit());
+
+        return resp;
+    }
+
+    @ApiOperation(value = "查询考试")
+    @PostMapping("getExam")
+    @Override
+    public GetExamResp getExam(@RequestBody GetExamReq req) {
+        Long id = req.getId();
+        Long rootOrgId = req.getRootOrgId();
+        String name = req.getName();
+        String code = req.getCode();
+
+        ExamEntity exam = null;
+
+        if (null != id) {
+            exam = GlobalHelper.getEntity(examRepo, id, ExamEntity.class);
+        } else if (StringUtils.isNotBlank(name)) {
+            if (null == rootOrgId) {
+                throw new StatusException("002004", "rootOrgId is null");
+            }
+            exam = examRepo.findByNameAndRootOrgId(name, rootOrgId);
+        } else if (StringUtils.isNotBlank(code)) {
+            if (null == rootOrgId) {
+                throw new StatusException("002003", "rootOrgId is null");
+            }
+            exam = examRepo.findByCodeAndRootOrgId(code, rootOrgId);
+        } else {
+            throw new StatusException("002002", "id,name,code are all null");
+        }
+
+        if (null == exam) {
+            throw new StatusException("002005", "考试不存在");
+        }
+        GetExamResp examResp = new GetExamResp();
+        examResp.setId(exam.getId());
+        ExamBean bean = new ExamBean();
+        examResp.setExamBean(bean);
+
+        bean.setId(exam.getId());
+        bean.setBeginTime(exam.getBeginTime());
+        bean.setDuration(exam.getDuration());
+        bean.setEnable(exam.getEnable());
+        bean.setEndTime(exam.getEndTime());
+        bean.setExamTimes(exam.getExamTimes());
+        bean.setExamType(exam.getExamType().name());
+        bean.setName(exam.getName());
+        bean.setCode(exam.getCode());
+        bean.setRemark(exam.getRemark());
+        bean.setRootOrgId(exam.getRootOrgId());
+        bean.setExamLimit(exam.getExamLimit());
+
+        return examResp;
+    }
+
+    @ApiOperation(value = "设置考试属性")
+    @PostMapping("setExamProperty")
+    @Transactional
+    @Override
+    public SetExamPropertyResp setExamProperty(@RequestBody SetExamPropertyReq req) {
+        Long examId = req.getExamId();
+        String key = req.getKey();
+        String value = req.getValue();
+
+        DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+        DynamicEnum de = manager.getByName(key);
+
+        ExamPropertyEntity entity = examPropertyRepo.findByExamIdAndKeyId(examId, de.getId());
+        if (null == entity) {
+            entity = new ExamPropertyEntity();
+            entity.setExamId(examId);
+            entity.setKeyId(de.getId());
+        }
+        entity.setValue(value);
+
+        ExamPropertyEntity saved = examPropertyRepo.save(entity);
+
+        SetExamPropertyResp resp = new SetExamPropertyResp();
+        resp.setPropertyId(saved.getId());
+        return resp;
+    }
+
+    @ApiOperation(value = "查询考试属性")
+    @PostMapping("getExamProperty")
+    @Override
+    public GetExamPropertyResp getExamProperty(@RequestBody GetExamPropertyReq req) {
+        Long examId = req.getExamId();
+        Long orgId = req.getOrgId();
+        String key = req.getKey();
+
+        String value = examService.getExamOrgProperty(examId, orgId, key);
+
+        GetExamPropertyResp resp = new GetExamPropertyResp();
+        resp.setValue(value);
+        return resp;
+    }
+
+    @ApiOperation(value = "查询考试属性集合")
+    @PostMapping("getExamPropertyList")
+    @Override
+    public GetExamPropertyListResp getExamPropertyList(@RequestBody GetExamPropertyListReq req) {
+
+        Long examId = req.getExamId();
+        Long orgId = req.getOrgId();
+        List<String> keys = req.getKeys();
+        Map<String, String> properties = Maps.newHashMap();
+        for (String key : keys) {
+            String value = examService.getExamOrgProperty(examId, orgId, key);
+            properties.put(key, value);
+        }
+
+        GetExamPropertyListResp resp = new GetExamPropertyListResp();
+        resp.setProperties(properties);
+        return resp;
+    }
+
+    @ApiOperation(value = "查询正在考试的考试集合")
+    @PostMapping("getOngoingExamList")
+    @Override
+    public GetOngoingExamListResp getOngoingExamList(@RequestBody GetOngoingExamListReq req) {
+        Long rootOrgId = req.getRootOrgId();
+        String examType = req.getExamType();
+        Long orgId = req.getOrgId();
+        Long studentId = req.getStudentId();
+
+        List<ExamSpecialSettingsEntity> resultList = onGoingExamService
+                .getOngoingExamList(rootOrgId, examType, orgId, studentId);
+
+        List<ExamSpecialSettingsBean> list = Lists.newArrayList();
+        for (ExamSpecialSettingsEntity cur : resultList) {
+            ExamSpecialSettingsBean bean = new ExamSpecialSettingsBean();
+            list.add(bean);
+            bean.setId(cur.getId());
+            bean.setOrgId(cur.getOrgId());
+            bean.setCourseId(cur.getCourseId());
+            bean.setStudentId(cur.getStudentId());
+            cur.setRootOrgId(cur.getRootOrgId());
+            cur.setUpdateTime(cur.getUpdateTime());
+            bean.setBeginTime(cur.getBeginTime());
+            bean.setEndTime(cur.getEndTime());
+            bean.setExamEnable(cur.getExamEnable());
+            bean.setExamId(cur.getExamId());
+            bean.setExamLimit(cur.getExamLimit());
+            bean.setExamType(cur.getExamType().name());
+            bean.setSpecialSettingsEnabled(cur.getSpecialSettingsEnabled());
+            bean.setSpecialSettingsType(cur.getSpecialSettingsType());
+        }
+
+        GetOngoingExamListResp resp = new GetOngoingExamListResp();
+        resp.setExamSpecialSettingsList(list);
+
+        return resp;
+    }
+
+    @ApiOperation(value = "锁定考试")
+    @PostMapping("lockExamStudents")
+    @Transactional
+    @Override
+    public LockExamStudentsResp lockExamStudents(@RequestBody LockExamStudentsReq req) {
+
+        List<Long> examIdList = req.getExamIdList();
+
+        if (CollectionUtils.isEmpty(examIdList)) {
+            throw new StatusException("002003", "examIdList is empty");
+        }
+        for (Long examId : examIdList) {
+            if (null == examId) {
+                throw new StatusException("002001", "examId is null");
+            }
+            ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+            if (null == exam) {
+                throw new StatusException("002002", "ExamEntity is null");
+            }
+            exam.setExamStudentLocked(true);
+            examRepo.save(exam);
+        }
+
+        LockExamStudentsResp resp = new LockExamStudentsResp();
+        resp.setExamIdList(examIdList);
+        return resp;
+    }
+
+    @ApiOperation(value = "解锁考试")
+    @PostMapping("unlockExamStudents")
+    @Transactional
+    @Override
+    public UnlockExamStudentsResp unlockExamStudents(@RequestBody UnlockExamStudentsReq req) {
+
+        List<Long> examIdList = req.getExamIdList();
+
+        if (CollectionUtils.isEmpty(examIdList)) {
+            throw new StatusException("002003", "examIdList is empty");
+        }
+
+        for (Long examId : examIdList) {
+            if (null == examId) {
+                throw new StatusException("002001", "examId is null");
+            }
+            ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+            if (null == exam) {
+                throw new StatusException("002002", "ExamEntity is null");
+            }
+            exam.setExamStudentLocked(false);
+            examRepo.save(exam);
+        }
+
+        UnlockExamStudentsResp resp = new UnlockExamStudentsResp();
+        resp.setExamIdList(examIdList);
+        return resp;
+    }
+
+    @ApiOperation(value = "查询考试集合")
+    @PostMapping("getExamList")
+    @Override
+    public GetExamListResp getExamList(@RequestBody GetExamListReq req) {
+
+        Long rootOrgId = req.getRootOrgId();
+        Boolean enable = req.getEnable();
+        List<String> examTypeList = req.getExamTypeList();
+
+        List<ExamType> examTypes = Lists.newArrayList();
+        if (CollectionUtils.isNotEmpty(examTypeList)) {
+            for (String cur : examTypeList) {
+                examTypes.add(ExamType.valueOf(cur));
+            }
+        }
+
+        final long start = null == req.getStart() ? 1 : req.getStart();
+
+        Pageable pageable = PageRequest.of(0, 100, Sort.Direction.ASC, "id");
+
+        Specification<ExamEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+
+            predicates.add(cb.greaterThanOrEqualTo(root.get("id"), start));
+
+            if (null != enable) {
+                predicates.add(cb.equal(root.get("enable"), enable));
+            }
+            if (CollectionUtils.isNotEmpty(examTypes)) {
+                if (1 == examTypeList.size()) {
+                    predicates.add(cb.equal(root.get("examType"), examTypes.get(0)));
+                } else {
+                    predicates.add(root.get("examType").in(examTypes));
+                }
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        Page<ExamEntity> page = examRepo.findAll(specification, pageable);
+
+        Iterator<ExamEntity> iterator = page.iterator();
+
+        List<ExamBean> list = Lists.newArrayList();
+        long next = start;
+        boolean has = false;
+        while (iterator.hasNext()) {
+            ExamEntity cur = iterator.next();
+            ExamBean bean = new ExamBean();
+            list.add(bean);
+
+            bean.setId(cur.getId());
+            bean.setBeginTime(cur.getBeginTime());
+            bean.setDuration(cur.getDuration());
+            bean.setEnable(cur.getEnable());
+            bean.setEndTime(cur.getEndTime());
+            bean.setExamTimes(cur.getExamTimes());
+            bean.setExamType(cur.getExamType().name());
+            bean.setName(cur.getName());
+            bean.setRemark(cur.getRemark());
+            bean.setRootOrgId(cur.getRootOrgId());
+
+            next = cur.getId();
+            has = true;
+        }
+
+        GetExamListResp resp = new GetExamListResp();
+        if (has) {
+            next++;
+        }
+        resp.setNext(next);
+        resp.setExamList(list);
+        return resp;
+    }
+
+    @ApiOperation(value = "查询考试的课程集合")
+    @PostMapping("getExamCourseList")
+    @Override
+    public GetExamCourseListResp getExamCourseList(@RequestBody GetExamCourseListReq req) {
+        Long examId = req.getExamId();
+
+        final long start = null == req.getStart() ? 1 : req.getStart();
+
+        Pageable pageable = PageRequest.of(0, 100, Sort.Direction.ASC, "courseId");
+
+        Specification<ExamCourseRelationEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("examId"), examId));
+
+            predicates.add(cb.greaterThanOrEqualTo(root.get("courseId"), start));
+
+            if (null != req.getCourseEnable()) {
+                predicates.add(cb.equal(root.get("courseEnable"), req.getCourseEnable()));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        Page<ExamCourseRelationEntity> page = examCourseRelationRepo.findAll(specification,
+                pageable);
+
+        Iterator<ExamCourseRelationEntity> iterator = page.iterator();
+
+        List<ExamCourseRelationBean> list = Lists.newArrayList();
+        long next = start;
+        boolean has = false;
+        while (iterator.hasNext()) {
+            ExamCourseRelationEntity e = iterator.next();
+            ExamCourseRelationBean b = new ExamCourseRelationBean();
+            b.setCourseCode(e.getCourseCode());
+            b.setCourseId(e.getCourseId());
+            b.setCourseLevel(e.getCourseLevel());
+            b.setCourseName(e.getCourseName());
+            b.setExamId(examId);
+            b.setCourseEnable(e.getCourseEnable());
+
+            next = e.getCourseId();
+            list.add(b);
+            has = true;
+        }
+
+        GetExamCourseListResp resp = new GetExamCourseListResp();
+        if (has) {
+            next++;
+        }
+        resp.setNext(next);
+        resp.setRelationList(list);
+
+        return resp;
+    }
+
+    @ApiOperation(value = "查询考试课程的试卷类型集")
+    @PostMapping("getExamCoursePaperTypeList")
+    @Override
+    public GetExamCoursePaperTypeListResp getExamCoursePaperTypeList(
+            @RequestBody GetExamCoursePaperTypeListReq req) {
+
+        Long examId = req.getExamId();
+
+        final long start = null == req.getStart() ? 1 : req.getStart();
+
+        Pageable pageable = PageRequest.of(0, 100, Sort.Direction.ASC, "courseId");
+
+        Specification<ExamPaperTypeRelationEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("examId"), examId));
+
+            predicates.add(cb.greaterThanOrEqualTo(root.get("courseId"), start));
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        Page<ExamPaperTypeRelationEntity> page = examPaperTypeRelationRepo.findAll(specification,
+                pageable);
+
+        Iterator<ExamPaperTypeRelationEntity> iterator = page.iterator();
+
+        List<ExamPaperTypeRelation> list = Lists.newArrayList();
+        long next = start;
+        boolean has = false;
+        while (iterator.hasNext()) {
+            ExamPaperTypeRelationEntity e = iterator.next();
+            ExamPaperTypeRelation b = new ExamPaperTypeRelation();
+            b.setCourseId(e.getCourseId());
+            b.setExamId(e.getExamId());
+            b.setPaperType(e.getPaperType());
+
+            next = e.getCourseId();
+            list.add(b);
+            has = true;
+        }
+
+        GetExamCoursePaperTypeListResp resp = new GetExamCoursePaperTypeListResp();
+        if (has) {
+            next++;
+        }
+        resp.setNext(next);
+        resp.setRelationList(list);
+
+        return resp;
+    }
+
+    @ApiOperation(value = "统计考生数量")
+    @PostMapping("countExamStudent")
+    @Override
+    public CountExamStudentResp countExamStudent(@RequestBody CountExamStudentReq req) {
+
+        Specification<ExamStudentEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+
+            if (null != req.getExamId()) {
+                predicates.add(cb.equal(root.get("examId"), req.getExamId()));
+            }
+            if (null != req.getCourseId()) {
+                predicates.add(cb.equal(root.get("courseId"), req.getCourseId()));
+            }
+            if (StringUtils.isNotEmpty(req.getPaperType())) {
+                predicates.add(cb.equal(root.get("paperType"), req.getPaperType()));
+            }
+            if (StringUtils.isNotEmpty(req.getExamSite())) {
+                predicates.add(cb.equal(root.get("examSite"), req.getExamSite()));
+            }
+            if (null != req.getOrgId()) {
+                predicates.add(cb.equal(root.get("orgId"), req.getOrgId()));
+            }
+            if (null != req.getExt1()) {
+                predicates.add(cb.equal(root.get("ext1"), req.getExt1()));
+            }
+            if (null != req.getExt2()) {
+                predicates.add(cb.equal(root.get("ext2"), req.getExt2()));
+            }
+            if (null != req.getExt3()) {
+                predicates.add(cb.equal(root.get("ext3"), req.getExt3()));
+            }
+            if (null != req.getExt4()) {
+                predicates.add(cb.equal(root.get("ext4"), req.getExt4()));
+            }
+            if (null != req.getExt5()) {
+                predicates.add(cb.equal(root.get("ext5"), req.getExt5()));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        long count = examStudentRepo.count(specification);
+        CountExamStudentResp resp = new CountExamStudentResp();
+        resp.setCount(count);
+        return resp;
+    }
+
+    @ApiOperation(value = "获取考试机构列表")
+    @PostMapping("getExamOrgList")
+    @Override
+    public GetExamOrgListResp getExamOrgList(@RequestBody GetExamOrgListReq req) {
+
+        long start = null == req.getStart() ? 1 : req.getStart();
+        Long examId = req.getExamId();
+        long next = start;
+
+        List<Long> bigIntegerList = examStudentRepo.queryOrgIdList(examId, start);
+        List<Long> orgIdList = Lists.newArrayList();
+        for (Object bigInteger : bigIntegerList) {
+            orgIdList.add(Long.parseLong(String.valueOf(bigInteger)));
+        }
+        if (CollectionUtils.isNotEmpty(orgIdList)) {
+            next = orgIdList.get(orgIdList.size() - 1);
+            next++;
+        }
+
+        GetExamOrgListResp resp = new GetExamOrgListResp();
+        resp.setNext(next);
+        resp.setOrgIdList(orgIdList);
+
+        return resp;
+    }
+
+    @ApiOperation(value = " 获取考生属性值集合")
+    @PostMapping("getExamStudentPropertyValueList")
+    @Override
+    public GetExamStudentPropertyValueListResp getExamStudentPropertyValueList(
+            @RequestBody GetExamStudentPropertyValueListReq req) {
+
+        Long examId = req.getExamId();
+        String start = null == req.getStart() ? "" : req.getStart();
+
+        List<String> valueList = null;
+        String name = req.getPropertyName();
+
+        if (StringUtils.isBlank(name)) {
+            throw new StatusException("003005", "propertyName is blank");
+        }
+
+        if (name.equals("examSite")) {
+            valueList = examStudentRepo.queryExamSiteList(examId, start);
+        } else if (name.equals("ext1")) {
+            valueList = examStudentRepo.queryExt1List(examId, start);
+        } else if (name.equals("ext2")) {
+            valueList = examStudentRepo.queryExt2List(examId, start);
+        } else if (name.equals("ext3")) {
+            valueList = examStudentRepo.queryExt3List(examId, start);
+        } else if (name.equals("ext4")) {
+            valueList = examStudentRepo.queryExt4List(examId, start);
+        } else if (name.equals("ext5")) {
+            valueList = examStudentRepo.queryExt5List(examId, start);
+        } else {
+            throw new StatusException("003006", "propertyName is wrong");
+        }
+
+        GetExamStudentPropertyValueListResp resp = new GetExamStudentPropertyValueListResp();
+        resp.setValueList(valueList);
+
+        return resp;
+    }
+
+    @ApiOperation(value = "按ID查询考试", notes = "")
+    @PostMapping("getExamsByIdList")
+    @Override
+    public GetExamsByIdListResp getExamsByIdList(@RequestBody GetExamsByIdListReq req) {
+        List<Long> examIdList = req.getExamIdList();
+        List<ExamBean> list = Lists.newArrayList();
+        for (Long cur : examIdList) {
+            ExamEntity exam = GlobalHelper.getEntity(examRepo, cur, ExamEntity.class);
+            if (null == exam) {
+                throw new StatusException("012001", "no exam [id=" + cur + "]");
+            }
+
+            ExamBean bean = new ExamBean();
+
+            bean.setId(exam.getId());
+            bean.setBeginTime(exam.getBeginTime());
+            bean.setDuration(exam.getDuration());
+            bean.setEnable(exam.getEnable());
+            bean.setEndTime(exam.getEndTime());
+            bean.setExamTimes(exam.getExamTimes());
+            bean.setExamType(exam.getExamType().name());
+            bean.setName(exam.getName());
+            bean.setCode(exam.getCode());
+            bean.setRemark(exam.getRemark());
+            bean.setRootOrgId(exam.getRootOrgId());
+            bean.setExamLimit(exam.getExamLimit());
+
+            list.add(bean);
+        }
+
+        GetExamsByIdListResp resp = new GetExamsByIdListResp();
+        resp.setExamList(list);
+        return resp;
+    }
+
+    @ApiOperation(value = "遍历考试", notes = "")
+    @PostMapping("getExams")
+    @Override
+    public GetExamsResp getExams(@RequestBody GetExamsReq req) {
+        Long rootOrgId = req.getRootOrgId();
+        Boolean enable = req.getEnable();
+
+        final long start = null == req.getStart() ? 1 : req.getStart();
+
+        Pageable pageable = PageRequest.of(0, 100, Sort.Direction.ASC, "id");
+
+        Specification<ExamEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+
+            predicates.add(cb.greaterThanOrEqualTo(root.get("id"), start));
+
+            if (null != enable) {
+                predicates.add(cb.equal(root.get("enable"), enable));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        Page<ExamEntity> page = examRepo.findAll(specification, pageable);
+
+        Iterator<ExamEntity> iterator = page.iterator();
+
+        List<ExamBean> list = Lists.newArrayList();
+        long next = start;
+        boolean has = false;
+        while (iterator.hasNext()) {
+            ExamEntity exam = iterator.next();
+            ExamBean bean = new ExamBean();
+            bean.setId(exam.getId());
+            bean.setBeginTime(exam.getBeginTime());
+            bean.setDuration(exam.getDuration());
+            bean.setEnable(exam.getEnable());
+            bean.setEndTime(exam.getEndTime());
+            bean.setExamTimes(exam.getExamTimes());
+            bean.setExamType(exam.getExamType().name());
+            bean.setName(exam.getName());
+            bean.setCode(exam.getCode());
+            bean.setRemark(exam.getRemark());
+            bean.setRootOrgId(exam.getRootOrgId());
+            bean.setExamLimit(exam.getExamLimit());
+
+            next = exam.getId();
+            list.add(bean);
+            has = true;
+        }
+
+        GetExamsResp resp = new GetExamsResp();
+        if (has) {
+            next++;
+        }
+        resp.setNext(next);
+        resp.setExamBeanList(list);
+
+        return resp;
+    }
+
+    @ApiOperation(value = "保存学生特殊设置", notes = "")
+    @PostMapping("saveStudentSpecialSettings")
+    @Override
+    public SaveStudentSpecialSettingsResp saveStudentSpecialSettings(
+            @RequestBody SaveStudentSpecialSettingsReq req) {
+
+        Long rootOrgId = req.getRootOrgId();
+        Long studentId = req.getStudentId();
+        Long examId = req.getExamId();
+        Date beginTime = req.getBeginTime();
+        Date endTime = req.getEndTime();
+
+        ExamSpecialSettingsInfo examSpecialInfo = new ExamSpecialSettingsInfo();
+        examSpecialInfo.setBeginTime(beginTime);
+        examSpecialInfo.setEndTime(endTime);
+        examSpecialInfo.setExamId(examId);
+        examSpecialInfo.setExamLimit(false);
+        examSpecialInfo.setRootOrgId(rootOrgId);
+        examSpecialInfo.setStudentId(studentId);
+
+        ExamSpecialSettingsEntity saved = examService.saveExamSpecialSettings(examSpecialInfo);
+
+        SaveStudentSpecialSettingsResp resp = new SaveStudentSpecialSettingsResp();
+        resp.setSpecialSettingsId(saved.getId());
+        return resp;
+    }
+
+    @Override
+    @PostMapping("getExamNames")
+    @ApiOperation(value = "获取考试名称")
+    public GetExamNamesResp getExamNames(@RequestBody GetExamNamesReq req) {
+        GetExamNamesResp resp = new GetExamNamesResp();
+        resp.setExamNames(examService.getExamNamesByIds(req.getExamIds()));
+        return resp;
+    }
 
 }

+ 116 - 0
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/provider/ExamStageCloudServiceProvider.java

@@ -0,0 +1,116 @@
+package cn.com.qmth.examcloud.core.examwork.api.provider;
+
+import cn.com.qmth.examcloud.api.commons.enums.ExamStageStartExamStatus;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.examwork.dao.ExamStageRepo;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStageEntity;
+import cn.com.qmth.examcloud.examwork.api.ExamStageCloudService;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamStageBean;
+import cn.com.qmth.examcloud.examwork.api.request.GetExamStageReq;
+import cn.com.qmth.examcloud.examwork.api.request.ModifyExamStageStartExamStatusReq;
+import cn.com.qmth.examcloud.examwork.api.response.GetExamStageResp;
+import cn.com.qmth.examcloud.examwork.api.response.ModifyExamStageStartExamStatusResp;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @Description 状态码范围:013XXX<br>考试场次相关接口
+ * @Author lideyin
+ * @Date 2020/8/6 15:53
+ * @Version 1.0
+ */
+@RestController
+@Api(tags = "考试场次相关接口")
+@RequestMapping("${$rmp.cloud.examwork}" + "examStage")
+public class ExamStageCloudServiceProvider extends ControllerSupport implements ExamStageCloudService {
+
+    private static final long serialVersionUID = -7011233758819589714L;
+
+    @Autowired
+    private ExamStageRepo examStageRepo;
+
+    /**
+     * 更新场次中的考试状态
+     *
+     * @param req
+     */
+    @ApiOperation(value = "更新场次中的考试状态", notes = "")
+    @PostMapping("modifyExamStageStartExamStatus")
+    @Override
+    public ModifyExamStageStartExamStatusResp modifyExamStageStartExamStatus(@RequestBody ModifyExamStageStartExamStatusReq req) {
+        ExamStageEntity stageEntity = GlobalHelper.getEntity(examStageRepo, req.getExamStageId(), ExamStageEntity.class);
+
+        if (null == stageEntity) {
+            throw new StatusException("013001", String.format("场次id:%s不存在", req.getExamStageId()));
+        }
+
+        stageEntity.setStartExamStatus(ExamStageStartExamStatus.valueOf(req.getExamStageStartExamStatus()));
+        examStageRepo.save(stageEntity);
+
+        return new ModifyExamStageStartExamStatusResp();
+    }
+
+    /**
+     * 获取场次信息
+     */
+    @ApiOperation(value = "根据查询条件获取场次信息", notes = "")
+    @PostMapping("getExamStage")
+    @Override
+    public GetExamStageResp getExamStage(@RequestBody GetExamStageReq req) {
+        GetExamStageResp resp = new GetExamStageResp();
+        ExamStageEntity esEntity;
+        if (null != req.getId()) {
+            esEntity = GlobalHelper.getEntity(examStageRepo, req.getId(), ExamStageEntity.class);
+            if (null == esEntity) {
+                throw new StatusException("013101", String.format("找不到id为:%s的场次信息", req.getId()));
+            }
+
+            resp.setId(req.getId());
+            resp.setExamStageBean(copyFrom(esEntity));
+            return resp;
+        }
+
+        if (null == req.getExamId()) {
+            throw new StatusException("013102", "考试id不允许为空");
+        }
+
+        if (null == req.getExamStageOrder()) {
+            throw new StatusException("013103", "场次号不允许为空");
+        }
+
+        esEntity = examStageRepo.findByExamIdAndStageOrder(req.getExamId(), req.getExamStageOrder());
+
+        if (null == esEntity) {
+            throw new StatusException("013101",
+                    String.format("找不到考试id:%s,场次号:%s的场次信息", req.getExamId(), req.getExamStageOrder()));
+        }
+
+        resp.setId(esEntity.getId());
+        resp.setExamStageBean(copyFrom(esEntity));
+        return resp;
+    }
+
+    /**
+     * 根据实体转化为bean
+     *
+     * @param esEntity
+     * @return
+     */
+    private ExamStageBean copyFrom(ExamStageEntity esEntity) {
+        ExamStageBean bean = new ExamStageBean();
+        bean.setId(esEntity.getId());
+        bean.setExamId(esEntity.getExamId());
+        bean.setRootOrgId(esEntity.getRootOrgId());
+        bean.setStageOrder(esEntity.getStageOrder());
+        bean.setEnable(esEntity.getEnable());
+        return bean;
+    }
+
+}

+ 25 - 0
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/provider/ExamStudentCloudServiceProvider.java

@@ -1,5 +1,6 @@
 package cn.com.qmth.examcloud.core.examwork.api.provider;
 
+import cn.com.qmth.examcloud.api.commons.security.bean.UserType;
 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.OrgCloudService;
@@ -27,9 +28,13 @@ import cn.com.qmth.examcloud.examwork.api.bean.ExamStudentBean;
 import cn.com.qmth.examcloud.examwork.api.bean.ExamStudentBean4Reset;
 import cn.com.qmth.examcloud.examwork.api.request.*;
 import cn.com.qmth.examcloud.examwork.api.response.*;
+import cn.com.qmth.examcloud.reports.commons.bean.OperateReport;
+import cn.com.qmth.examcloud.reports.commons.enums.OperateContent;
+import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import com.google.common.collect.Lists;
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -58,6 +63,7 @@ import java.util.List;
  * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  */
 @RestController
+@Api(tags = "考生相关接口")
 @RequestMapping("${$rmp.cloud.examwork}" + "examStudent")
 public class ExamStudentCloudServiceProvider extends ControllerSupport
         implements
@@ -127,6 +133,9 @@ public class ExamStudentCloudServiceProvider extends ControllerSupport
         info.setExt4(req.getExt4());
         info.setExt5(req.getExt5());
 
+        info.setExamStageId(req.getExamStageId());
+        info.setExamStageOrder(req.getExamStageOrder());
+
         ExamStudentInfo saved = examStudentService.saveExamStudent(info);
 
         SaveExamStudentResp resp = new SaveExamStudentResp();
@@ -150,9 +159,14 @@ public class ExamStudentCloudServiceProvider extends ControllerSupport
         examStudentBean.setOrgId(saved.getOrgId());
         examStudentBean.setOrgCode(saved.getOrgCode());
         examStudentBean.setOrgName(saved.getOrgName());
+        examStudentBean.setExamStageId(saved.getExamStageId());
+        examStudentBean.setExamStageOrder(saved.getExamStageOrder());
 
         resp.setExamStudentBean(examStudentBean);
 
+        //操作日志
+        ReportsUtil.report(new OperateReport(saved.getRootOrgId(), null, saved.getStudentId(),saved.getId(), UserType.COMMON,
+        		OperateContent.EXAM_STUDENT_IMPORT.getDesc()));
         return resp;
     }
 
@@ -250,6 +264,9 @@ public class ExamStudentCloudServiceProvider extends ControllerSupport
             one.setExt5(es.getExt5());
 
             ExamStudentInfo saved = examStudentService.saveExamStudent(one);
+//            //操作日志
+//            ReportsUtil.report(new OperateReport(saved.getRootOrgId(), null, saved.getStudentId(),saved.getId(), UserType.COMMON,
+//            		OperateContent.EXAM_STUDENT_COPY.getDesc()));
             examStudentIds.add(saved.getId());
 
             next = es.getId();
@@ -482,6 +499,14 @@ public class ExamStudentCloudServiceProvider extends ControllerSupport
 
         examStudentService.syncExamStudent(saved.getId());
 
+        //操作日志
+        if(enable) {
+        	ReportsUtil.report(new OperateReport(es.getRootOrgId(), null, es.getStudentId(),es.getId(), UserType.COMMON,
+        			OperateContent.EXAM_STUDENT_ENABLE.getDesc()));
+        }else {
+        	ReportsUtil.report(new OperateReport(es.getRootOrgId(), null, es.getStudentId(),es.getId(), UserType.COMMON,
+        			OperateContent.EXAM_STUDENT_DISABLE.getDesc()));
+        }
         UpdateExamStudentStatusResp resp = new UpdateExamStudentStatusResp();
         resp.setExamStudentId(saved.getId());
         return resp;

+ 114 - 125
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/provider/HandleSyncCloudServiceProvider.java

@@ -1,7 +1,14 @@
 package cn.com.qmth.examcloud.core.examwork.api.provider;
 
-import java.util.List;
-
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.examwork.dao.ExamCourseRelationRepo;
+import cn.com.qmth.examcloud.core.examwork.dao.ExamStudentRepo;
+import cn.com.qmth.examcloud.global.api.HandleSyncCloudService;
+import cn.com.qmth.examcloud.global.api.request.*;
+import cn.com.qmth.examcloud.global.api.response.*;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -11,26 +18,7 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
-import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamCourseRelationRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamStudentRepo;
-import cn.com.qmth.examcloud.global.api.HandleSyncCloudService;
-import cn.com.qmth.examcloud.global.api.request.SyncCourseReq;
-import cn.com.qmth.examcloud.global.api.request.SyncExamReq;
-import cn.com.qmth.examcloud.global.api.request.SyncExamStudentReq;
-import cn.com.qmth.examcloud.global.api.request.SyncOrgReq;
-import cn.com.qmth.examcloud.global.api.request.SyncSpecialtyReq;
-import cn.com.qmth.examcloud.global.api.request.SyncStudentReq;
-import cn.com.qmth.examcloud.global.api.request.SyncUserReq;
-import cn.com.qmth.examcloud.global.api.response.SyncCourseResp;
-import cn.com.qmth.examcloud.global.api.response.SyncExamResp;
-import cn.com.qmth.examcloud.global.api.response.SyncExamStudentResp;
-import cn.com.qmth.examcloud.global.api.response.SyncOrgResp;
-import cn.com.qmth.examcloud.global.api.response.SyncSpecialtyResp;
-import cn.com.qmth.examcloud.global.api.response.SyncStudentResp;
-import cn.com.qmth.examcloud.global.api.response.SyncUserResp;
-import cn.com.qmth.examcloud.web.support.ControllerSupport;
-import io.swagger.annotations.ApiOperation;
+import java.util.List;
 
 /**
  * 类注释
@@ -41,110 +29,111 @@ import io.swagger.annotations.ApiOperation;
  */
 @Transactional
 @RestController
+@Api(tags = "同步相关接口")
 @RequestMapping("${$rmp.cloud.examwork}" + "dataSync")
 public class HandleSyncCloudServiceProvider extends ControllerSupport
-		implements
-			HandleSyncCloudService {
-
-	private static final long serialVersionUID = 9122543745597481498L;
-
-	@Autowired
-	ExamStudentRepo examStudentRepo;
-
-	@Autowired
-	ExamCourseRelationRepo examCourseRelationRepo;
-
-	@ApiOperation(value = "同步课程")
-	@PostMapping("syncCourse")
-	@Override
-	public SyncCourseResp syncCourse(@RequestBody SyncCourseReq req) {
-		String courseName = req.getName();
-		Long courseId = req.getId();
-		String courseLevel = req.getLevel();
-		Long rootOrgId = req.getRootOrgId();
-		Boolean enable = req.getEnable();
-
-		if (StringUtils.isBlank(courseName)) {
-			throw new StatusException("100001", "courseName is null");
-		}
-		if (null == courseId) {
-			throw new StatusException("100002", "courseId is null");
-		}
-		if (StringUtils.isBlank(courseLevel)) {
-			throw new StatusException("100003", "courseLevel is null");
-		}
-		if (null == rootOrgId) {
-			throw new StatusException("100004", "rootOrgId is null");
-		}
-
-		examStudentRepo.updateCourse(courseName, courseLevel, courseId);
-
-		examCourseRelationRepo.updateCourse(courseName, courseLevel, enable, courseId);
-
-		SyncCourseResp resp = new SyncCourseResp();
-		return resp;
-	}
-
-	@ApiOperation(value = "同步机构")
-	@PostMapping("syncOrg")
-	@Override
-	public SyncOrgResp syncOrg(@RequestBody SyncOrgReq req) {
-		return null;
-	}
-
-	@ApiOperation(value = "同步学生")
-	@PostMapping("syncStudent")
-	@Override
-	public SyncStudentResp syncStudent(@RequestBody SyncStudentReq req) {
-		String name = req.getName();
-		Long id = req.getId();
-		List<String> unboundStudentCodeList = req.getUnboundStudentCodeList();
-
-		if (StringUtils.isBlank(name)) {
-			throw new StatusException("100001", "name is null");
-		}
-		if (null == id) {
-			throw new StatusException("100002", "id is null");
-		}
-
-		examStudentRepo.updateStudentName(name, id);
-
-		if (CollectionUtils.isNotEmpty(unboundStudentCodeList)) {
-			for (String cur : unboundStudentCodeList) {
-				examStudentRepo.unboundStudentCode(cur, id);
-			}
-		}
-
-		SyncStudentResp resp = new SyncStudentResp();
-		return resp;
-	}
-
-	@ApiOperation(value = "同步考生")
-	@PostMapping("syncExamStudent")
-	@Override
-	public SyncExamStudentResp syncExamStudent(@RequestBody SyncExamStudentReq req) {
-		return null;
-	}
-
-	@ApiOperation(value = "同步专业")
-	@PostMapping("syncSpecialty")
-	@Override
-	public SyncSpecialtyResp syncSpecialty(@RequestBody SyncSpecialtyReq req) {
-		return null;
-	}
-
-	@ApiOperation(value = "同步考试")
-	@PostMapping("syncExam")
-	@Override
-	public SyncExamResp syncExam(@RequestBody SyncExamReq req) {
-		return null;
-	}
-
-	@ApiOperation(value = "同步考试")
-	@PostMapping("syncUser")
-	@Override
-	public SyncUserResp syncUser(@RequestBody SyncUserReq req) {
-		return null;
-	}
+        implements
+        HandleSyncCloudService {
+
+    private static final long serialVersionUID = 9122543745597481498L;
+
+    @Autowired
+    ExamStudentRepo examStudentRepo;
+
+    @Autowired
+    ExamCourseRelationRepo examCourseRelationRepo;
+
+    @ApiOperation(value = "同步课程")
+    @PostMapping("syncCourse")
+    @Override
+    public SyncCourseResp syncCourse(@RequestBody SyncCourseReq req) {
+        String courseName = req.getName();
+        Long courseId = req.getId();
+        String courseLevel = req.getLevel();
+        Long rootOrgId = req.getRootOrgId();
+        Boolean enable = req.getEnable();
+
+        if (StringUtils.isBlank(courseName)) {
+            throw new StatusException("100001", "courseName is null");
+        }
+        if (null == courseId) {
+            throw new StatusException("100002", "courseId is null");
+        }
+        if (StringUtils.isBlank(courseLevel)) {
+            throw new StatusException("100003", "courseLevel is null");
+        }
+        if (null == rootOrgId) {
+            throw new StatusException("100004", "rootOrgId is null");
+        }
+
+        examStudentRepo.updateCourse(courseName, courseLevel, courseId);
+
+        examCourseRelationRepo.updateCourse(courseName, courseLevel, enable, courseId);
+
+        SyncCourseResp resp = new SyncCourseResp();
+        return resp;
+    }
+
+    @ApiOperation(value = "同步机构")
+    @PostMapping("syncOrg")
+    @Override
+    public SyncOrgResp syncOrg(@RequestBody SyncOrgReq req) {
+        return null;
+    }
+
+    @ApiOperation(value = "同步学生")
+    @PostMapping("syncStudent")
+    @Override
+    public SyncStudentResp syncStudent(@RequestBody SyncStudentReq req) {
+        String name = req.getName();
+        Long id = req.getId();
+        List<String> unboundStudentCodeList = req.getUnboundStudentCodeList();
+
+        if (StringUtils.isBlank(name)) {
+            throw new StatusException("100001", "name is null");
+        }
+        if (null == id) {
+            throw new StatusException("100002", "id is null");
+        }
+
+        examStudentRepo.updateStudentName(name, id);
+
+        if (CollectionUtils.isNotEmpty(unboundStudentCodeList)) {
+            for (String cur : unboundStudentCodeList) {
+                examStudentRepo.unboundStudentCode(cur, id);
+            }
+        }
+
+        SyncStudentResp resp = new SyncStudentResp();
+        return resp;
+    }
+
+    @ApiOperation(value = "同步考生")
+    @PostMapping("syncExamStudent")
+    @Override
+    public SyncExamStudentResp syncExamStudent(@RequestBody SyncExamStudentReq req) {
+        return null;
+    }
+
+    @ApiOperation(value = "同步专业")
+    @PostMapping("syncSpecialty")
+    @Override
+    public SyncSpecialtyResp syncSpecialty(@RequestBody SyncSpecialtyReq req) {
+        return null;
+    }
+
+    @ApiOperation(value = "同步考试")
+    @PostMapping("syncExam")
+    @Override
+    public SyncExamResp syncExam(@RequestBody SyncExamReq req) {
+        return null;
+    }
+
+    @ApiOperation(value = "同步考试")
+    @PostMapping("syncUser")
+    @Override
+    public SyncUserResp syncUser(@RequestBody SyncUserReq req) {
+        return null;
+    }
 
 }

+ 73 - 72
examcloud-core-examwork-api-provider/src/main/java/cn/com/qmth/examcloud/core/examwork/api/provider/NoticeCloudServiceProvider.java

@@ -1,11 +1,16 @@
 package cn.com.qmth.examcloud.core.examwork.api.provider;
 
 import cn.com.qmth.examcloud.core.examwork.dao.entity.NoticeRulePublishProgressEntity;
+import cn.com.qmth.examcloud.core.examwork.service.NoticeService;
+import cn.com.qmth.examcloud.examwork.api.NoticeCloudService;
 import cn.com.qmth.examcloud.examwork.api.bean.NoticeRulePublishProgressBean;
 import cn.com.qmth.examcloud.examwork.api.request.DisposePublishingUserNoticeReq;
 import cn.com.qmth.examcloud.examwork.api.request.UpdateNoticeStatusReq;
 import cn.com.qmth.examcloud.examwork.api.response.DisposePublishingUserNoticeResp;
 import cn.com.qmth.examcloud.examwork.api.response.GetNoticeRulePublishProgressListResp;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -13,11 +18,6 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
-import cn.com.qmth.examcloud.core.examwork.service.NoticeService;
-import cn.com.qmth.examcloud.examwork.api.NoticeCloudService;
-import cn.com.qmth.examcloud.web.support.ControllerSupport;
-import io.swagger.annotations.ApiOperation;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -29,82 +29,83 @@ import java.util.List;
  * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  */
 @RestController
+@Api(tags = "通知相关接口")
 @RequestMapping("${$rmp.cloud.examwork}" + "notice")
 public class NoticeCloudServiceProvider extends ControllerSupport implements NoticeCloudService {
 
-	private static final long serialVersionUID = 4359829563744893206L;
+    private static final long serialVersionUID = 4359829563744893206L;
 
-	@Autowired
-	private NoticeService noticeService;
+    @Autowired
+    private NoticeService noticeService;
 
-	@ApiOperation(value = "获取待处理的通知进度数据")
-	@PostMapping("getToBeDisposedNoticeRulePublishProgressList")
-	@Override
-	public GetNoticeRulePublishProgressListResp getToBeDisposedNoticeRulePublishProgressList() {
-		GetNoticeRulePublishProgressListResp resp = new GetNoticeRulePublishProgressListResp();
-		List<NoticeRulePublishProgressEntity> progressEntityList = noticeService
-				.getToBeDisposedNoticeRulePublishProgressList();
-		if (progressEntityList == null || progressEntityList.isEmpty()) {
-			return resp;
-		}
-		List<NoticeRulePublishProgressBean> beanList = new ArrayList<>();
-		for (NoticeRulePublishProgressEntity entity : progressEntityList) {
-			NoticeRulePublishProgressBean bean = new NoticeRulePublishProgressBean();
-			bean.setId(entity.getId());
-			bean.setMaxCommonUserId(entity.getMaxCommonUserId());
-			bean.setMaxStudentId(entity.getMaxStudentId());
-			bean.setNoticeId(entity.getNoticeId());
-			bean.setNoticeReceiverRuleType(entity.getNoticeReceiverRuleType());
-			bean.setRootOrgId(entity.getRootOrgId());
-			beanList.add(bean);
-		}
-		resp.setList(beanList);
-		return resp;
-	}
+    @ApiOperation(value = "获取待处理的通知进度数据")
+    @PostMapping("getToBeDisposedNoticeRulePublishProgressList")
+    @Override
+    public GetNoticeRulePublishProgressListResp getToBeDisposedNoticeRulePublishProgressList() {
+        GetNoticeRulePublishProgressListResp resp = new GetNoticeRulePublishProgressListResp();
+        List<NoticeRulePublishProgressEntity> progressEntityList = noticeService
+                .getToBeDisposedNoticeRulePublishProgressList();
+        if (progressEntityList == null || progressEntityList.isEmpty()) {
+            return resp;
+        }
+        List<NoticeRulePublishProgressBean> beanList = new ArrayList<>();
+        for (NoticeRulePublishProgressEntity entity : progressEntityList) {
+            NoticeRulePublishProgressBean bean = new NoticeRulePublishProgressBean();
+            bean.setId(entity.getId());
+            bean.setMaxCommonUserId(entity.getMaxCommonUserId());
+            bean.setMaxStudentId(entity.getMaxStudentId());
+            bean.setNoticeId(entity.getNoticeId());
+            bean.setNoticeReceiverRuleType(entity.getNoticeReceiverRuleType());
+            bean.setRootOrgId(entity.getRootOrgId());
+            beanList.add(bean);
+        }
+        resp.setList(beanList);
+        return resp;
+    }
 
-	@ApiOperation(value = "处理待发布和发布中的数据")
-	@PostMapping("disposePublishingUserNotice")
-	@Transactional
-	@Override
-	public DisposePublishingUserNoticeResp disposePublishingUserNotice(
-			@RequestBody DisposePublishingUserNoticeReq req) {
-		// 处理发布中的通知数据,并返回下一个要处理的用户id
-		Long nextUserId = noticeService.disposePublishingUserNotice(req.getStartUserId(),
-				copytNoticeRulePublishProgressEntityFrom(req.getNoticeRulePublishProgress()));
+    @ApiOperation(value = "处理待发布和发布中的数据")
+    @PostMapping("disposePublishingUserNotice")
+    @Transactional
+    @Override
+    public DisposePublishingUserNoticeResp disposePublishingUserNotice(
+            @RequestBody DisposePublishingUserNoticeReq req) {
+        // 处理发布中的通知数据,并返回下一个要处理的用户id
+        Long nextUserId = noticeService.disposePublishingUserNotice(req.getStartUserId(),
+                copytNoticeRulePublishProgressEntityFrom(req.getNoticeRulePublishProgress()));
 
-		DisposePublishingUserNoticeResp resp = new DisposePublishingUserNoticeResp();
-		resp.setNextUserId(nextUserId);
-		return resp;
-	}
+        DisposePublishingUserNoticeResp resp = new DisposePublishingUserNoticeResp();
+        resp.setNextUserId(nextUserId);
+        return resp;
+    }
 
-	// 实体转换
-	private NoticeRulePublishProgressEntity copytNoticeRulePublishProgressEntityFrom(
-			NoticeRulePublishProgressBean processBean) {
-		NoticeRulePublishProgressEntity processEntity = new NoticeRulePublishProgressEntity();
-		processEntity.setId(processBean.getId());
-		processEntity.setMaxCommonUserId(processBean.getMaxCommonUserId());
-		processEntity.setMaxStudentId(processBean.getMaxStudentId());
-		processEntity.setNoticeId(processBean.getNoticeId());
-		processEntity.setNoticeReceiverRuleType(processBean.getNoticeReceiverRuleType());
-		processEntity.setRootOrgId(processBean.getRootOrgId());
-		return processEntity;
-	}
+    // 实体转换
+    private NoticeRulePublishProgressEntity copytNoticeRulePublishProgressEntityFrom(
+            NoticeRulePublishProgressBean processBean) {
+        NoticeRulePublishProgressEntity processEntity = new NoticeRulePublishProgressEntity();
+        processEntity.setId(processBean.getId());
+        processEntity.setMaxCommonUserId(processBean.getMaxCommonUserId());
+        processEntity.setMaxStudentId(processBean.getMaxStudentId());
+        processEntity.setNoticeId(processBean.getNoticeId());
+        processEntity.setNoticeReceiverRuleType(processBean.getNoticeReceiverRuleType());
+        processEntity.setRootOrgId(processBean.getRootOrgId());
+        return processEntity;
+    }
 
-	@ApiOperation(value = "清理过期的通知数据")
-	@PostMapping("disposeOverdueNotice")
-	@Transactional
-	@Override
-	public void disposeOverdueNotice() {
-		noticeService.disposeOverdueNotice();
-	}
+    @ApiOperation(value = "清理过期的通知数据")
+    @PostMapping("disposeOverdueNotice")
+    @Transactional
+    @Override
+    public void disposeOverdueNotice() {
+        noticeService.disposeOverdueNotice();
+    }
 
-	@ApiOperation(value = "更新通知状态")
-	@PostMapping("updateNoticeStatus")
-	@Transactional
-	@Override
-	public void updateNoticeStatus(@RequestBody UpdateNoticeStatusReq updateNoticeStatusReq) {
-		noticeService.updateNoticeStatus(updateNoticeStatusReq.getNoticeId(),
-				updateNoticeStatusReq.getNoticeStatus());
-	}
+    @ApiOperation(value = "更新通知状态")
+    @PostMapping("updateNoticeStatus")
+    @Transactional
+    @Override
+    public void updateNoticeStatus(@RequestBody UpdateNoticeStatusReq updateNoticeStatusReq) {
+        noticeService.updateNoticeStatus(updateNoticeStatusReq.getNoticeId(),
+                updateNoticeStatusReq.getNoticeStatus());
+    }
 
 }

+ 33 - 0
examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/ExamStageRepo.java

@@ -0,0 +1,33 @@
+package cn.com.qmth.examcloud.core.examwork.dao;
+
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStageEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * @Description 考试场次
+ * @Author lideyin
+ * @Date 2020/7/22 14:54
+ * @Version 1.0
+ */
+public interface ExamStageRepo
+        extends
+        JpaRepository<ExamStageEntity, Long>,
+        QueryByExampleExecutor<ExamStageEntity>,
+        JpaSpecificationExecutor<ExamStageEntity> {
+
+    ExamStageEntity findFirstByExamIdOrderByStageOrderDesc(Long examId);
+
+    List<ExamStageEntity> findByExamId(Long examId);
+
+    ExamStageEntity findByExamIdAndStageOrder(Long examId,Integer stageOrder);
+
+    @Transactional
+    @Modifying
+    void deleteByExamId(Long examId);
+}

+ 16 - 0
examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/ExamStageSettingRepo.java

@@ -0,0 +1,16 @@
+package cn.com.qmth.examcloud.core.examwork.dao;
+
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStageSettingEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+public interface ExamStageSettingRepo
+        extends
+        JpaRepository<ExamStageSettingEntity, Long>,
+        QueryByExampleExecutor<ExamStageSettingEntity>,
+        JpaSpecificationExecutor<ExamStageSettingEntity> {
+
+    ExamStageSettingEntity findByExamId(Long examId);
+
+}

+ 5 - 0
examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/ExamStudentRepo.java

@@ -10,6 +10,7 @@ import org.springframework.data.repository.query.Param;
 import org.springframework.data.repository.query.QueryByExampleExecutor;
 
 import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStudentEntity;
+import org.springframework.transaction.annotation.Transactional;
 
 public interface ExamStudentRepo
 		extends
@@ -82,4 +83,8 @@ public interface ExamStudentRepo
 
 	List<ExamStudentEntity> findByExamIdAndStudentId(Long examId, Long studentId);
 
+	@Modifying
+	@Transactional
+	@Query("update ExamStudentEntity s set s.examStageId = null,s.examStageOrder=null where s.examId = ?1")
+    void unbindExamStudentExamStage(Long examId);
 }

+ 166 - 0
examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/entity/ExamStageEntity.java

@@ -0,0 +1,166 @@
+package cn.com.qmth.examcloud.core.examwork.dao.entity;
+
+import cn.com.qmth.examcloud.api.commons.enums.ExamStageStartExamStatus;
+import cn.com.qmth.examcloud.api.commons.enums.SubmitType;
+import cn.com.qmth.examcloud.web.jpa.WithIdJpaEntity;
+import org.hibernate.annotations.DynamicInsert;
+import org.hibernate.annotations.DynamicUpdate;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.persistence.*;
+import java.util.Date;
+
+/**
+ * @Description 场次
+ * @Author lideyin
+ * @Date 2020/7/21 17:51
+ * @Version 1.0
+ */
+@SuppressWarnings("ALL")
+@Entity
+@Table(name = "ec_e_exam_stage", indexes = {
+        @Index(name = "IDX_E_ES_001001", columnList = "examId")})
+@DynamicInsert
+@DynamicUpdate
+public class ExamStageEntity extends WithIdJpaEntity {
+
+
+    private static final long serialVersionUID = -3000782817649410227L;
+    /**
+     * 顶级机构Id
+     */
+    @Column(nullable = false)
+    private Long rootOrgId;
+
+    /**
+     * 考试id
+     */
+    @Column(nullable = false)
+    private Long examId;
+
+    /**
+     * 场次号
+     */
+    @Column(nullable = false)
+    private Integer stageOrder;
+
+    /**
+     * 开始进入考试时间
+     */
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date startTime;
+
+    /**
+     * 考试批次结束时间
+     */
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date endTime;
+
+    @Column(nullable = false)
+    private Boolean enable;
+
+    /**
+     * 是否特殊设置
+     */
+    @Column(nullable = false)
+    private Boolean specialSetting;
+
+    /**
+     * 回收试卷类型
+     */
+    @Column(nullable = false, length = 50)
+    @Enumerated(EnumType.STRING)
+    private SubmitType submitType;
+
+    /**
+     * 统一收卷考试时长
+     */
+    private Integer submitDuration;
+
+    /**
+     * 场次开考状态
+     */
+    @Column(nullable = false, length = 50)
+    @Enumerated(EnumType.STRING)
+    private ExamStageStartExamStatus startExamStatus;
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public Integer getStageOrder() {
+        return stageOrder;
+    }
+
+    public void setStageOrder(Integer stageOrder) {
+        this.stageOrder = stageOrder;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Boolean enable) {
+        this.enable = enable;
+    }
+
+    public Boolean getSpecialSetting() {
+        return specialSetting;
+    }
+
+    public void setSpecialSetting(Boolean specialSetting) {
+        this.specialSetting = specialSetting;
+    }
+
+    public SubmitType getSubmitType() {
+        return submitType;
+    }
+
+    public void setSubmitType(SubmitType submitType) {
+        this.submitType = submitType;
+    }
+
+    public Integer getSubmitDuration() {
+        return submitDuration;
+    }
+
+    public void setSubmitDuration(Integer submitDuration) {
+        this.submitDuration = submitDuration;
+    }
+
+    public ExamStageStartExamStatus getStartExamStatus() {
+        return startExamStatus;
+    }
+
+    public void setStartExamStatus(ExamStageStartExamStatus startExamStatus) {
+        this.startExamStatus = startExamStatus;
+    }
+}

+ 66 - 0
examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/entity/ExamStageSettingEntity.java

@@ -0,0 +1,66 @@
+package cn.com.qmth.examcloud.core.examwork.dao.entity;
+
+import cn.com.qmth.examcloud.api.commons.enums.SubmitType;
+import cn.com.qmth.examcloud.web.jpa.WithIdJpaEntity;
+import org.hibernate.annotations.DynamicInsert;
+import org.hibernate.annotations.DynamicUpdate;
+
+import javax.persistence.*;
+
+/**
+ * @Description 场次通用设置表
+ * @Author lideyin
+ * @Date 2020/7/21 17:51
+ * @Version 1.0
+ */
+@Entity
+@Table(name = "ec_e_exam_stage_setting", indexes = {
+        @Index(name = "IDX_E_ESS_001001", columnList = "examId", unique = true)})
+@DynamicInsert
+@DynamicUpdate
+public class ExamStageSettingEntity extends WithIdJpaEntity {
+
+    private static final long serialVersionUID = -7000467877941497018L;
+
+    /**
+     * 考试id
+     */
+    @Column(nullable = false)
+    private Long examId;
+
+    /**
+     * 回收试卷类型
+     */
+    @Column(nullable = false, length = 50)
+    @Enumerated(EnumType.STRING)
+    private SubmitType submitType;
+
+    /**
+     * 统一收卷考试时长
+     */
+    private Integer submitDuration;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public SubmitType getSubmitType() {
+        return submitType;
+    }
+
+    public void setSubmitType(SubmitType submitType) {
+        this.submitType = submitType;
+    }
+
+    public Integer getSubmitDuration() {
+        return submitDuration;
+    }
+
+    public void setSubmitDuration(Integer submitDuration) {
+        this.submitDuration = submitDuration;
+    }
+}

+ 25 - 0
examcloud-core-examwork-dao/src/main/java/cn/com/qmth/examcloud/core/examwork/dao/entity/ExamStudentEntity.java

@@ -130,6 +130,16 @@ public class ExamStudentEntity extends JpaEntity {
 	@Column(nullable = false)
 	private Boolean enable;
 
+	/**
+	 * 场次id
+	 */
+	private Long examStageId;
+
+	/**
+	 * 场次号
+	 */
+	private Integer examStageOrder;
+
 	/**
 	 * 扩展属性1
 	 */
@@ -355,4 +365,19 @@ public class ExamStudentEntity extends JpaEntity {
 		this.ext5 = ext5;
 	}
 
+	public Long getExamStageId() {
+		return examStageId;
+	}
+
+	public void setExamStageId(Long examStageId) {
+		this.examStageId = examStageId;
+	}
+
+	public Integer getExamStageOrder() {
+		return examStageOrder;
+	}
+
+	public void setExamStageOrder(Integer examStageOrder) {
+		this.examStageOrder = examStageOrder;
+	}
 }

+ 61 - 58
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/ExamService.java

@@ -1,15 +1,16 @@
 package cn.com.qmth.examcloud.core.examwork.service;
 
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-
 import cn.com.qmth.examcloud.api.commons.enums.CURD;
 import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamEntity;
 import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamSpecialSettingsEntity;
 import cn.com.qmth.examcloud.core.examwork.service.bean.ExamInfo;
 import cn.com.qmth.examcloud.core.examwork.service.bean.ExamSpecialSettingsInfo;
 
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * 类注释
  *
@@ -19,64 +20,66 @@ import cn.com.qmth.examcloud.core.examwork.service.bean.ExamSpecialSettingsInfo;
  */
 public interface ExamService {
 
-	/**
-	 * 保存考试
-	 *
-	 * @author WANGWEI
-	 * @param examInfo
-	 * @return
-	 */
-	ExamEntity saveExam(ExamInfo examInfo, CURD es);
+    /**
+     * 保存考试
+     *
+     * @param examInfo
+     * @return
+     * @author WANGWEI
+     */
+    ExamEntity saveExam(ExamInfo examInfo, CURD es);
+
+    /**
+     * 保存考试机构设置
+     *
+     * @param examSpecialInfo
+     * @return
+     * @author WANGWEI
+     */
+    ExamSpecialSettingsEntity saveExamSpecialSettings(ExamSpecialSettingsInfo examSpecialInfo);
 
-	/**
-	 * 保存考试机构设置
-	 *
-	 * @author WANGWEI
-	 * @param examSpecialInfo
-	 * @return
-	 */
-	ExamSpecialSettingsEntity saveExamSpecialSettings(ExamSpecialSettingsInfo examSpecialInfo);
+    /**
+     * 查询考试机构属性
+     *
+     * @param examId
+     * @param orgId
+     * @param key
+     * @return
+     * @author WANGWEI
+     */
+    String getExamOrgProperty(Long examId, Long orgId, String key);
 
-	/**
-	 * 查询考试机构属性
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @param orgId
-	 * @param key
-	 * @return
-	 */
-	String getExamOrgProperty(Long examId, Long orgId, String key);
+    /**
+     * 查询考试学生属性
+     *
+     * @param examId
+     * @param studentId
+     * @param key
+     * @return
+     * @author WANGWEI
+     */
+    String getExamStudentProperty(Long examId, Long studentId, String key);
 
-	/**
-	 * 查询考试学生属性
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @param studentId
-	 * @param key
-	 * @return
-	 */
-	String getExamStudentProperty(Long examId, Long studentId, String key);
+    /**
+     * 导入学习中心设置
+     *
+     * @param examId
+     * @param file
+     * @return
+     * @author WANGWEI
+     */
+    List<Map<String, Object>> importExamOrgSettings(Long examId, File file);
 
-	/**
-	 * 导入学习中心设置
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @param file
-	 * @return
-	 */
-	List<Map<String, Object>> importExamOrgSettings(Long examId, File file);
+    /**
+     * 查询考试属性
+     *
+     * @param examId
+     * @param key
+     * @return
+     * @author WANGWEI
+     */
+    String getExamProperty(Long examId, String key);
 
-	/**
-	 * 查询考试属性
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @param key
-	 * @return
-	 */
-	String getExamProperty(Long examId, String key);
+    Map<Long, String> getExamNamesByIds(Set<Long> examIds);
 
 }

+ 11 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/ExamStageService.java

@@ -0,0 +1,11 @@
+package cn.com.qmth.examcloud.core.examwork.service;
+
+/**
+ * @Description 考试场次
+ * @Author lideyin
+ * @Date 2020/7/22 14:56
+ * @Version 1.0
+ */
+public interface ExamStageService {
+
+}

+ 11 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/ExamStageSettingService.java

@@ -0,0 +1,11 @@
+package cn.com.qmth.examcloud.core.examwork.service;
+
+/**
+ * @Description 考试场次设置
+ * @Author lideyin
+ * @Date 2020/7/22 14:56
+ * @Version 1.0
+ */
+public interface ExamStageSettingService {
+
+}

+ 13 - 6
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/ExamStudentService.java

@@ -1,9 +1,9 @@
 package cn.com.qmth.examcloud.core.examwork.service;
 
-import java.util.List;
-
 import cn.com.qmth.examcloud.core.examwork.service.bean.ExamStudentInfo;
 
+import java.util.List;
+
 /**
  * 类注释
  *
@@ -13,12 +13,19 @@ import cn.com.qmth.examcloud.core.examwork.service.bean.ExamStudentInfo;
  */
 public interface ExamStudentService {
 
-	public ExamStudentInfo saveExamStudent(ExamStudentInfo examStudentInfo);
+    public ExamStudentInfo saveExamStudent(ExamStudentInfo examStudentInfo);
+
+    public void deleteExamStudentsByExamId(Long examId);
 
-	public void deleteExamStudentsByExamId(Long examId);
+    public void deleteExamStudentsByStudentIds(List<Long> studentIds);
 
-	public void deleteExamStudentsByStudentIds(List<Long> studentIds);
+    public void syncExamStudent(Long studentId);
 
-	public void syncExamStudent(Long studentId);
+    /**
+     * 解绑考试下所有的场次信息
+     *
+     * @param examId
+     */
+    void unbindExamStudentExamStage(Long examId);
 
 }

+ 71 - 71
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/NoticeService.java

@@ -20,85 +20,85 @@ import java.util.List;
  */
 public interface NoticeService {
 
-	/**
-	 * 获取用户通知列表
-	 * 
-	 * @param query
-	 * @return
-	 */
-	List<UserNoticeInfo> getNoticeList(UserNoticeInfoQuery query);
+    /**
+     * 获取用户通知列表
+     *
+     * @param query
+     * @return
+     */
+    List<UserNoticeInfo> getNoticeList(UserNoticeInfoQuery query);
 
-	/**
-	 * 更新用户的通知状态为已读
-	 * 
-	 * @param noticeId
-	 *            通知id(多个以逗号分隔)
-	 * @param userType
-	 *            用户类型
-	 * @param userId
-	 *            用户id
-	 * @return
-	 */
-	int updateNoticeReadStatus(String noticeId, UserType userType, Long userId);
+    /**
+     * 更新用户的通知状态为已读
+     *
+     * @param noticeId 通知id(多个以逗号分隔)
+     * @param userType 用户类型
+     * @param userId   用户id
+     * @return
+     */
+    int updateNoticeReadStatus(String noticeId, UserType userType, Long userId);
 
-	/**
-	 * 分页获取通知列表
-	 * 
-	 * @param curPage
-	 * @param pageSize
-	 * @param infoQuery
-	 * @return
-	 */
-	PageInfo<NoticeInfo> getPagedNoticeList(Integer curPage, Integer pageSize,
-			NoticeInfoQuery infoQuery);
+    /**
+     * 分页获取通知列表
+     *
+     * @param curPage
+     * @param pageSize
+     * @param infoQuery
+     * @return
+     */
+    PageInfo<NoticeInfo> getPagedNoticeList(Integer curPage, Integer pageSize,
+                                            NoticeInfoQuery infoQuery);
 
-	/**
-	 * 删除通知
-	 * 
-	 * @param rootOrgId
-	 * @param noticeIdList
-	 * @return
-	 */
-	int deleteNotice(Long rootOrgId, List<Long> noticeIdList);
+    /**
+     * 删除通知
+     *
+     * @param rootOrgId
+     * @param noticeIdList
+     * @return
+     */
+    int deleteNotice(Long rootOrgId, List<Long> noticeIdList);
 
-	/**
-	 * 添加通知
-	 * 
-	 * @param addNoticeInfo
-	 * @return
-	 */
-	int addNotice(AddNoticeInfo addNoticeInfo);
+    /**
+     * 添加通知
+     *
+     * @param addNoticeInfo
+     * @return
+     */
+    int addNotice(AddNoticeInfo addNoticeInfo);
 
-	/**
-	 * 更新通知信息
-	 * 
-	 * @param info
-	 * @return
-	 */
-	NoticeRulePublishProgressEntity updateNotice(UpdateNoticeInfo info);
+    /**
+     * 更新通知信息
+     *
+     * @param info
+     * @return
+     */
+    NoticeRulePublishProgressEntity updateNotice(UpdateNoticeInfo info);
 
-	/**
-	 * 获取待处理的通知规则发布进度集合
-	 * @return
-	 */
+    /**
+     * 获取待处理的通知规则发布进度集合
+     *
+     * @return
+     */
     List<NoticeRulePublishProgressEntity> getToBeDisposedNoticeRulePublishProgressList();
 
-	/**
-	 * 处理待发送的用户通知(自动服务调用)
-	 * @param startUserId
-	 * @return 下次请求的用户id
-	 */
-	Long disposePublishingUserNotice(Long startUserId,NoticeRulePublishProgressEntity rulePublishProcess);
+    /**
+     * 处理待发送的用户通知(自动服务调用)
+     *
+     * @param startUserId
+     * @return 下次请求的用户id
+     */
+    Long disposePublishingUserNotice(Long startUserId, NoticeRulePublishProgressEntity rulePublishProcess);
 
-	/**
-	 * 处理过期的通知数据(自动服务调用)
-	 */
-	void disposeOverdueNotice();
+    /**
+     * 处理过期的通知数据(自动服务调用)
+     */
+    void disposeOverdueNotice();
 
-	/**
-	 * 更新通知状态
-	 * @param noticeId
-	 * @param noticeStatus
-	 */
-	void updateNoticeStatus(Long noticeId, NoticeStatus noticeStatus);
+    /**
+     * 更新通知状态
+     *
+     * @param noticeId
+     * @param noticeStatus
+     */
+    void updateNoticeStatus(Long noticeId, NoticeStatus noticeStatus);
 }

+ 14 - 14
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/OnGoingExamService.java

@@ -1,9 +1,9 @@
 package cn.com.qmth.examcloud.core.examwork.service;
 
-import java.util.List;
-
 import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamSpecialSettingsEntity;
 
+import java.util.List;
+
 /**
  * 待考考试服务
  *
@@ -13,17 +13,17 @@ import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamSpecialSettingsEntity;
  */
 public interface OnGoingExamService {
 
-	/**
-	 * 待考考试列表
-	 *
-	 * @author WANGWEI
-	 * @param rootOrgId
-	 * @param examType
-	 * @param orgId
-	 * @param studentId
-	 * @return
-	 */
-	List<ExamSpecialSettingsEntity> getOngoingExamList(Long rootOrgId, String examType, Long orgId,
-			Long studentId);
+    /**
+     * 待考考试列表
+     *
+     * @param rootOrgId
+     * @param examType
+     * @param orgId
+     * @param studentId
+     * @return
+     * @author WANGWEI
+     */
+    List<ExamSpecialSettingsEntity> getOngoingExamList(Long rootOrgId, String examType, Long orgId,
+                                                       Long studentId);
 
 }

+ 5 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/AddNoticeInfo.java

@@ -26,14 +26,17 @@ public class AddNoticeInfo implements JsonSerializable {
      * 组织机构id
      */
     private Long rootOrgId;
+
     /**
      * 用户id
      */
     private Long userId;
+
     /**
      * 标题
      */
     private String title;
+
     /**
      * 内容
      */
@@ -51,10 +54,12 @@ public class AddNoticeInfo implements JsonSerializable {
     @Enumerated(EnumType.STRING)
     @NotNull(message = "公告状态")
     private NoticeStatus noticeStatus;
+
     /**
      * 发送对象
      */
     private String publishObjectId;
+
     /**
      * 发布者
      */

+ 187 - 188
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/ExamInfo.java

@@ -1,15 +1,14 @@
 package cn.com.qmth.examcloud.core.examwork.service.bean;
 
-import java.util.Date;
-import java.util.Map;
+import cn.com.qmth.examcloud.api.commons.enums.ExamSpecialSettingsType;
+import cn.com.qmth.examcloud.api.commons.enums.ExamType;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
 
 import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
 import javax.validation.constraints.NotNull;
-
-import cn.com.qmth.examcloud.api.commons.enums.ExamSpecialSettingsType;
-import cn.com.qmth.examcloud.api.commons.enums.ExamType;
-import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import java.util.Date;
+import java.util.Map;
 
 /**
  * 类注释
@@ -20,190 +19,190 @@ import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
  */
 public class ExamInfo implements JsonSerializable {
 
-	private static final long serialVersionUID = 4009839764353162256L;
-
-	/**
-	 * 顶级机构Id
-	 */
-	private Long rootOrgId;
-
-	/**
-	 * 考试批次开始时间
-	 */
-	private Date beginTime;
-
-	/**
-	 * 考试批次结束时间
-	 */
-	private Date endTime;
-
-	/**
-	 * 考试名称
-	 */
-	@NotNull
-	private String name;
-
-	/**
-	 * 考试编码
-	 */
-	private String code;
-
-	/**
-	 * 考试类型
-	 */
-	@Enumerated(EnumType.STRING)
-	private ExamType examType;
-
-	/**
-	 * 考试时长
-	 */
-	private Integer duration;
-
-	/**
-	 * 是否可用
-	 */
-	private Boolean enable;
-
-	/**
-	 * 考试备注
-	 */
-	private String remark;
-
-	/**
-	 * 考试次数
-	 */
-	private Long examTimes;
-
-	/**
-	 * 是否禁止考试
-	 */
-	private Boolean examLimit;
-
-	/**
-	 * 开启特殊设置
-	 */
-	private Boolean specialSettingsEnabled;
-
-	/**
-	 * 特殊设置类型
-	 */
-	private ExamSpecialSettingsType specialSettingsType;
-
-	/**
-	 * 考试属性
-	 */
-	private Map<String, String> properties;
-
-	public Long getRootOrgId() {
-		return rootOrgId;
-	}
-
-	public void setRootOrgId(Long rootOrgId) {
-		this.rootOrgId = rootOrgId;
-	}
-
-	public Date getBeginTime() {
-		return beginTime;
-	}
-
-	public void setBeginTime(Date beginTime) {
-		this.beginTime = beginTime;
-	}
-
-	public Date getEndTime() {
-		return endTime;
-	}
-
-	public void setEndTime(Date endTime) {
-		this.endTime = endTime;
-	}
-
-	public String getName() {
-		return name;
-	}
-
-	public void setName(String name) {
-		this.name = name;
-	}
-
-	public String getCode() {
-		return code;
-	}
-
-	public void setCode(String code) {
-		this.code = code;
-	}
-
-	public ExamType getExamType() {
-		return examType;
-	}
-
-	public void setExamType(ExamType examType) {
-		this.examType = examType;
-	}
-
-	public Integer getDuration() {
-		return duration;
-	}
-
-	public void setDuration(Integer duration) {
-		this.duration = duration;
-	}
-
-	public Boolean getEnable() {
-		return enable;
-	}
-
-	public void setEnable(Boolean enable) {
-		this.enable = enable;
-	}
-
-	public String getRemark() {
-		return remark;
-	}
-
-	public void setRemark(String remark) {
-		this.remark = remark;
-	}
-
-	public Long getExamTimes() {
-		return examTimes;
-	}
-
-	public void setExamTimes(Long examTimes) {
-		this.examTimes = examTimes;
-	}
-
-	public Boolean getExamLimit() {
-		return examLimit;
-	}
-
-	public void setExamLimit(Boolean examLimit) {
-		this.examLimit = examLimit;
-	}
-
-	public Map<String, String> getProperties() {
-		return properties;
-	}
-
-	public void setProperties(Map<String, String> properties) {
-		this.properties = properties;
-	}
-
-	public Boolean getSpecialSettingsEnabled() {
-		return specialSettingsEnabled;
-	}
+    private static final long serialVersionUID = 4009839764353162256L;
+
+    /**
+     * 顶级机构Id
+     */
+    private Long rootOrgId;
+
+    /**
+     * 考试批次开始时间
+     */
+    private Date beginTime;
+
+    /**
+     * 考试批次结束时间
+     */
+    private Date endTime;
+
+    /**
+     * 考试名称
+     */
+    @NotNull
+    private String name;
+
+    /**
+     * 考试编码
+     */
+    private String code;
+
+    /**
+     * 考试类型
+     */
+    @Enumerated(EnumType.STRING)
+    private ExamType examType;
+
+    /**
+     * 考试时长
+     */
+    private Integer duration;
+
+    /**
+     * 是否可用
+     */
+    private Boolean enable;
+
+    /**
+     * 考试备注
+     */
+    private String remark;
+
+    /**
+     * 考试次数
+     */
+    private Long examTimes;
+
+    /**
+     * 是否禁止考试
+     */
+    private Boolean examLimit;
+
+    /**
+     * 开启特殊设置
+     */
+    private Boolean specialSettingsEnabled;
+
+    /**
+     * 特殊设置类型
+     */
+    private ExamSpecialSettingsType specialSettingsType;
+
+    /**
+     * 考试属性
+     */
+    private Map<String, String> properties;
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public Date getBeginTime() {
+        return beginTime;
+    }
+
+    public void setBeginTime(Date beginTime) {
+        this.beginTime = beginTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public ExamType getExamType() {
+        return examType;
+    }
+
+    public void setExamType(ExamType examType) {
+        this.examType = examType;
+    }
+
+    public Integer getDuration() {
+        return duration;
+    }
+
+    public void setDuration(Integer duration) {
+        this.duration = duration;
+    }
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Boolean enable) {
+        this.enable = enable;
+    }
+
+    public String getRemark() {
+        return remark;
+    }
+
+    public void setRemark(String remark) {
+        this.remark = remark;
+    }
+
+    public Long getExamTimes() {
+        return examTimes;
+    }
+
+    public void setExamTimes(Long examTimes) {
+        this.examTimes = examTimes;
+    }
+
+    public Boolean getExamLimit() {
+        return examLimit;
+    }
+
+    public void setExamLimit(Boolean examLimit) {
+        this.examLimit = examLimit;
+    }
+
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public void setProperties(Map<String, String> properties) {
+        this.properties = properties;
+    }
+
+    public Boolean getSpecialSettingsEnabled() {
+        return specialSettingsEnabled;
+    }
 
-	public void setSpecialSettingsEnabled(Boolean specialSettingsEnabled) {
-		this.specialSettingsEnabled = specialSettingsEnabled;
-	}
+    public void setSpecialSettingsEnabled(Boolean specialSettingsEnabled) {
+        this.specialSettingsEnabled = specialSettingsEnabled;
+    }
 
-	public ExamSpecialSettingsType getSpecialSettingsType() {
-		return specialSettingsType;
-	}
+    public ExamSpecialSettingsType getSpecialSettingsType() {
+        return specialSettingsType;
+    }
 
-	public void setSpecialSettingsType(ExamSpecialSettingsType specialSettingsType) {
-		this.specialSettingsType = specialSettingsType;
-	}
+    public void setSpecialSettingsType(ExamSpecialSettingsType specialSettingsType) {
+        this.specialSettingsType = specialSettingsType;
+    }
 
 }

+ 103 - 103
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/ExamSpecialSettingsInfo.java

@@ -1,10 +1,10 @@
 package cn.com.qmth.examcloud.core.examwork.service.bean;
 
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
 import java.util.Date;
 import java.util.Map;
 
-import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
-
 /**
  * 考试特殊设置
  *
@@ -14,130 +14,130 @@ import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
  */
 public class ExamSpecialSettingsInfo implements JsonSerializable {
 
-	private static final long serialVersionUID = -3335725218626631530L;
+    private static final long serialVersionUID = -3335725218626631530L;
 
-	private Long id;
+    private Long id;
 
-	/**
-	 * 考试ID
-	 */
-	private Long examId;
+    /**
+     * 考试ID
+     */
+    private Long examId;
 
-	/**
-	 * 顶级机构ID
-	 */
-	private Long rootOrgId;
+    /**
+     * 顶级机构ID
+     */
+    private Long rootOrgId;
 
-	/**
-	 * 机构ID
-	 */
-	private Long orgId;
+    /**
+     * 机构ID
+     */
+    private Long orgId;
 
-	/**
-	 * 学生ID
-	 */
-	private Long studentId;
+    /**
+     * 学生ID
+     */
+    private Long studentId;
 
-	/**
-	 * 课程ID
-	 */
-	private Long courseId;
+    /**
+     * 课程ID
+     */
+    private Long courseId;
 
-	/**
-	 * 考试批次开始时间
-	 */
-	private Date beginTime;
+    /**
+     * 考试批次开始时间
+     */
+    private Date beginTime;
 
-	/**
-	 * 考试批次结束时间
-	 */
-	private Date endTime;
+    /**
+     * 考试批次结束时间
+     */
+    private Date endTime;
 
-	/**
-	 * 是否禁止考试
-	 */
-	private Boolean examLimit;
+    /**
+     * 是否禁止考试
+     */
+    private Boolean examLimit;
 
-	private Map<String, String> properties;
+    private Map<String, String> properties;
 
-	public Long getId() {
-		return id;
-	}
+    public Long getId() {
+        return id;
+    }
 
-	public void setId(Long id) {
-		this.id = id;
-	}
+    public void setId(Long id) {
+        this.id = id;
+    }
 
-	public Long getExamId() {
-		return examId;
-	}
+    public Long getExamId() {
+        return examId;
+    }
 
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
 
-	public Long getRootOrgId() {
-		return rootOrgId;
-	}
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
 
-	public void setRootOrgId(Long rootOrgId) {
-		this.rootOrgId = rootOrgId;
-	}
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
 
-	public Long getOrgId() {
-		return orgId;
-	}
+    public Long getOrgId() {
+        return orgId;
+    }
 
-	public void setOrgId(Long orgId) {
-		this.orgId = orgId;
-	}
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
 
-	public Long getStudentId() {
-		return studentId;
-	}
+    public Long getStudentId() {
+        return studentId;
+    }
 
-	public void setStudentId(Long studentId) {
-		this.studentId = studentId;
-	}
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
 
-	public Long getCourseId() {
-		return courseId;
-	}
+    public Long getCourseId() {
+        return courseId;
+    }
 
-	public void setCourseId(Long courseId) {
-		this.courseId = courseId;
-	}
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
 
-	public Date getBeginTime() {
-		return beginTime;
-	}
+    public Date getBeginTime() {
+        return beginTime;
+    }
 
-	public void setBeginTime(Date beginTime) {
-		this.beginTime = beginTime;
-	}
-
-	public Date getEndTime() {
-		return endTime;
-	}
-
-	public void setEndTime(Date endTime) {
-		this.endTime = endTime;
-	}
-
-	public Boolean getExamLimit() {
-		return examLimit;
-	}
-
-	public void setExamLimit(Boolean examLimit) {
-		this.examLimit = examLimit;
-	}
-
-	public Map<String, String> getProperties() {
-		return properties;
-	}
-
-	public void setProperties(Map<String, String> properties) {
-		this.properties = properties;
-	}
+    public void setBeginTime(Date beginTime) {
+        this.beginTime = beginTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public Boolean getExamLimit() {
+        return examLimit;
+    }
+
+    public void setExamLimit(Boolean examLimit) {
+        this.examLimit = examLimit;
+    }
+
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public void setProperties(Map<String, String> properties) {
+        this.properties = properties;
+    }
 
 }

+ 371 - 346
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/ExamStudentInfo.java

@@ -11,391 +11,416 @@ import java.util.Date;
  */
 public class ExamStudentInfo {
 
-	private Long id;
-
-	/**
-	 * 顶级机构ID
-	 */
-	private Long rootOrgId;
-
-	/**
-	 * 考试ID
-	 */
-	private Long examId;
-
-	/**
-	 * 考试编码
-	 */
-	private String examCode;
-
-	/**
-	 * 考试名称
-	 */
-	private String examName;
-
-	/**
-	 * 学生ID
-	 */
-	private Long studentId;
-
-	/**
-	 * 机构ID
-	 */
-	private Long orgId;
-
-	/**
-	 * 机构名称
-	 */
-	private String orgName;
-
-	/**
-	 * 机构编码
-	 */
-	private String orgCode;
-
-	/**
-	 * 学生姓名
-	 */
-	private String studentName;
-
-	/**
-	 * 学生学号
-	 */
-	private String studentCode;
-
-	/**
-	 * 学生身份证号
-	 */
-	private String identityNumber;
-
-	/**
-	 * 考试课程名称
-	 */
-	private String courseName;
-
-	/**
-	 * 考试课程ID
-	 */
-	private Long courseId;
-
-	/**
-	 * 考试课程code
-	 */
-	private String courseCode;
-
-	/**
-	 * 考试课程level
-	 */
-	private String courseLevel;
-
-	/**
-	 * 是否可用
-	 */
-	private Boolean enable;
-
-	/**
-	 * 试卷类型
-	 */
-	private String paperType;
-
-	/**
-	 * 信息采集人
-	 */
-	private String infoCollector;
-
-	/**
-	 * 考点
-	 */
-	private String examSite;
-
-	/**
-	 * 专业名称
-	 */
-	private String specialtyName;
-
-	/**
-	 * 年级
-	 */
-	private String grade;
-
-	/**
-	 * 备注
-	 */
-	private String remark;
-
-	/**
-	 * 特殊考试批次开始时间
-	 */
-	private Date specialBeginTime;
-
-	/**
-	 * 特殊考试批次结束时间
-	 */
-	private Date specialEndTime;
-
-	/**
-	 * 扩展属性1
-	 */
-	private String ext1;
-
-	/**
-	 * 扩展属性2
-	 */
-	private String ext2;
-
-	/**
-	 * 扩展属性3
-	 */
-	private String ext3;
-
-	/**
-	 * 扩展属性4
-	 */
-	private String ext4;
-
-	/**
-	 * 扩展属性5
-	 */
-	private String ext5;
-
-	public Long getId() {
-		return id;
-	}
-
-	public void setId(Long id) {
-		this.id = id;
-	}
-
-	public Long getRootOrgId() {
-		return rootOrgId;
-	}
-
-	public void setRootOrgId(Long rootOrgId) {
-		this.rootOrgId = rootOrgId;
-	}
-
-	public Long getExamId() {
-		return examId;
-	}
-
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
-
-	public String getExamCode() {
-		return examCode;
-	}
-
-	public void setExamCode(String examCode) {
-		this.examCode = examCode;
-	}
-
-	public String getExamName() {
-		return examName;
-	}
-
-	public void setExamName(String examName) {
-		this.examName = examName;
-	}
-
-	public Long getStudentId() {
-		return studentId;
-	}
-
-	public void setStudentId(Long studentId) {
-		this.studentId = studentId;
-	}
-
-	public String getStudentName() {
-		return studentName;
-	}
-
-	public void setStudentName(String studentName) {
-		this.studentName = studentName;
-	}
-
-	public String getStudentCode() {
-		return studentCode;
-	}
-
-	public void setStudentCode(String studentCode) {
-		this.studentCode = studentCode;
-	}
-
-	public String getIdentityNumber() {
-		return identityNumber;
-	}
-
-	public void setIdentityNumber(String identityNumber) {
-		this.identityNumber = identityNumber;
-	}
-
-	public String getCourseName() {
-		return courseName;
-	}
-
-	public void setCourseName(String courseName) {
-		this.courseName = courseName;
-	}
+    private Long id;
+
+    /**
+     * 顶级机构ID
+     */
+    private Long rootOrgId;
+
+    /**
+     * 考试ID
+     */
+    private Long examId;
+
+    /**
+     * 考试编码
+     */
+    private String examCode;
+
+    /**
+     * 考试名称
+     */
+    private String examName;
+
+    /**
+     * 学生ID
+     */
+    private Long studentId;
+
+    /**
+     * 机构ID
+     */
+    private Long orgId;
+
+    /**
+     * 机构名称
+     */
+    private String orgName;
+
+    /**
+     * 机构编码
+     */
+    private String orgCode;
+
+    /**
+     * 学生姓名
+     */
+    private String studentName;
+
+    /**
+     * 学生学号
+     */
+    private String studentCode;
+
+    /**
+     * 学生身份证号
+     */
+    private String identityNumber;
+
+    /**
+     * 考试课程名称
+     */
+    private String courseName;
+
+    /**
+     * 考试课程ID
+     */
+    private Long courseId;
+
+    /**
+     * 考试课程code
+     */
+    private String courseCode;
+
+    /**
+     * 考试课程level
+     */
+    private String courseLevel;
+
+    /**
+     * 是否可用
+     */
+    private Boolean enable;
+
+    /**
+     * 试卷类型
+     */
+    private String paperType;
+
+    /**
+     * 信息采集人
+     */
+    private String infoCollector;
+
+    /**
+     * 考点
+     */
+    private String examSite;
+
+    /**
+     * 专业名称
+     */
+    private String specialtyName;
+
+    /**
+     * 年级
+     */
+    private String grade;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 特殊考试批次开始时间
+     */
+    private Date specialBeginTime;
+
+    /**
+     * 特殊考试批次结束时间
+     */
+    private Date specialEndTime;
+
+    /**
+     * 场次id
+     */
+    private Long examStageId;
+
+    /**
+     * 场次号
+     */
+    private Integer examStageOrder;
+
+    /**
+     * 扩展属性1
+     */
+    private String ext1;
+
+    /**
+     * 扩展属性2
+     */
+    private String ext2;
+
+    /**
+     * 扩展属性3
+     */
+    private String ext3;
+
+    /**
+     * 扩展属性4
+     */
+    private String ext4;
+
+    /**
+     * 扩展属性5
+     */
+    private String ext5;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getExamCode() {
+        return examCode;
+    }
+
+    public void setExamCode(String examCode) {
+        this.examCode = examCode;
+    }
+
+    public String getExamName() {
+        return examName;
+    }
+
+    public void setExamName(String examName) {
+        this.examName = examName;
+    }
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
 
-	public Long getCourseId() {
-		return courseId;
-	}
+    public Long getCourseId() {
+        return courseId;
+    }
 
-	public void setCourseId(Long courseId) {
-		this.courseId = courseId;
-	}
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
 
-	public String getCourseCode() {
-		return courseCode;
-	}
+    public String getCourseCode() {
+        return courseCode;
+    }
 
-	public void setCourseCode(String courseCode) {
-		this.courseCode = courseCode;
-	}
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
 
-	public String getCourseLevel() {
-		return courseLevel;
-	}
+    public String getCourseLevel() {
+        return courseLevel;
+    }
 
-	public void setCourseLevel(String courseLevel) {
-		this.courseLevel = courseLevel;
-	}
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
 
-	public String getPaperType() {
-		return paperType;
-	}
+    public String getPaperType() {
+        return paperType;
+    }
 
-	public void setPaperType(String paperType) {
-		this.paperType = paperType;
-	}
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
 
-	public String getInfoCollector() {
-		return infoCollector;
-	}
+    public String getInfoCollector() {
+        return infoCollector;
+    }
 
-	public void setInfoCollector(String infoCollector) {
-		this.infoCollector = infoCollector;
-	}
+    public void setInfoCollector(String infoCollector) {
+        this.infoCollector = infoCollector;
+    }
 
-	public String getExamSite() {
-		return examSite;
-	}
+    public String getExamSite() {
+        return examSite;
+    }
 
-	public void setExamSite(String examSite) {
-		this.examSite = examSite;
-	}
+    public void setExamSite(String examSite) {
+        this.examSite = examSite;
+    }
 
-	public String getSpecialtyName() {
-		return specialtyName;
-	}
+    public String getSpecialtyName() {
+        return specialtyName;
+    }
 
-	public void setSpecialtyName(String specialtyName) {
-		this.specialtyName = specialtyName;
-	}
+    public void setSpecialtyName(String specialtyName) {
+        this.specialtyName = specialtyName;
+    }
 
-	public String getGrade() {
-		return grade;
-	}
+    public String getGrade() {
+        return grade;
+    }
 
-	public void setGrade(String grade) {
-		this.grade = grade;
-	}
+    public void setGrade(String grade) {
+        this.grade = grade;
+    }
 
-	public String getRemark() {
-		return remark;
-	}
+    public String getRemark() {
+        return remark;
+    }
 
-	public void setRemark(String remark) {
-		this.remark = remark;
-	}
+    public void setRemark(String remark) {
+        this.remark = remark;
+    }
 
-	public Long getOrgId() {
-		return orgId;
-	}
+    public Long getOrgId() {
+        return orgId;
+    }
 
-	public void setOrgId(Long orgId) {
-		this.orgId = orgId;
-	}
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
 
-	public String getOrgName() {
-		return orgName;
-	}
+    public String getOrgName() {
+        return orgName;
+    }
 
-	public void setOrgName(String orgName) {
-		this.orgName = orgName;
-	}
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
 
-	public String getOrgCode() {
-		return orgCode;
-	}
+    public String getOrgCode() {
+        return orgCode;
+    }
 
-	public void setOrgCode(String orgCode) {
-		this.orgCode = orgCode;
-	}
+    public void setOrgCode(String orgCode) {
+        this.orgCode = orgCode;
+    }
 
-	public String getExt1() {
-		return ext1;
-	}
+    public String getExt1() {
+        return ext1;
+    }
 
-	public void setExt1(String ext1) {
-		this.ext1 = ext1;
-	}
+    public void setExt1(String ext1) {
+        this.ext1 = ext1;
+    }
 
-	public String getExt2() {
-		return ext2;
-	}
+    public String getExt2() {
+        return ext2;
+    }
 
-	public void setExt2(String ext2) {
-		this.ext2 = ext2;
-	}
+    public void setExt2(String ext2) {
+        this.ext2 = ext2;
+    }
 
-	public String getExt3() {
-		return ext3;
-	}
+    public String getExt3() {
+        return ext3;
+    }
 
-	public void setExt3(String ext3) {
-		this.ext3 = ext3;
-	}
+    public void setExt3(String ext3) {
+        this.ext3 = ext3;
+    }
 
-	public String getExt4() {
-		return ext4;
-	}
+    public String getExt4() {
+        return ext4;
+    }
 
-	public void setExt4(String ext4) {
-		this.ext4 = ext4;
-	}
+    public void setExt4(String ext4) {
+        this.ext4 = ext4;
+    }
 
-	public String getExt5() {
-		return ext5;
-	}
+    public String getExt5() {
+        return ext5;
+    }
 
-	public void setExt5(String ext5) {
-		this.ext5 = ext5;
-	}
+    public void setExt5(String ext5) {
+        this.ext5 = ext5;
+    }
 
-	public Boolean getEnable() {
-		return enable;
-	}
+    public Boolean getEnable() {
+        return enable;
+    }
 
-	public void setEnable(Boolean enable) {
-		this.enable = enable;
-	}
+    public void setEnable(Boolean enable) {
+        this.enable = enable;
+    }
 
-	public Date getSpecialBeginTime() {
-		return specialBeginTime;
-	}
+    public Date getSpecialBeginTime() {
+        return specialBeginTime;
+    }
 
-	public void setSpecialBeginTime(Date specialBeginTime) {
-		this.specialBeginTime = specialBeginTime;
-	}
+    public void setSpecialBeginTime(Date specialBeginTime) {
+        this.specialBeginTime = specialBeginTime;
+    }
 
-	public Date getSpecialEndTime() {
-		return specialEndTime;
-	}
+    public Date getSpecialEndTime() {
+        return specialEndTime;
+    }
 
-	public void setSpecialEndTime(Date specialEndTime) {
-		this.specialEndTime = specialEndTime;
-	}
+    public void setSpecialEndTime(Date specialEndTime) {
+        this.specialEndTime = specialEndTime;
+    }
 
+    public Long getExamStageId() {
+        return examStageId;
+    }
+
+    public void setExamStageId(Long examStageId) {
+        this.examStageId = examStageId;
+    }
+
+    public Integer getExamStageOrder() {
+        return examStageOrder;
+    }
+
+    public void setExamStageOrder(Integer examStageOrder) {
+        this.examStageOrder = examStageOrder;
+    }
 }

+ 3 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/GetLimitUserIdResp.java

@@ -9,14 +9,17 @@ import java.util.List;
  * @Version 1.0
  */
 public class GetLimitUserIdResp {
+
     /**
      * id集合
      */
     List<Long> idList;
+
     /**
      * 集合中最大用户id
      */
     Long maxId;
+
     /**
      * 下一个id
      */

+ 113 - 113
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/NoticeInfo.java

@@ -26,117 +26,117 @@ import java.util.Map;
  */
 public class NoticeInfo implements JsonSerializable {
 
-	private static final long serialVersionUID = -7739022488162924094L;
-
-	/**
-	 * 公告id
-	 */
-	@ApiModelProperty("公告id")
-	private Long id;
-
-	/**
-	 * 标题
-	 */
-	@ApiModelProperty("标题")
-	private String title;
-
-	/**
-	 * 发送对象
-	 */
-	@ApiModelProperty("发送对象")
-	private java.util.List<Map<String, Object>> publishObject;
-
-	/**
-	 * 发布者
-	 */
-	@ApiModelProperty("发布者")
-	private String publisher;
-
-	/**
-	 * 发布时间
-	 */
-	@ApiModelProperty("发布时间")
-	private Date publishTime;
-
-	/**
-	 * 发送状态
-	 */
-	@ApiModelProperty("发送状态")
-	@Enumerated(EnumType.STRING)
-	private NoticeStatus publishStatus;
-
-	/**
-	 * 规则类型
-	 */
-	@Enumerated(EnumType.STRING)
-	private NoticeReceiverRuleType ruleType;
-
-	/**
-	 * 内容
-	 */
-	private String content;
-
-	public String getContent() {
-		return content;
-	}
-
-	public void setContent(String content) {
-		this.content = content;
-	}
-
-	public NoticeReceiverRuleType getRuleType() {
-		return ruleType;
-	}
-
-	public void setRuleType(NoticeReceiverRuleType ruleType) {
-		this.ruleType = ruleType;
-	}
-
-	public Long getId() {
-		return id;
-	}
-
-	public void setId(Long id) {
-		this.id = id;
-	}
-
-	public String getTitle() {
-		return title;
-	}
-
-	public void setTitle(String title) {
-		this.title = title;
-	}
-
-	public List<Map<String, Object>> getPublishObject() {
-		return publishObject;
-	}
-
-	public void setPublishObject(List<Map<String, Object>> publishObject) {
-		this.publishObject = publishObject;
-	}
-
-	public String getPublisher() {
-		return publisher;
-	}
-
-	public void setPublisher(String publisher) {
-		this.publisher = publisher;
-	}
-
-	public Date getPublishTime() {
-		return publishTime;
-	}
-
-	public void setPublishTime(Date publishTime) {
-		this.publishTime = publishTime;
-	}
-
-	public NoticeStatus getPublishStatus() {
-		return publishStatus;
-	}
-
-	public void setPublishStatus(NoticeStatus publishStatus) {
-		this.publishStatus = publishStatus;
-	}
+    private static final long serialVersionUID = -7739022488162924094L;
+
+    /**
+     * 公告id
+     */
+    @ApiModelProperty("公告id")
+    private Long id;
+
+    /**
+     * 标题
+     */
+    @ApiModelProperty("标题")
+    private String title;
+
+    /**
+     * 发送对象
+     */
+    @ApiModelProperty("发送对象")
+    private java.util.List<Map<String, Object>> publishObject;
+
+    /**
+     * 发布者
+     */
+    @ApiModelProperty("发布者")
+    private String publisher;
+
+    /**
+     * 发布时间
+     */
+    @ApiModelProperty("发布时间")
+    private Date publishTime;
+
+    /**
+     * 发送状态
+     */
+    @ApiModelProperty("发送状态")
+    @Enumerated(EnumType.STRING)
+    private NoticeStatus publishStatus;
+
+    /**
+     * 规则类型
+     */
+    @Enumerated(EnumType.STRING)
+    private NoticeReceiverRuleType ruleType;
+
+    /**
+     * 内容
+     */
+    private String content;
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public NoticeReceiverRuleType getRuleType() {
+        return ruleType;
+    }
+
+    public void setRuleType(NoticeReceiverRuleType ruleType) {
+        this.ruleType = ruleType;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public List<Map<String, Object>> getPublishObject() {
+        return publishObject;
+    }
+
+    public void setPublishObject(List<Map<String, Object>> publishObject) {
+        this.publishObject = publishObject;
+    }
+
+    public String getPublisher() {
+        return publisher;
+    }
+
+    public void setPublisher(String publisher) {
+        this.publisher = publisher;
+    }
+
+    public Date getPublishTime() {
+        return publishTime;
+    }
+
+    public void setPublishTime(Date publishTime) {
+        this.publishTime = publishTime;
+    }
+
+    public NoticeStatus getPublishStatus() {
+        return publishStatus;
+    }
+
+    public void setPublishStatus(NoticeStatus publishStatus) {
+        this.publishStatus = publishStatus;
+    }
 }

+ 3 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/NoticeInfoQuery.java

@@ -27,15 +27,18 @@ public class NoticeInfoQuery implements JsonSerializable {
      * 学校id
      */
     private Long rootOrgId;
+
     /**
      * 用户id
      */
     private long userId;
+
     /**
      * 标题
      */
     @ApiModelProperty("标题")
     private String title;
+
     /**
      * 发送状态
      */

+ 6 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/UpdateNoticeInfo.java

@@ -26,18 +26,22 @@ public class UpdateNoticeInfo implements JsonSerializable {
      * 通知id
      */
     private Long id;
+
     /**
      * 组织机构id
      */
     private Long rootOrgId;
+
     /**
      * 用户id
      */
     private Long userId;
+
     /**
      * 标题
      */
     private String title;
+
     /**
      * 内容
      */
@@ -47,10 +51,12 @@ public class UpdateNoticeInfo implements JsonSerializable {
      * 规则类型
      */
     private NoticeReceiverRuleType ruleType;
+
     /**
      * 发送对象
      */
     private String publishObjectId;
+
     /**
      * 发布者
      */

+ 6 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/UserNoticeInfo.java

@@ -18,31 +18,37 @@ import java.util.Date;
 public class UserNoticeInfo implements JsonSerializable {
 
     private static final long serialVersionUID = -7739022488162924094L;
+
     /**
      * 公告id
      */
     @ApiModelProperty("公告id")
     private Long id;
+
     /**
      * 标题
      */
     @ApiModelProperty("标题")
     private String title;
+
     /**
      * 内容
      */
     @ApiModelProperty("内容")
     private String content;
+
     /**
      * 发布者
      */
     @ApiModelProperty("发布者")
     private String publisher;
+
     /**
      * 发布时间
      */
     @ApiModelProperty("发布时间")
     private Date publishTime;
+
     /**
      * 读取状态
      */

+ 3 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/bean/UserNoticeInfoQuery.java

@@ -19,11 +19,13 @@ import io.swagger.annotations.ApiModelProperty;
 public class UserNoticeInfoQuery implements JsonSerializable {
 
     private static final long serialVersionUID = -1962646957678995495L;
+
     /**
      * 学校id
      */
     @ApiModelProperty("学校id")
     private Long rootOrgId;
+
     /**
      * 用户类型
      */
@@ -35,6 +37,7 @@ public class UserNoticeInfoQuery implements JsonSerializable {
      */
     @ApiModelProperty("用户id")
     private Long userId;
+
     /**
      * 是否已读(true,已读;false,未读)
      */

+ 43 - 44
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamOrgPropertyCache.java

@@ -1,8 +1,5 @@
 package cn.com.qmth.examcloud.core.examwork.service.cache;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
 import cn.com.qmth.examcloud.commons.helpers.DynamicEnum;
 import cn.com.qmth.examcloud.commons.helpers.DynamicEnumManager;
 import cn.com.qmth.examcloud.core.examwork.base.enums.ExamProperty;
@@ -11,50 +8,52 @@ import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamOrgPropertyEntity;
 import cn.com.qmth.examcloud.core.examwork.service.ExamService;
 import cn.com.qmth.examcloud.support.cache.bean.ExamOrgPropertyCacheBean;
 import cn.com.qmth.examcloud.web.cache.RandomObjectRedisCache;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 
 @Service
 public class ExamOrgPropertyCache extends RandomObjectRedisCache<ExamOrgPropertyCacheBean> {
 
-	@Autowired
-	ExamService examService;
-
-	@Autowired
-	ExamOrgPropertyRepo examOrgPropertyRepo;
-
-	@Override
-	public ExamOrgPropertyCacheBean loadFromResource(Object... keys) {
-		Long examId = (Long) keys[0];
-		Long orgId = (Long) keys[1];
-		String key = (String) keys[2];
-
-		DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-		DynamicEnum de = manager.getByName(key);
-
-		ExamOrgPropertyCacheBean b = new ExamOrgPropertyCacheBean();
-		b.setExamId(examId);
-		b.setOrgId(orgId);
-		b.setKey(key);
-
-		ExamOrgPropertyEntity examOrgPropertyEntity = examOrgPropertyRepo
-				.findByExamIdAndOrgIdAndKeyId(examId, orgId, de.getId());
-		if (null != examOrgPropertyEntity) {
-			b.setValue(examOrgPropertyEntity.getValue());
-		} else {
-			b.setHasValue(false);
-		}
-
-		return b;
-	}
-
-	@Override
-	protected String getKeyPrefix() {
-		return "E_EXAM_ORG_PROP:";
-	}
-
-	@Override
-	protected int getTimeout() {
-		// 10s
-		return 10;
-	}
+    @Autowired
+    ExamService examService;
+
+    @Autowired
+    ExamOrgPropertyRepo examOrgPropertyRepo;
+
+    @Override
+    public ExamOrgPropertyCacheBean loadFromResource(Object... keys) {
+        Long examId = (Long) keys[0];
+        Long orgId = (Long) keys[1];
+        String key = (String) keys[2];
+
+        DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+        DynamicEnum de = manager.getByName(key);
+
+        ExamOrgPropertyCacheBean b = new ExamOrgPropertyCacheBean();
+        b.setExamId(examId);
+        b.setOrgId(orgId);
+        b.setKey(key);
+
+        ExamOrgPropertyEntity examOrgPropertyEntity = examOrgPropertyRepo
+                .findByExamIdAndOrgIdAndKeyId(examId, orgId, de.getId());
+        if (null != examOrgPropertyEntity) {
+            b.setValue(examOrgPropertyEntity.getValue());
+        } else {
+            b.setHasValue(false);
+        }
+
+        return b;
+    }
+
+    @Override
+    protected String getKeyPrefix() {
+        return "E_EXAM_ORG_PROP:";
+    }
+
+    @Override
+    protected int getTimeout() {
+        // 10s
+        return 10;
+    }
 
 }

+ 44 - 45
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamOrgSettingsCache.java

@@ -1,8 +1,5 @@
 package cn.com.qmth.examcloud.core.examwork.service.cache;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.examwork.dao.ExamRepo;
 import cn.com.qmth.examcloud.core.examwork.dao.ExamSpecialSettingsRepo;
@@ -11,61 +8,63 @@ import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamSpecialSettingsEntity;
 import cn.com.qmth.examcloud.support.cache.bean.ExamOrgSettingsCacheBean;
 import cn.com.qmth.examcloud.web.cache.RandomObjectRedisCache;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 
 @Service
 public class ExamOrgSettingsCache extends RandomObjectRedisCache<ExamOrgSettingsCacheBean> {
 
-	@Autowired
-	ExamRepo examRepo;
+    @Autowired
+    ExamRepo examRepo;
 
-	@Autowired
-	ExamSpecialSettingsRepo examSpecialSettingsRepo;
+    @Autowired
+    ExamSpecialSettingsRepo examSpecialSettingsRepo;
 
-	@Override
-	public ExamOrgSettingsCacheBean loadFromResource(Object... keys) {
-		Long examId = (Long) keys[0];
-		Long orgId = (Long) keys[1];
+    @Override
+    public ExamOrgSettingsCacheBean loadFromResource(Object... keys) {
+        Long examId = (Long) keys[0];
+        Long orgId = (Long) keys[1];
 
-		ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
 
-		if (null == exam) {
-			throw new StatusException("002005", "考试不存在");
-		}
+        if (null == exam) {
+            throw new StatusException("002005", "考试不存在");
+        }
 
-		ExamOrgSettingsCacheBean bean = new ExamOrgSettingsCacheBean();
+        ExamOrgSettingsCacheBean bean = new ExamOrgSettingsCacheBean();
 
-		bean.setId(exam.getId());
-		bean.setExamType(exam.getExamType().name());
-		bean.setCode(exam.getCode());
+        bean.setId(exam.getId());
+        bean.setExamType(exam.getExamType().name());
+        bean.setCode(exam.getCode());
 
-		ExamSpecialSettingsEntity examOrgEntity = examSpecialSettingsRepo
-				.findByExamIdAndOrgIdAndCourseIdIsNullAndStudentIdIsNull(exam.getId(), orgId);
-		if (null != examOrgEntity) {
-			if (null != examOrgEntity.getBeginTime()) {
-				bean.setBeginTime(examOrgEntity.getBeginTime());
-			}
-			if (null != examOrgEntity.getEndTime()) {
-				bean.setEndTime(examOrgEntity.getEndTime());
-			}
-			if (null != examOrgEntity.getExamLimit()) {
-				bean.setExamLimit(examOrgEntity.getExamLimit());
-			}
-		} else {
-			bean.setHasValue(false);
-		}
+        ExamSpecialSettingsEntity examOrgEntity = examSpecialSettingsRepo
+                .findByExamIdAndOrgIdAndCourseIdIsNullAndStudentIdIsNull(exam.getId(), orgId);
+        if (null != examOrgEntity) {
+            if (null != examOrgEntity.getBeginTime()) {
+                bean.setBeginTime(examOrgEntity.getBeginTime());
+            }
+            if (null != examOrgEntity.getEndTime()) {
+                bean.setEndTime(examOrgEntity.getEndTime());
+            }
+            if (null != examOrgEntity.getExamLimit()) {
+                bean.setExamLimit(examOrgEntity.getExamLimit());
+            }
+        } else {
+            bean.setHasValue(false);
+        }
 
-		return bean;
-	}
+        return bean;
+    }
 
-	@Override
-	protected String getKeyPrefix() {
-		return "E_EXAM_ORG_SETTINGS:";
-	}
+    @Override
+    protected String getKeyPrefix() {
+        return "E_EXAM_ORG_SETTINGS:";
+    }
 
-	@Override
-	protected int getTimeout() {
-		// 10s
-		return 10;
-	}
+    @Override
+    protected int getTimeout() {
+        // 10s
+        return 10;
+    }
 
 }

+ 31 - 32
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamPropertyCache.java

@@ -1,8 +1,5 @@
 package cn.com.qmth.examcloud.core.examwork.service.cache;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
 import cn.com.qmth.examcloud.commons.helpers.DynamicEnum;
 import cn.com.qmth.examcloud.commons.helpers.DynamicEnumManager;
 import cn.com.qmth.examcloud.core.examwork.base.enums.ExamProperty;
@@ -10,46 +7,48 @@ import cn.com.qmth.examcloud.core.examwork.dao.ExamPropertyRepo;
 import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamPropertyEntity;
 import cn.com.qmth.examcloud.support.cache.bean.ExamPropertyCacheBean;
 import cn.com.qmth.examcloud.web.cache.RandomObjectRedisCache;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 
 @Service
 public class ExamPropertyCache extends RandomObjectRedisCache<ExamPropertyCacheBean> {
 
-	@Autowired
-	ExamPropertyRepo examPropertyRepo;
+    @Autowired
+    ExamPropertyRepo examPropertyRepo;
 
-	@Override
-	public ExamPropertyCacheBean loadFromResource(Object... keys) {
-		Long examId = (Long) keys[0];
-		String key = (String) keys[1];
+    @Override
+    public ExamPropertyCacheBean loadFromResource(Object... keys) {
+        Long examId = (Long) keys[0];
+        String key = (String) keys[1];
 
-		DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-		DynamicEnum de = manager.getByName(key);
+        DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+        DynamicEnum de = manager.getByName(key);
 
-		ExamPropertyEntity examPropertyEntity = examPropertyRepo.findByExamIdAndKeyId(examId,
-				de.getId());
+        ExamPropertyEntity examPropertyEntity = examPropertyRepo.findByExamIdAndKeyId(examId,
+                de.getId());
 
-		ExamPropertyCacheBean b = new ExamPropertyCacheBean();
-		b.setExamId(examId);
-		b.setKey(key);
+        ExamPropertyCacheBean b = new ExamPropertyCacheBean();
+        b.setExamId(examId);
+        b.setKey(key);
 
-		if (null == examPropertyEntity) {
-			b.setHasValue(false);
-			return b;
-		}
+        if (null == examPropertyEntity) {
+            b.setHasValue(false);
+            return b;
+        }
 
-		b.setValue(examPropertyEntity.getValue());
-		return b;
-	}
+        b.setValue(examPropertyEntity.getValue());
+        return b;
+    }
 
-	@Override
-	protected String getKeyPrefix() {
-		return "E_EXAM_PROP:";
-	}
+    @Override
+    protected String getKeyPrefix() {
+        return "E_EXAM_PROP:";
+    }
 
-	@Override
-	protected int getTimeout() {
-		// 10s
-		return 10;
-	}
+    @Override
+    protected int getTimeout() {
+        // 10s
+        return 10;
+    }
 
 }

+ 48 - 49
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamSettingsCache.java

@@ -1,8 +1,5 @@
 package cn.com.qmth.examcloud.core.examwork.service.cache;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.examwork.dao.ExamRepo;
 import cn.com.qmth.examcloud.core.examwork.dao.ExamSpecialSettingsRepo;
@@ -10,55 +7,57 @@ import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamEntity;
 import cn.com.qmth.examcloud.support.cache.bean.ExamSettingsCacheBean;
 import cn.com.qmth.examcloud.web.cache.RandomObjectRedisCache;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 
 @Service
 public class ExamSettingsCache extends RandomObjectRedisCache<ExamSettingsCacheBean> {
 
-	@Autowired
-	ExamRepo examRepo;
-
-	@Autowired
-	ExamSpecialSettingsRepo examSpecialSettingsRepo;
-
-	@Override
-	public ExamSettingsCacheBean loadFromResource(Object... keys) {
-		Long examId = (Long) keys[0];
-
-		ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-
-		if (null == exam) {
-			throw new StatusException("002005", "考试不存在");
-		}
-
-		ExamSettingsCacheBean bean = new ExamSettingsCacheBean();
-
-		bean.setId(exam.getId());
-		bean.setBeginTime(exam.getBeginTime());
-		bean.setDuration(exam.getDuration());
-		bean.setEnable(exam.getEnable());
-		bean.setEndTime(exam.getEndTime());
-		bean.setExamTimes(exam.getExamTimes());
-		bean.setExamType(exam.getExamType().name());
-		bean.setName(exam.getName());
-		bean.setCode(exam.getCode());
-		bean.setRemark(exam.getRemark());
-		bean.setRootOrgId(exam.getRootOrgId());
-		bean.setExamLimit(exam.getExamLimit());
-		bean.setSpecialSettingsEnabled(exam.getSpecialSettingsEnabled());
-		bean.setSpecialSettingsType(exam.getSpecialSettingsType());
-
-		return bean;
-	}
-
-	@Override
-	protected String getKeyPrefix() {
-		return "E_EXAM:";
-	}
-
-	@Override
-	protected int getTimeout() {
-		// 10s
-		return 10;
-	}
+    @Autowired
+    ExamRepo examRepo;
+
+    @Autowired
+    ExamSpecialSettingsRepo examSpecialSettingsRepo;
+
+    @Override
+    public ExamSettingsCacheBean loadFromResource(Object... keys) {
+        Long examId = (Long) keys[0];
+
+        ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+
+        if (null == exam) {
+            throw new StatusException("002005", "考试不存在");
+        }
+
+        ExamSettingsCacheBean bean = new ExamSettingsCacheBean();
+
+        bean.setId(exam.getId());
+        bean.setBeginTime(exam.getBeginTime());
+        bean.setDuration(exam.getDuration());
+        bean.setEnable(exam.getEnable());
+        bean.setEndTime(exam.getEndTime());
+        bean.setExamTimes(exam.getExamTimes());
+        bean.setExamType(exam.getExamType().name());
+        bean.setName(exam.getName());
+        bean.setCode(exam.getCode());
+        bean.setRemark(exam.getRemark());
+        bean.setRootOrgId(exam.getRootOrgId());
+        bean.setExamLimit(exam.getExamLimit());
+        bean.setSpecialSettingsEnabled(exam.getSpecialSettingsEnabled());
+        bean.setSpecialSettingsType(exam.getSpecialSettingsType());
+
+        return bean;
+    }
+
+    @Override
+    protected String getKeyPrefix() {
+        return "E_EXAM:";
+    }
+
+    @Override
+    protected int getTimeout() {
+        // 10s
+        return 10;
+    }
 
 }

+ 95 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamStageCache.java

@@ -0,0 +1,95 @@
+package cn.com.qmth.examcloud.core.examwork.service.cache;
+
+import cn.com.qmth.examcloud.api.commons.enums.SubmitType;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.core.examwork.dao.ExamStageRepo;
+import cn.com.qmth.examcloud.core.examwork.dao.ExamStageSettingRepo;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStageEntity;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStageSettingEntity;
+import cn.com.qmth.examcloud.support.cache.bean.ExamStageCacheBean;
+import cn.com.qmth.examcloud.web.cache.RandomObjectRedisCache;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ExamStageCache extends RandomObjectRedisCache<ExamStageCacheBean> {
+
+    @Autowired
+    ExamStageRepo examStageRepo;
+
+    @Autowired
+    ExamStageSettingRepo examStageSettingRepo;
+
+    @Override
+    public ExamStageCacheBean loadFromResource(Object... keys) {
+        Long examId = (Long) keys[0];
+        Long examStageId = (Long) keys[1];
+
+        if (null == examId) {
+            throw new StatusException("002001", "考试id不允许为空");
+        }
+        if (null == examStageId) {
+            throw new StatusException("002002", "场次id不允许为空");
+        }
+
+        ExamStageCacheBean result = new ExamStageCacheBean();
+        ExamStageEntity examStage = GlobalHelper.getEntity(examStageRepo, examStageId, ExamStageEntity.class);
+        if (null == examStage) {
+            result.setHasValue(false);
+            return result;
+        }
+
+        result.setId(examStage.getId());
+        result.setStageOrder(examStage.getStageOrder());
+        result.setRootOrgId(examStage.getRootOrgId());
+        result.setExamId(examStage.getExamId());
+        result.setEnable(examStage.getEnable());
+        result.setStartTime(DateUtil.parse(DateUtil.format(examStage.getStartTime(),
+                DateUtil.DatePatterns.CHINA_DEFAULT), DateUtil.DatePatterns.CHINA_DEFAULT));
+        result.setEndTime(DateUtil.parse(DateUtil.format(examStage.getEndTime(),
+                DateUtil.DatePatterns.CHINA_DEFAULT), DateUtil.DatePatterns.CHINA_DEFAULT));
+        result.setStartExamStatus(examStage.getStartExamStatus().name());
+
+        //通用设置or特殊设置
+        Boolean specialSetting = null == examStage.getSpecialSetting() ? false : examStage.getSpecialSetting();
+
+        result.setSpecialSetting(specialSetting);
+
+        //如果是特殊设置,取取场次表中设置
+        if (specialSetting) {
+            result.setSubmitType(examStage.getSubmitType().name());
+            result.setSubmitDuration(examStage.getSubmitDuration());
+        }
+        //如果是通用设置,则取通用设置表中的设置
+        else {
+            ExamStageSettingEntity examStageSetting = examStageSettingRepo.findByExamId(examId);
+
+            //如果未找到通用设置,则初始化通用设置
+            if (null == examStageSetting) {
+                examStageSetting = new ExamStageSettingEntity();
+                examStageSetting.setExamId(examId);
+                examStageSetting.setSubmitType(SubmitType.NORMAL);//默认正常交卷
+                examStageSettingRepo.save(examStageSetting);
+            }
+
+            result.setSubmitType(examStageSetting.getSubmitType().name());
+            result.setSubmitDuration(examStageSetting.getSubmitDuration());
+        }
+
+        return result;
+    }
+
+    @Override
+    protected String getKeyPrefix() {
+        return "E_EXAM_STAGE:";
+    }
+
+    @Override
+    protected int getTimeout() {
+        // 10s
+        return 10;
+    }
+
+}

+ 101 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamStagesCache.java

@@ -0,0 +1,101 @@
+package cn.com.qmth.examcloud.core.examwork.service.cache;
+
+import cn.com.qmth.examcloud.api.commons.enums.SubmitType;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.core.examwork.dao.ExamStageRepo;
+import cn.com.qmth.examcloud.core.examwork.dao.ExamStageSettingRepo;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStageEntity;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamStageSettingEntity;
+import cn.com.qmth.examcloud.support.cache.bean.ExamStageCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamStagesCacheBean;
+import cn.com.qmth.examcloud.web.cache.RandomObjectRedisCache;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class ExamStagesCache extends RandomObjectRedisCache<ExamStagesCacheBean> {
+
+    @Autowired
+    ExamStageRepo examStageRepo;
+
+    @Autowired
+    ExamStageSettingRepo examStageSettingRepo;
+
+    @Override
+    public ExamStagesCacheBean loadFromResource(Object... keys) {
+        Long examId = (Long) keys[0];
+
+        if (null == examId) {
+            throw new StatusException("002001", "考试id不允许为空");
+        }
+
+        ExamStagesCacheBean result = new ExamStagesCacheBean();
+        List<ExamStageEntity> entityList = examStageRepo.findByExamId(examId);
+        if (null == entityList || entityList.isEmpty()) {
+            result.setHasValue(false);
+            return result;
+        }
+
+        List<ExamStageCacheBean> beanList = new ArrayList<>();
+        for (ExamStageEntity esEntity : entityList) {
+            ExamStageCacheBean esBean = new ExamStageCacheBean();
+            esBean.setId(esEntity.getId());
+            esBean.setRootOrgId(esEntity.getRootOrgId());
+            esBean.setExamId(esEntity.getExamId());
+            esBean.setStageOrder(esEntity.getStageOrder());
+            esBean.setStartTime(DateUtil.parse(DateUtil.format(esEntity.getStartTime(),
+                    DateUtil.DatePatterns.CHINA_DEFAULT), DateUtil.DatePatterns.CHINA_DEFAULT));
+            esBean.setEndTime(DateUtil.parse(DateUtil.format(esEntity.getEndTime(),
+                    DateUtil.DatePatterns.CHINA_DEFAULT), DateUtil.DatePatterns.CHINA_DEFAULT));
+            esBean.setEnable(esEntity.getEnable());
+            esBean.setStartExamStatus(esEntity.getStartExamStatus().name());
+
+            //通用设置or特殊设置
+            Boolean specialSetting = null == esEntity.getSpecialSetting() ? false : esEntity.getSpecialSetting();
+
+            esBean.setSpecialSetting(specialSetting);
+
+            //如果是特殊设置,取取场次表中设置
+            if (specialSetting) {
+                esBean.setSubmitType(esEntity.getSubmitType().name());
+                esBean.setSubmitDuration(esEntity.getSubmitDuration());
+            }
+            //如果是通用设置,则取通用设置表中的设置
+            else {
+                ExamStageSettingEntity examStageSetting = examStageSettingRepo.findByExamId(examId);
+
+                //如果未找到通用设置,则初始化通用设置
+                if (null == examStageSetting) {
+                    examStageSetting = new ExamStageSettingEntity();
+                    examStageSetting.setExamId(examId);
+                    examStageSetting.setSubmitType(SubmitType.NORMAL);//默认正常交卷
+                    examStageSettingRepo.save(examStageSetting);
+                }
+
+                esBean.setSubmitType(examStageSetting.getSubmitType().name());
+                esBean.setSubmitDuration(examStageSetting.getSubmitDuration());
+            }
+            beanList.add(esBean);
+        }
+
+        result.setExamStages(beanList);
+
+        return result;
+    }
+
+    @Override
+    protected String getKeyPrefix() {
+        return "E_EXAM_STAGE:";
+    }
+
+    @Override
+    protected int getTimeout() {
+        // 10s
+        return 10;
+    }
+
+}

+ 29 - 30
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamStudentPropertyCache.java

@@ -1,41 +1,40 @@
 package cn.com.qmth.examcloud.core.examwork.service.cache;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
 import cn.com.qmth.examcloud.core.examwork.service.ExamService;
 import cn.com.qmth.examcloud.support.cache.bean.ExamStudentPropertyCacheBean;
 import cn.com.qmth.examcloud.web.cache.RandomObjectRedisCache;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 
 @Service
 public class ExamStudentPropertyCache extends RandomObjectRedisCache<ExamStudentPropertyCacheBean> {
 
-	@Autowired
-	ExamService examService;
-
-	@Override
-	public ExamStudentPropertyCacheBean loadFromResource(Object... keys) {
-		Long examId = (Long) keys[0];
-		Long studentId = (Long) keys[1];
-		String key = (String) keys[2];
-
-		ExamStudentPropertyCacheBean b = new ExamStudentPropertyCacheBean();
-		b.setExamId(examId);
-		b.setStudentId(studentId);
-		b.setKey(key);
-		b.setHasValue(false);
-		return b;
-	}
-
-	@Override
-	protected String getKeyPrefix() {
-		return "E_EXAM_STUDENT_PROP:";
-	}
-
-	@Override
-	protected int getTimeout() {
-		// 10s
-		return 10;
-	}
+    @Autowired
+    ExamService examService;
+
+    @Override
+    public ExamStudentPropertyCacheBean loadFromResource(Object... keys) {
+        Long examId = (Long) keys[0];
+        Long studentId = (Long) keys[1];
+        String key = (String) keys[2];
+
+        ExamStudentPropertyCacheBean b = new ExamStudentPropertyCacheBean();
+        b.setExamId(examId);
+        b.setStudentId(studentId);
+        b.setKey(key);
+        b.setHasValue(false);
+        return b;
+    }
+
+    @Override
+    protected String getKeyPrefix() {
+        return "E_EXAM_STUDENT_PROP:";
+    }
+
+    @Override
+    protected int getTimeout() {
+        // 10s
+        return 10;
+    }
 
 }

+ 44 - 45
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/cache/ExamStudentSettingsCache.java

@@ -1,8 +1,5 @@
 package cn.com.qmth.examcloud.core.examwork.service.cache;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.examwork.dao.ExamRepo;
 import cn.com.qmth.examcloud.core.examwork.dao.ExamSpecialSettingsRepo;
@@ -11,61 +8,63 @@ import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamSpecialSettingsEntity;
 import cn.com.qmth.examcloud.support.cache.bean.ExamStudentSettingsCacheBean;
 import cn.com.qmth.examcloud.web.cache.RandomObjectRedisCache;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 
 @Service
 public class ExamStudentSettingsCache extends RandomObjectRedisCache<ExamStudentSettingsCacheBean> {
 
-	@Autowired
-	ExamRepo examRepo;
+    @Autowired
+    ExamRepo examRepo;
 
-	@Autowired
-	ExamSpecialSettingsRepo examSpecialSettingsRepo;
+    @Autowired
+    ExamSpecialSettingsRepo examSpecialSettingsRepo;
 
-	@Override
-	public ExamStudentSettingsCacheBean loadFromResource(Object... keys) {
-		Long examId = (Long) keys[0];
-		Long studentId = (Long) keys[1];
+    @Override
+    public ExamStudentSettingsCacheBean loadFromResource(Object... keys) {
+        Long examId = (Long) keys[0];
+        Long studentId = (Long) keys[1];
 
-		ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
 
-		if (null == exam) {
-			throw new StatusException("002005", "考试不存在");
-		}
+        if (null == exam) {
+            throw new StatusException("002005", "考试不存在");
+        }
 
-		ExamStudentSettingsCacheBean bean = new ExamStudentSettingsCacheBean();
+        ExamStudentSettingsCacheBean bean = new ExamStudentSettingsCacheBean();
 
-		bean.setId(exam.getId());
-		bean.setExamType(exam.getExamType().name());
-		bean.setCode(exam.getCode());
+        bean.setId(exam.getId());
+        bean.setExamType(exam.getExamType().name());
+        bean.setCode(exam.getCode());
 
-		ExamSpecialSettingsEntity examOrgEntity = examSpecialSettingsRepo
-				.findByExamIdAndStudentIdAndOrgIdIsNullAndCourseIdIsNull(exam.getId(), studentId);
-		if (null != examOrgEntity) {
-			if (null != examOrgEntity.getBeginTime()) {
-				bean.setBeginTime(examOrgEntity.getBeginTime());
-			}
-			if (null != examOrgEntity.getEndTime()) {
-				bean.setEndTime(examOrgEntity.getEndTime());
-			}
-			if (null != examOrgEntity.getExamLimit()) {
-				bean.setExamLimit(examOrgEntity.getExamLimit());
-			}
-		} else {
-			bean.setHasValue(false);
-		}
+        ExamSpecialSettingsEntity examOrgEntity = examSpecialSettingsRepo
+                .findByExamIdAndStudentIdAndOrgIdIsNullAndCourseIdIsNull(exam.getId(), studentId);
+        if (null != examOrgEntity) {
+            if (null != examOrgEntity.getBeginTime()) {
+                bean.setBeginTime(examOrgEntity.getBeginTime());
+            }
+            if (null != examOrgEntity.getEndTime()) {
+                bean.setEndTime(examOrgEntity.getEndTime());
+            }
+            if (null != examOrgEntity.getExamLimit()) {
+                bean.setExamLimit(examOrgEntity.getExamLimit());
+            }
+        } else {
+            bean.setHasValue(false);
+        }
 
-		return bean;
-	}
+        return bean;
+    }
 
-	@Override
-	protected String getKeyPrefix() {
-		return "E_EXAM_STUDENT_SETTINGS:";
-	}
+    @Override
+    protected String getKeyPrefix() {
+        return "E_EXAM_STUDENT_SETTINGS:";
+    }
 
-	@Override
-	protected int getTimeout() {
-		// 10s
-		return 10;
-	}
+    @Override
+    protected int getTimeout() {
+        // 10s
+        return 10;
+    }
 
 }

+ 771 - 758
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/ExamServiceImpl.java

@@ -1,27 +1,5 @@
 package cn.com.qmth.examcloud.core.examwork.service.impl;
 
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.jsoup.nodes.Element;
-import org.jsoup.select.Elements;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-
 import cn.com.qmth.examcloud.api.commons.enums.CURD;
 import cn.com.qmth.examcloud.api.commons.enums.ExamType;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
@@ -59,6 +37,25 @@ import cn.com.qmth.examcloud.task.api.DataSyncCloudService;
 import cn.com.qmth.examcloud.task.api.request.SyncExamReq;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
 import cn.com.qmth.examcloud.web.helpers.SequenceLockHelper;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+
+import javax.persistence.criteria.Predicate;
+import java.io.File;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 /**
  * 类注释
@@ -69,741 +66,757 @@ import cn.com.qmth.examcloud.web.helpers.SequenceLockHelper;
  */
 @Service
 public class ExamServiceImpl implements ExamService {
-	private static Pattern pattern = Pattern.compile("\\s*");
-
-	@Autowired
-	ExamRepo examRepo;
-
-	@Autowired
-	ExamStudentService examStudentService;
-
-	@Autowired
-	ExamSpecialSettingsRepo examSpecialSettingsRepo;
-
-	@Autowired
-	OrgCloudService orgCloudService;
-
-	@Autowired
-	ExamPropertyRepo examPropertyRepo;
-
-	@Autowired
-	ExamOrgPropertyRepo examOrgPropertyRepo;
-
-	@Autowired
-	DataSyncCloudService dataSyncCloudService;
-
-	@Autowired
-	ExamSettingsCache examSettingsCache;
-
-	@Autowired
-	ExamPropertyCache examPropertyCache;
-
-	private static final String[] EXAM_ORG_SETTINGS_EXCEL_HEADER = new String[]{"学习中心ID", "学习中心代码",
-			"学习中心名称", "是否可以考试(是/否)", "开始考试时间 yyyy-MM-dd hh:mm:ss", "结束考试时间 yyyy-MM-dd hh:mm:ss"};
-
-	/*
-	 * 实现
-	 *
-	 * @author WANGWEI
-	 * 
-	 * @see
-	 * cn.com.qmth.examcloud.core.examwork.service.ExamService#saveExam(cn.com.
-	 * qmth.examcloud.core.examwork.service.bean.ExamInfo)
-	 */
-	@Override
-	public ExamEntity saveExam(ExamInfo examInfo, CURD curd) {
-
-		Long rootOrgId = examInfo.getRootOrgId();
-		String code = examInfo.getCode();
-		String name = examInfo.getName();
-		ExamType examType = examInfo.getExamType();
-
-		if (null == examType) {
-			throw new StatusException("001005", "考试类型不能为空");
-		}
-
-		if (StringUtils.isBlank(code)) {
-			throw new StatusException("001005", "考试编码不能为空");
-		}
-
-		if (null == rootOrgId) {
-			throw new StatusException("001005", "rootOrgId is null");
-		}
-
-		// 上锁
-		Object[] locker = new Object[]{"E_EXAM_SAVE_EXAM", rootOrgId, code};
-		SequenceLockHelper.getLock(locker);
-
-		GetOrgReq getOrgReq = new GetOrgReq();
-		getOrgReq.setOrgId(rootOrgId);
-		GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
-		OrgBean rootOrg = getOrgResp.getOrg();
-
-		Map<String, String> properties = examInfo.getProperties();
-		Map<DynamicEnum, String> map = checkAndGetExamProperties(rootOrgId, properties);
-
-		ExamEntity exam = null;
-		CURD realStatus = null;
-
-		// 更新
-		if (curd.equals(CURD.UPDATE)) {
-			exam = examRepo.findByCodeAndRootOrgId(code, rootOrgId);
-			if (null == exam) {
-				throw new StatusException("002001", "code is wrong");
-			}
-			realStatus = CURD.UPDATE;
-		}
-		// 创建
-		else if (curd.equals(CURD.CREATION)) {
-			exam = new ExamEntity();
-			realStatus = CURD.CREATION;
-		}
-		// (根据考试编码)新增或创建
-		else if (curd.equals(CURD.CREATION_OR_UPDATE)) {
-			exam = examRepo.findByCodeAndRootOrgId(code, rootOrgId);
-			// 创建
-			if (null == exam) {
-				exam = new ExamEntity();
-				realStatus = CURD.CREATION;
-			}
-			// 更新
-			else {
-				realStatus = CURD.UPDATE;
-			}
-		}
-
-		if (realStatus.equals(CURD.CREATION)) {
-			if (null == examInfo.getBeginTime()) {
-				throw new StatusException("002006", "beginTime is null");
-			}
-			if (null == examInfo.getEndTime()) {
-				throw new StatusException("002006", "endTime is null");
-			}
-			if (StringUtils.isBlank(name)) {
-				throw new StatusException("002004", "name is blank");
-			}
-
-			ExamEntity examByCode = examRepo.findByCodeAndRootOrgId(code, rootOrgId);
-			if (null != examByCode) {
-				throw new StatusException("002005", "考试编码已存在");
-			}
-
-			ExamEntity examByName = examRepo.findByNameAndRootOrgId(name, rootOrgId);
-			if (null != examByName) {
-				throw new StatusException("002005", "考试名称已存在");
-			}
-
-			exam.setCode(code);
-			exam.setRootOrgId(rootOrgId);
-			exam.setExamType(examType);
-			exam.setEnable(true);
-			exam.setExamLimit(false);
-			exam.setSpecialSettingsEnabled(false);
-		} else if (realStatus.equals(CURD.UPDATE)) {
-			if (!exam.getRootOrgId().equals(rootOrgId)) {
-				throw new StatusException("002003", "rootOrgId is wrong");
-			}
-			if (!exam.getExamType().equals(examType)) {
-				throw new StatusException("002100", "examType is wrong");
-			}
-		}
-
-		if (null != examInfo.getSpecialSettingsEnabled()) {
-			exam.setSpecialSettingsEnabled(examInfo.getSpecialSettingsEnabled());
-		}
-		if (null != examInfo.getSpecialSettingsType()) {
-			exam.setSpecialSettingsType(examInfo.getSpecialSettingsType());
-		}
-		if (null != examInfo.getBeginTime()) {
-			exam.setBeginTime(examInfo.getBeginTime());
-		}
-		if (null != examInfo.getEndTime()) {
-			exam.setEndTime(examInfo.getEndTime());
-		}
-		if (null != examInfo.getDuration()) {
-			exam.setDuration(examInfo.getDuration());
-		}
-		if (null != examInfo.getEnable()) {
-			exam.setEnable(examInfo.getEnable());
-		}
-		if (null != examInfo.getExamTimes()) {
-			exam.setExamTimes(examInfo.getExamTimes());
-		}
-		if (null != examInfo.getExamLimit()) {
-			exam.setExamLimit(examInfo.getExamLimit());
-		}
-		if (StringUtils.isNotBlank(name)) {
-			exam.setName(name);
-		}
-		if (StringUtils.isNotBlank(examInfo.getRemark())) {
-			exam.setRemark(examInfo.getRemark());
-		}
-
-		// 数据订正代码
-		exam.setExamLimit(false);
-
-		ExamEntity saved = examRepo.saveAndFlush(exam);
-
-		for (Entry<DynamicEnum, String> entry : map.entrySet()) {
-			DynamicEnum de = entry.getKey();
-			String value = entry.getValue();
-			ExamPropertyEntity entity = examPropertyRepo.findByExamIdAndKeyId(saved.getId(),
-					de.getId());
-			if (null == entity) {
-				entity = new ExamPropertyEntity();
-				entity.setExamId(saved.getId());
-				entity.setKeyId(de.getId());
-			}
-			entity.setValue(value);
-
-			examPropertyRepo.save(entity);
-		}
-
-		// 考试设置混入特殊设置
-		ExamSpecialSettingsInfo examSpecialInfo = new ExamSpecialSettingsInfo();
-		examSpecialInfo.setBeginTime(saved.getBeginTime());
-		examSpecialInfo.setEndTime(saved.getEndTime());
-		examSpecialInfo.setExamId(saved.getId());
-		examSpecialInfo.setExamLimit(saved.getExamLimit());
-		examSpecialInfo.setRootOrgId(saved.getRootOrgId());
-		examSpecialInfo.setExamLimit(saved.getExamLimit());
-		this.saveExamSpecialSettings(examSpecialInfo);
-
-		examSpecialSettingsRepo.updateExamEnableByExamId(saved.getId(), saved.getEnable());
-		examSpecialSettingsRepo.updateSpecialSettingsEnabledByExamId(saved.getId(),
-				saved.getSpecialSettingsEnabled());
-		if (null == saved.getSpecialSettingsType()) {
-			examSpecialSettingsRepo.updateSpecialSettingsTypeByExamId(saved.getId(), null);
-		} else {
-			examSpecialSettingsRepo.updateSpecialSettingsTypeByExamId(saved.getId(),
-					saved.getSpecialSettingsType());
-		}
-
-		SyncExamReq req = new SyncExamReq();
-		req.setId(saved.getId());
-		req.setBeginTime(saved.getBeginTime());
-		req.setDuration(saved.getDuration());
-		req.setEnable(saved.getEnable());
-		req.setEndTime(saved.getEndTime());
-		req.setExamTimes(saved.getExamTimes());
-		req.setExamType(saved.getExamType().name());
-		req.setName(saved.getName());
-		req.setRemark(saved.getRemark());
-		req.setRootOrgId(saved.getRootOrgId());
-		req.setRootOrgName(rootOrg.getName());
-		req.setSyncType("update");
-		dataSyncCloudService.syncExam(req);
-
-		examSettingsCache.remove(saved.getId());
-
-		if (null != properties) {
-			for (String cur : properties.keySet()) {
-				examPropertyCache.remove(saved.getId(), cur);
-			}
-		}
-
-		return saved;
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param rootOrgId
-	 * @param properties
-	 * @return
-	 */
-	private Map<DynamicEnum, String> checkAndGetExamProperties(Long rootOrgId,
-			Map<String, String> properties) {
-		DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-
-		Map<DynamicEnum, String> map = Maps.newHashMap();
-		if (null == properties) {
-			return map;
-		}
-
-		for (Entry<String, String> entry : properties.entrySet()) {
-			String key = entry.getKey();
-			String value = entry.getValue();
-			DynamicEnum de;
-			try {
-				de = manager.getByName(key);
-			} catch (Exception e) {
-				throw new StatusException("001004", "考试属性错误. key=" + key);
-			}
-			if (!de.isLegal(value)) {
-				throw new StatusException("001200", "考试属性值非法. key=" + key);
-			}
-			map.put(de, value);
-		}
-
-		String beforeExamRemark = properties.get("BEFORE_EXAM_REMARK");
-
-		if (null != beforeExamRemark) {
-			Document doc = Jsoup.parse(beforeExamRemark);
-			Matcher m = pattern.matcher(doc.text());
-			String simpleText = m.replaceAll("").replaceAll(" ", "");
-			if (simpleText.length() > 800) {
-				throw new StatusException("001002", "考前说明内容不得超过800个字符");
-			}
-			long imgsize = getImgSizeByBase64String(beforeExamRemark);
-			if (imgsize > 150 * 1024) {
-				throw new StatusException("001002", "考前说明图片总大小不得超过150KB");
-			}
-		}
-
-		String afterExamRemark = properties.get("AFTER_EXAM_REMARK");
-		if (null != afterExamRemark) {
-			Document doc = Jsoup.parse(afterExamRemark);
-			Matcher m = pattern.matcher(doc.text());
-			String simpleText = m.replaceAll("").replaceAll(" ", "");
-			if (simpleText.length() > 800) {
-				throw new StatusException("001002", "考后说明内容不得超过800个字符");
-			}
-			long imgsize = getImgSizeByBase64String(afterExamRemark);
-			if (imgsize > 150 * 1024) {
-				throw new StatusException("001002", "考后说明图片总大小不得超过150KB");
-			}
-		}
-
-		String cheatingRemark = properties.get("CHEATING_REMARK");
-		if (null != cheatingRemark) {
-			Document doc = Jsoup.parse(cheatingRemark);
-			Matcher m = pattern.matcher(doc.text());
-			String simpleText = m.replaceAll("").replaceAll(" ", "");
-			if (simpleText.length() > 800) {
-				throw new StatusException("001002", "作弊说明内容不得超过800个字符");
-			}
-			long imgsize = getImgSizeByBase64String(cheatingRemark);
-			if (imgsize > 150 * 1024) {
-				throw new StatusException("001002", "作弊说明图片总大小不得超过150KB");
-			}
-		}
-
-		// 校验机构权限
-		// 人脸识别功能校验
-		String faceCheck = PrivilegeDefine.RootOrgFunctions.OnlineExamFunctions.FaceCheck.CODE;
-		Boolean hasFaceCheckFunction = PrivilegeManager.judge(rootOrgId, faceCheck);
-		String isFaceEnable = properties.get("IS_FACE_ENABLE");
-		if (!hasFaceCheckFunction) {
-			if (StringUtils.isNotBlank(isFaceEnable)
-					&& isFaceEnable.equalsIgnoreCase(Boolean.toString(true))) {
-				throw new StatusException("001002", "人脸识别功能未开放");
-			}
-		}
-
-		// 活体检测功能校验
-		String IdentificationOfLivingBody = PrivilegeDefine.RootOrgFunctions.OnlineExamFunctions.IdentificationOfLivingBody.CODE;
-		Boolean hasIdentificationOfLivingBodyFunction = PrivilegeManager.judge(rootOrgId,
-				IdentificationOfLivingBody);
-		String isFceVerify = properties.get("IS_FACE_VERIFY");
-		if (!hasIdentificationOfLivingBodyFunction) {
-			if (StringUtils.isNotBlank(isFceVerify)
-					&& isFceVerify.equalsIgnoreCase(Boolean.toString(true))) {
-				throw new StatusException("001002", "活体检测功能未开放");
-			}
-		}
-
-		return map;
-	}
-
-	/**
-	 * @param content
-	 *            内容
-	 * @return 内容中图片文件总大小
-	 */
-	private long getImgSizeByBase64String(String content) {
-		long size = 0;
-		if (StringUtils.isNoneEmpty(content)) {
-			Document doc = Jsoup.parse(content);
-			Elements imgs = doc.getElementsByTag("img");
-			if (imgs != null) {
-				for (Element img : imgs) {
-					String src = img.attr("src");
-					if (src.startsWith("data:")) {
-						String base64 = src.split(",")[1];
-						int equalIndex = base64.indexOf("=");
-						if (equalIndex > 0) {
-							base64 = base64.substring(0, equalIndex);
-						}
-						long fileSize = base64.length() - (base64.length() / 8) * 2;
-						size = size + fileSize;
-					}
-				}
-			}
-		}
-		return size;
-	}
-
-	/*
-	 * 实现
-	 *
-	 * @author WANGWEI
-	 * 
-	 * @see
-	 * cn.com.qmth.examcloud.core.examwork.service.ExamService#saveExamOrg(cn.
-	 * com.qmth.examcloud.core.examwork.service.bean.ExamOrgInfo)
-	 */
-	@Override
-	public ExamSpecialSettingsEntity saveExamSpecialSettings(
-			ExamSpecialSettingsInfo examSpecialInfo) {
-
-		ExamSpecialSettingsEntity examSpecialSettingsEntity = null;
-
-		Long rootOrgId = examSpecialInfo.getRootOrgId();
-		Long examId = examSpecialInfo.getExamId();
-		Date beginTime = examSpecialInfo.getBeginTime();
-		Date endTime = examSpecialInfo.getEndTime();
-		Long orgId = examSpecialInfo.getOrgId();
-		Long studentId = examSpecialInfo.getStudentId();
-		Long courseId = examSpecialInfo.getCourseId();
-
-		int trueNum = BooleanUtil.countTrue(null != orgId, null != courseId, null != studentId);
-		if (1 < trueNum) {
-			throw new StatusException("001502", "参数错误");
-		}
-
-		// 上锁
-		Object[] locker = new Object[]{"E_EXAM_SPECIAL_SETTINGS", examId, "ORG" + orgId,
-				"COURSE" + courseId, "Student" + studentId};
-		SequenceLockHelper.getLock(locker);
-
-		if (!new Boolean(null == beginTime).equals(new Boolean(null == endTime))) {
-			throw new StatusException("001101", "beginTime & endTime  wrong");
-		}
-
-		ExamEntity examEntity = GlobalHelper.getPresentEntity(examRepo, examId, ExamEntity.class);
-		GlobalHelper.uniformRootOrg(examEntity.getRootOrgId(), rootOrgId);
-
-		StudentCacheBean student = null;
-		if (null != studentId) {
-			student = CacheHelper.getStudent(studentId);
-			GlobalHelper.uniformRootOrg(student.getRootOrgId(), rootOrgId);
-		}
-
-		if (null == examSpecialInfo.getId()) {
-			// 机构特殊设置
-			if (null != orgId && null == courseId && null == studentId) {
-				examSpecialSettingsEntity = examSpecialSettingsRepo
-						.findByExamIdAndOrgIdAndCourseIdIsNullAndStudentIdIsNull(
-								examSpecialInfo.getExamId(), orgId);
-			}
-			// 学生特殊设置
-			else if (null == orgId && null == courseId && null != studentId) {
-				examSpecialSettingsEntity = examSpecialSettingsRepo
-						.findByExamIdAndStudentIdAndOrgIdIsNullAndCourseIdIsNull(
-								examSpecialInfo.getExamId(), studentId);
-			}
-			// 课程特殊设置
-			else if (null == orgId && null != courseId && null == studentId) {
-
-			}
-			// 考试设置
-			else if (null == orgId && null == courseId && null == studentId) {
-				examSpecialSettingsEntity = examSpecialSettingsRepo
-						.findAllByExamIdAndOrgIdIsNullAndCourseIdIsNullAndStudentIdIsNull(
-								examSpecialInfo.getExamId());
-			} else {
-				throw new StatusException("001501", "参数错误");
-			}
-
-			if (null == examSpecialSettingsEntity) {
-				examSpecialSettingsEntity = new ExamSpecialSettingsEntity();
-			}
-
-			examSpecialSettingsEntity.setRootOrgId(rootOrgId);
-			examSpecialSettingsEntity.setExamId(examId);
-			examSpecialSettingsEntity.setExamType(examEntity.getExamType());
-			examSpecialSettingsEntity.setExamEnable(examEntity.getEnable());
-			examSpecialSettingsEntity
-					.setSpecialSettingsEnabled(examEntity.getSpecialSettingsEnabled());
-			examSpecialSettingsEntity.setSpecialSettingsType(examEntity.getSpecialSettingsType());
-
-			examSpecialSettingsEntity.setOrgId(orgId);
-			examSpecialSettingsEntity.setCourseId(courseId);
-			examSpecialSettingsEntity.setStudentId(studentId);
-
-			// 机构特殊设置
-			if (null != orgId && null == courseId && null == studentId) {
-			}
-			// 学生特殊设置
-			else if (null == orgId && null == courseId && null != studentId) {
-				if (null != studentId) {
-					examSpecialSettingsEntity.setExt1(student.getIdentityNumber());
-				}
-			}
-			// 课程特殊设置
-			else if (null == orgId && null != courseId && null == studentId) {
-			}
-			// 考试设置
-			else if (null == orgId && null == courseId && null == studentId) {
-			}
-
-		} else {
-			examSpecialSettingsEntity = GlobalHelper.getEntity(examSpecialSettingsRepo,
-					examSpecialInfo.getId(), ExamSpecialSettingsEntity.class);
-			if (null == examSpecialSettingsEntity) {
-				throw new StatusException("001101", "id is wrong");
-			}
-		}
-
-		examSpecialSettingsEntity.setBeginTime(examSpecialInfo.getBeginTime());
-		examSpecialSettingsEntity.setEndTime(examSpecialInfo.getEndTime());
-
-		Boolean examLimit = examSpecialInfo.getExamLimit();
-		examLimit = null == examLimit ? false : examLimit;
-		examSpecialSettingsEntity.setExamLimit(examLimit);
-
-		ExamSpecialSettingsEntity saved = examSpecialSettingsRepo.save(examSpecialSettingsEntity);
-
-		Map<String, String> props = examSpecialInfo.getProperties();
-
-		if (null != props) {
-			Map<DynamicEnum, String> map = checkAndGetExamProperties(examEntity.getRootOrgId(),
-					props);
-
-			for (Entry<DynamicEnum, String> entry : map.entrySet()) {
-				DynamicEnum de = entry.getKey();
-				String value = entry.getValue();
-				if (StringUtils.isBlank(value)) {
-					value = null;
-				} else {
-					value = value.trim();
-				}
-
-				// 机构特殊设置
-				if (null != orgId && null == courseId && null == studentId) {
-					ExamOrgPropertyEntity entity = examOrgPropertyRepo.findByExamIdAndOrgIdAndKeyId(
-							saved.getExamId(), saved.getOrgId(), de.getId());
-					if (null == entity) {
-						entity = new ExamOrgPropertyEntity();
-						entity.setExamId(saved.getExamId());
-						entity.setOrgId(saved.getOrgId());
-						entity.setKeyId(de.getId());
-					}
-					entity.setValue(value);
-
-					examOrgPropertyRepo.save(entity);
-				}
-				// 学生特殊设置
-				else if (null == orgId && null == courseId && null != studentId) {
-
-				}
-				// 课程特殊设置
-				else if (null == orgId && null != courseId && null == studentId) {
-
-				}
-
-			}
-		}
-
-		return saved;
-	}
-
-	@Override
-	public String getExamOrgProperty(Long examId, Long orgId, String key) {
-		DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-		DynamicEnum de = manager.getByName(key);
-
-		if (null != orgId) {
-			ExamOrgPropertyEntity examOrgPropertyEntity = examOrgPropertyRepo
-					.findByExamIdAndOrgIdAndKeyId(examId, orgId, de.getId());
-			if (null != examOrgPropertyEntity) {
-				return examOrgPropertyEntity.getValue();
-			}
-		}
-
-		ExamPropertyEntity examPropertyEntity = examPropertyRepo.findByExamIdAndKeyId(examId,
-				de.getId());
-		if (null == examPropertyEntity) {
-			return null;
-		}
-
-		return examPropertyEntity.getValue();
-	}
-
-	@Override
-	public String getExamStudentProperty(Long examId, Long studentId, String key) {
-		DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-		DynamicEnum de = manager.getByName(key);
-
-		if (null != studentId) {
-			// 待实现
-		}
-
-		ExamPropertyEntity examPropertyEntity = examPropertyRepo.findByExamIdAndKeyId(examId,
-				de.getId());
-		if (null == examPropertyEntity) {
-			return null;
-		}
-
-		return examPropertyEntity.getValue();
-	}
-
-	@Override
-	public List<Map<String, Object>> importExamOrgSettings(Long examId, File file) {
-
-		ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		List<String[]> lineList = null;
-		try {
-			lineList = ExcelReader.readSheetBySax(PathUtil.getCanonicalPath(file), 1, 6);
-		} catch (Exception e) {
-			throw new StatusException("200110", "Excel 解析失败");
-		}
-
-		if (CollectionUtils.isEmpty(lineList)) {
-			throw new StatusException("200111", "Excel无内容");
-		}
-
-		if (10001 < lineList.size()) {
-			throw new StatusException("200112", "数据行数不能超过10000");
-		}
-
-		List<Map<String, Object>> failRecords = Collections
-				.synchronizedList(new ArrayList<Map<String, Object>>());
-		List<ExamSpecialSettingsInfo> specialSettingsList = Lists.newArrayList();
-
-		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();
-
-			ExamSpecialSettingsInfo examSpecialInfo = new ExamSpecialSettingsInfo();
-			examSpecialInfo.setRootOrgId(examEntity.getRootOrgId());
-			examSpecialInfo.setExamId(examId);
-
-			String orgId = trimAndNullIfBlank(line[0]);
-			if (StringUtils.isBlank(orgId)) {
-				msg.append("  学习中心ID不能为空");
-				hasError = true;
-			} else {
-				try {
-					long orgIdLong = Long.parseLong(orgId);
-
-					GetOrgReq getOrgReq = new GetOrgReq();
-					getOrgReq.setOrgId(orgIdLong);
-					GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
-					OrgBean orgBean = getOrgResp.getOrg();
-
-					if (null == orgBean.getParentId()
-							|| !orgBean.getRootId().equals(examEntity.getRootOrgId())) {
-						msg.append("  学习中心ID非法");
-						hasError = true;
-					} else {
-						examSpecialInfo.setOrgId(orgIdLong);
-					}
-				} catch (NumberFormatException e) {
-					msg.append("  学习中心ID必须为整数");
-					hasError = true;
-				}
-			}
-
-			String examLimit = trimAndNullIfBlank(line[3]);
-			if (StringUtils.isNotBlank(examLimit)) {
-				if (examLimit.equals("是")) {
-					examSpecialInfo.setExamLimit(false);
-				} else if (examLimit.equals("否")) {
-					examSpecialInfo.setExamLimit(true);
-				} else {
-					msg.append("  是否可以开始考试必须为['是','否',空]");
-					hasError = true;
-				}
-			} else {
-				examSpecialInfo.setExamLimit(false);
-			}
-
-			String beginTime = trimAndNullIfBlank(line[4]);
-			if (StringUtils.isNotBlank(beginTime)) {
-				try {
-					Date beginDate = DateUtil.parse(beginTime, DatePatterns.CHINA_DEFAULT);
-					examSpecialInfo.setBeginTime(beginDate);
-				} catch (Exception e) {
-					msg.append("  开始考试时间格式错误. 正确格式为 " + DatePatterns.CHINA_DEFAULT);
-					hasError = true;
-				}
-			}
-			String endTime = trimAndNullIfBlank(line[5]);
-			if (StringUtils.isNotBlank(endTime)) {
-				try {
-					Date endDate = DateUtil.parse(endTime, DatePatterns.CHINA_DEFAULT);
-					examSpecialInfo.setEndTime(endDate);
-				} catch (Exception e) {
-					msg.append("  结束考试时间格式错误. 正确格式为 " + DatePatterns.CHINA_DEFAULT);
-					hasError = true;
-				}
-			}
-
-			if (hasError) {
-				failRecords.add(newError(i + 1, msg.toString()));
-			} else {
-				specialSettingsList.add(examSpecialInfo);
-			}
-
-		}
-
-		if (CollectionUtils.isNotEmpty(failRecords)) {
-			return failRecords;
-		}
-
-		for (ExamSpecialSettingsInfo cur : specialSettingsList) {
-			saveExamSpecialSettings(cur);
-		}
-
-		return failRecords;
-
-	}
-
-	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();
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param header
-	 */
-	private boolean headerError(String[] header) {
-		for (int i = 0; i < EXAM_ORG_SETTINGS_EXCEL_HEADER.length; i++) {
-			if (null == header[i]) {
-				return true;
-			}
-			if (!EXAM_ORG_SETTINGS_EXCEL_HEADER[i].equals(header[i].trim())) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	@Override
-	public String getExamProperty(Long examId, String key) {
-		DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
-		DynamicEnum de = manager.getByName(key);
-
-		ExamPropertyEntity examPropertyEntity = examPropertyRepo.findByExamIdAndKeyId(examId,
-				de.getId());
-		if (null == examPropertyEntity) {
-			return null;
-		}
-
-		return examPropertyEntity.getValue();
-	}
+
+    private static Pattern pattern = Pattern.compile("\\s*");
+
+    @Autowired
+    ExamRepo examRepo;
+
+    @Autowired
+    ExamStudentService examStudentService;
+
+    @Autowired
+    ExamSpecialSettingsRepo examSpecialSettingsRepo;
+
+    @Autowired
+    OrgCloudService orgCloudService;
+
+    @Autowired
+    ExamPropertyRepo examPropertyRepo;
+
+    @Autowired
+    ExamOrgPropertyRepo examOrgPropertyRepo;
+
+    @Autowired
+    DataSyncCloudService dataSyncCloudService;
+
+    @Autowired
+    ExamSettingsCache examSettingsCache;
+
+    @Autowired
+    ExamPropertyCache examPropertyCache;
+
+    private static final String[] EXAM_ORG_SETTINGS_EXCEL_HEADER = new String[]{"学习中心ID", "学习中心代码",
+            "学习中心名称", "是否可以考试(是/否)", "开始考试时间 yyyy-MM-dd hh:mm:ss", "结束考试时间 yyyy-MM-dd hh:mm:ss"};
+
+    /*
+     * 实现
+     *
+     * @author WANGWEI
+     *
+     * @see
+     * cn.com.qmth.examcloud.core.examwork.service.ExamService#saveExam(cn.com.
+     * qmth.examcloud.core.examwork.service.bean.ExamInfo)
+     */
+    @Override
+    public ExamEntity saveExam(ExamInfo examInfo, CURD curd) {
+
+        Long rootOrgId = examInfo.getRootOrgId();
+        String code = examInfo.getCode();
+        String name = examInfo.getName();
+        ExamType examType = examInfo.getExamType();
+
+        if (null == examType) {
+            throw new StatusException("001005", "考试类型不能为空");
+        }
+
+        if (StringUtils.isBlank(code)) {
+            throw new StatusException("001005", "考试编码不能为空");
+        }
+
+        if (null == rootOrgId) {
+            throw new StatusException("001005", "rootOrgId is null");
+        }
+
+        // 上锁
+        Object[] locker = new Object[]{"E_EXAM_SAVE_EXAM", rootOrgId, code};
+        SequenceLockHelper.getLock(locker);
+
+        GetOrgReq getOrgReq = new GetOrgReq();
+        getOrgReq.setOrgId(rootOrgId);
+        GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
+        OrgBean rootOrg = getOrgResp.getOrg();
+
+        Map<String, String> properties = examInfo.getProperties();
+        Map<DynamicEnum, String> map = checkAndGetExamProperties(rootOrgId, properties);
+
+        ExamEntity exam = null;
+        CURD realStatus = null;
+
+        // 更新
+        if (curd.equals(CURD.UPDATE)) {
+            exam = examRepo.findByCodeAndRootOrgId(code, rootOrgId);
+            if (null == exam) {
+                throw new StatusException("002001", "code is wrong");
+            }
+            realStatus = CURD.UPDATE;
+        }
+        // 创建
+        else if (curd.equals(CURD.CREATION)) {
+            exam = new ExamEntity();
+            realStatus = CURD.CREATION;
+        }
+        // (根据考试编码)新增或创建
+        else if (curd.equals(CURD.CREATION_OR_UPDATE)) {
+            exam = examRepo.findByCodeAndRootOrgId(code, rootOrgId);
+            // 创建
+            if (null == exam) {
+                exam = new ExamEntity();
+                realStatus = CURD.CREATION;
+            }
+            // 更新
+            else {
+                realStatus = CURD.UPDATE;
+            }
+        }
+
+        if (realStatus.equals(CURD.CREATION)) {
+            if (null == examInfo.getBeginTime()) {
+                throw new StatusException("002006", "beginTime is null");
+            }
+            if (null == examInfo.getEndTime()) {
+                throw new StatusException("002006", "endTime is null");
+            }
+            if (StringUtils.isBlank(name)) {
+                throw new StatusException("002004", "name is blank");
+            }
+
+            ExamEntity examByCode = examRepo.findByCodeAndRootOrgId(code, rootOrgId);
+            if (null != examByCode) {
+                throw new StatusException("002005", "考试编码已存在");
+            }
+
+            ExamEntity examByName = examRepo.findByNameAndRootOrgId(name, rootOrgId);
+            if (null != examByName) {
+                throw new StatusException("002005", "考试名称已存在");
+            }
+
+            exam.setCode(code);
+            exam.setRootOrgId(rootOrgId);
+            exam.setExamType(examType);
+            exam.setEnable(true);
+            exam.setExamLimit(false);
+            exam.setSpecialSettingsEnabled(false);
+        } else if (realStatus.equals(CURD.UPDATE)) {
+            if (!exam.getRootOrgId().equals(rootOrgId)) {
+                throw new StatusException("002003", "rootOrgId is wrong");
+            }
+            if (!exam.getExamType().equals(examType)) {
+                throw new StatusException("002100", "examType is wrong");
+            }
+        }
+
+        if (null != examInfo.getSpecialSettingsEnabled()) {
+            exam.setSpecialSettingsEnabled(examInfo.getSpecialSettingsEnabled());
+        }
+        if (null != examInfo.getSpecialSettingsType()) {
+            exam.setSpecialSettingsType(examInfo.getSpecialSettingsType());
+        }
+        if (null != examInfo.getBeginTime()) {
+            exam.setBeginTime(examInfo.getBeginTime());
+        }
+        if (null != examInfo.getEndTime()) {
+            exam.setEndTime(examInfo.getEndTime());
+        }
+        if (null != examInfo.getDuration()) {
+            exam.setDuration(examInfo.getDuration());
+        }
+        if (null != examInfo.getEnable()) {
+            exam.setEnable(examInfo.getEnable());
+        }
+        if (null != examInfo.getExamTimes()) {
+            exam.setExamTimes(examInfo.getExamTimes());
+        }
+        if (null != examInfo.getExamLimit()) {
+            exam.setExamLimit(examInfo.getExamLimit());
+        }
+        if (StringUtils.isNotBlank(name)) {
+            exam.setName(name);
+        }
+        if (StringUtils.isNotBlank(examInfo.getRemark())) {
+            exam.setRemark(examInfo.getRemark());
+        }
+
+        // 数据订正代码
+        exam.setExamLimit(false);
+
+        ExamEntity saved = examRepo.saveAndFlush(exam);
+
+        for (Entry<DynamicEnum, String> entry : map.entrySet()) {
+            DynamicEnum de = entry.getKey();
+            String value = entry.getValue();
+            ExamPropertyEntity entity = examPropertyRepo.findByExamIdAndKeyId(saved.getId(),
+                    de.getId());
+            if (null == entity) {
+                entity = new ExamPropertyEntity();
+                entity.setExamId(saved.getId());
+                entity.setKeyId(de.getId());
+            }
+            entity.setValue(value);
+
+            examPropertyRepo.save(entity);
+        }
+
+        // 考试设置混入特殊设置
+        ExamSpecialSettingsInfo examSpecialInfo = new ExamSpecialSettingsInfo();
+        examSpecialInfo.setBeginTime(saved.getBeginTime());
+        examSpecialInfo.setEndTime(saved.getEndTime());
+        examSpecialInfo.setExamId(saved.getId());
+        examSpecialInfo.setExamLimit(saved.getExamLimit());
+        examSpecialInfo.setRootOrgId(saved.getRootOrgId());
+        examSpecialInfo.setExamLimit(saved.getExamLimit());
+        this.saveExamSpecialSettings(examSpecialInfo);
+
+        examSpecialSettingsRepo.updateExamEnableByExamId(saved.getId(), saved.getEnable());
+        examSpecialSettingsRepo.updateSpecialSettingsEnabledByExamId(saved.getId(),
+                saved.getSpecialSettingsEnabled());
+        if (null == saved.getSpecialSettingsType()) {
+            examSpecialSettingsRepo.updateSpecialSettingsTypeByExamId(saved.getId(), null);
+        } else {
+            examSpecialSettingsRepo.updateSpecialSettingsTypeByExamId(saved.getId(),
+                    saved.getSpecialSettingsType());
+        }
+
+        SyncExamReq req = new SyncExamReq();
+        req.setId(saved.getId());
+        req.setBeginTime(saved.getBeginTime());
+        req.setDuration(saved.getDuration());
+        req.setEnable(saved.getEnable());
+        req.setEndTime(saved.getEndTime());
+        req.setExamTimes(saved.getExamTimes());
+        req.setExamType(saved.getExamType().name());
+        req.setName(saved.getName());
+        req.setRemark(saved.getRemark());
+        req.setRootOrgId(saved.getRootOrgId());
+        req.setRootOrgName(rootOrg.getName());
+        req.setSyncType("update");
+        dataSyncCloudService.syncExam(req);
+
+        examSettingsCache.remove(saved.getId());
+
+        if (null != properties) {
+            for (String cur : properties.keySet()) {
+                examPropertyCache.remove(saved.getId(), cur);
+            }
+        }
+
+        return saved;
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param rootOrgId
+     * @param properties
+     * @return
+     * @author WANGWEI
+     */
+    private Map<DynamicEnum, String> checkAndGetExamProperties(Long rootOrgId,
+                                                               Map<String, String> properties) {
+        DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+
+        Map<DynamicEnum, String> map = Maps.newHashMap();
+        if (null == properties) {
+            return map;
+        }
+
+        for (Entry<String, String> entry : properties.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            DynamicEnum de;
+            try {
+                de = manager.getByName(key);
+            } catch (Exception e) {
+                throw new StatusException("001004", "考试属性错误. key=" + key);
+            }
+            if (!de.isLegal(value)) {
+                throw new StatusException("001200", "考试属性值非法. key=" + key);
+            }
+            map.put(de, value);
+        }
+
+        String beforeExamRemark = properties.get("BEFORE_EXAM_REMARK");
+
+        if (null != beforeExamRemark) {
+            Document doc = Jsoup.parse(beforeExamRemark);
+            Matcher m = pattern.matcher(doc.text());
+            String simpleText = m.replaceAll("").replaceAll(" ", "");
+            if (simpleText.length() > 800) {
+                throw new StatusException("001002", "考前说明内容不得超过800个字符");
+            }
+            long imgsize = getImgSizeByBase64String(beforeExamRemark);
+            if (imgsize > 150 * 1024) {
+                throw new StatusException("001002", "考前说明图片总大小不得超过150KB");
+            }
+        }
+
+        String afterExamRemark = properties.get("AFTER_EXAM_REMARK");
+        if (null != afterExamRemark) {
+            Document doc = Jsoup.parse(afterExamRemark);
+            Matcher m = pattern.matcher(doc.text());
+            String simpleText = m.replaceAll("").replaceAll(" ", "");
+            if (simpleText.length() > 800) {
+                throw new StatusException("001002", "考后说明内容不得超过800个字符");
+            }
+            long imgsize = getImgSizeByBase64String(afterExamRemark);
+            if (imgsize > 150 * 1024) {
+                throw new StatusException("001002", "考后说明图片总大小不得超过150KB");
+            }
+        }
+
+        String cheatingRemark = properties.get("CHEATING_REMARK");
+        if (null != cheatingRemark) {
+            Document doc = Jsoup.parse(cheatingRemark);
+            Matcher m = pattern.matcher(doc.text());
+            String simpleText = m.replaceAll("").replaceAll(" ", "");
+            if (simpleText.length() > 800) {
+                throw new StatusException("001002", "作弊说明内容不得超过800个字符");
+            }
+            long imgsize = getImgSizeByBase64String(cheatingRemark);
+            if (imgsize > 150 * 1024) {
+                throw new StatusException("001002", "作弊说明图片总大小不得超过150KB");
+            }
+        }
+
+        // 校验机构权限
+        // 人脸识别功能校验
+        String faceCheck = PrivilegeDefine.RootOrgFunctions.OnlineExamFunctions.FaceCheck.CODE;
+        Boolean hasFaceCheckFunction = PrivilegeManager.judge(rootOrgId, faceCheck);
+        String isFaceEnable = properties.get("IS_FACE_ENABLE");
+        if (!hasFaceCheckFunction) {
+            if (StringUtils.isNotBlank(isFaceEnable)
+                    && isFaceEnable.equalsIgnoreCase(Boolean.toString(true))) {
+                throw new StatusException("001002", "人脸识别功能未开放");
+            }
+        }
+
+        // 活体检测功能校验
+        String IdentificationOfLivingBody = PrivilegeDefine.RootOrgFunctions.OnlineExamFunctions.IdentificationOfLivingBody.CODE;
+        Boolean hasIdentificationOfLivingBodyFunction = PrivilegeManager.judge(rootOrgId,
+                IdentificationOfLivingBody);
+        String isFceVerify = properties.get("IS_FACE_VERIFY");
+        if (!hasIdentificationOfLivingBodyFunction) {
+            if (StringUtils.isNotBlank(isFceVerify)
+                    && isFceVerify.equalsIgnoreCase(Boolean.toString(true))) {
+                throw new StatusException("001002", "活体检测功能未开放");
+            }
+        }
+
+        return map;
+    }
+
+    /**
+     * @param content 内容
+     * @return 内容中图片文件总大小
+     */
+    private long getImgSizeByBase64String(String content) {
+        long size = 0;
+        if (StringUtils.isNoneEmpty(content)) {
+            Document doc = Jsoup.parse(content);
+            Elements imgs = doc.getElementsByTag("img");
+            if (imgs != null) {
+                for (Element img : imgs) {
+                    String src = img.attr("src");
+                    if (src.startsWith("data:")) {
+                        String base64 = src.split(",")[1];
+                        int equalIndex = base64.indexOf("=");
+                        if (equalIndex > 0) {
+                            base64 = base64.substring(0, equalIndex);
+                        }
+                        long fileSize = base64.length() - (base64.length() / 8) * 2;
+                        size = size + fileSize;
+                    }
+                }
+            }
+        }
+        return size;
+    }
+
+    /*
+     * 实现
+     *
+     * @author WANGWEI
+     *
+     * @see
+     * cn.com.qmth.examcloud.core.examwork.service.ExamService#saveExamOrg(cn.
+     * com.qmth.examcloud.core.examwork.service.bean.ExamOrgInfo)
+     */
+    @Override
+    public ExamSpecialSettingsEntity saveExamSpecialSettings(
+            ExamSpecialSettingsInfo examSpecialInfo) {
+
+        ExamSpecialSettingsEntity examSpecialSettingsEntity = null;
+
+        Long rootOrgId = examSpecialInfo.getRootOrgId();
+        Long examId = examSpecialInfo.getExamId();
+        Date beginTime = examSpecialInfo.getBeginTime();
+        Date endTime = examSpecialInfo.getEndTime();
+        Long orgId = examSpecialInfo.getOrgId();
+        Long studentId = examSpecialInfo.getStudentId();
+        Long courseId = examSpecialInfo.getCourseId();
+
+        int trueNum = BooleanUtil.countTrue(null != orgId, null != courseId, null != studentId);
+        if (1 < trueNum) {
+            throw new StatusException("001502", "参数错误");
+        }
+
+        // 上锁
+        Object[] locker = new Object[]{"E_EXAM_SPECIAL_SETTINGS", examId, "ORG" + orgId,
+                "COURSE" + courseId, "Student" + studentId};
+        SequenceLockHelper.getLock(locker);
+
+        if (!new Boolean(null == beginTime).equals(new Boolean(null == endTime))) {
+            throw new StatusException("001101", "beginTime & endTime  wrong");
+        }
+
+        ExamEntity examEntity = GlobalHelper.getPresentEntity(examRepo, examId, ExamEntity.class);
+        GlobalHelper.uniformRootOrg(examEntity.getRootOrgId(), rootOrgId);
+
+        StudentCacheBean student = null;
+        if (null != studentId) {
+            student = CacheHelper.getStudent(studentId);
+            GlobalHelper.uniformRootOrg(student.getRootOrgId(), rootOrgId);
+        }
+
+        if (null == examSpecialInfo.getId()) {
+            // 机构特殊设置
+            if (null != orgId && null == courseId && null == studentId) {
+                examSpecialSettingsEntity = examSpecialSettingsRepo
+                        .findByExamIdAndOrgIdAndCourseIdIsNullAndStudentIdIsNull(
+                                examSpecialInfo.getExamId(), orgId);
+            }
+            // 学生特殊设置
+            else if (null == orgId && null == courseId && null != studentId) {
+                examSpecialSettingsEntity = examSpecialSettingsRepo
+                        .findByExamIdAndStudentIdAndOrgIdIsNullAndCourseIdIsNull(
+                                examSpecialInfo.getExamId(), studentId);
+            }
+            // 课程特殊设置
+            else if (null == orgId && null != courseId && null == studentId) {
+
+            }
+            // 考试设置
+            else if (null == orgId && null == courseId && null == studentId) {
+                examSpecialSettingsEntity = examSpecialSettingsRepo
+                        .findAllByExamIdAndOrgIdIsNullAndCourseIdIsNullAndStudentIdIsNull(
+                                examSpecialInfo.getExamId());
+            } else {
+                throw new StatusException("001501", "参数错误");
+            }
+
+            if (null == examSpecialSettingsEntity) {
+                examSpecialSettingsEntity = new ExamSpecialSettingsEntity();
+            }
+
+            examSpecialSettingsEntity.setRootOrgId(rootOrgId);
+            examSpecialSettingsEntity.setExamId(examId);
+            examSpecialSettingsEntity.setExamType(examEntity.getExamType());
+            examSpecialSettingsEntity.setExamEnable(examEntity.getEnable());
+            examSpecialSettingsEntity
+                    .setSpecialSettingsEnabled(examEntity.getSpecialSettingsEnabled());
+            examSpecialSettingsEntity.setSpecialSettingsType(examEntity.getSpecialSettingsType());
+
+            examSpecialSettingsEntity.setOrgId(orgId);
+            examSpecialSettingsEntity.setCourseId(courseId);
+            examSpecialSettingsEntity.setStudentId(studentId);
+
+            // 机构特殊设置
+            if (null != orgId && null == courseId && null == studentId) {
+            }
+            // 学生特殊设置
+            else if (null == orgId && null == courseId && null != studentId) {
+                if (null != studentId) {
+                    examSpecialSettingsEntity.setExt1(student.getIdentityNumber());
+                }
+            }
+            // 课程特殊设置
+            else if (null == orgId && null != courseId && null == studentId) {
+            }
+            // 考试设置
+            else if (null == orgId && null == courseId && null == studentId) {
+            }
+
+        } else {
+            examSpecialSettingsEntity = GlobalHelper.getEntity(examSpecialSettingsRepo,
+                    examSpecialInfo.getId(), ExamSpecialSettingsEntity.class);
+            if (null == examSpecialSettingsEntity) {
+                throw new StatusException("001101", "id is wrong");
+            }
+        }
+
+        examSpecialSettingsEntity.setBeginTime(examSpecialInfo.getBeginTime());
+        examSpecialSettingsEntity.setEndTime(examSpecialInfo.getEndTime());
+
+        Boolean examLimit = examSpecialInfo.getExamLimit();
+        examLimit = null == examLimit ? false : examLimit;
+        examSpecialSettingsEntity.setExamLimit(examLimit);
+
+        ExamSpecialSettingsEntity saved = examSpecialSettingsRepo.save(examSpecialSettingsEntity);
+
+        Map<String, String> props = examSpecialInfo.getProperties();
+
+        if (null != props) {
+            Map<DynamicEnum, String> map = checkAndGetExamProperties(examEntity.getRootOrgId(),
+                    props);
+
+            for (Entry<DynamicEnum, String> entry : map.entrySet()) {
+                DynamicEnum de = entry.getKey();
+                String value = entry.getValue();
+                if (StringUtils.isBlank(value)) {
+                    value = null;
+                } else {
+                    value = value.trim();
+                }
+
+                // 机构特殊设置
+                if (null != orgId && null == courseId && null == studentId) {
+                    ExamOrgPropertyEntity entity = examOrgPropertyRepo.findByExamIdAndOrgIdAndKeyId(
+                            saved.getExamId(), saved.getOrgId(), de.getId());
+                    if (null == entity) {
+                        entity = new ExamOrgPropertyEntity();
+                        entity.setExamId(saved.getExamId());
+                        entity.setOrgId(saved.getOrgId());
+                        entity.setKeyId(de.getId());
+                    }
+                    entity.setValue(value);
+
+                    examOrgPropertyRepo.save(entity);
+                }
+                // 学生特殊设置
+                else if (null == orgId && null == courseId && null != studentId) {
+
+                }
+                // 课程特殊设置
+                else if (null == orgId && null != courseId && null == studentId) {
+
+                }
+
+            }
+        }
+
+        return saved;
+    }
+
+    @Override
+    public String getExamOrgProperty(Long examId, Long orgId, String key) {
+        DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+        DynamicEnum de = manager.getByName(key);
+
+        if (null != orgId) {
+            ExamOrgPropertyEntity examOrgPropertyEntity = examOrgPropertyRepo
+                    .findByExamIdAndOrgIdAndKeyId(examId, orgId, de.getId());
+            if (null != examOrgPropertyEntity) {
+                return examOrgPropertyEntity.getValue();
+            }
+        }
+
+        ExamPropertyEntity examPropertyEntity = examPropertyRepo.findByExamIdAndKeyId(examId,
+                de.getId());
+        if (null == examPropertyEntity) {
+            return null;
+        }
+
+        return examPropertyEntity.getValue();
+    }
+
+    @Override
+    public String getExamStudentProperty(Long examId, Long studentId, String key) {
+        DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+        DynamicEnum de = manager.getByName(key);
+
+        if (null != studentId) {
+            // 待实现
+        }
+
+        ExamPropertyEntity examPropertyEntity = examPropertyRepo.findByExamIdAndKeyId(examId,
+                de.getId());
+        if (null == examPropertyEntity) {
+            return null;
+        }
+
+        return examPropertyEntity.getValue();
+    }
+
+    @Override
+    public List<Map<String, Object>> importExamOrgSettings(Long examId, File file) {
+
+        ExamEntity examEntity = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        List<String[]> lineList = null;
+        try {
+            lineList = ExcelReader.readSheetBySax(PathUtil.getCanonicalPath(file), 1, 6);
+        } catch (Exception e) {
+            throw new StatusException("200110", "Excel 解析失败");
+        }
+
+        if (CollectionUtils.isEmpty(lineList)) {
+            throw new StatusException("200111", "Excel无内容");
+        }
+
+        if (10001 < lineList.size()) {
+            throw new StatusException("200112", "数据行数不能超过10000");
+        }
+
+        List<Map<String, Object>> failRecords = Collections
+                .synchronizedList(new ArrayList<Map<String, Object>>());
+        List<ExamSpecialSettingsInfo> specialSettingsList = Lists.newArrayList();
+
+        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();
+
+            ExamSpecialSettingsInfo examSpecialInfo = new ExamSpecialSettingsInfo();
+            examSpecialInfo.setRootOrgId(examEntity.getRootOrgId());
+            examSpecialInfo.setExamId(examId);
+
+            String orgId = trimAndNullIfBlank(line[0]);
+            if (StringUtils.isBlank(orgId)) {
+                msg.append("  学习中心ID不能为空");
+                hasError = true;
+            } else {
+                try {
+                    long orgIdLong = Long.parseLong(orgId);
+
+                    GetOrgReq getOrgReq = new GetOrgReq();
+                    getOrgReq.setOrgId(orgIdLong);
+                    GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
+                    OrgBean orgBean = getOrgResp.getOrg();
+
+                    if (null == orgBean.getParentId()
+                            || !orgBean.getRootId().equals(examEntity.getRootOrgId())) {
+                        msg.append("  学习中心ID非法");
+                        hasError = true;
+                    } else {
+                        examSpecialInfo.setOrgId(orgIdLong);
+                    }
+                } catch (NumberFormatException e) {
+                    msg.append("  学习中心ID必须为整数");
+                    hasError = true;
+                }
+            }
+
+            String examLimit = trimAndNullIfBlank(line[3]);
+            if (StringUtils.isNotBlank(examLimit)) {
+                if (examLimit.equals("是")) {
+                    examSpecialInfo.setExamLimit(false);
+                } else if (examLimit.equals("否")) {
+                    examSpecialInfo.setExamLimit(true);
+                } else {
+                    msg.append("  是否可以开始考试必须为['是','否',空]");
+                    hasError = true;
+                }
+            } else {
+                examSpecialInfo.setExamLimit(false);
+            }
+
+            String beginTime = trimAndNullIfBlank(line[4]);
+            if (StringUtils.isNotBlank(beginTime)) {
+                try {
+                    Date beginDate = DateUtil.parse(beginTime, DatePatterns.CHINA_DEFAULT);
+                    examSpecialInfo.setBeginTime(beginDate);
+                } catch (Exception e) {
+                    msg.append("  开始考试时间格式错误. 正确格式为 " + DatePatterns.CHINA_DEFAULT);
+                    hasError = true;
+                }
+            }
+            String endTime = trimAndNullIfBlank(line[5]);
+            if (StringUtils.isNotBlank(endTime)) {
+                try {
+                    Date endDate = DateUtil.parse(endTime, DatePatterns.CHINA_DEFAULT);
+                    examSpecialInfo.setEndTime(endDate);
+                } catch (Exception e) {
+                    msg.append("  结束考试时间格式错误. 正确格式为 " + DatePatterns.CHINA_DEFAULT);
+                    hasError = true;
+                }
+            }
+
+            if (hasError) {
+                failRecords.add(newError(i + 1, msg.toString()));
+            } else {
+                specialSettingsList.add(examSpecialInfo);
+            }
+
+        }
+
+        if (CollectionUtils.isNotEmpty(failRecords)) {
+            return failRecords;
+        }
+
+        for (ExamSpecialSettingsInfo cur : specialSettingsList) {
+            saveExamSpecialSettings(cur);
+        }
+
+        return failRecords;
+
+    }
+
+    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 < EXAM_ORG_SETTINGS_EXCEL_HEADER.length; i++) {
+            if (null == header[i]) {
+                return true;
+            }
+            if (!EXAM_ORG_SETTINGS_EXCEL_HEADER[i].equals(header[i].trim())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String getExamProperty(Long examId, String key) {
+        DynamicEnumManager manager = ExamProperty.getDynamicEnumManager();
+        DynamicEnum de = manager.getByName(key);
+
+        ExamPropertyEntity examPropertyEntity = examPropertyRepo.findByExamIdAndKeyId(examId,
+                de.getId());
+        if (null == examPropertyEntity) {
+            return null;
+        }
+
+        return examPropertyEntity.getValue();
+    }
+
+    @Override
+    public Map<Long, String> getExamNamesByIds(Set<Long> examIds) {
+        if (CollectionUtils.isEmpty(examIds)) {
+            return new HashMap<>();
+        }
+
+        Specification<ExamEntity> spec = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(root.get("id").in(examIds));
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        List<ExamEntity> entities = examRepo.findAll(spec);
+        return entities.stream().collect(Collectors.toMap(v -> v.getId(), v -> v.getName(), (oldValue, newValue) -> newValue));
+    }
 
 }

+ 15 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/ExamStageServiceImpl.java

@@ -0,0 +1,15 @@
+package cn.com.qmth.examcloud.core.examwork.service.impl;
+
+import cn.com.qmth.examcloud.core.examwork.service.ExamStageService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @Description 场次
+ * @Author lideyin
+ * @Date 2020/7/22 14:58
+ * @Version 1.0
+ */
+@Service
+public class ExamStageServiceImpl implements ExamStageService {
+
+}

+ 15 - 0
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/ExamStageSettingServiceImpl.java

@@ -0,0 +1,15 @@
+package cn.com.qmth.examcloud.core.examwork.service.impl;
+
+import cn.com.qmth.examcloud.core.examwork.service.ExamStageSettingService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @Description 场次设置
+ * @Author lideyin
+ * @Date 2020/7/22 14:58
+ * @Version 1.0
+ */
+@Service
+public class ExamStageSettingServiceImpl implements ExamStageSettingService {
+
+}

+ 479 - 462
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/ExamStudentServiceImpl.java

@@ -1,14 +1,5 @@
 package cn.com.qmth.examcloud.core.examwork.service.impl;
 
-import java.util.Date;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.commons.lang3.StringUtils;
-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.util.DateUtil;
 import cn.com.qmth.examcloud.commons.util.DateUtil.DatePatterns;
@@ -43,6 +34,14 @@ import cn.com.qmth.examcloud.task.api.DataSyncCloudService;
 import cn.com.qmth.examcloud.task.api.request.SyncExamStudentReq;
 import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 考试学生服务类 Created by songyue on 17/1/14.
@@ -50,458 +49,476 @@ import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
 @Service
 public class ExamStudentServiceImpl implements ExamStudentService {
 
-	@Autowired
-	ExamStudentRepo examStudentRepo;
-
-	@Autowired
-	ExamRepo examRepo;
-
-	@Autowired
-	OrgCloudService orgCloudService;
-
-	@Autowired
-	ExamStudentServiceImpl examStudentService;
-
-	@Autowired
-	StudentCloudService studentCloudService;
-
-	@Autowired
-	CourseCloudService courseCloudService;
-
-	@Autowired
-	ExamCourseRelationRepo examCourseRelationRepo;
-
-	@Autowired
-	DataSyncCloudService dataSyncCloudService;
-
-	@Autowired
-	ExamRecordCloudService examRecordCloudService;
-
-	@Autowired
-	ExamPaperTypeRelationRepo examPaperTypeRelationRepo;
-
-	/**
-	 * 是否强制删除考生
-	 *
-	 * @author WANGWEI
-	 * @return
-	 */
-	private boolean forceDeleteExamStudent() {
-		boolean b = PropertyHolder.getBoolean("$deleteExamStudent.force", false);
-		return b;
-	}
-
-	/**
-	 * 是否开考
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 * @param StudentId
-	 * @param courseId
-	 * @return
-	 */
-	private boolean isStarted(Long examId, Long StudentId, Long courseId) {
-		CheckExamIsStartedReq checkExamIsStartedReq = new CheckExamIsStartedReq();
-		checkExamIsStartedReq.setExamId(examId);
-		checkExamIsStartedReq.setCourseId(courseId);
-		checkExamIsStartedReq.setStudentId(StudentId);
-		CheckExamIsStartedResp checkExamIsStartedResp = examRecordCloudService
-				.checkExamIsStarted(checkExamIsStartedReq);
-		return checkExamIsStartedResp.getIsStarted();
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param ids
-	 */
-	@Override
-	public void deleteExamStudentsByStudentIds(List<Long> ids) {
-		List<ExamStudentEntity> examStudents = examStudentRepo.findByIdIn(ids);
-
-		for (ExamStudentEntity examStudent : examStudents) {
-			// 网考判断
-			if ((!forceDeleteExamStudent()) && isStarted(examStudent.getExamId(), null, null)) {
-				throw new StatusException("150112", examStudent.getName() + "已开始考试,不能删除");
-			}
-			examStudentRepo.delete(examStudent);
-
-			List<ExamStudentEntity> top2 = examStudentRepo.findTop2ByExamIdAndCourseId(
-					examStudent.getExamId(), examStudent.getCourseId());
-			if (1 > top2.size()) {
-				examCourseRelationRepo.deleteByExamIdAndCourseId(examStudent.getExamId(),
-						examStudent.getCourseId());
-			}
-
-			top2 = examStudentRepo.findTop2ByExamIdAndCourseIdAndPaperType(examStudent.getExamId(),
-					examStudent.getCourseId(), examStudent.getPaperType());
-			if (1 > top2.size()) {
-				examPaperTypeRelationRepo.deleteByExamIdAndCourseIdAndPaperType(
-						examStudent.getExamId(), examStudent.getCourseId(),
-						examStudent.getPaperType());
-			}
-		}
-
-		for (ExamStudentEntity cur : examStudents) {
-			// 同步操作
-			SyncExamStudentReq req = new SyncExamStudentReq();
-			req.setSyncType("delete");
-			req.setId(cur.getId());
-			req.setCourseId(cur.getId());
-			req.setCourseCode(cur.getCourseCode());
-			req.setCourseName(cur.getCourseName());
-			req.setExamId(cur.getId());
-			req.setStudentId(cur.getStudentId());
-			req.setStudentName(cur.getName());
-			req.setIdentityNumber(cur.getIdentityNumber());
-			req.setStudentCode(cur.getStudentCode());
-			dataSyncCloudService.syncExamStudent(req);
-		}
-
-	}
-
-	/**
-	 * 方法注释
-	 *
-	 * @author WANGWEI
-	 * @param examId
-	 */
-	@Override
-	public void deleteExamStudentsByExamId(Long examId) {
-		ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-		// 已经开考
-		if ((!forceDeleteExamStudent()) && isStarted(exam.getId(), null, null)) {
-			throw new StatusException("150113", "该考试已开始,不能删除");
-		}
-
-		ExamStudentEntity entity = new ExamStudentEntity();
-		entity.setExamId(examId);
-		Example<ExamStudentEntity> example = Example.of(entity);
-		long count = examStudentRepo.count(example);
-		if (1000 < count) {
-			throw new StatusException("150116", "考生数量超过1000,无法删除");
-		}
-
-		List<ExamStudentEntity> list = examStudentRepo.findAll(example);
-
-		List<Long> IdList = list.stream().map(ExamStudentEntity::getId)
-				.collect(Collectors.toList());
-
-		deleteExamStudentsByStudentIds(IdList);
-	}
-
-	/*
-	 * 实现
-	 *
-	 * @author WANGWEI
-	 * 
-	 * @see cn.com.qmth.examcloud.core.examwork.service.ExamStudentService#
-	 * saveExamStudent(cn.com.qmth.examcloud.core.examwork.service.bean.
-	 * ExamStudentInfo)
-	 */
-	@Override
-	public ExamStudentInfo saveExamStudent(ExamStudentInfo examStudentInfo) {
-		String paperType = examStudentInfo.getPaperType();
-		String studentCode = examStudentInfo.getStudentCode();
-
-		if (StringUtils.isBlank(paperType)) {
-			paperType = "X";
-		} else if (!paperType.matches("[A-Z]")) {
-			throw new StatusException("100020", "试卷类型必须为一个大写的英文字母");
-		}
-
-		Long rootOrgId = examStudentInfo.getRootOrgId();
-		GetOrgReq getOrgReq = new GetOrgReq();
-		getOrgReq.setOrgId(rootOrgId);
-		GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
-		OrgBean rootOrg = getOrgResp.getOrg();
-		if (null != rootOrg.getParentId()) {
-			throw new StatusException("100001", "rootOrgId is wrong");
-		}
-
-		ExamEntity exam = null;
-		Long examId = examStudentInfo.getExamId();
-		String examName = examStudentInfo.getExamName();
-		String examCode = examStudentInfo.getExamCode();
-
-		if (null != examId) {
-			exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
-			if (null == exam) {
-				throw new StatusException("100020", "examId is wrong");
-			}
-		} else if (StringUtils.isNotBlank(examCode)) {
-			exam = examRepo.findByCodeAndRootOrgId(examCode, rootOrgId);
-			if (null == exam) {
-				throw new StatusException("1000050", "考试不存在");
-			}
-		} else if (StringUtils.isNotBlank(examName)) {
-			exam = examRepo.findByNameAndRootOrgId(examName, rootOrgId);
-			if (null == exam) {
-				throw new StatusException("100005", "考试不存在");
-			}
-		} else {
-			throw new StatusException("100002", "examId,examCode,examName cannot be all empty");
-		}
-
-		String identityNumber = examStudentInfo.getIdentityNumber();
-
-		if (StringUtils.isBlank(identityNumber)) {
-			throw new StatusException("100003", "identityNumber is null");
-		}
-		GetStudentReq getStudentReq = new GetStudentReq();
-		getStudentReq.setRootOrgId(rootOrgId);
-		getStudentReq.setIdentityNumber(identityNumber);
-		GetStudentResp getStudentResp = studentCloudService.getStudent(getStudentReq);
-
-		StudentBean studentInfo = getStudentResp.getStudentInfo();
-
-		if (StringUtils.isNotBlank(studentCode)) {
-			List<String> studentCodeList = studentInfo.getStudentCodeList();
-			boolean containIgnoreCase = false;
-			for (String cur : studentCodeList) {
-				if (cur.equalsIgnoreCase(studentCode)) {
-					containIgnoreCase = true;
-					studentCode = cur;
-					break;
-				}
-			}
-			if (!containIgnoreCase) {
-				throw new StatusException("100063", "学号未绑定到身份证");
-			}
-		} else {
-			studentCode = null;
-		}
-
-		String courseCode = examStudentInfo.getCourseCode();
-		String courseName = examStudentInfo.getCourseName();
-		String courseLevel = examStudentInfo.getCourseLevel();
-
-		SaveCourseReq saveCourseReq = new SaveCourseReq();
-		saveCourseReq.setCourseId(examStudentInfo.getCourseId());
-		saveCourseReq.setCourseCode(courseCode);
-		saveCourseReq.setCourseName(courseName);
-		saveCourseReq.setCourseLevel(courseLevel);
-		saveCourseReq.setRootOrgId(rootOrgId);
-
-		SaveCourseResp saveCourseResp = courseCloudService.saveCourse(saveCourseReq);
-		CourseBean courseBean = saveCourseResp.getCourseBean();
-
-		ExamStudentEntity examStudent = examStudentRepo.findByExamIdAndStudentIdAndCourseId(
-				exam.getId(), studentInfo.getId(), courseBean.getId());
-
-		if (null == examStudent) {
-			examStudent = new ExamStudentEntity();
-			examStudent.setEnable(true);
-		} else {
-			if (!paperType.equals(examStudent.getPaperType())) {
-				List<ExamStudentEntity> top2 = examStudentRepo
-						.findTop2ByExamIdAndCourseIdAndPaperType(examStudent.getExamId(),
-								examStudent.getCourseId(), examStudent.getPaperType());
-				if (2 > top2.size()) {
-					examPaperTypeRelationRepo.deleteByExamIdAndCourseIdAndPaperType(
-							examStudent.getExamId(), examStudent.getCourseId(),
-							examStudent.getPaperType());
-				}
-			}
-		}
-
-		examStudent.setInfoCollector(examStudentInfo.getInfoCollector());
-		examStudent.setName(studentInfo.getName());
-		examStudent.setRootOrgId(rootOrgId);
-
-		examStudent.setCourseId(courseBean.getId());
-		examStudent.setCourseName(courseBean.getName());
-		examStudent.setCourseCode(courseBean.getCode());
-		examStudent.setCourseLevel(courseBean.getLevel());
-
-		examStudent.setExamId(exam.getId());
-		examStudent.setIdentityNumber(studentInfo.getIdentityNumber());
-		examStudent.setStudentCode(studentCode);
-		examStudent.setPaperType(paperType);
-		examStudent.setStudentId(studentInfo.getId());
-		examStudent.setOrgId(studentInfo.getOrgId());
-		examStudent.setOrgCode(studentInfo.getOrgCode());
-		examStudent.setGrade(examStudentInfo.getGrade());
-		examStudent.setSpecialtyName(examStudentInfo.getSpecialtyName());
-		examStudent.setExamSite(examStudentInfo.getExamSite());
-		examStudent.setRemark(examStudentInfo.getRemark());
-
-		examStudent.setExt1(examStudentInfo.getExt1());
-		examStudent.setExt2(examStudentInfo.getExt2());
-		examStudent.setExt3(examStudentInfo.getExt3());
-		examStudent.setExt4(examStudentInfo.getExt4());
-		examStudent.setExt5(examStudentInfo.getExt5());
-
-		Date specialBeginTime = examStudentInfo.getSpecialBeginTime();
-		Date specialEndTime = examStudentInfo.getSpecialEndTime();
-
-		if (!new Boolean(null == specialBeginTime).equals(new Boolean(null == specialEndTime))) {
-			throw new StatusException("210101", "specialBeginTime & specialEndTime  wrong");
-		}
-
-		// 临时代码
-		if (null != specialBeginTime) {
-			examStudent.setExt4(DateUtil.format(specialBeginTime, DatePatterns.CHINA_DEFAULT));
-		} else {
-			examStudent.setExt4(null);
-		}
-		if (null != specialEndTime) {
-			examStudent.setExt5(DateUtil.format(specialEndTime, DatePatterns.CHINA_DEFAULT));
-		} else {
-			examStudent.setExt5(null);
-		}
-
-		Boolean enable = examStudentInfo.getEnable();
-
-		if (null != enable) {
-			examStudent.setEnable(enable);
-		}
-
-		ExamStudentEntity saved = examStudentRepo.save(examStudent);
-
-		ExamCourseRelationEntity relation = new ExamCourseRelationEntity();
-		relation.setExamId(saved.getExamId());
-		relation.setCourseId(saved.getCourseId());
-		relation.setCourseLevel(saved.getCourseLevel());
-		relation.setCourseCode(courseBean.getCode());
-		relation.setCourseName(saved.getCourseName());
-		relation.setCourseEnable(courseBean.getEnable());
-		examCourseRelationRepo.save(relation);
-
-		ExamPaperTypeRelationEntity pt = new ExamPaperTypeRelationEntity();
-		pt.setCourseId(saved.getCourseId());
-		pt.setExamId(saved.getExamId());
-		pt.setPaperType(saved.getPaperType());
-		examPaperTypeRelationRepo.save(pt);
-
-		// 同步操作
-		SyncExamStudentReq req = new SyncExamStudentReq();
-		req.setSyncType("update");
-		req.setId(saved.getId());
-
-		req.setEnable(saved.getEnable());
-
-		req.setCourseId(courseBean.getId());
-		req.setCourseCode(courseBean.getCode());
-		req.setCourseLevel(courseBean.getLevel());
-		req.setCourseName(courseBean.getName());
-
-		req.setExamId(exam.getId());
-		req.setExamName(exam.getName());
-
-		req.setRootOrgId(saved.getRootOrgId());
-		req.setStudentId(studentInfo.getId());
-		req.setStudentName(studentInfo.getName());
-		req.setIdentityNumber(saved.getIdentityNumber());
-		req.setStudentCode(saved.getStudentCode());
-		req.setOrgId(studentInfo.getOrgId());
-		req.setOrgName(studentInfo.getOrgName());
-		req.setOrgCode(studentInfo.getOrgCode());
-
-		req.setGrade(saved.getGrade());
-		req.setSpecialtyName(saved.getSpecialtyName());
-		req.setPaperType(saved.getPaperType());
-		req.setRemark(saved.getRemark());
-		req.setInfoCollector(saved.getInfoCollector());
-		req.setExamSite(saved.getExamSite());
-
-		req.setExt1(saved.getExt1());
-		req.setExt2(saved.getExt2());
-		req.setExt3(saved.getExt3());
-		req.setExt4(saved.getExt4());
-		req.setExt5(saved.getExt5());
-
-		dataSyncCloudService.syncExamStudent(req);
-
-		ExamStudentInfo ret = new ExamStudentInfo();
-		ret.setId(saved.getId());
-
-		ret.setCourseId(courseBean.getId());
-		ret.setCourseCode(courseBean.getCode());
-		ret.setCourseLevel(courseBean.getLevel());
-		ret.setCourseName(courseBean.getName());
-
-		ret.setExamId(exam.getId());
-		ret.setExamName(exam.getName());
-
-		ret.setRootOrgId(saved.getRootOrgId());
-		ret.setStudentName(studentInfo.getName());
-		ret.setIdentityNumber(saved.getIdentityNumber());
-		ret.setStudentCode(saved.getStudentCode());
-		ret.setOrgId(studentInfo.getOrgId());
-		ret.setOrgName(studentInfo.getOrgName());
-		ret.setOrgCode(studentInfo.getOrgCode());
-		ret.setStudentId(studentInfo.getId());
-
-		ret.setPaperType(saved.getPaperType());
-		ret.setRemark(saved.getRemark());
-		ret.setInfoCollector(saved.getInfoCollector());
-		ret.setExamSite(saved.getExamSite());
-
-		return ret;
-	}
-
-	@Override
-	public void syncExamStudent(Long studentId) {
-
-		ExamStudentEntity saved = GlobalHelper.getPresentEntity(examStudentRepo, studentId,
-				ExamStudentEntity.class);
-
-		GetCourseReq gcReq = new GetCourseReq();
-		gcReq.setRootOrgId(saved.getRootOrgId());
-		gcReq.setCode(saved.getCourseCode());
-		gcReq.setId(saved.getCourseId());
-		GetCourseResp gcResp = courseCloudService.getCourse(gcReq);
-		CourseBean courseBean = gcResp.getCourseBean();
-
-		GetOrgReq goReq = new GetOrgReq();
-		goReq.setRootOrgId(saved.getRootOrgId());
-		goReq.setOrgId(saved.getOrgId());
-		goReq.setOrgCode(saved.getOrgCode());
-		GetOrgResp goResp = orgCloudService.getOrg(goReq);
-		OrgBean orgBean = goResp.getOrg();
-
-		GetStudentReq getStudentReq = new GetStudentReq();
-		getStudentReq.setRootOrgId(saved.getRootOrgId());
-		getStudentReq.setIdentityNumber(saved.getIdentityNumber());
-		GetStudentResp getStudentResp = studentCloudService.getStudent(getStudentReq);
-		StudentBean studentInfo = getStudentResp.getStudentInfo();
-
-		ExamEntity exam = GlobalHelper.getEntity(examRepo, saved.getExamId(), ExamEntity.class);
-
-		// 同步操作
-		SyncExamStudentReq req = new SyncExamStudentReq();
-		req.setSyncType("update");
-		req.setId(saved.getId());
-
-		req.setEnable(saved.getEnable());
-
-		req.setCourseId(courseBean.getId());
-		req.setCourseCode(courseBean.getCode());
-		req.setCourseLevel(courseBean.getLevel());
-		req.setCourseName(courseBean.getName());
-
-		req.setExamId(exam.getId());
-		req.setExamName(exam.getName());
-
-		req.setRootOrgId(saved.getRootOrgId());
-		req.setStudentId(studentInfo.getId());
-		req.setStudentName(studentInfo.getName());
-		req.setIdentityNumber(studentInfo.getIdentityNumber());
-		req.setStudentCode(saved.getStudentCode());
-		req.setOrgId(saved.getOrgId());
-		req.setOrgName(orgBean.getName());
-		req.setOrgCode(saved.getOrgCode());
-
-		req.setGrade(saved.getGrade());
-		req.setSpecialtyName(saved.getSpecialtyName());
-		req.setPaperType(saved.getPaperType());
-		req.setRemark(saved.getRemark());
-		req.setInfoCollector(saved.getInfoCollector());
-		req.setExamSite(saved.getExamSite());
-
-		dataSyncCloudService.syncExamStudent(req);
-	}
+    @Autowired
+    ExamStudentRepo examStudentRepo;
+
+    @Autowired
+    ExamRepo examRepo;
+
+    @Autowired
+    OrgCloudService orgCloudService;
+
+    @Autowired
+    ExamStudentServiceImpl examStudentService;
+
+    @Autowired
+    StudentCloudService studentCloudService;
+
+    @Autowired
+    CourseCloudService courseCloudService;
+
+    @Autowired
+    ExamCourseRelationRepo examCourseRelationRepo;
+
+    @Autowired
+    DataSyncCloudService dataSyncCloudService;
+
+    @Autowired
+    ExamRecordCloudService examRecordCloudService;
+
+    @Autowired
+    ExamPaperTypeRelationRepo examPaperTypeRelationRepo;
+
+    /**
+     * 是否强制删除考生
+     *
+     * @return
+     * @author WANGWEI
+     */
+    private boolean forceDeleteExamStudent() {
+        boolean b = PropertyHolder.getBoolean("$deleteExamStudent.force", false);
+        return b;
+    }
+
+    /**
+     * 是否开考
+     *
+     * @param examId
+     * @param StudentId
+     * @param courseId
+     * @return
+     * @author WANGWEI
+     */
+    private boolean isStarted(Long examId, Long StudentId, Long courseId) {
+        CheckExamIsStartedReq checkExamIsStartedReq = new CheckExamIsStartedReq();
+        checkExamIsStartedReq.setExamId(examId);
+        checkExamIsStartedReq.setCourseId(courseId);
+        checkExamIsStartedReq.setStudentId(StudentId);
+        CheckExamIsStartedResp checkExamIsStartedResp = examRecordCloudService
+                .checkExamIsStarted(checkExamIsStartedReq);
+        return checkExamIsStartedResp.getIsStarted();
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param ids
+     * @author WANGWEI
+     */
+    @Override
+    public void deleteExamStudentsByStudentIds(List<Long> ids) {
+        List<ExamStudentEntity> examStudents = examStudentRepo.findByIdIn(ids);
+
+        for (ExamStudentEntity examStudent : examStudents) {
+            // 网考判断
+            if ((!forceDeleteExamStudent()) && isStarted(examStudent.getExamId(), null, null)) {
+                throw new StatusException("150112", examStudent.getName() + "已开始考试,不能删除");
+            }
+            examStudentRepo.delete(examStudent);
+
+            List<ExamStudentEntity> top2 = examStudentRepo.findTop2ByExamIdAndCourseId(
+                    examStudent.getExamId(), examStudent.getCourseId());
+            if (1 > top2.size()) {
+                examCourseRelationRepo.deleteByExamIdAndCourseId(examStudent.getExamId(),
+                        examStudent.getCourseId());
+            }
+
+            top2 = examStudentRepo.findTop2ByExamIdAndCourseIdAndPaperType(examStudent.getExamId(),
+                    examStudent.getCourseId(), examStudent.getPaperType());
+            if (1 > top2.size()) {
+                examPaperTypeRelationRepo.deleteByExamIdAndCourseIdAndPaperType(
+                        examStudent.getExamId(), examStudent.getCourseId(),
+                        examStudent.getPaperType());
+            }
+        }
+
+        for (ExamStudentEntity cur : examStudents) {
+            // 同步操作
+            SyncExamStudentReq req = new SyncExamStudentReq();
+            req.setSyncType("delete");
+            req.setId(cur.getId());
+            req.setCourseId(cur.getId());
+            req.setCourseCode(cur.getCourseCode());
+            req.setCourseName(cur.getCourseName());
+            req.setExamId(cur.getId());
+            req.setStudentId(cur.getStudentId());
+            req.setStudentName(cur.getName());
+            req.setIdentityNumber(cur.getIdentityNumber());
+            req.setStudentCode(cur.getStudentCode());
+            dataSyncCloudService.syncExamStudent(req);
+        }
+
+    }
+
+    /**
+     * 方法注释
+     *
+     * @param examId
+     * @author WANGWEI
+     */
+    @Override
+    public void deleteExamStudentsByExamId(Long examId) {
+        ExamEntity exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+        // 已经开考
+        if ((!forceDeleteExamStudent()) && isStarted(exam.getId(), null, null)) {
+            throw new StatusException("150113", "该考试已开始,不能删除");
+        }
+
+        ExamStudentEntity entity = new ExamStudentEntity();
+        entity.setExamId(examId);
+        Example<ExamStudentEntity> example = Example.of(entity);
+        long count = examStudentRepo.count(example);
+        if (1000 < count) {
+            throw new StatusException("150116", "考生数量超过1000,无法删除");
+        }
+
+        List<ExamStudentEntity> list = examStudentRepo.findAll(example);
+
+        List<Long> IdList = list.stream().map(ExamStudentEntity::getId)
+                .collect(Collectors.toList());
+
+        deleteExamStudentsByStudentIds(IdList);
+    }
+
+    /*
+     * 实现
+     *
+     * @author WANGWEI
+     *
+     * @see cn.com.qmth.examcloud.core.examwork.service.ExamStudentService#
+     * saveExamStudent(cn.com.qmth.examcloud.core.examwork.service.bean.
+     * ExamStudentInfo)
+     */
+    @Override
+    public ExamStudentInfo saveExamStudent(ExamStudentInfo examStudentInfo) {
+        String paperType = examStudentInfo.getPaperType();
+        String studentCode = examStudentInfo.getStudentCode();
+
+        if (StringUtils.isBlank(paperType)) {
+            paperType = "X";
+        } else if (!paperType.matches("[A-Z]")) {
+            throw new StatusException("100020", "试卷类型必须为一个大写的英文字母");
+        }
+
+        Long rootOrgId = examStudentInfo.getRootOrgId();
+        GetOrgReq getOrgReq = new GetOrgReq();
+        getOrgReq.setOrgId(rootOrgId);
+        GetOrgResp getOrgResp = orgCloudService.getOrg(getOrgReq);
+        OrgBean rootOrg = getOrgResp.getOrg();
+        if (null != rootOrg.getParentId()) {
+            throw new StatusException("100001", "rootOrgId is wrong");
+        }
+
+        ExamEntity exam = null;
+        Long examId = examStudentInfo.getExamId();
+        String examName = examStudentInfo.getExamName();
+        String examCode = examStudentInfo.getExamCode();
+
+        if (null != examId) {
+            exam = GlobalHelper.getEntity(examRepo, examId, ExamEntity.class);
+            if (null == exam) {
+                throw new StatusException("100020", "examId is wrong");
+            }
+        } else if (StringUtils.isNotBlank(examCode)) {
+            exam = examRepo.findByCodeAndRootOrgId(examCode, rootOrgId);
+            if (null == exam) {
+                throw new StatusException("1000050", "考试不存在");
+            }
+        } else if (StringUtils.isNotBlank(examName)) {
+            exam = examRepo.findByNameAndRootOrgId(examName, rootOrgId);
+            if (null == exam) {
+                throw new StatusException("100005", "考试不存在");
+            }
+        } else {
+            throw new StatusException("100002", "examId,examCode,examName cannot be all empty");
+        }
+
+        String identityNumber = examStudentInfo.getIdentityNumber();
+
+        if (StringUtils.isBlank(identityNumber)) {
+            throw new StatusException("100003", "identityNumber is null");
+        }
+        GetStudentReq getStudentReq = new GetStudentReq();
+        getStudentReq.setRootOrgId(rootOrgId);
+        getStudentReq.setIdentityNumber(identityNumber);
+        GetStudentResp getStudentResp = studentCloudService.getStudent(getStudentReq);
+
+        StudentBean studentInfo = getStudentResp.getStudentInfo();
+
+        if (StringUtils.isNotBlank(studentCode)) {
+            List<String> studentCodeList = studentInfo.getStudentCodeList();
+            boolean containIgnoreCase = false;
+            for (String cur : studentCodeList) {
+                if (cur.equalsIgnoreCase(studentCode)) {
+                    containIgnoreCase = true;
+                    studentCode = cur;
+                    break;
+                }
+            }
+            if (!containIgnoreCase) {
+                throw new StatusException("100063", "学号未绑定到身份证");
+            }
+        } else {
+            studentCode = null;
+        }
+
+        String courseCode = examStudentInfo.getCourseCode();
+        String courseName = examStudentInfo.getCourseName();
+        String courseLevel = examStudentInfo.getCourseLevel();
+
+        SaveCourseReq saveCourseReq = new SaveCourseReq();
+        saveCourseReq.setCourseId(examStudentInfo.getCourseId());
+        saveCourseReq.setCourseCode(courseCode);
+        saveCourseReq.setCourseName(courseName);
+        saveCourseReq.setCourseLevel(courseLevel);
+        saveCourseReq.setRootOrgId(rootOrgId);
+
+        SaveCourseResp saveCourseResp = courseCloudService.saveCourse(saveCourseReq);
+        CourseBean courseBean = saveCourseResp.getCourseBean();
+
+        ExamStudentEntity examStudent = examStudentRepo.findByExamIdAndStudentIdAndCourseId(
+                exam.getId(), studentInfo.getId(), courseBean.getId());
+
+        if (null == examStudent) {
+            examStudent = new ExamStudentEntity();
+            examStudent.setEnable(true);
+        } else {
+            if (!paperType.equals(examStudent.getPaperType())) {
+                List<ExamStudentEntity> top2 = examStudentRepo
+                        .findTop2ByExamIdAndCourseIdAndPaperType(examStudent.getExamId(),
+                                examStudent.getCourseId(), examStudent.getPaperType());
+                if (2 > top2.size()) {
+                    examPaperTypeRelationRepo.deleteByExamIdAndCourseIdAndPaperType(
+                            examStudent.getExamId(), examStudent.getCourseId(),
+                            examStudent.getPaperType());
+                }
+            }
+        }
+
+        examStudent.setInfoCollector(examStudentInfo.getInfoCollector());
+        examStudent.setName(studentInfo.getName());
+        examStudent.setRootOrgId(rootOrgId);
+
+        examStudent.setCourseId(courseBean.getId());
+        examStudent.setCourseName(courseBean.getName());
+        examStudent.setCourseCode(courseBean.getCode());
+        examStudent.setCourseLevel(courseBean.getLevel());
+
+        examStudent.setExamId(exam.getId());
+        examStudent.setIdentityNumber(studentInfo.getIdentityNumber());
+        examStudent.setStudentCode(studentCode);
+        examStudent.setPaperType(paperType);
+        examStudent.setStudentId(studentInfo.getId());
+        examStudent.setOrgId(studentInfo.getOrgId());
+        examStudent.setOrgCode(studentInfo.getOrgCode());
+        examStudent.setGrade(examStudentInfo.getGrade());
+        examStudent.setSpecialtyName(examStudentInfo.getSpecialtyName());
+        examStudent.setExamSite(examStudentInfo.getExamSite());
+        examStudent.setRemark(examStudentInfo.getRemark());
+
+        examStudent.setExt1(examStudentInfo.getExt1());
+        examStudent.setExt2(examStudentInfo.getExt2());
+        examStudent.setExt3(examStudentInfo.getExt3());
+        examStudent.setExt4(examStudentInfo.getExt4());
+        examStudent.setExt5(examStudentInfo.getExt5());
+
+        examStudent.setExamStageId(examStudentInfo.getExamStageId());
+        examStudent.setExamStageOrder(examStudentInfo.getExamStageOrder());
+
+        Date specialBeginTime = examStudentInfo.getSpecialBeginTime();
+        Date specialEndTime = examStudentInfo.getSpecialEndTime();
+
+        if (!new Boolean(null == specialBeginTime).equals(new Boolean(null == specialEndTime))) {
+            throw new StatusException("210101", "specialBeginTime & specialEndTime  wrong");
+        }
+
+        // 临时代码
+        if (null != specialBeginTime) {
+            examStudent.setExt4(DateUtil.format(specialBeginTime, DatePatterns.CHINA_DEFAULT));
+        } else {
+            examStudent.setExt4(null);
+        }
+        if (null != specialEndTime) {
+            examStudent.setExt5(DateUtil.format(specialEndTime, DatePatterns.CHINA_DEFAULT));
+        } else {
+            examStudent.setExt5(null);
+        }
+
+        Boolean enable = examStudentInfo.getEnable();
+
+        if (null != enable) {
+            examStudent.setEnable(enable);
+        }
+
+        ExamStudentEntity saved = examStudentRepo.save(examStudent);
+
+        ExamCourseRelationEntity relation = new ExamCourseRelationEntity();
+        relation.setExamId(saved.getExamId());
+        relation.setCourseId(saved.getCourseId());
+        relation.setCourseLevel(saved.getCourseLevel());
+        relation.setCourseCode(courseBean.getCode());
+        relation.setCourseName(saved.getCourseName());
+        relation.setCourseEnable(courseBean.getEnable());
+        examCourseRelationRepo.save(relation);
+
+        ExamPaperTypeRelationEntity pt = new ExamPaperTypeRelationEntity();
+        pt.setCourseId(saved.getCourseId());
+        pt.setExamId(saved.getExamId());
+        pt.setPaperType(saved.getPaperType());
+        examPaperTypeRelationRepo.save(pt);
+
+        // 同步操作
+        SyncExamStudentReq req = new SyncExamStudentReq();
+        req.setSyncType("update");
+        req.setId(saved.getId());
+
+        req.setEnable(saved.getEnable());
+
+        req.setCourseId(courseBean.getId());
+        req.setCourseCode(courseBean.getCode());
+        req.setCourseLevel(courseBean.getLevel());
+        req.setCourseName(courseBean.getName());
+
+        req.setExamId(exam.getId());
+        req.setExamName(exam.getName());
+
+        req.setRootOrgId(saved.getRootOrgId());
+        req.setStudentId(studentInfo.getId());
+        req.setStudentName(studentInfo.getName());
+        req.setIdentityNumber(saved.getIdentityNumber());
+        req.setStudentCode(saved.getStudentCode());
+        req.setOrgId(studentInfo.getOrgId());
+        req.setOrgName(studentInfo.getOrgName());
+        req.setOrgCode(studentInfo.getOrgCode());
+
+        req.setGrade(saved.getGrade());
+        req.setSpecialtyName(saved.getSpecialtyName());
+        req.setPaperType(saved.getPaperType());
+        req.setRemark(saved.getRemark());
+        req.setInfoCollector(saved.getInfoCollector());
+        req.setExamSite(saved.getExamSite());
+        req.setExamStageId(saved.getExamStageId());
+        req.setExamStageOrder(saved.getExamStageOrder());
+
+        req.setExt1(saved.getExt1());
+        req.setExt2(saved.getExt2());
+        req.setExt3(saved.getExt3());
+        req.setExt4(saved.getExt4());
+        req.setExt5(saved.getExt5());
+
+        dataSyncCloudService.syncExamStudent(req);
+
+        ExamStudentInfo ret = new ExamStudentInfo();
+        ret.setId(saved.getId());
+
+        ret.setCourseId(courseBean.getId());
+        ret.setCourseCode(courseBean.getCode());
+        ret.setCourseLevel(courseBean.getLevel());
+        ret.setCourseName(courseBean.getName());
+
+        ret.setExamId(exam.getId());
+        ret.setExamName(exam.getName());
+
+        ret.setRootOrgId(saved.getRootOrgId());
+        ret.setStudentName(studentInfo.getName());
+        ret.setIdentityNumber(saved.getIdentityNumber());
+        ret.setStudentCode(saved.getStudentCode());
+        ret.setOrgId(studentInfo.getOrgId());
+        ret.setOrgName(studentInfo.getOrgName());
+        ret.setOrgCode(studentInfo.getOrgCode());
+        ret.setStudentId(studentInfo.getId());
+
+        ret.setPaperType(saved.getPaperType());
+        ret.setRemark(saved.getRemark());
+        ret.setInfoCollector(saved.getInfoCollector());
+        ret.setExamSite(saved.getExamSite());
+
+        return ret;
+    }
+
+    @Override
+    public void syncExamStudent(Long studentId) {
+
+        ExamStudentEntity saved = GlobalHelper.getPresentEntity(examStudentRepo, studentId,
+                ExamStudentEntity.class);
+
+        GetCourseReq gcReq = new GetCourseReq();
+        gcReq.setRootOrgId(saved.getRootOrgId());
+        gcReq.setCode(saved.getCourseCode());
+        gcReq.setId(saved.getCourseId());
+        GetCourseResp gcResp = courseCloudService.getCourse(gcReq);
+        CourseBean courseBean = gcResp.getCourseBean();
+
+        GetOrgReq goReq = new GetOrgReq();
+        goReq.setRootOrgId(saved.getRootOrgId());
+        goReq.setOrgId(saved.getOrgId());
+        goReq.setOrgCode(saved.getOrgCode());
+        GetOrgResp goResp = orgCloudService.getOrg(goReq);
+        OrgBean orgBean = goResp.getOrg();
+
+        GetStudentReq getStudentReq = new GetStudentReq();
+        getStudentReq.setRootOrgId(saved.getRootOrgId());
+        getStudentReq.setIdentityNumber(saved.getIdentityNumber());
+        GetStudentResp getStudentResp = studentCloudService.getStudent(getStudentReq);
+        StudentBean studentInfo = getStudentResp.getStudentInfo();
+
+        ExamEntity exam = GlobalHelper.getEntity(examRepo, saved.getExamId(), ExamEntity.class);
+
+        // 同步操作
+        SyncExamStudentReq req = new SyncExamStudentReq();
+        req.setSyncType("update");
+        req.setId(saved.getId());
+
+        req.setEnable(saved.getEnable());
+
+        req.setCourseId(courseBean.getId());
+        req.setCourseCode(courseBean.getCode());
+        req.setCourseLevel(courseBean.getLevel());
+        req.setCourseName(courseBean.getName());
+
+        req.setExamId(exam.getId());
+        req.setExamName(exam.getName());
+
+        req.setRootOrgId(saved.getRootOrgId());
+        req.setStudentId(studentInfo.getId());
+        req.setStudentName(studentInfo.getName());
+        req.setIdentityNumber(studentInfo.getIdentityNumber());
+        req.setStudentCode(saved.getStudentCode());
+        req.setOrgId(saved.getOrgId());
+        req.setOrgName(orgBean.getName());
+        req.setOrgCode(saved.getOrgCode());
+
+        req.setGrade(saved.getGrade());
+        req.setSpecialtyName(saved.getSpecialtyName());
+        req.setPaperType(saved.getPaperType());
+        req.setRemark(saved.getRemark());
+        req.setInfoCollector(saved.getInfoCollector());
+        req.setExamSite(saved.getExamSite());
+
+        req.setExamStageId(saved.getExamStageId());
+        req.setExamStageOrder(saved.getExamStageOrder());
+
+        dataSyncCloudService.syncExamStudent(req);
+    }
+
+    /**
+     * 解绑考试下所有的场次信息
+     *
+     * @param examId
+     */
+    @Override
+    public void unbindExamStudentExamStage(Long examId) {
+        examStudentRepo.unbindExamStudentExamStage(examId);
+    }
 
 }

+ 17 - 9
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/NoticeServiceImpl.java

@@ -45,20 +45,28 @@ import java.util.stream.Collectors;
  */
 @Service("noticeService")
 public class NoticeServiceImpl implements NoticeService {
+
     @Autowired
     MarkWorkCloudService markWorkCloudService;
+
     @Autowired
     ExamStudentRepo examStudentRepo;
+
     @Autowired
     UserCloudService userCloudService;
+
     @Autowired
     private NoticeRepo noticeRepo;
+
     @Autowired
     private UserNoticeRepo userNoticeRepo;
+
     @Autowired
     private NoticeRulePublishProgressRepo noticeRulePublishProgressRepo;
+
     @Autowired
     private NoticeReceiverRuleRepo noticeReceiverRuleRepo;
+
     @Autowired
     private ExamRepo examRepo;
 
@@ -145,7 +153,7 @@ public class NoticeServiceImpl implements NoticeService {
             ni.setPublishTime(ne.getPublishTime());
             ni.setTitle(ne.getTitle());
             ni.setContent(ne.getContent());
-            ni.setPublishObject(getPublishObject(rootOrgId, ruleType,ne.getId(), ruleList));
+            ni.setPublishObject(getPublishObject(rootOrgId, ruleType, ne.getId(), ruleList));
             ni.setPublishStatus(ne.getNoticeStatus());
             ni.setRuleType(ruleType);
             resultList.add(ni);
@@ -260,7 +268,7 @@ public class NoticeServiceImpl implements NoticeService {
         List<Long> limitStudentIdList = getLimitUserIdResp.getIdList();
 
         //如果没有取到数据,则直接返回
-        if (null == limitStudentIdList || limitStudentIdList.size()==0){
+        if (null == limitStudentIdList || limitStudentIdList.size() == 0) {
             return getLimitUserIdResp.getNextId();
         }
 
@@ -330,7 +338,7 @@ public class NoticeServiceImpl implements NoticeService {
             Example<UserNoticeEntity> queryExample = Example.of(query);
 
             //如果用户未发过当前通知,则添加相当通知数据,否则跳过
-            if (!userNoticeRepo.exists(queryExample)){
+            if (!userNoticeRepo.exists(queryExample)) {
                 UserNoticeEntity userNotice = initUserNoticeEntity(rootOrgId, userId, noticeId,
                         userType);
                 userNoticeList.add(userNotice);
@@ -357,7 +365,7 @@ public class NoticeServiceImpl implements NoticeService {
                                                       Long startUserId, NoticeReceiverRuleType ruleType,
                                                       List<NoticeReceiverRuleEntity> ruleList) {
         GetLimitUserIdResp resultResp = new GetLimitUserIdResp();
-        
+
         switch (ruleType) {
             case TEACHER_OF_MARK_WORK:
                 return getGetLimitUserIdByMarkWork(rootOrgId, rowNumber, startUserId, ruleList);
@@ -367,10 +375,10 @@ public class NoticeServiceImpl implements NoticeService {
                 return getLimitUserIdByExamStudent(rootOrgId, startUserId, rowNumber, ruleList);
             case ALL_STUDENTS_OF_ROOT_ORG:
                 return getLimitUserIdByAllStudent(rootOrgId, rowNumber, startUserId);
-                default:
-                    resultResp.setNextId(0L);
-                    resultResp.setIdList(null);
-                    return resultResp;
+            default:
+                resultResp.setNextId(0L);
+                resultResp.setIdList(null);
+                return resultResp;
         }
     }
 
@@ -477,7 +485,7 @@ public class NoticeServiceImpl implements NoticeService {
      * @return
      */
     private List<Map<String, Object>> getPublishObject(Long rootOrgId,
-                                                       NoticeReceiverRuleType ruleType,Long noticeId, List<NoticeReceiverRuleEntity> ruleList) {
+                                                       NoticeReceiverRuleType ruleType, Long noticeId, List<NoticeReceiverRuleEntity> ruleList) {
         List<Map<String, Object>> resultList = new ArrayList<>();
         Map<String, Object> objectMap;
         List<NoticeReceiverRuleEntity> currentRuleList;

+ 182 - 183
examcloud-core-examwork-service/src/main/java/cn/com/qmth/examcloud/core/examwork/service/impl/OnGoingExamServiceImpl.java

@@ -1,11 +1,12 @@
 package cn.com.qmth.examcloud.core.examwork.service.impl;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import javax.persistence.criteria.Predicate;
-
+import cn.com.qmth.examcloud.api.commons.enums.ExamSpecialSettingsType;
+import cn.com.qmth.examcloud.api.commons.enums.ExamType;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.examwork.dao.ExamSpecialSettingsRepo;
+import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamSpecialSettingsEntity;
+import cn.com.qmth.examcloud.core.examwork.service.OnGoingExamService;
+import com.google.common.collect.Lists;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.PageRequest;
@@ -14,14 +15,10 @@ import org.springframework.data.domain.Sort.Direction;
 import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 
-import com.google.common.collect.Lists;
-
-import cn.com.qmth.examcloud.api.commons.enums.ExamSpecialSettingsType;
-import cn.com.qmth.examcloud.api.commons.enums.ExamType;
-import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.core.examwork.dao.ExamSpecialSettingsRepo;
-import cn.com.qmth.examcloud.core.examwork.dao.entity.ExamSpecialSettingsEntity;
-import cn.com.qmth.examcloud.core.examwork.service.OnGoingExamService;
+import javax.persistence.criteria.Predicate;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
 
 /**
  * 待考考试服务
@@ -33,174 +30,176 @@ import cn.com.qmth.examcloud.core.examwork.service.OnGoingExamService;
 @Service
 public class OnGoingExamServiceImpl implements OnGoingExamService {
 
-	@Autowired
-	ExamSpecialSettingsRepo examSpecialSettingsRepo;
-
-	@Override
-	public List<ExamSpecialSettingsEntity> getOngoingExamList(Long rootOrgId, String examType,
-			Long orgId, Long studentId) {
-
-		if (null == rootOrgId) {
-			throw new StatusException("008001", "rootOrgId is wrong");
-		}
-
-		ExamType et = null;
-		if (StringUtils.isNotBlank(examType)) {
-			try {
-				et = ExamType.valueOf(examType);
-			} catch (Exception e) {
-				throw new StatusException("008002", "examType is wrong");
-			}
-		}
-
-		if (null == orgId) {
-			throw new StatusException("008003", "orgId is wrong");
-		}
-
-		if (null == studentId) {
-			throw new StatusException("008004", "studentId is wrong");
-		}
-
-		List<ExamSpecialSettingsEntity> byExam = getByExam(rootOrgId, et);
-		List<ExamSpecialSettingsEntity> byOrg = getByOrg(rootOrgId, et, orgId);
-		List<ExamSpecialSettingsEntity> byStudentId = getByStudentId(rootOrgId, et, studentId);
-
-		List<ExamSpecialSettingsEntity> examList = Lists.newArrayListWithCapacity(0);
-		examList.addAll(byOrg);
-		examList.addAll(byStudentId);
-
-		for (ExamSpecialSettingsEntity cur : byExam) {
-			if (!cur.getSpecialSettingsEnabled()) {
-				examList.add(cur);
-			}
-			ExamSpecialSettingsType specialSettingsType = cur.getSpecialSettingsType();
-			if (null == specialSettingsType) {
-				examList.add(cur);
-			} else if (specialSettingsType.equals(ExamSpecialSettingsType.ORG_BASED)) {
-				ExamSpecialSettingsEntity specialSettings = examSpecialSettingsRepo
-						.findByExamIdAndOrgIdAndCourseIdIsNullAndStudentIdIsNull(cur.getExamId(),
-								orgId);
-				if (null == specialSettings) {
-					examList.add(cur);
-				} else if (null == specialSettings.getBeginTime()
-						&& null == specialSettings.getEndTime()) {
-					examList.add(cur);
-				}
-			} else if (specialSettingsType.equals(ExamSpecialSettingsType.STUDENT_BASED)) {
-				ExamSpecialSettingsEntity specialSettings = examSpecialSettingsRepo
-						.findByExamIdAndStudentIdAndOrgIdIsNullAndCourseIdIsNull(cur.getExamId(),
-								studentId);
-				if (null == specialSettings) {
-					examList.add(cur);
-				} else if (null == specialSettings.getBeginTime()
-						&& null == specialSettings.getEndTime()) {
-					examList.add(cur);
-				}
-			}
-		}
-
-		examList.sort((a, b) -> a.getBeginTime().after(b.getBeginTime()) ? 1 : -1);
-
-		return examList;
-	}
-
-	/**
-	 * 考试设置
-	 *
-	 * @author WANGWEI
-	 * @param rootOrgId
-	 * @param type
-	 * @return
-	 */
-	private List<ExamSpecialSettingsEntity> getByExam(Long rootOrgId, ExamType type) {
-		Specification<ExamSpecialSettingsEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-			predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
-			predicates.add(cb.greaterThan(root.get("endTime"), new Date()));
-			predicates.add(cb.equal(root.get("examType"), type));
-			predicates.add(cb.equal(root.get("examEnable"), true));
-
-			predicates.add(cb.isNull(root.get("courseId")));
-			predicates.add(cb.isNull(root.get("orgId")));
-			predicates.add(cb.isNull(root.get("studentId")));
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		PageRequest pageRequest = PageRequest.of(0, 100, new Sort(Direction.ASC, "beginTime"));
-
-		List<ExamSpecialSettingsEntity> entityList = examSpecialSettingsRepo
-				.findAll(specification, pageRequest).getContent();
-		return entityList;
-	}
-
-	/**
-	 * 机构特殊设置
-	 *
-	 * @author WANGWEI
-	 * @param rootOrgId
-	 * @param type
-	 * @param orgId
-	 * @return
-	 */
-	private List<ExamSpecialSettingsEntity> getByOrg(Long rootOrgId, ExamType type, Long orgId) {
-		Specification<ExamSpecialSettingsEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-			predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
-			predicates.add(cb.greaterThan(root.get("endTime"), new Date()));
-			predicates.add(cb.equal(root.get("examType"), type));
-			predicates.add(cb.equal(root.get("examEnable"), true));
-
-			predicates.add(cb.isNull(root.get("courseId")));
-			predicates.add(cb.isNull(root.get("studentId")));
-			predicates.add(cb.equal(root.get("orgId"), orgId));
-			predicates.add(cb.equal(root.get("specialSettingsEnabled"), true));
-			predicates.add(
-					cb.equal(root.get("specialSettingsType"), ExamSpecialSettingsType.ORG_BASED));
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		PageRequest pageRequest = PageRequest.of(0, 100, new Sort(Direction.ASC, "beginTime"));
-
-		List<ExamSpecialSettingsEntity> entityList = examSpecialSettingsRepo
-				.findAll(specification, pageRequest).getContent();
-		return entityList;
-	}
-
-	/**
-	 * 学生特殊设置
-	 *
-	 * @author WANGWEI
-	 * @param rootOrgId
-	 * @param type
-	 * @param studentId
-	 * @return
-	 */
-	private List<ExamSpecialSettingsEntity> getByStudentId(Long rootOrgId, ExamType type,
-			Long studentId) {
-		Specification<ExamSpecialSettingsEntity> specification = (root, query, cb) -> {
-			List<Predicate> predicates = new ArrayList<>();
-			predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
-			predicates.add(cb.greaterThan(root.get("endTime"), new Date()));
-			predicates.add(cb.equal(root.get("examType"), type));
-			predicates.add(cb.equal(root.get("examEnable"), true));
-
-			predicates.add(cb.isNull(root.get("courseId")));
-			predicates.add(cb.isNull(root.get("orgId")));
-			predicates.add(cb.equal(root.get("studentId"), studentId));
-			predicates.add(cb.equal(root.get("specialSettingsEnabled"), true));
-			predicates.add(cb.equal(root.get("specialSettingsType"),
-					ExamSpecialSettingsType.STUDENT_BASED));
-
-			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
-		};
-
-		PageRequest pageRequest = PageRequest.of(0, 100, new Sort(Direction.ASC, "beginTime"));
-
-		List<ExamSpecialSettingsEntity> entityList = examSpecialSettingsRepo
-				.findAll(specification, pageRequest).getContent();
-		return entityList;
-	}
+    @Autowired
+    ExamSpecialSettingsRepo examSpecialSettingsRepo;
+
+    @Override
+    public List<ExamSpecialSettingsEntity> getOngoingExamList(Long rootOrgId, String examType,
+                                                              Long orgId, Long studentId) {
+
+        if (null == rootOrgId) {
+            throw new StatusException("008001", "rootOrgId is wrong");
+        }
+
+        ExamType et = null;
+        if (StringUtils.isNotBlank(examType)) {
+            try {
+                et = ExamType.valueOf(examType);
+            } catch (Exception e) {
+                throw new StatusException("008002", "examType is wrong");
+            }
+        }
+
+        if (null == orgId) {
+            throw new StatusException("008003", "orgId is wrong");
+        }
+
+        if (null == studentId) {
+            throw new StatusException("008004", "studentId is wrong");
+        }
+
+        List<ExamSpecialSettingsEntity> byExam = getByExam(rootOrgId, et);
+        List<ExamSpecialSettingsEntity> byOrg = getByOrg(rootOrgId, et, orgId);
+        List<ExamSpecialSettingsEntity> byStudentId = getByStudentId(rootOrgId, et, studentId);
+
+        List<ExamSpecialSettingsEntity> examList = Lists.newArrayListWithCapacity(0);
+        examList.addAll(byOrg);
+        examList.addAll(byStudentId);
+
+        for (ExamSpecialSettingsEntity cur : byExam) {
+            if (!cur.getSpecialSettingsEnabled()) {
+                examList.add(cur);
+            }
+            ExamSpecialSettingsType specialSettingsType = cur.getSpecialSettingsType();
+            if (null == specialSettingsType) {
+                examList.add(cur);
+            } else if (specialSettingsType.equals(ExamSpecialSettingsType.ORG_BASED)) {
+                ExamSpecialSettingsEntity specialSettings = examSpecialSettingsRepo
+                        .findByExamIdAndOrgIdAndCourseIdIsNullAndStudentIdIsNull(cur.getExamId(),
+                                orgId);
+                if (null == specialSettings) {
+                    examList.add(cur);
+                } else if (null == specialSettings.getBeginTime()
+                        && null == specialSettings.getEndTime()) {
+                    examList.add(cur);
+                }
+            } else if (specialSettingsType.equals(ExamSpecialSettingsType.STUDENT_BASED)) {
+                ExamSpecialSettingsEntity specialSettings = examSpecialSettingsRepo
+                        .findByExamIdAndStudentIdAndOrgIdIsNullAndCourseIdIsNull(cur.getExamId(),
+                                studentId);
+                if (null == specialSettings) {
+                    examList.add(cur);
+                } else if (null == specialSettings.getBeginTime()
+                        && null == specialSettings.getEndTime()) {
+                    examList.add(cur);
+                }
+            } else if (specialSettingsType.equals(ExamSpecialSettingsType.STAGE_BASED)) {
+                examList.add(cur);//按场次特殊设置,直接返回
+            }
+        }
+
+        examList.sort((a, b) -> a.getBeginTime().after(b.getBeginTime()) ? 1 : -1);
+
+        return examList;
+    }
+
+    /**
+     * 考试设置
+     *
+     * @param rootOrgId
+     * @param type
+     * @return
+     * @author WANGWEI
+     */
+    private List<ExamSpecialSettingsEntity> getByExam(Long rootOrgId, ExamType type) {
+        Specification<ExamSpecialSettingsEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+            predicates.add(cb.greaterThan(root.get("endTime"), new Date()));
+            predicates.add(cb.equal(root.get("examType"), type));
+            predicates.add(cb.equal(root.get("examEnable"), true));
+
+            predicates.add(cb.isNull(root.get("courseId")));
+            predicates.add(cb.isNull(root.get("orgId")));
+            predicates.add(cb.isNull(root.get("studentId")));
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        PageRequest pageRequest = PageRequest.of(0, 100, new Sort(Direction.ASC, "beginTime"));
+
+        List<ExamSpecialSettingsEntity> entityList = examSpecialSettingsRepo
+                .findAll(specification, pageRequest).getContent();
+        return entityList;
+    }
+
+    /**
+     * 机构特殊设置
+     *
+     * @param rootOrgId
+     * @param type
+     * @param orgId
+     * @return
+     * @author WANGWEI
+     */
+    private List<ExamSpecialSettingsEntity> getByOrg(Long rootOrgId, ExamType type, Long orgId) {
+        Specification<ExamSpecialSettingsEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+            predicates.add(cb.greaterThan(root.get("endTime"), new Date()));
+            predicates.add(cb.equal(root.get("examType"), type));
+            predicates.add(cb.equal(root.get("examEnable"), true));
+
+            predicates.add(cb.isNull(root.get("courseId")));
+            predicates.add(cb.isNull(root.get("studentId")));
+            predicates.add(cb.equal(root.get("orgId"), orgId));
+            predicates.add(cb.equal(root.get("specialSettingsEnabled"), true));
+            predicates.add(
+                    cb.equal(root.get("specialSettingsType"), ExamSpecialSettingsType.ORG_BASED));
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        PageRequest pageRequest = PageRequest.of(0, 100, new Sort(Direction.ASC, "beginTime"));
+
+        List<ExamSpecialSettingsEntity> entityList = examSpecialSettingsRepo
+                .findAll(specification, pageRequest).getContent();
+        return entityList;
+    }
+
+    /**
+     * 学生特殊设置
+     *
+     * @param rootOrgId
+     * @param type
+     * @param studentId
+     * @return
+     * @author WANGWEI
+     */
+    private List<ExamSpecialSettingsEntity> getByStudentId(Long rootOrgId, ExamType type,
+                                                           Long studentId) {
+        Specification<ExamSpecialSettingsEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+            predicates.add(cb.greaterThan(root.get("endTime"), new Date()));
+            predicates.add(cb.equal(root.get("examType"), type));
+            predicates.add(cb.equal(root.get("examEnable"), true));
+
+            predicates.add(cb.isNull(root.get("courseId")));
+            predicates.add(cb.isNull(root.get("orgId")));
+            predicates.add(cb.equal(root.get("studentId"), studentId));
+            predicates.add(cb.equal(root.get("specialSettingsEnabled"), true));
+            predicates.add(cb.equal(root.get("specialSettingsType"),
+                    ExamSpecialSettingsType.STUDENT_BASED));
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        PageRequest pageRequest = PageRequest.of(0, 100, new Sort(Direction.ASC, "beginTime"));
+
+        List<ExamSpecialSettingsEntity> entityList = examSpecialSettingsRepo
+                .findAll(specification, pageRequest).getContent();
+        return entityList;
+    }
 
 }

+ 0 - 16
examcloud-core-examwork-starter/.springBeans

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beansProjectDescription>
-	<version>1</version>
-	<pluginVersion><![CDATA[3.9.4.201804120850-RELEASE]]></pluginVersion>
-	<configSuffixes>
-		<configSuffix><![CDATA[xml]]></configSuffix>
-	</configSuffixes>
-	<enableImports><![CDATA[false]]></enableImports>
-	<configs>
-		<config>java:cn.com.qmth.examcloud.core.examwork.starter.CoreExamWorkApplication</config>
-	</configs>
-	<autoconfigs>
-	</autoconfigs>
-	<configSets>
-	</configSets>
-</beansProjectDescription>

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

@@ -1,7 +1,7 @@
 spring.profiles.active=dev
 
 examcloud.startup.startupCode=8001
-examcloud.startup.configCenterHost=127.0.0.1
+examcloud.startup.configCenterHost=192.168.10.39
 examcloud.startup.configCenterPort=9999
 examcloud.startup.appCode=E
 

+ 77 - 58
examcloud-core-examwork-starter/src/main/resources/log4j2.xml

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

BIN
examcloud-core-examwork-starter/src/main/resources/templates/studentImportTemplate.xlsx


+ 0 - 19
jenkins-dev.sh

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

+ 0 - 19
jenkins-test.sh

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