diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a2a3040aa86debfd8826d9c2b5c816314c17d9fe
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/mvnw b/mvnw
new file mode 100644
index 0000000000000000000000000000000000000000..a16b5431b4c3cab50323a3f558003fd0abd87dad
--- /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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# 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 "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000000000000000000000000000000000000..c8d43372c986d97911cdc21bd87e0cbe3d83bdda
--- /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 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%
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bab71a659dea3cfc7ed2bbdd470f7e11264a127c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,73 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.6.RELEASE
+
+
+ com.cheri
+ keller
+ 0.0.1-SNAPSHOT
+ keller
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 2.1.1
+
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+ javax.mail
+ mail
+ 1.4.7
+
+
+ com.alibaba
+ fastjson
+ 1.2.51
+
+
+ org.xmlunit
+ xmlunit-core
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/src/main/java/com/cheri/keller/KellerApplication.java b/src/main/java/com/cheri/keller/KellerApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..12ca5233916b2098783ad5b9bea59dca0b3b4c9a
--- /dev/null
+++ b/src/main/java/com/cheri/keller/KellerApplication.java
@@ -0,0 +1,30 @@
+package com.cheri.keller;
+
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
+
+@MapperScan("com.cheri.keller.mapper")
+@SpringBootApplication
+public class KellerApplication {
+
+ @Bean
+ /**
+ * 引入RestTemplate Bean
+ * 用来进行服务间的Http通信
+ * 同时重新定义其解析时用到的字符集,防止中文乱码
+ */
+ RestTemplate restTemplate(){
+ RestTemplate restTemplate = new RestTemplate();
+ restTemplate.getMessageConverters().clear();
+ restTemplate.getMessageConverters().add(new FastJsonHttpMessageConverter());
+ return restTemplate;
+ }
+ public static void main(String[] args) {
+ SpringApplication.run(KellerApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/com/cheri/keller/common/KellerRunner.java b/src/main/java/com/cheri/keller/common/KellerRunner.java
new file mode 100644
index 0000000000000000000000000000000000000000..361f2cd31c5d2ac780e601da0fd5e3898b5ab5ce
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/KellerRunner.java
@@ -0,0 +1,33 @@
+package com.cheri.keller.common;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:35
+ **/
+
+import com.cheri.keller.common.config.CommonConfig;
+import com.cheri.keller.common.util.RequestUtil;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * 继承Application接口后项目启动时会按照执行顺序执行run方法
+ * 通过设置Order的value来指定执行的顺序
+ */
+@Component
+@Order(value = 1)
+public class KellerRunner implements ApplicationRunner {
+ @Resource
+ private CommonConfig config;
+
+ @Override
+ public void run(ApplicationArguments args) throws Exception {
+ RequestUtil.port = config.port;
+ RequestUtil.address = config.address;
+ }
+}
diff --git a/src/main/java/com/cheri/keller/common/config/CommonConfig.java b/src/main/java/com/cheri/keller/common/config/CommonConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec322c7e4ec265e0abeac26b5a59844754aa6042
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/config/CommonConfig.java
@@ -0,0 +1,23 @@
+package com.cheri.keller.common.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+/**
+ * 项目配置文件,从application.properties中加载
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:08
+ **/
+@Configuration
+@PropertySource("classpath:application.properties")
+public class CommonConfig {
+ @Value("${server.port:8080}")
+ public String port;
+
+ @Value("${server.address:http://127.0.0.1}")
+ public String address;
+
+}
diff --git a/src/main/java/com/cheri/keller/common/config/PublicConstant.java b/src/main/java/com/cheri/keller/common/config/PublicConstant.java
new file mode 100644
index 0000000000000000000000000000000000000000..23056e82ea64fc49d9a94d22502d0d7f6294ba26
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/config/PublicConstant.java
@@ -0,0 +1,108 @@
+package com.cheri.keller.common.config;
+
+/**
+ * 公用常量,项目中要到的一些常量
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 22:05
+ **/
+public class PublicConstant {
+
+ public static final int DEFAULT_USER_TYPE = 0;
+
+ /**
+ * 业务成功标示
+ */
+ public static final int SUCCESS = 0;
+ /**
+ * 业务失败标示
+ */
+ public static final int FAILED = 1;
+
+ /**
+ * 注册类型
+ */
+ public static final int REGISTER_TYPE = 0;
+
+ /**
+ * 登录类型
+ */
+ public static final int LOGIN_TYPE = 1;
+
+ /**
+ * 重置密码类型
+ */
+ public static final int RESET_PASSWORD_TYPE = 2;
+
+ /**
+ * 邮件验证码有效期
+ */
+ public static final int EMAIL_CODE_TIME = 5;
+
+ /**
+ * 邮件验证码长度
+ */
+ public static final int EMAIL_CODE_LENGTH = 6;
+
+
+ /**
+ * 应用启动的端口号
+ */
+ public static String port ;
+
+ /**
+ * 应用运行的 IP 地址
+ */
+ public static String address ;
+
+
+ /**
+ * 应用名
+ */
+ public static String appName = "Keller笔记";
+
+ /**
+ * 服务器访问地址
+ */
+ public static String serviceUrl = "http://" + address + ":" + port;
+
+ /**
+ * 邮件服务器地址
+ */
+ public static String mailServerHost;
+
+
+ /**
+ * 邮件服务器登录用户名
+ */
+ public static String mailServerUser = "1227826445@qq.com";
+
+
+ /**
+ * 邮件服务器登录密码
+ */
+ public static String mailServerPassword = "";
+
+ /**
+ * 通用,不做访问权限设置
+ */
+ public static final int AUTHORITY_COMMON = 1 << 0;
+
+ /**
+ * 用户登录后可以访问
+ */
+ public static final int AUTHORITY_LOGON = 1 << 1;
+
+ /**
+ * 管理员可以访问
+ */
+ public static final int AUTHORITY_ADMIN = 1 << 2;
+
+ /**
+ * 超级管理员可以访问
+ */
+ public static final int AUTHORITY_SUPPER_ADMIN = 1 << 3;
+
+
+}
diff --git a/src/main/java/com/cheri/keller/common/config/RequestConfig.java b/src/main/java/com/cheri/keller/common/config/RequestConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..75b659f9a38b7d53a9f2882bf6e9ca84ddcc1782
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/config/RequestConfig.java
@@ -0,0 +1,22 @@
+package com.cheri.keller.common.config;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:09
+ **/
+public class RequestConfig {
+ public static final String URL = "URL";
+ public static final String URI = "URI";
+ public static final String REMOTE_ADDR = "REMOTE_ADDR";
+ public static final String REMOTE_HOST = "REMOTE_HOST";
+ public static final String REMOTE_PORT = "REMOTE_PORT";
+ public static final String REMOTE_USER = "REMOTE_USER";
+
+ public static final String REQUEST_METHOD = "REQUEST_METHOD";
+
+ public static final String METHOD = "method";
+ public static final String TOKEN = "token";
+
+
+}
diff --git a/src/main/java/com/cheri/keller/common/mybatis/BaseEntity.java b/src/main/java/com/cheri/keller/common/mybatis/BaseEntity.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfb4a3af55b4df51e9da84cda13f26cc8a391007
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/BaseEntity.java
@@ -0,0 +1,86 @@
+package com.cheri.keller.common.mybatis;
+
+/**
+ * * BaseEntity,使用复杂查询(带条件的增删改查和分页查询)时需要继承的父类
+ * * 该类提供了可供选择的多条件查询方式、排序方式、分页查询相关参数等
+ * * 数据实体类继承该类即可使用
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:15
+ **/
+public class BaseEntity {
+ /**
+ * 是否查询明细字段
+ */
+ private boolean baseKyleDetailed = true;
+ /**
+ * 多个查询条件是否用And连接
+ */
+ private Boolean baseKyleUseAnd = true;
+ /**
+ * 是否按排序关键字升序排列
+ */
+ private Boolean baseKyleUseASC = true;
+ /**
+ * 页面大小
+ */
+ private int baseKylePageSize = 10;
+ /**
+ * 要查询的页码
+ */
+ private int baseKyleCurrentPage = 1;
+ /**
+ * 根据页面大小和要查询的页码计算出的起始行号
+ */
+ private int baseKyleStartRows ;
+ public Boolean getBaseKyleUseAnd() {
+ return baseKyleUseAnd;
+ }
+
+ public void setBaseKyleUseAnd(Boolean baseKyleUseAnd) {
+ this.baseKyleUseAnd = baseKyleUseAnd;
+ }
+
+ public Boolean getBaseKyleUseASC() {
+ return baseKyleUseASC;
+ }
+
+ public void setBaseKyleUseASC(Boolean baseKyleUseASC) {
+ this.baseKyleUseASC = baseKyleUseASC;
+ }
+
+ public void setBaseKylePageSize(int baseKylePageSize) {
+ this.baseKylePageSize = baseKylePageSize;
+ this.baseKyleStartRows = this.baseKylePageSize * (this.baseKyleCurrentPage - 1);
+ }
+
+ public int getBaseKylePageSize() {
+ return baseKylePageSize;
+ }
+
+ public int getBaseKyleStartRows() {
+ return baseKyleStartRows;
+ }
+
+ public void setBaseKyleStartRows(int baseKyleStartRows) {
+ this.baseKyleStartRows = baseKyleStartRows;
+ }
+
+ public int getBaseKyleCurrentPage() {
+ return baseKyleCurrentPage;
+ }
+
+ public void setBaseKyleCurrentPage(int baseKyleCurrentPage) {
+ this.baseKyleStartRows = this.baseKylePageSize * (this.baseKyleCurrentPage - 1);
+ this.baseKyleCurrentPage = baseKyleCurrentPage;
+ }
+
+ public boolean isBaseKyleDetailed() {
+ return baseKyleDetailed;
+ }
+
+ public void setBaseKyleDetailed(boolean baseKyleDetailed) {
+ this.baseKyleDetailed = baseKyleDetailed;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/common/mybatis/BaseException.java b/src/main/java/com/cheri/keller/common/mybatis/BaseException.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e9c822746210bf33105527f1de56abe63cf5398
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/BaseException.java
@@ -0,0 +1,14 @@
+package com.cheri.keller.common.mybatis;
+
+/**
+ * 自定义异常,用来处理BaseMapper使用过程中抛出的异常
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:16
+ **/
+public class BaseException extends RuntimeException{
+ public BaseException(String message){
+ super(message);
+ }
+}
diff --git a/src/main/java/com/cheri/keller/common/mybatis/BaseMapper.java b/src/main/java/com/cheri/keller/common/mybatis/BaseMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f8170685b46c53250e299099fbb32daf747a507
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/BaseMapper.java
@@ -0,0 +1,161 @@
+package com.cheri.keller.common.mybatis;
+
+import com.cheri.keller.common.mybatis.provider.*;
+import org.apache.ibatis.annotations.*;
+import org.springframework.dao.DuplicateKeyException;
+
+import java.util.List;
+
+/**
+ * * BaseMapper提供了通用的Mapper
+ * * 实现了insert、insertAndReturnKey、deleteById、SelectById、updateById等基本的增删改查方法
+ * * 数据实体的Mapper继承该接口即可
+ * *
+ * * BaseMapper还提供了带条件的删除和查询操作,以及带条件的分页查询,需要实体类继承BaseEntity方可使用
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:17
+ **/
+public interface BaseMapper {
+ /**
+ * 创建表
+ * @param entity
+ */
+ @UpdateProvider(type = BaseCreateProvider.class , method = "create")
+ void baseCreate(T entity);
+
+ /**
+ * 插入操作
+ * 将实体类的所有字段和字段的值分别列出来,适用于主键不是自增的表
+ * @param entity
+ * @return INSERT INTO tableName (id,name...) VALUES (#{id},#{name}...)
+ * @throws DuplicateKeyException 当唯一字段重复插入时,会抛该异常
+ */
+ @InsertProvider(type = BaseInsertProvider.class,method = "insert")
+ Integer baseInsert(T entity) throws DuplicateKeyException;
+
+ /**
+ * 插入数据并返回自增的主键(建议使用id)
+ * 将实体类中除主键以外的字段和值分别列出来,适用于主键是自增的表
+ * @param entity
+ * @return INSERT INTO tableName (name...) VALUES(#{name}...)
+ * @throws DuplicateKeyException 当唯一字段重复插入时,会抛该异常
+ */
+ @InsertProvider(type = BaseInsertProvider.class,method = "insertAndReturnKey")
+ @Options(useGeneratedKeys=true,keyProperty = "id", keyColumn = "id")
+ Integer baseInsertAndReturnKey(T entity) throws DuplicateKeyException;
+
+
+ /**
+ * 根据Id删除数据,要求必须有id字段
+ * @param entity
+ * @return
+ */
+ @DeleteProvider(type = BaseDeleteProvider.class,method = "deleteById")
+ Integer baseDeleteById(T entity);
+
+ /**
+ * 根据条件删除
+ * 传入的对象中带@IndexAttribute注解的字段有值的都作为查询条件
+ * 多个查询条件用And连接
+ * @param entity 实体对象
+ * @return DELETE FROM router WHERE name = #{name} AND serviceName = #{serviceName}
+ */
+ @SelectProvider(type= BaseDeleteProvider.class,method = "deleteByCondition")
+ Integer baseDeleteByCondition(T entity);
+
+ /**
+ * 根据id 更新数据,空值不更新 ,要求必须有id字段
+ * @param entity
+ * @return
+ */
+ @UpdateProvider(type = BaseUpdateProvider.class,method = "updateById")
+ Integer baseUpdateById(T entity);
+
+ /**
+ * 根据主键更新数据,空值不更新,要求数据至少有一个主键,且主键有值
+ * @param entity
+ * @return
+ */
+ @UpdateProvider(type = BaseUpdateProvider.class,method = "updateByKey")
+ Integer baseUpdateByKey(T entity);
+
+ /**
+ * 根据Id 查找数据,要求必须有id 字段
+ * @param entity
+ * @return
+ */
+ @SelectProvider(type= BaseSelectProvider.class,method = "selectById")
+ T baseSelectById(T entity);
+
+ /**
+ * 根据主键查询数据,要求至少有一个主键,且主键必须有值
+ * @param entity
+ * @return
+ */
+ @SelectProvider(type= BaseSelectProvider.class,method = "selectByKey")
+ T baseSelectByKey(T entity);
+
+
+
+ /**
+ * 查询全部数据
+ * @param entity
+ * @return
+ */
+ @SelectProvider(type= BaseSelectProvider.class,method = "selectAll")
+ List baseSelectAll(T entity);
+
+ /**
+ * 带条件的查询,该查询为动态查询,不可缓存
+ * 传入的对象中带@IndexAttribute注解的字段有值的都作为查询条件
+ * 传入对象中带@SortAttribute注解的字段作为排序字段
+ * @param entity 实体对象
+ * param and 多个查询条件组合方式 true:AND false:OR
+ * param asc 排序方式 null:不指定排序方式 true:按指定排序字段升序 false:按指定排序字段降序
+ * @return SELECT id,name... FROM router WHERE name = #{name} AND serviceName = #{serviceName} ORDER BY createTime ASC
+ */
+ @SelectProvider(type= BaseSelectProvider.class,method = "selectByCondition")
+ List baseSelectByCondition(T entity);
+
+
+ /**
+ * 查询记录总数
+ * 返回的是 "SELECT COUNT(1) FROM 表名" 的结果
+ * 不带查询条件
+ * @param entity
+ * @return
+ */
+ @SelectProvider(type = BaseSelectProvider.class,method = "selectCount")
+ Integer baseSelectCount(T entity);
+
+ /**
+ * 根据条件查询记录总数
+ * 传入的对象中带@IndexAttribute注解的字段有值的都作为查询条件
+ * @param entity
+ * @return SELECT COUNT(1) FROM router WHERE name = #{name} AND serviceName = #{serviceName}
+ */
+ @SelectProvider(type = BaseSelectProvider.class,method = "selectCountByCondition")
+ Integer baseSelectCountByCondition(T entity);
+
+
+ /**
+ * 分页查询
+ * 返回 “SELECT 所有字段 FROM 表名 LIMIT startRows,pageSize” 的结果
+ * 不带查询条件
+ * @param entity
+ * @return
+ */
+ @SelectProvider(type = BaseSelectProvider.class,method = "selectPageList")
+ List baseSelectPageList(T entity);
+
+ /**
+ * 加条件的分页查询
+ * 传入的对象中带@IndexAttribute注解的字段有值的都作为查询条件
+ * @return SELECT id,name... FROM router WHERE name = #{name} AND serviceName = #{serviceName} ORDER BY createTime ASC LIMIT #{startRows},#{pageSize}
+ */
+ @SelectProvider(type = BaseSelectProvider.class,method = "selectPageListByCondition")
+ List baseSelectPageListByCondition(T entity);
+
+}
diff --git a/src/main/java/com/cheri/keller/common/mybatis/SqlFieldReader.java b/src/main/java/com/cheri/keller/common/mybatis/SqlFieldReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd5e2bfc0bd2e24a10ea6baa5b4ae824ad7ad71f
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/SqlFieldReader.java
@@ -0,0 +1,401 @@
+package com.cheri.keller.common.mybatis;
+
+import com.cheri.keller.common.mybatis.annotation.*;
+import com.cheri.keller.common.util.Console;
+import com.cheri.keller.common.util.StringUtils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * * Provider工具类
+ * * 提供获取读取表名、字段名等公用方法
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:17
+ **/
+public class SqlFieldReader {
+
+ public static ConcurrentHashMap tableNameMap = new ConcurrentHashMap<>(16);
+
+ /**
+ * 读取表名,要求类上有@TableAttribute注解
+ * @param entity 实体对象
+ * @return tableName
+ */
+ public static String getTableName(T entity) {
+ Class cls = entity.getClass();
+ String tableName = tableNameMap.get(cls.getName());
+ if(StringUtils.isNotEmpty(tableName)){
+ return tableName;
+ }
+ TableAttribute table = entity.getClass().getAnnotation(TableAttribute.class);
+ if(table == null){
+ throw new BaseException("需要解析表名,但未找到@TableAttribute注解");
+ }
+ tableNameMap.put(cls.getName(),table.name());
+ return table.name();
+ }
+
+ /**
+ * 将所有字段名以逗号拼接起来返回
+ * 从属性前的@FieldAttribute注解解析要查询的字段名
+ * 当所有属性都没有@FieldAttribute注解时,解析所有属性名作为字段名
+ * @param entity 实体对象
+ * @return id,name
+ */
+ public static String getFieldStr(T entity){
+ Class cls = entity.getClass();
+ Field[] fields = cls.getDeclaredFields();
+ //带@FieldAttribute注解的属性名
+ StringBuilder builder = new StringBuilder();
+ //所有属性名
+ StringBuilder allFields = new StringBuilder();
+ for(Field field:fields){
+ allFields.append(field.getName()).append(",");
+ if(field.getAnnotation(FieldAttribute.class) != null){
+ FieldAttribute fieldAttribute = field.getAnnotation(FieldAttribute.class);
+ //如果查询明细字段,返回明细字段
+ if(entity.isBaseKyleDetailed()){
+ builder.append(field.getName()).append(",");
+ //如果不查询明细字段,不返回明细字段
+ }else {
+ if(!fieldAttribute.detailed()){
+ builder.append(field.getName()).append(",");
+ }
+ }
+
+ }
+ }
+ if(builder.length() > 0){
+ return builder.substring(0,builder.length() - 1);
+ }else if(allFields.length() > 0){
+ return allFields.substring(0,allFields.length() - 1);
+ }else {
+ return null;
+ }
+ }
+
+ /**
+ * 根据索引字段生成查询条件
+ * 传入的对象中带@IndexAttribute注解的字段有值的都作为查询条件
+ * @param entity 实体对象
+ * @param 实体类型
+ * @return WHERE name = #{name} OR controllerName = #{controllerName}
+ */
+ public static String getConditionSuffix(T entity){
+ String condition;
+ if(entity.getBaseKyleUseAnd() == null){
+ return "";
+ }
+ if(entity.getBaseKyleUseAnd()){
+ condition = "AND";
+ }else {
+ condition = "OR";
+ }
+ Class cls = entity.getClass();
+ Field[] fields = cls.getDeclaredFields();
+ StringBuilder builder = new StringBuilder();
+ builder.append(" WHERE ");
+ try {
+ for(Field field:fields){
+ if(field.getAnnotation(IndexAttribute.class) != null){
+ if(SqlFieldReader.hasValue(entity,field.getName())){
+ builder.append(field.getName())
+ .append(" = #{").append(field.getName()).append("} ")
+ .append(condition).append(" ");
+ }
+
+ }
+ }
+ int index = builder.lastIndexOf(condition);
+ if(index < 0){
+ return "";
+ }
+ return builder.substring(0,index);
+
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ //注意,不要return null
+ return "";
+ }
+
+ /**
+ * 获取主键的查询条件
+ * 传入的对象必须满足以下条件:
+ * 1. 必须有且只有一个带有@keyAttribute注解的字段,如果有多个,只取第一个
+ * 2. 带有@KeyAttribute注解的字段必须有值
+ * 这是为了避免产生因为没有设置@KeyAttribute注解而造成全部数据修改或删除的问题
+ * @param entity
+ * @param
+ * @return WHERE userId = #{userId}
+ */
+ public static String getConditionByKeySuffix(T entity) throws BaseException {
+ Class cls = entity.getClass();
+ Field[] fields = cls.getDeclaredFields();
+ StringBuilder builder = new StringBuilder();
+ builder.append(" WHERE ");
+ try {
+ for(Field field:fields){
+ if(field.getAnnotation(KeyAttribute.class) != null){
+ if(hasValue(entity,field.getName())){
+ builder.append(field.getName())
+ .append(" = #{").append(field.getName()).append("} ");
+ }else {
+ throw new BaseException("@KeyAttribute修饰的字段不能为空");
+ }
+ break;
+ }
+ }
+ int index = builder.lastIndexOf("=");
+ if(index < 0){
+ throw new BaseException("没有找到@KeyAttribute修饰的字段");
+ }
+ return builder.toString();
+ }catch (Exception e){
+ e.printStackTrace();
+ throw new BaseException(e.getMessage());
+ }
+ }
+
+ /**
+ *
+ * @param entity
+ * @param
+ * @return
+ */
+ public static String getSortSuffix(T entity){
+ String condition;
+ if(entity.getBaseKyleUseASC() == null){
+ return "";
+ }
+ if(entity.getBaseKyleUseASC()){
+ condition = "ASC";
+ }else {
+ condition = "DESC";
+ }
+ Class cls = entity.getClass();
+ Field[] fields = cls.getDeclaredFields();
+ StringBuilder builder = new StringBuilder();
+ builder.append(" ORDER BY ");
+ try {
+ for(Field field:fields){
+ if(field.getAnnotation(SortAttribute.class) != null){
+ builder.append(field.getName()).append(" ")
+ .append(condition).append(",");
+
+ }
+ }
+ int index = builder.lastIndexOf(",");
+ if(index < 0){
+ return "";
+ }
+ return builder.substring(0,index);
+
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ /**
+ * 获取所有字段列表
+ * 读取类中带@FieldAttribute注解的字段,如果都没有带该注解,则返回类中所有字段
+ * @return {id,name}
+ */
+ public static List getFields(T entity){
+ Field[] fields = entity.getClass().getDeclaredFields();
+ List fieldList = new ArrayList<>();
+ List allList = new ArrayList<>();
+ //带@FieldAttribute注解的属性名
+ for(Field field:fields){
+ allList.add(field.getName());
+ if(field.getAnnotation(FieldAttribute.class) != null){
+ fieldList.add(field.getName());
+ }
+ }
+ if(fieldList.size() == 0){
+ return allList;
+ }
+ return fieldList;
+ }
+
+ /**
+ * 读取@TableAttribute注解,解析表名和描述
+ * 读取@FieldAttribute注解,解析字段名和描述
+ * 读取@KeyAttribute注解,解析主键
+ * 读取@IndexAttribute注解,解析索引
+ *
+ * 创建的数据表,含表名、数据表描述、字段名、字段描述、主键、自增主键、索引
+ * @param entity
+ * @return
+ */
+ public static String getCreateTableSql(T entity){
+ TableAttribute table = entity.getClass().getAnnotation(TableAttribute.class);
+ if(table == null){
+ throw new BaseException("要解析表名,未发现@TableAttribute注解");
+ }
+ String tableName = table.name();
+ String tableComment = table.comment();
+
+ StringBuilder builder = new StringBuilder();
+
+ /*
+ * 拼写基础建表语句
+ */
+ builder.append("create table ")
+ .append(tableName)
+ .append("( \n");
+
+
+ // 添加字段
+ builder.append(getAddFieldSql(entity));
+ builder.append(") ");
+
+ // 如果有表说明,添加表说明
+ if(StringUtils.isNotEmpty(tableComment)){
+ builder.append("comment '")
+ .append(tableComment)
+ .append("'; \n");
+ }else {
+ builder.append("; \n");
+ }
+
+ //添加主键
+ builder.append(getCreateKeySql(entity));
+
+ //添加索引
+ builder.append(getCreateIndexSql(entity));
+
+ Console.print("",builder.toString());
+ return builder.toString();
+ }
+
+
+ public static String getAddFieldSql(T entity){
+ Field[] fields = entity.getClass().getDeclaredFields();
+ StringBuilder builder = new StringBuilder();
+
+ /*
+ 解析字段描述:是否唯一、是否必填、是否设置了最大长度等
+ */
+ for(Field field:fields){
+ FieldAttribute fieldAttribute = field.getAnnotation(FieldAttribute.class);
+ if(fieldAttribute != null){
+
+ builder.append(field.getName())
+ .append(" ")
+ .append(TypeCaster.getType(field.getType().getSimpleName(),fieldAttribute.length()));
+ if(fieldAttribute.notNull()){
+ builder.append(" not null ");
+ }
+
+ if(fieldAttribute.unique()){
+ builder.append(" unique ");
+ }
+
+ //如果有字段说明,添加字段说明
+ if(StringUtils.isNotEmpty(fieldAttribute.value())) {
+ builder.append(" comment '")
+ .append(fieldAttribute.value())
+ .append("'");
+ }
+ builder.append(", \n");
+ }
+ }
+ builder.deleteCharAt(builder.lastIndexOf(","));
+ return builder.toString();
+ }
+
+ private static String getCreateKeySql(T entity){
+ Field[] fields = entity.getClass().getDeclaredFields();
+ StringBuilder builder = new StringBuilder();
+ for(Field field:fields){
+ KeyAttribute keyAttribute = field.getAnnotation(KeyAttribute.class);
+ if(keyAttribute != null){
+ FieldAttribute fieldAttribute = field.getAnnotation(FieldAttribute.class);
+ if(fieldAttribute == null){
+ return "";
+ }
+ builder .append("alter table ")
+ .append(getTableName(entity))
+ .append(" change ")
+ .append(field.getName())
+ .append(" ")
+ .append(field.getName())
+ .append(" ")
+ .append(TypeCaster.getType(field.getType().getSimpleName(),fieldAttribute.length()));
+ if(keyAttribute.autoIncr()){
+ builder.append(" auto_increment ");
+ }
+ builder.append(" primary key comment '")
+ .append(fieldAttribute.value())
+ .append("'; \n");
+
+ break;
+ }
+ }
+ return builder.toString();
+ }
+
+
+ /**
+ * 获取索引字段列表
+ * @return
+ */
+ public static String getCreateIndexSql(T entity){
+
+ String tableName = getTableName(entity);
+ StringBuilder builder = new StringBuilder();
+
+ Field[] fields = entity.getClass().getDeclaredFields();
+ for(Field field:fields){
+ if(field.getAnnotation(IndexAttribute.class) != null){
+
+ builder.append("alter table ")
+ .append(tableName)
+ .append(" add index ")
+ .append(tableName)
+ .append("_index_")
+ .append(field.getName())
+ .append(" (")
+ .append(field.getName())
+ .append("); \n");
+ }
+ }
+ return builder.toString();
+ }
+
+
+ /**
+ * 判断一个对象的指定字段有没有值
+ * @param entity 实体对象
+ * @param fieldName 对象的字段名
+ * @param 实体类型
+ * @return 值存在且不为null:返回true; 否则:返回false
+ */
+ public static boolean hasValue(T entity, String fieldName){
+ try {
+ Class cls = entity.getClass();
+ Method method = cls.getMethod("get" + StringUtils.captureName(fieldName));
+ if(method.invoke(entity) == null){
+ return false;
+ }else {
+ return true;
+ }
+ }catch (IllegalAccessException e){
+ e.printStackTrace();
+ }catch (NoSuchMethodException e){
+ e.printStackTrace();
+ }catch (InvocationTargetException e){
+ e.printStackTrace();
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/cheri/keller/common/mybatis/TypeCaster.java b/src/main/java/com/cheri/keller/common/mybatis/TypeCaster.java
new file mode 100644
index 0000000000000000000000000000000000000000..c78657fab323a0eb837dc79d68be51db549e5715
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/TypeCaster.java
@@ -0,0 +1,113 @@
+package com.cheri.keller.common.mybatis;
+
+import com.cheri.keller.common.util.Console;
+import org.springframework.util.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Java类型和mysql类型间的转换,仅创建数据表时使用
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:17
+ **/
+public class TypeCaster {
+ /**
+ * varchar/varbinary类型,允许最大长度为65535,在这里限制:如果超过3000,转换为text/blob
+ */
+ private static final int MAX = 3000;
+
+ /**
+ * TINYTEXT 256 bytes
+ * TEXT 65,535 bytes ~64kb
+ * MEDIUMTEXT 16,777,215 bytes ~16Mb
+ * LONGTEXT 4,294,967,295 bytes ~4Gb
+ */
+ private static final int TEXT_MAX = 65535;
+
+ /**
+ * decimal类型的最大长度为65,根据平时使用的需要,设置为20,足够大多数场景使用了
+ */
+ private static final int DECIMAL_MAX = 20;
+ private static Map map = new HashMap<>(16);
+
+ private static final String STRING = "string";
+ private static final String INT = "int";
+ private static final String INTEGER = "integer";
+ private static final String LONG = "long";
+ private static final String DATE = "date";
+ private static final String BYTE_ARRAY = "byte[]";
+ private static final String FLOAT = "float";
+ private static final String DOUBLE = "double";
+ static {
+ map.put(STRING,"varchar(50)");
+ map.put(INT,"int");
+ map.put(INTEGER,"int");
+ map.put(LONG,"bigint");
+ map.put(DATE,"datetime");
+ map.put(BYTE_ARRAY,"varbinary(50)");
+ map.put(FLOAT,"decimal(10,2)");
+ map.put(DOUBLE,"decimal(10,2)");
+ }
+ /**
+ * 根据Java数据类型和设置的长度,转换为MySQL的数据类型
+ * @param key
+ * @param length
+ * @return
+ */
+ public static String getType(String key,int length){
+ if(StringUtils.isEmpty(key)){
+ return null;
+ }
+
+ if(length <= 0){
+ return map.get(key.toLowerCase());
+ }
+
+ /*
+ float/Float/double/Double类型判断设置的长度是否符合规则,如果超长,将长度设置为允许的最大长度
+ */
+ if(FLOAT.equalsIgnoreCase(key)
+ || DOUBLE.equalsIgnoreCase(key)){
+ length = length > DECIMAL_MAX ? DECIMAL_MAX:length;
+ return "decimal(" + length + ",2)";
+ }
+
+ //String 根据长度,转换为 varchar 或 text
+ if(STRING.equalsIgnoreCase(key)){
+ if(length < MAX){
+ return "varchar(" + length + ")";
+ }
+ if(length < TEXT_MAX){
+ return "text";
+ }
+
+ return "mediumtext";
+
+
+ }
+
+ //byte[] 根据长度,转换为 varbinary 或 blob
+ if(BYTE_ARRAY.equalsIgnoreCase(key)){
+ if(length < MAX){
+ return "varbinary(" + length + ")";
+ }
+ return "blob";
+ }
+
+ return map.get(key.toLowerCase());
+ }
+
+ public static void main(String[] args){
+ Console.println("String",getType("String",10000));
+ Console.println("Integer",getType("Integer",100));
+ Console.println("float",getType("float",100));
+ Console.println("Float",getType("Float",10));
+ Console.println("long",getType("long",10));
+ Console.println("Long",getType("Long",10));
+ Console.println("Date",getType("Date",10));
+ Console.println("double",getType("double",10));
+ }
+}
diff --git a/src/main/java/com/cheri/keller/common/mybatis/annotation/FieldAttribute.java b/src/main/java/com/cheri/keller/common/mybatis/annotation/FieldAttribute.java
new file mode 100644
index 0000000000000000000000000000000000000000..46b514a0f43bff2c03470c86120709abb1fc98a8
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/annotation/FieldAttribute.java
@@ -0,0 +1,51 @@
+package com.cheri.keller.common.mybatis.annotation;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:34
+ **/
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 字段名注解,用于要查询的字段名之前
+ * 在select * from tableName时,代替*的位置
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface FieldAttribute {
+ /**
+ * 字段说明
+ * @return
+ */
+ String value() default "";
+
+ /**
+ * 是否必填字段,默认不是必填
+ * @return
+ */
+ boolean notNull() default false;
+
+ /**
+ * 字段长度 ,仅可变长类型设置
+ * String 、byte[] 类型分别对应 mysql 中 varchar、varbinary类型,需要设置长度,默认50
+ * @return
+ */
+ int length() default 0;
+
+ /**
+ * 是否唯一,默认不唯一
+ * @return
+ */
+ boolean unique() default false;
+
+ /**
+ * 是否是明细字段,如果是明细字段,在查询列表时不显示该字段
+ * @return
+ */
+ boolean detailed() default false;
+}
diff --git a/src/main/java/com/cheri/keller/common/mybatis/annotation/IndexAttribute.java b/src/main/java/com/cheri/keller/common/mybatis/annotation/IndexAttribute.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c92be34aa49633c60123036a005eeca3f7a1b64
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/annotation/IndexAttribute.java
@@ -0,0 +1,22 @@
+package com.cheri.keller.common.mybatis.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * * 索引字段注解,可以根据索引字段进行查找、删除操作
+ * * 注意:索引字段尽量使用包装类,如:Integer,避免使用 int long等基本类型
+ * * 因为条件查询会将有值的索引字段作为查询条件,
+ * * 此时,如果字段类型为int,初始值为0,0 将作为查询条件。
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:42
+ **/
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface IndexAttribute {
+
+}
diff --git a/src/main/java/com/cheri/keller/common/mybatis/annotation/KeyAttribute.java b/src/main/java/com/cheri/keller/common/mybatis/annotation/KeyAttribute.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ac38009412e4dac7180c17da49495f0dc1545e7
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/annotation/KeyAttribute.java
@@ -0,0 +1,26 @@
+package com.cheri.keller.common.mybatis.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * * 数据库主键的注解
+ * * 1. 仅支持单字段作为主键使用,将该字段上添加该注解即可
+ * * 2. 当使用id字段作为主键时,不需要添加该注解
+ * * 3. BaseMapper里后缀为ById的方法均是依照id字段作为主键设计的
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:47
+ **/
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface KeyAttribute {
+ /**
+ * 是否是自增主键
+ * @return
+ */
+ boolean autoIncr() default false;
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/common/mybatis/annotation/SortAttribute.java b/src/main/java/com/cheri/keller/common/mybatis/annotation/SortAttribute.java
new file mode 100644
index 0000000000000000000000000000000000000000..386f6482ba68e62c136fae0cbaba43d7bf5bb8da
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/annotation/SortAttribute.java
@@ -0,0 +1,40 @@
+package com.cheri.keller.common.mybatis.annotation;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:48
+ **/
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 排序字段的注解
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SortAttribute {
+ /**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 21:05
+ **/
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface TableAttribute {
+ /**
+ * 表名
+ * @return
+ */
+ String name() ;
+
+ /**
+ * 描述
+ * @return
+ */
+ String comment() default "";
+ }
+}
diff --git a/src/main/java/com/cheri/keller/common/mybatis/annotation/TableAttribute.java b/src/main/java/com/cheri/keller/common/mybatis/annotation/TableAttribute.java
new file mode 100644
index 0000000000000000000000000000000000000000..1cbde654eb0b0b84d0f7b5cc75a9721c8f931e38
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/annotation/TableAttribute.java
@@ -0,0 +1,33 @@
+package com.cheri.keller.common.mybatis.annotation;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:54
+ **/
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 表注解,用在entity的类声明之前
+ * value赋值为表名
+ * @author Cheri
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TableAttribute {
+ /**
+ * 表名
+ * @return
+ */
+ String name() ;
+
+ /**
+ * 描述
+ * @return
+ */
+ String comment() default "";
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/common/mybatis/provider/BaseCreateProvider.java b/src/main/java/com/cheri/keller/common/mybatis/provider/BaseCreateProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..469c7e30104333723ce58b3f0c6203d9160dd83a
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/provider/BaseCreateProvider.java
@@ -0,0 +1,25 @@
+package com.cheri.keller.common.mybatis.provider;
+
+import com.cheri.keller.common.mybatis.BaseEntity;
+import com.cheri.keller.common.mybatis.SqlFieldReader;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:56
+ **/
+public class BaseCreateProvider {
+
+ /**
+ *
+ * 创建表的同时要创建索引,会执行多条语句,在application.properties中要设置 allowMultiQueries=true
+ * spring.datasource.url = jdbc:mysql://localhost:3306/my_core
+ * ?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
+ * @param entity
+ * @param
+ * @return
+ */
+ public static String create(T entity){
+ return SqlFieldReader.getCreateTableSql(entity);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/common/mybatis/provider/BaseDeleteProvider.java b/src/main/java/com/cheri/keller/common/mybatis/provider/BaseDeleteProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..81d9d501549b72d58227e3e8916d0ac411958d17
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/provider/BaseDeleteProvider.java
@@ -0,0 +1,73 @@
+package com.cheri.keller.common.mybatis.provider;
+
+import com.cheri.keller.common.mybatis.BaseEntity;
+import com.cheri.keller.common.mybatis.SqlFieldReader;
+import com.cheri.keller.common.util.Console;
+import org.springframework.util.StringUtils;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:56
+ **/
+public class BaseDeleteProvider {
+
+ public static Map deleteByIdMap = new ConcurrentHashMap<>(16);
+
+ /**
+ * 根据Id 删除数据,要求必须有id字段
+ *
+ * @param entity
+ * @param
+ * @return DELETE FROM router WHERE id = #{id}
+ */
+ public static String deleteById(T entity) {
+ Class cls = entity.getClass();
+ String className = cls.getName();
+ String sql = deleteByIdMap.get(className);
+ if (StringUtils.isEmpty(sql)) {
+ sql = getDeletePrefix(entity) + " WHERE id = #{id} ";
+ deleteByIdMap.put(className, sql);
+ }
+ Console.info("deleteById", sql, entity);
+ return sql;
+ }
+
+ public static String deleteByKey(T entity) {
+ try {
+ String sql = getDeletePrefix(entity) + SqlFieldReader.getConditionByKeySuffix(entity);
+ Console.info("deleteByKey", sql, entity);
+ return sql;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * 根据条件删除,该查询为动态查询,不可缓存
+ * 传入的对象中带@IndexAttribute注解的字段有值的都作为查询条件
+ * 多个查询条件用And连接
+ *
+ * @param entity 实体对象
+ * param and 多个查询条件组合方式 null:不指定查询条件 true:多个查询条件用AND连接 false:多个查询条件用OR连接
+ * @param 对象类型
+ * @return DELETE FROM router WHERE name = #{name} AND serviceName = #{serviceName}
+ */
+ public static String deleteByCondition(T entity) {
+ String sql = getDeletePrefix(entity) + SqlFieldReader.getConditionSuffix(entity);
+ Console.info("deleteByCondition", sql, entity);
+ return sql;
+ }
+
+ private static String getDeletePrefix(T entity) {
+ return "DELETE FROM " + SqlFieldReader.getTableName(entity) + " ";
+ }
+
+ public static void main(String[] args) {
+
+ }
+
+}
diff --git a/src/main/java/com/cheri/keller/common/mybatis/provider/BaseInsertProvider.java b/src/main/java/com/cheri/keller/common/mybatis/provider/BaseInsertProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2771ce7cb6fa3a9a981cc2c3847e1d84f74c8a4
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/provider/BaseInsertProvider.java
@@ -0,0 +1,104 @@
+package com.cheri.keller.common.mybatis.provider;
+
+import com.cheri.keller.common.mybatis.BaseEntity;
+import com.cheri.keller.common.mybatis.SqlFieldReader;
+import com.cheri.keller.common.util.Console;
+import org.springframework.util.StringUtils;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:57
+ **/
+public class BaseInsertProvider {
+ /**
+ * 缓存insert语句
+ */
+ public static Map insertMap = new ConcurrentHashMap<>(16);
+
+ public static Map insertAndReturnKeyMap = new ConcurrentHashMap<>(16);
+
+ /**
+ * 基础的添加语句
+ * 读取对象的所有字段属性,生成insert语句
+ *
+ * @param entity
+ * @param
+ * @return
+ */
+ public static String insert(T entity) {
+ Class cls = entity.getClass();
+ String className = cls.getName();
+ String sql = insertMap.get(className);
+ if (StringUtils.isEmpty(sql)) {
+ String fieldStr = SqlFieldReader.getFieldStr(entity);
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("INSERT INTO ")
+ .append(SqlFieldReader.getTableName(entity)).append(" ")
+ .append("(").append(fieldStr).append(") ")
+ .append("VALUES(");
+
+ StringBuilder valuesStr = new StringBuilder();
+ String[] arrays = fieldStr.split(",");
+ for (String str : arrays) {
+ valuesStr.append("#{").append(str).append("}").append(",");
+ }
+ builder.append(valuesStr.substring(0, valuesStr.length() - 1))
+ .append(") ");
+ sql = builder.toString();
+ insertMap.put(className, sql);
+ }
+ Console.info("insert", sql, entity);
+ return sql;
+ }
+
+ public static String insertAndReturnKey(T entity) {
+ Class cls = entity.getClass();
+ String className = cls.getName();
+ String sql = insertAndReturnKeyMap.get(className);
+ if (StringUtils.isEmpty(sql)) {
+ String fieldStr = SqlFieldReader.getFieldStr(entity);
+ String[] arrays = fieldStr.split(",");
+
+ StringBuilder builder = new StringBuilder();
+
+ StringBuilder valuesStr = new StringBuilder();
+
+ builder.append("INSERT INTO ")
+ .append(SqlFieldReader.getTableName(entity)).append(" ")
+ .append("(");
+ for (String str : arrays) {
+ if ("id".equals(str)) {
+ continue;
+ }
+ valuesStr.append(str).append(",");
+ }
+ builder.append(valuesStr.substring(0, valuesStr.length() - 1));
+
+ builder.append(") ").append("VALUES(");
+
+ valuesStr = new StringBuilder();
+ for (String str : arrays) {
+ if ("id".equals(str)) {
+ continue;
+ }
+ valuesStr.append("#{").append(str).append("}").append(",");
+ }
+ builder.append(valuesStr.substring(0, valuesStr.length() - 1))
+ .append(") ");
+ sql = builder.toString();
+ insertAndReturnKeyMap.put(className, sql);
+ }
+ Console.info("insertAndReturnKey", sql, entity);
+ return sql;
+ }
+
+
+ public static void main(String[] args) {
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/common/mybatis/provider/BaseSelectProvider.java b/src/main/java/com/cheri/keller/common/mybatis/provider/BaseSelectProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..8aa60145f575f21f2efca4fee24756a8087138ca
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/provider/BaseSelectProvider.java
@@ -0,0 +1,170 @@
+package com.cheri.keller.common.mybatis.provider;
+
+import com.cheri.keller.common.mybatis.BaseEntity;
+import com.cheri.keller.common.mybatis.SqlFieldReader;
+import com.cheri.keller.common.util.Console;
+import org.springframework.util.StringUtils;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:57
+ **/
+public class BaseSelectProvider {
+
+ public static Map selectPrefixWithDetailedMap = new ConcurrentHashMap<>(16);
+ public static Map selectPrefixMap = new ConcurrentHashMap<>(16);
+
+
+ /**
+ * 根据ID 查询数据
+ *
+ * @param entity 实体对象
+ * @param 实体类型
+ * @return SELECT id,name... FROM route WHERE id = #{id}
+ */
+ public static String selectById(T entity) {
+ String sql = getSelectPrefix(entity) + " WHERE id = #{id}";
+ Console.info("selectById", sql, entity);
+ return sql;
+ }
+
+ /**
+ * 根据主键查询数据,要求至少有一个主键,且主键必须有值
+ *
+ * @param entity
+ * @param
+ * @return
+ */
+ public static String selectByKey(T entity) {
+ try {
+ String sql = getSelectPrefix(entity) + SqlFieldReader.getConditionByKeySuffix(entity);
+ Console.info("selectByKey", sql, entity);
+ return sql;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+
+ /**
+ * 查询所有数据,不带条件
+ *
+ * @param entity 实体对象
+ * @param 实体类型
+ * @return SELECT id,name... FROM router
+ */
+ public static String selectAll(T entity) {
+ String sql = getSelectPrefix(entity);
+ Console.info("selectAll", sql, entity);
+ return sql;
+ }
+
+
+ /**
+ * 带条件的查询,该查询为动态查询,不可缓存
+ * 传入的对象中带@IndexAttribute注解的字段有值的都作为查询条件
+ * 传入对象中带@SortAttribute注解的字段作为排序字段
+ *
+ * @param entity 实体对象
+ * param and 多个查询条件组合方式 null:不指定查询条件 true:多个查询条件用AND连接 false:多个查询条件用OR连接
+ * param asc 排序方式 null:不指定排序方式 true:按指定排序字段升序 false:按指定排序字段降序
+ * @param 实体类型
+ * @return SELECT id,name... FROM router WHERE name = #{name} AND serviceName = #{serviceName} ORDER BY createTime ASC
+ */
+ public static String selectByCondition(T entity) {
+ String sql = getSelectPrefix(entity)
+ + SqlFieldReader.getConditionSuffix(entity)
+ + SqlFieldReader.getSortSuffix(entity);
+ Console.info("selectByCondition", sql, entity);
+ return sql;
+ }
+
+ /**
+ * 查询记录总数
+ *
+ * @param entity
+ * @param
+ * @return SELECT COUNT(1) FROM router
+ */
+ public static String selectCount(T entity) {
+ String sql = "SELECT COUNT(1) FROM " + SqlFieldReader.getTableName(entity);
+ Console.info("selectCount", sql, entity);
+ return sql;
+ }
+
+ /**
+ * 根据条件查询记录总数
+ * 传入的对象中带@IndexAttribute注解的字段有值的都作为查询条件
+ *
+ * @param entity param and 多个查询条件组合方式 null:不指定查询条件 true:多个查询条件用AND连接 false:多个查询条件用OR连接
+ * @param
+ * @return SELECT COUNT(1) FROM router WHERE name = #{name} AND serviceName = #{serviceName}
+ */
+ public static String selectCountByCondition(T entity) {
+ String sql = selectCount(entity) + SqlFieldReader.getConditionSuffix(entity);
+ Console.info("selectCountByCondition", sql, entity);
+ return sql;
+ }
+
+ /**
+ * 不加条件的分页查询
+ *
+ * @param entity 实体对象
+ * param startRows 起始行
+ * param pageSize 查询页大小
+ * @param 实体类型
+ * @return SELECT id,name... FROM router LIMIT #{startRows},#{pageSize}
+ */
+ public static String selectPageList(T entity) {
+ String sql = selectAll(entity) + " LIMIT #{baseKyleStartRows},#{baseKylePageSize}";
+ Console.info("selectPageList", sql, entity);
+ return sql;
+ }
+
+ /**
+ * 加条件的分页查询
+ * 传入的对象中带@IndexAttribute注解的字段有值的都作为查询条件
+ *
+ * @param entity param and 多个查询条件组合方式 null:不指定查询条件 true:多个查询条件用AND连接 false:多个查询条件用OR连接
+ * param asc 排序方式 null:不指定排序方式 true:按指定排序字段升序 false:按指定排序字段降序
+ * param startRows 起始行数
+ * param pageSize 查询条数
+ * @param
+ * @return SELECT id,name... FROM router WHERE name = #{name} AND serviceName = #{serviceName} ORDER BY createTime ASC LIMIT #{startRows},#{pageSize}
+ */
+ public static String selectPageListByCondition(T entity) {
+ String sql = selectByCondition(entity) + " LIMIT #{baseKyleStartRows},#{baseKylePageSize}";
+ Console.info("selectPageListByCondition", sql, entity);
+ return sql;
+ }
+
+ /**
+ * 获取通用查询前缀
+ *
+ * @param entity 实体类类型
+ * @return SELECT 所有字段 FROM 表名
+ */
+ private static String getSelectPrefix(T entity) {
+ String className = entity.getClass().getName();
+ String sql;
+ if (entity.isBaseKyleDetailed()) {
+ sql = selectPrefixWithDetailedMap.get(className);
+ } else {
+ sql = selectPrefixMap.get(className);
+ }
+ if (StringUtils.isEmpty(sql)) {
+ sql = "SELECT " + SqlFieldReader.getFieldStr(entity) + " FROM " + SqlFieldReader.getTableName(entity) + " ";
+ if (entity.isBaseKyleDetailed()) {
+ selectPrefixWithDetailedMap.put(className, sql);
+ } else {
+ selectPrefixMap.put(className, sql);
+ }
+ }
+ return sql;
+
+ }
+}
diff --git a/src/main/java/com/cheri/keller/common/mybatis/provider/BaseUpdateProvider.java b/src/main/java/com/cheri/keller/common/mybatis/provider/BaseUpdateProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1f998d76f2cc50bea2518abdbef611d6a7f0397
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/mybatis/provider/BaseUpdateProvider.java
@@ -0,0 +1,70 @@
+package com.cheri.keller.common.mybatis.provider;
+
+import com.cheri.keller.common.mybatis.BaseEntity;
+import com.cheri.keller.common.mybatis.SqlFieldReader;
+import com.cheri.keller.common.util.Console;
+
+import java.util.List;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:57
+ **/
+public class BaseUpdateProvider {
+
+ /**
+ * 根据id 更新数据,空值不更新 ,要求数据有id字段
+ *
+ * @param entity
+ * @param
+ * @return UPDATE router SET methodName = #{methodName} ,createTime = #{createTime} WHERE id = #{id}
+ */
+ public static String updateById(T entity) {
+ String sql = getUpdatePrefix(entity) + " WHERE id = #{id}";
+ Console.info("updateById", sql, entity);
+ return sql;
+ }
+
+ /**
+ * 根据主键更新数据,空值不更新,要求数据至少有一个主键,且主键有值
+ *
+ * @param entity
+ * @param
+ * @return
+ */
+ public static String updateByKey(T entity) {
+ try {
+ String sql = getUpdatePrefix(entity) + SqlFieldReader.getConditionByKeySuffix(entity);
+ Console.info("updateByKey", sql, entity);
+ return sql;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * 获取更新操作的前缀部分
+ *
+ * @param entity
+ * @return UPDATE 表名 SET
+ */
+ private static String getUpdatePrefix(T entity) {
+ Class cls = entity.getClass();
+ StringBuilder builder = new StringBuilder();
+ builder.append("UPDATE ").append(SqlFieldReader.getTableName(entity)).append(" SET ");
+ List fields = SqlFieldReader.getFields(entity);
+ try {
+ for (String field : fields) {
+ if (SqlFieldReader.hasValue(entity, field)) {
+ builder.append(field).append(" = #{")
+ .append(field).append("} ").append(",");
+ }
+ }
+ return builder.substring(0, builder.lastIndexOf(","));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/cheri/keller/common/proxy/ApiController.java b/src/main/java/com/cheri/keller/common/proxy/ApiController.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0cb36696462011f05c3c006821642f98a38b099
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/proxy/ApiController.java
@@ -0,0 +1,111 @@
+package com.cheri.keller.common.proxy;
+
+import com.cheri.keller.common.response.Response;
+import com.cheri.keller.common.util.RequestUtil;
+import com.cheri.keller.common.util.ResponseUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:27
+ **/
+@RestController
+@RequestMapping("/api")
+@CrossOrigin(origins = "*", allowedHeaders = "*", maxAge = 3600)
+public class ApiController {
+
+ @Resource
+ HttpServletRequest request;
+
+ @Autowired
+ RestTemplate restTemplate;
+
+ // TODO 转发前校验JWT和用户权限
+
+ /**
+ * json 格式的GET请求
+ *
+ * @param params
+ * @return
+ */
+ @GetMapping
+ public ResponseEntity get(@RequestBody Map params) {
+
+ ResponseEntity responseEntity;
+ try {
+ responseEntity = restTemplate.getForEntity(RequestUtil.getUrl(params, request), String.class, params);
+ RequestUtil.successLog(request, params, responseEntity);
+ } catch (Exception e) {
+ responseEntity = ResponseUtils.getResponseFromException(e);
+ RequestUtil.errorLog(request, params, responseEntity);
+ }
+ return responseEntity;
+ }
+
+
+ /**
+ * JSON 形式的 POST 请求
+ *
+ * @param params
+ * @return
+ */
+ @PostMapping
+ public ResponseEntity post(@RequestBody Map params) {
+ ResponseEntity responseEntity;
+ try {
+ responseEntity = restTemplate.postForEntity(RequestUtil.getUrl(params, request), null, String.class, params);
+ RequestUtil.successLog(request, params, responseEntity);
+ } catch (Exception e) {
+ responseEntity = ResponseUtils.getResponseFromException(e);
+ RequestUtil.errorLog(request, params, responseEntity);
+ }
+ return responseEntity;
+ }
+
+ /**
+ * JSON 格式的 PUT 请求
+ *
+ * @param params
+ * @return
+ */
+ @PutMapping
+ public ResponseEntity put(@RequestBody Map params) {
+ ResponseEntity responseEntity = Response.ok();
+ try {
+ restTemplate.put(RequestUtil.getUrl(params, request), null, params);
+ RequestUtil.successLog(request, params, responseEntity);
+ } catch (Exception e) {
+ responseEntity = ResponseUtils.getResponseFromException(e);
+ RequestUtil.errorLog(request, params, responseEntity);
+ }
+ return responseEntity;
+ }
+
+ /**
+ * JSON 形式的 DELETE 请求
+ *
+ * @param params
+ * @return
+ */
+ @DeleteMapping
+ public ResponseEntity delete(@RequestBody Map params) {
+ ResponseEntity responseEntity = Response.ok();
+ try {
+ restTemplate.delete(RequestUtil.getUrl(params, request), params);
+ RequestUtil.successLog(request, params, responseEntity);
+ } catch (Exception e) {
+ responseEntity = ResponseUtils.getResponseFromException(e);
+ RequestUtil.errorLog(request, params, responseEntity);
+ }
+ return responseEntity;
+ }
+}
+
diff --git a/src/main/java/com/cheri/keller/common/proxy/FormController.java b/src/main/java/com/cheri/keller/common/proxy/FormController.java
new file mode 100644
index 0000000000000000000000000000000000000000..96e68756fe2fb78e26f47ea8ed60d778d52a611b
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/proxy/FormController.java
@@ -0,0 +1,120 @@
+package com.cheri.keller.common.proxy;
+
+import com.cheri.keller.common.response.Response;
+import com.cheri.keller.common.util.RequestUtil;
+import com.cheri.keller.common.util.ResponseUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+/**
+ * 接口转发
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:28
+ **/
+@RestController
+@RequestMapping("/form")
+@CrossOrigin(origins = "*", allowedHeaders = "*", maxAge = 3600)
+public class FormController {
+
+ @Resource
+ HttpServletRequest request;
+
+ @Autowired
+ RestTemplate restTemplate;
+
+ // TODO 转发前校验JWT和用户权限
+
+ /**
+ * 表单形式的GET请求
+ *
+ * @return
+ */
+ @GetMapping
+ public ResponseEntity get() {
+ Map map = RequestUtil.getParam(request);
+ ResponseEntity responseEntity;
+ try {
+ String url = RequestUtil.getUrl(map, request);
+ if (map == null) {
+ responseEntity = restTemplate.getForEntity(url, String.class);
+ } else {
+ responseEntity = restTemplate.getForEntity(url, String.class, map);
+ }
+ RequestUtil.successLog(request, responseEntity);
+ } catch (Exception e) {
+ responseEntity = ResponseUtils.getResponseFromException(e);
+ RequestUtil.errorLog(request, responseEntity);
+ }
+ return responseEntity;
+ }
+
+
+ /**
+ * 表单形式的 POST 请求
+ *
+ * @return
+ */
+ @PostMapping
+ public ResponseEntity post() {
+ Map map = RequestUtil.getParam(request);
+ ResponseEntity responseEntity;
+ try {
+ responseEntity = restTemplate.postForEntity(RequestUtil.getUrl(map, request), null, String.class, map);
+ RequestUtil.successLog(request, responseEntity);
+ } catch (Exception e) {
+ responseEntity = ResponseUtils.getResponseFromException(e);
+ RequestUtil.errorLog(request, responseEntity);
+ }
+ return responseEntity;
+ }
+
+
+ /**
+ * 表单形式的 PUT 请求
+ *
+ * @return
+ */
+ @PutMapping
+ public ResponseEntity put() {
+ Map map = RequestUtil.getParam(request);
+ ResponseEntity responseEntity = Response.ok();
+ try {
+ restTemplate.put(RequestUtil.getUrl(map, request), null, map);
+ RequestUtil.successLog(request, responseEntity);
+ } catch (Exception e) {
+ responseEntity = ResponseUtils.getResponseFromException(e);
+ RequestUtil.errorLog(request, responseEntity);
+ }
+ return responseEntity;
+ }
+
+ /**
+ * 表单形式的 DELETE 请求
+ * restTemplate.delete()方法没有返回值,因此在delete操作时无法知道执行状态
+ *
+ * @return
+ */
+ @DeleteMapping
+ public ResponseEntity delete() {
+ Map map = RequestUtil.getParam(request);
+ ResponseEntity responseEntity = Response.ok();
+ try {
+ restTemplate.delete(RequestUtil.getUrl(map, request), map);
+ RequestUtil.successLog(request, responseEntity);
+ } catch (Exception e) {
+ responseEntity = ResponseUtils.getResponseFromException(e);
+ RequestUtil.errorLog(request, responseEntity);
+ }
+ return responseEntity;
+
+ }
+
+}
diff --git a/src/main/java/com/cheri/keller/common/response/Response.java b/src/main/java/com/cheri/keller/common/response/Response.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e6a01452f3032b1f2795b72a16452ac8710b1a8
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/response/Response.java
@@ -0,0 +1,80 @@
+package com.cheri.keller.common.response;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+
+/**
+ * 规定Controller统一的消息返回格式
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:18
+ **/
+public class Response {
+ /**
+ * 返回OK
+ *
+ * @param object
+ * @return
+ */
+ public static ResponseEntity ok(ResultData object) {
+ return ResponseEntity.ok()
+ .contentType(MediaType.APPLICATION_JSON).body(object);
+ }
+
+ /**
+ * 返回OK
+ *
+ * @return
+ */
+ public static ResponseEntity ok() {
+ ResultData response = ResultData.success();
+ return ResponseEntity.ok()
+ .contentType(MediaType.APPLICATION_JSON).body(response);
+ }
+
+ /**
+ * 异常请求
+ *
+ * @return
+ */
+ public static ResponseEntity badRequest() {
+ ResultData response = ResultData.error("请求参数异常");
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
+ .contentType(MediaType.APPLICATION_JSON).body(response);
+ }
+
+ /**
+ * 没有登录
+ *
+ * @return
+ */
+ public static ResponseEntity unauthorized() {
+ ResultData response = ResultData.error("用户未登录");
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
+ .contentType(MediaType.APPLICATION_JSON).body(response);
+ }
+
+ /**
+ * 没有访问权限
+ *
+ * @return
+ */
+ public static ResponseEntity forbidden() {
+ ResultData response = ResultData.error("没有权限");
+ return ResponseEntity.status(HttpStatus.FORBIDDEN)
+ .contentType(MediaType.APPLICATION_JSON).body(response);
+ }
+
+ /**
+ * 系统内部错误
+ *
+ * @return
+ */
+ public static ResponseEntity error() {
+ ResultData response = ResultData.error("系统错误");
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+ .contentType(MediaType.APPLICATION_JSON).body(response);
+ }
+}
diff --git a/src/main/java/com/cheri/keller/common/response/ResultData.java b/src/main/java/com/cheri/keller/common/response/ResultData.java
new file mode 100644
index 0000000000000000000000000000000000000000..311487fee172a0f4a163ba953ee86c751b8bf86a
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/response/ResultData.java
@@ -0,0 +1,89 @@
+package com.cheri.keller.common.response;
+
+/**
+ * * 规定Service统一的消息返回格式
+ * * 在Controller中统一返回ResponseEntity格式的数据,在ResponseEntity的body里,必须使用MyResponseObject格式的数据
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:19
+ **/
+public class ResultData {
+
+ /**
+ * 业务状态 0:成功 1:失败
+ */
+ private int success;
+ /**
+ * 返回数据
+ */
+ private Object data;
+ /**
+ * 文字描述,一般放业务处理失败时返回的错误信息
+ */
+ private String message;
+
+ public final static int SUCCESS_CODE_SUCCESS = 0;
+ public final static int SUCCESS_CODE_FAILED = 1;
+
+ public ResultData() {
+ }
+
+ public static ResultData success() {
+ ResultData resultData = new ResultData();
+ resultData.setSuccess(SUCCESS_CODE_SUCCESS);
+ return resultData;
+ }
+
+ public static ResultData success(Object data) {
+ ResultData resultData = new ResultData();
+ resultData.setSuccess(SUCCESS_CODE_SUCCESS);
+ resultData.setData(data);
+ return resultData;
+ }
+
+ public static ResultData error(String message) {
+ ResultData resultData = new ResultData();
+ resultData.setSuccess(SUCCESS_CODE_FAILED);
+ resultData.setMessage(message);
+ return resultData;
+ }
+
+ public boolean isSuccess() {
+ return success == SUCCESS_CODE_SUCCESS;
+ }
+
+
+ public int getSuccess() {
+ return success;
+ }
+
+ public void setSuccess(int success) {
+ this.success = success;
+ }
+
+ public Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public String toString() {
+ return "ResultData{" +
+ "success=" + success +
+ ", data=" + data +
+ ", message='" + message + '\'' +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/common/util/Console.java b/src/main/java/com/cheri/keller/common/util/Console.java
new file mode 100644
index 0000000000000000000000000000000000000000..772caf4941574e1040c097e5c824fd22f7f32243
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/util/Console.java
@@ -0,0 +1,57 @@
+package com.cheri.keller.common.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 控制台日志输出工具类
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:11
+ **/
+public class Console {
+
+ private static Logger logger = LoggerFactory.getLogger(Console.class);
+
+ public static void print(String title,Object ... objects){
+ System.out.println("=======" + title + "========");
+ for(Object object : objects){
+ System.out.print(object + "\t");
+ }
+ System.out.println();
+ }
+
+ public static void println(String title,Object ... objects){
+ System.out.println("=======" + title + "========");
+ for(Object object : objects){
+ System.out.println(object);
+ }
+ System.out.println();
+ }
+
+ public static void info(String title,Object ... objects){
+ if(logger.isInfoEnabled()){
+ StringBuilder builder = new StringBuilder();
+ builder.append("======").append(title).append("======");
+ for (Object object:objects){
+ builder.append("\n").append(object);
+ }
+ builder.append("\n");
+ logger.info(builder.toString());
+ }
+ }
+
+ public static void error(String title,Object ... objects){
+ if(logger.isErrorEnabled()){
+ StringBuilder builder = new StringBuilder();
+ builder.append("======").append(title).append("======");
+ for (Object object:objects){
+ builder.append("\n").append(object);
+ }
+ builder.append("\n");
+ logger.error(builder.toString());
+ }
+ }
+
+}
diff --git a/src/main/java/com/cheri/keller/common/util/RequestUtil.java b/src/main/java/com/cheri/keller/common/util/RequestUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..79c23ac843165a1ec61f8357d897d6c9a16d7402
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/util/RequestUtil.java
@@ -0,0 +1,142 @@
+package com.cheri.keller.common.util;
+
+import com.cheri.keller.common.config.RequestConfig;
+import org.springframework.http.ResponseEntity;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 请求参数验证工具类
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:12
+ **/
+public class RequestUtil {
+
+ public static String port;
+ public static String address;
+
+ /**
+ * 记录请求成功的日志(使用json提交参数的请求)
+ * @param request 请求
+ * @param params 请求参数
+ * @param response 应答
+ */
+ public static void successLog(HttpServletRequest request, Map params, ResponseEntity response){
+ String requestId = StringUtils.getUUID();
+ Console.info("api header",requestId,getHeader(request));
+ Console.info("param",requestId,params);
+ Console.info("response success",requestId,response.getBody());
+ }
+
+ /**
+ * 记录请求成功的日志(使用form提交参数的请求,请求在Request中)
+ * @param request 请求
+ * @param response 应答
+ */
+ public static void successLog(HttpServletRequest request, ResponseEntity response){
+ String requestId = StringUtils.getUUID();
+ Console.info("form header",requestId,getHeader(request));
+ Console.info("param",requestId,getParam(request));
+ Console.info("response success",requestId,response.getBody());
+ }
+
+ /**
+ * 记录请求失败的日志(使用json提交参数的请求)
+ * @param request 请求
+ * @param params 参数
+ * @param response 应答
+ */
+ public static void errorLog(HttpServletRequest request, Map params, ResponseEntity response){
+ String requestId = StringUtils.getUUID();
+ Console.error("api header",requestId,getHeader(request));
+ Console.error("param",requestId,params);
+ Console.error("response error",requestId,response.getBody());
+ }
+
+ /**
+ * 记录请求失败的日志(使用form提交参数的请求,请求在Request中)
+ * @param request 请求
+ * @param response 应答
+ */
+ public static void errorLog(HttpServletRequest request, ResponseEntity response){
+ String requestId = StringUtils.getUUID();
+ Console.error("form header",requestId,getHeader(request));
+ Console.error("param",requestId,getParam(request));
+ Console.error("response error",requestId,response.getBody());
+ }
+
+ public static HashMap getHeader(HttpServletRequest request){
+ HashMap headerMap = new HashMap<>(16);
+ //请求的URL地址
+ headerMap.put(RequestConfig.URL,request.getRequestURL().toString());
+ //请求的资源
+ headerMap.put(RequestConfig.URI,request.getRequestURI());
+ //请求方式 GET/POST
+ headerMap.put(RequestConfig.REQUEST_METHOD,request.getMethod());
+
+ //来访者的IP地址
+ headerMap.put(RequestConfig.REMOTE_ADDR,request.getRemoteAddr());
+ //来访者的HOST
+ headerMap.put(RequestConfig.REMOTE_HOST,request.getRemoteHost());
+ //来访者的端口
+ headerMap.put(RequestConfig.REMOTE_PORT,request.getRemotePort() + "");
+ //来访者的用户名
+ headerMap.put(RequestConfig.REMOTE_USER,request.getRemoteUser());
+
+
+ //自定义的Header (接口名)
+ headerMap.put(RequestConfig.METHOD,request.getHeader(RequestConfig.METHOD));
+ //自定义的Header (TOKEN)
+ headerMap.put(RequestConfig.TOKEN,request.getHeader(RequestConfig.TOKEN));
+ return headerMap;
+ }
+
+ public static Map getParam(HttpServletRequest request){
+ Map paramMap = new HashMap<>(16);
+ //request对象封装的参数是以Map的形式存储的
+ Map map = request.getParameterMap();
+ for(Map.Entry entry :map.entrySet()){
+ String paramName = entry.getKey();
+ String paramValue = "";
+ String[] paramValueArr = entry.getValue();
+ for (int i = 0; paramValueArr!=null && i < paramValueArr.length; i++) {
+ if (i == paramValueArr.length-1) {
+ paramValue += paramValueArr[i];
+ }else {
+ paramValue += paramValueArr[i]+",";
+ }
+ }
+ paramMap.put(paramName,paramValue);
+ }
+ if(paramMap.size() == 0){
+ return null;
+ }
+ return paramMap;
+ }
+ public static String getUrl(Map params, HttpServletRequest request){
+ HashMap headers = getHeader(request);
+ StringBuilder builder = new StringBuilder();
+
+ builder
+ .append(address).append(":")
+ .append(port).append("/")
+ .append(headers.get(RequestConfig.METHOD));
+ if(params == null){
+ return builder.toString();
+ }
+ builder.append("?");
+ for(String key :params.keySet()){
+ builder.append(key)
+ .append("={")
+ .append(key)
+ .append("}&");
+ }
+ Console.info(builder.toString());
+ return builder.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/common/util/ResponseUtils.java b/src/main/java/com/cheri/keller/common/util/ResponseUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..54023003624a482b384d2cc55c69ef01355c369b
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/util/ResponseUtils.java
@@ -0,0 +1,52 @@
+package com.cheri.keller.common.util;
+
+import com.cheri.keller.common.response.Response;
+import com.cheri.keller.common.response.ResultData;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.HttpClientErrorException;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:12
+ **/
+public class ResponseUtils {
+ /**
+ * 根据组件返回的错误码重组应答报文
+ *
+ * @param exception
+ * @return
+ */
+ public static ResponseEntity getResponseFromException(Exception exception) {
+ ResponseEntity response;
+ if (exception instanceof HttpClientErrorException) {
+ HttpClientErrorException errorException = (HttpClientErrorException) exception;
+ switch (errorException.getStatusCode()) {
+ case FORBIDDEN:
+ response = Response.forbidden();
+ break;
+ case BAD_REQUEST:
+ response = Response.badRequest();
+ break;
+ case UNAUTHORIZED:
+ response = Response.unauthorized();
+ break;
+ case INTERNAL_SERVER_ERROR:
+ response = Response.error();
+ break;
+ default: {
+ ResultData resultData = ResultData.error("ERROR");
+ response = ResponseEntity.status(errorException.getStatusCode()).contentType(MediaType.APPLICATION_JSON).body(resultData);
+ }
+ }
+ } else {
+ response = Response.badRequest();
+ }
+ return response;
+ }
+
+ public static ResultData getResultDataFromException(HttpClientErrorException exception) {
+ return (ResultData) getResponseFromException(exception).getBody();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/common/util/SendEmailUtils.java b/src/main/java/com/cheri/keller/common/util/SendEmailUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..e765436174165fd492e5bbc433e05507f95620c3
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/util/SendEmailUtils.java
@@ -0,0 +1,144 @@
+package com.cheri.keller.common.util;
+
+import com.cheri.keller.common.config.PublicConstant;
+import com.cheri.keller.entity.EmailLog;
+
+import javax.mail.Message;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import java.util.Properties;
+
+/**
+ * * SendEmailUtils.getSender(
+ * * myConfig.mailServerHost,
+ * * myConfig.mailServerUser,
+ * * myConfig.mailServerPassword)
+ * * .sendSimpleMail("atomiclong@aliyun.com",
+ * * "你好",
+ * * "邮件内容");
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 21:30
+ **/
+public class SendEmailUtils {
+ /**
+ * 邮件验证码标题
+ */
+ public static final String TITLE ="【From】" + PublicConstant.appName;
+
+ /**
+ * 邮件验证码正文内容
+ */
+ public static final String RegisterBody =
+ "验证码:%1$s,用于账号: %2$s 注册,泄露有风险。"
+ + PublicConstant.EMAIL_CODE_TIME + "分钟内使用有效。";
+
+ public static final String LoginBody =
+ "验证码:%1$s,用于账号: %2$s 登录,泄露有风险。"
+ + PublicConstant.EMAIL_CODE_TIME + "分钟内使用有效。";
+
+ public static final String ResetPasswordBody =
+ "验证码:%1$s,用于账号: %2$s 找回密码,泄露有风险。"
+ + PublicConstant.EMAIL_CODE_TIME + "分钟内使用有效。";
+
+
+ public static Session session= null;
+
+ /**
+ * 初始化Session
+ * @return
+ */
+ private static Session initSession(){
+ if(session == null) {
+ try {
+ Properties prop = new Properties();
+ //邮件服务器地址
+ prop.setProperty("mail.host", "smtp.qq.com");
+ //邮件发送协议
+ prop.setProperty("mail.transport.protocol", "smtp");
+ //是否需要身份验证
+ prop.setProperty("mail.smtp.auth", "true");
+ //创建session
+ session = Session.getInstance(prop);
+ //开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
+ session.setDebug(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return session;
+ }
+
+ /**
+ * 发送简单邮件
+ * @param toEmailAddress 收件人地址
+ * @param title 邮件标题
+ * @param content 邮件内容
+ * @throws Exception
+ */
+ private static void sendSimpleMail(String toEmailAddress,String title,String content)
+ throws Exception {
+ Session session = initSession();
+ //创建邮件对象
+ MimeMessage message = new MimeMessage(session);
+ //指明邮件的发件人
+ message.setFrom(new InternetAddress(PublicConstant.mailServerUser));
+ //指明邮件的收件人
+ message.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmailAddress));
+ //邮件的标题
+ message.setSubject(title);
+ //邮件的文本内容
+ message.setContent(content, "text/html;charset=UTF-8");
+ //返回创建好的邮件对象
+ Transport transport = session.getTransport();
+ //使用邮箱的用户名和密码连上邮件服务器,发送邮件时,发件人需要提交邮箱的用户名和密码给smtp服务器,用户名和密码都通过验证之后才能够正常发送邮件给收件人
+ transport.connect(PublicConstant.mailServerHost,PublicConstant.mailServerUser,PublicConstant.mailServerPassword);
+ transport.sendMessage(message, message.getAllRecipients());
+ transport.close();
+ }
+
+ /**
+ * 发送邮件验证码
+ * 1.随机生成指定位数的验证码
+ * 2.创建邮件实体类,设置收件人地址、邮件标题、邮件内容、验证码
+ * 3.发送邮件并记录发送结果
+ * 4.返回包含发送结果的邮件实体类
+ * @param email 收件人邮箱
+ * @return 邮件实体类EmailEntity
+ */
+ public static EmailLog sendVCode(int type, String email){
+ String code = StringUtils.getAllCharString(PublicConstant.EMAIL_CODE_LENGTH);
+ EmailLog entity = new EmailLog();
+ entity.setEmail(email);
+ entity.setType(type);
+ entity.setTitle(TITLE);
+ String body;
+ switch (type){
+ case PublicConstant.REGISTER_TYPE :
+ body = String.format(RegisterBody,code,email);
+ break;
+ case PublicConstant.LOGIN_TYPE :
+ body = String.format(LoginBody,code,email);
+ break;
+ case PublicConstant.RESET_PASSWORD_TYPE :
+ body = String.format(ResetPasswordBody,code,email);
+ break;
+ default:return null;
+ }
+ entity.setContent(body);
+ entity.setCode(code);
+ try {
+ sendSimpleMail(entity.getEmail(),entity.getTitle(),entity.getContent());
+ }catch (Exception e){
+ Console.error("send sendVerificationCode error :",e.getMessage());
+ entity.setResult(e.getMessage());
+ entity.setStatusCode(PublicConstant.FAILED);
+ }
+ return entity;
+ }
+
+
+}
diff --git a/src/main/java/com/cheri/keller/common/util/StringUtils.java b/src/main/java/com/cheri/keller/common/util/StringUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..d60aabd8ec8b724ab16bab376a3f592039cfec7a
--- /dev/null
+++ b/src/main/java/com/cheri/keller/common/util/StringUtils.java
@@ -0,0 +1,177 @@
+package com.cheri.keller.common.util;
+
+import java.util.Random;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:14
+ **/
+public class StringUtils {
+
+ /**
+ * 纯数字集合
+ */
+ private static final String numbersChar = "0123456789";
+
+ /**
+ * 大小写字母、数字集合
+ */
+ private static final String allChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+ /**
+ * 手机号校验规则:1开头的11位数字
+ */
+ private static final String PHONE_NUMBER_PATTERN = "^1+\\d{10}$";
+
+ /**
+ * 用户名校验规则:允许字母、数字、下划线
+ */
+ private static final String USER_NAME_PATTERN = "^[a-zA-Z0-9_]+$";
+
+ /**
+ * 邮箱校验规则:**@**.**
+ */
+ private static final String EMAIL_ADDRESS_PATTERN = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|" +
+ "(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$";
+
+ /**
+ * 校验是否是用户名
+ *
+ * @param string
+ * @return
+ */
+ public static boolean isUserName(String string) {
+ if (isEmpty(string)) {
+ return false;
+ }
+ Pattern pattern = Pattern.compile(USER_NAME_PATTERN);
+ Matcher matcher = pattern.matcher(string);
+ return matcher.matches();
+ }
+
+ /**
+ * 校验是否是手机号
+ *
+ * @param string
+ * @return
+ */
+ public static boolean isPhoneNo(String string) {
+ if (isEmpty(string)) {
+ return false;
+ }
+ Pattern pattern = Pattern.compile(PHONE_NUMBER_PATTERN);
+ Matcher matcher = pattern.matcher(string);
+ return matcher.matches();
+
+ }
+
+
+ public static boolean notPhoneOrEmail(String string) {
+ return !isEmail(string) && !isPhoneNo(string);
+ }
+
+ public static boolean isPhoneOrEmail(String string) {
+ return isEmail(string) || isPhoneNo(string);
+ }
+
+ /**
+ * 校验是否是邮箱
+ *
+ * @param string
+ * @return
+ */
+ public static boolean isEmail(String string) {
+ if (isEmpty(string)) {
+ return false;
+ }
+ Pattern pattern = Pattern.compile(EMAIL_ADDRESS_PATTERN);
+ Matcher matcher = pattern.matcher(string);
+ return matcher.matches();
+ }
+
+ public static boolean notEmail(String string) {
+ return !isEmail(string);
+ }
+
+
+ /**
+ * 判断一系列字符串中是否有空的(包含:空字符串、null、纯空格字符)
+ *
+ * @param parameters 需要判断的字符串,可以是多个
+ * @return
+ */
+ public static boolean isEmpty(String... parameters) {
+ for (String str : parameters) {
+ if (str == null || str.isEmpty() || str.trim().isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isNotEmpty(String... parameters) {
+ return !isEmpty(parameters);
+ }
+
+ /**
+ * 将字符串的首字母转大写
+ *
+ * @param str 需要转换的字符串
+ * @return
+ */
+ public static String captureName(String str) {
+ // 进行字母的ascii编码前移,效率要高于截取字符串进行转换的操作
+ char[] cs = str.toCharArray();
+ cs[0] -= 32;
+ return String.valueOf(cs);
+ }
+
+ /**
+ * 生成指定长度的数字字符串
+ *
+ * @param length 字符串长度
+ * @return
+ */
+ public static String getNumberString(int length) {
+ StringBuffer sb = new StringBuffer();
+ Random random = new Random();
+ for (int i = 0; i < length; i++) {
+ sb.append(numbersChar.charAt(random.nextInt(numbersChar.length())));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 生成指定长度的字符串(含大小写字母及数字)
+ *
+ * @param length
+ * @return
+ */
+ public static String getAllCharString(int length) {
+ StringBuffer sb = new StringBuffer();
+ Random random = new Random();
+ for (int i = 0; i < length; i++) {
+ sb.append(allChar.charAt(random.nextInt(allChar.length())));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 获取UUID(32位的字母数字的集合)
+ *
+ * @return
+ */
+ public static String getUUID() {
+ return UUID.randomUUID().toString().replace("-", "").toUpperCase();
+ }
+
+ public static void main(String[] args) {
+ Console.print("", isEmail("guyexing@cc.com"));
+ Console.print("UUID", getUUID());
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/controller/BaseController.java b/src/main/java/com/cheri/keller/controller/BaseController.java
new file mode 100644
index 0000000000000000000000000000000000000000..edd351d34d189af21cfaaa7bd715a6053a8566a0
--- /dev/null
+++ b/src/main/java/com/cheri/keller/controller/BaseController.java
@@ -0,0 +1,62 @@
+package com.cheri.keller.controller;
+
+import com.cheri.keller.common.config.PublicConstant;
+import com.cheri.keller.common.response.Response;
+import com.cheri.keller.common.util.Console;
+import com.cheri.keller.common.util.StringUtils;
+import com.cheri.keller.entity.UserInfo;
+import com.cheri.keller.service.UserService;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.Map;
+
+/**
+ * 不需要登录就能调用的接口
+ *
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 22:21
+ **/
+@RestController
+@RequestMapping("/base")
+@CrossOrigin(origins = "*",allowedHeaders="*", maxAge = 3600)
+public class BaseController {
+ @Resource
+ private UserService userService;
+
+ /**
+ * 注册功能
+ */
+ @PostMapping("/register")
+ public ResponseEntity register(@RequestBody Map params){
+ Console.info("register",params);
+ String email = params.get("email");
+ String password = params.get("password");
+ String code = params.get("code");
+ if(StringUtils.isEmpty(password,code) || StringUtils.notEmail(email)){
+ return Response.badRequest();
+ }
+ UserInfo userInfo = new UserInfo();
+ userInfo.setEmail(email);
+ userInfo.setPassword(password);
+ userInfo.setType(PublicConstant.DEFAULT_USER_TYPE);
+
+ return Response.ok(userService.register(userInfo,code));
+ }
+
+ /**
+ * 获取验证码
+ * @param email
+ * @return
+ */
+ @GetMapping("/getCodeForRegister")
+ public ResponseEntity getCode(String email){
+ Console.info("getCodeForRegister",email);
+ if(StringUtils.notEmail(email)){
+ return Response.badRequest();
+ }
+ return Response.ok(userService.sendRegisterCode(PublicConstant.DEFAULT_USER_TYPE,email));
+ }
+}
diff --git a/src/main/java/com/cheri/keller/controller/JsonResourceUtils.java b/src/main/java/com/cheri/keller/controller/JsonResourceUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..216f8511e1bbaa24a68665d57fb881ba95d1d50d
--- /dev/null
+++ b/src/main/java/com/cheri/keller/controller/JsonResourceUtils.java
@@ -0,0 +1,36 @@
+package com.cheri.keller.controller;
+
+import java.io.*;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/23 20:35
+ **/
+public class JsonResourceUtils {
+ public static String readJsonFile(String fileName) {
+ String jsonStr = "";
+ try {
+ File jsonFile = new File(fileName);
+ FileReader fileReader = new FileReader(jsonFile);
+
+ Reader reader = new InputStreamReader(new FileInputStream(jsonFile),"utf-8");
+ int ch = 0;
+ StringBuffer sb = new StringBuffer();
+ while ((ch = reader.read()) != -1) {
+ sb.append((char) ch);
+ }
+ fileReader.close();
+ reader.close();
+ jsonStr = sb.toString();
+ return jsonStr;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static void main(String[] args) throws Exception{
+
+ }
+}
diff --git a/src/main/java/com/cheri/keller/controller/PageUtil.java b/src/main/java/com/cheri/keller/controller/PageUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..68c9591755ff1a5d6287ae18e8090e0bf57b6620
--- /dev/null
+++ b/src/main/java/com/cheri/keller/controller/PageUtil.java
@@ -0,0 +1,50 @@
+package com.cheri.keller.controller;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/23 19:51
+ **/
+public class PageUtil {
+
+ private int code=0;
+ private String msg;
+ private Long count; //总条数
+ private List data = new ArrayList(); //装前台当前页的数据
+ //getter/setter方法...
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public Long getCount() {
+ return count;
+ }
+
+ public void setCount(Long count) {
+ this.count = count;
+ }
+
+ public List getData() {
+ return data;
+ }
+
+ public void setData(List data) {
+ this.data = data;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/controller/TestTableController.java b/src/main/java/com/cheri/keller/controller/TestTableController.java
new file mode 100644
index 0000000000000000000000000000000000000000..1fca5571f91ba2dc82cdc2be7ac5bd53689b0f89
--- /dev/null
+++ b/src/main/java/com/cheri/keller/controller/TestTableController.java
@@ -0,0 +1,32 @@
+package com.cheri.keller.controller;
+
+import com.cheri.keller.common.response.Response;
+import com.cheri.keller.common.response.ResultData;
+import com.cheri.keller.mapper.TestTableMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/23 19:30
+ **/
+@CrossOrigin(origins = "*",allowedHeaders="*", maxAge = 3600)
+@RestController
+@RequestMapping("/testTable")
+public class TestTableController {
+ @Autowired
+ TestTableMapper mapper;
+ @RequestMapping("/all")
+ @PostMapping
+ public String getAll(@RequestParam("taskIds") Long taskIds) {
+ List longs = Arrays.asList(taskIds);
+ int resultData = mapper.batchUpdate("1",longs,"0");
+ return JsonResourceUtils.readJsonFile("H:\\SoftwareDev\\LearningSpace\\keller\\src\\main\\resources\\config\\json.json");
+
+ }
+}
diff --git a/src/main/java/com/cheri/keller/controller/UserController.java b/src/main/java/com/cheri/keller/controller/UserController.java
new file mode 100644
index 0000000000000000000000000000000000000000..e70ebe4c6164016df9c7d34a8ed19f0215d8dbb4
--- /dev/null
+++ b/src/main/java/com/cheri/keller/controller/UserController.java
@@ -0,0 +1,39 @@
+package com.cheri.keller.controller;
+
+import com.cheri.keller.common.response.Response;
+import com.cheri.keller.common.response.ResultData;
+import com.cheri.keller.common.util.StringUtils;
+import com.cheri.keller.service.UserService;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:45
+ **/
+@RestController
+@RequestMapping("/user")
+public class UserController {
+ @Resource
+ private UserService userService;
+
+ @GetMapping
+ public ResponseEntity getAll() {
+ ResultData resultData = userService.getAll();
+ return Response.ok(resultData);
+ }
+
+ @GetMapping("/getByEmail")
+ public ResponseEntity getByEmail(String email) {
+ if (StringUtils.isEmpty(email)) {
+ return Response.badRequest();
+ }
+ ResultData resultData = userService.getByEmail(email);
+ return Response.ok(resultData);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/entity/EmailLog.java b/src/main/java/com/cheri/keller/entity/EmailLog.java
new file mode 100644
index 0000000000000000000000000000000000000000..fdd57d3c9e91d88872e35e3a60850123fb3f5017
--- /dev/null
+++ b/src/main/java/com/cheri/keller/entity/EmailLog.java
@@ -0,0 +1,194 @@
+package com.cheri.keller.entity;
+
+import com.cheri.keller.common.mybatis.BaseEntity;
+import com.cheri.keller.common.mybatis.annotation.*;
+
+import java.util.Date;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 21:04
+ **/
+@TableAttribute(name = "email_log")
+public class EmailLog extends BaseEntity {
+ @FieldAttribute
+ @KeyAttribute(autoIncr = true)
+ private int id;
+ /**
+ * 邮件发送类型
+ */
+ @FieldAttribute(value = "邮件发送类型,不能为空", notNull = true)
+ @IndexAttribute
+ private Integer type;
+ /**
+ * 收件人
+ */
+ @FieldAttribute(value = "收件人,不能为空", notNull = true)
+ @IndexAttribute
+ private String email;
+ /**
+ * 标题
+ */
+ @FieldAttribute(value = "邮件标题,不能为空", notNull = true, length = 200)
+ private String title;
+ /**
+ * 内容
+ */
+ @FieldAttribute(value = "邮件内容,不能为空", notNull = true, length = 500)
+ private String content;
+ /**
+ * 验证码
+ * 包含验证码的邮件,需要将验证码单独填写,方便查询
+ */
+ @FieldAttribute("验证码")
+ private String code;
+
+ @FieldAttribute(value = "发送结果描述,如:发送失败的原因等", length = 500)
+ private String result;
+ /**
+ * 状态码
+ * 0 成功
+ * 1 失败
+ */
+ @FieldAttribute(value = "发送状态", notNull = true)
+ private int statusCode;
+
+ /**
+ * 发送时间,验证码邮件的有效时间为5分钟
+ */
+ @FieldAttribute(value = "发送时间", notNull = true)
+ @SortAttribute
+ private Date createTime = new Date();
+
+ /**
+ * 验证码是否已使用 0:未使用 1:已使用
+ */
+ @FieldAttribute("验证码是否已使用")
+ private int isUsed = 0;
+
+ public EmailLog() {
+ super();
+ }
+
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public Integer getType() {
+ return type;
+ }
+
+ public void setType(Integer type) {
+ this.type = type;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getResult() {
+ return result;
+ }
+
+ public void setResult(String result) {
+ this.result = result;
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ public void setStatusCode(int statusCode) {
+ this.statusCode = statusCode;
+ }
+
+ public Date getCreateTime() {
+ return createTime;
+ }
+
+ public void setCreateTime(Date createTime) {
+ this.createTime = createTime;
+ }
+
+ public int getIsUsed() {
+ return isUsed;
+ }
+
+ public void setIsUsed(int isUsed) {
+ this.isUsed = isUsed;
+ }
+
+ @Override
+ public String toString() {
+ return "Email{" +
+ "id=" + id +
+ ", type=" + type +
+ ", email='" + email + '\'' +
+ ", title='" + title + '\'' +
+ ", content='" + content + '\'' +
+ ", code='" + code + '\'' +
+ ", result='" + result + '\'' +
+ ", statusCode=" + statusCode +
+ ", createTime=" + createTime +
+ ", isUsed=" + isUsed +
+ '}';
+ }
+
+
+ public boolean equalsCode(EmailLog emailLog) {
+ if (emailLog.getType().equals(type)
+ && emailLog.getEmail().equals(email)
+ && emailLog.getCode().equals(code)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * 是否是有效的验证码
+ * 要求成功发送,发送时间在5分钟内,且未使用过
+ *
+ * @return
+ */
+ public boolean isEfficientVerificationCode() {
+ return (System.currentTimeMillis() - createTime.getTime() < 5 * 60 * 1000
+ && isUsed == 0
+ && statusCode == 0
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/entity/TestTable.java b/src/main/java/com/cheri/keller/entity/TestTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..f2a618c62f9355b885d2a06f7b9f8973db00f980
--- /dev/null
+++ b/src/main/java/com/cheri/keller/entity/TestTable.java
@@ -0,0 +1,56 @@
+package com.cheri.keller.entity;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/23 17:42
+ **/
+public class TestTable {
+ private Integer id;
+ private String name;
+ private String state;
+ private Integer age;
+
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ public Integer getAge() {
+ return age;
+ }
+
+ public void setAge(Integer age) {
+ this.age = age;
+ }
+
+ @Override
+ public String toString() {
+ return "TestTable{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", state='" + state + '\'' +
+ ", age=" + age +
+ '}';
+ }
+}
diff --git a/src/main/java/com/cheri/keller/entity/UserInfo.java b/src/main/java/com/cheri/keller/entity/UserInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..d0b3f49d40a4d49ab7fff3d78ac269ba3e3c86e8
--- /dev/null
+++ b/src/main/java/com/cheri/keller/entity/UserInfo.java
@@ -0,0 +1,145 @@
+package com.cheri.keller.entity;
+
+import com.cheri.keller.common.mybatis.BaseEntity;
+import com.cheri.keller.common.mybatis.annotation.FieldAttribute;
+import com.cheri.keller.common.mybatis.annotation.IndexAttribute;
+import com.cheri.keller.common.mybatis.annotation.KeyAttribute;
+import com.cheri.keller.common.mybatis.annotation.TableAttribute;
+
+import java.util.Date;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 11:02
+ **/
+@TableAttribute(name = "user_info", comment = "用户信息表")
+public class UserInfo extends BaseEntity {
+ @KeyAttribute(autoIncr = true)
+ @FieldAttribute
+ private int id;
+
+ @FieldAttribute(value = "用户类型", notNull = true)
+ @IndexAttribute
+ private Integer type;
+
+
+ @FieldAttribute(value = "密码", length = 200)
+ private String password;
+
+ @FieldAttribute(value = "邮箱", notNull = true, length = 200)
+ @IndexAttribute
+ private String email;
+
+ @FieldAttribute
+ private Date createTime = new Date();
+
+ @FieldAttribute("用户账号状态")
+ @IndexAttribute
+ private Integer status;
+
+ @FieldAttribute("是否删除,1 表示删除")
+ @IndexAttribute
+ private Integer isDelete;
+
+ @FieldAttribute("最后一次修改时间")
+ private Date updateTime = new Date();
+
+ @FieldAttribute("修改人")
+ private Integer updateUserId;
+
+ public UserInfo() {
+ }
+
+ public UserInfo(int id) {
+ this.id = id;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public Integer getType() {
+ return type;
+ }
+
+ public void setType(Integer type) {
+ this.type = type;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+
+ public Date getCreateTime() {
+ return createTime;
+ }
+
+ public void setCreateTime(Date createTime) {
+ this.createTime = createTime;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public Integer getIsDelete() {
+ return isDelete;
+ }
+
+ public void setIsDelete(Integer isDelete) {
+ this.isDelete = isDelete;
+ }
+
+ public Date getUpdateTime() {
+ return updateTime;
+ }
+
+ public void setUpdateTime(Date updateTime) {
+ this.updateTime = updateTime;
+ }
+
+ public Integer getUpdateUserId() {
+ return updateUserId;
+ }
+
+ public void setUpdateUserId(Integer updateUserId) {
+ this.updateUserId = updateUserId;
+ }
+
+ @Override
+ public String toString() {
+ return "UserInfo{" +
+ "id=" + id +
+ ", type=" + type +
+ ", password='" + password + '\'' +
+ ", email='" + email + '\'' +
+ ", createTime=" + createTime +
+ ", status=" + status +
+ ", isDelete=" + isDelete +
+ ", updateTime=" + updateTime +
+ ", updateUserId=" + updateUserId +
+ '}';
+ }
+}
diff --git a/src/main/java/com/cheri/keller/mapper/EmailMapper.java b/src/main/java/com/cheri/keller/mapper/EmailMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..15f79e8674f88e2ba6db59cb38a64706e1dcb0aa
--- /dev/null
+++ b/src/main/java/com/cheri/keller/mapper/EmailMapper.java
@@ -0,0 +1,14 @@
+package com.cheri.keller.mapper;
+
+import com.cheri.keller.common.mybatis.BaseMapper;
+import com.cheri.keller.entity.EmailLog;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 21:28
+ **/
+@Mapper
+public interface EmailMapper extends BaseMapper {
+}
\ No newline at end of file
diff --git a/src/main/java/com/cheri/keller/mapper/TestTableMapper.java b/src/main/java/com/cheri/keller/mapper/TestTableMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3e83ceb03f9395ffbe84c7d0143df70c18abd1b
--- /dev/null
+++ b/src/main/java/com/cheri/keller/mapper/TestTableMapper.java
@@ -0,0 +1,21 @@
+package com.cheri.keller.mapper;
+
+import com.cheri.keller.entity.TestTable;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/23 17:31
+ **/
+public interface TestTableMapper {
+
+ List bacthSelect(@Param("ids")List ids);
+
+ int batchUpdate(@Param("state")String state, @Param("ids") List ids,
+ @Param("newState") String newState);
+
+ int batchDelete(@Param("ids")List ids);
+}
diff --git a/src/main/java/com/cheri/keller/mapper/UserMapper.java b/src/main/java/com/cheri/keller/mapper/UserMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..cf9c17818f4f72bf36964145fac819e39f6ee097
--- /dev/null
+++ b/src/main/java/com/cheri/keller/mapper/UserMapper.java
@@ -0,0 +1,36 @@
+package com.cheri.keller.mapper;
+
+import com.cheri.keller.common.mybatis.BaseMapper;
+import com.cheri.keller.entity.UserInfo;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Options;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:42
+ **/
+@Mapper
+public interface UserMapper extends BaseMapper {
+ @Select("select id, type, email, password, createTime, status, isDelete, updateTime, updateUserId " +
+ "from user_info")
+ List selectAll();
+
+ @Select("select id, type, email, password, createTime, status, isDelete, updateTime, updateUserId " +
+ "from user_info where id = #{id}")
+ UserInfo selectById(int id);
+
+ @Select("Select id, type, email, password, createTime, status, isDelete, updateTime, updateUserId " +
+ "from user_info where email = #{email} ")
+ UserInfo selectByEmail(String email);
+
+ @Insert("Insert into user_info (id, type, email, password, createTime, status, isDelete) " +
+ "values(#{id}, #{type}, #{email}, #{password}, #{createTime}, #{status}, #{isDelete})")
+ @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
+ Integer insertAndReturnKey(UserInfo userInfo);
+
+}
diff --git a/src/main/java/com/cheri/keller/service/EmailService.java b/src/main/java/com/cheri/keller/service/EmailService.java
new file mode 100644
index 0000000000000000000000000000000000000000..8bef9782d7140437facfacd2b959093c4adb8467
--- /dev/null
+++ b/src/main/java/com/cheri/keller/service/EmailService.java
@@ -0,0 +1,57 @@
+package com.cheri.keller.service;
+
+import com.cheri.keller.common.response.ResultData;
+import com.cheri.keller.common.util.SendEmailUtils;
+import com.cheri.keller.entity.EmailLog;
+import com.cheri.keller.mapper.EmailMapper;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 21:29
+ **/
+@Service
+public class EmailService {
+ @Resource
+ private EmailMapper mapper;
+
+ /**
+ * 发送邮件验证码
+ * @param type
+ * @param email
+ * @return
+ */
+ public ResultData sendCode(int type, String email){
+ EmailLog emailLog = SendEmailUtils.sendVCode(type,email);
+ if(emailLog == null){
+ return ResultData.error("邮件发送失败");
+ }
+ mapper.baseInsertAndReturnKey(emailLog);
+ return ResultData.success();
+ }
+
+ public boolean checkCode(EmailLog emailLog){
+ emailLog.setEmail(emailLog.getEmail());
+ emailLog.setBaseKyleUseASC(false);
+ List list = mapper.baseSelectByCondition(emailLog);
+ if(list == null || list.size() <= 0){
+ return false;
+ }
+ EmailLog result = list.get(0);
+ if(result.isEfficientVerificationCode() &&
+ result.equalsCode(emailLog)){
+ setCodeUsed(result);
+ return true;
+ }
+ return false;
+ }
+
+ private void setCodeUsed(EmailLog emailLog){
+ emailLog.setIsUsed(1);
+ mapper.baseUpdateById(emailLog);
+ }
+}
diff --git a/src/main/java/com/cheri/keller/service/UserService.java b/src/main/java/com/cheri/keller/service/UserService.java
new file mode 100644
index 0000000000000000000000000000000000000000..a0265e76a1b2baf9570006570c4ebfdbe88588f7
--- /dev/null
+++ b/src/main/java/com/cheri/keller/service/UserService.java
@@ -0,0 +1,131 @@
+package com.cheri.keller.service;
+
+import com.cheri.keller.common.config.PublicConstant;
+import com.cheri.keller.common.response.ResultData;
+import com.cheri.keller.entity.EmailLog;
+import com.cheri.keller.entity.UserInfo;
+import com.cheri.keller.mapper.UserMapper;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 12:43
+ **/
+@Service
+public class UserService {
+
+ @Resource
+ private UserMapper userMapper;
+
+ @Resource
+ private EmailService emailService;
+
+ /**
+ * 发送注册验证码
+ * 根据用户类型和邮箱判断用户是否已经注册,如果已注册,返回:该邮箱已注册
+ * 否则,发送邮件验证码
+ * @param type 用户类型
+ * @param email 邮箱
+ * @return
+ */
+ public ResultData sendRegisterCode(int type,String email){
+
+ UserInfo userInfo = getByEmailAndType(type,email);
+ System.out.println("======================>>>检查" + userInfo );
+ if(userInfo != null){
+ return ResultData.error("该邮箱已被注册");
+ }
+ return emailService.sendCode(PublicConstant.REGISTER_TYPE,email);
+ }
+
+
+ public ResultData register(UserInfo userInfo,String code){
+
+ EmailLog emailLog = new EmailLog();
+ emailLog.setEmail(userInfo.getEmail());
+ emailLog.setType(PublicConstant.REGISTER_TYPE);
+ emailLog.setCode(code);
+ if(emailService.checkCode(emailLog)){
+ userInfo = insert(userInfo);
+ if(userInfo == null){
+ return ResultData.error("注册失败");
+ }
+ return ResultData.success(userInfo.getId());
+ }
+ return ResultData.error("验证码错误或已过期,请重新获取");
+ }
+
+
+ public ResultData getAll(){
+ UserInfo userInfo = new UserInfo();
+ List list = userMapper.baseSelectAll(userInfo);
+ if(list == null || list.size() < 1){
+ return ResultData.error("没有数据");
+ }
+ return ResultData.success(list);
+ }
+
+ public ResultData getByEmail(String email){
+ UserInfo userInfo = new UserInfo();
+ userInfo.setEmail(email);
+ List list = userMapper.baseSelectByCondition(userInfo);
+ if(list != null && list.size() > 0){
+ return ResultData.success(list.get(0));
+ }
+ return ResultData.error("该邮箱未注册");
+ }
+ /**
+ * 根据类型和邮箱查询用户
+ * @param type
+ * @param email
+ * @return
+ */
+// private UserInfo getByEmailAndType(int type,String email){
+// UserInfo userInfo = new UserInfo();
+// userInfo.setEmail(email);
+// userInfo.setType(type);
+// userInfo.setBaseKyleUseAnd(true);
+// List list = userMapper.baseSelectByCondition(userInfo);
+// if(list != null && list.size() > 0){
+// return list.get(0);
+// }
+// return null;
+// }
+
+
+ private UserInfo insert(UserInfo userInfo){
+ UserInfo user = getByEmailAndType(userInfo.getType(),userInfo.getEmail());
+ if(user != null){
+ return null;
+ }
+ userMapper.baseInsertAndReturnKey(userInfo);
+ return userInfo;
+ }
+
+ /**
+ * 根据类型和邮箱查询用户
+ * @param type
+ * @param email
+ * @return
+ */
+ public UserInfo getByEmailAndType(int type,String email){
+ UserInfo userInfo = new UserInfo();
+ //指定邮箱地址为第一个查询条件
+ userInfo.setEmail(email);
+ //指定用户类型为第二个查询条件
+ userInfo.setType(type);
+ //指定多条件间使用 AND 连接
+ userInfo.setBaseKyleUseAnd(true);
+ //使用通用 Mapper 实现条件查询
+ List list = userMapper.baseSelectByCondition(userInfo);
+ System.out.println(list);
+ if(list != null && list.size() > 0){
+ return list.get(0);
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 0000000000000000000000000000000000000000..b51b9433f29ff87e6870afb5f41e4641c869c674
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1,24 @@
+## \u7AEF\u53E3
+server.port=8089
+## \u5E94\u7528\u540D
+spring.application.name=KellerNotes
+
+#mysql\u9A71\u52A8\u7C7B
+spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
+#mysql\u8FDE\u63A5\u5730\u5740
+spring.datasource.url = jdbc:mysql://localhost:3307/kyle_notes?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT
+#mysql\u7528\u6237\u540D
+spring.datasource.username = root
+#mysql\u5BC6\u7801
+spring.datasource.password = root
+
+mybatis.mapper-locations=classpath:mappers/*.xml
+logging.level.com.cheri.keller.mapper=debug
+
+##=====\u4EE5\u4E0B\u4E3A\u81EA\u5B9A\u4E49\u914D\u7F6E========
+#\u90AE\u4EF6\u53D1\u9001\u670D\u52A1\u5668\u5730\u5740
+mail.server.host = smtp.qq.com
+#\u7528\u6237\u540D
+mail.server.user =
+#\u5BC6\u7801
+mail.server.password =
\ No newline at end of file
diff --git a/src/main/resources/config/json.json b/src/main/resources/config/json.json
new file mode 100644
index 0000000000000000000000000000000000000000..7d2c474f3c2afd30280040c813a46c58759d60ef
--- /dev/null
+++ b/src/main/resources/config/json.json
@@ -0,0 +1,129 @@
+
+
+{
+ "code":0,
+ "msg":"",
+ "count":1000,
+ "data":[
+ {
+ "id":1,
+ "username":"user-0",
+ "sex":"女",
+ "city":"城市-0",
+ "sign":"签名-0",
+ "experience":255,
+ "logins":24,
+ "wealth":82830700,
+ "classify":"作家",
+ "score":57
+ },
+ {
+ "id":2,
+ "username":"user-0",
+ "sex":"女",
+ "city":"城市-0",
+ "sign":"签名-0",
+ "experience":255,
+ "logins":24,
+ "wealth":82830700,
+ "classify":"作家",
+ "score":57
+ },
+ {
+ "id":1,
+ "username":"user-0",
+ "sex":"女",
+ "city":"城市-0",
+ "sign":"签名-0",
+ "experience":255,
+ "logins":24,
+ "wealth":82830700,
+ "classify":"作家",
+ "score":57
+ },
+ {
+ "id":1,
+ "username":"user-0",
+ "sex":"女",
+ "city":"城市-0",
+ "sign":"签名-0",
+ "experience":255,
+ "logins":24,
+ "wealth":82830700,
+ "classify":"作家",
+ "score":57
+ },
+ {
+ "id":1,
+ "username":"user-0",
+ "sex":"女",
+ "city":"城市-0",
+ "sign":"签名-0",
+ "experience":255,
+ "logins":24,
+ "wealth":82830700,
+ "classify":"作家",
+ "score":57
+ },
+ {
+ "id":1,
+ "username":"user-0",
+ "sex":"女",
+ "city":"城市-0",
+ "sign":"签名-0",
+ "experience":255,
+ "logins":24,
+ "wealth":82830700,
+ "classify":"作家",
+ "score":57
+ },
+ {
+ "id":1,
+ "username":"user-0",
+ "sex":"女",
+ "city":"城市-0",
+ "sign":"签名-0",
+ "experience":255,
+ "logins":24,
+ "wealth":82830700,
+ "classify":"作家",
+ "score":57
+ },
+ {
+ "id":1,
+ "username":"user-0",
+ "sex":"女",
+ "city":"城市-0",
+ "sign":"签名-0",
+ "experience":255,
+ "logins":24,
+ "wealth":82830700,
+ "classify":"作家",
+ "score":57
+ },
+ {
+ "id":1,
+ "username":"user-0",
+ "sex":"女",
+ "city":"城市-0",
+ "sign":"签名-0",
+ "experience":255,
+ "logins":24,
+ "wealth":82830700,
+ "classify":"作家",
+ "score":57
+ },
+ {
+ "id":1,
+ "username":"user-0",
+ "sex":"女",
+ "city":"城市-0",
+ "sign":"签名-0",
+ "experience":255,
+ "logins":24,
+ "wealth":82830700,
+ "classify":"作家",
+ "score":57
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/main/resources/dbinfo/user_info.sql b/src/main/resources/dbinfo/user_info.sql
new file mode 100644
index 0000000000000000000000000000000000000000..9fa4100df15c671f9a116fa1eae846aff6b2598b
--- /dev/null
+++ b/src/main/resources/dbinfo/user_info.sql
@@ -0,0 +1,31 @@
+create table user_info
+(
+ id int auto_increment
+ primary key,
+ type int not null comment '用户类型',
+ email varchar(200) not null comment '邮箱',
+ password varchar(200) null comment '密码',
+ createTime datetime null,
+ status int null comment '用户账号状态',
+ isDelete int null,
+ updateTime datetime null,
+ updateUserId int null
+)
+comment '用户信息表'
+;
+
+create index user_info_index_email
+ on user_info (email)
+;
+
+create index user_info_index_status
+ on user_info (status)
+;
+
+create index user_info_index_type
+ on user_info (type)
+;
+
+
+INSERT INTO kyle_notes.user_info (id, type, email, password, createTime, status, isDelete, updateTime, updateUserId)
+VALUES (1, 0, 'guyexing@foxmail.com', '123456', '2019-11-25 19:34:25', null, null, null, null);
\ No newline at end of file
diff --git a/src/main/resources/mappers/TestTableMapper.xml b/src/main/resources/mappers/TestTableMapper.xml
new file mode 100644
index 0000000000000000000000000000000000000000..aded79ac1d55d279e41d79e868850f7d11918be9
--- /dev/null
+++ b/src/main/resources/mappers/TestTableMapper.xml
@@ -0,0 +1,44 @@
+
+
+
+
+ id,
+ name,
+ age,
+ state
+
+
+
+ update test_table t
+
+
+
+ when id= #{id} then #{newState}
+
+
+
+ where t.state = #{state}
+ and t.id in
+
+ #{id}
+
+
+
+ delete from test_table t
+ where
+ t.id in
+
+ #{id}
+
+
+
+
diff --git a/src/test/java/com/cheri/keller/KellerApplicationTests.java b/src/test/java/com/cheri/keller/KellerApplicationTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd9d65ab757c7a11caf59998c111fb552df6f047
--- /dev/null
+++ b/src/test/java/com/cheri/keller/KellerApplicationTests.java
@@ -0,0 +1,13 @@
+package com.cheri.keller;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class KellerApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/src/test/java/com/cheri/keller/mapper/TestTableMapperTest.java b/src/test/java/com/cheri/keller/mapper/TestTableMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..15671002c925303277a6b199eab79f1446670e8f
--- /dev/null
+++ b/src/test/java/com/cheri/keller/mapper/TestTableMapperTest.java
@@ -0,0 +1,40 @@
+package com.cheri.keller.mapper;
+
+import com.cheri.keller.entity.TestTable;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/23 17:46
+ **/
+@SpringBootTest
+class TestTableMapperTest {
+
+ @Autowired
+ TestTableMapper mapper;
+
+ @Test
+ void bacthSelect() {
+ List testTables = mapper.bacthSelect(Arrays.asList(1L,2L,3L,4L,4L,5L,6L));
+ System.out.println(testTables);
+ }
+
+ @Test
+ void batchUpdate() {
+
+ mapper.batchUpdate("1",Arrays.asList(1L,2L,3L,4L,4L,5L,6L),"9");
+ }
+
+ public static void main(String[] args) {
+ String s = "1,2,3,3,5,";
+ String[] S1 = s.split(",");
+ System.out.println(S1.length);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/cheri/keller/mapper/UserMapperTest.java b/src/test/java/com/cheri/keller/mapper/UserMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a3cf2572a5a9368a737ae4cc020fde23546942f
--- /dev/null
+++ b/src/test/java/com/cheri/keller/mapper/UserMapperTest.java
@@ -0,0 +1,55 @@
+package com.cheri.keller.mapper;
+
+import com.cheri.keller.common.util.Console;
+import com.cheri.keller.common.util.StringUtils;
+import com.cheri.keller.entity.EmailLog;
+import com.cheri.keller.entity.UserInfo;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * @author Aaron Du
+ * @version V1.0
+ * @date 2020/5/5 13:11
+ **/
+@SpringBootTest
+class UserMapperTest {
+
+ @Resource
+ private UserMapper userMapper;
+ @Resource
+ private EmailMapper emailMapper;
+
+ @Test
+ void contextLoads() {
+ }
+
+
+ @Test
+ public void initTestData() {
+ for (int i = 0; i < 10; i++) {
+ UserInfo userInfo = new UserInfo();
+ userInfo.setType(1);
+ userInfo.setEmail(StringUtils.getAllCharString(10));
+ userInfo.setPassword("123456");
+ userMapper.baseInsertAndReturnKey(userInfo);
+ Console.println(userInfo.getId() + "", userInfo);
+ }
+ }
+
+ @Test
+ public void getAll() {
+ List list = userMapper.baseSelectAll(new UserInfo());
+ for (UserInfo userInfo : list) {
+ Console.println(userInfo.getId() + "", userInfo);
+ }
+ }
+
+ @Test
+ public void createUserInfoTable() {
+ emailMapper.baseCreate(new EmailLog());
+ }
+}