Przeglądaj źródła

网考学生端代码部分迁移

lideyin 5 lat temu
rodzic
commit
2ab62331b6
68 zmienionych plików z 6508 dodań i 0 usunięć
  1. 31 0
      examcloud-core-oe-student-base/.gitignore
  2. 118 0
      examcloud-core-oe-student-base/.mvn/wrapper/MavenWrapperDownloader.java
  3. BIN
      examcloud-core-oe-student-base/.mvn/wrapper/maven-wrapper.jar
  4. 2 0
      examcloud-core-oe-student-base/.mvn/wrapper/maven-wrapper.properties
  5. 310 0
      examcloud-core-oe-student-base/mvnw
  6. 182 0
      examcloud-core-oe-student-base/mvnw.cmd
  7. 129 0
      examcloud-core-oe-student-base/pom.xml
  8. 182 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/Constants.java
  9. 91 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/Model.java
  10. 20 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/Op.java
  11. 70 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/Order.java
  12. 146 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/Searcher.java
  13. 135 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/SpecUtils.java
  14. 265 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/SqlWrapper.java
  15. 97 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/Check.java
  16. 166 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/CommonUtil.java
  17. 101 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/DateUtils.java
  18. 248 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/FileDisposeUtil.java
  19. 95 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/HtmlUtil.java
  20. 91 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/HttpPoolUtil.java
  21. 172 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/JsonMapper.java
  22. 63 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/NewQuestionType.java
  23. 101 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/PagingAndSortingDTO.java
  24. 74 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/PagingAndSortingSpecification.java
  25. 162 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/QEncodeUtil.java
  26. 17 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/QuerySpecification.java
  27. 40 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/QuestionTypeUtil.java
  28. 63 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/RowIterator.java
  29. 180 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/Sentence.java
  30. 126 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/baiduFaceVerify/BaiduFaceVerifyUtil.java
  31. 65 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/baiduFaceVerify/Base64Util.java
  32. 29 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/baiduFaceVerify/GsonUtils.java
  33. 75 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/baiduFaceVerify/HttpUtil.java
  34. 61 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ColumnSetting.java
  35. 48 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelError.java
  36. 98 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelErrorType.java
  37. 34 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelProperty.java
  38. 95 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelReader.java
  39. 7 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelReaderHandle.java
  40. 77 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelUtils.java
  41. 93 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelWriter.java
  42. 43 0
      examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExportUtils.java
  43. 31 0
      examcloud-core-oe-student-dao/.gitignore
  44. 118 0
      examcloud-core-oe-student-dao/.mvn/wrapper/MavenWrapperDownloader.java
  45. BIN
      examcloud-core-oe-student-dao/.mvn/wrapper/maven-wrapper.jar
  46. 2 0
      examcloud-core-oe-student-dao/.mvn/wrapper/maven-wrapper.properties
  47. 310 0
      examcloud-core-oe-student-dao/mvnw
  48. 182 0
      examcloud-core-oe-student-dao/mvnw.cmd
  49. 19 0
      examcloud-core-oe-student-dao/pom.xml
  50. 42 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/ExamFaceLivenessVerifyRepo.java
  51. 96 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/ExamRecordDataRepo.java
  52. 66 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/FaceBiopsyItemRepo.java
  53. 20 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/FaceBiopsyItemStepRepo.java
  54. 29 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/FaceBiopsyRepo.java
  55. 34 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/UniqueRuleHolder.java
  56. 155 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/ExamFaceLivenessVerifyEntity.java
  57. 524 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/ExamRecordDataEntity.java
  58. 82 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/FaceBiopsyEntity.java
  59. 118 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/FaceBiopsyItemEntity.java
  60. 193 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/FaceBiopsyItemStepEntity.java
  61. 46 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/ExamRecordStatus.java
  62. 42 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/ExamType.java
  63. 25 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/FaceBiopsyAction.java
  64. 41 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/FaceBiopsyScheme.java
  65. 17 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/FaceBiopsyType.java
  66. 49 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/FaceVerifyResult.java
  67. 48 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/IsSuccess.java
  68. 17 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/ResourceType.java

+ 31 - 0
examcloud-core-oe-student-base/.gitignore

@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/

+ 118 - 0
examcloud-core-oe-student-base/.mvn/wrapper/MavenWrapperDownloader.java

@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+    private static final String WRAPPER_VERSION = "0.5.5";
+    /**
+     * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+     */
+    private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+            + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+    /**
+     * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+     * use instead of the default one.
+     */
+    private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+            ".mvn/wrapper/maven-wrapper.properties";
+
+    /**
+     * Path where the maven-wrapper.jar will be saved to.
+     */
+    private static final String MAVEN_WRAPPER_JAR_PATH =
+            ".mvn/wrapper/maven-wrapper.jar";
+
+    /**
+     * Name of the property which should be used to override the default download url for the wrapper.
+     */
+    private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+    public static void main(String args[]) {
+        System.out.println("- Downloader started");
+        File baseDirectory = new File(args[0]);
+        System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+        // If the maven-wrapper.properties exists, read it and check if it contains a custom
+        // wrapperUrl parameter.
+        File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+        String url = DEFAULT_DOWNLOAD_URL;
+        if (mavenWrapperPropertyFile.exists()) {
+            FileInputStream mavenWrapperPropertyFileInputStream = null;
+            try {
+                mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+                Properties mavenWrapperProperties = new Properties();
+                mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+                url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+            } catch (IOException e) {
+                System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+            } finally {
+                try {
+                    if (mavenWrapperPropertyFileInputStream != null) {
+                        mavenWrapperPropertyFileInputStream.close();
+                    }
+                } catch (IOException e) {
+                    // Ignore ...
+                }
+            }
+        }
+        System.out.println("- Downloading from: " + url);
+
+        File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+        if (!outputFile.getParentFile().exists()) {
+            if (!outputFile.getParentFile().mkdirs()) {
+                System.out.println(
+                        "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+            }
+        }
+        System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+        try {
+            downloadFileFromURL(url, outputFile);
+            System.out.println("Done");
+            System.exit(0);
+        } catch (Throwable e) {
+            System.out.println("- Error downloading");
+            e.printStackTrace();
+            System.exit(1);
+        }
+    }
+
+    private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+        if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+            String username = System.getenv("MVNW_USERNAME");
+            char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+            Authenticator.setDefault(new Authenticator() {
+                @Override
+                protected PasswordAuthentication getPasswordAuthentication() {
+                    return new PasswordAuthentication(username, password);
+                }
+            });
+        }
+        URL website = new URL(urlString);
+        ReadableByteChannel rbc;
+        rbc = Channels.newChannel(website.openStream());
+        FileOutputStream fos = new FileOutputStream(destination);
+        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+        fos.close();
+        rbc.close();
+    }
+
+}

BIN
examcloud-core-oe-student-base/.mvn/wrapper/maven-wrapper.jar


+ 2 - 0
examcloud-core-oe-student-base/.mvn/wrapper/maven-wrapper.properties

@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.2/apache-maven-3.6.2-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar

+ 310 - 0
examcloud-core-oe-student-base/mvnw

@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        export JAVA_HOME="`/usr/libexec/java_home`"
+      else
+        export JAVA_HOME="/Library/Java/Home"
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`which java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    if [ -n "$MVNW_REPOURL" ]; then
+      jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+    else
+      jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+    fi
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $jarUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+    if $cygwin; then
+      wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+    fi
+
+    if command -v wget > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget "$jarUrl" -O "$wrapperJarPath"
+        else
+            wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+        fi
+    elif command -v curl > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl -o "$wrapperJarPath" "$jarUrl" -f
+        else
+            curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+        fi
+        
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaClass=`cygpath --path --windows "$javaClass"`
+        fi
+        if [ -e "$javaClass" ]; then
+            if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaClass")
+            fi
+            if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

+ 182 - 0
examcloud-core-oe-student-base/mvnw.cmd

@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %DOWNLOAD_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%

+ 129 - 0
examcloud-core-oe-student-base/pom.xml

@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cn.com.qmth.examcloud</groupId>
+        <artifactId>examcloud-core-oe-student</artifactId>
+        <version>2019-SNAPSHOT</version>
+    </parent>
+    <artifactId>examcloud-core-oe-student-base</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-mongodb</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud</groupId>
+            <artifactId>examcloud-web</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud</groupId>
+            <artifactId>examcloud-support</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-exchange-inner-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-core-basic-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-core-examwork-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-core-questions-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-core-oe-admin-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-ws-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-core-marking-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-global-api</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.json</groupId>
+            <artifactId>json</artifactId>
+            <version>20140107</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-validator</artifactId>
+            <version>5.3.6.Final</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.github.openfeign</groupId>
+            <artifactId>feign-okhttp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>logging-interceptor</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.upyun</groupId>
+            <artifactId>java-sdk</artifactId>
+            <version>3.16</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baidu.aip</groupId>
+            <artifactId>java-sdk</artifactId>
+            <version>4.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.esotericsoftware</groupId>
+            <artifactId>reflectasm</artifactId>
+            <version>1.11.3</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 182 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/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.core.oe.student.base;
+
+/**
+ * 系统常量
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+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";
+}

+ 91 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/Model.java

@@ -0,0 +1,91 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-20 10:57:14.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.student.base.jpa;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+public class Model {
+
+    public static <T> T of(Optional<T> optional) {
+        try {
+            if (optional != null) {
+                return optional.get();
+            }
+        } catch (NoSuchElementException e) {
+            //ignore
+        }
+        return null;
+    }
+
+    public static String splitFieldName(String fieldName) {
+        if (fieldName == null) {
+            return null;
+        }
+        StringBuffer result = new StringBuffer();
+        char[] chars = fieldName.toCharArray();
+        final int A = 65, Z = 90, DOT = 46;
+        for (int i = 0; i < chars.length; i++) {
+            Character c = chars[i];
+            if (i == 0) {
+                result.append(c);
+                continue;
+            }
+            if (c == DOT) {
+                result.append("_");
+                continue;
+            }
+            if (c >= A && c <= Z) {
+                result.append("_");
+            }
+            result.append(c);
+        }
+        return result.toString().toUpperCase();
+    }
+
+    public static List<String> parseFields(final Class clazz) {
+        return parseFields(clazz, null);
+    }
+
+    public static List<String> parseFields(final Class clazz, final Class rootClazz) {
+        List<String> fieldNames = new ArrayList<>();
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field : fields) {
+            if (Modifier.isStatic(field.getModifiers())) {
+                continue;
+            }
+            if (Modifier.isFinal(field.getModifiers())) {
+                continue;
+            }
+            Class<?> fieldType = field.getType();
+            String fieldName = field.getName();
+            if (rootClazz != null && rootClazz.isAssignableFrom(fieldType)) {
+                List<String> subFieldNames = parseFields(fieldType, rootClazz);
+                if (subFieldNames.size() == 0) {
+                    continue;
+                }
+                fieldNames.addAll(subFieldNames.stream()
+                        .map(subName -> subName = fieldName + "." + subName)
+                        .collect(Collectors.toList()));
+            } else {
+                fieldNames.add(fieldName);
+            }
+        }
+        return fieldNames;
+    }
+
+}

+ 20 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/Op.java

@@ -0,0 +1,20 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-17 15:21:52.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.student.base.jpa;
+
+/**
+ * 操作枚举类
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+public enum Op {
+
+    EQ, NOT_EQ, GT, LT, GTE, LTE, LIKE, L_LIKE, R_LIKE, IS_NULL, NOT_NULL, IN, NOT_IN
+
+}

+ 70 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/Order.java

@@ -0,0 +1,70 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-17 15:21:52.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.student.base.jpa;
+
+import org.springframework.data.domain.Sort;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 排序条件类
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+public class Order implements Serializable {
+    protected static final long serialVersionUID = -1L;
+    private String fieldName;
+    private Sort.Direction direction;
+
+    private List<Order> orders = new ArrayList<>();
+
+    public Order asc(String fieldName) {
+        orders.add(new Order(fieldName, Sort.Direction.ASC));
+        return this;
+    }
+
+    public Order desc(String fieldName) {
+        orders.add(new Order(fieldName, Sort.Direction.DESC));
+        return this;
+    }
+
+    public Sort build() {
+        if (orders.size() == 0) {
+            return null;
+        }
+        List<Sort.Order> list = new ArrayList<>();
+        for (Order order : orders) {
+            list.add(new Sort.Order(order.getDirection(), order.getFieldName()));
+        }
+        return new Sort(list);
+    }
+
+    private Order(String fieldName, Sort.Direction direction) {
+        if (fieldName == null || "".equals(fieldName.trim())) {
+            throw new IllegalArgumentException("FieldName must be not empty.");
+        }
+        this.fieldName = fieldName.trim();
+        this.direction = direction;
+    }
+
+    public Order() {
+
+    }
+
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    public Sort.Direction getDirection() {
+        return direction;
+    }
+
+}

+ 146 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/Searcher.java

@@ -0,0 +1,146 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-17 15:21:52.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.student.base.jpa;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 查询条件类
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+public class Searcher implements Serializable {
+    protected static final long serialVersionUID = -1L;
+    /**
+     * 属性名
+     */
+    private String fieldName;
+    /**
+     * 属性值
+     */
+    private Object value;
+    /**
+     * 操作的表达式
+     */
+    private Op op;
+
+    private List<Searcher> searchers = new ArrayList<>();
+
+    public <T> Searcher eq(String fieldName, T value) {
+        searchers.add(new Searcher(fieldName, value, Op.EQ));
+        return this;
+    }
+
+    public <T> Searcher notEq(String fieldName, T value) {
+        searchers.add(new Searcher(fieldName, value, Op.NOT_EQ));
+        return this;
+    }
+
+    public <T> Searcher gt(String fieldName, T value) {
+        searchers.add(new Searcher(fieldName, value, Op.GT));
+        return this;
+    }
+
+    public <T> Searcher gte(String fieldName, T value) {
+        searchers.add(new Searcher(fieldName, value, Op.GTE));
+        return this;
+    }
+
+    public <T> Searcher lt(String fieldName, T value) {
+        searchers.add(new Searcher(fieldName, value, Op.LT));
+        return this;
+    }
+
+    public <T> Searcher lte(String fieldName, T value) {
+        searchers.add(new Searcher(fieldName, value, Op.LTE));
+        return this;
+    }
+
+    public <T> Searcher like(String fieldName, T value) {
+        searchers.add(new Searcher(fieldName, value, Op.LIKE));
+        return this;
+    }
+
+    public <T> Searcher leftLike(String fieldName, T value) {
+        searchers.add(new Searcher(fieldName, value, Op.L_LIKE));
+        return this;
+    }
+
+    public <T> Searcher rightLike(String fieldName, T value) {
+        searchers.add(new Searcher(fieldName, value, Op.R_LIKE));
+        return this;
+    }
+
+    public Searcher in(String fieldName, Collection<?> values) {
+        if (values == null || values.size() == 0) {
+            throw new IllegalArgumentException("Values must be not empty.");
+        }
+        if (values.size() == 1) {
+            searchers.add(new Searcher(fieldName, values.iterator().next(), Op.EQ));
+        } else {
+            searchers.add(new Searcher(fieldName, values.toArray(), Op.IN));
+        }
+        return this;
+    }
+
+    public Searcher notIn(String fieldName, Collection<?> values) {
+        if (values == null || values.size() == 0) {
+            throw new IllegalArgumentException("Values must be not empty.");
+        }
+        if (values.size() == 1) {
+            searchers.add(new Searcher(fieldName, values.iterator().next(), Op.NOT_EQ));
+        } else {
+            searchers.add(new Searcher(fieldName, values.toArray(), Op.NOT_IN));
+        }
+        return this;
+    }
+
+    public Searcher isNull(String fieldName) {
+        searchers.add(new Searcher(fieldName, null, Op.IS_NULL));
+        return this;
+    }
+
+    public Searcher notNull(String fieldName) {
+        searchers.add(new Searcher(fieldName, null, Op.NOT_NULL));
+        return this;
+    }
+
+    public List<Searcher> build() {
+        return searchers;
+    }
+
+    private Searcher(String fieldName, Object value, Op op) {
+        if (fieldName == null || "".equals(fieldName.trim())) {
+            throw new IllegalArgumentException("FieldName must be not empty.");
+        }
+        this.fieldName = fieldName.trim();
+        this.value = value;
+        this.op = op;
+    }
+
+    public Searcher() {
+
+    }
+
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public Op getOp() {
+        return op;
+    }
+
+}

+ 135 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/SpecUtils.java

@@ -0,0 +1,135 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-17 15:21:52.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.student.base.jpa;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.jpa.domain.Specifications;
+
+import javax.persistence.criteria.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JPA查询工具类
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/17
+ */
+public class SpecUtils {
+    /**
+     * 默认当前页数
+     */
+    public static final int DEFAULT_PAGE_NO = 1;
+    /**
+     * 默认每页条数
+     */
+    public static final int DEFAULT_PAGE_SIZE = 10;
+
+    public static Pageable buildPageable(Integer pageNo, Integer pageSize) {
+        return buildPageable(pageNo, pageSize, Sort.unsorted());
+    }
+
+    public static Pageable buildPageable(Integer pageNo, Integer pageSize, Sort sort) {
+        if (pageNo == null || pageNo < 1) {
+            pageNo = DEFAULT_PAGE_NO;
+        }
+        if (pageSize == null || pageSize < 1) {
+            pageSize = DEFAULT_PAGE_SIZE;
+        }
+        return PageRequest.of (pageNo - 1, pageSize, sort);
+    }
+
+    public static <T> Specification<T> buildSearchers(final Class<T> clazz, final List<Searcher> searchers) {
+        return buildSearchers(clazz, searchers, false);
+    }
+
+    public static <T> Specification<T> buildSearchers(final Class<T> clazz, final List<Searcher> searchers, final boolean isOR) {
+        if (searchers == null || searchers.size() == 0) {
+            return null;
+        }
+        return new Specification<T>() {
+            @Override
+            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
+                List<Predicate> predicates = new ArrayList<>();
+                for (Searcher filter : searchers) {
+                    if (filter.getFieldName() == null || "".equals(filter.getFieldName())) {
+                        continue;
+                    }
+                    String[] names = filter.getFieldName().split("\\.");
+                    Path expression = root.get(names[0]);
+                    for (int i = 1; i < names.length; i++) {
+                        expression = expression.get(names[i]);
+                    }
+                    switch (filter.getOp()) {
+                        case EQ:
+                            predicates.add(builder.equal(expression, filter.getValue()));
+                            break;
+                        case NOT_EQ:
+                            predicates.add(builder.notEqual(expression, filter.getValue()));
+                            break;
+                        case GT:
+                            predicates.add(builder.greaterThan(expression, (Comparable) filter.getValue()));
+                            break;
+                        case LT:
+                            predicates.add(builder.lessThan(expression, (Comparable) filter.getValue()));
+                            break;
+                        case GTE:
+                            predicates.add(builder.greaterThanOrEqualTo(expression, (Comparable) filter.getValue()));
+                            break;
+                        case LTE:
+                            predicates.add(builder.lessThanOrEqualTo(expression, (Comparable) filter.getValue()));
+                            break;
+                        case LIKE:
+                            predicates.add(builder.like(expression, "%" + filter.getValue() + "%"));
+                            break;
+                        case L_LIKE:
+                            predicates.add(builder.like(expression, "%" + filter.getValue()));
+                            break;
+                        case R_LIKE:
+                            predicates.add(builder.like(expression, filter.getValue() + "%"));
+                            break;
+                        case IS_NULL:
+                            predicates.add(builder.isNull(expression));
+                            break;
+                        case NOT_NULL:
+                            predicates.add(builder.isNotNull(expression));
+                            break;
+                        case IN:
+                            predicates.add(expression.in((Object[]) filter.getValue()));
+                            break;
+                        case NOT_IN:
+                            predicates.add(expression.in((Object[]) filter.getValue()).not());
+                            break;
+                    }
+                }
+                if (!predicates.isEmpty()) {
+                    if (isOR) {
+                        //将所有条件用 or 联合起来
+                        return builder.or(predicates.toArray(new Predicate[predicates.size()]));
+                    } else {
+                        //将所有条件用 and 联合起来
+                        return builder.and(predicates.toArray(new Predicate[predicates.size()]));
+                    }
+                }
+                return builder.conjunction();
+            }
+        };
+    }
+
+    public static Specification andMerge(Specification target1, Specification target2) {
+        return Specifications.where(target1).and(target2);
+    }
+
+    public static Specification orMerge(Specification target1, Specification target2) {
+        return Specifications.where(target1).or(target2);
+    }
+
+}

+ 265 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/jpa/SqlWrapper.java

@@ -0,0 +1,265 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-28 09:38:53.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.student.base.jpa;
+
+import java.util.Collection;
+
+/**
+ * 原生SQL包装类
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/28
+ */
+public class SqlWrapper {
+    private static final String SELECT = "SELECT ";
+    private static final String UPDATE = "UPDATE ";
+    private static final String DELETE = "DELETE ";
+    private static final String SET = " SET ";
+    private static final String COUNT = " COUNT ";
+    private static final String SUM = " SUM ";
+    private static final String DISTINCT = " DISTINCT ";
+    private static final String FROM = " FROM ";
+    private static final String WHERE = " WHERE ";
+    private static final String INNER_JOIN = " INNER JOIN ";
+    private static final String LEFT_JOIN = " LEFT JOIN ";
+    private static final String UNION_ALL = " UNION ALL ";
+    private static final String UNION = " UNION ";
+    private static final String ON = " ON ";
+    private static final String AS = " AS ";
+    private static final String AND = " AND ";
+    private static final String OR = " OR ";
+    private static final String LIKE = " LIKE ";
+    private static final String IN = " IN ";
+    private static final String NOT_IN = " NOT IN ";
+    private static final String IS_NULL = " IS NULL ";
+    private static final String IS_NOT_NULL = " IS NOT NULL ";
+    private static final String GROUP_BY = " GROUP BY ";
+    private static final String ORDER_BY = " ORDER BY ";
+    private static final String ASC = " ASC ";
+    private static final String DESC = " DESC ";
+
+    private StringBuilder sql = new StringBuilder();
+
+    public SqlWrapper select() {
+        sql.append(SELECT).append("*");
+        return this;
+    }
+
+    public SqlWrapper select(String columns) {
+        /* 多个按逗号分隔 */
+        sql.append(SELECT).append(columns);
+        return this;
+    }
+
+    public SqlWrapper delete() {
+        sql.append(DELETE);
+        return this;
+    }
+
+    public SqlWrapper update(String tableName) {
+        sql.append(UPDATE).append(tableName);
+        return this;
+    }
+
+    public SqlWrapper set() {
+        sql.append(SET);
+        return this;
+    }
+
+    public SqlWrapper count(String fieldName) {
+        sql.append(COUNT).append("(").append(fieldName).append(")");
+        return this;
+    }
+
+    public SqlWrapper sum(String fieldName) {
+        sql.append(SUM).append("(").append(fieldName).append(")");
+        return this;
+    }
+
+    public SqlWrapper distinct(String fieldName) {
+        sql.append(DISTINCT).append(fieldName);
+        return this;
+    }
+
+    public SqlWrapper from(String tableName) {
+        sql.append(FROM).append(tableName);
+        return this;
+    }
+
+    public SqlWrapper innerJoin(String tableName) {
+        sql.append(INNER_JOIN).append(tableName);
+        return this;
+    }
+
+    public SqlWrapper leftJoin(String tableName) {
+        sql.append(LEFT_JOIN).append(tableName);
+        return this;
+    }
+
+    public SqlWrapper on(String fieldName, String refFieldName) {
+        sql.append(ON).append(fieldName).append(" = ").append(refFieldName);
+        return this;
+    }
+
+    public SqlWrapper where() {
+        sql.append(WHERE);
+        return this;
+    }
+
+    public SqlWrapper and() {
+        sql.append(AND);
+        return this;
+    }
+
+    public SqlWrapper or() {
+        sql.append(OR);
+        return this;
+    }
+
+    public SqlWrapper like(String fieldName, CharSequence value) {
+        sql.append(fieldName).append(LIKE).append("'%").append(value).append("%'");
+        return this;
+    }
+
+    public SqlWrapper like(String fieldName, Number value) {
+        sql.append(fieldName).append(LIKE).append("'%").append(value).append("%'");
+        return this;
+    }
+
+    public SqlWrapper eq(String fieldName, Number value) {
+        sql.append(fieldName).append(" = ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper eq(String fieldName, CharSequence value) {
+        sql.append(fieldName).append(" = ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper notEq(String fieldName, CharSequence value) {
+        sql.append(fieldName).append(" != ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper notEq(String fieldName, Number value) {
+        sql.append(fieldName).append(" != ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper gt(String fieldName, CharSequence value) {
+        sql.append(fieldName).append(" > ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper gt(String fieldName, Number value) {
+        sql.append(fieldName).append(" > ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper gte(String fieldName, CharSequence value) {
+        sql.append(fieldName).append(" >= ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper gte(String fieldName, Number value) {
+        sql.append(fieldName).append(" >= ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper lt(String fieldName, CharSequence value) {
+        sql.append(fieldName).append(" < ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper lt(String fieldName, Number value) {
+        sql.append(fieldName).append(" < ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper lte(String fieldName, CharSequence value) {
+        sql.append(fieldName).append(" <= ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper lte(String fieldName, Number value) {
+        sql.append(fieldName).append(" <= ").append("'").append(value).append("'");
+        return this;
+    }
+
+    public SqlWrapper in(String fieldName, Collection<?> values) {
+        String str = this.spilt(values);
+        sql.append(fieldName).append(IN).append("(").append(str).append(")");
+        return this;
+    }
+
+    public SqlWrapper notIn(String fieldName, Collection<?> values) {
+        String str = this.spilt(values);
+        sql.append(fieldName).append(NOT_IN).append("(").append(str).append(")");
+        return this;
+    }
+
+    public SqlWrapper isNull(String fieldName) {
+        sql.append(fieldName).append(IS_NULL);
+        return this;
+    }
+
+    public SqlWrapper isNotNull(String fieldName) {
+        sql.append(fieldName).append(IS_NOT_NULL);
+        return this;
+    }
+
+    public SqlWrapper orderBy(String columns, boolean isDesc) {
+        /* 多个按逗号分隔 */
+        if (isDesc) {
+            sql.append(ORDER_BY).append(columns).append(DESC);
+        } else {
+            sql.append(ORDER_BY).append(columns).append(ASC);
+        }
+        return this;
+    }
+
+    public SqlWrapper groupBy(String columns) {
+        /* 多个按逗号分隔 */
+        sql.append(GROUP_BY).append(columns);
+        return this;
+    }
+
+    public SqlWrapper as(String name) {
+        sql.append(AS).append(name);
+        return this;
+    }
+
+    public SqlWrapper append(String str) {
+        sql.append(str);
+        return this;
+    }
+
+    public String build() {
+        return sql.toString();
+    }
+
+    private String spilt(Collection<?> values) {
+        if (values == null || values.size() == 0) {
+            throw new IllegalArgumentException("Values must be not empty.");
+        }
+        int index = 0, total = values.size();
+        StringBuilder str = new StringBuilder();
+        for (Object value : values) {
+            if (!(value instanceof CharSequence || value instanceof Number)) {
+                throw new IllegalArgumentException("Values must be charSequence or number.");
+            }
+            str.append(value.toString());
+            if (index < (total - 1)) {
+                str.append(",");
+            }
+            index++;
+        }
+        return str.toString();
+    }
+
+}

+ 97 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/Check.java

@@ -0,0 +1,97 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-22 11:21:09.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+
+import static cn.com.qmth.examcloud.core.oe.student.base.Constants.OE_CODE_400;
+
+
+/**
+ * 参数校验类
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/22
+ */
+public class Check {
+
+    public static void isNull(Object obj, String message) {
+        if (obj == null) {
+            throw new StatusException(OE_CODE_400, message);
+        }
+    }
+
+    public static void isEmpty(Object obj, String message) {
+        if (isEmpty(obj)) {
+            throw new StatusException(OE_CODE_400, message);
+        }
+    }
+
+    public static void isEmpty(Object[] array, String message) {
+        if (isEmpty(array)) {
+            throw new StatusException(OE_CODE_400, message);
+        }
+    }
+
+    public static void isEmpty(Collection<?> collection, String message) {
+        if (isEmpty(collection)) {
+            throw new StatusException(OE_CODE_400, message);
+        }
+    }
+
+    public static void isEmpty(Map<?, ?> map, String message) {
+        if (isEmpty(map)) {
+            throw new StatusException(OE_CODE_400, message);
+        }
+    }
+
+    public static void isBlank(CharSequence str, String message) {
+        if (isBlank(str)) {
+            throw new StatusException(OE_CODE_400, message);
+        }
+    }
+
+    public static void isFalse(Boolean expression, String message) {
+        if (expression == null || !expression) {
+            throw new StatusException(OE_CODE_400, message);
+        }
+    }
+
+    private static boolean isEmpty(Object obj) {
+        if (obj == null) {
+            return true;
+        } else if (obj instanceof CharSequence) {
+            return ((CharSequence) obj).length() == 0;
+        } else if (obj.getClass().isArray()) {
+            return Array.getLength(obj) == 0;
+        } else if (obj instanceof Collection) {
+            return ((Collection) obj).isEmpty();
+        } else {
+            return obj instanceof Map ? ((Map) obj).isEmpty() : false;
+        }
+    }
+
+    private static boolean isBlank(CharSequence cs) {
+        int strLen;
+        if (cs != null && (strLen = cs.length()) != 0) {
+            for (int i = 0; i < strLen; ++i) {
+                if (!Character.isWhitespace(cs.charAt(i))) {
+                    return false;
+                }
+            }
+            return true;
+        } else {
+            return true;
+        }
+    }
+
+}

+ 166 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/CommonUtil.java

@@ -0,0 +1,166 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.reflect.Field;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+
+/**
+ * @author  	chenken
+ * @date    	2017年12月13日 下午3:39:56
+ * @company 	QMTH
+ * @description CommonUtil.java
+ */
+public class CommonUtil {
+	public static double nullToZero(Double score) {
+        if (score == null) {
+            return 0D;
+        } else {
+            return score;
+        }
+    }
+	/**
+	 * 计算number1 - number2之间的随机数
+	 * @param number1
+	 * @param number2
+	 * @return
+	 */
+	public static int calculationRandomNumber(int number1,int number2){
+		if(number1>number2){
+			throw new RuntimeException("number1 must less than number2");
+		}
+		return (int)(Math.random()*(number2-number1+1)+number1);
+	}
+
+
+	/**
+	 * 格式化日期精确到秒
+	 * @param date	日期
+	 * @param format yyyy-MM-dd  yyyy-MM-dd HH:mm:ss
+	 * @return
+	 */
+	public static String getDateStrWithSecond(Date date){
+		if(date == null){
+			return null;
+		}
+		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+		return sdf.format(date);
+	}
+	
+	/**
+	 * 格式化日期精确到日
+	 * @param date	日期
+	 * @param format yyyy-MM-dd  yyyy-MM-dd HH:mm:ss
+	 * @return
+	 */
+	public static String getDateStrWithoutSecond(Date date){
+		if(date == null){
+			return null;
+		}
+		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+		return sdf.format(date);
+	}
+	
+	/**
+	 * 生成随机数
+	 * @param several  个数
+	 * @return
+	 */
+	public static String makeRandomNum(int several){
+		StringBuffer sb = new StringBuffer();
+		for(int i = 0;i<several;i++){
+			sb.append(new Random().nextInt(10));
+		}
+		return sb.toString();
+	}
+	
+	/**
+     * 重新排列 Integer数组
+     * @param arr
+     * @return
+     */
+	public static Integer[] reorderArray(Integer[] arr){
+    	int temp;
+		for(int i = 0;i<arr.length;i++){
+		     int e=(int) Math.round((arr.length-1)*Math.random());
+		     temp = arr[e];
+		     arr[e] = arr[arr.length-1];
+		     arr[arr.length-1] = temp;
+		}
+		return arr;
+    }
+	
+	public static boolean isTrue(Object obj){
+		if(obj == null){
+			return false;
+		}
+		if(obj instanceof Boolean){
+			return (boolean) obj;
+		}else{
+			return false;
+		}
+	}
+	
+    public static Map<String, Object> getKeyAndValue(Object obj) {
+        Map<String, Object> map = new HashMap<String, Object>();
+        // 得到类对象
+        Class<?> userCla = (Class<?>) obj.getClass();
+        /* 得到类中的所有属性集合 */
+        Field[] fs = userCla.getDeclaredFields();
+        for (int i = 0; i < fs.length; i++) {
+            Field f = fs[i];
+            f.setAccessible(true); // 设置些属性是可以访问的
+            Object val = new Object();
+            try {
+                val = f.get(obj);
+                // 得到此属性的值
+                map.put(f.getName(), val);// 设置键值
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        }
+        System.out.println("单个对象的所有键值==反射==" + map.toString());
+        return map;
+    }
+	
+    /**
+     * 判断字符串是不是json格式
+     * @param content
+     * @return
+     */
+    public static boolean isJson(String content) {
+        try {
+            JSONObject.parseObject(content);
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+    
+    /**
+     * 判断字符串是否为null,或者为 "null"
+     * @param content
+     * @return
+     */
+    public static boolean isBlank(String content){
+    	if(StringUtils.isBlank(content) || "null".equals(content)){
+    		return true;
+    	}
+    	return false;
+    }
+    
+	public static void main(String[] args) {
+		for(int i = 0;i<200;i++){
+			System.out.println(i+":"+calculationRandomNumber(1,2));
+		}
+	}
+}
+

+ 101 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/DateUtils.java

@@ -0,0 +1,101 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-30 15:54:06.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author: fengdesheng
+ * @since: 2018/8/30
+ */
+public class DateUtils {
+    public static final String PATTERN = "yyyy-MM-dd";
+    public static final String FULL_PATTERN = "yyyy-MM-dd HH:mm:ss";
+
+    /**
+     * 日期字符串转日期对象
+     *
+     * @param dateStr 支持格式:yyyy/MM/dd 或 yyyy-MM-dd
+     * @return
+     */
+    public static Date parse(String dateStr) {
+        if (dateStr == null) {
+            return null;
+        }
+        try {
+            dateStr = dateStr.replaceAll("/", "-");
+            if (dateStr.length() == 10) {
+                return new SimpleDateFormat(PATTERN).parse(dateStr);
+            }
+            return new SimpleDateFormat(FULL_PATTERN).parse(dateStr);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static String format(Date date) {
+        if (date == null) {
+            return null;
+        }
+        return new SimpleDateFormat(FULL_PATTERN).format(date);
+    }
+
+    /**
+     * 计算时长
+     */
+    public static String diff(long diff) {
+        long nd = 1000 * 24 * 60 * 60;//一天的毫秒数
+        long nh = 1000 * 60 * 60;//一小时的毫秒数
+        long nm = 1000 * 60;//一分钟的毫秒数
+        long ns = 1000;//一秒钟的毫秒数
+        long day = diff / nd;//计算差多少天
+        long hour = diff % nd / nh;//计算差多少小时
+        long min = diff % nd % nh / nm;//计算差多少分钟
+        long sec = diff % nd % nh % nm / ns;//计算差多少秒//输出结果
+
+        StringBuilder sb = new StringBuilder();
+        if (day != 0) {
+            sb.append(day + "d ");
+        }
+        if (hour != 0) {
+            sb.append(hour + "h");
+        }
+        if (min != 0) {
+            sb.append(min + "m");
+        }
+        if (sec != 0) {
+            sb.append(sec + "s");
+        }
+        return sb.toString();
+    }
+    /**
+     * 在指定日期添加固定小时数
+     * @param date 日期
+     * @param hours 小时数(可为负数)
+     * @return
+     */
+    public static Date addHours(Date date,int hours) {
+        return addDateByField(date, Calendar.HOUR_OF_DAY,hours);
+    }
+    /**
+     * 给日期指定区块添加相应的数值
+     * @param date 日期
+     * @param field 日期区块
+     * @param amount 数值(可为负数)
+     * @return
+     */
+    public static Date addDateByField(Date date,int field,int amount) {
+        Calendar ca = Calendar.getInstance();
+        ca.setTime(date);
+        ca.add(field,amount);
+        return ca.getTime();
+    }
+}

+ 248 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/FileDisposeUtil.java

@@ -0,0 +1,248 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+
+/**
+ * @author chenken
+ * @date 2017年7月17日 上午9:36:32
+ * @company QMTH
+ * @description FileUtil.java
+ */
+public class FileDisposeUtil {
+	
+	private static final Logger logger = LoggerFactory.getLogger(FileDisposeUtil.class);
+	
+	/**
+	 * 将网络文件保存到本地
+	 * @param fileUrl		   网络文件URL
+	 * @param localFilePath  例如D:/123.txt
+	 * @return
+	 */
+	public static boolean saveUrlAs(String fileUrl,String localFilePath) {
+		HttpURLConnection connection = null;
+		FileOutputStream fileOutputStream = null;
+		DataOutputStream dataOutputStream = null;
+		DataInputStream dataInputStream = null;
+		try {
+			URL url = new URL(fileUrl);
+			connection = (HttpURLConnection) url.openConnection();
+			dataInputStream = new DataInputStream(connection.getInputStream());
+			fileOutputStream = new FileOutputStream(localFilePath);
+			dataOutputStream = new DataOutputStream(fileOutputStream);
+			byte[] buffer = new byte[4096];
+			int count = 0;
+			while ((count = dataInputStream.read(buffer)) > 0) {
+				dataOutputStream.write(buffer, 0, count);
+			}
+			return true;
+		} catch (Exception e) {
+			return false;
+		}finally {
+			try {
+				if(fileOutputStream!=null){
+					fileOutputStream.flush();
+					fileOutputStream.close();
+					fileOutputStream = null;
+				}
+				if (dataOutputStream != null) {
+					dataOutputStream.flush();
+					dataOutputStream.close();
+					dataOutputStream = null;
+				}
+				if (dataInputStream != null) {
+					dataInputStream.close();
+					dataInputStream = null;
+				}
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+			if (connection != null) {
+				connection.disconnect();
+				connection = null;
+			}
+		}
+	}
+	
+	/**
+	 * 下载服务器上的文件
+	 * @param filename		文件名称
+	 * @param fullFilePath	文件全路径
+	 * @param response
+	 */
+	public static void downloadFile(String filename,String fullFilePath,HttpServletResponse response){
+		FileInputStream input = null;
+		OutputStream output = null;
+		try {
+	        //设置文件MIME类型  
+			response.setContentType(getContentType(filename));
+	        response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(filename,"UTF-8"));
+	        //读取目标文件,通过response将目标文件写到客户端  
+	        input =  new FileInputStream(fullFilePath);
+			output =  response.getOutputStream();  
+	        //写文件  
+			byte[] b = new byte[2048];
+            int len;
+            while ((len = input.read(b)) != -1) {
+            	output.write(b, 0, len);
+            } 
+            response.setHeader("Content-Length", String.valueOf(input.getChannel().size()));
+	        input.close();  
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+	/**
+	 * 获得文件MIME类型
+	 * @param filename
+	 * @return
+	 */
+	public static String getContentType(String filename){
+		String type = null;
+		Path path = Paths.get(filename);
+		try {
+			type = Files.probeContentType(path);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return type;
+	}
+
+	/**
+	 * 将存放在sourceFilePath目录下的源文件,打包成fileName名称的zip文件,并存放到zipFilePath路径下
+	 * 
+	 * @param sourceFilePath
+	 *            :待压缩的文件夹路径
+	 * @param zipFilePath
+	 *            :压缩后zip文件的存放路径
+	 * @param fileName
+	 *            :zip文件的名称
+	 * @return
+	 */
+	public static boolean fileToZip(String sourceFilePath, String zipFilePath,String fileName) {
+		logger.info("压缩"+sourceFilePath+"目录开始");
+		boolean flag = false;
+		File sourceFile = new File(sourceFilePath);
+		FileInputStream fis = null;
+		BufferedInputStream bis = null;
+		FileOutputStream fos = null;
+		ZipOutputStream zos = null;
+		if (sourceFile.exists() == false) {
+			logger.error("待压缩的文件目录:" + sourceFilePath + "不存在.");
+		} else {
+			try {
+				File zipFile = new File(zipFilePath+File.separator+fileName+".zip");
+				if (zipFile.exists()) {
+					logger.error(zipFilePath + "目录下存在名字为:"+fileName+".zip"+"打包文件.");
+				} else {
+					File[] sourceFiles = sourceFile.listFiles();
+					if (null == sourceFiles || sourceFiles.length < 1) {
+						logger.error("待压缩的文件目录:" + sourceFilePath+ "里面不存在文件,无需压缩.");
+					} else {
+						fos = new FileOutputStream(zipFile);
+						zos = new ZipOutputStream(new BufferedOutputStream(fos));
+						byte[] bufs = new byte[1024 * 10];
+						for (int i = 0; i < sourceFiles.length; i++) {
+							try{
+								//创建ZIP实体,并添加进压缩包
+								String fileEncode = System.getProperty("file.encoding");
+								String name = new String(sourceFiles[i].getName().getBytes(fileEncode),"UTF-8");
+								ZipEntry zipEntry = new ZipEntry(name);
+								zos.putNextEntry(zipEntry);
+								//读取待压缩的文件并写进压缩包里
+								fis = new FileInputStream(sourceFiles[i]);
+								bis = new BufferedInputStream(fis, 1024 * 10);
+								int read = 0;
+								while ((read = bis.read(bufs, 0, 1024 * 10)) != -1) {
+									zos.write(bufs, 0, read);
+								}
+								zos.flush();
+							}catch(Exception e){
+								e.printStackTrace();
+							}finally{
+								IOUtils.closeQuietly(bis);
+								IOUtils.closeQuietly(fis);
+							}
+						}
+						flag = true;
+					}
+				}
+			} catch (Exception e) {
+				e.printStackTrace();
+			} finally {
+				IOUtils.closeQuietly(bis);
+				IOUtils.closeQuietly(fis);
+				IOUtils.closeQuietly(zos);
+				IOUtils.closeQuietly(fos);
+			}
+		}
+		logger.info("压缩"+sourceFilePath+"目录完成");
+		return flag;
+	}
+	
+	public static void createDirectory(String downloadDirectory) {
+		File directory = new File(downloadDirectory);
+		if(!directory.exists()){
+			directory.mkdirs();
+		}else{
+			FileUtils.deleteQuietly(directory);
+			directory.mkdirs();
+		}
+	}
+	
+	/**
+	 * 获得文件的byte数组
+	 * @param filePath
+	 * @return
+	 * @throws IOException
+	 */
+	public static byte[] getBytes(String filePath) throws IOException{  
+    	File file = new File(filePath);
+        if (!file.exists()) {
+            throw new FileNotFoundException(filePath);
+        }
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(((int) file.length()));
+        BufferedInputStream in = null;
+        try {
+            in = new BufferedInputStream(new FileInputStream(file));
+            int bufSize = 1024;
+            byte[] buffer = new byte[bufSize];
+            int len = 0;
+            while (-1 != (len = in.read(buffer, 0, bufSize))) {
+                bos.write(buffer, 0, len);
+            }
+            return bos.toByteArray();
+        } finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            bos.close();
+        }
+    }
+	
+	public static void main(String[] args) {
+		System.out.println(System.getProperty("sun.jnu.encoding"));
+		System.out.println(System.getProperty("file.encoding"));
+	}
+}

+ 95 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/HtmlUtil.java

@@ -0,0 +1,95 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * Created by yuanpan on 2017/5/16.
+ */
+public class HtmlUtil {
+
+    /**
+     * 去掉html中的span
+     *
+     * @param html
+     * @return
+     */
+    public static String removeSpan(String html) {
+        if (StringUtils.isEmpty(html)) {
+            return "";
+        } else {
+            return html.replaceAll("<span>", "").replaceAll("</span>", "");
+        }
+    }
+
+    public static String ToCH(int intInput) {
+        String si = String.valueOf(intInput);
+        String sd = "";
+        if (si.length() == 1) // 個
+        {
+            sd += GetCH(intInput);
+            return sd;
+        } else if (si.length() == 2)// 十
+        {
+            if (si.substring(0, 1).equals("1"))
+                sd += "十";
+            else
+                sd += (GetCH(intInput / 10) + "十");
+            sd += ToCH(intInput % 10);
+        } else if (si.length() == 3)// 百
+        {
+            sd += (GetCH(intInput / 100) + "百");
+            if (String.valueOf(intInput % 100).length() < 2)
+                sd += "零";
+            sd += ToCH(intInput % 100);
+        } else if (si.length() == 4)// 千
+        {
+            sd += (GetCH(intInput / 1000) + "千");
+            if (String.valueOf(intInput % 1000).length() < 3)
+                sd += "零";
+            sd += ToCH(intInput % 1000);
+        } else if (si.length() == 5)// 萬
+        {
+            sd += (GetCH(intInput / 10000) + "萬");
+            if (String.valueOf(intInput % 10000).length() < 4)
+                sd += "零";
+            sd += ToCH(intInput % 10000);
+        }
+        return sd;
+    }
+
+    private static String GetCH(int input) {
+        String sd = "";
+        switch (input) {
+            case 1:
+                sd = "一";
+                break;
+            case 2:
+                sd = "二";
+                break;
+            case 3:
+                sd = "三";
+                break;
+            case 4:
+                sd = "四";
+                break;
+            case 5:
+                sd = "五";
+                break;
+            case 6:
+                sd = "六";
+                break;
+            case 7:
+                sd = "七";
+                break;
+            case 8:
+                sd = "八";
+                break;
+            case 9:
+                sd = "九";
+                break;
+            default:
+                break;
+        }
+        return sd;
+    }
+}

+ 91 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/HttpPoolUtil.java

@@ -0,0 +1,91 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.springframework.stereotype.Component;
+
+
+/** 
+* 连接池工具类 
+*/ 
+@Component
+public class HttpPoolUtil { 
+	private static Log logger = LogFactory.getLog(HttpPoolUtil.class); 
+	
+	public static final String UTF8 = "UTF-8"; 
+	public static volatile boolean isClosed = false; 
+	
+	public static final int maxTotalPool = 200;
+	public static final int MAX_TIMEOUT = 15000;
+	public static final int RequestTimeout = 5000;
+	
+	private static RequestConfig requestConfig; 
+	private static HttpClientBuilder httpClientBuilder; 
+	private static PoolingHttpClientConnectionManager poolConnManager;
+	
+	static { 
+		// 设置连接池 
+		poolConnManager = new PoolingHttpClientConnectionManager(); 
+		poolConnManager.setMaxTotal(maxTotalPool);//设置连接池大小 
+		poolConnManager.setDefaultMaxPerRoute(maxTotalPool); 
+		
+		RequestConfig.Builder configBuilder = RequestConfig.custom(); 
+		// 设置连接超时 
+		configBuilder.setConnectTimeout(MAX_TIMEOUT); 
+		// 设置读取超时 
+		configBuilder.setSocketTimeout(MAX_TIMEOUT); 
+		// 设置从连接池获取连接实例的超时 
+		configBuilder.setConnectionRequestTimeout(RequestTimeout); 
+		// 在提交请求之前 测试连接是否可用 
+		//configBuilder.setStaleConnectionCheckEnabled(true); 
+		requestConfig = configBuilder.build(); 
+		// 
+		httpClientBuilder = HttpClients.custom()
+									   .setConnectionManager(poolConnManager)
+									   .setDefaultRequestConfig(requestConfig); 
+		System.out.println(">>>>>>>>>>> PoolingHttpClientConnectionManager初始化成功 >>>>>>>>>>>");
+	}
+	
+	/** 
+	* 获取HttpClient客户端 
+	* @return httpClient 
+	*/ 
+	/*public static CloseableHttpClient getClient() { 
+		CloseableHttpClient httpClient = HttpClients.custom() 
+		.setConnectionManager(poolConnManager) 
+		.setDefaultRequestConfig(requestConfig) 
+		.build(); 
+		if(null == httpClient){ 
+			httpClient = HttpClients.createDefault(); 
+		} 
+		return httpClient; 
+	}*/
+	
+	/** 
+	* 从http连接池里获取客户端实例 
+	* @return httpClient 
+	*/ 
+	public static CloseableHttpClient getHttpClient() { 
+		CloseableHttpClient httpClient = httpClientBuilder.build(); 
+		if(null == httpClient){ 
+			logger.info("---------HttpClients.createDefault()---------"); 
+			httpClient = HttpClients.createDefault(); 
+		} 
+		return httpClient; 
+	}
+	
+	/** 
+	* 关闭连接池资源 
+	*/ 
+	public synchronized static void closePool() { 
+		if( !isClosed ){ 
+			isClosed = true; 
+			poolConnManager.close(); 
+		} 
+	}
+} 

+ 172 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/JsonMapper.java

@@ -0,0 +1,172 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-21 14:12:14.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.util.JSONPObject;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 封装Jackson,实现JSON 与 Java Object互相转换的Mapper
+ *
+ * @author: fengdesheng
+ * @since: 2018/8/20
+ */
+@SuppressWarnings("unchecked")
+public class JsonMapper {
+    private static Logger log = LoggerFactory.getLogger(JsonMapper.class);
+    private ObjectMapper mapper;
+
+    public JsonMapper() {
+        this(null);
+    }
+
+    public JsonMapper(Include include) {
+        mapper = new ObjectMapper();
+        if (include != null) {
+            mapper.setSerializationInclusion(include);
+        }
+        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+    }
+
+    public static JsonMapper nonEmptyMapper() {
+        return new JsonMapper(Include.NON_EMPTY);
+    }
+
+    public static JsonMapper nonNullMapper() {
+        return new JsonMapper(Include.NON_NULL);
+    }
+
+    public static JsonMapper nonDefaultMapper() {
+        return new JsonMapper(Include.NON_DEFAULT);
+    }
+
+    public String toJson(Object object) {
+        try {
+            return mapper.writeValueAsString(object);
+        } catch (IOException e) {
+            log.error("write to json string error:" + object);
+            return null;
+        }
+    }
+
+    public <T> T fromJson(String jsonString, Class<T> clazz) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            return mapper.readValue(jsonString, clazz);
+        } catch (IOException e) {
+            log.error("parse json string error", e);
+            return null;
+        }
+    }
+
+    public <T> T fromJson(String jsonString, JavaType javaType) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            return (T) mapper.readValue(jsonString, javaType);
+        } catch (IOException e) {
+            log.error("parse json string error", e);
+            return null;
+        }
+    }
+
+    public <T> T fromJson(String jsonString, TypeReference javaType) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            return (T) mapper.readValue(jsonString, javaType);
+        } catch (IOException e) {
+            log.error("parse json string error", e);
+            return null;
+        }
+    }
+
+    public <T> List<T> toList(String jsonString, Class<T> bean) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            JavaType javaType = constructCollectionType(List.class, bean);
+            return mapper.readValue(jsonString, javaType);
+        } catch (IOException e) {
+            log.error("parse json string error", e);
+            return null;
+        }
+    }
+
+    public <T> Map<String, T> toHashMap(String jsonString, Class<T> bean) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            JavaType javaType = constructMapType(HashMap.class, String.class, bean);
+            return mapper.readValue(jsonString, javaType);
+        } catch (IOException e) {
+            log.error("parse json string error:", e);
+            return null;
+        }
+    }
+
+    public JavaType constructCollectionType(Class<? extends Collection> collectionClass, Class<?> elementClass) {
+        return mapper.getTypeFactory().constructCollectionType(collectionClass, elementClass);
+    }
+
+    public JavaType constructMapType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass) {
+        return mapper.getTypeFactory().constructMapType(mapClass, keyClass, valueClass);
+    }
+
+    public void update(String jsonString, Object object) {
+        try {
+            mapper.readerForUpdating(object).readValue(jsonString);
+        } catch (JsonProcessingException e) {
+            log.error("update json string:" + jsonString + " to object:" + object + " error.");
+        } catch (IOException e) {
+            log.error("update json string:" + jsonString + " to object:" + object + " error.");
+        }
+    }
+
+    public String toJsonP(String functionName, Object object) {
+        return toJson(new JSONPObject(functionName, object));
+    }
+
+    public void enableEnumUseToString() {
+        mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
+        mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
+    }
+
+    public ObjectMapper getMapper() {
+        return mapper;
+    }
+
+    public JsonNode getNode(String jsonStr) {
+        try {
+            //mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
+            return mapper.readTree(jsonStr);
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+}

+ 63 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/NewQuestionType.java

@@ -0,0 +1,63 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-09-12 09:55:02.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+public enum NewQuestionType {
+
+    SINGLE_CHOICE("单选题", true),
+
+    MULTIPLE_CHOICE("多选题", true),
+
+    TRUE_OR_FALSE("判断题", true),
+
+    FILL_UP("填空题", false),
+
+    ESSAY("问答题", false),
+
+    NESTED("套题", false);
+
+    /**
+     * 转换来源于题库的题型值
+     */
+    public static NewQuestionType convert(String value) {
+        if (value == null) {
+            return null;
+        }
+        if ("SINGLE_ANSWER_QUESTION".equals(value)) {
+            return SINGLE_CHOICE;
+        } else if ("MULTIPLE_ANSWER_QUESTION".equals(value)) {
+            return MULTIPLE_CHOICE;
+        } else if ("BOOL_ANSWER_QUESTION".equals(value)) {
+            return TRUE_OR_FALSE;
+        } else if ("FILL_BLANK_QUESTION".equals(value)) {
+            return FILL_UP;
+        } else if ("TEXT_ANSWER_QUESTION".equals(value)) {
+            return ESSAY;
+        } else if ("NESTED_ANSWER_QUESTION".equals(value)) {
+            return NESTED;
+        }
+        return null;
+    }
+
+    private boolean objective;
+    private String desc;
+
+    NewQuestionType(String desc, boolean objective) {
+        this.objective = objective;
+        this.desc = desc;
+    }
+
+    public boolean isObjective() {
+        return objective;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+}

+ 101 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/PagingAndSortingDTO.java

@@ -0,0 +1,101 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+/**
+ * 分页DTO
+ *
+ * @author xudengqi
+ * 2016年8月18日
+ */
+public class PagingAndSortingDTO {
+
+    private int page;
+
+    private int size;
+
+    private int pageCount;
+
+    private String[] properties;
+
+    private String order;
+
+    private long totalCount;
+
+    private Object data;
+
+    protected PagingAndSortingDTO() {
+    }
+
+    public PagingAndSortingDTO(int page, int size) {
+        this.page = page;
+        this.size = size;
+    }
+
+    public PagingAndSortingDTO(Object data) {
+        this.data = data;
+    }
+
+    public PagingAndSortingDTO(int page, int size, int pageCount, long totalCount, Object data) {
+        this.page = page;
+        this.size = size;
+        this.pageCount = pageCount;
+        this.data = data;
+        this.totalCount = totalCount;
+    }
+
+    public int getPage() {
+        return page;
+    }
+
+    public void setPage(int page) {
+        this.page = page;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    public void setSize(int size) {
+        this.size = size;
+    }
+
+    public int getPageCount() {
+        return pageCount;
+    }
+
+    public void setPageCount(int pageCount) {
+        this.pageCount = pageCount;
+    }
+
+    public String[] getProperties() {
+        return properties;
+    }
+
+    public void setProperties(String[] properties) {
+        this.properties = properties;
+    }
+
+    public String getOrder() {
+        return order;
+    }
+
+    public void setOrder(String order) {
+        this.order = order;
+    }
+
+    public long getTotalCount() {
+        return totalCount;
+    }
+
+    public void setTotalCount(long totalCount) {
+        this.totalCount = totalCount;
+    }
+
+    public Object getData() {
+        return data;
+    }
+
+    public void setData(Object data) {
+        this.data = data;
+    }
+
+}

+ 74 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/PagingAndSortingSpecification.java

@@ -0,0 +1,74 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+
+/**
+ * 构建动态查询、分页、排序
+ *
+ * @author xudengqi
+ * 2016年8月18日
+ */
+public abstract class PagingAndSortingSpecification implements QuerySpecification {
+
+    private Integer page;
+    private Integer size;
+    private String order;
+    private String[] properties;
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getSize() {
+        return size;
+    }
+
+    public void setSize(Integer size) {
+        this.size = size;
+    }
+
+    public String getOrder() {
+        return order;
+    }
+
+    public void setOrder(String order) {
+        this.order = order;
+    }
+
+    public String[] getProperties() {
+        return properties;
+    }
+
+    public void setProperties(String[] properties) {
+        this.properties = properties;
+    }
+
+    @Override
+    public abstract Specification<?> getSpecification();
+
+    @Override
+    public PageRequest getPageRequest() {
+        PageRequest pageRequest = null;
+
+        if (order == null || order.trim().length() == 0) {
+            order = "ASC";
+        }
+        if (properties != null) {
+            Sort sort = new Sort(Sort.Direction.valueOf(order), properties);
+            pageRequest = new PageRequest(page, size, sort);
+        } else if (page != null && size != null) {
+            pageRequest = new PageRequest(page, size);
+        } else {
+            //TODO 暂时返还一条数据
+            pageRequest = new PageRequest(0, 1);
+        }
+        return pageRequest;
+    }
+
+}

+ 162 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/QEncodeUtil.java

@@ -0,0 +1,162 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import org.apache.commons.lang3.StringUtils;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.spec.SecretKeySpec;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.Base64;
+
+/**
+ * 编码工具类
+ * 1.将byte[]转为各种进制的字符串
+ * 2.base 64 encode
+ * 3.base 64 decode
+ * 4.获取byte[]的md5值
+ * 5.获取字符串md5值
+ * 6.结合base64实现md5加密
+ * 7.AES加密
+ * 8.AES加密为base 64 code
+ * 9.AES解密
+ * 10.将base 64 code AES解密
+ * @author uikoo9
+ * @version 0.0.7.20140601
+ */
+public class QEncodeUtil {
+	
+	public static void main(String[] args) throws Exception {
+		String content = "abcdefg";
+		System.out.println("加密前:" + content);
+ 
+		String key = "123456";
+		System.out.println("加密密钥和解密密钥:" + key);
+		
+		String encrypt = aesEncrypt(content, key);
+		System.out.println("加密后:" + encrypt);
+		
+		String decrypt = aesDecrypt(encrypt, key);
+		System.out.println("解密后:" + decrypt);
+	}
+	
+	/**
+	 * 将byte[]转为各种进制的字符串
+	 * @param bytes byte[]
+	 * @param radix 可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制
+	 * @return 转换后的字符串
+	 */
+	public static String binary(byte[] bytes, int radix){
+		return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数
+	}
+	
+	/**
+	 * base 64 encode
+	 * @param bytes 待编码的byte[]
+	 * @return 编码后的base 64 code
+	 */
+	public static String base64Encode(byte[] bytes){
+		return Base64.getEncoder().encodeToString(bytes);
+	}
+	
+	/**
+	 * base 64 decode
+	 * @param base64Code 待解码的base 64 code
+	 * @return 解码后的byte[]
+	 * @throws Exception
+	 */
+	public static byte[] base64Decode(String base64Code) throws Exception{
+		return StringUtils.isEmpty(base64Code) ? null : Base64.getDecoder().decode(base64Code);
+	}
+	
+	/**
+	 * 获取byte[]的md5值
+	 * @param bytes byte[]
+	 * @return md5
+	 * @throws Exception
+	 */
+	public static byte[] md5(byte[] bytes) throws Exception {
+		MessageDigest md = MessageDigest.getInstance("MD5");
+		md.update(bytes);
+		
+		return md.digest();
+	}
+	
+	/**
+	 * 获取字符串md5值
+	 * @param msg 
+	 * @return md5
+	 * @throws Exception
+	 */
+	public static byte[] md5(String msg) throws Exception {
+		return StringUtils.isEmpty(msg) ? null : md5(msg.getBytes());
+	}
+	
+	/**
+	 * 结合base64实现md5加密
+	 * @param msg 待加密字符串
+	 * @return 获取md5后转为base64
+	 * @throws Exception
+	 */
+	public static String md5Encrypt(String msg) throws Exception{
+		return StringUtils.isEmpty(msg) ? null : base64Encode(md5(msg));
+	}
+	
+	/**
+	 * AES加密
+	 * @param content 待加密的内容
+	 * @param encryptKey 加密密钥
+	 * @return 加密后的byte[]
+	 * @throws Exception
+	 */
+	public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
+		KeyGenerator kgen = KeyGenerator.getInstance("AES");
+		kgen.init(128, new SecureRandom(encryptKey.getBytes()));
+ 
+		Cipher cipher = Cipher.getInstance("AES");
+		cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
+		
+		return cipher.doFinal(content.getBytes("utf-8"));
+	}
+	
+	/**
+	 * AES加密为base 64 code
+	 * @param content 待加密的内容
+	 * @param encryptKey 加密密钥
+	 * @return 加密后的base 64 code
+	 * @throws Exception
+	 */
+	public static String aesEncrypt(String content, String encryptKey) throws Exception {
+		return base64Encode(aesEncryptToBytes(content, encryptKey));
+	}
+	
+	/**
+	 * AES解密
+	 * @param encryptBytes 待解密的byte[]
+	 * @param decryptKey 解密密钥
+	 * @return 解密后的String
+	 * @throws Exception
+	 */
+	public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
+		KeyGenerator kgen = KeyGenerator.getInstance("AES");
+		kgen.init(128, new SecureRandom(decryptKey.getBytes()));
+		
+		Cipher cipher = Cipher.getInstance("AES");
+		cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
+		byte[] decryptBytes = cipher.doFinal(encryptBytes);
+		
+		return new String(decryptBytes);
+	}
+	
+	/**
+	 * 将base 64 code AES解密
+	 * @param encryptStr 待解密的base 64 code
+	 * @param decryptKey 解密密钥
+	 * @return 解密后的string
+	 * @throws Exception
+	 */
+	public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {
+		return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
+	}
+}

+ 17 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/QuerySpecification.java

@@ -0,0 +1,17 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.jpa.domain.Specification;
+
+/**
+ * 通过Specification动态构建动态查询、分页、排序
+ *
+ * @author xudengqi
+ * 2016年8月18日
+ */
+public interface QuerySpecification {
+
+    Specification<?> getSpecification();
+
+    PageRequest getPageRequest();
+}

+ 40 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/QuestionTypeUtil.java

@@ -0,0 +1,40 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import cn.com.qmth.examcloud.question.commons.core.question.QuestionType;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月1日 下午2:24:07
+ * @company 	QMTH
+ * @description QuestionTypeUtil.java
+ */
+public class QuestionTypeUtil {
+
+	/**
+	 * 是否是客观题,客观题返回true
+	 * @param questionType
+	 * @return
+	 */
+	public static boolean isObjectiveQuestion(QuestionType questionType){
+		if(questionType==QuestionType.SINGLE_CHOICE
+    			||questionType==QuestionType.MULTIPLE_CHOICE
+    				||questionType==QuestionType.TRUE_OR_FALSE){
+    		return true;
+    	}
+		return false;
+	}
+	
+	/**
+	 * 是否是选择题,单选多选返回true
+	 * @param questionType
+	 * @return
+	 */
+	public static boolean isChoiceQuestion(QuestionType questionType){
+		if(questionType==QuestionType.SINGLE_CHOICE
+    			||questionType==QuestionType.MULTIPLE_CHOICE){
+    		return true;
+    	}
+		return false;
+	}
+}

+ 63 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/RowIterator.java

@@ -0,0 +1,63 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import com.esotericsoftware.reflectasm.ConstructorAccess;
+import com.esotericsoftware.reflectasm.FieldAccess;
+import org.apache.poi.ss.formula.functions.T;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.function.Consumer;
+
+/**
+ * Created by zhengmin on 2016/8/19.
+ */
+public class RowIterator implements Iterator<T>{
+
+    private Class<T> clazz;
+
+    private XSSFWorkbook workbook;
+    private XSSFSheet sheet;
+    private int size = 0;
+    private int times = 0;
+
+    public RowIterator(InputStream inputStream,int sheetIndex,Class<T> clazz) throws IOException {
+        this.clazz = clazz;
+        this.workbook = new XSSFWorkbook(inputStream);
+        this.sheet = workbook.getSheetAt(sheetIndex);
+        this.size = sheet.getLastRowNum();
+    }
+
+    @Override
+    public boolean hasNext() {
+        return times <= size;
+    }
+
+    @Override
+    public T next() {
+        XSSFRow row = this.sheet.getRow(times);
+        ConstructorAccess<T> access = ConstructorAccess.get(clazz);
+        T instance = access.newInstance();
+        FieldAccess fa = FieldAccess.get(clazz);
+        fa.set(instance,0,row.getCell(0).getStringCellValue());
+        times++;
+        return instance;
+    }
+
+    @Override
+    public void remove() {
+
+    }
+
+    @Override
+    public void forEachRemaining(Consumer<? super T> action) {
+
+    }
+
+    public int getSize(){
+        return this.size;
+    }
+}

+ 180 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/Sentence.java

@@ -0,0 +1,180 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-09-12 09:55:46.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.oe.student.base.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Mysql相关语句处理工具类
+ *
+ * @author: fengdesheng
+ * @since: 2018/9/5
+ */
+public class Sentence {
+    private static final String YYYY_MM_DD = "yyyy-MM-dd";
+    private static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+
+    /**
+     * 处理字符串
+     */
+    public static String dealString(Object value, String defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof CharSequence) {
+            return String.valueOf(value);
+        }
+        throw new IllegalArgumentException("[" + value + "] Can't convert to String.");
+    }
+
+    public static String dealString(Object value) {
+        return dealString(value, null);
+    }
+
+    /**
+     * 处理Integer
+     */
+    public static Integer dealInteger(Object value, Integer defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        throw new IllegalArgumentException("[" + value + "] Can't convert to Integer.");
+    }
+
+    public static Integer dealInteger(Object value) {
+        return dealInteger(value, null);
+    }
+
+    /**
+     * 处理Long
+     */
+    public static Long dealLong(Object value, Long defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).longValue();
+        }
+        throw new IllegalArgumentException("[" + value + "] Can't convert to Long.");
+    }
+
+    public static Long dealLong(Object value) {
+        return dealLong(value, null);
+    }
+
+    /**
+     * 处理Double
+     */
+    public static Double dealDouble(Object value, Double defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).doubleValue();
+        }
+        throw new IllegalArgumentException("[" + value + "] Can't convert to Double.");
+    }
+
+    public static Double dealDouble(Object value) {
+        return dealDouble(value, null);
+    }
+
+    /**
+     * 处理Float
+     */
+    public static Float dealFloat(Object value, Float defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).floatValue();
+        }
+        throw new IllegalArgumentException("[" + value + "] Can't convert to Float.");
+    }
+
+    public static Float dealFloat(Object value) {
+        return dealFloat(value, null);
+    }
+
+    /**
+     * 处理Boolean
+     */
+    public static Boolean dealBoolean(Object value, Boolean defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Boolean) {
+            return (Boolean) value;
+        }
+        String str = String.valueOf(value);
+        final String trueStr = "1";
+        if (trueStr.equals(str)) {
+            return true;
+        }
+        final String falseStr = "0";
+        if (falseStr.equals(str)) {
+            return false;
+        }
+        throw new IllegalArgumentException("[" + value + "] Can't convert to Boolean.");
+    }
+
+    public static Boolean dealBoolean(Object value) {
+        return dealBoolean(value, null);
+    }
+
+    /**
+     * 处理日期
+     */
+    public static String dealDate(Object value, Date defaultValue) {
+        if (value == null) {
+            if (defaultValue != null) {
+                return formatDate(defaultValue);
+            }
+            return null;
+        }
+        if (value instanceof Date) {
+            return formatDate((Date) value);
+        }
+        //判断是否为日期格式
+        String dateStr = String.valueOf(value);
+        Date date = parseDate(dateStr);
+        if (date != null) {
+            return formatDate(date);
+        }
+        throw new IllegalArgumentException("[" + value + "] Can't convert to Date.");
+    }
+
+    public static String dealDate(Object value) {
+        return dealDate(value, null);
+    }
+
+    public static Date parseDate(String dateStr) {
+        if (dateStr == null) {
+            return null;
+        }
+        try {
+            final int len = 10;
+            if (dateStr.length() == len) {
+                return new SimpleDateFormat(YYYY_MM_DD).parse(dateStr);
+            }
+            return new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS).parse(dateStr);
+        } catch (ParseException e) {
+            return null;
+        }
+    }
+
+    public static String formatDate(Date date) {
+        return new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS).format(date);
+    }
+
+}

+ 126 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/baiduFaceVerify/BaiduFaceVerifyUtil.java

@@ -0,0 +1,126 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.baiduFaceVerify;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.SysPropertyCacheBean;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年10月8日 上午9:43:04
+ * @company 	QMTH
+ * @description 百度活体检测工具
+ */
+public class BaiduFaceVerifyUtil {
+	
+	/**
+     * 获取API访问token
+     * 该token有一定的有效期,需要自行管理,当失效时需重新获取.
+     * @param ak - 百度云官网获取的 API Key
+     * @param sk - 百度云官网获取的 Securet Key
+     * @return assess_token 示例:
+     * "24.460da4889caad24cccdb1fea17221975.2592000.1491995545.282335-1234567"
+     */
+    public static String getAccessToken(String ak, String sk) {
+        // 获取token地址
+        String authHost = "https://aip.baidubce.com/oauth/2.0/token?";
+        String getAccessTokenUrl = authHost
+                // 1. grant_type为固定参数
+                + "grant_type=client_credentials"
+                // 2. 官网获取的 API Key
+                + "&client_id=" + ak
+                // 3. 官网获取的 Secret Key
+                + "&client_secret=" + sk;
+        try {
+            URL realUrl = new URL(getAccessTokenUrl);
+            // 打开和URL之间的连接
+            HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
+            connection.setRequestMethod("GET");
+            connection.connect();
+            // 获取所有响应头字段
+            /*Map<String, List<String>> map = connection.getHeaderFields();
+            // 遍历所有的响应头字段
+            for (String key : map.keySet()) {
+                System.err.println(key + "--->" + map.get(key));
+            }*/
+            // 定义 BufferedReader输入流来读取URL的响应
+            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+            String result = "";
+            String line;
+            while ((line = in.readLine()) != null) {
+                result += line;
+            }
+            /**
+             * 返回结果
+             */
+            return result;
+        } catch (Exception e) {
+            e.printStackTrace(System.err);
+        }
+        return null;
+    }
+	
+    /**
+     * 重要提示代码中所需工具类
+     * FileUtil,Base64Util,HttpUtil,GsonUtils请从
+     * https://ai.baidu.com/file/658A35ABAB2D404FBF903F64D47C1F72
+     * https://ai.baidu.com/file/C8D81F3301E24D2892968F09AE1AD6E2
+     * https://ai.baidu.com/file/544D677F5D4E4F17B4122FBD60DB82B3
+     * https://ai.baidu.com/file/470B3ACCA3FE43788B5A963BF0B625F3
+     * 下载
+     * param 必须是json数组
+     */
+     public static String faceVerify(String accessToken,String imageUrl) {
+         // 请求url
+         try {
+             String param = getFaceVerifyParam(imageUrl);
+             return HttpUtil.post(getFaceVerifyUrl(), accessToken, "application/json", param);
+         } catch (Exception e) {
+             e.printStackTrace();
+         }
+         return null;
+     }
+     
+     /**
+      * 得到活体检测参数
+      * @param imageUrl
+      * @return
+      */
+     private static String getFaceVerifyParam(String imageUrl){
+    	JsonObject jsonObject = new JsonObject();
+     	jsonObject.addProperty("image",imageUrl);
+     	jsonObject.addProperty("image_type","URL");
+     	JsonArray jsonArray = new JsonArray();
+     	jsonArray.add(jsonObject);
+     	return jsonArray.toString();
+     }
+    
+    /*public static void main(String[] args) {
+    	String accessToken = getAccessToken();
+    	String imageUrl = "https://ecs-test-static.qmth.com.cn/capture_photo/6/1537953767307.jpg";
+    	System.out.println(accessToken);
+	}*/
+
+    /**
+     * 获取百度活体检测url
+     * @return
+     */
+    private static String getFaceVerifyUrl(){
+        SysPropertyCacheBean sysProperty = CacheHelper.getSysProperty("baidu.faceLiveness.url");
+        if (sysProperty.getHasValue()){
+            Object value = sysProperty.getValue();
+            if (null!=value){
+                String url = value.toString();
+                return url;
+            }
+        }
+        throw new StatusException("300005","未找到百度活体检测的配置路径");
+    }
+}

+ 65 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/baiduFaceVerify/Base64Util.java

@@ -0,0 +1,65 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.baiduFaceVerify;
+
+/**
+ * Base64 工具类
+ */
+public class Base64Util {
+    private static final char last2byte = (char) Integer.parseInt("00000011", 2);
+    private static final char last4byte = (char) Integer.parseInt("00001111", 2);
+    private static final char last6byte = (char) Integer.parseInt("00111111", 2);
+    private static final char lead6byte = (char) Integer.parseInt("11111100", 2);
+    private static final char lead4byte = (char) Integer.parseInt("11110000", 2);
+    private static final char lead2byte = (char) Integer.parseInt("11000000", 2);
+    private static final char[] encodeTable = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
+
+    public Base64Util() {
+    }
+
+    public static String encode(byte[] from) {
+        StringBuilder to = new StringBuilder((int) ((double) from.length * 1.34D) + 3);
+        int num = 0;
+        char currentByte = 0;
+
+        int i;
+        for (i = 0; i < from.length; ++i) {
+            for (num %= 8; num < 8; num += 6) {
+                switch (num) {
+                    case 0:
+                        currentByte = (char) (from[i] & lead6byte);
+                        currentByte = (char) (currentByte >>> 2);
+                    case 1:
+                    case 3:
+                    case 5:
+                    default:
+                        break;
+                    case 2:
+                        currentByte = (char) (from[i] & last6byte);
+                        break;
+                    case 4:
+                        currentByte = (char) (from[i] & last4byte);
+                        currentByte = (char) (currentByte << 2);
+                        if (i + 1 < from.length) {
+                            currentByte = (char) (currentByte | (from[i + 1] & lead2byte) >>> 6);
+                        }
+                        break;
+                    case 6:
+                        currentByte = (char) (from[i] & last2byte);
+                        currentByte = (char) (currentByte << 4);
+                        if (i + 1 < from.length) {
+                            currentByte = (char) (currentByte | (from[i + 1] & lead4byte) >>> 4);
+                        }
+                }
+
+                to.append(encodeTable[currentByte]);
+            }
+        }
+
+        if (to.length() % 4 != 0) {
+            for (i = 4 - to.length() % 4; i > 0; --i) {
+                to.append("=");
+            }
+        }
+
+        return to.toString();
+    }
+}

+ 29 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/baiduFaceVerify/GsonUtils.java

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
+ */
+package cn.com.qmth.examcloud.core.oe.student.base.utils.baiduFaceVerify;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParseException;
+
+import java.lang.reflect.Type;
+
+/**
+ * Json工具类.
+ */
+public class GsonUtils {
+    private static Gson gson = new GsonBuilder().create();
+
+    public static String toJson(Object value) {
+    	return gson.toJsonTree(value).getAsString();
+    }
+
+    public static <T> T fromJson(String json, Class<T> classOfT) throws JsonParseException {
+        return gson.fromJson(json, classOfT);
+    }
+
+    public static <T> T fromJson(String json, Type typeOfT) throws JsonParseException {
+        return (T) gson.fromJson(json, typeOfT);
+    }
+}

+ 75 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/baiduFaceVerify/HttpUtil.java

@@ -0,0 +1,75 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.baiduFaceVerify;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * http 工具类
+ */
+public class HttpUtil {
+
+    public static String post(String requestUrl, String accessToken, String params)
+            throws Exception {
+        String contentType = "application/x-www-form-urlencoded";
+        return HttpUtil.post(requestUrl, accessToken, contentType, params);
+    }
+
+    public static String post(String requestUrl, String accessToken, String contentType, String params)
+            throws Exception {
+        String encoding = "UTF-8";
+        if (requestUrl.contains("nlp")) {
+            encoding = "GBK";
+        }
+        return HttpUtil.post(requestUrl, accessToken, contentType, params, encoding);
+    }
+
+    public static String post(String requestUrl, String accessToken, String contentType, String params, String encoding)
+            throws Exception {
+        String url = requestUrl + "?access_token=" + accessToken;
+        return HttpUtil.postGeneralUrl(url, contentType, params, encoding);
+    }
+
+    public static String postGeneralUrl(String generalUrl, String contentType, String params, String encoding)
+            throws Exception {
+        URL url = new URL(generalUrl);
+        // 打开和URL之间的连接
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        connection.setRequestMethod("POST");
+        // 设置通用的请求属性
+        connection.setRequestProperty("Content-Type", contentType);
+        connection.setRequestProperty("Connection", "Keep-Alive");
+        connection.setUseCaches(false);
+        connection.setDoOutput(true);
+        connection.setDoInput(true);
+
+        // 得到请求的输出流对象
+        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
+        out.write(params.getBytes(encoding));
+        out.flush();
+        out.close();
+
+        // 建立实际的连接
+        connection.connect();
+        // 获取所有响应头字段
+        /*Map<String, List<String>> headers = connection.getHeaderFields();
+        // 遍历所有的响应头字段
+        for (String key : headers.keySet()) {
+            System.err.println(key + "--->" + headers.get(key));
+        }*/
+        // 定义 BufferedReader输入流来读取URL的响应
+        BufferedReader in = null;
+        in = new BufferedReader(
+                new InputStreamReader(connection.getInputStream(), encoding));
+        String result = "";
+        String getLine;
+        while ((getLine = in.readLine()) != null) {
+            result += getLine;
+        }
+        in.close();
+        //System.err.println("result:" + result);
+        return result;
+    }
+}

+ 61 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ColumnSetting.java

@@ -0,0 +1,61 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.excel;
+
+/**
+ * Created by zhengmin on 2016/6/22.
+ */
+public class ColumnSetting implements Comparable<ColumnSetting> {
+
+    private String header;
+    private String getMethodName;
+    private int width;
+    private int index;
+
+    public ColumnSetting(String header, String getMethodName, int width, int index) {
+        this.header = header;
+        this.getMethodName = getMethodName;
+        this.width = width;
+        this.index = index;
+    }
+
+    public String getHeader() {
+        return header;
+    }
+
+    public void setHeader(String header) {
+        this.header = header;
+    }
+
+    public String getGetMethodName() {
+        return getMethodName;
+    }
+
+    public void setGetMethodName(String getMethodName) {
+        this.getMethodName = getMethodName;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+
+    @Override
+    public int compareTo(ColumnSetting columnSetting) {
+        if (index < columnSetting.getIndex())
+            return - 1 ;
+        if (index > columnSetting.getIndex())
+            return 1 ;
+        return 0 ;
+    }
+}

+ 48 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelError.java

@@ -0,0 +1,48 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.excel;
+/**
+ * 
+ * @Description: excel导入错误
+ * @author ting.yin
+ * @date 2016年8月19日
+ */
+public class ExcelError {
+
+	/**
+	 * 错误行数
+	 */
+	private int row;
+
+	/**
+	 * 错误类型
+	 */
+	private String excelErrorType;
+
+	public ExcelError() {
+
+	}
+
+	public ExcelError(int row, String excelErrorType) {
+		this.row = row;
+		this.excelErrorType = excelErrorType;
+	}
+	
+	public ExcelError(String excelErrorType) {
+		this.excelErrorType = excelErrorType;
+	}
+	public int getRow() {
+		return row;
+	}
+
+	public void setRow(int row) {
+		this.row = row;
+	}
+
+	public String getExcelErrorType() {
+		return excelErrorType;
+	}
+
+	public void setExcelErrorType(String excelErrorType) {
+		this.excelErrorType = excelErrorType;
+	}
+
+}

+ 98 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelErrorType.java

@@ -0,0 +1,98 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.excel;
+
+/**
+ * 
+ * @Description: excel导入错误类型
+ * @author ting.yin
+ * @date 2016年8月19日
+ */
+public enum ExcelErrorType {
+
+	NOT_CAMPUS("学习中心不存在"),
+
+	NOT_EXAM_SITE("学习中心下的考点不存在"),
+	
+	EXAM_SITE_CODE("考点代码为空或格式错误,只能包含数字,英文字母"),
+	
+	EXAM_SITE_CODE_LENGTH("考点代码必须在1-15个字符"),
+	
+	EXAM_SITE_NAME_LENGTH("考点名称必须在1-50个字符"),
+	
+	CAMPUS_CODE("学习中心代码为空或格式错误,只能包含数字,英文字母"),
+	
+	CAMPUS_CODE_LENGTH("学习中心代码必须在1-15个字符"),
+	
+	CAMPUS_NAME_LENGTH("学习中心名必须在1-50个字符"),
+
+	COURSE_CODE("课程代码格式错误,只能包含数字,英文字母"),
+
+	SPECIALTY_CODE("专业代码格式错误,只能包含数字,英文字母"),
+	
+	ONLY_EXAM_NUMBER("准考证号重复"),
+	
+	STUDENT_NAME_NULL("考生姓名不能为空"),
+	
+	STUDENT_NAME_LENGTH("考生姓名必须在1-50个字符"),
+	
+	STUDENT_SEX_NULL("考生性别不能为空"),
+	
+	STUDENT_CODE_NULL("考生学号不能为空"),
+	
+	STUDENT_CODE_LENGTH("考生学号必须在6-15个字符"),
+	
+	CAMPUS_NAME_NULL("学习中心名称不能为空"),
+	
+	COURSE_NAME_NULL("课程名称不能为空"),
+	
+	COURSE_CODE_NULL("课程代码不能为空"),
+	
+	SPECIALTY_NAME_NULL("专业名称不能为空"),
+	
+	SPECIALTY_CODE_NULL("专业代码不能为空"),
+	
+	EXCEL_FORMAT_ERROR("Excel文件格式错误"),
+	
+	EXAM_SITE_NAME_NULL("考点名称不能为空"),
+	
+	GRADE_NULL("年级不能为空"),
+	
+	GRADE_FORMAT("年级必须是数字格式"),
+	
+	STUDENT_EXIST("已有相同考生学号,课程代码,批次的考生存在"),
+	
+	EXCEL_HYPERLINK_FORMAT_ERROR("Excel文件包含有超链接格式的文本"),
+	
+	STUDENT_NAME_CHECK("考生名称不能为空且长度在1-50个字符"),
+	
+	STUDENT_CODE_CHECK("考生学号不能为空且长度在6-15个字符"),
+	
+	STUDENT_GRADE_CHECK("年级不能为空且只能是数字格式"),
+	
+	CAMPUS_NAME_CHECK("学习中心名称不能为空且长度在1-50个字符"),
+	
+	CAMPUS_CODE_CHECK("学习中心代码不能为空且长度在1-15个字符且只能包含数字和英文字母"),
+	
+	COURSE_NAME_CHECK("课程名称不能为空且长度在1-50个字符"),
+	
+	COURSE_CODE_CHECK("课程代码不能为空且长度在1-15个字符且只能包含数字和英文字母"),
+	
+	SPECIALTY_NAME_CHECK("专业名称不能为空且长度在1-50个字符"),
+	
+	SPECIALTY_CODE_CHECK("专业代码不能为空且长度在1-15个字符且只能包含数字和英文字母")
+	
+	;
+	
+	
+	
+	
+	private String name;
+
+	public String getName() {
+		return name;
+	}
+
+	private ExcelErrorType(String name) {
+		this.name = name;
+	}
+
+}

+ 34 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelProperty.java

@@ -0,0 +1,34 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.excel;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Created by zhengmin on 2016/6/22.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ExcelProperty {
+	/**
+	 * 导出时列名
+	 */
+    String name() default "";
+    
+	/**
+	 * 导出时列宽
+	 */
+    int width() default 0;
+	/**
+	 * 排序
+	 */
+    int index();
+    /**
+     * 类型
+     * 0:导入(读excel)
+     * 1:导出(写excel)
+     * 2:导入&导出
+     */
+    int type() default 2;
+}

+ 95 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelReader.java

@@ -0,0 +1,95 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.excel;
+
+import com.esotericsoftware.reflectasm.ConstructorAccess;
+import com.esotericsoftware.reflectasm.FieldAccess;
+import org.apache.poi.ss.usermodel.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 
+ * @Description: 读取excel
+ * @author ting.yin
+ * @date 2016年8月19日
+ */
+public class ExcelReader extends ExcelUtils {
+
+	public ExcelReader(Class<?> dataClass) {
+		super(dataClass);
+	}
+
+	/**
+	 * 
+	 * @param inputStream
+	 *            输入流
+	 * @param handle
+	 *            单个对象处理器
+	 * @return 错误信息集合
+	 */
+	public List<ExcelError> reader(InputStream inputStream,
+			ExcelReaderHandle handle) {
+		List<ExcelError> excelErrors = new ArrayList<ExcelError>();
+		try {
+			Workbook wb = WorkbookFactory.create(inputStream);
+			Sheet sheet = wb.getSheetAt(0);
+			for (int i = 1; i <= sheet.getLastRowNum(); i++) {
+				Row row = sheet.getRow(i);
+				if (row == null) {
+					return excelErrors;
+				}
+				Object dto = ConstructorAccess.get(getDataClass()).newInstance();
+				FieldAccess access = FieldAccess.get(getDataClass());
+				try {
+					for (int j = 0; j < row.getLastCellNum(); j++) {
+						Cell cell = row.getCell(j);
+						Object obj = convert(cell);
+						ColumnSetting columnSetting = this.getColumnSettings().get(j);
+						access.set(dto ,columnSetting.getHeader(), obj);
+					}
+				}catch (Exception e) {
+					ExcelError error2 = new ExcelError(ExcelErrorType.EXCEL_FORMAT_ERROR.getName());
+					error2.setRow(i+1);
+					excelErrors.add(error2);
+					e.printStackTrace();
+					break;
+				}
+				ExcelError error = handle.handle(dto);
+				if (error != null) {
+					error.setRow(i+1);
+					excelErrors.add(error);
+				}
+			}
+
+		} catch (Exception e) {
+			ExcelError error2 = new ExcelError(ExcelErrorType.EXCEL_HYPERLINK_FORMAT_ERROR.getName());
+			excelErrors.add(error2);
+			e.printStackTrace();
+		} finally {
+			try {
+				inputStream.close();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		return excelErrors;
+	}
+
+	@SuppressWarnings("deprecation")
+	private Object convert(Cell cell) {
+		if(cell!=null){
+			switch (cell.getCellType()) {
+			case Cell.CELL_TYPE_STRING:
+				return cell.getStringCellValue().toString();
+			case Cell.CELL_TYPE_BOOLEAN:
+				return String.valueOf(cell.getBooleanCellValue());
+			case Cell.CELL_TYPE_NUMERIC:
+				return Math.round(cell.getNumericCellValue());
+			}
+		}
+		
+		return null;
+	}
+}

+ 7 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelReaderHandle.java

@@ -0,0 +1,7 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.excel;
+
+public interface ExcelReaderHandle {
+	
+	ExcelError handle(Object dto);
+
+}

+ 77 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelUtils.java

@@ -0,0 +1,77 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.excel;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Created by zhengmin on 2016/8/17.
+ */
+public abstract class ExcelUtils {
+
+    private Class<?> dataClass;
+    private List<ColumnSetting> columnSettings;
+
+    public ExcelUtils(Class<?> dataClass){
+        this.dataClass = dataClass;
+        this.columnSettings = getColumnSettings(dataClass);
+    }
+
+    public Class<?> getDataClass() {
+        return dataClass;
+    }
+
+    public void setDataClass(Class<?> dataClass) {
+        this.dataClass = dataClass;
+    }
+
+    public List<ColumnSetting> getColumnSettings() {
+        return columnSettings;
+    }
+
+    public void setColumnSettings(List<ColumnSetting> columnSettings) {
+        this.columnSettings = columnSettings;
+    }
+
+    /**
+     * 提取ExcelProperty注解类的字段信息
+     * @param dataClass 需要解析 写入excel的数据类型
+     * @return
+     */
+    protected List<ColumnSetting> getColumnSettings(Class<?> dataClass){
+        List<ColumnSetting> columnSettings = new ArrayList<>();
+        //先在方法上找ExcelProperty注解
+        Method[] methods = dataClass.getDeclaredMethods();
+        for(Method method : methods){
+            ExcelProperty exportProperty = method.getAnnotation(ExcelProperty.class);
+            if(exportProperty != null && exportProperty.name().trim().length() > 0){
+                ColumnSetting columnSetting = new ColumnSetting(exportProperty.name(),method.getName(),
+                        exportProperty.width(),exportProperty.index());
+                columnSettings.add(columnSetting);
+            }
+        }
+        //如果方法上找不到注解,再到属性上找 
+        if(columnSettings.size() == 0){
+        	Field[] fields = dataClass.getDeclaredFields();
+        	for(Field field:fields){
+        		ExcelProperty exportProperty = field.getAnnotation(ExcelProperty.class);
+        		if(exportProperty != null && exportProperty.name().trim().length() > 0){
+                    ColumnSetting columnSetting = new ColumnSetting(exportProperty.name(),"get"+toUpperCaseFirstOne(field.getName()),
+                            exportProperty.width(),exportProperty.index());
+                    columnSettings.add(columnSetting);
+                }
+        	}
+        }
+        Collections.sort(columnSettings);
+        return columnSettings;
+    }
+    
+    private static String toUpperCaseFirstOne(String s){
+	  if(Character.isUpperCase(s.charAt(0)))
+	    return s;
+	  else
+	    return (new StringBuilder()).append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).toString();
+	}
+}

+ 93 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExcelWriter.java

@@ -0,0 +1,93 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.excel;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFColor;
+import org.apache.poi.xssf.usermodel.XSSFRichTextString;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.awt.*;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.List;
+
+public class ExcelWriter extends ExcelUtils {
+	
+	private XSSFWorkbook workbook;//工作簿
+	
+	private Sheet  sheet;	//工作表
+	
+	private Row row = null;// 创建一行
+	
+	private Cell cell = null;
+	
+	private XSSFCellStyle style;
+	
+	private XSSFCellStyle style2;
+
+    public ExcelWriter(Class<?> dataClass,String sheetName) {
+        super(dataClass);
+        // 声明一个工作薄
+        //workbook = new SXSSFWorkbook(100);//使用该方法会有权限问题
+        workbook = new XSSFWorkbook();
+        // 生成一个表格
+        sheet =  workbook.createSheet(sheetName);
+        // 设置表格默认列宽度为15个字节
+        sheet.setDefaultColumnWidth((short) 15);
+    }
+    
+    private List<ColumnSetting> createColumnSettings(){
+    	List<ColumnSetting> columnSettings = this.getColumnSettings();
+        // 产生表格标题行
+        row =  sheet.createRow(0);
+        for (short i = 0; i < columnSettings.size(); i++) {
+            cell =  row.createCell(i);
+            style =  workbook.createCellStyle();
+            style.setFillForegroundColor(new XSSFColor(new Color(227, 239, 217)));
+            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);  
+            cell.setCellStyle(style);
+            XSSFRichTextString text = new XSSFRichTextString(columnSettings.get(i).getHeader());
+            cell.setCellValue(text);
+            if (columnSettings.get(i).getWidth() > 0) {
+                sheet.setColumnWidth(i, columnSettings.get(i).getWidth() * 256);
+            }
+        }
+        return columnSettings;
+    }
+
+    /**
+     * 写入excel
+     *
+     * @param dataset   数据集合
+     * @param out       输出流
+     * @throws SecurityException 
+     * @throws NoSuchMethodException 
+     * @throws InvocationTargetException 
+     * @throws IllegalArgumentException 
+     * @throws IllegalAccessException 
+     */
+    public void write(Collection<?> dataset, OutputStream out) throws Exception {
+    	List<ColumnSetting> columnSettings = this.createColumnSettings();
+        int index = 0;
+        for (Object obj : dataset) {
+            index++;
+            row = sheet.createRow(index);//创建行
+            // 利用反射,根据javabean属性的先后顺序,动态调用getXxx()方法得到属性值
+            for (short i = 0; i < columnSettings.size(); i++) {
+                cell = row.createCell(i);//创建列
+                cell.setCellStyle(style2);
+                String methodName = columnSettings.get(i).getGetMethodName();
+                Method method = this.getDataClass().getMethod(methodName, new Class[]{});
+                Object value = method.invoke(obj, new Object[] {});
+                cell.setCellValue(value==null?"":value.toString());
+            }
+        }
+        workbook.write(out);
+    }
+    
+}

+ 43 - 0
examcloud-core-oe-student-base/src/main/java/cn/com/qmth/examcloud/core/oe/student/base/utils/excel/ExportUtils.java

@@ -0,0 +1,43 @@
+package cn.com.qmth.examcloud.core.oe.student.base.utils.excel;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.util.Collection;
+
+/*
+ * excel导出工具
+ */
+public class ExportUtils {
+
+	private static final Logger log = LoggerFactory.getLogger(ExportUtils.class);
+	
+    private static final String DEFALUT_CONTENT_TYPE = "application/vnd.ms-excel";
+
+    private static final String DEFALUT_EXT = ".xlsx";
+
+    public static void exportEXCEL(String fileName,Class<?> dataClass,
+                             Collection<?> dataset,HttpServletResponse response) throws Exception {
+    	log.info("导出Excel开始...");
+        response.setHeader("Content-Disposition","inline;filename="+URLEncoder.encode(fileName, "UTF-8")+DEFALUT_EXT);
+        response.setContentType(DEFALUT_CONTENT_TYPE);
+        ServletOutputStream outputStream = response.getOutputStream();
+    	
+        ExcelWriter excelExporter = new ExcelWriter(dataClass,"sheet1");
+        excelExporter.write(dataset,outputStream);
+        outputStream.flush();
+        outputStream.close();
+        log.info("导出Excel结束");
+    }
+    
+    public static void makeExcel(Class<?> dataClass,Collection<?> dataset,OutputStream outputStream ) throws Exception {
+		log.info("生成Excel开始...");
+		ExcelWriter excelExporter = new ExcelWriter(dataClass,"sheet1");
+		excelExporter.write(dataset,outputStream);
+		log.info("生成Excel结束");
+	}
+}

+ 31 - 0
examcloud-core-oe-student-dao/.gitignore

@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/

+ 118 - 0
examcloud-core-oe-student-dao/.mvn/wrapper/MavenWrapperDownloader.java

@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+    private static final String WRAPPER_VERSION = "0.5.5";
+    /**
+     * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+     */
+    private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+            + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+    /**
+     * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+     * use instead of the default one.
+     */
+    private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+            ".mvn/wrapper/maven-wrapper.properties";
+
+    /**
+     * Path where the maven-wrapper.jar will be saved to.
+     */
+    private static final String MAVEN_WRAPPER_JAR_PATH =
+            ".mvn/wrapper/maven-wrapper.jar";
+
+    /**
+     * Name of the property which should be used to override the default download url for the wrapper.
+     */
+    private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+    public static void main(String args[]) {
+        System.out.println("- Downloader started");
+        File baseDirectory = new File(args[0]);
+        System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+        // If the maven-wrapper.properties exists, read it and check if it contains a custom
+        // wrapperUrl parameter.
+        File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+        String url = DEFAULT_DOWNLOAD_URL;
+        if (mavenWrapperPropertyFile.exists()) {
+            FileInputStream mavenWrapperPropertyFileInputStream = null;
+            try {
+                mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+                Properties mavenWrapperProperties = new Properties();
+                mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+                url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+            } catch (IOException e) {
+                System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+            } finally {
+                try {
+                    if (mavenWrapperPropertyFileInputStream != null) {
+                        mavenWrapperPropertyFileInputStream.close();
+                    }
+                } catch (IOException e) {
+                    // Ignore ...
+                }
+            }
+        }
+        System.out.println("- Downloading from: " + url);
+
+        File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+        if (!outputFile.getParentFile().exists()) {
+            if (!outputFile.getParentFile().mkdirs()) {
+                System.out.println(
+                        "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+            }
+        }
+        System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+        try {
+            downloadFileFromURL(url, outputFile);
+            System.out.println("Done");
+            System.exit(0);
+        } catch (Throwable e) {
+            System.out.println("- Error downloading");
+            e.printStackTrace();
+            System.exit(1);
+        }
+    }
+
+    private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+        if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+            String username = System.getenv("MVNW_USERNAME");
+            char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+            Authenticator.setDefault(new Authenticator() {
+                @Override
+                protected PasswordAuthentication getPasswordAuthentication() {
+                    return new PasswordAuthentication(username, password);
+                }
+            });
+        }
+        URL website = new URL(urlString);
+        ReadableByteChannel rbc;
+        rbc = Channels.newChannel(website.openStream());
+        FileOutputStream fos = new FileOutputStream(destination);
+        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+        fos.close();
+        rbc.close();
+    }
+
+}

BIN
examcloud-core-oe-student-dao/.mvn/wrapper/maven-wrapper.jar


+ 2 - 0
examcloud-core-oe-student-dao/.mvn/wrapper/maven-wrapper.properties

@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.2/apache-maven-3.6.2-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar

+ 310 - 0
examcloud-core-oe-student-dao/mvnw

@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        export JAVA_HOME="`/usr/libexec/java_home`"
+      else
+        export JAVA_HOME="/Library/Java/Home"
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`which java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    if [ -n "$MVNW_REPOURL" ]; then
+      jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+    else
+      jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+    fi
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $jarUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+    if $cygwin; then
+      wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+    fi
+
+    if command -v wget > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget "$jarUrl" -O "$wrapperJarPath"
+        else
+            wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+        fi
+    elif command -v curl > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl -o "$wrapperJarPath" "$jarUrl" -f
+        else
+            curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+        fi
+        
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaClass=`cygpath --path --windows "$javaClass"`
+        fi
+        if [ -e "$javaClass" ]; then
+            if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaClass")
+            fi
+            if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

+ 182 - 0
examcloud-core-oe-student-dao/mvnw.cmd

@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %DOWNLOAD_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%

+ 19 - 0
examcloud-core-oe-student-dao/pom.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cn.com.qmth.examcloud</groupId>
+        <artifactId>examcloud-core-oe-student</artifactId>
+        <version>2019-SNAPSHOT</version>
+    </parent>
+    <artifactId>examcloud-core-oe-admin-dao</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud</groupId>
+            <artifactId>examcloud-core-oe-student-base</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+    </dependencies>
+</project>

+ 42 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/ExamFaceLivenessVerifyRepo.java

@@ -0,0 +1,42 @@
+package cn.com.qmth.examcloud.core.oe.student.dao;
+
+import cn.com.qmth.examcloud.core.oe.student.dao.entity.ExamFaceLivenessVerifyEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * @Description 人脸活体检测
+ * @Author lideyin
+ * @Date 2019/12/9 18:00
+ * @Version 1.0
+ */
+@Repository
+public interface ExamFaceLivenessVerifyRepo extends JpaRepository<ExamFaceLivenessVerifyEntity, Long>, JpaSpecificationExecutor<ExamFaceLivenessVerifyEntity> {
+    /**
+     * 使用examRecordId查询
+     * @param examRecordDataId
+     * @return
+     */
+    @Query(nativeQuery = true, value = "select * from ec_oe_exam_face_liveness_verify t where t.exam_record_data_id = ?1 and t.is_error = 0 order by id asc")
+    public List<ExamFaceLivenessVerifyEntity> findByExamRecordDataIdOrderById(Long examRecordDataId);
+    
+    /**
+     * 测试是否存在
+     * @param examRecordDataId
+     * @return
+     */
+    @Query(nativeQuery = true, value = "select 1 from ec_oe_exam_face_liveness_verify t where t.exam_record_data_id = ?1 and t.is_error = 0 limit 1")
+    public Long countByExamRecordDataId(Long examRecordDataId);
+
+    /**
+     * 取出is_error=1的最新的一条
+     * @param examRecordDataId
+     * @return
+     */
+    @Query(nativeQuery = true, value = "select * from ec_oe_exam_face_liveness_verify t where t.exam_record_data_id = ?1 and t.is_error = 1 order by id desc limit 1")
+    public ExamFaceLivenessVerifyEntity findErrorFaceVerifyByExamRecordDataId(Long examRecordDataId);
+}

+ 96 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/ExamRecordDataRepo.java

@@ -0,0 +1,96 @@
+package cn.com.qmth.examcloud.core.oe.student.dao;
+
+import cn.com.qmth.examcloud.core.oe.student.dao.entity.ExamRecordDataEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * @Description 考试记录
+ * @Author lideyin
+ * @Date 2019/12/9 18:00
+ * @Version 1.0
+ */
+@Repository
+public interface ExamRecordDataRepo extends JpaRepository<ExamRecordDataEntity, Long>, JpaSpecificationExecutor<ExamRecordDataEntity> {
+
+    public List<ExamRecordDataEntity> findByExamStudentId(Long examStudentId);
+
+    @Query(value = "select erd.* " +
+            "from ec_oe_exam_record_data erd " +
+            "where erd.exam_record_status in('EXAM_END','EXAM_OVERDUE') " +
+            "and erd.is_illegality=0 and (is_warn=0 or is_warn=1  and erd.is_audit=1) and erd.exam_student_id in ?1", nativeQuery = true)
+    public List<ExamRecordDataEntity> findByExamStudentIdList(List<Long> examStudentIdList);
+
+    public List<ExamRecordDataEntity> findByExamIdAndStudentCode(Long examId, String studentCode);
+
+    List<ExamRecordDataEntity> findByExamIdAndIdentityNumberAndCourseId(Long examId, String identityNumber, Long courseId);
+
+    /**
+     * 同步更新考生信息
+     *
+     * @param studentName
+     * @param studentCode
+     * @param infoCollector
+     */
+    @javax.transaction.Transactional
+    @Modifying
+    @Query(nativeQuery = true, value = "update ec_oe_exam_record_data set student_name = ?2,student_code = ?3,info_collector=?4 where exam_student_id = ?1")
+    public void syncUpdateExamStudentInfo(Long examStudentId, String studentName, String studentCode, String infoCollector);
+
+    @javax.transaction.Transactional
+    @Modifying
+    @Query(nativeQuery = true, value = "update ec_oe_exam_record_data set student_name = ?2  where student_id = ?1")
+    public void updateStudentName(Long studentId, String studentName);
+
+    @javax.transaction.Transactional
+    @Modifying
+    @Query(nativeQuery = true, value = "update ec_oe_exam_record_data set course_level = ?2 where course_id = ?1")
+    public void updateCourse(Long courseId, String courseLevel);
+
+    /**
+     * 根据studentId查询状态为"考试中"的在线考试记录
+     *
+     * @param studentId
+     * @return
+     */
+    @Query(value = " select * from ec_oe_exam_record_data t1 " +
+            " where t1.student_id = ?1  " +
+            " and t1.exam_record_status = 'EXAM_ING' " +
+            " and t1.exam_type = 'ONLINE'", nativeQuery = true)
+    public ExamRecordDataEntity findOnlineExamingRecordByStudentId(Long studentId);
+
+    /**
+     * 更新考试记录中的考试作答记录id
+     *
+     * @param examRecordQuestionId 考试作答记录id
+     * @param id                   examRecordDataId
+     * @return
+     */
+    @Transactional
+    @Modifying
+    @Query(value = "update ec_oe_exam_record_data set exam_record_questions_id=?1 where id=?2", nativeQuery = true)
+    int updateExamRecordDataQuestionIdById(String examRecordQuestionId, Long id);
+
+    /**
+     * 更新活体检测结果
+     *
+     * @param faceVerifyResult
+     * @param id
+     * @return
+     */
+    @Transactional
+    @Modifying
+    @Query(value = "update ec_oe_exam_record_data set face_verify_result = ?1  where student_id = ?2", nativeQuery = true)
+    int updateFaceVerifyResult(String faceVerifyResult, Long id);
+
+    @Query(value = "select * from ec_oe_exam_record_data " +
+            "where exam_record_status in('EXAM_END','EXAM_OVERDUE') and is_illegality=0 and (is_warn=0 or is_warn=1  and is_audit=1) " +
+            "and exam_id=?1 and id>=?2 order by id asc limit ?3", nativeQuery = true)
+    List<ExamRecordDataEntity> findLimitedDataByExamIdAndIdMoreThan(Long examId, Long startExamRecordId, int rowCount);
+}

+ 66 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/FaceBiopsyItemRepo.java

@@ -0,0 +1,66 @@
+package cn.com.qmth.examcloud.core.oe.student.dao;
+
+import cn.com.qmth.examcloud.core.oe.student.dao.entity.FaceBiopsyItemEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import javax.transaction.Transactional;
+import java.util.List;
+
+/**
+ * @Description 人脸检测明细仓库
+ * @Author lideyin
+ * @Date 2019/7/17 18:58
+ * @Version 1.0
+ */
+@Repository
+public interface FaceBiopsyItemRepo extends JpaRepository<FaceBiopsyItemEntity, Long>, JpaSpecificationExecutor<FaceBiopsyItemEntity> {
+
+    List<FaceBiopsyItemEntity> findByExamRecordDataIdOrderByIdAsc(Long examRecordDataId);
+
+    /**
+     * 根据活检结果id,按明细id正序获取活检明细
+     * @param faceBiopsyId 活检最终结果表id
+     * @return
+     */
+    List<FaceBiopsyItemEntity> findByFaceBiopsyIdOrderByIdAsc(Long faceBiopsyId);
+
+    /**
+     * 根据活检结果id,按明细id正序获取第一条活检明细(即第一次活检)
+     * @param faceBiopsyId 活检最终结果表id
+     * @return
+     */
+    FaceBiopsyItemEntity findFirstByFaceBiopsyIdOrderByIdAsc(Long faceBiopsyId);
+
+    /**
+     * 根据活检结果id,按明细id倒序获取第一条活检明细(即最后一次活检)
+     * @param faceBiopsyId 活检最终结果表id
+     * @return
+     */
+    FaceBiopsyItemEntity findFirstByFaceBiopsyIdOrderByIdDesc(Long faceBiopsyId);
+
+    /**
+     * 根据考试记录id,按明细id正序获取第一条活检明细(即第一次活检)
+     * @param examRecordDataId 考试记录id
+     * @return
+     */
+    FaceBiopsyItemEntity findFirstByExamRecordDataIdOrderByIdAsc(Long examRecordDataId);
+
+    /**
+     * 根据考试记录id和人检检测完成状态获取数据
+     *
+     * @param examRecordDataId 考试记录id
+     * @param completed        是否检测完成
+     * @return
+     */
+    List<FaceBiopsyItemEntity> findByExamRecordDataIdAndCompleted(Long examRecordDataId, Boolean completed);
+
+    @Transactional
+    @Modifying
+    @Query(nativeQuery = true,
+            value = "update ec_oe_exam_face_biopsy_item set result=?2,error_msg=?3,completed=?4 where id=?1")
+    void updateFaceBiopsyItemResult(Long id, boolean result, String errorMsg, boolean completed);
+}

+ 20 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/FaceBiopsyItemStepRepo.java

@@ -0,0 +1,20 @@
+package cn.com.qmth.examcloud.core.oe.student.dao;
+
+import cn.com.qmth.examcloud.core.oe.student.dao.entity.FaceBiopsyItemStepEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * @Description 人脸检测明细仓库
+ * @Author lideyin
+ * @Date 2019/7/17 18:58
+ * @Version 1.0
+ */
+@Repository
+public interface FaceBiopsyItemStepRepo extends JpaRepository<FaceBiopsyItemStepEntity, Long>, JpaSpecificationExecutor<FaceBiopsyItemStepEntity> {
+	List<FaceBiopsyItemStepEntity> findByFaceBiopsyItemId(Long faceBiopsyItemId);
+
+}

+ 29 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/FaceBiopsyRepo.java

@@ -0,0 +1,29 @@
+package cn.com.qmth.examcloud.core.oe.student.dao;
+
+import cn.com.qmth.examcloud.core.oe.student.dao.entity.FaceBiopsyEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import javax.transaction.Transactional;
+
+/**
+ * @Description 人脸检测结果仓库
+ * @Author lideyin
+ * @Date 2019/7/17 18:58
+ * @Version 1.0
+ */
+@Repository
+public interface FaceBiopsyRepo extends JpaRepository<FaceBiopsyEntity, Long>, JpaSpecificationExecutor<FaceBiopsyEntity> {
+
+
+    FaceBiopsyEntity findByExamRecordDataId(Long examRecordDataId);
+
+    @Transactional
+    @Modifying
+    @Query(nativeQuery = true,
+            value = "update ec_oe_exam_face_biopsy set result=?2,error_msg=?3 where exam_record_data_id=?1")
+    void updateFaceBiopsyResult(Long examRecordDataId, boolean result, String errorMsg);
+}

+ 34 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/UniqueRuleHolder.java

@@ -0,0 +1,34 @@
+package cn.com.qmth.examcloud.core.oe.student.dao;
+
+import cn.com.qmth.examcloud.web.jpa.UniqueRule;
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+/**
+ * @Description 唯一约束holder 状态码范围110XXX
+ * @Author lideyin
+ * @Date 2019/9/7 11:43
+ * @Version 1.0
+ */
+public class UniqueRuleHolder {
+    private static List<UniqueRule> LIST = Lists.newArrayList();
+
+    public static List<UniqueRule> getUniqueRuleList() {
+        return LIST;
+    }
+
+    static {
+        // ResourceEntity
+        LIST.add(new UniqueRule("IDX_E_O_E_A_001", "110001", "考试审核记录已存在"));
+        LIST.add(new UniqueRule("IDX_E_O_E_C_001", "110002", "抓拍照片处理结果已存在"));
+        LIST.add(new UniqueRule("IDX_E_O_E_C_Q_001", "110003", "拍拍照片队列已存在"));
+        LIST.add(new UniqueRule("IDX_E_O_E_F_A_T_001", "110004", "文件作答结果已存在"));
+        LIST.add(new UniqueRule("IDX_E_O_E_R_002", "110005", "考生已存在进行中的考试"));
+        LIST.add(new UniqueRule("IDX_E_O_E_R_D_001", "110006", "考试记录已存在"));
+        LIST.add(new UniqueRule("IDX_E_O_E_R_4_M_001", "110007", "待阅卷的考试记录已存在"));
+        LIST.add(new UniqueRule("IDX_E_O_E_S_001", "110008", "考试分数已存在"));
+        LIST.add(new UniqueRule("IDX_E_O_E_S_001", "110009", "考生已存在"));
+        LIST.add(new UniqueRule("IDX_E_O_E_O_S_H_001", "110010", "机构推分队列已存在"));
+    }
+}

+ 155 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/ExamFaceLivenessVerifyEntity.java

@@ -0,0 +1,155 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.entity;
+
+import cn.com.qmth.examcloud.core.oe.student.dao.enums.FaceVerifyResult;
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+import javax.persistence.*;
+import java.util.Date;
+
+
+/**
+ * @author  	chenken
+ * @date    	2018年2月5日 上午8:28:34
+ * @company 	QMTH
+ * @description 人脸活体检测
+ */
+@Entity
+@Table(name="ec_oes_exam_face_liveness_verify",indexes ={
+@Index(name = "IDX_E_O_E_F_L_V_001",columnList = "examRecordDataId, isError")
+} )
+public class ExamFaceLivenessVerifyEntity extends JpaEntity{
+	
+	private static final long serialVersionUID = -3428631813990503829L;
+	/**
+	 * 主键ID
+	 */
+	@Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+	/**
+	 * 考试记录 DataID
+	 */
+	private Long examRecordDataId;
+	/**
+	 * 验证考试时间
+	 */
+	private Date startTime;
+	/**
+	 * 验证使用时间
+	 */
+	private Long usedTime;
+	/**
+	 * faceId返回json:notify_url返回
+	 * 网页端活体检测及比对返回值说明:https://faceid.com/pages/documents/5680508
+	 */
+	private String resultJson;
+	/**
+	 * 验证结果
+	 */
+	@Enumerated(EnumType.STRING)
+	private FaceVerifyResult verifyResult;
+	
+	/**
+	 * https://api.megvii.com/faceid/liveness/v2/get_result 
+	 * 用于活体结果反查
+	 */
+	private String bizId;
+	
+	/**
+	 * 发生错误
+	 */
+	private Boolean isError;
+	/**
+	 * 错误信息
+	 */
+	private String errorMsg;
+	
+	/**
+	 * 当前记录操作次数
+	 */
+	private Integer operateNum;
+	
+	public ExamFaceLivenessVerifyEntity(){}
+	
+	public Long getId() {
+		return id;
+	}
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public Boolean getError() {
+		return isError;
+	}
+
+	public void setError(Boolean error) {
+		isError = error;
+	}
+
+	public Date getStartTime() {
+		return startTime;
+	}
+	public void setStartTime(Date startTime) {
+		this.startTime = startTime;
+	}
+	public Long getUsedTime() {
+		return usedTime;
+	}
+	public void setUsedTime(Long usedTime) {
+		this.usedTime = usedTime;
+	}
+	public String getResultJson() {
+		return resultJson;
+	}
+	public void setResultJson(String resultJson) {
+		this.resultJson = resultJson;
+	}
+	public FaceVerifyResult getVerifyResult() {
+		return verifyResult;
+	}
+	public void setVerifyResult(FaceVerifyResult verifyResult) {
+		this.verifyResult = verifyResult;
+	}
+
+	public String getBizId() {
+		return bizId;
+	}
+
+	public void setBizId(String bizId) {
+		this.bizId = bizId;
+	}
+
+	public Boolean getIsError() {
+		return isError;
+	}
+
+	public void setIsError(Boolean isError) {
+		this.isError = isError;
+	}
+
+	public String getErrorMsg() {
+		return errorMsg;
+	}
+
+	public void setErrorMsg(String errorMsg) {
+		this.errorMsg = errorMsg;
+	}
+
+	public Integer getOperateNum() {
+		return operateNum;
+	}
+
+	public void setOperateNum(Integer operateNum) {
+		this.operateNum = operateNum;
+	}
+	
+}
+

+ 524 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/ExamRecordDataEntity.java

@@ -0,0 +1,524 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.entity;
+
+import cn.com.qmth.examcloud.core.oe.student.dao.enums.ExamRecordStatus;
+import cn.com.qmth.examcloud.core.oe.student.dao.enums.ExamType;
+import cn.com.qmth.examcloud.core.oe.student.dao.enums.IsSuccess;
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+import org.hibernate.annotations.DynamicInsert;
+
+import java.util.Date;
+import javax.persistence.*;
+
+/**
+ * @author chenken
+ * @date 2018年8月13日 上午11:41:34
+ * @company QMTH
+ * @description 考试记录数据表, 与ec_oe_exam_record一对一关系
+ */
+@Entity
+@Table(name = "ec_oes_exam_record_data", indexes = {
+        @Index(name = "IDX_E_O_E_R_D_001", columnList = "examStudentId"),
+        @Index(name = "IDX_E_O_E_R_D_002", columnList = "studentCode"),
+        @Index(name = "IDX_E_O_E_R_D_003", columnList = "studentId,examType"),
+        @Index(name = "IDX_E_O_E_R_D_004", columnList = "examId"),
+        @Index(name = "IDX_E_O_E_R_D_005", columnList = "courseId"),
+        @Index(name = "IDX_E_O_E_R_D_006", columnList = "examRecordStatus"),
+})
+@DynamicInsert
+public class ExamRecordDataEntity extends JpaEntity {
+    /**
+     *
+     */
+    private static final long serialVersionUID = -242327915801750970L;
+    /**
+     * 主键
+     */
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    /**
+     * 考试ID
+     */
+    private Long examId;
+    /**
+     * 考试类型
+     */
+    @Column(length = 20)
+    @Enumerated(EnumType.STRING)
+    private ExamType examType;
+    /**
+     * 考生ID
+     */
+    private Long examStudentId;
+    /**
+     * 学生ID
+     */
+    private Long studentId;
+    /**
+     * 学号
+     */
+    @Column(length = 50)
+    private String studentCode;
+    /**
+     * 学生姓名
+     */
+    @Column(length = 20)
+    private String studentName;
+    /**
+     * 身份证号
+     */
+    @Column(length = 20)
+    private String identityNumber;
+    /**
+     * 课程ID
+     */
+    private Long courseId;
+
+    private String courseLevel;
+    /**
+     * 学习中心ID
+     */
+    private Long orgId;
+    /**
+     * 顶级机构ID
+     */
+    private Long rootOrgId;
+    /**
+     * 基础试卷ID
+     */
+    private String basePaperId;
+
+    /**
+     * 试卷类型
+     */
+    private String paperType;
+    /**
+     * 试卷结构 ID
+     */
+    private String paperStructId;
+    /**
+     * 采集人
+     */
+    @Column(length = 255)
+    private String infoCollector;
+
+    /**
+     * 考试作答记录id
+     */
+    private String examRecordQuestionsId;
+    /**
+     * 考试记录状态(考试中,考试结束,考试过期,考试作废)
+     */
+    @Enumerated(EnumType.STRING)
+    private ExamRecordStatus examRecordStatus;
+    /**
+     * 考试开始时间
+     */
+    private Date startTime;
+    /**
+     * 考试结束时间
+     */
+    private Date endTime;
+    /**
+     * 考试被清理时间
+     */
+    private Date cleanTime;
+    /**
+     * 是否异常数据
+     */
+    private Boolean isWarn;
+    /**
+     * 是否被审核过
+     */
+    private Boolean isAudit;
+    /**
+     * 是否违纪
+     */
+    @Column(name = "is_illegality")
+    private Boolean isIllegality;
+
+    /**
+     * 考试时长
+     */
+    private Long usedExamTime;
+    /**
+     * 第几次考试
+     */
+    private Integer examOrder;
+    /**
+     * 是否为重考
+     */
+    @Column(name = "is_reexamine")
+    private Boolean isReexamine;
+    /**
+     * 是否断点续考
+     */
+    @Column(name = "is_continued")
+    private Boolean isContinued;
+    /**
+     * 是否是全客观题卷  1:是   0:否
+     */
+    private Boolean isAllObjectivePaper;
+    /**
+     * 断点续考次数
+     */
+    private Integer continuedCount;
+    /**
+     * 是否达到最大断点限制
+     */
+    private Boolean isExceed;
+    /**
+     * 抓拍比对成功次数
+     */
+    private Integer faceSuccessCount;
+    /**
+     * 抓拍比对失败次数
+     */
+    private Integer faceFailedCount;
+    /**
+     * 抓拍存在陌生人的次数
+     */
+    private Integer faceStrangerCount;
+    /**
+     * 抓拍比对总次数
+     */
+    private Integer faceTotalCount;
+    /**
+     * 抓拍比对成功比率
+     */
+    private Double faceSuccessPercent;
+    /**
+     * @see IsSuccess
+     * 活体检测结果
+     */
+    @Column(length = 20)
+    @Enumerated(EnumType.STRING)
+    private IsSuccess faceVerifyResult;
+
+    /**
+     * 百度人脸活体检测通过率
+     */
+    private Double baiduFaceLivenessSuccessPercent;
+    /**
+     * 人脸五官坐标比对值
+     */
+    private Double faceLandmarkVal;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public ExamType getExamType() {
+        return examType;
+    }
+
+    public void setExamType(ExamType examType) {
+        this.examType = examType;
+    }
+
+    public Long getExamStudentId() {
+        return examStudentId;
+    }
+
+    public void setExamStudentId(Long examStudentId) {
+        this.examStudentId = examStudentId;
+    }
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public String getBasePaperId() {
+        return basePaperId;
+    }
+
+    public void setBasePaperId(String basePaperId) {
+        this.basePaperId = basePaperId;
+    }
+
+    public String getPaperStructId() {
+        return paperStructId;
+    }
+
+    public void setPaperStructId(String paperStructId) {
+        this.paperStructId = paperStructId;
+    }
+
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+    public String getInfoCollector() {
+        return infoCollector;
+    }
+
+    public void setInfoCollector(String infoCollector) {
+        this.infoCollector = infoCollector;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public Date getCleanTime() {
+        return cleanTime;
+    }
+
+    public void setCleanTime(Date cleanTime) {
+        this.cleanTime = cleanTime;
+    }
+
+    public Boolean getIsWarn() {
+        return isWarn;
+    }
+
+    public void setIsWarn(Boolean isWarn) {
+        this.isWarn = isWarn;
+    }
+
+    public Boolean getIsAudit() {
+        return isAudit;
+    }
+
+    public void setIsAudit(Boolean isAudit) {
+        this.isAudit = isAudit;
+    }
+
+    public Boolean getIsIllegality() {
+        return isIllegality;
+    }
+
+    /**
+     * 设置是否违纪
+     *
+     * @param isIllegality
+     */
+    public void setIsIllegality(Boolean isIllegality) {
+        this.isIllegality = isIllegality;
+    }
+
+    public ExamRecordStatus getExamRecordStatus() {
+        return examRecordStatus;
+    }
+
+    public void setExamRecordStatus(ExamRecordStatus examRecordStatus) {
+        this.examRecordStatus = examRecordStatus;
+    }
+
+    public Long getUsedExamTime() {
+        return usedExamTime;
+    }
+
+    public void setUsedExamTime(Long usedExamTime) {
+        this.usedExamTime = usedExamTime;
+    }
+
+    public Integer getExamOrder() {
+        return examOrder;
+    }
+
+    public void setExamOrder(Integer examOrder) {
+        this.examOrder = examOrder;
+    }
+
+    public Boolean getIsReexamine() {
+        return isReexamine;
+    }
+
+    public void setIsReexamine(Boolean isReexamine) {
+        this.isReexamine = isReexamine;
+    }
+
+    public Boolean getIsContinued() {
+        return isContinued;
+    }
+
+    public void setIsContinued(Boolean isContinued) {
+        this.isContinued = isContinued;
+    }
+
+    public Integer getContinuedCount() {
+        return continuedCount;
+    }
+
+    public void setContinuedCount(Integer continuedCount) {
+        this.continuedCount = continuedCount;
+    }
+
+    public Integer getFaceSuccessCount() {
+        return faceSuccessCount;
+    }
+
+    public void setFaceSuccessCount(Integer faceSuccessCount) {
+        this.faceSuccessCount = faceSuccessCount;
+    }
+
+    public Integer getFaceFailedCount() {
+        return faceFailedCount;
+    }
+
+    public void setFaceFailedCount(Integer faceFailedCount) {
+        this.faceFailedCount = faceFailedCount;
+    }
+
+    public Integer getFaceStrangerCount() {
+        return faceStrangerCount;
+    }
+
+    public void setFaceStrangerCount(Integer faceStrangerCount) {
+        this.faceStrangerCount = faceStrangerCount;
+    }
+
+    public Integer getFaceTotalCount() {
+        return faceTotalCount;
+    }
+
+    public void setFaceTotalCount(Integer faceTotalCount) {
+        this.faceTotalCount = faceTotalCount;
+    }
+
+    public Double getFaceSuccessPercent() {
+        return faceSuccessPercent;
+    }
+
+    public void setFaceSuccessPercent(Double faceSuccessPercent) {
+        this.faceSuccessPercent = faceSuccessPercent;
+    }
+
+    public IsSuccess getFaceVerifyResult() {
+        return faceVerifyResult;
+    }
+
+    public void setFaceVerifyResult(IsSuccess faceVerifyResult) {
+        this.faceVerifyResult = faceVerifyResult;
+    }
+
+    public Double getFaceLandmarkVal() {
+        return faceLandmarkVal;
+    }
+
+    public void setFaceLandmarkVal(Double faceLandmarkVal) {
+        this.faceLandmarkVal = faceLandmarkVal;
+    }
+
+    public Boolean getIsAllObjectivePaper() {
+        return isAllObjectivePaper;
+    }
+
+    public void setIsAllObjectivePaper(Boolean isAllObjectivePaper) {
+        this.isAllObjectivePaper = isAllObjectivePaper;
+    }
+
+    public Double getBaiduFaceLivenessSuccessPercent() {
+        return baiduFaceLivenessSuccessPercent;
+    }
+
+    public void setBaiduFaceLivenessSuccessPercent(
+            Double baiduFaceLivenessSuccessPercent) {
+        this.baiduFaceLivenessSuccessPercent = baiduFaceLivenessSuccessPercent;
+    }
+
+    public Boolean getIsExceed() {
+        return isExceed;
+    }
+
+    public void setIsExceed(Boolean isExceed) {
+        this.isExceed = isExceed;
+    }
+
+    public String getExamRecordQuestionsId() {
+        return examRecordQuestionsId;
+    }
+
+    public void setExamRecordQuestionsId(String examRecordQuestionsId) {
+        this.examRecordQuestionsId = examRecordQuestionsId;
+    }
+}

+ 82 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/FaceBiopsyEntity.java

@@ -0,0 +1,82 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.entity;
+
+import cn.com.qmth.examcloud.web.jpa.WithIdJpaEntity;
+
+import javax.persistence.*;
+
+/**
+ * @Description 人脸活体检测结果实体
+ * @Author lideyin
+ * @Date 2019/7/17 15:15
+ * @Version 1.0
+ */
+@Entity
+@Table(name = "EC_OES_EXAM_FACE_BIOPSY", indexes = {
+        @Index(name = "IDX_E_F_B_001001", columnList = "examRecordDataId", unique = true)
+})
+public class FaceBiopsyEntity extends WithIdJpaEntity {
+    /**
+     * 组织机构id
+     */
+    @Column(nullable = false)
+    private Long rootOrgId;
+    /**
+     * 考试记录id
+     */
+    @Column(nullable = false)
+    private Long examRecordDataId;
+    /**
+     * 检测结果
+     */
+    private Boolean result;
+
+    /**
+     * 已检测次数
+     */
+    private int verifiedTimes;
+    /**
+     * 错误信息
+     */
+    @Column(length = 500)
+    private String errorMsg;
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public Long getExamRecordDataId() {
+        return examRecordDataId;
+    }
+
+    public void setExamRecordDataId(Long examRecordDataId) {
+        this.examRecordDataId = examRecordDataId;
+    }
+
+    public Boolean getResult() {
+        return result;
+    }
+
+    public void setResult(Boolean result) {
+        this.result = result;
+    }
+
+    public String getErrorMsg() {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg) {
+        this.errorMsg = errorMsg;
+    }
+
+    public int getVerifiedTimes() {
+        return verifiedTimes;
+    }
+
+    public void setVerifiedTimes(int verifiedTimes) {
+        this.verifiedTimes = verifiedTimes;
+    }
+}

+ 118 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/FaceBiopsyItemEntity.java

@@ -0,0 +1,118 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.entity;
+
+import cn.com.qmth.examcloud.core.oe.student.dao.enums.FaceBiopsyType;
+import cn.com.qmth.examcloud.web.jpa.WithIdJpaEntity;
+
+import javax.persistence.*;
+
+/**
+ * @Description 人脸活体检测明细
+ * @Author lideyin
+ * @Date 2019/7/17 16:30
+ * @Version 1.0
+ */
+@Entity
+@Table(name = "EC_OES_EXAM_FACE_BIOPSY_ITEM", indexes = {
+        @Index(name = "IDX_E_F_B_I_001001", columnList = "examRecordDataId"),
+        @Index(name = "IDX_E_F_B_I_001002", columnList = "faceBiopsyId")
+})
+public class FaceBiopsyItemEntity extends WithIdJpaEntity {
+
+    private static final long serialVersionUID = -8411713406344895994L;
+    /**
+     * 考试记录id
+     */
+    @Column(nullable = false)
+    private Long examRecordDataId;
+
+    /**
+     * 人脸识别结果id
+     */
+    @Column(nullable = false)
+    private Long faceBiopsyId;
+
+    /**
+     * 人脸识别类型
+     */
+    @Column(nullable = false)
+    @Enumerated(EnumType.STRING)
+    private FaceBiopsyType faceBiopsyType;
+
+    /**
+     * 检测是否已完成
+     */
+    @Column(nullable = false)
+    private Boolean completed;
+
+    /**
+     * 检测结果
+     */
+    private Boolean result;
+    /**
+     * 错误信息
+     */
+    @Column(length = 500)
+    private String errorMsg;
+
+    /**
+     * 是否在冻结时间内
+     */
+    @Column(nullable = false)
+    private Boolean inFreezeTime;
+
+    public Long getExamRecordDataId() {
+        return examRecordDataId;
+    }
+
+    public void setExamRecordDataId(Long examRecordDataId) {
+        this.examRecordDataId = examRecordDataId;
+    }
+
+    public Long getFaceBiopsyId() {
+        return faceBiopsyId;
+    }
+
+    public void setFaceBiopsyId(Long faceBiopsyId) {
+        this.faceBiopsyId = faceBiopsyId;
+    }
+
+    public FaceBiopsyType getFaceBiopsyType() {
+        return faceBiopsyType;
+    }
+
+    public void setFaceBiopsyType(FaceBiopsyType faceBiopsyType) {
+        this.faceBiopsyType = faceBiopsyType;
+    }
+
+    public Boolean getCompleted() {
+        return completed;
+    }
+
+    public void setCompleted(Boolean completed) {
+        this.completed = completed;
+    }
+
+    public Boolean getResult() {
+        return result;
+    }
+
+    public void setResult(Boolean result) {
+        this.result = result;
+    }
+
+    public String getErrorMsg() {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg) {
+        this.errorMsg = errorMsg;
+    }
+
+    public Boolean getInFreezeTime() {
+        return inFreezeTime;
+    }
+
+    public void setInFreezeTime(Boolean inFreezeTime) {
+        this.inFreezeTime = inFreezeTime;
+    }
+}

+ 193 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/FaceBiopsyItemStepEntity.java

@@ -0,0 +1,193 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.entity;
+
+import cn.com.qmth.examcloud.core.oe.student.dao.enums.FaceBiopsyAction;
+import cn.com.qmth.examcloud.core.oe.student.dao.enums.ResourceType;
+import cn.com.qmth.examcloud.web.jpa.WithIdJpaEntity;
+import javax.persistence.*;
+
+/**
+ * @Description 人脸活体检测步骤
+ * @Author lideyin
+ * @Date 2019/7/17 16:31
+ * @Version 1.0
+ */
+@Entity
+@Table(name = "EC_OES_EXAM_FACE_BIOPSY_ITEM_STEP", indexes = {
+        @Index(name = "IDX_E_F_B_I_S_001001", columnList = "examRecordDataId"),
+        @Index(name = "IDX_E_F_B_I_S_001002", columnList = "faceBiopsyItemId")
+})
+public class FaceBiopsyItemStepEntity extends WithIdJpaEntity {
+    /**
+     * 考试记录id
+     */
+    @Column(nullable = false)
+    private Long examRecordDataId;
+    /**
+     * 人脸活体检测明细id
+     */
+    @Column(nullable = false)
+    private Long faceBiopsyItemId;
+
+    /**
+     * 步骤动作
+     */
+    @Column(nullable = false, length = 20)
+    @Enumerated(EnumType.STRING)
+    private FaceBiopsyAction action;
+
+    /**
+     * 资源文件相对路径
+     */
+    @Column(length = 200)
+    private String resourceRelativePath;
+    /**
+     * 资源文件类型
+     */
+    @Column(length = 20)
+    @Enumerated(EnumType.STRING)
+    private ResourceType resourceType;
+
+    /**
+     * 动作时长
+     */
+    private Integer actionStay;
+
+    /**
+     * 检测结果
+     */
+    private Boolean result;
+    /**
+     * 错误信息
+     */
+    @Column(length = 500)
+    private String errorMsg;
+    /**
+     * 扩展属性1
+     */
+    @Column(length = 2000)
+    private String ext1;
+    /**
+     * 扩展属性2
+     */
+    @Column(length = 2000)
+    private String ext2;
+    /**
+     * 扩展属性3
+     */
+    @Column(length = 2000)
+    private String ext3;
+    /**
+     * 扩展属性4
+     */
+    @Column(length = 2000)
+    private String ext4;
+    /**
+     * 扩展属性5
+     */
+    @Column(length = 2000)
+    private String ext5;
+
+    public Long getExamRecordDataId() {
+        return examRecordDataId;
+    }
+
+    public void setExamRecordDataId(Long examRecordDataId) {
+        this.examRecordDataId = examRecordDataId;
+    }
+
+    public Long getFaceBiopsyItemId() {
+        return faceBiopsyItemId;
+    }
+
+    public void setFaceBiopsyItemId(Long faceBiopsyItemId) {
+        this.faceBiopsyItemId = faceBiopsyItemId;
+    }
+
+    public FaceBiopsyAction getAction() {
+        return action;
+    }
+
+    public void setAction(FaceBiopsyAction action) {
+        this.action = action;
+    }
+
+    public String getResourceRelativePath() {
+        return resourceRelativePath;
+    }
+
+    public void setResourceRelativePath(String resourceRelativePath) {
+        this.resourceRelativePath = resourceRelativePath;
+    }
+
+    public ResourceType getResourceType() {
+        return resourceType;
+    }
+
+    public void setResourceType(ResourceType resourceType) {
+        this.resourceType = resourceType;
+    }
+
+    public Integer getActionStay() {
+        return actionStay;
+    }
+
+    public void setActionStay(Integer actionStay) {
+        this.actionStay = actionStay;
+    }
+
+    public Boolean getResult() {
+        return result;
+    }
+
+    public void setResult(Boolean result) {
+        this.result = result;
+    }
+
+    public String getErrorMsg() {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg) {
+        this.errorMsg = errorMsg;
+    }
+
+    public String getExt1() {
+        return ext1;
+    }
+
+    public void setExt1(String ext1) {
+        this.ext1 = ext1;
+    }
+
+    public String getExt2() {
+        return ext2;
+    }
+
+    public void setExt2(String ext2) {
+        this.ext2 = ext2;
+    }
+
+    public String getExt3() {
+        return ext3;
+    }
+
+    public void setExt3(String ext3) {
+        this.ext3 = ext3;
+    }
+
+    public String getExt4() {
+        return ext4;
+    }
+
+    public void setExt4(String ext4) {
+        this.ext4 = ext4;
+    }
+
+    public String getExt5() {
+        return ext5;
+    }
+
+    public void setExt5(String ext5) {
+        this.ext5 = ext5;
+    }
+}

+ 46 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/ExamRecordStatus.java

@@ -0,0 +1,46 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.enums;
+
+/**
+ * 考试记录状态
+ *
+ * @author xudengqi
+ * 2016年8月23日
+ */
+public enum ExamRecordStatus {
+    /**
+     * 考试中
+     */
+    EXAM_ING,
+    /**
+     * 手工交卷处理中
+     */
+    EXAM_HAND_IN,
+    /**
+     * 系统交卷处理中
+     */
+    EXAM_AUTO_HAND_IN,
+    /**
+     * 考试结束
+     */
+    EXAM_END,
+
+    /**
+     * 考试过期
+     */
+    EXAM_OVERDUE,
+
+    /**
+     * 考试无效/作废
+     */
+    EXAM_INVALID;
+
+    public static ExamRecordStatus getByName(String name) {
+        for (ExamRecordStatus status : values()) {
+            if (status.name().equals(name)) {
+                return status;
+            }
+        }
+        return null;
+    }
+
+}

+ 42 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/ExamType.java

@@ -0,0 +1,42 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.enums;
+
+
+/**
+ * @author ting.yin
+ * @Description: 考试类型
+ * @date 2017年1月5日
+ */
+public enum ExamType {
+    /**
+     * 传统考试
+     */
+    TRADITION,
+    /**
+     * 在线考试
+     */
+    ONLINE,
+    /**
+     * 在线练习
+     */
+    PRACTICE,
+
+    /**
+     * 离线考试
+     */
+    OFFLINE,
+
+    /**
+     * 分布式印刷考试
+     */
+    PRINT_EXAM;
+
+    public static ExamType strToEnum(String str) {
+        for (ExamType examType : ExamType.values()) {
+            if (examType.name().equals(str)) {
+                return examType;
+            }
+        }
+        return null;
+    }
+
+}

+ 25 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/FaceBiopsyAction.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.enums;
+/*
+ * @Description 人脸活体检测动作
+ * @Author lideyin
+ * @Date 2019/7/17 16:46
+ * @Version 1.0
+ */
+public enum FaceBiopsyAction {
+    /**
+     * 人脸比对
+     */
+    FACE_COMPARE,
+    /**
+     * 开心
+     */
+    HAPPY,
+    /**
+     * 严肃
+     */
+    SERIOUS,
+    /**
+     * 视频比对
+     */
+    VIDEO_COMPARE
+}

+ 41 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/FaceBiopsyScheme.java

@@ -0,0 +1,41 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.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;
+    }
+}

+ 17 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/FaceBiopsyType.java

@@ -0,0 +1,17 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.enums;
+/*
+ * @Description 人脸活体检测类型
+ * @Author lideyin
+ * @Date 2019/7/17 16:46
+ * @Version 1.0
+ */
+public enum FaceBiopsyType {
+    /**
+     * faceid人脸活体识别技术
+     */
+    FACE_ID,
+    /**
+     * 人脸移动识别技术
+     */
+    FACE_MOTION
+}

+ 49 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/FaceVerifyResult.java

@@ -0,0 +1,49 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.enums;
+
+/**
+ * @author  	chenken
+ * @date    	2018年2月5日 上午8:38:04
+ * @company 	QMTH
+ * @description 人脸活体检测验证结果
+ */
+public enum FaceVerifyResult {
+	
+	VERIFY_SUCCESS("验证成功"),
+	
+	VERIFY_FAILED("动作有误,验证失败"),
+	
+	NOT_ONESELF("不是本人"),
+	
+	TIME_OUT("超时未完成");
+	
+	public static String getDescByName(Object name){
+		if(name == null){
+			return "检测界面打开后未开启检测";
+		}
+		String nameString = name.toString();
+		for(FaceVerifyResult faceVerifyResult:FaceVerifyResult.values()){
+			if(nameString.equals(faceVerifyResult.name())){
+				return faceVerifyResult.getDesc();
+			}
+		}
+		return "";
+	}
+	
+	private String desc;
+	
+	private FaceVerifyResult(String desc){
+		this.desc = desc;
+	}
+
+	public String getDesc() {
+		return desc;
+	}
+
+	public void setDesc(String desc) {
+		this.desc = desc;
+	}
+	public static void main(String[] args) {
+		
+	}
+}	
+

+ 48 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/IsSuccess.java

@@ -0,0 +1,48 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.enums;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author  	chenken
+ * @date    	2018年2月26日 下午3:13:05
+ * @company 	QMTH
+ * @description 成功或失败
+ */
+public enum IsSuccess {
+	/**
+	 * 成功
+	 */
+	SUCCESS("成功"),
+	/**
+	 * 失败
+	 */
+	FAILED("失败");
+	
+	private String desc;
+	
+	public static IsSuccess strToEnum(String name){
+		if(StringUtils.isBlank(name)){
+			return null;
+		}
+		for(IsSuccess isSuccess:IsSuccess.values()){
+			if(name.equals(isSuccess.name())){
+				return isSuccess;
+			}
+		}
+		
+		return null;
+	}
+	
+	private IsSuccess(String desc){
+		this.desc = desc;
+	}
+	
+	public String getDesc() {
+		return desc;
+	}
+
+	public void setDesc(String desc) {
+		this.desc = desc;
+	}
+}
+

+ 17 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/enums/ResourceType.java

@@ -0,0 +1,17 @@
+package cn.com.qmth.examcloud.core.oe.student.dao.enums;
+/*
+ * @Description 资源类型
+ * @Author lideyin
+ * @Date 2019/7/17 16:46
+ * @Version 1.0
+ */
+public enum ResourceType {
+    /**
+     * 图片
+     */
+    PIC,
+    /**
+     * 视频
+     */
+    VIDEO
+}