deason 3 年之前
父节点
当前提交
15feba9dbb

+ 216 - 227
examcloud-commons/src/main/java/cn/com/qmth/examcloud/commons/helpers/pipeline/SimpleNode.java

@@ -1,21 +1,19 @@
 package cn.com.qmth.examcloud.commons.helpers.pipeline;
 
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.logging.log4j.ThreadContext;
-
-import com.google.common.collect.Lists;
-
 import cn.com.qmth.examcloud.commons.helpers.KeyValuePair;
 import cn.com.qmth.examcloud.commons.helpers.ObjectHolder;
 import cn.com.qmth.examcloud.commons.util.ThreadLocalUtil;
 import cn.com.qmth.examcloud.commons.util.Util;
+import com.google.common.collect.Lists;
+import org.apache.logging.log4j.ThreadContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
 /**
  * 简单节点
  *
@@ -24,223 +22,214 @@ import org.slf4j.LoggerFactory;
  * @Copyright (c) 2018-2020 http://www.qmth.com.cn/ All Rights Reserved.
  */
 public class SimpleNode<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
-		implements
-			Node<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {
-
-	private static final Logger LOG = LoggerFactory.getLogger(SimpleNode.class);
-
-	private String nodeName;
-
-	private Storer<KEYIN, VALUEIN> storer = new Storer<KEYIN, VALUEIN>();
-
-	private Counter counter = new Counter();
-
-	private TaskContext context;
-
-	private NodeExecuter<KEYIN, VALUEIN, KEYOUT, VALUEOUT> executer;
-
-	private SimpleNode<KEYIN, VALUEIN, KEYOUT, VALUEOUT> myself;
-
-	private Node<KEYOUT, VALUEOUT, ?, ?> lowerNode;
-
-	private Storer<KEYOUT, VALUEOUT> lowerStorer;
-
-	private boolean first;
-
-	private int sleep = 10;
-
-	private String traceId = ThreadLocalUtil.getTraceId();
-
-	/**
-	 * 构造函数
-	 *
-	 * @param nodeName
-	 * @param context
-	 */
-	public SimpleNode(String nodeName, NodeExecuter<KEYIN, VALUEIN, KEYOUT, VALUEOUT> executer,
-			TaskContext context) {
-		super();
-		this.nodeName = nodeName;
-		this.executer = executer;
-		this.context = context;
-		this.myself = this;
-	}
-
-	/**
-	 * 启动
-	 *
-	 * @author WANGWEI
-	 */
-	@Override
-	public void start() {
-		this.startNodeExecuter();
-
-		this.startLogThread();
-	}
-
-	/**
-	 * 创建节点执行器
-	 *
-	 * @author WANGWEI
-	 */
-	private void startNodeExecuter() {
-		new Thread(() -> {
-
-			while (true) {
-				traceId = ThreadLocalUtil.getTraceId();
-				// 设置log4j线程上下文
-				ThreadContext.put("TRACE_ID", traceId);
-
-				LOG.info(new StringBuilder("[" + nodeName + "]. ").append("start ... ...")
-						.toString());
-				counter = new Counter();
-				if (isFirst()) {
-					execute(null, null);
-				} else {
-					Set<Entry<KEYIN, VALUEIN>> entrySet = getStorer().getEntrySet();
-
-					for (Entry<KEYIN, VALUEIN> entry : entrySet) {
-						execute(entry.getKey(), entry.getValue());
-					}
-					LOG.info(new StringBuilder("[" + nodeName + "]. ").append("end .status: ")
-							.append(myself.getNodeStatusInfo()).toString());
-				}
-				try {
-					Util.sleep(getSleep());
-				} catch (Exception e) {
-					LOG.error("sleep Exception.", e);
-				}
-
-				// 清理log4j线程上下文
-				ThreadContext.clearAll();
-			}
-
-		}).start();
-	}
-
-	private void execute(KEYIN key, VALUEIN value) {
-		long s = System.currentTimeMillis();
-		try {
-			if (LOG.isDebugEnabled()) {
-				LOG.debug(new StringBuilder("[" + nodeName + "]. ").append("handle entry. key=")
-						.append(null == key ? null : key.toString()).append("; value=")
-						.append(null == value ? null : value.toString()).toString());
-			}
-			counter.incrementTotal();
-			List<KeyValuePair<KEYOUT, VALUEOUT>> outList = Lists.newLinkedList();
-			ObjectHolder<Boolean> removable = new ObjectHolder<Boolean>(true);
-			executer.execute(key, value, outList, removable, context);
-
-			if (null != outList && null != getLowerStorer()) {
-				for (KeyValuePair<KEYOUT, VALUEOUT> pair : outList) {
-					getLowerStorer().putElement(pair.getKey(), pair.getValue());
-				}
-			}
-
-			if (null != removable.get() && removable.get()) {
-				if (null != key) {
-					getStorer().remove(key);
-				}
-			}
-
-			counter.incrementSuccessAmount();
-
-			if (LOG.isDebugEnabled()) {
-				LOG.debug(new StringBuilder("[" + nodeName + "]. ")
-						.append("handle entry successfully. key=")
-						.append(null == key ? null : key.toString()).append("; value=")
-						.append(null == value ? null : value.toString()).toString());
-			}
-		} catch (Exception e) {
-			counter.incrementFailureAmount();
-			if (LOG.isErrorEnabled()) {
-				LOG.error(new StringBuilder("[" + nodeName + "]. ")
-						.append("fail to handle entry. key=")
-						.append(null == key ? null : key.toString()).append("; value=")
-						.append(null == value ? null : value.toString()).toString(), e);
-			}
-		} finally {
-			if (LOG.isDebugEnabled()) {
-				LOG.debug(new StringBuilder("[" + nodeName + "]. ").append("cost ")
-						.append(System.currentTimeMillis() - s).append("ms.").toString());
-			}
-		}
-	}
-
-	/**
-	 * 启动日志线程
-	 *
-	 * @author WANGWEI
-	 */
-	private void startLogThread() {
-		new Thread(() -> {
-			while (true) {
-				// 设置log4j线程上下文
-				ThreadContext.put("TRACE_ID", traceId);
-
-				Util.sleep(TimeUnit.SECONDS, 2);
-				if (LOG.isInfoEnabled()) {
-					LOG.info(new StringBuilder("[" + nodeName + "]. ").append("status: ")
-							.append(myself.getNodeStatusInfo()).toString());
-				}
-
-				// 清理log4j线程上下文
-				ThreadContext.clearAll();
-			}
-
-		}).start();
-	}
-
-	private String getNodeStatusInfo() {
-		long total = 0;
-		long successAmount = 0;
-		long failureAmount = 0;
-		Counter c = this.counter;
-		if (null != c) {
-			total = c.getTotal();
-			successAmount = c.getSuccessAmount();
-			failureAmount = c.getFailureAmount();
-		}
-		return new StringBuilder("Total=").append(total).append(",SuccessAmount=")
-				.append(successAmount).append(",FailureAmount=").append(failureAmount).toString();
-	}
-
-	@Override
-	public void setLowerNode(Node<KEYOUT, VALUEOUT, ?, ?> lowerNode) {
-		this.lowerNode = lowerNode;
-		lowerStorer = this.lowerNode.getStorer();
-	}
-
-	@Override
-	public Node<KEYOUT, VALUEOUT, ?, ?> getLowerNode() {
-		return this.lowerNode;
-	}
-
-	@Override
-	public Storer<KEYIN, VALUEIN> getStorer() {
-		return this.storer;
-	}
-
-	public Storer<KEYOUT, VALUEOUT> getLowerStorer() {
-		return lowerStorer;
-	}
-
-	public boolean isFirst() {
-		return first;
-	}
-
-	@Override
-	public void setFirst(boolean first) {
-		this.first = first;
-	}
-
-	public int getSleep() {
-		return sleep;
-	}
-
-	@Override
-	public void setSleep(int sleep) {
-		this.sleep = sleep;
-	}
+        implements Node<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SimpleNode.class);
+
+    private String nodeName;
+
+    private Storer<KEYIN, VALUEIN> storer = new Storer<>();
+
+    private Counter counter = new Counter();
+
+    private TaskContext context;
+
+    private NodeExecuter<KEYIN, VALUEIN, KEYOUT, VALUEOUT> executer;
+
+    private SimpleNode<KEYIN, VALUEIN, KEYOUT, VALUEOUT> myself;
+
+    private Node<KEYOUT, VALUEOUT, ?, ?> lowerNode;
+
+    private Storer<KEYOUT, VALUEOUT> lowerStorer;
+
+    private boolean first;
+
+    private int sleep = 10;
+
+    private String traceId = ThreadLocalUtil.getTraceId();
+
+    /**
+     * 构造函数
+     *
+     * @param nodeName
+     * @param context
+     */
+    public SimpleNode(String nodeName, NodeExecuter<KEYIN, VALUEIN, KEYOUT, VALUEOUT> executer,
+                      TaskContext context) {
+        super();
+        this.nodeName = nodeName;
+        this.executer = executer;
+        this.context = context;
+        this.myself = this;
+    }
+
+    /**
+     * 启动
+     *
+     * @author WANGWEI
+     */
+    @Override
+    public void start() {
+        this.startNodeExecuter();
+
+        this.startLogThread();
+    }
+
+    /**
+     * 创建节点执行器
+     *
+     * @author WANGWEI
+     */
+    private void startNodeExecuter() {
+        new Thread(() -> {
+
+            while (true) {
+                traceId = ThreadLocalUtil.getTraceId();
+                // 设置log4j线程上下文
+                ThreadContext.put("TRACE_ID", traceId);
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("{}, {} start", myself.getNodeStatusInfo(), nodeName);
+                }
+
+                counter = new Counter();
+                if (isFirst()) {
+                    execute(null, null);
+                } else {
+                    Set<Entry<KEYIN, VALUEIN>> entrySet = getStorer().getEntrySet();
+
+                    for (Entry<KEYIN, VALUEIN> entry : entrySet) {
+                        execute(entry.getKey(), entry.getValue());
+                    }
+
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("{}, {} end", myself.getNodeStatusInfo(), nodeName);
+                    }
+                }
+
+                try {
+                    Util.sleep(getSleep());
+                } catch (Exception e) {
+                    LOG.error(e.getMessage(), e);
+                }
+
+                // 清理log4j线程上下文
+                ThreadContext.clearAll();
+            }
+
+        }).start();
+    }
+
+    private void execute(KEYIN key, VALUEIN value) {
+        long s = System.currentTimeMillis();
+        try {
+            counter.incrementTotal();
+            List<KeyValuePair<KEYOUT, VALUEOUT>> outList = Lists.newLinkedList();
+            ObjectHolder<Boolean> removable = new ObjectHolder<>(true);
+            executer.execute(key, value, outList, removable, context);
+
+            if (null != outList && null != getLowerStorer()) {
+                for (KeyValuePair<KEYOUT, VALUEOUT> pair : outList) {
+                    getLowerStorer().putElement(pair.getKey(), pair.getValue());
+                }
+            }
+
+            if (null != removable.get() && removable.get()) {
+                if (null != key) {
+                    getStorer().remove(key);
+                }
+            }
+
+            counter.incrementSuccessAmount();
+
+            if (LOG.isInfoEnabled()) {
+                LOG.info("{}, {} execute successful... key = {}, cost {} ms",
+                        myself.getNodeStatusInfo(), nodeName,
+                        key != null ? key.toString() : null,
+                        System.currentTimeMillis() - s);
+            }
+        } catch (Exception e) {
+            counter.incrementFailureAmount();
+
+            LOG.error("{}, {} execute fail... key = {}, value = {}",
+                    myself.getNodeStatusInfo(), nodeName,
+                    key != null ? key.toString() : null,
+                    value != null ? value.toString() : null, e);
+        }
+    }
+
+    /**
+     * 启动日志线程
+     *
+     * @author WANGWEI
+     */
+    private void startLogThread() {
+        new Thread(() -> {
+            while (true) {
+                // 设置log4j线程上下文
+                ThreadContext.put("TRACE_ID", traceId);
+
+                Util.sleep(TimeUnit.SECONDS, 2);
+
+                // 清理log4j线程上下文
+                ThreadContext.clearAll();
+            }
+        }).start();
+    }
+
+    private String getNodeStatusInfo() {
+        long total = 0;
+        long successAmount = 0;
+        long failureAmount = 0;
+        Counter c = this.counter;
+        if (null != c) {
+            total = c.getTotal();
+            successAmount = c.getSuccessAmount();
+            failureAmount = c.getFailureAmount();
+        }
+
+        return new StringBuilder().append("Total = ").append(total)
+                .append(", Success = ").append(successAmount)
+                .append(", Fail = ").append(failureAmount)
+                .toString();
+    }
+
+    @Override
+    public void setLowerNode(Node<KEYOUT, VALUEOUT, ?, ?> lowerNode) {
+        this.lowerNode = lowerNode;
+        lowerStorer = this.lowerNode.getStorer();
+    }
+
+    @Override
+    public Node<KEYOUT, VALUEOUT, ?, ?> getLowerNode() {
+        return this.lowerNode;
+    }
+
+    @Override
+    public Storer<KEYIN, VALUEIN> getStorer() {
+        return this.storer;
+    }
+
+    public Storer<KEYOUT, VALUEOUT> getLowerStorer() {
+        return lowerStorer;
+    }
+
+    public boolean isFirst() {
+        return first;
+    }
+
+    @Override
+    public void setFirst(boolean first) {
+        this.first = first;
+    }
+
+    public int getSleep() {
+        return sleep;
+    }
+
+    @Override
+    public void setSleep(int sleep) {
+        this.sleep = sleep;
+    }
 
 }

+ 77 - 19
examcloud-commons/src/main/java/cn/com/qmth/examcloud/commons/util/FileUtil.java

@@ -1,8 +1,14 @@
 package cn.com.qmth.examcloud.commons.util;
 
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.io.File;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * 文件工具
@@ -13,24 +19,76 @@ import java.util.Comparator;
  */
 public class FileUtil {
 
-	/**
-	 * 排序
-	 *
-	 * @author WANGWEI
-	 * @param files
-	 */
-	public static void sortByLastUpdateTime(File... files) {
-		Arrays.sort(files, new Comparator<File>() {
-			public int compare(File f1, File f2) {
-				long diff = f1.lastModified() - f2.lastModified();
-				if (diff > 0)
-					return 1;
-				else if (diff == 0)
-					return 0;
-				else
-					return -1;
-			}
-		});
-	}
+    private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
+
+    /**
+     * 排序
+     *
+     * @param files
+     * @author WANGWEI
+     */
+    public static void sortByLastUpdateTime(File... files) {
+        Arrays.sort(files, new Comparator<File>() {
+            public int compare(File f1, File f2) {
+                long diff = f1.lastModified() - f2.lastModified();
+                if (diff > 0)
+                    return 1;
+                else if (diff == 0)
+                    return 0;
+                else
+                    return -1;
+            }
+        });
+    }
+
+    /**
+     * 获取文件后缀名,包括"."
+     */
+    public static String getFileSuffix(String fileName) {
+        if (StringUtils.isEmpty(fileName)) {
+            return "";
+        }
+
+        int index = fileName.lastIndexOf(".");
+        if (index > 0) {
+            return fileName.substring(index).toLowerCase();
+        }
+
+        return "";
+    }
+
+    /**
+     * 检查文件后缀名是否有效(忽略大小写)
+     *
+     * @param allowFileSuffix 允许文件后缀名,多个后缀用|分割,示例:jpg|png
+     * @param fileName        当前文件名 或 后缀名,示例:xxx.jpg 或 .jpg
+     * @return
+     */
+    public static boolean checkFileSuffix(String allowFileSuffix, String fileName) {
+        if (StringUtils.isEmpty(allowFileSuffix) || StringUtils.isEmpty(fileName)) {
+            log.warn("allowFileSuffix = {}, fileName = {}", allowFileSuffix, fileName);
+            return false;
+        }
+
+        String regex = String.format("\\.(?:%s)$", allowFileSuffix.toLowerCase());
+        Matcher matcher = Pattern.compile(regex).matcher(fileName.toLowerCase());
+        return matcher.find();
+    }
+
+    /**
+     * 创建文件目录
+     */
+    public static boolean makeDirs(String dirPath) {
+        if (StringUtils.isEmpty(dirPath)) {
+            return false;
+        }
+
+        File dir = new File(dirPath);
+        if (!dir.exists()) {
+            return dir.mkdirs();
+        }
+
+        return true;
+    }
 
 }

+ 159 - 154
examcloud-support/src/main/java/cn/com/qmth/examcloud/support/Constants.java

@@ -15,182 +15,187 @@ package cn.com.qmth.examcloud.support;
  */
 public interface Constants {
 
-	/**
-	 * 系统错误
-	 */
-	String OE_CODE_500 = "OE-000500";
+    /**
+     * 系统错误
+     */
+    String OE_CODE_500 = "OE-000500";
 
-	/**
-	 * 参数错误
-	 */
-	String OE_CODE_400 = "OE-000400";
+    /**
+     * 参数错误
+     */
+    String OE_CODE_400 = "OE-000400";
 
-	/**
-	 * 权限错误
-	 */
-	String OE_CODE_403 = "OE-000403";
+    /**
+     * 权限错误
+     */
+    String OE_CODE_403 = "OE-000403";
 
-	/**
-	 * 考试控制锁
-	 */
-	String EXAM_CONTROL_LOCK_PREFIX = "oe_student:exam_control_lock_studentid_";
+    /**
+     * 考试控制锁
+     */
+    String EXAM_CONTROL_LOCK_PREFIX = "oe_student:exam_control_lock_studentid_";
 
-	/**
-	 * 考试同步控制锁
-	 */
-	String EXAM_SYNC_CONTROL_LOCK_PREFIX = "oe_student:exam_sync_control_lock_studentid_";
+    /**
+     * 考试同步控制锁
+     */
+    String EXAM_SYNC_CONTROL_LOCK_PREFIX = "oe_student:exam_sync_control_lock_studentid_";
 
-	/**
-	 * 考试交卷锁前缀
-	 */
-	String HAND_IN_EXAM_LOCK_PREFIX = "oe_student:hand_in_exam_lock_";
+    /**
+     * 考试交卷锁前缀
+     */
+    String HAND_IN_EXAM_LOCK_PREFIX = "oe_student:hand_in_exam_lock_";
 
-	/**
-	 * 获取人脸活体检测基本信息前缀
-	 */
-	String GET_FACE_BIOPSY_INFO_PREFIX = "oe_student:get_face_biopsy_info_lock_";
+    /**
+     * 获取人脸活体检测基本信息前缀
+     */
+    String GET_FACE_BIOPSY_INFO_PREFIX = "oe_student:get_face_biopsy_info_lock_";
 
-	/**
-	 * 考试数据清理锁
-	 */
-	String EXAM_DATA_CLEAN_LOCK_PREFIX = "exam_data_clean";
+    /**
+     * 考试数据清理锁
+     */
+    String EXAM_DATA_CLEAN_LOCK_PREFIX = "exam_data_clean";
 
-	//
-	String ERROR_MSG = "error_message";
+    //
+    String ERROR_MSG = "error_message";
 
-	/**
-	 * 学生考试session前缀
-	 */
-	String OE_STUDENT_EXAM_SESSION_PREFIX = "oe_student:exam_session_";
+    /**
+     * 学生考试session前缀
+     */
+    String OE_STUDENT_EXAM_SESSION_PREFIX = "oe_student:exam_session_";
 
-	String EXAM_CAPTURE_QUEUE_LOCK_PREFIX = "oe_student:exam_capture_queue_lock_";
+    String EXAM_CAPTURE_QUEUE_LOCK_PREFIX = "oe_student:exam_capture_queue_lock_";
 
-	// 缓存
-	String CACHE_EXAM_STUDENT_PREFIX = "cache_examStudent_";
+    // 缓存
+    String CACHE_EXAM_STUDENT_PREFIX = "cache_examStudent_";
 
-	String CACHE_PHONE_PREFIX = "cache_phone_";
+    String CACHE_PHONE_PREFIX = "cache_phone_";
 
-	String CACHE_STUDENT_PREFIX = "cache_student_";
-
-	String CACHE_ORG_PREFIX = "cache_org_";
+    String CACHE_STUDENT_PREFIX = "cache_student_";
+
+    String CACHE_ORG_PREFIX = "cache_org_";
 
-	String CACHE_COURSE_PREFIX = "cache_course_";
-
-	String isTrue = "true";
+    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";
+    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";
 
-	/**
-	 * 百度在线活体检测QPS超过上限的错误码
-	 */
-	String BAIDU_FACELIVENESS_QPS_LIMIT_EXCEEDED_CODE = "18";
+    /**
+     * 抓拍照片又拍云签名前缀
+     */
+    String EXAM_CAPTURE_PHOTO_UPYUN_SIGN_PREFIX = "OE_EXAM_CAPTURE_PHOTO_UPYUN_SIGN_";
 
-	/**
-	 * 抓拍照片又拍云签名前缀
-	 */
-	String EXAM_CAPTURE_PHOTO_UPYUN_SIGN_PREFIX = "OE_EXAM_CAPTURE_PHOTO_UPYUN_SIGN_";
+    // 抓拍照片的又拍云id
+    String CAPTURE_PHOTO_UPYUN_SITEID = "capturePhoto";
 
-	// 抓拍照片的又拍云id
-	String CAPTURE_PHOTO_UPYUN_SITEID = "capturePhoto";
+    /**
+     * 处理照片高优先级
+     */
+    int PROCESS_CAPTURE_HIGH_PRIORITY = 1;
 
-	/**
-	 * 处理照片高优先级
-	 */
-	int PROCESS_CAPTURE_HIGH_PRIORITY = 1;
+    /**
+     * 照片处理中状态码
+     */
+    String CAPTURE_PROCESSING_STATUS_CODE = "101222";
 
-	/**
-	 * 照片处理中状态码
-	 */
-	String CAPTURE_PROCESSING_STATUS_CODE = "101222";
+    /**
+     * 考试未结束状态码
+     */
+    String EXAM_RECORD_NOT_END_STATUS_CODE = "101333";
 
-	/**
-	 * 考试未结束状态码
-	 */
-	String EXAM_RECORD_NOT_END_STATUS_CODE = "101333";
+    /**
+     * 交卷处理中
+     */
+    String PROCESSING_EXAM_RECORD_CODE = "S-101000";
 
-	/**
-	 * 交卷处理中
-	 */
-	String PROCESSING_EXAM_RECORD_CODE = "S-101000";
+    /**
+     * 通用成功编码
+     */
+    String COMMON_SUCCESS_CODE = "000000";
 
-	/**
-	 * 通用成功编码
-	 */
-	String COMMON_SUCCESS_CODE = "000000";
+    /**
+     * 同步人脸比对结果前缀
+     */
+    String FACE_SYNC_COMPARE_RESULT_PREFIX = "FACE_SYNC_COMPARE_RESULT_";
 
-	/**
-	 * 同步人脸比对结果前缀
-	 */
-	String FACE_SYNC_COMPARE_RESULT_PREFIX = "FACE_SYNC_COMPARE_RESULT_";
+    /**
+     * 活体检测方案key
+     */
+    String IDENTIFICATION_OF_LIVING_BODY_SCHEME_KEY = "IDENTIFICATION_OF_LIVING_BODY_SCHEME";
 
-	/**
-	 * 活体检测方案key
-	 */
-	String IDENTIFICATION_OF_LIVING_BODY_SCHEME_KEY = "IDENTIFICATION_OF_LIVING_BODY_SCHEME";
-
-	/**
-	 * 默认的百度活检阈值
-	 */
-	Double DEFAULT_BAIDU_FACELIVENESS_THRESHOLD = 0.39;
-
-	/**
-	 * 小程序作答文件上传id
-	 */
-	String MINI_PROGRAM_ANWSER_SITEID = "miniProgramAnwser";
-
-	/**
-	 * 证件隐私模式key
-	 */
-	String ID_NUMBER_PRIVATE_MODE_KEY = "ID_NUMBER_PRIVATE_MODE";
-
-	/**
-	 * 违纪-非法考生端应用 缓存Key前缀
-	 */
-	String OE_DISCIPLINE_ILLEGAL_CLIENT = "OE_DISCIPLINE:ILLEGAL_CLIENT_";
-
-	/**
-	 * 违纪-非法数据 缓存Key前缀
-	 */
-	String OE_DISCIPLINE_ILLEGAL_DATA = "OE_DISCIPLINE:ILLEGAL_DATA_";
-
-}
+    /**
+     * 默认的百度活检阈值
+     */
+    Double DEFAULT_BAIDU_FACELIVENESS_THRESHOLD = 0.39;
+
+    /**
+     * 小程序作答文件上传id
+     */
+    String MINI_PROGRAM_ANWSER_SITEID = "miniProgramAnwser";
+
+    /**
+     * 证件隐私模式key
+     */
+    String ID_NUMBER_PRIVATE_MODE_KEY = "ID_NUMBER_PRIVATE_MODE";
+
+    /**
+     * 违纪-非法考生端应用 缓存Key前缀
+     */
+    String OE_DISCIPLINE_ILLEGAL_CLIENT = "OE_DISCIPLINE:ILLEGAL_CLIENT_";
+
+    /**
+     * 违纪-非法数据 缓存Key前缀
+     */
+    String OE_DISCIPLINE_ILLEGAL_DATA = "OE_DISCIPLINE:ILLEGAL_DATA_";
+
+    /**
+     * 客户端请求中User-Agent特征值
+     */
+    String ELECTRON_EXAM_SHELL = "electron-exam-shell";
+
+}