lideyin 5 سال پیش
والد
کامیت
4daf03887a

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

@@ -26,6 +26,7 @@ import cn.com.qmth.examcloud.core.oe.student.controller.bean.GetUpyunSignDomain;
 import cn.com.qmth.examcloud.core.oe.student.controller.bean.GetUpyunSignDomainQuery;
 import cn.com.qmth.examcloud.core.oe.student.service.ExamControlService;
 import cn.com.qmth.examcloud.core.oe.student.service.ExamRecordDataService;
+import cn.com.qmth.examcloud.core.oe.student.service.ExamSessionInfoService;
 import cn.com.qmth.examcloud.core.oe.student.service.ExamStudentService;
 import cn.com.qmth.examcloud.support.cache.CacheHelper;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
@@ -83,6 +84,8 @@ public class ExamControlController extends ControllerSupport {
     private ExamRecordRepo examRecordRepo;
     @Autowired
     ExamRecordDataService examRecordDataService;
+    @Autowired
+    private ExamSessionInfoService examSessionInfoService;
 
     /**
      * 获取当前服务器时间
@@ -138,14 +141,18 @@ public class ExamControlController extends ControllerSupport {
 
         long st = System.currentTimeMillis();
         long startTime = System.currentTimeMillis();
-        EndExamPreInfo endExamPreInfo = examControlService.endExamPre(studentId);
-        if (endExamPreInfo == null) {
-            throw new StatusException("oestudent-100100", "初始化交卷信息失败");
+
+        // 获取考试会话,判断考生是否已结束考试
+        ExamSessionInfo examSessionInfo = examSessionInfoService.getExamSessionInfo(studentId);
+        if (examSessionInfo == null) {
+            throw new StatusException("oestudent-100100", "考试会话已过期");
         }
+
+
         if (log.isDebugEnabled()) {
             log.debug("0 [END_EXAM] 交卷前处理耗时:" + (System.currentTimeMillis() - startTime) + " ms");
         }
-        examControlService.handInExam(endExamPreInfo.getExamRecordData(), HandInExamType.MANUAL);
+        examControlService.handInExam(examSessionInfo.getExamRecordDataId(), HandInExamType.MANUAL);
         if (log.isDebugEnabled()) {
             log.debug("1 [END_EXAM]合计 耗时:" + (System.currentTimeMillis() - st) + " ms");
         }

+ 140 - 136
examcloud-core-oe-student-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/student/controller/ExamFaceLivenessVerifyController.java

@@ -24,152 +24,156 @@ import java.util.List;
 
 
 /**
- * 
- * @author  	chenken
- * @date    	2018年9月5日 下午6:28:34
- * @company 	QMTH
+ * @author chenken
+ * @date 2018年9月5日 下午6:28:34
+ * @company QMTH
  * @description 活体检测controller
  */
 @Api(tags = "活体检测")
 @RestController
 @RequestMapping("${app.api.oe.student}/examFaceLivenessVerify")
-public class ExamFaceLivenessVerifyController extends ControllerSupport{
+public class ExamFaceLivenessVerifyController extends ControllerSupport {
+
+    @Autowired
+    private ExamFaceLivenessVerifyService examFaceLivenessVerifyService;
 
-	@Autowired
-	private ExamFaceLivenessVerifyService examFaceLivenessVerifyService;
-	
     @Autowired
     private WebsocketCloudService websocketCloudService;
-	
-	@Autowired
-	private ExamFaceLivenessVerifyRepo examFaceLivenessVerifyRepo;
-	
-	
-	@ApiOperation(value = "检测学生底照是否能获取到faceId验证的token")
-	@GetMapping("/checkFaceLiveness")
-	public GetFaceVerifyTokenInfo checkFaceLiveness(){
-		User user = getAccessUser();
-		String bizNo = user.getUserId().toString();
-		return examFaceLivenessVerifyService.getFaceVerifyToken(user.getUserId(),bizNo);
-	} 
-	
-	/**
-	 * 获得一个faceid用于网页端活体检测的token
-	 * @param request
-	 * @param examRecordId
-	 * @return
-	 */
-	@ApiOperation(value = "获得一个faceid用于网页端活体检测的token")
-	@GetMapping("/getFaceLivenessVerifyToken/{examRecordDataId}")
-	public GetFaceVerifyTokenInfo getFaceVerifyToken(@PathVariable Long examRecordDataId){
-		Check.isNull(examRecordDataId, "examRecordDataId不能为空");
-		User user = getAccessUser();
+
+    @Autowired
+    private ExamFaceLivenessVerifyRepo examFaceLivenessVerifyRepo;
+
+
+    @ApiOperation(value = "检测学生底照是否能获取到faceId验证的token")
+    @GetMapping("/checkFaceLiveness")
+    public GetFaceVerifyTokenInfo checkFaceLiveness() {
+        User user = getAccessUser();
+        String bizNo = user.getUserId().toString();
+        return examFaceLivenessVerifyService.getFaceVerifyToken(user.getUserId(), bizNo);
+    }
+
+    /**
+     * 获得一个faceid用于网页端活体检测的token
+     *
+     * @param examRecordDataId
+     * @return
+     */
+    @ApiOperation(value = "获得一个faceid用于网页端活体检测的token")
+    @GetMapping("/getFaceLivenessVerifyToken/{examRecordDataId}")
+    public GetFaceVerifyTokenInfo getFaceVerifyToken(@PathVariable Long examRecordDataId) {
+        Check.isNull(examRecordDataId, "examRecordDataId不能为空");
+        User user = getAccessUser();
         ExamFaceLivenessVerifyEntity faceVerify = examFaceLivenessVerifyService.saveFaceVerifyByExamRecordDataId(examRecordDataId);
-        GetFaceVerifyTokenInfo getFaceVerifyTokenInfo = examFaceLivenessVerifyService.getFaceVerifyToken(user.getUserId(),faceVerify.getId().toString());
-        if(!getFaceVerifyTokenInfo.getSuccess()){
-        	faceVerify.setIsError(true);
-        	faceVerify.setErrorMsg(getFaceVerifyTokenInfo.getErrorMsg());
-			examFaceLivenessVerifyRepo.save(faceVerify);
+        GetFaceVerifyTokenInfo getFaceVerifyTokenInfo = examFaceLivenessVerifyService.getFaceVerifyToken(user.getUserId(), faceVerify.getId().toString());
+        if (!getFaceVerifyTokenInfo.getSuccess()) {
+            faceVerify.setIsError(true);
+            faceVerify.setErrorMsg(getFaceVerifyTokenInfo.getErrorMsg());
+            examFaceLivenessVerifyRepo.save(faceVerify);
+        }
+        return getFaceVerifyTokenInfo;
+    }
+
+    @ApiOperation(value = "更新活体检测结果")
+    @GetMapping("/updateFaceLivenessVerify/{examRecordDataId}")
+    public void updateFaceVerify(@PathVariable Long examRecordDataId, @RequestParam String errorMsg) {
+        Check.isNull(examRecordDataId, "examRecordDataId不能为空");
+        List<ExamFaceLivenessVerifyEntity> examFaceLivenessVerifyEntities = examFaceLivenessVerifyService.listFaceVerifyByExamRecordId(examRecordDataId);
+        if (examFaceLivenessVerifyEntities != null && examFaceLivenessVerifyEntities.size() > 0) {
+            ExamFaceLivenessVerifyEntity examFaceLivenessVerifyEntity = examFaceLivenessVerifyEntities.get(0);
+            examFaceLivenessVerifyEntity.setIsError(true);
+            examFaceLivenessVerifyEntity.setErrorMsg(errorMsg);
+            examFaceLivenessVerifyRepo.save(examFaceLivenessVerifyEntity);
+        }
+    }
+
+    /**
+     * 人脸验证完成后的回调,由faceId调用
+     *
+     * @param data
+     * @throws Exception
+     */
+    @Naked
+    @ApiOperation(value = "人脸验证完成后的回调,由faceId调用")
+    @PostMapping("/faceLivenessVerifyCallback")
+    public void faceLivenessVerifyCallback(@RequestParam String data) throws Exception {
+        log.info("faceId回调,data=" + data);
+
+        JSONObject returnJsonObject = new JSONObject(data);
+        Long faceVerifyId = Long.parseLong(returnJsonObject.get("biz_no") + "");
+        ExamFaceLivenessVerifyEntity currentFaceVerify = examFaceLivenessVerifyService.findFaceVerifyById(faceVerifyId);
+        /**
+         * 如果该检测记录结果已经非空了,直接返回,
+         * 有可能超时程序已经将结果填成TIME_OUT了
+         */
+        if (currentFaceVerify.getVerifyResult() != null) {
+            return;
+        }
+
+        ExamFaceLivenessVerifyEntity faceVerify = examFaceLivenessVerifyService.faceIdNotify(data);
+        List<ExamFaceLivenessVerifyEntity> faceVerifies = examFaceLivenessVerifyService.listFaceVerifyByExamRecordId(faceVerify.getExamRecordDataId());
+        JSONObject jsonObject = new JSONObject();
+        //验证次数
+        jsonObject.put("verifyCount", faceVerifies.size());
+        //取最后一次验证结果
+        jsonObject.put("verifyResult", faceVerifies.get(faceVerifies.size() - 1).getVerifyResult().name());
+        jsonObject.put("examRecordDataId", faceVerify.getExamRecordDataId());
+
+        SendMessageReq sendMessageReq = new SendMessageReq();
+        sendMessageReq.setExamRecordDataId(faceVerify.getExamRecordDataId());
+        sendMessageReq.setReturnMsgJson(jsonObject.toString());
+        websocketCloudService.sendMessage(sendMessageReq);
+    }
+
+    /**
+     * 人脸检测超时处理
+     *
+     * @param examRecordDataId
+     * @return
+     */
+    @ApiOperation(value = "人脸检测超时处理")
+    @GetMapping("/faceLivenessVerifyTimeOut/{examRecordDataId}")
+    public String faceTestTimeOut(@PathVariable Long examRecordDataId) {
+        JSONObject jsonObject = new JSONObject();
+        examFaceLivenessVerifyService.faceTestTimeOut(examRecordDataId);
+        List<ExamFaceLivenessVerifyEntity> faceVerifies = examFaceLivenessVerifyService.listFaceVerifyByExamRecordId(examRecordDataId);
+        //验证次数
+        try {
+            jsonObject.put("verifyCount", faceVerifies.size());
+            //取最后一次验证结果
+            jsonObject.put("verifyResult", faceVerifies.get(faceVerifies.size() - 1).getVerifyResult().name());
+            jsonObject.put("examRecordDataId", examRecordDataId);
+        } catch (JSONException e) {
+            e.printStackTrace();
         }
-		return getFaceVerifyTokenInfo;
-	}
-	
-	@ApiOperation(value = "更新活体检测结果")
-	@GetMapping("/updateFaceLivenessVerify/{examRecordDataId}")
-	public void updateFaceVerify(@PathVariable Long examRecordDataId,@RequestParam String errorMsg){
-		Check.isNull(examRecordDataId, "examRecordDataId不能为空");
-		List<ExamFaceLivenessVerifyEntity> examFaceLivenessVerifyEntities = examFaceLivenessVerifyService.listFaceVerifyByExamRecordId(examRecordDataId);
-		if(examFaceLivenessVerifyEntities!=null && examFaceLivenessVerifyEntities.size()>0){
-			ExamFaceLivenessVerifyEntity examFaceLivenessVerifyEntity = examFaceLivenessVerifyEntities.get(0);
-			examFaceLivenessVerifyEntity.setIsError(true);
-			examFaceLivenessVerifyEntity.setErrorMsg(errorMsg);
-			examFaceLivenessVerifyRepo.save(examFaceLivenessVerifyEntity);
-		}
-	}
-	
-	/**
-	 * 人脸验证完成后的回调,由faceId调用
-	 * @param data
-	 * @throws Exception 
-	 */
-	@Naked
-	@ApiOperation(value = "人脸验证完成后的回调,由faceId调用")
-	@PostMapping("/faceLivenessVerifyCallback")
-	public void faceLivenessVerifyCallback(@RequestParam String data) throws Exception{
-		log.info("faceId回调,data="+data);
-		
-		JSONObject returnJsonObject = new JSONObject(data);
-		Long faceVerifyId = Long.parseLong(returnJsonObject.get("biz_no")+"");
-		ExamFaceLivenessVerifyEntity currentFaceVerify = examFaceLivenessVerifyService.findFaceVerifyById(faceVerifyId);
-		/**
-		 * 如果该检测记录结果已经非空了,直接返回,
-		 * 有可能超时程序已经将结果填成TIME_OUT了
-		 */
-		if(currentFaceVerify.getVerifyResult()!=null){
-			return;
-		}
-		
-		ExamFaceLivenessVerifyEntity faceVerify = examFaceLivenessVerifyService.faceIdNotify(data);
-		List<ExamFaceLivenessVerifyEntity> faceVerifies = examFaceLivenessVerifyService.listFaceVerifyByExamRecordId(faceVerify.getExamRecordDataId());
-		JSONObject jsonObject = new JSONObject();
-		//验证次数
-		jsonObject.put("verifyCount", faceVerifies.size());
-		//取最后一次验证结果
-		jsonObject.put("verifyResult", faceVerifies.get(faceVerifies.size()-1).getVerifyResult().name());
-		jsonObject.put("examRecordDataId",faceVerify.getExamRecordDataId());
-		
-		SendMessageReq sendMessageReq = new SendMessageReq();
-		sendMessageReq.setExamRecordDataId(faceVerify.getExamRecordDataId());
-		sendMessageReq.setReturnMsgJson(jsonObject.toString());
-		websocketCloudService.sendMessage(sendMessageReq);
-	}
-	
-	/**
-	 * 人脸检测超时处理
-	 * @param examRecordId
-	 * @return
-	 */
-	@ApiOperation(value = "人脸检测超时处理")
-	@GetMapping("/faceLivenessVerifyTimeOut/{examRecordDataId}")
-	public String faceTestTimeOut(@PathVariable Long examRecordDataId){
-		JSONObject jsonObject = new JSONObject();
-		examFaceLivenessVerifyService.faceTestTimeOut(examRecordDataId);
-		List<ExamFaceLivenessVerifyEntity> faceVerifies = examFaceLivenessVerifyService.listFaceVerifyByExamRecordId(examRecordDataId);
-		//验证次数
-		try {
-			jsonObject.put("verifyCount", faceVerifies.size());
-			//取最后一次验证结果
-			jsonObject.put("verifyResult", faceVerifies.get(faceVerifies.size()-1).getVerifyResult().name());
-			jsonObject.put("examRecordDataId",examRecordDataId);
-		} catch (JSONException e) {
-			e.printStackTrace();
-		}
-		
-		return jsonObject.toString();
-	}
-	
-	 /**
-     * 人脸检测结束处理
-     * @param examRecordId
-     * @throws Exception 
+
+        return jsonObject.toString();
+    }
+
+    /**
+     * 人脸活体检测结束处理
+     *
+     * @param examRecordDataId 考试记录id
+     * @param result           活体检测结果
+     * @throws Exception
      */
-	@ApiOperation(value = "人脸检测结束处理")
-	@GetMapping(value = "faceLivenessVerifyEnd/{examRecordDataId}")
-    public ExamProcessResultDomain faceTestEndHandle(@PathVariable Long examRecordDataId, @RequestParam String result) throws Exception{
-		ExamProcessResultDomain res = new ExamProcessResultDomain();
-		try {
-			examFaceLivenessVerifyService.faceTestEndHandle(examRecordDataId,result);
-			res.setCode(Constants.COMMON_SUCCESS_CODE);
-			return res;
-		} catch (StatusException e) {
-			if (e.getCode().equals(Constants.EXAM_RECORD_NOT_END_STATUS_CODE)) {
-				res.setCode(Constants.PROCESSING_EXAM_RECORD_CODE);
-				return res;
-			}
-			throw e;
-		} catch (Exception e) {
-			throw e;
-		}
+    @ApiOperation(value = "人脸检测结束处理")
+    @GetMapping(value = "faceLivenessVerifyEnd/{examRecordDataId}")
+    public ExamProcessResultDomain faceTestEndHandle(@PathVariable Long examRecordDataId, @RequestParam String result) throws Exception {
+        ExamProcessResultDomain res = new ExamProcessResultDomain();
+        try {
+            User user = getAccessUser();
+            examFaceLivenessVerifyService.faceTestEndHandle(examRecordDataId, user.getUserId(), result);
+            res.setCode(Constants.COMMON_SUCCESS_CODE);
+            return res;
+        } catch (StatusException e) {
+            if (e.getCode().equals(Constants.EXAM_RECORD_NOT_END_STATUS_CODE)) {
+                res.setCode(Constants.PROCESSING_EXAM_RECORD_CODE);
+                return res;
+            }
+            throw e;
+        } catch (Exception e) {
+            throw e;
+        }
     }
-	
+
 }

+ 3 - 10
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/ExamControlService.java

@@ -27,17 +27,10 @@ public interface ExamControlService {
      * @param user
      */
     public StartExamInfo startExam(Long examStudentId, User user);
-    
-    /**
-     * 结束考试前置处理
-     * @param studentId
-     * @return
-     */
-    public EndExamPreInfo endExamPre(Long studentId);
 
     /**
      * 考试心跳
-     * @param studentId
+     * @param
      */
     public long examHeartbeat(User user);
 
@@ -114,10 +107,10 @@ public interface ExamControlService {
 
 	/**
 	 * 交卷
-	 * @param examRecordData
+	 * @param examRecordDataId 考试记录id
 	 * @param handInExamType 交卷类型
 	 */
-	void handInExam(ExamRecordDataEntity examRecordData, HandInExamType handInExamType);
+	void handInExam(Long examRecordDataId, HandInExamType handInExamType);
 
 	/**
 	 * 交卷的后续处理

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

@@ -56,9 +56,10 @@ public interface ExamFaceLivenessVerifyService {
 	/**
 	 * 人脸活体检测结束处理
 	 * @param examRecordDataId
+	 * @param  studentId
 	 * @param result
 	 */
-	public void faceTestEndHandle(Long examRecordDataId, String result);
+	public void faceTestEndHandle(Long examRecordDataId,Long studentId, String result);
 	/**
 	 * 断点续考,获取活体检测开启时间
 	 * @param examId

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

@@ -1,40 +1,5 @@
 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 javax.validation.Valid;
-
-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.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-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 com.google.common.base.Splitter;
-
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.commons.util.ByteUtil;
@@ -45,56 +10,13 @@ import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
 import cn.com.qmth.examcloud.core.oe.common.base.Constants;
 import cn.com.qmth.examcloud.core.oe.common.base.utils.CommonUtil;
 import cn.com.qmth.examcloud.core.oe.common.base.utils.QuestionTypeUtil;
-import cn.com.qmth.examcloud.core.oe.common.entity.ExamFileAnswerTempEntity;
-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.ExamRecordPaperStructEntity;
-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.entity.ExamStudentEntity;
-import cn.com.qmth.examcloud.core.oe.common.entity.ExamingRecordEntity;
-import cn.com.qmth.examcloud.core.oe.common.entity.HandInExamRecordEntity;
-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.ExamType;
-import cn.com.qmth.examcloud.core.oe.common.enums.FileAnswerAcknowledgeStatus;
-import cn.com.qmth.examcloud.core.oe.common.enums.HandInExamType;
-import cn.com.qmth.examcloud.core.oe.common.repository.ExamCaptureQueueRepo;
-import cn.com.qmth.examcloud.core.oe.common.repository.ExamFileAnswerTempRepo;
-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.oe.common.repository.ExamScoreRepo;
-import cn.com.qmth.examcloud.core.oe.common.repository.ExamStudentRepo;
-import cn.com.qmth.examcloud.core.oe.common.repository.ExamingRecordRepo;
+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.student.bean.CalculateFaceCheckResultInfo;
-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.EndExamPreInfo;
-import cn.com.qmth.examcloud.core.oe.student.bean.ExamSessionInfo;
-import cn.com.qmth.examcloud.core.oe.student.bean.GetQrCodeReq;
-import cn.com.qmth.examcloud.core.oe.student.bean.GetUploadedFileAcknowledgeStatusReq;
-import cn.com.qmth.examcloud.core.oe.student.bean.GetUploadedFileAnswerListReq;
-import cn.com.qmth.examcloud.core.oe.student.bean.GetUpyunSignatureReq;
-import cn.com.qmth.examcloud.core.oe.student.bean.SaveUploadedFileAcknowledgeStatusReq;
-import cn.com.qmth.examcloud.core.oe.student.bean.SaveUploadedFileReq;
-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.ExamCacheTransferHelper;
-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.ExamRecordForMarkingService;
-import cn.com.qmth.examcloud.core.oe.student.service.ExamRecordQuestionsService;
-import cn.com.qmth.examcloud.core.oe.student.service.ExamRecordService;
-import cn.com.qmth.examcloud.core.oe.student.service.ExamScoreService;
-import cn.com.qmth.examcloud.core.oe.student.service.ExamSessionInfoService;
-import cn.com.qmth.examcloud.core.oe.student.service.ExamStudentService;
+import cn.com.qmth.examcloud.core.oe.student.bean.*;
+import cn.com.qmth.examcloud.core.oe.student.service.*;
 import cn.com.qmth.examcloud.core.oe.websocket.api.FileAnswerWebsocketCloudService;
 import cn.com.qmth.examcloud.core.oe.websocket.api.enums.WebSocketEventType;
 import cn.com.qmth.examcloud.core.oe.websocket.api.request.SendFileAnswerMessageReq;
@@ -121,9 +43,30 @@ 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 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.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+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 javax.validation.Valid;
+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
@@ -142,8 +85,6 @@ public class ExamControlServiceImpl implements ExamControlService {
     private static final String OE_ANSWER_FILE_PATH = "oe-answer-file";
     private static final Logger log = LoggerFactory.getLogger(ExamControlServiceImpl.class);
     private static final Log cleanExamRecordTaskLog = LogFactory.getLog("CLEAN_EXAM_RECORD_TASK_LOGGER");
-    // 发送题目答案序号集合
-//    private static ConcurrentHashMap<String,Integer> sendQuestionAnswerSequenceMap=new ConcurrentHashMap<String,Integer>();
     @Autowired
     private ExamStudentService examStudentService;
     @Autowired
@@ -355,7 +296,7 @@ public class ExamControlServiceImpl implements ExamControlService {
             log.debug("12 合计 耗时:" + (System.currentTimeMillis() - st) + " ms");
         }
         //在线考生开考打点
-  		ReportsUtil.report(new OnlineExamStudentReport(user.getRootOrgId(), user.getUserId(), examBean.getId(), examStudentId));
+        ReportsUtil.report(new OnlineExamStudentReport(user.getRootOrgId(), user.getUserId(), examBean.getId(), examStudentId));
         return buildStartExamInfo(examRecordData.getId(), originalExamStudent, examBean, courseBean);
 //        } finally {
 //
@@ -649,9 +590,7 @@ public class ExamControlServiceImpl implements ExamControlService {
      * @return
      * @Param studentId
      */
-    private Long checkAndComputeExamDuration(ExamSessionInfo examSessionInfo, Long studentId) {
-        long st = System.currentTimeMillis();
-        long startTime = System.currentTimeMillis();
+    private Long checkAndComputeExamDuration(ExamSessionInfo examSessionInfo) {
 
         long now = System.currentTimeMillis();
         Long lastHeartbeat = examSessionInfo.getLastHeartbeat();
@@ -667,11 +606,7 @@ public class ExamControlServiceImpl implements ExamControlService {
         }
         // 如果没有超过冻结时间,抛出异常
         if (examSessionInfo.getExamType().equals(ExamType.ONLINE.name())) {
-            startTime = System.currentTimeMillis();
             ExamingRecordEntity rec = GlobalHelper.getEntity(examingRecordRepo, examSessionInfo.getExamRecordId(), ExamingRecordEntity.class);
-            if (log.isDebugEnabled()) {
-                log.debug("1.1 [CHECK_AND_COMPUTE_EXAMD_URATION]获取状态为examing的进行中的考试记录 耗时:" + (System.currentTimeMillis() - startTime) + " ms");
-            }
 
             if (rec != null && rec.getIsExceed() != null && rec.getIsExceed()) {// 超过断点最大次数的不校验冻结时间
                 return usedTime;
@@ -683,61 +618,18 @@ public class ExamControlServiceImpl implements ExamControlService {
             }
         }
 
-        if (log.isDebugEnabled()) {
-            log.debug("1.2 [CHECK_AND_COMPUTE_EXAMD_URATION]合计 耗时:" + (System.currentTimeMillis() - st) + " ms");
-        }
         return usedTime;
     }
 
-    @Override
-    public EndExamPreInfo endExamPre(Long studentId) {
-        long st = System.currentTimeMillis();
-        long startTime = System.currentTimeMillis();
-        // 获取考试会话,判断考生是否已结束考试
-        ExamSessionInfo examSessionInfo = examSessionInfoService.getExamSessionInfo(studentId);
-        if (examSessionInfo == null) {
-            return null;
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("1 [END_EXAM_PRE]获取考试会话 耗时:" + (System.currentTimeMillis() - startTime) + " ms");
-        }
-
-        long examRecordDataId = examSessionInfo.getExamRecordDataId();
-
-        startTime = System.currentTimeMillis();
-        // 得到考试时长,校验是否达到冻结时间
-        long usedExamTime = checkAndComputeExamDuration(examSessionInfo, studentId);
-        if (log.isDebugEnabled()) {
-            log.debug("2 [END_EXAM_PRE]校验是否达到冻结时间 耗时:" + (System.currentTimeMillis() - startTime) + " ms");
-        }
-
-        startTime = System.currentTimeMillis();
-        // 查询考试记录
-        ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo, examRecordDataId,
-                ExamRecordDataEntity.class);
-        examRecordData.setUsedExamTime(usedExamTime);
-        EndExamPreInfo endExamPreInfo = new EndExamPreInfo();
-        endExamPreInfo.setStudentId(studentId);
-        endExamPreInfo.setExamRecordData(examRecordData);
-        if (log.isDebugEnabled()) {
-            log.debug("3 [END_EXAM_PRE]查询考试记录 耗时:" + (System.currentTimeMillis() - startTime) + " ms");
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("4 [END_EXAM_PRE]合计 耗时:" + (System.currentTimeMillis() - st) + " ms");
-        }
-        return endExamPreInfo;
-    }
-
     /**
-     * @param examRecordData 考试记录对象
-     * @param handInExamType 交卷类型
+     * @param examRecordDataId 考试记录id
+     * @param handInExamType   交卷类型
      */
     @Override
     @Transactional
-    public void handInExam(ExamRecordDataEntity examRecordData, HandInExamType handInExamType) {
+    public void handInExam(Long examRecordDataId, HandInExamType handInExamType) {
         //此锁是为了避免自动交卷服务与断点续考交卷或活检失败交卷,同一时刻交卷争抢资源导致死锁
-        String sequenceLockKey = Constants.HAND_IN_EXAM_LOCK_PREFIX + examRecordData.getId();
+        String sequenceLockKey = Constants.HAND_IN_EXAM_LOCK_PREFIX + examRecordDataId;
         //系统在请求结束后会,自动释放锁,无需手动解锁
         SequenceLockHelper.getLock(sequenceLockKey);
 
@@ -748,6 +640,7 @@ public class ExamControlServiceImpl implements ExamControlService {
         long st = System.currentTimeMillis();
         long startTime = System.currentTimeMillis();
 
+        ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo, examRecordDataId, ExamRecordDataEntity.class);
         ExamRecordEntity examRecord = GlobalHelper.getEntity(examRecordRepo, examRecordData.getExamRecordId(), ExamRecordEntity.class);
         if (log.isDebugEnabled()) {
             log.debug("1  [HAND_IN_EXAM]获取考试记录耗时:" + (System.currentTimeMillis() - startTime) + " ms");
@@ -755,6 +648,14 @@ public class ExamControlServiceImpl implements ExamControlService {
 
         if (handInExamType == HandInExamType.MANUAL) {
             startTime = System.currentTimeMillis();
+            // 获取考试会话,判断考生是否已结束考试(二次校验)
+            ExamSessionInfo examSessionInfo = examSessionInfoService.getExamSessionInfo(examRecord.getStudentId());
+            if (examSessionInfo == null) {
+                throw new StatusException("oestudent-100100", "考试会话已过期");
+            }
+            // 得到考试时长,校验是否达到冻结时间
+            long usedExamTime = checkAndComputeExamDuration(examSessionInfo);
+            examRecordData.setUsedExamTime(usedExamTime);
             examRecordData.setExamRecordStatus(ExamRecordStatus.EXAM_HAND_IN);
             examRecordData.setEndTime(new Date());
 
@@ -889,11 +790,11 @@ public class ExamControlServiceImpl implements ExamControlService {
     /**
      * 考试心跳每分钟调用一次
      *
-     * @param studentId 学生id
+     * @param user 学生
      */
     @Override
     public long examHeartbeat(User user) {
-    	Long studentId=user.getUserId();
+        Long studentId = user.getUserId();
         ExamSessionInfo examSessionInfo = examSessionInfoService.getExamSessionInfo(studentId);
         if (examSessionInfo == null) {
             throw new StatusException("ExamControlServiceImpl-examHeartbeat-exception", "会话已过期,请离开考试!");
@@ -973,12 +874,10 @@ public class ExamControlServiceImpl implements ExamControlService {
 
         // 如果会话不存在,自动交卷
         if (examSessionInfo == null) {
-            // 查询考试记录
-            ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo, examingRecord.getExamRecordDataId(),
-                    ExamRecordDataEntity.class);
-            delayHandInExamIfLocked(examRecordData);
+            delayHandInExamIfLocked(examingRecord.getExamRecordDataId());
             return null;
         }
+
         // 如果已经过了断点续考时间,清除考试记录,清除会话
         Long lastHeartbeat = examSessionInfo.getLastHeartbeat();
         if (lastHeartbeat == null) {
@@ -987,9 +886,7 @@ public class ExamControlServiceImpl implements ExamControlService {
         long now = System.currentTimeMillis();
         if (now - lastHeartbeat >= examSessionInfo.getExamReconnectTime().intValue() * 60 * 1000) {
             examSessionInfoService.deleteExamSessionInfo(studentId);
-            ExamRecordDataEntity examRecordDataEntity = GlobalHelper.getEntity(examRecordDataRepo,
-                    examingRecord.getExamRecordDataId(), ExamRecordDataEntity.class);
-            delayHandInExamIfLocked(examRecordDataEntity);
+            delayHandInExamIfLocked(examingRecord.getExamRecordDataId());
             return null;
         }
         return examingRecord;
@@ -998,18 +895,18 @@ public class ExamControlServiceImpl implements ExamControlService {
     /**
      * 如果有序列化锁,则延迟交卷
      *
-     * @param examRecordData
+     * @param examRecordDataId 考试记录id
      * @return
      */
-    private void delayHandInExamIfLocked(ExamRecordDataEntity examRecordData) {
+    private void delayHandInExamIfLocked(Long examRecordDataId) {
         try {
-            handInExam(examRecordData, HandInExamType.AUTO);
+            handInExam(examRecordDataId, HandInExamType.AUTO);
         } catch (SequenceLockException e) {
             //如果发现自动服务正在交卷,则重试1500毫秒获取考试记录状态,判断是否已交卷
             int loopTimes = 0;
             while (loopTimes <= 15) {
                 loopTimes++;
-                examRecordData = GlobalHelper.getEntity(examRecordDataRepo, examRecordData.getId(),
+                ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo, examRecordDataId,
                         ExamRecordDataEntity.class);
 
                 //1500毫秒内如果交卷成功,则退出循环
@@ -1032,15 +929,14 @@ public class ExamControlServiceImpl implements ExamControlService {
             return;
         }
 
-        ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo, examingRecord.getExamRecordDataId(), ExamRecordDataEntity.class);
 
         //只有考试状态为考试中,系统才需要执行交卷动作,因为有可能已经手动交卷,所以这里再做一次判断
 //        if (examRecordData.getExamRecordStatus() == ExamRecordStatus.EXAM_ING) {
-        cleanExamRecordTaskLog.debug("[CLEAN_EXAMING_RECORD_" + examRecordData.getId() + "]状态为进行中考试,执行交卷[handInExam],.");
-        handInExam(examRecordData, HandInExamType.AUTO);
+        cleanExamRecordTaskLog.debug("[CLEAN_EXAMING_RECORD_" + examingRecord.getExamRecordDataId() + "]状态为进行中考试,执行交卷[handInExam],.");
+        handInExam(examingRecord.getExamRecordDataId(), HandInExamType.AUTO);
 //        }
 
-        cleanExamRecordTaskLog.debug("[CLEAN_EXAMING_RECORD_" + examRecordData.getId() + "]考试记录交卷完成.");
+        cleanExamRecordTaskLog.debug("[CLEAN_EXAMING_RECORD_" + examingRecord.getExamRecordDataId() + "]考试记录交卷完成.");
     }
 
     @Override
@@ -1465,7 +1361,7 @@ public class ExamControlServiceImpl implements ExamControlService {
         try {
             this.sendScanQrCodeToWebSocket(clientId, Long.valueOf(examRecordDataId), Integer.valueOf(order));
         } catch (Exception e) {
-        	throw new StatusException("100011", "消息通知失败", e);
+            throw new StatusException("100011", "消息通知失败", e);
         }
         if (log.isDebugEnabled()) {
             log.debug("4 发websocket耗时:" + (System.currentTimeMillis() - startTime) + " ms");

+ 142 - 145
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamFaceLivenessVerifyServiceImpl.java

@@ -57,20 +57,20 @@ import org.springframework.stereotype.Service;
 @Service("examFaceLivenessVerifyService")
 public class ExamFaceLivenessVerifyServiceImpl implements ExamFaceLivenessVerifyService {
 
-	private static final Logger log = LoggerFactory.getLogger(ExamFaceLivenessVerifyServiceImpl.class);
-	
+    private static final Logger log = LoggerFactory.getLogger(ExamFaceLivenessVerifyServiceImpl.class);
+
     @Autowired
     private ExamFaceLivenessVerifyRepo examFaceLivenessVerifyRepo;
-    
+
     @Autowired
     private ExamRecordDataService examRecordDataService;
-    
+
     @Autowired
     private ExamRecordDataRepo examRecordDataRepo;
-    
+
     @Autowired
     private ExamSessionInfoService examSessionInfoService;
-    
+
     @Autowired
     private GainBaseDataService gainBaseDataService;
 
@@ -96,7 +96,7 @@ public class ExamFaceLivenessVerifyServiceImpl implements ExamFaceLivenessVerify
 
     @Override
     public ExamFaceLivenessVerifyEntity saveFaceVerify(Long examRecordDataId) {
-    	ExamFaceLivenessVerifyEntity faceVerify = new ExamFaceLivenessVerifyEntity();
+        ExamFaceLivenessVerifyEntity faceVerify = new ExamFaceLivenessVerifyEntity();
         faceVerify.setExamRecordDataId(examRecordDataId);
         faceVerify.setStartTime(new Date());
         faceVerify.setIsError(false);
@@ -105,61 +105,61 @@ public class ExamFaceLivenessVerifyServiceImpl implements ExamFaceLivenessVerify
     }
 
     @Override
-    public List<ExamFaceLivenessVerifyEntity> listFaceVerifyByExamRecordId(Long examRecordDataId){
-        if(examRecordDataId == null){
+    public List<ExamFaceLivenessVerifyEntity> listFaceVerifyByExamRecordId(Long examRecordDataId) {
+        if (examRecordDataId == null) {
             return new ArrayList<ExamFaceLivenessVerifyEntity>();
         }
         return examFaceLivenessVerifyRepo.findByExamRecordDataIdOrderById(examRecordDataId);
     }
 
     @Override
-    public ExamFaceLivenessVerifyEntity faceIdNotify(String faceIdResultJson){
+    public ExamFaceLivenessVerifyEntity faceIdNotify(String faceIdResultJson) {
         JSONObject faceIdResultJsonObject;
         Long faceVerifyId;
         ExamFaceLivenessVerifyEntity faceVerify = new ExamFaceLivenessVerifyEntity();
-        try{
-        	faceIdResultJsonObject = new JSONObject(faceIdResultJson);
-        	faceVerifyId = faceIdResultJsonObject.getLong("biz_no");
+        try {
+            faceIdResultJsonObject = new JSONObject(faceIdResultJson);
+            faceVerifyId = faceIdResultJsonObject.getLong("biz_no");
             faceVerify = findFaceVerifyById(faceVerifyId);
-	        if(faceIdResultJsonObject.has("verify_result") 
-	        		&& faceIdResultJsonObject.get("verify_result") != null
-	        			&& !"null".equals(faceIdResultJsonObject.get("verify_result")+"")){
-	    		JSONObject verifyResultJson = faceIdResultJsonObject.getJSONObject("verify_result");
-	        	JSONObject result_ref1 = verifyResultJson.getJSONObject("result_ref1");
-	            double confidence = result_ref1.getDouble("confidence");
-	            //人脸相似度
-	            if(confidence > 50D){
-	            	JSONObject livenessResultJson = faceIdResultJsonObject.getJSONObject("liveness_result");
-	            	if(livenessResultJson.has("result") && "success".equals(livenessResultJson.getString("result"))){
-	            		faceVerify.setVerifyResult(FaceVerifyResult.VERIFY_SUCCESS);
-	            	}
-	            }else{
-	                faceVerify.setVerifyResult(FaceVerifyResult.NOT_ONESELF);
-	            }
-	        }else{
-	            faceVerify.setVerifyResult(FaceVerifyResult.VERIFY_FAILED);
-	        }
-	        faceVerify.setResultJson(faceIdResultJsonObject.toString());
-	        faceVerify.setBizId(faceIdResultJsonObject.getString("biz_id"));
-        }catch(Exception e){
-    		log.error("faceIdNotify error",e);
-    		faceVerify.setVerifyResult(FaceVerifyResult.VERIFY_FAILED);
-    	}
-        long usedTime = System.currentTimeMillis()-faceVerify.getStartTime().getTime();
+            if (faceIdResultJsonObject.has("verify_result")
+                    && faceIdResultJsonObject.get("verify_result") != null
+                    && !"null".equals(faceIdResultJsonObject.get("verify_result") + "")) {
+                JSONObject verifyResultJson = faceIdResultJsonObject.getJSONObject("verify_result");
+                JSONObject result_ref1 = verifyResultJson.getJSONObject("result_ref1");
+                double confidence = result_ref1.getDouble("confidence");
+                //人脸相似度
+                if (confidence > 50D) {
+                    JSONObject livenessResultJson = faceIdResultJsonObject.getJSONObject("liveness_result");
+                    if (livenessResultJson.has("result") && "success".equals(livenessResultJson.getString("result"))) {
+                        faceVerify.setVerifyResult(FaceVerifyResult.VERIFY_SUCCESS);
+                    }
+                } else {
+                    faceVerify.setVerifyResult(FaceVerifyResult.NOT_ONESELF);
+                }
+            } else {
+                faceVerify.setVerifyResult(FaceVerifyResult.VERIFY_FAILED);
+            }
+            faceVerify.setResultJson(faceIdResultJsonObject.toString());
+            faceVerify.setBizId(faceIdResultJsonObject.getString("biz_id"));
+        } catch (Exception e) {
+            log.error("faceIdNotify error", e);
+            faceVerify.setVerifyResult(FaceVerifyResult.VERIFY_FAILED);
+        }
+        long usedTime = System.currentTimeMillis() - faceVerify.getStartTime().getTime();
         faceVerify.setUsedTime(usedTime);
         return examFaceLivenessVerifyRepo.save(faceVerify);
     }
 
     @Override
-    public GetFaceVerifyTokenInfo getFaceVerifyToken(Long studentId,String bizNo){
-    	GetFaceVerifyTokenInfo getFaceVerifyTokenInfo = new GetFaceVerifyTokenInfo();
-    	CloseableHttpResponse httpResponse = null;
-    	CloseableHttpClient httpClient = null;
-        try{
-        	httpClient = HttpPoolUtil.getHttpClient();
+    public GetFaceVerifyTokenInfo getFaceVerifyToken(Long studentId, String bizNo) {
+        GetFaceVerifyTokenInfo getFaceVerifyTokenInfo = new GetFaceVerifyTokenInfo();
+        CloseableHttpResponse httpResponse = null;
+        CloseableHttpClient httpClient = null;
+        try {
+            httpClient = HttpPoolUtil.getHttpClient();
             HttpPost httpPost = new HttpPost(faceIdGetTokenUrl);
             File basePhotoFile = getStudentBasePhotoFile(studentId);
-            MultipartEntityBuilder multipartEntityBuilder = getMultipartEntityBuilder(bizNo,basePhotoFile);
+            MultipartEntityBuilder multipartEntityBuilder = getMultipartEntityBuilder(bizNo, basePhotoFile);
             HttpEntity httpEntity = multipartEntityBuilder.build();
             httpPost.setEntity(httpEntity);
             httpResponse = httpClient.execute(httpPost);
@@ -167,36 +167,36 @@ public class ExamFaceLivenessVerifyServiceImpl implements ExamFaceLivenessVerify
             BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
             StringBuffer buffer = new StringBuffer();
             String str = "";
-            while(StringUtils.isNoneBlank((str = reader.readLine()))) {
+            while (StringUtils.isNoneBlank((str = reader.readLine()))) {
                 buffer.append(str);
             }
             String result = buffer.toString();
             basePhotoFile.delete();
             JSONObject jsonObject = new JSONObject(result);
-            if(jsonObject.has("error_message")){
-            	getFaceVerifyTokenInfo.setSuccess(false);
-            	getFaceVerifyTokenInfo.setErrorMsg(jsonObject.getString("error_message"));
-            	log.error("getFaceVerifyToken error",result);
-            }else if(jsonObject.has("error")){
-            	getFaceVerifyTokenInfo.setSuccess(false);
-            	getFaceVerifyTokenInfo.setErrorMsg(jsonObject.getString("error"));
-            	log.error("getFaceVerifyToken error",result);
-            }else if(jsonObject.has("token")){
-            	getFaceVerifyTokenInfo.setSuccess(true);
-            	getFaceVerifyTokenInfo.setFaceLivenessToken(jsonObject.getString("token"));
+            if (jsonObject.has("error_message")) {
+                getFaceVerifyTokenInfo.setSuccess(false);
+                getFaceVerifyTokenInfo.setErrorMsg(jsonObject.getString("error_message"));
+                log.error("getFaceVerifyToken error", result);
+            } else if (jsonObject.has("error")) {
+                getFaceVerifyTokenInfo.setSuccess(false);
+                getFaceVerifyTokenInfo.setErrorMsg(jsonObject.getString("error"));
+                log.error("getFaceVerifyToken error", result);
+            } else if (jsonObject.has("token")) {
+                getFaceVerifyTokenInfo.setSuccess(true);
+                getFaceVerifyTokenInfo.setFaceLivenessToken(jsonObject.getString("token"));
             }
             httpPost.abort();
-        }catch(Exception e){
-        	log.error("getFaceVerifyToken error",e);
-        	getFaceVerifyTokenInfo.setSuccess(false);
-        	getFaceVerifyTokenInfo.setErrorMsg(e.getMessage());
-        }finally{
-            if(httpResponse!=null){
+        } catch (Exception e) {
+            log.error("getFaceVerifyToken error", e);
+            getFaceVerifyTokenInfo.setSuccess(false);
+            getFaceVerifyTokenInfo.setErrorMsg(e.getMessage());
+        } finally {
+            if (httpResponse != null) {
                 try {
-					httpResponse.close();
-				} catch (IOException e) {
-					e.printStackTrace();
-				}
+                    httpResponse.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
             }
         }
         return getFaceVerifyTokenInfo;
@@ -204,50 +204,46 @@ public class ExamFaceLivenessVerifyServiceImpl implements ExamFaceLivenessVerify
 
     @Override
     public ExamFaceLivenessVerifyEntity findFaceVerifyById(Long id) {
-        if(id == null){
+        if (id == null) {
             return null;
         }
-        return GlobalHelper.getEntity(examFaceLivenessVerifyRepo,id,ExamFaceLivenessVerifyEntity.class); 
+        return GlobalHelper.getEntity(examFaceLivenessVerifyRepo, id, ExamFaceLivenessVerifyEntity.class);
     }
 
     @Override
-    public void faceTestTimeOut(Long examRecordDataId){
+    public void faceTestTimeOut(Long examRecordDataId) {
         List<ExamFaceLivenessVerifyEntity> faceVerifies = examFaceLivenessVerifyRepo.findByExamRecordDataIdOrderById(examRecordDataId);
-        ExamFaceLivenessVerifyEntity faceVerify = faceVerifies.get(faceVerifies.size()-1);
-        if(faceVerify.getVerifyResult() == null){
-        	 faceVerify.setVerifyResult(FaceVerifyResult.TIME_OUT);
-             examFaceLivenessVerifyRepo.save(faceVerify);
+        ExamFaceLivenessVerifyEntity faceVerify = faceVerifies.get(faceVerifies.size() - 1);
+        if (faceVerify.getVerifyResult() == null) {
+            faceVerify.setVerifyResult(FaceVerifyResult.TIME_OUT);
+            examFaceLivenessVerifyRepo.save(faceVerify);
         }
     }
 
     @Override
-    public void faceTestEndHandle(Long examRecordDataId, String result) {
-        ExamRecordDataEntity examRecordDataEntity = GlobalHelper.getEntity(examRecordDataRepo,examRecordDataId,ExamRecordDataEntity.class);
-        if(IsSuccess.strToEnum(result) == IsSuccess.FAILED){
-
-            ExamRecordEntity examRecord =GlobalHelper.getEntity(
-                    examRecordRepo,examRecordDataEntity.getExamRecordId(),ExamRecordEntity.class);
-            examSessionInfoService.deleteExamSessionInfo(examRecord.getStudentId());
-            examRecordDataEntity.setFaceVerifyResult(IsSuccess.FAILED);
-
-            //尝试交卷
-            examControlService.handInExam(examRecordDataEntity,HandInExamType.AUTO);
-        }else{
-            examRecordDataEntity.setFaceVerifyResult(IsSuccess.SUCCESS);
-            examRecordDataRepo.save(examRecordDataEntity);
+    public void faceTestEndHandle(Long examRecordDataId,Long studentId, String result) {
+        //保存活体检测的结果
+        ExamRecordDataEntity examRecordDataEntity = GlobalHelper.getEntity(examRecordDataRepo, examRecordDataId, ExamRecordDataEntity.class);
+        examRecordDataEntity.setFaceVerifyResult(IsSuccess.strToEnum(result) == IsSuccess.FAILED ? IsSuccess.FAILED : IsSuccess.SUCCESS);
+        examRecordDataRepo.save(examRecordDataEntity);
+
+        //如果活体检失败,需要清除会话并自动交卷
+        if (IsSuccess.strToEnum(result) == IsSuccess.FAILED) {
+            examSessionInfoService.deleteExamSessionInfo(studentId);
+            examControlService.handInExam(examRecordDataId, HandInExamType.AUTO);
         }
     }
 
     @Override
     public ExamFaceLivenessVerifyEntity saveFaceVerifyByExamRecordDataId(Long examRecordDataId) {
-    	ExamFaceLivenessVerifyEntity examFaceVerifyEntity = examFaceLivenessVerifyRepo.findErrorFaceVerifyByExamRecordDataId(examRecordDataId);
-        if(examFaceVerifyEntity !=null){
+        ExamFaceLivenessVerifyEntity examFaceVerifyEntity = examFaceLivenessVerifyRepo.findErrorFaceVerifyByExamRecordDataId(examRecordDataId);
+        if (examFaceVerifyEntity != null) {
             examFaceVerifyEntity.setStartTime(new Date());
             examFaceVerifyEntity.setIsError(false);
             examFaceVerifyEntity.setErrorMsg(null);
-            examFaceVerifyEntity.setOperateNum(examFaceVerifyEntity.getOperateNum().intValue()+1);
+            examFaceVerifyEntity.setOperateNum(examFaceVerifyEntity.getOperateNum().intValue() + 1);
             return examFaceLivenessVerifyRepo.save(examFaceVerifyEntity);
-        }else{
+        } else {
             examFaceVerifyEntity = saveFaceVerify(examRecordDataId);
         }
         return examFaceVerifyEntity;
@@ -255,78 +251,79 @@ public class ExamFaceLivenessVerifyServiceImpl implements ExamFaceLivenessVerify
 
     private MultipartEntityBuilder getMultipartEntityBuilder(String bizNo, File basePhotoFile) {
         SysPropertyCacheBean sysProperty = CacheHelper.getSysProperty("app.faceid.notify_url");
-        if (!sysProperty.getHasValue()){
-            throw new StatusException("200001","未找到活体检测回调地址的配置信息");
+        if (!sysProperty.getHasValue()) {
+            throw new StatusException("200001", "未找到活体检测回调地址的配置信息");
         }
-        String faceidNotifyUrl=sysProperty.getValue().toString();
+        String faceidNotifyUrl = sysProperty.getValue().toString();
         //详见:https://faceid.com/pages/documents/5680502
         MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
-        multipartEntityBuilder.addTextBody("api_key",faceIdApiKey);
-        multipartEntityBuilder.addTextBody("api_secret",faceIdApiSecret);
+        multipartEntityBuilder.addTextBody("api_key", faceIdApiKey);
+        multipartEntityBuilder.addTextBody("api_secret", faceIdApiSecret);
         multipartEntityBuilder.addTextBody("comparison_type", "0");
-        multipartEntityBuilder.addTextBody("return_url",faceidNotifyUrl);
-        multipartEntityBuilder.addTextBody("notify_url",faceidNotifyUrl);
-        multipartEntityBuilder.addTextBody("biz_no",bizNo);
-        multipartEntityBuilder.addTextBody("uuid",bizNo);
+        multipartEntityBuilder.addTextBody("return_url", faceidNotifyUrl);
+        multipartEntityBuilder.addTextBody("notify_url", faceidNotifyUrl);
+        multipartEntityBuilder.addTextBody("biz_no", bizNo);
+        multipartEntityBuilder.addTextBody("uuid", bizNo);
 
-        multipartEntityBuilder.addBinaryBody("image_ref1",basePhotoFile);
+        multipartEntityBuilder.addBinaryBody("image_ref1", basePhotoFile);
         return multipartEntityBuilder;
     }
 
     /**
      * 获取学生底照文件
+     *
      * @param studentId
      * @return
      */
-    private File getStudentBasePhotoFile(Long studentId){
+    private File getStudentBasePhotoFile(Long studentId) {
         StudentCacheBean studentBean = CacheHelper.getStudent(studentId);
         String photoUrl = studentBean.getPhotoPath();
-        String photoName = photoUrl.substring(photoUrl.lastIndexOf("/")+1,photoUrl.length());
-        FileDisposeUtil.saveUrlAs(photoUrl,photoName);
+        String photoName = photoUrl.substring(photoUrl.lastIndexOf("/") + 1, photoUrl.length());
+        FileDisposeUtil.saveUrlAs(photoUrl, photoName);
         return new File(photoName);
     }
 
-	@Override
-	public Integer getFaceLivenessVerifyMinute(Long orgId,Long examId,Long examRecordDataId, Integer heartbeat) {
-    	String isFaceVerifyStr = CacheHelper.getExamOrgProperty(examId,orgId,
+    @Override
+    public Integer getFaceLivenessVerifyMinute(Long orgId, Long examId, Long examRecordDataId, Integer heartbeat) {
+        String isFaceVerifyStr = CacheHelper.getExamOrgProperty(examId, orgId,
                 ExamProperties.IS_FACE_VERIFY.name()).getValue();
-    	//开启了人脸检测
-    	if(Constants.isTrue.equals(isFaceVerifyStr)){
-    		
-    		List<ExamFaceLivenessVerifyEntity> faceLivenessVerifys = listFaceVerifyByExamRecordId(examRecordDataId);
-    		//如果没有进行过人脸检测
-    		if(faceLivenessVerifys.size() == 0){
-    			
-    			String faceVerifyStartMinuteStr = CacheHelper.getExamOrgProperty(examId,orgId,
+        //开启了人脸检测
+        if (Constants.isTrue.equals(isFaceVerifyStr)) {
+
+            List<ExamFaceLivenessVerifyEntity> faceLivenessVerifys = listFaceVerifyByExamRecordId(examRecordDataId);
+            //如果没有进行过人脸检测
+            if (faceLivenessVerifys.size() == 0) {
+
+                String faceVerifyStartMinuteStr = CacheHelper.getExamOrgProperty(examId, orgId,
                         ExamProperties.FACE_VERIFY_START_MINUTE.name()).getValue();
-    			Integer faceVerifyStartMinute = Integer.valueOf(faceVerifyStartMinuteStr);
-    			
-    			String faceVerifyEndMinuteStr = CacheHelper.getExamOrgProperty(examId,orgId,
+                Integer faceVerifyStartMinute = Integer.valueOf(faceVerifyStartMinuteStr);
+
+                String faceVerifyEndMinuteStr = CacheHelper.getExamOrgProperty(examId, orgId,
                         ExamProperties.FACE_VERIFY_END_MINUTE.name()).getValue();
-    			Integer faceVerifyEndMinute = Integer.valueOf(faceVerifyEndMinuteStr);
-    			//	case1.如果考生已使用的考试时间(即心跳时间)还未达到系统设置的活体检测开始时间,则实际活体检测时间=random(配置结束时间-配置结束时间)-考试已用时间
-    			if(heartbeat<faceVerifyStartMinute){
-        			return CommonUtil.calculationRandomNumber(faceVerifyStartMinute, faceVerifyEndMinute)-heartbeat;
-        		}
-    			//	case2如果配置开始时间<考生已使用的考试时间<配置结束时间,则实际活体检测时间=random(配置结束时间-考试已用时间)-考试已用时间,如果结果小于1分钟则默认1分钟
-    			else if(heartbeat>=faceVerifyStartMinute&&heartbeat<faceVerifyEndMinute){
-        			int verifyTime = CommonUtil.calculationRandomNumber(heartbeat, faceVerifyEndMinute)-heartbeat;
-        			return verifyTime<1?1:verifyTime;
-        		}
-    			//case3如果考试已用时间>配置结束时间,则默认1分钟后开始人脸检测
-    			else if(heartbeat>=faceVerifyEndMinute){
-        			return CommonUtil.calculationRandomNumber(1, secondFaceCheckMinute);
-        		}
-    		}else if(faceLivenessVerifys.size()==1){
-    			//如果已经人脸检测过一次且未成功,再安排一次检测
-    			ExamFaceLivenessVerifyEntity faceVerify = faceLivenessVerifys.get(0);
-    			if(faceVerify.getVerifyResult()==null
-        				||faceVerify.getVerifyResult() != FaceVerifyResult.VERIFY_SUCCESS){
-        			return  CommonUtil.calculationRandomNumber(1, secondFaceCheckMinute);
-        		}
-    		}
-    	}
-    	
-		return null;
-	}
+                Integer faceVerifyEndMinute = Integer.valueOf(faceVerifyEndMinuteStr);
+                //	case1.如果考生已使用的考试时间(即心跳时间)还未达到系统设置的活体检测开始时间,则实际活体检测时间=random(配置结束时间-配置结束时间)-考试已用时间
+                if (heartbeat < faceVerifyStartMinute) {
+                    return CommonUtil.calculationRandomNumber(faceVerifyStartMinute, faceVerifyEndMinute) - heartbeat;
+                }
+                //	case2如果配置开始时间<考生已使用的考试时间<配置结束时间,则实际活体检测时间=random(配置结束时间-考试已用时间)-考试已用时间,如果结果小于1分钟则默认1分钟
+                else if (heartbeat >= faceVerifyStartMinute && heartbeat < faceVerifyEndMinute) {
+                    int verifyTime = CommonUtil.calculationRandomNumber(heartbeat, faceVerifyEndMinute) - heartbeat;
+                    return verifyTime < 1 ? 1 : verifyTime;
+                }
+                //case3如果考试已用时间>配置结束时间,则默认1分钟后开始人脸检测
+                else if (heartbeat >= faceVerifyEndMinute) {
+                    return CommonUtil.calculationRandomNumber(1, secondFaceCheckMinute);
+                }
+            } else if (faceLivenessVerifys.size() == 1) {
+                //如果已经人脸检测过一次且未成功,再安排一次检测
+                ExamFaceLivenessVerifyEntity faceVerify = faceLivenessVerifys.get(0);
+                if (faceVerify.getVerifyResult() == null
+                        || faceVerify.getVerifyResult() != FaceVerifyResult.VERIFY_SUCCESS) {
+                    return CommonUtil.calculationRandomNumber(1, secondFaceCheckMinute);
+                }
+            }
+        }
+
+        return null;
+    }
 }