소스 검색

update 百度人脸API离线版和在线版支持随时切换。

deason 1 년 전
부모
커밋
5464eb3dda

+ 3 - 1
examcloud-core-oe-task-service/src/main/java/cn/com/qmth/examcloud/core/oe/task/service/ExamCaptureQueueService.java

@@ -1,10 +1,12 @@
 package cn.com.qmth.examcloud.core.oe.task.service;
 
+import cn.com.qmth.examcloud.core.oe.task.service.bean.FaceApiParam;
+
 /**
  * 处理考试抓拍队列
  */
 public interface ExamCaptureQueueService {
 
-    void handlerExamCaptureQueuesByExamRecordDataId(Long examRecordDataId) throws Exception;
+    void handlerExamCaptureQueuesByExamRecordDataId(Long examRecordDataId, FaceApiParam param) throws Exception;
 
 }

+ 48 - 0
examcloud-core-oe-task-service/src/main/java/cn/com/qmth/examcloud/core/oe/task/service/bean/FaceApiParam.java

@@ -0,0 +1,48 @@
+package cn.com.qmth.examcloud.core.oe.task.service.bean;
+
+import java.io.Serializable;
+
+public class FaceApiParam implements Serializable {
+
+    private static final long serialVersionUID = 2983309896884898091L;
+
+    /**
+     * 人脸比对 - 使用百度API还是Face++API【默认:true】
+     */
+    private boolean useBaiduApi = true;
+
+    /**
+     * 百度人脸比对 - 使用离线版还是在线版【默认:true】
+     */
+    private boolean useLocalBaiduApiForFaceCompare = true;
+
+    /**
+     * 百度真实性检测 - 使用离线版还是在线版【默认:false】
+     */
+    private boolean useLocalBaiduApiForFaceLiveness = false;
+
+    public boolean isUseBaiduApi() {
+        return useBaiduApi;
+    }
+
+    public void setUseBaiduApi(boolean useBaiduApi) {
+        this.useBaiduApi = useBaiduApi;
+    }
+
+    public boolean isUseLocalBaiduApiForFaceCompare() {
+        return useLocalBaiduApiForFaceCompare;
+    }
+
+    public void setUseLocalBaiduApiForFaceCompare(boolean useLocalBaiduApiForFaceCompare) {
+        this.useLocalBaiduApiForFaceCompare = useLocalBaiduApiForFaceCompare;
+    }
+
+    public boolean isUseLocalBaiduApiForFaceLiveness() {
+        return useLocalBaiduApiForFaceLiveness;
+    }
+
+    public void setUseLocalBaiduApiForFaceLiveness(boolean useLocalBaiduApiForFaceLiveness) {
+        this.useLocalBaiduApiForFaceLiveness = useLocalBaiduApiForFaceLiveness;
+    }
+
+}

+ 30 - 19
examcloud-core-oe-task-service/src/main/java/cn/com/qmth/examcloud/core/oe/task/service/impl/ExamCaptureQueueServiceImpl.java

@@ -8,6 +8,7 @@ import cn.com.qmth.examcloud.core.oe.student.dao.entity.ExamCaptureQueueEntity;
 import cn.com.qmth.examcloud.core.oe.student.dao.enums.ExamCaptureQueueStatus;
 import cn.com.qmth.examcloud.core.oe.task.service.ExamCaptureQueueService;
 import cn.com.qmth.examcloud.core.oe.task.service.ExamRecordDataService;
+import cn.com.qmth.examcloud.core.oe.task.service.bean.FaceApiParam;
 import cn.com.qmth.examcloud.starters.face.verify.common.CommonUtils;
 import cn.com.qmth.examcloud.starters.face.verify.common.FaceVerifyException;
 import cn.com.qmth.examcloud.starters.face.verify.model.FaceResult;
@@ -60,7 +61,7 @@ public class ExamCaptureQueueServiceImpl implements ExamCaptureQueueService {
     private OssClient ossClient;
 
     @Override
-    public void handlerExamCaptureQueuesByExamRecordDataId(Long examRecordDataId) throws Exception {
+    public void handlerExamCaptureQueuesByExamRecordDataId(Long examRecordDataId, FaceApiParam param) throws Exception {
         // 每个考试记录ID下通常只有几条待处理抓拍记录,直接取所有一次性处理。
         List<ExamCaptureQueueEntity> queues = examCaptureQueueRepo.findByExamRecordDataId(examRecordDataId);
         if (CollectionUtils.isEmpty(queues)) {
@@ -77,17 +78,21 @@ public class ExamCaptureQueueServiceImpl implements ExamCaptureQueueService {
         // 获取学校人脸API方案配置
         boolean useBaiduApi = false;
         Double baiduExpectFaceCompareScore = null;
-        OrgPropertyCacheBean property1 = CacheHelper.getOrgProperty(studentCache.getRootOrgId(), Constants.FACE_COMPARE_API_PROVIDER);
+        OrgPropertyCacheBean property1 = CacheHelper.getOrgProperty(studentCache.getRootOrgId(),
+                Constants.FACE_COMPARE_API_PROVIDER);
         if (property1.getHasValue() && FaceApiProvider.BAIDU.name().equals(property1.getValue())) {
             useBaiduApi = true;
         }
-        OrgPropertyCacheBean property2 = CacheHelper.getOrgProperty(studentCache.getRootOrgId(), Constants.BAIDU_EXPECT_FACE_COMPARE_SCORE);
+        OrgPropertyCacheBean property2 = CacheHelper.getOrgProperty(studentCache.getRootOrgId(),
+                Constants.BAIDU_EXPECT_FACE_COMPARE_SCORE);
         if (property2.getHasValue() && StringUtils.isNotEmpty(property2.getValue())) {
             baiduExpectFaceCompareScore = Double.parseDouble(property2.getValue());
         }
 
-        log.warn("studentId:{} examRecordDataId:{} todoCaptureQueueSize:{} api:{} {}", studentId, examRecordDataId,
-                queues.size(), useBaiduApi ? "baidu" : "face++", baiduExpectFaceCompareScore);
+        log.warn(
+                "studentId:{} examRecordDataId:{} todoSize:{} useBaiduApi:{} useLocalBaiduApiForFaceLiveness:{} useLocalBaiduApiForFaceCompare:{} expectScore:{}",
+                studentId, examRecordDataId, queues.size(), useBaiduApi, param.isUseLocalBaiduApiForFaceLiveness(),
+                param.isUseLocalBaiduApiForFaceCompare(), baiduExpectFaceCompareScore);
 
         // 将学生底照图片转成Base64格式
         ImageParm basePhoto;
@@ -108,8 +113,8 @@ public class ExamCaptureQueueServiceImpl implements ExamCaptureQueueService {
             String capturePhotoUrl = ossClient.valueOfOssFilePath(FileStorageUtil.realPath(queue.getFileUrl()));
             if (StringUtils.isBlank(capturePhotoUrl)) {
                 // 删除错误无效数据
-                log.warn("人脸抓拍图片地址错误!examRecordDataId:{} queueId:{} imgUrl:{}",
-                        queue.getExamRecordDataId(), queue.getId(), queue.getFileUrl());
+                log.warn("人脸抓拍图片地址错误!examRecordDataId:{} queueId:{} imgUrl:{}", queue.getExamRecordDataId(),
+                        queue.getId(), queue.getFileUrl());
                 examCaptureQueueRepo.deleteById(queue.getId());
                 continue;
             }
@@ -118,8 +123,8 @@ public class ExamCaptureQueueServiceImpl implements ExamCaptureQueueService {
             ImageParm capturePhoto = new ImageBase64Parm(Base64.encodeBase64String(capturePhotoBytes));
             if (StringUtils.isBlank(capturePhoto.value())) {
                 // 删除错误无效数据
-                log.warn("人脸抓拍图片为空白!examRecordDataId:{} queueId:{} imgUrl:{}",
-                        queue.getExamRecordDataId(), queue.getId(), queue.getFileUrl());
+                log.warn("人脸抓拍图片为空白!examRecordDataId:{} queueId:{} imgUrl:{}", queue.getExamRecordDataId(),
+                        queue.getId(), queue.getFileUrl());
                 examCaptureQueueRepo.deleteById(queue.getId());
                 continue;
             }
@@ -135,7 +140,8 @@ public class ExamCaptureQueueServiceImpl implements ExamCaptureQueueService {
 
                     FaceResult faceCompareResult;
                     if (useBaiduApi) {
-                        faceCompareResult = faceVerifyService.faceCompareByBaidu(basePhoto, capturePhoto, baiduExpectFaceCompareScore);
+                        faceCompareResult = faceVerifyService.faceCompareByBaidu(basePhoto, capturePhoto,
+                                baiduExpectFaceCompareScore, param.isUseLocalBaiduApiForFaceCompare());
                     } else {
                         faceCompareResult = faceVerifyService.faceCompareByFacePlus(basePhoto, capturePhoto);
                     }
@@ -161,10 +167,12 @@ public class ExamCaptureQueueServiceImpl implements ExamCaptureQueueService {
                         }
                     } else if (faceCompareResult.getFaceNum() > 1) {
                         // 是否启用陌生人检测
-                        ExamRecordData examRecordDataCache = examRecordDataService.getExamRecordDataCache(examRecordDataId);
+                        ExamRecordData examRecordDataCache = examRecordDataService.getExamRecordDataCache(
+                                examRecordDataId);
                         if (examRecordDataCache != null) {
                             ExamPropertyCacheBean examProperty = ExamCacheTransferHelper.getCachedExamProperty(
-                                    examRecordDataCache.getExamId(), studentId, ExamProperties.IS_STRANGER_ENABLE.name());
+                                    examRecordDataCache.getExamId(), studentId,
+                                    ExamProperties.IS_STRANGER_ENABLE.name());
                             if ("true".equalsIgnoreCase(examProperty.getValue())) {
                                 queue.setIsStranger(true);
                             }
@@ -177,8 +185,8 @@ public class ExamCaptureQueueServiceImpl implements ExamCaptureQueueService {
                 queue.setErrorMsg(e.getMessage());
                 queue.setStatus(ExamCaptureQueueStatus.PROCESS_FACE_COMPARE_FAILED);
 
-                log.error("人脸比对错误!examRecordDataId:{} queueId:{} errorNum:{} error:{}", queue.getExamRecordDataId(),
-                        queue.getId(), queue.getErrorNum(), e.getMessage());
+                log.error("人脸比对错误!examRecordDataId:{} queueId:{} errorNum:{} error:{}",
+                        queue.getExamRecordDataId(), queue.getId(), queue.getErrorNum(), e.getMessage());
                 examCaptureQueueRepo.save(queue);
                 continue;
             }
@@ -191,7 +199,8 @@ public class ExamCaptureQueueServiceImpl implements ExamCaptureQueueService {
 
             try {
                 // 人脸活体检测开始
-                FaceResult faceLivenessResult = faceVerifyService.faceVerifyByBaidu(capturePhoto);
+                FaceResult faceLivenessResult = faceVerifyService.faceVerifyByBaidu(capturePhoto,
+                        param.isUseLocalBaiduApiForFaceLiveness());
                 queue.setFacelivenessResult(faceLivenessResult.getJsonResult());
 
                 if (faceLivenessResult.isApiNeedRetry()) {
@@ -208,8 +217,8 @@ public class ExamCaptureQueueServiceImpl implements ExamCaptureQueueService {
                 queue.setErrorMsg(e.getMessage());
                 queue.setStatus(ExamCaptureQueueStatus.PROCESS_FACELIVENESS_FAILED);
 
-                log.error("人脸活体检测错误!examRecordDataId:{} queueId:{} errorNum:{} error:{}", queue.getExamRecordDataId(),
-                        queue.getId(), queue.getErrorNum(), e.getMessage());
+                log.error("人脸活体检测错误!examRecordDataId:{} queueId:{} errorNum:{} error:{}",
+                        queue.getExamRecordDataId(), queue.getId(), queue.getErrorNum(), e.getMessage());
                 examCaptureQueueRepo.save(queue);
                 continue;
             }
@@ -228,7 +237,8 @@ public class ExamCaptureQueueServiceImpl implements ExamCaptureQueueService {
         if (examCaptureRepo.exists(example)) {
             // 照片处理结果中若已存在则以已有的数据为准,删除该队列记录
             examCaptureQueueRepo.deleteById(queue.getId());
-            log.info("==>ExamCaptureQueue clear... examRecordDataId:{} fileName:{}", queue.getExamRecordDataId(), queue.getFileName());
+            log.info("==>ExamCaptureQueue clear... examRecordDataId:{} fileName:{}", queue.getExamRecordDataId(),
+                    queue.getFileName());
             return;
         }
 
@@ -262,7 +272,8 @@ public class ExamCaptureQueueServiceImpl implements ExamCaptureQueueService {
 
         // 删除该队列记录
         examCaptureQueueRepo.deleteById(queue.getId());
-        log.info("-->ExamCaptureQueue clear... examRecordDataId:{} fileName:{}", queue.getExamRecordDataId(), queue.getFileName());
+        log.info("-->ExamCaptureQueue clear... examRecordDataId:{} fileName:{}", queue.getExamRecordDataId(),
+                queue.getFileName());
     }
 
 }

+ 22 - 5
examcloud-core-oe-task-service/src/main/java/cn/com/qmth/examcloud/core/oe/task/service/job/FaceVerifyJobHandler.java

@@ -1,10 +1,13 @@
 package cn.com.qmth.examcloud.core.oe.task.service.job;
 
+import cn.com.qmth.examcloud.commons.util.JsonMapper;
 import cn.com.qmth.examcloud.core.oe.student.dao.ExamCaptureQueueRepo;
 import cn.com.qmth.examcloud.core.oe.task.service.ExamCaptureQueueService;
+import cn.com.qmth.examcloud.core.oe.task.service.bean.FaceApiParam;
 import cn.com.qmth.examcloud.support.CacheConstants;
 import cn.com.qmth.examcloud.web.exception.SequenceLockException;
 import cn.com.qmth.examcloud.web.helpers.SequenceLockHelper;
+import com.fasterxml.jackson.databind.JsonNode;
 import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,31 +32,45 @@ public class FaceVerifyJobHandler {
 
     public void run(int shardTotal, int shardIndex, String jobParam) throws Exception {
         int batchSize = 100, maxErrorNum = 10;
-        List<Long> todoExamRecordDataIds = examCaptureQueueRepo.findQueuesGroupByExamRecordDataId(shardTotal, shardIndex, batchSize, maxErrorNum);
+        List<Long> todoExamRecordDataIds = examCaptureQueueRepo.findQueuesGroupByExamRecordDataId(shardTotal,
+                shardIndex, batchSize, maxErrorNum);
 
-        log.warn("分片任务_FACE_{}_{}  curTodoExamRecordDataIds_size:{}", shardTotal, shardIndex, todoExamRecordDataIds.size());
+        log.warn("分片任务_FACE_{}_{}  curTodoExamRecordDataIds_size:{}", shardTotal, shardIndex,
+                todoExamRecordDataIds.size());
         if (CollectionUtils.isEmpty(todoExamRecordDataIds)) {
             return;
         }
 
+        FaceApiParam param = new FaceApiParam();
+        JsonNode jsonParams = new JsonMapper().getNode(jobParam);
+        if (jsonParams != null) {
+            boolean useLocalBaiduApiForFaceCompare = jsonParams.get("useLocalBaiduApiForFaceCompare").asBoolean(true);
+            param.setUseLocalBaiduApiForFaceCompare(useLocalBaiduApiForFaceCompare);
+
+            boolean useLocalBaiduApiForFaceLiveness = jsonParams.get("useLocalBaiduApiForFaceLiveness").asBoolean();
+            param.setUseLocalBaiduApiForFaceLiveness(useLocalBaiduApiForFaceLiveness);
+        }
+
         for (Long examRecordDataId : todoExamRecordDataIds) {
             final String lockKey = CacheConstants.LOCK_FACE_COMPARE + examRecordDataId;
             try {
                 SequenceLockHelper.getLockSimple(lockKey);
 
                 // 处理未比对的抓拍照片
-                examCaptureQueueService.handlerExamCaptureQueuesByExamRecordDataId(examRecordDataId);
+                examCaptureQueueService.handlerExamCaptureQueuesByExamRecordDataId(examRecordDataId, param);
             } catch (Exception e) {
                 if (e instanceof InterruptedException) {
                     // 若线程终止,则抛出交由任务调度中心处理
-                    log.warn("当前人脸比对任务线程被终止!examRecordDataId:{}, error:{}", examRecordDataId, e.getMessage());
+                    log.warn("当前人脸比对任务线程被终止!examRecordDataId:{}, error:{}", examRecordDataId,
+                            e.getMessage());
                     throw e;
                 } else if (e instanceof SequenceLockException) {
                     // 若锁问题,下次会继续执行
                     log.warn("当前人脸比对任务获取锁失败!examRecordDataId:{}, redisKey:{}", examRecordDataId, lockKey);
                 } else {
                     // 若异常,下次会继续执行(需要排查原因)
-                    log.error("当前人脸比对任务处理失败!examRecordDataId:{}, error:{}", examRecordDataId, e.getMessage());
+                    log.error("当前人脸比对任务处理失败!examRecordDataId:{}, error:{}", examRecordDataId,
+                            e.getMessage());
                 }
             } finally {
                 SequenceLockHelper.releaseLockSimple(lockKey);