xiatian 5 rokov pred
rodič
commit
968f12d83d

+ 3 - 1
examcloud-core-oe-student-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/student/api/controller/ExamQuestionController.java

@@ -42,6 +42,8 @@ public class ExamQuestionController extends ControllerSupport {
 	@Autowired
     private ExamingSessionService examingSessionService;
 	
+	
+	
 	/**
 	 * 将mongodb中的答过的题和redis中的题目列表合并返回给前端
 	 * 返回给前端时注意将正确答案和得分置成null
@@ -56,7 +58,7 @@ public class ExamQuestionController extends ControllerSupport {
                 || examSessionInfo.getCost() >= examSessionInfo.getExamDuration()) {
 			throw new StatusException("1001","考试会话已过期,请重新开考");
 		}
-		ExamRecordQuestions qers = examRecordQuestionsService.getExamRecordQuestions(examSessionInfo.getExamRecordDataId(), examSessionInfo.getQuestionCount());
+		ExamRecordQuestions qers = examRecordQuestionsService.getExamRecordQuestions(examSessionInfo.getExamRecordDataId());
 		List<ExamQuestion> examQuestionList=qers.getExamQuestions();
 		for(ExamQuestion examQuestion:examQuestionList){
 			examQuestion.setCorrectAnswer(null);

+ 1 - 1
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/ExamRecordQuestionsService.java

@@ -34,7 +34,7 @@ public interface ExamRecordQuestionsService {
 
     public ExamRecordQuestions createExamRecordQuestions(Long examRecordDataId, DefaultPaper defaultPaper);
     
-    public ExamRecordQuestions getExamRecordQuestions(Long examRecordDataId,Integer quesCount);
+    public ExamRecordQuestions getExamRecordQuestions(Long examRecordDataId);
 
     public String getQuestionContent(Long studentId, String questionId);
 

+ 273 - 222
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamControlServiceImpl.java

@@ -1,19 +1,68 @@
 package cn.com.qmth.examcloud.core.oe.student.service.impl;
 
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.math.RandomUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DateUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.common.base.Splitter;
+
 import cn.com.qmth.examcloud.api.commons.enums.ExamSpecialSettingsType;
 import cn.com.qmth.examcloud.api.commons.enums.ExamType;
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
 import cn.com.qmth.examcloud.api.commons.security.bean.UserType;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.util.ByteUtil;
+import cn.com.qmth.examcloud.commons.util.JsonUtil;
+import cn.com.qmth.examcloud.commons.util.SHA256;
+import cn.com.qmth.examcloud.commons.util.StringUtil;
 import cn.com.qmth.examcloud.commons.util.UUID;
-import cn.com.qmth.examcloud.commons.util.*;
+import cn.com.qmth.examcloud.commons.util.UrlUtil;
+import cn.com.qmth.examcloud.commons.util.Util;
 import cn.com.qmth.examcloud.core.oe.student.base.bean.ExamQuestion;
 import cn.com.qmth.examcloud.core.oe.student.base.bean.ExamRecordQuestions;
 import cn.com.qmth.examcloud.core.oe.student.base.utils.CommonUtil;
 import cn.com.qmth.examcloud.core.oe.student.base.utils.QuestionTypeUtil;
-import cn.com.qmth.examcloud.core.oe.student.bean.*;
-import cn.com.qmth.examcloud.core.oe.student.dao.entity.ExamRecordDataEntity;
-import cn.com.qmth.examcloud.core.oe.student.service.*;
+import cn.com.qmth.examcloud.core.oe.student.bean.CheckExamInProgressInfo;
+import cn.com.qmth.examcloud.core.oe.student.bean.CheckQrCodeInfo;
+import cn.com.qmth.examcloud.core.oe.student.bean.EndExamInfo;
+import cn.com.qmth.examcloud.core.oe.student.bean.ExamRecordPaperStruct;
+import cn.com.qmth.examcloud.core.oe.student.bean.GetQrCodeReq;
+import cn.com.qmth.examcloud.core.oe.student.bean.GetUpyunSignatureReq;
+import cn.com.qmth.examcloud.core.oe.student.bean.StartExamInfo;
+import cn.com.qmth.examcloud.core.oe.student.bean.UploadedFileAnswerInfo;
+import cn.com.qmth.examcloud.core.oe.student.bean.UpyunSignatureInfo;
+import cn.com.qmth.examcloud.core.oe.student.service.ExamBossService;
+import cn.com.qmth.examcloud.core.oe.student.service.ExamControlService;
+import cn.com.qmth.examcloud.core.oe.student.service.ExamFaceLivenessVerifyService;
+import cn.com.qmth.examcloud.core.oe.student.service.ExamRecordDataService;
+import cn.com.qmth.examcloud.core.oe.student.service.ExamRecordPaperStructService;
+import cn.com.qmth.examcloud.core.oe.student.service.ExamRecordQuestionsService;
+import cn.com.qmth.examcloud.core.oe.student.service.ExamingSessionService;
+import cn.com.qmth.examcloud.core.oe.student.service.FaceBiopsyService;
 import cn.com.qmth.examcloud.core.oe.task.api.ExamCaptureCloudService;
 import cn.com.qmth.examcloud.core.oe.task.api.request.SaveExamCaptureSyncCompareResultReq;
 import cn.com.qmth.examcloud.core.oe.task.api.request.UpdateExamCaptureQueuePriorityReq;
@@ -29,7 +78,17 @@ import cn.com.qmth.examcloud.reports.commons.bean.OnlineExamStudentReport;
 import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
 import cn.com.qmth.examcloud.support.Constants;
 import cn.com.qmth.examcloud.support.cache.CacheHelper;
-import cn.com.qmth.examcloud.support.cache.bean.*;
+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.ExamPropertyCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamSettingsCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamStudentCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamStudentSettingsCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExtractConfigCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExtractConfigDetailCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExtractConfigPaperCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.StudentCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.SysPropertyCacheBean;
 import cn.com.qmth.examcloud.support.enums.ExamProperties;
 import cn.com.qmth.examcloud.support.enums.ExamRecordStatus;
 import cn.com.qmth.examcloud.support.enums.FaceBiopsyScheme;
@@ -43,7 +102,6 @@ import cn.com.qmth.examcloud.support.helper.FaceBiopsyHelper;
 import cn.com.qmth.examcloud.support.redis.RedisKeyHelper;
 import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
 import cn.com.qmth.examcloud.web.exception.SequenceLockException;
-import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
 import cn.com.qmth.examcloud.web.helpers.SequenceLockHelper;
 import cn.com.qmth.examcloud.web.redis.RedisClient;
 import cn.com.qmth.examcloud.ws.api.WsCloudService;
@@ -52,28 +110,9 @@ import cn.com.qmth.examcloud.ws.api.enums.WebSocketEventType;
 import cn.com.qmth.examcloud.ws.api.request.SendFileAnswerMessageReq;
 import cn.com.qmth.examcloud.ws.api.request.SendScanQrCodeMessageReq;
 import cn.com.qmth.examcloud.ws.api.request.SendTextReq;
-import com.google.common.base.Splitter;
 import main.java.com.upyun.Base64Coder;
 import main.java.com.upyun.UpException;
 import main.java.com.upyun.UpYunUtils;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang.math.RandomUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.time.DateUtils;
-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.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.bind.annotation.RequestParam;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.text.SimpleDateFormat;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
 
 /**
  * @author chenken
@@ -125,16 +164,18 @@ public class ExamControlServiceImpl implements ExamControlService {
 
     // 又拍云音频答案上传目录
     private static final String OE_ANSWER_FILE_PATH = "oe-answer-file";
+
     private static final String SESSION_TIMEOUT = "$core.basic.sessionTimeout";
+
     // 又拍云签名有效时间(秒)
     private static final Integer SIGN_TIMEOUT = 60;
 
     @Transactional
     @Override
     public StartExamInfo startExam(Long examStudentId, User user) {
-    	//开考预处理
-    	prepare4Exam(examStudentId, user);
-    	
+        // 开考预处理
+        prepare4Exam(examStudentId, user);
+
         Long studentId = user.getUserId();
         long st = System.currentTimeMillis();
 
@@ -227,19 +268,20 @@ public class ExamControlServiceImpl implements ExamControlService {
 
         // 创建考试作答记录
         startTime = System.currentTimeMillis();
-        ExamRecordQuestions examRecordQuestions = examRecordQuestionsService.createExamRecordQuestions(examRecordData.getId(),
-                extractConfigPaper.getDefaultPaper());
-        //记录试卷题目数量
-        examingSession.setQuestionCount(examRecordQuestions.getExamQuestions().size());
+        ExamRecordQuestions examRecordQuestions = examRecordQuestionsService
+                .createExamRecordQuestions(examRecordData.getId(), extractConfigPaper.getDefaultPaper());
+        // 记录试卷题目数量
+        examRecordData.setQuestionCount(examRecordQuestions.getExamQuestions().size());
+        examRecordDataService.saveExamRecordDataCache(examRecordData.getId(), examRecordData);
         if (log.isDebugEnabled()) {
             log.debug("8 创建考试作答记录耗时:" + (System.currentTimeMillis() - startTime) + " ms");
         }
 
-        //保存考试次数控制信息
+        // 保存考试次数控制信息
         ExamBoss eb = examBossService.getExamBoss(examingSession.getExamId());
-		if (eb == null) {
-			throw new StatusException("008001", "ExamBoss is not created");
-		}
+        if (eb == null) {
+            throw new StatusException("008001", "ExamBoss is not created");
+        }
         eb.setStartCount(eb.getStartCount() + 1);
         examBossService.saveExamBoss(examId, eb);
 
@@ -260,171 +302,165 @@ public class ExamControlServiceImpl implements ExamControlService {
         return startExamInfo;
 
     }
-    
-	/**
-	 *开考预处理
-	 *
-	 * @param examStudentId
-	 * @param user
-	 */
-	private void prepare4Exam(Long examStudentId,User user ) {
-
-		// 开始考试上锁,分布式锁,系统在请求结束后会,自动释放锁,无需手动解锁
-		String sequenceLockKey = Constants.EXAM_CONTROL_LOCK_PREFIX + user.getUserId();
-		SequenceLockHelper.getLock(sequenceLockKey);
-
-		SysPropertyCacheBean stuClientLoginLimit = CacheHelper
-				.getSysProperty("STU_CLIENT_LOGIN_LIMIT");
-		Boolean stuClientLoginLimitBoolean = false;
-		if (stuClientLoginLimit.getHasValue()) {
-			stuClientLoginLimitBoolean = Boolean.valueOf(stuClientLoginLimit.getValue().toString());
-		}
-		if (stuClientLoginLimitBoolean) {
-			throw new StatusException("008001", "系统维护中... ...");
-		}
-
-		ExamStudentCacheBean examStudent = CacheHelper.getExamStudent(examStudentId);
-
-		if (null == examStudent) {
-			throw new StatusException("008002", "考生不存在");
-		}
-
-		if (!examStudent.getStudentId().equals(user.getUserId().longValue())) {
-			throw new StatusException("008003", "考生与当前用户不吻合");
-		}
-
-		String examingSessionKey = RedisKeyHelper.getBuilder().examingSessionKey(user.getUserId());
-
-		ExamingSession examingSession = redisClient.get(examingSessionKey, ExamingSession.class);
-
-		if (null != examingSession
-				&& examingSession.getExamingStatus().equals(ExamingStatus.FORMAL)) {
-			throw new StatusException("008004", "已经有考试中的科目");
-		}
-
-		ExamSettingsCacheBean examSettingsCacheBean = ExamCacheTransferHelper
-				.getCachedExam(examStudent.getExamId(), examStudent.getStudentId());
-
-		StudentCacheBean studentCacheBean = CacheHelper.getStudent(examStudent.getStudentId());
-
-		CourseCacheBean courseCacheBean = CacheHelper.getCourse(examStudent.getCourseId());
-		if (!courseCacheBean.getEnable()) {
-			throw new StatusException("100020", "课程被禁用");
-		}
-
-		// 如果启用了了特殊设置,并且无特殊设置时结束考试 配置 设置为true..且实际未设置特殊设置则不允许考试
-		ExamPropertyCacheBean limitedIfNoSpecialSettings = ExamCacheTransferHelper
-				.getDefaultCachedExamProperty(examSettingsCacheBean.getId(),
-						ExamProperties.LIMITED_IF_NO_SPECIAL_SETTINGS.toString());
-
-		if (examSettingsCacheBean.getSpecialSettingsEnabled()
-				&& (limitedIfNoSpecialSettings.getHasValue()
-						&& Boolean.valueOf(limitedIfNoSpecialSettings.getValue()))) {
-
-			// 学生特殊设置开启未配置,不允许考试
-			if (examSettingsCacheBean
-					.getSpecialSettingsType() == ExamSpecialSettingsType.STUDENT_BASED) {
-				ExamStudentSettingsCacheBean specialSettings = CacheHelper.getExamStudentSettings(
-						examSettingsCacheBean.getId(), examStudent.getStudentId());
-				if (!specialSettings.getHasValue()) {
-					throw new StatusException("100014", "考试配置未完成,不允许考试");
-				}
-			}
-
-			// 机构特殊设置开启未配置,不允许考试
-			if (examSettingsCacheBean
-					.getSpecialSettingsType() == ExamSpecialSettingsType.ORG_BASED) {
-				// 需求调整,所有的组织机构取学生表所关联的orgId
-				Long orgId = CacheHelper.getStudent(examStudent.getStudentId()).getOrgId();
-				ExamOrgSettingsCacheBean specialSettings = CacheHelper
-						.getExamOrgSettings(examSettingsCacheBean.getId(), orgId);
-				if (!specialSettings.getHasValue()) {
-					throw new StatusException("100015", "考试配置未完成,不允许考试");
-				}
-			}
-
-		}
-
-		if (!examSettingsCacheBean.getEnable() || (examSettingsCacheBean.getExamLimit() != null
-				&& examSettingsCacheBean.getExamLimit())) {
-			throw new StatusException("100016", "暂无考试资格,请与学校老师联系");
-		}
-		if (new Date().before(examSettingsCacheBean.getBeginTime())) {
-			throw new StatusException("100017", "考试未开始");
-		}
-		if (examSettingsCacheBean.getEndTime().before(new Date())) {
-			throw new StatusException("100018", "本次考试已结束");
-		}
-
-		if ((!ExamType.ONLINE.name().equals(examSettingsCacheBean.getExamType()))
-				&& (!ExamType.PRACTICE.name().equals(examSettingsCacheBean.getExamType()))) {
-			throw new StatusException("100019", "考试类型错误");
-		}
-
-		int examTimes = examSettingsCacheBean.getExamTimes().intValue();
-		int usedNum = examStudent.getUsedNum();
-		int extraNum = examStudent.getExtraNum();
-
-		String examBossKey = RedisKeyHelper.getBuilder().examBossKey(examSettingsCacheBean.getId());
-		ExamBoss examBoss = redisClient.get(examBossKey, ExamBoss.class);
-
-		int asynchronousTimes = 0;
-		if (null != examBoss) {
-			asynchronousTimes = examBoss.getStartCount() - examBoss.getEndCount();
-		} else {
-			examBoss = new ExamBoss();
-			examBoss.setStartCount(0);
-			examBoss.setEndCount(0);
-		}
-
-		if (examTimes + extraNum <= (usedNum + asynchronousTimes)) {
-			throw new StatusException("100021", "无剩余考试次数");
-		}
-
-		examingSession = new ExamingSession();
-		examingSession.setActiveTime(0L);
-		examingSession.setCost(0L);
-		examingSession.setCourseCode(courseCacheBean.getCode());
-		examingSession.setCourseId(courseCacheBean.getId());
-		examingSession.setCreationTime(new Date());
-		examingSession.setExamDuration((long) examSettingsCacheBean.getDuration());
-		examingSession.setExamId(examSettingsCacheBean.getId());
-		examingSession.setExamingStatus(ExamingStatus.INFORMAL);
-
-		ExamPropertyCacheBean examReconnectTime = ExamCacheTransferHelper
-				.getDefaultCachedExamProperty(examSettingsCacheBean.getId(),
-						ExamProperties.EXAM_RECONNECT_TIME.toString());
-		if (null == examReconnectTime || !examReconnectTime.getHasValue()) {
-			throw new StatusException("100022", "断点续考时间未配置");
-		}
-		examingSession.setExamReconnectTime(StringUtil.toInteger(examReconnectTime.getValue()));
-
-		examingSession.setExamRecordDataId(null);
-		examingSession.setExamStudentId(examStudent.getExamStudentId());
-		examingSession.setExamType(examSettingsCacheBean.getExamType());
-
-		ExamPropertyCacheBean freezeTime = ExamCacheTransferHelper.getDefaultCachedExamProperty(
-				examSettingsCacheBean.getId(), ExamProperties.FREEZE_TIME.toString());
-		if (null == freezeTime || !freezeTime.getHasValue()) {
-			throw new StatusException("100023", "交卷冻结时间未配置");
-		}
-		examingSession.setFreezeTime(StringUtil.toInteger(freezeTime.getValue()));
-		examingSession.setOrgId(studentCacheBean.getOrgId());
-		examingSession.setPaperType(examStudent.getPaperType());
-		examingSession.setRootOrgId(studentCacheBean.getRootOrgId());
-		examingSession.setStudentId(studentCacheBean.getId());
-
-		redisClient.set(examingSessionKey, examingSession, 10);
-		redisClient.set(examBossKey, examBoss, 60);
-
-	}
 
+    /**
+     * 开考预处理
+     *
+     * @param examStudentId
+     * @param user
+     */
+    private void prepare4Exam(Long examStudentId, User user) {
+
+        // 开始考试上锁,分布式锁,系统在请求结束后会,自动释放锁,无需手动解锁
+        String sequenceLockKey = Constants.EXAM_CONTROL_LOCK_PREFIX + user.getUserId();
+        SequenceLockHelper.getLock(sequenceLockKey);
+
+        SysPropertyCacheBean stuClientLoginLimit = CacheHelper.getSysProperty("STU_CLIENT_LOGIN_LIMIT");
+        Boolean stuClientLoginLimitBoolean = false;
+        if (stuClientLoginLimit.getHasValue()) {
+            stuClientLoginLimitBoolean = Boolean.valueOf(stuClientLoginLimit.getValue().toString());
+        }
+        if (stuClientLoginLimitBoolean) {
+            throw new StatusException("008001", "系统维护中... ...");
+        }
+
+        ExamStudentCacheBean examStudent = CacheHelper.getExamStudent(examStudentId);
+
+        if (null == examStudent) {
+            throw new StatusException("008002", "考生不存在");
+        }
+
+        if (!examStudent.getStudentId().equals(user.getUserId().longValue())) {
+            throw new StatusException("008003", "考生与当前用户不吻合");
+        }
+
+        String examingSessionKey = RedisKeyHelper.getBuilder().examingSessionKey(user.getUserId());
+
+        ExamingSession examingSession = redisClient.get(examingSessionKey, ExamingSession.class);
+
+        if (null != examingSession && examingSession.getExamingStatus().equals(ExamingStatus.FORMAL)) {
+            throw new StatusException("008004", "已经有考试中的科目");
+        }
+
+        ExamSettingsCacheBean examSettingsCacheBean = ExamCacheTransferHelper.getCachedExam(examStudent.getExamId(),
+                examStudent.getStudentId());
+
+        StudentCacheBean studentCacheBean = CacheHelper.getStudent(examStudent.getStudentId());
+
+        CourseCacheBean courseCacheBean = CacheHelper.getCourse(examStudent.getCourseId());
+        if (!courseCacheBean.getEnable()) {
+            throw new StatusException("100020", "课程被禁用");
+        }
+
+        // 如果启用了了特殊设置,并且无特殊设置时结束考试 配置 设置为true..且实际未设置特殊设置则不允许考试
+        ExamPropertyCacheBean limitedIfNoSpecialSettings = ExamCacheTransferHelper.getDefaultCachedExamProperty(
+                examSettingsCacheBean.getId(), ExamProperties.LIMITED_IF_NO_SPECIAL_SETTINGS.toString());
+
+        if (examSettingsCacheBean.getSpecialSettingsEnabled() && (limitedIfNoSpecialSettings.getHasValue()
+                && Boolean.valueOf(limitedIfNoSpecialSettings.getValue()))) {
+
+            // 学生特殊设置开启未配置,不允许考试
+            if (examSettingsCacheBean.getSpecialSettingsType() == ExamSpecialSettingsType.STUDENT_BASED) {
+                ExamStudentSettingsCacheBean specialSettings = CacheHelper
+                        .getExamStudentSettings(examSettingsCacheBean.getId(), examStudent.getStudentId());
+                if (!specialSettings.getHasValue()) {
+                    throw new StatusException("100014", "考试配置未完成,不允许考试");
+                }
+            }
+
+            // 机构特殊设置开启未配置,不允许考试
+            if (examSettingsCacheBean.getSpecialSettingsType() == ExamSpecialSettingsType.ORG_BASED) {
+                // 需求调整,所有的组织机构取学生表所关联的orgId
+                Long orgId = CacheHelper.getStudent(examStudent.getStudentId()).getOrgId();
+                ExamOrgSettingsCacheBean specialSettings = CacheHelper.getExamOrgSettings(examSettingsCacheBean.getId(),
+                        orgId);
+                if (!specialSettings.getHasValue()) {
+                    throw new StatusException("100015", "考试配置未完成,不允许考试");
+                }
+            }
+
+        }
+
+        if (!examSettingsCacheBean.getEnable()
+                || (examSettingsCacheBean.getExamLimit() != null && examSettingsCacheBean.getExamLimit())) {
+            throw new StatusException("100016", "暂无考试资格,请与学校老师联系");
+        }
+        if (new Date().before(examSettingsCacheBean.getBeginTime())) {
+            throw new StatusException("100017", "考试未开始");
+        }
+        if (examSettingsCacheBean.getEndTime().before(new Date())) {
+            throw new StatusException("100018", "本次考试已结束");
+        }
+
+        if ((!ExamType.ONLINE.name().equals(examSettingsCacheBean.getExamType()))
+                && (!ExamType.PRACTICE.name().equals(examSettingsCacheBean.getExamType()))) {
+            throw new StatusException("100019", "考试类型错误");
+        }
+
+        int examTimes = examSettingsCacheBean.getExamTimes().intValue();
+        int usedNum = examStudent.getUsedNum();
+        int extraNum = examStudent.getExtraNum();
+
+        String examBossKey = RedisKeyHelper.getBuilder().examBossKey(examSettingsCacheBean.getId());
+        ExamBoss examBoss = redisClient.get(examBossKey, ExamBoss.class);
+
+        int asynchronousTimes = 0;
+        if (null != examBoss) {
+            asynchronousTimes = examBoss.getStartCount() - examBoss.getEndCount();
+        } else {
+            examBoss = new ExamBoss();
+            examBoss.setStartCount(0);
+            examBoss.setEndCount(0);
+        }
+
+        if (examTimes + extraNum <= (usedNum + asynchronousTimes)) {
+            throw new StatusException("100021", "无剩余考试次数");
+        }
+
+        examingSession = new ExamingSession();
+        examingSession.setActiveTime(0L);
+        examingSession.setCost(0L);
+        examingSession.setCourseCode(courseCacheBean.getCode());
+        examingSession.setCourseId(courseCacheBean.getId());
+        examingSession.setCreationTime(new Date());
+        examingSession.setExamDuration((long) examSettingsCacheBean.getDuration());
+        examingSession.setExamId(examSettingsCacheBean.getId());
+        examingSession.setExamingStatus(ExamingStatus.INFORMAL);
+
+        ExamPropertyCacheBean examReconnectTime = ExamCacheTransferHelper.getDefaultCachedExamProperty(
+                examSettingsCacheBean.getId(), ExamProperties.EXAM_RECONNECT_TIME.toString());
+        if (null == examReconnectTime || !examReconnectTime.getHasValue()) {
+            throw new StatusException("100022", "断点续考时间未配置");
+        }
+        examingSession.setExamReconnectTime(StringUtil.toInteger(examReconnectTime.getValue()));
+
+        examingSession.setExamRecordDataId(null);
+        examingSession.setExamStudentId(examStudent.getExamStudentId());
+        examingSession.setExamType(examSettingsCacheBean.getExamType());
+
+        ExamPropertyCacheBean freezeTime = ExamCacheTransferHelper
+                .getDefaultCachedExamProperty(examSettingsCacheBean.getId(), ExamProperties.FREEZE_TIME.toString());
+        if (null == freezeTime || !freezeTime.getHasValue()) {
+            throw new StatusException("100023", "交卷冻结时间未配置");
+        }
+        examingSession.setFreezeTime(StringUtil.toInteger(freezeTime.getValue()));
+        examingSession.setOrgId(studentCacheBean.getOrgId());
+        examingSession.setPaperType(examStudent.getPaperType());
+        examingSession.setRootOrgId(studentCacheBean.getRootOrgId());
+        examingSession.setStudentId(studentCacheBean.getId());
+
+        redisClient.set(examingSessionKey, examingSession, 10);
+        redisClient.set(examBossKey, examBoss, 60);
+
+    }
 
     /**
      * 交卷
      *
-     * @param examRecordDataId 考试记录id
-     * @param handInExamType   交卷类型
+     * @param examRecordDataId
+     *            考试记录id
+     * @param handInExamType
+     *            交卷类型
      */
     @Transactional
     @Override
@@ -549,8 +585,8 @@ public class ExamControlServiceImpl implements ExamControlService {
         res.setCourseId(courseBean.getId());
         res.setCourseName(courseBean.getName());
 
-        ExamRecordQuestions examRecordQuestions = examRecordQuestionsService.getExamRecordQuestions(
-                Long.valueOf(examRecordDataId), examSessionInfo.getQuestionCount());
+        ExamRecordQuestions examRecordQuestions = examRecordQuestionsService
+                .getExamRecordQuestions(Long.valueOf(examRecordDataId));
 
         List<ExamQuestion> examQuestionList = examRecordQuestions.getExamQuestions();
 
@@ -568,7 +604,8 @@ public class ExamControlServiceImpl implements ExamControlService {
         res.setQuestionMainNumber(eqe.getMainNumber());
         res.setSubNumber(getSubNumber(examRecordQuestions, Integer.valueOf(order)));
         try {
-            ExamRecordData examRecordData = examRecordDataService.getExamRecordDataCache(Long.valueOf(examRecordDataId));
+            ExamRecordData examRecordData = examRecordDataService
+                    .getExamRecordDataCache(Long.valueOf(examRecordDataId));
             this.sendScanQrCodeToWebSocket(clientId, Long.valueOf(examRecordDataId), Integer.valueOf(order),
                     Long.valueOf(userId), examRecordData.getRootOrgId());
         } catch (Exception e) {
@@ -577,20 +614,23 @@ public class ExamControlServiceImpl implements ExamControlService {
         return res;
     }
 
-
     /**
      * 发送消息到websocket
      *
-     * @param examRecordDataId 考试记录id
-     * @param order            题序号
-     * @param fileUrl          文件路径
-     * @param transferFileType 传输文件类型
+     * @param examRecordDataId
+     *            考试记录id
+     * @param order
+     *            题序号
+     * @param fileUrl
+     *            文件路径
+     * @param transferFileType
+     *            传输文件类型
      * @param userId
      * @throws Exception
      */
     @Override
     public void sendFileAnswerToWebSocket(Long examRecordDataId, Integer order, String fileUrl, String transferFileType,
-                                          Long userId, Long rootOrgId) throws Exception {
+            Long userId, Long rootOrgId) throws Exception {
         Map<String, Object> data = new HashMap<String, Object>();
         data.put("examRecordDataId", examRecordDataId);
         data.put("order", order);
@@ -630,8 +670,8 @@ public class ExamControlServiceImpl implements ExamControlService {
      * @throws Exception
      */
     @Override
-    public void sendScanQrCodeToWebSocket(String clientId, Long examRecordDataId, Integer order,
-                                          Long userId, Long rootOrgId) throws Exception {
+    public void sendScanQrCodeToWebSocket(String clientId, Long examRecordDataId, Integer order, Long userId,
+            Long rootOrgId) throws Exception {
         Map<String, Object> data = new HashMap<String, Object>();
         data.put("examRecordDataId", examRecordDataId);
         data.put("order", order);
@@ -759,11 +799,16 @@ public class ExamControlServiceImpl implements ExamControlService {
     }
 
     /**
-     * @param bucketName //不能为空
-     * @param expiration //不能为空
-     * @param filePath   //不能为空
-     * @param date       为空时,以又怕云时间和expiration比较,不为空时以此date和expiration比较
-     * @param md5        //可以为空
+     * @param bucketName
+     *            //不能为空
+     * @param expiration
+     *            //不能为空
+     * @param filePath
+     *            //不能为空
+     * @param date
+     *            为空时,以又怕云时间和expiration比较,不为空时以此date和expiration比较
+     * @param md5
+     *            //可以为空
      * @return
      */
     private String policy(String bucketName, Long expiration, String filePath, Date date, String md5) {
@@ -805,7 +850,7 @@ public class ExamControlServiceImpl implements ExamControlService {
      * @throws UpException
      */
     private String sign(String method, String date, String bucketName, String policy, String userName, String password,
-                        String md5) throws UpException {
+            String md5) throws UpException {
 
         StringBuilder sb = new StringBuilder();
         String sp = "&";
@@ -953,8 +998,10 @@ public class ExamControlServiceImpl implements ExamControlService {
     /**
      * 获取试卷结构 小题乱序、选项乱序
      *
-     * @param extractConfig 调卷规则对象
-     * @param paperStruct   试卷结构对象
+     * @param extractConfig
+     *            调卷规则对象
+     * @param paperStruct
+     *            试卷结构对象
      */
     private void reorderPaperStruct(ExtractConfigCacheBean extractConfig, ExtractConfigPaperCacheBean paperStruct) {
         // 小题乱序
@@ -1038,8 +1085,8 @@ public class ExamControlServiceImpl implements ExamControlService {
         return false;
     }
 
-    private StartExamInfo buildStartExamInfo(Long examRecordDataId, ExamingSession examingSession, ExamSettingsCacheBean examBean,
-                                             CourseCacheBean courseBean) {
+    private StartExamInfo buildStartExamInfo(Long examRecordDataId, ExamingSession examingSession,
+            ExamSettingsCacheBean examBean, CourseCacheBean courseBean) {
         StartExamInfo startExamInfo = new StartExamInfo();
         startExamInfo.setExamRecordDataId(examRecordDataId);
         startExamInfo.setCourseName(courseBean.getName());
@@ -1086,7 +1133,7 @@ public class ExamControlServiceImpl implements ExamControlService {
      * @param examBean
      */
     public void initializeExamRecordSession(ExamingSession examSessionInfo, ExamRecordData examRecordData,
-                                            final ExamSettingsCacheBean examBean) {
+            final ExamSettingsCacheBean examBean) {
         examSessionInfo.setExamRecordDataId(examRecordData.getId());
         examSessionInfo.setStartTime(examRecordData.getStartTime().getTime());
         examSessionInfo.setExamType(examBean.getExamType());
@@ -1193,7 +1240,8 @@ public class ExamControlServiceImpl implements ExamControlService {
     /**
      * 如果有序列化锁,则延迟交卷
      *
-     * @param examRecordDataId 考试记录id
+     * @param examRecordDataId
+     *            考试记录id
      * @return
      */
     private void delayHandInExamIfLocked(Long examRecordDataId) {
@@ -1239,7 +1287,8 @@ public class ExamControlServiceImpl implements ExamControlService {
     /**
      * 考试心跳每分钟调用一次
      *
-     * @param user 学生
+     * @param user
+     *            学生
      */
     @Override
     public long examHeartbeat(User user) {
@@ -1268,7 +1317,8 @@ public class ExamControlServiceImpl implements ExamControlService {
     /**
      * 计算考试时长 校验是否达到冻结时间
      *
-     * @param studentId 学生id
+     * @param studentId
+     *            学生id
      * @return
      */
     private Long checkAndComputeExamDuration(Long studentId) {
@@ -1281,7 +1331,8 @@ public class ExamControlServiceImpl implements ExamControlService {
         Long examUsedMilliSeconds = examingSession.getCost() * 1000;
         // 如果没有超过冻结时间,抛出异常
         if (examingSession.getExamType().equals(ExamType.ONLINE.name())) {
-            ExamRecordData examRecordData = examRecordDataService.getExamRecordDataCache(examingSession.getExamRecordDataId());
+            ExamRecordData examRecordData = examRecordDataService
+                    .getExamRecordDataCache(examingSession.getExamRecordDataId());
 
             if (examRecordData != null && examRecordData.getIsExceed() != null && examRecordData.getIsExceed()) {// 超过断点最大次数的不校验冻结时间
                 return examUsedMilliSeconds;

+ 1 - 2
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamRecordDataServiceImpl.java

@@ -265,8 +265,7 @@ public class ExamRecordDataServiceImpl implements ExamRecordDataService {
     public CalcExamScoreResp calcExamScore(CalcExamScoreReq req) {
 
         Long examRecordDataId = req.getExamRecordDataId();
-        //FIXME this is a bug .to be fix
-        ExamRecordQuestions examRecordQuestions = examRecordQuestionsService.getExamRecordQuestions(examRecordDataId,0);
+        ExamRecordQuestions examRecordQuestions = examRecordQuestionsService.getExamRecordQuestions(examRecordDataId);
         // 考生作答记录明细
         List<ExamQuestion> examQuestionList = examRecordQuestions.getExamQuestions();
 

+ 12 - 2
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamRecordQuestionsServiceImpl.java

@@ -21,6 +21,7 @@ import cn.com.qmth.examcloud.core.oe.student.base.bean.ExamRecordQuestions;
 import cn.com.qmth.examcloud.core.oe.student.bean.ExamStudentQuestionInfo;
 import cn.com.qmth.examcloud.core.oe.student.dao.ExamRecordQuestionTempRepo;
 import cn.com.qmth.examcloud.core.oe.student.dao.entity.ExamQuestionTempEntity;
+import cn.com.qmth.examcloud.core.oe.student.service.ExamRecordDataService;
 import cn.com.qmth.examcloud.core.oe.student.service.ExamRecordQuestionsService;
 import cn.com.qmth.examcloud.core.oe.student.service.ExamingSessionService;
 import cn.com.qmth.examcloud.question.commons.core.paper.DefaultPaper;
@@ -31,6 +32,7 @@ import cn.com.qmth.examcloud.question.commons.core.question.DefaultQuestionStruc
 import cn.com.qmth.examcloud.question.commons.core.question.DefaultQuestionUnit;
 import cn.com.qmth.examcloud.support.cache.CacheHelper;
 import cn.com.qmth.examcloud.support.cache.bean.QuestionCacheBean;
+import cn.com.qmth.examcloud.support.examing.ExamRecordData;
 import cn.com.qmth.examcloud.support.examing.ExamingSession;
 import cn.com.qmth.examcloud.support.examing.ExamingStatus;
 import cn.com.qmth.examcloud.support.redis.RedisKeyHelper;
@@ -56,6 +58,9 @@ public class ExamRecordQuestionsServiceImpl implements ExamRecordQuestionsServic
 
     @Autowired
     private ExamingSessionService examingSessionService;
+    
+    @Autowired
+    private ExamRecordDataService examRecordDataService;
 
     @Override
     public void saveExamQuestion(Long examRecordDataId, Integer order, ExamQuestion question) {
@@ -117,7 +122,12 @@ public class ExamRecordQuestionsServiceImpl implements ExamRecordQuestionsServic
     }
 
     @Override
-    public ExamRecordQuestions getExamRecordQuestions(Long examRecordDataId, Integer quesCount) {
+    public ExamRecordQuestions getExamRecordQuestions(Long examRecordDataId) {
+        ExamRecordData ed=examRecordDataService.getExamRecordDataCache(examRecordDataId);
+        if(ed==null) {
+            throw new StatusException("1001", "考试信息未找到");
+        }
+        Integer quesCount=ed.getQuestionCount();
         ExamRecordQuestions erqs = new ExamRecordQuestions();
         List<ExamQuestion> examQuestions = new ArrayList<ExamQuestion>();
         erqs.setExamQuestions(examQuestions);
@@ -127,7 +137,7 @@ public class ExamRecordQuestionsServiceImpl implements ExamRecordQuestionsServic
             if(eq.getIsInMongo()) {
                 Optional<ExamQuestionTempEntity> op=examRecordQuestionTempRepo.findById(eq.getExamQuestionTempId());
                 if(!op.isPresent()) {
-                    throw new StatusException("1001", "试题内容未找到");
+                    throw new StatusException("1002", "试题内容未找到");
                 }
                 copyFromTemp(eq, op.get());
             }