diff --git a/.gitignore b/.gitignore
index 9154f4c..dcfad87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,4 +23,36 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
+
diff --git a/mvnw b/mvnw
new file mode 100644
index 0000000..21d3ee8
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..84d60ab
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..856cb9d
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,349 @@
+
+
+ 4.0.0
+
+ com.chenxi
+ chenxi
+ 0.0.1-SNAPSHOT
+ chenxi
+ Demo project for Spring Boot
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.1.RELEASE
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 2.9.2
+ 1.3.2
+ 1.2.5
+ 1.2.47
+ 1.1.14
+ 2.5
+ 1.3.3
+ 1.19
+ 0.9.0
+ 2.9.2
+ 3.17
+ 3.9.1
+ 5.0.6
+ 3.2.0
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+
+
+ org.apache.commons
+ commons-pool2
+
+
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+
+
+
+
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ ${mybatisplus.version}
+
+
+
+
+
+ com.alibaba
+ druid-spring-boot-starter
+ ${druid.version}
+
+
+
+
+ cn.hutool
+ hutool-all
+ ${hutool.version}
+
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+ commons-io
+ commons-io
+ ${commons.io.version}
+
+
+
+
+ commons-fileupload
+ commons-fileupload
+ ${commons.fileupload.version}
+
+
+
+
+ eu.bitwalker
+ UserAgentUtils
+ ${bitwalker.version}
+
+
+
+
+ com.alibaba
+ fastjson
+ ${fastjson.version}
+
+
+
+
+ org.springframework
+ spring-context-support
+
+
+
+
+ io.jsonwebtoken
+ jjwt
+ ${jwt.version}
+
+
+
+
+ io.springfox
+ springfox-swagger2
+ ${swagger.version}
+
+
+ io.swagger
+ swagger-annotations
+
+
+ io.swagger
+ swagger-models
+
+
+
+
+
+
+ io.swagger
+ swagger-annotations
+ 1.5.21
+
+
+
+ io.swagger
+ swagger-models
+ 1.5.21
+
+
+
+
+ io.springfox
+ springfox-swagger-ui
+ ${swagger.version}
+
+
+
+
+ com.github.oshi
+ oshi-core
+ ${oshi.version}
+
+
+
+ net.java.dev.jna
+ jna
+
+
+
+ net.java.dev.jna
+ jna-platform
+
+
+
+
+ org.apache.poi
+ poi-ooxml
+ ${poi.version}
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
+
+
+ io.springfox
+ springfox-swagger2
+ ${swagger.version}
+
+
+ io.springfox
+ springfox-swagger-ui
+ ${swagger.version}
+
+
+
+ org.quartz-scheduler
+ quartz
+ 2.3.0
+
+
+
+ com.Algorithm_vibration
+ Algorithm_vibration
+ system
+ 1.0
+ ${project.basedir}/src/main/resources/lib/Algorithm_vibration-1.0.jar
+
+
+ com.javabuilder
+ javabuilder
+ system
+ 1.0
+ ${project.basedir}/src/main/resources/lib/javabuilder-1.0.jar
+
+
+ com.arraytest
+ arraytest
+ system
+ 1.0
+ ${project.basedir}/src/main/resources/lib/arraytest-1.0.jar
+
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+
+
+
+
+
+
+
+ public
+ aliyun nexus
+ http://maven.aliyun.com/nexus/content/groups/public/
+
+ true
+
+
+
+
+
+
+ public
+ aliyun nexus
+ http://maven.aliyun.com/nexus/content/groups/public/
+
+ true
+
+
+ false
+
+
+
+
+
diff --git a/src/main/java/TestMain.java b/src/main/java/TestMain.java
new file mode 100644
index 0000000..e29c792
--- /dev/null
+++ b/src/main/java/TestMain.java
@@ -0,0 +1,7 @@
+public class TestMain {
+
+// public static void main(String[] args)
+// {
+// new BigDecimal(0.002364345566222).setScale(4, RoundingMode.UP).doubleValue();
+// }
+}
diff --git a/src/main/java/com/chenxuan/ChenxiApplication.java b/src/main/java/com/chenxuan/ChenxiApplication.java
new file mode 100644
index 0000000..34efa2a
--- /dev/null
+++ b/src/main/java/com/chenxuan/ChenxiApplication.java
@@ -0,0 +1,18 @@
+package com.chenxuan;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+
+@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
+@ComponentScan(value = "com.chenxuan")
+@MapperScan("com.chenxuan.mapper")
+@SpringBootApplication
+public class ChenxiApplication {
+ public static void main(String[] args) {
+ SpringApplication.run( ChenxiApplication.class, args );
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/chenxuan/base/controller/BaseController.java b/src/main/java/com/chenxuan/base/controller/BaseController.java
new file mode 100644
index 0000000..2bd17d0
--- /dev/null
+++ b/src/main/java/com/chenxuan/base/controller/BaseController.java
@@ -0,0 +1,55 @@
+package com.chenxuan.base.controller;
+
+
+import com.chenxuan.base.entity.AjaxResult;
+
+import com.chenxuan.entity.model.SysUser;
+import com.chenxuan.security.LoginUser;
+import com.chenxuan.security.service.JwtTokenService;
+import com.chenxuan.utils.ServletUtils;
+import com.chenxuan.utils.spring.SpringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+
+import java.beans.PropertyEditorSupport;
+import java.util.Date;
+
+import com.chenxuan.utils.DateUtils;
+
+public class BaseController {
+
+
+ private JwtTokenService jwtTokenService = SpringUtils.getBean( JwtTokenService.class );
+
+ public LoginUser getLoginInfo(){
+ LoginUser loginUser = jwtTokenService.getLoginUser( ServletUtils.getRequest() );
+ return loginUser ;
+ }
+
+ public SysUser getLoginUser(){
+ LoginUser loginUser = jwtTokenService.getLoginUser( ServletUtils.getRequest() );
+ if(null != loginUser){
+ return loginUser.getUser();
+ }
+ return null ;
+ }
+
+ /**
+ * 将前台传递过来的日期格式的字符串,自动转化为Date类型
+ */
+ @InitBinder
+ public void initBinder(WebDataBinder binder) {
+ // Date 类型转换
+ binder.registerCustomEditor( Date.class, new PropertyEditorSupport() {
+ @Override
+ public void setAsText(String text) {
+ setValue( DateUtils.parseDate( text ) );
+ }
+ } );
+ }
+
+ public AjaxResult returnAjax(boolean flag) {
+ return flag ? AjaxResult.success() : AjaxResult.error();
+ }
+}
diff --git a/src/main/java/com/chenxuan/base/entity/AjaxResult.java b/src/main/java/com/chenxuan/base/entity/AjaxResult.java
new file mode 100644
index 0000000..44f8ec8
--- /dev/null
+++ b/src/main/java/com/chenxuan/base/entity/AjaxResult.java
@@ -0,0 +1,146 @@
+package com.chenxuan.base.entity;
+
+
+import com.chenxuan.constants.HttpStatusCode;
+
+import java.util.HashMap;
+
+/**
+ * @ClassName:AjaxResult
+ * @Description: Ajax操作返回
+ * @Author: Arno_Fu
+ * @CreatTime:11/26/2019 - 12:51 PM
+ * @Version V1.0
+ */
+public class AjaxResult extends HashMap {
+
+ /**
+ * 状态码
+ */
+ public static final String CODE_TAG = "code";
+
+ /**
+ * 返回内容
+ */
+ public static final String MSG_TAG = "msg";
+
+ /**
+ * 数据对象
+ */
+ public static final String DATA_TAG = "data";
+
+ /**
+ * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
+ */
+ public AjaxResult() {
+
+ }
+
+ /**
+ * 初始化一个新创建的 AjaxResult 对象
+ *
+ * @param code 状态码
+ * @param msg 返回内容
+ */
+ public AjaxResult(int code, String msg) {
+ super.put(CODE_TAG, code);
+ super.put(MSG_TAG, msg);
+ }
+
+ /**
+ * 初始化一个新创建的 AjaxResult 对象
+ *
+ * @param code 状态码
+ * @param msg 返回内容
+ * @param data 数据对象
+ */
+ public AjaxResult(int code, String msg, Object data) {
+ super.put(CODE_TAG, code);
+ super.put(MSG_TAG, msg);
+ if (data != null) {
+ super.put(DATA_TAG, data);
+ }
+ }
+
+
+ /**
+ * 返回成功消息
+ *
+ * @return 成功消息
+ */
+ public static AjaxResult success() {
+ return AjaxResult.success("操作成功");
+ }
+
+ /**
+ * 返回成功数据
+ *
+ * @return 成功消息
+ */
+ public static AjaxResult success(Object data) {
+ return AjaxResult.success("操作成功", data);
+ }
+
+ /**
+ * 返回成功消息
+ *
+ * @param msg 返回内容
+ * @return 成功消息
+ */
+ public static AjaxResult success(String msg) {
+ return AjaxResult.success(msg, null);
+ }
+
+ /**
+ * 返回成功消息
+ *
+ * @param msg 返回内容
+ * @param data 数据对象
+ * @return 成功消息
+ */
+ public static AjaxResult success(String msg, Object data) {
+ return new AjaxResult(HttpStatusCode.SUCCESS, msg, data);
+ }
+
+
+ /**
+ * 返回错误消息
+ *
+ * @return
+ */
+ public static AjaxResult error() {
+ return AjaxResult.error("操作失败");
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @param msg 返回内容
+ * @return 警告消息
+ */
+ public static AjaxResult error(String msg) {
+ return AjaxResult.error(msg, null);
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @param msg 返回内容
+ * @param data 数据对象
+ * @return 警告消息
+ */
+ public static AjaxResult error(String msg, Object data) {
+ return new AjaxResult(HttpStatusCode.ERROR, msg, data);
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @param code 状态码
+ * @param msg 返回内容
+ * @return 警告消息
+ */
+ public static AjaxResult error(int code, String msg) {
+ return new AjaxResult(code, msg, null);
+ }
+}
diff --git a/src/main/java/com/chenxuan/base/entity/CustomQuartzJobBean.java b/src/main/java/com/chenxuan/base/entity/CustomQuartzJobBean.java
new file mode 100644
index 0000000..8d2901a
--- /dev/null
+++ b/src/main/java/com/chenxuan/base/entity/CustomQuartzJobBean.java
@@ -0,0 +1,51 @@
+package com.chenxuan.base.entity;
+
+import com.chenxuan.entity.model.ScheduleJob;
+import com.chenxuan.utils.ScheduleRunnable;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.PersistJobDataAfterExecution;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * @ClassName:DynamicQuartzJobManager
+ * @Description: 任务动态调用
+ * @Author: Arno_Fu
+ * @CreatTime:12/25/2019 - 5:49 PM
+ * @Version V1.0
+ */
+@Slf4j
+@PersistJobDataAfterExecution
+@DisallowConcurrentExecution// 不允许并发执行
+public class CustomQuartzJobBean extends QuartzJobBean {
+
+
+ private ExecutorService service = Executors.newSingleThreadExecutor();
+
+ @Override
+ protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
+ //任务开始时间
+ ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get( JobConstant.JOB_PARAM_KEY );
+ //任务开始时间
+ long startTime = System.currentTimeMillis();
+ try {
+ ScheduleRunnable task = new ScheduleRunnable( scheduleJob.getBeanName(),
+ scheduleJob.getMethodName(), scheduleJob.getMethodParams() );
+ Future> future = service.submit( task );
+ Object object = future.get();
+
+ } catch (Exception e) {
+ log.error( "任务执行失败,任务ID:" + scheduleJob.getJobId(), e );
+
+ } finally {
+
+ }
+
+
+ }
+}
diff --git a/src/main/java/com/chenxuan/base/entity/JobConstant.java b/src/main/java/com/chenxuan/base/entity/JobConstant.java
new file mode 100644
index 0000000..7b78cdc
--- /dev/null
+++ b/src/main/java/com/chenxuan/base/entity/JobConstant.java
@@ -0,0 +1,21 @@
+package com.chenxuan.base.entity;
+
+
+public class JobConstant {
+
+ /**
+ * 任务调度参数key
+ */
+ public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";
+
+ /**
+ * 任务调度
+ */
+ public final static String JOB_KEY = "JOB_KEY";
+
+
+ /**
+ * 任务调度
+ */
+ public final static String TRIGGER_kEY = "TRIGGER_kEY";
+}
diff --git a/src/main/java/com/chenxuan/base/entity/JobOperateEnum.java b/src/main/java/com/chenxuan/base/entity/JobOperateEnum.java
new file mode 100644
index 0000000..a5625e9
--- /dev/null
+++ b/src/main/java/com/chenxuan/base/entity/JobOperateEnum.java
@@ -0,0 +1,40 @@
+package com.chenxuan.base.entity;
+
+
+
+
+/**
+ * @ClassName:JobOperateEnum
+ * @Description: 定时任务
+ * @Author: Arno_Fu
+ * @CreatTime:12/25/2019 - 3:51 PM
+ * @Version V1.0
+ */
+
+public enum JobOperateEnum {
+
+ START( "1", "启动" ),
+ PAUSE( "2", "暂停" ),
+ DELETE( "3", "删除" );
+
+ private final String value;
+ private final String desc;
+
+ JobOperateEnum(final String value, final String desc) {
+ this.value = value;
+ this.desc = desc;
+ }
+
+ public String getValue() {
+ return this.value;
+ }
+
+ // Jackson 注解为 JsonValue 返回中文 json 描述
+ public String getDesc() {
+ return this.desc;
+ }
+
+ public String getEnumName() {
+ return name();
+ }
+}
diff --git a/src/main/java/com/chenxuan/base/entity/Query.java b/src/main/java/com/chenxuan/base/entity/Query.java
new file mode 100644
index 0000000..30101c2
--- /dev/null
+++ b/src/main/java/com/chenxuan/base/entity/Query.java
@@ -0,0 +1,65 @@
+package com.chenxuan.base.entity;
+
+
+import com.alibaba.fastjson.JSON;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Query implements Serializable {
+
+ public String page = "1";
+ public String limit = "10";
+ public String orderByField = "";
+ public String isAsc = "";
+
+ /**
+ * 请求参数
+ */
+ private String params;
+
+ public Integer getPage() {
+ return Integer.parseInt( page );
+ }
+
+ public void setPage(String page) {
+ this.page = page;
+ }
+
+ public Integer getLimit() {
+ return Integer.parseInt( limit );
+ }
+
+ public void setLimit(String limit) {
+ this.limit = limit;
+ }
+
+ public String getOrderByField() {
+ return orderByField;
+ }
+
+ public void setOrderByField(String orderByField) {
+ this.orderByField = orderByField;
+ }
+
+ public String getIsAsc() {
+ return isAsc;
+ }
+
+ public void setIsAsc(String isAsc) {
+ this.isAsc = isAsc;
+ }
+
+ public Map getParams() {
+ if (params == null) {
+ return new HashMap<>();
+ }
+ return JSON.parseObject(params , Map.class );
+ }
+
+ public void setParams(String params) {
+ this.params = params;
+ }
+
+}
diff --git a/src/main/java/com/chenxuan/base/exception/GlobalExceptionHandler.java b/src/main/java/com/chenxuan/base/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..1e2ef27
--- /dev/null
+++ b/src/main/java/com/chenxuan/base/exception/GlobalExceptionHandler.java
@@ -0,0 +1,105 @@
+package com.chenxuan.base.exception;
+
+
+import cn.hutool.core.util.StrUtil;
+import com.chenxuan.base.entity.AjaxResult;
+import com.chenxuan.constants.HttpStatusCode;
+import com.chenxuan.exception.BaseException;
+import com.chenxuan.exception.CustomException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.AccountExpiredException;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.validation.BindException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.NoHandlerFoundException;
+
+
+/**
+ * @ClassName:GlobalExceptionHandler
+ * @Description: 全局异常处理器
+ * @Author: Arno_Fu
+ * @CreatTime:11/26/2019 - 9:57 PM
+ * @Version V1.0
+ */
+@Slf4j
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ /**
+ * 基础异常
+ */
+ @ExceptionHandler(BaseException.class)
+ public AjaxResult baseException(BaseException e) {
+ return AjaxResult.error( e.getMessage() );
+ }
+
+ /**
+ * 业务异常
+ */
+ @ExceptionHandler(CustomException.class)
+ public AjaxResult businessException(CustomException e) {
+ if (e.getCode() == null)
+ {
+ return AjaxResult.error(e.getMessage());
+ }
+ return AjaxResult.error( e.getCode(), e.getMessage() );
+ }
+
+ @ExceptionHandler(NoHandlerFoundException.class)
+ public AjaxResult handlerNoFoundException(Exception e) {
+ log.error( e.getMessage(), e );
+ return AjaxResult.error( HttpStatusCode.NOT_FOUND, "路径不存在,请检查路径是否正确" );
+ }
+
+ @ExceptionHandler(AccessDeniedException.class)
+ public AjaxResult handleAuthorizationException(AccessDeniedException e) {
+ log.error( e.getMessage() );
+ return AjaxResult.error( HttpStatusCode.FORBIDDEN, "没有权限,请联系管理员授权" );
+ }
+
+ @ExceptionHandler(AccountExpiredException.class)
+ public AjaxResult handleAccountExpiredException(AccountExpiredException e) {
+ log.error( e.getMessage(), e );
+ return AjaxResult.error( e.getMessage() );
+ }
+
+ @ExceptionHandler(UsernameNotFoundException.class)
+ public AjaxResult handleUsernameNotFoundException(UsernameNotFoundException e) {
+ log.error( e.getMessage(), e );
+ return AjaxResult.error( e.getMessage() );
+ }
+
+ @ExceptionHandler(Exception.class)
+ public AjaxResult handleException(Exception e) {
+ log.error( e.getMessage(), e );
+ return AjaxResult.error( e.getMessage() );
+ }
+
+ /**
+ * 自定义验证异常
+ */
+ @ExceptionHandler(BindException.class)
+ public AjaxResult validatedBindException(BindException e) {
+ log.error( e.getMessage(), e );
+ String message = e.getAllErrors().get( 0 ).getDefaultMessage();
+ return AjaxResult.error( message );
+ }
+
+ /**
+ * 自定义验证异常
+ */
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public Object validExceptionHandler(MethodArgumentNotValidException e) {
+ log.error( e.getMessage(), e );
+ String message = e.getBindingResult().getFieldError().getDefaultMessage();
+ return AjaxResult.error( message );
+ }
+}
+
diff --git a/src/main/java/com/chenxuan/bean/annotation/DsAnnotation.java b/src/main/java/com/chenxuan/bean/annotation/DsAnnotation.java
new file mode 100644
index 0000000..bdd6229
--- /dev/null
+++ b/src/main/java/com/chenxuan/bean/annotation/DsAnnotation.java
@@ -0,0 +1,27 @@
+package com.chenxuan.bean.annotation;
+
+
+import com.chenxuan.enums.DataSourceType;
+
+import java.lang.annotation.*;
+
+
+/**
+ * @ClassName:DsAnnotation
+ * @Description: 自定义多数据源切换注解
+ * @Author: Arno_Fu
+ * @CreatTime:12/23/2019 - 5:21 PM
+ * @Version V1.0
+ */
+
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface DsAnnotation {
+
+ /**
+ * 切换数据源名称
+ */
+ public DataSourceType value() default DataSourceType.MASTER;
+}
diff --git a/src/main/java/com/chenxuan/bean/annotation/LogAnnotation.java b/src/main/java/com/chenxuan/bean/annotation/LogAnnotation.java
new file mode 100644
index 0000000..0f09770
--- /dev/null
+++ b/src/main/java/com/chenxuan/bean/annotation/LogAnnotation.java
@@ -0,0 +1,46 @@
+package com.chenxuan.bean.annotation;
+
+import com.chenxuan.enums.ClientType;
+import com.chenxuan.enums.OperateType;
+
+import java.lang.annotation.*;
+
+/**
+ * @ClassName:LogAnnotation
+ * @Description: 日志注解
+ * @Author: Arno_Fu
+ * @CreatTime:11/26/2019 - 4:18 PM
+ * @Version V1.0
+ */
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface LogAnnotation {
+
+ /**
+ * 模块
+ */
+ String module();
+
+
+ /**
+ * 记录参数值
+ */
+ boolean recordParam() default true;
+
+
+ /**
+ * 务操作类型
+ */
+ public OperateType operateType() default OperateType.OTHER;
+
+
+ /**
+ * 务操作类型
+ */
+ public ClientType clientType() default ClientType.MANAGE;
+
+
+}
+
diff --git a/src/main/java/com/chenxuan/bean/aop/DataSourceAop.java b/src/main/java/com/chenxuan/bean/aop/DataSourceAop.java
new file mode 100644
index 0000000..61d4c40
--- /dev/null
+++ b/src/main/java/com/chenxuan/bean/aop/DataSourceAop.java
@@ -0,0 +1,70 @@
+package com.chenxuan.bean.aop;
+
+
+import com.chenxuan.bean.annotation.DsAnnotation;
+import com.chenxuan.bean.datasource.DynamicDataSourceContextHolder;
+import com.chenxuan.utils.StringUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * @ClassName:DataSourceAop
+ * @Description: 多数据源处理
+ * @Author: Arno_Fu
+ * @CreatTime:12/23/2019 - 5:33 PM
+ * @Version V1.0
+ */
+@Aspect
+@Order(1)
+@Component
+public class DataSourceAop {
+ protected Logger logger = LoggerFactory.getLogger( getClass() );
+
+ @Pointcut("@annotation(com.chenxuan.bean.annotation.DsAnnotation)"
+ + "|| @within(com.chenxuan.bean.annotation.DsAnnotation)")
+ public void dsPointCut() {
+
+ }
+
+ @Around("dsPointCut()")
+ public Object around(ProceedingJoinPoint point) throws Throwable {
+ DsAnnotation dataSource = getDataSource( point );
+
+ if (StringUtils.isNotNull( dataSource )) {
+ DynamicDataSourceContextHolder.setDataSourceType( dataSource.value().name() );
+ }
+
+ try {
+ return point.proceed();
+ } finally {
+ // 销毁数据源 在执行方法之后
+ DynamicDataSourceContextHolder.clearDataSourceType();
+ }
+ }
+
+ /**
+ * 获取需要切换的数据源
+ */
+ public DsAnnotation getDataSource(ProceedingJoinPoint point) {
+ MethodSignature signature = (MethodSignature) point.getSignature();
+ Class extends Object> targetClass = point.getTarget().getClass();
+ DsAnnotation targetDataSource = targetClass.getAnnotation( DsAnnotation.class );
+ if (StringUtils.isNotNull( targetDataSource )) {
+ return targetDataSource;
+ } else {
+ Method method = signature.getMethod();
+ DsAnnotation dataSource = method.getAnnotation( DsAnnotation.class );
+ return dataSource;
+ }
+ }
+}
diff --git a/src/main/java/com/chenxuan/bean/aop/LogAop.java b/src/main/java/com/chenxuan/bean/aop/LogAop.java
new file mode 100644
index 0000000..98b6ad7
--- /dev/null
+++ b/src/main/java/com/chenxuan/bean/aop/LogAop.java
@@ -0,0 +1,257 @@
+package com.chenxuan.bean.aop;
+
+
+import com.alibaba.fastjson.JSON;
+import com.chenxuan.bean.annotation.LogAnnotation;
+import com.chenxuan.entity.model.SysOperLog;
+import com.chenxuan.enums.BusinessStatus;
+import com.chenxuan.enums.HttpMethod;
+import com.chenxuan.manager.AsyncManager;
+import com.chenxuan.manager.factory.AsyncFactory;
+import com.chenxuan.security.LoginUser;
+import com.chenxuan.security.service.JwtTokenService;
+import com.chenxuan.utils.ServletUtils;
+import com.chenxuan.utils.StringUtils;
+import com.chenxuan.utils.ip.IpUtils;
+import com.chenxuan.utils.spring.SpringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.HandlerMapping;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.Map;
+
+
+/**
+ * @ClassName:LogAop
+ * @Description: 日志切面.
+ * @Author: Arno_Fu
+ * @CreatTime:11/26/2019 - 4:20 PM
+ * @Version V1.0
+ */
+
+@Slf4j
+@Aspect
+@Component
+public class LogAop {
+
+ @Pointcut("@annotation(com.chenxuan.bean.annotation.LogAnnotation)")
+ public void logPointCut() {
+
+ }
+
+
+ /**
+ * 处理完请求后执行
+ *
+ * @param joinPoint 切点
+ */
+ @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
+ public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
+ handleLog( joinPoint, null, jsonResult );
+ }
+
+ /**
+ * 拦截异常操作
+ *
+ * @param joinPoint 切点
+ * @param e 异常
+ */
+ @AfterThrowing(value = "logPointCut()", throwing = "e")
+ public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
+ handleLog( joinPoint, e, null );
+ }
+
+ protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
+ try {
+ // 获得注解
+ LogAnnotation controllerLog = getAnnotationLog( joinPoint );
+ if (controllerLog == null) {
+ return;
+ }
+
+ // 获取当前的用户
+ LoginUser loginUser = SpringUtils.getBean( JwtTokenService.class ).getLoginUser( ServletUtils.getRequest() );
+
+ // *========数据库日志=========*//
+ SysOperLog operLog = new SysOperLog();
+ operLog.setStatus( BusinessStatus.SUCCESS.ordinal() );
+ // 请求的地址
+ String ip = IpUtils.getIpAddr( ServletUtils.getRequest() );
+ operLog.setOperIp( ip );
+ // 返回参数
+ operLog.setJsonResult( JSON.toJSONString( jsonResult ) );
+ operLog.setOperUrl( ServletUtils.getRequest().getRequestURI() );
+ if (loginUser != null) {
+ operLog.setOperName( loginUser.getUsername() );
+ }
+
+ if (e != null) {
+ operLog.setStatus( BusinessStatus.FAIL.ordinal() );
+ operLog.setErrorMsg( StringUtils.substring( e.getMessage(), 0, 2000 ) );
+ }
+ // 设置方法名称
+ String className = joinPoint.getTarget().getClass().getName();
+ String methodName = joinPoint.getSignature().getName();
+ operLog.setMethod( className + "." + methodName + "()" );
+ // 设置请求方式
+ operLog.setRequestMethod( ServletUtils.getRequest().getMethod() );
+ operLog.setOperTime( new Date() );
+ // 处理设置注解上的参数
+ getControllerMethodDescription( joinPoint, controllerLog, operLog );
+
+ // 保存数据库
+ AsyncManager.me().execute( AsyncFactory.recordOperLog( operLog ) );
+ } catch (Exception exp) {
+ // 记录本地异常日志
+ log.error( "==前置通知异常==" );
+ log.error( "异常信息:{}", exp.getMessage() );
+ exp.printStackTrace();
+ }
+ }
+
+
+ /**
+ * 获取注解中对方法的描述信息 用于Controller层注解
+ *
+ * @param log 日志
+ * @param operLog 操作日志
+ * @throws Exception
+ */
+ public void getControllerMethodDescription(JoinPoint joinPoint, LogAnnotation log, SysOperLog operLog) throws Exception {
+ // 设置action动作
+ operLog.setOperatorType( log.operateType().ordinal() );
+ // 设置标题
+ operLog.setTitle( log.module() );
+ // 设置操作人类别
+ operLog.setOperatorType( log.clientType().ordinal() );
+ // 是否需要保存request,参数和值
+ if (log.recordParam()) {
+ // 获取参数的信息,传入到数据库中。
+ setRequestValue( joinPoint, operLog );
+ }
+ }
+
+ /**
+ * 获取请求的参数,放到log中
+ *
+ * @param operLog 操作日志
+ * @throws Exception 异常
+ */
+ private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception {
+ String requestMethod = operLog.getRequestMethod();
+ if (HttpMethod.PUT.name().equals( requestMethod ) || HttpMethod.POST.name().equals( requestMethod )) {
+ String params = argsArrayToString( joinPoint.getArgs() );
+ operLog.setOperParam( StringUtils.substring( params, 0, 2000 ) );
+ } else {
+ Map, ?> paramsMap = (Map, ?>) ServletUtils.getRequest().getAttribute( HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE );
+ operLog.setOperParam( StringUtils.substring( paramsMap.toString(), 0, 2000 ) );
+ }
+ }
+
+ /**
+ * 是否存在注解,如果存在就获取
+ */
+ private LogAnnotation getAnnotationLog(JoinPoint joinPoint) throws Exception {
+ Signature signature = joinPoint.getSignature();
+ MethodSignature methodSignature = (MethodSignature) signature;
+ Method method = methodSignature.getMethod();
+
+ if (method != null) {
+ return method.getAnnotation( LogAnnotation.class );
+ }
+ return null;
+ }
+
+ /**
+ * 参数拼装
+ */
+ private String argsArrayToString(Object[] paramsArray) {
+ String params = "";
+ if (paramsArray != null && paramsArray.length > 0) {
+ for (int i = 0; i < paramsArray.length; i++) {
+ if (!isFilterObject( paramsArray[i] )) {
+ Object jsonObj = JSON.toJSON( paramsArray[i] );
+ params += jsonObj.toString() + " ";
+ }
+ }
+ }
+ return params.trim();
+ }
+
+ /**
+ * 判断是否需要过滤的对象。
+ *
+ * @param o 对象信息。
+ * @return 如果是需要过滤的对象,则返回true;否则返回false。
+ */
+ public boolean isFilterObject(final Object o) {
+ return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
+ }
+
+ /* @Around("logPointCut()")
+ public Object around(ProceedingJoinPoint point) throws Throwable {
+
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+ HttpServletRequest request = attributes.getRequest();
+
+ LogVO logVO = new LogVO( );
+
+ logVO.setCreateTime( new Date( ) );
+ logVO.setUserName("");
+ logVO.setIpAddr( "" );
+ logVO.setRequestUri( request.getRequestURL().toString() );
+
+
+ MethodSignature signature = (MethodSignature) point.getSignature();
+ Method method = signature.getMethod();
+ //请求的方法名
+ String className = point.getTarget().getClass().getName();
+ String methodName = signature.getName();
+ logVO.setMethod( className + "." + methodName );
+
+
+ LogAnnotation logAnnotation = method.getDeclaredAnnotation( LogAnnotation.class );
+ logVO.setModule( logAnnotation.module() );
+
+ if(logAnnotation.recordParam()){
+ String[] parameterNames = signature.getParameterNames();
+ if(null != parameterNames && parameterNames.length > 0){
+ Map params = new HashMap<>( );
+
+ Object[] args = point.getArgs();
+ for (int i = 0 ; i < parameterNames.length; i++ ) {
+ params.put( parameterNames [i], args[i] );
+ }
+
+ try {
+ logVO.setParams( JSONObject.toJSONString( params ) );
+ } catch (Exception e) {
+ log.error( "记录日志失败:{}", e.getMessage() );
+ }
+ }
+ }
+ try {
+ Object object = point.proceed();
+ logVO.setFlag( "1" );
+ return object;
+ } catch (Exception e) {
+ logVO.setFlag( "0" );
+ logVO.setMark( e.getMessage() );
+ throw e;
+ }finally {
+ // TODO 保存日志
+ }
+ }*/
+}
\ No newline at end of file
diff --git a/src/main/java/com/chenxuan/bean/component/RedisCacheService.java b/src/main/java/com/chenxuan/bean/component/RedisCacheService.java
new file mode 100644
index 0000000..4efbc07
--- /dev/null
+++ b/src/main/java/com/chenxuan/bean/component/RedisCacheService.java
@@ -0,0 +1,205 @@
+package com.chenxuan.bean.component;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.*;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @ClassName:RedisCacheService
+ * @Description: redis
+ * @Author: Arno_Fu
+ * @CreatTime:11/26/2019 - 6:40 PM
+ * @Version V1.0
+ */
+
+@Component
+public class RedisCacheService {
+
+ @Autowired
+ public RedisTemplate redisTemplate;
+
+ /**
+ * 缓存基本的对象,Integer、String、实体类等
+ *
+ * @param key 缓存的键值
+ * @param value 缓存的值
+ * @return 缓存的对象
+ */
+ public ValueOperations setCacheObject(String key, T value)
+ {
+ ValueOperations operation = redisTemplate.opsForValue();
+ operation.set(key, value);
+ return operation;
+ }
+
+ /**
+ * 缓存基本的对象,Integer、String、实体类等
+ *
+ * @param key 缓存的键值
+ * @param value 缓存的值
+ * @param timeout 时间
+ * @param timeUnit 时间颗粒度
+ * @return 缓存的对象
+ */
+ public ValueOperations setCacheObject(String key, T value, Integer timeout, TimeUnit timeUnit)
+ {
+ ValueOperations operation = redisTemplate.opsForValue();
+ operation.set(key, value, timeout, timeUnit);
+ return operation;
+ }
+
+ /**
+ * 获得缓存的基本对象。
+ *
+ * @param key 缓存键值
+ * @return 缓存键值对应的数据
+ */
+ public T getCacheObject(String key)
+ {
+ ValueOperations operation = redisTemplate.opsForValue();
+ return operation.get(key);
+ }
+
+ /**
+ * 删除单个对象
+ *
+ * @param key
+ */
+ public void deleteObject(String key)
+ {
+ redisTemplate.delete(key);
+ }
+
+ /**
+ * 删除集合对象
+ *
+ * @param collection
+ */
+ public void deleteObject(Collection collection)
+ {
+ redisTemplate.delete(collection);
+ }
+
+ /**
+ * 缓存List数据
+ *
+ * @param key 缓存的键值
+ * @param dataList 待缓存的List数据
+ * @return 缓存的对象
+ */
+ public ListOperations setCacheList(String key, List dataList)
+ {
+ ListOperations listOperation = redisTemplate.opsForList();
+ if (null != dataList)
+ {
+ int size = dataList.size();
+ for (int i = 0; i < size; i++)
+ {
+ listOperation.leftPush(key, dataList.get(i));
+ }
+ }
+ return listOperation;
+ }
+
+ /**
+ * 获得缓存的list对象
+ *
+ * @param key 缓存的键值
+ * @return 缓存键值对应的数据
+ */
+ public List getCacheList(String key)
+ {
+ List dataList = new ArrayList();
+ ListOperations listOperation = redisTemplate.opsForList();
+ Long size = listOperation.size(key);
+
+ for (int i = 0; i < size; i++)
+ {
+ dataList.add(listOperation.index(key, i));
+ }
+ return dataList;
+ }
+
+ /**
+ * 缓存Set
+ *
+ * @param key 缓存键值
+ * @param dataSet 缓存的数据
+ * @return 缓存数据的对象
+ */
+ public BoundSetOperations setCacheSet(String key, Set dataSet)
+ {
+ BoundSetOperations setOperation = redisTemplate.boundSetOps(key);
+ Iterator it = dataSet.iterator();
+ while (it.hasNext())
+ {
+ setOperation.add(it.next());
+ }
+ return setOperation;
+ }
+
+ /**
+ * 获得缓存的set
+ *
+ * @param key
+ * @return
+ */
+ public Set getCacheSet(String key)
+ {
+ Set dataSet = new HashSet();
+ BoundSetOperations operation = redisTemplate.boundSetOps(key);
+ Long size = operation.size();
+ for (int i = 0; i < size; i++)
+ {
+ dataSet.add(operation.pop());
+ }
+ return dataSet;
+ }
+
+ /**
+ * 缓存Map
+ *
+ * @param key
+ * @param dataMap
+ * @return
+ */
+ public HashOperations setCacheMap(String key, Map dataMap)
+ {
+ HashOperations hashOperations = redisTemplate.opsForHash();
+ if (null != dataMap)
+ {
+ for (Map.Entry entry : dataMap.entrySet())
+ {
+ hashOperations.put(key, entry.getKey(), entry.getValue());
+ }
+ }
+ return hashOperations;
+ }
+
+ /**
+ * 获得缓存的Map
+ *
+ * @param key
+ * @return
+ */
+ public Map getCacheMap(String key)
+ {
+ Map map = redisTemplate.opsForHash().entries(key);
+ return map;
+ }
+
+ /**
+ * 获得缓存的基本对象列表
+ *
+ * @param pattern 字符串前缀
+ * @return 对象列表
+ */
+ public Collection keys(String pattern)
+ {
+ return redisTemplate.keys(pattern);
+ }
+}
diff --git a/src/main/java/com/chenxuan/bean/config/DruidConfig.java b/src/main/java/com/chenxuan/bean/config/DruidConfig.java
new file mode 100644
index 0000000..3592c72
--- /dev/null
+++ b/src/main/java/com/chenxuan/bean/config/DruidConfig.java
@@ -0,0 +1,102 @@
+package com.chenxuan.bean.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
+import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
+import com.alibaba.druid.util.Utils;
+
+import com.chenxuan.bean.config.properties.DruidProperties;
+import com.chenxuan.bean.datasource.DynamicDataSource;
+import com.chenxuan.enums.DataSourceType;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+import javax.servlet.*;
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * @ClassName:DruidConfig
+ * @Description: druid 配置多数据源
+ * @Author: Arno_Fu
+ * @CreatTime:12/23/2019 - 4:57 PM
+ * @Version V1.0
+ */
+
+@Configuration
+public class DruidConfig {
+
+ @Bean
+ @ConfigurationProperties("spring.datasource.druid.master")
+ public DataSource masterDataSource(DruidProperties druidProperties) {
+ DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
+ return druidProperties.dataSource( dataSource );
+ }
+
+ @Bean
+ @ConfigurationProperties("spring.datasource.druid.slave")
+ @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
+ public DataSource slaveDataSource(DruidProperties druidProperties) {
+ DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
+ return druidProperties.dataSource( dataSource );
+ }
+
+ @Bean(name = "dynamicDataSource")
+ @Primary
+ public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource) {
+ Map