Эх сурвалжийг харах

人脸活体检测功能完善

lideyin 5 жил өмнө
parent
commit
d9debc47e8

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

@@ -14,11 +14,10 @@ import cn.com.qmth.examcloud.core.oe.common.enums.ExamProperties;
 import cn.com.qmth.examcloud.core.oe.common.enums.HandInExamType;
 import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
 import cn.com.qmth.examcloud.core.oe.common.service.ExamScoreNoticeQueueService;
-import cn.com.qmth.examcloud.core.oe.common.service.GainBaseDataService;
 import cn.com.qmth.examcloud.core.oe.student.api.request.GetStudentOnlineExamInfoReq;
 import cn.com.qmth.examcloud.core.oe.student.api.response.GetStudentOnlineExamInfoResp;
 import cn.com.qmth.examcloud.core.oe.student.bean.*;
-import cn.com.qmth.examcloud.core.oe.student.controller.bean.ExamProcessResultDomain;
+import cn.com.qmth.examcloud.core.oe.student.bean.ExamProcessResultInfo;
 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;
@@ -154,9 +153,9 @@ public class ExamControlController extends ControllerSupport {
      */
     @ApiOperation(value = "交卷后续处理")
     @GetMapping("/processAfterEndExam")
-    public ExamProcessResultDomain processAfterEndExam(@RequestParam Long examRecordDataId) {
+    public ExamProcessResultInfo processAfterEndExam(@RequestParam Long examRecordDataId) {
         User user = getAccessUser();
-        ExamProcessResultDomain res = new ExamProcessResultDomain();
+        ExamProcessResultInfo res = new ExamProcessResultInfo();
         ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo, examRecordDataId,
                 ExamRecordDataEntity.class);
         try {
@@ -211,12 +210,12 @@ public class ExamControlController extends ControllerSupport {
      */
     @ApiOperation(value = "断点续考:检查正在进行中的考试")
     @GetMapping("/checkExamInProgress")
-    public ExamProcessResultDomain checkExamInProgress() {
+    public ExamProcessResultInfo checkExamInProgress() {
         User user = getAccessUser();
         String sequenceLockKey = Constants.START_EXAM_LOCK_PREFIX + user.getUserId();
         //系统在请求结束后会,自动释放锁,无需手动解锁
         SequenceLockHelper.getLock(sequenceLockKey);
-        ExamProcessResultDomain res = new ExamProcessResultDomain();
+        ExamProcessResultInfo res = new ExamProcessResultInfo();
         try {
             CheckExamInProgressInfo info = examControlService.checkExamInProgress(user.getUserId());
             res.setCode(Constants.COMMON_SUCCESS_CODE);

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

@@ -7,7 +7,7 @@ import cn.com.qmth.examcloud.core.oe.common.base.utils.Check;
 import cn.com.qmth.examcloud.core.oe.common.entity.ExamFaceLivenessVerifyEntity;
 import cn.com.qmth.examcloud.core.oe.common.repository.ExamFaceLivenessVerifyRepo;
 import cn.com.qmth.examcloud.core.oe.student.bean.GetFaceVerifyTokenInfo;
-import cn.com.qmth.examcloud.core.oe.student.controller.bean.ExamProcessResultDomain;
+import cn.com.qmth.examcloud.core.oe.student.bean.ExamProcessResultInfo;
 import cn.com.qmth.examcloud.core.oe.student.service.ExamFaceLivenessVerifyService;
 import cn.com.qmth.examcloud.core.oe.websocket.api.WebsocketCloudService;
 import cn.com.qmth.examcloud.core.oe.websocket.api.request.SendMessageReq;
@@ -158,8 +158,8 @@ public class ExamFaceLivenessVerifyController extends ControllerSupport {
      */
     @ApiOperation(value = "人脸检测结束处理")
     @GetMapping(value = "faceLivenessVerifyEnd/{examRecordDataId}")
-    public ExamProcessResultDomain faceTestEndHandle(@PathVariable Long examRecordDataId, @RequestParam String result) throws Exception {
-        ExamProcessResultDomain res = new ExamProcessResultDomain();
+    public ExamProcessResultInfo faceTestEndHandle(@PathVariable Long examRecordDataId, @RequestParam String result) throws Exception {
+        ExamProcessResultInfo res = new ExamProcessResultInfo();
         try {
             User user = getAccessUser();
             examFaceLivenessVerifyService.faceTestEndHandle(examRecordDataId, user.getUserId(), result);

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

@@ -1,33 +1,87 @@
 package cn.com.qmth.examcloud.core.oe.student.controller;
 
-import cn.com.qmth.examcloud.core.oe.student.controller.bean.FaceBiopsyDomain;
-import cn.com.qmth.examcloud.core.oe.student.controller.bean.SaveFaceBiopsyResultResp;
-import cn.com.qmth.examcloud.core.oe.student.controller.bean.SaveFaceBiopsyResultReq;
+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.common.base.Constants;
+import cn.com.qmth.examcloud.core.oe.common.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.FaceBiopsyEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.FaceBiopsyItemEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.FaceBiopsyItemStepEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.FaceBiopsyType;
+import cn.com.qmth.examcloud.core.oe.common.repository.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.FaceBiopsyItemRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.FaceBiopsyItemStepRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.FaceBiopsyRepo;
+import cn.com.qmth.examcloud.core.oe.student.bean.ExamSessionInfo;
+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.SaveFaceBiopsyResultResp;
+import cn.com.qmth.examcloud.core.oe.student.bean.SaveFaceBiopsyResultReq;
+import cn.com.qmth.examcloud.core.oe.student.service.ExamSessionInfoService;
+import cn.com.qmth.examcloud.core.oe.student.service.FaceBiopsyService;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.helpers.SequenceLockHelper;
 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.ArrayList;
+import java.util.List;
+
 /**
  * @Description 人脸活体检测接口
  * @Author lideyin
  * @Date 2019/10/14 10:59
  * @Version 1.0
  */
-@Api(tags="人脸活体检测")
+@Api(tags = "人脸活体检测")
 @RestController
 @RequestMapping("${app.api.oe.student}/faceBiopsy")
 public class FaceBiopsyController extends ControllerSupport {
+    @Autowired
+    private ExamSessionInfoService examSessionInfoService;
+    @Autowired
+    private FaceBiopsyService faceBiopsyService;
+    @Autowired
+    private FaceBiopsyRepo faceBiopsyRepo;
+    @Autowired
+    private FaceBiopsyItemRepo faceBiopsyItemRepo;
+    @Autowired
+    private FaceBiopsyItemStepRepo faceBiopsyItemStepRepo;
+    @Autowired
+    private ExamRecordDataRepo examRecordDataRepo;
+
     @ApiOperation(value = "获取人脸活体检测基本信息")
     @GetMapping("/getFaceBiopsyInfo")
-    public FaceBiopsyDomain getFaceBiopsyInfo(@RequestParam Long examRecordDataId){
-        FaceBiopsyDomain result = new FaceBiopsyDomain();
-        return result;
+    public FaceBiopsyInfo getFaceBiopsyInfo(@RequestParam Long examRecordDataId) {
+        User user = getAccessUser();
+        Long studentId = user.getUserId();
+        String sequenceLockKey = Constants.GET_FACE_BIOPSY_INFO_PREFIX + studentId;
+        //系统在请求结束后会,自动释放锁,无需手动解锁
+        SequenceLockHelper.getLock(sequenceLockKey);
+
+        //判断考试记录id是否有效
+        ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo, examRecordDataId,
+                ExamRecordDataEntity.class);
+        if (examRecordData == null) {
+            throw new StatusException("200101", "无效的考试记录");
+        }
+
+        // 获取考试会话,判断考生是否已结束考试
+        ExamSessionInfo examSessionInfo = examSessionInfoService.getExamSessionInfo(studentId);
+        if (examSessionInfo == null) {
+            throw new StatusException("200102", "考试会话已过期");
+        }
+
+        return faceBiopsyService.getFaceBiopsyInfo(user.getRootOrgId(), examRecordDataId, FaceBiopsyType.FACE_MOTION);
     }
 
+
     @ApiOperation(value = "保存活体检测结果")
     @PostMapping("/saveFaceBiopsyResult")
-    public SaveFaceBiopsyResultResp saveFaceBiopsyResult(@RequestBody SaveFaceBiopsyResultReq req){
+    public SaveFaceBiopsyResultResp saveFaceBiopsyResult(@RequestBody SaveFaceBiopsyResultReq req) {
         SaveFaceBiopsyResultResp result = new SaveFaceBiopsyResultResp();
         return result;
     }

+ 2 - 2
examcloud-core-oe-student-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/student/controller/bean/ExamProcessResultDomain.java → examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/ExamProcessResultInfo.java

@@ -1,4 +1,4 @@
-package cn.com.qmth.examcloud.core.oe.student.controller.bean;
+package cn.com.qmth.examcloud.core.oe.student.bean;
 
 import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
 
@@ -8,7 +8,7 @@ import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
  * @Date 2019/8/9 19:49
  * @Version 1.0
  */
-public class ExamProcessResultDomain implements JsonSerializable {
+public class ExamProcessResultInfo implements JsonSerializable {
     private String code;
 
     private Object data;

+ 5 - 5
examcloud-core-oe-student-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/student/controller/bean/FaceBiopsyDomain.java → examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/FaceBiopsyInfo.java

@@ -1,4 +1,4 @@
-package cn.com.qmth.examcloud.core.oe.student.controller.bean;
+package cn.com.qmth.examcloud.core.oe.student.bean;
 
 import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
 import io.swagger.annotations.ApiModelProperty;
@@ -12,7 +12,7 @@ import java.util.List;
  * @Date 2019/10/14 11:21
  * @Version 1.0
  */
-public class FaceBiopsyDomain implements JsonSerializable {
+public class FaceBiopsyInfo implements JsonSerializable {
 	private static final long serialVersionUID = -8323306595159483818L;
 
 	@ApiModelProperty(value = "活体检测开始时间",required = true)
@@ -22,7 +22,7 @@ public class FaceBiopsyDomain implements JsonSerializable {
 	private Long faceBiopsyItemId;
 
 	@ApiModelProperty(value = "人脸活体检测步骤",required = true)
-	private List<FaceBiopsyStepDomain>  verifySteps;
+	private List<FaceBiopsyStepInfo>  verifySteps;
 
 	public Date getStartTime() {
 		return startTime;
@@ -40,11 +40,11 @@ public class FaceBiopsyDomain implements JsonSerializable {
 		this.faceBiopsyItemId = faceBiopsyItemId;
 	}
 
-	public List<FaceBiopsyStepDomain> getVerifySteps() {
+	public List<FaceBiopsyStepInfo> getVerifySteps() {
 		return verifySteps;
 	}
 
-	public void setVerifySteps(List<FaceBiopsyStepDomain> verifySteps) {
+	public void setVerifySteps(List<FaceBiopsyStepInfo> verifySteps) {
 		this.verifySteps = verifySteps;
 	}
 }

+ 2 - 2
examcloud-core-oe-student-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/student/controller/bean/FaceBiopsyStepDomain.java → examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/FaceBiopsyStepInfo.java

@@ -1,4 +1,4 @@
-package cn.com.qmth.examcloud.core.oe.student.controller.bean;
+package cn.com.qmth.examcloud.core.oe.student.bean;
 
 import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
 import cn.com.qmth.examcloud.core.oe.common.enums.FaceBiopsyAction;
@@ -14,7 +14,7 @@ import java.util.Date;
  * @Date 2019/10/14 11:21
  * @Version 1.0
  */
-public class FaceBiopsyStepDomain implements JsonSerializable {
+public class FaceBiopsyStepInfo implements JsonSerializable {
 	private static final long serialVersionUID = -2108253508943543996L;
 
 	@ApiModelProperty(value = "步骤id",required = true)

+ 4 - 4
examcloud-core-oe-student-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/student/controller/bean/SaveFaceBiopsyResultReq.java → examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/SaveFaceBiopsyResultReq.java

@@ -1,4 +1,4 @@
-package cn.com.qmth.examcloud.core.oe.student.controller.bean;
+package cn.com.qmth.examcloud.core.oe.student.bean;
 
 import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
 import com.sun.tools.javac.util.List;
@@ -20,7 +20,7 @@ public class SaveFaceBiopsyResultReq implements JsonSerializable {
 	private Long faceBiopsyItemId;
 
 	@ApiModelProperty(value = "实际活体检测步骤",required = true)
-	private List<FaceBiopsyStepDomain> verifySteps;
+	private List<FaceBiopsyStepInfo> verifySteps;
 
 	public Long getExamRecordDataId() {
 		return examRecordDataId;
@@ -38,11 +38,11 @@ public class SaveFaceBiopsyResultReq implements JsonSerializable {
 		this.faceBiopsyItemId = faceBiopsyItemId;
 	}
 
-	public List<FaceBiopsyStepDomain> getVerifySteps() {
+	public List<FaceBiopsyStepInfo> getVerifySteps() {
 		return verifySteps;
 	}
 
-	public void setVerifySteps(List<FaceBiopsyStepDomain> verifySteps) {
+	public void setVerifySteps(List<FaceBiopsyStepInfo> verifySteps) {
 		this.verifySteps = verifySteps;
 	}
 }

+ 1 - 1
examcloud-core-oe-student-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/student/controller/bean/SaveFaceBiopsyResultResp.java → examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/SaveFaceBiopsyResultResp.java

@@ -1,4 +1,4 @@
-package cn.com.qmth.examcloud.core.oe.student.controller.bean;
+package cn.com.qmth.examcloud.core.oe.student.bean;
 
 import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
 import io.swagger.annotations.ApiModelProperty;

+ 13 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/FaceBiopsyService.java

@@ -1,5 +1,10 @@
 package cn.com.qmth.examcloud.core.oe.student.service;
 
+import cn.com.qmth.examcloud.core.oe.common.enums.FaceBiopsyType;
+import cn.com.qmth.examcloud.core.oe.student.bean.FaceBiopsyInfo;
+
+import java.util.Date;
+
 /**
  * @Description 人脸活体检测接口
  * @Author lideyin
@@ -8,4 +13,12 @@ package cn.com.qmth.examcloud.core.oe.student.service;
  */
 public interface FaceBiopsyService {
 
+    /**
+     * 获取人脸活体检测基本信息
+     *
+     * @param rootOrgId        学校id
+     * @param examRecordDataId 考试记录id
+     */
+    FaceBiopsyInfo getFaceBiopsyInfo(Long rootOrgId, Long examRecordDataId,FaceBiopsyType faceBiopsyType);
+
 }

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

@@ -1,7 +1,29 @@
 package cn.com.qmth.examcloud.core.oe.student.service.impl;
 
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.oe.common.entity.FaceBiopsyEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.FaceBiopsyItemEntity;
+import cn.com.qmth.examcloud.core.oe.common.entity.FaceBiopsyItemStepEntity;
+import cn.com.qmth.examcloud.core.oe.common.enums.FaceBiopsyAction;
+import cn.com.qmth.examcloud.core.oe.common.enums.FaceBiopsyType;
+import cn.com.qmth.examcloud.core.oe.common.repository.FaceBiopsyItemRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.FaceBiopsyItemStepRepo;
+import cn.com.qmth.examcloud.core.oe.common.repository.FaceBiopsyRepo;
+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.service.FaceBiopsyService;
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.List;
+
+import java.util.ArrayList;
+import java.util.Random;
 
 /**
  * @Description 人脸活体检测接口实现类
@@ -12,4 +34,206 @@ import org.springframework.stereotype.Service;
 @Service("faceBiopsyService")
 public class FaceBiopsyServiceImpl implements FaceBiopsyService {
 
+    @Autowired
+    private FaceBiopsyRepo faceBiopsyRepo;
+    @Autowired
+    private FaceBiopsyItemRepo faceBiopsyItemRepo;
+    @Autowired
+    private FaceBiopsyItemStepRepo faceBiopsyItemStepRepo;
+
+    @Override
+    public FaceBiopsyInfo getFaceBiopsyInfo(Long rootOrgId, Long examRecordDataId, FaceBiopsyType faceBiopsyType) {
+        //如果是第一次进行人脸活体检测,则初始化相关信息保存并返回
+        FaceBiopsyEntity faceBiopsyEntity = faceBiopsyRepo.findByExamRecordDataId(examRecordDataId);
+        if (faceBiopsyEntity == null) {
+            return addFirstFaceBiopsy(rootOrgId, examRecordDataId, faceBiopsyType);
+        }
+
+        //如果不是第一次人脸活体检测,判断是否有未检测完的记录,
+        FaceBiopsyInfo result = new FaceBiopsyInfo();
+        // 未完成的活体检测列表
+        List<FaceBiopsyItemEntity> unCompletedFaceBiopsyItemList =
+                faceBiopsyItemRepo.findByExamRecordDataIdAndCompleted(examRecordDataId, false);
+
+        //如果存在未完成的活体检测信息,则直接返回上次未完成的活检记录
+        if (unCompletedFaceBiopsyItemList != null && !unCompletedFaceBiopsyItemList.isEmpty()) {
+            FaceBiopsyItemEntity faceBiopsyItemEntity = unCompletedFaceBiopsyItemList.get(0);
+            result.setFaceBiopsyItemId(faceBiopsyItemEntity.getId());
+            result.setStartTime(calculateFaceBiopsyStartTime(examRecordDataId));
+
+            List<FaceBiopsyItemStepEntity> faceBiopsyItemStepEntityList =
+                    faceBiopsyItemStepRepo.findByFaceBiopsyItemId(faceBiopsyItemEntity.getId());
+            result.setVerifySteps(copyFaceBiopsyStepDomainListFrom(faceBiopsyItemStepEntityList));
+            return result;
+        }
+
+        // 如果不存在未完成的活体检测信息,则判断活检次数是否超过最大活检次数
+        int verifiedTimes = faceBiopsyEntity.getVerifiedTimes();//已活检次数
+        //最大活体检测次数
+        int maxVerifyTimes = PropertyHolder.getInt("oe.faceBiopsy.maxVerifyTimes", 3);
+        //如果超过则提示超过最大活检次数,不允许活检
+        if (verifiedTimes >= maxVerifyTimes) {
+            throw new StatusException("201001", "本次考试已超过最大允许活检次数:" + maxVerifyTimes + ".不允许继续活检");
+        }
+
+        //没有超过,则追加活检次数
+        verifiedTimes++;
+        return appendFirstFaceBiopsy(faceBiopsyEntity.getId(), verifiedTimes, examRecordDataId, faceBiopsyType);
+    }
+
+    /**
+     * 第一次添加人脸活体检测结果
+     *
+     * @param rootOrgId
+     * @param examRecordDataId
+     * @param faceBiopsyType
+     * @return
+     */
+    @Transactional
+    public FaceBiopsyInfo addFirstFaceBiopsy(Long rootOrgId, Long examRecordDataId, FaceBiopsyType faceBiopsyType) {
+        //保存人脸活体检测相关信息
+        Long faceBiopsyId = addFaceBiopsyEntity(rootOrgId, examRecordDataId);
+        Long faceBiopsyItemId = addFaceBiopsyItemEntity(examRecordDataId, faceBiopsyType, faceBiopsyId);
+        List<FaceBiopsyItemStepEntity> faceBiopsyItemStepEntityList =
+                addFaceBiopsyItemStepList(examRecordDataId, faceBiopsyItemId);
+
+        //构建业务实体
+        return buildFaceBiopsyInfo(examRecordDataId, faceBiopsyItemId, faceBiopsyItemStepEntityList);
+    }
+
+    /**
+     * 追加人脸活体检测结果
+     *
+     * @param faceBiopsyId
+     * @param verifiedTimes
+     * @param examRecordDataId
+     * @param faceBiopsyType
+     * @return
+     */
+    @Transactional
+    public FaceBiopsyInfo appendFirstFaceBiopsy(Long faceBiopsyId, Integer verifiedTimes, Long examRecordDataId,
+                                                FaceBiopsyType faceBiopsyType) {
+        //更新人脸活体检测次数
+        FaceBiopsyEntity faceBiopsyEntity = GlobalHelper.getEntity(faceBiopsyRepo, faceBiopsyId, FaceBiopsyEntity.class);
+        faceBiopsyEntity.setVerifiedTimes(verifiedTimes);
+        faceBiopsyRepo.save(faceBiopsyEntity);
+        //添加人脸活体检测明细
+        Long faceBiopsyItemId = addFaceBiopsyItemEntity(examRecordDataId, faceBiopsyType, faceBiopsyId);
+        //添加人脸活体检测步骤
+        List<FaceBiopsyItemStepEntity> faceBiopsyItemStepEntityList =
+                addFaceBiopsyItemStepList(examRecordDataId, faceBiopsyItemId);
+
+        return buildFaceBiopsyInfo(examRecordDataId, faceBiopsyItemId, faceBiopsyItemStepEntityList);
+
+    }
+
+    /**
+     * 构建人脸检测基本信息的业务实体对象
+     *
+     * @param examRecordDataId
+     * @param faceBiopsyItemId
+     * @param faceBiopsyItemStepEntityList
+     * @return
+     */
+    private FaceBiopsyInfo buildFaceBiopsyInfo(Long examRecordDataId, Long faceBiopsyItemId, List<FaceBiopsyItemStepEntity> faceBiopsyItemStepEntityList) {
+        //构建业务实体
+        FaceBiopsyInfo faceBiopsyInfo = new FaceBiopsyInfo();
+        faceBiopsyInfo.setFaceBiopsyItemId(faceBiopsyItemId);
+        faceBiopsyInfo.setStartTime(calculateFaceBiopsyStartTime(examRecordDataId));
+        faceBiopsyInfo.setVerifySteps(copyFaceBiopsyStepDomainListFrom(faceBiopsyItemStepEntityList));
+        return faceBiopsyInfo;
+    }
+
+    private List<FaceBiopsyStepInfo> copyFaceBiopsyStepDomainListFrom(List<FaceBiopsyItemStepEntity> faceBiopsyItemStepEntityList) {
+        List<FaceBiopsyStepInfo> resultList = new ArrayList<>();
+        for (FaceBiopsyItemStepEntity entity : faceBiopsyItemStepEntityList) {
+            FaceBiopsyStepInfo domain = new FaceBiopsyStepInfo();
+            domain.setAction(entity.getAction());
+            domain.setStay(entity.getActionStay());
+            domain.setStepId(entity.getId());
+            resultList.add(domain);
+        }
+        return resultList;
+    }
+
+    /**
+     * 添加活体检测的具体步骤
+     *
+     * @param examRecordDataId 考试记录id
+     * @param faceBiopsyItemId 人脸活体检测明细id
+     */
+    private List<FaceBiopsyItemStepEntity> addFaceBiopsyItemStepList(Long examRecordDataId, Long faceBiopsyItemId) {
+        List<FaceBiopsyItemStepEntity> faceBiopsyItemStepEntityList = new ArrayList<FaceBiopsyItemStepEntity>();
+        FaceBiopsyItemStepEntity firstStep = new FaceBiopsyItemStepEntity();
+        firstStep.setExamRecordDataId(examRecordDataId);
+        firstStep.setFaceBiopsyItemId(faceBiopsyItemId);
+        firstStep.setAction(FaceBiopsyAction.FACE_COMPARE);
+        faceBiopsyItemStepEntityList.add(firstStep);
+
+        FaceBiopsyItemStepEntity secondStep = new FaceBiopsyItemStepEntity();
+        secondStep.setExamRecordDataId(examRecordDataId);
+        secondStep.setFaceBiopsyItemId(faceBiopsyItemId);
+        secondStep.setAction(FaceBiopsyAction.HAPPY);
+        secondStep.setActionStay(randomGenerateActionStay());//随机生成指令时长(3至6秒)
+        faceBiopsyItemStepEntityList.add(secondStep);
+
+        faceBiopsyItemStepRepo.saveAll(faceBiopsyItemStepEntityList);
+        return faceBiopsyItemStepEntityList;
+    }
+
+    /**
+     * 随机生成指令持续的时长
+     *
+     * @return
+     */
+    private Integer randomGenerateActionStay() {
+        int minStay = PropertyHolder.getInt("oe.faceBiopsy.minActionStay", 3);
+        int maxStay = PropertyHolder.getInt("oe.faceBiopsy.maxActionStay", 6);
+        return RandomUtils.nextInt(minStay, maxStay);
+    }
+
+    /**
+     * 添加人脸活体检测明细
+     *
+     * @param examRecordDataId 考试记录id
+     * @param faceBiopsyType   人脸活体检测类型
+     * @param faceBiopsyId     人脸活体检测id
+     * @return Long 人脸活体检测明细id
+     */
+    private Long addFaceBiopsyItemEntity(Long examRecordDataId, FaceBiopsyType faceBiopsyType, Long faceBiopsyId) {
+        FaceBiopsyItemEntity faceBiopsyItemEntity = new FaceBiopsyItemEntity();
+        faceBiopsyItemEntity.setExamRecordDataId(examRecordDataId);
+        faceBiopsyItemEntity.setFaceBiopsyId(faceBiopsyId);
+        faceBiopsyItemEntity.setFaceBiopsyType(faceBiopsyType);
+        faceBiopsyItemRepo.save(faceBiopsyItemEntity);
+        return faceBiopsyItemEntity.getId();
+    }
+
+    /**
+     * 添加人脸活体检测结果
+     *
+     * @param rootOrgId        学校id
+     * @param examRecordDataId 考试记录id
+     * @return Long 人脸活体检测结果id
+     */
+    private Long addFaceBiopsyEntity(Long rootOrgId, Long examRecordDataId) {
+        FaceBiopsyEntity faceBiopsyEntity = new FaceBiopsyEntity();
+        faceBiopsyEntity.setRootOrgId(rootOrgId);
+        faceBiopsyEntity.setExamRecordDataId(examRecordDataId);
+        faceBiopsyEntity.setVerifiedTimes(1);
+        faceBiopsyRepo.save(faceBiopsyEntity);
+        return faceBiopsyEntity.getId();
+    }
+
+
+    /**
+     * 计算人脸活体检测开始时间
+     *
+     * @param examRecordDataId 考试记录id
+     * @return
+     */
+    private Date calculateFaceBiopsyStartTime(Long examRecordDataId) {
+        //TODO
+        return null;
+    }
 }