my 3 vuotta sitten
commit
929fde3461
96 muutettua tiedostoa jossa 9466 lisäystä ja 0 poistoa
  1. 310 0
      mvnw
  2. 182 0
      mvnw.cmd
  3. 187 0
      pom.xml
  4. BIN
      src/.DS_Store
  5. 7 0
      src/.idea/misc.xml
  6. 8 0
      src/.idea/modules.xml
  7. 9 0
      src/.idea/src.iml
  8. 6 0
      src/.idea/vcs.xml
  9. 347 0
      src/.idea/workspace.xml
  10. BIN
      src/main/.DS_Store
  11. BIN
      src/main/java/.DS_Store
  12. BIN
      src/main/java/com/.DS_Store
  13. BIN
      src/main/java/com/shs/.DS_Store
  14. BIN
      src/main/java/com/shs/official/.DS_Store
  15. 14 0
      src/main/java/com/shs/official/LiveStatisticApplication.java
  16. BIN
      src/main/java/com/shs/official/common/.DS_Store
  17. 18 0
      src/main/java/com/shs/official/common/ApplcationConfig.java
  18. 19 0
      src/main/java/com/shs/official/common/FilesConfig.java
  19. 393 0
      src/main/java/com/shs/official/common/LiveConfig.java
  20. 26 0
      src/main/java/com/shs/official/common/MyBatisPlusPlugin.java
  21. 43 0
      src/main/java/com/shs/official/common/RedisConfig.java
  22. 47 0
      src/main/java/com/shs/official/common/ResourcesConfig.java
  23. 25 0
      src/main/java/com/shs/official/common/SpringApplication.java
  24. 37 0
      src/main/java/com/shs/official/common/ThreadConfig.java
  25. 100 0
      src/main/java/com/shs/official/common/constant/Constants.java
  26. 18 0
      src/main/java/com/shs/official/common/em/USER_STATUS.java
  27. 53 0
      src/main/java/com/shs/official/common/exception/BaseException.java
  28. 24 0
      src/main/java/com/shs/official/common/exception/GlobalErrorAttribute.java
  29. 24 0
      src/main/java/com/shs/official/common/exception/GlobalExceptionHandler.java
  30. 26 0
      src/main/java/com/shs/official/common/exception/UtilException.java
  31. 20 0
      src/main/java/com/shs/official/common/exception/file/FileException.java
  32. 19 0
      src/main/java/com/shs/official/common/exception/file/FileNameLengthLimitExceededException.java
  33. 19 0
      src/main/java/com/shs/official/common/exception/file/FileSizeLimitExceededException.java
  34. 72 0
      src/main/java/com/shs/official/common/exception/file/InvalidExtensionException.java
  35. 34 0
      src/main/java/com/shs/official/common/exception/job/TaskException.java
  36. 19 0
      src/main/java/com/shs/official/common/exception/user/CaptchaException.java
  37. 18 0
      src/main/java/com/shs/official/common/exception/user/CaptchaExpireException.java
  38. 18 0
      src/main/java/com/shs/official/common/exception/user/UserException.java
  39. 19 0
      src/main/java/com/shs/official/common/exception/user/UserPasswordNotMatchException.java
  40. 33 0
      src/main/java/com/shs/official/common/factory/YamlConfigFactory.java
  41. 31 0
      src/main/java/com/shs/official/common/filter/CorsFilter.java
  42. 48 0
      src/main/java/com/shs/official/common/filter/RepeatableFilter.java
  43. 73 0
      src/main/java/com/shs/official/common/filter/RepeatedlyRequestWrapper.java
  44. 93 0
      src/main/java/com/shs/official/common/filter/XssFilter.java
  45. 106 0
      src/main/java/com/shs/official/common/filter/XssHttpServletRequestWrapper.java
  46. 80 0
      src/main/java/com/shs/official/common/http/HttpPoolConfig.java
  47. 108 0
      src/main/java/com/shs/official/common/http/HttpRequest.java
  48. 64 0
      src/main/java/com/shs/official/common/http/RestTemplateConfig.java
  49. 63 0
      src/main/java/com/shs/official/common/interceptor/LoginInterceptor.java
  50. 56 0
      src/main/java/com/shs/official/common/interceptor/RepeatSubmitInterceptor.java
  51. 18 0
      src/main/java/com/shs/official/common/interceptor/annotation/RepeatSubmit.java
  52. 31 0
      src/main/java/com/shs/official/common/mybatis/MyBatisPlusIdentifierGenerator.java
  53. 78 0
      src/main/java/com/shs/official/common/mybatis/MyBatisPlusMate.java
  54. 99 0
      src/main/java/com/shs/official/common/template/HttpStatus.java
  55. 161 0
      src/main/java/com/shs/official/common/template/ResultTemplate.java
  56. 156 0
      src/main/java/com/shs/official/common/utils/DateUtils.java
  57. 91 0
      src/main/java/com/shs/official/common/utils/IdGen.java
  58. 25 0
      src/main/java/com/shs/official/common/utils/MessageUtils.java
  59. 137 0
      src/main/java/com/shs/official/common/utils/ServletUtils.java
  60. 114 0
      src/main/java/com/shs/official/common/utils/SpringUtils.java
  61. 449 0
      src/main/java/com/shs/official/common/utils/StringUtils.java
  62. 250 0
      src/main/java/com/shs/official/common/utils/ffmpeg/FfmpegUtils.java
  63. 55 0
      src/main/java/com/shs/official/common/utils/ffmpeg/ProcessListNow.java
  64. 274 0
      src/main/java/com/shs/official/common/utils/file/FileUploadUtils.java
  65. 137 0
      src/main/java/com/shs/official/common/utils/file/FileUtils.java
  66. 39 0
      src/main/java/com/shs/official/common/utils/file/MD5Checksum.java
  67. 56 0
      src/main/java/com/shs/official/common/utils/file/MimeTypeUtils.java
  68. 152 0
      src/main/java/com/shs/official/common/utils/html/EscapeUtil.java
  69. 565 0
      src/main/java/com/shs/official/common/utils/html/HTMLFilter.java
  70. 100 0
      src/main/java/com/shs/official/common/utils/http/HttpHelper.java
  71. 537 0
      src/main/java/com/shs/official/common/utils/redis/RedisUtils.java
  72. 67 0
      src/main/java/com/shs/official/common/utils/security/Md5Utils.java
  73. 24 0
      src/main/java/com/shs/official/common/utils/session/SessionUtils.java
  74. 87 0
      src/main/java/com/shs/official/common/utils/str/CharsetKit.java
  75. 1000 0
      src/main/java/com/shs/official/common/utils/str/Convert.java
  76. 92 0
      src/main/java/com/shs/official/common/utils/str/StrFormatter.java
  77. 51 0
      src/main/java/com/shs/official/common/utils/token/TokenEncryptUtils.java
  78. 81 0
      src/main/java/com/shs/official/controller/BaseController.java
  79. 34 0
      src/main/java/com/shs/official/controller/common/CommonController.java
  80. 108 0
      src/main/java/com/shs/official/controller/media/MediaController.java
  81. 68 0
      src/main/java/com/shs/official/controller/user/UserController.java
  82. 74 0
      src/main/java/com/shs/official/entity/BaseEntity.java
  83. 145 0
      src/main/java/com/shs/official/entity/media/TbMedia.java
  84. 94 0
      src/main/java/com/shs/official/entity/user/TbUser.java
  85. 49 0
      src/main/java/com/shs/official/entity/vo/MediaVo.java
  86. 29 0
      src/main/java/com/shs/official/mapper/media/MediaMapper.java
  87. 9 0
      src/main/java/com/shs/official/mapper/user/UserMapper.java
  88. 93 0
      src/main/java/com/shs/official/service/BaseService.java
  89. 56 0
      src/main/java/com/shs/official/service/common/CommonService.java
  90. 350 0
      src/main/java/com/shs/official/service/media/MediaService.java
  91. 76 0
      src/main/java/com/shs/official/service/task/ConverTaskService.java
  92. 101 0
      src/main/java/com/shs/official/service/user/UserService.java
  93. 131 0
      src/main/resources/application-dev.yml
  94. 129 0
      src/main/resources/application-prod.yml
  95. 12 0
      src/main/resources/application.yml
  96. 66 0
      src/main/resources/mybatis/mapper/MediaMapper.xml

+ 310 - 0
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 copyrishst 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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven 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.6/maven-wrapper-0.5.6.jar"
+    else
+      jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.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
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 copyrishst 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 Maven 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 keystroke 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.6/maven-wrapper-0.5.6.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.6/maven-wrapper-0.5.6.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%

+ 187 - 0
pom.xml

@@ -0,0 +1,187 @@
+<?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>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.2.6.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.shs.official</groupId>
+    <artifactId>live</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>live</name>
+    <description>Live Project</description>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <mysql.connector.version>8.0.15</mysql.connector.version>
+        <mybatis.plus.version>3.3.1</mybatis.plus.version>
+        <druid.version>1.1.10</druid.version>
+        <fastjson.version>1.2.60</fastjson.version>
+        <kaptcha.version>1.0.0</kaptcha.version>
+        <commons.io.version>2.5</commons.io.version>
+        <commons.fileupload.version>1.3.3</commons.fileupload.version>
+        <bitwalker.version>1.19</bitwalker.version>
+        <poi.version>3.17</poi.version>
+        <commoms.lang3.version>3.10</commoms.lang3.version>
+        <commons.lang.version>2.4</commons.lang.version>
+        <ffmpeg.version>0.6.2</ffmpeg.version>
+        <httpcomponents.version>4.5.13</httpcomponents.version>
+    </properties>
+
+    <dependencies>
+        <!-- SpringBoot 核心包 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <scope>runtime</scope>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <!--redis工具-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>${mysql.connector.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>${mybatis.plus.version}</version>
+        </dependency>
+
+        <!--http请求工具-->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>${httpcomponents.version}</version>
+        </dependency>
+
+        <!--mybatis plus extension,包含了mybatis plus core-->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-extension</artifactId>
+            <version>${mybatis.plus.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+            <version>${druid.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>${fastjson.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>kaptcha-spring-boot-starter</artifactId>
+            <version>${kaptcha.version}</version>
+        </dependency>
+
+        <!-- pool 对象池 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+
+        <!--常用工具类 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version> ${commoms.lang3.version}</version>
+        </dependency>
+
+        <!--io常用工具类 -->
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>${commons.io.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>${commons.lang.version}</version>
+        </dependency>
+
+
+        <!--文件上传工具类 -->
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>${commons.fileupload.version}</version>
+        </dependency>
+
+        <!-- excel工具 -->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+
+        <!-- ffmpeg -->
+        <dependency>
+            <groupId>net.bramp.ffmpeg</groupId>
+            <artifactId>ffmpeg</artifactId>
+            <version>${ffmpeg.version}</version>
+        </dependency>
+
+        <!-- 解析客户端操作系统、浏览器等 -->
+        <dependency>
+            <groupId>eu.bitwalker</groupId>
+            <artifactId>UserAgentUtils</artifactId>
+            <version>${bitwalker.version}</version>
+        </dependency>
+
+        <!--<dependency>-->
+            <!--<groupId>org.springframework.boot</groupId>-->
+            <!--<artifactId>spring-boot-starter-test</artifactId>-->
+            <!--<scope>test</scope>-->
+            <!--<exclusions>-->
+                <!--<exclusion>-->
+                    <!--<groupId>org.junit.vintage</groupId>-->
+                    <!--<artifactId>junit-vintage-engine</artifactId>-->
+                <!--</exclusion>-->
+            <!--</exclusions>-->
+        <!--</dependency>-->
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

BIN
src/.DS_Store


+ 7 - 0
src/.idea/misc.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_9" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
+</project>

+ 8 - 0
src/.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/src.iml" filepath="$PROJECT_DIR$/.idea/src.iml" />
+    </modules>
+  </component>
+</project>

+ 9 - 0
src/.idea/src.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 6 - 0
src/.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
+  </component>
+</project>

+ 347 - 0
src/.idea/workspace.xml

@@ -0,0 +1,347 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ChangeListManager">
+    <list default="true" id="2fc3dcf6-f108-4669-8b29-c06c8bdd2605" name="Default" comment="">
+      <change afterPath="$PROJECT_DIR$/main/java/com/shs/official/entity/vo/MediaVo.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/main/java/com/shs/official/common/ResourcesConfig.java" beforeDir="false" afterPath="$PROJECT_DIR$/main/java/com/shs/official/common/ResourcesConfig.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/main/java/com/shs/official/common/utils/ffmpeg/FfmpegUtils.java" beforeDir="false" afterPath="$PROJECT_DIR$/main/java/com/shs/official/common/utils/ffmpeg/FfmpegUtils.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/main/java/com/shs/official/controller/media/MediaController.java" beforeDir="false" afterPath="$PROJECT_DIR$/main/java/com/shs/official/controller/media/MediaController.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/main/java/com/shs/official/entity/media/TbMedia.java" beforeDir="false" afterPath="$PROJECT_DIR$/main/java/com/shs/official/entity/media/TbMedia.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/main/java/com/shs/official/mapper/media/MediaMapper.java" beforeDir="false" afterPath="$PROJECT_DIR$/main/java/com/shs/official/mapper/media/MediaMapper.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/main/java/com/shs/official/service/media/MediaService.java" beforeDir="false" afterPath="$PROJECT_DIR$/main/java/com/shs/official/service/media/MediaService.java" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/main/resources/application-dev.yml" beforeDir="false" afterPath="$PROJECT_DIR$/main/resources/application-dev.yml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/main/resources/application-prod.yml" beforeDir="false" afterPath="$PROJECT_DIR$/main/resources/application-prod.yml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/main/resources/mybatis/mapper/MediaMapper.xml" beforeDir="false" afterPath="$PROJECT_DIR$/main/resources/mybatis/mapper/MediaMapper.xml" afterDir="false" />
+    </list>
+    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
+    <option name="TRACKING_ENABLED" value="true" />
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="DatabaseView">
+    <option name="SHOW_INTERMEDIATE" value="true" />
+    <option name="GROUP_DATA_SOURCES" value="true" />
+    <option name="GROUP_SCHEMA" value="true" />
+    <option name="GROUP_CONTENTS" value="false" />
+    <option name="SORT_POSITIONED" value="false" />
+    <option name="SHOW_EMPTY_GROUPS" value="false" />
+    <option name="AUTO_SCROLL_FROM_SOURCE" value="false" />
+    <option name="HIDDEN_KINDS">
+      <set />
+    </option>
+    <expand />
+    <select />
+  </component>
+  <component name="FileEditorManager">
+    <leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
+      <file leaf-file-name="LiveStatisticApplication.java" pinned="false" current-in-tab="true">
+        <entry file="file://$PROJECT_DIR$/main/java/com/shs/official/LiveStatisticApplication.java">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="150">
+              <caret line="14" lean-forward="true" selection-start-line="14" selection-end-line="14" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+    </leaf>
+  </component>
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
+  </component>
+  <component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
+  <component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" />
+  <component name="JsGulpfileManager">
+    <detection-done>true</detection-done>
+    <sorting>DEFINITION_ORDER</sorting>
+  </component>
+  <component name="NodePackageJsonFileManager">
+    <packageJsonPaths />
+  </component>
+  <component name="ProjectFrameBounds">
+    <option name="y" value="23" />
+    <option name="width" value="1440" />
+    <option name="height" value="877" />
+  </component>
+  <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
+  <component name="ProjectView">
+    <navigator proportions="" version="1">
+      <foldersAlwaysOnTop value="true" />
+    </navigator>
+    <panes>
+      <pane id="PackagesPane" />
+      <pane id="Scope" />
+      <pane id="AndroidView" />
+      <pane id="ProjectPane">
+        <subPane>
+          <expand>
+            <path>
+              <item name="src" type="b2602c69:ProjectViewProjectNode" />
+              <item name="src" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="src" type="b2602c69:ProjectViewProjectNode" />
+              <item name="src" type="462c0819:PsiDirectoryNode" />
+              <item name="main" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="src" type="b2602c69:ProjectViewProjectNode" />
+              <item name="src" type="462c0819:PsiDirectoryNode" />
+              <item name="main" type="462c0819:PsiDirectoryNode" />
+              <item name="java" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="src" type="b2602c69:ProjectViewProjectNode" />
+              <item name="src" type="462c0819:PsiDirectoryNode" />
+              <item name="main" type="462c0819:PsiDirectoryNode" />
+              <item name="java" type="462c0819:PsiDirectoryNode" />
+              <item name="com" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="src" type="b2602c69:ProjectViewProjectNode" />
+              <item name="src" type="462c0819:PsiDirectoryNode" />
+              <item name="main" type="462c0819:PsiDirectoryNode" />
+              <item name="java" type="462c0819:PsiDirectoryNode" />
+              <item name="com" type="462c0819:PsiDirectoryNode" />
+              <item name="shs" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="src" type="b2602c69:ProjectViewProjectNode" />
+              <item name="src" type="462c0819:PsiDirectoryNode" />
+              <item name="main" type="462c0819:PsiDirectoryNode" />
+              <item name="java" type="462c0819:PsiDirectoryNode" />
+              <item name="com" type="462c0819:PsiDirectoryNode" />
+              <item name="shs" type="462c0819:PsiDirectoryNode" />
+              <item name="official" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="src" type="b2602c69:ProjectViewProjectNode" />
+              <item name="src" type="462c0819:PsiDirectoryNode" />
+              <item name="main" type="462c0819:PsiDirectoryNode" />
+              <item name="resources" type="462c0819:PsiDirectoryNode" />
+            </path>
+          </expand>
+          <select />
+        </subPane>
+      </pane>
+    </panes>
+  </component>
+  <component name="PropertiesComponent">
+    <property name="WebServerToolWindowFactoryState" value="false" />
+    <property name="aspect.path.notification.shown" value="true" />
+    <property name="last_opened_file_path" value="$PROJECT_DIR$" />
+    <property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
+    <property name="nodejs_npm_path_reset_for_default_project" value="true" />
+    <property name="project.structure.last.edited" value="Project" />
+    <property name="project.structure.proportion" value="0.15" />
+    <property name="project.structure.side.proportion" value="0.2" />
+    <property name="settings.editor.selected.configurable" value="project.propVCSSupport.Mappings" />
+    <property name="show.migrate.to.gradle.popup" value="false" />
+  </component>
+  <component name="RunDashboard">
+    <option name="ruleStates">
+      <list>
+        <RuleState>
+          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
+        </RuleState>
+        <RuleState>
+          <option name="name" value="StatusDashboardGroupingRule" />
+        </RuleState>
+      </list>
+    </option>
+  </component>
+  <component name="RunManager">
+    <configuration default="true" type="Application" factoryName="Application">
+      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    </configuration>
+    <configuration default="true" type="JUnit" factoryName="JUnit">
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" />
+      <option name="PACKAGE_NAME" />
+      <option name="MAIN_CLASS_NAME" />
+      <option name="METHOD_NAME" />
+      <option name="TEST_OBJECT" value="class" />
+      <option name="VM_PARAMETERS" value="-ea" />
+      <option name="PARAMETERS" />
+      <option name="WORKING_DIRECTORY" value="%MODULE_WORKING_DIR%" />
+      <option name="PASS_PARENT_ENVS" value="true" />
+      <option name="TEST_SEARCH_SCOPE">
+        <value defaultName="singleModule" />
+      </option>
+      <patterns />
+    </configuration>
+    <configuration default="true" type="TestNG" factoryName="TestNG">
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" />
+      <option name="SUITE_NAME" />
+      <option name="PACKAGE_NAME" />
+      <option name="MAIN_CLASS_NAME" />
+      <option name="METHOD_NAME" />
+      <option name="GROUP_NAME" />
+      <option name="TEST_OBJECT" value="CLASS" />
+      <option name="VM_PARAMETERS" value="-ea" />
+      <option name="PARAMETERS" />
+      <option name="WORKING_DIRECTORY" value="%MODULE_WORKING_DIR%" />
+      <option name="OUTPUT_DIRECTORY" />
+      <option name="PASS_PARENT_ENVS" value="true" />
+      <option name="TEST_SEARCH_SCOPE">
+        <value defaultName="singleModule" />
+      </option>
+      <option name="USE_DEFAULT_REPORTERS" value="false" />
+      <option name="PROPERTIES_FILE" />
+      <properties />
+      <listeners />
+    </configuration>
+  </component>
+  <component name="SvnConfiguration">
+    <configuration />
+  </component>
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="2fc3dcf6-f108-4669-8b29-c06c8bdd2605" name="Default" comment="" />
+      <created>1641891238065</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1641891238065</updated>
+      <workItem from="1641891239419" duration="97000" />
+      <workItem from="1641891343680" duration="32000" />
+    </task>
+    <servers />
+  </component>
+  <component name="TimeTrackingManager">
+    <option name="totallyTimeSpent" value="129000" />
+  </component>
+  <component name="ToolWindowManager">
+    <frame x="0" y="23" width="1440" height="877" extended-state="6" />
+    <layout>
+      <window_info anchor="right" id="Palette" order="3" />
+      <window_info anchor="bottom" id="TODO" order="6" />
+      <window_info anchor="right" id="Palette&#9;" order="3" />
+      <window_info id="Image Layers" order="2" />
+      <window_info anchor="right" id="Capture Analysis" order="3" />
+      <window_info anchor="bottom" id="Event Log" order="7" side_tool="true" />
+      <window_info anchor="bottom" id="Flutter Performance" order="7" side_tool="true" />
+      <window_info anchor="right" id="Maven Projects" order="3" />
+      <window_info anchor="bottom" id="Database Changes" order="7" show_stripe_button="false" />
+      <window_info anchor="bottom" id="Run" order="2" />
+      <window_info anchor="bottom" id="Version Control" order="7" />
+      <window_info anchor="bottom" id="Terminal" order="7" />
+      <window_info anchor="right" id="Flutter Outline" order="3" />
+      <window_info id="Capture Tool" order="2" />
+      <window_info id="Designer" order="2" />
+      <window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.2532189" />
+      <window_info anchor="right" id="Database" order="3" weight="0.3297568" />
+      <window_info id="Structure" order="1" side_tool="true" weight="0.25" />
+      <window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
+      <window_info id="UI Designer" order="2" />
+      <window_info anchor="right" id="Theme Preview" order="3" />
+      <window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
+      <window_info id="Favorites" order="2" side_tool="true" />
+      <window_info anchor="right" id="Flutter Inspector" order="3" />
+      <window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
+      <window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
+      <window_info anchor="bottom" id="Message" order="0" />
+      <window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
+      <window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
+      <window_info anchor="bottom" id="Find" order="1" />
+    </layout>
+  </component>
+  <component name="TypeScriptGeneratedFilesManager">
+    <option name="version" value="1" />
+  </component>
+  <component name="VcsContentAnnotationSettings">
+    <option name="myLimit" value="2678400000" />
+  </component>
+  <component name="editorHistoryManager">
+    <entry file="file://$PROJECT_DIR$/main/java/com/shs/official/LiveStatisticApplication.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="150">
+          <caret line="14" selection-start-line="14" selection-end-line="14" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/main/java/com/shs/official/LiveStatisticApplication.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="150">
+          <caret line="14" lean-forward="true" selection-start-line="14" selection-end-line="14" />
+        </state>
+      </provider>
+    </entry>
+  </component>
+  <component name="masterDetails">
+    <states>
+      <state key="ArtifactsStructureConfigurable.UI">
+        <settings>
+          <artifact-editor />
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+      <state key="FacetStructureConfigurable.UI">
+        <settings>
+          <last-edited>No facets are configured</last-edited>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+      <state key="GlobalLibrariesConfigurable.UI">
+        <settings>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+      <state key="JdkListConfigurable.UI">
+        <settings>
+          <last-edited>1.8</last-edited>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+      <state key="ModuleStructureConfigurable.UI">
+        <settings>
+          <last-edited>src</last-edited>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+                <option value="0.6" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+      <state key="ProjectLibrariesConfigurable.UI">
+        <settings>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+    </states>
+  </component>
+</project>

BIN
src/main/.DS_Store


BIN
src/main/java/.DS_Store


BIN
src/main/java/com/.DS_Store


BIN
src/main/java/com/shs/.DS_Store


BIN
src/main/java/com/shs/official/.DS_Store


+ 14 - 0
src/main/java/com/shs/official/LiveStatisticApplication.java

@@ -0,0 +1,14 @@
+package com.shs.official;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+@MapperScan("com.shs.official.mapper")
+public class LiveStatisticApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(LiveStatisticApplication.class, args);
+    }
+}

BIN
src/main/java/com/shs/official/common/.DS_Store


+ 18 - 0
src/main/java/com/shs/official/common/ApplcationConfig.java

@@ -0,0 +1,18 @@
+package com.shs.official.common;
+
+
+import com.shs.official.common.factory.YamlConfigFactory;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+
+/*
+* Read Config
+* @auth shs
+* */
+@Configuration
+@Component
+@PropertySource(value = {"classpath:application.yml"}, factory = YamlConfigFactory.class)
+public class ApplcationConfig {
+}

+ 19 - 0
src/main/java/com/shs/official/common/FilesConfig.java

@@ -0,0 +1,19 @@
+package com.shs.official.common;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@Configuration
+public class FilesConfig extends WebMvcConfigurerAdapter {
+
+    @Autowired
+    private LiveConfig liveConfig;
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("classpath:"+liveConfig.getProfile()+"/");
+        super.addResourceHandlers(registry);
+    }
+}

+ 393 - 0
src/main/java/com/shs/official/common/LiveConfig.java

@@ -0,0 +1,393 @@
+package com.shs.official.common;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 读取项目相关配置
+ *
+ * @author shs
+ */
+@Component
+@ConfigurationProperties(prefix = "live")
+public class LiveConfig {
+    /**
+     * 项目名称
+     */
+    private String name;
+
+    /**
+     * 版本
+     */
+    private String version;
+
+    /**
+     * 服务地址
+     */
+    private String baseUrl;
+
+    /**
+     * 上传路径
+     */
+    private static String profile;
+
+    /**
+     * 是否删除原始文件
+     */
+    private static boolean sourceDelete;
+
+    /**
+     * 原始文件路径
+     */
+    private static String sourceProfile;
+
+    /**
+     * 视频编码格式
+     */
+    private static String videoCodec;
+
+    /**
+     * 视频针率
+     */
+    private static Double frameRate;
+
+    /**
+     * ffmpeg路径
+     */
+    private static String ffmpegPath;
+
+    /**
+     * ffmprobe路径
+     */
+    private static String ffprobePath;
+
+    /**
+     * 视频码率
+     */
+    private static Integer videoRate;
+
+    /**
+     * 音频编码格式
+     */
+    private static String audioCodec;
+
+    /**
+     * 音频通道
+     */
+    private static Integer audioChannels;
+
+    /**
+     * 音频码率
+     */
+    private static Integer audioRate;
+
+    /**
+     * 采样率
+     */
+    private static Integer sampling;
+
+    /**
+     * 等比缩放
+     */
+    private static boolean zoom;
+
+    /**
+     * 缩放尺寸
+     */
+    private static Integer zoomSize;
+
+    /*
+    *强制调整分辨率
+    */
+    private static boolean relove;
+
+    /*
+    * 调整分辨率
+    */
+    private static String reloveSize;
+
+    /*
+     * 视频切片时间
+     */
+    private static String segmentTime;
+
+    /*
+    * 视频封面切割尺寸
+    */
+    private static String coverSize;
+
+    /*
+     * 切割针率
+     */
+    private static String coverT;
+
+    /*
+     * 视频格式
+     */
+    private static String format;
+
+    /*
+     * 切片时间
+     */
+    private static String hlsTime;
+
+    /*
+     * 切片开始编号
+     */
+    private static String hlsStartNumber;
+
+    /*
+     * 多少片之后开始覆盖
+     */
+    private static String hlsWrap;
+
+    /*
+     * 总共切片数量
+     */
+    private static String hlsListSize;
+
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getBaseUrl() {
+        return baseUrl;
+    }
+
+    public void setBaseUrl(String baseUrl) {
+        this.baseUrl = baseUrl;
+    }
+
+    public static String getProfile() {
+        return profile;
+    }
+
+    public void setProfile(String profile) {
+        LiveConfig.profile = profile;
+    }
+
+    public static boolean isSourceDelete() {
+        return sourceDelete;
+    }
+
+    public void setSourceDelete(boolean sourceDelete) {
+        LiveConfig.sourceDelete = sourceDelete;
+    }
+
+    public static String getSourceProfile() {
+        return sourceProfile;
+    }
+
+    public void setSourceProfile(String sourceProfile) {
+        LiveConfig.sourceProfile = sourceProfile;
+    }
+
+    public static String getVideoCodec() {
+        return videoCodec;
+    }
+
+    public void setVideoCodec(String videoCodec) {
+        LiveConfig.videoCodec = videoCodec;
+    }
+
+    public static Double getFrameRate() {
+        return frameRate;
+    }
+
+    public void setFrameRate(Double frameRate) {
+        LiveConfig.frameRate = frameRate;
+    }
+
+    public static String getFfmpegPath() {
+        return ffmpegPath;
+    }
+
+    public void setFfmpegPath(String ffmpegPath) {
+        LiveConfig.ffmpegPath = ffmpegPath;
+    }
+
+    public static String getFfprobePath() {
+        return ffprobePath;
+    }
+
+    public void setFfprobePath(String ffprobePath) {
+        LiveConfig.ffprobePath = ffprobePath;
+    }
+
+    public static Integer getVideoRate() {
+        return videoRate;
+    }
+
+    public void setVideoRate(Integer videoRate) {
+        LiveConfig.videoRate = videoRate;
+    }
+
+    public static String getAudioCodec() {
+        return audioCodec;
+    }
+
+    public void setAudioCodec(String audioCodec) {
+        LiveConfig.audioCodec = audioCodec;
+    }
+
+    public static Integer getAudioChannels() {
+        return audioChannels;
+    }
+
+    public void setAudioChannels(Integer audioChannels) {
+        LiveConfig.audioChannels = audioChannels;
+    }
+
+    public static Integer getAudioRate() {
+        return audioRate;
+    }
+
+    public void setAudioRate(Integer audioRate) {
+        LiveConfig.audioRate = audioRate;
+    }
+
+    public static Integer getSampling() {
+        return sampling;
+    }
+
+    public void setSampling(Integer sampling) {
+        LiveConfig.sampling = sampling;
+    }
+
+    public static boolean isZoom() {
+        return zoom;
+    }
+
+    public void setZoom(boolean zoom) {
+        LiveConfig.zoom = zoom;
+    }
+
+    public static Integer getZoomSize() {
+        return zoomSize;
+    }
+
+    public void setZoomSize(Integer zoomSize) {
+        LiveConfig.zoomSize = zoomSize;
+    }
+
+    public static boolean isRelove() {
+        return relove;
+    }
+
+    public void setRelove(boolean relove) {
+        LiveConfig.relove = relove;
+    }
+
+    public static String getReloveSize() {
+        return reloveSize;
+    }
+
+    public void setReloveSize(String reloveSize) {
+        LiveConfig.reloveSize = reloveSize;
+    }
+
+    public static String getSegmentTime() {
+        return segmentTime;
+    }
+
+    public void setSegmentTime(String segmentTime) {
+        LiveConfig.segmentTime = segmentTime;
+    }
+
+    public static String getCoverSize() {
+        return coverSize;
+    }
+
+    public void setCoverSize(String coverSize) {
+        LiveConfig.coverSize = coverSize;
+    }
+
+    public static String getCoverT() {
+        return coverT;
+    }
+
+    public void setCoverT(String coverT) {
+        LiveConfig.coverT = coverT;
+    }
+
+    public static String getFormat() {
+        return format;
+    }
+
+    public void setFormat(String format) {
+        LiveConfig.format = format;
+    }
+
+    public static String getHlsTime() {
+        return hlsTime;
+    }
+
+    public void setHlsTime(String hlsTime) {
+        LiveConfig.hlsTime = hlsTime;
+    }
+
+    public static String getHlsStartNumber() {
+        return hlsStartNumber;
+    }
+
+    public void setHlsStartNumber(String hlsStartNumber) {
+        LiveConfig.hlsStartNumber = hlsStartNumber;
+    }
+
+    public static String getHlsWrap() {
+        return hlsWrap;
+    }
+
+    public void setHlsWrap(String hlsWrap) {
+        LiveConfig.hlsWrap = hlsWrap;
+    }
+
+    public static String getHlsListSize() {
+        return hlsListSize;
+    }
+
+    public void setHlsListSize(String hlsListSize) {
+        LiveConfig.hlsListSize = hlsListSize;
+    }
+
+    /**
+
+     * video cover
+     */
+    public static String getCoverPath() {
+        return "/cover";
+    }
+
+    /**
+     * video ts
+     */
+    public static String getTSPath() {
+        return "/ts";
+    }
+
+    /**video m3u8
+     */
+    public static String getM3u8Path() {
+        return "/m3u8";
+    }
+
+    /**
+     * music
+     */
+    public static String getMusicPath() {
+        return "/music";
+    }
+}

+ 26 - 0
src/main/java/com/shs/official/common/MyBatisPlusPlugin.java

@@ -0,0 +1,26 @@
+package com.shs.official.common;
+
+import com.baomidou.mybatisplus.core.config.GlobalConfig;
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import com.shs.official.common.mybatis.MyBatisPlusIdentifierGenerator;
+import com.shs.official.common.mybatis.MyBatisPlusMate;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MyBatisPlusPlugin {
+
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        return new PaginationInterceptor();
+    }
+
+    @Bean
+    public GlobalConfig globalConfig() {
+        GlobalConfig conf = new GlobalConfig();
+        conf.setMetaObjectHandler(new MyBatisPlusMate());
+        conf.setIdentifierGenerator(new MyBatisPlusIdentifierGenerator());
+        return conf;
+    }
+
+}

+ 43 - 0
src/main/java/com/shs/official/common/RedisConfig.java

@@ -0,0 +1,43 @@
+package com.shs.official.common;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import org.springframework.stereotype.Component;
+
+@Configuration
+public class RedisConfig {
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    @Bean
+    @SuppressWarnings("all")
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
+        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
+        template.setConnectionFactory(factory);
+        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+        ObjectMapper om = new ObjectMapper();
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        jackson2JsonRedisSerializer.setObjectMapper(om);
+        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
+        // key采用String的序列化方式
+        template.setKeySerializer(stringRedisSerializer);
+        // hash的key也采用String的序列化方式
+        template.setHashKeySerializer(stringRedisSerializer);
+        // value序列化方式采用jackson
+        template.setValueSerializer(jackson2JsonRedisSerializer);
+        // hash的value序列化方式采用jackson
+        template.setHashValueSerializer(jackson2JsonRedisSerializer);
+        template.afterPropertiesSet();
+        return template;
+    }
+}

+ 47 - 0
src/main/java/com/shs/official/common/ResourcesConfig.java

@@ -0,0 +1,47 @@
+package com.shs.official.common;
+
+import com.shs.official.common.constant.Constants;
+import com.shs.official.common.interceptor.LoginInterceptor;
+import com.shs.official.common.interceptor.RepeatSubmitInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * 通用配置
+ * 
+ * @author shs
+ */
+@Configuration
+public class ResourcesConfig implements WebMvcConfigurer
+{
+//    @Autowired
+//    private RepeatSubmitInterceptor repeatSubmitInterceptor;
+    @Autowired
+    private LoginInterceptor loginInterceptor;
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry)
+    {
+        /** 本地文件上传路径 */
+//        registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + shsConfig.getProfile() + "/");
+
+        /** swagger配置 */
+//        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
+//        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
+    }
+
+    /**
+     * 自定义拦截规则
+     */
+    @Override
+    public void addInterceptors(InterceptorRegistry registry)
+    {
+//        registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
+//        registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("");
+        //"/competition/**","/province/**",
+        //                "/project/**","/qr/**","/projectShow/**","/user/login","/user/loginOut","/common/upload"
+    }
+}

+ 25 - 0
src/main/java/com/shs/official/common/SpringApplication.java

@@ -0,0 +1,25 @@
+package com.shs.official.common;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SpringApplication implements ApplicationContextAware {
+
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringApplication.applicationContext = applicationContext;
+    }
+
+    public static <T> T getBean(Class<T> clazz) {
+        return applicationContext.getBean(clazz);
+    }
+
+    public static String getActiveProfile() {
+        return applicationContext.getEnvironment().getActiveProfiles()[0];
+    }
+}

+ 37 - 0
src/main/java/com/shs/official/common/ThreadConfig.java

@@ -0,0 +1,37 @@
+package com.shs.official.common;
+
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+/*
+* Async Thread Configurer
+* AUTH SHS
+* CREATED 2021-05-08
+* */
+@Configuration
+@ComponentScan("com.shs.official.service.task")
+@EnableAsync
+public class ThreadConfig implements AsyncConfigurer {
+
+    @Override
+    public Executor getAsyncExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(10);
+        executor.setMaxPoolSize(20);
+        executor.setQueueCapacity(100);
+//        executor.setWaitForTasksToCompleteOnShutdown(true);//是否等待其他线程执行结束
+        executor.setAwaitTerminationSeconds(60 * 60);//如果一个小时执行未结束或中断  强制结束当前线程
+        executor.initialize();
+        return executor;
+    }
+    @Override
+    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+        return null;
+    }
+}

+ 100 - 0
src/main/java/com/shs/official/common/constant/Constants.java

@@ -0,0 +1,100 @@
+package com.shs.official.common.constant;
+
+/**
+ * 通用常量信息
+ *
+ * @author shs
+ */
+public class Constants
+{
+    /**
+     * UTF-8 字符集
+     */
+    public static final String UTF8 = "UTF-8";
+
+    /**
+     * 通用成功标识
+     */
+    public static final String SUCCESS = "0";
+
+    /**
+     * 通用失败标识
+     */
+    public static final String FAIL = "1";
+
+    /**
+     * 登录成功
+     */
+    public static final String LOGIN_SUCCESS = "Success";
+
+    /**
+     * 注销
+     */
+    public static final String LOGOUT = "Logout";
+
+    /**
+     * 登录失败
+     */
+    public static final String LOGIN_FAIL = "Error";
+
+    /**
+     * 验证码 redis key
+     */
+    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
+
+    /**
+     * 登录用户 redis key
+     */
+    public static final String LOGIN_TOKEN_KEY = "login_tokens:";
+
+    /**
+     * 验证码有效期(分钟)
+     */
+    public static final Integer CAPTCHA_EXPIRATION = 2;
+
+    /**
+     * 令牌
+     */
+    public static final String TOKEN = "token";
+
+    /**
+     * 令牌前缀
+     */
+    public static final String TOKEN_PREFIX = "Bearer ";
+
+    /**
+     * 令牌前缀
+     */
+    public static final String LOGIN_USER_KEY = "login_user_key";
+
+    /**
+     * 用户ID
+     */
+    public static final String JWT_USERID = "userid";
+
+    /**
+     * 用户名称
+     */
+//    public static final String JWT_USERNAME = Claims.SUBJECT;
+
+    /**
+     * 用户头像
+     */
+    public static final String JWT_AVATAR = "avatar";
+
+    /**
+     * 创建时间
+     */
+    public static final String JWT_CREATED = "created";
+
+    /**
+     * 用户权限
+     */
+    public static final String JWT_AUTHORITIES = "authorities";
+
+    /**
+     * 资源映射路径 前缀
+     */
+    public static final String RESOURCE_PREFIX = "/profile";
+}
+

+ 18 - 0
src/main/java/com/shs/official/common/em/USER_STATUS.java

@@ -0,0 +1,18 @@
+package com.shs.official.common.em;
+
+public enum USER_STATUS {
+
+    NORMAL(1,"正常"),DISABLE(0,"容许创建子赛事");
+
+    private Integer key;
+    private String remark;
+
+    USER_STATUS(Integer key, String remark){
+        this.key = key;
+        this.remark = remark;
+    }
+
+    public Integer getKey(){
+        return this.key;
+    }
+}

+ 53 - 0
src/main/java/com/shs/official/common/exception/BaseException.java

@@ -0,0 +1,53 @@
+package com.shs.official.common.exception;
+
+import com.shs.official.common.template.ResultTemplate;
+
+/**
+ * 基础异常
+ * 
+ * @author SHS
+ */
+public class BaseException extends RuntimeException {
+
+    private Integer code;
+    private String message;
+    private Object data;
+
+    public BaseException(int code, String message, Object data) {
+        this.code = code;
+        this.message = message;
+        this.data = data;
+    }
+
+    public BaseException(ResultTemplate resultTemplate) {
+        this.code = Integer.valueOf(resultTemplate.get("code").toString());
+        this.message = resultTemplate.get("msg").toString();
+        this.data = resultTemplate.get("data");
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public void setCode(Integer code) {
+        this.code = code;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public Object getData() {
+        return data;
+    }
+
+    public void setData(Object data) {
+        this.data = data;
+    }
+
+}

+ 24 - 0
src/main/java/com/shs/official/common/exception/GlobalErrorAttribute.java

@@ -0,0 +1,24 @@
+package com.shs.official.common.exception;
+
+import com.shs.official.common.template.HttpStatus;
+import com.shs.official.common.template.ResultTemplate;
+import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.WebRequest;
+
+import java.util.Map;
+
+/**
+ * 默认异常处理
+ *
+ * @author SHS
+ */
+@Component
+public class GlobalErrorAttribute extends DefaultErrorAttributes {
+
+    @Override
+    public ResultTemplate getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
+        Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
+        return ResultTemplate.error(Integer.parseInt(map.get("status").toString()),map.get("message").toString());
+    }
+}

+ 24 - 0
src/main/java/com/shs/official/common/exception/GlobalExceptionHandler.java

@@ -0,0 +1,24 @@
+package com.shs.official.common.exception;
+
+import com.shs.official.common.template.ResultTemplate;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.Map;
+
+/**
+ * 全局异常
+ *
+ * @author SHS
+ */
+
+@ControllerAdvice
+public class GlobalExceptionHandler {
+
+    @ResponseBody
+    @ExceptionHandler(BaseException.class)
+    public Map<String, Object> handleCustomException(BaseException baseException) {
+        return ResultTemplate.error(baseException.getCode(),baseException.getMessage(),baseException.getData());
+    }
+}

+ 26 - 0
src/main/java/com/shs/official/common/exception/UtilException.java

@@ -0,0 +1,26 @@
+package com.shs.official.common.exception;
+
+/**
+ * 工具类异常
+ * 
+ * @author shs
+ */
+public class UtilException extends RuntimeException
+{
+    private static final long serialVersionUID = 8247610319171014183L;
+
+    public UtilException(Throwable e)
+    {
+        super(e.getMessage(), e);
+    }
+
+    public UtilException(String message)
+    {
+        super(message);
+    }
+
+    public UtilException(String message, Throwable throwable)
+    {
+        super(message, throwable);
+    }
+}

+ 20 - 0
src/main/java/com/shs/official/common/exception/file/FileException.java

@@ -0,0 +1,20 @@
+package com.shs.official.common.exception.file;
+
+
+import com.shs.official.common.exception.BaseException;
+
+/**
+ * 文件信息异常类
+ * 
+ * @author SHS
+ */
+public class FileException extends BaseException
+{
+    private static final long serialVersionUID = 1L;
+
+    public FileException(Integer code, String msg)
+    {
+        super(code, msg, null);
+    }
+
+}

+ 19 - 0
src/main/java/com/shs/official/common/exception/file/FileNameLengthLimitExceededException.java

@@ -0,0 +1,19 @@
+package com.shs.official.common.exception.file;
+
+
+import com.shs.official.common.template.HttpStatus;
+
+/**
+ * 文件名称超长限制异常类
+ * 
+ * @author SHS
+ */
+public class FileNameLengthLimitExceededException extends FileException
+{
+    private static final long serialVersionUID = 1L;
+
+    public FileNameLengthLimitExceededException(int defaultFileNameLength)
+    {
+        super(HttpStatus.FILENAME_TOO_LONG_ERROR,"filename too long!");
+    }
+}

+ 19 - 0
src/main/java/com/shs/official/common/exception/file/FileSizeLimitExceededException.java

@@ -0,0 +1,19 @@
+package com.shs.official.common.exception.file;
+
+
+import com.shs.official.common.template.HttpStatus;
+
+/**
+ * 文件大小限制异常类
+ * 
+ * @author SHS
+ */
+public class FileSizeLimitExceededException extends FileException
+{
+    private static final long serialVersionUID = 1L;
+
+    public FileSizeLimitExceededException(long defaultMaxSize)
+    {
+        super(HttpStatus.FILE_TOO_LARGE_ERROR,"file too large!");
+    }
+}

+ 72 - 0
src/main/java/com/shs/official/common/exception/file/InvalidExtensionException.java

@@ -0,0 +1,72 @@
+package com.shs.official.common.exception.file;
+
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+
+import java.util.Arrays;
+
+/**
+ * 文件上传 异常类
+ * 
+ * @author SHS
+ */
+public class InvalidExtensionException extends FileUploadException
+{
+    private static final long serialVersionUID = 1L;
+
+    private String[] allowedExtension;
+    private String extension;
+    private String filename;
+
+    public InvalidExtensionException(String[] allowedExtension, String extension, String filename)
+    {
+        super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]");
+        this.allowedExtension = allowedExtension;
+        this.extension = extension;
+        this.filename = filename;
+    }
+
+    public String[] getAllowedExtension()
+    {
+        return allowedExtension;
+    }
+
+    public String getExtension()
+    {
+        return extension;
+    }
+
+    public String getFilename()
+    {
+        return filename;
+    }
+
+    public static class InvalidImageExtensionException extends InvalidExtensionException
+    {
+        private static final long serialVersionUID = 1L;
+
+        public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename)
+        {
+            super(allowedExtension, extension, filename);
+        }
+    }
+
+    public static class InvalidFlashExtensionException extends InvalidExtensionException
+    {
+        private static final long serialVersionUID = 1L;
+
+        public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename)
+        {
+            super(allowedExtension, extension, filename);
+        }
+    }
+
+    public static class InvalidMediaExtensionException extends InvalidExtensionException
+    {
+        private static final long serialVersionUID = 1L;
+
+        public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename)
+        {
+            super(allowedExtension, extension, filename);
+        }
+    }
+}

+ 34 - 0
src/main/java/com/shs/official/common/exception/job/TaskException.java

@@ -0,0 +1,34 @@
+package com.shs.official.common.exception.job;
+
+/**
+ * 计划策略异常
+ * 
+ * @author SHS
+ */
+public class TaskException extends Exception
+{
+    private static final long serialVersionUID = 1L;
+
+    private Code code;
+
+    public TaskException(String msg, Code code)
+    {
+        this(msg, code, null);
+    }
+
+    public TaskException(String msg, Code code, Exception nestedEx)
+    {
+        super(msg, nestedEx);
+        this.code = code;
+    }
+
+    public Code getCode()
+    {
+        return code;
+    }
+
+    public enum Code
+    {
+        TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
+    }
+}

+ 19 - 0
src/main/java/com/shs/official/common/exception/user/CaptchaException.java

@@ -0,0 +1,19 @@
+package com.shs.official.common.exception.user;
+
+
+import com.shs.official.common.template.HttpStatus;
+
+/**
+ * 验证码错误异常类
+ * 
+ * @author SHS
+ */
+public class CaptchaException extends UserException
+{
+    private static final long serialVersionUID = 1L;
+
+    public CaptchaException()
+    {
+        super(HttpStatus.VERIFICATION_CODE_ERROR,"verification code error!");
+    }
+}

+ 18 - 0
src/main/java/com/shs/official/common/exception/user/CaptchaExpireException.java

@@ -0,0 +1,18 @@
+package com.shs.official.common.exception.user;
+
+import com.shs.official.common.template.HttpStatus;
+
+/**
+ * 验证码失效异常类
+ * 
+ * @author SHS
+ */
+public class CaptchaExpireException extends UserException
+{
+    private static final long serialVersionUID = 1L;
+
+    public CaptchaExpireException()
+    {
+        super(HttpStatus.VERIFICATION_DOES_NOT_EXISTS,"verification does not exists!");
+    }
+}

+ 18 - 0
src/main/java/com/shs/official/common/exception/user/UserException.java

@@ -0,0 +1,18 @@
+package com.shs.official.common.exception.user;
+
+import com.shs.official.common.exception.BaseException;
+
+/**
+ * 用户信息异常类
+ * 
+ * @author SHS
+ */
+public class UserException extends BaseException
+{
+    private static final long serialVersionUID = 1L;
+
+    public UserException(Integer code,String msg)
+    {
+        super(code, msg, null);
+    }
+}

+ 19 - 0
src/main/java/com/shs/official/common/exception/user/UserPasswordNotMatchException.java

@@ -0,0 +1,19 @@
+package com.shs.official.common.exception.user;
+
+
+import com.shs.official.common.template.HttpStatus;
+
+/**
+ * 用户密码不正确或不符合规范异常类
+ * 
+ * @author SHS
+ */
+public class UserPasswordNotMatchException extends UserException
+{
+    private static final long serialVersionUID = 1L;
+
+    public UserPasswordNotMatchException()
+    {
+        super(HttpStatus.USER_PASSWORD_ERROR, "wrong user name or password!");
+    }
+}

+ 33 - 0
src/main/java/com/shs/official/common/factory/YamlConfigFactory.java

@@ -0,0 +1,33 @@
+package com.shs.official.common.factory;
+
+import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
+import org.springframework.core.env.PropertiesPropertySource;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.io.support.DefaultPropertySourceFactory;
+import org.springframework.core.io.support.EncodedResource;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class YamlConfigFactory extends DefaultPropertySourceFactory {
+
+    @Override
+    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
+        String sourceName = name != null ? name : resource.getResource().getFilename();
+        if (!resource.getResource().exists()) {
+            return new PropertiesPropertySource(sourceName, new Properties());
+        } else if (sourceName.endsWith(".yml") || sourceName.endsWith(".yaml")) {
+            Properties propertiesFromYaml = loadYml(resource);
+            return new PropertiesPropertySource(sourceName, propertiesFromYaml);
+        } else {
+            return super.createPropertySource(name, resource);
+        }
+    }
+
+    private Properties loadYml(EncodedResource resource) throws IOException {
+        YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
+        factory.setResources(resource.getResource());
+        factory.afterPropertiesSet();
+        return factory.getObject();
+    }
+}

+ 31 - 0
src/main/java/com/shs/official/common/filter/CorsFilter.java

@@ -0,0 +1,31 @@
+package com.shs.official.common.filter;
+
+import org.springframework.stereotype.Component;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Component
+public class CorsFilter implements Filter {
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        response.setHeader("Access-Control-Allow-Origin", "*");
+        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
+        response.setHeader("Access-Control-Max-Age", "3600");
+        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,x-token,x-key");
+        filterChain.doFilter(servletRequest, servletResponse);
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}

+ 48 - 0
src/main/java/com/shs/official/common/filter/RepeatableFilter.java

@@ -0,0 +1,48 @@
+package com.shs.official.common.filter;
+
+import com.shs.official.common.utils.StringUtils;
+import org.springframework.http.MediaType;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * Repeatable 过滤器
+ * 
+ * @author shs
+ */
+public class RepeatableFilter implements Filter
+{
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException
+    {
+
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+            throws IOException, ServletException
+    {
+        ServletRequest requestWrapper = null;
+        if (request instanceof HttpServletRequest && StringUtils.equalsAnyIgnoreCase(request.getContentType(),
+                MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE))
+        {
+            requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
+        }
+        if (null == requestWrapper)
+        {
+            chain.doFilter(request, response);
+        }
+        else
+        {
+            chain.doFilter(requestWrapper, response);
+        }
+    }
+
+    @Override
+    public void destroy()
+    {
+
+    }
+}

+ 73 - 0
src/main/java/com/shs/official/common/filter/RepeatedlyRequestWrapper.java

@@ -0,0 +1,73 @@
+package com.shs.official.common.filter;
+
+import com.shs.official.common.utils.http.HttpHelper;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * 构建可重复读取inputStream的request
+ * 
+ * @author shs
+ */
+public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
+{
+    private final byte[] body;
+
+    public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException
+    {
+        super(request);
+        request.setCharacterEncoding("UTF-8");
+        response.setCharacterEncoding("UTF-8");
+
+        body = HttpHelper.getBodyString(request).getBytes("UTF-8");
+    }
+
+    @Override
+    public BufferedReader getReader() throws IOException
+    {
+        return new BufferedReader(new InputStreamReader(getInputStream()));
+    }
+
+    @Override
+    public ServletInputStream getInputStream() throws IOException
+    {
+
+        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
+
+        return new ServletInputStream()
+        {
+
+            @Override
+            public int read() throws IOException
+            {
+                return bais.read();
+            }
+
+            @Override
+            public boolean isFinished()
+            {
+                return false;
+            }
+
+            @Override
+            public boolean isReady()
+            {
+                return false;
+            }
+
+            @Override
+            public void setReadListener(ReadListener readListener)
+            {
+
+            }
+        };
+    }
+}

+ 93 - 0
src/main/java/com/shs/official/common/filter/XssFilter.java

@@ -0,0 +1,93 @@
+package com.shs.official.common.filter;
+
+import com.shs.official.common.utils.StringUtils;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 防止XSS攻击的过滤器
+ * 
+ * @author shs
+ */
+public class XssFilter implements Filter
+{
+    /**
+     * 排除链接
+     */
+    public List<String> excludes = new ArrayList<>();
+
+    /**
+     * xss过滤开关
+     */
+    public boolean enabled = false;
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException
+    {
+        String tempExcludes = filterConfig.getInitParameter("excludes");
+        String tempEnabled = filterConfig.getInitParameter("enabled");
+        if (StringUtils.isNotEmpty(tempExcludes))
+        {
+            String[] url = tempExcludes.split(",");
+            for (int i = 0; url != null && i < url.length; i++)
+            {
+                excludes.add(url[i]);
+            }
+        }
+        if (StringUtils.isNotEmpty(tempEnabled))
+        {
+            enabled = Boolean.valueOf(tempEnabled);
+        }
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+            throws IOException, ServletException
+    {
+        HttpServletRequest req = (HttpServletRequest) request;
+        HttpServletResponse resp = (HttpServletResponse) response;
+        if (handleExcludeURL(req, resp))
+        {
+            chain.doFilter(request, response);
+            return;
+        }
+        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
+        chain.doFilter(xssRequest, response);
+    }
+
+    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
+    {
+        if (!enabled)
+        {
+            return true;
+        }
+        if (excludes == null || excludes.isEmpty())
+        {
+            return false;
+        }
+        String url = request.getServletPath();
+        for (String pattern : excludes)
+        {
+            Pattern p = Pattern.compile("^" + pattern);
+            Matcher m = p.matcher(url);
+            if (m.find())
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void destroy()
+    {
+
+    }
+}

+ 106 - 0
src/main/java/com/shs/official/common/filter/XssHttpServletRequestWrapper.java

@@ -0,0 +1,106 @@
+package com.shs.official.common.filter;
+
+import com.shs.official.common.utils.StringUtils;
+import com.shs.official.common.utils.html.EscapeUtil;
+import org.apache.commons.io.IOUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+/**
+ * XSS过滤处理
+ * 
+ * @author shs
+ */
+public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
+{
+    /**
+     * @param request
+     */
+    public XssHttpServletRequestWrapper(HttpServletRequest request)
+    {
+        super(request);
+    }
+
+    @Override
+    public String[] getParameterValues(String name)
+    {
+        String[] values = super.getParameterValues(name);
+        if (values != null)
+        {
+            int length = values.length;
+            String[] escapseValues = new String[length];
+            for (int i = 0; i < length; i++)
+            {
+                // 防xss攻击和过滤前后空格
+                escapseValues[i] = EscapeUtil.clean(values[i]).trim();
+            }
+            return escapseValues;
+        }
+        return super.getParameterValues(name);
+    }
+
+    @Override
+    public ServletInputStream getInputStream() throws IOException
+    {
+        // 非json类型,直接返回
+        if (!isJsonRequest())
+        {
+            return super.getInputStream();
+        }
+
+        // 为空,直接返回
+        String json = IOUtils.toString(super.getInputStream(), "utf-8");
+        if (StringUtils.isEmpty(json))
+        {
+            return super.getInputStream();
+        }
+
+        // xss过滤
+        json = EscapeUtil.clean(json).trim();
+        final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8"));
+        return new ServletInputStream()
+        {
+            @Override
+            public boolean isFinished()
+            {
+                return true;
+            }
+
+            @Override
+            public boolean isReady()
+            {
+                return true;
+            }
+
+            @Override
+            public void setReadListener(ReadListener readListener)
+            {
+            }
+
+            @Override
+            public int read() throws IOException
+            {
+                return bis.read();
+            }
+        };
+    }
+
+    /**
+     * 是否是Json请求
+     * 
+     * @param request
+     */
+    public boolean isJsonRequest()
+    {
+        String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
+        return MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(header)
+                || MediaType.APPLICATION_JSON_UTF8_VALUE.equalsIgnoreCase(header);
+    }
+}

+ 80 - 0
src/main/java/com/shs/official/common/http/HttpPoolConfig.java

@@ -0,0 +1,80 @@
+package com.shs.official.common.http;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/*
+* @author SHS
+* @version v1.0
+* @description http request
+* */
+@Component
+public class HttpPoolConfig {
+
+    @Value("${http_pool.max_total}")
+    private int maxTotal;
+
+    @Value("${http_pool.default_max_per_route}")
+    private int maxPerRoute;
+
+    @Value("${http_pool.connect_timeout}")
+    private int connTimeOut;
+
+    @Value("${http_pool.connection_request_timeout}")
+    private int connReqTimeOut;
+
+    @Value("${http_pool.socket_timeout}")
+    private int socketTimeout;
+
+    @Value("${http_pool.validate_after_inactivity}")
+    private int inactivity;
+
+
+    public int getMaxTotal() {
+        return maxTotal;
+    }
+
+    public void setMaxTotal(int maxTotal) {
+        this.maxTotal = maxTotal;
+    }
+
+    public int getMaxPerRoute() {
+        return maxPerRoute;
+    }
+
+    public void setMaxPerRoute(int maxPerRoute) {
+        this.maxPerRoute = maxPerRoute;
+    }
+
+    public int getConnTimeOut() {
+        return connTimeOut;
+    }
+
+    public void setConnTimeOut(int connTimeOut) {
+        this.connTimeOut = connTimeOut;
+    }
+
+    public int getConnReqTimeOut() {
+        return connReqTimeOut;
+    }
+
+    public void setConnReqTimeOut(int connReqTimeOut) {
+        this.connReqTimeOut = connReqTimeOut;
+    }
+
+    public int getSocketTimeout() {
+        return socketTimeout;
+    }
+
+    public void setSocketTimeout(int socketTimeout) {
+        this.socketTimeout = socketTimeout;
+    }
+
+    public int getInactivity() {
+        return inactivity;
+    }
+
+    public void setInactivity(int inactivity) {
+        this.inactivity = inactivity;
+    }
+}

+ 108 - 0
src/main/java/com/shs/official/common/http/HttpRequest.java

@@ -0,0 +1,108 @@
+package com.shs.official.common.http;
+
+import com.shs.official.common.utils.SpringUtils;
+import com.shs.official.common.utils.StringUtils;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Map;
+
+/*
+ * @author SHS
+ * @version v1.0
+ * @description http request
+ * */
+public class HttpRequest {
+
+    private RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);
+
+    /**
+     * 生成post请求的JSON请求参数
+     *
+     * @return
+     */
+    public HttpEntity<Map<String, String>> generatePostJson(Map<String, String> headerMap,
+                                                            String mediaType,Map<String, String> jsonMap) {
+        mediaType = mediaType == null?"application/json;charset=UTF-8":mediaType;
+        //如果需要其它的请求头信息、都可以在这里追加
+        HttpHeaders httpHeaders = new HttpHeaders();
+        if (StringUtils.isNotEmpty(headerMap)){
+            for (Map.Entry map : headerMap.entrySet()) {
+                httpHeaders.add(map.getKey().toString(),map.getValue().toString());
+            }
+        }
+        MediaType type = MediaType.parseMediaType(mediaType);
+
+        httpHeaders.setContentType(type);
+
+        HttpEntity<Map<String, String>> httpEntity = new HttpEntity<>(jsonMap, httpHeaders);
+
+        return httpEntity;
+    }
+
+
+    /**
+     * 生成get参数请求url
+     * 示例:https://0.0.0.0:80/api?u=u&o=o
+     * 示例:https://0.0.0.0:80/api
+     *
+     * @param url      请求的url 请求地址
+     * @param params   请求参数
+     * @return
+     */
+    public String generateRequestParameters(String url, Map<String, String> params) {
+        StringBuilder sb = new StringBuilder(url);
+        if (StringUtils.isNotEmpty(params)) {
+            sb.append("?");
+            for (Map.Entry map : params.entrySet()) {
+                sb.append(map.getKey())
+                        .append("=")
+                        .append(map.getValue())
+                        .append("&");
+            }
+            url = sb.substring(0, sb.length() - 1);
+            return url;
+        }
+        return sb.toString();
+    }
+
+
+
+    /**
+     * get请求、请求参数为?拼接形式的
+     * <p>
+     * 最终请求的URI如下:
+     * <p>
+     * http://127.0.0.1:80/?name=xxx&sex=男
+     *
+     * @return
+     */
+    public String sendGet(String url,Map<String,String> urlMap) {
+        ResponseEntity responseEntity = restTemplate.getForEntity
+                (
+                        generateRequestParameters(url, urlMap),
+                        String.class
+                );
+        return (String) responseEntity.getBody();
+    }
+
+    /**
+     * post请求、请求参数为json
+     *
+     * @return
+     */
+    public String sendPost(String url, Map<String,String> headerParams,String mediaType,Map<String,String> jsonMap) {
+
+        ResponseEntity<String> apiResponse = restTemplate.postForEntity
+                (
+                        url,
+                        generatePostJson(headerParams,mediaType,jsonMap),
+                        String.class
+                );
+        return apiResponse.getBody();
+    }
+
+}

+ 64 - 0
src/main/java/com/shs/official/common/http/RestTemplateConfig.java

@@ -0,0 +1,64 @@
+package com.shs.official.common.http;
+
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+/*
+ * @author SHS
+ * @version v1.0
+ * @description http request
+ * */
+@Configuration
+public class RestTemplateConfig {
+
+    @Autowired
+    private HttpPoolConfig httpPoolConfig;
+
+    @Bean
+    public RestTemplate restTemplate() {
+        return new RestTemplate(httpRequestFactory());
+    }
+
+    @Bean
+    public ClientHttpRequestFactory httpRequestFactory() {
+        return new HttpComponentsClientHttpRequestFactory(httpClient());
+    }
+
+    @Bean
+    public HttpClient httpClient() {
+        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
+                .register("http", PlainConnectionSocketFactory.getSocketFactory())
+                .register("https", SSLConnectionSocketFactory.getSocketFactory())
+                .build();
+        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
+        connectionManager.setMaxTotal(httpPoolConfig.getMaxTotal());
+        connectionManager.setDefaultMaxPerRoute(httpPoolConfig.getMaxPerRoute());
+        connectionManager.setValidateAfterInactivity(httpPoolConfig.getInactivity());
+        RequestConfig requestConfig = RequestConfig.custom()
+                //服务器返回数据(response)的时间,超过抛出read timeout
+                .setSocketTimeout(httpPoolConfig.getSocketTimeout())
+                //连接上服务器(握手成功)的时间,超出抛出connect timeout
+                .setConnectTimeout(httpPoolConfig.getConnTimeOut())
+                //从连接池中获取连接的超时时间,超时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
+                .setConnectionRequestTimeout(httpPoolConfig.getConnReqTimeOut())
+                .build();
+        return HttpClientBuilder.create()
+                .setDefaultRequestConfig(requestConfig)
+                .setConnectionManager(connectionManager)
+                .build();
+    }
+
+}

+ 63 - 0
src/main/java/com/shs/official/common/interceptor/LoginInterceptor.java

@@ -0,0 +1,63 @@
+package com.shs.official.common.interceptor;
+
+import com.alibaba.fastjson.JSONObject;
+import com.shs.official.common.ApplcationConfig;
+import com.shs.official.common.template.ResultTemplate;
+import com.shs.official.common.utils.redis.RedisUtils;
+//import com.shs.official.entity.user.User;
+import com.shs.official.common.utils.session.SessionUtils;
+import com.shs.official.entity.user.TbUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Import;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Component
+@Import(ApplcationConfig.class)
+public class LoginInterceptor implements HandlerInterceptor {
+
+    @Autowired
+    private RedisUtils redisUtils;
+
+    @Autowired
+    private SessionUtils sessionUtils;
+
+    // 令牌自定义标识
+    @Value("${token.header}")
+    private String header;
+
+    // 令牌密钥
+    @Value("${token.key}")
+    private String key;
+
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        response.setCharacterEncoding("UTF-8");
+        if (handler instanceof HandlerMethod) {
+            // 判断是否登陆了
+            String xToken = request.getHeader(header);
+            if (xToken == null || xToken == "") {
+                ResultTemplate r = ResultTemplate.error("unauthorized access");
+                response.getWriter().write(JSONObject.toJSONString(r));
+                return false;
+            }
+            TbUser user = sessionUtils.getSession(xToken);
+            //session不存在则未登录
+            if(user == null)
+                 return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+
+    }
+}

+ 56 - 0
src/main/java/com/shs/official/common/interceptor/RepeatSubmitInterceptor.java

@@ -0,0 +1,56 @@
+package com.shs.official.common.interceptor;
+
+import com.alibaba.fastjson.JSONObject;
+import com.shs.official.common.interceptor.annotation.RepeatSubmit;
+import com.shs.official.common.template.ResultTemplate;
+import com.shs.official.common.utils.ServletUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+
+/**
+ * 防止重复提交拦截器
+ * 
+ * @author shs
+ */
+@Component
+public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter
+{
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
+    {
+        if (handler instanceof HandlerMethod)
+        {
+            HandlerMethod handlerMethod = (HandlerMethod) handler;
+            Method method = handlerMethod.getMethod();
+            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
+            if (annotation != null)
+            {
+                if (this.isRepeatSubmit(request))
+                {
+                    ResultTemplate res = ResultTemplate.error("不允许重复提交,请稍后再试");
+                    ServletUtils.renderString(response, JSONObject.toJSONString(res));
+                    return false;
+                }
+            }
+            return true;
+        }
+        else
+        {
+            return super.preHandle(request, response, handler);
+        }
+    }
+
+    /**
+     * 验证是否重复提交由子类实现具体的防重复提交的规则
+     * 
+     * @param httpServletRequest
+     * @return
+     * @throws Exception
+     */
+    public abstract boolean isRepeatSubmit(HttpServletRequest request);
+}

+ 18 - 0
src/main/java/com/shs/official/common/interceptor/annotation/RepeatSubmit.java

@@ -0,0 +1,18 @@
+package com.shs.official.common.interceptor.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义注解防止表单重复提交
+ * 
+ * @author shs
+ *
+ */
+@Inherited
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RepeatSubmit
+{
+
+}

+ 31 - 0
src/main/java/com/shs/official/common/mybatis/MyBatisPlusIdentifierGenerator.java

@@ -0,0 +1,31 @@
+package com.shs.official.common.mybatis;
+
+import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
+import com.shs.official.common.utils.IdGen;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.annotation.Id;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * MyBatisPlus自定义Id生成
+ *
+ * @description: 注意前端传值时要为null
+ * @author: shs
+ */
+@Component
+public class MyBatisPlusIdentifierGenerator implements IdentifierGenerator {
+
+    private static final Logger logger = LoggerFactory.getLogger(MyBatisPlusIdentifierGenerator.class);
+
+    @Override
+    public Long nextId(Object entity) {
+        logger.info("\n mybatis plus Id gen: ==========================");
+        //ID生成
+        Long id = IdGen.getId();
+        //返回生成的id值即可.
+        return id;
+    }
+
+}

+ 78 - 0
src/main/java/com/shs/official/common/mybatis/MyBatisPlusMate.java

@@ -0,0 +1,78 @@
+package com.shs.official.common.mybatis;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.shs.official.common.utils.IdGen;
+import com.shs.official.common.utils.ServletUtils;
+import com.shs.official.common.utils.http.HttpHelper;
+import com.shs.official.common.utils.redis.RedisUtils;
+import com.shs.official.controller.BaseController;
+//import com.shs.official.entity.operate.OperatrEntity;
+//import com.shs.official.entity.user.User;
+import org.apache.ibatis.reflection.MetaObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * MyBatisPlus自定义字段自动填充处理类 - 实体类中使用 @TableField注解
+ *
+ * @description: 注意前端传值时要为null
+ * @author: shs
+ */
+@Component
+public class MyBatisPlusMate implements MetaObjectHandler {
+
+    private static final Logger logger = LoggerFactory.getLogger(MyBatisPlusMate.class);
+
+    @Autowired
+    private RedisUtils redisUtils;
+
+    // 令牌自定义标识
+    @Value("${token.header}")
+    private String header;
+
+    /**
+     * 创建填充
+     */
+    @Override
+    public void insertFill(MetaObject metaObject) {
+//        logger.info("\n -------------------- start insert fill ...  --------------------");
+//        OperatrEntity operatrEntity = getOperate();
+//        this.strictInsertFill(metaObject,"operate",String.class,operatrEntity.getOperate());
+//        this.strictInsertFill(metaObject,"operate_time", Long.class,operatrEntity.getOperateTime());
+//        this.strictInsertFill(metaObject,"operate_id",Long.class,operatrEntity.getOperatrId());
+//        this.strictInsertFill(metaObject,"operate_ip", String.class,operatrEntity.getOperateIp());
+//        this.strictInsertFill(metaObject,"status",Integer.class, STATUS.BASESTATUS.ENABLE.getStatus());
+    }
+
+    /**
+     * 更新填充
+     */
+    @Override
+    public void updateFill(MetaObject metaObject) {
+//        logger.info(" \n-------------------- start update fill ...  --------------------");
+//        OperatrEntity operatrEntity = getOperate();
+//        this.strictUpdateFill(metaObject, "update_operate", String.class, operatrEntity.getOperate());
+//        this.strictUpdateFill(metaObject, "update_operate_time", Long.class, operatrEntity.getOperateTime());
+//        this.strictUpdateFill(metaObject, "update_operate_id", Long.class, operatrEntity.getOperatrId());
+//        this.strictUpdateFill(metaObject, "update_operate_ip", String.class, operatrEntity.getOperateIp());
+    }
+
+
+//    public OperatrEntity getOperate(){
+//        OperatrEntity operatrEntity = new OperatrEntity();
+//        HttpServletRequest request = ServletUtils.getRequest();
+//        String token = request.getHeader(header);
+//        User user = (User) redisUtils.get(token);
+//        operatrEntity.setOperatrId(user.getId());
+//        operatrEntity.setOperate(user.getName());
+//        operatrEntity.setOperateTime(System.currentTimeMillis());
+//        operatrEntity.setOperateIp(HttpHelper.getIpAddr(request));
+//        return operatrEntity;
+//    }
+}

+ 99 - 0
src/main/java/com/shs/official/common/template/HttpStatus.java

@@ -0,0 +1,99 @@
+package com.shs.official.common.template;
+
+/**
+ * 返回状态码
+ *
+ * @author shs
+ */
+public interface HttpStatus
+{
+    /**
+     * 操作成功
+     */
+    public static final int SUCCESS = 200;
+
+    /**
+     * 对象创建成功
+     */
+    public static final int CREATED = 201;
+
+    /**
+     * 请求已经被接受
+     */
+    public static final int ACCEPTED = 202;
+
+    /**
+     * 操作已经执行成功,但是没有返回数据
+     */
+    public static final int NO_CONTENT = 204;
+
+    /**
+     * 资源已被移除
+     */
+    public static final int MOVED_PERM = 301;
+
+    /**
+     * 重定向
+     */
+    public static final int SEE_OTHER = 303;
+
+    /**
+     * 资源没有被修改
+     */
+    public static final int NOT_MODIFIED = 304;
+
+    /**
+     * 参数列表错误(缺少,格式不匹配)
+     */
+    public static final int BAD_REQUEST = 400;
+
+    /**
+     * 未授权
+     */
+    public static final int UNAUTHORIZED = 401;
+
+    /**
+     * 访问受限,授权过期
+     */
+    public static final int FORBIDDEN = 403;
+
+    /**
+     * 资源,服务未找到
+     */
+    public static final int NOT_FOUND = 404;
+
+    /**
+     * 不允许的http方法
+     */
+    public static final int BAD_METHOD = 405;
+
+    /**
+     * 资源冲突,或者资源被锁
+     */
+    public static final int CONFLICT = 409;
+
+    /**
+     * 不支持的数据,媒体类型
+     */
+    public static final int UNSUPPORTED_TYPE = 415;
+
+    /**
+     * 系统内部错误
+     */
+    public static final int ERROR = 500;
+
+    /**
+     * 接口未实现
+     */
+    public static final int NOT_IMPLEMENTED = 501;
+
+
+    public static final int SYSTEM_ERROR = 9999;
+    public static final int VERIFICATION_CODE_ERROR = 5501;
+    public static final int VERIFICATION_DOES_NOT_EXISTS = 5502;
+    public static final int USER_PASSWORD_ERROR = 5503;
+
+
+    public static final int FILE_TOO_LARGE_ERROR = 5511;
+    public static final int FILENAME_TOO_LONG_ERROR = 5512;
+}

+ 161 - 0
src/main/java/com/shs/official/common/template/ResultTemplate.java

@@ -0,0 +1,161 @@
+package com.shs.official.common.template;
+
+import com.shs.official.common.utils.StringUtils;
+
+import java.util.HashMap;
+
+/**
+ * 操作消息提醒
+ *
+ * @author shs
+ */
+public class ResultTemplate extends HashMap<String,Object> {
+    private static final long serialVersionUID = 1L;
+
+    /** 状态码 */
+    public static final String CODE_TAG = "code";
+
+    /** 返回内容 */
+    public static final String MSG_TAG = "msg";
+
+    /** 数据对象 */
+    public static final String DATA_TAG = "data";
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
+     */
+    public ResultTemplate()
+    {
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     *
+     * @param code 状态码
+     * @param msg 返回内容
+     */
+    public ResultTemplate(int code, String msg)
+    {
+        super.put(CODE_TAG, code);
+        super.put(MSG_TAG, msg);
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     *
+     * @param code 状态码
+     * @param msg 返回内容
+     * @param data 数据对象
+     */
+    public ResultTemplate(int code, String msg, Object data)
+    {
+        super.put(CODE_TAG, code);
+        super.put(MSG_TAG, msg);
+        if (StringUtils.isNotNull(data))
+        {
+            super.put(DATA_TAG, data);
+        }
+    }
+
+    /**
+     * 返回成功消息
+     *
+     * @return 成功消息
+     */
+    public static ResultTemplate success()
+    {
+        return ResultTemplate.success("操作成功");
+    }
+
+    /**
+     * 返回成功数据
+     *
+     * @return 成功消息
+     */
+    public static ResultTemplate success(Object data)
+    {
+        return ResultTemplate.success("操作成功", data);
+    }
+
+    /**
+     * 返回成功消息
+     *
+     * @param msg 返回内容
+     * @return 成功消息
+     */
+    public static ResultTemplate success(String msg)
+    {
+        return ResultTemplate.success(msg, null);
+    }
+
+    /**
+     * 返回成功消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 成功消息
+     */
+    public static ResultTemplate success(String msg, Object data)
+    {
+        return new ResultTemplate(HttpStatus.SUCCESS, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @return
+     */
+    public static ResultTemplate error()
+    {
+        return ResultTemplate.error("操作失败");
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static ResultTemplate error(String msg)
+    {
+        return ResultTemplate.error(msg, null);
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static ResultTemplate error(String msg, Object data)
+    {
+        return new ResultTemplate(HttpStatus.ERROR, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @param code 状态码
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static ResultTemplate error(int code, String msg)
+    {
+        return new ResultTemplate(code, msg, null);
+    }
+
+
+    /**
+     * 返回错误消息
+     *
+     * @param code 状态码
+     * @param msg 返回内容
+     * @param data 数据
+     * @return 警告消息
+     */
+    public static ResultTemplate error(int code, String msg, Object data)
+    {
+        return new ResultTemplate(code, msg, data);
+    }
+}

+ 156 - 0
src/main/java/com/shs/official/common/utils/DateUtils.java

@@ -0,0 +1,156 @@
+package com.shs.official.common.utils;
+
+import org.apache.commons.lang3.time.DateFormatUtils;
+
+import java.lang.management.ManagementFactory;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 时间工具类
+ *
+ * @author shs
+ */
+public class DateUtils extends org.apache.commons.lang3.time.DateUtils
+{
+    public static String YYYY = "yyyy";
+
+    public static String YYYY_MM = "yyyy-MM";
+
+    public static String YYYY_MM_DD = "yyyy-MM-dd";
+
+    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
+
+    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+
+    private static String[] parsePatterns = {
+            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
+            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
+            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
+
+    /**
+     * 获取当前Date型日期
+     *
+     * @return Date() 当前日期
+     */
+    public static Date getNowDate()
+    {
+        return new Date();
+    }
+
+    /**
+     * 获取当前日期, 默认格式为yyyy-MM-dd
+     *
+     * @return String
+     */
+    public static String getDate()
+    {
+        return dateTimeNow(YYYY_MM_DD);
+    }
+
+    public static final String getTime()
+    {
+        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
+    }
+
+    public static final String dateTimeNow()
+    {
+        return dateTimeNow(YYYYMMDDHHMMSS);
+    }
+
+    public static final String dateTimeNow(final String format)
+    {
+        return parseDateToStr(format, new Date());
+    }
+
+    public static final String dateTime(final Date date)
+    {
+        return parseDateToStr(YYYY_MM_DD, date);
+    }
+
+    public static final String parseDateToStr(final String format, final Date date)
+    {
+        return new SimpleDateFormat(format).format(date);
+    }
+
+    public static final Date dateTime(final String format, final String ts)
+    {
+        try
+        {
+            return new SimpleDateFormat(format).parse(ts);
+        }
+        catch (ParseException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 日期路径 即年/月/日 如2018/08/08
+     */
+    public static final String datePath()
+    {
+        Date now = new Date();
+        return DateFormatUtils.format(now, "yyyy/MM/dd");
+    }
+
+    /**
+     * 日期路径 即年/月/日 如20180808
+     */
+    public static final String dateTime()
+    {
+        Date now = new Date();
+        return DateFormatUtils.format(now, "yyyyMMdd");
+    }
+
+    /**
+     * 日期型字符串转化为日期 格式
+     */
+    public static Date parseDate(Object str)
+    {
+        if (str == null)
+        {
+            return null;
+        }
+        try
+        {
+            return parseDate(str.toString(), parsePatterns);
+        }
+        catch (ParseException e)
+        {
+            return null;
+        }
+    }
+
+    /**
+     * 获取服务器启动时间
+     */
+    public static Date getServerStartDate()
+    {
+        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
+        return new Date(time);
+    }
+
+    /**
+     * 计算两个时间差
+     */
+    public static String getDatePoor(Date endDate, Date nowDate)
+    {
+        long nd = 1000 * 24 * 60 * 60;
+        long nh = 1000 * 60 * 60;
+        long nm = 1000 * 60;
+        // long ns = 1000;
+        // 获得两个时间的毫秒时间差异
+        long diff = endDate.getTime() - nowDate.getTime();
+        // 计算差多少天
+        long day = diff / nd;
+        // 计算差多少小时
+        long hour = diff % nd / nh;
+        // 计算差多少分钟
+        long min = diff % nd % nh / nm;
+        // 计算差多少秒//输出结果
+        // long sec = diff % nd % nh % nm / ns;
+        return day + "天" + hour + "小时" + min + "分钟";
+    }
+}

+ 91 - 0
src/main/java/com/shs/official/common/utils/IdGen.java

@@ -0,0 +1,91 @@
+package com.shs.official.common.utils;
+
+public class IdGen
+{
+    private long workerId;
+    private long datacenterId;
+    private long sequence;
+    private long twepoch;
+    private long workerIdBits;
+    private long datacenterIdBits;
+    private long maxWorkerId;
+    private long maxDatacenterId;
+    private long sequenceBits;
+    private long workerIdShift;
+    private long datacenterIdShift;
+    private long timestampLeftShift;
+    private long sequenceMask;
+    private long lastTimestamp;
+
+    public static IdGen getInstance() {
+        return IdGenHolder.instance;
+    }
+
+    public IdGen() {
+        this(0L, 0L);
+    }
+
+    public IdGen(final long workerId, final long datacenterId) {
+        this.sequence = 0L;
+        this.twepoch = 1288834974657L;
+        this.workerIdBits = 5L;
+        this.datacenterIdBits = 5L;
+        this.maxWorkerId = (-1L ^ -1L << (int)this.workerIdBits);
+        this.maxDatacenterId = (-1L ^ -1L << (int)this.datacenterIdBits);
+        this.sequenceBits = 12L;
+        this.workerIdShift = this.sequenceBits;
+        this.datacenterIdShift = this.sequenceBits + this.workerIdBits;
+        this.timestampLeftShift = this.sequenceBits + this.workerIdBits + this.datacenterIdBits;
+        this.sequenceMask = (-1L ^ -1L << (int)this.sequenceBits);
+        this.lastTimestamp = -1L;
+        if (workerId > this.maxWorkerId || workerId < 0L) {
+            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", this.maxWorkerId));
+        }
+        if (datacenterId > this.maxDatacenterId || datacenterId < 0L) {
+            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", this.maxDatacenterId));
+        }
+        this.workerId = workerId;
+        this.datacenterId = datacenterId;
+    }
+
+    public synchronized long Id() {
+        long timestamp = this.timeGen();
+        if (timestamp < this.lastTimestamp) {
+            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", this.lastTimestamp - timestamp));
+        }
+        if (this.lastTimestamp == timestamp) {
+            this.sequence = (this.sequence + 1L & this.sequenceMask);
+            if (this.sequence == 0L) {
+                timestamp = this.tilNextMillis(this.lastTimestamp);
+            }
+        }
+        else {
+            this.sequence = 0L;
+        }
+        this.lastTimestamp = timestamp;
+        return timestamp - this.twepoch << (int)this.timestampLeftShift | this.datacenterId << (int)this.datacenterIdShift | this.workerId << (int)this.workerIdShift | this.sequence;
+    }
+
+    protected long tilNextMillis(final long lastTimestamp) {
+        long timestamp;
+        for (timestamp = this.timeGen(); timestamp <= lastTimestamp; timestamp = this.timeGen()) {}
+        return timestamp;
+    }
+
+    protected long timeGen() {
+        return System.currentTimeMillis();
+    }
+
+    public static long getId() {
+        return getInstance().Id();
+    }
+
+    private static class IdGenHolder
+    {
+        private static final IdGen instance;
+
+        static {
+            instance = new IdGen();
+        }
+    }
+}

+ 25 - 0
src/main/java/com/shs/official/common/utils/MessageUtils.java

@@ -0,0 +1,25 @@
+package com.shs.official.common.utils;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
+
+/**
+ * 获取i18n资源文件
+ *
+ * @author shs
+ */
+public class MessageUtils
+{
+    /**
+     * 根据消息键和参数 获取消息 委托给spring messageSource
+     *
+     * @param code 消息键
+     * @param args 参数
+     * @return 获取国际化翻译值
+     */
+    public static String message(String code, Object... args)
+    {
+        MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
+        return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
+    }
+}

+ 137 - 0
src/main/java/com/shs/official/common/utils/ServletUtils.java

@@ -0,0 +1,137 @@
+package com.shs.official.common.utils;
+
+import com.shs.official.common.utils.str.Convert;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+
+/**
+ * 客户端工具类
+ *
+ * @author shs
+ */
+public class ServletUtils
+{
+    /**
+     * 获取String参数
+     */
+    public static String getParameter(String name)
+    {
+        return getRequest().getParameter(name);
+    }
+
+    /**
+     * 获取String参数
+     */
+    public static String getParameter(String name, String defaultValue)
+    {
+        return Convert.toStr(getRequest().getParameter(name), defaultValue);
+    }
+
+    /**
+     * 获取Integer参数
+     */
+    public static Integer getParameterToInt(String name)
+    {
+        return Convert.toInt(getRequest().getParameter(name));
+    }
+
+    /**
+     * 获取Integer参数
+     */
+    public static Integer getParameterToInt(String name, Integer defaultValue)
+    {
+        return Convert.toInt(getRequest().getParameter(name), defaultValue);
+    }
+
+    /**
+     * 获取request
+     */
+    public static HttpServletRequest getRequest()
+    {
+        return getRequestAttributes().getRequest();
+    }
+
+    /**
+     * 获取response
+     */
+    public static HttpServletResponse getResponse()
+    {
+        return getRequestAttributes().getResponse();
+    }
+
+    /**
+     * 获取session
+     */
+    public static HttpSession getSession()
+    {
+        return getRequest().getSession();
+    }
+
+    public static ServletRequestAttributes getRequestAttributes()
+    {
+        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
+        return (ServletRequestAttributes) attributes;
+    }
+
+    /**
+     * 将字符串渲染到客户端
+     *
+     * @param response 渲染对象
+     * @param string 待渲染的字符串
+     * @return null
+     */
+    public static String renderString(HttpServletResponse response, String string)
+    {
+        try
+        {
+            response.setStatus(200);
+            response.setContentType("application/json");
+            response.setCharacterEncoding("utf-8");
+            response.getWriter().print(string);
+        }
+        catch (IOException e)
+        {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 是否是Ajax异步请求
+     *
+     * @param request
+     */
+    public static boolean isAjaxRequest(HttpServletRequest request)
+    {
+        String accept = request.getHeader("accept");
+        if (accept != null && accept.indexOf("application/json") != -1)
+        {
+            return true;
+        }
+
+        String xRequestedWith = request.getHeader("X-Requested-With");
+        if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
+        {
+            return true;
+        }
+
+        String uri = request.getRequestURI();
+        if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml"))
+        {
+            return true;
+        }
+
+        String ajax = request.getParameter("__ajax");
+        if (StringUtils.inStringIgnoreCase(ajax, "json", "xml"))
+        {
+            return true;
+        }
+        return false;
+    }
+}

+ 114 - 0
src/main/java/com/shs/official/common/utils/SpringUtils.java

@@ -0,0 +1,114 @@
+package com.shs.official.common.utils;
+
+import org.springframework.aop.framework.AopContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * spring工具类 方便在非spring管理环境中获取bean
+ *
+ * @author shs
+ */
+@Component
+public final class SpringUtils implements BeanFactoryPostProcessor
+{
+    /** Spring应用上下文环境 */
+    private static ConfigurableListableBeanFactory beanFactory;
+
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
+    {
+        SpringUtils.beanFactory = beanFactory;
+    }
+
+    /**
+     * 获取对象
+     *
+     * @param name
+     * @return Object 一个以所给名字注册的bean的实例
+     * @throws org.springframework.beans.BeansException
+     *
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String name) throws BeansException
+    {
+        return (T) beanFactory.getBean(name);
+    }
+
+    /**
+     * 获取类型为requiredType的对象
+     *
+     * @param clz
+     * @return
+     * @throws org.springframework.beans.BeansException
+     *
+     */
+    public static <T> T getBean(Class<T> clz) throws BeansException
+    {
+        T result = (T) beanFactory.getBean(clz);
+        return result;
+    }
+
+    /**
+     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+     *
+     * @param name
+     * @return boolean
+     */
+    public static boolean containsBean(String name)
+    {
+        return beanFactory.containsBean(name);
+    }
+
+    /**
+     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+     *
+     * @param name
+     * @return boolean
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     *
+     */
+    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
+    {
+        return beanFactory.isSingleton(name);
+    }
+
+    /**
+     * @param name
+     * @return Class 注册对象的类型
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     *
+     */
+    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
+    {
+        return beanFactory.getType(name);
+    }
+
+    /**
+     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+     *
+     * @param name
+     * @return
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     *
+     */
+    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
+    {
+        return beanFactory.getAliases(name);
+    }
+
+    /**
+     * 获取aop代理对象
+     *
+     * @param invoker
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getAopProxy(T invoker)
+    {
+        return (T) AopContext.currentProxy();
+    }
+}

+ 449 - 0
src/main/java/com/shs/official/common/utils/StringUtils.java

@@ -0,0 +1,449 @@
+package com.shs.official.common.utils;
+
+import com.shs.official.common.utils.str.StrFormatter;
+
+import java.util.*;
+
+/**
+ * 字符串工具类
+ * 
+ * @author shs
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils
+{
+    /** 空字符串 */
+    private static final String NULLSTR = "";
+
+    /** 下划线 */
+    private static final char SEPARATOR = '_';
+
+    /**
+     * 获取参数不为空值
+     * 
+     * @param value defaultValue 要判断的value
+     * @return value 返回值
+     */
+    public static <T> T nvl(T value, T defaultValue)
+    {
+        return value != null ? value : defaultValue;
+    }
+
+    /**
+     * * 判断一个Collection是否为空, 包含List,Set,Queue
+     * 
+     * @param coll 要判断的Collection
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Collection<?> coll)
+    {
+        return isNull(coll) || coll.isEmpty();
+    }
+
+    /**
+     * * 判断一个Collection是否非空,包含List,Set,Queue
+     * 
+     * @param coll 要判断的Collection
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Collection<?> coll)
+    {
+        return !isEmpty(coll);
+    }
+
+    /**
+     * * 判断一个对象数组是否为空
+     * 
+     * @param objects 要判断的对象数组
+     ** @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Object[] objects)
+    {
+        return isNull(objects) || (objects.length == 0);
+    }
+
+    /**
+     * * 判断一个对象数组是否非空
+     * 
+     * @param objects 要判断的对象数组
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Object[] objects)
+    {
+        return !isEmpty(objects);
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     * 
+     * @param map 要判断的Map
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Map<?, ?> map)
+    {
+        return isNull(map) || map.isEmpty();
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     * 
+     * @param map 要判断的Map
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Map<?, ?> map)
+    {
+        return !isEmpty(map);
+    }
+
+    /**
+     * * 判断一个字符串是否为空串
+     * 
+     * @param str String
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(String str)
+    {
+        return isNull(str) || NULLSTR.equals(str.trim());
+    }
+
+    /**
+     * * 判断一个字符串是否为非空串
+     * 
+     * @param str String
+     * @return true:非空串 false:空串
+     */
+    public static boolean isNotEmpty(String str)
+    {
+        return !isEmpty(str);
+    }
+
+    /**
+     * * 判断一个对象是否为空
+     * 
+     * @param object Object
+     * @return true:为空 false:非空
+     */
+    public static boolean isNull(Object object)
+    {
+        return object == null;
+    }
+
+    /**
+     * * 判断一个对象是否非空
+     * 
+     * @param object Object
+     * @return true:非空 false:空
+     */
+    public static boolean isNotNull(Object object)
+    {
+        return !isNull(object);
+    }
+
+    /**
+     * * 判断一个对象是否是数组类型(Java基本型别的数组)
+     * 
+     * @param object 对象
+     * @return true:是数组 false:不是数组
+     */
+    public static boolean isArray(Object object)
+    {
+        return isNotNull(object) && object.getClass().isArray();
+    }
+
+    /**
+     * 去空格
+     */
+    public static String trim(String str)
+    {
+        return (str == null ? "" : str.trim());
+    }
+
+    /**
+     * 截取字符串
+     * 
+     * @param str 字符串
+     * @param start 开始
+     * @return 结果
+     */
+    public static String substring(final String str, int start)
+    {
+        if (str == null)
+        {
+            return NULLSTR;
+        }
+
+        if (start < 0)
+        {
+            start = str.length() + start;
+        }
+
+        if (start < 0)
+        {
+            start = 0;
+        }
+        if (start > str.length())
+        {
+            return NULLSTR;
+        }
+
+        return str.substring(start);
+    }
+
+    /**
+     * 截取字符串
+     * 
+     * @param str 字符串
+     * @param start 开始
+     * @param end 结束
+     * @return 结果
+     */
+    public static String substring(final String str, int start, int end)
+    {
+        if (str == null)
+        {
+            return NULLSTR;
+        }
+
+        if (end < 0)
+        {
+            end = str.length() + end;
+        }
+        if (start < 0)
+        {
+            start = str.length() + start;
+        }
+
+        if (end > str.length())
+        {
+            end = str.length();
+        }
+
+        if (start > end)
+        {
+            return NULLSTR;
+        }
+
+        if (start < 0)
+        {
+            start = 0;
+        }
+        if (end < 0)
+        {
+            end = 0;
+        }
+
+        return str.substring(start, end);
+    }
+
+    /**
+     * 格式化文本, {} 表示占位符<br>
+     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
+     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
+     * 例:<br>
+     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
+     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
+     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
+     * 
+     * @param template 文本模板,被替换的部分用 {} 表示
+     * @param params 参数值
+     * @return 格式化后的文本
+     */
+    public static String format(String template, Object... params)
+    {
+        if (isEmpty(params) || isEmpty(template))
+        {
+            return template;
+        }
+        return StrFormatter.format(template, params);
+    }
+
+    /**
+     * 字符串转set
+     * 
+     * @param str 字符串
+     * @param sep 分隔符
+     * @return set集合
+     */
+    public static final Set<String> str2Set(String str, String sep)
+    {
+        return new HashSet<String>(str2List(str, sep, true, false));
+    }
+
+    /**
+     * 字符串转list
+     * 
+     * @param str 字符串
+     * @param sep 分隔符
+     * @param filterBlank 过滤纯空白
+     * @param trim 去掉首尾空白
+     * @return list集合
+     */
+    public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
+    {
+        List<String> list = new ArrayList<String>();
+        if (StringUtils.isEmpty(str))
+        {
+            return list;
+        }
+
+        // 过滤空白字符串
+        if (filterBlank && StringUtils.isBlank(str))
+        {
+            return list;
+        }
+        String[] split = str.split(sep);
+        for (String string : split)
+        {
+            if (filterBlank && StringUtils.isBlank(string))
+            {
+                continue;
+            }
+            if (trim)
+            {
+                string = string.trim();
+            }
+            list.add(string);
+        }
+
+        return list;
+    }
+
+    /**
+     * 下划线转驼峰命名
+     */
+    public static String toUnderScoreCase(String str)
+    {
+        if (str == null)
+        {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        // 前置字符是否大写
+        boolean preCharIsUpperCase = true;
+        // 当前字符是否大写
+        boolean curreCharIsUpperCase = true;
+        // 下一字符是否大写
+        boolean nexteCharIsUpperCase = true;
+        for (int i = 0; i < str.length(); i++)
+        {
+            char c = str.charAt(i);
+            if (i > 0)
+            {
+                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
+            }
+            else
+            {
+                preCharIsUpperCase = false;
+            }
+
+            curreCharIsUpperCase = Character.isUpperCase(c);
+
+            if (i < (str.length() - 1))
+            {
+                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
+            }
+
+            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
+            {
+                sb.append(SEPARATOR);
+            }
+            else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
+            {
+                sb.append(SEPARATOR);
+            }
+            sb.append(Character.toLowerCase(c));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * 是否包含字符串
+     * 
+     * @param str 验证字符串
+     * @param strs 字符串组
+     * @return 包含返回true
+     */
+    public static boolean inStringIgnoreCase(String str, String... strs)
+    {
+        if (str != null && strs != null)
+        {
+            for (String s : strs)
+            {
+                if (str.equalsIgnoreCase(trim(s)))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
+     * 
+     * @param name 转换前的下划线大写方式命名的字符串
+     * @return 转换后的驼峰式命名的字符串
+     */
+    public static String convertToCamelCase(String name)
+    {
+        StringBuilder result = new StringBuilder();
+        // 快速检查
+        if (name == null || name.isEmpty())
+        {
+            // 没必要转换
+            return "";
+        }
+        else if (!name.contains("_"))
+        {
+            // 不含下划线,仅将首字母大写
+            return name.substring(0, 1).toUpperCase() + name.substring(1);
+        }
+        // 用下划线将原始字符串分割
+        String[] camels = name.split("_");
+        for (String camel : camels)
+        {
+            // 跳过原始字符串中开头、结尾的下换线或双重下划线
+            if (camel.isEmpty())
+            {
+                continue;
+            }
+            // 首字母大写
+            result.append(camel.substring(0, 1).toUpperCase());
+            result.append(camel.substring(1).toLowerCase());
+        }
+        return result.toString();
+    }
+
+    /**
+     * 驼峰式命名法 例如:user_name->userName
+     */
+    public static String toCamelCase(String s)
+    {
+        if (s == null)
+        {
+            return null;
+        }
+        s = s.toLowerCase();
+        StringBuilder sb = new StringBuilder(s.length());
+        boolean upperCase = false;
+        for (int i = 0; i < s.length(); i++)
+        {
+            char c = s.charAt(i);
+
+            if (c == SEPARATOR)
+            {
+                upperCase = true;
+            }
+            else if (upperCase)
+            {
+                sb.append(Character.toUpperCase(c));
+                upperCase = false;
+            }
+            else
+            {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+}

+ 250 - 0
src/main/java/com/shs/official/common/utils/ffmpeg/FfmpegUtils.java

@@ -0,0 +1,250 @@
+package com.shs.official.common.utils.ffmpeg;
+
+import com.shs.official.common.LiveConfig;
+import com.shs.official.common.exception.BaseException;
+import com.shs.official.common.template.ResultTemplate;
+import com.shs.official.common.utils.StringUtils;
+import com.shs.official.entity.media.TbMedia;
+import com.shs.official.service.media.MediaService;
+import net.bramp.ffmpeg.FFmpeg;
+import net.bramp.ffmpeg.FFmpegExecutor;
+import net.bramp.ffmpeg.FFprobe;
+import net.bramp.ffmpeg.builder.FFmpegBuilder;
+import net.bramp.ffmpeg.builder.FFmpegOutputBuilder;
+import net.bramp.ffmpeg.probe.FFmpegProbeResult;
+import net.bramp.ffmpeg.probe.FFmpegStream;
+import net.bramp.ffmpeg.progress.Progress;
+import net.bramp.ffmpeg.progress.ProgressListener;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.File;
+
+
+public class FfmpegUtils {
+
+    @Autowired
+    private MediaService mediaService;
+
+    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(FfmpegUtils.class);
+    private static Progress progress;
+
+    /*
+     * 视频转换
+     * */
+    public static TbMedia convertCommand(String file,String outFile ,String outName,Long key,Integer type) {
+        FfmpegUtils.checkFile(file,outFile);
+        try {
+            FFprobe fFprobe = new FFprobe(LiveConfig.getFfprobePath());
+            FFmpegProbeResult probeResult = fFprobe.probe(file);
+            FFmpegStream stream = probeResult.getStreams().get(0).width == 0 ?probeResult.getStreams().get(1): probeResult.getStreams().get(0) ;
+            String ffmpegFormart = "";
+            String outFormat = type.equals(CONVERTTYPE.TS.getType())?CONVERTTYPE.TS.getFormat():CONVERTTYPE.M3U8.getFormat();
+            FFmpegBuilder fFmpegBuilder = new FFmpegBuilder();
+            fFmpegBuilder.addInput(file);
+            fFmpegBuilder.overrideOutputFiles(true);
+            fFmpegBuilder.setVerbosity(FFmpegBuilder.Verbosity.ERROR);
+            FFmpegOutputBuilder fFmpegOutputBuilder = fFmpegBuilder.addOutput(outFile+outName+outFormat).setVideoCodec(LiveConfig.getVideoCodec());
+//            fFmpegBuilder.addExtraArgs(new String[]{"-bufsize",""},{"-minrate",""},{"-maxrate",""});
+            if(LiveConfig.isZoom() && !LiveConfig.isRelove()) {
+                if (stream.width > stream.height && stream.width > LiveConfig.getZoomSize()) {
+                    //如果宽度比高度大且又大于960的情况下,以宽度为960来缩放
+                    Integer width = new Double(stream.height / (stream.width * 1.00 / LiveConfig.getZoomSize())).intValue();
+                    ffmpegFormart = String.format("scale=%d:%d", LiveConfig.getZoomSize(), width % 2 == 0 ? width : width + 1);
+                } else if (stream.width < stream.height && stream.height > LiveConfig.getZoomSize()) {
+                    //如果高度比宽度大且又大于540,以高度540来缩放
+                    Integer height = new Double(stream.width / (stream.height * 1.00 / LiveConfig.getZoomSize())).intValue();
+                    ffmpegFormart = String.format("scale=%d:%d", height % 2 == 0 ? height : height + 1, LiveConfig.getZoomSize());
+                } else {
+                    ffmpegFormart = String.format("scale=%d:%d",stream.width,stream.height);
+                }
+                fFmpegOutputBuilder.setVideoFrameRate((stream.r_frame_rate.doubleValue()>LiveConfig.getFrameRate())?LiveConfig.getFrameRate():stream.r_frame_rate.doubleValue())
+                      //  .setVideoBitRate(stream.bit_rate > Long.valueOf(LiveConfig.getVideoRate())?LiveConfig.getVideoRate():stream.bit_rate)
+                        .setVideoFilter(ffmpegFormart)
+                        .disableSubtitle()
+                        .setAudioChannels(LiveConfig.getAudioChannels())
+                        .setAudioCodec(LiveConfig.getAudioCodec())
+                        .setAudioSampleRate(LiveConfig.getSampling())
+                        .setAudioBitRate(LiveConfig.getAudioRate())
+                        .setStrict(FFmpegBuilder.Strict.EXPERIMENTAL); // Allow FFmpeg to use experimental specs
+            }else if(LiveConfig.isRelove() && !LiveConfig.isZoom()){
+                String[] reloves = LiveConfig.getReloveSize().split("x");
+                fFmpegOutputBuilder.setVideoFrameRate((stream.r_frame_rate.doubleValue()>LiveConfig.getFrameRate())?LiveConfig.getFrameRate():stream.r_frame_rate.doubleValue())
+                     //   .setVideoBitRate(stream.bit_rate>Long.valueOf(LiveConfig.getVideoRate())?LiveConfig.getVideoRate():stream.bit_rate)
+                        .setVideoHeight(Integer.valueOf(reloves[0]))
+                        .setVideoWidth(Integer.valueOf(reloves[1]))
+                        .disableSubtitle()
+                        .setAudioChannels(LiveConfig.getAudioChannels())
+                        .setAudioCodec(LiveConfig.getAudioCodec())
+                        .setAudioSampleRate(LiveConfig.getSampling())
+                        .setAudioBitRate(LiveConfig.getAudioRate())
+                        .setStrict(FFmpegBuilder.Strict.EXPERIMENTAL); // Allow FFmpeg to use experimental specs
+            }else {
+                fFmpegOutputBuilder.setVideoFrameRate((stream.r_frame_rate.doubleValue()>LiveConfig.getFrameRate())?LiveConfig.getFrameRate():stream.r_frame_rate.doubleValue())
+                     //   .setVideoBitRate(stream.bit_rate>Long.valueOf(LiveConfig.getVideoRate())?LiveConfig.getVideoRate():stream.bit_rate)
+                        .disableSubtitle()
+                        .setAudioChannels(LiveConfig.getAudioChannels())
+                        .setAudioCodec(LiveConfig.getAudioCodec())
+                        .setAudioSampleRate(LiveConfig.getSampling())
+                        .setAudioBitRate(LiveConfig.getAudioRate())
+                        .setStrict(FFmpegBuilder.Strict.EXPERIMENTAL); // Allow FFmpeg to use experimental specs
+            }
+            if (type.equals(CONVERTTYPE.M3U8.getType())){
+                fFmpegOutputBuilder.setFormat(LiveConfig.getFormat());
+                fFmpegOutputBuilder.addExtraArgs("-hls_wrap", LiveConfig.getHlsWrap(), "-hls_time", LiveConfig.getHlsTime(), "-hls_list_size",LiveConfig.getHlsListSize());
+            }
+            fFmpegOutputBuilder.done();
+            FFmpegExecutor executor = new FFmpegExecutor(new FFmpeg(LiveConfig.getFfmpegPath()));
+            executor.createJob(fFmpegBuilder, new ProgressListener() {
+                @Override
+                public void progress(Progress progress) {
+                    new ProcessListNow().progress(progress,probeResult,type,key);
+                }
+            }).run();
+            if (LiveConfig.isSourceDelete()) {
+                new File(file).delete();
+            }
+            TbMedia media = new TbMedia();
+            media.setId(key);
+            media.setAudio_codec(LiveConfig.getAudioCodec());
+            media.setCodec(LiveConfig.getVideoCodec());
+            media.setRate(LiveConfig.getFrameRate().toString());
+            if (type.equals(CONVERTTYPE.TS.getType())) {
+                media.setRtsp(key + LiveConfig.getTSPath() + outName+outFormat);
+            }else {
+                media.setM3u8(key + LiveConfig.getM3u8Path() + outName+outFormat);
+            }
+            return media;
+            // Or run a two-pass encode (which is better quality at the cost of being slower)
+//            executor.createTwoPassJob(fFmpegBuilder).run();
+        }catch (Exception e){
+            if(e.getMessage() != null && e.getMessage().contains("non-zero"))
+                throw new BaseException(ResultTemplate.error(1214,"ffmpeg convert params error!plase check configuration parameters and file path!"));
+            else
+                throw new BaseException(ResultTemplate.error(1215,"ffmpeg convert fail! plase check ffmpeg path or file and file path!"));
+        }
+    }
+
+    /*
+    * 封面截取
+    * */
+    public static TbMedia processImg(String file,String outFile ,String outName,String cutTime,Long key) {
+        FfmpegUtils.checkFile(file,outFile);
+        try {
+            FFprobe fFprobe = new FFprobe(LiveConfig.getFfprobePath());
+            FFmpegProbeResult probeResult = fFprobe.probe(file);
+            FFmpegStream stream = probeResult.getStreams().get(0).width == 0 ?probeResult.getStreams().get(1): probeResult.getStreams().get(0) ;
+            String ffmpegFormart = "";
+            FFmpegBuilder fFmpegBuilder = new FFmpegBuilder();
+            fFmpegBuilder.addInput(file);
+            fFmpegBuilder.overrideOutputFiles(true);
+            fFmpegBuilder.setVerbosity(FFmpegBuilder.Verbosity.ERROR);
+            FFmpegOutputBuilder fFmpegOutputBuilder = fFmpegBuilder.addOutput(outFile+outName+CONVERTTYPE.THUMG.getFormat());
+//            fFmpegBuilder.addExtraArgs(new String[]{"-bufsize",""},{"-minrate",""},{"-maxrate",""});
+            String ss = LiveConfig.getCoverSize();
+            if(LiveConfig.getCoverSize().isEmpty() || LiveConfig.getCoverSize() == null) {
+                if (stream.width > stream.height && stream.width > LiveConfig.getZoomSize()) {
+                    //如果宽度比高度大且又大于960的情况下,以宽度为960来缩放
+                    Integer width = new Double(stream.height / (stream.width * 1.00 / LiveConfig.getZoomSize())).intValue();
+                    ffmpegFormart = String.format("%dx%d", LiveConfig.getZoomSize(), width % 2 == 0 ? width : width - 1);
+                } else if (stream.width < stream.height && stream.height > LiveConfig.getZoomSize()) {
+                    //如果高度比宽度大且又大于540,以高度540来缩放
+                    Integer height = new Double(stream.width / (stream.height * 1.00 / LiveConfig.getZoomSize())).intValue();
+                    ffmpegFormart = String.format("%dx%d", height % 2 == 0 ? height : height - 1, LiveConfig.getZoomSize());
+                }
+                else {
+                    ffmpegFormart = String.format("%dx%d",stream.width,stream.height);
+                }
+            }else {
+                ffmpegFormart = LiveConfig.getCoverSize();
+            }
+            fFmpegOutputBuilder.setFormat("image2");
+            fFmpegOutputBuilder.addExtraArgs("-t",LiveConfig.getCoverT(),"-ss",cutTime,"-s",ffmpegFormart);
+            fFmpegOutputBuilder.done();
+            FFmpegExecutor executor = new FFmpegExecutor(new FFmpeg(LiveConfig.getFfmpegPath()));
+            executor.createJob(fFmpegBuilder).run();
+            TbMedia media = new TbMedia();
+            media.setId(key);
+            media.setThumg(key+LiveConfig.getCoverPath()+outName+CONVERTTYPE.THUMG.getFormat());
+            return media;
+            // Or run a two-pass encode (which is better quality at the cost of being slower)
+//            executor.createTwoPassJob(fFmpegBuilder).run();
+        }catch (Exception e){
+            throw new BaseException(ResultTemplate.error(1210,"please check cut time!"));
+        }
+    }
+
+    /*
+     * 音频转换
+     * */
+    public static TbMedia convertMusic(String file,String outFile ,String outName,Long key) {
+        FfmpegUtils.checkFile(file,outFile);
+        try {
+            FFmpegBuilder fFmpegBuilder = new FFmpegBuilder();
+            fFmpegBuilder.addInput(file);
+            fFmpegBuilder.overrideOutputFiles(true);
+            fFmpegBuilder.setVerbosity(FFmpegBuilder.Verbosity.ERROR);
+            FFmpegOutputBuilder fFmpegOutputBuilder = fFmpegBuilder.addOutput(outFile+outName+CONVERTTYPE.AMR.getFormat());
+            fFmpegOutputBuilder.setAudioBitRate(LiveConfig.getAudioRate())
+                    .setAudioSampleRate(LiveConfig.getSampling())
+//                    .setAudioCodec(LiveConfig.getAudioCodec())
+                    .setAudioChannels(LiveConfig.getAudioChannels())
+                    .done();
+            FFmpegExecutor executor = new FFmpegExecutor(new FFmpeg(LiveConfig.getFfmpegPath()));
+            executor.createJob(fFmpegBuilder).run();
+            TbMedia media = new TbMedia();
+            media.setId(key);
+            media.setAmr(key+LiveConfig.getMusicPath()+outName+CONVERTTYPE.AMR.getFormat());
+            return media;
+            // Or run a two-pass encode (which is better quality at the cost of being slower)
+//            executor.createTwoPassJob(fFmpegBuilder).run();
+        }catch (Exception e){
+            throw new BaseException(ResultTemplate.error(1210,"please cut time!"));
+        }
+    }
+
+    private static void checkFile(String file ,String outFile){
+        if(StringUtils.isEmpty(file)){
+            throw new BaseException(ResultTemplate.error(4111,"The file to be converted is empty!"));
+        }
+        File fileSource = new File(file);
+        if (!fileSource.exists()) {
+            throw new BaseException(ResultTemplate.error(4112,"The file to be converted does not exist!"));
+        }
+        File dirChecksum = new File(outFile);
+        if  (!dirChecksum.exists()  && !dirChecksum.isDirectory()) {
+            dirChecksum.mkdirs();
+        }
+    }
+
+    public enum CONVERTTYPE{
+        TS(0,".ts","rtsp://","直播流文件"),
+        M3U8(1,".m3u8","http://","视频切片文件"),
+        AMR(1,".amr","http://","音频切片文件"),
+        THUMG(1,".jpg","http://","视频缩略图");
+
+        private Integer type;
+        private String format;
+        private String agre;
+        private String remark;
+
+        CONVERTTYPE(Integer type,String format,String agre,String remark){
+            this.type = type;
+            this.format = format;
+            this.agre = agre;
+            this.remark = remark;
+        }
+
+        public Integer getType(){
+            return this.type;
+        }
+
+        public String getFormat(){
+            return this.format;
+        }
+
+        public String getAgre(){
+            return this.agre;
+        }
+    }
+}

+ 55 - 0
src/main/java/com/shs/official/common/utils/ffmpeg/ProcessListNow.java

@@ -0,0 +1,55 @@
+package com.shs.official.common.utils.ffmpeg;
+
+import com.alibaba.fastjson.JSON;
+import com.shs.official.common.utils.SpringUtils;
+import com.shs.official.common.utils.redis.RedisUtils;
+import net.bramp.ffmpeg.FFmpegUtils;
+import net.bramp.ffmpeg.probe.FFmpegProbeResult;
+import net.bramp.ffmpeg.progress.Progress;
+import net.bramp.ffmpeg.progress.ProgressListener;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class ProcessListNow implements ProgressListener {
+
+    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ProcessListNow.class);
+
+    private RedisUtils redisUtils = SpringUtils.getBean(RedisUtils.class);
+
+    @Override
+    public void progress(Progress progress){}
+
+    public void progress(Progress progress,FFmpegProbeResult probeResult, Integer type,Long key){
+        final double duration_ns = probeResult.getFormat().duration * TimeUnit.SECONDS.toNanos(1);
+        double percentage = progress.out_time_ns / duration_ns;
+        // Print out interesting information about the progress
+        String info = String.format(
+                "[%.0f%%] status:%s frame:%d time:%s ms fps:%.0f speed:%.2fx key:%s",
+                percentage * 100,
+                progress.status,
+                progress.frame,
+                FFmpegUtils.toTimecode(progress.out_time_ns, TimeUnit.NANOSECONDS),
+                progress.fps.doubleValue(),
+                progress.speed,
+                key
+        );
+        logger.info(info);
+        Map res = new HashMap();
+        res.put("speed",Math.round(percentage * 100));
+        res.put("info",info);
+        if (type.equals(FfmpegUtils.CONVERTTYPE.TS.getType()))
+            redisUtils.set(String.valueOf(key)+FfmpegUtils.CONVERTTYPE.TS.getFormat(),JSON.toJSONString(res));
+        else
+            redisUtils.set(String.valueOf(key)+FfmpegUtils.CONVERTTYPE.M3U8.getFormat(),JSON.toJSONString(res));
+        if(Math.round(percentage * 100) == 100) {
+            if (type.equals(FfmpegUtils.CONVERTTYPE.TS.getType()))
+                redisUtils.del(String.valueOf(key)+FfmpegUtils.CONVERTTYPE.TS.getFormat());
+            else
+                redisUtils.del(String.valueOf(key)+FfmpegUtils.CONVERTTYPE.M3U8.getFormat());
+            logger.info("ConvertSuccess!");
+        }
+    }
+}

+ 274 - 0
src/main/java/com/shs/official/common/utils/file/FileUploadUtils.java

@@ -0,0 +1,274 @@
+package com.shs.official.common.utils.file;
+
+import com.shs.official.common.LiveConfig;
+import com.shs.official.common.exception.file.FileNameLengthLimitExceededException;
+import com.shs.official.common.exception.file.FileSizeLimitExceededException;
+import com.shs.official.common.exception.file.InvalidExtensionException;
+import com.shs.official.common.utils.StringUtils;
+import com.shs.official.common.utils.security.Md5Utils;
+import com.shs.official.service.media.MediaService;
+import org.apache.commons.io.FilenameUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * 文件上传工具类
+ *
+ * @author shs
+ */
+public class FileUploadUtils {
+
+    /**
+     * 默认大小 300M
+     */
+    public static final long DEFAULT_MAX_SIZE = 1024 * 1024 * 1024;
+
+    /**
+     * 默认的文件名最大长度 100
+     */
+    public static final int DEFAULT_FILE_NAME_LENGTH = 100;
+
+    /**
+     * 默认上传的地址
+     */
+    private static String defaultBaseDir = LiveConfig.getSourceProfile();
+
+    private static int counter = 0;
+
+    public static void setDefaultBaseDir(String defaultBaseDir)
+    {
+        FileUploadUtils.defaultBaseDir = defaultBaseDir;
+    }
+
+    public static String getDefaultBaseDir()
+    {
+        return defaultBaseDir;
+    }
+
+    /**
+     * 以默认配置进行文件上传
+     *
+     * @param file 上传的文件
+     * @return 文件名称
+     * @throws Exception
+     */
+    public static final String upload(MultipartFile file) throws IOException
+    {
+        try
+        {
+            return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+        }
+        catch (Exception e)
+        {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 根据文件路径上传
+     *
+     * @param baseDir 相对应用的基目录
+     * @param file 上传的文件
+     * @return 文件名称
+     * @throws IOException
+     */
+    public static final String upload(String baseDir, MultipartFile file) throws IOException
+    {
+        try
+        {
+            return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+        }
+        catch (Exception e)
+        {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 文件上传
+     *
+     * @param baseDir 相对应用的基目录
+     * @param file 上传的文件
+     * @param allowedExtension 上传文件类型
+     * @return 返回上传成功的文件名
+     * @throws FileSizeLimitExceededException 如果超出最大大小
+     * @throws FileNameLengthLimitExceededException 文件名太长
+     * @throws IOException 比如读写文件出错时
+     * @throws InvalidExtensionException 文件校验异常
+     */
+    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
+            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
+            InvalidExtensionException
+    {
+        int fileNamelength = file.getOriginalFilename().length();
+        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
+        {
+            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
+        }
+
+        assertAllowed(file, allowedExtension);
+
+        String fileName = extractFilename(file);
+
+        File desc = getAbsoluteFile(baseDir, fileName);
+        file.transferTo(desc);
+//        String pathFileName = getPathFileName(baseDir, fileName);
+        return fileName;
+    }
+
+    /**
+     * 编码文件名
+     */
+    public static final String extractFilename(MultipartFile file)
+    {
+        String fileName = file.getOriginalFilename();
+        String extension = getExtension(file);
+        fileName = encodingFilename(fileName) + "." + extension;
+        return fileName;
+    }
+
+    private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
+    {
+        File desc = new File(uploadDir + File.separator + fileName);
+
+        if (!desc.getParentFile().exists())
+        {
+            desc.getParentFile().mkdirs();
+        }
+        if (!desc.exists())
+        {
+            desc.createNewFile();
+        }
+        return desc;
+    }
+
+    private static final String getPathFileName(String uploadDir, String fileName) throws IOException
+    {
+        int dirLastIndex = LiveConfig.getProfile().length() + 1;
+        String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
+        String pathFileName = "/" + currentDir + "/" + fileName;
+        // Constants.RESOURCE_PREFIX +
+        return pathFileName;
+    }
+
+    /**
+     * 编码文件名
+     */
+    private static final String encodingFilename(String fileName)
+    {
+        fileName = fileName.replace("_", " ");
+        fileName = Md5Utils.hash(fileName + System.nanoTime() + counter++);
+        return fileName;
+    }
+
+    /**
+     * 文件大小校验
+     *
+     * @param file 上传的文件
+     * @return
+     * @throws FileSizeLimitExceededException 如果超出最大大小
+     * @throws InvalidExtensionException
+     */
+    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
+            throws FileSizeLimitExceededException, InvalidExtensionException
+    {
+        long size = file.getSize();
+        if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE)
+        {
+            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
+        }
+
+        String fileName = file.getOriginalFilename();
+        String extension = getExtension(file);
+        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
+        {
+            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
+            {
+                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
+                        fileName);
+            }
+            else if (allowedExtension == MimeTypeUtils.MUSIC_EXTENSION)
+            {
+                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
+                        fileName);
+            }
+            else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
+            {
+                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
+                        fileName);
+            }
+            else
+            {
+                throw new InvalidExtensionException(allowedExtension, extension, fileName);
+            }
+        }
+
+    }
+
+    /**
+     * 判断MIME类型是否是允许的MIME类型
+     *
+     * @param extension
+     * @param allowedExtension
+     * @return
+     */
+    public static final boolean isAllowedExtension(String extension, String[] allowedExtension)
+    {
+        for (String str : allowedExtension)
+        {
+            if (str.equalsIgnoreCase(extension))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 获取文件名的后缀
+     *
+     * @param file 表单文件
+     * @return 后缀名
+     */
+    public static final String getExtension(MultipartFile file)
+    {
+        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+        if (StringUtils.isEmpty(extension))
+        {
+            extension = MimeTypeUtils.getExtension(file.getContentType());
+        }
+        return extension;
+    }
+
+    /**
+     * 文件类型校验
+     *
+     * @param file 上传的文件
+     * @return
+     * @throws FileSizeLimitExceededException 如果超出最大大小
+     * @throws InvalidExtensionException
+     */
+    public static final Integer assertAllowedExtrnsion(MultipartFile file)
+            throws FileSizeLimitExceededException
+    {
+        String extension = getExtension(file);
+        if (isAllowedExtension(extension,MimeTypeUtils.IMAGE_EXTENSION))
+        {
+            return MediaService.TYPE.UNKNOW.getType();
+        }
+        else if (isAllowedExtension(extension,MimeTypeUtils.MUSIC_EXTENSION))
+        {
+            return MediaService.TYPE.AUDIO.getType();
+        }
+        else if (isAllowedExtension(extension,MimeTypeUtils.MEDIA_EXTENSION))
+        {
+            return MediaService.TYPE.VIDEO.getType();
+        }
+        else
+        {
+            return MediaService.TYPE.UNKNOW.getType();
+        }
+    }
+}

+ 137 - 0
src/main/java/com/shs/official/common/utils/file/FileUtils.java

@@ -0,0 +1,137 @@
+package com.shs.official.common.utils.file;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+import java.net.URLEncoder;
+
+/**
+ * 文件处理工具类
+ *
+ * @author shs
+ */
+public class FileUtils
+{
+    public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
+
+    /**
+     * 输出指定文件的byte数组
+     *
+     * @param filePath 文件路径
+     * @param os 输出流
+     * @return
+     */
+    public static void writeBytes(String filePath, OutputStream os) throws IOException
+    {
+        FileInputStream fis = null;
+        try
+        {
+            File file = new File(filePath);
+            if (!file.exists())
+            {
+                throw new FileNotFoundException(filePath);
+            }
+            fis = new FileInputStream(file);
+            byte[] b = new byte[1024];
+            int length;
+            while ((length = fis.read(b)) > 0)
+            {
+                os.write(b, 0, length);
+            }
+        }
+        catch (IOException e)
+        {
+            throw e;
+        }
+        finally
+        {
+            if (os != null)
+            {
+                try
+                {
+                    os.close();
+                }
+                catch (IOException e1)
+                {
+                    e1.printStackTrace();
+                }
+            }
+            if (fis != null)
+            {
+                try
+                {
+                    fis.close();
+                }
+                catch (IOException e1)
+                {
+                    e1.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     * 删除文件
+     *
+     * @param filePath 文件
+     * @return
+     */
+    public static boolean deleteFile(String filePath)
+    {
+        boolean flag = false;
+        File file = new File(filePath);
+        // 路径为文件且不为空则进行删除
+        if (file.isFile() && file.exists())
+        {
+            file.delete();
+            flag = true;
+        }
+        return flag;
+    }
+
+    /**
+     * 文件名称验证
+     *
+     * @param filename 文件名称
+     * @return true 正常 false 非法
+     */
+    public static boolean isValidFilename(String filename)
+    {
+        return filename.matches(FILENAME_PATTERN);
+    }
+
+    /**
+     * 下载文件名重新编码
+     *
+     * @param request 请求对象
+     * @param fileName 文件名
+     * @return 编码后的文件名
+     */
+    public static String setFileDownloadHeader(HttpServletRequest request, String fileName)
+            throws UnsupportedEncodingException
+    {
+        final String agent = request.getHeader("USER-AGENT");
+        String filename = fileName;
+        if (agent.contains("MSIE"))
+        {
+            // IE浏览器
+            filename = URLEncoder.encode(filename, "utf-8");
+            filename = filename.replace("+", " ");
+        }
+        else if (agent.contains("Firefox"))
+        {
+            // 火狐浏览器
+            filename = new String(fileName.getBytes(), "ISO8859-1");
+        }
+        else if (agent.contains("Chrome"))
+        {
+            // google浏览器
+            filename = URLEncoder.encode(filename, "utf-8");
+        }
+        else
+        {
+            // 其它浏览器
+            filename = URLEncoder.encode(filename, "utf-8");
+        }
+        return filename;
+    }
+}

+ 39 - 0
src/main/java/com/shs/official/common/utils/file/MD5Checksum.java

@@ -0,0 +1,39 @@
+package com.shs.official.common.utils.file;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.MessageDigest;
+
+/*
+* 数据流Md5获取及校验
+* */
+public class MD5Checksum {
+
+    public static byte[] createChecksum(InputStream fis) throws Exception {
+        byte[] buffer = new byte[1024];
+        MessageDigest complete = MessageDigest.getInstance("MD5");
+        int numRead;
+
+        do {
+            numRead = fis.read(buffer);
+            if (numRead > 0) {
+                complete.update(buffer, 0, numRead);
+            }
+        } while (numRead != -1);
+
+        fis.close();
+        return complete.digest();
+    }
+
+    // see this How-to for a faster way to convert
+    // a byte array to a HEX string
+    public static String getMD5Checksum(InputStream files) throws Exception {
+        byte[] b = createChecksum(files);
+        String result ="";
+
+        for (int i=0; i < b.length; i++) {
+            result += Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
+        }
+        return result;
+    }
+}

+ 56 - 0
src/main/java/com/shs/official/common/utils/file/MimeTypeUtils.java

@@ -0,0 +1,56 @@
+package com.shs.official.common.utils.file;
+
+/**
+ * 媒体类型工具类
+ *
+ * @author shs
+ */
+public class MimeTypeUtils
+{
+    public static final String IMAGE_PNG = "image/png";
+
+    public static final String IMAGE_JPG = "image/jpg";
+
+    public static final String IMAGE_JPEG = "image/jpeg";
+
+    public static final String IMAGE_BMP = "image/bmp";
+
+    public static final String IMAGE_GIF = "image/gif";
+
+    public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };
+
+    public static final String[] MUSIC_EXTENSION = { "acc", "ogg", "mp3", "wma", "ra", "rm", "vqf", "amr", "ape", "flac"};
+
+    public static final String[] MEDIA_EXTENSION = { "ts", "mkv", "webm", "wav", "wmv", "mov", "avi", "mpg",
+            "mp4", "3gp" };
+
+    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
+            // 图片
+            "bmp", "gif", "jpg", "jpeg", "png",
+            //音频
+            "acc", "ogg", "mp3", "wav", "wma", "wmv", "avi", "mpg", "rm",
+            //视频
+            "ts", "mkv", "webm", "wav", "wma", "wmv", "mov", "avi", "mpg",
+            "mp4", "3gp"
+    };
+
+    public static String getExtension(String prefix)
+    {
+        switch (prefix)
+        {
+            case IMAGE_PNG:
+                return "png";
+            case IMAGE_JPG:
+                return "jpg";
+            case IMAGE_JPEG:
+                return "jpeg";
+            case IMAGE_BMP:
+                return "bmp";
+            case IMAGE_GIF:
+                return "gif";
+            default:
+                return "";
+        }
+    }
+}
+

+ 152 - 0
src/main/java/com/shs/official/common/utils/html/EscapeUtil.java

@@ -0,0 +1,152 @@
+package com.shs.official.common.utils.html;
+
+import com.shs.official.common.utils.StringUtils;
+
+/**
+ * 转义和反转义工具类
+ *
+ * @author shs
+ */
+public class EscapeUtil
+{
+    public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
+
+    private static final char[][] TEXT = new char[64][];
+
+    static
+    {
+        for (int i = 0; i < 64; i++)
+        {
+            TEXT[i] = new char[] { (char) i };
+        }
+
+        // special HTML characters
+        TEXT['\''] = "&#039;".toCharArray(); // 单引号
+        TEXT['"'] = "&#34;".toCharArray(); // 单引号
+        TEXT['&'] = "&#38;".toCharArray(); // &符
+        TEXT['<'] = "&#60;".toCharArray(); // 小于号
+        TEXT['>'] = "&#62;".toCharArray(); // 大于号
+    }
+
+    /**
+     * 转义文本中的HTML字符为安全的字符
+     *
+     * @param text 被转义的文本
+     * @return 转义后的文本
+     */
+    public static String escape(String text)
+    {
+        return encode(text);
+    }
+
+    /**
+     * 还原被转义的HTML特殊字符
+     *
+     * @param content 包含转义符的HTML内容
+     * @return 转换后的字符串
+     */
+    public static String unescape(String content)
+    {
+        return decode(content);
+    }
+
+    /**
+     * 清除所有HTML标签,但是不删除标签内的内容
+     *
+     * @param content 文本
+     * @return 清除标签后的文本
+     */
+    public static String clean(String content)
+    {
+        return new HTMLFilter().filter(content);
+    }
+
+    /**
+     * Escape编码
+     *
+     * @param text 被编码的文本
+     * @return 编码后的字符
+     */
+    private static String encode(String text)
+    {
+        int len;
+        if ((text == null) || ((len = text.length()) == 0))
+        {
+            return StringUtils.EMPTY;
+        }
+        StringBuilder buffer = new StringBuilder(len + (len >> 2));
+        char c;
+        for (int i = 0; i < len; i++)
+        {
+            c = text.charAt(i);
+            if (c < 64)
+            {
+                buffer.append(TEXT[c]);
+            }
+            else
+            {
+                buffer.append(c);
+            }
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Escape解码
+     *
+     * @param content 被转义的内容
+     * @return 解码后的字符串
+     */
+    public static String decode(String content)
+    {
+        if (StringUtils.isEmpty(content))
+        {
+            return content;
+        }
+
+        StringBuilder tmp = new StringBuilder(content.length());
+        int lastPos = 0, pos = 0;
+        char ch;
+        while (lastPos < content.length())
+        {
+            pos = content.indexOf("%", lastPos);
+            if (pos == lastPos)
+            {
+                if (content.charAt(pos + 1) == 'u')
+                {
+                    ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
+                    tmp.append(ch);
+                    lastPos = pos + 6;
+                }
+                else
+                {
+                    ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
+                    tmp.append(ch);
+                    lastPos = pos + 3;
+                }
+            }
+            else
+            {
+                if (pos == -1)
+                {
+                    tmp.append(content.substring(lastPos));
+                    lastPos = content.length();
+                }
+                else
+                {
+                    tmp.append(content.substring(lastPos, pos));
+                    lastPos = pos;
+                }
+            }
+        }
+        return tmp.toString();
+    }
+
+    public static void main(String[] args)
+    {
+        String html = "alert('11111');";
+        System.out.println(EscapeUtil.clean(html));
+        System.out.println(EscapeUtil.escape(html));
+        System.out.println(EscapeUtil.unescape(html));
+    }
+}

+ 565 - 0
src/main/java/com/shs/official/common/utils/html/HTMLFilter.java

@@ -0,0 +1,565 @@
+package com.shs.official.common.utils.html;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * HTML过滤器,用于去除XSS漏洞隐患。
+ *
+ * @author shs
+ */
+public final class HTMLFilter
+{
+    /**
+     * regex flag union representing /si modifiers in php
+     **/
+    private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
+    private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
+    private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
+    private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
+    private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
+    private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
+    private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
+    private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
+    private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
+    private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
+    private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
+    private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
+    private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
+    private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
+    private static final Pattern P_END_ARROW = Pattern.compile("^>");
+    private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
+    private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
+    private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
+    private static final Pattern P_STRAY_RIshsT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
+    private static final Pattern P_AMP = Pattern.compile("&");
+    private static final Pattern P_QUOTE = Pattern.compile("\"");
+    private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
+    private static final Pattern P_RIshsT_ARROW = Pattern.compile(">");
+    private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
+
+    // @xxx could grow large... maybe use sesat's ReferenceMap
+    private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
+    private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
+
+    /**
+     * set of allowed html elements, along with allowed attributes for each element
+     **/
+    private final Map<String, List<String>> vAllowed;
+    /**
+     * counts of open tags for each (allowable) html element
+     **/
+    private final Map<String, Integer> vTagCounts = new HashMap<>();
+
+    /**
+     * html elements which must always be self-closing (e.g. "<img />")
+     **/
+    private final String[] vSelfClosingTags;
+    /**
+     * html elements which must always have separate opening and closing tags (e.g. "<b></b>")
+     **/
+    private final String[] vNeedClosingTags;
+    /**
+     * set of disallowed html elements
+     **/
+    private final String[] vDisallowed;
+    /**
+     * attributes which should be checked for valid protocols
+     **/
+    private final String[] vProtocolAtts;
+    /**
+     * allowed protocols
+     **/
+    private final String[] vAllowedProtocols;
+    /**
+     * tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
+     **/
+    private final String[] vRemoveBlanks;
+    /**
+     * entities allowed within html markup
+     **/
+    private final String[] vAllowedEntities;
+    /**
+     * flag determining whether comments are allowed in input String.
+     */
+    private final boolean stripComment;
+    private final boolean encodeQuotes;
+    /**
+     * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>"
+     * becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.
+     */
+    private final boolean alwaysMakeTags;
+
+    /**
+     * Default constructor.
+     */
+    public HTMLFilter()
+    {
+        vAllowed = new HashMap<>();
+
+        final ArrayList<String> a_atts = new ArrayList<>();
+        a_atts.add("href");
+        a_atts.add("target");
+        vAllowed.put("a", a_atts);
+
+        final ArrayList<String> img_atts = new ArrayList<>();
+        img_atts.add("src");
+        img_atts.add("width");
+        img_atts.add("heishst");
+        img_atts.add("alt");
+        vAllowed.put("img", img_atts);
+
+        final ArrayList<String> no_atts = new ArrayList<>();
+        vAllowed.put("b", no_atts);
+        vAllowed.put("strong", no_atts);
+        vAllowed.put("i", no_atts);
+        vAllowed.put("em", no_atts);
+
+        vSelfClosingTags = new String[] { "img" };
+        vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" };
+        vDisallowed = new String[] {};
+        vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp.
+        vProtocolAtts = new String[] { "src", "href" };
+        vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" };
+        vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" };
+        stripComment = true;
+        encodeQuotes = true;
+        alwaysMakeTags = true;
+    }
+
+    /**
+     * Map-parameter configurable constructor.
+     *
+     * @param conf map containing configuration. keys match field names.
+     */
+    @SuppressWarnings("unchecked")
+    public HTMLFilter(final Map<String, Object> conf)
+    {
+
+        assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
+        assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
+        assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
+        assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
+        assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
+        assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
+        assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
+        assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
+
+        vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
+        vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
+        vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
+        vDisallowed = (String[]) conf.get("vDisallowed");
+        vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
+        vProtocolAtts = (String[]) conf.get("vProtocolAtts");
+        vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
+        vAllowedEntities = (String[]) conf.get("vAllowedEntities");
+        stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
+        encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
+        alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
+    }
+
+    private void reset()
+    {
+        vTagCounts.clear();
+    }
+
+    // ---------------------------------------------------------------
+    // my versions of some PHP library functions
+    public static String chr(final int decimal)
+    {
+        return String.valueOf((char) decimal);
+    }
+
+    public static String htmlSpecialChars(final String s)
+    {
+        String result = s;
+        result = regexReplace(P_AMP, "&amp;", result);
+        result = regexReplace(P_QUOTE, "&quot;", result);
+        result = regexReplace(P_LEFT_ARROW, "&lt;", result);
+        result = regexReplace(P_RIshsT_ARROW, "&gt;", result);
+        return result;
+    }
+
+    // ---------------------------------------------------------------
+
+    /**
+     * given a user submitted input String, filter out any invalid or restricted html.
+     *
+     * @param input text (i.e. submitted by a user) than may contain html
+     * @return "clean" version of input, with only valid, whitelisted html elements allowed
+     */
+    public String filter(final String input)
+    {
+        reset();
+        String s = input;
+
+        s = escapeComments(s);
+
+        s = balanceHTML(s);
+
+        s = checkTags(s);
+
+        s = processRemoveBlanks(s);
+
+        s = validateEntities(s);
+
+        return s;
+    }
+
+    public boolean isAlwaysMakeTags()
+    {
+        return alwaysMakeTags;
+    }
+
+    public boolean isStripComments()
+    {
+        return stripComment;
+    }
+
+    private String escapeComments(final String s)
+    {
+        final Matcher m = P_COMMENTS.matcher(s);
+        final StringBuffer buf = new StringBuffer();
+        if (m.find())
+        {
+            final String match = m.group(1); // (.*?)
+            m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
+        }
+        m.appendTail(buf);
+
+        return buf.toString();
+    }
+
+    private String balanceHTML(String s)
+    {
+        if (alwaysMakeTags)
+        {
+            //
+            // try and form html
+            //
+            s = regexReplace(P_END_ARROW, "", s);
+            s = regexReplace(P_BODY_TO_END, "<$1>", s);
+            s = regexReplace(P_XML_CONTENT, "$1<$2", s);
+
+        }
+        else
+        {
+            //
+            // escape stray brackets
+            //
+            s = regexReplace(P_STRAY_LEFT_ARROW, "&lt;$1", s);
+            s = regexReplace(P_STRAY_RIshsT_ARROW, "$1$2&gt;<", s);
+
+            //
+            // the last regexp causes '<>' entities to appear
+            // (we need to do a lookahead assertion so that the last bracket can
+            // be used in the next pass of the regexp)
+            //
+            s = regexReplace(P_BOTH_ARROWS, "", s);
+        }
+
+        return s;
+    }
+
+    private String checkTags(String s)
+    {
+        Matcher m = P_TAGS.matcher(s);
+
+        final StringBuffer buf = new StringBuffer();
+        while (m.find())
+        {
+            String replaceStr = m.group(1);
+            replaceStr = processTag(replaceStr);
+            m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
+        }
+        m.appendTail(buf);
+
+        // these get tallied in processTag
+        // (remember to reset before subsequent calls to filter method)
+        final StringBuilder sBuilder = new StringBuilder(buf.toString());
+        for (String key : vTagCounts.keySet())
+        {
+            for (int ii = 0; ii < vTagCounts.get(key); ii++)
+            {
+                sBuilder.append("</").append(key).append(">");
+            }
+        }
+        s = sBuilder.toString();
+
+        return s;
+    }
+
+    private String processRemoveBlanks(final String s)
+    {
+        String result = s;
+        for (String tag : vRemoveBlanks)
+        {
+            if (!P_REMOVE_PAIR_BLANKS.containsKey(tag))
+            {
+                P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
+            }
+            result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
+            if (!P_REMOVE_SELF_BLANKS.containsKey(tag))
+            {
+                P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
+            }
+            result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
+        }
+
+        return result;
+    }
+
+    private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s)
+    {
+        Matcher m = regex_pattern.matcher(s);
+        return m.replaceAll(replacement);
+    }
+
+    private String processTag(final String s)
+    {
+        // ending tags
+        Matcher m = P_END_TAG.matcher(s);
+        if (m.find())
+        {
+            final String name = m.group(1).toLowerCase();
+            if (allowed(name))
+            {
+                if (false == inArray(name, vSelfClosingTags))
+                {
+                    if (vTagCounts.containsKey(name))
+                    {
+                        vTagCounts.put(name, vTagCounts.get(name) - 1);
+                        return "</" + name + ">";
+                    }
+                }
+            }
+        }
+
+        // starting tags
+        m = P_START_TAG.matcher(s);
+        if (m.find())
+        {
+            final String name = m.group(1).toLowerCase();
+            final String body = m.group(2);
+            String ending = m.group(3);
+
+            // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
+            if (allowed(name))
+            {
+                final StringBuilder params = new StringBuilder();
+
+                final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
+                final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
+                final List<String> paramNames = new ArrayList<>();
+                final List<String> paramValues = new ArrayList<>();
+                while (m2.find())
+                {
+                    paramNames.add(m2.group(1)); // ([a-z0-9]+)
+                    paramValues.add(m2.group(3)); // (.*?)
+                }
+                while (m3.find())
+                {
+                    paramNames.add(m3.group(1)); // ([a-z0-9]+)
+                    paramValues.add(m3.group(3)); // ([^\"\\s']+)
+                }
+
+                String paramName, paramValue;
+                for (int ii = 0; ii < paramNames.size(); ii++)
+                {
+                    paramName = paramNames.get(ii).toLowerCase();
+                    paramValue = paramValues.get(ii);
+
+                    // debug( "paramName='" + paramName + "'" );
+                    // debug( "paramValue='" + paramValue + "'" );
+                    // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
+
+                    if (allowedAttribute(name, paramName))
+                    {
+                        if (inArray(paramName, vProtocolAtts))
+                        {
+                            paramValue = processParamProtocol(paramValue);
+                        }
+                        params.append(' ').append(paramName).append("=\"").append(paramValue).append("\"");
+                    }
+                }
+
+                if (inArray(name, vSelfClosingTags))
+                {
+                    ending = " /";
+                }
+
+                if (inArray(name, vNeedClosingTags))
+                {
+                    ending = "";
+                }
+
+                if (ending == null || ending.length() < 1)
+                {
+                    if (vTagCounts.containsKey(name))
+                    {
+                        vTagCounts.put(name, vTagCounts.get(name) + 1);
+                    }
+                    else
+                    {
+                        vTagCounts.put(name, 1);
+                    }
+                }
+                else
+                {
+                    ending = " /";
+                }
+                return "<" + name + params + ending + ">";
+            }
+            else
+            {
+                return "";
+            }
+        }
+
+        // comments
+        m = P_COMMENT.matcher(s);
+        if (!stripComment && m.find())
+        {
+            return "<" + m.group() + ">";
+        }
+
+        return "";
+    }
+
+    private String processParamProtocol(String s)
+    {
+        s = decodeEntities(s);
+        final Matcher m = P_PROTOCOL.matcher(s);
+        if (m.find())
+        {
+            final String protocol = m.group(1);
+            if (!inArray(protocol, vAllowedProtocols))
+            {
+                // bad protocol, turn into local anchor link instead
+                s = "#" + s.substring(protocol.length() + 1);
+                if (s.startsWith("#//"))
+                {
+                    s = "#" + s.substring(3);
+                }
+            }
+        }
+
+        return s;
+    }
+
+    private String decodeEntities(String s)
+    {
+        StringBuffer buf = new StringBuffer();
+
+        Matcher m = P_ENTITY.matcher(s);
+        while (m.find())
+        {
+            final String match = m.group(1);
+            final int decimal = Integer.decode(match).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        buf = new StringBuffer();
+        m = P_ENTITY_UNICODE.matcher(s);
+        while (m.find())
+        {
+            final String match = m.group(1);
+            final int decimal = Integer.valueOf(match, 16).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        buf = new StringBuffer();
+        m = P_ENCODE.matcher(s);
+        while (m.find())
+        {
+            final String match = m.group(1);
+            final int decimal = Integer.valueOf(match, 16).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        s = validateEntities(s);
+        return s;
+    }
+
+    private String validateEntities(final String s)
+    {
+        StringBuffer buf = new StringBuffer();
+
+        // validate entities throushsout the string
+        Matcher m = P_VALID_ENTITIES.matcher(s);
+        while (m.find())
+        {
+            final String one = m.group(1); // ([^&;]*)
+            final String two = m.group(2); // (?=(;|&|$))
+            m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
+        }
+        m.appendTail(buf);
+
+        return encodeQuotes(buf.toString());
+    }
+
+    private String encodeQuotes(final String s)
+    {
+        if (encodeQuotes)
+        {
+            StringBuffer buf = new StringBuffer();
+            Matcher m = P_VALID_QUOTES.matcher(s);
+            while (m.find())
+            {
+                final String one = m.group(1); // (>|^)
+                final String two = m.group(2); // ([^<]+?)
+                final String three = m.group(3); // (<|$)
+                // 不替换双引号为&quot;,防止json格式无效 regexReplace(P_QUOTE, "&quot;", two)
+                m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
+            }
+            m.appendTail(buf);
+            return buf.toString();
+        }
+        else
+        {
+            return s;
+        }
+    }
+
+    private String checkEntity(final String preamble, final String term)
+    {
+
+        return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&amp;" + preamble;
+    }
+
+    private boolean isValidEntity(final String entity)
+    {
+        return inArray(entity, vAllowedEntities);
+    }
+
+    private static boolean inArray(final String s, final String[] array)
+    {
+        for (String item : array)
+        {
+            if (item != null && item.equals(s))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean allowed(final String name)
+    {
+        return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
+    }
+
+    private boolean allowedAttribute(final String name, final String paramName)
+    {
+        return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
+    }
+}

+ 100 - 0
src/main/java/com/shs/official/common/utils/http/HttpHelper.java

@@ -0,0 +1,100 @@
+package com.shs.official.common.utils.http;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+/**
+ * 通用http工具封装
+ *
+ * @author shs
+ */
+public class HttpHelper
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class);
+
+    public static String getBodyString(ServletRequest request)
+    {
+        StringBuilder sb = new StringBuilder();
+        BufferedReader reader = null;
+        try (InputStream inputStream = request.getInputStream())
+        {
+            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
+            String line = "";
+            while ((line = reader.readLine()) != null)
+            {
+                sb.append(line);
+            }
+        }
+        catch (IOException e)
+        {
+            LOGGER.warn("getBodyString出现问题!");
+        }
+        finally
+        {
+            if (reader != null)
+            {
+                try
+                {
+                    reader.close();
+                }
+                catch (IOException e)
+                {
+                    LOGGER.error(ExceptionUtils.getFullStackTrace(e));
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+
+    public static String getIpAddr(HttpServletRequest request) {
+        String ip = request.getHeader("x-forwarded-for");
+        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+        return ip;
+    }
+
+    /**
+
+     * 判断一个字符串是否为url
+
+     * @param str String 字符串
+
+     * @return boolean 是否为url
+
+     * @author peng1 chen
+
+     * **/
+
+    public static boolean isURL(String str){
+        //转换为小写
+        str = str.toLowerCase();
+        String regex = "^((https|http)?://)" //https、http
+        + "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" //ftp的user@
+                + "(([0-9]{1,3}\\.){3}[0-9]{1,3}" // IP形式的URL- 例如:199.194.52.184
+                + "|" // 允许IP和DOMAIN(域名)
+                + "([0-9a-z_!~*'()-]+\\.)*" // 域名- www.
+                + "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\." // 二级域名
+                + "[a-z]{2,6})" // first level domain- .com or .museum
+                + "(:[0-9]{1,5})?" // 端口号最大为65535,5位数
+                + "((/?)|" // a slash isn't required if there is no file name
+                + "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$";
+        return str.matches(regex);
+    }
+}

+ 537 - 0
src/main/java/com/shs/official/common/utils/redis/RedisUtils.java

@@ -0,0 +1,537 @@
+package com.shs.official.common.utils.redis;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+
+/*
+* Redis Utils
+* @auth shs
+* */
+@Component
+public class RedisUtils {
+
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+
+
+    // =============================common============================
+    /**
+     * 指定缓存失效时间
+     * @param key 键
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean expire(String key, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据key 获取过期时间
+     * @param key 键 不能为null
+     * @return 时间(秒) 返回0代表为永久有效
+     */
+    public long getExpire(String key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断key是否存在
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public boolean hasKey(String key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     * @param key 可以传一个值 或多个
+     */
+    @SuppressWarnings("unchecked")
+    public void del(String... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                redisTemplate.delete(key[0]);
+            } else {
+                redisTemplate.delete(CollectionUtils.arrayToList(key));
+            }
+        }
+    }
+
+    // ============================String=============================
+    /**
+     * 普通缓存获取
+     * @param key 键
+     * @return 值
+     */
+    public Object get(String key) {
+        return key == null ? null : redisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 普通缓存放入
+     * @param key 键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public boolean set(String key, Object value) {
+        try {
+            redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
+     * @return true成功 false 失败
+     */
+    public boolean set(String key, Object value, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 递增
+     * @param key 键
+     * @param delta 要增加几(大于0)
+     * @return
+     */
+    public long incr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递增因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+    /**
+     * 递减
+     * @param key 键
+     * @param delta 要减少几(小于0)
+     * @return
+     */
+    public long decr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递减因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, -delta);
+    }
+
+    // ================================Map=================================
+    /**
+     * HashGet
+     * @param key 键 不能为null
+     * @param item 项 不能为null
+     * @return 值
+     */
+    public Object hget(String key, String item) {
+        return redisTemplate.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取hashKey对应的所有键值
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    public Map<Object, Object> hmget(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * HashSet
+     * @param key 键
+     * @param map 对应多个键值
+     * @return true 成功 false 失败
+     */
+    public boolean hmset(String key, Map<String, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * HashSet 并设置时间
+     * @param key 键
+     * @param map 对应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public boolean hmset(String key, Map<String, Object> map, long time) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key 键
+     * @param item 项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key 键
+     * @param item 项
+     * @param value 值
+     * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value, long time) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除hash表中的值
+     * @param key 键 不能为null
+     * @param item 项 可以使多个 不能为null
+     */
+    public void hdel(String key, Object... item) {
+        redisTemplate.opsForHash().delete(key, item);
+    }
+
+    /**
+     * 判断hash表中是否有该项的值
+     * @param key 键 不能为null
+     * @param item 项 不能为null
+     * @return true 存在 false不存在
+     */
+    public boolean hHasKey(String key, String item) {
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     * @param key 键
+     * @param item 项
+     * @param by 要增加几(大于0)
+     * @return
+     */
+    public double hincr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     * @param key 键
+     * @param item 项
+     * @param by 要减少记(小于0)
+     * @return
+     */
+    public double hdecr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, -by);
+    }
+
+    // ============================set=============================
+    /**
+     * 根据key获取Set中的所有值
+     * @param key 键
+     * @return
+     */
+    public Set<Object> sGet(String key) {
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 根据value从一个set中查询,是否存在
+     * @param key 键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public boolean sHasKey(String key, Object value) {
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将数据放入set缓存
+     * @param key 键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSet(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 将set数据放入缓存
+     * @param key 键
+     * @param time 时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSetAndTime(String key, long time, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if (time > 0)
+                expire(key, time);
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 获取set缓存的长度
+     * @param key 键
+     * @return
+     */
+    public long sGetSetSize(String key) {
+        try {
+            return redisTemplate.opsForSet().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 移除值为value的
+     * @param key 键
+     * @param values 值 可以是多个
+     * @return 移除的个数
+     */
+    public long setRemove(String key, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().remove(key, values);
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    // ===============================list=================================
+    /**
+     * 获取list缓存的内容
+     * @param key 键
+     * @param start 开始
+     * @param end 结束 0 到 -1代表所有值
+     * @return
+     */
+    public List<Object> lGet(String key, long start, long end) {
+        try {
+            return redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 获取list缓存的长度
+     * @param key 键
+     * @return
+     */
+    public long lGetListSize(String key) {
+        try {
+            return redisTemplate.opsForList().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 通过索引 获取list中的值
+     * @param key 键
+     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
+     * @return
+     */
+    public Object lGetIndex(String key, long index) {
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, Object value) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, Object value, long time) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0)
+                expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+
+    public boolean lSet(String key, List<Object> value, long time) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0)
+                expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据索引修改list中的某条数据
+     * @param key 键
+     * @param index 索引
+     * @param value 值
+     * @return
+     */
+    public boolean lUpdateIndex(String key, long index, Object value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 移除N个值为value
+     * @param key 键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public long lRemove(String key, long count, Object value) {
+        try {
+            Long remove = redisTemplate.opsForList().remove(key, count, value);
+            return remove;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+}

+ 67 - 0
src/main/java/com/shs/official/common/utils/security/Md5Utils.java

@@ -0,0 +1,67 @@
+package com.shs.official.common.utils.security;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.MessageDigest;
+
+/**
+ * Md5加密方法
+ * 
+ * @author shs
+ */
+public class Md5Utils
+{
+    private static final Logger log = LoggerFactory.getLogger(Md5Utils.class);
+
+    private static byte[] md5(String s)
+    {
+        MessageDigest algorithm;
+        try
+        {
+            algorithm = MessageDigest.getInstance("MD5");
+            algorithm.reset();
+            algorithm.update(s.getBytes("UTF-8"));
+            byte[] messageDigest = algorithm.digest();
+            return messageDigest;
+        }
+        catch (Exception e)
+        {
+            log.error("MD5 Error...", e);
+        }
+        return null;
+    }
+
+    private static final String toHex(byte hash[])
+    {
+        if (hash == null)
+        {
+            return null;
+        }
+        StringBuffer buf = new StringBuffer(hash.length * 2);
+        int i;
+
+        for (i = 0; i < hash.length; i++)
+        {
+            if ((hash[i] & 0xff) < 0x10)
+            {
+                buf.append("0");
+            }
+            buf.append(Long.toString(hash[i] & 0xff, 16));
+        }
+        return buf.toString();
+    }
+
+    public static String hash(String s)
+    {
+        try
+        {
+            return new String(toHex(md5(s)).getBytes("UTF-8"), "UTF-8");
+        }
+        catch (Exception e)
+        {
+            log.error("not supported charset...{}", e);
+            return s;
+        }
+    }
+}

+ 24 - 0
src/main/java/com/shs/official/common/utils/session/SessionUtils.java

@@ -0,0 +1,24 @@
+package com.shs.official.common.utils.session;
+
+import com.shs.official.entity.user.TbUser;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpSession;
+
+@Component
+public class SessionUtils {
+
+    HttpSession session;
+
+    public void setSession(String sid, TbUser user) {
+        session.setAttribute(sid,user);
+    }
+
+    public TbUser getSession(String sid) {
+       return (TbUser) session.getAttribute(sid);
+    }
+
+    public void cleanSeeion(String sid){
+        session.removeAttribute(sid);
+    }
+}

+ 87 - 0
src/main/java/com/shs/official/common/utils/str/CharsetKit.java

@@ -0,0 +1,87 @@
+package com.shs.official.common.utils.str;
+
+import com.shs.official.common.utils.StringUtils;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 字符集工具类
+ *
+ * @author shs
+ */
+public class CharsetKit
+{
+    /** ISO-8859-1 */
+    public static final String ISO_8859_1 = "ISO-8859-1";
+    /** UTF-8 */
+    public static final String UTF_8 = "UTF-8";
+    /** GBK */
+    public static final String GBK = "GBK";
+
+    /** ISO-8859-1 */
+    public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
+    /** UTF-8 */
+    public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
+    /** GBK */
+    public static final Charset CHARSET_GBK = Charset.forName(GBK);
+
+    /**
+     * 转换为Charset对象
+     *
+     * @param charset 字符集,为空则返回默认字符集
+     * @return Charset
+     */
+    public static Charset charset(String charset)
+    {
+        return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
+    }
+
+    /**
+     * 转换字符串的字符集编码
+     *
+     * @param source 字符串
+     * @param srcCharset 源字符集,默认ISO-8859-1
+     * @param destCharset 目标字符集,默认UTF-8
+     * @return 转换后的字符集
+     */
+    public static String convert(String source, String srcCharset, String destCharset)
+    {
+        return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
+    }
+
+    /**
+     * 转换字符串的字符集编码
+     *
+     * @param source 字符串
+     * @param srcCharset 源字符集,默认ISO-8859-1
+     * @param destCharset 目标字符集,默认UTF-8
+     * @return 转换后的字符集
+     */
+    public static String convert(String source, Charset srcCharset, Charset destCharset)
+    {
+        if (null == srcCharset)
+        {
+            srcCharset = StandardCharsets.ISO_8859_1;
+        }
+
+        if (null == destCharset)
+        {
+            srcCharset = StandardCharsets.UTF_8;
+        }
+
+        if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset))
+        {
+            return source;
+        }
+        return new String(source.getBytes(srcCharset), destCharset);
+    }
+
+    /**
+     * @return 系统字符集编码
+     */
+    public static String systemCharset()
+    {
+        return Charset.defaultCharset().name();
+    }
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1000 - 0
src/main/java/com/shs/official/common/utils/str/Convert.java


+ 92 - 0
src/main/java/com/shs/official/common/utils/str/StrFormatter.java

@@ -0,0 +1,92 @@
+package com.shs.official.common.utils.str;
+
+import com.shs.official.common.utils.StringUtils;
+
+/**
+ * 字符串格式化
+ *
+ * @author shs
+ */
+public class StrFormatter
+{
+    public static final String EMPTY_JSON = "{}";
+    public static final char C_BACKSLASH = '\\';
+    public static final char C_DELIM_START = '{';
+    public static final char C_DELIM_END = '}';
+
+    /**
+     * 格式化字符串<br>
+     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
+     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
+     * 例:<br>
+     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
+     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
+     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
+     *
+     * @param strPattern 字符串模板
+     * @param argArray 参数列表
+     * @return 结果
+     */
+    public static String format(final String strPattern, final Object... argArray)
+    {
+        if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray))
+        {
+            return strPattern;
+        }
+        final int strPatternLength = strPattern.length();
+
+        // 初始化定义好的长度以获得更好的性能
+        StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
+
+        int handledPosition = 0;
+        int delimIndex;// 占位符所在位置
+        for (int argIndex = 0; argIndex < argArray.length; argIndex++)
+        {
+            delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
+            if (delimIndex == -1)
+            {
+                if (handledPosition == 0)
+                {
+                    return strPattern;
+                }
+                else
+                { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
+                    sbuf.append(strPattern, handledPosition, strPatternLength);
+                    return sbuf.toString();
+                }
+            }
+            else
+            {
+                if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH)
+                {
+                    if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH)
+                    {
+                        // 转义符之前还有一个转义符,占位符依旧有效
+                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
+                        sbuf.append(Convert.utf8Str(argArray[argIndex]));
+                        handledPosition = delimIndex + 2;
+                    }
+                    else
+                    {
+                        // 占位符被转义
+                        argIndex--;
+                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
+                        sbuf.append(C_DELIM_START);
+                        handledPosition = delimIndex + 1;
+                    }
+                }
+                else
+                {
+                    // 正常占位符
+                    sbuf.append(strPattern, handledPosition, delimIndex);
+                    sbuf.append(Convert.utf8Str(argArray[argIndex]));
+                    handledPosition = delimIndex + 2;
+                }
+            }
+        }
+        // 加入最后一个占位符后所有的字符
+        sbuf.append(strPattern, handledPosition, strPattern.length());
+
+        return sbuf.toString();
+    }
+}

+ 51 - 0
src/main/java/com/shs/official/common/utils/token/TokenEncryptUtils.java

@@ -0,0 +1,51 @@
+package com.shs.official.common.utils.token;
+
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * token编码工具类
+ * @author shs
+ */
+public class TokenEncryptUtils {
+
+    /**
+     * 利用java原生的类实现SHA256加密
+     * @param str 加密后的报文
+     * @return
+     */
+    public static String getSHA256(String str){
+        MessageDigest messageDigest;
+        String encodestr = "";
+        try {
+            messageDigest = MessageDigest.getInstance("SHA-256");
+            messageDigest.update(str.getBytes("UTF-8"));
+            encodestr = byte2Hex(messageDigest.digest());
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return encodestr;
+    }
+    /**
+     * 将byte转为16进制
+     * @param bytes
+     * @return
+     */
+    private static String byte2Hex(byte[] bytes){
+        StringBuffer stringBuffer = new StringBuffer();
+        String temp = null;
+        for (int i=0;i<bytes.length;i++){
+            temp = Integer.toHexString(bytes[i] & 0xFF);
+            if (temp.length()==1){
+                //1得到一位的进行补0操作
+                stringBuffer.append("0");
+            }
+            stringBuffer.append(temp);
+        }
+        return stringBuffer.toString();
+    }
+}

+ 81 - 0
src/main/java/com/shs/official/controller/BaseController.java

@@ -0,0 +1,81 @@
+package com.shs.official.controller;
+
+import com.shs.official.common.ApplcationConfig;
+import com.shs.official.common.utils.ServletUtils;
+import com.shs.official.common.utils.http.HttpHelper;
+import com.shs.official.common.utils.redis.RedisUtils;
+import com.shs.official.common.utils.session.SessionUtils;
+import com.shs.official.entity.user.TbUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.propertyeditors.CustomDateEditor;
+import org.springframework.context.annotation.Import;
+import org.springframework.web.bind.ServletRequestDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+
+import javax.servlet.http.HttpServletRequest;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+@Import(ApplcationConfig.class)
+public class BaseController {
+
+    @Autowired
+    private RedisUtils redisUtils;
+
+    @Autowired
+    private SessionUtils sessionUtils;
+
+    // 令牌自定义标识
+    @Value("${token.header}")
+    private String header;
+
+    @InitBinder
+    protected void init(HttpServletRequest request, ServletRequestDataBinder binder) {
+        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd");
+        s.setLenient(false);
+        binder.registerCustomEditor(Date.class, new CustomDateEditor(s, false));
+    }
+
+    /**
+     * 获取完整的请求路径,包括:域名,端口,上下文访问路径
+     *
+     * @return 服务地址
+     */
+    public String getUrl()
+    {
+        HttpServletRequest request = ServletUtils.getRequest();
+        return getDomain(request);
+    }
+
+    public static String getDomain(HttpServletRequest request)
+    {
+        StringBuffer url = request.getRequestURL();
+        String contextPath = request.getServletContext().getContextPath();
+        return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
+    }
+
+//    public OperatrEntity getOperate(){
+//        OperatrEntity operatrEntity = new OperatrEntity();
+//        HttpServletRequest request = ServletUtils.getRequest();
+//        String token = request.getHeader(header);
+//        User user = (User) redisUtils.get(token);
+//        operatrEntity.setOperatrId(user.getId());
+//        operatrEntity.setOperate(user.getName());
+//        operatrEntity.setOperateTime(System.currentTimeMillis());
+//        operatrEntity.setOperateIp(HttpHelper.getIpAddr(request));
+//        return operatrEntity;
+//    }
+//
+    public TbUser getUser(){
+        HttpServletRequest request = ServletUtils.getRequest();
+        String token = request.getHeader(header);
+        return sessionUtils.getSession(token);
+    }
+
+    public void removeUser(){
+        HttpServletRequest request = ServletUtils.getRequest();
+        String token = request.getHeader(header);
+        sessionUtils.cleanSeeion(token);
+    }
+}

+ 34 - 0
src/main/java/com/shs/official/controller/common/CommonController.java

@@ -0,0 +1,34 @@
+package com.shs.official.controller.common;
+
+import com.shs.official.common.template.ResultTemplate;
+import com.shs.official.controller.BaseController;
+import com.shs.official.service.common.CommonService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+
+/**
+ * 通用请求处理
+ *
+ * @author shs
+ */
+@RestController
+@RequestMapping("/common")
+public class CommonController extends BaseController {
+
+    @Autowired
+    private CommonService commonService;
+
+    /**
+     * 通用上传请求
+     */
+    @PostMapping("/upload")
+    public ResultTemplate uploadFile(MultipartFile file)
+    {
+       return ResultTemplate.success("file Upload Success!", commonService.saveFile(file));
+    }
+
+}

+ 108 - 0
src/main/java/com/shs/official/controller/media/MediaController.java

@@ -0,0 +1,108 @@
+package com.shs.official.controller.media;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.shs.official.common.template.ResultTemplate;
+import com.shs.official.common.utils.ffmpeg.FfmpegUtils;
+import com.shs.official.controller.BaseController;
+import com.shs.official.entity.media.TbMedia;
+import com.shs.official.service.media.MediaService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+@RestController
+@RequestMapping("/media")
+public class MediaController extends BaseController {
+
+    @Autowired
+    private MediaService mediaService;
+
+    @ResponseBody
+    @GetMapping("/findRtspSpeed")
+    public ResultTemplate findRtspSpeed(String key){
+        return ResultTemplate.success(mediaService.findRtspSpeed(key));
+    }
+
+    @ResponseBody
+    @GetMapping("/findM3u8Speed")
+    public ResultTemplate findM3u8Speed(String key){
+        return ResultTemplate.success(mediaService.findM3u8Speed(key));
+    }
+
+    @ResponseBody
+    @GetMapping("/findThumg")
+    public ResultTemplate findThumg(String key){
+        return ResultTemplate.success(mediaService.findThumg(key));
+    }
+
+    @ResponseBody
+    @GetMapping("/findRtspUrl")
+    public ResultTemplate findRtspUrl(String key){
+        return ResultTemplate.success(mediaService.findURL(key,FfmpegUtils.CONVERTTYPE.TS.getType()));
+    }
+
+    @ResponseBody
+    @GetMapping("/findM3u8Url")
+    public ResultTemplate findM3u8Url(String key){
+        return ResultTemplate.success(mediaService.findURL(key,FfmpegUtils.CONVERTTYPE.M3U8.getType()));
+    }
+
+    @ResponseBody
+    @GetMapping("/findAmrUrl")
+    public ResultTemplate findAmrUrl(String key){
+        return ResultTemplate.success(mediaService.findMusicUrl(key));
+    }
+
+    @ResponseBody
+    @GetMapping("/findMedidInfo")
+    public ResultTemplate findMedidInfo(String key){
+        return ResultTemplate.success(mediaService.findMedidInfo(key));
+    }
+
+    @ResponseBody
+    @GetMapping("/mediaRtspConvert")
+    public ResultTemplate mediaRtspConvert(String key,@RequestParam(value="callBack",defaultValue="",required=true)String callBack){
+        mediaService.mediaRtspConvert(key,callBack);
+        return ResultTemplate.success("begin conver RTSP! please wating!");
+    }
+
+    @ResponseBody
+    @GetMapping("/test")
+    public ResultTemplate test(String key,@RequestParam(value="callBack",defaultValue="",required=true)String callBack){
+        mediaService.test(key,callBack);
+        return ResultTemplate.success("begin conver RTSP! please wating!");
+    }
+
+    @ResponseBody
+    @GetMapping("/mediaM3u8Convert")
+    public ResultTemplate mediaM3u8Convert(String key,@RequestParam(value="callBack",defaultValue="",required=true)String callBack){
+        mediaService.mediaM3u8Convert(key,callBack);
+        return ResultTemplate.success("begin cut M3U8! please wating!");
+    }
+
+    @ResponseBody
+    @GetMapping("/mediaAmrConvert")
+    public ResultTemplate mediaAmrConvert(String key){
+        return ResultTemplate.success(mediaService.mediaAmrConvert(key));
+    }
+
+    @ResponseBody
+    @GetMapping("/cutThumbnail")
+    public ResultTemplate cutThumbnail(String key,String cutTime,@RequestParam(value="cover",defaultValue="false",required=true) Boolean cover ){
+        return ResultTemplate.success(mediaService.cutThumbnail(key,cutTime,cover));
+    }
+
+    @ResponseBody
+    @GetMapping("/page")
+    public ResultTemplate page(Page page){
+        return ResultTemplate.success(mediaService.page(page));
+    }
+
+    @ResponseBody
+    @GetMapping("/del")
+    public ResultTemplate del(Long id){
+        mediaService.del(id);
+        return ResultTemplate.success("删除成功!!");
+    }
+}

+ 68 - 0
src/main/java/com/shs/official/controller/user/UserController.java

@@ -0,0 +1,68 @@
+package com.shs.official.controller.user;
+
+import com.shs.official.common.template.ResultTemplate;
+import com.shs.official.controller.BaseController;
+//import com.shs.official.entity.operate.PageEntity;
+//import com.shs.official.entity.user.User;
+import com.shs.official.entity.user.TbUser;
+import com.shs.official.service.user.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/user")
+public class UserController extends BaseController {
+
+    @Autowired
+    private UserService userService;
+
+
+    @ResponseBody
+    @PostMapping("/save")
+    public ResultTemplate save(TbUser user){
+        userService.save(user);
+        return ResultTemplate.success("user save success!");
+    }
+
+    @ResponseBody
+    @PostMapping("/login")
+    public ResultTemplate login(String account,String pwd){
+        return ResultTemplate.success("login success",userService.login(account,pwd));
+    }
+
+    @ResponseBody
+    @PostMapping("/updatePwd")
+    public ResultTemplate updatePwd(String pwd,String oldPwd){
+        userService.updatePasswd(this.getUser(),pwd,oldPwd);
+        return ResultTemplate.success("user update success!");
+    }
+
+    @ResponseBody
+    @PostMapping("/loginOut")
+    public ResultTemplate loginOut(){
+        this.removeUser();
+        return ResultTemplate.success("loginOut!");
+    }
+
+
+    @ResponseBody
+    @PostMapping("/changePasswd")
+    public ResultTemplate changePasswd(Long id, String pwd){
+        userService.changePasswd(id,pwd);
+        return ResultTemplate.success("find success!");
+    }
+
+    @ResponseBody
+    @PostMapping("/disable")
+    public ResultTemplate disable(Long id){
+        userService.disable(id);
+        return ResultTemplate.success("find success!");
+    }
+
+    @ResponseBody
+    @PostMapping("/enable")
+    public ResultTemplate enable(Long id){
+        userService.enable(id);
+        return ResultTemplate.success("find success!");
+    }
+}

+ 74 - 0
src/main/java/com/shs/official/entity/BaseEntity.java

@@ -0,0 +1,74 @@
+package com.shs.official.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+
+public class BaseEntity {
+
+    @TableField(value = "operate_id", fill = FieldFill.INSERT)
+    private Long operate_id;
+
+    @TableField(value = "operate", fill = FieldFill.INSERT)
+    private String operate;
+
+    @TableField(value = "operate_time", fill = FieldFill.INSERT)
+    private Long operate_time;
+
+    @TableField(value = "update_operate", fill = FieldFill.UPDATE)
+    private String update_operate;
+
+    @TableField(value = "update_operate_time", fill = FieldFill.UPDATE)
+    private Long update_operate_time;
+
+    @TableField(value = "update_operate_id", fill = FieldFill.UPDATE)
+    private Long update_operate_id;
+
+
+    public Long getOperate_id() {
+        return operate_id;
+    }
+
+    public void setOperate_id(Long operate_id) {
+        this.operate_id = operate_id;
+    }
+
+    public String getOperate() {
+        return operate;
+    }
+
+    public void setOperate(String operate) {
+        this.operate = operate;
+    }
+
+    public Long getOperate_time() {
+        return operate_time;
+    }
+
+    public void setOperate_time(Long operate_time) {
+        this.operate_time = operate_time;
+    }
+
+    public String getUpdate_operate() {
+        return update_operate;
+    }
+
+    public void setUpdate_operate(String update_operate) {
+        this.update_operate = update_operate;
+    }
+
+    public Long getUpdate_operate_time() {
+        return update_operate_time;
+    }
+
+    public void setUpdate_operate_time(Long update_operate_time) {
+        this.update_operate_time = update_operate_time;
+    }
+
+    public Long getUpdate_operate_id() {
+        return update_operate_id;
+    }
+
+    public void setUpdate_operate_id(Long update_operate_id) {
+        this.update_operate_id = update_operate_id;
+    }
+}

+ 145 - 0
src/main/java/com/shs/official/entity/media/TbMedia.java

@@ -0,0 +1,145 @@
+package com.shs.official.entity.media;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.shs.official.entity.BaseEntity;
+
+@TableName("tb_media")
+public class TbMedia {
+
+    private static final long serialVersionUID = 1L;
+
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long id;
+
+    private String name;
+    private String md5;
+    private String rtsp;
+    private String amr;
+    private String thumg;
+    private String m3u8;
+    private String sources;
+    private String codec;
+    private String rate;
+    private String audio_codec;
+    private Integer type;
+    private String base_url;
+
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long created;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getMd5() {
+        return md5;
+    }
+
+    public void setMd5(String md5) {
+        this.md5 = md5;
+    }
+
+    public String getRtsp() {
+        return rtsp;
+    }
+
+    public void setRtsp(String rtsp) {
+        this.rtsp = rtsp;
+    }
+
+    public String getAmr() {
+        return amr;
+    }
+
+    public void setAmr(String amr) {
+        this.amr = amr;
+    }
+
+    public String getThumg() {
+        return thumg;
+    }
+
+    public void setThumg(String thumg) {
+        this.thumg = thumg;
+    }
+
+    public String getM3u8() {
+        return m3u8;
+    }
+
+    public void setM3u8(String m3u8) {
+        this.m3u8 = m3u8;
+    }
+
+    public String getSources() {
+        return sources;
+    }
+
+    public void setSources(String sources) {
+        this.sources = sources;
+    }
+
+    public String getCodec() {
+        return codec;
+    }
+
+    public void setCodec(String codec) {
+        this.codec = codec;
+    }
+
+    public String getRate() {
+        return rate;
+    }
+
+    public void setRate(String rate) {
+        this.rate = rate;
+    }
+
+    public String getAudio_codec() {
+        return audio_codec;
+    }
+
+    public void setAudio_codec(String audio_codec) {
+        this.audio_codec = audio_codec;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    public Long getCreated() {
+        return created;
+    }
+
+    public void setCreated(Long created) {
+        this.created = created;
+    }
+
+    public String getBaseUrl() {
+        return base_url;
+    }
+
+    public void setBase_url(String base_url) {
+        this.base_url = base_url;
+    }
+}

+ 94 - 0
src/main/java/com/shs/official/entity/user/TbUser.java

@@ -0,0 +1,94 @@
+package com.shs.official.entity.user;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+
+@TableName("tb_user")
+public class TbUser {
+    private static final long serialVersionUID = 1L;
+
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long id;
+
+    private String name;
+    private String account;
+    private String password;
+    private String slat;
+    private String email;
+    private String phone;
+    private String avator;
+    private Integer status;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getSlat() {
+        return slat;
+    }
+
+    public void setSlat(String slat) {
+        this.slat = slat;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getAvator() {
+        return avator;
+    }
+
+    public void setAvator(String avator) {
+        this.avator = avator;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+}

+ 49 - 0
src/main/java/com/shs/official/entity/vo/MediaVo.java

@@ -0,0 +1,49 @@
+package com.shs.official.entity.vo;
+
+public class MediaVo {
+    private String name;
+    private String url;
+    private String baseUrl;
+    private String path;
+    private String thumg;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getBaseUrl() {
+        return baseUrl;
+    }
+
+    public void setBaseUrl(String baseUrl) {
+        this.baseUrl = baseUrl;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public String getThumg() {
+        return thumg;
+    }
+
+    public void setThumg(String thumg) {
+        this.thumg = thumg;
+    }
+}

+ 29 - 0
src/main/java/com/shs/official/mapper/media/MediaMapper.java

@@ -0,0 +1,29 @@
+package com.shs.official.mapper.media;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.shs.official.entity.media.TbMedia;
+import org.springframework.stereotype.Repository;
+
+
+@Repository
+public interface MediaMapper extends BaseMapper<TbMedia> {
+
+    void save(TbMedia media);
+
+    void updateRtsp(TbMedia media);
+
+    void updateM3u8(TbMedia media);
+
+    void updateThumg(TbMedia media);
+
+    void updateAmr(TbMedia media);
+
+    TbMedia findById(Long id);
+
+    TbMedia findByMd5(String md5);
+
+    void del(Long id);
+
+    IPage page(Page page);
+}

+ 9 - 0
src/main/java/com/shs/official/mapper/user/UserMapper.java

@@ -0,0 +1,9 @@
+package com.shs.official.mapper.user;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.shs.official.entity.user.TbUser;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface UserMapper extends BaseMapper<TbUser> {
+}

+ 93 - 0
src/main/java/com/shs/official/service/BaseService.java

@@ -0,0 +1,93 @@
+package com.shs.official.service;
+
+import com.shs.official.common.ApplcationConfig;
+import com.shs.official.common.utils.IdGen;
+import com.shs.official.common.utils.redis.RedisUtils;
+import com.shs.official.common.utils.session.SessionUtils;
+import com.shs.official.common.utils.token.TokenEncryptUtils;
+//import com.shs.official.entity.user.User;
+import com.shs.official.entity.user.TbUser;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Import;
+
+import java.util.UUID;
+
+@Import(ApplcationConfig.class)
+public class BaseService {
+
+    @Autowired
+    private RedisUtils redisUtils;
+
+    @Autowired
+    private SessionUtils sessionUtils;
+
+    // 令牌自定义标识
+    @Value("${token.header}")
+    private String header;
+
+
+    // 令牌有效期(默认30分钟)
+    @Value("${token.expireTime}")
+    private int expireTime;
+
+    public Boolean verifyPasswd(String oldPasswd,String passwd,String salt){
+        if(oldPasswd.equals(getPassword(salt,passwd))){
+            return true;
+        }else {
+            return false;
+        }
+    }
+
+    public String setToken(TbUser user){
+        String token = TokenEncryptUtils.getSHA256(user.getAccount()+user.getPassword())+System.currentTimeMillis()+'.'+user.getId();
+        sessionUtils.setSession(token,user);
+        return token;
+    }
+
+    public String getSalt(){
+        StringBuffer shortBuffer = new StringBuffer();
+        String uuid = UUID.randomUUID().toString().replace("-", "");
+        for (int i = 0; i < 8; i++) {
+            String str = uuid.substring(i * 4, i * 4 + 4);
+            int x = Integer.parseInt(str, 16);
+            shortBuffer.append(chars[x % 0x3E]);
+        }
+        return shortBuffer.toString();
+    }
+
+    public String[] chars = new String[] { "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", "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" };
+
+    public String getPassword(String slat ,String passwd) {
+        return TokenEncryptUtils.getSHA256(slat+passwd);
+    }
+
+    public String getConvetPath(){
+        return "/" + IdGen.getId();
+    }
+
+    public enum DEL_FLAG{
+        DELETE(true,"已删除"),
+        NOTDELETE(false,"未删除");
+
+        private Boolean status;
+        private String remark;
+
+        private DEL_FLAG(Boolean status,String remark){
+            this.status = status;
+            this.remark = remark;
+        }
+
+        public Boolean getStatus(){
+            return this.status;
+        }
+    }
+
+}

+ 56 - 0
src/main/java/com/shs/official/service/common/CommonService.java

@@ -0,0 +1,56 @@
+package com.shs.official.service.common;
+
+import com.shs.official.common.LiveConfig;
+import com.shs.official.common.exception.BaseException;
+import com.shs.official.common.template.ResultTemplate;
+import com.shs.official.common.utils.IdGen;
+import com.shs.official.common.utils.file.FileUploadUtils;
+import com.shs.official.common.utils.file.MD5Checksum;
+import com.shs.official.entity.media.TbMedia;
+import com.shs.official.service.BaseService;
+import com.shs.official.service.media.MediaService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+@Service
+public class CommonService extends BaseService {
+
+    @Autowired
+    private MediaService mediaService;
+
+    public TbMedia saveFile(MultipartFile file){
+        TbMedia media = new TbMedia();
+        String fileName;
+        String fileMd5;
+        try
+        {
+            fileMd5 = MD5Checksum.getMD5Checksum(file.getInputStream());
+            media = mediaService.findByMd5(fileMd5);
+            if (media != null)
+                return media;
+            media = new TbMedia();
+            // 上传文件路径
+            String filePath = LiveConfig.getSourceProfile();
+            // 上传并返回新文件名称
+            fileName = FileUploadUtils.upload(filePath, file);
+            media.setId(IdGen.getId());
+            media.setName(file.getOriginalFilename());
+            media.setMd5(fileMd5);
+            media.setSources(LiveConfig.getSourceProfile()+"/"+fileName);
+            media.setType(FileUploadUtils.assertAllowedExtrnsion(file));
+            media.setCreated(System.currentTimeMillis());
+            mediaService.save(media);
+            return media;
+        }
+        catch (Exception e) {
+            if(e.getMessage().contains("allowed extension"))
+                throw new BaseException(ResultTemplate.error(8211,"file formats not supported!"));
+            else if(e.getMessage().contains("No such file or directory"))
+                throw new BaseException(ResultTemplate.error(4453,"No such file or directory!"));
+            else
+                throw new BaseException(ResultTemplate.error(4453,"file upload error!"));
+        }
+    }
+
+}

+ 350 - 0
src/main/java/com/shs/official/service/media/MediaService.java

@@ -0,0 +1,350 @@
+package com.shs.official.service.media;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.google.gson.Gson;
+import com.shs.official.common.LiveConfig;
+import com.shs.official.common.exception.BaseException;
+import com.shs.official.common.template.ResultTemplate;
+import com.shs.official.common.utils.StringUtils;
+import com.shs.official.common.utils.ffmpeg.FfmpegUtils;
+import com.shs.official.common.utils.http.HttpHelper;
+import com.shs.official.common.utils.redis.RedisUtils;
+import com.shs.official.entity.media.TbMedia;
+import com.shs.official.entity.vo.MediaVo;
+import com.shs.official.mapper.media.MediaMapper;
+import com.shs.official.service.BaseService;
+import com.shs.official.service.task.ConverTaskService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+/*
+* AUTH shs
+* DATA 2021-05-08
+* Media Services
+* */
+@Service
+public class MediaService extends BaseService {
+
+    @Autowired
+    private MediaMapper mediaMapper;
+    @Autowired
+    private RedisUtils redisUtils;
+    @Autowired
+    private ConverTaskService converTaskService;
+    @Autowired
+    private LiveConfig liveConfig;
+
+    /*
+    * Media Info Save
+    * param media Media Info Entity
+    * */
+    @Transactional
+    public void save(TbMedia media){
+        media.setBase_url(liveConfig.getBaseUrl());
+        mediaMapper.save(media);
+    }
+
+    /*
+     * Media RTSP Conver Result Upadte
+     * param media Media Info Entity
+     * */
+    @Transactional
+    public void updateM3U8(TbMedia media){
+        mediaMapper.updateM3u8(media);
+    }
+
+    /*
+     * Media RTSP Conver Result Upadte
+     * param media Media Info Entity
+     * */
+    @Transactional
+    public void updateRTSP(TbMedia media){
+        mediaMapper.updateRtsp(media);
+    }
+
+    /*
+     * Media FindByMd5
+     * param md5 Media File Pirmary Key
+     * */
+    public TbMedia findByMd5(String md5){
+        return mediaMapper.findByMd5(md5);
+    }
+
+
+    /*
+     * Media RTSP Conver Speed
+     * param key Media paramkey
+     * */
+    public String findRtspSpeed(String key){
+        TbMedia media = mediaMapper.findById(Long.valueOf(key));
+        if (media == null)
+            throw new BaseException(ResultTemplate.error(7721,"media not found!"));
+        if(!media.getType().equals(TYPE.VIDEO.getType()))
+            throw new BaseException(ResultTemplate.error(1231,"this media is not video!"));
+        if (media.getRtsp() != null && !media.getRtsp().isEmpty())
+            return media.getRtsp();
+        Object speed = redisUtils.get(key+FfmpegUtils.CONVERTTYPE.TS.getFormat());
+        if(speed == null)
+            throw new BaseException(ResultTemplate.error(7722,"media not begin conver! please start convert!"));
+        return speed.toString();
+    }
+
+    /*
+     * Media M3U8 Conver Speed
+     * param key Media Pirmary Key
+     * */
+    public String findM3u8Speed(String key){
+        TbMedia media = mediaMapper.findById(Long.valueOf(key));
+        if (media == null)
+            throw new BaseException(ResultTemplate.error(7721,"media not found!"));
+        if(!media.getType().equals(TYPE.VIDEO.getType()))
+            throw new BaseException(ResultTemplate.error(1231,"this media is not video!"));
+        if (media.getM3u8() != null && !media.getM3u8().isEmpty())
+            return media.getM3u8();
+        Object speed = redisUtils.get(key+FfmpegUtils.CONVERTTYPE.M3U8.getFormat());
+        if(speed == null)
+            throw new BaseException(ResultTemplate.error(7722,"media not begin conver! please start convert!"));
+        return speed.toString();
+    }
+
+    /*
+     * Media Thumg Cut Find
+     * param key Media Pirmary Key
+     * */
+    public String findThumg(String key){
+        TbMedia media = mediaMapper.findById(Long.valueOf(key));
+        if (media == null)
+            throw new BaseException(ResultTemplate.error(7721,"media not found!"));
+        if(!media.getType().equals(TYPE.VIDEO.getType()))
+            throw new BaseException(ResultTemplate.error(1231,"this media is not video!"));
+        if (media.getThumg() == null || media.getThumg().isEmpty())
+            throw new BaseException(ResultTemplate.error(7722,"media not cut thumbnail! please cut thumbnail!"));
+        return FfmpegUtils.CONVERTTYPE.THUMG.getAgre()+liveConfig.getBaseUrl()+media.getThumg();
+    }
+
+    /*
+     * Media Info Find
+     * param key Media Pirmary Key
+     * */
+    public TbMedia findMedidInfo(String key){
+        TbMedia media = mediaMapper.findById(Long.valueOf(key));
+        if (media == null)
+            throw new BaseException(ResultTemplate.error(7721,"media not found!"));
+        return media;
+    }
+
+    /*
+    * Media RTSP Rate Scale Adjustment
+    * param key Media Pirmary Key
+    * param callBackUrl  callback Address
+    * */
+    public void mediaRtspConvert(String key, String callBackUrl){
+        if(StringUtils.isNotEmpty(callBackUrl)){
+            if(!HttpHelper.isURL(callBackUrl))
+                throw new BaseException(ResultTemplate.error(2163,"callback address error! please calllback url!"));
+        }
+        TbMedia media = mediaMapper.findById(Long.valueOf(key));
+        if (media == null)
+            throw new BaseException(ResultTemplate.error(7721,"media not found!"));
+        if(!media.getType().equals(TYPE.VIDEO.getType()))
+            throw new BaseException(ResultTemplate.error(1231,"this media is not video!"));
+        if (media.getRtsp() != null && !media.getRtsp().isEmpty())
+            throw new BaseException(ResultTemplate.error(7724,"media convert success!"));
+        Object speed = redisUtils.get(key+FfmpegUtils.CONVERTTYPE.TS.getFormat());
+        if(speed != null)
+            throw new BaseException(ResultTemplate.error(7726,"conversion in progress! please wait!"));
+        converTaskService.startConvertTs(media,callBackUrl);
+    }
+
+    public void test(String key, String callBackUrl){
+        TbMedia media = mediaMapper.findById(Long.valueOf(key));
+
+        String fileConvertPath = LiveConfig.getProfile() + "/" + media.getId() + LiveConfig.getTSPath();
+        String outName = "/index";
+        TbMedia mediaRes = new TbMedia();
+        mediaRes = FfmpegUtils.convertCommand(media.getSources(), fileConvertPath, outName, media.getId(),FfmpegUtils.CONVERTTYPE.TS.getType());
+    }
+    /*
+     * Media M3U8 Rate Scale Adjustment
+     * param key Media Pirmary Key
+     * param callBackUrl  callback Address
+     * */
+    public void mediaM3u8Convert(String key, String callBackUrl){
+        if(StringUtils.isNotEmpty(callBackUrl)){
+            if(!HttpHelper.isURL(callBackUrl))
+                throw new BaseException(ResultTemplate.error(2163,"callback address error! please calllback url!"));
+        }
+        TbMedia media = mediaMapper.findById(Long.valueOf(key));
+        if (media == null)
+            throw new BaseException(ResultTemplate.error(7721,"media not found!"));
+        if(!media.getType().equals(TYPE.VIDEO.getType()))
+            throw new BaseException(ResultTemplate.error(1231,"this media is not video!"));
+        if (media.getM3u8() != null && !media.getM3u8().isEmpty())
+            throw new BaseException(ResultTemplate.error(7724,"media convert success!"));
+        Object speed = redisUtils.get(key+FfmpegUtils.CONVERTTYPE.M3U8.getFormat());
+        if(speed != null)
+            throw new BaseException(ResultTemplate.error(7726,"conversion in progress! please wait!"));
+        converTaskService.startConvertM3u8(media,callBackUrl);
+    }
+
+    /*
+     * Audio Amr Rate Scale Adjustment
+     * param key Audio Pirmary Key
+     * param callBackUrl  callback Address
+     * */
+    public String mediaAmrConvert(String key){
+        TbMedia media = mediaMapper.findById(Long.valueOf(key));
+        if (media == null)
+            throw new BaseException(ResultTemplate.error(7721,"media not found!"));
+        if(!media.getType().equals(TYPE.AUDIO.getType()))
+            throw new BaseException(ResultTemplate.error(1232,"this media is not audio!"));
+        if (media.getAmr() != null && !media.getAmr().isEmpty())
+            return media.getAmr();
+        String fileConvertPath = LiveConfig.getProfile() + "/" + media.getId() + LiveConfig.getMusicPath();
+        String outName = "/index";
+        TbMedia mediaRes = FfmpegUtils.convertMusic(media.getSources(),fileConvertPath,outName,media.getId());
+        mediaMapper.updateAmr(mediaRes);
+        return mediaRes.getAmr();
+    }
+
+    /*
+     * Media Ctu The thumbnail
+     * param key Media Pirmary Key
+     * param cutTime Meidia Thumbnail Cut Time (seconds)
+     * param cover Meida Thumbnail Cover (default false)
+     * */
+    @Transactional
+    public String cutThumbnail(String key,String cutTime,Boolean cover){
+        TbMedia media = mediaMapper.findById(Long.valueOf(key));
+        if (media == null)
+            throw new BaseException(ResultTemplate.error(7721,"media not found!"));
+        if(!media.getType().equals(TYPE.VIDEO.getType()))
+            throw new BaseException(ResultTemplate.error(1231,"this media is not video!"));
+        if (media.getThumg() != null && !media.getThumg().isEmpty())
+            if (!cover){
+                return media.getThumg();
+        }
+        String fileConvertPath = LiveConfig.getProfile() + "/" + media.getId() + LiveConfig.getCoverPath();
+        String outName = "/index";
+        TbMedia mediaRes = new TbMedia();
+        try {
+            mediaRes = FfmpegUtils.processImg(media.getSources(), fileConvertPath,outName,cutTime,media.getId());
+        }catch (Exception e){
+            throw new BaseException(ResultTemplate.error(8218,"media parsing error!"));
+        }
+        mediaMapper.updateThumg(mediaRes);
+        return mediaRes.getThumg();
+    }
+
+    public String findMusicUrl(String key){
+        TbMedia media = mediaMapper.findById(Long.valueOf(key));
+        if (media == null)
+            throw new BaseException(ResultTemplate.error(7721,"media not found!"));
+        if(!media.getType().equals(TYPE.AUDIO.getType()))
+            throw new BaseException(ResultTemplate.error(1231,"this media is not audio!"));
+        if(media.getAmr() != null && !media.getAmr().isEmpty())
+            return FfmpegUtils.CONVERTTYPE.AMR.getAgre()+liveConfig.getBaseUrl()+media.getAmr();
+        else
+            throw new BaseException(ResultTemplate.error(8812,"audio not found!please conver amr!"));
+    }
+
+    public MediaVo findURL(String key, Integer type){
+        TbMedia media = mediaMapper.findById(Long.valueOf(key));
+        MediaVo mediaVo = new MediaVo();
+        if (media == null)
+            throw new BaseException(ResultTemplate.error(7721,"media not found!"));
+        mediaVo.setName(media.getName());
+        mediaVo.setBaseUrl(media.getBaseUrl());
+        mediaVo.setThumg(FfmpegUtils.CONVERTTYPE.THUMG.getAgre()+liveConfig.getBaseUrl()+media.getThumg());
+        if(!media.getType().equals(TYPE.VIDEO.getType()))
+            throw new BaseException(ResultTemplate.error(1231,"this media is not video!"));
+        if(type.equals(FfmpegUtils.CONVERTTYPE.TS.getType())){
+            if (media.getRtsp() == null || media.getRtsp().isEmpty())
+                throw new BaseException(ResultTemplate.error(7724,"media not convert !"));
+            mediaVo.setPath(media.getRtsp());
+            mediaVo.setUrl(FfmpegUtils.CONVERTTYPE.TS.getAgre()+liveConfig.getBaseUrl()+media.getRtsp());
+            return mediaVo;
+        }else {
+            if (media.getM3u8() == null || media.getM3u8().isEmpty())
+                throw new BaseException(ResultTemplate.error(7724,"media not convert !"));
+            mediaVo.setPath(media.getM3u8());
+            mediaVo.setUrl(FfmpegUtils.CONVERTTYPE.M3U8.getAgre()+liveConfig.getBaseUrl()+media.getM3u8());
+            return mediaVo;
+        }
+    }
+
+    public IPage<TbMedia> page(Page page){
+        return mediaMapper.page(page);
+    }
+
+    @Transactional
+    public void del(Long id){
+        mediaMapper.del(id);
+    }
+
+    public void callbackDemo(HttpServletRequest request){
+        Gson gson = new Gson();
+        String type = request.getContentType();
+        Map<String,Object> receiveMap = new HashMap<String,Object>();
+        if("application/x-www-form-urlencoded".equals(type)){
+            Enumeration<String> enu = request.getParameterNames();
+            while (enu.hasMoreElements()) {
+                String key = String.valueOf(enu.nextElement());
+                String value = request.getParameter(key);
+                receiveMap.put(key, value);
+            }
+        }else{	//else是text/plain、application/json这两种情况
+            BufferedReader reader = null;
+            StringBuilder sb = new StringBuilder();
+            try{
+                reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "utf-8"));
+                String line = null;
+                while ((line = reader.readLine()) != null){
+                    sb.append(line);
+                }
+            } catch (IOException e){
+                e.printStackTrace();
+            } finally {
+                try{
+                    if (null != reader){
+                        reader.close();
+                    }
+                } catch (IOException e){
+                    e.printStackTrace();
+                }
+            }
+            System.out.println(sb.toString());
+//            receiveMap = gson.fromJson(sb.toString(), new TypeToken<Map<String, String>>(){}.getType());//把JSON字符串转为对象
+        }
+//        return receiveMap;
+    }
+
+
+    public enum TYPE{
+        VIDEO(0,"视频"),
+        AUDIO(1,"音频"),
+        UNKNOW(-1,"未知文件");
+
+        private Integer type;
+        private String remark;
+
+        TYPE(Integer type,String remark){
+            this.remark = remark;
+            this.type = type;
+        }
+
+        public Integer getType(){
+            return this.type;
+        }
+    }
+}

+ 76 - 0
src/main/java/com/shs/official/service/task/ConverTaskService.java

@@ -0,0 +1,76 @@
+package com.shs.official.service.task;
+
+import com.shs.official.common.LiveConfig;
+import com.shs.official.common.exception.BaseException;
+import com.shs.official.common.http.HttpRequest;
+import com.shs.official.common.template.ResultTemplate;
+import com.shs.official.common.utils.StringUtils;
+import com.shs.official.common.utils.ffmpeg.FfmpegUtils;
+import com.shs.official.entity.media.TbMedia;
+import com.shs.official.service.media.MediaService;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+public class ConverTaskService {
+
+    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ConverTaskService.class);
+
+    @Autowired
+    private MediaService mediaService;
+    @Autowired
+    private LiveConfig liveConfig;
+
+    private HttpRequest httpRequest = new HttpRequest();
+
+    @Async
+    public void startConvertTs(TbMedia media,String callBakUrl) {
+        String fileConvertPath = LiveConfig.getProfile() + "/" + media.getId() + LiveConfig.getTSPath();
+        String outName = "/index";
+        TbMedia mediaRes = new TbMedia();
+        try {
+            mediaRes = FfmpegUtils.convertCommand(media.getSources(), fileConvertPath, outName, media.getId(),FfmpegUtils.CONVERTTYPE.TS.getType());
+        }catch (Exception e){
+            throw new BaseException(ResultTemplate.error(8218,"media parsing error!"));
+        }
+        mediaService.updateRTSP(mediaRes);
+        if(StringUtils.isNotEmpty(callBakUrl))
+            this.calllBack(callBakUrl,mediaRes,FfmpegUtils.CONVERTTYPE.TS);
+        logger.error(media.getId() + " : conver success!");
+    }
+
+    @Async
+    public void startConvertM3u8(TbMedia media,String callBakUrl) {
+        String fileConvertPath = LiveConfig.getProfile() + "/" + media.getId() + LiveConfig.getM3u8Path();
+        String outName = "/index";
+        TbMedia mediaRes = new TbMedia();
+        try {
+            mediaRes = FfmpegUtils.convertCommand(media.getSources(), fileConvertPath, outName, media.getId(), FfmpegUtils.CONVERTTYPE.M3U8.getType());
+        }catch (Exception e){
+            throw new BaseException(ResultTemplate.error(8218,"media parsing error!"));
+        }
+        mediaService.updateM3U8(mediaRes);
+        if(StringUtils.isNotEmpty(callBakUrl))
+            this.calllBack(callBakUrl,mediaRes,FfmpegUtils.CONVERTTYPE.M3U8);
+        logger.error(media.getId() + " : conver success!");
+    }
+
+    public void calllBack(String url, TbMedia media, FfmpegUtils.CONVERTTYPE converttype){
+
+        Map<String, String> headerMap = new HashMap<>(6);
+
+        Map<String, String> jsonMap = new HashMap<>(6);
+        jsonMap.put("id", media.getId().toString());
+        jsonMap.put("type", converttype.getType().toString());
+        jsonMap.put("address", converttype.getType().equals(FfmpegUtils.CONVERTTYPE.TS.getType())?media.getRtsp():media.getM3u8());
+        jsonMap.put("url", converttype.getAgre()+liveConfig.getBaseUrl()+(converttype.getType().equals(FfmpegUtils.CONVERTTYPE.TS.getType())?media.getRtsp():media.getM3u8()));
+
+        httpRequest.sendPost(url,headerMap,null,jsonMap);
+        //FIXME 此处后期加入队列 判断回掉状态 进行重复回掉操作
+    }
+}

+ 101 - 0
src/main/java/com/shs/official/service/user/UserService.java

@@ -0,0 +1,101 @@
+package com.shs.official.service.user;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.shs.official.common.em.USER_STATUS;
+import com.shs.official.common.exception.user.UserException;
+import com.shs.official.common.exception.user.UserPasswordNotMatchException;
+import com.shs.official.entity.user.TbUser;
+import com.shs.official.mapper.user.UserMapper;
+import com.shs.official.service.BaseService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+@Service
+public class UserService extends BaseService {
+
+    @Autowired
+    private UserMapper userMapper;
+
+    @Transactional
+    public void save(TbUser user){
+        try {
+            user.setSlat(getSalt());
+            user.setPassword(getPassword(user.getSlat(),user.getPassword()));
+            user.setStatus(USER_STATUS.NORMAL.getKey());
+            userMapper.insert(user);
+        }catch (Exception e){
+            if(e.getMessage().contains("account-unique") && e.getMessage() != null){
+                throw new UserException(5531,"User already exists!");
+            } else {
+                throw new UserException(5530,"User Save Fail!");
+            }
+        }
+    }
+
+    public Map login(String account,String pwd){
+        QueryWrapper<TbUser> userQueryWrapper = new QueryWrapper<>();
+        userQueryWrapper.eq("account",account);
+        TbUser user = userMapper.selectOne(userQueryWrapper);
+        if (user==null)
+            throw new UserException(5517,"user exsits!");
+        if(verifyPasswd(user.getPassword(),pwd,user.getSlat())){
+            Map login = new HashMap();
+            login.put("token",setToken(user));
+            login.put("username",user.getName());
+            login.put("useremail",user.getEmail());
+            login.put("useraccount",user.getAccount());
+            login.put("useraccount",user.getAvator());
+            return login;
+        }else {
+            throw new UserPasswordNotMatchException();
+        }
+    }
+
+    @Transactional
+    public void updatePasswd(TbUser user,String pwd,String oldPwd){
+        TbUser userVo = userMapper.selectById(user.getId());
+        if(!userVo.getPassword().equals(getPassword(user.getSlat() ,oldPwd))){
+            throw new UserException(2213,"Old Password Error!");
+        }
+        TbUser userUp = new TbUser();
+        userUp.setId(Long.valueOf(user.getId()));
+        userUp.setSlat(getSalt());
+        userUp.setPassword(getPassword(userUp.getSlat(),pwd));
+        userMapper.updateById(userUp);
+    }
+
+    @Transactional
+    public void changePasswd(Long id,String pwd){
+        TbUser user = new TbUser();
+        user.setId(id);
+        user.setSlat(getSalt());
+        user.setPassword(getPassword(user.getSlat(),pwd));
+        userMapper.updateById(user);
+    }
+
+    @Transactional
+    public void disable(Long id){
+        TbUser user = new TbUser();
+        user.setId(id);
+        user.setStatus(USER_STATUS.DISABLE.getKey());
+        userMapper.updateById(user);
+    }
+
+    @Transactional
+    public void enable(Long id){
+        TbUser user = new TbUser();
+        user.setId(id);
+        user.setStatus(USER_STATUS.NORMAL.getKey());
+        userMapper.updateById(user);
+    }
+
+//    public IPage<TbUser> getUser(Page page){
+//        Page<TbUser> ipage = new Page<TbUser>(page.getCurrent(),page.getSize());
+//        return userMapper.;
+//    }
+}

+ 131 - 0
src/main/resources/application-dev.yml

@@ -0,0 +1,131 @@
+server:
+  port: 9998
+
+live:
+  # 名称
+  name: shs_live
+  # 版本
+  version: 0.0.1
+  # 服务地址
+  baseUrl: 127.0.0.1:1212/
+  # 文件保存路径
+  profile: /Users/project/upload
+  # 是否删除原始文件
+  sourceDelete: false
+  # 原始文件路径
+  sourceProfile: /Users/project/upload
+  # 视频编码格式
+  videoCodec: libx264
+  # 视频针率 每秒29帧
+  frameRate: 29.99
+  # ffmpeg路径
+  ffmpegPath: /Users/project/ffmpeg/ffmpeg
+  # ffprobe路径
+  ffprobePath: /Users/project/ffmpeg/ffprobe
+  # 浮动码率
+  rate: false
+  # 视频码率 768KHz
+  videoRate: 768_000
+  # 缓冲大小 1M
+  bufSize: 1024_000
+  # 最小码率 328KHz
+  minRate: 328_000
+  # 最大码率 768Khz
+  maxRate: 768_000
+  # 音频编码格式
+  audioCodec: aac
+  # 音频通道
+  audioChannels: 2
+  # 音频码率 32kbit/s
+  audioRate: 32768
+  # 采样率 8KHZ
+  sampling: 8_000
+  # 等比缩放(true开启默认开启)
+  zoom: true
+  # 缩放尺寸
+  zoomSize: 760
+  # 强制调整分辨率
+  relove: false
+  reloveSize: 1920x1080
+  # 视频切片时间(单位s)
+  segmentTime: 10
+  # 视频封面切割尺寸 默认以缩放比例切割
+  coverSize:
+  # 切割针率 毫秒级
+  coverT: 0.001
+  # 视频格式
+  format: hls
+  # 切片时间 10S
+  hlsTime: 10
+  # 切片开始编号 默认0
+  hlsStartNumber: 0
+  # 多少片之后开始覆盖 (0则不会覆盖,默认值为0)
+  hlsWrap: 0
+  # 总共切片数量 (0会保存有所片信息,默认值为5)
+  hlsListSize: 0
+
+
+
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://data.skyshs.com:3309/db_live?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
+    username: root
+    password: Shs2514
+    druid:
+      initial-size: 8
+      min-idle: 1
+      max-active: 20
+      max-wait: 60000
+  servlet:
+     multipart:
+       # 单个文件大小
+       max-file-size:  500MB
+       # 设置总上传的文件大小
+       max-request-size:  500MB
+  jackson:
+    date-format: yyyy-MM-dd
+    time-zone: GMT+8
+  #redis 配置
+  redis:
+      # 地址
+      host: 127.0.0.1
+      # 端口,默认为6379
+      port: 6379
+      # 密码
+      password:
+      # 连接超时时间
+      timeout: 10s
+      lettuce:
+        pool:
+          # 连接池中的最小空闲连接
+          min-idle: 0
+          # 连接池中的最大空闲连接
+          max-idle: 8
+          # 连接池的最大数据库连接数
+          max-active: 8
+          # #连接池最大阻塞等待时间(使用负值表示没有限制)
+          max-wait: -1ms
+
+mybatis-plus:
+  mapper-locations: classpath*:/mybatis/mapper/**/*.xml
+  configuration:
+    map-underscore-to-camel-case: false
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+    #map-underscore-to-camel-case=false
+
+# 文件上传
+servlet:
+   multipart:
+     # 单个文件大小
+     max-file-size:  500MB
+     # 设置总上传的文件大小
+     max-request-size:  500MB
+
+http_pool:
+  max_total: 200
+  default_max_per_route: 100
+  connect_timeout: 5000
+  connection_request_timeout: 1000
+  socket_timeout: 65000
+  validate_after_inactivity: 2000

+ 129 - 0
src/main/resources/application-prod.yml

@@ -0,0 +1,129 @@
+server:
+  port: 1234
+
+live:
+  # 名称
+  name: shs_live
+  # 版本
+  version: 0.0.1
+  # 服务地址
+  baseUrl: 127.0.0.1/conver/
+  # 文件保存路径
+  profile: /Users/project/upload/conver
+  # 是否删除原始文件
+  sourceDelete: false
+  # 原始文件路径
+  sourceProfile: /Users/project/upload/base
+  # 视频编码格式
+  videoCodec: libx264
+  # 视频针率 每秒29帧
+  frameRate: 24
+  # ffmpeg路径
+  ffmpegPath: /usr/bin/ffmpeg
+  # ffprobe路径
+  ffprobePath: /usr/bin/ffprobe
+  # 浮动码率
+  rate: false
+  # 视频码率 768KHz
+  videoRate: 768_000
+  # 缓冲大小 1M
+  bufSize: 1024_000
+  # 最小码率 328KHz
+  minRate: 328_000
+  # 最大码率 768Khz
+  maxRate: 768_000
+  # 音频编码格式
+  audioCodec: aac
+  # 音频通道
+  audioChannels: 1
+  # 音频码率 32kbit/s
+  audioRate: 32768
+  # 采样率 8KHZ
+  sampling: 8_000
+  # 等比缩放(true开启默认开启)
+  zoom: true
+  # 缩放尺寸
+  zoomSize: 760
+  # 强制调整分辨率
+  relove: false
+  reloveSize: 1920x1080
+  # 视频切片时间(单位s)
+  segmentTime: 10
+  # 视频封面切割尺寸 默认以缩放比例切割
+  coverSize:
+  # 切割针率 毫秒级
+  coverT: 0.001
+  # 视频格式
+  format: hls
+  # 切片时间 10S
+  hlsTime: 10
+  # 切片开始编号 默认0
+  hlsStartNumber: 0
+  # 多少片之后开始覆盖 (0则不会覆盖,默认值为0)
+  hlsWrap: 0
+  # 总共切片数量 (0会保存有所片信息,默认值为5)
+  hlsListSize: 0
+
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://10.31.154.21:3306/db_live?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
+    username: root
+    password: Csdy_007
+    druid:
+      initial-size: 8
+      min-idle: 1
+      max-active: 20
+      max-wait: 60000
+  servlet:
+     multipart:
+       # 单个文件大小
+       max-file-size:  500MB
+       # 设置总上传的文件大小
+       max-request-size:  500MB
+  jackson:
+    date-format: yyyy-MM-dd
+    time-zone: GMT+8
+  #redis 配置
+  redis:
+      # 地址
+      host: 127.0.0.1
+      # 端口,默认为6379
+      port: 6379
+      # 密码
+      password:
+      # 连接超时时间
+      timeout: 10s
+      lettuce:
+        pool:
+          # 连接池中的最小空闲连接
+          min-idle: 0
+          # 连接池中的最大空闲连接
+          max-idle: 8
+          # 连接池的最大数据库连接数
+          max-active: 8
+          # #连接池最大阻塞等待时间(使用负值表示没有限制)
+          max-wait: -1ms
+
+mybatis-plus:
+  mapper-locations: classpath*:/mybatis/mapper/**/*.xml
+  configuration:
+    map-underscore-to-camel-case: false
+    #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+    #map-underscore-to-camel-case=false
+
+# 文件上传
+servlet:
+   multipart:
+     # 单个文件大小
+     max-file-size:  1024MB
+     # 设置总上传的文件大小
+     max-request-size:  1024MB
+
+http_pool:
+  max_total: 200
+  default_max_per_route: 100
+  connect_timeout: 5000
+  connection_request_timeout: 1000
+  socket_timeout: 65000
+  validate_after_inactivity: 2000

+ 12 - 0
src/main/resources/application.yml

@@ -0,0 +1,12 @@
+spring:
+  profiles:
+    active: prod
+
+# token配置
+token:
+  # 令牌自定义标识
+  header: x-token
+  # 令牌密钥
+  key: x-key
+  # 令牌有效期(默认30分钟)
+  expireTime: 60

+ 66 - 0
src/main/resources/mybatis/mapper/MediaMapper.xml

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.shs.official.mapper.media.MediaMapper">
+
+
+    <insert id="save" parameterType="com.shs.official.entity.media.TbMedia" keyProperty="id" keyColumn="id" useGeneratedKeys="true">
+        INSERT INTO tb_media
+        (`id` ,`name` ,`md5` ,`rtsp`, `amr` ,`thumg` ,`m3u8` ,`sources`,`codec` ,`rate` ,`audio_codec`,`type` ,base_url,`created` )
+        VALUES
+        (#{id} ,#{name} ,#{md5} ,#{rtsp}, #{amr} ,#{thumg} ,#{m3u8} ,#{sources} ,#{codec} ,#{rate} ,#{audio_codec} ,#{type} ,#{base_url},#{created})
+    </insert>
+
+    <update id="updateRtsp" parameterType="com.shs.official.entity.media.TbMedia" keyProperty="id" keyColumn="id" useGeneratedKeys="true">
+        update tb_media
+        <set>
+            rtsp = #{rtsp},
+            codec = #{codec},
+            rate = #{rate},
+            audio_codec = #{audio_codec}
+        </set>
+        where id = #{id}
+    </update>
+
+    <update id="updateM3u8" parameterType="com.shs.official.entity.media.TbMedia" keyProperty="id" keyColumn="id" useGeneratedKeys="true">
+        update tb_media
+        <set>
+            m3u8 = #{m3u8},
+            codec = #{codec},
+            rate = #{rate},
+            audio_codec = #{audio_codec}
+        </set>
+        where id = #{id}
+    </update>
+
+    <update id="updateThumg" parameterType="com.shs.official.entity.media.TbMedia" keyProperty="id" keyColumn="id" useGeneratedKeys="true">
+        update tb_media
+        <set>
+            thumg = #{thumg}
+        </set>
+        where id = #{id}
+    </update>
+
+    <update id="updateAmr" parameterType="com.shs.official.entity.media.TbMedia" keyProperty="id" keyColumn="id" useGeneratedKeys="true">
+        update tb_media
+        <set>
+            amr = #{amr}
+        </set>
+        where id = #{id}
+    </update>
+
+    <select id="findById" parameterType="java.lang.Long" resultType="com.shs.official.entity.media.TbMedia">
+        select * from tb_media where id = #{id}
+    </select>
+
+    <select id="findByMd5" parameterType="java.lang.String" resultType="com.shs.official.entity.media.TbMedia">
+        select * from tb_media where md5 = #{md5}
+    </select>
+
+    <select id="page" resultType="com.shs.official.entity.media.TbMedia">
+        select * from tb_media where type = 0
+    </select>
+
+    <delete id="del" parameterType="java.lang.Long">
+        DELETE FROM tb_media WHERE id = #{id}
+    </delete>
+</mapper>