WANG 5 年 前
コミット
cf290ab5bf
92 ファイル変更12837 行追加0 行削除
  1. 61 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamAuditBeanConvert.java
  2. 20 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamCaptureBeanConvert.java
  3. 62 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamRecordDataBeanConvert.java
  4. 362 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamRecordDataDomain.java
  5. 20 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamScoreBeanConvert.java
  6. 117 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamStudentBeanConvert.java
  7. 55 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/BaseInfoController.java
  8. 161 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamAuditController.java
  9. 50 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamCaptureController.java
  10. 137 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamRecordController.java
  11. 54 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamRecordDataController.java
  12. 38 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamRecordPaperStructController.java
  13. 39 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamRecordQuestionsController.java
  14. 64 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamScoreController.java
  15. 214 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamStudentController.java
  16. 159 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamRecordCloudServiceProvider.java
  17. 182 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamRecordForMarkingCloudServiceProvider.java
  18. 89 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamRecordQuestionsCloudServiceProvider.java
  19. 65 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamScoreCloudServiceProvider.java
  20. 335 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamScoreDataCloudServiceProvider.java
  21. 64 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamScoreObtainQueueCloudServiceProvider.java
  22. 160 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamStudentCloudServiceProvider.java
  23. 176 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamStudentDataCloudServiceProvider.java
  24. 39 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/OeAdminScorePushCloudServiceProvider.java
  25. 147 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/SyncCloudServiceProvider.java
  26. 72 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamAuditService.java
  27. 100 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamCacheTransferHelper.java
  28. 39 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamCaptureService.java
  29. 33 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordForMarkingService.java
  30. 14 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordPaperStructService.java
  31. 23 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordQuestionsService.java
  32. 104 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordService.java
  33. 23 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamScorePushService.java
  34. 29 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamScoreQueueService.java
  35. 29 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamScoreService.java
  36. 44 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamService.java
  37. 120 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamStudentService.java
  38. 208 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/ExamRecordQuestionsInfo.java
  39. 117 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/ExamStudentEffectiveScoreInfo.java
  40. 64 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/SubjectiveQuestionScoreInfo.java
  41. 95 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditEntityConvert.java
  42. 154 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditExcel.java
  43. 465 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditInfo.java
  44. 47 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditMapper.java
  45. 181 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditQuery.java
  46. 71 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/RedoAuditInfo.java
  47. 289 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examcapture/ExamCaptureAuditInfo.java
  48. 124 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examcapture/ExamCaptureInfo.java
  49. 20 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examcapture/ExamCaptureQuery.java
  50. 164 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordEntityConvert.java
  51. 707 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordInfo.java
  52. 58 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordMapper.java
  53. 276 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordQuery.java
  54. 126 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamStudentQuestionScoreInfo.java
  55. 41 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreEntityConvert.java
  56. 30 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreExcel.java
  57. 351 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreInfo.java
  58. 18 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreMapper.java
  59. 148 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreQuery.java
  60. 100 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/CourseProgressInfo.java
  61. 223 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentEntityConvert.java
  62. 123 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentExcel.java
  63. 44 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentFinishedStatistic.java
  64. 398 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentInfo.java
  65. 78 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentMapper.java
  66. 92 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentOrgStatistic.java
  67. 68 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentPartInfo.java
  68. 172 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentQuery.java
  69. 151 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentUnFinishedExcel.java
  70. 321 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamAuditServiceImpl.java
  71. 237 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamCaptureServiceImpl.java
  72. 217 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordForMarkingServiceImpl.java
  73. 186 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordPaperStructServiceImpl.java
  74. 78 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordQuestionsServiceImpl.java
  75. 743 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordServiceImpl.java
  76. 63 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamScoreQueueServiceImpl.java
  77. 440 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamScoreServiceImpl.java
  78. 61 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamServiceImpl.java
  79. 654 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamStudentServiceImpl.java
  80. 189 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/pushscore/bean/ExamScorePushInfo.java
  81. 182 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/pushscore/cugr/CugrExamScorePushServiceImpl.java
  82. 202 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/pushscore/cup/CupExamScorePushServiceImpl.java
  83. 202 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/pushscore/swufe/SwufeExamScorePushServiceImpl.java
  84. 41 0
      examcloud-core-oe-admin-starter/src/main/java/cn/com/qmth/examcloud/core/oe/admin/stater/AdminApplication.java
  85. 130 0
      examcloud-core-oe-admin-starter/src/main/java/cn/com/qmth/examcloud/core/oe/admin/stater/config/ExamCloudResourceManager.java
  86. 53 0
      examcloud-core-oe-admin-starter/src/main/java/cn/com/qmth/examcloud/core/oe/admin/stater/config/ExamCloudWebMvcConfigurer.java
  87. 44 0
      examcloud-core-oe-admin-starter/src/main/java/cn/com/qmth/examcloud/core/oe/admin/stater/config/SwaggerConfig.java
  88. 6 0
      examcloud-core-oe-admin-starter/src/main/resources/application.properties
  89. 0 0
      examcloud-core-oe-admin-starter/src/main/resources/limited.properties
  90. 71 0
      examcloud-core-oe-admin-starter/src/main/resources/log4j2.xml
  91. 14 0
      examcloud-core-oe-admin-starter/src/main/resources/security-exclusions.conf
  92. 0 0
      examcloud-core-oe-admin-starter/src/main/resources/security.properties

+ 61 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamAuditBeanConvert.java

@@ -0,0 +1,61 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-29 10:28:31.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.api.bean;
+
+import cn.com.qmth.examcloud.core.oe.common.enums.DisciplineType;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 类信息转换
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+public class ExamAuditBeanConvert implements JsonSerializable {
+	
+	private Long examRecordDataId;
+	
+	private String disciplineDetail;
+	
+	private Boolean isPass;
+	
+	private DisciplineType disciplineType;
+
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public String getDisciplineDetail() {
+		return disciplineDetail;
+	}
+
+	public void setDisciplineDetail(String disciplineDetail) {
+		this.disciplineDetail = disciplineDetail;
+	}
+
+	public Boolean getIsPass() {
+		return isPass;
+	}
+
+	public void setIsPass(Boolean isPass) {
+		this.isPass = isPass;
+	}
+
+	public DisciplineType getDisciplineType() {
+		return disciplineType;
+	}
+
+	public void setDisciplineType(DisciplineType disciplineType) {
+		this.disciplineType = disciplineType;
+	}
+	
+}

+ 20 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamCaptureBeanConvert.java

@@ -0,0 +1,20 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 17:26:26.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.api.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 类信息转换
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+public class ExamCaptureBeanConvert implements JsonSerializable {
+
+}

+ 62 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamRecordDataBeanConvert.java

@@ -0,0 +1,62 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-29 10:49:29.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.api.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+
+/**
+ * 类信息转换
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+public class ExamRecordDataBeanConvert implements JsonSerializable {
+    /**
+     * 将数据库实体转换为业务实体
+     * @param erdEntity
+     * @return
+     */
+    public static ExamRecordDataDomain copyFrom(ExamRecordDataEntity erdEntity){
+        if (erdEntity==null){
+            return null;
+        }
+
+        ExamRecordDataDomain result = new ExamRecordDataDomain();
+        result.setAllObjectivePaper(erdEntity.getIsAllObjectivePaper());
+        result.setAudit(erdEntity.getIsAudit());
+        result.setBaiduFaceLivenessSuccessPercent(erdEntity.getBaiduFaceLivenessSuccessPercent());
+        result.setCleanTime(erdEntity.getCleanTime());
+        result.setContinued(erdEntity.getIsContinued());
+        result.setContinuedCount(erdEntity.getContinuedCount());
+        result.setCreationTime(erdEntity.getCreationTime());
+        result.setEndTime(erdEntity.getEndTime());
+        result.setExamOrder(erdEntity.getExamOrder());
+        result.setExamRecordId(erdEntity.getExamRecordId());
+        result.setExamOrder(erdEntity.getExamOrder());
+        result.setExamRecordQuestionsId(erdEntity.getExamRecordQuestionsId());
+        result.setExamRecordStatus(erdEntity.getExamRecordStatus());
+        result.setExceed(erdEntity.getIsExceed());
+        result.setFaceFailedCount(erdEntity.getFaceFailedCount());
+        result.setFaceLandmarkVal(erdEntity.getFaceLandmarkVal());
+        result.setFaceStrangerCount(erdEntity.getFaceStrangerCount());
+        result.setFaceSuccessCount(erdEntity.getFaceSuccessCount());
+        result.setFaceSuccessPercent(erdEntity.getFaceSuccessPercent());
+        result.setFaceTotalCount(erdEntity.getFaceTotalCount());
+        result.setFaceVerifyResult(erdEntity.getFaceVerifyResult());
+        result.setId(erdEntity.getId());
+        result.setIllegality(erdEntity.getIsIllegality());
+        result.setReexamine(erdEntity.getIsReexamine());
+        result.setStartTime(erdEntity.getStartTime());
+        result.setUpdateTime(erdEntity.getUpdateTime());
+        result.setUsedExamTime(erdEntity.getUsedExamTime());
+        result.setWarn(erdEntity.getIsWarn());
+        return result;
+    }
+}

+ 362 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamRecordDataDomain.java

@@ -0,0 +1,362 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamRecordStatus;
+import cn.com.qmth.examcloud.core.oe.common.enums.IsSuccess;
+
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import java.util.Date;
+
+/**
+ * @Description 考试记录Data实体
+ * @Author lideyin
+ * @Date 2019/8/20 18:40
+ * @Version 1.0
+ */
+public class ExamRecordDataDomain implements JsonSerializable {
+    private static final long serialVersionUID = 5819718857965384297L;
+    private Long id;
+
+    /**
+     * 考试作答记录id
+     */
+    private String examRecordQuestionsId;
+    /**
+     * 考试记录id
+     */
+    private Long examRecordId;
+    /**
+     * 考试记录状态(考试中,考试结束,考试过期,考试作废)
+     */
+    @Enumerated(EnumType.STRING)
+    private ExamRecordStatus examRecordStatus;
+    /**
+     * 考试开始时间
+     */
+    private Date startTime;
+    /**
+     * 考试结束时间
+     */
+    private Date endTime;
+    /**
+     * 考试被清理时间
+     */
+    private Date cleanTime;
+    /**
+     * 是否异常数据
+     */
+    private Boolean isWarn;
+    /**
+     * 是否被审核过
+     */
+    private Boolean isAudit;
+    /**
+     * 是否违纪
+     */
+    private Boolean isIllegality;
+
+    /**
+     * 考试时长
+     */
+    private Long usedExamTime;
+    /**
+     * 第几次考试
+     */
+    private Integer examOrder;
+    /**
+     * 是否为重考
+     */
+    private Boolean isReexamine;
+    /**
+     * 是否断点续考
+     */
+    private Boolean isContinued;
+    /**
+     * 是否是全客观题卷  1:是   0:否
+     */
+    private Boolean isAllObjectivePaper;
+    /**
+     * 断点续考次数
+     */
+    private Integer continuedCount;
+    /**
+     * 是否达到最大断点限制
+     */
+    private Boolean isExceed;
+    /**
+     * 抓拍比对成功次数
+     */
+    private Integer faceSuccessCount;
+    /**
+     * 抓拍比对失败次数
+     */
+    private Integer faceFailedCount;
+    /**
+     * 抓拍存在陌生人的次数
+     */
+    private Integer faceStrangerCount;
+    /**
+     * 抓拍比对总次数
+     */
+    private Integer faceTotalCount;
+    /**
+     * 抓拍比对成功比率
+     */
+    private Double faceSuccessPercent;
+    /**
+     * @see cn.com.qmth.examcloud.core.oe.common.enums.IsSuccess
+     * 活体检测结果
+     */
+    @Enumerated(EnumType.STRING)
+    private IsSuccess faceVerifyResult;
+
+    /**
+     * 百度人脸活体检测通过率
+     */
+    private Double baiduFaceLivenessSuccessPercent;
+    /**
+     * 人脸五官坐标比对值
+     */
+    private Double faceLandmarkVal;
+
+    /**
+     * 考试记录对象//TODO 应该再加一个实体转换一下比较好,由于时间问题,暂时先这么处理
+     */
+    private ExamRecordEntity examRecord;
+
+    /**
+     * 修改时间
+     */
+    private Date updateTime;
+
+    /**
+     * 创建时间
+     */
+    private Date creationTime;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getExamRecordQuestionsId() {
+        return examRecordQuestionsId;
+    }
+
+    public void setExamRecordQuestionsId(String examRecordQuestionsId) {
+        this.examRecordQuestionsId = examRecordQuestionsId;
+    }
+
+    public Long getExamRecordId() {
+        return examRecordId;
+    }
+
+    public void setExamRecordId(Long examRecordId) {
+        this.examRecordId = examRecordId;
+    }
+
+    public ExamRecordStatus getExamRecordStatus() {
+        return examRecordStatus;
+    }
+
+    public void setExamRecordStatus(ExamRecordStatus examRecordStatus) {
+        this.examRecordStatus = examRecordStatus;
+    }
+
+    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 Date getCleanTime() {
+        return cleanTime;
+    }
+
+    public void setCleanTime(Date cleanTime) {
+        this.cleanTime = cleanTime;
+    }
+
+    public Boolean getWarn() {
+        return isWarn;
+    }
+
+    public void setWarn(Boolean warn) {
+        isWarn = warn;
+    }
+
+    public Boolean getAudit() {
+        return isAudit;
+    }
+
+    public void setAudit(Boolean audit) {
+        isAudit = audit;
+    }
+
+    public Boolean getIllegality() {
+        return isIllegality;
+    }
+
+    public void setIllegality(Boolean illegality) {
+        isIllegality = illegality;
+    }
+
+    public Long getUsedExamTime() {
+        return usedExamTime;
+    }
+
+    public void setUsedExamTime(Long usedExamTime) {
+        this.usedExamTime = usedExamTime;
+    }
+
+    public Integer getExamOrder() {
+        return examOrder;
+    }
+
+    public void setExamOrder(Integer examOrder) {
+        this.examOrder = examOrder;
+    }
+
+    public Boolean getReexamine() {
+        return isReexamine;
+    }
+
+    public void setReexamine(Boolean reexamine) {
+        isReexamine = reexamine;
+    }
+
+    public Boolean getContinued() {
+        return isContinued;
+    }
+
+    public void setContinued(Boolean continued) {
+        isContinued = continued;
+    }
+
+    public Boolean getAllObjectivePaper() {
+        return isAllObjectivePaper;
+    }
+
+    public void setAllObjectivePaper(Boolean allObjectivePaper) {
+        isAllObjectivePaper = allObjectivePaper;
+    }
+
+    public Integer getContinuedCount() {
+        return continuedCount;
+    }
+
+    public void setContinuedCount(Integer continuedCount) {
+        this.continuedCount = continuedCount;
+    }
+
+    public Boolean getExceed() {
+        return isExceed;
+    }
+
+    public void setExceed(Boolean exceed) {
+        isExceed = exceed;
+    }
+
+    public Integer getFaceSuccessCount() {
+        return faceSuccessCount;
+    }
+
+    public void setFaceSuccessCount(Integer faceSuccessCount) {
+        this.faceSuccessCount = faceSuccessCount;
+    }
+
+    public Integer getFaceFailedCount() {
+        return faceFailedCount;
+    }
+
+    public void setFaceFailedCount(Integer faceFailedCount) {
+        this.faceFailedCount = faceFailedCount;
+    }
+
+    public Integer getFaceStrangerCount() {
+        return faceStrangerCount;
+    }
+
+    public void setFaceStrangerCount(Integer faceStrangerCount) {
+        this.faceStrangerCount = faceStrangerCount;
+    }
+
+    public Integer getFaceTotalCount() {
+        return faceTotalCount;
+    }
+
+    public void setFaceTotalCount(Integer faceTotalCount) {
+        this.faceTotalCount = faceTotalCount;
+    }
+
+    public Double getFaceSuccessPercent() {
+        return faceSuccessPercent;
+    }
+
+    public void setFaceSuccessPercent(Double faceSuccessPercent) {
+        this.faceSuccessPercent = faceSuccessPercent;
+    }
+
+    public IsSuccess getFaceVerifyResult() {
+        return faceVerifyResult;
+    }
+
+    public void setFaceVerifyResult(IsSuccess faceVerifyResult) {
+        this.faceVerifyResult = faceVerifyResult;
+    }
+
+    public Double getBaiduFaceLivenessSuccessPercent() {
+        return baiduFaceLivenessSuccessPercent;
+    }
+
+    public void setBaiduFaceLivenessSuccessPercent(Double baiduFaceLivenessSuccessPercent) {
+        this.baiduFaceLivenessSuccessPercent = baiduFaceLivenessSuccessPercent;
+    }
+
+    public Double getFaceLandmarkVal() {
+        return faceLandmarkVal;
+    }
+
+    public void setFaceLandmarkVal(Double faceLandmarkVal) {
+        this.faceLandmarkVal = faceLandmarkVal;
+    }
+
+    public ExamRecordEntity getExamRecord() {
+        return examRecord;
+    }
+
+    public void setExamRecord(ExamRecordEntity examRecord) {
+        this.examRecord = examRecord;
+    }
+
+    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;
+    }
+}

+ 20 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamScoreBeanConvert.java

@@ -0,0 +1,20 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 10:23:05.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.api.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 类信息转换
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+public class ExamScoreBeanConvert implements JsonSerializable {
+
+}

+ 117 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/bean/ExamStudentBeanConvert.java

@@ -0,0 +1,117 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-17 11:36:14.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.api.bean;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.core.oe.admin.api.request.ExamStudentSyncAllDataReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.ExamStudentSyncPartDataReq;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentPartInfo;
+import cn.com.qmth.examcloud.global.api.request.SyncExamStudentReq;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 类信息转换
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+public class ExamStudentBeanConvert implements JsonSerializable {
+
+    public static ExamStudentInfo of(ExamStudentSyncAllDataBean bean) {
+        if (bean == null) {
+            return null;
+        }
+        ExamStudentInfo info = new ExamStudentInfo();
+        info.setExamStudentId(bean.getExamStudentId());
+        info.setExamId(bean.getExamId());
+        info.setStudentId(bean.getStudentId());
+        info.setStudentCode(bean.getStudentCode());
+        info.setStudentName(bean.getStudentName());
+        info.setIdentityNumber(bean.getIdentityNumber());
+        info.setCourseId(bean.getCourseId());
+        info.setCourseCode(bean.getCourseCode());
+        info.setCourseName(bean.getCourseName());
+        info.setCourseLevel(bean.getCourseLevel());
+        info.setOrgId(bean.getOrgId());
+        info.setOrgCode(bean.getOrgCode());
+        info.setOrgName(bean.getOrgName());
+        info.setRootOrgId(bean.getRootOrgId());
+        info.setSpecialtyCode(bean.getSpecialtyCode());
+        info.setSpecialtyName(bean.getSpecialtyName());
+        info.setPaperType(bean.getPaperType());
+        info.setInfoCollector(bean.getInfoCollector());
+        info.setFinished(bean.getFinished());
+        info.setNormalExamTimes(bean.getNormalExamTimes());
+        info.setIsReExamine(bean.getReExamine());
+        info.setReExamineCompleted(bean.getReExamineCompleted());
+        return info;
+    }
+
+    public static ExamStudentPartInfo of(ExamStudentSyncPartDataBean bean) {
+        if (bean == null) {
+            return null;
+        }
+        ExamStudentPartInfo info = new ExamStudentPartInfo();
+        info.setStudentId(bean.getStudentId());
+        info.setStudentCode(bean.getStudentCode());
+        info.setStudentName(bean.getStudentName());
+        info.setIdentityNumber(bean.getIdentityNumber());
+        return info;
+    }
+
+    public static List<ExamStudentInfo> of(ExamStudentSyncAllDataReq req) {
+        if (req == null) {
+            return Lists.newArrayList();
+        }
+        List<ExamStudentSyncAllDataBean> beans = req.getExamStudents();
+        return beans.stream().map(bean -> of(bean)).collect(Collectors.toList());
+    }
+
+    public static List<ExamStudentPartInfo> of(ExamStudentSyncPartDataReq req) {
+        if (req == null) {
+            return Lists.newArrayList();
+        }
+        List<ExamStudentSyncPartDataBean> beans = req.getExamStudents();
+        return beans.stream().map(bean -> of(bean)).collect(Collectors.toList());
+    }
+
+    public static ExamStudentInfo of(SyncExamStudentReq req) {
+        ExamStudentInfo info = new ExamStudentInfo();
+        info.setExamStudentId(req.getId());
+        info.setExamId(req.getExamId());
+        info.setStudentId(req.getStudentId());
+        info.setStudentCode(req.getStudentCode());
+        info.setStudentName(req.getStudentName());
+        info.setIdentityNumber(req.getIdentityNumber());
+        info.setCourseId(req.getCourseId());
+        info.setCourseCode(req.getCourseCode());
+        info.setCourseName(req.getCourseName());
+        info.setCourseLevel(req.getCourseLevel());
+        info.setOrgId(req.getOrgId());
+        info.setOrgCode(req.getOrgCode());
+        info.setOrgName(req.getOrgName());
+        info.setRootOrgId(req.getRootOrgId());
+        info.setSpecialtyName(req.getSpecialtyName());
+        info.setPaperType(req.getPaperType());
+        info.setInfoCollector(req.getInfoCollector());
+        info.setGrade(req.getGrade());
+        info.setExamSiteName(req.getExamSite());
+        info.setFinished(false);
+        info.setNormalExamTimes(0);
+        info.setIsReExamine(false);
+        info.setReExamineCompleted(false);
+        info.setEnable(req.getEnable());
+        return info;
+    }
+
+}

+ 55 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/BaseInfoController.java

@@ -0,0 +1,55 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.controller;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.basic.api.OrgCloudService;
+import cn.com.qmth.examcloud.core.basic.api.bean.OrgBean;
+import cn.com.qmth.examcloud.core.basic.api.request.GetOrgsReq;
+import cn.com.qmth.examcloud.core.basic.api.response.GetOrgsResp;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+
+/**
+ * 
+ * @author chenken
+ *
+ */
+@Api(tags = "考试记录审核相关接口")
+@RestController
+@RequestMapping("${$rmp.ctr.oe}/baseInfo")
+public class BaseInfoController extends ControllerSupport {
+	
+	@Autowired
+	private OrgCloudService orgCloudService;
+
+	@GetMapping("/org/list")
+	public List<OrgBean> queryOrg(){
+		List<OrgBean> orgBeanList = new ArrayList<OrgBean>();
+		Long start = 1l;
+        GetOrgsReq req = new GetOrgsReq();
+        req.setRootOrgId(this.getRootOrgId());
+        while(true){
+        	req.setStart(start);
+     		GetOrgsResp getOrgsResp = orgCloudService.getOrgs(req);
+     		List<OrgBean> subOrgBeanList = getOrgsResp.getOrgBeanList();
+     		for(OrgBean orgBean:subOrgBeanList){
+ 				if(orgBean.getEnable()){
+ 					orgBeanList.add(orgBean);
+ 				}
+     		}
+     		if(start.equals(getOrgsResp.getNext())){
+             	break;
+            }else {
+             	start = getOrgsResp.getNext();
+ 			}
+        }
+		return orgBeanList;
+	}
+	
+}

+ 161 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamAuditController.java

@@ -0,0 +1,161 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-27 16:16:29.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.api.controller;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamAuditService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit.*;
+import cn.com.qmth.examcloud.core.oe.common.base.Constants;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.JsonMapper;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.excel.ExportUtils;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.AuditStatus;
+import cn.com.qmth.examcloud.core.oe.common.enums.DisciplineType;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamProperties;
+import cn.com.qmth.examcloud.core.oe.common.enums.SelectType;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import cn.com.qmth.examcloud.web.support.Naked;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 考试记录审核相关接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/27
+ */
+@RestController
+@Api(tags = "考试记录审核相关接口")
+@RequestMapping("${$rmp.ctr.oe}/exam/audit")
+public class ExamAuditController extends ControllerSupport {
+	
+    @Autowired
+    private ExamAuditService examAuditService;
+    
+    @Autowired
+    private ExamRecordService examRecordService;
+    
+    @Autowired
+    private ExamRecordDataRepo examRecordDataRepo;
+
+    @Autowired
+    private ExamRecordRepo examRecordRepo;
+    
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+
+    @PostMapping("/list")
+    @ApiOperation(value = "查询“监考已审”列表(分页)")
+    public Page<ExamAuditInfo> getExamAuditList(@RequestBody ExamAuditQuery query) {
+    	User user = getAccessUser();
+    	query.setRootOrgId(user.getRootOrgId());
+        return examAuditService.getExamAuditList(query);
+    }
+
+    @PostMapping("/discipline/list")
+    @ApiOperation(value = "查询“违纪名单”列表(分页)")
+    public Page<ExamAuditInfo> getExamAuditUnPassList(@RequestBody ExamAuditQuery query) {
+        query.setStatus(AuditStatus.UN_PASS.name());
+        User user = getAccessUser();
+        query.setRootOrgId(user.getRootOrgId());
+        return examAuditService.getExamAuditList(query);
+    }
+
+    @Naked
+    @GetMapping("/discipline/list/export")
+    @ApiOperation(value = "导出“违纪名单”列表(Excel)", notes = "参数示例:query={\"pageNo\":1,\"pageSize\":10,\"examId\":123, ...}")
+    public void exportExamAuditUnPassList(@RequestParam String query, HttpServletResponse response) throws Exception {
+        ExamAuditQuery newQuery = new JsonMapper().fromJson(query, ExamAuditQuery.class);
+        Check.isNull(newQuery, "请求参数不能为空!");
+        Check.isNull(newQuery.getExamId(), "请先选择考试批次!");
+
+        newQuery.setStatus(AuditStatus.UN_PASS.name());
+        newQuery.setSelectType(SelectType.EXPORT);
+        Page<ExamAuditInfo> page = examAuditService.getExamAuditList(newQuery);
+        List<ExamAuditExcel> list = ExamAuditEntityConvert.ofExcel(page);
+        ExportUtils.exportEXCEL("违纪名单列表", ExamAuditExcel.class, list, response);
+    }
+
+    @PostMapping(value = "/single/audit")
+    @ApiOperation(value = "监考待审-单个审核")
+    public void singleAudit(@RequestParam(required = true) Long examRecordDataId,
+                            @RequestParam(required = false) String disciplineDetail,
+                            @RequestParam(required = true) Boolean isPass,
+                            @RequestParam(required = false) DisciplineType disciplineType) {
+    	if(examRecordDataId == null){
+    		throw new StatusException("singleAudit-001", "examRecordDataId不能为空");
+    	}
+    	if(isPass == null){
+    		throw new StatusException("singleAudit-002", "isPass不能为空");
+    	}
+        User user = getAccessUser();
+        examAuditService.singleAudit(examRecordDataId, isPass, disciplineDetail, disciplineType, user);
+    }
+
+    @PostMapping(value = "/batch/audit")
+    @ApiOperation(value = "监考待审-批量审核")
+    public void batchAudit(@RequestParam List<Long> examRecordDataIds,@RequestParam Boolean isPass) {
+    	if(examRecordDataIds == null || examRecordDataIds.size() == 0){
+    		throw new StatusException("batchAudit-001", "examRecordDataIds不能为空");
+    	}
+    	if(isPass == null){
+    		throw new StatusException("batchAudit-002", "isPass不能为空");
+    	}
+        User user = getAccessUser();
+        examAuditService.batchAudit(examRecordDataIds, isPass, user);
+    }
+
+    @PostMapping(value = "/redoAudit")
+    @ApiOperation(value = "重新审核")
+    public void redoAudit(@RequestBody RedoAuditInfo redoAuditInfo){
+    	User user = getAccessUser();
+    	if(redoAuditInfo.getExamRecordDataIds() == null || redoAuditInfo.getExamRecordDataIds().size()==0){
+    		throw new StatusException("redoAudit-001", "examRecordDataIds不能为空");
+    	}
+    	if(redoAuditInfo.getIsPass() == null){
+    		throw new StatusException("redoAudit-002", "isPass不能为空");
+    	}
+    	if(!redoAuditInfo.getIsPass() && StringUtils.isBlank(redoAuditInfo.getDisciplineType())){
+    		throw new StatusException("redoAudit-003", "审核为不通过时违纪类型不能为空");
+    	}
+    	Set<Long> examIds = new HashSet<Long>();
+		for(Long examRecordDataId:redoAuditInfo.getExamRecordDataIds()){
+			ExamRecordDataEntity examRecordData =GlobalHelper.getEntity(examRecordDataRepo,examRecordDataId,ExamRecordDataEntity.class);
+            ExamRecordEntity examRecord =GlobalHelper.getEntity(examRecordRepo,examRecordData.getExamRecordId(),ExamRecordEntity.class);
+			examIds.add(examRecord.getExamId().longValue());
+		}
+		if(examIds.size()>1){
+			throw new StatusException("redoAudit-004", "考试ID中包含了多个批次");
+		}
+    	long examId = examIds.iterator().next();
+		//是否已经生成了评卷任务
+		String markingTaskBuilded = gainBaseDataService.getExamProperty(examId, ExamProperties.MARKING_TASK_BUILDED.name());
+		if(StringUtils.isNotBlank(markingTaskBuilded) && Constants.isTrue.equals(markingTaskBuilded)){
+			throw new StatusException("redoAudit-005", "该考试评卷任务已生成,不能重审");
+		}
+    	examAuditService.redoAudit(redoAuditInfo,user);
+    }
+    
+}

+ 50 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamCaptureController.java

@@ -0,0 +1,50 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 17:26:51.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.api.controller;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCaptureService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examcapture.ExamCaptureAuditInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examcapture.ExamCaptureInfo;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 考试抓拍检测相关接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/27
+ */
+@RestController
+@Api(tags = "考试抓拍检测相关接口")
+@RequestMapping("${$rmp.ctr.oe}/exam/capture")
+public class ExamCaptureController extends ControllerSupport {
+    @Autowired
+    private ExamCaptureService examCaptureService;
+
+    @PostMapping("/audit/detail")
+    @ApiOperation(value = "抓拍详情-审核详细信息")
+    public ExamCaptureAuditInfo getAuditDetail(@RequestParam Long examRecordDataId) {
+        return examCaptureService.getExamCaptureAuditDetail(examRecordDataId);
+    }
+
+    @PostMapping("/list")
+    @ApiOperation(value = "抓拍详情-抓拍记录列表")
+    public List<ExamCaptureInfo> list(@RequestParam Long examRecordDataId) {
+        return examCaptureService.getExamCaptureList(examRecordDataId);
+    }
+
+}

+ 137 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamRecordController.java

@@ -0,0 +1,137 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-27 14:52:17.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.api.controller;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamRecordInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamRecordQuery;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamStudentQuestionScoreInfo;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.JsonMapper;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.excel.ExportUtils;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordForMarkingEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamType;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordForMarkingRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import cn.com.qmth.examcloud.web.support.Naked;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 考试记录相关接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/27
+ */
+@RestController
+@Api(tags = "考试记录相关接口")
+@RequestMapping("${$rmp.ctr.oe}/exam/record")
+public class ExamRecordController extends ControllerSupport {
+    @Autowired
+    private ExamRecordService examRecordService;
+    
+    @Autowired
+    private ExamRecordRepo examRecodRepo;
+    
+    @Autowired
+    private ExamRecordForMarkingRepo examRecordForMarkingRepo;
+    
+
+    @PostMapping("/waiting/audit/list")
+    @ApiOperation(value = "查询“监考待审”列表(分页)")
+    public Page<ExamRecordInfo> getExamRecordWaitingAuditList(@RequestBody ExamRecordQuery query) {
+        return examRecordService.getExamRecordWaitingAuditList(query);
+    }
+    
+    @PostMapping("/detail/check")
+    @ApiOperation(value = "查询“考试明细” 是否存在待审数据")
+    public Long existsWarnExamRecordDetail(@RequestBody ExamRecordQuery query) {
+        return examRecordService.existsWarnExamRecordDetail(query);
+    }
+    
+    @PostMapping("/detail/list")
+    @ApiOperation(value = "查询“考试明细”列表(分页)")
+    public Page<ExamRecordInfo> getExamRecordDetailList(@RequestBody ExamRecordQuery query) {
+    	Page<ExamRecordInfo> examRecordInfoPage = examRecordService.getExamRecordDetailListForPage(query);
+    	List<ExamRecordInfo> examRecordInfoList = examRecordInfoPage.getContent();
+    	if(examRecordInfoList!=null && examRecordInfoList.size() > 0){
+    		String examType = examRecordInfoList.get(0).getExamType();
+    		if(ExamType.OFFLINE.name() == examType){
+    			for(ExamRecordInfo examRecordInfo:examRecordInfoList){
+    				long examRecordDataId = examRecordInfo.getDataId();
+        			ExamRecordForMarkingEntity examRecordForMarkingEntity = examRecordForMarkingRepo.findByExamRecordDataId(examRecordDataId);
+        			if(examRecordForMarkingEntity!=null){
+        				examRecordInfo.setOfflineFileUrl(examRecordForMarkingEntity.getOfflineFileUrl());
+        			}
+    			}
+    		}
+    	}
+        return examRecordInfoPage;
+    }
+
+    @Naked
+    @GetMapping("/detail/list/export")
+    @ApiOperation(value = "导出“考试明细”列表(Excel)", notes = "参数示例:query={\"pageNo\":1,\"pageSize\":10,\"examId\":123, ...}")
+    public void exportExamRecordDetailList(@RequestParam String query, HttpServletResponse response) throws Exception {
+        ExamRecordQuery newQuery = new JsonMapper().fromJson(query, ExamRecordQuery.class);
+        Check.isNull(newQuery, "请求参数不能为空!");
+        Check.isNull(newQuery.getExamId(), "请先选择考试批次!");
+        List<ExamRecordInfo> examRecordInfoList = examRecordService.getExamRecordDetailList(newQuery);
+        ExportUtils.exportEXCEL("考试明细列表", ExamRecordInfo.class,examRecordInfoList,response);
+    }
+
+    @PostMapping(value = "/refresh/capture/statistic")
+    @ApiOperation(value = "监考待审-重新统计", notes = "根据人脸识别阀值重新计算数据")
+    public ResponseEntity<String> refreshCaptureStatistic(@RequestParam Long examId) {
+        //todo
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/clean/capture/statistic")
+    @ApiOperation(value = "监考待审-清理数据")
+    public ResponseEntity<String> cleanCaptureStatistic() {
+        //todo
+        //examRecordService.cleanCaptureStatistic();
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @GetMapping("/select/byExamStudentId")
+    @ApiOperation(value = "根据考生ID查询考试记录")
+    public List<ExamRecordEntity> getExamRecordByExamStudentId(@RequestParam Long examStudentId){
+    	List<ExamRecordEntity> examRecordEntityList = examRecodRepo.findByExamStudentId(examStudentId);
+    	return 	examRecordEntityList;	
+    }
+    
+    @GetMapping("/detail/examStudentQuestionScoreList")
+    @ApiOperation(value = "导出考生作答分数明细列表(Excel)", notes = "参数examId和examId必传")
+    public void getExamStudentQuestionScoreList(@RequestParam Long examId,@RequestParam String courseCode, HttpServletResponse response) throws Exception {
+        Check.isNull(examId, "考试id不能为空!");
+        Check.isBlank(courseCode, "课程代码不能空!");
+        List<ExamStudentQuestionScoreInfo> examRecordInfoList = examRecordService.getExamStudentQuestionScoreList(examId,courseCode);
+        ExportUtils.exportEXCEL("考试明细列表", ExamStudentQuestionScoreInfo.class,examRecordInfoList,response);
+        
+//        return examRecordInfoList;List<ExamStudentQuestionScoreInfo>
+    }
+}

+ 54 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamRecordDataController.java

@@ -0,0 +1,54 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.controller;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.ExamRecordDataBeanConvert;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.ExamRecordDataDomain;
+import io.swagger.annotations.Api;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordEntity;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+
+
+@RestController
+@Api(tags = "考试记录相关接口")
+@RequestMapping("${$rmp.ctr.oe}/exam/record/data")
+public class ExamRecordDataController {
+
+    @Autowired
+    private ExamRecordDataRepo examRecordDataRepo;
+
+    @Autowired
+    private ExamRecordRepo examRecordRepo;
+
+    @GetMapping("/findExamRecordDataEntity")
+    public ExamRecordDataDomain findExamRecordDataEntity(@RequestParam Long examRecordDataId) {
+        ExamRecordDataDomain domain = ExamRecordDataBeanConvert.copyFrom(
+                GlobalHelper.getEntity(examRecordDataRepo, examRecordDataId, ExamRecordDataEntity.class));
+        domain.setExamRecord(GlobalHelper.getEntity(examRecordRepo, domain.getExamRecordId(), ExamRecordEntity.class));
+        return domain;
+    }
+
+    @GetMapping("/findByExamStudentId")
+    public List<ExamRecordDataDomain> findByExamStudentId(@RequestParam Long examStudentId) {
+        List<ExamRecordEntity> examRecordEntities = examRecordRepo.findByExamStudentId(examStudentId);
+        List<ExamRecordDataDomain> examRecordDataList = new ArrayList<ExamRecordDataDomain>();
+        for (ExamRecordEntity examRecord : examRecordEntities) {
+            ExamRecordDataDomain domain = ExamRecordDataBeanConvert.copyFrom(
+                    examRecordDataRepo.findByExamRecordId(examRecord.getId()));
+            domain.setExamRecord(examRecord);
+            examRecordDataList.add(domain);
+        }
+        return examRecordDataList;
+    }
+}

+ 38 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamRecordPaperStructController.java

@@ -0,0 +1,38 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordPaperStructService;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordPaperStructEntity;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月5日 下午4:02:15
+ * @company 	QMTH
+ * @description 考试记录-试卷结构controller
+ */
+@Api(tags = "考试记录-试卷结构")
+@RestController
+@RequestMapping("${$rmp.ctr.oe}/examRecordPaperStruct")
+public class ExamRecordPaperStructController extends ControllerSupport{
+
+	@Autowired
+	private ExamRecordPaperStructService examRecordPaperStructService;
+	
+	@ApiOperation(value = "获取考试记录试卷结构")
+	@GetMapping("/getExamRecordPaperStruct")
+	public ExamRecordPaperStructEntity getExamRecordPaperStruct(@RequestParam Long examRecordDataId){
+		Check.isNull(examRecordDataId, "examRecordDataId不能为空");
+		return examRecordPaperStructService.getExamRecordPaperStruct(examRecordDataId);
+	}
+	
+}

+ 39 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamRecordQuestionsController.java

@@ -0,0 +1,39 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordQuestionsEntity;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordQuestionsRepo;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年10月12日 上午9:48:47
+ * @company 	QMTH
+ * @description ExamRecordQuestionsController.java
+ */
+@Api(tags = "考试结束后-作答记录接口")
+@RestController
+@RequestMapping("${$rmp.ctr.oe}/examRecordQuestions")
+public class ExamRecordQuestionsController extends ControllerSupport{
+
+	@Autowired
+	private ExamRecordQuestionsRepo examRecordQuestionsRepo;
+	
+	@ApiOperation(value = "获取交卷之后的所有试题作答信息")
+	@GetMapping("/getExamRecordQuestions")
+	public ExamRecordQuestionsEntity getExamRecordQuestions(@RequestParam Long examRecordDataId){
+		Check.isNull(examRecordDataId, "examRecordDataId不能为空");
+		return examRecordQuestionsRepo.findByExamRecordDataId(examRecordDataId);
+	}
+	
+}

+ 64 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamScoreController.java

@@ -0,0 +1,64 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 09:28:03.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.api.controller;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.GetMapping;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScoreService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore.ExamScoreInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore.ExamScoreQuery;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.JsonMapper;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.excel.ExportUtils;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import cn.com.qmth.examcloud.web.support.Naked;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 考试分数相关接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/27
+ */
+@RestController
+@Api(tags = "考试分数相关接口")
+@RequestMapping("${$rmp.ctr.oe}/exam/score")
+public class ExamScoreController extends ControllerSupport {
+    @Autowired
+    private ExamScoreService examScoreService;
+
+    @PostMapping("/statistic/list")
+    @ApiOperation(value = "查询“成绩统计”列表(分页)")
+    public Page<ExamScoreInfo> getExamAuditList(@RequestBody ExamScoreQuery query) {
+        return examScoreService.getExamScoreList(query);
+    }
+
+    @Naked
+    @GetMapping("/statistic/list/export")
+    @ApiOperation(value = "导出“成绩统计”列表(Excel)", notes = "参数示例:query={\"pageNo\":1,\"pageSize\":10,\"examId\":123, ...}")
+    public void exportExamRecordDetailList(@RequestParam String query, HttpServletResponse response) throws Exception {
+        ExamScoreQuery newQuery = new JsonMapper().fromJson(query, ExamScoreQuery.class);
+        Check.isNull(newQuery, "请求参数不能为空!");
+        Check.isNull(newQuery.getExamId(), "请先选择考试批次!");
+        List<ExamScoreInfo> examScoreList = examScoreService.exportExamScoreList(newQuery);
+        ExportUtils.exportEXCEL("成绩统计列表", ExamScoreInfo.class,examScoreList,response);
+    }
+
+}

+ 214 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExamStudentController.java

@@ -0,0 +1,214 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-16 17:40:53.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.api.controller;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.servlet.http.HttpServletResponse;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCacheTransferHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.GetMapping;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamStudentService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.CourseProgressInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentEntityConvert;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentExcel;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentFinishedStatistic;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentOrgStatistic;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentQuery;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentUnFinishedExcel;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.JsonMapper;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.excel.ExportUtils;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import cn.com.qmth.examcloud.web.support.Naked;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 考生信息接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/16
+ */
+@RestController
+@Api(tags = "考生信息相关接口")
+@RequestMapping("${$rmp.ctr.oe}/exam/student")
+public class ExamStudentController extends ControllerSupport {
+    @Autowired
+    private ExamStudentService examStudentService;
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+    
+    /**
+     * 查询课程本地缓存
+     */
+    private static Map<Long,CourseBean> courseBeanLocalCache;
+    
+    private static Timer timer;
+    
+    static{
+    	courseBeanLocalCache = new ConcurrentHashMap<Long, CourseBean>();
+    	timer = new Timer();
+    }
+    
+    class CleanCourseBeanCacheTask extends TimerTask{
+		@Override
+		public void run() {
+			courseBeanLocalCache.clear();
+		}
+    }
+    
+    public ExamStudentController(){
+    	long delay = 10*60*1000;//10分钟后开始
+    	long period = 30*60*1000;//30分钟清理一次
+    	timer.schedule(new CleanCourseBeanCacheTask(),delay,period);
+    }
+
+    @PostMapping("/examScheduling/list")
+    @ApiOperation(value = "查询“考试进度详情”列表(分页)")
+    public Page<ExamStudentInfo> getExamScheduling(@RequestBody ExamStudentQuery query) {
+    	Check.isNull(query, "查询参数不能为空!");
+        Check.isNull(query.getExamId(), "考试批次不能为空!");
+        return examStudentService.getExamStudentListPage(query);
+    }
+
+    @GetMapping("/examScheduling/list/export")
+    @ApiOperation(value = "导出“考试进度详情”列表(Excel)", notes = "参数示例:query={\"pageNo\":1,\"pageSize\":10,\"examId\":123, ...}")
+    public void exportExamStudentList(@RequestParam String query, HttpServletResponse response) throws Exception {
+        ExamStudentQuery newQuery = new JsonMapper().fromJson(query, ExamStudentQuery.class);
+        Check.isNull(newQuery, "请求参数不能为空!");
+        Check.isNull(newQuery.getExamId(), "请先选择考试批次!");
+        List<ExamStudentInfo> examStudentInfoList = examStudentService.getExamStudentInfoList(newQuery);
+        List<ExamStudentExcel> list = ExamStudentEntityConvert.ofExcel(examStudentInfoList);
+        ExportUtils.exportEXCEL("考试详情列表", ExamStudentExcel.class, list, response);
+    }
+
+    @PostMapping("/unfinished/list")
+    @ApiOperation(value = "查询“缺考登记”列表(分页)")
+    public Page<ExamStudentInfo> getExamStudentUnFinishedList(@RequestBody ExamStudentQuery query) {
+    	Check.isNull(query, "查询参数不能为空!");
+        Check.isNull(query.getExamId(), "考试批次不能为空!");
+        query.setFinished(0);
+        return examStudentService.getExamStudentListPage(query);
+    }
+
+    @Naked
+    @GetMapping("/unfinished/list/export")
+    @ApiOperation(value = "导出“缺考登记”列表(Excel)", notes = "参数示例:query={\"pageNo\":1,\"pageSize\":10,\"examId\":123, ...}")
+    public void exportExamStudentUnFinishedList(@RequestParam String query, HttpServletResponse response) throws Exception {
+        ExamStudentQuery newQuery = new JsonMapper().fromJson(query, ExamStudentQuery.class);
+        Check.isNull(newQuery, "请求参数不能为空!");
+        Check.isNull(newQuery.getExamId(), "请先选择考试批次!");
+        newQuery.setFinished(0);//未完成
+        List<ExamStudentInfo> examStudentInfos = examStudentService.getExamStudentInfoList(newQuery);
+        List<ExamStudentUnFinishedExcel> list = ExamStudentEntityConvert.ofUnFinishedExcel(examStudentInfos);
+        ExportUtils.exportEXCEL("缺考登记列表", ExamStudentUnFinishedExcel.class, list, response);
+    }
+
+    @PostMapping("/reexamine/list")
+    @ApiOperation(value = "查询重考考生列表(分页)")
+    public Page<ExamStudentInfo> getReExamineStudentList(@RequestBody ExamStudentQuery query) {
+    	Check.isNull(query, "查询参数不能为空!");
+        Check.isNull(query.getExamId(), "考试批次不能为空!");
+        return examStudentService.getReExamineStudentList(query);
+    }
+    
+    @PostMapping("/reexamine")
+    @ApiOperation(value = "设置重考")
+    public void setReexamine(@RequestParam Long examStudentId){
+    	if(examStudentId == null){
+    		return;
+    	}
+    	examStudentService.setReexamine(examStudentId);
+    }
+
+    @PostMapping("/statistic/by/finished")
+    @ApiOperation(value = "考试概况-按考试完成进度统计")
+    public ExamStudentFinishedStatistic getExamStudentStatisticByFinished(@RequestParam Long examId) {
+    	if(examId == null){
+    		return null;
+    	}
+        return examStudentService.getExamStudentStatisticByFinished(examId);
+    }
+
+    @PostMapping("/statistic/by/org")
+    @ApiOperation(value = "考试概况-按学习中心统计")
+    public List<ExamStudentOrgStatistic> getExamStudentStatisticByOrg(@RequestParam Long examId, @RequestParam(required = false) Long orgId) {
+    	if(examId == null){
+    		return null;
+    	}
+        return examStudentService.getExamStudentStatisticByOrg(examId, orgId);
+    }
+    
+    /**
+     * 查询课程
+     * @param examId
+     * @param orgId
+     * @return
+     */
+    @GetMapping("/findCoursesByExamIdAndOrgId")
+    @ApiOperation(value = "查询课程")
+    public List<CourseBean> findCoursesByExamIdAndOrgId(@RequestParam Long examId, @RequestParam(required=false) Long orgId){
+    	if(examId == null){
+    		return null;
+    	}
+    	User user = getAccessUser();
+    	List<Long> courseIdList = examStudentService.findCoursesFromExamStudent(examId, orgId);
+    	List<CourseBean> courseBeanList = new ArrayList<CourseBean>();
+    	for(Long courseId:courseIdList){
+    		CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(courseId);
+    		courseBeanList.add(courseBean);
+    	}
+    	return courseBeanList;
+    }
+    
+    /**
+     * 课程完成进度
+     * @param examId
+     * @param courseId
+     * @return
+     */
+    @GetMapping("/courseProgress/list")
+    @ApiOperation(value = "课程完成进度")
+    public List<CourseProgressInfo> queryCourseProgressInfos(@RequestParam Long examId,
+    														  @RequestParam(required=false) Long courseId,
+    														  @RequestParam(required=false) String orderColumn){
+    	if(examId == null){
+    		return null;
+    	}
+    	List<CourseProgressInfo> courseProgressInfoList = examStudentService.queryCourseProgressInfos(examId,courseId,orderColumn);
+    	if(courseProgressInfoList!=null && courseProgressInfoList.size()>0){
+    		for(CourseProgressInfo courseProgressInfo:courseProgressInfoList){
+    			long key = courseProgressInfo.getCourseId();
+        		CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(key);
+        		courseProgressInfo.setCourseCode(courseBean.getCode());
+        		courseProgressInfo.setCourseName(courseBean.getName());
+        	}
+    	}
+    	return courseProgressInfoList;
+    }
+    
+    
+}

+ 159 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamRecordCloudServiceProvider.java

@@ -0,0 +1,159 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.provider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+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 cn.com.qmth.examcloud.core.oe.admin.api.ExamRecordCloudService;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.ExamRecordForSelectScore;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.CheckExamIsStartedReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.GetExamPhotoVerifyDataReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.QueryExamRecordForSelectScoreReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.CheckExamIsStartedResp;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.GetExamPhotoVerifyDataResp;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.QueryExamRecordForSelectScoreResp;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamCaptureEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamScoreEntity;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamCaptureRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScoreRepo;
+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;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月19日 下午2:44:12
+ * @company 	QMTH
+ * @description ExamRecordCloudServiceProvider.java
+ */
+@Api(tags = "考试记录相关接口")
+@RestController
+@RequestMapping("${$rmp.cloud.oe}/examRecord")
+public class ExamRecordCloudServiceProvider extends ControllerSupport implements ExamRecordCloudService{
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 7678785677176906341L;
+	
+	@Autowired
+	private ExamRecordRepo examRecordRepo;
+	
+	@Autowired
+	private ExamScoreRepo examScoreRepo;
+	
+	@Autowired
+	private ExamRecordDataRepo examRecordDataRepo;
+	
+	@Autowired
+	private ExamCaptureRepo examCaptureRepo;
+
+	@Override
+	@ApiOperation(value = "查询是否已经开考")
+	@PostMapping("/checkExamIsStarted")
+	public CheckExamIsStartedResp checkExamIsStarted(@RequestBody CheckExamIsStartedReq examRecordReq) {
+		Check.isNull(examRecordReq.getExamId(), "examId不能为空");
+		ExamRecordEntity examRecordEntity = new ExamRecordEntity();
+		examRecordEntity.setExamId(examRecordReq.getExamId());
+		examRecordEntity.setCourseId(examRecordReq.getCourseId());
+		examRecordEntity.setStudentId(examRecordReq.getStudentId());
+		long examRecordNum = examRecordRepo.count(Example.of(examRecordEntity));
+		CheckExamIsStartedResp checkExamIsStartedResp = new CheckExamIsStartedResp();
+		checkExamIsStartedResp.setIsStarted(examRecordNum>0);
+		return checkExamIsStartedResp;
+	}
+
+	@Override
+	@ApiOperation(value = "查询考试记录-供exchange调用")
+	@PostMapping("/queryExamRecordForSelectScoreByScoreId")
+	public QueryExamRecordForSelectScoreResp queryExamRecordForSelectScoreByScoreId(@RequestBody QueryExamRecordForSelectScoreReq req) {
+		QueryExamRecordForSelectScoreResp resp = new QueryExamRecordForSelectScoreResp();
+		if(req.getExamScoreId() != null){
+			ExamScoreEntity examScore =GlobalHelper.getEntity(examScoreRepo,req.getExamScoreId(),ExamScoreEntity.class);
+			ExamRecordDataEntity examRecordData =GlobalHelper.getEntity(examRecordDataRepo,examScore.getExamRecordDataId(),ExamRecordDataEntity.class);
+			List<ExamRecordForSelectScore> examRecordForSelectScoreList = new ArrayList<ExamRecordForSelectScore>();
+			examRecordForSelectScoreList.add(buildExamRecordForSelectScore(examRecordData));
+			resp.setExamRecordForSelectScoreList(examRecordForSelectScoreList);
+		}
+		return resp;
+	}
+	
+	@Override
+	@ApiOperation(value = "查询考试记录-供exchange调用")
+	@PostMapping("/queryExamRecordForSelectScoreByExamStudentId")
+	public QueryExamRecordForSelectScoreResp queryExamRecordForSelectScoreByExamStudentId(@RequestBody QueryExamRecordForSelectScoreReq req) {
+		QueryExamRecordForSelectScoreResp resp = new QueryExamRecordForSelectScoreResp();
+		if(req.getExamStudentId() != null){
+			List<ExamRecordEntity> examRecordList = examRecordRepo.findByExamStudentId(req.getExamStudentId());
+			List<ExamRecordForSelectScore> examRecordForSelectScoreList = new ArrayList<ExamRecordForSelectScore>();
+			for(ExamRecordEntity examRecord:examRecordList){
+				ExamRecordDataEntity examRecordData = examRecordDataRepo.findByExamRecordId(examRecord.getId());
+				examRecordForSelectScoreList.add(buildExamRecordForSelectScore(examRecordData));
+			}
+			resp.setExamRecordForSelectScoreList(examRecordForSelectScoreList);
+		}
+		return resp;
+	}
+
+	public ExamRecordForSelectScore buildExamRecordForSelectScore(ExamRecordDataEntity examRecordData){
+		ExamRecordForSelectScore examRecordForSelectScore = new ExamRecordForSelectScore();
+		ExamRecordEntity examRecord =GlobalHelper.getEntity(examRecordRepo,examRecordData.getExamRecordId(),ExamRecordEntity.class);
+		examRecordForSelectScore.setExamStudentId(examRecord.getExamStudentId());
+		examRecordForSelectScore.setRootOrgId(examRecord.getRootOrgId());
+		examRecordForSelectScore.setExamRecordDataId(examRecordData.getId());
+		examRecordForSelectScore.setStartTime(examRecordData.getStartTime());
+		examRecordForSelectScore.setEndTime(examRecordData.getEndTime());
+		examRecordForSelectScore.setCleanTime(examRecordData.getCleanTime());
+		examRecordForSelectScore.setIsWarn(examRecordData.getIsWarn());
+		examRecordForSelectScore.setIsAudit(examRecordData.getIsAudit());
+		examRecordForSelectScore.setIsIllegality(examRecordData.getIsIllegality());
+		examRecordForSelectScore.setStatus(examRecordData.getExamRecordStatus().name());
+		
+		return examRecordForSelectScore;
+	}
+
+	@Override
+	@ApiOperation(value = "查询照片数据-供exchange调用")
+	@PostMapping("/getExamPhotoVerifyData")
+	public GetExamPhotoVerifyDataResp getExamPhotoVerifyData(@RequestBody GetExamPhotoVerifyDataReq req) {
+		Long scoreId = req.getScoreId();
+		ExamScoreEntity examScoreEntity =GlobalHelper.getEntity(examScoreRepo,scoreId,ExamScoreEntity.class);
+		if(examScoreEntity == null){
+			return null;
+		}
+		Long examRecordDataId = examScoreEntity.getExamRecordDataId();
+		ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo,examRecordDataId,ExamRecordDataEntity.class);
+		if(examRecordData == null){
+			return null;
+		}
+		List<ExamCaptureEntity> examCaptureList = examCaptureRepo.findByExamRecordDataId(examRecordDataId);
+		
+		GetExamPhotoVerifyDataResp resp = new GetExamPhotoVerifyDataResp();
+		resp.setScoreId(scoreId);
+		resp.setSuccessRate(examRecordData.getFaceSuccessPercent());
+		int strangerCount = 0;
+		List<String> photoUrls = new ArrayList<String>();
+		for(ExamCaptureEntity examCaptureEntity:examCaptureList){
+			if(examCaptureEntity.getIsStranger()){
+				strangerCount++;
+			}
+			photoUrls.add(examCaptureEntity.getFileUrl());
+		}
+		resp.setStrangerCount(strangerCount);
+		resp.setPhotoUrls(photoUrls);
+		return resp;
+	}
+}

+ 182 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamRecordForMarkingCloudServiceProvider.java

@@ -0,0 +1,182 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.provider;
+
+import static cn.com.qmth.examcloud.core.oe.common.base.Constants.OE_CODE_400;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.persistence.criteria.Predicate;
+
+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.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 cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.oe.admin.api.ExamRecordForMarkingCloudService;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.ExamRecordForMarkingBean;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.FindExamRecordForMarkingInfoReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.QueryValidExamRecordInfoPageReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.QueryValidExamRecordInfoReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.FindExamRecordForMarkingInfoResp;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.QueryValidExamRecordInfoPageResp;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.QueryValidExamRecordInfoResp;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordForMarkingService;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordForMarkingEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamStudentRepo;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月27日 上午11:01:23
+ * @company 	QMTH
+ * @description 阅卷相关接口
+ */
+@Api(tags = "阅卷获取信息相关接口")
+@RestController
+@RequestMapping("${$rmp.cloud.oe}/examRecordForMarking")
+public class ExamRecordForMarkingCloudServiceProvider extends ControllerSupport implements ExamRecordForMarkingCloudService{
+	
+	private static final long serialVersionUID = -8343697288418890873L;
+	
+	@Autowired
+	private ExamRecordForMarkingService examRecordForMarkingService;
+	@Autowired
+	private ExamStudentRepo examStudentRepo;
+
+	@Override
+	@ApiOperation(value = "根据条件查询阅卷需要的信息")
+	@PostMapping("/findExamRecordForMarkingInfo")
+	public FindExamRecordForMarkingInfoResp findExamRecordForMarkingInfo(@RequestBody FindExamRecordForMarkingInfoReq req) {
+		Long id =  req.getId();
+		Long examId = req.getExamId();
+		FindExamRecordForMarkingInfoResp resp = new FindExamRecordForMarkingInfoResp();
+		if(id == null && examId == null){
+			return resp;
+		}
+		List<ExamRecordForMarkingEntity> examRecordForMarkingList = examRecordForMarkingService.findExamRecordForMarkingInfo(id,examId,req.getCourseId());
+		List<ExamRecordForMarkingBean> examRecordForMarkingBeanList = new ArrayList<ExamRecordForMarkingBean>();
+		for(ExamRecordForMarkingEntity entity:examRecordForMarkingList){
+			ExamRecordForMarkingBean examRecordForMarkingBean = new ExamRecordForMarkingBean();
+			examRecordForMarkingBean.setId(entity.getId());
+			examRecordForMarkingBean.setExamId(entity.getExamId());
+			examRecordForMarkingBean.setBasePaperId(entity.getBasePaperId());
+			examRecordForMarkingBean.setPaperType(entity.getPaperType());
+			examRecordForMarkingBean.setCourseId(entity.getCourseId());
+			examRecordForMarkingBean.setOfflineFileName(entity.getOfflineFileName());
+			examRecordForMarkingBean.setOfflineFileUrl(entity.getOfflineFileUrl());
+			examRecordForMarkingBeanList.add(examRecordForMarkingBean);
+		}
+		resp.setExamRecordForMarkingBeanList(examRecordForMarkingBeanList);
+		return resp;
+	}
+
+	@Override
+	@ApiOperation(value = "查询有效成绩")
+	@PostMapping("/queryValidExamRecordInfo")
+	public QueryValidExamRecordInfoResp queryValidExamRecordInfo(@RequestBody QueryValidExamRecordInfoReq req) {
+		Check.isNull(req.getExamId(), "examId不能为空");
+		Check.isNull(req.getCourseId(), "courseId不能为空");
+		List<ExamRecordForMarkingEntity> examRecordForMarkingList = examRecordForMarkingService.queryValidExamRecordInfo(req.getExamId(), req.getCourseId());
+		
+		QueryValidExamRecordInfoResp resp = new QueryValidExamRecordInfoResp();
+		List<ExamRecordForMarkingBean> examRecordForMarkingBeanList = new ArrayList<ExamRecordForMarkingBean>();
+		for(ExamRecordForMarkingEntity entity:examRecordForMarkingList){
+			ExamRecordForMarkingBean examRecordForMarkingBean = new ExamRecordForMarkingBean();
+			examRecordForMarkingBean.setId(entity.getId());
+			examRecordForMarkingBean.setExamId(entity.getExamId());
+			examRecordForMarkingBean.setExamRecordDataId(entity.getExamRecordDataId());
+			examRecordForMarkingBean.setExamStudentId(entity.getExamStudentId());
+			examRecordForMarkingBean.setBasePaperId(entity.getBasePaperId());
+			examRecordForMarkingBean.setPaperType(entity.getPaperType());
+			examRecordForMarkingBean.setCourseId(entity.getCourseId());
+			examRecordForMarkingBean.setObjectiveScore(entity.getObjectiveScore());
+			examRecordForMarkingBeanList.add(examRecordForMarkingBean);
+		}
+		resp.setExamRecordForMarkingBeanList(examRecordForMarkingBeanList);
+		return resp;
+	}
+
+	@Override
+	@ApiOperation(value = "分页查询有效成绩")
+	@PostMapping("/queryValidExamRecordInfoPage")
+	public QueryValidExamRecordInfoPageResp queryValidExamRecordInfoPage(@RequestBody QueryValidExamRecordInfoPageReq req) {
+		Check.isNull(req.getExamId(), "examId不能为空");
+		Check.isNull(req.getCourseId(), "courseId不能为空");
+		Check.isNull(req.getStart(), "start不能为空");
+		Check.isNull(req.getSize(), "size不能为空");
+		if (req.getStart().longValue()<=0) {
+            throw new StatusException(OE_CODE_400, "start必须大于0");
+        }
+		if (req.getSize().longValue()<=0) {
+			throw new StatusException(OE_CODE_400, "size必须大于0");
+        }
+		Long courseId=req.getCourseId();
+		Long examId = req.getExamId();
+		Long size=req.getSize();
+		Long start =req.getStart();
+		//分页获取考生id
+		Pageable pageable = PageRequest.of(0, size.intValue(), Sort.Direction.ASC, "id");
+
+		Specification<ExamStudentEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("examId"), examId));
+			predicates.add(cb.equal(root.get("courseId"), courseId));
+			predicates.add(cb.greaterThanOrEqualTo(root.get("id"), start));
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		Page<ExamStudentEntity> page = examStudentRepo.findAll(specification,
+				pageable);
+
+		Iterator<ExamStudentEntity> iterator = page.iterator();
+
+		List<Long> stuIdList = Lists.newArrayList();
+		Long next = start;
+		while (iterator.hasNext()) {
+			ExamStudentEntity e = iterator.next();
+			next = e.getId();
+			stuIdList.add(e.getExamStudentId());
+		}
+
+		QueryValidExamRecordInfoPageResp resp = new QueryValidExamRecordInfoPageResp();
+		if (!next .equals(start)) {
+			next++;
+		}
+		resp.setNext(next);
+		//根据分页获取的考生id获取需要阅卷的试卷
+		List<ExamRecordForMarkingBean> examRecordForMarkingBeanList = new ArrayList<ExamRecordForMarkingBean>();
+		if(stuIdList.size()>0) {
+			List<ExamRecordForMarkingEntity> examRecordForMarkingList = examRecordForMarkingService.queryValidExamRecordInfoByStuIds(examId, courseId,stuIdList);
+			for(ExamRecordForMarkingEntity entity:examRecordForMarkingList){
+				ExamRecordForMarkingBean examRecordForMarkingBean = new ExamRecordForMarkingBean();
+				examRecordForMarkingBean.setId(entity.getId());
+				examRecordForMarkingBean.setExamId(entity.getExamId());
+				examRecordForMarkingBean.setExamRecordDataId(entity.getExamRecordDataId());
+				examRecordForMarkingBean.setExamStudentId(entity.getExamStudentId());
+				examRecordForMarkingBean.setBasePaperId(entity.getBasePaperId());
+				examRecordForMarkingBean.setPaperType(entity.getPaperType());
+				examRecordForMarkingBean.setCourseId(entity.getCourseId());
+				examRecordForMarkingBean.setObjectiveScore(entity.getObjectiveScore());
+				examRecordForMarkingBeanList.add(examRecordForMarkingBean);
+			}
+		}
+		resp.setExamRecordForMarkingBeanList(examRecordForMarkingBeanList);
+		return resp;
+	}
+
+}

+ 89 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamRecordQuestionsCloudServiceProvider.java

@@ -0,0 +1,89 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.provider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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;
+
+import cn.com.qmth.examcloud.core.oe.admin.api.ExamRecordQuestionsCloudService;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.QuerySubjectiveAnswerBean;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.SaveSubjectiveQuestionScoreBean;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.QuerySubjectiveAnswerListReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.SaveSubjectiveQuestionScoreReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.QuerySubjectiveAnswerListResp;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordQuestionsService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.SubjectiveQuestionScoreInfo;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamQuestionEntity;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月27日 上午11:02:55
+ * @company 	QMTH
+ * @description 学生作答答案相关接口
+ */
+@Api(tags = "学生作答答案相关接口")
+@RestController
+@RequestMapping("${$rmp.cloud.oe}/examRecordQuestions")
+public class ExamRecordQuestionsCloudServiceProvider extends ControllerSupport implements ExamRecordQuestionsCloudService{
+
+	private static final long serialVersionUID = -1058894529829651231L;
+	
+	@Autowired
+	private ExamRecordQuestionsService examRecordQuestionsService;
+	
+	/**
+	 * 阅卷获取考试记录主观题答案信息
+	 */
+	@Override
+	@ApiOperation(value = "阅卷获取考试记录主观题答案信息")
+	@PostMapping("/querySubjectiveAnswerList")
+	public QuerySubjectiveAnswerListResp querySubjectiveAnswerList(@RequestBody QuerySubjectiveAnswerListReq req) {
+		List<ExamQuestionEntity> examQuestionList = examRecordQuestionsService.querySubjectiveAnswerList(req.getExamRecordDataId());
+		QuerySubjectiveAnswerListResp resp = new QuerySubjectiveAnswerListResp();
+		List<QuerySubjectiveAnswerBean> querySubjectiveAnswerBeanList = new ArrayList<QuerySubjectiveAnswerBean>();
+		for(ExamQuestionEntity examQuestionEntity:examQuestionList){
+			QuerySubjectiveAnswerBean querySubjectiveAnswerBean = new QuerySubjectiveAnswerBean();
+			querySubjectiveAnswerBean.setExamRecordDataId(examQuestionEntity.getExamRecordDataId());
+			querySubjectiveAnswerBean.setMainNumber(examQuestionEntity.getMainNumber());
+			querySubjectiveAnswerBean.setOrder(examQuestionEntity.getOrder());
+			querySubjectiveAnswerBean.setQuestionId(examQuestionEntity.getQuestionId());
+			querySubjectiveAnswerBean.setStudentAnswer(examQuestionEntity.getStudentAnswer());
+			querySubjectiveAnswerBean.setAnswerType(examQuestionEntity.getAnswerType());
+			querySubjectiveAnswerBeanList.add(querySubjectiveAnswerBean);
+		}
+		resp.setQuerySubjectiveAnswerBeanList(querySubjectiveAnswerBeanList);
+		return resp;
+	}
+
+	@Override
+	@ApiOperation(value = "阅卷回传主观题得分")
+	@PostMapping("/saveSubjectiveQuestionScore")
+	public void saveSubjectiveQuestionScore(@RequestBody SaveSubjectiveQuestionScoreReq req) {
+		Check.isNull(req.getExamRecordDataId(), "examRecordDataId 不能为空");
+		List<SaveSubjectiveQuestionScoreBean> saveSubjectiveQuestionScoreList = req.getSaveSubjectiveQuestionScoreList();
+		if(saveSubjectiveQuestionScoreList == null || saveSubjectiveQuestionScoreList.size() == 0){
+			return;
+		}
+		long examRecordDataId = req.getExamRecordDataId();
+		List<SubjectiveQuestionScoreInfo> subjectiveQuestionScoreInfoList = new ArrayList<SubjectiveQuestionScoreInfo>();
+		for(SaveSubjectiveQuestionScoreBean bean:saveSubjectiveQuestionScoreList){
+			SubjectiveQuestionScoreInfo info = new SubjectiveQuestionScoreInfo();
+			info.setExamRecordDataId(bean.getExamRecordDataId());
+			info.setOrder(bean.getOrder());
+			info.setQuestionId(bean.getQuestionId());
+			info.setScore(bean.getScore());
+			subjectiveQuestionScoreInfoList.add(info);
+		}
+		examRecordQuestionsService.saveSubjectiveQuestionScore(examRecordDataId, subjectiveQuestionScoreInfoList);
+	}
+
+}

+ 65 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamScoreCloudServiceProvider.java

@@ -0,0 +1,65 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.provider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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;
+
+import cn.com.qmth.examcloud.core.oe.admin.api.ExamScoreCloudService;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.ExamScoreBean;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.QueryExamScoreReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.QueryExamScoreResp;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamScoreEntity;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScoreRepo;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+
+@Api(tags = "考试分数信息相关接口")
+@RestController
+@RequestMapping("${$rmp.cloud.oe}/examScore")
+public class ExamScoreCloudServiceProvider extends ControllerSupport implements ExamScoreCloudService{
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -1957765271029136836L;
+	
+	@Autowired
+	private ExamScoreRepo examScoreRepo;
+	
+	@Override
+	@ApiOperation(value = "分数查询")
+	@PostMapping("/queryExamScore")
+	public QueryExamScoreResp queryExamScore(@RequestBody QueryExamScoreReq req) {
+		List<Long> examRecordDataIds = req.getExamRecordDataIds();
+		
+		if(examRecordDataIds == null || examRecordDataIds.size() == 0){
+			return null;
+		}
+		
+		List<ExamScoreEntity> examScoreList = examScoreRepo.findByExamRecordDataIdIn(req.getExamRecordDataIds());
+		
+		List<ExamScoreBean> examScoreBeans = new ArrayList<ExamScoreBean>();
+		for(ExamScoreEntity examScore:examScoreList){
+			ExamScoreBean examScoreBean = new ExamScoreBean();
+			examScoreBean.setId(examScore.getId());
+			examScoreBean.setExamRecordDataId(examScore.getExamRecordDataId());
+			examScoreBean.setObjectiveAccuracy(examScore.getObjectiveAccuracy());
+			examScoreBean.setObjectiveScore(examScore.getObjectiveScore());
+			examScoreBean.setSubjectiveScore(examScore.getSubjectiveScore());
+			examScoreBean.setSuccPercent(examScore.getSuccPercent());
+			examScoreBean.setTotalScore(examScore.getTotalScore());
+			examScoreBeans.add(examScoreBean);
+		}
+		QueryExamScoreResp queryExamScoreResp = new QueryExamScoreResp();
+		queryExamScoreResp.setExamScoreBeans(examScoreBeans);
+		return queryExamScoreResp;
+	}
+
+}

+ 335 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamScoreDataCloudServiceProvider.java

@@ -0,0 +1,335 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.provider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCacheTransferHelper;
+import org.apache.commons.lang3.StringUtils;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+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 cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.CourseCloudService;
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.basic.api.request.GetCourseReq;
+import cn.com.qmth.examcloud.core.basic.api.response.GetCourseResp;
+import cn.com.qmth.examcloud.core.oe.admin.api.ExamScoreDataCloudService;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.ExamScoreDataBean;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.QueryCapturePhotoBean;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.ScoreDataBean;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.FindExamScoreDataReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.GetAuditDataReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.GetScoreDataReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.QueryCapturePhotoReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.QueryScoreDataReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.FindExamScoreDataResp;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.GetAuditDataResp;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.GetScoreDataResp;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.QueryCapturePhotoResp;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.QueryScoreDataResp;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamAuditEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamCaptureEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamScoreEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.DisciplineType;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamAuditRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamCaptureRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScoreRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamStudentRepo;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.examwork.api.ExamCloudService;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.examwork.api.request.GetExamReq;
+import cn.com.qmth.examcloud.examwork.api.response.GetExamResp;
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
+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;
+
+@Api(tags = "考试分数信息相关接口")
+@RestController
+@RequestMapping("${$rmp.cloud.oe}/examScoreData")
+public class ExamScoreDataCloudServiceProvider extends ControllerSupport implements ExamScoreDataCloudService{
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 8280332746440316604L;
+	
+	@Autowired
+	private ExamScoreRepo examScoreRepo;
+	
+	@Autowired
+	private ExamRecordDataRepo examRecordDataRepo;
+	
+	@Autowired
+	private ExamRecordRepo examRecordRepo;
+	
+	@Autowired
+	private ExamStudentRepo examStudentRepo;
+	
+	@Autowired
+	private ExamAuditRepo examAuditRepo;
+	
+	@Autowired
+	private ExamCaptureRepo examCaptureRepo;
+	
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+    
+    @Autowired
+    private ExamCloudService examCloudService;
+    
+    @Autowired
+    private CourseCloudService courseCloudService;
+    
+	@Override
+	@ApiOperation(value = "根据分数id查询考试记录,学生信息,分数信息等数据")
+	@PostMapping("/findExamScoreDataByScoreId")
+	public FindExamScoreDataResp findExamScoreDataByScoreId(@RequestBody FindExamScoreDataReq findExamScoreDataReq) {
+		long scoreId = findExamScoreDataReq.getScoreId();
+		ExamScoreEntity examScore = GlobalHelper.getEntity(examScoreRepo,scoreId,ExamScoreEntity.class);
+		ExamRecordDataEntity examRecordDataEntity = GlobalHelper.getEntity(examRecordDataRepo,examScore.getExamRecordDataId(),ExamRecordDataEntity.class);
+		ExamRecordEntity examRecord =GlobalHelper.getEntity(examRecordRepo,examScore.getExamRecordDataId(),ExamRecordEntity.class);
+		ExamStudentEntity examStudentEntity = examStudentRepo.findByExamStudentId(examRecord.getExamStudentId());
+		
+		FindExamScoreDataResp findExamScoreDataResp = new FindExamScoreDataResp();
+		List<ExamScoreDataBean> examScoreDatas = new ArrayList<ExamScoreDataBean>();
+		examScoreDatas.add(buildExamScoreDataBean(examScore, examRecordDataEntity, examStudentEntity));
+		findExamScoreDataResp.setExamScoreDatas(examScoreDatas);
+		
+		return findExamScoreDataResp;
+	}
+	
+	@Override
+	@ApiOperation(value = "根据examIdAndStudentCode查询考试记录,学生信息,分数信息等数据")
+	@PostMapping("/findExamScoreDataByExamIdAndStudentCode")
+	public FindExamScoreDataResp findExamScoreDataByExamIdAndStudentCode(@RequestBody FindExamScoreDataReq findExamScoreDataReq) {
+		Long examId = findExamScoreDataReq.getExamId();
+		String studentCode = findExamScoreDataReq.getStudentCode();
+		List<ExamRecordEntity> examRecordList = examRecordRepo.findByExamIdAndStudentCode(examId, studentCode);
+		
+		List<ExamScoreDataBean> examScoreDatas = new ArrayList<ExamScoreDataBean>();
+		for(ExamRecordEntity examRecord:examRecordList){
+			ExamRecordDataEntity examRecordData = examRecordDataRepo.findByExamRecordId(examRecord.getId());
+			ExamScoreEntity examScore = examScoreRepo.findByExamRecordDataId(examRecordData.getId());
+			ExamStudentEntity examStudent = examStudentRepo.findByExamStudentId(examRecord.getExamStudentId());
+			examScoreDatas.add(buildExamScoreDataBean(examScore, examRecordData, examStudent));
+		}
+		
+		FindExamScoreDataResp findExamScoreDataResp = new FindExamScoreDataResp();
+		findExamScoreDataResp.setExamScoreDatas(examScoreDatas);
+		return findExamScoreDataResp;
+	}
+
+	private ExamScoreDataBean buildExamScoreDataBean(ExamScoreEntity examScore,ExamRecordDataEntity examRecordDataEntity,ExamStudentEntity examStudentEntity){
+		ExamScoreDataBean examScoreDataBean = new ExamScoreDataBean();
+		examScoreDataBean.setExamId(examStudentEntity.getExamId());
+		examScoreDataBean.setStudentName(examStudentEntity.getStudentName());
+		examScoreDataBean.setStudentCode(examStudentEntity.getStudentCode());
+		examScoreDataBean.setIdentityNumber(examStudentEntity.getIdentityNumber());
+		
+		CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examStudentEntity.getCourseId());
+		examScoreDataBean.setCourseName(courseBean.getName());
+		examScoreDataBean.setCourseCode(examStudentEntity.getCourseCode());
+		examScoreDataBean.setStartTime(examRecordDataEntity.getStartTime());
+		examScoreDataBean.setEndTime(examRecordDataEntity.getEndTime());
+		examScoreDataBean.setSuccPercent(examRecordDataEntity.getFaceSuccessPercent());
+		examScoreDataBean.setIsIllegality(examRecordDataEntity.getIsIllegality());
+		examScoreDataBean.setScoreId(examScore.getId());
+		examScoreDataBean.setTotalScore(examScore.getTotalScore());
+		examScoreDataBean.setObjectiveScore(examScore.getObjectiveScore());
+		examScoreDataBean.setSubjectiveScore(examScore.getSubjectiveScore());
+		return examScoreDataBean;
+	}
+
+	@Override
+	@ApiOperation(value = "根据examRecordDataId查询成绩数据")
+	@PostMapping("/getScoreData")
+	public GetScoreDataResp getScoreData(@RequestBody GetScoreDataReq req) {
+		Long examRecordDataId = req.getExamRecordDataId();
+		ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo,examRecordDataId,ExamRecordDataEntity.class);
+		if(null ==examRecordData) {
+			throw new StatusException("getScoreData-exception", "请求参数不正确");
+		}
+		ExamRecordEntity examRecord =GlobalHelper.getEntity(examRecordRepo,examRecordData.getExamRecordId(),ExamRecordEntity.class);
+		ExamScoreEntity examScore = examScoreRepo.findByExamRecordDataId(examRecordDataId);
+		
+		GetScoreDataResp resp = new GetScoreDataResp();
+		resp.setExamRecordDataId(examRecordDataId);
+		resp.setStudentCode(examRecord.getStudentCode());
+		resp.setStudentName(examRecord.getStudentName());
+		resp.setIdentityNumber(examRecord.getIdentityNumber());
+		
+		CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examRecord.getCourseId());
+		resp.setCourseCode(courseBean.getCode());
+		resp.setCourseName(courseBean.getName());
+		resp.setStartTime(examRecordData.getStartTime());
+		resp.setEndTime(examRecordData.getEndTime());
+		resp.setTotalScore(examScore.getTotalScore());
+		
+		resp.setIsWarn(examRecordData.getIsWarn());
+		resp.setIsAudit(examRecordData.getIsAudit());
+		resp.setIsIllegality(examRecordData.getIsIllegality());
+		
+		return resp;
+	}
+
+	@Override
+	@ApiOperation(value = "根据examRecordDataId查询成绩审核数据")
+	@PostMapping("/getAuditData")
+	public GetAuditDataResp getAuditData(@RequestBody GetAuditDataReq req) {
+		Check.isEmpty(req.getExamRecordDataId(), "考试记录ID不能为空");
+		ExamAuditEntity examAuditEntity = examAuditRepo.findByExamRecordDataId(req.getExamRecordDataId());
+		//考试记录
+		ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo,req.getExamRecordDataId(),ExamRecordDataEntity.class);
+		if(null == examRecordData) {
+			throw new StatusException("100001", "找不到考试记录id为"+req.getExamRecordDataId()+"数据");
+		}
+		GetAuditDataResp resp = new GetAuditDataResp();
+		if(examAuditEntity!=null){
+			resp.setExamRecordDataId(examAuditEntity.getExamRecordDataId());
+			DisciplineType disciplineType = examAuditEntity.getDisciplineType();
+			if(disciplineType!=null){
+				resp.setDisciplineType(disciplineType.getName());
+			}
+			resp.setDisciplineDetail(examAuditEntity.getDisciplineDetail());
+			resp.setCreationTime(examAuditEntity.getCreationTime());
+			resp.setAuditUserName(examAuditEntity.getAuditUserName());
+			//是否违纪
+			resp.setIsDiscipline(examRecordData.getIsIllegality()?1:0);			 
+		}
+		return resp;
+	}
+
+	@Override
+	@ApiOperation(value = "根据examRecordDataId查询抓拍照片数据")
+	@PostMapping("/queryCapturePhoto")
+	public QueryCapturePhotoResp queryCapturePhoto(@RequestBody QueryCapturePhotoReq req) {
+		Check.isEmpty(req.getExamRecordDataId(), "考试记录ID不能为空");
+		List<ExamCaptureEntity> examCaptureList = examCaptureRepo.findByExamRecordDataId(req.getExamRecordDataId());
+		QueryCapturePhotoResp queryCapturePhotoResp = new QueryCapturePhotoResp();
+		queryCapturePhotoResp.setExamRecordDataId(req.getExamRecordDataId());
+		List<QueryCapturePhotoBean> capturePhotoList = new ArrayList<QueryCapturePhotoBean>();
+		if(examCaptureList.size()>0){
+			for(ExamCaptureEntity examCaptureEntity:examCaptureList){
+				QueryCapturePhotoBean queryCapturePhotoBean = new QueryCapturePhotoBean();
+				queryCapturePhotoBean.setFileUrl(examCaptureEntity.getFileUrl());
+				queryCapturePhotoBean.setIsPass(examCaptureEntity.getIsPass());
+				queryCapturePhotoBean.setIsStranger(examCaptureEntity.getIsStranger());
+				String faceLivenessResult = examCaptureEntity.getFacelivenessResult();
+				queryCapturePhotoBean.setIsLivenessPass(isLivenessPass(faceLivenessResult));
+				capturePhotoList.add(queryCapturePhotoBean);
+			}
+		}
+		queryCapturePhotoResp.setCapturePhotoList(capturePhotoList);
+		return queryCapturePhotoResp;
+	}
+	
+	/**
+	 * 人脸真实性是否通过
+	 * @param faceLivenessResult
+	 * @return
+	 */
+	private Boolean isLivenessPass(String faceLivenessResult){
+		if(StringUtils.isNotBlank(faceLivenessResult)){
+			JSONObject jsonObject;
+			try {
+				jsonObject = new JSONObject(faceLivenessResult);
+				if(jsonObject.has("error_code")&&jsonObject.getInt("error_code") == 0&&jsonObject.has("result")){
+					JSONObject resultJson = jsonObject.getJSONObject("result");
+					if(resultJson.has("face_liveness")){
+						double faceLivenessVal = resultJson.getDouble("face_liveness");
+						double baiduFacelivenessThreshold = Double.parseDouble(PropertyHolder.getString("$baidu.faceliveness.threshold"));
+						if(faceLivenessVal > baiduFacelivenessThreshold){
+							return true;
+						}
+					}
+				}
+			} catch (JSONException e) {
+				e.printStackTrace();
+				return false;
+			}
+			
+		}
+		return false;
+	}
+
+	@Override
+	@ApiOperation(value = "按考试名称,rootOrgId,courseCode,身份证号或学号查询成绩信息")
+	@PostMapping("/queryScoreData")
+	public QueryScoreDataResp queryScoreData(@RequestBody QueryScoreDataReq req) {
+		if(StringUtils.isBlank(req.getExamName())){
+			throw new StatusException("OE-ADMIN-1000005", "考试名称不能为空");
+		}
+		if(StringUtils.isBlank(req.getCourseCode())){
+			throw new StatusException("OE-ADMIN-1000005", "课程code不能为空");
+		}
+		if(req.getRootOrgId() == null){
+			throw new StatusException("OE-ADMIN-1000005", "rootOrgId不能为空");
+		}
+		if(StringUtils.isBlank(req.getIdentityNumber()) && StringUtils.isBlank(req.getStudentCode())){
+			throw new StatusException("OE-ADMIN-1000005", "身份证号和学号不能都为空");
+		}
+		GetExamReq getExamReq = new GetExamReq();
+		getExamReq.setRootOrgId(req.getRootOrgId());
+        getExamReq.setName(req.getExamName());
+        GetExamResp getExamResp = examCloudService.getExam(getExamReq);
+        ExamBean examBean = getExamResp.getExamBean();
+        
+        GetCourseReq getCourseReq = new GetCourseReq();
+        getCourseReq.setRootOrgId(req.getRootOrgId());
+        getCourseReq.setCode(req.getCourseCode());
+        GetCourseResp getCourseResp = courseCloudService.getCourse(getCourseReq);
+        CourseBean courseBean = getCourseResp.getCourseBean();
+        
+        ExamRecordEntity selectCondition = new ExamRecordEntity();
+        selectCondition.setExamId(examBean.getId());
+        selectCondition.setCourseId(courseBean.getId());
+        if(StringUtils.isNotBlank(req.getIdentityNumber())){
+        	selectCondition.setIdentityNumber(req.getIdentityNumber());
+        }
+        if(StringUtils.isNotBlank(req.getStudentCode())){
+        	selectCondition.setStudentCode(req.getStudentCode());
+        }
+        //查询考试记录
+        List<ScoreDataBean> scoreDataBeanList = new ArrayList<ScoreDataBean>();
+        List<ExamRecordEntity> examRecordList = examRecordRepo.findAll(Example.of(selectCondition));
+        for(ExamRecordEntity examRecord:examRecordList){
+        	ExamRecordDataEntity examRecordData = examRecordDataRepo.findByExamRecordId(examRecord.getId());
+        	ExamScoreEntity examScore = examScoreRepo.findByExamRecordDataId(examRecordData.getId());
+        	
+        	ScoreDataBean scoreDataBean = new ScoreDataBean();
+        	scoreDataBean.setExamRecordDataId(examRecordData.getId());
+        	scoreDataBean.setStudentCode(examRecord.getStudentCode());
+        	scoreDataBean.setStudentName(examRecord.getStudentName());
+        	scoreDataBean.setIdentityNumber(examRecord.getIdentityNumber());
+    		
+        	scoreDataBean.setCourseCode(courseBean.getCode());
+        	scoreDataBean.setCourseName(courseBean.getName());
+        	scoreDataBean.setStartTime(examRecordData.getStartTime());
+        	scoreDataBean.setEndTime(examRecordData.getEndTime());
+        	scoreDataBean.setTotalScore(examScore.getTotalScore());
+    		
+        	scoreDataBean.setIsWarn(examRecordData.getIsWarn());
+        	scoreDataBean.setIsAudit(examRecordData.getIsAudit());
+        	scoreDataBean.setIsIllegality(examRecordData.getIsIllegality());
+        	scoreDataBeanList.add(scoreDataBean);
+        }
+        QueryScoreDataResp queryScoreDataResp = new QueryScoreDataResp();
+        queryScoreDataResp.setScoreDataBeanList(scoreDataBeanList);
+		return queryScoreDataResp;
+	}
+}

+ 64 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamScoreObtainQueueCloudServiceProvider.java

@@ -0,0 +1,64 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.provider;
+
+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;
+
+import cn.com.qmth.examcloud.core.oe.admin.api.ExamScoreObtainQueueCloudService;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.DeleteExamScoreQueueReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.GetTopExamScoreQueueReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.GetTopExamScoreQueueResp;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamScoreObtainQueueEntity;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScoreObtainQueueRepo;
+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;
+
+@Api(tags = "考试分数队列接口")
+@RestController
+@RequestMapping("${$rmp.cloud.oe}/examScoreObtainQueue")
+public class ExamScoreObtainQueueCloudServiceProvider extends ControllerSupport implements ExamScoreObtainQueueCloudService{
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 9204539038969246524L;
+	
+	@Autowired
+	private ExamScoreObtainQueueRepo examScoreObtainQueueRepo;
+	
+	@Override
+	@ApiOperation(value = "获取当前机构分数队列顶层记录")
+	@PostMapping("/getTopExamScoreQueue")
+	public GetTopExamScoreQueueResp getTopExamScoreQueue(@RequestBody GetTopExamScoreQueueReq req) {
+		Check.isNull(req.getRootOrgId(), "顶级机构ID不能为空");
+		ExamScoreObtainQueueEntity examScoreObtainQueue = examScoreObtainQueueRepo.findTopByRootOrgIdAndIsValidOrderByIdAsc(req.getRootOrgId(), true);
+		GetTopExamScoreQueueResp getTopExamScoreQueueResp = new GetTopExamScoreQueueResp();
+		getTopExamScoreQueueResp.setIsEmpty(true);
+		if(examScoreObtainQueue!=null){
+			getTopExamScoreQueueResp.setExamId(examScoreObtainQueue.getExamId());
+			getTopExamScoreQueueResp.setQueueId(examScoreObtainQueue.getId());
+			getTopExamScoreQueueResp.setExamRecordDataId(examScoreObtainQueue.getExamRecordDataId());
+			getTopExamScoreQueueResp.setIsEmpty(false);
+		}
+		return getTopExamScoreQueueResp;
+	}
+
+	@Override
+	@ApiOperation(value = "删除分数队列记录")
+	@PostMapping("/deleteExamScoreQueue")
+	public void deleteExamScoreQueue(@RequestBody DeleteExamScoreQueueReq req) {
+		Check.isNull(req.getQueueId(), "队列ID不能为空");
+		Check.isNull(req.getRootOrgId(), "顶级机构ID不能为空");
+		ExamScoreObtainQueueEntity examScoreObtainQueue =GlobalHelper.getEntity(examScoreObtainQueueRepo,req.getQueueId(),ExamScoreObtainQueueEntity.class);
+		if(examScoreObtainQueue!=null && examScoreObtainQueue.getRootOrgId().equals(req.getRootOrgId())){
+			examScoreObtainQueue.setIsValid(false);
+			examScoreObtainQueueRepo.save(examScoreObtainQueue);
+		}
+	}
+
+}

+ 160 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamStudentCloudServiceProvider.java

@@ -0,0 +1,160 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-15 14:15:05.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.api.provider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.oe.admin.api.request.UnbindExamStudentReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.UnbindExamStudentResp;
+import com.mysql.cj.util.StringUtils;
+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;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.oe.admin.api.OeExamStudentCloudService;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.FindStudentType;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.OeExamStudentBean;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.ExamStudentSyncAllDataReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.ExamStudentSyncPartDataReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.FindExamStudentInfoReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.ExamStudentSyncAllDataResp;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.ExamStudentSyncPartDataResp;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.FindExamStudentInfoResp;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamStudentService;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamStudentRepo;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 考生信息接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/15
+ */
+@Api(tags = "考生信息相关接口")
+@RestController
+@RequestMapping("${$rmp.cloud.oe}/exam/student")
+public class ExamStudentCloudServiceProvider extends ControllerSupport implements OeExamStudentCloudService {
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1638873981075522570L;
+
+    @Autowired
+    private ExamStudentService examStudentService;
+
+    @Autowired
+    private ExamStudentRepo examStudentRepo;
+
+    @Autowired
+    private ExamRecordRepo examRecordRepo;
+
+    @Deprecated
+    @ApiOperation(value = "同步考生(全部)数据")
+    @PostMapping("/sync/all/data")
+    public ExamStudentSyncAllDataResp syncExamStudentAllData(@RequestBody ExamStudentSyncAllDataReq req) {
+        /*List<ExamStudentInfo> examStudents = ExamStudentBeanConvert.of(req);
+        examStudentService.syncExamStudentAllData(examStudents);
+        return new ExamStudentSyncAllDataResp();*/
+        return null;
+    }
+
+    @Deprecated
+    @ApiOperation(value = "同步考生(部分)数据")
+    @PostMapping("/sync/part/data")
+    public ExamStudentSyncPartDataResp syncExamStudentPartData(@RequestBody ExamStudentSyncPartDataReq req) {
+        /*List<ExamStudentPartInfo> examStudents = ExamStudentBeanConvert.of(req);
+        examStudentService.syncExamStudentPartData(examStudents);
+        return new ExamStudentSyncPartDataResp();*/
+        return null;
+    }
+
+    @Override
+    @ApiOperation(value = "根据条件查询考生")
+    @PostMapping("/findExamStudentInfoBy")
+    public FindExamStudentInfoResp findExamStudentInfoBy(@RequestBody FindExamStudentInfoReq req) {
+        FindStudentType findStudentType = req.getFindStudentType();
+        if (findStudentType == null) {
+            throw new StatusException("ExamStudentCloudServiceProvider-findExamStudentInfoBy-exception", "查询方式不能为空");
+        }
+        FindExamStudentInfoResp resp = new FindExamStudentInfoResp();
+
+        if (findStudentType == FindStudentType.BY_EXAMID) {
+            Long examId = req.getFindStudentBean().getExamId();
+            if (examId != null) {
+                List<ExamStudentEntity> examStudents = examStudentRepo.findByExamId(examId);
+                resp.setExamStudents(buildExamStudentBeans(examStudents));
+            }
+        } else if (findStudentType == FindStudentType.BY_EXAMID_AND_IDENTITYNUMBERS) {
+            Long examId = req.getFindStudentBean().getExamId();
+            List<String> identityNumbes = req.getFindStudentBean().getIdentityNumbers();
+            if (examId != null && identityNumbes != null && identityNumbes.size() > 0) {
+                List<ExamStudentEntity> examStudents = examStudentRepo.findByExamIdAndIdentityNumberIn(examId, identityNumbes);
+                resp.setExamStudents(buildExamStudentBeans(examStudents));
+            }
+        } else if (findStudentType == FindStudentType.BY_IDENTITYNUMBERS_AND_ROOTORGID) {
+            Long rootOrgId = req.getFindStudentBean().getRootOrgId();
+            List<String> identityNumbes = req.getFindStudentBean().getIdentityNumbers();
+            if (rootOrgId != null && identityNumbes != null && identityNumbes.size() > 0) {
+                List<ExamStudentEntity> examStudents = examStudentRepo.findByRootOrgIdAndIdentityNumberIn(rootOrgId, identityNumbes);
+                resp.setExamStudents(buildExamStudentBeans(examStudents));
+            }
+        } else if (findStudentType == FindStudentType.BY_EXAMID_AND_STUDENTCODES) {
+            Long examId = req.getFindStudentBean().getExamId();
+            List<String> studentCodes = req.getFindStudentBean().getStudentCodes();
+            if (examId != null && studentCodes != null && studentCodes.size() > 0) {
+                List<ExamStudentEntity> examStudents = examStudentRepo.findByExamIdAndStudentCodeIn(examId, studentCodes);
+                resp.setExamStudents(buildExamStudentBeans(examStudents));
+            }
+        }
+        return resp;
+    }
+
+    @Override
+    @ApiOperation(value = "解绑考生学号")
+    @PostMapping("/unbindExamStudent")
+    public UnbindExamStudentResp unbindExamStudent(UnbindExamStudentReq req) {
+        UnbindExamStudentResp resp = new UnbindExamStudentResp();
+        if (StringUtils.isNullOrEmpty(req.getIdentityNumber())) {
+            resp.setSuccess(false);
+            resp.setMsg("身份证号不允许为空");
+            return resp;
+        }
+        if (null == req.getStudentCodeList() || req.getStudentCodeList().isEmpty()) {
+            resp.setSuccess(false);
+            resp.setMsg("学号不允许为空");
+            return resp;
+        }
+        examStudentRepo.updateExamStudentCode(req.getIdentityNumber(), req.getStudentCodeList());
+        resp.setSuccess(true);
+        return resp;
+    }
+
+    private List<OeExamStudentBean> buildExamStudentBeans(List<ExamStudentEntity> examStudents) {
+        List<OeExamStudentBean> examStudentBeans = new ArrayList<OeExamStudentBean>();
+        for (ExamStudentEntity examStudent : examStudents) {
+            OeExamStudentBean examStudentBean = new OeExamStudentBean();
+            examStudentBean.setExamStudentId(examStudent.getExamStudentId());
+            examStudentBean.setExamId(examStudent.getExamId());
+            examStudentBean.setStudentCode(examStudent.getStudentCode());
+            examStudentBean.setStudentName(examStudent.getStudentName());
+            examStudentBean.setIdentityNumber(examStudent.getIdentityNumber());
+            examStudentBean.setCourseCode(examStudent.getCourseCode());
+            examStudentBean.setFinished(examStudent.getFinished());
+            examStudentBeans.add(examStudentBean);
+        }
+        return examStudentBeans;
+    }
+}

+ 176 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/ExamStudentDataCloudServiceProvider.java

@@ -0,0 +1,176 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.provider;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.oe.admin.api.ExamStudentDataCloudService;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.ExamStudentDataBean;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.ExamStudentScoreDataBean;
+import cn.com.qmth.examcloud.core.oe.admin.api.request.GetExamStudentDataReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.GetExamStudentDataResp;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamStudentService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.ExamStudentEffectiveScoreInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentInfo;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamQuestionEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordQuestionsEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamProperties;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordQuestionsRepo;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
+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;
+
+import java.util.Collections;
+import java.util.DoubleSummaryStatistics;
+import java.util.List;
+
+import java.util.ArrayList;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @Description 获取考生考试相关数据接口
+ * @Author lideyin
+ * @Date 2019/7/18 11:10
+ * @Version 1.0
+ */
+@Api(tags = "获取考生考试相关数据接口")
+@RestController
+@RequestMapping("${$rmp.cloud.oe}/examStudentData")
+public class ExamStudentDataCloudServiceProvider extends ControllerSupport implements ExamStudentDataCloudService {
+    @Autowired
+    ExamRecordQuestionsRepo examRecordQuestionsRepo;
+    @Autowired
+    private ExamStudentService examStudentService;
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+    @Autowired
+    private ExamRecordService examRecordService;
+
+    @Override
+    @ApiOperation(value = "获取考生分数相关数据")
+    @PostMapping("/getExamStudentData")
+    public GetExamStudentDataResp getExamStudentData(@RequestBody GetExamStudentDataReq req) {
+        validateRequestData(req);
+        Long nextId = req.getStartId();
+
+        GetExamStudentDataResp resp = new GetExamStudentDataResp();
+        List<ExamStudentInfo> examStudentList = examStudentService.getLimitExamStudentList(req.getExamId(), req.getStartId(), req.getSize());
+        if (examStudentList == null || examStudentList.isEmpty()) {
+            resp.setNextId(nextId);
+            return resp;
+        }
+
+        nextId = examStudentList.get(examStudentList.size() - 1).getId();
+        nextId++;
+        resp.setNextId(nextId);
+
+        List<Long> examStudentIdList = examStudentList.stream().map(p -> p.getExamStudentId()).collect(Collectors.toList());
+        //阅卷方式
+        String markingType = gainBaseDataService.getExamProperty(req.getExamId(), ExamProperties.MARKING_TYPE.name());
+        List<ExamStudentEffectiveScoreInfo> effectiveScoreList = examRecordService.getExamStudentEffectiveScoreList(markingType, examStudentIdList);
+        List<ExamRecordQuestionsEntity> questionList = null;
+        if (effectiveScoreList != null && !effectiveScoreList.isEmpty()) {
+            List<Long> examRecordDataIdList = effectiveScoreList.stream().map(p -> p.getExamRecordDataId()).collect(Collectors.toList());
+            questionList = examRecordQuestionsRepo.findByExamRecordDataIdIn(examRecordDataIdList);
+            //错误数据的特殊处理
+            String strErrorExamRecordDataIds="";
+            List<Long> errorExamRecordDataIdList;
+            if (questionList == null || questionList.isEmpty()) {
+                errorExamRecordDataIdList=effectiveScoreList.stream().map(p->p.getExamRecordDataId()).collect(Collectors.toList());
+                for (Long eid:errorExamRecordDataIdList){
+                    strErrorExamRecordDataIds+=eid +",";
+                }
+                log.error("[GETEXAMSTUDENTDATA],200105考试作答数据异常,错误的考试记录id:"+strErrorExamRecordDataIds);
+                throw new StatusException("200105", "考试作答数据异常,相关考试记录id为:"+strErrorExamRecordDataIds);
+            }
+            if (questionList.size() != effectiveScoreList.size()){
+                List<Long> finalExamRecordDataIdList = questionList.stream().map(p->p.getExamRecordDataId()).collect(Collectors.toList());
+                errorExamRecordDataIdList=effectiveScoreList.stream().filter(p-> !finalExamRecordDataIdList.contains(p.getExamRecordDataId())).map(p->p.getExamRecordDataId()).collect(Collectors.toList());
+                for (Long eid:errorExamRecordDataIdList){
+                    strErrorExamRecordDataIds+=eid +",";
+                }
+                log.error("[GETEXAMSTUDENTDATA],200106考试作答数据异常,错误的考试记录id:"+strErrorExamRecordDataIds);
+                throw new StatusException("200106", "考试作答数据异常,相关考试记录id为:"+strErrorExamRecordDataIds);
+            }
+        }
+        List<ExamStudentDataBean> examStudentDataBeanList = new ArrayList<>();
+        for (ExamStudentInfo es : examStudentList) {
+            ExamStudentDataBean esBean = new ExamStudentDataBean();
+            ExamStudentEffectiveScoreInfo score = null;
+
+            esBean.setExamStudentId(es.getExamStudentId());
+            esBean.setExamId(es.getExamId());
+            esBean.setCourseCode(es.getCourseCode());
+            esBean.setCourseId(es.getCourseId());
+            esBean.setAbsent(!es.getFinished());
+            esBean.setRootOrgId(es.getRootOrgId());
+            esBean.setOrgId(es.getOrgId());
+
+            if (effectiveScoreList != null && !effectiveScoreList.isEmpty()) {
+                List<ExamStudentEffectiveScoreInfo> scoreList = effectiveScoreList.stream().
+                        filter(p -> p.getExamStudentId().equals(es.getExamStudentId())).collect(Collectors.toList());
+                if (scoreList != null && !scoreList.isEmpty()) {
+                    score = scoreList.get(0);
+                }
+                if (score != null) {
+                    esBean.setBasePaperId(score.getBasePaperId());
+                    //考生总得分
+                    esBean.setScore(score.getScore());
+
+                    ExamStudentEffectiveScoreInfo finalScore = score;
+                    //试卷总分
+                    List<ExamRecordQuestionsEntity> curQuestionList = questionList.stream().
+                            filter(p -> p.getExamRecordDataId().equals(finalScore.getExamRecordDataId())).collect(Collectors.toList());
+//                    if (curQuestionList==null || curQuestionList.isEmpty()){
+//                        //正常的数据不会出现这种情况con,暂时先记日志
+//                        log.error("[GETEXAMSTUDENTDATA]200106考试作答记录数据异常,examRecordDataId:"+finalScore.getExamRecordDataId());
+////                        throw new StatusException("200106","考试作答记录数据异常");
+//                        continue;
+//                    }
+                    ExamRecordQuestionsEntity examRecordQuestionsEntity =curQuestionList.get(0);
+                    List<ExamQuestionEntity> examQuestionList = examRecordQuestionsEntity.getExamQuestionEntities();
+                    DoubleSummaryStatistics questionScoreStatistics = examQuestionList.stream().
+                            collect(Collectors.summarizingDouble(ExamQuestionEntity::getQuestionScore));
+
+                    esBean.setTotalScore(questionScoreStatistics.getSum());
+                    List<ExamStudentScoreDataBean> examStudentScoreDetailList = new ArrayList<>();
+                    for (ExamQuestionEntity eq : examQuestionList) {
+                        ExamStudentScoreDataBean essBean = new ExamStudentScoreDataBean();
+                        essBean.setQuestionId(eq.getQuestionId());
+                        essBean.setQuestionOrder(eq.getOrder());
+                        essBean.setScore(eq.getStudentScore());
+                        essBean.setTotalScore(eq.getQuestionScore());
+                        examStudentScoreDetailList.add(essBean);
+                    }
+                    esBean.setScoreDetails(examStudentScoreDetailList);
+                }
+            }
+            examStudentDataBeanList.add(esBean);
+        }
+        resp.setList(examStudentDataBeanList);
+
+        return resp;
+    }
+
+    private void validateRequestData(GetExamStudentDataReq req) {
+        if (req.getStartId() == null) {
+            throw new StatusException("200101", "起始考生id不允许为空");
+        }
+        if (req.getExamId() == null) {
+            throw new StatusException("200102", "考试id不允许为空");
+        }
+        if (req.getSize() == null) {
+            throw new StatusException("200103", "数据条数不允许为空");
+        }
+        Integer maxSize = PropertyHolder.getInt("exam.oe.getExamStudentData.maxSize", 200);
+        if (req.getSize() > maxSize) {
+            throw new StatusException("200104", "该接口一次最多只能返回" + maxSize + "条数据");
+        }
+    }
+}

+ 39 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/OeAdminScorePushCloudServiceProvider.java

@@ -0,0 +1,39 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.provider;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.oe.admin.api.OeAdminScorePushCloudService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScoreQueueService;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScorePushQueueRepo;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+
+
+@Api(tags = "推送成绩接口")
+@RestController
+@RequestMapping("${$rmp.cloud.oe}/oeAdminScorePush")
+public class OeAdminScorePushCloudServiceProvider extends ControllerSupport implements OeAdminScorePushCloudService{
+
+	private static final long serialVersionUID = -113490665725775879L;
+
+	@Autowired
+	private ExamScoreQueueService examScoreQueueService;
+	
+	@Autowired
+	private ExamScorePushQueueRepo examScorePushQueueRepo;
+	
+	@Autowired
+	private ExamRecordDataRepo examRecordDataRepo;
+	
+	@Override
+	@PostMapping("/disposeScoreQueue")
+	public void disposeScoreQueue() {
+		examScoreQueueService.disposeScoreQueue();
+	}
+
+}

+ 147 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/provider/SyncCloudServiceProvider.java

@@ -0,0 +1,147 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.provider;
+
+import cn.com.qmth.examcloud.core.oe.admin.api.request.UnbindExamStudentReq;
+import cn.com.qmth.examcloud.core.oe.admin.api.response.UnbindExamStudentResp;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+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 cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.oe.admin.api.bean.ExamStudentBeanConvert;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamStudentService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentInfo;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.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.Api;
+import io.swagger.annotations.ApiOperation;
+
+import java.util.List;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年10月29日 下午4:39:24
+ * @company 	QMTH
+ * @description 数据同步相关接口
+ */
+@Api(tags = "数据同步相关接口")
+@RestController
+@RequestMapping("${$rmp.cloud.oe}/exam/dataSync")
+public class SyncCloudServiceProvider  extends ControllerSupport implements HandleSyncCloudService {
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -4093980356473146740L;
+
+	@Autowired
+    private ExamStudentService examStudentService;
+	
+    @Autowired
+    private ExamStudentRepo examStudentRepo;
+    
+    @Autowired
+    private ExamRecordRepo examRecordRepo;
+
+	@ApiOperation(value = "同步考生")
+    @PostMapping("/syncExamStudent")
+    public SyncExamStudentResp syncExamStudent(@RequestBody SyncExamStudentReq req) {
+    	if("deleteByExamId".equals(req.getSyncType())){
+    		long examId = req.getExamId();
+    		ExamRecordEntity examRecordEntity = new ExamRecordEntity();
+    		examRecordEntity.setExamId(examId);
+    		long examRecordNum = examRecordRepo.count(Example.of(examRecordEntity));
+    		if(examRecordNum > 0){
+    			throw new StatusException("syncExamStudent-exception", "该考试已开始,不能删除考生");
+    		}
+    		examStudentRepo.deleteByExamId(examId);
+    	}else if("delete".equals(req.getSyncType())){
+    		ExamStudentEntity examStudent = examStudentRepo.findByExamStudentId(req.getId());
+    		if(examStudent != null && (examStudent.getFinished() == null || !examStudent.getFinished())){
+    			examStudentRepo.delete(examStudent);
+    		}
+    	}else if("update".equals(req.getSyncType())){
+	        ExamStudentInfo examStudent = ExamStudentBeanConvert.of(req);
+	        examStudentService.syncExamStudentAllData(Lists.newArrayList(examStudent));
+    	}
+    	return null;
+    }
+
+    @ApiOperation(value = "同步课程")
+    @PostMapping("/syncCourse")
+    public SyncCourseResp syncCourse(@RequestBody SyncCourseReq req) {
+    	Long courseId = req.getId();
+    	String courseLevel = req.getLevel();
+    	examStudentRepo.updateCourse(courseId, courseLevel);
+    	examRecordRepo.updateCourse(courseId, courseLevel);
+        return null;
+    }
+
+    @ApiOperation(value = "同步学生")
+    @PostMapping("/syncStudent")
+    public SyncStudentResp syncStudent(@RequestBody SyncStudentReq req) {
+    	//考生表和考试记录表
+    	long studentId = req.getId();
+    	String studentName = req.getName();
+
+//		examStudentRepo.updateStudentNameAndStudentCode(studentId, studentName, studentCode);
+//    	examRecordRepo.updateStudentNameAndStudentCode(studentId, studentName, studentCode);
+		examStudentRepo.updateStudentName(studentId,studentName);
+		examRecordRepo.updateStudentName(studentId, studentName);
+		//如果有需要解绑的学号信息,需要对学号进行解绑
+		if (null != req.getUnboundStudentCodeList() && !req.getUnboundStudentCodeList().isEmpty()) {
+			examStudentRepo.updateExamStudentCode(req.getIdentityNumber(), req.getUnboundStudentCodeList());
+		}
+    	return null;
+    }
+
+    @ApiOperation(value = "同步机构")
+    @PostMapping("/syncOrg")
+    public SyncOrgResp syncOrg(@RequestBody SyncOrgReq req) {
+        return null;
+    }
+    
+    @ApiOperation(value = "同步专业")
+    @PostMapping("syncSpecialty")
+    public SyncSpecialtyResp syncSpecialty(@RequestBody SyncSpecialtyReq req) {
+        return null;
+    }
+
+    @ApiOperation(value = "同步考试")
+    @PostMapping("syncExam")
+    public SyncExamResp syncExam(@RequestBody SyncExamReq req) {
+        return null;
+    }
+
+	@Override
+	public SyncUserResp syncUser(SyncUserReq req) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	
+	
+}

+ 72 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamAuditService.java

@@ -0,0 +1,72 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-27 16:20:17.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import java.util.List;
+
+import org.springframework.data.domain.Page;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit.ExamAuditInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit.ExamAuditQuery;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit.RedoAuditInfo;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamAuditEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.DisciplineType;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+
+/**
+ * 考试记录审核相关接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/15
+ */
+public interface ExamAuditService {
+
+    /**
+     * 查询监考已审列表(分页)
+     *
+     * @param query
+     * @return
+     */
+    Page<ExamAuditInfo> getExamAuditList(ExamAuditQuery query);
+
+
+    /**
+     * 获取审核信息
+     *
+     * @param examRecordDataId
+     * @return
+     */
+    ExamAuditEntity getExamAuditByExamRecordDataId(Long examRecordDataId);
+    
+    /**
+     * 监考待审-批量审核
+     *
+     * @param examRecordIds
+     * @param isPass
+     * @param disciplineDetail
+     * @param userId
+     */
+    void batchAudit(List<Long> examRecordDataIds, Boolean isPass,User user);
+
+    /**
+     * 单个审核
+     * @param examRecordDataId
+     * @param isPass
+     * @param disciplineDetail
+     * @param disciplineType
+     */
+	void singleAudit(Long examRecordDataId, Boolean isPass,String disciplineDetail, DisciplineType disciplineType,User user);
+
+	/**
+	 * 重新审核
+	 * @param redoAuditInfo
+	 * @param user
+	 */
+	public void redoAudit(RedoAuditInfo redoAuditInfo,User user);
+
+}

+ 100 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamCacheTransferHelper.java

@@ -0,0 +1,100 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-29 10:44:05.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.CourseCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamOrgSettingsCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamSettingsCacheBean;
+
+/**
+ * @Description 网考缓存实体转换服务
+ * @Author lideyin
+ * @Date 2019/8/13 16:00
+ * @Version 1.0
+ */
+public class ExamCacheTransferHelper {
+
+    /**
+     * 获取缓存的考试信息
+     * @param examId 考试id
+     * @param orgId 组织机构id
+     * @return ExamBean
+     */
+    public static ExamBean getCachedExam(Long examId,Long orgId){
+        ExamOrgSettingsCacheBean examCacheBean = CacheHelper.getExamOrgSettings(examId,orgId);
+        return copyExamBeanFrom(examCacheBean);
+    }
+    /**
+     * 获取缓存的考试信息
+     * @param examId 考试id
+     * @return ExamBean
+     */
+    public static ExamBean getCachedExam(Long examId){
+        ExamSettingsCacheBean examCacheBean = CacheHelper.getExamSettings(examId);
+        return copyExamBeanFrom(examCacheBean);
+    }
+
+    /**
+     * 获取能在的课程
+     * @param courseId 课程id
+     * @return CourseBean
+     */
+    public static CourseBean getCachedCourse(Long courseId){
+        CourseCacheBean courseCacheBean = CacheHelper.getCourse(courseId);
+        return copyCourseBeanFrom(courseCacheBean);
+    }
+
+    private static ExamBean copyExamBeanFrom(ExamOrgSettingsCacheBean examCacheBean) {
+        ExamBean resultBean = new ExamBean();
+        resultBean.setId(examCacheBean.getId());
+        resultBean.setBeginTime(examCacheBean.getBeginTime());
+        resultBean.setCode(examCacheBean.getCode());
+        resultBean.setDuration(examCacheBean.getDuration());
+        resultBean.setEnable(examCacheBean.getEnable());
+        resultBean.setEndTime(examCacheBean.getEndTime());
+        resultBean.setExamLimit(examCacheBean.getExamLimit());
+        resultBean.setExamTimes(examCacheBean.getExamTimes());
+        resultBean.setExamType(examCacheBean.getExamType());
+        resultBean.setName(examCacheBean.getName());
+        resultBean.setRemark(examCacheBean.getRemark());
+        resultBean.setRootOrgId(examCacheBean.getRootOrgId());
+        return resultBean;
+    }
+
+    private static ExamBean copyExamBeanFrom(ExamSettingsCacheBean examCacheBean) {
+        ExamBean resultBean = new ExamBean();
+        resultBean.setId(examCacheBean.getId());
+        resultBean.setBeginTime(examCacheBean.getBeginTime());
+        resultBean.setCode(examCacheBean.getCode());
+        resultBean.setDuration(examCacheBean.getDuration());
+        resultBean.setEnable(examCacheBean.getEnable());
+        resultBean.setEndTime(examCacheBean.getEndTime());
+        resultBean.setExamLimit(examCacheBean.getExamLimit());
+        resultBean.setExamTimes(examCacheBean.getExamTimes());
+        resultBean.setExamType(examCacheBean.getExamType());
+        resultBean.setName(examCacheBean.getName());
+        resultBean.setRemark(examCacheBean.getRemark());
+        resultBean.setRootOrgId(examCacheBean.getRootOrgId());
+        return resultBean;
+    }
+
+    private static CourseBean copyCourseBeanFrom(CourseCacheBean courseCacheBean) {
+        CourseBean resultBean = new CourseBean();
+        resultBean.setCode(courseCacheBean.getCode());
+        resultBean.setEnable(courseCacheBean.getEnable());
+        resultBean.setId(courseCacheBean.getId());
+        resultBean.setLevel(courseCacheBean.getLevel());
+        resultBean.setName(courseCacheBean.getName());
+        resultBean.setRootOrgId(courseCacheBean.getRootOrgId());
+
+        return resultBean;
+    }
+}

+ 39 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamCaptureService.java

@@ -0,0 +1,39 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 17:23:46.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examcapture.ExamCaptureAuditInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examcapture.ExamCaptureInfo;
+
+import java.util.List;
+
+/**
+ * 考试抓拍检测相关接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/31
+ */
+public interface ExamCaptureService {
+
+    /**
+     * 抓拍详情-审核详细信息
+     *
+     * @param examRecordId
+     * @return
+     */
+    ExamCaptureAuditInfo getExamCaptureAuditDetail(Long examRecordDataId);
+
+    /**
+     * 抓拍详情-抓拍记录列表
+     *
+     * @param examRecordId
+     * @return
+     */
+    List<ExamCaptureInfo> getExamCaptureList(Long examRecordDataId);
+
+}

+ 33 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordForMarkingService.java

@@ -0,0 +1,33 @@
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordForMarkingEntity;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月17日 下午3:57:58
+ * @company 	QMTH
+ * @description ExamRecordForMarkingService.java
+ */
+public interface ExamRecordForMarkingService {
+
+	/**
+	 * 获取 有效考试记录
+	 */
+	public List<ExamRecordForMarkingEntity> queryValidExamRecordInfo(Long examId,Long courseId);
+	
+	/**
+	 * 根据条件查询阅卷需要的信息
+	 * @param id
+	 * @param examId
+	 * @param courseCode
+	 * @return
+	 */
+	public List<ExamRecordForMarkingEntity> findExamRecordForMarkingInfo(Long id,Long examId,Long courseId);
+
+	List<ExamRecordForMarkingEntity> queryValidExamRecordInfoByStuIds(Long examId, Long courseId,
+			List<Long> examStudentIds);
+	
+}

+ 14 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordPaperStructService.java

@@ -0,0 +1,14 @@
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordPaperStructEntity;
+
+public interface ExamRecordPaperStructService {
+
+	/**
+	 * 根据examRecordDataId查询考试记录试卷结构
+	 * @param examRecordDataId
+	 * @return
+	 */
+	public ExamRecordPaperStructEntity getExamRecordPaperStruct(Long examRecordDataId);
+	
+}

+ 23 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordQuestionsService.java

@@ -0,0 +1,23 @@
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.SubjectiveQuestionScoreInfo;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamQuestionEntity;
+
+public interface ExamRecordQuestionsService {
+
+	/**
+	 * 获取考生主观题答案
+	 * @param examRecordDataId	考试记录ID
+	 * @return
+	 */
+	public List<ExamQuestionEntity> querySubjectiveAnswerList(Long examRecordDataId);
+	
+	/**
+	 * 保存主观题得分
+	 * @param subjectiveQuestionScoreInfoList
+	 */
+	public void saveSubjectiveQuestionScore(Long examRecordDataId,List<SubjectiveQuestionScoreInfo> subjectiveQuestionScoreInfoList);
+	
+}

+ 104 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordService.java

@@ -0,0 +1,104 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-23 16:34:26.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.ExamStudentEffectiveScoreInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamRecordInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamRecordQuery;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamStudentQuestionScoreInfo;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+
+import org.springframework.data.domain.Page;
+
+import java.util.List;
+
+/**
+ * 考试记录接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/15
+ */
+public interface ExamRecordService {
+
+    /**
+     * 查询考试记录列表(分页)
+     *
+     * @param query
+     * @return
+     */
+    Page<ExamRecordInfo> getExamRecordList(ExamRecordQuery query);
+
+    /**
+     * 查询“考试明细”列表(分页)
+     *
+     * @param query
+     * @return
+     */
+    Page<ExamRecordInfo> getExamRecordDetailListForPage(ExamRecordQuery query);
+
+    /**
+     * 查询“监考待审”列表(分页)
+     *
+     * @param query
+     * @return
+     */
+    Page<ExamRecordInfo> getExamRecordWaitingAuditList(ExamRecordQuery query);
+
+    /**
+     * 获取正在考试的考生ID列表
+     *
+     * @param examId
+     * @return
+     */
+    List<Long> getExamRecordingStudentIds(Long examId);
+
+    /**
+     * 获取某个考生的考试记录列表
+     *
+     * @param examStudentId
+     * @return
+     */
+    List<ExamRecordDataEntity> getExamRecordListByExamStudentId(Long examStudentId);
+
+    /**
+     * 获取考试记录详细数据ID
+     *
+     * @param examRecordId
+     * @return
+     */
+    Long findExamRecordDataId(Long examRecordId);
+    /**
+     * 查询“考试明细” 是否存在待审数据
+     * @param query
+     * @return
+     */
+	Long existsWarnExamRecordDetail(ExamRecordQuery query);
+	
+	/**
+	 * 根据条件查询 “考试明细”
+	 * @param newQuery
+	 * @return
+	 */
+	List<ExamRecordInfo> getExamRecordDetailList(ExamRecordQuery newQuery);
+
+	/**
+	 * 根据考试id和课程代码获取学生作答成绩
+	 * @param examId
+	 * @param courseCode
+	 * @return
+	 */
+	List<ExamStudentQuestionScoreInfo> getExamStudentQuestionScoreList(Long examId, String courseCode);
+
+    /**
+     * 获取考生的最终有效成绩
+     * @param markingType
+     * @param examStudentIdList
+     * @return
+     */
+    List<ExamStudentEffectiveScoreInfo> getExamStudentEffectiveScoreList(String markingType, List<Long> examStudentIdList);
+}

+ 23 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamScorePushService.java

@@ -0,0 +1,23 @@
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamScorePushQueue;
+
+
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月8日 上午11:16:50
+ * @company 	QMTH
+ * @description 推送分数接口
+ */
+public interface ExamScorePushService {
+
+	/**
+	 * 机构推送成绩逻辑
+	 * @param examScorePushQueue
+	 * @throws Exception
+	 */
+	public void scorePush(ExamScorePushQueue examScorePushQueue);
+	
+}

+ 29 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamScoreQueueService.java

@@ -0,0 +1,29 @@
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamScorePushQueue;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamScoreQueueStatus;
+
+
+
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月8日 上午10:45:36
+ * @company 	QMTH
+ * @description 分数推送队列服务接口 
+ */
+public interface ExamScoreQueueService {
+	
+	/**
+	 * 处理推分队列
+	 */
+	public void disposeScoreQueue();
+	
+	/**
+	 * 
+	 * @param errorMsg
+	 * @param examScorePushQueue
+	 */
+	public void saveExamScorePushQueue(String returnMsg,ExamScorePushQueue examScorePushQueue,ExamScoreQueueStatus examScoreQueueStatus);
+}

+ 29 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamScoreService.java

@@ -0,0 +1,29 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 09:28:53.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore.ExamScoreInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore.ExamScoreQuery;
+
+import org.springframework.data.domain.Page;
+
+/**
+ * 考试分数相关接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/31
+ */
+public interface ExamScoreService {
+
+    Page<ExamScoreInfo> getExamScoreList(ExamScoreQuery query);
+
+    List<ExamScoreInfo> exportExamScoreList(ExamScoreQuery query);
+    
+}

+ 44 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamService.java

@@ -0,0 +1,44 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-29 10:44:05.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+
+/**
+ * 考试相关接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/29
+ */
+public interface ExamService {
+
+    /**
+     * 获取考试信息
+     *
+     * @param examId
+     * @return
+     */
+    ExamBean getExam(Long examId);
+
+    /**
+     * 获取考试名称
+     *
+     * @param examId
+     * @return
+     */
+    String getExamNameById(Long examId);
+
+    /**
+     * 获取考试的默认次数
+     *
+     * @param examId
+     * @return
+     */
+    Long getExamTimesById(Long examId);
+
+}

+ 120 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamStudentService.java

@@ -0,0 +1,120 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-15 14:15:19.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.*;
+
+import org.springframework.data.domain.Page;
+
+import java.util.List;
+
+/**
+ * 考生信息接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/15
+ */
+public interface ExamStudentService {
+
+    /**
+     * 同步考生(全部)数据
+     *
+     * @param examStudents
+     * @return
+     */
+    void syncExamStudentAllData(List<ExamStudentInfo> examStudents);
+
+    /**
+     * 同步考生(部分)数据
+     *
+     * @param examStudents
+     * @return
+     */
+    void syncExamStudentPartData(List<ExamStudentPartInfo> examStudents);
+
+    /**
+     * 同步清理考生数据
+     *
+     * @param examId
+     * @return
+     */
+    void syncRemoveExamStudentByExamId(Long examId);
+
+    /**
+     * 查询考生列表(分页)
+     *
+     * @param query
+     * @return
+     */
+    Page<ExamStudentInfo> getExamStudentListPage(ExamStudentQuery query);
+    
+    
+    public List<ExamStudentInfo> getExamStudentInfoList(ExamStudentQuery query);
+    
+    /**
+     * 查询重考考生列表(分页)
+     *
+     * @param query
+     * @return
+     */
+    Page<ExamStudentInfo> getReExamineStudentList(ExamStudentQuery query);
+
+    /**
+     * 考试概况-按考试完成进度统计
+     *
+     * @param examId
+     * @return
+     */
+    ExamStudentFinishedStatistic getExamStudentStatisticByFinished(Long examId);
+
+    /**
+     * 考试概况-按学习中心统计
+     *
+     * @param examId
+     * @param orgId
+     * @return
+     */
+    List<ExamStudentOrgStatistic> getExamStudentStatisticByOrg(Long examId, Long orgId);
+
+    /**
+     * 获取考生信息
+     *
+     * @param examStudentId
+     * @return
+     */
+    ExamStudentInfo getExamStudentInfo(Long examStudentId);
+    /**
+     * 查询课程信息
+     * @param examId
+     * @param orgId
+     * @return
+     */
+	List<Long> findCoursesFromExamStudent(Long examId, Long orgId);
+
+	/**
+	 * 设置重考
+	 * @param examStudentId
+	 */
+	public void setReexamine(Long examStudentId);
+	/**
+	 * 课程进度查询
+	 * @param examId
+	 * @param courseId
+	 */
+	public List<CourseProgressInfo> queryCourseProgressInfos(Long examId, Long courseId,String orderColumn);
+
+	/**
+	 * @Author lideyin
+	 * @Description 获取指定数量的考生数据
+	 * @Date 2019/7/18 14:36
+	 * @Param [ examId, startId, size]
+	 * @return java.util.List<cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentInfo>
+	 */
+	public List<ExamStudentInfo> getLimitExamStudentList(Long examId,Long startId,Integer size);
+
+}

+ 208 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/ExamRecordQuestionsInfo.java

@@ -0,0 +1,208 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-09-11 16:59:23.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean;
+
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.oe.common.base.utils.NewQuestionType;
+
+/**
+ * 考试作答试题集合
+ *
+ * @author: fengdesheng
+ * @since: 2018/9/11
+ */
+public class ExamRecordQuestionsInfo implements Serializable {
+    private static final long serialVersionUID = -1688201571728312142L;
+    private String id;
+    private Long examRecordDataId;
+    private Date creationTime;
+    private List<ExamQuestionEntity> examQuestionEntities;
+
+    public void addExamQuestion(ExamQuestionEntity examQuestion) {
+        if (examQuestionEntities == null) {
+            examQuestionEntities = new ArrayList<>();
+        }
+        examQuestionEntities.add(examQuestion);
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Long getExamRecordDataId() {
+        return examRecordDataId;
+    }
+
+    public void setExamRecordDataId(Long examRecordDataId) {
+        this.examRecordDataId = examRecordDataId;
+    }
+
+    public Date getCreationTime() {
+        return creationTime;
+    }
+
+    public void setCreationTime(Date creationTime) {
+        this.creationTime = creationTime;
+    }
+
+    public List<ExamQuestionEntity> getExamQuestionEntities() {
+        return examQuestionEntities;
+    }
+
+    public void setExamQuestionEntities(List<ExamQuestionEntity> examQuestionEntities) {
+        this.examQuestionEntities = examQuestionEntities;
+    }
+
+    public static ExamQuestionEntity createExamQuestionEntity() {
+        return new ExamQuestionEntity();
+    }
+
+    public static class ExamQuestionEntity {
+        /**
+         * 大题号
+         */
+        private Integer mainNumber;
+        /**
+         * 原题ID
+         */
+        private String questionId;
+        /**
+         * 顺序
+         */
+        private Integer order;
+        /**
+         * 小题分数
+         */
+        private Float questionScore;
+        /**
+         * 小题类型
+         */
+        private NewQuestionType questionType;
+        /**
+         * 标准答案
+         */
+        private String correctAnswer;
+        /**
+         * 考生作答
+         */
+        private String studentAnswer;
+        /**
+         * 学生小题得分
+         */
+        private Float studentScore;
+        /**
+         * 是否作答
+         */
+        private Boolean isAnswer;
+        /**
+         * 是否标记
+         */
+        private Boolean isSign;
+        
+        private Integer[] optionPermutation;
+
+        public Integer getMainNumber() {
+            return mainNumber;
+        }
+
+        public void setMainNumber(Integer mainNumber) {
+            this.mainNumber = mainNumber;
+        }
+
+        public String getQuestionId() {
+            return questionId;
+        }
+
+        public void setQuestionId(String questionId) {
+            this.questionId = questionId;
+        }
+
+        public Integer getOrder() {
+            return order;
+        }
+
+        public void setOrder(Integer order) {
+            this.order = order;
+        }
+
+        public Float getQuestionScore() {
+            return questionScore;
+        }
+
+        public void setQuestionScore(Float questionScore) {
+            this.questionScore = questionScore;
+        }
+
+        public NewQuestionType getQuestionType() {
+            return questionType;
+        }
+
+        public void setQuestionType(NewQuestionType questionType) {
+            this.questionType = questionType;
+        }
+
+        public String getCorrectAnswer() {
+            return correctAnswer;
+        }
+
+        public void setCorrectAnswer(String correctAnswer) {
+            this.correctAnswer = correctAnswer;
+        }
+
+        public String getStudentAnswer() {
+            return studentAnswer;
+        }
+
+        public void setStudentAnswer(String studentAnswer) {
+            this.studentAnswer = studentAnswer;
+        }
+
+        public Float getStudentScore() {
+            return studentScore;
+        }
+
+        public void setStudentScore(Float studentScore) {
+            this.studentScore = studentScore;
+        }
+
+        public Boolean getAnswer() {
+            return isAnswer;
+        }
+
+        public void setAnswer(Boolean answer) {
+            this.isAnswer = answer;
+        }
+
+        public Boolean getSign() {
+            return isSign;
+        }
+
+        public void setSign(Boolean sign) {
+            this.isSign = sign;
+        }
+
+		public Integer[] getOptionPermutation() {
+			return optionPermutation;
+		}
+
+		public void setOptionPermutation(Integer[] optionPermutation) {
+			this.optionPermutation = optionPermutation;
+		}
+        
+    }
+
+}

+ 117 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/ExamStudentEffectiveScoreInfo.java

@@ -0,0 +1,117 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-24 11:23:29.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * @Description 考生有效成绩基本信息
+ * @Author lideyin
+ * @Date 2019/7/18 16:42
+ * @Version 1.0
+ */
+public class ExamStudentEffectiveScoreInfo implements JsonSerializable {
+    private static final long serialVersionUID = -8096091096954899775L;
+    private Long examStudentId;
+
+    private Long rootOrgId;
+
+    private Long examId;
+
+    private Long orgId;
+
+    private Long courseId;
+
+    private String basePaperId;
+
+    /**
+     * 试卷满分
+     */
+    private Double totalScore;
+
+    /**
+     * 试卷得分
+     */
+    private Double score;
+    /**
+     * 考试记录id
+     */
+    private Long examRecordDataId;
+
+    public Long getExamStudentId() {
+        return examStudentId;
+    }
+
+    public void setExamStudentId(Long examStudentId) {
+        this.examStudentId = examStudentId;
+    }
+
+    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 Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getBasePaperId() {
+        return basePaperId;
+    }
+
+    public void setBasePaperId(String basePaperId) {
+        this.basePaperId = basePaperId;
+    }
+
+    public Double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(Double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public Double getScore() {
+        return score;
+    }
+
+    public void setScore(Double score) {
+        this.score = score;
+    }
+
+    public Long getExamRecordDataId() {
+        return examRecordDataId;
+    }
+
+    public void setExamRecordDataId(Long examRecordDataId) {
+        this.examRecordDataId = examRecordDataId;
+    }
+}

+ 64 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/SubjectiveQuestionScoreInfo.java

@@ -0,0 +1,64 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+public class SubjectiveQuestionScoreInfo implements JsonSerializable{
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 4161659045086103440L;
+
+	/**
+	 * 考试记录ID
+	 */
+	private Long examRecordDataId;
+	
+	/**
+	 * 
+	 */
+	private String questionId;
+	
+	/**
+	 * 小题序号
+	 */
+	private Integer order;
+	
+	/**
+	 * 得分
+	 */
+	private Double score;
+
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public String getQuestionId() {
+		return questionId;
+	}
+
+	public void setQuestionId(String questionId) {
+		this.questionId = questionId;
+	}
+
+	public Integer getOrder() {
+		return order;
+	}
+
+	public void setOrder(Integer order) {
+		this.order = order;
+	}
+
+	public Double getScore() {
+		return score;
+	}
+
+	public void setScore(Double score) {
+		this.score = score;
+	}
+	
+}

+ 95 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditEntityConvert.java

@@ -0,0 +1,95 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-27 16:27:13.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.beanutils.BeanUtils;
+import org.springframework.data.domain.Page;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.commons.exception.ExamCloudRuntimeException;
+import cn.com.qmth.examcloud.core.oe.common.enums.AuditStatus;
+import cn.com.qmth.examcloud.core.oe.common.enums.CourseLevel;
+import cn.com.qmth.examcloud.core.oe.common.enums.DisciplineType;
+import cn.com.qmth.examcloud.core.oe.common.enums.IsSuccess;
+
+/**
+ * 类信息转换
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/21
+ */
+public class ExamAuditEntityConvert {
+
+    public static List<ExamAuditInfo> of(List<HashMap> maps, String examName) {
+        if (maps == null || maps.size() == 0) {
+            return Lists.newArrayList();
+        }
+        List<ExamAuditInfo> list = new ArrayList<>();
+        for (Map map : maps) {
+            try {
+                //转换Map数据
+                ExamAuditInfo info = new ExamAuditInfo();
+                BeanUtils.populate(info, map);
+                //转换枚举名称
+                info.setCourseLevel(CourseLevel.getCourseLevelTitle(info.getCourseLevel()));
+                info.setStatus(AuditStatus.getTitle(info.getStatus()));
+                IsSuccess result = IsSuccess.strToEnum(info.getFaceVerifyResult());
+                if (result != null) {
+                    info.setFaceVerifyResult(result.getDesc());
+                }
+                info.setExamName(examName);
+                
+                DisciplineType disciplineType = DisciplineType.tramsformByType(info.getDisciplineType());
+                if(disciplineType!=null){
+                	info.setDisciplineType(disciplineType.getName());	
+                }
+                
+                list.add(info);
+            } catch (Exception e) {
+                throw new ExamCloudRuntimeException(e.getMessage(), e.getCause());
+            }
+        }
+        return list;
+    }
+
+    public static List<ExamAuditExcel> ofExcel(Page<ExamAuditInfo> page) {
+        List<ExamAuditExcel> list = new ArrayList<>();
+        if (page == null || page.getContent() == null) {
+            return list;
+        }
+        page.getContent().forEach(e -> {
+            ExamAuditExcel excel = new ExamAuditExcel();
+            excel.setExamRecordDataId(e.getExamRecordDataId());
+            /*DisciplineType disciplineType = DisciplineType.tramsformByType(e.getDisciplineType());
+            if(disciplineType!=null){
+            	excel.setDisciplineType(disciplineType.getName());
+            }*/
+            excel.setOrgName(e.getOrgName());
+            excel.setDisciplineType(e.getDisciplineType());
+            excel.setExamName(e.getExamName());
+            excel.setStudentCode(e.getStudentCode());
+            excel.setStudentName(e.getStudentName());
+            excel.setCourseCode(e.getCourseCode());
+            excel.setCourseName(e.getCourseName());
+            excel.setCourseLevel(e.getCourseLevel());
+            excel.setFaceSuccessCount(e.getFaceSuccessCount());
+            excel.setFaceStrangerCount(e.getFaceStrangerCount());
+            excel.setFaceTotalCount(e.getFaceTotalCount());
+            excel.setFaceSuccessPercent(e.getFaceSuccessPercent());
+            list.add(excel);
+        });
+        return list;
+    }
+
+}

+ 154 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditExcel.java

@@ -0,0 +1,154 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-30 14:36:48.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit;
+
+import cn.com.qmth.examcloud.core.oe.common.base.utils.excel.ExcelProperty;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 监考已审信息
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/15
+ */
+public class ExamAuditExcel implements JsonSerializable {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 1269194645888320744L;
+	@ExcelProperty(name = "考试记录ID", index = 2)
+    private Long examRecordDataId;
+    @ExcelProperty(name = "学习中心", index = 2)
+    private String orgName;
+    @ExcelProperty(name = "违纪类型", index = 12)
+    private String disciplineType;
+    @ExcelProperty(name = "批次名称", index = 1)
+    private String examName;
+    @ExcelProperty(name = "学号", index = 6)
+    private String studentCode;
+    @ExcelProperty(name = "姓名", index = 7)
+    private String studentName;
+    @ExcelProperty(name = "科目代码", index = 4)
+    private String courseCode;
+    @ExcelProperty(name = "考试科目", index = 3)
+    private String courseName;
+    @ExcelProperty(name = "课程层次", index = 5)
+    private String courseLevel;
+    @ExcelProperty(name = "成功次数", index = 9)
+    private Integer faceSuccessCount;
+    @ExcelProperty(name = "陌生人记录", index = 10)
+    private Integer faceStrangerCount;
+    @ExcelProperty(name = "校验次数", index = 8)
+    private Integer faceTotalCount;
+    @ExcelProperty(name = "成功率", index = 11)
+    private Double faceSuccessPercent;
+    
+    public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+    public String getDisciplineType() {
+		return disciplineType;
+	}
+
+	public void setDisciplineType(String disciplineType) {
+		this.disciplineType = disciplineType;
+	}
+
+	public String getExamName() {
+        return examName;
+    }
+
+    public void setExamName(String examName) {
+        this.examName = examName;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+    public Integer getFaceSuccessCount() {
+        return faceSuccessCount;
+    }
+
+    public void setFaceSuccessCount(Integer faceSuccessCount) {
+        this.faceSuccessCount = faceSuccessCount;
+    }
+
+    public Integer getFaceStrangerCount() {
+        return faceStrangerCount;
+    }
+
+    public void setFaceStrangerCount(Integer faceStrangerCount) {
+        this.faceStrangerCount = faceStrangerCount;
+    }
+
+    public Integer getFaceTotalCount() {
+        return faceTotalCount;
+    }
+
+    public void setFaceTotalCount(Integer faceTotalCount) {
+        this.faceTotalCount = faceTotalCount;
+    }
+
+    public Double getFaceSuccessPercent() {
+        return faceSuccessPercent;
+    }
+
+    public void setFaceSuccessPercent(Double faceSuccessPercent) {
+        this.faceSuccessPercent = faceSuccessPercent;
+    }
+
+    public String getOrgName() {
+        return orgName;
+    }
+
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
+}

+ 465 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditInfo.java

@@ -0,0 +1,465 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-27 16:21:30.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 监考已审信息
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/15
+ */
+public class ExamAuditInfo implements JsonSerializable {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -7260035187571848686L;
+	/**
+     * 主键
+     */
+    private Long id;
+    /**
+     * 考试记录ID
+     */
+    private Long examRecordId;
+    
+    private Long examRecordDataId;
+    
+    /**
+     * 审核状态
+     */
+    private String status;
+    /**
+     * 违纪详情
+     */
+    private String disciplineDetail;
+    
+    private String disciplineType;
+    /**
+     * 审核人姓名
+     */
+    private String auditUserName;
+    
+    /**
+     * 考试ID
+     */
+    private Long examId;
+    /**
+     * 考试名称
+     */
+    private String examName;
+    /**
+     * 考试类型
+     */
+    private String examType;
+    /**
+     * 考生ID
+     */
+    private Long examStudentId;
+    /**
+     * 学生ID
+     */
+    private Long studentId;
+    /**
+     * 学号
+     */
+    private String studentCode;
+    /**
+     * 学生姓名
+     */
+    private String studentName;
+    /**
+     * 身份证号
+     */
+    private String identityNumber;
+    /**
+     * 课程ID
+     */
+    private Long courseId;
+    /**
+     * 课程Code
+     */
+    private String courseCode;
+    /**
+     * 课程名称
+     */
+    private String courseName;
+    /**
+     * 课程层次
+     */
+    private String courseLevel;
+    /**
+     * 学习中心ID
+     */
+    private Long orgId;
+    /**
+     * 学习中名称
+     */
+    private String orgName;
+    /**
+     * 是否异常数据
+     */
+    private Boolean isWarn;
+    /**
+     * 是否被审核过
+     */
+    private Boolean isAudit;
+    /**
+     * 是否违纪
+     */
+    private Boolean isIllegality;
+    /**
+     * 考试记录状态(考试中,考试结束,考试过期,考试作废)
+     */
+    private String examRecordStatus;
+    /**
+     * 考试时长
+     */
+    private Long usedExamTime;
+    /**
+     * 第几次考试
+     */
+    private Integer examOrder;
+    /**
+     * 是否为重考
+     */
+    private Boolean isReexamine;
+    /**
+     * 是否断点续考
+     */
+    private Boolean isContinued;
+    /**
+     * 断点续考次数
+     */
+    private Integer continuedCount;
+    /**
+     * 抓拍比对成功次数
+     */
+    private Integer faceSuccessCount;
+    /**
+     * 抓拍比对失败次数
+     */
+    private Integer faceFailedCount;
+    /**
+     * 抓拍存在陌生人的次数
+     */
+    private Integer faceStrangerCount;
+    /**
+     * 抓拍比对总次数
+     */
+    private Integer faceTotalCount;
+    /**
+     * 抓拍比对成功比率
+     */
+    private Double faceSuccessPercent;
+    /**
+     * 活体检测结果
+     */
+    private String faceVerifyResult;
+    /**
+     * 人脸五官坐标比对值
+     */
+    private Double faceLandmarkVal;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getExamRecordId() {
+        return examRecordId;
+    }
+
+    public void setExamRecordId(Long examRecordId) {
+        this.examRecordId = examRecordId;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getDisciplineDetail() {
+        return disciplineDetail;
+    }
+
+    public void setDisciplineDetail(String disciplineDetail) {
+        this.disciplineDetail = disciplineDetail;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getExamName() {
+        return examName;
+    }
+
+    public void setExamName(String examName) {
+        this.examName = examName;
+    }
+
+    public String getExamType() {
+        return examType;
+    }
+
+    public void setExamType(String examType) {
+        this.examType = examType;
+    }
+
+    public Long getExamStudentId() {
+        return examStudentId;
+    }
+
+    public void setExamStudentId(Long examStudentId) {
+        this.examStudentId = examStudentId;
+    }
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public Boolean getWarn() {
+        return isWarn;
+    }
+
+    public void setWarn(Boolean warn) {
+        this.isWarn = warn;
+    }
+
+    public Boolean getAudit() {
+        return isAudit;
+    }
+
+    public void setAudit(Boolean audit) {
+        this.isAudit = audit;
+    }
+
+    public Boolean getIllegality() {
+        return isIllegality;
+    }
+
+    public void setIllegality(Boolean illegality) {
+        this.isIllegality = illegality;
+    }
+
+    public String getExamRecordStatus() {
+        return examRecordStatus;
+    }
+
+    public void setExamRecordStatus(String examRecordStatus) {
+        this.examRecordStatus = examRecordStatus;
+    }
+
+    public Long getUsedExamTime() {
+        return usedExamTime;
+    }
+
+    public void setUsedExamTime(Long usedExamTime) {
+        this.usedExamTime = usedExamTime;
+    }
+
+    public Integer getExamOrder() {
+        return examOrder;
+    }
+
+    public void setExamOrder(Integer examOrder) {
+        this.examOrder = examOrder;
+    }
+
+    public Boolean getReexamine() {
+        return isReexamine;
+    }
+
+    public void setReexamine(Boolean reexamine) {
+        this.isReexamine = reexamine;
+    }
+
+    public Boolean getContinued() {
+        return isContinued;
+    }
+
+    public void setContinued(Boolean continued) {
+        this.isContinued = continued;
+    }
+
+    public Integer getContinuedCount() {
+        return continuedCount != null ? continuedCount : 0;
+    }
+
+    public void setContinuedCount(Integer continuedCount) {
+        this.continuedCount = continuedCount;
+    }
+
+    public Integer getFaceSuccessCount() {
+        return faceSuccessCount != null ? faceSuccessCount : 0;
+    }
+
+    public void setFaceSuccessCount(Integer faceSuccessCount) {
+        this.faceSuccessCount = faceSuccessCount;
+    }
+
+    public Integer getFaceFailedCount() {
+        return faceFailedCount != null ? faceFailedCount : 0;
+    }
+
+    public void setFaceFailedCount(Integer faceFailedCount) {
+        this.faceFailedCount = faceFailedCount;
+    }
+
+    public Integer getFaceStrangerCount() {
+        return faceStrangerCount != null ? faceStrangerCount : 0;
+    }
+
+    public void setFaceStrangerCount(Integer faceStrangerCount) {
+        this.faceStrangerCount = faceStrangerCount;
+    }
+
+    public Integer getFaceTotalCount() {
+        return faceTotalCount != null ? faceTotalCount : 0;
+    }
+
+    public void setFaceTotalCount(Integer faceTotalCount) {
+        this.faceTotalCount = faceTotalCount;
+    }
+
+    public Double getFaceSuccessPercent() {
+        return faceSuccessPercent != null ? faceSuccessPercent : 0d;
+    }
+
+    public void setFaceSuccessPercent(Double faceSuccessPercent) {
+        this.faceSuccessPercent = faceSuccessPercent;
+    }
+
+    public String getFaceVerifyResult() {
+        return faceVerifyResult;
+    }
+
+    public void setFaceVerifyResult(String faceVerifyResult) {
+        this.faceVerifyResult = faceVerifyResult;
+    }
+
+    public Double getFaceLandmarkVal() {
+        return faceLandmarkVal != null ? faceLandmarkVal : 0d;
+    }
+
+    public void setFaceLandmarkVal(Double faceLandmarkVal) {
+        this.faceLandmarkVal = faceLandmarkVal;
+    }
+
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public String getDisciplineType() {
+		return disciplineType;
+	}
+
+	public void setDisciplineType(String disciplineType) {
+		this.disciplineType = disciplineType;
+	}
+
+	public String getAuditUserName() {
+		return auditUserName;
+	}
+
+	public void setAuditUserName(String auditUserName) {
+		this.auditUserName = auditUserName;
+	}
+
+    public String getOrgName() {
+        return orgName;
+    }
+
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
+}

+ 47 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditMapper.java

@@ -0,0 +1,47 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-27 16:25:43.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit;
+
+/**
+ * 实体元数据
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/24
+ */
+public class ExamAuditMapper {
+
+    public static final String EXAM_RECORD_DATA_ID = "examRecordDataId";
+
+    public static String defaultColumns() {
+        StringBuilder columns = new StringBuilder();
+        columns.append("audit.id as id,");
+        columns.append("audit.status as status,");
+        columns.append("audit.discipline_detail as disciplineDetail,");
+        columns.append("audit.discipline_type as disciplineType,");
+        columns.append("audit.audit_user_name as auditUserName,");
+        columns.append("record.id as examRecordId,");
+        columns.append("record.exam_id as examId,");
+        columns.append("record.exam_student_id as examStudentId,");
+        columns.append("record.student_id as studentId,");
+        columns.append("record.student_code as studentCode,");
+        columns.append("record.student_name as studentName,");
+        columns.append("record.identity_number as identityNumber,");
+        columns.append("record.course_id as courseId,");
+        columns.append("record.org_id as orgId,");
+        columns.append("data.id as examRecordDataId,");
+        columns.append("data.exam_order as examOrder,");
+        columns.append("data.face_total_count as faceTotalCount,");
+        columns.append("data.face_success_count as faceSuccessCount,");
+        columns.append("data.face_failed_count as faceFailedCount,");
+        columns.append("data.face_stranger_count as faceStrangerCount,");
+        columns.append("data.face_success_percent as faceSuccessPercent,");
+        columns.append("data.face_verify_result as faceVerifyResult");
+        return columns.toString();
+    }
+
+}

+ 181 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditQuery.java

@@ -0,0 +1,181 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-27 16:18:39.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.core.oe.common.enums.SelectType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 监考已审查询条件类
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+@ApiModel
+public class ExamAuditQuery implements JsonSerializable {
+    @ApiModelProperty("当前页数")
+    private Integer pageNo;
+    @ApiModelProperty("每页条数")
+    private Integer pageSize;
+    @ApiModelProperty("学习中心ID")
+    private Long orgId;
+    @ApiModelProperty("机构ID")
+    private Long rootOrgId;
+    @ApiModelProperty("考试ID")
+    private Long examId;
+    @ApiModelProperty("学号")
+    private String studentCode;
+    @ApiModelProperty("学生姓名")
+    private String studentName;
+    @ApiModelProperty("身份证号")
+    private String identityNumber;
+    @ApiModelProperty("课程ID")
+    private Long courseId;
+    @ApiModelProperty("课程层次")
+    private String courseLevel;
+    @ApiModelProperty("考试记录ID")
+    private Long examRecordDataId;
+    @ApiModelProperty("审核状态")
+    private String status;
+    @ApiModelProperty("违纪类型")
+    private String disciplineType;
+    @ApiModelProperty("审核人")
+    private String auditUserName;
+    
+    /**
+     * 查询或导出  select  export
+     */
+    private SelectType selectType;
+
+    public Integer getPageNo() {
+        return pageNo;
+    }
+
+    public void setPageNo(Integer pageNo) {
+        this.pageNo = pageNo;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+    public Long getExamRecordDataId() {
+        return examRecordDataId;
+    }
+
+    public void setExamRecordId(Long examRecordDataId) {
+        this.examRecordDataId = examRecordDataId;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public SelectType getSelectType() {
+		return selectType;
+	}
+
+	public void setSelectType(SelectType selectType) {
+		this.selectType = selectType;
+	}
+
+	public String getDisciplineType() {
+		return disciplineType;
+	}
+
+	public void setDisciplineType(String disciplineType) {
+		this.disciplineType = disciplineType;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public String getAuditUserName() {
+		return auditUserName;
+	}
+
+	public void setAuditUserName(String auditUserName) {
+		this.auditUserName = auditUserName;
+	}
+    
+}

+ 71 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/RedoAuditInfo.java

@@ -0,0 +1,71 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2019年1月15日 上午9:59:14
+ * @company 	QMTH
+ * @description 重新审核
+ */
+public class RedoAuditInfo implements JsonSerializable{
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 5158278733859663008L;
+	
+	private List<Long> examRecordDataIds;
+	
+	/**
+	 * 是否通过
+	 */
+	private Boolean isPass;
+	
+	/**
+	 * 违纪类型
+	 */
+	private String disciplineType;
+	
+	/**
+	 * 违纪详情
+	 */
+	private String disciplineDetail;
+
+	public List<Long> getExamRecordDataIds() {
+		return examRecordDataIds;
+	}
+
+	public void setExamRecordDataIds(List<Long> examRecordDataIds) {
+		this.examRecordDataIds = examRecordDataIds;
+	}
+
+	public Boolean getIsPass() {
+		return isPass;
+	}
+
+	public void setIsPass(Boolean isPass) {
+		this.isPass = isPass;
+	}
+
+	public String getDisciplineType() {
+		return disciplineType;
+	}
+
+	public void setDisciplineType(String disciplineType) {
+		this.disciplineType = disciplineType;
+	}
+
+	public String getDisciplineDetail() {
+		return disciplineDetail;
+	}
+
+	public void setDisciplineDetail(String disciplineDetail) {
+		this.disciplineDetail = disciplineDetail;
+	}
+	
+}

+ 289 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examcapture/ExamCaptureAuditInfo.java

@@ -0,0 +1,289 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-09-03 16:52:14.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examcapture;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 考试抓拍检测信息
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/31
+ */
+public class ExamCaptureAuditInfo implements JsonSerializable {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 6991258456890821018L;
+
+	/**
+     * 考试记录ID
+     */
+	private Long examRecordDataId;
+	
+    private Long examRecordId;
+    
+    private Long studentId;
+    
+    private String studentName;
+    
+    private String studentCode;
+    
+    private String identityNumber;
+    
+    private String courseCode;
+    
+    private String courseName;
+    
+    
+    /**
+     * 审核状态
+     */
+    private String status;
+    /**
+     * 违纪详情
+     */
+    private String disciplineDetail;
+    private String disciplineType;
+    /**
+     * 是否异常数据
+     */
+    private Boolean isWarn;
+    /**
+     * 是否被审核过
+     */
+    private Boolean isAudit;
+    /**
+     * 是否违纪
+     */
+    private Boolean isIllegality;
+    /**
+     * 抓拍比对成功次数
+     */
+    private Integer faceSuccessCount;
+    /**
+     * 抓拍比对失败次数
+     */
+    private Integer faceFailedCount;
+    /**
+     * 抓拍存在陌生人的次数
+     */
+    private Integer faceStrangerCount;
+    /**
+     * 抓拍比对总次数
+     */
+    private Integer faceTotalCount;
+    /**
+     * 抓拍比对成功比率
+     */
+    private Double faceSuccessPercent;
+    /**
+     * 活体检测结果
+     */
+    private String faceVerifyResult;
+    /**
+     * 人脸五官坐标比对值
+     */
+    private Double faceLandmarkVal;
+    
+    /**
+     * 百度人脸活体检测通过率
+     */
+    private Double baiduFaceLivenessSuccessPercent;
+    
+    /**
+     * 虚拟摄像头名称
+     */
+    private String virtualCameraNames;
+
+    public Long getExamRecordId() {
+        return examRecordId;
+    }
+
+    public void setExamRecordId(Long examRecordId) {
+        this.examRecordId = examRecordId;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getDisciplineDetail() {
+        return disciplineDetail;
+    }
+
+    public void setDisciplineDetail(String disciplineDetail) {
+        this.disciplineDetail = disciplineDetail;
+    }
+
+    public Integer getFaceSuccessCount() {
+        return faceSuccessCount;
+    }
+
+    public void setFaceSuccessCount(Integer faceSuccessCount) {
+        this.faceSuccessCount = faceSuccessCount;
+    }
+
+    public Integer getFaceFailedCount() {
+        return faceFailedCount;
+    }
+
+    public void setFaceFailedCount(Integer faceFailedCount) {
+        this.faceFailedCount = faceFailedCount;
+    }
+
+    public Integer getFaceStrangerCount() {
+        return faceStrangerCount;
+    }
+
+    public void setFaceStrangerCount(Integer faceStrangerCount) {
+        this.faceStrangerCount = faceStrangerCount;
+    }
+
+    public Integer getFaceTotalCount() {
+        return faceTotalCount;
+    }
+
+    public void setFaceTotalCount(Integer faceTotalCount) {
+        this.faceTotalCount = faceTotalCount;
+    }
+
+    public Double getFaceSuccessPercent() {
+        return faceSuccessPercent;
+    }
+
+    public void setFaceSuccessPercent(Double faceSuccessPercent) {
+        this.faceSuccessPercent = faceSuccessPercent;
+    }
+
+    public String getFaceVerifyResult() {
+        return faceVerifyResult;
+    }
+
+    public void setFaceVerifyResult(String faceVerifyResult) {
+        this.faceVerifyResult = faceVerifyResult;
+    }
+
+    public Double getFaceLandmarkVal() {
+        return faceLandmarkVal;
+    }
+
+    public void setFaceLandmarkVal(Double faceLandmarkVal) {
+        this.faceLandmarkVal = faceLandmarkVal;
+    }
+
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public String getDisciplineType() {
+		return disciplineType;
+	}
+
+	public void setDisciplineType(String disciplineType) {
+		this.disciplineType = disciplineType;
+	}
+
+	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 getCourseCode() {
+		return courseCode;
+	}
+
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+
+	public String getCourseName() {
+		return courseName;
+	}
+
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+
+	public Boolean getIsWarn() {
+		return isWarn;
+	}
+
+	public void setIsWarn(Boolean isWarn) {
+		this.isWarn = isWarn;
+	}
+
+	public Long getStudentId() {
+		return studentId;
+	}
+
+	public void setStudentId(Long studentId) {
+		this.studentId = studentId;
+	}
+
+	public Boolean getIsAudit() {
+		return isAudit;
+	}
+
+	public void setIsAudit(Boolean isAudit) {
+		this.isAudit = isAudit;
+	}
+
+	public Boolean getIsIllegality() {
+		return isIllegality;
+	}
+
+	public void setIsIllegality(Boolean isIllegality) {
+		this.isIllegality = isIllegality;
+	}
+
+	public Double getBaiduFaceLivenessSuccessPercent() {
+		return baiduFaceLivenessSuccessPercent;
+	}
+
+	public void setBaiduFaceLivenessSuccessPercent(
+			Double baiduFaceLivenessSuccessPercent) {
+		this.baiduFaceLivenessSuccessPercent = baiduFaceLivenessSuccessPercent;
+	}
+
+	public String getVirtualCameraNames() {
+		return virtualCameraNames;
+	}
+
+	public void setVirtualCameraNames(String virtualCameraNames) {
+		this.virtualCameraNames = virtualCameraNames;
+	}
+    
+}

+ 124 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examcapture/ExamCaptureInfo.java

@@ -0,0 +1,124 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 17:29:44.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examcapture;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 考试抓拍检测信息
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/31
+ */
+public class ExamCaptureInfo implements JsonSerializable {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 3199275931294184644L;
+	/**
+     * 主键
+     */
+    private Long id;
+    /**
+     * ec_oe_exam_record_data_ID
+     */
+    private Long examRecordDataId;
+    /**
+     * 文件URL
+     */
+    private String fileUrl;
+    /**
+     * 返回信息(JSON)
+     */
+    private String returnMessage;
+
+    /**
+     * 人脸比较是否通过
+     */
+    private Boolean isPass;
+    /**
+     * 是否有陌生人 1是 0否
+     * 就是摄像头拍到不止考生一人
+     */
+    private Boolean isStranger;
+
+    /**
+     * 百度活体检测是否通过
+     */
+    private Boolean isFacelivenessPass;
+    
+    /**
+     * 虚拟摄像头名称
+     */
+    private String virtualCameraNames;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getExamRecordDataId() {
+        return examRecordDataId;
+    }
+
+    public void setExamRecordDataId(Long examRecordDataId) {
+        this.examRecordDataId = examRecordDataId;
+    }
+
+    public String getFileUrl() {
+        return fileUrl;
+    }
+
+    public void setFileUrl(String fileUrl) {
+        this.fileUrl = fileUrl;
+    }
+
+    public String getReturnMessage() {
+        return returnMessage;
+    }
+
+    public void setReturnMessage(String returnMessage) {
+        this.returnMessage = returnMessage;
+    }
+
+    public Boolean getPass() {
+        return isPass != null ? isPass : false;
+    }
+
+    public void setPass(Boolean pass) {
+        this.isPass = pass;
+    }
+
+    public Boolean getStranger() {
+        return isStranger != null ? isStranger : false;
+    }
+
+    public void setStranger(Boolean stranger) {
+        this.isStranger = stranger;
+    }
+
+	public Boolean getIsFacelivenessPass() {
+		return isFacelivenessPass;
+	}
+
+	public void setIsFacelivenessPass(Boolean isFacelivenessPass) {
+		this.isFacelivenessPass = isFacelivenessPass;
+	}
+
+	public String getVirtualCameraNames() {
+		return virtualCameraNames;
+	}
+
+	public void setVirtualCameraNames(String virtualCameraNames) {
+		this.virtualCameraNames = virtualCameraNames;
+	}
+	
+}

+ 20 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examcapture/ExamCaptureQuery.java

@@ -0,0 +1,20 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 17:30:01.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examcapture;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 考试抓拍检测查询条件类
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/31
+ */
+public class ExamCaptureQuery implements JsonSerializable {
+
+}

+ 164 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordEntityConvert.java

@@ -0,0 +1,164 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-24 13:54:59.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCacheTransferHelper;
+import cn.com.qmth.examcloud.core.oe.common.base.Constants;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.DateUtils;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.CourseLevel;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamRecordStatus;
+import cn.com.qmth.examcloud.core.oe.common.enums.IsSuccess;
+import cn.com.qmth.examcloud.core.oe.common.enums.TrueFalse;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.core.oe.common.service.LocalCacheService;
+import cn.com.qmth.examcloud.support.cache.bean.OrgCacheBean;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import com.google.common.collect.Lists;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 类信息转换
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/21
+ */
+@Component
+public class ExamRecordEntityConvert {
+	
+	@Autowired
+	private ExamRecordRepo examRecordRepo;
+
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+    public  ExamRecordInfo of(ExamRecordDataEntity recordData,Map<String,Object> cahcheMap) {
+        if (recordData == null) {
+            return null;
+        }
+        ExamRecordEntity record = GlobalHelper.getEntity(examRecordRepo,recordData.getExamRecordId(),ExamRecordEntity.class);
+        if (record == null) {
+            throw new StatusException(Constants.OE_CODE_500, "考试记录基础信息不存在!");
+        }
+        ExamRecordInfo info = new ExamRecordInfo();
+        //封装基础数据
+        info.setId(record.getId());
+        info.setDataId(recordData.getId());
+        info.setExamId(record.getExamId());
+        if (record.getExamType() != null) {
+            info.setExamType(record.getExamType().name());
+        }
+        info.setExamStudentId(record.getExamStudentId());
+        info.setStudentId(record.getStudentId());
+        info.setStudentCode(record.getStudentCode());
+        info.setStudentName(record.getStudentName());
+        info.setIdentityNumber(record.getIdentityNumber());
+        info.setCourseId(record.getCourseId());
+        
+        long courseId = record.getCourseId();
+    	CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(courseId);
+        
+        info.setCourseCode(courseBean.getCode());
+        info.setCourseName(courseBean.getName());
+        info.setCourseNameAndCode(courseBean.getName() + "(" + courseBean.getCode() + ")");
+        info.setCourseLevel(CourseLevel.getCourseLevelTitle(courseBean.getLevel()));
+        info.setOrgId(record.getOrgId());
+        
+        long orgId = record.getOrgId();
+        OrgCacheBean orgBean = gainBaseDataService.getOrgBean(orgId);
+        info.setOrgName(orgBean.getName());
+        info.setRootOrgId(record.getRootOrgId());
+        info.setBasePaperId(record.getBasePaperId());
+        info.setPaperStructId(record.getPaperStructId());
+        info.setInfoCollector(record.getInfoCollector());
+        //封装详细数据
+        info.setStartTime(recordData.getStartTime());
+        info.setEndTime(recordData.getEndTime());
+        info.setCleanTime(recordData.getCleanTime());
+        info.setIsWarn(recordData.getIsWarn());
+        info.setIsAudit(recordData.getIsAudit());
+        if (recordData.getStartTime() != null) {
+            info.setPaperStartTime(DateUtils.format(recordData.getStartTime()));
+        } else {
+            info.setPaperStartTime("");
+        }
+        if (recordData.getEndTime() != null) {
+            info.setPaperSubmitTime(DateUtils.format(recordData.getEndTime()));
+            info.setIsSubmit("是");
+        } else {
+            info.setPaperSubmitTime("");
+            info.setIsSubmit("否");
+        }
+
+        //每次考试持续时间
+        if (recordData.getStartTime() != null && recordData.getEndTime() != null) {
+            info.setExamTime(recordData.getUsedExamTime() > 0 ? DateUtils.diff(recordData.getUsedExamTime()) : "");
+        } else {
+            info.setExamTime("");
+        }
+
+        //违纪标志
+        info.setDisciplineSign(TrueFalse.of(recordData.getIsWarn()));
+        info.setIsIllegality(TrueFalse.of(recordData.getIsIllegality()));
+
+        ExamRecordStatus status = recordData.getExamRecordStatus();
+        if (status != null) {
+            info.setExamRecordStatus(status.name());
+        }
+        info.setUsedExamTime(recordData.getUsedExamTime());
+        info.setExamOrder(recordData.getExamOrder());
+        info.setIsReexamine(recordData.getIsReexamine());
+        info.setIsContinued(recordData.getIsContinued());
+        info.setContinuedCount(recordData.getContinuedCount());
+        info.setFaceSuccessCount(recordData.getFaceSuccessCount());
+        info.setFaceFailedCount(recordData.getFaceFailedCount());
+        info.setFaceStrangerCount(recordData.getFaceStrangerCount());
+        info.setFaceTotalCount(recordData.getFaceTotalCount());
+        info.setFaceSuccessPercent(recordData.getFaceSuccessPercent());
+        info.setBaiduFaceLivenessSuccessPercent(recordData.getBaiduFaceLivenessSuccessPercent());
+        IsSuccess result = recordData.getFaceVerifyResult();
+        if (result != null) {
+            info.setFaceVerifyResult(result.getDesc());
+        }
+        info.setFaceLandmarkVal(recordData.getFaceLandmarkVal());
+        return info;
+    }
+
+    public Page<ExamRecordInfo> of(Page<ExamRecordDataEntity> page) {
+        Pageable pageable = new PageRequest(page.getNumber(), page.getSize());
+        List<ExamRecordDataEntity> entities = page.getContent();
+        if (entities == null || entities.size() == 0) {
+            return new PageImpl<ExamRecordInfo>(Lists.newArrayList(), pageable, page.getTotalElements());
+        }
+        //缓存
+        Map<String,Object> cahcheMap = new HashMap<String, Object>();
+        List<ExamRecordInfo> list = entities.stream().map(entity -> of(entity,cahcheMap)).collect(Collectors.toList());
+        return new PageImpl<ExamRecordInfo>(list, pageable, page.getTotalElements());
+    }
+
+    
+    public List<ExamRecordInfo> of(List<ExamRecordDataEntity> entities) {
+    	//缓存
+        Map<String,Object> cahcheMap = new HashMap<String, Object>();
+        List<ExamRecordInfo> list = entities.stream().map(entity -> of(entity,cahcheMap)).collect(Collectors.toList());
+        return list;
+    }
+}

+ 707 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordInfo.java

@@ -0,0 +1,707 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-24 11:22:55.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord;
+
+import java.util.Date;
+
+import cn.com.qmth.examcloud.core.oe.common.base.utils.excel.ExcelProperty;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 考试记录
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/15
+ */
+public class ExamRecordInfo implements JsonSerializable {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -735354536432038902L;
+	/**
+     * 考试记录ID
+     */
+    @ExcelProperty(name = "考试编码", width = 30, index = 14)
+    private Long id;
+    /**
+     * 考试记录详细ID
+     */
+    private Long dataId;
+    /**
+     * 考试ID
+     */
+    private Long examId;
+    /**
+     * 考试类型
+     */
+    private String examType;
+    /**
+     * 考试批次
+     */
+    @ExcelProperty(name = "考试批次", width = 30, index = 13)
+    private String examName;
+    /**
+     * 考生ID
+     */
+    private Long examStudentId;
+    /**
+     * 学生ID
+     */
+    private Long studentId;
+    /**
+     * 学号
+     */
+    @ExcelProperty(name = "学号", width = 30, index = 2)
+    private String studentCode;
+    /**
+     * 学生姓名
+     */
+    @ExcelProperty(name = "姓名", width = 30, index = 1)
+    private String studentName;
+    /**
+     * 准考证号
+     */
+    @ExcelProperty(name = "准考证号", width = 30, index = 21)
+    private String studentNumber;
+    /**
+     * 身份证号
+     */
+    @ExcelProperty(name = "身份证号", width = 30, index = 12)
+    private String identityNumber;
+    /**
+     * 课程ID
+     */
+    private Long courseId;
+    /**
+     * 课程Code
+     */
+    @ExcelProperty(name = "课程代码", width = 30, index = 11)
+    private String courseCode;
+    /**
+     * 课程名称
+     */
+    @ExcelProperty(name = "课程名称", width = 30, index = 7)
+    private String courseName;
+    /**
+     * 课程名称(课程代码)
+     */
+    @ExcelProperty(name = "课程名称(课程代码)", width = 30, index = 10)
+    private String courseNameAndCode;
+    /**
+     * 课程层次
+     */
+    @ExcelProperty(name = "层次", width = 30, index = 4)
+    private String courseLevel;
+    /**
+     * 年级
+     */
+    @ExcelProperty(name = "年级", width = 30, index = 5)
+    private String grade;
+    /**
+     * 专业
+     */
+    @ExcelProperty(name = "专业", width = 30, index = 6)
+    private String specialtyName;
+    /**
+     * 学习中心ID
+     */
+    private Long orgId;
+    /**
+     * 学习中心名称
+     */
+    @ExcelProperty(name = "学习中心", width = 30, index = 3)
+    private String orgName;
+    /**
+     * 顶级机构ID
+     */
+    private Long rootOrgId;
+    /**
+     * 基础试卷ID
+     */
+    private String basePaperId;
+    /**
+     * 试卷结构 ID
+     */
+    private String paperStructId;
+    /**
+     * 考试开始时间
+     */
+    private Date startTime;
+    /**
+     * 考试进入时间
+     */
+    @ExcelProperty(name = "考试进入时间", width = 30, index = 8)
+    private String paperStartTime;
+    /**
+     * 考试结束时间
+     */
+    private Date endTime;
+    /**
+     * 考试被清理时间
+     */
+    private Date cleanTime;
+    /**
+     * 是否异常数据
+     */
+    private Boolean isWarn;
+    /**
+     * 是否被审核过
+     */
+    private Boolean isAudit;
+    /**
+     * 是否违纪
+     */
+    @ExcelProperty(name = "是否违纪(终审)", width = 30, index = 26)
+    private String isIllegality;
+    /**
+     * 考试记录状态(考试中,考试结束,考试过期,考试作废)
+     */
+    private String examRecordStatus;
+    /**
+     * 采集人
+     */
+    @ExcelProperty(name = "信息采集人", width = 30, index = 29)
+    private String infoCollector;
+    /**
+     * 考试时长
+     */
+    private Long usedExamTime;
+    /**
+     * 每次考试持续时间
+     */
+    @ExcelProperty(name = "每次考试持续时间", width = 30, index = 16)
+    private String examTime;
+    /**
+     * 第几次考试
+     */
+    @ExcelProperty(name = "考试次数", width = 30, index = 15)
+    private Integer examOrder;
+    /**
+     * 是否为重考
+     */
+    private Boolean isReexamine;
+    /**
+     * 是否断点续考
+     */
+    private Boolean isContinued;
+    /**
+     * 断点续考次数
+     */
+    @ExcelProperty(name = "断点续考次数", width = 30, index = 20)
+    private Integer continuedCount;
+    /**
+     * 抓拍比对成功次数
+     */
+    @ExcelProperty(name = "成功次数", width = 30, index = 18)
+    private Integer faceSuccessCount;
+    /**
+     * 抓拍比对失败次数
+     */
+    private Integer faceFailedCount;
+    /**
+     * 抓拍存在陌生人的次数
+     */
+    private Integer faceStrangerCount;
+    /**
+     * 抓拍比对总次数
+     */
+    @ExcelProperty(name = "校验次数", width = 30, index = 17)
+    private Integer faceTotalCount;
+    /**
+     * 抓拍比对成功比率
+     */
+    @ExcelProperty(name = "成功率", width = 30, index = 19)
+    private Double faceSuccessPercent;
+    /**
+     * 活体检测结果
+     */
+    private String faceVerifyResult;
+    /**
+     * 人脸五官坐标比对值
+     */
+    private Double faceLandmarkVal;
+    /**
+     * 百度人脸活体检测通过率
+     */
+    private Double baiduFaceLivenessSuccessPercent;
+    /**
+     * 卷面总分
+     */
+    @ExcelProperty(name = "卷面总分", width = 30, index = 24)
+    private String paperTotalScore;
+    /**
+     * 是否提交试卷
+     */
+    @ExcelProperty(name = "是否提交交卷", width = 30, index = 27)
+    private String isSubmit;
+    /**
+     * 考试交卷时间
+     */
+    @ExcelProperty(name = "考试交卷时间", width = 30, index = 9)
+    private String paperSubmitTime;
+    /**
+     * 客观题总分
+     */
+    @ExcelProperty(name = "客观题总分", width = 30, index = 22)
+    private String objectiveTotalScore;
+    /**
+     * 主观题总分
+     */
+    @ExcelProperty(name = "主观题总分", width = 30, index = 23)
+    private String subjectiveTotalScore;
+    /**
+     * 违纪标志
+     */
+    @ExcelProperty(name = "违纪标志", width = 30, index = 25)
+    private String disciplineSign;
+    /**
+     * 学生电话
+     */
+    @ExcelProperty(name = "学生电话", width = 30, index = 30)
+    private String phone;
+    
+    /**
+     * 离线考试作答文件
+     */
+    private String offlineFileUrl;
+    
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getDataId() {
+        return dataId;
+    }
+
+    public void setDataId(Long dataId) {
+        this.dataId = dataId;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getExamType() {
+        return examType;
+    }
+
+    public void setExamType(String examType) {
+        this.examType = examType;
+    }
+
+    public Long getExamStudentId() {
+        return examStudentId;
+    }
+
+    public void setExamStudentId(Long examStudentId) {
+        this.examStudentId = examStudentId;
+    }
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public String getBasePaperId() {
+        return basePaperId;
+    }
+
+    public void setBasePaperId(String basePaperId) {
+        this.basePaperId = basePaperId;
+    }
+
+    public String getPaperStructId() {
+        return paperStructId;
+    }
+
+    public void setPaperStructId(String paperStructId) {
+        this.paperStructId = paperStructId;
+    }
+
+    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 Date getCleanTime() {
+        return cleanTime;
+    }
+
+    public void setCleanTime(Date cleanTime) {
+        this.cleanTime = cleanTime;
+    }
+
+    public String getExamRecordStatus() {
+        return examRecordStatus;
+    }
+
+    public void setExamRecordStatus(String examRecordStatus) {
+        this.examRecordStatus = examRecordStatus;
+    }
+
+    public String getInfoCollector() {
+        return infoCollector;
+    }
+
+    public void setInfoCollector(String infoCollector) {
+        this.infoCollector = infoCollector;
+    }
+
+    public Long getUsedExamTime() {
+        return usedExamTime;
+    }
+
+    public void setUsedExamTime(Long usedExamTime) {
+        this.usedExamTime = usedExamTime;
+    }
+
+    public Integer getExamOrder() {
+        return examOrder;
+    }
+
+    public void setExamOrder(Integer examOrder) {
+        this.examOrder = examOrder;
+    }
+
+    public Integer getContinuedCount() {
+        return continuedCount != null ? continuedCount : 0;
+    }
+
+    public void setContinuedCount(Integer continuedCount) {
+        this.continuedCount = continuedCount;
+    }
+
+    public Integer getFaceSuccessCount() {
+        return faceSuccessCount != null ? faceSuccessCount : 0;
+    }
+
+    public void setFaceSuccessCount(Integer faceSuccessCount) {
+        this.faceSuccessCount = faceSuccessCount;
+    }
+
+    public Integer getFaceFailedCount() {
+        return faceFailedCount != null ? faceFailedCount : 0;
+    }
+
+    public void setFaceFailedCount(Integer faceFailedCount) {
+        this.faceFailedCount = faceFailedCount;
+    }
+
+    public Integer getFaceStrangerCount() {
+        return faceStrangerCount != null ? faceStrangerCount : 0;
+    }
+
+    public void setFaceStrangerCount(Integer faceStrangerCount) {
+        this.faceStrangerCount = faceStrangerCount;
+    }
+
+    public Integer getFaceTotalCount() {
+        return faceTotalCount != null ? faceTotalCount : 0;
+    }
+
+    public void setFaceTotalCount(Integer faceTotalCount) {
+        this.faceTotalCount = faceTotalCount;
+    }
+
+    public Double getFaceSuccessPercent() {
+        return faceSuccessPercent != null ? faceSuccessPercent : 0d;
+    }
+
+    public void setFaceSuccessPercent(Double faceSuccessPercent) {
+        this.faceSuccessPercent = faceSuccessPercent;
+    }
+
+    public String getFaceVerifyResult() {
+        return faceVerifyResult;
+    }
+
+    public void setFaceVerifyResult(String faceVerifyResult) {
+        this.faceVerifyResult = faceVerifyResult;
+    }
+
+    public Double getFaceLandmarkVal() {
+        return faceLandmarkVal != null ? faceLandmarkVal : 0d;
+    }
+
+    public void setFaceLandmarkVal(Double faceLandmarkVal) {
+        this.faceLandmarkVal = faceLandmarkVal;
+    }
+
+    public String getIsIllegality() {
+        return isIllegality;
+    }
+
+    public void setIsIllegality(String isIllegality) {
+        this.isIllegality = isIllegality;
+    }
+
+    public String getPaperTotalScore() {
+        return paperTotalScore;
+    }
+
+    public void setPaperTotalScore(String paperTotalScore) {
+        this.paperTotalScore = paperTotalScore;
+    }
+
+    public String getIsSubmit() {
+        return isSubmit;
+    }
+
+    public void setIsSubmit(String isSubmit) {
+        this.isSubmit = isSubmit;
+    }
+
+    public String getPaperSubmitTime() {
+        return paperSubmitTime;
+    }
+
+    public void setPaperSubmitTime(String paperSubmitTime) {
+        this.paperSubmitTime = paperSubmitTime;
+    }
+
+    public String getExamName() {
+        return examName;
+    }
+
+    public void setExamName(String examName) {
+        this.examName = examName;
+    }
+
+    public String getStudentNumber() {
+        return studentNumber;
+    }
+
+    public void setStudentNumber(String studentNumber) {
+        this.studentNumber = studentNumber;
+    }
+
+    public String getCourseNameAndCode() {
+        return courseNameAndCode;
+    }
+
+    public void setCourseNameAndCode(String courseNameAndCode) {
+        this.courseNameAndCode = courseNameAndCode;
+    }
+
+    public String getGrade() {
+        return grade;
+    }
+
+    public void setGrade(String grade) {
+        this.grade = grade;
+    }
+
+    public String getSpecialtyName() {
+        return specialtyName;
+    }
+
+    public void setSpecialtyName(String specialtyName) {
+        this.specialtyName = specialtyName;
+    }
+
+    public String getOrgName() {
+        return orgName;
+    }
+
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
+
+    public String getObjectiveTotalScore() {
+        return objectiveTotalScore;
+    }
+
+    public void setObjectiveTotalScore(String objectiveTotalScore) {
+        this.objectiveTotalScore = objectiveTotalScore;
+    }
+
+    public String getSubjectiveTotalScore() {
+        return subjectiveTotalScore;
+    }
+
+    public void setSubjectiveTotalScore(String subjectiveTotalScore) {
+        this.subjectiveTotalScore = subjectiveTotalScore;
+    }
+
+    public String getDisciplineSign() {
+        return disciplineSign;
+    }
+
+    public void setDisciplineSign(String disciplineSign) {
+        this.disciplineSign = disciplineSign;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getExamTime() {
+        return examTime;
+    }
+
+    public void setExamTime(String examTime) {
+        this.examTime = examTime;
+    }
+
+    public String getPaperStartTime() {
+        return paperStartTime;
+    }
+
+    public void setPaperStartTime(String paperStartTime) {
+        this.paperStartTime = paperStartTime;
+    }
+
+	public Boolean getIsWarn() {
+		return isWarn;
+	}
+
+	public void setIsWarn(Boolean isWarn) {
+		this.isWarn = isWarn;
+	}
+
+	public Boolean getIsAudit() {
+		return isAudit;
+	}
+
+	public void setIsAudit(Boolean isAudit) {
+		this.isAudit = isAudit;
+	}
+
+	public Boolean getIsReexamine() {
+		return isReexamine;
+	}
+
+	public void setIsReexamine(Boolean isReexamine) {
+		this.isReexamine = isReexamine;
+	}
+
+	public Boolean getIsContinued() {
+		return isContinued;
+	}
+
+	public void setIsContinued(Boolean isContinued) {
+		this.isContinued = isContinued;
+	}
+
+	public Double getBaiduFaceLivenessSuccessPercent() {
+		return baiduFaceLivenessSuccessPercent;
+	}
+
+	public void setBaiduFaceLivenessSuccessPercent(
+			Double baiduFaceLivenessSuccessPercent) {
+		this.baiduFaceLivenessSuccessPercent = baiduFaceLivenessSuccessPercent;
+	}
+
+	public String getOfflineFileUrl() {
+		return offlineFileUrl;
+	}
+
+	public void setOfflineFileUrl(String offlineFileUrl) {
+		this.offlineFileUrl = offlineFileUrl;
+	}
+    
+}

+ 58 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordMapper.java

@@ -0,0 +1,58 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-24 11:22:55.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord;
+
+/**
+ * 实体元数据
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/24
+ */
+public interface ExamRecordMapper {
+
+    String EXAM_RECORD_DATA_ID = "id";
+    String EXAM_RECORD_ID = "examRecord.id";
+    String EXAM_RECORD_EXAM_ID = "examRecord.examId";
+    String EXAM_RECORD_EXAM_TYPE = "examRecord.examType";
+    String EXAM_RECORD_EXAM_STUDENT_ID = "examRecord.examStudentId";
+    String EXAM_RECORD_STUDENT_ID = "examRecord.studentId";
+    String EXAM_RECORD_STUDENT_CODE = "examRecord.studentCode";
+    String EXAM_RECORD_STUDENT_NAME = "examRecord.studentName";
+    String EXAM_RECORD_IDENTITY_NUMBER = "examRecord.identityNumber";
+    String EXAM_RECORD_COURSE_ID = "examRecord.courseId";
+    String EXAM_RECORD_COURSE_CODE = "examRecord.courseCode";
+    String EXAM_RECORD_COURSE_NAME = "examRecord.courseName";
+    String EXAM_RECORD_COURSE_LEVEL = "examRecord.courseLevel";
+    String EXAM_RECORD_ORG_ID = "examRecord.orgId";
+    String EXAM_RECORD_ROOT_ORG_ID = "examRecord.rootOrgId";
+    String EXAM_RECORD_BASE_PAPER_ID = "examRecord.basePaperId";
+    String EXAM_RECORD_PAPER_STRUCT_ID = "examRecord.paperStructId";
+    String START_TIME = "startTime";
+    String END_TIME = "endTime";
+    String CLEAN_TIME = "cleanTime";
+    String IS_WARN = "isWarn";
+    String IS_AUDIT = "isAudit";
+    String IS_ILLEGALITY = "isIllegality";
+    String INFO_COLLECTOR = "infoCollector";
+    String EXAM_RECORD_STATUS = "examRecordStatus";
+    String USED_EXAM_TIME = "usedExamTime";
+    String EXAM_ORDER = "examOrder";
+    String IS_REEXAMINE = "isReexamine";
+    String IS_CONTINUED = "isContinued";
+    String CONTINUED_COUNT = "continuedCount";
+    String FACE_SUCCESS_COUNT = "faceSuccessCount";
+    String FACE_FAILED_COUNT = "faceFailedCount";
+    String FACE_STRANGER_COUNT = "faceStrangerCount";
+    String FACE_TOTAL_COUNT = "faceTotalCount";
+    String FACE_SUCCESS_PERCENT = "faceSuccessPercent";
+    String FACE_VERIFY_RESULT = "faceVerifyResult";
+    String FACE_LANDMARK_VAL = "faceLandmarkVal";
+    String UPDATE_TIME = "updateTime";
+    String CREATION_TIME = "creationTime";
+
+}

+ 276 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordQuery.java

@@ -0,0 +1,276 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-24 11:22:55.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamRecordStatus;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 考试记录查询条件类
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+@ApiModel
+public class ExamRecordQuery implements JsonSerializable {
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 4227973258005786636L;
+	private Long examRecordDataId;
+    @ApiModelProperty("当前页数")
+    private Integer pageNo;
+    @ApiModelProperty("每页条数")
+    private Integer pageSize;
+    @ApiModelProperty("学习中心ID")
+    private Long orgId;
+    @ApiModelProperty("考试ID")
+    private Long examId;
+    @ApiModelProperty("学号")
+    private String studentCode;
+    @ApiModelProperty("学生姓名")
+    private String studentName;
+    @ApiModelProperty("身份证号")
+    private String identityNumber;
+    @ApiModelProperty("课程ID")
+    private Long courseId;
+    @ApiModelProperty("课程层次")
+    private String courseLevel;
+    @ApiModelProperty("考试记录ID")
+    private Long examRecordId;
+    @ApiModelProperty("审核状态")
+    private Boolean isAudit;
+    @ApiModelProperty("是否需要审核")
+    private Boolean isWarn;
+    @ApiModelProperty("是否有陌生人脸")
+    private Boolean hasStranger;
+    @ApiModelProperty("人脸识别成功率下限")
+    private Double faceSuccessPercentLower;
+    @ApiModelProperty("人脸识别成功率上限")
+    private Double faceSuccessPercentUpper;
+    @ApiModelProperty("人脸真实性比率下限")
+    private Double livenessSuccessPercentLower;
+    @ApiModelProperty("人脸真实性比率上限")
+    private Double livenessSuccessPercentUpper;
+    @ApiModelProperty("考试记录状态:EXAM_ING、EXAM_END、EXAM_OVERDUE、EXAM_INVALID")
+    private List<String> recordStatuses;
+    @ApiModelProperty("采集人")
+    private String infoCollector;
+    @ApiModelProperty("考试开始时间")
+    private String startTime;
+    @ApiModelProperty("考试结束时间")
+    private String endTime;
+    
+    public ExamRecordQuery addRecordStatus(String recordStatus) {
+        if (recordStatuses == null) {
+            recordStatuses = new ArrayList<>();
+        }
+        if (StringUtils.isNotBlank(recordStatus)) {
+            recordStatuses.add(recordStatus);
+        }
+        return this;
+    }
+
+    public List<ExamRecordStatus> getRecordStatusList() {
+        List<ExamRecordStatus> list = new ArrayList<>();
+        if (recordStatuses != null) {
+            for (String name : recordStatuses) {
+                ExamRecordStatus status = ExamRecordStatus.getByName(name);
+                if (status != null) {
+                    list.add(status);
+                }
+            }
+        }
+        return list;
+    }
+
+    public List<String> getRecordStatuses() {
+        return recordStatuses;
+    }
+
+    public void setRecordStatuses(List<String> recordStatuses) {
+        this.recordStatuses = recordStatuses;
+    }
+
+    public Integer getPageNo() {
+        return pageNo;
+    }
+
+    public void setPageNo(Integer pageNo) {
+        this.pageNo = pageNo;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+    public Long getExamRecordId() {
+        return examRecordId;
+    }
+
+    public void setExamRecordId(Long examRecordId) {
+        this.examRecordId = examRecordId;
+    }
+
+    public Boolean getHasStranger() {
+        return hasStranger;
+    }
+
+    public void setHasStranger(Boolean hasStranger) {
+        this.hasStranger = hasStranger;
+    }
+
+    public Double getFaceSuccessPercentLower() {
+        return faceSuccessPercentLower;
+    }
+
+    public void setFaceSuccessPercentLower(Double faceSuccessPercentLower) {
+        this.faceSuccessPercentLower = faceSuccessPercentLower;
+    }
+
+    public Double getFaceSuccessPercentUpper() {
+        return faceSuccessPercentUpper;
+    }
+
+    public void setFaceSuccessPercentUpper(Double faceSuccessPercentUpper) {
+        this.faceSuccessPercentUpper = faceSuccessPercentUpper;
+    }
+
+    public Double getLivenessSuccessPercentLower() {
+		return livenessSuccessPercentLower;
+	}
+
+	public void setLivenessSuccessPercentLower(Double livenessSuccessPercentLower) {
+		this.livenessSuccessPercentLower = livenessSuccessPercentLower;
+	}
+
+	public Double getLivenessSuccessPercentUpper() {
+		return livenessSuccessPercentUpper;
+	}
+
+	public void setLivenessSuccessPercentUpper(Double livenessSuccessPercentUpper) {
+		this.livenessSuccessPercentUpper = livenessSuccessPercentUpper;
+	}
+
+	public String getInfoCollector() {
+        return infoCollector;
+    }
+
+    public void setInfoCollector(String infoCollector) {
+        this.infoCollector = infoCollector;
+    }
+
+    public String getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(String startTime) {
+        this.startTime = startTime;
+    }
+
+    public String getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(String endTime) {
+        this.endTime = endTime;
+    }
+
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public Boolean getIsAudit() {
+		return isAudit;
+	}
+
+	public void setIsAudit(Boolean isAudit) {
+		this.isAudit = isAudit;
+	}
+
+	public Boolean getIsWarn() {
+		return isWarn;
+	}
+
+	public void setIsWarn(Boolean isWarn) {
+		this.isWarn = isWarn;
+	}
+
+}

+ 126 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamStudentQuestionScoreInfo.java

@@ -0,0 +1,126 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.excel.ExcelProperty;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+/**
+ * 
+ * @author lideyin
+ * @date 2019年3月14日 下午2:37:57
+ * @description 考生作答得分详情
+ */
+public class ExamStudentQuestionScoreInfo implements JsonSerializable {
+
+	private static final long serialVersionUID = -735354116432038902L;
+	/**
+     * 考试记录ID
+     */
+	@ExcelProperty(name = "考试记录ID", width = 30, index = 1)
+	private Long examRecordDataId;
+    /**
+     * 课程代码
+     */
+	@ExcelProperty(name = "课程代码", width = 30, index = 2)
+	private String courseCode;
+    /**
+     * 课程名称
+     */
+	@ExcelProperty(name = "课程名称", width = 30, index = 3)
+	private String courseName;
+    /**
+     * 考试名称
+     */
+	@ExcelProperty(name = "考试名称", width = 30, index = 4)
+	private String examName;
+   /**
+    * 学号
+    */
+	@ExcelProperty(name = "学号", width = 30, index = 5)
+	private String studentCode;
+    /**
+     * 原始试卷Id
+     */
+	@ExcelProperty(name = "原始试卷Id", width = 30, index = 6)
+	private String basePaperId;
+    /**
+     * 大题号
+     */
+	@ExcelProperty(name = "大题号", width = 30, index = 7)
+	private Integer mainNumber;
+    /**
+     * 顺序/小题号
+     */
+	@ExcelProperty(name = "小题号", width = 30, index = 8)
+	private Integer order;
+    /**
+     * 每题得分
+     */
+	@ExcelProperty(name = "每题得分", width = 30, index = 9)
+	private Double studentScore;
+    /**
+     * 考生作答
+     */
+	@ExcelProperty(name = "考生作答", width = 30, index = 10)
+	private String studentAnswer;
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+	public String getCourseCode() {
+		return courseCode;
+	}
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+	
+	public String getExamName() {
+		return examName;
+	}
+	public void setExamName(String examName) {
+		this.examName = examName;
+	}
+	public String getStudentCode() {
+		return studentCode;
+	}
+	public void setStudentCode(String studentCode) {
+		this.studentCode = studentCode;
+	}
+	
+	public String getCourseName() {
+		return courseName;
+	}
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+	public String getBasePaperId() {
+		return basePaperId;
+	}
+	public void setBasePaperId(String basePaperId) {
+		this.basePaperId = basePaperId;
+	}
+	public Integer getMainNumber() {
+		return mainNumber;
+	}
+	public void setMainNumber(Integer mainNumber) {
+		this.mainNumber = mainNumber;
+	}
+	public Integer getOrder() {
+		return order;
+	}
+	public void setOrder(Integer order) {
+		this.order = order;
+	}
+	public Double getStudentScore() {
+		return studentScore;
+	}
+	public void setStudentScore(Double studentScore) {
+		this.studentScore = studentScore;
+	}
+	public String getStudentAnswer() {
+		return studentAnswer;
+	}
+	public void setStudentAnswer(String studentAnswer) {
+		this.studentAnswer = studentAnswer;
+	}
+    
+}

+ 41 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreEntityConvert.java

@@ -0,0 +1,41 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 09:36:00.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentQuery;
+
+/**
+ * 类信息转换
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/31
+ */
+public class ExamScoreEntityConvert {
+
+    public static ExamStudentQuery of(ExamScoreQuery scoreQuery) {
+        ExamStudentQuery studentQuery = new ExamStudentQuery();
+        studentQuery.setPageNo(scoreQuery.getPageNo());
+        studentQuery.setPageSize(scoreQuery.getPageSize());
+        studentQuery.setOrgId(scoreQuery.getOrgId());
+        studentQuery.setExamId(scoreQuery.getExamId());
+        studentQuery.setStudentCode(scoreQuery.getStudentCode());
+        studentQuery.setStudentName(scoreQuery.getStudentName());
+        studentQuery.setIdentityNumber(scoreQuery.getIdentityNumber());
+        studentQuery.setCourseId(scoreQuery.getCourseId());
+        studentQuery.setCourseLevel(scoreQuery.getCourseLevel());
+        if(scoreQuery.getFinished()!=null){
+        	studentQuery.setFinished(scoreQuery.getFinished()?1:0);
+        }else{
+        	studentQuery.setFinished(null);
+        }
+        studentQuery.setStartLimit(scoreQuery.getStartLimit());
+        studentQuery.setEndLimit(scoreQuery.getEndLimit());
+        return studentQuery;
+    }
+
+}

+ 30 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreExcel.java

@@ -0,0 +1,30 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 09:35:32.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 考试分数信息
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/31
+ */
+public class ExamScoreExcel implements JsonSerializable {
+
+    private Long examId;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+}

+ 351 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreInfo.java

@@ -0,0 +1,351 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 09:35:22.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.excel.ExcelProperty;
+
+/**
+ * 考试分数信息
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/31
+ */
+public class ExamScoreInfo implements JsonSerializable {
+    /**
+     * 考试ID
+     */
+    private Long examId;
+    /**
+     * 考试名称
+     */
+    @ExcelProperty(name = "批次名称", width = 30, index = 10)
+    private String examName;
+    /**
+     * 学生ID
+     */
+    private Long studentId;
+    /**
+     * 学号
+     */
+    @ExcelProperty(name = "学号", width = 30, index = 2)
+    private String studentCode;
+    /**
+     * 学生姓名
+     */
+    @ExcelProperty(name = "姓名", width = 30, index = 1)
+    private String studentName;
+    /**
+     * 身份证号
+     */
+    @ExcelProperty(name = "身份证号", width = 30, index = 3)
+    private String identityNumber;
+    /**
+     * 课程ID
+     */
+    private Long courseId;
+    /**
+     * 课程Code
+     */
+    @ExcelProperty(name = "课程代码", width = 30, index = 9)
+    private String courseCode;
+    /**
+     * 课程名称
+     */
+    private String courseName;
+    /**
+     * 课程层次
+     */
+    @ExcelProperty(name = "层次", width = 30, index = 5)
+    private String courseLevel;
+    /**
+     * 课程名称(课程代码)
+     */
+    @ExcelProperty(name = "课程名称(课程代码)", width = 30, index = 8)
+    private String courseNameAndCode;
+    /**
+     * 学习中心ID
+     */
+    private Long orgId;
+    /**
+     * 学习中心代码
+     */
+    private String orgCode;
+    /**
+     * 学习中心名称
+     */
+    @ExcelProperty(name = "学习中心", width = 30, index = 4)
+    private String orgName;
+    /**
+     * 专业名称
+     */
+    @ExcelProperty(name = "专业", width = 30, index = 7)
+    private String specialtyName;
+    /**
+     * 年级
+     */
+    @ExcelProperty(name = "年级", width = 30, index = 6)
+    private String grade;
+    /**
+     * 是否完成考试
+     */
+    private String isFinished;
+    
+    
+    @ExcelProperty(name = "是否缺考", width = 30, index = 13)
+    private String isAbsent;
+    
+    /**
+     * 最终成绩
+     */
+    @ExcelProperty(name = "考试最终成绩", width = 30, index = 17)
+    private String finalExamScore;
+    /**
+     * 客观题得分总分
+     */
+    @ExcelProperty(name = "客观题得分", width = 30, index = 15)
+    private String objectiveScore;
+    /**
+     * 主观题得分总分
+     */
+    @ExcelProperty(name = "主观题得分", width = 30, index = 16)
+    private String subjectiveScore;
+
+    /**
+     * 违纪次数
+     */
+    @ExcelProperty(name = "违纪次数", width = 30, index = 11)
+    private String disciplineCount;
+    /**
+     * 正常提交试卷次数
+     */
+    @ExcelProperty(name = "正常提交试卷次数", width = 30, index = 12)
+    private String submitCount;
+    /**
+     * 考试开始时间
+     */
+    @ExcelProperty(name = "成绩统计时间", width = 31, index = 14)
+    private String startTime;
+    
+    /**
+     * 剩余考试次数
+     */
+    private Long leftExamTimes;
+    /**
+     * 采集人
+     */
+    @ExcelProperty(name = "采集人", width = 30, index = 18)
+    private String infoCollector;
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    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 getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public String getOrgCode() {
+        return orgCode;
+    }
+
+    public void setOrgCode(String orgCode) {
+        this.orgCode = orgCode;
+    }
+
+    public String getOrgName() {
+        return orgName;
+    }
+
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
+
+    public String getSpecialtyName() {
+        return specialtyName;
+    }
+
+    public void setSpecialtyName(String specialtyName) {
+        this.specialtyName = specialtyName;
+    }
+
+    public String getGrade() {
+        return grade;
+    }
+
+    public void setGrade(String grade) {
+        this.grade = grade;
+    }
+
+    public String getCourseNameAndCode() {
+        return courseNameAndCode;
+    }
+
+    public void setCourseNameAndCode(String courseNameAndCode) {
+        this.courseNameAndCode = courseNameAndCode;
+    }
+
+    public String getIsFinished() {
+        return isFinished;
+    }
+
+    public void setIsFinished(String isFinished) {
+        this.isFinished = isFinished;
+    }
+
+    public String getFinalExamScore() {
+        return finalExamScore;
+    }
+
+    public void setFinalExamScore(String finalExamScore) {
+        this.finalExamScore = finalExamScore;
+    }
+
+    public String getDisciplineCount() {
+        return disciplineCount;
+    }
+
+    public void setDisciplineCount(String disciplineCount) {
+        this.disciplineCount = disciplineCount;
+    }
+
+    public String getSubmitCount() {
+        return submitCount;
+    }
+
+    public void setSubmitCount(String submitCount) {
+        this.submitCount = submitCount;
+    }
+
+    public String getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(String startTime) {
+        this.startTime = startTime;
+    }
+
+	public String getIsAbsent() {
+		return isAbsent;
+	}
+
+	public void setIsAbsent(String isAbsent) {
+		this.isAbsent = isAbsent;
+	}
+
+	public Long getLeftExamTimes() {
+		return leftExamTimes;
+	}
+
+	public void setLeftExamTimes(Long leftExamTimes) {
+		this.leftExamTimes = leftExamTimes;
+	}
+
+	public String getInfoCollector() {
+		return infoCollector;
+	}
+
+	public void setInfoCollector(String infoCollector) {
+		this.infoCollector = infoCollector;
+	}
+
+    public String getObjectiveScore() {
+        return objectiveScore;
+    }
+
+    public void setObjectiveScore(String objectiveScore) {
+        this.objectiveScore = objectiveScore;
+    }
+
+    public String getSubjectiveScore() {
+        return subjectiveScore;
+    }
+
+    public void setSubjectiveScore(String subjectiveScore) {
+        this.subjectiveScore = subjectiveScore;
+    }
+}

+ 18 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreMapper.java

@@ -0,0 +1,18 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 09:36:14.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore;
+
+/**
+ * 实体元数据
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/31
+ */
+public class ExamScoreMapper {
+
+}

+ 148 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreQuery.java

@@ -0,0 +1,148 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 09:35:03.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 考试分数信息查询条件类
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/31
+ */
+@ApiModel
+public class ExamScoreQuery implements JsonSerializable {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 7583473311721120552L;
+	
+	@ApiModelProperty("当前页数")
+    private Integer pageNo;
+    @ApiModelProperty("每页条数")
+    private Integer pageSize;
+    @ApiModelProperty("学习中心ID")
+    private Long orgId;
+    @ApiModelProperty("考试ID")
+    private Long examId;
+    @ApiModelProperty("学号")
+    private String studentCode;
+    @ApiModelProperty("学生姓名")
+    private String studentName;
+    @ApiModelProperty("身份证号")
+    private String identityNumber;
+    @ApiModelProperty("课程ID")
+    private Long courseId;
+    @ApiModelProperty("课程层次")
+    private String courseLevel;
+    @ApiModelProperty("是否完成考试")
+    private Boolean finished;
+    
+    private Integer startLimit;
+    
+    private Integer endLimit;
+    
+    public Integer getPageNo() {
+        return pageNo;
+    }
+
+    public void setPageNo(Integer pageNo) {
+        this.pageNo = pageNo;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+	public Boolean getFinished() {
+		return finished;
+	}
+
+	public void setFinished(Boolean finished) {
+		this.finished = finished;
+	}
+
+	public Integer getStartLimit() {
+		return startLimit;
+	}
+
+	public void setStartLimit(Integer startLimit) {
+		this.startLimit = startLimit;
+	}
+
+	public Integer getEndLimit() {
+		return endLimit;
+	}
+
+	public void setEndLimit(Integer endLimit) {
+		this.endLimit = endLimit;
+	}
+	
+}

+ 100 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/CourseProgressInfo.java

@@ -0,0 +1,100 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年10月25日 下午2:46:03
+ * @company 	QMTH
+ * @description 课程进度
+ */
+public class CourseProgressInfo implements JsonSerializable {
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 5188606307210455329L;
+
+	private Long examId;
+	
+	private Long courseId;
+	
+	private String courseCode;
+	
+	private String courseName;
+	
+	private Integer completedNum;
+	
+	private Integer noCompletedNum;
+	
+	private Integer allNum;
+	
+	private Double completedProportion;
+
+	public String getCourseCode() {
+		return courseCode;
+	}
+
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+
+	public String getCourseName() {
+		return courseName;
+	}
+
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+
+	public Integer getCompletedNum() {
+		return completedNum;
+	}
+
+	public void setCompletedNum(Integer completedNum) {
+		this.completedNum = completedNum;
+	}
+
+	public Integer getNoCompletedNum() {
+		return noCompletedNum;
+	}
+
+	public void setNoCompletedNum(Integer noCompletedNum) {
+		this.noCompletedNum = noCompletedNum;
+	}
+
+	public Integer getAllNum() {
+		return allNum;
+	}
+
+	public void setAllNum(Integer allNum) {
+		this.allNum = allNum;
+	}
+
+	public Long getExamId() {
+		return examId;
+	}
+
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+
+	public Long getCourseId() {
+		return courseId;
+	}
+
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
+
+	public Double getCompletedProportion() {
+		return completedProportion;
+	}
+
+	public void setCompletedProportion(Double completedProportion) {
+		this.completedProportion = completedProportion;
+	}
+	
+}

+ 223 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentEntityConvert.java

@@ -0,0 +1,223 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-24 11:23:29.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.commons.beanutils.BeanUtils;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.commons.exception.ExamCloudRuntimeException;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.CourseLevel;
+
+/**
+ * 类信息转换
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/21
+ */
+public class ExamStudentEntityConvert {
+
+    public static ExamStudentEntity of(ExamStudentInfo info) {
+        if (info == null) {
+            return null;
+        }
+        ExamStudentEntity entity = new ExamStudentEntity();
+        entity.setId(info.getId());
+        entity.setExamStudentId(info.getExamStudentId());
+        entity.setExamId(info.getExamId());
+        entity.setStudentId(info.getStudentId());
+        entity.setStudentCode(info.getStudentCode());
+        entity.setStudentName(info.getStudentName());
+        entity.setIdentityNumber(info.getIdentityNumber());
+        entity.setCourseId(info.getCourseId());
+        entity.setCourseCode(info.getCourseCode());
+        entity.setCourseLevel(info.getCourseLevel());
+        entity.setOrgId(info.getOrgId());
+        entity.setRootOrgId(info.getRootOrgId());
+        entity.setSpecialtyCode(info.getSpecialtyCode());
+        entity.setSpecialtyName(info.getSpecialtyName());
+        entity.setPaperType(info.getPaperType());
+        entity.setInfoCollector(info.getInfoCollector());
+        entity.setFinished(info.getFinished());
+        entity.setNormalExamTimes(info.getNormalExamTimes());
+        entity.setIsReExamine(info.getIsReExamine());
+        entity.setReExamineCompleted(info.getReExamineCompleted());
+        return entity;
+    }
+
+    public static ExamStudentInfo of(ExamStudentEntity entity) {
+        if (entity == null) {
+            return null;
+        }
+        ExamStudentInfo info = new ExamStudentInfo();
+        info.setId(entity.getId());
+        info.setExamStudentId(entity.getExamStudentId());
+        info.setExamId(entity.getExamId());
+        info.setStudentId(entity.getStudentId());
+        info.setStudentCode(entity.getStudentCode());
+        info.setStudentName(entity.getStudentName());
+        info.setIdentityNumber(entity.getIdentityNumber());
+        info.setCourseId(entity.getCourseId());
+        info.setCourseCode(entity.getCourseCode());
+        info.setCourseLevel(CourseLevel.getCourseLevelTitle(entity.getCourseLevel()));
+        info.setOrgId(entity.getOrgId());
+        info.setRootOrgId(entity.getRootOrgId());
+        info.setSpecialtyCode(entity.getSpecialtyCode());
+        info.setSpecialtyName(entity.getSpecialtyName());
+        info.setPaperType(entity.getPaperType());
+        info.setInfoCollector(entity.getInfoCollector());
+        info.setFinished(entity.getFinished());
+        info.setNormalExamTimes(entity.getNormalExamTimes());
+        info.setIsReExamine(entity.getIsReExamine());
+        info.setReExamineCompleted(entity.getReExamineCompleted());
+        info.setGrade(entity.getGrade());
+        return info;
+    }
+    
+    public static List<ExamStudentExcel> ofExcel(List<ExamStudentInfo> examStudentInfoList) {
+        List<ExamStudentExcel> list = new ArrayList<>();
+        if (examStudentInfoList == null || examStudentInfoList.size() == 0) {
+            return list;
+        }
+        examStudentInfoList.forEach(e -> {
+            ExamStudentExcel excel = new ExamStudentExcel();
+            excel.setStudentCode(e.getStudentCode());
+            excel.setStudentName(e.getStudentName());
+            excel.setIdentityNumber(e.getIdentityNumber());
+            excel.setGrade(e.getGrade());
+            excel.setPhone(e.getPhone());
+            excel.setInfoCollector(e.getInfoCollector());
+            excel.setCourseCode(e.getCourseCode());
+            excel.setCourseName(e.getCourseName());
+            excel.setOrgName(e.getOrgName());
+            excel.setFinished(e.getFinishedStatus());
+            list.add(excel);
+        });
+        return list;
+    }
+
+    public static List<ExamStudentUnFinishedExcel> ofUnFinishedExcel(Page<ExamStudentInfo> page) {
+        List<ExamStudentUnFinishedExcel> list = new ArrayList<>();
+        if (page == null || page.getContent() == null) {
+            return list;
+        }
+        page.getContent().forEach(e -> {
+            ExamStudentUnFinishedExcel excel = new ExamStudentUnFinishedExcel();
+            excel.setStudentCode(e.getStudentCode());
+            excel.setStudentName(e.getStudentName());
+            excel.setIdentityNumber(e.getIdentityNumber());
+            excel.setCourseCode(e.getCourseCode());
+            excel.setCourseName(e.getCourseName());
+            excel.setOrgCode(e.getOrgCode());
+            excel.setOrgName(e.getOrgName());
+            excel.setInfoCollector(e.getInfoCollector());
+            excel.setPaperType(e.getPaperType());
+            excel.setSpecialtyName(e.getSpecialtyName());
+            excel.setExamSiteName(e.getExamSiteName());
+            excel.setGrade(e.getGrade());
+            excel.setPhone(e.getPhone());
+            list.add(excel);
+        });
+        return list;
+    }
+    
+    
+    public static List<ExamStudentUnFinishedExcel> ofUnFinishedExcel(List<ExamStudentInfo> examStudentInfos) {
+        List<ExamStudentUnFinishedExcel> list = new ArrayList<>();
+        if (examStudentInfos == null || examStudentInfos.size() == 0) {
+            return list;
+        }
+        examStudentInfos.forEach(e -> {
+            ExamStudentUnFinishedExcel excel = new ExamStudentUnFinishedExcel();
+            excel.setStudentCode(e.getStudentCode());
+            excel.setStudentName(e.getStudentName());
+            excel.setIdentityNumber(e.getIdentityNumber());
+            excel.setCourseCode(e.getCourseCode());
+            excel.setCourseName(e.getCourseName());
+            excel.setOrgCode(e.getOrgCode());
+            excel.setOrgName(e.getOrgName());
+            excel.setInfoCollector(e.getInfoCollector());
+            excel.setPaperType(e.getPaperType());
+            excel.setSpecialtyName(e.getSpecialtyName());
+            excel.setExamSiteName(e.getExamSiteName());
+            excel.setGrade(e.getGrade());
+            excel.setPhone(e.getPhone());
+            list.add(excel);
+        });
+        return list;
+    }
+    
+    
+
+    public static Page<ExamStudentInfo> of(Page<ExamStudentEntity> page) {
+        Pageable pageable = new PageRequest(page.getNumber(), page.getSize());
+        List<ExamStudentEntity> entities = page.getContent();
+        if (entities == null || entities.size() == 0) {
+            return new PageImpl<>(Lists.newArrayList(), pageable, page.getTotalElements());
+        }
+        List<ExamStudentInfo> list = entities.stream().map(entity -> of(entity)).collect(Collectors.toList());
+        return new PageImpl<>(list, pageable, page.getTotalElements());
+    }
+    
+    public static List<ExamStudentInfo> of(List<HashMap> maps) {
+        if (maps == null || maps.size() == 0) {
+            return Lists.newArrayList();
+        }
+        List<ExamStudentInfo> list = new ArrayList<>();
+        for (Map map : maps) {
+            try {
+                //转换Map数据
+                ExamStudentInfo info = new ExamStudentInfo();
+                BeanUtils.populate(info, map);
+                //转换枚举名称
+                info.setCourseLevel(CourseLevel.getCourseLevelTitle(info.getCourseLevel()));
+                list.add(info);
+            } catch (Exception e) {
+                throw new ExamCloudRuntimeException(e.getMessage(), e.getCause());
+            }
+        }
+        return list;
+    }
+
+    public static List<ExamStudentOrgStatistic> ofList(List<HashMap> maps) {
+        if (maps == null || maps.size() == 0) {
+            return Lists.newArrayList();
+        }
+        List<ExamStudentOrgStatistic> list = new ArrayList<>();
+        for (Map map : maps) {
+            try {
+                //转换Map数据
+                ExamStudentOrgStatistic info = new ExamStudentOrgStatistic();
+                BeanUtils.populate(info, map);
+                if (info.getTotalCount() == 0 || info.getFinishedCount() == 0) {
+                    info.setFinishedPercent("0");
+                } else {
+                    double percent = (double) info.getFinishedCount() / info.getTotalCount();
+                    info.setFinishedPercent(new DecimalFormat("#.00").format(percent * 100));
+                }
+                list.add(info);
+            } catch (Exception e) {
+                throw new ExamCloudRuntimeException(e.getMessage(), e.getCause());
+            }
+        }
+        return list;
+    }
+
+}

+ 123 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentExcel.java

@@ -0,0 +1,123 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-30 13:40:36.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent;
+
+
+import java.io.Serializable;
+
+import cn.com.qmth.examcloud.core.oe.common.base.utils.excel.ExcelProperty;
+
+/**
+ * 考生信息-Excel格式
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/30
+ */
+public class ExamStudentExcel implements Serializable {
+    @ExcelProperty(name = "学号", index = 1)
+    private String studentCode;
+    @ExcelProperty(name = "考生姓名", index = 2)
+    private String studentName;
+    @ExcelProperty(name = "身份证号", index = 3)
+    private String identityNumber;
+    @ExcelProperty(name = "学生电话", index = 4)
+    private String phone;
+    @ExcelProperty(name = "年级", index = 5)
+    private String grade;
+    @ExcelProperty(name = "采集人", index = 6)
+    private String infoCollector;
+    @ExcelProperty(name = "课程代码", index = 7)
+    private String courseCode;
+    @ExcelProperty(name = "课程", index = 8)
+    private String courseName;
+    @ExcelProperty(name = "学习中心", index = 9)
+    private String orgName;
+    @ExcelProperty(name = "完成状态", index = 10)
+    private String finished;
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public String getPhone() {
+        return phone == null ? "" : phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getGrade() {
+        return grade == null ? "" : grade;
+    }
+
+    public void setGrade(String grade) {
+        this.grade = grade;
+    }
+
+    public String getInfoCollector() {
+        return infoCollector == null ? "" : infoCollector;
+    }
+
+    public void setInfoCollector(String infoCollector) {
+        this.infoCollector = infoCollector;
+    }
+
+    public String getCourseCode() {
+        return courseCode == null ? "" : courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName == null ? "" : courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getOrgName() {
+        return orgName;
+    }
+
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
+
+    public String getFinished() {
+        return finished == null ? "" : finished;
+    }
+
+    public void setFinished(String finished) {
+        this.finished = finished;
+    }
+
+}

+ 44 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentFinishedStatistic.java

@@ -0,0 +1,44 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-30 10:07:33.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 考试统计信息
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/30
+ */
+public class ExamStudentFinishedStatistic implements JsonSerializable {
+    /**
+     * 完成数
+     */
+    private Integer finished;
+    /**
+     * 未完成数
+     */
+    private Integer unFinished;
+
+    public Integer getFinished() {
+        return finished != null ? finished : 0;
+    }
+
+    public void setFinished(Integer finished) {
+        this.finished = finished;
+    }
+
+    public Integer getUnFinished() {
+        return unFinished != null ? unFinished : 0;
+    }
+
+    public void setUnFinished(Integer unFinished) {
+        this.unFinished = unFinished;
+    }
+
+}

+ 398 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentInfo.java

@@ -0,0 +1,398 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-24 11:23:29.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 考生信息
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/15
+ */
+public class ExamStudentInfo implements JsonSerializable {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -3697944484049914650L;
+	/**
+     * 主键
+     */
+    private Long id;
+    /**
+     * 考生ID(与考务的考生ID一致)
+     */
+    private Long examStudentId;
+    /**
+     * 考试ID
+     */
+    private Long examId;
+    
+    private String examType;
+    
+    /**
+     * 学生ID
+     */
+    private Long studentId;
+    /**
+     * 学号
+     */
+    private String studentCode;
+    /**
+     * 学生姓名
+     */
+    private String studentName;
+    /**
+     * 身份证号
+     */
+    private String identityNumber;
+    /**
+     * 课程ID
+     */
+    private Long courseId;
+    /**
+     * 课程Code
+     */
+    private String courseCode;
+    /**
+     * 课程名称
+     */
+    private String courseName;
+    /**
+     * 课程层次
+     */
+    private String courseLevel;
+    /**
+     * 学习中心ID
+     */
+    private Long orgId;
+    /**
+     * 学习中心代码
+     */
+    private String orgCode;
+    /**
+     * 学习中心名称
+     */
+    private String orgName;
+    /**
+     * 顶级机构ID
+     */
+    private Long rootOrgId;
+    /**
+     * 专业code
+     */
+    private String specialtyCode;
+    /**
+     * 专业名称
+     */
+    private String specialtyName;
+    /**
+     * 试卷类型
+     */
+    private String paperType;
+    /**
+     * 采集人
+     */
+    private String infoCollector;
+    /**
+     * 是否完成考试
+     */
+    private Boolean finished;
+    
+    /**
+     * 完成状态
+     * 在线考试:已完成、未完成
+     * 离线考试:已抽题、未抽题、已上传
+     */
+    private String finishedStatus;
+    
+    private String offlineFileUrl;
+    
+    /**
+     * 正常考试次数
+     */
+    private Integer normalExamTimes;
+    /**
+     * 是否重考
+     */
+    private Boolean isReExamine;
+    /**
+     * 重考是否已完成
+     */
+    private Boolean reExamineCompleted;
+    /**
+     * 考点
+     */
+    private String examSiteName;
+    /**
+     * 学生电话
+     */
+    private String phone;
+    /**
+     * 年级
+     */
+    private String grade;
+    /**
+     * 是否允许上传附件
+     */
+    private Boolean canUploadAttachment;
+    
+    /**
+	 * 是否可用
+	 */
+	private Boolean enable;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getExamStudentId() {
+        return examStudentId;
+    }
+
+    public void setExamStudentId(Long examStudentId) {
+        this.examStudentId = examStudentId;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public String getOrgCode() {
+        return orgCode;
+    }
+
+    public void setOrgCode(String orgCode) {
+        this.orgCode = orgCode;
+    }
+
+    public String getOrgName() {
+        return orgName;
+    }
+
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public String getSpecialtyCode() {
+        return specialtyCode;
+    }
+
+    public void setSpecialtyCode(String specialtyCode) {
+        this.specialtyCode = specialtyCode;
+    }
+
+    public String getSpecialtyName() {
+        return specialtyName;
+    }
+
+    public void setSpecialtyName(String specialtyName) {
+        this.specialtyName = specialtyName;
+    }
+
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    public String getInfoCollector() {
+        return infoCollector;
+    }
+
+    public void setInfoCollector(String infoCollector) {
+        this.infoCollector = infoCollector;
+    }
+
+    public Boolean getFinished() {
+        return finished;
+    }
+
+    public void setFinished(Boolean finished) {
+        this.finished = finished;
+    }
+
+    public Integer getNormalExamTimes() {
+        return normalExamTimes;
+    }
+
+    public void setNormalExamTimes(Integer normalExamTimes) {
+        this.normalExamTimes = normalExamTimes;
+    }
+
+    public Boolean getReExamineCompleted() {
+        return reExamineCompleted;
+    }
+
+    public void setReExamineCompleted(Boolean reExamineCompleted) {
+        this.reExamineCompleted = reExamineCompleted;
+    }
+
+    public String getExamSiteName() {
+        return examSiteName;
+    }
+
+    public void setExamSiteName(String examSiteName) {
+        this.examSiteName = examSiteName;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getGrade() {
+        return grade;
+    }
+
+    public void setGrade(String grade) {
+        this.grade = grade;
+    }
+
+	public String getExamType() {
+		return examType;
+	}
+
+	public void setExamType(String examType) {
+		this.examType = examType;
+	}
+
+	public Boolean getIsReExamine() {
+		return isReExamine;
+	}
+
+	public void setIsReExamine(Boolean isReExamine) {
+		this.isReExamine = isReExamine;
+	}
+
+	public Boolean getCanUploadAttachment() {
+		return canUploadAttachment;
+	}
+
+	public void setCanUploadAttachment(Boolean canUploadAttachment) {
+		this.canUploadAttachment = canUploadAttachment;
+	}
+
+	public String getFinishedStatus() {
+		return finishedStatus;
+	}
+
+	public void setFinishedStatus(String finishedStatus) {
+		this.finishedStatus = finishedStatus;
+	}
+
+	public String getOfflineFileUrl() {
+		return offlineFileUrl;
+	}
+
+	public void setOfflineFileUrl(String offlineFileUrl) {
+		this.offlineFileUrl = offlineFileUrl;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+    
+}

+ 78 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentMapper.java

@@ -0,0 +1,78 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-24 11:25:56.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent;
+
+/**
+ * 实体元数据
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/24
+ */
+public class ExamStudentMapper {
+
+    public static final String ID = "id";
+    public static final String EXAM_STUDENT_ID = "examStudentId";
+    public static final String EXAM_ID = "examId";
+    public static final String STUDENT_ID = "studentId";
+    public static final String STUDENT_CODE = "studentCode";
+    public static final String STUDENT_NAME = "studentName";
+    public static final String IDENTITY_NUMBER = "identityNumber";
+    public static final String COURSE_ID = "courseId";
+    public static final String COURSE_CODE = "courseCode";
+    public static final String COURSE_NAME = "courseName";
+    public static final String COURSE_LEVEL = "courseLevel";
+    public static final String ORG_ID = "orgId";
+    public static final String ORG_CODE = "orgCode";
+    public static final String ORG_NAME = "orgName";
+    public static final String ROOT_ORG_ID = "rootOrgId";
+    public static final String SPECIALTY_CODE = "specialtyCode";
+    public static final String SPECIALTY_NAME = "specialtyName";
+    public static final String PAPER_TYPE = "paperType";
+    public static final String INFO_COLLECTOR = "infoCollector";
+    public static final String FINISHED = "finished";
+    public static final String NORMAL_EXAM_TIMES = "normalExamTimes";
+    public static final String IS_RE_EXAMINE = "isReExamine";
+    public static final String RE_EXAMINE_COMPLETED = "reExamineCompleted";
+    public static final String CREATION_TIME = "creationTime";
+    public static final String UPDATE_TIME = "updateTime";
+
+    public static String defaultColumns() {
+        StringBuilder columns = new StringBuilder();
+        columns.append("student.id as id,");
+        columns.append("student.exam_student_id as examStudentId,");
+        columns.append("student.exam_id as examId,");
+        columns.append("student.student_id as studentId,");
+        columns.append("student.student_code as studentCode,");
+        columns.append("student.student_name as studentName,");
+        columns.append("student.identity_number as identityNumber,");
+        columns.append("student.course_id as courseId,");
+        columns.append("student.course_code as courseCode,");
+        columns.append("student.course_level as courseLevel,");
+        columns.append("student.org_id as orgId,");
+        columns.append("student.specialty_name as specialtyName,");
+        columns.append("student.finished as finished,");
+        columns.append("student.normal_exam_times as normalExamTimes");
+        return columns.toString();
+    }
+
+    public static String statisticOrgColumns() {
+        StringBuilder columns = new StringBuilder();
+        columns.append("student.org_id as orgId,");
+        columns.append("COUNT(student.id) as totalCount,");
+        columns.append("SUM(CASE WHEN student.finished = 1 THEN 1 ELSE 0 END) finishedCount");
+        return columns.toString();
+    }
+
+    public static String statisticFinishedColumns() {
+        StringBuilder columns = new StringBuilder();
+        columns.append("SUM(CASE WHEN student.finished = 1 THEN 1 ELSE 0 END) finished,");
+        columns.append("SUM(CASE WHEN student.finished = 1 THEN 0 ELSE 1 END) unFinished");
+        return columns.toString();
+    }
+
+}

+ 92 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentOrgStatistic.java

@@ -0,0 +1,92 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-30 10:01:51.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 考试统计信息
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/15
+ */
+public class ExamStudentOrgStatistic implements JsonSerializable {
+    /**
+     * 学习中心ID
+     */
+    private Long orgId;
+    /**
+     * 学习中心代码
+     */
+    private String orgCode;
+    /**
+     * 学习中心名称
+     */
+    private String orgName;
+    /**
+     * 报考人数(计划数)
+     */
+    private Integer totalCount;
+    /**
+     * 实考人数(完成数)
+     */
+    private Integer finishedCount;
+    /**
+     * 完成比率(%)
+     */
+    private String finishedPercent;
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public String getOrgCode() {
+        return orgCode;
+    }
+
+    public void setOrgCode(String orgCode) {
+        this.orgCode = orgCode;
+    }
+
+    public String getOrgName() {
+        return orgName;
+    }
+
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
+
+    public Integer getTotalCount() {
+        return totalCount != null ? totalCount : 0;
+    }
+
+    public void setTotalCount(Integer totalCount) {
+        this.totalCount = totalCount;
+    }
+
+    public Integer getFinishedCount() {
+        return finishedCount != null ? finishedCount : 0;
+    }
+
+    public void setFinishedCount(Integer finishedCount) {
+        this.finishedCount = finishedCount;
+    }
+
+    public String getFinishedPercent() {
+        return finishedPercent;
+    }
+
+    public void setFinishedPercent(String finishedPercent) {
+        this.finishedPercent = finishedPercent;
+    }
+
+}

+ 68 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentPartInfo.java

@@ -0,0 +1,68 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-24 11:23:29.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 考生信息
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/15
+ */
+public class ExamStudentPartInfo implements JsonSerializable {
+    /**
+     * 学生ID
+     */
+    private Long studentId;
+    /**
+     * 学号
+     */
+    private String studentCode;
+    /**
+     * 学生姓名
+     */
+    private String studentName;
+    /**
+     * 身份证号
+     */
+    private String identityNumber;
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+}

+ 172 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentQuery.java

@@ -0,0 +1,172 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-24 11:23:29.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent;
+
+import cn.com.qmth.examcloud.core.oe.common.enums.SelectType;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 考生信息查询条件类
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+@ApiModel
+public class ExamStudentQuery implements JsonSerializable {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 4853280464208226631L;
+	
+	@ApiModelProperty("当前页数")
+    private Integer pageNo;
+    @ApiModelProperty("每页条数")
+    private Integer pageSize;
+    @ApiModelProperty("学习中心ID")
+    private Long orgId;
+    @ApiModelProperty("考试ID")
+    private Long examId;
+    @ApiModelProperty("学号")
+    private String studentCode;
+    @ApiModelProperty("学生姓名")
+    private String studentName;
+    @ApiModelProperty("身份证号")
+    private String identityNumber;
+    @ApiModelProperty("课程ID")
+    private Long courseId;
+    @ApiModelProperty("课程层次")
+    private String courseLevel;
+    @ApiModelProperty("是否完成考试")
+    private Integer finished;
+    @ApiModelProperty("采集人")
+    private String infoCollector;
+
+    private Integer startLimit;
+    
+    private Integer endLimit;
+    
+    /**
+     * 查询或导出  select  export
+     */
+    private SelectType selectType;
+
+    public Integer getPageNo() {
+        return pageNo;
+    }
+
+    public void setPageNo(Integer pageNo) {
+        this.pageNo = pageNo;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+    public Integer getFinished() {
+        return finished;
+    }
+
+    public void setFinished(Integer finished) {
+        this.finished = finished;
+    }
+
+	public SelectType getSelectType() {
+		return selectType;
+	}
+
+	public void setSelectType(SelectType selectType) {
+		this.selectType = selectType;
+	}
+
+	public Integer getStartLimit() {
+		return startLimit;
+	}
+
+	public void setStartLimit(Integer startLimit) {
+		this.startLimit = startLimit;
+	}
+
+	public Integer getEndLimit() {
+		return endLimit;
+	}
+
+	public void setEndLimit(Integer endLimit) {
+		this.endLimit = endLimit;
+	}
+
+	public String getInfoCollector() {
+		return infoCollector;
+	}
+
+	public void setInfoCollector(String infoCollector) {
+		this.infoCollector = infoCollector;
+	}
+    
+}

+ 151 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentUnFinishedExcel.java

@@ -0,0 +1,151 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-30 13:52:30.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent;
+
+import cn.com.qmth.examcloud.core.oe.common.base.utils.excel.ExcelProperty;
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 考生信息-Excel格式
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/15
+ */
+public class ExamStudentUnFinishedExcel implements JsonSerializable {
+    @ExcelProperty(name = "姓名", index = 1)
+    private String studentName;
+    @ExcelProperty(name = "学号", index = 2)
+    private String studentCode;
+    @ExcelProperty(name = "身份证号", index = 3)
+    private String identityNumber;
+    @ExcelProperty(name = "学习中心代码", index = 4)
+    private String orgCode;
+    @ExcelProperty(name = "学习中心名称", index = 5)
+    private String orgName;
+    @ExcelProperty(name = "课程代码", index = 6)
+    private String courseCode;
+    @ExcelProperty(name = "课程名称", index = 7)
+    private String courseName;
+    @ExcelProperty(name = "试卷类型", index = 8)
+    private String paperType;
+    @ExcelProperty(name = "专业名称", index = 9)
+    private String specialtyName;
+    @ExcelProperty(name = "信息采集人", index = 11)
+    private String infoCollector;
+    @ExcelProperty(name = "考点", index = 10)
+    private String examSiteName;
+    @ExcelProperty(name = "学生电话", index = 12)
+    private String phone;
+    @ExcelProperty(name = "年级", index = 13)
+    private String grade;
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public String getOrgCode() {
+        return orgCode;
+    }
+
+    public void setOrgCode(String orgCode) {
+        this.orgCode = orgCode;
+    }
+
+    public String getOrgName() {
+        return orgName;
+    }
+
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    public String getSpecialtyName() {
+        return specialtyName;
+    }
+
+    public void setSpecialtyName(String specialtyName) {
+        this.specialtyName = specialtyName;
+    }
+
+    public String getInfoCollector() {
+        return infoCollector;
+    }
+
+    public void setInfoCollector(String infoCollector) {
+        this.infoCollector = infoCollector;
+    }
+
+    public String getExamSiteName() {
+        return examSiteName;
+    }
+
+    public void setExamSiteName(String examSiteName) {
+        this.examSiteName = examSiteName;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getGrade() {
+        return grade;
+    }
+
+    public void setGrade(String grade) {
+        this.grade = grade;
+    }
+
+}

+ 321 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamAuditServiceImpl.java

@@ -0,0 +1,321 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-27 16:24:08.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamAuditService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCacheTransferHelper;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit.*;
+import cn.com.qmth.examcloud.core.oe.common.base.jpa.Searcher;
+import cn.com.qmth.examcloud.core.oe.common.base.jpa.SpecUtils;
+import cn.com.qmth.examcloud.core.oe.common.base.jpa.SqlWrapper;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.entity.*;
+import cn.com.qmth.examcloud.core.oe.common.enums.*;
+import cn.com.qmth.examcloud.core.oe.common.repository.*;
+import cn.com.qmth.examcloud.core.oe.common.service.ExamScoreObtainQueueService;
+import cn.com.qmth.examcloud.core.oe.common.service.ExamScorePushQueueService;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.core.oe.common.service.LocalCacheService;
+import cn.com.qmth.examcloud.core.oe.student.api.OeExamRecordForMarkingCloudService;
+import cn.com.qmth.examcloud.core.oe.student.api.request.SaveExamRecordForMarkingReq;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.support.cache.bean.OrgCacheBean;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.StringUtils;
+import org.hibernate.query.NativeQuery;
+import org.hibernate.transform.Transformers;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.List;
+
+import static cn.com.qmth.examcloud.core.oe.admin.service.bean.examaudit.ExamAuditMapper.EXAM_RECORD_DATA_ID;
+
+/**
+ * 考试记录审核相关接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/27
+ */
+@Service
+public class ExamAuditServiceImpl implements ExamAuditService {
+    @Autowired
+    private ExamAuditRepo examAuditRepo;
+    @Autowired
+    private ExamScoreRepo examScoreRepo;
+    @Autowired
+    private EntityManager entityManager;
+    @Autowired
+    private ExamService examService;
+    @Autowired
+    private ExamRecordDataRepo examRecordDataRepo;
+    @Autowired
+    private OeExamRecordForMarkingCloudService oeExamRecordForMarkingCloudService;
+	@Autowired
+	private ExamRecordForMarkingRepo examRecordForMarkingRepo;
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+    @Autowired
+    private ExamScorePushQueueService examScorePushQueueService;
+    @Autowired
+    private ExamScoreObtainQueueService examScoreObtainQueueService;
+    @Autowired
+    private LocalCacheService localCacheService;
+    @Autowired
+    private ExamRecordRepo examRecordRepo;
+    @Override
+    public Page<ExamAuditInfo> getExamAuditList(ExamAuditQuery query) {
+        Check.isNull(query, "查询参数不能为空!");
+        Check.isNull(query.getExamId(), "请先选择考试批次!");
+        //封装查询条件
+        Pageable pageable = SpecUtils.buildPageable(query.getPageNo(), query.getPageSize());
+        final String columns = ExamAuditMapper.defaultColumns();
+        SqlWrapper wrapper = new SqlWrapper()
+                .select(columns).from("ec_oe_exam_audit").as("audit")
+                .innerJoin("ec_oe_exam_record_data").as("data").on("data.id", "audit.exam_record_data_id")
+                .innerJoin("ec_oe_exam_record").as("record").on("record.id", "data.exam_record_id")
+                .where().notEq("data.exam_record_status", ExamRecordStatus.EXAM_INVALID.name());
+        if (query.getOrgId() != null) {
+            wrapper.and().eq("record.org_id", query.getOrgId());
+        }
+        if (query.getExamId() != null) {
+            wrapper.and().eq("record.exam_id", query.getExamId());
+        }
+        if (StringUtils.isNotBlank(query.getStudentCode())) {
+            wrapper.and().like("record.student_code", query.getStudentCode());
+        }
+        if (StringUtils.isNotBlank(query.getStudentName())) {
+            wrapper.and().like("record.student_name", query.getStudentName());
+        }
+        if (StringUtils.isNotBlank(query.getIdentityNumber())) {
+            wrapper.and().like("record.identity_number", query.getIdentityNumber());
+        }
+        if (query.getCourseId() != null) {
+            wrapper.and().eq("record.course_id", query.getCourseId());
+        }
+        if (StringUtils.isNotBlank(query.getCourseLevel())) {
+            wrapper.and().eq("record.course_level", query.getCourseLevel());
+        }
+        if (query.getExamRecordDataId() != null) {
+            wrapper.and().eq("data.id", query.getExamRecordDataId());
+        }
+        if (StringUtils.isNotBlank(query.getStatus())) {
+            wrapper.and().eq("audit.status", query.getStatus());
+        }
+        if(StringUtils.isNoneBlank(query.getDisciplineType())){
+        	wrapper.and().eq("audit.discipline_type", query.getDisciplineType());
+        }
+        if(StringUtils.isNoneBlank(query.getAuditUserName())){
+        	wrapper.and().like("audit.audit_user_name", query.getAuditUserName());
+        }
+        long totalSize = 0;
+        //查询总记录数
+        if(query.getSelectType() == null || query.getSelectType() != SelectType.EXPORT){
+        	final String count = "count(0)";
+            String countSql = wrapper.build().replace(columns, count);
+            Query countQuery = entityManager.createNativeQuery(countSql);
+            BigInteger element = (BigInteger) countQuery.getSingleResult();
+            totalSize = element.longValue();
+            if (totalSize == 0) {
+                return new PageImpl<>(Lists.newArrayList(), pageable, totalSize);
+            }
+        }
+
+        //查询分页记录
+        wrapper.orderBy("record.id", true);
+        Query dataQuery = entityManager.createNativeQuery(wrapper.build());
+
+//        dataQuery.unwrap(SQLQuery.class).setResultTransformer(Transformers.aliasToBean(HashMap.class));
+        dataQuery.unwrap(NativeQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
+        if(query.getSelectType() == null || query.getSelectType() != SelectType.EXPORT){
+	        dataQuery.setFirstResult((int) pageable.getOffset());
+	        dataQuery.setMaxResults(pageable.getPageSize());
+        }
+        List resultList = dataQuery.getResultList();
+        //获取考试名称
+        ExamBean examBean = ExamCacheTransferHelper.getCachedExam(query.getExamId());
+
+        String examName = examBean.getName();
+        List<ExamAuditInfo> list = ExamAuditEntityConvert.of(resultList, examName);
+        
+        for(ExamAuditInfo examAuditInfo:list){
+            OrgCacheBean orgBean = gainBaseDataService.getOrgBean(examAuditInfo.getOrgId());
+            examAuditInfo.setOrgName(orgBean.getName());
+        	CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examAuditInfo.getCourseId());
+        	examAuditInfo.setCourseName(courseBean.getName());
+        	examAuditInfo.setCourseCode(courseBean.getCode());
+        	examAuditInfo.setCourseLevel(CourseLevel.getCourseLevel(courseBean.getLevel()).getTitle());
+        }
+        return new PageImpl<ExamAuditInfo>(list, pageable, totalSize);
+    }
+
+    @Override
+    public ExamAuditEntity getExamAuditByExamRecordDataId(Long examRecordDataId) {
+        if (examRecordDataId == null) {
+            return null;
+        }
+        Searcher searchers = new Searcher()
+                .eq(EXAM_RECORD_DATA_ID, examRecordDataId);
+        Specification<ExamAuditEntity> spec = SpecUtils.buildSearchers(ExamAuditEntity.class, searchers.build());
+        List<ExamAuditEntity> list = examAuditRepo.findAll(spec);
+        if (list != null && list.size() > 0) {
+            return list.get(0);
+        }
+        return null;
+    }
+    
+    @Override
+    public void batchAudit(List<Long> examRecordDataIds, Boolean isPass,User user) {
+        for (Long examRecordDataId : examRecordDataIds) {
+        	DisciplineType disciplineType = isPass?DisciplineType.BATCH_PASS:DisciplineType.BATCH_NOTPASS;
+        	singleAudit(examRecordDataId,isPass,"批量审核",disciplineType,user);
+        }
+    }
+
+	@Override
+	@Transactional
+	public void singleAudit(Long examRecordDataId, Boolean isPass,String disciplineDetail, DisciplineType disciplineType,User user) {
+		ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo,examRecordDataId,ExamRecordDataEntity.class);
+        if (examRecordData == null) {
+            return;
+        }
+        //已经审核过了,这里直接返回,如果需要重新审核,调用重审redoAudit方法
+        if(examRecordData.getIsAudit() !=null && examRecordData.getIsAudit()){
+        	return;
+        }
+        
+        //保存审核记录
+        ExamAuditEntity examAudit = new ExamAuditEntity();
+        examAudit.setExamRecordDataId(examRecordDataId);
+        examAudit.setStatus(isPass ? AuditStatus.PASS : AuditStatus.UN_PASS);
+        examAudit.setDisciplineType(disciplineType);
+        examAudit.setDisciplineDetail(disciplineDetail);
+        if(user != null){
+        	examAudit.setUserId(user.getUserId().toString());
+        	examAudit.setAuditUserName(user.getDisplayName());
+        }
+        examAuditRepo.save(examAudit);
+        
+        //保存考试记录
+        examRecordData.setIsAudit(true);
+        examRecordData.setIsIllegality(isPass?false:true);
+        examRecordDataRepo.save(examRecordData);
+        
+        doAuditPostProcesser(isPass,examRecordData);
+	}
+	
+	@Override
+	public void redoAudit(RedoAuditInfo redoAuditInfo,User user) {
+		for(Long examRecordDataId:redoAuditInfo.getExamRecordDataIds()){
+			ExamAuditEntity examAudit = examAuditRepo.findByExamRecordDataId(examRecordDataId);
+			if(examAudit == null){
+				examAudit = new ExamAuditEntity();
+				examAudit.setCreationTime(new Date());
+				examAudit.setExamRecordDataId(examRecordDataId);
+			}
+			examAudit.setAuditUserName(user.getDisplayName());
+			examAudit.setUserId(user.getUserId().toString());
+			if(StringUtils.isBlank(redoAuditInfo.getDisciplineDetail())){
+				examAudit.setDisciplineDetail("重审");
+			}else{
+				examAudit.setDisciplineDetail(redoAuditInfo.getDisciplineDetail());
+			}
+			examAudit.setDisciplineType(DisciplineType.tramsformByType(redoAuditInfo.getDisciplineType()));
+			examAudit.setStatus(redoAuditInfo.getIsPass()?AuditStatus.PASS:AuditStatus.UN_PASS);
+			examAudit.setUpdateTime(new Date());
+			examAuditRepo.save(examAudit);
+			
+			ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo,examRecordDataId,ExamRecordDataEntity.class);
+			examRecordData.setIsWarn(true);
+			examRecordData.setIsAudit(true);
+			examRecordData.setIsIllegality(!redoAuditInfo.getIsPass());//是否违纪
+			examRecordDataRepo.save(examRecordData);
+			
+			doAuditPostProcesser(redoAuditInfo.getIsPass(),examRecordData);
+		}
+	}
+
+	/**
+	 * 重审后置处理
+	 * @param isPass
+	 * @param examRecordData
+	 */
+	private void doAuditPostProcesser(Boolean isPass,ExamRecordDataEntity examRecordData) {
+		if(isPass){
+			auditPassPostProcesser(examRecordData);
+		}else{
+			auditUnPassPostProcesser(examRecordData);
+		}
+	}
+	
+	/**
+	 * 审核通过后置处理
+	 */
+	private void auditPassPostProcesser(ExamRecordDataEntity examRecordData){
+    	ExamScoreEntity examScore = examScoreRepo.findByExamRecordDataId(examRecordData.getId());
+    	//审核通过且为非全客观题卷,保存阅卷需要数据
+    	saveExamRecordForMarkingWithAudit(examRecordData,examScore);
+    	//保存推送分数队列
+        examScorePushQueueService.saveScoreDataInfoToQueue(examRecordData, examScore.getId());
+        //保存获取分数队列
+        examScoreObtainQueueService.saveExamScoreObtainQueue(examRecordData);
+        ExamRecordEntity examRecord =GlobalHelper.getEntity(examRecordRepo,examRecordData.getExamRecordId(),ExamRecordEntity.class);
+        //发送通知
+        examScoreObtainQueueService.sendObtainScoreNotify(examRecord.getRootOrgId());
+	}
+	
+	/**
+	 * 审核通过且为非全客观题卷,保存阅卷需要数据
+	 * @param examRecordData
+	 * @param examScore
+	 */
+	private void saveExamRecordForMarkingWithAudit(ExamRecordDataEntity examRecordData,ExamScoreEntity examScore){
+    	if(examRecordData.getIsAllObjectivePaper() != null && !examRecordData.getIsAllObjectivePaper()){
+            ExamRecordEntity examRecord =GlobalHelper.getEntity(examRecordRepo,examRecordData.getExamRecordId(),ExamRecordEntity.class);
+    		SaveExamRecordForMarkingReq req = new SaveExamRecordForMarkingReq();
+    		req.setExamRecordDataId(examRecordData.getId());
+    		req.setExamId(examRecord.getExamId());
+    		req.setExamStudentId(examRecord.getExamStudentId());
+    		req.setPaperType(examRecord.getPaperType());
+    		req.setBasePaperId(examRecord.getBasePaperId());
+    		req.setCourseId(examRecord.getCourseId());
+    		req.setObjectiveScore(examScore.getObjectiveScore());
+    		oeExamRecordForMarkingCloudService.saveExamRecordForMarking(req);
+    	}
+	}
+
+	/**
+	 * 审核不通过后置处理
+	 * @param examRecordData
+	 */
+	private void auditUnPassPostProcesser(ExamRecordDataEntity examRecordData) {
+		ExamRecordForMarkingEntity examRecordForMarking = examRecordForMarkingRepo.findByExamRecordDataId(examRecordData.getId());
+		if(examRecordForMarking != null){
+			//删除阅卷需要数据,防止违纪数据进入阅卷
+			examRecordForMarkingRepo.delete(examRecordForMarking);
+		}
+		//保存获取分数队列
+        examScoreObtainQueueService.saveExamScoreObtainQueue(examRecordData);
+        //发送通知
+        ExamRecordEntity examRecord =GlobalHelper.getEntity(examRecordRepo,examRecordData.getExamRecordId(),ExamRecordEntity.class);
+        examScoreObtainQueueService.sendObtainScoreNotify(examRecord.getRootOrgId());
+	}
+}

+ 237 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamCaptureServiceImpl.java

@@ -0,0 +1,237 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 17:24:19.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCacheTransferHelper;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.CourseCacheBean;
+import org.apache.commons.lang3.StringUtils;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.google.common.collect.Lists;
+import com.netflix.discovery.CommonConstants;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamAuditService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCaptureService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examcapture.ExamCaptureAuditInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examcapture.ExamCaptureInfo;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamAuditEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamCaptureEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.AuditStatus;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamCaptureRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+
+/**
+ * 考试抓拍检测相关接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/29
+ */
+@Service
+public class ExamCaptureServiceImpl implements ExamCaptureService {
+    @Autowired
+    private ExamCaptureRepo examCaptureRepo;
+    @Autowired
+    private ExamAuditService examAuditService;
+    @Autowired
+    private ExamRecordService examRecordService;
+    @Autowired
+    private ExamRecordDataRepo examRecordDataRepo;
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+    @Autowired
+    private ExamRecordRepo examRecordRepo;
+
+    @Override
+    public ExamCaptureAuditInfo getExamCaptureAuditDetail(Long examRecordDataId) {
+        if (examRecordDataId == null) {
+            return null;
+        }
+        ExamRecordDataEntity recordData = GlobalHelper.getEntity(examRecordDataRepo,examRecordDataId,ExamRecordDataEntity.class);
+        if (recordData == null) {
+            return null;
+        }
+        ExamRecordEntity examRecord =GlobalHelper.getEntity(examRecordRepo,recordData.getExamRecordId(),ExamRecordEntity.class);
+
+        ExamAuditEntity audit = examAuditService.getExamAuditByExamRecordDataId(examRecordDataId);
+        ExamCaptureAuditInfo detail = new ExamCaptureAuditInfo();
+        if (audit != null) {
+             if (audit.getStatus() != null) {
+                 detail.setStatus(audit.getStatus().getName());
+             } else {
+                 detail.setStatus(AuditStatus.UN_PASS.getName());
+             }
+             detail.setDisciplineDetail(audit.getDisciplineDetail());
+             if(audit.getDisciplineType() != null){
+             	detail.setDisciplineType(audit.getDisciplineType().getName());
+             }
+        }
+        
+        detail.setExamRecordDataId(examRecordDataId);
+        detail.setExamRecordId(examRecord.getId());
+        detail.setStudentId(examRecord.getStudentId());
+        detail.setStudentCode(examRecord.getStudentCode());
+        detail.setStudentName(examRecord.getStudentName());
+        detail.setIdentityNumber(examRecord.getIdentityNumber());
+        
+        CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examRecord.getCourseId());
+
+        detail.setCourseCode(courseBean.getCode());
+        detail.setCourseName(courseBean.getName());
+        
+        detail.setIsWarn(recordData.getIsWarn());
+        detail.setIsAudit(recordData.getIsAudit());
+        detail.setIsIllegality(recordData.getIsIllegality());
+        detail.setFaceSuccessCount(recordData.getFaceSuccessCount());
+        detail.setFaceFailedCount(recordData.getFaceFailedCount());
+        detail.setFaceStrangerCount(recordData.getFaceStrangerCount());
+        detail.setFaceTotalCount(recordData.getFaceTotalCount());
+        detail.setFaceSuccessPercent(recordData.getFaceSuccessPercent());
+        detail.setFaceLandmarkVal(recordData.getFaceLandmarkVal());
+        detail.setBaiduFaceLivenessSuccessPercent(recordData.getBaiduFaceLivenessSuccessPercent());
+        if(recordData.getFaceVerifyResult()!=null){
+        	detail.setFaceVerifyResult(recordData.getFaceVerifyResult().name());
+        }
+        detail.setVirtualCameraNames(getVirtualCameraNames(examRecordDataId));
+        return detail;
+    }
+
+    /**
+     * 获取虚拟摄像头名称
+     * @param examRecordDataId
+     * @return
+     */
+    private String getVirtualCameraNames(Long examRecordDataId){
+    	 Set<String> virtualCameraNames = new HashSet<String>();
+         List<ExamCaptureEntity> examCaptureList = examCaptureRepo.findByExamRecordDataId(examRecordDataId);
+         if(examCaptureList != null && examCaptureList.size() >0){
+         	for(ExamCaptureEntity examCapture:examCaptureList){
+         		String cameraInfos = examCapture.getCameraInfos();
+         		if(StringUtils.isNotBlank(cameraInfos)){
+         			JSONArray jsonArray;
+					try {
+						jsonArray = new JSONArray(cameraInfos);						
+						for(int i = 0;i<jsonArray.length();i++){
+	         				JSONObject jsonObject = (JSONObject) jsonArray.get(i);
+	         				if(StringUtils.isBlank(jsonObject.getString("pid"))){
+	         					virtualCameraNames.add(jsonObject.getString("name"));
+	         				}
+	         			}
+					} catch (JSONException e) {
+						e.printStackTrace();
+						throw new StatusException("ExamCaptureService-001", "获取虚拟摄像头名称失败");
+					}
+         			
+         		}
+         	}
+         }
+         StringBuffer sb = new StringBuffer();
+         for(String cameraName:virtualCameraNames){
+        	 sb.append(cameraName).append(";");
+         }
+         return sb.toString();
+    }
+
+    @Override
+    public List<ExamCaptureInfo> getExamCaptureList(Long examRecordDataId) {
+        if (examRecordDataId == null) {
+            return Lists.newArrayList();
+        }
+        List<ExamCaptureEntity> examCaptureEntityList = examCaptureRepo.findByExamRecordDataId(examRecordDataId);
+        List<ExamCaptureInfo> examCaptureInfoList = new ArrayList<ExamCaptureInfo>();
+        for(ExamCaptureEntity examCapture:examCaptureEntityList){
+        	examCaptureInfoList.add(buildExamCaptureInfo(examCapture));
+        }
+        return examCaptureInfoList;
+    }
+
+    
+    public ExamCaptureInfo buildExamCaptureInfo(ExamCaptureEntity entity) {
+        ExamCaptureInfo info = new ExamCaptureInfo();
+        info.setId(entity.getId());
+        info.setExamRecordDataId(entity.getExamRecordDataId());
+        info.setFileUrl(entity.getFileUrl());
+        info.setReturnMessage(entity.getFaceCompareResult());
+        info.setPass(entity.getIsPass());
+        info.setStranger(entity.getIsStranger());
+        
+        String faceLivenessResult = entity.getFacelivenessResult();
+        info.setIsFacelivenessPass(isFaceLivenessPass(faceLivenessResult));
+        
+        String cameraInfos = entity.getCameraInfos();
+        info.setVirtualCameraNames(getVirtualCameraNames(cameraInfos));
+        return info;
+    }
+    
+    private String getVirtualCameraNames(String cameraInfos){
+    	StringBuffer sb = new StringBuffer();
+    	if(StringUtils.isNotBlank(cameraInfos)){
+ 			JSONArray jsonArray;
+			try {
+				jsonArray = new JSONArray(cameraInfos);
+				for(int i = 0;i<jsonArray.length();i++){
+	 				JSONObject jsonObject = (JSONObject) jsonArray.get(i);
+	 				if(StringUtils.isBlank(jsonObject.getString("pid"))){
+	 					sb.append(jsonObject.getString("name")+",");
+	 				}
+	 			}
+			} catch (JSONException e) {
+				e.printStackTrace();
+				throw new StatusException("ExamCaptureService-002", "获取虚拟摄像头名称失败");
+			}
+ 			
+ 			if(sb.lastIndexOf(",")>-1){
+ 				return sb.substring(0, sb.lastIndexOf(","));
+ 			}
+ 		}
+    	return sb.toString();
+    }
+    
+    private boolean isFaceLivenessPass(String faceLivenessResult){
+    	if(StringUtils.isNotBlank(faceLivenessResult)){
+			JSONObject jsonObject;
+			try {
+				jsonObject = new JSONObject(faceLivenessResult);
+				if(jsonObject.has("error_code")&&jsonObject.getInt("error_code") == 0&&jsonObject.has("result")){
+					JSONObject resultJson = jsonObject.getJSONObject("result");
+					if(resultJson.has("face_liveness")){
+						double faceLivenessVal = resultJson.getDouble("face_liveness");
+						double baiduFacelivenessThreshold = Double.parseDouble(PropertyHolder.getString("$baidu.faceliveness.threshold"));
+						if(faceLivenessVal > baiduFacelivenessThreshold){
+							return true;
+						}
+					}
+				}
+			} catch (JSONException e) {
+				e.printStackTrace();
+				return false;
+			}
+			
+    	}
+		return false;
+	}
+    
+}

+ 217 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordForMarkingServiceImpl.java

@@ -0,0 +1,217 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordForMarkingService;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordForMarkingEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamType;
+import cn.com.qmth.examcloud.core.oe.common.enums.MarkingType;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordForMarkingRepo;
+import cn.com.qmth.examcloud.examwork.api.ExamCloudService;
+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.response.GetExamPropertyResp;
+import cn.com.qmth.examcloud.examwork.api.response.GetExamResp;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月19日 下午3:20:29
+ * @company 	QMTH
+ * @description ExamRecordForMarkingServiceImpl.java
+ */
+@Service("examRecordForMarkingService")
+public class ExamRecordForMarkingServiceImpl implements ExamRecordForMarkingService {
+	
+	@Autowired
+	private ExamRecordForMarkingRepo examRecordForMarkingRepo;
+	
+	@Autowired
+	private ExamCloudService examCloudService;
+	
+	@Autowired
+    private JdbcTemplate jdbcTemplate;
+	@Override
+	public List<ExamRecordForMarkingEntity> queryValidExamRecordInfoByStuIds(Long examId,Long courseId,List<Long> examStudentIds) {
+		GetExamReq getExamReq = new GetExamReq();
+		getExamReq.setId(examId);
+		GetExamResp getExamResp = examCloudService.getExam(getExamReq);
+		ExamType examType = ExamType.strToEnum(getExamResp.getExamBean().getExamType());
+		
+		List<ExamRecordForMarkingEntity> examRecordForMarkingList = examRecordForMarkingRepo.findByExamIdAndCourseIdAndExamStudentIds(examId, courseId, examStudentIds);
+		if(examType == ExamType.OFFLINE){
+			return examRecordForMarkingList;
+		}else if(examType == ExamType.ONLINE){
+			GetExamPropertyReq getExamPropertyReq = new GetExamPropertyReq();
+			getExamPropertyReq.setExamId(examId);
+			getExamPropertyReq.setKey("MARKING_TYPE");
+			GetExamPropertyResp getExamPropertyResp = examCloudService.getExamProperty(getExamPropertyReq);
+			
+			String markingType = getExamPropertyResp.getValue();
+			if(markingType.equals(MarkingType.ALL.name())){
+				return examRecordForMarkingList;
+			}else if(markingType.equals(MarkingType.OBJECT_SCORE_MAX.name())){
+				return queryStudentPapersByObjectScoreMax(examRecordForMarkingList);
+			}else if(markingType.equals(MarkingType.LAST_SUBMIT.name())){
+				return queryStudentPapersByLastSubmit(examRecordForMarkingList);
+			}
+		}
+		return null;
+	}
+	@Override
+	public List<ExamRecordForMarkingEntity> queryValidExamRecordInfo(Long examId,Long courseId) {
+		GetExamReq getExamReq = new GetExamReq();
+		getExamReq.setId(examId);
+		GetExamResp getExamResp = examCloudService.getExam(getExamReq);
+		ExamType examType = ExamType.strToEnum(getExamResp.getExamBean().getExamType());
+		
+		List<ExamRecordForMarkingEntity> examRecordForMarkingList = examRecordForMarkingRepo.findByExamIdAndCourseId(examId, courseId);
+		if(examType == ExamType.OFFLINE){
+			return examRecordForMarkingList;
+		}else if(examType == ExamType.ONLINE){
+			GetExamPropertyReq getExamPropertyReq = new GetExamPropertyReq();
+			getExamPropertyReq.setExamId(examId);
+			getExamPropertyReq.setKey("MARKING_TYPE");
+			GetExamPropertyResp getExamPropertyResp = examCloudService.getExamProperty(getExamPropertyReq);
+			
+			String markingType = getExamPropertyResp.getValue();
+			if(markingType.equals(MarkingType.ALL.name())){
+				return examRecordForMarkingList;
+			}else if(markingType.equals(MarkingType.OBJECT_SCORE_MAX.name())){
+				return queryStudentPapersByObjectScoreMax(examRecordForMarkingList);
+			}else if(markingType.equals(MarkingType.LAST_SUBMIT.name())){
+				return queryStudentPapersByLastSubmit(examRecordForMarkingList);
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * 最后一次提交
+	 * @param examRecordForMarkingList
+	 * @return
+	 */
+	private List<ExamRecordForMarkingEntity> queryStudentPapersByLastSubmit(List<ExamRecordForMarkingEntity> examRecordForMarkingAllList) {
+		Map<Long,List<ExamRecordForMarkingEntity>> groupByExamStudentIdMap = groupByExamStudentId(examRecordForMarkingAllList);
+		Iterator<Entry<Long,List<ExamRecordForMarkingEntity>>> iterator = groupByExamStudentIdMap.entrySet().iterator();
+		List<ExamRecordForMarkingEntity> finalExamRecordForMarkingEntityList = new ArrayList<ExamRecordForMarkingEntity>();
+		while (iterator.hasNext()) {
+			Entry<Long,List<ExamRecordForMarkingEntity>> entry = iterator.next();
+			List<ExamRecordForMarkingEntity> examRecordForMarkingList = entry.getValue();
+			List<ExamRecordForMarkingEntity> listSortByExamRecordDataId = examRecordForMarkingList.stream().sorted((p1,p2) -> p2.getExamRecordDataId().compareTo(p1.getExamRecordDataId())).collect(Collectors.toList());
+			finalExamRecordForMarkingEntityList.add(listSortByExamRecordDataId.get(0));
+		}
+		/**
+		 * 解决下面的问题:
+		 *  当一个课程下设置两套试卷一套有主观题,一套没有主观题时,考试完成后给阅卷端传试卷时会传错,例如:
+		 *	考试批次设置为取最后一次考试成绩;
+		 *	在调卷规则中给课程A设置了两份试卷A1和A2,A1试卷中有主观题,A2试卷没有主观题;
+		 *	考生AA在考试时,第一次抽取到了A1试卷,第二次抽取到了A2试卷;
+		 *	考试完成后考生AA应该没有试卷传给阅卷端,但实际上系统将A1试卷传给了阅卷端
+		 */
+		//todo
+		return finalExamRecordForMarkingEntityList;
+	}
+	
+	/**
+	 * 客观分最高
+	 * @param examRecordForMarkingList
+	 * @return
+	 */
+	private List<ExamRecordForMarkingEntity> queryStudentPapersByObjectScoreMax(List<ExamRecordForMarkingEntity> examRecordForMarkingAllList) {
+		Map<Long,List<ExamRecordForMarkingEntity>> groupByExamStudentIdMap = groupByExamStudentId(examRecordForMarkingAllList);
+		Iterator<Entry<Long,List<ExamRecordForMarkingEntity>>> iterator = groupByExamStudentIdMap.entrySet().iterator();
+		List<ExamRecordForMarkingEntity> finalExamRecordForMarkingEntityList = new ArrayList<ExamRecordForMarkingEntity>();
+		while (iterator.hasNext()) {
+			Entry<Long,List<ExamRecordForMarkingEntity>> entry = iterator.next();
+			List<ExamRecordForMarkingEntity> examRecordForMarkingList = entry.getValue();
+			//得出该考生的最高客观分
+			double maxObjectiveScore = examRecordForMarkingList.stream().sorted((p1,p2) -> p2.getObjectiveScore().compareTo(p1.getObjectiveScore()))
+			.limit(1).findFirst().get().getObjectiveScore();
+			//过滤出最高分集合
+			List<ExamRecordForMarkingEntity> examRecordForMarkingByFilterList = examRecordForMarkingList.stream().filter(score -> {
+				return score.getObjectiveScore() == maxObjectiveScore;
+			}).collect(Collectors.toList());
+			//如果最高客观分的有多个,继续按主观题答案长度过滤
+			if(examRecordForMarkingByFilterList.size()>1){
+				int maxAnswerLength = examRecordForMarkingByFilterList.stream().sorted((p1,p2) -> p2.getSubjectiveAnswerLength()-p1.getSubjectiveAnswerLength())
+				.limit(1).findFirst().get().getSubjectiveAnswerLength();
+				examRecordForMarkingByFilterList = examRecordForMarkingList.stream().filter(score -> {
+					return score.getSubjectiveAnswerLength() == maxAnswerLength;
+				}).collect(Collectors.toList());
+				//如果主观题答案最长长度一样的还有多个,继续按时间(考试记录ID)过滤出最新的那条
+				if(examRecordForMarkingByFilterList.size()>1){
+					examRecordForMarkingByFilterList = examRecordForMarkingByFilterList.stream().sorted((p1,p2) -> p2.getExamRecordDataId().compareTo(p1.getExamRecordDataId()))
+					.limit(1)
+					.collect(Collectors.toList());
+				}
+			}
+			finalExamRecordForMarkingEntityList.add(examRecordForMarkingByFilterList.get(0));
+		}
+		return finalExamRecordForMarkingEntityList;
+	}
+	
+	private Map<Long,List<ExamRecordForMarkingEntity>> groupByExamStudentId(List<ExamRecordForMarkingEntity> examRecordForMarkingList){
+		Map<Long,List<ExamRecordForMarkingEntity>> groupByExamStudentIdMap = new HashMap<Long,List<ExamRecordForMarkingEntity>>();
+		for(ExamRecordForMarkingEntity examRecordForMarkingEntity:examRecordForMarkingList){
+			Long examStudentId = examRecordForMarkingEntity.getExamStudentId();
+			List<ExamRecordForMarkingEntity> value = null;
+			if(groupByExamStudentIdMap.containsKey(examStudentId)){
+				value = groupByExamStudentIdMap.get(examStudentId);
+			}else{
+				value = new ArrayList<ExamRecordForMarkingEntity>();
+			}
+			value.add(examRecordForMarkingEntity);
+			groupByExamStudentIdMap.put(examStudentId, value);
+		}
+		return groupByExamStudentIdMap;
+	}
+
+	@Override
+	public List<ExamRecordForMarkingEntity> findExamRecordForMarkingInfo(Long id,Long examId,Long courseId) {
+		if(id == null && examId == null){
+			return null;
+		}
+		StringBuffer sql = new StringBuffer();
+		sql.append("select id,exam_id,base_paper_id,paper_type,course_id,offline_file_name,offline_file_url "+
+					 " from ec_oe_exam_record_4_marking where 1=1 ");
+		if(id != null){
+			sql.append(" and id = "+id);
+		}
+		if(examId != null){
+			sql.append(" and exam_id = "+examId);
+		}
+		if(courseId != null){
+			sql.append(" and course_id = '"+courseId+"'");
+		}
+		sql.append(" group by base_paper_id");
+		return jdbcTemplate.query(sql.toString(), new RowMapper<ExamRecordForMarkingEntity>(){
+			@Override
+			public ExamRecordForMarkingEntity mapRow(ResultSet rs, int arg1) throws SQLException {
+				ExamRecordForMarkingEntity entity = new ExamRecordForMarkingEntity();
+				entity.setId(rs.getLong("id"));
+				entity.setExamId(rs.getLong("exam_id"));
+				entity.setBasePaperId(rs.getString("base_paper_id"));
+				entity.setPaperType(rs.getString("paper_type"));
+				entity.setCourseId(rs.getLong("course_id"));
+				entity.setOfflineFileName(rs.getString("offline_file_name"));
+				entity.setOfflineFileUrl(rs.getString("offline_file_url"));
+				return entity;
+			}
+		});
+	}
+
+}

+ 186 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordPaperStructServiceImpl.java

@@ -0,0 +1,186 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.BasePaperCacheBean;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordPaperStructService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.ExamRecordQuestionsInfo;
+import cn.com.qmth.examcloud.core.oe.common.base.Constants;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.NewQuestionType;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Sentence;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordPaperStructEntity;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordPaperStructRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.core.questions.api.ExtractConfigCloudService;
+import cn.com.qmth.examcloud.core.questions.api.request.GetBasePaperReq;
+import cn.com.qmth.examcloud.core.questions.api.response.GetBasePaperResp;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年11月13日 上午11:52:02
+ * @company 	QMTH
+ * @description 试卷结构服务类
+ */
+@Service("examRecordPaperStructService")
+public class ExamRecordPaperStructServiceImpl implements ExamRecordPaperStructService{
+
+	@Autowired
+	private ExamRecordDataRepo examRecordDataRepo;
+	
+	@Autowired
+	private ExamRecordRepo examRecordRepo;
+	
+	@Autowired
+	private ExamRecordPaperStructRepo examRecordPaperStructRepo;
+	
+	@Autowired
+	private JdbcTemplate jdbcTemplate;
+	
+	@Autowired
+	private MongoTemplate mongoTemplate;
+	
+	@Autowired
+	private ExtractConfigCloudService extractConfigCloudService;
+	
+	@Override
+	public ExamRecordPaperStructEntity getExamRecordPaperStruct(Long examRecordDataId) {
+		ExamRecordDataEntity examRecordDataEntity = GlobalHelper.getEntity(examRecordDataRepo,examRecordDataId,ExamRecordDataEntity.class);
+		ExamRecordEntity examRecordEntity =GlobalHelper.getEntity(examRecordRepo,examRecordDataEntity.getExamRecordId(),ExamRecordEntity.class);
+		String paperStructId = examRecordEntity.getPaperStructId();
+		ExamRecordPaperStructEntity examRecordPaperStruct = null;
+		if(StringUtils.isBlank(paperStructId)){
+			Map<String,Object> paperInfoMap = getPaperInfoFromOeExamQuestion(examRecordEntity.getId());
+			String paperId = paperInfoMap.get("paper_id").toString();
+			//从缓存中获取试卷结构
+			BasePaperCacheBean getBasePaperResp = CacheHelper.getBasePaper(paperId);
+
+			examRecordPaperStruct = new ExamRecordPaperStructEntity();
+			examRecordPaperStruct.setDefaultPaper(getBasePaperResp.getDefaultPaper());
+			//保存作答记录
+			List<ExamRecordQuestionsInfo> examRecordQuestionList = encapsulationExamQuestion(examRecordDataEntity);
+			if (examRecordQuestionList.size() > 0) {
+	             mongoTemplate.insert(examRecordQuestionList, Constants.EXAM_RECORD_QUESTIONS);
+	        }
+			//保存试卷结构
+			examRecordPaperStruct = examRecordPaperStructRepo.save(examRecordPaperStruct);
+			//保存考试记录
+			//经测试,从oe_exam_question中获取的paperId最准确
+			examRecordEntity.setBasePaperId(paperId);
+			examRecordEntity.setPaperType(paperInfoMap.get("paper_type").toString());
+			examRecordEntity.setPaperStructId(examRecordPaperStruct.getId());
+			examRecordRepo.save(examRecordEntity);
+			return examRecordPaperStruct;
+		}else{
+			examRecordPaperStruct =GlobalHelper.getEntity(examRecordPaperStructRepo,paperStructId,ExamRecordPaperStructEntity.class);
+			return examRecordPaperStruct;
+		}
+	}
+
+	/**
+	 * 使用examRecordId查询paperId
+	 * @param examRecordId
+	 * @return
+	 */
+	private Map<String,Object> getPaperInfoFromOeExamQuestion(Long examRecordId){
+		String sql = "select paper_id,paper_type from oe_exam_question where exam_record_id = "+examRecordId+" limit 1";
+		Map<String,Object> examQuestionMap = jdbcTemplate.queryForMap(sql);
+		return examQuestionMap;
+	}
+	
+	/**
+	 * 封装ExamQuestion
+	 * @param examRecordDataEntity
+	 */
+	private List<ExamRecordQuestionsInfo> encapsulationExamQuestion(ExamRecordDataEntity examRecordDataEntity){
+		 List<ExamRecordQuestionsInfo> examRecordQuestionList = new ArrayList<ExamRecordQuestionsInfo>();
+		 ExamRecordQuestionsInfo examRecordQuestion = new ExamRecordQuestionsInfo();
+         examRecordQuestion.setExamRecordDataId(examRecordDataEntity.getId());
+         examRecordQuestion.setCreationTime(examRecordDataEntity.getStartTime());
+         List<Map<String, Object>> rows = jdbcTemplate.queryForList(selectOldRecordQuestionByExamRecordIdSql(examRecordDataEntity.getExamRecordId()));
+         for (Map<String, Object> row : rows) {
+             ExamRecordQuestionsInfo.ExamQuestionEntity examQuestion = ExamRecordQuestionsInfo.createExamQuestionEntity();
+
+             //转换试题型
+             NewQuestionType questionType = NewQuestionType.convert(Sentence.dealString(row.get("question_type")));
+             if (questionType == null || questionType == NewQuestionType.NESTED) {
+                 continue;
+             }
+
+             //获取试题ID
+             String questionId = Sentence.dealString(row.get("question_id"));
+             if (questionId == null || "".equals(questionId)) {
+                 questionId = Sentence.dealString(row.get("parent_question_id"));
+             }
+
+             //计算考生试题得分
+             Float questionScore = Sentence.dealFloat(row.get("score"));
+             String correctAnswer = Sentence.dealString(row.get("answer"));
+             String studentAnswer = Sentence.dealString(row.get("stu_answer"));
+             Float studentScore = computeStudentScore(questionType, questionScore, correctAnswer, studentAnswer);
+
+             examQuestion.setQuestionId(questionId);
+             examQuestion.setMainNumber(Sentence.dealInteger(row.get("main_number")));
+             examQuestion.setOrder(Sentence.dealInteger(row.get("orders")));
+             examQuestion.setQuestionScore(questionScore);
+             examQuestion.setQuestionType(questionType);
+             examQuestion.setCorrectAnswer(correctAnswer);
+             examQuestion.setStudentAnswer(studentAnswer);
+             examQuestion.setStudentScore(studentScore);
+             examQuestion.setAnswer(Sentence.dealBoolean(row.get("is_answered")));
+             examQuestion.setSign(Sentence.dealBoolean(row.get("is_sign")));
+             examQuestion.setOptionPermutation(new Integer[]{0,1,2,3});
+             examRecordQuestion.addExamQuestion(examQuestion);
+         }
+         examRecordQuestionList.add(examRecordQuestion);
+         return examRecordQuestionList;
+	}
+	
+	private String selectOldRecordQuestionByExamRecordIdSql(Long examRecordId) {
+        return new StringBuilder().append("SELECT ")
+                .append("id,exam_record_id,is_answered,is_multiple,is_nested_question,is_sign,orders,")
+                .append("main_number,sub_number,base_paper_id,paper_id,paper_name,paper_number,paper_type,")
+                .append("parent_question_id,question_id,question_type,score,stu_score,answer,stu_answer,create_at")
+                .append(" FROM ").append(Constants.OE_EXAM_QUESTION)
+                .append(" WHERE exam_record_id = ").append(examRecordId)
+                .append(" ORDER BY orders ASC").toString();
+    }
+	
+    private Float computeStudentScore(NewQuestionType questionType, Float questionScore, String correctAnswer, String studentAnswer) {
+        //若为客观题重新计算考生试题得分
+        if (questionType == null || !questionType.isObjective()) {
+            return 0F;
+        }
+        if (studentAnswer == null || "".equals(studentAnswer)) {
+            return 0F;
+        }
+
+        //按字母顺序排序  D,A,C --> ACD
+        String[] answers = studentAnswer.split(",");
+        Arrays.sort(answers);
+        String answerStr = new String();
+        for (String answer : answers) {
+            answerStr += answer;
+        }
+
+        if (!answerStr.equals(correctAnswer)) {
+            return 0F;
+        }
+        return questionScore;
+    }
+	
+}

+ 78 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordQuestionsServiceImpl.java

@@ -0,0 +1,78 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import cn.com.qmth.examcloud.core.oe.common.entity.*;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordQuestionsService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.SubjectiveQuestionScoreInfo;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.QuestionTypeUtil;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamType;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordQuestionsRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScoreRepo;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+
+@Service("examRecordQuestionsService")
+public class ExamRecordQuestionsServiceImpl implements ExamRecordQuestionsService {
+
+	@Autowired
+	private ExamRecordQuestionsRepo examRecordQuestionsRepo;
+	
+	@Autowired
+	private ExamRecordDataRepo examRecordDataRepo;
+	
+	@Autowired
+	private ExamScoreRepo examScoreRepo;
+
+	@Autowired
+	private ExamRecordRepo examRecordRepo;
+	
+	@Override
+	public List<ExamQuestionEntity> querySubjectiveAnswerList(Long examRecordDataId) {
+		ExamRecordQuestionsEntity examRecordQuestionsEntity = examRecordQuestionsRepo.findByExamRecordDataId(examRecordDataId);
+		List<ExamQuestionEntity> examRecordQuestionList = examRecordQuestionsEntity.getExamQuestionEntities();
+		
+		return examRecordQuestionList.stream().filter((obj->{
+			return !QuestionTypeUtil.isObjectiveQuestion(obj.getQuestionType());
+		})).collect(Collectors.toList());
+	}
+
+	@Override
+	public void saveSubjectiveQuestionScore(Long examRecordDataId,List<SubjectiveQuestionScoreInfo> subjectiveQuestionScoreInfoList) {
+		ExamRecordDataEntity examRecordDataEntity = GlobalHelper.getEntity(examRecordDataRepo,examRecordDataId,ExamRecordDataEntity.class);
+		ExamRecordEntity examRecord =GlobalHelper.getEntity(examRecordRepo,examRecordDataEntity.getExamRecordId(),ExamRecordEntity.class);
+		if(examRecord.getExamType() == ExamType.ONLINE){
+			ExamRecordQuestionsEntity examRecordQuestionsEntity = examRecordQuestionsRepo.findByExamRecordDataId(examRecordDataId);
+			List<ExamQuestionEntity> examRecordQuestionList = examRecordQuestionsEntity.getExamQuestionEntities();
+			
+			for(SubjectiveQuestionScoreInfo scoreInfo:subjectiveQuestionScoreInfoList){
+				for(ExamQuestionEntity examQuestionEntity:examRecordQuestionList){
+					if(scoreInfo.getQuestionId().equals(examQuestionEntity.getQuestionId())
+							&& scoreInfo.getOrder().intValue() == examQuestionEntity.getOrder().intValue()){
+						examQuestionEntity.setStudentScore(scoreInfo.getScore());
+						break;
+					}
+				}
+			}
+			examRecordQuestionsRepo.save(examRecordQuestionsEntity);
+		}
+		//计算主观题总得分
+		double totalSubjectiveScore = 0D;
+		for(SubjectiveQuestionScoreInfo scoreInfo:subjectiveQuestionScoreInfoList){
+			totalSubjectiveScore += scoreInfo.getScore();
+		}
+		
+		//更新ec_oe_exam_score
+		ExamScoreEntity examScoreEntity = examScoreRepo.findByExamRecordDataId(examRecordDataId);
+		examScoreEntity.setSubjectiveScore(totalSubjectiveScore);
+		double totalScore = totalSubjectiveScore+examScoreEntity.getObjectiveScore();
+		examScoreEntity.setTotalScore(totalScore);
+		examScoreRepo.save(examScoreEntity);
+	}
+
+}

+ 743 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordServiceImpl.java

@@ -0,0 +1,743 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-23 16:38:01.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.bean.OrgBean;
+import cn.com.qmth.examcloud.core.basic.api.bean.StudentBean;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCacheTransferHelper;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamStudentService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.ExamStudentEffectiveScoreInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamRecordEntityConvert;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamRecordInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamRecordQuery;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamStudentQuestionScoreInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentInfo;
+import cn.com.qmth.examcloud.core.oe.common.base.jpa.Searcher;
+import cn.com.qmth.examcloud.core.oe.common.base.jpa.SpecUtils;
+import cn.com.qmth.examcloud.core.oe.common.base.jpa.SqlWrapper;
+import cn.com.qmth.examcloud.core.oe.common.enums.MarkingType;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.core.oe.common.service.LocalCacheService;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamQuestionEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordQuestionsEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamScoreEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamRecordStatus;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamType;
+import cn.com.qmth.examcloud.core.oe.common.enums.IsSuccess;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamAuditRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordQuestionsRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScoreRepo;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.OrgCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.StudentCacheBean;
+import com.google.common.collect.Lists;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+
+import java.math.BigInteger;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamRecordMapper.*;
+
+/**
+ * 考试记录接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/16
+ */
+@Service
+public class ExamRecordServiceImpl implements ExamRecordService {
+    @Autowired
+    private ExamRecordRepo examRecordRepo;
+    @Autowired
+    private ExamScoreRepo examScoreRepo;
+    @Autowired
+    private ExamRecordDataRepo examRecordDataRepo;
+    @Autowired
+    private ExamAuditRepo examAuditRepo;
+    @Autowired
+    private EntityManager entityManager;
+    @Autowired
+    private ExamService examService;
+    @Autowired
+    private ExamStudentService examStudentService;
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+    @Autowired
+    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
+    @Autowired
+    private ExamRecordEntityConvert examRecordEntityConvert;
+    @Autowired
+    private LocalCacheService localCacheService;
+    /**
+     * ec_oe_exam_record_data 联合 ec_oe_exam_record 查询sql
+     */
+    private static final String EXAM_RECORD_SQL = "select  record_data.id," +
+            "record_data.exam_record_id," +
+            "record_data.exam_record_status," +
+            "record_data.start_time," +
+            "record_data.end_time," +
+            "record_data.clean_time," +
+            "record_data.used_exam_time," +
+            "record_data.exam_order," +
+            "record_data.is_warn," +
+            "record_data.is_audit," +
+            "record_data.is_illegality," +
+            "record_data.is_reexamine," +
+            "record_data.is_continued," +
+            "record_data.is_all_objective_paper," +
+            "record_data.continued_count," +
+            "record_data.face_success_count," +
+            "record_data.face_failed_count," +
+            "record_data.face_stranger_count," +
+            "record_data.face_total_count," +
+            "record_data.face_success_percent," +
+            "record_data.face_verify_result," +
+            "record_data.baidu_face_liveness_success_percent," +
+            "record_data.creation_time," +
+            "record_data.update_time," +
+            "record.exam_id exam_id," +
+            "record.exam_type exam_type," +
+            "record.exam_student_id exam_student_id," +
+            "record.student_id student_id," +
+            "record.student_code student_code," +
+            "record.student_name student_name," +
+            "record.identity_number identity_number," +
+            "record.course_id course_id," +
+            "record.course_level course_level," +
+            "record.root_org_id root_org_id," +
+            "record.org_id org_id," +
+            "record.base_paper_id base_paper_id," +
+            "record.paper_type paper_type," +
+            "record.paper_struct_id paper_struct_id," +
+            "record.info_collector info_collector" +
+            " from ec_oe_exam_record_data record_data " +
+            " left join ec_oe_exam_record record on record_data.exam_record_id = record.id where 1=1";
+    /**
+     * 考生作答成绩
+     */
+    private static final String EXAM_STUDENT_QUESTION_SCORE_SQL = "select " +
+            "erd.id exam_record_data_id,co.`code` course_code,co.`name` course_name, ex.`name` exam_name" +
+            ",er.student_code,er.base_paper_id " +
+            "from ec_oe_exam_record er " +
+            "inner join ec_oe_exam_record_data erd on er.id=erd.exam_record_id " +
+            "inner join ec_b_course co on er.course_id=co.id " +
+            "inner join ec_e_exam ex on er.exam_id=ex.id " +
+            "where 1=1 ";
+    @Autowired
+    private ExamRecordQuestionsRepo examRecordQuestionsRepo;
+
+    @Override
+    public Page<ExamRecordInfo> getExamRecordDetailListForPage(ExamRecordQuery query) {
+        Check.isNull(query, "查询参数不能为空!");
+        query.addRecordStatus(ExamRecordStatus.EXAM_END.name());
+        query.addRecordStatus(ExamRecordStatus.EXAM_OVERDUE.name());
+        return this.loadData(_getExamRecordDetailListForPage(query), query.getExamId());
+    }
+
+    @Override
+    public Page<ExamRecordInfo> getExamRecordWaitingAuditList(ExamRecordQuery query) {
+        Check.isNull(query, "查询参数不能为空!");
+        //默认条件
+        query.setIsAudit(false);
+        query.addRecordStatus(ExamRecordStatus.EXAM_END.name());
+        query.addRecordStatus(ExamRecordStatus.EXAM_OVERDUE.name());
+        return this.getExamRecordList(query);
+    }
+
+    /**
+     * 考试明细查询
+     *
+     * @param query
+     * @return
+     */
+    private Page<ExamRecordInfo> _getExamRecordDetailListForPage(ExamRecordQuery query) {
+        Check.isNull(query, "查询参数不能为空!");
+        //查询条件
+        StringBuilder sqlBuilder = new StringBuilder();
+        sqlBuilder.append(EXAM_RECORD_SQL);
+        sqlBuilder.append(buildExamRecordCommonSelectCondition(query));
+        sqlBuilder.append(" and ((record_data.is_warn = 0) OR (record_data.is_warn  = 1 and record_data.is_audit = 1))");
+        sqlBuilder.append(" order by record.id desc");
+        //分页条件
+        int currentNum = (query.getPageNo() - 1) * query.getPageSize();
+        sqlBuilder.append(" limit " + currentNum + "," + query.getPageSize());
+        List<ExamRecordDataEntity> examRecordDataList = jdbcTemplate.query(sqlBuilder.toString(), new RowMapper<ExamRecordDataEntity>() {
+            @Override
+            public ExamRecordDataEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
+                return getExamRecordDataEntityByResultSet(rs);
+            }
+        });
+        //查询总数
+        long total = countExamRecordDetailListForPage(query);
+        Pageable pageable = new PageRequest(query.getPageNo() - 1, query.getPageSize());
+        Page<ExamRecordDataEntity> page = new PageImpl<ExamRecordDataEntity>(examRecordDataList, pageable, total);
+        return examRecordEntityConvert.of(page);
+    }
+
+    private ExamRecordDataEntity getExamRecordDataEntityByResultSet(ResultSet rs) throws SQLException {
+        ExamRecordDataEntity examRecordData = new ExamRecordDataEntity();
+        examRecordData.setId(rs.getLong("id"));
+        examRecordData.setExamRecordStatus(ExamRecordStatus.getByName(rs.getString("exam_record_status")));
+        examRecordData.setStartTime(rs.getTimestamp("start_time"));
+        examRecordData.setEndTime(rs.getTimestamp("end_time"));
+        examRecordData.setCleanTime(rs.getTimestamp("clean_time"));
+        examRecordData.setUsedExamTime(rs.getLong("used_exam_time"));
+        examRecordData.setExamOrder(rs.getInt("exam_order"));
+        examRecordData.setIsWarn(rs.getBoolean("is_warn"));
+        examRecordData.setIsAudit(rs.getBoolean("is_audit"));
+        examRecordData.setIsIllegality(rs.getBoolean("is_illegality"));
+        examRecordData.setIsReexamine(rs.getBoolean("is_reexamine"));
+        examRecordData.setIsContinued(rs.getBoolean("is_continued"));
+        examRecordData.setIsAllObjectivePaper(rs.getBoolean("is_all_objective_paper"));
+        examRecordData.setContinuedCount(rs.getInt("continued_count"));
+        examRecordData.setFaceSuccessCount(rs.getInt("face_success_count"));
+        examRecordData.setFaceFailedCount(rs.getInt("face_failed_count"));
+        examRecordData.setFaceStrangerCount(rs.getInt("face_stranger_count"));
+        examRecordData.setFaceTotalCount(rs.getInt("face_total_count"));
+        examRecordData.setFaceSuccessPercent(rs.getDouble("face_success_percent"));
+        examRecordData.setFaceVerifyResult(IsSuccess.strToEnum(rs.getString("face_verify_result")));
+        examRecordData.setBaiduFaceLivenessSuccessPercent(rs.getDouble("baidu_face_liveness_success_percent"));
+
+//        ExamRecordEntity examRecord = new ExamRecordEntity();
+//        examRecord.setId(rs.getLong("exam_record_id"));
+//        examRecord.setExamId(rs.getLong("exam_id"));
+//        examRecord.setExamType(ExamType.strToEnum(rs.getString("exam_type")));
+//        examRecord.setExamStudentId(rs.getLong("exam_student_id"));
+//        examRecord.setStudentId(rs.getLong("student_id"));
+//        examRecord.setStudentCode(rs.getString("student_code"));
+//        examRecord.setStudentName(rs.getString("student_name"));
+//        examRecord.setIdentityNumber(rs.getString("identity_number"));
+//        examRecord.setCourseId(rs.getLong("course_id"));
+//        examRecord.setCourseLevel(rs.getString("course_level"));
+//        examRecord.setRootOrgId(rs.getLong("root_org_id"));
+//        examRecord.setOrgId(rs.getLong("org_id"));
+//        examRecord.setBasePaperId(rs.getString("base_paper_id"));
+//        examRecord.setPaperStructId(rs.getString("paper_struct_id"));
+//        examRecord.setInfoCollector(rs.getString("info_collector"));
+        examRecordData.setExamRecordId(rs.getLong("exam_record_id"));
+
+        return examRecordData;
+    }
+
+    private long countExamRecordDetailListForPage(ExamRecordQuery query) {
+        StringBuilder sqlBuilder = new StringBuilder();
+        sqlBuilder.append("select count(record_data.id) from ec_oe_exam_record_data record_data left join ec_oe_exam_record record on record_data.exam_record_id = record.id where 1=1");
+        sqlBuilder.append(buildExamRecordCommonSelectCondition(query));
+        sqlBuilder.append(" and ((record_data.is_warn = 0) OR (record_data.is_warn  = 1 and record_data.is_audit = 1))");
+        return jdbcTemplate.queryForObject(sqlBuilder.toString(), Long.class);
+    }
+
+    /**
+     * 构建查询 考试明细sql语句
+     *
+     * @param query
+     * @return
+     */
+    private StringBuilder buildExamRecordCommonSelectCondition(ExamRecordQuery query) {
+        StringBuilder sql = new StringBuilder();
+        if (query.getExamRecordDataId() != null) {
+            sql.append(" and record_data.id = " + query.getExamRecordDataId());
+        }
+        if (query.getOrgId() != null) {
+            sql.append(" and record.org_id = " + query.getOrgId());
+        }
+        if (query.getExamId() != null) {
+            sql.append(" and record.exam_id = " + query.getExamId());
+        }
+        if (StringUtils.isNotBlank(query.getStudentCode())) {
+            sql.append(" and record.student_code LIKE '%" + query.getStudentCode() + "%'");
+        }
+        if (StringUtils.isNotBlank(query.getStudentName())) {
+            sql.append(" and record.student_name LIKE '%" + query.getStudentName() + "%'");
+        }
+        if (StringUtils.isNotBlank(query.getIdentityNumber())) {
+            sql.append(" and record.identity_number LIKE '%" + query.getIdentityNumber() + "%'");
+        }
+        if (StringUtils.isNotBlank(query.getInfoCollector())) {
+            sql.append(" and record.info_collector LIKE '%" + query.getInfoCollector() + "%'");
+        }
+        if (query.getCourseId() != null) {
+            sql.append(" and record.course_id = " + query.getCourseId());
+        }
+        if (StringUtils.isNotBlank(query.getCourseLevel())) {
+            sql.append(" and record.course_level = '" + query.getCourseLevel() + "'");
+        }
+        if (query.getExamRecordId() != null) {
+            sql.append(" and record.id = " + query.getExamRecordId());
+        }
+        if (StringUtils.isNotBlank(query.getStartTime()) && StringUtils.isNotBlank(query.getEndTime())) {
+            sql.append(" and record_data.start_time >= str_to_date('" + query.getStartTime() + "','%Y/%m/%d %H:%i:%s')"
+                    + " and record_data.start_time <= str_to_date('" + query.getEndTime() + "','%Y/%m/%d %H:%i:%s')");
+        }
+        //考试记录状态
+        List<ExamRecordStatus> statusList = query.getRecordStatusList();
+        if (statusList != null && statusList.size() > 0) {
+            String status = "";
+            for (int i = 0; i < statusList.size(); i++) {
+                status += "'" + statusList.get(i) + "',";
+            }
+            status = status.substring(0, status.lastIndexOf(","));
+            sql.append(" and record_data.exam_record_status IN (" + status + ")");
+        }
+        //是否有陌生人脸
+        if (query.getHasStranger() != null) {
+            if (query.getHasStranger()) {
+                sql.append(" and record_data.face_stranger_count > 0");
+            } else {
+                sql.append(" and record_data.face_stranger_count = 0");
+            }
+        }
+        //人脸识别成功率
+        if (query.getFaceSuccessPercentLower() != null && query.getFaceSuccessPercentUpper() != null) {
+            sql.append(" and record_data.face_success_percent >=" + query.getFaceSuccessPercentLower());
+            sql.append(" and record_data.face_success_percent <=" + query.getFaceSuccessPercentUpper());
+        }
+        if (query.getLivenessSuccessPercentLower() != null && query.getLivenessSuccessPercentUpper() != null) {
+            sql.append(" and record_data.baidu_face_liveness_success_percent >=" + query.getLivenessSuccessPercentLower());
+            sql.append(" and record_data.baidu_face_liveness_success_percent <=" + query.getLivenessSuccessPercentUpper());
+        }
+        return sql;
+    }
+
+    /**
+     * 监考待审
+     * 查询无异常的数据和有异常未审核的数据
+     */
+    @Override
+    public Page<ExamRecordInfo> getExamRecordList(ExamRecordQuery query) {
+        Check.isNull(query, "查询参数不能为空!");
+        //查询条件
+        StringBuilder sqlBuilder = new StringBuilder();
+        sqlBuilder.append(EXAM_RECORD_SQL);
+        sqlBuilder.append(buildExamRecordCommonSelectCondition(query));
+        //只查有异常未审核
+        if (query.getIsWarn() != null) {
+            if (query.getIsWarn()) {
+                sqlBuilder.append(" and record_data.is_warn  = 1 and record_data.is_audit = 0 ");
+            } else {
+                sqlBuilder.append(" and record_data.is_warn  = 0 ");
+            }
+        } else {
+            sqlBuilder.append(" and ((record_data.is_warn = 0) OR (record_data.is_warn  = 1 and record_data.is_audit = 0))");
+        }
+        sqlBuilder.append(" and record_data.is_illegality = 0");
+        sqlBuilder.append(" order by record.id desc");
+        //分页条件
+        int currentNum = (query.getPageNo() - 1) * query.getPageSize();
+        sqlBuilder.append(" limit " + currentNum + "," + query.getPageSize());
+        List<ExamRecordDataEntity> examRecordDataList = jdbcTemplate.query(sqlBuilder.toString(), new RowMapper<ExamRecordDataEntity>() {
+            @Override
+            public ExamRecordDataEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
+                return getExamRecordDataEntityByResultSet(rs);
+            }
+        });
+        //查询总数
+        long total = countExamRecordListForPage(query);
+        Pageable pageable = new PageRequest(query.getPageNo() - 1, query.getPageSize());
+        Page<ExamRecordDataEntity> page = new PageImpl<ExamRecordDataEntity>(examRecordDataList, pageable, total);
+        return examRecordEntityConvert.of(page);
+    }
+
+    /**
+     * 监考待审:数量查询
+     * 查询无异常的数据和有异常未审核的数据
+     */
+    private long countExamRecordListForPage(ExamRecordQuery query) {
+        StringBuilder sqlBuilder = new StringBuilder();
+        sqlBuilder.append("select count(record_data.id) from ec_oe_exam_record_data record_data left join ec_oe_exam_record record on record_data.exam_record_id = record.id where 1=1");
+        sqlBuilder.append(buildExamRecordCommonSelectCondition(query));
+        if (query.getIsWarn() != null) {
+            if (query.getIsWarn()) {
+                sqlBuilder.append(" and record_data.is_warn  = 1 and record_data.is_audit = 0 ");
+            } else {
+                sqlBuilder.append(" and record_data.is_warn  = 0 ");
+            }
+        } else {
+            sqlBuilder.append(" and ((record_data.is_warn = 0) OR (record_data.is_warn  = 1 and record_data.is_audit = 0))");
+        }
+        sqlBuilder.append(" and record_data.is_illegality = 0");
+        return jdbcTemplate.queryForObject(sqlBuilder.toString(), Long.class);
+    }
+
+    @Override
+    public List<Long> getExamRecordingStudentIds(Long examId) {
+        Check.isNull(examId, "考试ID不能为空!");
+
+        SqlWrapper wrapper = new SqlWrapper()
+                .select("record.exam_student_id")
+                .from("ec_oe_exam_record").as("record")
+                .innerJoin("ec_oe_exam_record_data").as("data")
+                .on("record.id", "data.exam_record_id")
+                .where().eq("data.exam_record_status", ExamRecordStatus.EXAM_ING.name())
+                .and().eq("record.exam_id", examId);
+
+        Query query = entityManager.createNativeQuery(wrapper.build());
+        List<BigInteger> list = query.getResultList();
+        if (list == null || list.size() == 0) {
+            return Lists.newArrayList();
+        }
+        List<Long> ids = new ArrayList<>();
+        for (BigInteger big : list) {
+            ids.add(big.longValue());
+        }
+        return ids;
+    }
+
+    @Override
+    public List<ExamRecordDataEntity> getExamRecordListByExamStudentId(Long examStudentId) {
+        Check.isNull(examStudentId, "考生ID不能为空!");
+        return examRecordDataRepo.findByExamStudentId(examStudentId);
+    }
+
+    @Override
+    public Long findExamRecordDataId(Long examRecordId) {
+        SqlWrapper wrapper = new SqlWrapper()
+                .select("exam_record_id")
+                .from("ec_oe_exam_record_data")
+                .where()
+                .eq("exam_record_id", examRecordId);
+        Query query = entityManager.createNativeQuery(wrapper.build());
+        try {
+            BigInteger result = (BigInteger) query.getSingleResult();
+            if (result != null) {
+                return result.longValue();
+            }
+        } catch (NoResultException e) {
+            //ignore
+        }
+        return null;
+    }
+
+    private Page<ExamRecordInfo> loadData(Page<ExamRecordInfo> page, Long examId) {
+        if (page == null || page.getContent() == null) {
+            return page;
+        }
+
+        //缓存
+        Map<String, Object> cahcheMap = new HashMap<String, Object>();
+        //获取考试名称
+        ExamBean examBean = ExamCacheTransferHelper.getCachedExam(examId);
+
+        page.getContent().forEach(e -> {
+            ExamStudentInfo examStudent = examStudentService.getExamStudentInfo(e.getExamStudentId());
+            if (examStudent != null) {
+                String photoNumber = null;
+                if (cahcheMap.containsKey("photoNumber_" + examStudent.getStudentId())) {
+                    photoNumber = (String) cahcheMap.get("photoNumber_" + examStudent.getStudentId());
+                } else {
+                    StudentCacheBean studentBean = CacheHelper.getStudent(examStudent.getStudentId());
+                    photoNumber = studentBean.getPhoneNumber();
+                    cahcheMap.put("photoNumber_" + examStudent.getStudentId(), photoNumber);
+                }
+                e.setPhone(photoNumber);//电话号码
+
+                e.setOrgName(examStudent.getOrgName());
+                e.setSpecialtyName(examStudent.getSpecialtyName());
+                e.setGrade(examStudent.getGrade());
+                OrgCacheBean orgBean = gainBaseDataService.getOrgBean(examStudent.getOrgId());
+                e.setOrgName(orgBean.getName());
+            }
+
+            e.setExamName(examBean.getName());
+            Map<String, String> data = this.getPaperScore(e.getDataId());
+            //试卷总分
+            e.setPaperTotalScore(data.get("paperTotalScore"));
+            //客观题总分
+            e.setObjectiveTotalScore(data.get("objectiveTotalScore"));
+            //主观题总分
+            e.setSubjectiveTotalScore(data.get("subjectiveTotalScore"));
+        });
+        return page;
+    }
+
+    /**
+     * 获取卷面分
+     */
+    public Map<String, String> getPaperScore(Long examRecordDataId) {
+        Map<String, String> data = new HashMap<>();
+        ExamScoreEntity examScore = examScoreRepo.findByExamRecordDataId(examRecordDataId);
+        //卷面总分
+        Double paperTotalScore = 0d;
+        //客观题总分
+        Double objectiveTotalScore = 0d;
+        //主观题总分
+        Double subjectiveTotalScore = 0d;
+        if (examScore != null) {
+            paperTotalScore = examScore.getObjectiveScore() == null ? 0d : examScore.getObjectiveScore();
+            objectiveTotalScore = examScore.getObjectiveScore() == null ? 0d : examScore.getObjectiveScore();
+            subjectiveTotalScore = examScore.getSubjectiveScore() == null ? 0d : examScore.getSubjectiveScore();
+        }
+        data.put("paperTotalScore", String.valueOf(paperTotalScore));
+        data.put("objectiveTotalScore", String.valueOf(objectiveTotalScore));
+        data.put("subjectiveTotalScore", String.valueOf(subjectiveTotalScore));
+        return data;
+    }
+
+    @Override
+    public Long existsWarnExamRecordDetail(ExamRecordQuery query) {
+        StringBuilder sqlBuilder = new StringBuilder();
+        sqlBuilder.append("select count(record_data.id) from ec_oe_exam_record_data record_data "
+                + " left join ec_oe_exam_record record on record_data.exam_record_id = record.id "
+                + " where 1=1 ");
+        sqlBuilder.append(buildExamRecordCommonSelectCondition(query));
+        sqlBuilder.append(" and record_data.is_warn = 1 and record_data.is_audit = 0");
+        return jdbcTemplate.queryForObject(sqlBuilder.toString(), Long.class);
+    }
+
+    @Override
+    public List<ExamRecordInfo> getExamRecordDetailList(ExamRecordQuery query) {
+        Check.isNull(query, "查询参数不能为空!");
+        query.addRecordStatus(ExamRecordStatus.EXAM_END.name());
+        query.addRecordStatus(ExamRecordStatus.EXAM_OVERDUE.name());
+
+        StringBuilder sqlBuilder = new StringBuilder();
+        sqlBuilder.append(EXAM_RECORD_SQL);
+        sqlBuilder.append(buildExamRecordCommonSelectCondition(query));
+        sqlBuilder.append(" and ((record_data.is_warn = 0) OR (record_data.is_warn  = 1 and record_data.is_audit = 1))");
+        sqlBuilder.append(" order by record.id desc");
+
+        List<ExamRecordDataEntity> examRecordDataList = jdbcTemplate.query(sqlBuilder.toString(), new RowMapper<ExamRecordDataEntity>() {
+            @Override
+            public ExamRecordDataEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
+                return getExamRecordDataEntityByResultSet(rs);
+            }
+        });
+
+        List<ExamRecordInfo> examRecordInfoList = examRecordEntityConvert.of(examRecordDataList);
+        return this.loadExamRecordDetailData(examRecordInfoList, query.getExamId());
+    }
+
+    private List<ExamRecordInfo> loadExamRecordDetailData(List<ExamRecordInfo> list, Long examId) {
+        if (list == null || list.size() == 0) {
+            return null;
+        }
+        //缓存
+        Map<String, Object> cahcheMap = new HashMap<String, Object>();
+        //获取考试名称
+        ExamBean examBean = ExamCacheTransferHelper.getCachedExam(examId);
+        list.forEach(examRecordInfo -> {
+            examRecordInfo.setExamName(examBean.getName());
+            ExamStudentInfo examStudent = (ExamStudentInfo) cahcheMap.get("examStudentinfo_" + examRecordInfo.getExamStudentId());
+            if (examStudent == null) {
+                examStudent = examStudentService.getExamStudentInfo(examRecordInfo.getExamStudentId());
+                cahcheMap.put("examStudentinfo_" + examRecordInfo.getExamStudentId(), examStudent);
+            }
+
+            String photoNumber = localCacheService.getStudentPhotoNumber(cahcheMap, examRecordInfo.getStudentId());
+            examRecordInfo.setPhone(photoNumber);//电话号码
+            if (examStudent != null) {
+                examRecordInfo.setSpecialtyName(examStudent.getSpecialtyName());
+                examRecordInfo.setGrade(examStudent.getGrade());
+            }
+            Map<String, String> data = this.getPaperScore(examRecordInfo.getDataId());
+            //试卷总分
+            examRecordInfo.setPaperTotalScore(data.get("paperTotalScore"));
+            //客观题总分
+            examRecordInfo.setObjectiveTotalScore(data.get("objectiveTotalScore"));
+            //主观题总分
+            examRecordInfo.setSubjectiveTotalScore(data.get("subjectiveTotalScore"));
+        });
+        return list;
+    }
+
+    @Override
+    public List<ExamStudentQuestionScoreInfo> getExamStudentQuestionScoreList(Long examId, String courseCode) {
+        //最终返回的结果集
+        List<ExamStudentQuestionScoreInfo> resultList = new ArrayList<ExamStudentQuestionScoreInfo>();
+        //获取考试记录相关基本信息
+        List<ExamStudentQuestionScoreInfo> partialExamStudentQuestionScoreList = getPartialExamStudentQuestionScoreList(examId, courseCode);
+        List<Long> examRecordDataIdList = new ArrayList<Long>();
+        if (partialExamStudentQuestionScoreList != null && !partialExamStudentQuestionScoreList.isEmpty()) {
+            partialExamStudentQuestionScoreList.forEach(p -> examRecordDataIdList.add(p.getExamRecordDataId()));
+            //批量查询考试记录数据
+            List<ExamRecordQuestionsEntity> examRecordQuestionsList = examRecordQuestionsRepo.findByExamRecordDataIdIn(examRecordDataIdList);
+            for (ExamStudentQuestionScoreInfo scoreInfo : partialExamStudentQuestionScoreList) {
+                List<ExamRecordQuestionsEntity> filterExamRecordQuestionsList = examRecordQuestionsList.stream().filter(p -> p.getExamRecordDataId().equals(scoreInfo.getExamRecordDataId())).collect(Collectors.toList());
+                if (examRecordQuestionsList != null && !examRecordQuestionsList.isEmpty()) {
+                    ExamRecordQuestionsEntity filterExamRecordQuestionsEntity = filterExamRecordQuestionsList.get(0);
+                    //当前考试记录中的所有考题成绩集合
+                    List<ExamQuestionEntity> examQuestionEntities = filterExamRecordQuestionsEntity.getExamQuestionEntities();
+                    if (examQuestionEntities != null && !examQuestionEntities.isEmpty()) {
+                        examQuestionEntities.forEach(qa -> {
+                            ExamStudentQuestionScoreInfo examStudentQuestionScoreInfo = buildExamStudentQuestionScore(scoreInfo, qa);
+                            resultList.add(examStudentQuestionScoreInfo);
+                        });
+                    }
+                }
+            }
+        }
+
+        return resultList;
+    }
+    @Override
+    public List<ExamStudentEffectiveScoreInfo> getExamStudentEffectiveScoreList(String markingType, List<Long> examStudentIdList) {
+        List<ExamStudentEffectiveScoreInfo> resultList = new ArrayList<>();
+        //考生所有的考试记录
+        List<ExamRecordEntity> allExamRecordList = examRecordRepo.findByExamStudentIdList(examStudentIdList);
+        if (allExamRecordList==null || allExamRecordList.isEmpty()){
+            return null;
+        }
+        List<Long> examRecordIdList = allExamRecordList.stream().map(p -> p.getId()).collect(Collectors.toList());
+
+        List<ExamRecordDataEntity> allExamRecordDataList = examRecordDataRepo.findByExamRecordIdIn(examRecordIdList);
+        if (allExamRecordDataList==null || allExamRecordDataList.isEmpty()){
+            return null;
+        }
+        List<Long> examRecordDataIdList = allExamRecordDataList.stream().map(p -> p.getId()).collect(Collectors.toList());
+        //当前考试记录的所有成绩
+        List<ExamScoreEntity> allExamScoreList = examScoreRepo.findByExamRecordDataIdIn(examRecordDataIdList);
+        if (allExamScoreList==null || allExamScoreList.isEmpty()){
+            return null;
+        }
+        Map<Long, List<ExamRecordEntity>> groupedByExamStudentExamRecordMap = allExamRecordList.stream().collect(Collectors.groupingBy(ExamRecordEntity::getExamStudentId));
+        for (Long examStudentId: groupedByExamStudentExamRecordMap.keySet()){
+            //当前考生的所有考试记录集合
+            List<ExamRecordEntity> curExamRecordList=groupedByExamStudentExamRecordMap.get(examStudentId);
+            List<Long> curExamRecordIdList=curExamRecordList.stream().map(p->p.getId()).collect(Collectors.toList());
+            List<ExamRecordDataEntity> curExamRecordDataList=allExamRecordDataList.stream().
+                    filter(p-> curExamRecordIdList.contains(p.getExamRecordId())).collect(Collectors.toList());
+            List<Long> curExamRecordDataIdList = curExamRecordDataList.stream().map(p->p.getId()).collect(Collectors.toList());
+            //当前考生的所有考试成绩集合
+            List<ExamScoreEntity> curExamScoreList=allExamScoreList.stream().
+                    filter(p->curExamRecordDataIdList.contains(p.getExamRecordDataId())).collect(Collectors.toList());
+
+            //最终生效的成绩
+            ExamScoreEntity effectiveScore=getStudentEffectiveScore(curExamScoreList,markingType);
+            //最终生效的考试记录
+            ExamRecordDataEntity effectiveExamRecordData=curExamRecordDataList.stream().
+                    filter(p->p.getId().equals(effectiveScore.getExamRecordDataId())).collect(Collectors.toList()).get(0);
+            ExamRecordEntity effectiveExamRecord = curExamRecordList.stream().
+                    filter(p->p.getId().equals(effectiveExamRecordData.getExamRecordId())).collect(Collectors.toList()).get(0);
+            ExamStudentEffectiveScoreInfo info = new ExamStudentEffectiveScoreInfo();
+            info.setExamStudentId(examStudentId);
+            info.setRootOrgId(effectiveExamRecord.getRootOrgId());
+            info.setOrgId(effectiveExamRecord.getOrgId());
+            info.setBasePaperId(effectiveExamRecord.getBasePaperId());
+            info.setCourseId(effectiveExamRecord.getCourseId());
+            info.setScore(effectiveScore.getTotalScore());
+            info.setExamRecordDataId(effectiveExamRecordData.getId());
+            resultList.add(info);
+        }
+
+        return resultList;
+    }
+    /**
+     * @Description 获取考生一次考试的最终有效成绩
+     * @Author lideyin
+     * @Date 2019/7/18 17:49
+     * @Version 1.0
+     */
+    private ExamScoreEntity getStudentEffectiveScore(List<ExamScoreEntity> curExamScoreList,String markingType){
+        if (curExamScoreList==null || curExamScoreList.isEmpty()){
+            throw new StatusException("201101","考试记录不为空,考试分数数据为空");
+        }
+        ExamScoreEntity examScoreEntity;
+        //阅卷方式为全部评阅或客观题分数最高,均取总分最高
+        if (MarkingType.ALL.toString().equals(markingType) || MarkingType.OBJECT_SCORE_MAX.toString().equals(markingType)) {
+            examScoreEntity = curExamScoreList.stream().max(Comparator.comparing(p -> p.getTotalScore())).get();
+        } else if (MarkingType.LAST_SUBMIT.toString().equals(markingType)) {
+            examScoreEntity=curExamScoreList.stream().max(Comparator.comparing(p -> p.getId())).get();
+        } else {
+            throw new StatusException("201001", "阅卷类型不支持");
+        }
+        return examScoreEntity;
+    }
+
+    /**
+     * 根据考试记录信息和考题信息构建完整 的考生考题作答记录
+     *
+     * @param scoreInfo
+     * @param qa
+     * @return
+     */
+    private ExamStudentQuestionScoreInfo buildExamStudentQuestionScore(ExamStudentQuestionScoreInfo scoreInfo,
+                                                                       ExamQuestionEntity qa) {
+        ExamStudentQuestionScoreInfo examStudentQuestionScoreInfo = new ExamStudentQuestionScoreInfo();
+        examStudentQuestionScoreInfo.setExamRecordDataId(scoreInfo.getExamRecordDataId());
+        examStudentQuestionScoreInfo.setCourseCode(scoreInfo.getCourseCode());
+        examStudentQuestionScoreInfo.setCourseName(scoreInfo.getCourseName());
+        examStudentQuestionScoreInfo.setExamName(scoreInfo.getExamName());
+        examStudentQuestionScoreInfo.setStudentCode(scoreInfo.getStudentCode());
+        examStudentQuestionScoreInfo.setBasePaperId(scoreInfo.getBasePaperId());
+        examStudentQuestionScoreInfo.setMainNumber(qa.getMainNumber());
+        examStudentQuestionScoreInfo.setOrder(qa.getOrder());
+        examStudentQuestionScoreInfo.setStudentScore(qa.getStudentScore());
+        examStudentQuestionScoreInfo.setStudentAnswer(qa.getStudentAnswer());
+        return examStudentQuestionScoreInfo;
+    }
+
+    /**
+     * 根据考试id和课程代码获取学生考试成绩的部分信息
+     *
+     * @param examId     考试id
+     * @param courseCode 课程代码
+     * @return
+     */
+    private List<ExamStudentQuestionScoreInfo> getPartialExamStudentQuestionScoreList(Long examId, String courseCode) {
+        //查询条件
+        StringBuilder sqlBuilder = new StringBuilder();
+        sqlBuilder.append(EXAM_STUDENT_QUESTION_SCORE_SQL);
+        sqlBuilder.append(" and er.exam_id=" + examId);
+        sqlBuilder.append(" and co.`code`='" + courseCode + "'");
+        List<ExamStudentQuestionScoreInfo> examRecordDataList = jdbcTemplate.query(sqlBuilder.toString(), new RowMapper<ExamStudentQuestionScoreInfo>() {
+            @Override
+            public ExamStudentQuestionScoreInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
+                return getExamStudentQuestionScoreInfoByResultSet(rs);
+            }
+        });
+        return examRecordDataList;
+    }
+
+    /**
+     * 根据结果集获取相应的实体对象
+     *
+     * @param rs
+     * @return
+     * @throws SQLException
+     */
+    private ExamStudentQuestionScoreInfo getExamStudentQuestionScoreInfoByResultSet(ResultSet rs) throws SQLException {
+        ExamStudentQuestionScoreInfo info = new ExamStudentQuestionScoreInfo();
+        info.setExamRecordDataId(rs.getLong("exam_record_data_id"));
+        info.setCourseCode(rs.getString("course_code"));
+        info.setCourseName(rs.getString("course_name"));
+        info.setExamName(rs.getString("exam_name"));
+        info.setStudentCode(rs.getString("student_code"));
+        info.setBasePaperId(rs.getString("base_paper_id"));
+        return info;
+    }
+}

+ 63 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamScoreQueueServiceImpl.java

@@ -0,0 +1,63 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import java.util.Date;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScorePushService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScoreQueueService;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamScorePushQueue;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamScoreQueueStatus;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScorePushQueueRepo;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.web.support.SpringContextHolder;
+
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月10日 上午9:26:24
+ * @company 	QMTH
+ * @description 分数推送队列接口实现
+ */
+@Service("examScoreQueueService")
+public class ExamScoreQueueServiceImpl implements ExamScoreQueueService {
+
+	@Autowired
+	private ExamScorePushQueueRepo examScorePushQueueRepo;
+	
+	@Autowired
+	private GainBaseDataService gainBaseDataService;
+	
+	@Override
+	public void disposeScoreQueue(){
+		List<ExamScorePushQueue> examScorePushQueues = examScorePushQueueRepo.findExamScorePushQueue();
+		for(ExamScorePushQueue examScorePushQueue:examScorePushQueues){
+			ExamScorePushService examScorePushService =  (ExamScorePushService) SpringContextHolder.getBean(examScorePushQueue.getServiceName());
+			if(examScorePushService != null){
+				examScorePushService.scorePush(examScorePushQueue);
+			}
+		}
+	}
+
+	@Override
+	public void saveExamScorePushQueue(String returnMsg,ExamScorePushQueue examScorePushQueue,ExamScoreQueueStatus examScoreQueueStatus) {
+		examScorePushQueue.setStatus(examScoreQueueStatus);
+		if(returnMsg != null && returnMsg.length()>500){
+			returnMsg = returnMsg.substring(0,500);
+		}
+		if(examScoreQueueStatus == ExamScoreQueueStatus.PROCESS_SUCCESS){
+			examScorePushQueue.setFailedTimes(0);
+			examScorePushQueue.setErrorMsg(null);
+			examScorePushQueue.setSuccessMsg(returnMsg);
+		}else if(examScoreQueueStatus == ExamScoreQueueStatus.PROCESS_FAILED){
+			int failedTimes = examScorePushQueue.getFailedTimes();
+			examScorePushQueue.setFailedTimes(failedTimes+1);
+			examScorePushQueue.setErrorMsg(returnMsg);
+		}
+		examScorePushQueue.setUpdateTime(new Date());
+		examScorePushQueueRepo.save(examScorePushQueue);
+	}
+}

+ 440 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamScoreServiceImpl.java

@@ -0,0 +1,440 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-31 09:33:22.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.service.*;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore.ExamScoreEntityConvert;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore.ExamScoreInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore.ExamScoreQuery;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentInfo;
+import cn.com.qmth.examcloud.core.oe.common.base.jpa.SpecUtils;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.CommonUtil;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamScoreEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.CourseLevel;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamProperties;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamRecordStatus;
+import cn.com.qmth.examcloud.core.oe.common.enums.MarkingType;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScoreRepo;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.core.oe.common.service.LocalCacheService;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.support.cache.bean.OrgCacheBean;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * 考试分数相关接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/29
+ */
+@Service
+public class ExamScoreServiceImpl implements ExamScoreService {
+    @Autowired
+    private ExamScoreRepo examScoreRepo;
+    @Autowired
+    private ExamRecordDataRepo examRecordDataRepo;
+    @Autowired
+    private ExamStudentService examStudentService;
+    @Autowired
+    private ExamRecordService examRecordService;
+    @Autowired
+    private ExamService examService;
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+    @Autowired
+    private LocalCacheService localCacheService;
+
+    @Override
+    public Page<ExamScoreInfo> getExamScoreList(ExamScoreQuery query) {
+        Check.isNull(query, "查询参数不能为空!");
+        Check.isNull(query.getExamId(), "请先选择考试批次!");
+        Pageable pageable = SpecUtils.buildPageable(query.getPageNo(), query.getPageSize());
+
+        //获取考试信息
+        ExamBean examBean = ExamCacheTransferHelper.getCachedExam(query.getExamId());
+
+        //阅卷方式
+        String markingType = gainBaseDataService.getExamProperty(examBean.getId(),ExamProperties.MARKING_TYPE.name());
+        
+        //获取考生列表
+        Page<ExamStudentInfo> page = examStudentService.getExamStudentListPage(ExamScoreEntityConvert.of(query));
+        List<ExamStudentInfo> examStudentList = page.getContent();
+        if (examStudentList == null || examStudentList.size() == 0) {
+            return new PageImpl<ExamScoreInfo>(Lists.newArrayList(), pageable, page.getTotalElements());
+        }
+
+        //封装成绩统计结果
+        List<ExamScoreInfo> examScoreList = new ArrayList<>();
+        examStudentList.forEach(examStudent -> {
+            examScoreList.add(of(examStudent,examBean,markingType));
+        });
+        return new PageImpl<ExamScoreInfo>(examScoreList, pageable, page.getTotalElements());
+    }
+
+    /**
+     * 封装成绩统计结果
+     */
+    private ExamScoreInfo of(ExamStudentInfo examStudent,ExamBean examBean,String markingType) {
+        ExamScoreInfo examScore = new ExamScoreInfo();
+        examScore.setExamId(examStudent.getExamId());
+        examScore.setExamName(examBean.getName());
+        examScore.setStudentId(examStudent.getStudentId());
+        examScore.setStudentCode(examStudent.getStudentCode());
+        examScore.setStudentName(examStudent.getStudentName());
+        examScore.setIdentityNumber(examStudent.getIdentityNumber());
+        
+        examScore.setCourseId(examStudent.getCourseId());
+        examScore.setCourseCode(examStudent.getCourseCode());
+        examScore.setCourseName(examStudent.getCourseName());
+        examScore.setCourseNameAndCode(examStudent.getCourseName() + "(" + examStudent.getCourseCode() + ")");
+        examScore.setCourseLevel(CourseLevel.getCourseLevelTitle(examStudent.getCourseLevel()));
+        
+        examScore.setOrgId(examStudent.getOrgId());
+        examScore.setOrgCode(examStudent.getOrgCode());
+        examScore.setOrgName(examStudent.getOrgName());
+        examScore.setInfoCollector(examStudent.getInfoCollector());
+        examScore.setSpecialtyName(examStudent.getSpecialtyName());
+        examScore.setGrade(examStudent.getGrade());
+        Long examTimes = examBean.getExamTimes();
+        if(examTimes != null){
+        	examScore.setLeftExamTimes(examTimes - examStudent.getNormalExamTimes());
+        }
+        return setCommomScoreInfo(examScore,examStudent.getFinished(),examStudent.getExamStudentId(),examBean,markingType);
+    }
+
+	@Override
+	public List<ExamScoreInfo> exportExamScoreList(ExamScoreQuery query) {
+		Check.isNull(query, "查询参数不能为空!");
+        Check.isNull(query.getExamId(), "请先选择考试批次!");
+        //获取考试信息
+        ExamBean examBean = ExamCacheTransferHelper.getCachedExam(query.getExamId());
+
+        //阅卷方式
+        String markingType = gainBaseDataService.getExamProperty(examBean.getId(),ExamProperties.MARKING_TYPE.name());
+        return getExamStudentInfoListOfScoreExport(query,examBean,markingType);
+	}
+	
+	private List<ExamScoreInfo> getExamStudentInfoListOfScoreExport(ExamScoreQuery query,ExamBean examBean,String markingType) {
+		Check.isNull(query, "查询参数不能为空!");
+        //查询条件
+        StringBuffer sql = new StringBuffer();
+        sql.append("select id,exam_student_id,exam_id,course_id,course_code,course_level,finished,student_id,student_code,student_name,identity_number"
+        		+ ",info_collector,root_org_id,org_id,paper_type,normal_exam_times,is_reexamine,reexamine_completed"
+        		+ ",specialty_code,specialty_name,grade from ec_oe_exam_student where 1=1 ");
+        if (query.getOrgId() != null) {
+            sql.append(" and org_id="+query.getOrgId());
+        }
+        if (query.getExamId() != null) {
+            sql.append(" and exam_id="+query.getExamId());
+        }
+        if (StringUtils.isNotBlank(query.getStudentCode())) {
+            sql.append(" and student_code LIKE '%"+query.getStudentCode()+"%'");
+        }
+        if (StringUtils.isNotBlank(query.getStudentName())) {
+            sql.append(" and student_name LIKE '%"+query.getStudentName()+"%'");
+        }
+        if (StringUtils.isNotBlank(query.getIdentityNumber())) {
+            sql.append(" and identity_number LIKE '%"+query.getIdentityNumber()+"%'");
+        }
+        if (query.getCourseId() != null) {
+            sql.append(" and course_id="+query.getCourseId());
+        }
+        if (StringUtils.isNotBlank(query.getCourseLevel())) {
+            sql.append(" and course_level= '"+query.getCourseLevel()+"'");
+        }
+        if (query.getFinished() != null) {
+        	sql.append(" and finished= "+query.getFinished());
+        }
+        sql.append(" order by update_time desc");
+        
+        if(query.getStartLimit()!=null && query.getEndLimit() != null){
+        	int offset = query.getEndLimit() - query.getStartLimit() + 1;
+        	sql.append(" limit "+(query.getStartLimit()-1)+","+offset);
+        }
+        List<ExamStudentEntity> examStudentList = jdbcTemplate.query(sql.toString(), new RowMapper<ExamStudentEntity>(){
+			@Override
+			public ExamStudentEntity mapRow(ResultSet rs, int rowNum)throws SQLException {
+				return getExamStudentEntityByResultSet(rs);
+			}
+        });
+        
+        List<ExamScoreInfo> examScoreInfoList = new ArrayList<ExamScoreInfo>();
+        //缓存
+        Map<String,Object> cahcheMap = new HashMap<String, Object>();
+        for(ExamStudentEntity examStudent:examStudentList){
+        	long courseId = examStudent.getCourseId();
+        	CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(courseId);
+        	
+        	long orgId = examStudent.getOrgId();
+            OrgCacheBean orgBean = gainBaseDataService.getOrgBean(orgId);
+
+        	ExamScoreInfo examScoreInfo = convertToExamScoreInfo(examStudent,courseBean,orgBean,examBean,markingType);
+        	examScoreInfoList.add(examScoreInfo);
+        }
+        return examScoreInfoList;
+	}
+	
+    private ExamScoreInfo convertToExamScoreInfo(ExamStudentEntity examStudent,CourseBean courseBean,OrgCacheBean orgBean,ExamBean examBean,String markingType) {
+        if (examStudent == null) {
+            return null;
+        }
+        ExamScoreInfo examScore = new ExamScoreInfo();
+        examScore.setExamId(examStudent.getExamId());
+        examScore.setExamName(examBean.getName());
+        examScore.setStudentId(examStudent.getStudentId());
+        examScore.setStudentCode(examStudent.getStudentCode());
+        examScore.setStudentName(examStudent.getStudentName());
+        examScore.setIdentityNumber(examStudent.getIdentityNumber());
+        examScore.setInfoCollector(examStudent.getInfoCollector());
+        
+        examScore.setCourseId(examStudent.getCourseId());
+        examScore.setCourseCode(examStudent.getCourseCode());
+        examScore.setCourseName(courseBean.getName());
+        examScore.setCourseNameAndCode(courseBean.getName() + "(" + courseBean.getCode() + ")");
+        examScore.setCourseLevel(CourseLevel.getCourseLevelTitle(examStudent.getCourseLevel()));
+        
+        examScore.setOrgId(examStudent.getOrgId());
+        examScore.setOrgCode(orgBean.getCode());
+        examScore.setOrgName(orgBean.getName());
+        
+        examScore.setSpecialtyName(examStudent.getSpecialtyName());
+        examScore.setGrade(examStudent.getGrade());
+
+        Long examTimes = examBean.getExamTimes();
+        if(examTimes != null){
+        	examScore.setLeftExamTimes(examTimes - examStudent.getNormalExamTimes());
+        }
+        
+        return setCommomScoreInfo(examScore,examStudent.getFinished(),examStudent.getExamStudentId(),examBean,markingType);
+    }
+    
+    private ExamScoreInfo setCommomScoreInfo(ExamScoreInfo examScoreInfo,Boolean finished,long examStudentId,ExamBean examBean,String markingType){
+    	//查询考试记录
+        if(finished != null && finished){
+        	//查询考试记录
+        	List<ExamRecordDataEntity> examRecordList = examRecordService.getExamRecordListByExamStudentId(examStudentId);
+        	examScoreInfo.setIsFinished("是");//是否完成考试
+        	examScoreInfo.setIsAbsent("否");//是否缺考
+        	//违纪次数
+        	examScoreInfo.setDisciplineCount(disciplineCount(examRecordList));
+            //提交次数
+        	examScoreInfo.setSubmitCount(submitCount(examRecordList));
+            //设置相关成绩(总分,主观分,客观分)
+            ExamScoreEntity finalExamScore = getFinalEffectiveExamScore(examRecordList,examBean,markingType);
+            setFinalExamScore(examScoreInfo,finalExamScore);
+
+            //考试开始时间
+            examScoreInfo.setStartTime(getExamRecordStartTime(finalExamScore,examRecordList));
+        }else{
+        	examScoreInfo.setIsFinished("否");//是否完成考试
+        	examScoreInfo.setIsAbsent("是");//是否缺考
+        	examScoreInfo.setDisciplineCount("--");//违纪次数
+        	examScoreInfo.setSubmitCount("--"); //提交次数
+            examScoreInfo.setObjectiveScore("--");//主观分
+            examScoreInfo.setSubjectiveScore("--");//客观分
+        	examScoreInfo.setFinalExamScore("--");//最终成绩
+        	examScoreInfo.setStartTime("--");//考试开始时间
+        }
+        return examScoreInfo;
+    }
+
+    /**
+     * 计算得出最终有效成绩
+     */
+    private ExamScoreEntity getFinalEffectiveExamScore(List<ExamRecordDataEntity> examRecordAllList,ExamBean examBean,String markingType) {
+        /*
+         * 第一次过滤考试记录:
+         * 状态为EXAM_END或EXAM_OVERDUE
+         */
+        List<ExamRecordDataEntity> firstFilterExamRecordList = examRecordAllList.stream().filter(examRecord -> {
+            return examRecord.getExamRecordStatus() == ExamRecordStatus.EXAM_END ||
+                    examRecord.getExamRecordStatus() == ExamRecordStatus.EXAM_OVERDUE;
+        }).collect(Collectors.toList());
+
+        if (firstFilterExamRecordList == null || firstFilterExamRecordList.size() == 0) {
+            return null;
+        }
+
+        /*
+         * 第二次过滤考试记录:
+         * 没有违纪的&&(没有警告或有警告已审核通过的)
+         */
+        Stream<ExamRecordDataEntity> secondFilterExamRecordStream = firstFilterExamRecordList.stream().filter(examRecord -> {
+            return !examRecord.getIsIllegality() && (!examRecord.getIsWarn() || (examRecord.getIsWarn() && examRecord.getIsAudit()));
+        });
+
+        List<ExamRecordDataEntity> secondFilterExamRecords = secondFilterExamRecordStream.collect(Collectors.toList());
+        if (secondFilterExamRecords == null || secondFilterExamRecords.size() == 0) {
+            return null;
+        }
+
+        //取出有效记录的成绩
+        List<Long> examRecordDataIds = new ArrayList<>();
+        for (int i = 0; i < secondFilterExamRecords.size(); i++) {
+            examRecordDataIds.add(secondFilterExamRecords.get(i).getId());
+        }
+        List<ExamScoreEntity> effectiveExamScoreList = examScoreRepo.findByExamRecordDataIdIn(examRecordDataIds);
+        if (effectiveExamScoreList == null || effectiveExamScoreList.size() == 0) {
+            return null;
+        }
+        //离线考试
+        if("OFFLINE".equals(examBean.getExamType())){
+            return effectiveExamScoreList.get(0);
+        }else{
+            if (markingType.equals(MarkingType.ALL.name()) || markingType.equals(MarkingType.OBJECT_SCORE_MAX.name())) {
+             	//全部评阅规则或客观分最高规则:取总分最高
+                List<ExamScoreEntity> examScores = effectiveExamScoreList
+                         .stream()
+                         .sorted((o1, o2) -> o2.getTotalScore().compareTo(o1.getTotalScore()))
+                         .collect(Collectors.toList());
+                return examScores.get(0);
+            } else if (markingType.equals(MarkingType.LAST_SUBMIT.name())) {
+                 //最后一次提交规则:取最后一次的成绩
+                List<ExamScoreEntity> examScores = effectiveExamScoreList
+                         .stream()
+                         .sorted((o1, o2) -> o2.getId().compareTo(o1.getId()))
+                         .collect(Collectors.toList());
+                return examScores.get(0);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 给考生最终成绩赋值
+     * @param examScoreInfo 考试模型实体
+     * @param examScore 考试数据实体
+     */
+    private void setFinalExamScore(ExamScoreInfo examScoreInfo, final ExamScoreEntity examScore) {
+        if (examScore == null) {
+            examScoreInfo.setObjectiveScore("0");
+            examScoreInfo.setSubjectiveScore("0");
+            examScoreInfo.setFinalExamScore("0");
+        } else {
+            DecimalFormat decimalFormat = new DecimalFormat("#.##");
+            examScoreInfo.setObjectiveScore(decimalFormat.format(examScore.getObjectiveScore()));
+            examScoreInfo.setSubjectiveScore(decimalFormat.format(examScore.getSubjectiveScore()));
+            examScoreInfo.setFinalExamScore(decimalFormat.format(examScore.getTotalScore()));
+        }
+    }
+
+
+    /**
+     * 考试开始时间
+     */
+    private String getExamRecordStartTime(ExamScoreEntity examScore,List<ExamRecordDataEntity> examRecordList) {
+        if (examScore == null) {
+            return "";
+        }
+        ExamRecordDataEntity examRecordData = null;
+        for(ExamRecordDataEntity examRecordDataEntity:examRecordList){
+        	if(examRecordDataEntity.getId().equals(examScore.getExamRecordDataId())){
+        		examRecordData = examRecordDataEntity;
+        		break;
+        	}
+        }
+        if (examRecordData == null) {
+            return "";
+        }
+        if (examRecordData.getStartTime() == null) {
+            return "";
+        }
+        return CommonUtil.getDateStrWithSecond(examRecordData.getStartTime());
+    }
+
+    /**
+     * 违纪次数
+     */
+    private String disciplineCount(List<ExamRecordDataEntity> examRecordList) {
+        Integer disciplineCount = 0;
+        if (examRecordList == null) {
+            return disciplineCount.toString();
+        }
+        for (ExamRecordDataEntity e : examRecordList) {
+            if (e.getIsIllegality() != null && e.getIsIllegality()) {
+                disciplineCount++;
+            }
+        }
+        return disciplineCount.toString();
+    }
+
+    /**
+     * 正常交卷次数:无警告或者有警告,有审核,有正常提交时间
+     */
+    private String submitCount(List<ExamRecordDataEntity> examRecordList) {
+        Integer submitCount = 0;
+        for (ExamRecordDataEntity e : examRecordList) {
+            if (e.getIsWarn() == null) {
+                e.setIsWarn(false);
+            }
+            if (e.getIsAudit() == null) {
+                e.setIsAudit(false);
+            }
+            if (e.getIsWarn() && !e.getIsAudit()) {
+                //ignore
+            } else {
+                if (e.getEndTime() != null) {
+                    submitCount++;
+                }
+            }
+        }
+        return submitCount.toString();
+    }
+    
+    private ExamStudentEntity getExamStudentEntityByResultSet(ResultSet rs) throws SQLException{
+    	ExamStudentEntity examStudentEntity = new ExamStudentEntity();
+		examStudentEntity.setId(rs.getLong("id"));
+		examStudentEntity.setExamStudentId(rs.getLong("exam_student_id"));
+		examStudentEntity.setExamId(rs.getLong("exam_id"));
+		examStudentEntity.setCourseId(rs.getLong("course_id"));
+		examStudentEntity.setCourseCode(rs.getString("course_code"));
+		examStudentEntity.setCourseLevel(rs.getString("course_level"));
+		examStudentEntity.setFinished(rs.getBoolean("finished"));
+		examStudentEntity.setStudentId(rs.getLong("student_id"));
+		examStudentEntity.setStudentCode(rs.getString("student_code"));
+		examStudentEntity.setStudentName(rs.getString("student_name"));
+		examStudentEntity.setIdentityNumber(rs.getString("identity_number"));
+		examStudentEntity.setInfoCollector(rs.getString("info_collector"));
+		examStudentEntity.setRootOrgId(rs.getLong("root_org_id"));
+		examStudentEntity.setOrgId(rs.getLong("org_id"));
+		examStudentEntity.setPaperType(rs.getString("paper_type"));
+		examStudentEntity.setNormalExamTimes(rs.getInt("normal_exam_times"));
+		examStudentEntity.setIsReExamine(rs.getBoolean("is_reexamine"));
+		examStudentEntity.setReExamineCompleted(rs.getBoolean("reexamine_completed"));
+		examStudentEntity.setSpecialtyCode(rs.getString("specialty_code"));
+		examStudentEntity.setSpecialtyName(rs.getString("specialty_name"));
+		examStudentEntity.setGrade(rs.getString("grade"));
+		return examStudentEntity;
+    }
+    
+}

+ 61 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamServiceImpl.java

@@ -0,0 +1,61 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-29 10:45:43.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamService;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.examwork.api.client.ExamCloudServiceClient;
+import cn.com.qmth.examcloud.examwork.api.request.GetExamReq;
+import cn.com.qmth.examcloud.examwork.api.response.GetExamResp;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 考试相关接口
+ * 基于Spring Cloud远程调用
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/29
+ */
+@Service
+public class ExamServiceImpl implements ExamService {
+    @Autowired
+    private ExamCloudServiceClient examCloudServiceClient;
+
+    @Override
+    public String getExamNameById(Long examId) {
+        ExamBean exam = this.getExam(examId);
+        if (exam != null) {
+            return exam.getName();
+        }
+        return null;
+    }
+
+    @Override
+    public Long getExamTimesById(Long examId) {
+        ExamBean exam = this.getExam(examId);
+        if (exam != null) {
+            return exam.getExamTimes();
+        }
+        return 0L;
+    }
+
+    @Override
+    public ExamBean getExam(Long examId) {
+        Check.isNull(examId, "考试ID不能为空!");
+        GetExamReq req = new GetExamReq();
+        req.setId(examId);
+        GetExamResp resp = examCloudServiceClient.getExam(req);
+        if (resp != null) {
+            return resp.getExamBean();
+        }
+        return null;
+    }
+
+}

+ 654 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamStudentServiceImpl.java

@@ -0,0 +1,654 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-16 17:43:37.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCacheTransferHelper;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamStudentService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.*;
+import cn.com.qmth.examcloud.core.oe.common.base.jpa.SpecUtils;
+import cn.com.qmth.examcloud.core.oe.common.base.jpa.SqlWrapper;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordForMarkingEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.*;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordForMarkingRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamStudentRepo;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.core.oe.common.service.LocalCacheService;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.support.cache.bean.OrgCacheBean;
+import org.apache.commons.lang3.StringUtils;
+import org.hibernate.query.NativeQuery;
+import org.hibernate.transform.Transformers;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+
+import static cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentMapper.statisticFinishedColumns;
+import static cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentMapper.statisticOrgColumns;
+
+/**
+ * 考生信息接口
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/16
+ */
+@Service
+public class ExamStudentServiceImpl implements ExamStudentService {
+    private static final Logger log = LoggerFactory.getLogger(ExamStudentServiceImpl.class);
+    @Autowired
+    private ExamStudentRepo examStudentRepo;
+    @Autowired
+    private ExamRecordDataRepo examRecordDataRepo;
+    @Autowired
+    private ExamRecordRepo examRecordRepo;
+    @Autowired
+    private ExamRecordForMarkingRepo examRecordForMarkingRepo;
+    @Autowired
+    private ExamRecordService examRecordService;
+    @Autowired
+    private EntityManager entityManager;
+    @Autowired
+    private ExamService examService;
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+    @Autowired
+    private LocalCacheService localCacheService;
+
+    @Override
+    public void syncExamStudentAllData(List<ExamStudentInfo> examStudents) {
+        Check.isEmpty(examStudents, "考生信息不能为空!");
+        //封装并校验数据
+        for (ExamStudentInfo cur : examStudents) {
+            Check.isNull(cur.getRootOrgId(), "顶级机构ID不能为空!");
+            Check.isNull(cur.getOrgId(), "学习中心ID不能为空!");
+            Check.isEmpty(cur.getExamStudentId(), "考生ID不能为空!");
+            Check.isNull(cur.getExamId(), "考试ID不能为空!");
+            Check.isNull(cur.getCourseId(), "课程ID不能为空!");
+            Check.isNull(cur.getStudentId(), "学生ID不能为空!");
+            ExamStudentEntity entity = examStudentRepo.findByExamStudentId(cur.getExamStudentId());
+
+            if (entity == null) {
+                entity = new ExamStudentEntity();
+                entity.setFinished(false);
+                entity.setNormalExamTimes(0);
+                entity.setIsReExamine(false);
+                entity.setReExamineCompleted(false);
+                entity.setCreationTime(new Date());
+            }
+            if (entity.getIsReExamine() == null) {
+                entity.setIsReExamine(false);
+            }
+            entity.setExamStudentId(cur.getExamStudentId());
+            entity.setRootOrgId(cur.getRootOrgId());
+            entity.setOrgId(cur.getOrgId());
+            entity.setExamId(cur.getExamId());
+            entity.setStudentId(cur.getStudentId());
+            entity.setStudentName(cur.getStudentName());
+            entity.setStudentCode(cur.getStudentCode());
+            entity.setCourseId(cur.getCourseId());
+            entity.setCourseCode(cur.getCourseCode());
+            entity.setCourseLevel(cur.getCourseLevel());
+            entity.setIdentityNumber(cur.getIdentityNumber());
+            entity.setInfoCollector(cur.getInfoCollector());
+            entity.setSpecialtyCode(cur.getSpecialtyCode());
+            entity.setSpecialtyName(cur.getSpecialtyName());
+            entity.setPaperType(cur.getPaperType());
+            entity.setGrade(cur.getGrade());
+            entity.setUpdateTime(new Date());
+            entity.setEnable(cur.getEnable());
+
+            //保存考生
+            ExamStudentEntity examStudentEntity = examStudentRepo.save(entity);
+            //已完成 考试,更新考试记录表
+            if (examStudentEntity.getFinished() != null && examStudentEntity.getFinished()) {
+                long examStudentId = examStudentEntity.getExamStudentId();
+                String studentName = examStudentEntity.getStudentName();
+                String studentCode = examStudentEntity.getStudentCode();
+                String infoCollector = examStudentEntity.getInfoCollector();
+                examRecordRepo.syncUpdateExamStudentInfo(examStudentId, studentName, studentCode, infoCollector);
+            }
+        }
+    }
+
+    @Override
+    public void syncExamStudentPartData(List<ExamStudentPartInfo> examStudents) {
+        Check.isEmpty(examStudents, "考生信息不能为空!");
+        for (ExamStudentPartInfo info : examStudents) {
+            Check.isNull(info.getStudentId(), "学生ID不能为空!");
+            Check.isNull(info.getStudentCode(), "学号不能为空!");
+            Check.isNull(info.getStudentName(), "学生姓名不能为空!");
+            List<ExamStudentEntity> entities = examStudentRepo.findByStudentId(info.getStudentId());
+            if (entities == null || entities.size() == 0) {
+                continue;
+            }
+            for (ExamStudentEntity entity : entities) {
+                //更新学号、姓名等信息
+                entity.setStudentCode(info.getStudentCode());
+                entity.setStudentName(info.getStudentName());
+            }
+            //批量更新
+            examStudentRepo.saveAll(entities);
+        }
+    }
+
+    @Override
+    @Transactional
+    public void syncRemoveExamStudentByExamId(Long examId) {
+        Check.isNull(examId, "考试ID不能为空!");
+        SqlWrapper wrapper = new SqlWrapper()
+                .delete().from("ec_oe_exam_student").where().eq("exam_id", examId);
+        int result = entityManager.createNativeQuery(wrapper.build()).executeUpdate();
+        log.debug(String.format("[syncRemoveExamStudentByExamId] examId = %s size = %s", examId, result));
+    }
+
+    @Override
+    public Page<ExamStudentInfo> getExamStudentListPage(ExamStudentQuery query) {
+        Check.isNull(query, "查询参数不能为空!");
+
+        ExamBean examBean = ExamCacheTransferHelper.getCachedExam(query.getExamId());
+
+
+        StringBuffer sql = new StringBuffer();
+        sql.append("select id,exam_student_id,exam_id,course_id,course_code,course_level,finished,student_id,student_code,student_name,identity_number"
+                + ",info_collector,root_org_id,org_id,paper_type,normal_exam_times,is_reexamine,reexamine_completed"
+                + ",specialty_code,specialty_name,grade from ec_oe_exam_student t1 where 1=1 ");
+        sql.append(selectExamStudentConfitionSql(query, examBean.getExamType()));
+        sql.append(" order by id desc");
+        int currentNum = (query.getPageNo() - 1) * query.getPageSize();
+        sql.append(" limit " + currentNum + "," + query.getPageSize());
+        List<ExamStudentEntity> examStudentList = jdbcTemplate.query(sql.toString(), new RowMapper<ExamStudentEntity>() {
+            @Override
+            public ExamStudentEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
+                return getExamStudentEntityByResultSet(rs);
+            }
+        });
+
+        List<ExamStudentInfo> examStudentInfoList = new ArrayList<ExamStudentInfo>();
+        //缓存
+        Map<String, Object> cahcheMap = new HashMap<String, Object>();
+        for (ExamStudentEntity examStudentEntity : examStudentList) {
+            ExamStudentInfo examStudentInfo = buildExamStudentInfo(examStudentEntity, cahcheMap, examBean.getExamType());
+            examStudentInfoList.add(examStudentInfo);
+        }
+        cahcheMap.clear();
+        Pageable pageable = SpecUtils.buildPageable(query.getPageNo(), query.getPageSize());
+        long totalSize = countExamStudent(query, examBean.getExamType());
+        return new PageImpl<>(examStudentInfoList, pageable, totalSize);
+    }
+
+    private long countExamStudent(ExamStudentQuery query, String examType) {
+        //查询条件
+        StringBuffer sql = new StringBuffer();
+        sql.append("select count(t1.id) from ec_oe_exam_student t1 where 1=1 ");
+        sql.append(selectExamStudentConfitionSql(query, examType));
+        return jdbcTemplate.queryForObject(sql.toString(), Long.class);
+    }
+
+    @Override
+    public List<ExamStudentInfo> getExamStudentInfoList(ExamStudentQuery query) {
+        Check.isNull(query, "查询参数不能为空!");
+        ExamBean examBean = ExamCacheTransferHelper.getCachedExam(query.getExamId());
+
+        //查询条件
+        StringBuffer sql = new StringBuffer();
+        sql.append("select id,exam_student_id,exam_id,course_id,course_code,course_level,finished,student_id,student_code,student_name,identity_number"
+                + ",info_collector,root_org_id,org_id,paper_type,normal_exam_times,is_reexamine,reexamine_completed"
+                + ",specialty_code,specialty_name,grade from ec_oe_exam_student t1 where 1=1 ");
+        sql.append(selectExamStudentConfitionSql(query, examBean.getExamType()));
+        sql.append(" order by id desc");
+
+        List<ExamStudentEntity> examStudentList = jdbcTemplate.query(sql.toString(), new RowMapper<ExamStudentEntity>() {
+            @Override
+            public ExamStudentEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
+                return getExamStudentEntityByResultSet(rs);
+            }
+        });
+        List<ExamStudentInfo> examStudentInfoList = new ArrayList<ExamStudentInfo>();
+        //缓存
+        Map<String, Object> cahcheMap = new HashMap<String, Object>();
+        for (ExamStudentEntity examStudentEntity : examStudentList) {
+            ExamStudentInfo examStudentInfo = buildExamStudentInfo(examStudentEntity, cahcheMap, examBean.getExamType());
+            examStudentInfoList.add(examStudentInfo);
+        }
+        cahcheMap.clear();
+        return examStudentInfoList;
+    }
+
+    private StringBuffer selectExamStudentConfitionSql(ExamStudentQuery query, String examType) {
+        StringBuffer sql = new StringBuffer();
+        if (query.getOrgId() != null) {
+            sql.append(" and org_id=" + query.getOrgId());
+        }
+        if (query.getExamId() != null) {
+            sql.append(" and exam_id = " + query.getExamId());
+        }
+        if (StringUtils.isNotBlank(query.getStudentCode())) {
+            sql.append(" and student_code LIKE '%" + query.getStudentCode() + "%'");
+        }
+        if (StringUtils.isNotBlank(query.getStudentName())) {
+            sql.append(" and student_name LIKE '%" + query.getStudentName() + "%'");
+        }
+        if (StringUtils.isNotBlank(query.getIdentityNumber())) {
+            sql.append(" and identity_number LIKE '%" + query.getIdentityNumber() + "%'");
+        }
+        if (StringUtils.isNotBlank(query.getInfoCollector())) {
+            sql.append(" and info_collector LIKE '%" + query.getInfoCollector() + "%'");
+        }
+        if (query.getCourseId() != null) {
+            sql.append(" and course_id=" + query.getCourseId());
+        }
+        if (StringUtils.isNotBlank(query.getCourseLevel())) {
+            sql.append(" and course_level= '" + query.getCourseLevel() + "'");
+        }
+        if (query.getFinished() != null) {
+            if (ExamType.ONLINE.name().equals(examType)) {
+                sql.append(" and finished = " + query.getFinished());
+            } else if (ExamType.OFFLINE.name().equals(examType)) {
+                if (query.getFinished() == 0) {                    //未抽题
+                    sql.append(" and finished = 0");
+                } else if (query.getFinished() == 1) {                //已抽题未上传
+                    sql.append(" and finished = 1  and not exists (select id from ec_oe_exam_record_4_marking t2 where t1.exam_student_id = t2.exam_student_id)");
+                } else if (query.getFinished() == 2) {                //已抽题已上传
+                    sql.append(" and finished = 1  and  exists (select id from ec_oe_exam_record_4_marking t2 where t1.exam_student_id = t2.exam_student_id)");
+                }
+            }
+        }
+        return sql;
+    }
+
+    private ExamStudentInfo buildExamStudentInfo(ExamStudentEntity examStudentEntity, Map<String, Object> cahcheMap, String examType) {
+        ExamStudentInfo examStudentInfo = ExamStudentEntityConvert.of(examStudentEntity);
+        examStudentInfo.setExamType(examType);
+        CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examStudentInfo.getCourseId());
+        examStudentInfo.setCourseName(courseBean.getName());
+        examStudentInfo.setCourseCode(courseBean.getCode());
+        examStudentInfo.setCourseLevel(CourseLevel.getCourseLevel(courseBean.getLevel()).getTitle());
+
+        OrgCacheBean orgBean = gainBaseDataService.getOrgBean(examStudentInfo.getOrgId());
+        examStudentInfo.setOrgCode(orgBean.getCode());
+        examStudentInfo.setOrgName(orgBean.getName());
+
+        String photoNumber = localCacheService.getStudentPhotoNumber(cahcheMap, examStudentInfo.getStudentId());
+        examStudentInfo.setPhone(photoNumber);//电话号码
+
+        if (ExamType.ONLINE.name().equals(examType)) {
+            //完成状态
+            examStudentInfo.setFinishedStatus(examStudentEntity.getFinished() ? FinishStatus.已完成.name() : FinishStatus.未完成.name());
+        } else if (ExamType.OFFLINE.name().equals(examType)) {
+            //离线考试:当前机构是否允许上传附件
+            String canUploadAttachment = gainBaseDataService.getExamOrgProperty(examStudentInfo.getExamId(), examStudentInfo.getOrgId(), ExamProperties.CAN_UPLOAD_ATTACHMENT.name());
+            if (StringUtils.isNotBlank(canUploadAttachment)) {
+                examStudentInfo.setCanUploadAttachment(Boolean.valueOf(canUploadAttachment));
+            } else {
+                examStudentInfo.setCanUploadAttachment(false);
+            }
+            //完成状态
+            if (!examStudentEntity.getFinished()) {
+                examStudentInfo.setFinishedStatus(FinishStatus.未抽题.name());
+            } else {
+                ExamRecordForMarkingEntity examRecordForMarking = examRecordForMarkingRepo.findTopByExamStudentId(examStudentEntity.getExamStudentId());
+                if (examRecordForMarking != null && StringUtils.isNotBlank(examRecordForMarking.getOfflineFileUrl())) {
+                    examStudentInfo.setFinishedStatus(FinishStatus.已上传.name());
+                    examStudentInfo.setOfflineFileUrl(examRecordForMarking.getOfflineFileUrl());
+                } else {
+                    examStudentInfo.setFinishedStatus(FinishStatus.已抽题.name());
+                }
+            }
+        }
+        return examStudentInfo;
+    }
+
+    private ExamStudentEntity getExamStudentEntityByResultSet(ResultSet rs) throws SQLException {
+        ExamStudentEntity examStudentEntity = new ExamStudentEntity();
+        examStudentEntity.setId(rs.getLong("id"));
+        examStudentEntity.setExamStudentId(rs.getLong("exam_student_id"));
+        examStudentEntity.setExamId(rs.getLong("exam_id"));
+        examStudentEntity.setCourseId(rs.getLong("course_id"));
+        examStudentEntity.setCourseCode(rs.getString("course_code"));
+        examStudentEntity.setCourseLevel(rs.getString("course_level"));
+        examStudentEntity.setFinished(rs.getBoolean("finished"));
+        examStudentEntity.setStudentId(rs.getLong("student_id"));
+        examStudentEntity.setStudentCode(rs.getString("student_code"));
+        examStudentEntity.setStudentName(rs.getString("student_name"));
+        examStudentEntity.setIdentityNumber(rs.getString("identity_number"));
+        examStudentEntity.setInfoCollector(rs.getString("info_collector"));
+        examStudentEntity.setRootOrgId(rs.getLong("root_org_id"));
+        examStudentEntity.setOrgId(rs.getLong("org_id"));
+        examStudentEntity.setPaperType(rs.getString("paper_type"));
+        examStudentEntity.setNormalExamTimes(rs.getInt("normal_exam_times"));
+        examStudentEntity.setIsReExamine(rs.getBoolean("is_reexamine"));
+        examStudentEntity.setReExamineCompleted(rs.getBoolean("reexamine_completed"));
+        examStudentEntity.setSpecialtyCode(rs.getString("specialty_code"));
+        examStudentEntity.setSpecialtyName(rs.getString("specialty_name"));
+        examStudentEntity.setGrade(rs.getString("grade"));
+        return examStudentEntity;
+    }
+
+    @Override
+    public Page<ExamStudentInfo> getReExamineStudentList(ExamStudentQuery query) {
+        //获取考试的默认次数
+        ExamBean examBean = ExamCacheTransferHelper.getCachedExam(query.getExamId());
+
+        //封装查询条件
+        Pageable pageable = SpecUtils.buildPageable(query.getPageNo(), query.getPageSize());
+        StringBuffer sql = new StringBuffer();
+
+        sql.append("select t.id," +
+                "t.exam_student_id," +
+                "t.exam_id," +
+                "t.student_id," +
+                "t.student_code," +
+                "t.student_name," +
+                "t.identity_number," +
+                "t.course_id," +
+                "t.course_code," +
+                "t.course_level," +
+                "t.org_id," +
+                "t.root_org_id," +
+                "t.specialty_name," +
+                "t.finished," +
+                "t.normal_exam_times from ( ");
+        sql.append("SELECT * " +
+                " FROM ec_oe_exam_student student" +
+                " WHERE exam_id = " + examBean.getId() +
+                " AND normal_exam_times >= " + examBean.getExamTimes().longValue() +
+                " AND ((is_reexamine = 0 OR is_reexamine is null) OR ( is_reexamine = 1 AND reexamine_completed = 1))" +
+                " AND NOT EXISTS (" +
+                "	SELECT * FROM ec_oe_exam_record t1" +
+                "	LEFT JOIN ec_oe_exam_record_data t2 ON t1.id = t2.exam_record_id" +
+                "	WHERE" +
+                "		t1.exam_id = " + examBean.getId() + " and student.exam_student_id = t1.exam_student_id" +
+                "	AND t2.exam_record_status = 'EXAM_ING'" +
+                ") ");
+
+        sql.append(" order by student.id desc ");
+        sql.append(") t where 1=1 ");
+        if (query.getOrgId() != null) {
+            sql.append(" and t.org_id = " + query.getOrgId());
+        }
+        if (StringUtils.isNotBlank(query.getStudentCode())) {
+            sql.append(" and t.student_code LIKE '%" + query.getStudentCode() + "%'");
+        }
+        if (StringUtils.isNotBlank(query.getStudentName())) {
+            sql.append(" and t.student_name LIKE '%" + query.getStudentName() + "%'");
+        }
+        if (StringUtils.isNotBlank(query.getIdentityNumber())) {
+            sql.append(" and t.identity_number LIKE '%" + query.getIdentityNumber() + "%'");
+        }
+        if (query.getCourseId() != null && StringUtils.isNotBlank(query.getCourseId() + "")) {
+            sql.append(" and t.course_id=" + query.getCourseId());
+        }
+        if (StringUtils.isNotBlank(query.getCourseLevel())) {
+            sql.append(" and t.course_level= '" + query.getCourseLevel() + "'");
+        }
+        int currentNum = (query.getPageNo() - 1) * query.getPageSize();
+        sql.append(" limit " + currentNum + "," + query.getPageSize());
+
+        log.info("重考列表sql:" + sql.toString());
+        List<ExamStudentEntity> examStudentList = jdbcTemplate.query(sql.toString(), new RowMapper<ExamStudentEntity>() {
+            @Override
+            public ExamStudentEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
+                ExamStudentEntity examStudentEntity = new ExamStudentEntity();
+                examStudentEntity.setId(rs.getLong("id"));
+                examStudentEntity.setExamStudentId(rs.getLong("exam_student_id"));
+                examStudentEntity.setExamId(rs.getLong("exam_id"));
+                examStudentEntity.setStudentId(rs.getLong("student_id"));
+                examStudentEntity.setStudentCode(rs.getString("student_code"));
+                examStudentEntity.setStudentName(rs.getString("student_name"));
+                examStudentEntity.setIdentityNumber(rs.getString("identity_number"));
+                examStudentEntity.setCourseId(rs.getLong("course_id"));
+                examStudentEntity.setCourseCode(rs.getString("course_code"));
+                examStudentEntity.setCourseLevel(rs.getString("course_level"));
+                examStudentEntity.setOrgId(rs.getLong("org_id"));
+                examStudentEntity.setRootOrgId(rs.getLong("root_org_id"));
+                examStudentEntity.setSpecialtyName(rs.getString("specialty_name"));
+                examStudentEntity.setFinished(rs.getBoolean("finished"));
+                examStudentEntity.setNormalExamTimes(rs.getInt("normal_exam_times"));
+                return examStudentEntity;
+            }
+        });
+        long totalSize = countReExamine(query, examBean);
+        List<ExamStudentInfo> list = new ArrayList<ExamStudentInfo>();
+        Map<String, Object> cahcheMap = new HashMap<String, Object>();
+        for (ExamStudentEntity examStudentEntity : examStudentList) {
+            ExamStudentInfo examStudentInfo = ExamStudentEntityConvert.of(examStudentEntity);
+            OrgCacheBean orgBean = gainBaseDataService.getOrgBean(examStudentInfo.getOrgId());
+            examStudentInfo.setOrgName(orgBean.getName());
+            examStudentInfo.setOrgCode(orgBean.getCode());
+            CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examStudentInfo.getCourseId());
+            examStudentInfo.setCourseName(courseBean.getName());
+            list.add(examStudentInfo);
+        }
+        return new PageImpl<>(list, pageable, totalSize);
+    }
+
+    private Long countReExamine(ExamStudentQuery query, ExamBean examBean) {
+        StringBuffer sql = new StringBuffer();
+        sql.append("SELECT count(*)" +
+                " FROM " +
+                "	ec_oe_exam_student student" +
+                " WHERE" +
+                "	exam_id = " + examBean.getId() +
+                " AND normal_exam_times >= " + examBean.getExamTimes().longValue() +
+                " AND ((is_reexamine = 0 OR is_reexamine is null) OR ( is_reexamine = 1 AND reexamine_completed = 1))" +
+                " AND NOT EXISTS (" +
+                "	SELECT * FROM ec_oe_exam_record t1" +
+                "	LEFT JOIN ec_oe_exam_record_data t2 ON t1.id = t2.exam_record_id" +
+                "	WHERE t1.exam_id = " + examBean.getId() +
+                "   AND student.exam_student_id = t1.exam_student_id" +
+                "	AND t2.exam_record_status = 'EXAM_ING'" +
+                ")");
+        if (query.getOrgId() != null) {
+            sql.append(" and student.org_id = " + query.getOrgId());
+        }
+        if (StringUtils.isNotBlank(query.getStudentCode())) {
+            sql.append(" and student.student_code LIKE '%" + query.getStudentCode() + "%'");
+        }
+        if (StringUtils.isNotBlank(query.getStudentName())) {
+            sql.append(" and student.student_name LIKE '%" + query.getStudentName() + "%'");
+        }
+        if (StringUtils.isNotBlank(query.getIdentityNumber())) {
+            sql.append(" and student.identity_number LIKE '%" + query.getIdentityNumber() + "%'");
+        }
+        if (query.getCourseId() != null && StringUtils.isNotBlank(query.getCourseId() + "")) {
+            sql.append(" and student.course_id=" + query.getCourseId());
+        }
+        if (StringUtils.isNotBlank(query.getCourseLevel())) {
+            sql.append(" and student.course_level= '" + query.getCourseLevel() + "'");
+        }
+        return jdbcTemplate.queryForObject(sql.toString(), Long.class);
+    }
+
+    @Override
+    public ExamStudentFinishedStatistic getExamStudentStatisticByFinished(Long examId) {
+        SqlWrapper wrapper = new SqlWrapper()
+                .select(statisticFinishedColumns())
+                .from("ec_oe_exam_student").as("student")
+                .where().eq("student.exam_id", examId);
+        Query dataQuery = entityManager.createNativeQuery(wrapper.build());
+//        dataQuery.unwrap(SQLQuery.class).setResultTransformer(Transformers.aliasToBean(HashMap.class));
+        dataQuery.unwrap(NativeQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
+        Map<String, BigDecimal> map = (HashMap) dataQuery.getSingleResult();
+        ExamStudentFinishedStatistic statistic = new ExamStudentFinishedStatistic();
+        if (map != null) {
+            if (map.get("finished") == null) {
+                statistic.setFinished(0);
+            } else {
+                statistic.setFinished(map.get("finished").intValue());
+            }
+            if (map.get("unFinished") == null) {
+                statistic.setUnFinished(0);
+            } else {
+                statistic.setUnFinished(map.get("unFinished").intValue());
+            }
+        }
+        return statistic;
+    }
+
+    @Override
+    public List<ExamStudentOrgStatistic> getExamStudentStatisticByOrg(Long examId, Long orgId) {
+        SqlWrapper wrapper = new SqlWrapper()
+                .select(statisticOrgColumns())
+                .from("ec_oe_exam_student").as("student")
+                .where().eq("student.exam_id", examId);
+        if (orgId != null) {
+            wrapper.and().eq("student.org_id", orgId);
+        }
+        wrapper.groupBy("student.org_id").orderBy("student.org_id", false);
+        Query dataQuery = entityManager.createNativeQuery(wrapper.build());
+//        dataQuery.unwrap(SQLQuery.class).setResultTransformer(Transformers.aliasToBean(HashMap.class));
+        dataQuery.unwrap(NativeQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
+        List<ExamStudentOrgStatistic> examStudentOrgStatisticList = ExamStudentEntityConvert.ofList(dataQuery.getResultList());
+        Map<String, Object> cahcheMap = new HashMap<String, Object>();
+        for (ExamStudentOrgStatistic statistic : examStudentOrgStatisticList) {
+            OrgCacheBean orgBean = gainBaseDataService.getOrgBean(statistic.getOrgId());
+            statistic.setOrgCode(orgBean.getCode());
+            statistic.setOrgName(orgBean.getName());
+        }
+        return examStudentOrgStatisticList;
+    }
+
+    @Override
+    public ExamStudentInfo getExamStudentInfo(Long examStudentId) {
+        ExamStudentEntity entity = examStudentRepo.findByExamStudentId(examStudentId);
+        return ExamStudentEntityConvert.of(entity);
+    }
+
+    @Override
+    public List<Long> findCoursesFromExamStudent(Long examId, Long orgId) {
+        String sql = "select course_id from ec_oe_exam_student where exam_id = " + examId;
+        if (orgId != null) {
+            sql += " and org_id = " + orgId;
+        }
+        sql += "  group by course_id";
+        return jdbcTemplate.queryForList(sql, Long.class);
+    }
+
+    @Override
+    @Transactional
+    public void setReexamine(Long examStudentId) {
+        List<ExamRecordDataEntity> examRecordDataList = examRecordService.getExamRecordListByExamStudentId(examStudentId);
+        //查询出上一次重考的记录
+        Optional<ExamRecordDataEntity> examRecordOptional = examRecordDataList.stream().filter(examRecordData ->
+                examRecordData.getExamRecordStatus() != ExamRecordStatus.EXAM_INVALID
+                        && examRecordData.getIsReexamine() != null
+                        && examRecordData.getIsReexamine()
+        ).findFirst();
+        ExamRecordDataEntity examRecordData = null;
+        if (examRecordOptional.isPresent()) {
+            examRecordData = examRecordOptional.get();
+        }
+        if (examRecordData != null) {
+            //将上一次重考的记录设置成无效
+            examRecordData.setExamRecordStatus(ExamRecordStatus.EXAM_INVALID);
+            examRecordDataRepo.save(examRecordData);
+            //删除生成的阅卷数据,避免传到阅卷
+            ExamRecordForMarkingEntity examRecordForMarking = examRecordForMarkingRepo.findByExamRecordDataId(examRecordData.getId());
+            if (examRecordForMarking != null) {
+                examRecordForMarkingRepo.delete(examRecordForMarking);
+            }
+        }
+        //在考生信息中设置重考为true
+        ExamStudentEntity examStudent = examStudentRepo.findByExamStudentId(examStudentId);
+        examStudent.setIsReExamine(true);
+        //设置了重考,但考生重考未完成,考生开始重考时,该字段改为true
+        examStudent.setReExamineCompleted(false);
+        examStudentRepo.save(examStudent);
+    }
+
+    @Override
+    public List<CourseProgressInfo> queryCourseProgressInfos(Long examId, Long courseId, String orderColumn) {
+        if (examId == null) {
+            return null;
+        }
+        if (StringUtils.isBlank(orderColumn)) {
+            orderColumn = "all_num";
+        }
+        String sql = "select *,ROUND(tb.completed_num/tb.all_num,2)*100 completed_proportion from ( " +
+                " select " +
+                " course_id, " +
+                " sum(case when finished = 1 then 1 else 0 end) completed_num, " +
+                " sum(case when finished = 0 then 1 else 0 end) no_completed_num, " +
+                " count(course_id) all_num" +
+                " from ec_oe_exam_student " +
+                " where exam_id = " + examId;
+        if (courseId != null) {
+            sql += " and course_id = " + courseId;
+        }
+        sql += " group by course_id ) tb ORDER BY " + orderColumn + " desc";
+
+        return jdbcTemplate.query(sql, new RowMapper<CourseProgressInfo>() {
+            @Override
+            public CourseProgressInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
+                CourseProgressInfo courseProgressInfo = new CourseProgressInfo();
+                courseProgressInfo.setCourseId(rs.getLong("course_id"));
+                courseProgressInfo.setCompletedNum(rs.getInt("completed_num"));
+                courseProgressInfo.setNoCompletedNum(rs.getInt("no_completed_num"));
+                courseProgressInfo.setAllNum(rs.getInt("all_num"));
+                courseProgressInfo.setCompletedProportion(rs.getDouble("completed_proportion"));
+                return courseProgressInfo;
+            }
+        });
+    }
+
+    @Override
+    public List<ExamStudentInfo> getLimitExamStudentList(Long examId, Long startId, Integer size) {
+        List<ExamStudentInfo> resultList = new ArrayList<>();
+        List<ExamStudentEntity> studentEntityList = examStudentRepo.getLimitExamStudentList(examId, startId, size);
+        for (ExamStudentEntity se : studentEntityList) {
+            ExamStudentInfo info = new ExamStudentInfo();
+            info.setId(se.getId());
+            info.setExamStudentId(se.getExamStudentId());
+            info.setExamId(se.getExamId());
+            info.setCourseId(se.getCourseId());
+            info.setCourseCode(se.getCourseCode());
+            info.setCourseLevel(se.getCourseLevel());
+            info.setFinished(se.getFinished());
+            info.setStudentId(se.getStudentId());
+            info.setStudentCode(se.getStudentCode());
+            info.setStudentName(se.getStudentName());
+            info.setIdentityNumber(se.getIdentityNumber());
+            info.setInfoCollector(se.getInfoCollector());
+            info.setRootOrgId(se.getRootOrgId());
+            info.setOrgId(se.getOrgId());
+            info.setPaperType(se.getPaperType());
+            info.setNormalExamTimes(se.getNormalExamTimes());
+            info.setIsReExamine(se.getIsReExamine());
+            info.setReExamineCompleted(se.getReExamineCompleted());
+            info.setSpecialtyCode(se.getSpecialtyCode());
+            info.setSpecialtyName(se.getSpecialtyName());
+            info.setGrade(se.getGrade());
+            resultList.add(info);
+        }
+        return resultList;
+    }
+
+}

+ 189 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/pushscore/bean/ExamScorePushInfo.java

@@ -0,0 +1,189 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.impl.pushscore.bean;
+
+import java.util.Date;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年10月15日 下午4:05:20
+ * @company 	QMTH
+ * @description 推送给学校的考试记录,学生信息,分数信息等数据
+ */
+public class ExamScorePushInfo  implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -5176922468872092382L;
+	
+	private String examName;
+	
+	private Long examId;
+	
+	private String studentName;
+	
+	private String studentCode;
+	
+	private String identityNumber;
+	
+	private String courseName;
+	
+	private String courseCode;
+	
+	private Date startTime;
+	
+	private Date endTime;
+	
+	/**
+	 * 人脸检测通过率
+	 */
+	private Double succPercent;
+	
+	/**
+     * 是否缺考
+     */
+    private Boolean isMissExam;
+	
+	private Long scoreId;
+	
+	private Double totalScore;
+	
+	private Double objectiveScore;
+	
+	private Double subjectiveScore;
+	/**
+	 * 是否违纪
+	 */
+	private Boolean isIllegality;
+
+	public Long getExamId() {
+		return examId;
+	}
+
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+
+	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 String getCourseCode() {
+		return courseCode;
+	}
+
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+
+	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 Double getSuccPercent() {
+		return succPercent;
+	}
+
+	public void setSuccPercent(Double succPercent) {
+		this.succPercent = succPercent;
+	}
+
+	public Long getScoreId() {
+		return scoreId;
+	}
+
+	public void setScoreId(Long scoreId) {
+		this.scoreId = scoreId;
+	}
+
+	public Double getTotalScore() {
+		return totalScore;
+	}
+
+	public void setTotalScore(Double totalScore) {
+		this.totalScore = totalScore;
+	}
+
+	public Double getObjectiveScore() {
+		return objectiveScore;
+	}
+
+	public void setObjectiveScore(Double objectiveScore) {
+		this.objectiveScore = objectiveScore;
+	}
+
+	public Double getSubjectiveScore() {
+		return subjectiveScore;
+	}
+
+	public void setSubjectiveScore(Double subjectiveScore) {
+		this.subjectiveScore = subjectiveScore;
+	}
+
+	public Boolean getIsIllegality() {
+		return isIllegality;
+	}
+
+	public void setIsIllegality(Boolean isIllegality) {
+		this.isIllegality = isIllegality;
+	}
+
+	public String getExamName() {
+		return examName;
+	}
+
+	public void setExamName(String examName) {
+		this.examName = examName;
+	}
+
+	public Boolean getIsMissExam() {
+		return isMissExam;
+	}
+
+	public void setIsMissExam(Boolean isMissExam) {
+		this.isMissExam = isMissExam;
+	}
+	
+}

+ 182 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/pushscore/cugr/CugrExamScorePushServiceImpl.java

@@ -0,0 +1,182 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.impl.pushscore.cugr;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCacheTransferHelper;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.CourseCacheBean;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScorePushService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScoreQueueService;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamScorePushQueue;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamScoreQueueStatus;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScorePushQueueRepo;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年10月18日 下午4:08:51
+ * @company 	QMTH
+ * @description 地质大学(武汉)入学  orgCode:cugr.ecs.qmth.com.cn
+ */
+@Service("cugrExamScorePushServiceImpl")
+public class CugrExamScorePushServiceImpl implements ExamScorePushService{
+	
+	private static final Logger log = LoggerFactory.getLogger(CugrExamScorePushServiceImpl.class);
+
+	@Value("${$cug_wh_push_score_key}")
+	private String pushScoreKey;
+	
+	@Autowired
+	private JdbcTemplate jdbcTemplate;
+	
+	@Autowired
+	private ExamScorePushQueueRepo examScorePushQueueRepo;
+	
+	@Autowired
+	private ExamScoreQueueService examScoreQueueService;
+	
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+	
+	private static final String CUGR_URL = "http://202.114.196.58/busInfcCenter/setexamscore.infc";
+	
+	@Override
+	public void scorePush(ExamScorePushQueue examScorePushQueue) {
+		CloseableHttpResponse httpResponse = null;
+		CloseableHttpClient httpClient = HttpClients.createDefault();
+		String result = "";
+		try{
+			String url = getUrl(examScorePushQueue);
+			log.info("cugr_push_score_url:"+url);
+			HttpPost httpPost = new HttpPost(url);
+			httpResponse = httpClient.execute(httpPost);
+			HttpEntity responseEntity = httpResponse.getEntity();
+			BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
+	        StringBuffer buffer = new StringBuffer();
+	        String str = "";
+	        while(StringUtils.isNoneBlank((str = reader.readLine()))) {
+	            buffer.append(str);
+	        }
+	        result = buffer.toString();
+	        JSONObject jsonObject = new JSONObject(result);
+	        if(jsonObject.has("describe")&&"success".equals(jsonObject.getString("describe"))){
+	        	examScoreQueueService.saveExamScorePushQueue(result, examScorePushQueue, ExamScoreQueueStatus.PROCESS_SUCCESS);
+	        }else{
+	        	examScoreQueueService.saveExamScorePushQueue(result, examScorePushQueue, ExamScoreQueueStatus.PROCESS_FAILED);
+	        }
+		}catch(Exception e){
+			log.error(examScorePushQueue.getId() + "成绩推送失败", e);
+			examScoreQueueService.saveExamScorePushQueue(e.getMessage(), examScorePushQueue, ExamScoreQueueStatus.PROCESS_FAILED);
+		}finally{
+			try {
+				if(httpClient !=null){
+					httpClient.close();
+				}
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+            try {
+            	if(httpResponse!=null){
+            		 httpResponse.close();
+            	}
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+	}
+	private String getUrl(ExamScorePushQueue examScorePushQueue){
+		long examRecordId = examScorePushQueue.getExamRecordId();
+		long scoreId = examScorePushQueue.getExamScoreId();
+		
+		String examRecordsql = "select identity_number,course_id from ec_oe_exam_record where id="+examRecordId;
+		String scoreSql = "select total_score from  ec_oe_exam_score where id="+scoreId;
+		
+		List<TmpExamRecord> examRecordList = jdbcTemplate.query(examRecordsql,new RowMapper<TmpExamRecord>(){
+			@Override
+			public TmpExamRecord mapRow(ResultSet rs, int rowNum) throws SQLException {
+				TmpExamRecord tmpExamRecord = new TmpExamRecord();
+				tmpExamRecord.setIdentityNumber(rs.getString("identity_number"));
+				tmpExamRecord.setCourseId(rs.getLong("course_id"));
+				return tmpExamRecord;
+			}
+		});
+		
+		TmpExamRecord examRecord = examRecordList.get(0);
+		
+		CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examRecord.getCourseId());
+
+		double totalScore = jdbcTemplate.queryForObject(scoreSql, Double.class);
+		return buildUrl(totalScore,courseBean.getCode(),examRecord.getIdentityNumber());
+	}
+
+	private String buildUrl(Double totalScore, String courseCode, String identityNumber) {
+		String now = DateUtil.now("yyyy-MM-dd%20HH:mm:ss");
+
+		StringBuilder sb = new StringBuilder();
+		sb.append(courseCode).append(",").append(identityNumber).append(",").append("'cugexma'").append(pushScoreKey);
+		String sign = null;
+		try {
+			sign = DigestUtils.md5Hex(sb.toString().getBytes("utf-8"));
+		} catch (UnsupportedEncodingException e) {
+			throw new RuntimeException(e);
+		}
+		StringBuilder params = new StringBuilder();
+		params.append("new_date").append("=").append(now);
+		params.append("&").append("total_score").append("=").append(totalScore);
+		params.append("&").append("rec_exam_course").append("=").append(courseCode);
+		params.append("&").append("card_no").append("=").append(identityNumber);
+		params.append("&").append("md5sign").append("=").append(sign);
+
+		return CUGR_URL + "?" + params.toString();
+	}
+	
+}
+
+class TmpExamRecord{
+	
+	private String identityNumber;
+	
+	private Long courseId;
+	
+	public String getIdentityNumber() {
+		return identityNumber;
+	}
+
+	public void setIdentityNumber(String identityNumber) {
+		this.identityNumber = identityNumber;
+	}
+
+	public Long getCourseId() {
+		return courseId;
+	}
+
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
+	
+}
+

+ 202 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/pushscore/cup/CupExamScorePushServiceImpl.java

@@ -0,0 +1,202 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.impl.pushscore.cup;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URI;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCacheTransferHelper;
+import cn.com.qmth.examcloud.core.oe.common.entity.*;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.CourseCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamOrgSettingsCacheBean;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.commons.util.JsonUtil;
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScorePushService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScoreQueueService;
+import cn.com.qmth.examcloud.core.oe.admin.service.impl.pushscore.bean.ExamScorePushInfo;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamScoreQueueStatus;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScorePushQueueRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScoreRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamStudentRepo;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年10月18日 下午4:09:26
+ * @company 	QMTH
+ * @description 石油大学  orgCode:cup.ecs.qmth.com.cn
+ */
+@Service("cupExamScorePushServiceImpl")
+public class CupExamScorePushServiceImpl implements ExamScorePushService{
+	
+	private static final Logger log = LoggerFactory.getLogger(CupExamScorePushServiceImpl.class);
+
+	@Autowired
+	private ExamScoreRepo examScoreRepo;
+	
+	@Autowired
+	private ExamRecordDataRepo examRecordDataRepo;
+	
+	@Autowired
+	private ExamRecordRepo examRecordRepo;
+	
+	@Autowired
+	private ExamStudentRepo examStudentRepo;
+	
+	@Autowired
+	private ExamScorePushQueueRepo examScorePushQueueRepo;
+	
+	@Autowired
+	private ExamScoreQueueService examScoreQueueService;
+	
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+    
+    private String pushUrlSchema = "http";
+
+    private String pushUrlHost = "www.cupde.cn";
+
+    private String pushUrlPort = "80";
+	
+	@Override
+	public void scorePush(ExamScorePushQueue examScorePushQueue) {
+		log.info("开始推送石油大学成绩:"+examScorePushQueue.getId());
+		Long scoreId = examScorePushQueue.getExamScoreId();
+		ExamScoreEntity examScore = GlobalHelper.getEntity(examScoreRepo,scoreId,ExamScoreEntity.class);
+		ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo,examScore.getExamRecordDataId(),ExamRecordDataEntity.class);
+
+		ExamRecordEntity examRecord =GlobalHelper.getEntity(
+				examRecordRepo,examRecordData.getExamRecordId(),ExamRecordEntity.class);
+
+		ExamStudentEntity examStudent = examStudentRepo.findByExamStudentId(examRecord.getExamStudentId());
+		if(examScore == null || examRecordData == null || examStudent == null){
+			return;
+		}
+		
+		ExamScorePushInfo examScorePushInfo = buildexamScorePushInfo(examScore,examRecordData,examStudent);
+    	String courseCode = examScorePushInfo.getCourseCode();
+    	//将Z000001变成000001
+    	if(courseCode.startsWith("Z")||courseCode.startsWith("Q")||courseCode.startsWith("S")){
+    		examScorePushInfo.setCourseCode(courseCode.substring(1, courseCode.length()));
+    	}else{
+    		examScorePushInfo.setCourseCode(courseCode);
+    	}
+        CloseableHttpResponse httpResponse = null;
+        CloseableHttpClient httpClient = null;
+        try {
+            String scoreStr = JsonUtil.toJson(examScorePushInfo);
+            log.info("石油大学成绩数据:{}", scoreStr);
+            RequestConfig config = RequestConfig.custom()
+                    .setConnectTimeout(5000)
+                    .setConnectionRequestTimeout(5000)
+                    .setSocketTimeout(5000)
+                    .build();
+            httpClient = HttpClientBuilder
+                    .create()
+                    .setDefaultRequestConfig(config)
+                    .build();
+            URI uri = new URIBuilder()
+                    .setScheme(pushUrlSchema)
+                    .setHost(pushUrlHost)
+                    .setPort(Integer.parseInt(pushUrlPort))
+                    .setPath("/entity/learning/interface/stuHomeworkScore_syncFinalExamScore.action")
+                    .setParameter("loginId", "bjsy")
+                    .setParameter("password", "1111")
+                    .setParameter("examRemark",examScorePushInfo.getExamName())
+                    .setParameter("examStuRemark","recruit")
+                    .setParameter("scoreJsonStr", scoreStr)//通过查询参数的方式传递
+                    .build();
+            HttpPost httpPost = new HttpPost(uri);
+            httpResponse = httpClient.execute(httpPost);
+            HttpEntity responseEntity = httpResponse.getEntity();
+            String result = "";
+            int statusCode = httpResponse.getStatusLine().getStatusCode();
+            if(statusCode == 200){
+                BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
+                StringBuffer buffer = new StringBuffer();
+                String str = "";
+                while(StringUtils.isNoneBlank((str = reader.readLine()))) {
+                    buffer.append(str);
+                }
+                result = buffer.toString();
+            }
+            log.info("推送石油大学成绩:"+examScorePushQueue.getId()+"结果:"+result);
+        	JSONObject jsonObject = new JSONObject(result);
+        	//推送成功
+        	if(jsonObject.has("success")&&"true".equals(jsonObject.getString("success"))){
+        		examScoreQueueService.saveExamScorePushQueue(result, examScorePushQueue, ExamScoreQueueStatus.PROCESS_SUCCESS);
+        	}else{
+        		examScoreQueueService.saveExamScorePushQueue(result, examScorePushQueue, ExamScoreQueueStatus.PROCESS_FAILED);
+        	}
+        } catch (Exception e) {
+        	log.error(examScorePushQueue.getId() + "成绩推送失败", e);
+            examScoreQueueService.saveExamScorePushQueue(e.getMessage(), examScorePushQueue, ExamScoreQueueStatus.PROCESS_FAILED);
+        } finally {
+            if (httpResponse != null) {
+                try {
+                    httpResponse.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                } finally {
+                    httpResponse = null;
+                }
+            }
+            if (httpClient != null) {
+                try {
+                    httpClient.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                } finally {
+                    httpClient = null;
+                }
+            }
+	    }
+	}
+	
+	private ExamScorePushInfo buildexamScorePushInfo(ExamScoreEntity examScore,ExamRecordDataEntity examRecordDataEntity,ExamStudentEntity examStudentEntity){
+		ExamScorePushInfo examScorePushInfo = new ExamScorePushInfo();
+		examScorePushInfo.setExamId(examStudentEntity.getExamId());
+		ExamBean examBean = ExamCacheTransferHelper.getCachedExam(examStudentEntity.getExamId(),
+				examStudentEntity.getOrgId());
+
+		examScorePushInfo.setExamName(examBean.getName());
+		examScorePushInfo.setStudentName(examStudentEntity.getStudentName());
+		examScorePushInfo.setStudentCode(examStudentEntity.getStudentCode());
+		examScorePushInfo.setIdentityNumber(examStudentEntity.getIdentityNumber());
+
+		CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examStudentEntity.getCourseId());
+
+		examScorePushInfo.setCourseName(courseBean.getName());
+		examScorePushInfo.setCourseCode(examStudentEntity.getCourseCode());
+		examScorePushInfo.setStartTime(examRecordDataEntity.getStartTime());
+		examScorePushInfo.setEndTime(examRecordDataEntity.getEndTime());
+		examScorePushInfo.setSuccPercent(examRecordDataEntity.getFaceSuccessPercent());
+		examScorePushInfo.setIsIllegality(examRecordDataEntity.getIsIllegality());
+		examScorePushInfo.setScoreId(examScore.getId());
+		examScorePushInfo.setTotalScore(examScore.getTotalScore());
+		examScorePushInfo.setObjectiveScore(examScore.getObjectiveScore());
+		examScorePushInfo.setSubjectiveScore(examScore.getSubjectiveScore());
+		return examScorePushInfo;
+	}
+
+}

+ 202 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/pushscore/swufe/SwufeExamScorePushServiceImpl.java

@@ -0,0 +1,202 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.impl.pushscore.swufe;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamCacheTransferHelper;
+import cn.com.qmth.examcloud.core.oe.common.entity.*;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.CourseCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamOrgSettingsCacheBean;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
+import cn.com.qmth.examcloud.core.oe.common.base.utils.CommonUtil;
+import cn.com.qmth.examcloud.core.oe.common.enums.ExamScoreQueueStatus;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScorePushQueueRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamScoreRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamStudentRepo;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScoreQueueService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScorePushService;
+import cn.com.qmth.examcloud.core.oe.admin.service.impl.pushscore.bean.ExamScorePushInfo;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年10月18日 下午4:09:47
+ * @company 	QMTH
+ * @description 西南财经大学 orgCode:swufe.ecs.qmth.com.cn
+ */
+@Service("swufeExamScorePushServiceImpl")
+public class SwufeExamScorePushServiceImpl implements ExamScorePushService{
+
+	private static final Logger log = LoggerFactory.getLogger(SwufeExamScorePushServiceImpl.class);
+	
+	@Autowired
+	private ExamScoreRepo examScoreRepo;
+	
+	@Autowired
+	private ExamRecordDataRepo examRecordDataRepo;
+	
+	@Autowired
+	private ExamRecordRepo examRecordRepo;
+	
+	@Autowired
+	private ExamStudentRepo examStudentRepo;
+	
+	@Autowired
+	private ExamScorePushQueueRepo examScorePushQueueRepo;
+	
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+    
+	@Autowired
+	private ExamScoreQueueService examScoreQueueService;
+	
+    private static final String pushScoreUrl = "http://www.swufe-online.com/netedu/qmth.do";
+    
+	@Override
+	public void scorePush(ExamScorePushQueue examScorePushQueue) {
+		log.info("开始推送西南财大成绩:"+examScorePushQueue.getId());
+		Long scoreId = examScorePushQueue.getExamScoreId();
+		ExamScoreEntity examScore = GlobalHelper.getEntity(examScoreRepo,scoreId,ExamScoreEntity.class);
+		ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo,examScore.getExamRecordDataId(),ExamRecordDataEntity.class);
+
+		ExamRecordEntity examRecord =GlobalHelper.getEntity(
+				examRecordRepo,examRecordData.getExamRecordId(),ExamRecordEntity.class);
+		ExamStudentEntity examStudent = examStudentRepo.findByExamStudentId(examRecord.getExamStudentId());
+		if(examScore == null || examRecordData == null || examStudent == null){
+			return;
+		}
+		ExamScorePushInfo examScorePushInfo = buildexamScorePushInfo(examScore,examRecordData,examStudent);
+		
+		if (examScorePushInfo != null) {
+			 CloseableHttpResponse httpResponse = null;
+			 CloseableHttpClient httpClient = null;
+			 try {
+	            httpClient = HttpClients.createDefault();
+	            HttpPost httpPost = new HttpPost(pushScoreUrl);
+	            List<NameValuePair> params = buildParamsData(examScorePushInfo);
+	            httpPost.setEntity(new UrlEncodedFormEntity(params,"UTF-8"));
+	            httpResponse = httpClient.execute(httpPost);
+	            String result = "";
+	            HttpEntity responseEntity = httpResponse.getEntity();
+	            int statusCode= httpResponse.getStatusLine().getStatusCode();
+	            if(statusCode == 200){
+	                BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
+	                StringBuffer buffer = new StringBuffer();
+	                String str = "";
+	                while(StringUtils.isNoneBlank((str = reader.readLine()))) {
+	                    buffer.append(str);
+	                }
+	                result = buffer.toString();
+	            }
+	            log.info("推送西南财大成绩:"+examScorePushQueue.getId()+"结果:"+result);
+	            JSONObject jsonObject = new JSONObject(result);
+	            if(jsonObject.getBoolean("success")){
+	            	examScoreQueueService.saveExamScorePushQueue(result, examScorePushQueue, ExamScoreQueueStatus.PROCESS_SUCCESS);
+	            }else{
+	            	examScoreQueueService.saveExamScorePushQueue(result, examScorePushQueue, ExamScoreQueueStatus.PROCESS_FAILED);
+	            }
+			 }catch (Exception e) {
+				log.error(examScorePushQueue.getId() + "成绩推送失败", e);
+				examScoreQueueService.saveExamScorePushQueue(e.getMessage(), examScorePushQueue, ExamScoreQueueStatus.PROCESS_FAILED);
+	        } finally {
+	            if (httpResponse != null) {
+	                try {
+	                    httpResponse.close();
+	                } catch (IOException e) {
+	                    e.printStackTrace();
+	                } finally {
+	                    httpResponse = null;
+	                }
+	            }
+	            if (httpClient != null) {
+	                try {
+	                    httpClient.close();
+	                } catch (IOException e) {
+	                    e.printStackTrace();
+	                } finally {
+	                    httpClient = null;
+	                }
+	            }
+		    }
+        }
+	}
+
+	private List<NameValuePair> buildParamsData(ExamScorePushInfo examScorePushInfo){
+		Map<String,Object> map = CommonUtil.getKeyAndValue(examScorePushInfo);
+        map.put("op","pushScore");
+        map.put("x","xczx_white_list");
+        map.put("LoginName","qmth");
+        map.put("PassWord","qmth123!");
+        
+        //是否违纪
+        boolean isIllegality = (boolean) map.get("isIllegality");
+        map.put("isBreachThePrinciple", isIllegality?1:0);
+        map.put("successRate", map.get("succPercent"));
+        //以下属性西南财大接口不需要
+        map.remove("succPercent");
+        map.remove("isIllegality");
+        map.remove("examRemark");
+        map.remove("studentRemark");
+        map.remove("isMissExam");
+        map.remove("serialVersionUID");
+        
+        List<NameValuePair> params = new ArrayList<NameValuePair>();
+        for(String key:map.keySet()){
+        	params.add(new BasicNameValuePair(key,map.get(key)+""));
+        }
+        
+        return params;
+	}
+
+	private ExamScorePushInfo buildexamScorePushInfo(ExamScoreEntity examScore,ExamRecordDataEntity examRecordDataEntity, ExamStudentEntity examStudentEntity) {
+		ExamScorePushInfo examScorePushInfo = new ExamScorePushInfo();
+		examScorePushInfo.setExamId(examStudentEntity.getExamId());
+
+		ExamBean examBean = ExamCacheTransferHelper.getCachedExam(examStudentEntity.getExamId(),
+				examStudentEntity.getOrgId());
+		examScorePushInfo.setExamName(examBean.getName());
+		
+		examScorePushInfo.setStudentName(examStudentEntity.getStudentName());
+		examScorePushInfo.setStudentCode(examStudentEntity.getStudentCode());
+		examScorePushInfo.setIdentityNumber(examStudentEntity.getIdentityNumber());
+
+		CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examStudentEntity.getCourseId());
+		examScorePushInfo.setCourseName(courseBean.getName());
+		examScorePushInfo.setCourseCode(examStudentEntity.getCourseCode());
+		examScorePushInfo.setStartTime(examRecordDataEntity.getStartTime());
+		examScorePushInfo.setEndTime(examRecordDataEntity.getEndTime());
+		examScorePushInfo.setSuccPercent(examRecordDataEntity.getFaceSuccessPercent());
+		examScorePushInfo.setIsIllegality(examRecordDataEntity.getIsIllegality());
+		examScorePushInfo.setScoreId(examScore.getId());
+		examScorePushInfo.setTotalScore(examScore.getTotalScore());
+		examScorePushInfo.setObjectiveScore(examScore.getObjectiveScore());
+		examScorePushInfo.setSubjectiveScore(examScore.getSubjectiveScore());
+		return examScorePushInfo;
+	}
+
+}

+ 41 - 0
examcloud-core-oe-admin-starter/src/main/java/cn/com/qmth/examcloud/core/oe/admin/stater/AdminApplication.java

@@ -0,0 +1,41 @@
+package cn.com.qmth.examcloud.core.oe.admin.stater;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import cn.com.qmth.examcloud.web.bootstrap.AppBootstrap;
+
+@SpringBootApplication
+@Configuration
+@EnableAutoConfiguration
+@EnableJpaAuditing
+@EnableTransactionManagement
+@EnableEurekaClient
+@EnableDiscoveryClient
+@ComponentScan(basePackages = {"cn.com.qmth"})
+@EntityScan(basePackages = {"cn.com.qmth"})
+@EnableJpaRepositories(basePackages = {"cn.com.qmth"})
+@EnableMongoRepositories(basePackages= { "cn.com.qmth" })
+public class AdminApplication {
+	static {
+		String runtimeLevel = System.getProperty("log.commonLevel");
+		if (null == runtimeLevel) {
+			System.setProperty("log.commonLevel", "DEBUG");
+		}
+		System.setProperty("hibernate.dialect.storage_engine", "innodb");
+	}
+
+	public static void main(String[] args) {
+		AppBootstrap.run(AdminApplication.class, args);
+	}
+}

+ 130 - 0
examcloud-core-oe-admin-starter/src/main/java/cn/com/qmth/examcloud/core/oe/admin/stater/config/ExamCloudResourceManager.java

@@ -0,0 +1,130 @@
+package cn.com.qmth.examcloud.core.oe.admin.stater.config;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.common.collect.Sets;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.AccessApp;
+import cn.com.qmth.examcloud.api.commons.security.bean.Role;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserType;
+import cn.com.qmth.examcloud.api.commons.security.enums.RoleMeta;
+import cn.com.qmth.examcloud.commons.util.PathUtil;
+import cn.com.qmth.examcloud.commons.util.PropertiesUtil;
+import cn.com.qmth.examcloud.commons.util.RegExpUtil;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.AppCacheBean;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+import cn.com.qmth.examcloud.web.security.ResourceManager;
+import cn.com.qmth.examcloud.web.support.ApiInfo;
+
+/**
+ * Demo资源管理器
+ *
+ * @author WANGWEI
+ * @date 2019年2月18日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+@Component
+public class ExamCloudResourceManager implements ResourceManager {
+
+	@Autowired
+	RedisClient redisClient;
+
+	static {
+		PropertiesUtil.loadFromPath(PathUtil.getResoucePath("security.properties"));
+	}
+
+	@Override
+	public AccessApp getAccessApp(Long appId) {
+		AppCacheBean appCacheBean = CacheHelper.getApp(appId);
+		AccessApp app = new AccessApp();
+		app.setAppId(appCacheBean.getId());
+		app.setAppCode(appCacheBean.getCode());
+		app.setAppName(appCacheBean.getName());
+		app.setSecretKey(appCacheBean.getSecretKey());
+		app.setTimeRange(appCacheBean.getTimeRange());
+		return app;
+	}
+
+	@Override
+	public boolean isNaked(ApiInfo apiInfo, String mapping) {
+		if (null == apiInfo) {
+			return true;
+		}
+
+		if (null != apiInfo) {
+			if (apiInfo.isNaked()) {
+				return true;
+			}
+		}
+		if(mapping.matches(".*swagger.*")) {
+			return true;
+		}
+
+		return false;
+	}
+
+	@Override
+	public boolean hasPermission(User user, ApiInfo apiInfo, String mapping) {
+
+		// 学生鉴权
+		if (user.getUserType().equals(UserType.STUDENT)) {
+			return true;
+		}
+
+		List<Role> roleList = user.getRoleList();
+
+		if (CollectionUtils.isEmpty(roleList)) {
+			return false;
+		}
+
+		for (Role role : roleList) {
+			if (role.getRoleCode().equals(RoleMeta.SUPER_ADMIN.name())) {
+				return true;
+			}
+		}
+
+		// 权限组集合
+		String privilegeGroups = PropertiesUtil.getString(mapping);
+		if (StringUtils.isBlank(privilegeGroups)) {
+			return true;
+		}
+
+		// 用户权限集合
+		Set<String> rolePrivilegeList = Sets.newHashSet();
+		Long rootOrgId = user.getRootOrgId();
+		for (Role role : roleList) {
+			String key = "$_P_" + rootOrgId + "_" + role.getRoleId();
+			String rolePrivileges = redisClient.get(key, String.class);
+
+			List<String> rpList = RegExpUtil.findAll(rolePrivileges, "\\w+");
+			rolePrivilegeList.addAll(rpList);
+		}
+
+		List<String> privilegeGroupList = RegExpUtil.findAll(privilegeGroups, "[^\\;]+");
+
+		for (String pg : privilegeGroupList) {
+			pg = pg.trim();
+			if (StringUtils.isBlank(pg)) {
+				continue;
+			}
+
+			List<String> pList = RegExpUtil.findAll(pg, "[^\\,]+");
+			if (rolePrivilegeList.containsAll(pList)) {
+				return true;
+			} else {
+				continue;
+			}
+		}
+
+		return false;
+	}
+
+}

+ 53 - 0
examcloud-core-oe-admin-starter/src/main/java/cn/com/qmth/examcloud/core/oe/admin/stater/config/ExamCloudWebMvcConfigurer.java

@@ -0,0 +1,53 @@
+package cn.com.qmth.examcloud.core.oe.admin.stater.config;
+
+import cn.com.qmth.examcloud.web.interceptor.ApiFlowLimitedInterceptor;
+import cn.com.qmth.examcloud.web.interceptor.ApiStatisticInterceptor;
+import cn.com.qmth.examcloud.web.interceptor.SeqlockInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import cn.com.qmth.examcloud.web.interceptor.FirstInterceptor;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+import cn.com.qmth.examcloud.web.security.RequestPermissionInterceptor;
+import cn.com.qmth.examcloud.web.security.ResourceManager;
+import cn.com.qmth.examcloud.web.security.RpcInterceptor;
+
+/**
+ * WebMvcConfigurer
+ *
+ * @author WANGWEI
+ * @date 2019年1月30日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+@Configuration
+public class ExamCloudWebMvcConfigurer implements WebMvcConfigurer {
+
+	@Autowired
+	ResourceManager resourceManager;
+
+	@Autowired
+	RedisClient redisClient;
+
+	@Override
+	public void addInterceptors(InterceptorRegistry registry) {
+		registry.addInterceptor(new FirstInterceptor()).addPathPatterns("/**");
+		registry.addInterceptor(new ApiFlowLimitedInterceptor()).addPathPatterns("/**");
+		registry.addInterceptor(new RpcInterceptor(resourceManager)).addPathPatterns("/**");
+
+		RequestPermissionInterceptor permissionInterceptor = new RequestPermissionInterceptor(
+				resourceManager, redisClient);
+		registry.addInterceptor(permissionInterceptor).addPathPatterns("/**");
+		registry.addInterceptor(new SeqlockInterceptor(redisClient)).addPathPatterns("/**");
+		registry.addInterceptor(new ApiStatisticInterceptor()).addPathPatterns("/**");
+	}
+
+	@Override
+	public void addCorsMappings(CorsRegistry registry) {
+		registry.addMapping("/**").allowedOrigins("*").allowCredentials(false).allowedMethods("*")
+				.maxAge(3600);
+	}
+
+}

+ 44 - 0
examcloud-core-oe-admin-starter/src/main/java/cn/com/qmth/examcloud/core/oe/admin/stater/config/SwaggerConfig.java

@@ -0,0 +1,44 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-15 16:17:41.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.admin.stater.config;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+    @Bean
+    public Docket buildDocket() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .groupName("Version 3.0")
+                .apiInfo(buildApiInfo())
+                .useDefaultResponseMessages(false)
+                .select()
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+    public ApiInfo buildApiInfo() {
+        return new ApiInfoBuilder()
+                .title("网考管理端接口文档")
+                .version("3.0")
+                .build();
+    }
+
+}

+ 6 - 0
examcloud-core-oe-admin-starter/src/main/resources/application.properties

@@ -0,0 +1,6 @@
+spring.profiles.active=dev
+examcloud.startup.startupCode=2000
+#examcloud.startup.configCenterHost=192.168.10.125
+examcloud.startup.configCenterHost=localhost
+examcloud.startup.configCenterPort=9999
+examcloud.startup.appSimpleName=oe-admin

+ 0 - 0
examcloud-core-oe-admin-starter/src/main/resources/limited.properties


+ 71 - 0
examcloud-core-oe-admin-starter/src/main/resources/log4j2.xml

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN" monitorInterval="30">
+
+	<Properties>
+		<Property name="commonLevel" value="${sys:log.commonLevel}" />
+	</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="100MB" />
+			</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="100MB" />
+			</Policies>
+			<DefaultRolloverStrategy max="10000">
+				<Delete basePath="./logs/interface" maxDepth="1">
+					<IfFileName glob="interface-*.log">
+						<IfAccumulatedFileSize exceeds="10 GB" />
+					</IfFileName>
+				</Delete>
+			</DefaultRolloverStrategy>
+		</RollingFile>
+	</Appenders>
+
+	<Loggers>
+		<Logger name="cn.com.qmth" level="${commonLevel}" additivity="false">
+			<AppenderRef ref="DEBUG_APPENDER" />
+			<AppenderRef ref="Console" />
+		</Logger>
+
+		<Logger name="INTERFACE_LOGGER" level="INFO" additivity="false">
+			<AppenderRef ref="INTERFACE_APPENDER" />
+			<AppenderRef ref="Console" />
+		</Logger>
+
+		<Logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="${commonLevel}" />
+		<Logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="${commonLevel}" />
+		<Logger name="org.hibernate.SQL" level="${commonLevel}" />
+		<Logger name="org.hibernate.type" level="${commonLevel}" />
+		<Logger name="org.hibernate.engine.QueryParameters" level="${commonLevel}" />
+		<Logger name="org.hibernate.engine.query.HQLQueryPlan" level="${commonLevel}" />
+
+		<Root level="INFO">
+			<AppenderRef ref="Console" />
+			<AppenderRef ref="DEBUG_APPENDER" />
+		</Root>
+	</Loggers>
+
+</Configuration>

+ 14 - 0
examcloud-core-oe-admin-starter/src/main/resources/security-exclusions.conf

@@ -0,0 +1,14 @@
+[][/][GET]
+[][/doc][GET]
+[][/swagger/ui/index][GET]
+[/swagger-resources][][GET]
+[/swagger-resources][/configuration/ui][GET]
+[/swagger-resources][/configuration/security][GET]
+[][${springfox.documentation.swagger.v2.path:/v2/api-docs}][GET]
+[][/doc.html][GET]
+[][/docs.html][GET]
+[${$rmp.ctr.oe}/exam/student][/all/list/export][GET]
+[${$rmp.ctr.oe}/exam/student][/unfinished/list/export][GET]
+[${$rmp.ctr.oe}/exam/audit][/discipline/list/export][GET]
+[${$rmp.ctr.oe}/exam/record][/detail/list/export][GET]
+[${$rmp.ctr.oe}/exam/score][/statistic/list/export][GET]

+ 0 - 0
examcloud-core-oe-admin-starter/src/main/resources/security.properties