Просмотр исходного кода

图片抓拍代码迁移和重构

lideyin 5 лет назад
Родитель
Сommit
9d49645894

+ 182 - 0
src/main/java/cn/com/qmth/examcloud/support/Constants.java

@@ -0,0 +1,182 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-24 11:34:17.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.support;
+
+/**
+ * @Description 系统常量
+ * @Author lideyin
+ * @Date 2019/12/11 15:52
+ * @Version 1.0
+ */
+public interface Constants {
+
+    /**
+     * 系统错误
+     */
+    String OE_CODE_500 = "OE-000500";
+
+    /**
+     * 参数错误
+     */
+    String OE_CODE_400 = "OE-000400";
+
+    /**
+     * 权限错误
+     */
+    String OE_CODE_403 = "OE-000403";
+
+    /**
+     * 开始考试锁
+     */
+    String START_EXAM_LOCK_PREFIX = "oe_student:start_exam_lock_studentid_";
+    /**
+     * 结束考试锁前缀
+     */
+    String END_EXAM_LOCK_PREFIX = "oe_student:end_exam_lock_studentid_";
+    /**
+     * 考试交卷锁前缀
+     */
+    String HAND_IN_EXAM_LOCK_PREFIX = "oe_student:hand_in_exam_lock_";
+    /**
+     * 处理照片锁前缀
+     */
+    String EXAM_CAPTURE_PHOTO_LOCK_PREFIX = "oe_student:exam_capture_lock_";
+    /**
+     * 获取人脸活体检测基本信息前缀
+     */
+    String GET_FACE_BIOPSY_INFO_PREFIX = "oe_student:get_face_biopsy_info_lock_";
+    //
+    String ERROR_MSG = "error_message";
+
+
+    /**
+     * 学生考试session前缀
+     */
+    String OE_STUDENT_EXAM_SESSION_PREFIX = "oe_student:exam_session_";
+
+
+    String EXAM_CAPTURE_QUEUE_LOCK_PREFIX = "oe_student:exam_capture_queue_lock_";
+
+
+    /**
+     * 原始数据库表
+     */
+    String OE_EXAM_RECORD = "oe_exam_record";
+    String ECS_EXAM_STUDENT = "ecs_exam_student";
+    String OE_EXAM_QUESTION = "oe_exam_question";
+    String OE_EXAM_CAPTURE = "oe_exam_capture";
+    String OE_EXAM_SCORE = "oe_exam_score";
+    String OE_EXAM_AUDIT = "oe_exam_audit";
+    String OE_FACE_VERIFY = "oe_face_verify";
+
+    /**
+     * 新数据库表
+     */
+    String EC_OE_EXAM_RECORD_DATA = "ec_oe_exam_record_data";
+    String EC_OE_EXAM_STUDENT = "ec_oe_exam_student";
+    String EXAM_RECORD_QUESTIONS = "examRecordQuestions";
+    String EC_OE_EXAM_CAPTURE = "ec_oe_exam_capture";
+    String EC_OE_EXAM_SCORE = "ec_oe_exam_score";
+    String EC_OE_EXAM_AUDIT = "ec_oe_exam_audit";
+    String EC_OE_EXAM_FACE_VERIFY = "ec_oe_exam_face_liveness_verify";
+
+    /**
+     * 课程信息表
+     */
+    String ECS_CORE_COURSE = "ecs_core_course";
+
+    //缓存
+    String CACHE_EXAM_STUDENT_PREFIX = "cache_examStudent_";
+    String CACHE_PHONE_PREFIX = "cache_phone_";
+    String CACHE_STUDENT_PREFIX = "cache_student_";
+    String CACHE_ORG_PREFIX = "cache_org_";
+    String CACHE_COURSE_PREFIX = "cache_course_";
+
+    String isTrue = "true";
+
+    String isFalse = "false";
+    /**
+     * 考试成绩通知路径前缀
+     */
+    String OE_EXAM_SCORE_NOTIFY_URL_PREFIX="oe.examScoreNotify.url.";
+    /**
+     * 考试成绩通知路径传输方法前缀
+     */
+    String OE_EXAM_SCORE_NOTIFY_URL_HTTP_METHOD_PREFIX="oe.examScoreNotify.url.httpMethod.";
+
+    //face++ 人脸比对相关错误详情
+    /**
+     * face++ 人脸比对API并发次数超过上限
+     */
+    String FACE_COMPARE_CONCURRENCY_LIMIT_EXCEEDED="CONCURRENCY_LIMIT_EXCEEDED";
+    /**
+     * face++ api_key没有调用本API的权限,具体原因为:用户自己禁止该api_key调用、管理员禁止该api_key调用、由于账户余额不足禁止调用。
+     */
+    String FACE_COMPARE_AUTHORIZATION_ERROR="AUTHORIZATION_ERROR";
+    /**
+     * face++ 下载图片超时
+     */
+    String FACE_COMPARE_IMAGE_DOWNLOAD_TIMEOUT="IMAGE_DOWNLOAD_TIMEOUT";
+
+
+    //百度活检错误码 http://ai.baidu.com/docs#/Face-Java-SDK/514d7ea4
+    String BAIDU_ERROR_CODE = "error_code";
+    String BAIDU_ERROR_MSG="error_msg";
+    String BAIDU_SUCCESS_ERROR_CODE_VALUE = "0";
+
+    /**
+     * 连接超时或读取数据超时
+     */
+    String BAIDU_FACELIVENESS_CONNECTION_OR_READ_DATA_TIME_OUT_CODE ="SDK108";
+
+    /**
+     * 百度在线活体检测QPS超过上限的错误码
+     */
+    String BAIDU_FACELIVENESS_QPS_LIMIT_EXCEEDED_CODE ="18";
+
+
+    /**
+     * 抓拍照片又拍云签名前缀
+     */
+    String EXAM_CAPTURE_PHOTO_UPYUN_SIGN_PREFIX ="OE_EXAM_CAPTURE_PHOTO_UPYUN_SIGN_";
+
+    /**
+     * 处理照片高优先级
+     */
+    int PROCESS_CAPTURE_HIGH_PRIORITY=1;
+
+    /**
+     * 照片处理中状态码
+     */
+    String CAPTURE_PROCESSING_STATUS_CODE="101222";
+
+    /**
+     * 考试未结束状态码
+     */
+    String EXAM_RECORD_NOT_END_STATUS_CODE="101333";
+
+    /**
+     * 交卷处理中
+     */
+    String PROCESSING_EXAM_RECORD_CODE="S-101000";
+
+    /**
+     * 通用成功编码
+     */
+    String COMMON_SUCCESS_CODE = "000000";
+
+    /**
+     * 同步人脸比对结果前缀
+     */
+    String FACE_SYNC_COMPARE_RESULT_PREFIX = "FACE_SYNC_COMPARE_RESULT_";
+
+    /**
+     * 活体检测方案key
+     */
+    String IDENTIFICATION_OF_LIVING_BODY_SCHEME_KEY ="IDENTIFICATION_OF_LIVING_BODY_SCHEME";
+}

+ 68 - 0
src/main/java/cn/com/qmth/examcloud/support/enums/ExamProperties.java

@@ -0,0 +1,68 @@
+package cn.com.qmth.examcloud.support.enums;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年11月15日 下午5:37:35
+ * @company 	QMTH
+ * @description 考试属性
+ */
+public enum ExamProperties {
+
+	SCORE_PUBLISHING("发布成绩"),
+	IS_ENTRANCE_EXAM("是否入学考试"),
+	FREEZE_TIME("交卷冻结时间"),
+	EXAM_RECONNECT_TIME("断点续考时间"),
+	BEFORE_EXAM_REMARK("考前说明"),
+	AFTER_EXAM_REMARK("考后说明"),
+	SHOW_CHEATING_REMARK("是否展示作弊"),
+	CHEATING_REMARK("作弊说明"),
+	PRACTICE_TYPE("练习模式"),
+	SINGLE_EDIT("单选题补充说明是否可填"),
+	MUTIPLE_EDIT("多选题补充说明是否可填"),
+	BOOL_EDIT("判断题补充说明是否可填"),
+	FILL_BLANK_EDIT("填空题补充说明是否可填"),
+	SINGLE_ANSWER_REMARK("单选题补充说明"),
+	MUTIPLE_ANSWER_REMARK("多选题补充说明"),
+	BOOL_ANSWER_REMARK("判断题补充说明"),
+	FILL_BLANK_REMARK("填空题补充说明"),
+	TEXT_ANSWER_REMARK("问答题补充说明"),
+	nestedAnswerRemark("套题补充说明"),
+	IS_FACE_ENABLE("是否启用人脸识别"),
+	IS_FACE_CHECK("进入考试是否验证人脸识别"),
+	WARN_THRESHOLD("人脸检测预警阈值"),
+	MARKING_TYPE("阅卷方式"),
+	IS_FACE_VERIFY("是否开启人脸活体检测"),
+	FACE_VERIFY_START_MINUTE("活体检测开始分钟数"),
+	FACE_VERIFY_END_MINUTE("活体检测结束分钟数"),
+	ADD_FACE_VERIFY_OUT_FREEZE_TIME("冻结时间外新加人脸活体检测"),
+	OUT_FREEZE_TIME_FACE_VERIFY_START_MINUTE("冻结时间外活体检测开始分钟数"),
+	OUT_FREEZE_TIME_FACE_VERIFY_END_MINUTE("冻结时间外活体检测结束分钟数"),
+	IP_LIMIT("是否IP限制"),
+	IP_ADDRESSES("IP白名单"),
+	IS_OBJ_SCORE_VIEW("是否显示客观题成绩"),
+	CAN_UPLOAD_ATTACHMENT("是否允许上传附件(离线考试)"),
+	LIVING_WARN_THRESHOLD("人脸真实性阈值"),
+	MARKING_TASK_BUILDED("阅卷是否生成评卷任务"),
+	OFFLINE_UPLOAD_FILE_TYPE("离线考试上传文件类型"),
+	PUSH_SCORE("是否推送分数"),
+	MAX_INTERRUPT_NUM("最大断点续考次数"),
+	IS_STRANGER_ENABLE("是否启用陌生人检测"),
+	LIMITED_IF_NO_SPECIAL_SETTINGS("无特殊设置时禁止考试"),
+	WEIXIN_ANSWER_ENABLED("是否开放微信小程序作答");
+	
+	private ExamProperties(String desc){
+		this.desc = desc;
+	}
+	
+	private String desc;
+
+	public String getDesc() {
+		return desc;
+	}
+
+	public void setDesc(String desc) {
+		this.desc = desc;
+	}
+	
+}

+ 41 - 0
src/main/java/cn/com/qmth/examcloud/support/enums/FaceBiopsyScheme.java

@@ -0,0 +1,41 @@
+package cn.com.qmth.examcloud.support.enums;
+
+/**
+ * @Description 活体检测方案枚举
+ * @Author lideyin
+ * @Date 2019/11/6 15:03
+ * @Version 1.0
+ */
+public enum FaceBiopsyScheme {
+    /**
+     * FaceID活体检测方案(即旧活体检测方案)
+     */
+    FACE_ID("S1", "FaceID活体检测方案"),
+    /**
+     * 新活体检测方案(暂时无法给出具体命名,以后有需要再改动)
+     */
+    NEW("S2", "新活体检测方案");
+    private String code;
+    private String desc;
+
+    FaceBiopsyScheme(String code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    /**
+     * 获取活检方案代码
+     * @return
+     */
+    public String getCode(){
+        return this.code;
+    }
+
+    /**
+     * 获取活检方案描述
+     * @return
+     */
+    public String getDesc(){
+        return this.desc;
+    }
+}

+ 220 - 0
src/main/java/cn/com/qmth/examcloud/support/helper/ExamCacheTransferHelper.java

@@ -0,0 +1,220 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-29 10:44:05.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.support.helper;
+
+import cn.com.qmth.examcloud.api.commons.enums.ExamSpecialSettingsType;
+import cn.com.qmth.examcloud.commons.util.StringUtil;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.CourseCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamPropertyCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamSettingsCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.OrgPropertyCacheBean;
+import cn.com.qmth.examcloud.support.enums.ExamProperties;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+/**
+ * @Description 网考缓存实体转换服务
+ * @Author lideyin
+ * @Date 2019/8/13 16:00
+ * @Version 1.0
+ */
+public class ExamCacheTransferHelper {
+
+    /**
+     * 获取缓存的考试信息
+     *
+     * @param examId    考试id
+     * @param studentId 学生id
+     * @return
+     */
+    public static ExamSettingsCacheBean getCachedExam(Long examId, Long studentId) {
+        //默认取考试中的通用设置
+        ExamSettingsCacheBean examBean = CacheHelper.getExamSettings(examId);
+
+        //是否开启特殊设置
+        Boolean specialSettingsEnabled = examBean.getSpecialSettingsEnabled();
+        if (specialSettingsEnabled == true) {
+            ExamSpecialSettingsType specialSettingsType = examBean.getSpecialSettingsType();
+
+            //开启特殊设置却未进行任何设置则直接返回通用设置
+            if (null == specialSettingsType) {
+                return examBean;
+            }
+
+            switch (specialSettingsType) {
+                case ORG_BASED:
+                    //需求调整,所有的组织机构取学生表所关联的orgId
+                    Long orgId = CacheHelper.getStudent(studentId).getOrgId();
+                    initOrgSpecialSettings(examId, orgId, examBean);
+                    break;
+                case STUDENT_BASED:
+                    //初始化学生的特殊化设置
+                    initStudentSpecialSettings(examId, studentId, examBean);
+                    break;
+                case COURSE_BASED:
+                    //暂无此需求
+                    throw new NotImplementedException();
+            }
+        }
+        return examBean;
+    }
+
+    /**
+     * 获取默认(即非个性化)的考试信息
+     * <b>注意:只有非个性化的信息才可调用此方法</b>
+     *
+     * @param examId 考试id
+     * @return
+     */
+    public static ExamSettingsCacheBean getDefaultCachedExam(Long examId) {
+        return CacheHelper.getExamSettings(examId);
+    }
+
+    /**
+     * 获取考试的属性
+     *
+     * @param examId
+     * @param studentId
+     * @param propKey
+     * @return
+     */
+    public static ExamPropertyCacheBean getCachedExamProperty(Long examId, Long studentId, String propKey) {
+        ExamSettingsCacheBean examCacheBean = CacheHelper.getExamSettings(examId);
+        //默认取考试中的通用设置
+        ExamPropertyCacheBean examPropertyCacheBean = CacheHelper.getExamProperty(examId, propKey);
+
+        //是否开启特殊设置
+        Boolean specialSettingsEnabled = examCacheBean.getSpecialSettingsEnabled();
+        if (specialSettingsEnabled == true) {
+            ExamSpecialSettingsType specialSettingsType = examCacheBean.getSpecialSettingsType();
+
+            //开启特殊设置却未进行任何设置则直接返回通用设置
+            if (null == specialSettingsType) {
+                return examPropertyCacheBean;
+            }
+
+            ExamPropertyCacheBean specialExamProperty = null;
+            switch (specialSettingsType) {
+                case ORG_BASED:
+                    //需求调整,所有的组织机构取学生表所关联的orgId
+                    Long orgId = CacheHelper.getStudent(studentId).getOrgId();
+                    specialExamProperty = CacheHelper.getExamOrgProperty(examId, orgId, propKey);
+                    break;
+                case STUDENT_BASED:
+                    specialExamProperty = CacheHelper.getExamStudentProperty(examId, studentId, propKey);
+                    break;
+                case COURSE_BASED:
+                    //暂无此需求
+                    throw new NotImplementedException();
+            }
+            if (specialExamProperty.getHasValue()) {
+                return specialExamProperty;
+            }
+        }
+
+        return examPropertyCacheBean;
+    }
+
+    /**
+     * 获取默认(即非个性化)的考试属性信息
+     *
+     * @param examId
+     * @param propKey
+     * @return
+     */
+    public static ExamPropertyCacheBean getDefaultCachedExamProperty(Long examId, String propKey) {
+        return CacheHelper.getExamProperty(examId, propKey);
+    }
+
+    /**
+     * 获取课程
+     *
+     * @param courseId 课程id
+     * @return CourseBean
+     */
+    public static CourseCacheBean getCachedCourse(Long courseId) {
+        return CacheHelper.getCourse(courseId);
+    }
+
+    @ApiOperation(value = "是否可以微信作答", notes = "")
+    @GetMapping("weixinAnswerEnabled/{examId}")
+    public static Boolean weixinAnswerEnabled(@PathVariable Long examId) {
+
+        ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
+
+        OrgPropertyCacheBean orgConf = CacheHelper.getOrgProperty(examSettings.getRootOrgId(),
+                ExamProperties.WEIXIN_ANSWER_ENABLED.name());
+        ExamPropertyCacheBean examConf = CacheHelper.getExamProperty(examId,
+                ExamProperties.WEIXIN_ANSWER_ENABLED.name());
+
+        String orgValue = orgConf.getValue();
+        String examValue = examConf.getValue();
+
+        if (!orgConf.getHasValue()) {
+            return false;
+        }
+        if (StringUtils.isBlank(orgValue)) {
+            return false;
+        }
+
+        if (StringUtils.isBlank(examValue)) {
+            return false;
+        }
+
+        if (StringUtil.isTrue(orgValue) && StringUtil.isTrue(examValue)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 初始化学生的特殊化设置
+     *
+     * @param examId
+     * @param studentId
+     * @param examBean
+     */
+    private static void initStudentSpecialSettings(Long examId, Long studentId, ExamSettingsCacheBean examBean) {
+        ExamSettingsCacheBean examSpecialSetting =
+                CacheHelper.getExamStudentSettings(examId, studentId);
+        if (null != examSpecialSetting.getBeginTime()) {
+            examBean.setBeginTime(examSpecialSetting.getBeginTime());
+        }
+        if (null != examSpecialSetting.getEndTime()) {
+            examBean.setEndTime(examSpecialSetting.getEndTime());
+        }
+    }
+
+    /**
+     * 初始化组织机构的特殊化设置
+     *
+     * @param examId
+     * @param orgId
+     * @param examBean
+     */
+    private static void initOrgSpecialSettings(Long examId, Long orgId, ExamSettingsCacheBean examBean) {
+        ExamSettingsCacheBean examSpecialSetting =
+                CacheHelper.getExamOrgSettings(examId, orgId);
+        if (null != examSpecialSetting.getBeginTime()) {
+            examBean.setBeginTime(examSpecialSetting.getBeginTime());
+        }
+        if (null != examSpecialSetting.getEndTime()) {
+            examBean.setEndTime(examSpecialSetting.getEndTime());
+        }
+
+        if (null != examSpecialSetting.getExamLimit()) {
+            examBean.setExamLimit(examSpecialSetting.getExamLimit());
+        }
+    }
+
+}

+ 160 - 0
src/main/java/cn/com/qmth/examcloud/support/helper/FaceBiopsyHelper.java

@@ -0,0 +1,160 @@
+package cn.com.qmth.examcloud.support.helper;
+
+import cn.com.qmth.examcloud.commons.util.StringUtil;
+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.ExamSettingsCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.OrgPropertyCacheBean;
+import cn.com.qmth.examcloud.support.enums.ExamProperties;
+import cn.com.qmth.examcloud.support.enums.FaceBiopsyScheme;
+import cn.com.qmth.examcloud.support.privilege.PrivilegeDefine;
+import cn.com.qmth.examcloud.support.privilege.PrivilegeManager;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @Description 活体检测帮助类
+ * @Author lideyin
+ * @Date 2019/11/6 15:01
+ * @Version 1.0
+ */
+public class FaceBiopsyHelper {
+    /**
+     * 获取活体检测方案
+     *
+     * @param rootOrgId
+     * @return
+     */
+    public static FaceBiopsyScheme getFaceBiopsyScheme(Long rootOrgId) {
+        OrgPropertyCacheBean orgProperty = CacheHelper.getOrgProperty(rootOrgId,
+                Constants.IDENTIFICATION_OF_LIVING_BODY_SCHEME_KEY);
+        if (orgProperty.getHasValue()) {
+            if (FaceBiopsyScheme.FACE_ID.getCode().equals(orgProperty.getValue())) {
+                return FaceBiopsyScheme.FACE_ID;
+            }
+            return FaceBiopsyScheme.NEW;
+        }
+        //默认使用旧活体检测方案(即faceId方案)
+        return FaceBiopsyScheme.FACE_ID;
+    }
+
+    /**
+     * 是否开启人脸
+     *
+     * @param rootOrgId
+     * @param examId
+     * @param studentId
+     * @return
+     */
+    public static Boolean isFaceEnable(Long rootOrgId, Long examId, Long studentId) {
+        //判断是否有人脸识别的权限
+        String faceCheck = PrivilegeDefine.RootOrgFunctions.OnlineExamFunctions.FaceCheck.CODE;
+        Boolean hasFaceCheckFunction = PrivilegeManager.judge(rootOrgId, faceCheck);
+
+        //如果有权限,具体的人脸识别的配置值
+        ExamPropertyCacheBean examConf = ExamCacheTransferHelper.getCachedExamProperty(examId,
+                studentId, ExamProperties.IS_FACE_ENABLE.name());
+        String examValue = examConf.getValue();
+
+        if (!hasFaceCheckFunction) {
+            return false;
+        }
+
+        if (StringUtils.isBlank(examValue)) {
+            return false;
+        }
+
+        if (StringUtil.isTrue(examValue)) {
+            return true;
+        }
+
+        return false;
+
+    }
+
+    /**
+     * 是否开启人脸活体检测
+     *
+     * @param rootOrgId
+     * @param examId
+     * @param studentId
+     * @return
+     */
+    public static Boolean isFaceVerify(Long rootOrgId, Long examId, Long studentId) {
+        ExamSettingsCacheBean examSettings = CacheHelper.getExamSettings(examId);
+
+        //如果未开启人脸识别,则直接返回false
+        Boolean faceEnable = isFaceEnable(rootOrgId, examId, studentId);
+        if (!faceEnable) {
+            return false;
+        }
+
+        //人脸活体检测权限
+        String IdentificationOfLivingBody = PrivilegeDefine.RootOrgFunctions.OnlineExamFunctions.IdentificationOfLivingBody.CODE;
+        Boolean hasIdentificationOfLivingBodyFunction = PrivilegeManager
+                .judge(examSettings.getRootOrgId(), IdentificationOfLivingBody);
+
+        //人脸活体检测配置值
+        ExamPropertyCacheBean examConf = CacheHelper.getExamProperty(examId, ExamProperties.IS_FACE_VERIFY.name());
+        String examValue = examConf.getValue();
+
+        if (!hasIdentificationOfLivingBodyFunction) {
+            return false;
+        }
+
+        if (StringUtils.isBlank(examValue)) {
+            return false;
+        }
+
+        if (StringUtil.isTrue(examValue)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 进入考试是否验证人脸识别(强制、非强制)
+     *
+     * @return
+     */
+    public static Boolean isFaceCheck(Long examId, Long studentId) {
+        String isFaceCheck = ExamCacheTransferHelper.getCachedExamProperty(examId,
+                studentId, ExamProperties.IS_FACE_CHECK.name()).getValue();
+        if (StringUtils.isBlank(isFaceCheck)) {
+            return false;
+        }
+        return Boolean.valueOf(isFaceCheck);
+    }
+
+    /**
+     * 判断是否有人脸识别权限
+     *
+     * @param rootOrgId
+     * @param examId
+     * @return
+     * @author WANGWEI
+     */
+    private Boolean faceCheckEnabled(Long rootOrgId, Long examId, Long studentId) {
+        String faceCheck = PrivilegeDefine.RootOrgFunctions.OnlineExamFunctions.FaceCheck.CODE;
+        Boolean hasFaceCheckFunction = PrivilegeManager.judge(rootOrgId, faceCheck);
+
+        ExamPropertyCacheBean examConf = ExamCacheTransferHelper.getCachedExamProperty(examId,
+                studentId, ExamProperties.IS_FACE_ENABLE.name());
+        String examValue = examConf.getValue();
+
+        if (!hasFaceCheckFunction) {
+            return false;
+        }
+
+        if (StringUtils.isBlank(examValue)) {
+            return false;
+        }
+
+        if (StringUtil.isTrue(examValue)) {
+            return true;
+        }
+
+        return false;
+    }
+}