|
@@ -1,35 +1,14 @@
|
|
|
package cn.com.qmth.examcloud.core.oe.task.service.job;
|
|
|
|
|
|
-import cn.com.qmth.examcloud.api.commons.enums.FaceApiProvider;
|
|
|
import cn.com.qmth.examcloud.core.oe.student.dao.ExamCaptureQueueRepo;
|
|
|
-import cn.com.qmth.examcloud.core.oe.student.dao.ExamCaptureRepo;
|
|
|
-import cn.com.qmth.examcloud.core.oe.student.dao.entity.ExamCaptureEntity;
|
|
|
-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.ExamRecordDataService;
|
|
|
-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;
|
|
|
-import cn.com.qmth.examcloud.starters.face.verify.model.param.ImageBase64Parm;
|
|
|
-import cn.com.qmth.examcloud.starters.face.verify.model.param.ImageParm;
|
|
|
-import cn.com.qmth.examcloud.starters.face.verify.service.FaceVerifyService;
|
|
|
+import cn.com.qmth.examcloud.core.oe.task.service.ExamCaptureQueueService;
|
|
|
import cn.com.qmth.examcloud.support.Constants;
|
|
|
-import cn.com.qmth.examcloud.support.cache.CacheHelper;
|
|
|
-import cn.com.qmth.examcloud.support.cache.bean.ExamPropertyCacheBean;
|
|
|
-import cn.com.qmth.examcloud.support.cache.bean.OrgPropertyCacheBean;
|
|
|
-import cn.com.qmth.examcloud.support.cache.bean.StudentCacheBean;
|
|
|
-import cn.com.qmth.examcloud.support.enums.ExamProperties;
|
|
|
-import cn.com.qmth.examcloud.support.examing.ExamRecordData;
|
|
|
-import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
|
|
|
-import cn.com.qmth.examcloud.support.helper.ExamCacheTransferHelper;
|
|
|
import cn.com.qmth.examcloud.web.exception.SequenceLockException;
|
|
|
import cn.com.qmth.examcloud.web.helpers.SequenceLockHelper;
|
|
|
import org.apache.commons.collections4.CollectionUtils;
|
|
|
-import org.apache.commons.lang3.StringUtils;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
-import org.springframework.data.domain.Example;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
import java.util.List;
|
|
@@ -46,13 +25,7 @@ public class FaceVerifyJobHandler {
|
|
|
private ExamCaptureQueueRepo examCaptureQueueRepo;
|
|
|
|
|
|
@Autowired
|
|
|
- private ExamCaptureRepo examCaptureRepo;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private FaceVerifyService faceVerifyService;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private ExamRecordDataService examRecordDataService;
|
|
|
+ private ExamCaptureQueueService examCaptureQueueService;
|
|
|
|
|
|
public void run(int shardTotal, int shardIndex, String jobParam) throws Exception {
|
|
|
int batchSize = 100, maxErrorNum = 100;
|
|
@@ -69,7 +42,7 @@ public class FaceVerifyJobHandler {
|
|
|
SequenceLockHelper.getLockSimple(lockKey);
|
|
|
|
|
|
// 处理业务
|
|
|
- this.handler(examRecordDataId);
|
|
|
+ examCaptureQueueService.handlerExamCaptureQueuesByExamRecordDataId(examRecordDataId);
|
|
|
} catch (Exception e) {
|
|
|
if (e instanceof InterruptedException) {
|
|
|
// 若线程终止,则抛出交由任务调度中心处理
|
|
@@ -88,182 +61,4 @@ public class FaceVerifyJobHandler {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void handler(Long examRecordDataId) throws Exception {
|
|
|
- // 每个考试记录ID下通常只有几条待处理抓拍记录,直接取所有一次性处理。
|
|
|
- List<ExamCaptureQueueEntity> queues = examCaptureQueueRepo.findByExamRecordDataId(examRecordDataId);
|
|
|
- if (CollectionUtils.isEmpty(queues)) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 获取当前学生的底照信息
|
|
|
- Long studentId = queues.get(0).getStudentId();
|
|
|
- StudentCacheBean studentCache = CacheHelper.getStudent(studentId);
|
|
|
- if (StringUtils.isBlank(studentCache.getPhotoPath())) {
|
|
|
- throw new IllegalArgumentException("学生底照不存在!studentId is " + studentId);
|
|
|
- }
|
|
|
-
|
|
|
- // 获取学校人脸API方案配置
|
|
|
- boolean useBaiduApi = false;
|
|
|
- Double baiduExpectFaceCompareScore = null;
|
|
|
- OrgPropertyCacheBean property1 = CacheHelper.getOrgProperty(studentCache.getRootOrgId(), "FACE_API_PROVIDER");
|
|
|
- if (FaceApiProvider.BAIDU.name().equals(property1.getValue())) {
|
|
|
- useBaiduApi = true;
|
|
|
- }
|
|
|
- OrgPropertyCacheBean property2 = CacheHelper.getOrgProperty(studentCache.getRootOrgId(), "BAIDU_EXPECT_FACE_COMPARE_SCORE");
|
|
|
- if (StringUtils.isNotEmpty(property2.getValue())) {
|
|
|
- baiduExpectFaceCompareScore = Double.parseDouble(property2.getValue());
|
|
|
- }
|
|
|
- log.warn("studentId:{} examRecordDataId:{} todoCaptureQueueSize:{} api:{} {}", studentId, examRecordDataId,
|
|
|
- queues.size(), useBaiduApi ? "baidu" : "face++", baiduExpectFaceCompareScore);
|
|
|
-
|
|
|
- // 将学生底照图片转成Base64格式
|
|
|
- ImageParm basePhoto = new ImageBase64Parm(CommonUtils.toBase64(studentCache.getPhotoPath()));
|
|
|
-
|
|
|
- for (ExamCaptureQueueEntity queue : queues) {
|
|
|
- queue.setFaceCompareStartTime(System.currentTimeMillis());
|
|
|
-
|
|
|
- // 将抓拍照图片转成Base64格式
|
|
|
- ImageParm capturePhoto = new ImageBase64Parm(CommonUtils.toBase64(FileStorageUtil.realPath(queue.getFileUrl())));
|
|
|
-
|
|
|
- String extMsg = "";
|
|
|
- boolean errFace = false;
|
|
|
-
|
|
|
- try {
|
|
|
- if (ExamCaptureQueueStatus.PROCESS_FACE_COMPARE_COMPLETE != queue.getStatus()
|
|
|
- && ExamCaptureQueueStatus.PROCESS_FACELIVENESS_FAILED != queue.getStatus()) {
|
|
|
- // 人脸比对开始处理
|
|
|
- queue.setStatus(ExamCaptureQueueStatus.PROCESSING);
|
|
|
-
|
|
|
- FaceResult faceCompareResult;
|
|
|
- if (useBaiduApi) {
|
|
|
- faceCompareResult = faceVerifyService.faceCompareByBaidu(basePhoto, capturePhoto, baiduExpectFaceCompareScore);
|
|
|
- } else {
|
|
|
- faceCompareResult = faceVerifyService.faceCompareByFacePlus(basePhoto, capturePhoto);
|
|
|
- }
|
|
|
-
|
|
|
- if (faceCompareResult.isApiNeedRetry()) {
|
|
|
- throw new FaceVerifyException(faceCompareResult.getError());
|
|
|
- }
|
|
|
-
|
|
|
- queue.setIsPass(faceCompareResult.isFacePass());
|
|
|
- queue.setFaceCompareResult(faceCompareResult.getJsonResult());
|
|
|
- queue.setStatus(ExamCaptureQueueStatus.PROCESS_FACE_COMPARE_COMPLETE);
|
|
|
- queue.setIsStranger(false);
|
|
|
-
|
|
|
- if (!faceCompareResult.isFacePass()) {
|
|
|
- extMsg = "比对:" + faceCompareResult.getError();
|
|
|
- queue.setExtMsg(extMsg);
|
|
|
- }
|
|
|
-
|
|
|
- if (faceCompareResult.getFaceNum() == 0) {
|
|
|
- // 百度人脸比对接口(不支持人脸数量检测)
|
|
|
- if (!useBaiduApi) {
|
|
|
- errFace = true;
|
|
|
- }
|
|
|
- } else if (faceCompareResult.getFaceNum() > 1) {
|
|
|
- // 是否启用陌生人检测
|
|
|
- ExamRecordData examRecordDataCache = examRecordDataService.getExamRecordDataCache(examRecordDataId);
|
|
|
- if (examRecordDataCache != null) {
|
|
|
- ExamPropertyCacheBean examProperty = ExamCacheTransferHelper.getCachedExamProperty(
|
|
|
- examRecordDataCache.getExamId(), studentId, ExamProperties.IS_STRANGER_ENABLE.name());
|
|
|
- if ("true".equalsIgnoreCase(examProperty.getValue())) {
|
|
|
- queue.setIsStranger(true);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- int errorNum = queue.getErrorNum() != null ? queue.getErrorNum() : 0;
|
|
|
- queue.setErrorNum(errorNum + 1);
|
|
|
- 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());
|
|
|
- examCaptureQueueRepo.save(queue);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if (errFace) {
|
|
|
- // 没有检测到正常人脸,直接保存人脸检测最终结果并删除该队列记录
|
|
|
- this.saveExamCaptureAndDeleteQueue(queue);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- // 人脸活体检测开始
|
|
|
- FaceResult faceLivenessResult = faceVerifyService.faceVerifyByBaidu(capturePhoto);
|
|
|
- queue.setFacelivenessResult(faceLivenessResult.getJsonResult());
|
|
|
-
|
|
|
- if (faceLivenessResult.isApiNeedRetry()) {
|
|
|
- throw new FaceVerifyException(faceLivenessResult.getError());
|
|
|
- }
|
|
|
-
|
|
|
- if (!faceLivenessResult.isFacePass()) {
|
|
|
- extMsg = extMsg + " 活检:" + faceLivenessResult.getError();
|
|
|
- queue.setExtMsg(extMsg);
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- int errorNum = queue.getErrorNum() != null ? queue.getErrorNum() : 0;
|
|
|
- queue.setErrorNum(errorNum + 1);
|
|
|
- queue.setErrorMsg(e.getMessage());
|
|
|
- queue.setStatus(ExamCaptureQueueStatus.PROCESS_FACELIVENESS_FAILED);
|
|
|
-
|
|
|
- log.error("人脸活体检测错误!examRecordDataId:{} queueId:{} errorNum:{} error:{}", queue.getExamRecordDataId(),
|
|
|
- queue.getId(), queue.getErrorNum(), e.getMessage());
|
|
|
- examCaptureQueueRepo.save(queue);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // 保存人脸检测最终结果并删除该队列记录
|
|
|
- this.saveExamCaptureAndDeleteQueue(queue);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void saveExamCaptureAndDeleteQueue(ExamCaptureQueueEntity queue) {
|
|
|
- ExamCaptureEntity query = new ExamCaptureEntity();
|
|
|
- query.setExamRecordDataId(queue.getExamRecordDataId());
|
|
|
- query.setFileName(queue.getFileName());
|
|
|
- Example<ExamCaptureEntity> example = Example.of(query);
|
|
|
-
|
|
|
- if (examCaptureRepo.exists(example)) {
|
|
|
- // 照片处理结果中若已存在则以已有的数据为准,删除该队列记录
|
|
|
- examCaptureQueueRepo.deleteById(queue.getId());
|
|
|
- log.info("==>ExamCaptureQueue clear... examRecordDataId:{} fileName:{}", queue.getExamRecordDataId(), queue.getFileName());
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- ExamCaptureEntity result = new ExamCaptureEntity();
|
|
|
- result.setExamRecordDataId(queue.getExamRecordDataId());
|
|
|
- result.setFileName(queue.getFileName());
|
|
|
- result.setFileUrl(queue.getFileUrl());
|
|
|
- result.setIsPass(queue.getIsPass());
|
|
|
- result.setIsStranger(queue.getIsStranger());
|
|
|
- result.setFaceCompareResult(queue.getFaceCompareResult());
|
|
|
- result.setFacelivenessResult(queue.getFacelivenessResult());
|
|
|
- result.setExtMsg(queue.getExtMsg());
|
|
|
-
|
|
|
- long currentTimeMillis = System.currentTimeMillis();
|
|
|
- // 从创建队列到处理完毕的时间
|
|
|
- result.setProcessTime(currentTimeMillis - queue.getCreationTime().getTime());
|
|
|
-
|
|
|
- // 从开始处理到处理完毕的时间
|
|
|
- result.setUsedTime(currentTimeMillis - queue.getFaceCompareStartTime());
|
|
|
-
|
|
|
- result.setHasVirtualCamera(queue.getHasVirtualCamera() != null ? queue.getHasVirtualCamera() : false);
|
|
|
-
|
|
|
- if (StringUtils.length(queue.getCameraInfos()) >= Constants.VM_CAMERA_SIZE_LIMIT) {
|
|
|
- log.warn("虚拟摄像头信息超长!examRecordDataId:{} queueId:{}", queue.getExamRecordDataId(), queue.getId());
|
|
|
- result.setCameraInfos(Constants.VM_CAMERA_WARN);
|
|
|
- } else {
|
|
|
- result.setCameraInfos(queue.getCameraInfos());
|
|
|
- }
|
|
|
-
|
|
|
- examCaptureRepo.save(result);
|
|
|
-
|
|
|
- // 删除该队列记录
|
|
|
- examCaptureQueueRepo.deleteById(queue.getId());
|
|
|
- log.info("-->ExamCaptureQueue clear... examRecordDataId:{} fileName:{}", queue.getExamRecordDataId(), queue.getFileName());
|
|
|
- }
|
|
|
-
|
|
|
}
|