wangwei преди 5 години
родител
ревизия
dc7525fb57

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

@@ -19,8 +19,11 @@ import cn.com.qmth.examcloud.core.oe.student.base.utils.Check;
 import cn.com.qmth.examcloud.core.oe.student.bean.ExamStudentQuestionInfo;
 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.support.examing.ExamingHeartbeat;
 import cn.com.qmth.examcloud.support.examing.ExamingSession;
 import cn.com.qmth.examcloud.support.examing.ExamingStatus;
+import cn.com.qmth.examcloud.support.redis.RedisKeyHelper;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -42,7 +45,8 @@ public class ExamQuestionController extends ControllerSupport {
 	@Autowired
     private ExamingSessionService examingSessionService;
 	
-	
+    @Autowired
+    private RedisClient redisClient;
 	
 	/**
 	 * 将mongodb中的答过的题和redis中的题目列表合并返回给前端
@@ -54,10 +58,19 @@ public class ExamQuestionController extends ControllerSupport {
 	public List<ExamQuestion> findExamQuestionList(){
 		User user = getAccessUser();
 		ExamingSession examSessionInfo = examingSessionService.getExamingSession(user.getUserId());
-        if (examSessionInfo == null || examSessionInfo.getExamingStatus().equals(ExamingStatus.INFORMAL)
-                || examSessionInfo.getCost() >= examSessionInfo.getExamDuration()) {
-			throw new StatusException("1001","考试会话已过期,请重新开考");
+		if (examSessionInfo == null
+				|| examSessionInfo.getExamingStatus().equals(ExamingStatus.INFORMAL)) {
+			throw new StatusException("1001", "考试会话已过期,请重新开考");
+		}
+        
+        String examingHeartbeatKey = RedisKeyHelper.getBuilder().examingHeartbeatKey(examSessionInfo.getExamRecordDataId());
+        ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,ExamingHeartbeat.class);
+        
+		if (null != examingHeartbeat
+				&& examingHeartbeat.getCost() >= examSessionInfo.getExamDuration()) {
+			throw new StatusException("1001", "考试会话已过期,请重新开考");
 		}
+        
 		ExamRecordQuestions qers = examRecordQuestionsService.getExamRecordQuestions(examSessionInfo.getExamRecordDataId());
 		List<ExamQuestion> examQuestionList=qers.getExamQuestions();
 		for(ExamQuestion examQuestion:examQuestionList){

+ 34 - 7
examcloud-core-oe-student-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/student/api/controller/FaceBiopsyController.java

@@ -1,30 +1,47 @@
 package cn.com.qmth.examcloud.core.oe.student.api.controller;
 
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+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.api.commons.security.bean.User;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.core.oe.student.bean.*;
+import cn.com.qmth.examcloud.core.oe.student.bean.FaceBiopsyBaseInfo;
+import cn.com.qmth.examcloud.core.oe.student.bean.FaceBiopsyInfo;
+import cn.com.qmth.examcloud.core.oe.student.bean.FaceBiopsyStepInfo;
+import cn.com.qmth.examcloud.core.oe.student.bean.SaveFaceBiopsyResultReq;
+import cn.com.qmth.examcloud.core.oe.student.bean.SaveFaceBiopsyResultResp;
 import cn.com.qmth.examcloud.core.oe.student.dao.FaceBiopsyItemRepo;
 import cn.com.qmth.examcloud.core.oe.student.dao.FaceBiopsyItemStepRepo;
 import cn.com.qmth.examcloud.core.oe.student.dao.entity.FaceBiopsyItemEntity;
 import cn.com.qmth.examcloud.core.oe.student.dao.entity.FaceBiopsyItemStepEntity;
 import cn.com.qmth.examcloud.core.oe.student.dao.enums.FaceBiopsyType;
-import cn.com.qmth.examcloud.core.oe.student.service.*;
+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.ExamingSessionService;
+import cn.com.qmth.examcloud.core.oe.student.service.FaceBiopsyService;
 import cn.com.qmth.examcloud.support.Constants;
 import cn.com.qmth.examcloud.support.enums.ExamRecordStatus;
 import cn.com.qmth.examcloud.support.enums.FaceBiopsyScheme;
 import cn.com.qmth.examcloud.support.enums.HandInExamType;
 import cn.com.qmth.examcloud.support.examing.ExamRecordData;
+import cn.com.qmth.examcloud.support.examing.ExamingHeartbeat;
 import cn.com.qmth.examcloud.support.examing.ExamingSession;
 import cn.com.qmth.examcloud.support.helper.FaceBiopsyHelper;
+import cn.com.qmth.examcloud.support.redis.RedisKeyHelper;
 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.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.*;
-
-import java.util.List;
 
 /**
  * @Description 人脸活体检测接口
@@ -52,6 +69,9 @@ public class FaceBiopsyController extends ControllerSupport {
 
     @Autowired
     private ExamControlService examControlService;
+    
+    @Autowired
+    private RedisClient redisClient;
 
     @ApiOperation(value = "获取活体检测基本信息")
     @GetMapping("/getFaceBiopsyBaseInfo")
@@ -96,8 +116,15 @@ public class FaceBiopsyController extends ControllerSupport {
         }
         // 非新活检,默认使用旧的活检计算方式
         else {
+        	
+            String examingHeartbeatKey = RedisKeyHelper.getBuilder().examingHeartbeatKey(examSessionInfo.getExamRecordDataId());
+            ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,ExamingHeartbeat.class);
+            
+			int usedMinute = null == examingHeartbeat
+					? 0
+					: examingHeartbeat.getCost().intValue() / 60;
             faceVerifyMinute = examFaceLivenessVerifyService.getFaceLivenessVerifyMinute(user.getRootOrgId(),
-                    orgId, examId, studentId, examRecordData.getId(), examSessionInfo.getCost().intValue() / 60);
+                    orgId, examId, studentId, examRecordData.getId(), usedMinute);
         }
 
         FaceBiopsyBaseInfo faceBiopsyBaseInfo = new FaceBiopsyBaseInfo();

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

@@ -95,6 +95,8 @@ import cn.com.qmth.examcloud.support.enums.FaceBiopsyScheme;
 import cn.com.qmth.examcloud.support.enums.HandInExamType;
 import cn.com.qmth.examcloud.support.examing.ExamBoss;
 import cn.com.qmth.examcloud.support.examing.ExamRecordData;
+import cn.com.qmth.examcloud.support.examing.ExamingActivityTime;
+import cn.com.qmth.examcloud.support.examing.ExamingHeartbeat;
 import cn.com.qmth.examcloud.support.examing.ExamingSession;
 import cn.com.qmth.examcloud.support.examing.ExamingStatus;
 import cn.com.qmth.examcloud.support.helper.ExamCacheTransferHelper;
@@ -169,7 +171,7 @@ public class ExamControlServiceImpl implements ExamControlService {
 
     // 又拍云签名有效时间(秒)
     private static final Integer SIGN_TIMEOUT = 60;
-
+    
     @Transactional
     @Override
     public StartExamInfo startExam(Long examStudentId, User user) {
@@ -419,8 +421,6 @@ public class ExamControlServiceImpl implements ExamControlService {
         }
 
         examingSession = new ExamingSession();
-        examingSession.setActiveTime(0L);
-        examingSession.setCost(0L);
         examingSession.setCourseCode(courseCacheBean.getCode());
         examingSession.setCourseId(courseCacheBean.getId());
         examingSession.setCreationTime(new Date());
@@ -1191,10 +1191,17 @@ public class ExamControlServiceImpl implements ExamControlService {
             }
             // 更新考试中的断点续考属性
             examRecordDataService.saveExamRecordDataCache(examingRecord.getId(), examingRecord);
+            
+			String examingHeartbeatKey = RedisKeyHelper.getBuilder()
+					.examingHeartbeatKey(examSessionInfo.getExamRecordDataId());
+			ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,
+					ExamingHeartbeat.class);
+
+			long UsedTime = null == examingHeartbeat ? 0 : examingHeartbeat.getCost();
 
             checkExamInProgressInfo.setExamRecordDataId(examingRecord.getId());
             checkExamInProgressInfo.setExamId(examSessionInfo.getExamId());
-            checkExamInProgressInfo.setUsedTime(examSessionInfo.getCost());
+            checkExamInProgressInfo.setUsedTime(UsedTime);
             checkExamInProgressInfo.setMaxInterruptNum(maxInterruptNum);
             checkExamInProgressInfo.setInterruptNum(examingRecord.getContinuedCount());
 
@@ -1208,7 +1215,7 @@ public class ExamControlServiceImpl implements ExamControlService {
             } else {// 非新活检,默认使用旧的活检计算方式
                 faceVerifyMinute = examFaceLivenessVerifyService.getFaceLivenessVerifyMinute(
                         examSessionInfo.getRootOrgId(), examSessionInfo.getOrgId(), examSessionInfo.getExamId(),
-                        studentId, examSessionInfo.getExamRecordDataId(), examSessionInfo.getCost().intValue() / 60);
+						studentId, examSessionInfo.getExamRecordDataId(), (int) UsedTime / 60);
             }
 
             checkExamInProgressInfo.setFaceVerifyMinute(faceVerifyMinute);
@@ -1222,19 +1229,35 @@ public class ExamControlServiceImpl implements ExamControlService {
         if (examingRecord == null) {
             return null;
         }
+        
+    	String examingHeartbeatKey = RedisKeyHelper.getBuilder()
+				.examingHeartbeatKey(examSessionInfo.getExamRecordDataId());
+		ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,
+				ExamingHeartbeat.class);
 
-        // 如果考试时间结束,自动交卷
-        if (examSessionInfo.getExamDuration() <= examSessionInfo.getCost() * 1000) {
-            delayHandInExamIfLocked(examingRecord.getId());
-            return null;
-        }
+		long UsedTime = null == examingHeartbeat ? 0 : examingHeartbeat.getCost();
 
-        // 如果已经过了断点续考时间,自动交卷
-        long now = System.currentTimeMillis();
-        if (now - examSessionInfo.getActiveTime() >= examSessionInfo.getExamReconnectTime().intValue() * 60 * 1000) {
+        // 如果考试时间结束,自动交卷
+        if (examSessionInfo.getExamDuration() <= UsedTime * 1000) {
             delayHandInExamIfLocked(examingRecord.getId());
             return null;
         }
+        
+		String examingActiveTimeKey = RedisKeyHelper.getBuilder()
+				.examingActiveTimeKey(examSessionInfo.getExamRecordDataId());
+		ExamingActivityTime examingActiveTime = redisClient.get(examingActiveTimeKey,
+				ExamingActivityTime.class);
+
+		long activeTime = null == examingActiveTime
+				? System.currentTimeMillis()
+				: examingActiveTime.getActiveTime();
+
+		// 如果已经过了断点续考时间,自动交卷
+		long now = System.currentTimeMillis();
+		if (now - activeTime >= examSessionInfo.getExamReconnectTime().intValue() * 60 * 1000) {
+			delayHandInExamIfLocked(examingRecord.getId());
+			return null;
+		}
         return examingRecord;
     }
 
@@ -1295,24 +1318,56 @@ public class ExamControlServiceImpl implements ExamControlService {
     public long examHeartbeat(User user) {
         Long studentId = user.getUserId();
         ExamingSession examSessionInfo = examingSessionService.getExamingSession(studentId);
-        if (examSessionInfo == null || examSessionInfo.getExamingStatus().equals(ExamingStatus.INFORMAL)
-                || examSessionInfo.getCost() >= examSessionInfo.getExamDuration()) {
-            throw new StatusException("8001", "无效的会话,请离开考试");
-        }
-        // 考试耗时加60秒
-        examSessionInfo.setCost(examSessionInfo.getCost() + 60);
+		if (examSessionInfo == null
+				|| examSessionInfo.getExamingStatus().equals(ExamingStatus.INFORMAL)) {
+			throw new StatusException("8001", "无效的会话,请离开考试");
+		}
+
+        String examingHeartbeatKey = RedisKeyHelper.getBuilder().examingHeartbeatKey(examSessionInfo.getExamRecordDataId());
+        ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,ExamingHeartbeat.class);
+        
+		if (null != examingHeartbeat
+				&& examingHeartbeat.getCost() >= examSessionInfo.getExamDuration()) {
+			throw new StatusException("8002", "考试会话已过期,请重新开考");
+		}
+		
+		if (null==examingHeartbeat) {
+			examingHeartbeat=new ExamingHeartbeat();
+			examingHeartbeat.setCost(0L);
+			examingHeartbeat.setTimes(0L);
+			examingHeartbeat.setExamRecordDataId(examSessionInfo.getExamRecordDataId());
+		}
+		
+		examingHeartbeat.setTimes(examingHeartbeat.getTimes()+1);
+		examingHeartbeat.setCost(examingHeartbeat.getCost() + 60);
+		
+		redisClient.set(examingHeartbeatKey, examingHeartbeat);
+		
+		String examingActiveTimeKey = RedisKeyHelper.getBuilder()
+				.examingActiveTimeKey(examSessionInfo.getExamRecordDataId());
+		ExamingActivityTime examingActiveTime = redisClient.get(examingActiveTimeKey,
+				ExamingActivityTime.class);
+		if (null==examingActiveTime) {
+			examingActiveTime=new ExamingActivityTime();
+			examingActiveTime.setActiveTime(System.currentTimeMillis());
+			examingActiveTime.setExamRecordDataId(examSessionInfo.getExamRecordDataId());
+		}
+		
+		redisClient.set(examingActiveTimeKey, examingActiveTime);
+
+		long activeTime = examingActiveTime.getActiveTime();
+				
         long now = System.currentTimeMillis();
-        if (now - examSessionInfo.getActiveTime() >= examSessionInfo.getExamReconnectTime().intValue() * 60 * 1000) {
+        if (now - activeTime>= examSessionInfo.getExamReconnectTime().intValue() * 60 * 1000) {
             delayHandInExamIfLocked(examSessionInfo.getExamRecordDataId());
             return 0L;
         }
-        // 更新考试会话过期时间
-        examingSessionService.saveExamingSession(examSessionInfo.getStudentId(), examSessionInfo);
+ 
         // 在线考生心跳打点
         ReportsUtil.report(new OnlineExamStudentReport(user.getRootOrgId(), user.getUserId(),
                 examSessionInfo.getExamId(), examSessionInfo.getExamStudentId()));
         // 返回考试剩余时间
-        return examSessionInfo.getExamDuration() - (examSessionInfo.getCost() * 1000);
+        return examSessionInfo.getExamDuration() - (examingHeartbeat.getCost() * 1000);
     }
 
     /**
@@ -1329,7 +1384,15 @@ public class ExamControlServiceImpl implements ExamControlService {
         if (examingSession == null) {
             throw new StatusException("oestudent-100100", "考试会话已过期");
         }
-        Long examUsedMilliSeconds = examingSession.getCost() * 1000;
+        
+		String examingHeartbeatKey = RedisKeyHelper.getBuilder()
+				.examingHeartbeatKey(examingSession.getExamRecordDataId());
+		ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,
+				ExamingHeartbeat.class);
+
+		long cost = null == examingHeartbeat ? 0L : examingHeartbeat.getCost();
+        
+        Long examUsedMilliSeconds = cost * 1000;
         // 如果没有超过冻结时间,抛出异常
         if (examingSession.getExamType().equals(ExamType.ONLINE.name())) {
             ExamRecordData examRecordData = examRecordDataService

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

@@ -149,10 +149,10 @@ public class ExamRecordQuestionsServiceImpl implements ExamRecordQuestionsServic
     @Override
     public String getQuestionContent(Long studentId, String questionId) {
         ExamingSession examSessionInfo = examingSessionService.getExamingSession(studentId);
-        if (examSessionInfo == null || examSessionInfo.getExamingStatus().equals(ExamingStatus.INFORMAL)
-                || examSessionInfo.getCost() >= examSessionInfo.getExamDuration()) {
-            throw new StatusException("2001", "考试已结束,不允许获取试题内容");
-        }
+		if (examSessionInfo == null
+				|| examSessionInfo.getExamingStatus().equals(ExamingStatus.INFORMAL)) {
+			throw new StatusException("2001", "考试已结束,不允许获取试题内容");
+		}
 
         // 本地缓存key规则:examId_courseCode_paperType_questionId
         String cacheKey = examSessionInfo.getExamId() + "_" + examSessionInfo.getCourseCode() + "_"
@@ -183,10 +183,10 @@ public class ExamRecordQuestionsServiceImpl implements ExamRecordQuestionsServic
     @Override
     public void submitQuestionAnswer(Long studentId, List<ExamStudentQuestionInfo> examQuestionInfos) {
         ExamingSession examSessionInfo = examingSessionService.getExamingSession(studentId);
-        if (examSessionInfo == null || examSessionInfo.getExamingStatus().equals(ExamingStatus.INFORMAL)
-                || examSessionInfo.getCost() >= examSessionInfo.getExamDuration()) {
-            throw new StatusException("3001", "考试会话已过期");
-        }
+		if (examSessionInfo == null
+				|| examSessionInfo.getExamingStatus().equals(ExamingStatus.INFORMAL)) {
+			throw new StatusException("3001", "考试会话已过期");
+		}
         long examRecordDataId = examSessionInfo.getExamRecordDataId();
 
         for (ExamStudentQuestionInfo examQuestionInfo : examQuestionInfos) {

+ 28 - 14
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/FaceBiopsyServiceImpl.java

@@ -23,11 +23,15 @@ import cn.com.qmth.examcloud.support.Constants;
 import cn.com.qmth.examcloud.support.enums.ExamProperties;
 import cn.com.qmth.examcloud.support.enums.IsSuccess;
 import cn.com.qmth.examcloud.support.examing.ExamRecordData;
+import cn.com.qmth.examcloud.support.examing.ExamingHeartbeat;
 import cn.com.qmth.examcloud.support.examing.ExamingSession;
 import cn.com.qmth.examcloud.support.helper.ExamCacheTransferHelper;
 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.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+
 import com.google.common.collect.Lists;
 import com.mysql.cj.util.StringUtils;
 import org.apache.commons.lang3.RandomUtils;
@@ -61,6 +65,8 @@ public class FaceBiopsyServiceImpl implements FaceBiopsyService {
     private ExamRecordDataService examRecordDataService;
     @Autowired
     ExamingSessionService examingSessionService;
+    @Autowired
+    private RedisClient redisClient;
 
     @Override
     @Transactional
@@ -834,18 +840,26 @@ public class FaceBiopsyServiceImpl implements FaceBiopsyService {
                 examRecordData.getExamId(), examRecordData.getStudentId());
     }
 
-    /**
-     * 获取心跳时间
-     *
-     * @param studentId
-     * @return
-     */
-    private Integer getExamUsedMinutes(Long studentId) {
-        ExamingSession examingSession = examingSessionService.getExamingSession(studentId);
-        if (examingSession == null) {
-            throw new StatusException("201002", "考试会话已过期");
-        }
-
-        return Math.toIntExact(examingSession.getCost() / 60);
-    }
+	/**
+	 * 获取心跳时间
+	 *
+	 * @param studentId
+	 * @return
+	 */
+	private Integer getExamUsedMinutes(Long studentId) {
+		ExamingSession examingSession = examingSessionService.getExamingSession(studentId);
+		if (examingSession == null) {
+			throw new StatusException("201002", "考试会话已过期");
+		}
+
+		String examingHeartbeatKey = RedisKeyHelper.getBuilder()
+				.examingHeartbeatKey(examingSession.getExamRecordDataId());
+		ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,
+				ExamingHeartbeat.class);
+
+		long cost = null == examingHeartbeat ? 0 : examingHeartbeat.getCost();
+
+		return Math.toIntExact(cost / 60);
+	}
+    
 }