diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..492e19852 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.mvn +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/target/ +/.nb-gradle/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..91e3c252c --- /dev/null +++ b/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "{}" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2018 Elune + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..0a15f203e --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# eladmin + +项目基于 Spring Boot 2.1.0 、 Spring boot Jpa、 Spring Security、redis、Vue的前后端分离的权限管理系统, 权限控制采用 RBAC 思想,支持 动态路由、项目1.0版本提供一个纯净的后台管理,第三方工具将在后面的版本中添加,前端源码地址:[el-admin-qd](el-admin-qd) + +#### 预览地址 +[https://auauz.net](https://auauz.net) + +#### 系统功能模块 + +- 用户管理 提供用户的相关配置 +- 角色管理 角色菜单分配权限 +- 权限管理 权限细化到接口 +- 菜单管理 已实现动态路由,后端可配置化 +- 系统日志 记录用户访问监控异常信息 +- 系统缓存管理 将redis的操作可视化,提供对redis的基本操作 +- Sql监控 采用 druid 监控数据库访问性能 + +#### 后端技术栈 + +- 基础框架:Spring Boot 2.1.0.RELEASE +- 持久层框架:Spring boot Jpa +- 安全框架:Spring Security +- 缓存框架:Redis +- 日志打印:logback+log4jdbc +- 接口文档 swagger2 +- 其他:fastjson,aop,MapStruct等。 + +#### 前端技术栈 + +- Vue +- vue-router +- axios +- element ui + +#### 系统预览 + + + + + + + + + + + + + + + + +
+ +#### 反馈交流 + +- QQ交流群:891137268 + +- 作者邮箱:zhengjie@tom.com \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100644 index 000000000..5551fde8e --- /dev/null +++ b/mvnw @@ -0,0 +1,286 @@ +#!/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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +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 + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" + 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 command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + wget "$jarUrl" -O "$wrapperJarPath" + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + curl -o "$wrapperJarPath" "$jarUrl" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + 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 + +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 000000000..e5cfb0ae9 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,161 @@ +@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 http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing my 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.4.2/maven-wrapper-0.4.2.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% ( + echo Found %WRAPPER_JAR% +) else ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" + echo Finished downloading %WRAPPER_JAR% +) +@REM End of extension + +%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 000000000..1c6cee676 --- /dev/null +++ b/pom.xml @@ -0,0 +1,167 @@ + + + 4.0.0 + + me.zhengjie + eladmin + v1.0 + jar + + elune + + + org.springframework.boot + spring-boot-starter-parent + 2.1.0.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + 0.9.1 + 1.2.0.Final + + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-cache + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + + redis.clients + jedis + 2.9.0 + + + + + org.apache.commons + commons-pool2 + 2.5.0 + + + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + + + org.bgee.log4jdbc-log4j2 + log4jdbc-log4j2-jdbc4.1 + 1.16 + + + + + io.springfox + springfox-swagger2 + 2.9.2 + + + io.springfox + springfox-swagger-ui + 2.9.2 + + + + mysql + mysql-connector-java + runtime + + + + com.alibaba + druid-spring-boot-starter + 1.1.10 + + + + + org.projectlombok + lombok + true + + + + cn.hutool + hutool-all + [4.1.12,) + + + + + com.alibaba + fastjson + 1.2.54 + + + + org.mapstruct + mapstruct-jdk8 + ${mapstruct.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + + + javax.inject + javax.inject + 1 + + + + + + + nexus-aliyun + Nexus aliyun + http://maven.aliyun.com/nexus/content/groups/public + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/src/main/java/me/zhengjie/AppRun.java b/src/main/java/me/zhengjie/AppRun.java new file mode 100644 index 000000000..17506c1d8 --- /dev/null +++ b/src/main/java/me/zhengjie/AppRun.java @@ -0,0 +1,17 @@ +package me.zhengjie; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author jie + * @date 2018/11/15 9:20:19 + */ +@SpringBootApplication +public class AppRun { + + public static void main(String[] args) { + + SpringApplication.run(AppRun.class, args); + } +} diff --git a/src/main/java/me/zhengjie/common/aop/log/Log.java b/src/main/java/me/zhengjie/common/aop/log/Log.java new file mode 100644 index 000000000..437a26ae5 --- /dev/null +++ b/src/main/java/me/zhengjie/common/aop/log/Log.java @@ -0,0 +1,16 @@ +package me.zhengjie.common.aop.log; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author jie + * @date 2018-11-24 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Log { + String description() default ""; +} diff --git a/src/main/java/me/zhengjie/common/aop/log/LogAspect.java b/src/main/java/me/zhengjie/common/aop/log/LogAspect.java new file mode 100644 index 000000000..1a81f7130 --- /dev/null +++ b/src/main/java/me/zhengjie/common/aop/log/LogAspect.java @@ -0,0 +1,69 @@ +package me.zhengjie.common.aop.log; + +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.common.exception.BadRequestException; +import me.zhengjie.monitor.domain.Logging; +import me.zhengjie.monitor.service.LoggingService; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author jie + * @date 2018-11-24 + */ +@Component +@Aspect +@Slf4j +public class LogAspect { + + @Autowired + private LoggingService loggingService; + + private long currentTime = 0L; + + /** + * 配置切入点 + */ + @Pointcut("@annotation(me.zhengjie.common.aop.log.Log)") + public void logPointcut() { + // 该方法无方法体,主要为了让同类中其他方法使用此切入点 + } + + /** + * 配置环绕通知,使用在方法logPointcut()上注册的切入点 + * + * @param joinPoint join point for advice + */ + @Around("logPointcut()") + public Object logAround(ProceedingJoinPoint joinPoint){ + Object result = null; + currentTime = System.currentTimeMillis(); + try { + result = joinPoint.proceed(); + } catch (Throwable e) { + throw new BadRequestException(e.getMessage()); + } + Logging logging = new Logging("INFO",System.currentTimeMillis() - currentTime); + loggingService.save(joinPoint, logging); + return result; + } + + /** + * 配置异常通知 + * + * @param joinPoint join point for advice + * @param e exception + */ + @AfterThrowing(pointcut = "logPointcut()", throwing = "e") + public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { + Logging logging = new Logging("ERROR",System.currentTimeMillis() - currentTime); + logging.setExceptionDetail(e.getMessage()); + loggingService.save((ProceedingJoinPoint)joinPoint, logging); + } +} diff --git a/src/main/java/me/zhengjie/common/exception/BadRequestException.java b/src/main/java/me/zhengjie/common/exception/BadRequestException.java new file mode 100644 index 000000000..efe7801de --- /dev/null +++ b/src/main/java/me/zhengjie/common/exception/BadRequestException.java @@ -0,0 +1,16 @@ +package me.zhengjie.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +/** + * @author jie + * @date 2018-11-23 + * 统一异常处理 + */ +@Getter +public class BadRequestException extends RuntimeException{ + public BadRequestException(String msg){ + super(msg); + } +} diff --git a/src/main/java/me/zhengjie/common/exception/EntityExistException.java b/src/main/java/me/zhengjie/common/exception/EntityExistException.java new file mode 100644 index 000000000..05db35e40 --- /dev/null +++ b/src/main/java/me/zhengjie/common/exception/EntityExistException.java @@ -0,0 +1,34 @@ +package me.zhengjie.common.exception; + +import org.springframework.util.StringUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.IntStream; + +/** + * @author jie + * @date 2018-11-23 + */ +public class EntityExistException extends RuntimeException { + + public EntityExistException(Class clazz, Object... saveBodyParamsMap) { + super(EntityExistException.generateMessage(clazz.getSimpleName(), toMap(String.class, String.class, saveBodyParamsMap))); + } + + private static String generateMessage(String entity, Map saveBodyParams) { + return StringUtils.capitalize(entity) + + " 已存在 " + + saveBodyParams; + } + + private static Map toMap( + Class keyType, Class valueType, Object... entries) { + if (entries.length % 2 == 1) + throw new IllegalArgumentException("Invalid entries"); + return IntStream.range(0, entries.length / 2).map(i -> i * 2) + .collect(HashMap::new, + (m, i) -> m.put(keyType.cast(entries[i]), valueType.cast(entries[i + 1])), + Map::putAll); + } +} \ No newline at end of file diff --git a/src/main/java/me/zhengjie/common/exception/EntityNotFoundException.java b/src/main/java/me/zhengjie/common/exception/EntityNotFoundException.java new file mode 100644 index 000000000..2dba29e21 --- /dev/null +++ b/src/main/java/me/zhengjie/common/exception/EntityNotFoundException.java @@ -0,0 +1,35 @@ +package me.zhengjie.common.exception; + +import org.springframework.util.StringUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.IntStream; + +/** + * @author jie + * @date 2018-11-23 + */ +public class EntityNotFoundException extends RuntimeException { + + public EntityNotFoundException(Class clazz, Object... searchParamsMap) { + super(EntityNotFoundException.generateMessage(clazz.getSimpleName(), toMap(String.class, String.class, searchParamsMap))); + } + + private static String generateMessage(String entity, Map searchParams) { + return StringUtils.capitalize(entity) + + " 不存在 " + + searchParams; + } + + private static Map toMap( + Class keyType, Class valueType, Object... entries) { + if (entries.length % 2 == 1) + throw new IllegalArgumentException("Invalid entries"); + return IntStream.range(0, entries.length / 2).map(i -> i * 2) + .collect(HashMap::new, + (m, i) -> m.put(keyType.cast(entries[i]), valueType.cast(entries[i + 1])), + Map::putAll); + } + +} \ No newline at end of file diff --git a/src/main/java/me/zhengjie/common/exception/handler/ApiError.java b/src/main/java/me/zhengjie/common/exception/handler/ApiError.java new file mode 100644 index 000000000..d4df6610e --- /dev/null +++ b/src/main/java/me/zhengjie/common/exception/handler/ApiError.java @@ -0,0 +1,30 @@ +package me.zhengjie.common.exception.handler; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import java.time.LocalDateTime; + +/** + * @author jie + * @date 2018-11-23 + */ +@Data +class ApiError { + + private Integer status; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime timestamp; + private String message; + + private ApiError() { + timestamp = LocalDateTime.now(); + } + + public ApiError(Integer status,String message) { + this(); + this.status = status; + this.message = message; + } +} + + diff --git a/src/main/java/me/zhengjie/common/exception/handler/GlobalExceptionHandler.java b/src/main/java/me/zhengjie/common/exception/handler/GlobalExceptionHandler.java new file mode 100644 index 000000000..7f812169a --- /dev/null +++ b/src/main/java/me/zhengjie/common/exception/handler/GlobalExceptionHandler.java @@ -0,0 +1,82 @@ +package me.zhengjie.common.exception.handler; + +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.common.exception.BadRequestException; +import me.zhengjie.common.exception.EntityExistException; +import me.zhengjie.common.exception.EntityNotFoundException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.NOT_FOUND; + +/** + * @author jie + * @date 2018-11-23 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 处理自定义异常 + * @param e + * @return + */ + @ExceptionHandler(value = BadRequestException.class) + public ResponseEntity badRequestException(BadRequestException e) { + log.error(e.getMessage()); + ApiError apiError = new ApiError(BAD_REQUEST.value(),e.getMessage()); + return buildResponseEntity(apiError); + } + + /** + * 处理 EntityExist + * @param e + * @return + */ + @ExceptionHandler(value = EntityExistException.class) + public ResponseEntity entityExistException(EntityExistException e) { + log.error(e.getMessage()); + ApiError apiError = new ApiError(BAD_REQUEST.value(),e.getMessage()); + return buildResponseEntity(apiError); + } + + /** + * 处理 EntityNotFound + * @param e + * @return + */ + @ExceptionHandler(value = EntityNotFoundException.class) + public ResponseEntity entityNotFoundException(EntityNotFoundException e) { + log.error(e.getMessage()); + ApiError apiError = new ApiError(NOT_FOUND.value(),e.getMessage()); + return buildResponseEntity(apiError); + } + + /** + * 处理所有接口数据验证异常 + * @param e + * @returns + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e){ + log.error(e.getMessage()); + String[] str = e.getBindingResult().getAllErrors().get(0).getCodes()[1].split("\\."); + StringBuffer msg = new StringBuffer(str[1]+":"); + msg.append(e.getBindingResult().getAllErrors().get(0).getDefaultMessage()); + ApiError apiError = new ApiError(BAD_REQUEST.value(),msg.toString()); + return buildResponseEntity(apiError); + } + + /** + * 统一返回 + * @param apiError + * @return + */ + private ResponseEntity buildResponseEntity(ApiError apiError) { + return new ResponseEntity(apiError, HttpStatus.valueOf(apiError.getStatus())); + } +} diff --git a/src/main/java/me/zhengjie/common/mapper/EntityMapper.java b/src/main/java/me/zhengjie/common/mapper/EntityMapper.java new file mode 100644 index 000000000..b6ec389a0 --- /dev/null +++ b/src/main/java/me/zhengjie/common/mapper/EntityMapper.java @@ -0,0 +1,38 @@ +package me.zhengjie.common.mapper; + +import java.util.List; + +/** + * @author jie + * @date 2018-11-23 + */ +public interface EntityMapper { + + /** + * DTO转Entity + * @param dto + * @return + */ + E toEntity(D dto); + + /** + * Entity转DTO + * @param entity + * @return + */ + D toDto(E entity); + + /** + * DTO集合转Entity集合 + * @param dtoList + * @return + */ + List toEntity(List dtoList); + + /** + * Entity集合转DTO集合 + * @param entityList + * @return + */ + List toDto(List entityList); +} diff --git a/src/main/java/me/zhengjie/common/redis/FastJsonRedisSerializer.java b/src/main/java/me/zhengjie/common/redis/FastJsonRedisSerializer.java new file mode 100644 index 000000000..8ccad494b --- /dev/null +++ b/src/main/java/me/zhengjie/common/redis/FastJsonRedisSerializer.java @@ -0,0 +1,43 @@ +package me.zhengjie.common.redis; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import java.nio.charset.Charset; + +/** + * Value 序列化 + * + * @author / + * @param + */ +public class FastJsonRedisSerializer implements RedisSerializer { + + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private Class clazz; + + public FastJsonRedisSerializer(Class clazz) { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException { + if (t == null) { + return new byte[0]; + } + return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException { + if (bytes == null || bytes.length <= 0) { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + return (T) JSON.parseObject(str, clazz); + } + +} diff --git a/src/main/java/me/zhengjie/common/redis/RedisConfig.java b/src/main/java/me/zhengjie/common/redis/RedisConfig.java new file mode 100644 index 000000000..4159f5e37 --- /dev/null +++ b/src/main/java/me/zhengjie/common/redis/RedisConfig.java @@ -0,0 +1,123 @@ +package me.zhengjie.common.redis; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.parser.ParserConfig; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.serializer.*; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.data.redis.core.RedisTemplate; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; +import java.time.Duration; + +/** + * @author jie + * @date 2018-11-24 + */ +@Slf4j +@Configuration +@EnableCaching +@ConditionalOnClass(RedisOperations.class) +@EnableConfigurationProperties(RedisProperties.class) +public class RedisConfig extends CachingConfigurerSupport { + + @Value("${spring.redis.host}") + private String host; + + @Value("${spring.redis.port}") + private int port; + + @Value("${spring.redis.timeout}") + private int timeout; + + @Value("${spring.redis.jedis.pool.max-idle}") + private int maxIdle; + + @Value("${spring.redis.jedis.pool.max-wait}") + private long maxWaitMillis; + + @Value("${spring.redis.password}") + private String password; + + /** + * 配置 redis 连接池 + * @return + */ + @Bean + public JedisPool redisPoolFactory(){ + JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); + jedisPoolConfig.setMaxIdle(maxIdle); + jedisPoolConfig.setMaxWaitMillis(maxWaitMillis); + if (StrUtil.isNotBlank(password)) { + return new JedisPool(jedisPoolConfig, host, port, timeout, password); + } else { + return new JedisPool(jedisPoolConfig, host, port,timeout); + } + } + + /** + * 设置 redis 数据默认过期时间 + * 设置@cacheable 序列化方式 + * @return + */ + @Bean + public RedisCacheConfiguration redisCacheConfiguration(){ + FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); + RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig(); + configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofDays(30)); + return configuration; + } + + @Bean(name = "redisTemplate") + @ConditionalOnMissingBean(name = "redisTemplate") + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + //序列化 + FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class); + // value值的序列化采用fastJsonRedisSerializer + template.setValueSerializer(fastJsonRedisSerializer); + template.setHashValueSerializer(fastJsonRedisSerializer); + // 全局开启AutoType,不建议使用 + // ParserConfig.getGlobalInstance().setAutoTypeSupport(true); + // 建议使用这种方式,小范围指定白名单 + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.system.service.dto"); + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.system.domain"); + // key的序列化采用StringRedisSerializer + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setConnectionFactory(redisConnectionFactory); + return template; + } + + /** + * 自定义缓存key生成策略 + * 使用方法 @Cacheable(keyGenerator="keyGenerator") + * @return + */ + @Bean + @Override + public KeyGenerator keyGenerator() { + return (target, method, params) -> { + StringBuilder sb = new StringBuilder(); + sb.append(target.getClass().getName()); + sb.append(method.getName()); + for (Object obj : params) { + sb.append(obj.toString()); + } + log.info(sb.toString()); + return sb.toString(); + }; + } +} \ No newline at end of file diff --git a/src/main/java/me/zhengjie/common/redis/StringRedisSerializer.java b/src/main/java/me/zhengjie/common/redis/StringRedisSerializer.java new file mode 100644 index 000000000..52bc80d75 --- /dev/null +++ b/src/main/java/me/zhengjie/common/redis/StringRedisSerializer.java @@ -0,0 +1,44 @@ +package me.zhengjie.common.redis; + +import cn.hutool.core.lang.Assert; +import com.alibaba.fastjson.JSON; +import org.springframework.data.redis.serializer.RedisSerializer; +import java.nio.charset.Charset; + +/** + * 重写序列化器 + * + * @author / + */ +public class StringRedisSerializer implements RedisSerializer { + + private final Charset charset; + + private final String target = "\""; + + private final String replacement = ""; + + public StringRedisSerializer() { + this(Charset.forName("UTF8")); + } + + public StringRedisSerializer(Charset charset) { + Assert.notNull(charset, "Charset must not be null!"); + this.charset = charset; + } + + @Override + public String deserialize(byte[] bytes) { + return (bytes == null ? null : new String(bytes, charset)); + } + + @Override + public byte[] serialize(Object object) { + String string = JSON.toJSONString(object); + if (string == null) { + return null; + } + string = string.replace(target, replacement); + return string.getBytes(charset); + } +} \ No newline at end of file diff --git a/src/main/java/me/zhengjie/common/swagger2/SwaggerConfig.java b/src/main/java/me/zhengjie/common/swagger2/SwaggerConfig.java new file mode 100644 index 000000000..39e5225a1 --- /dev/null +++ b/src/main/java/me/zhengjie/common/swagger2/SwaggerConfig.java @@ -0,0 +1,58 @@ +package me.zhengjie.common.swagger2; + +import com.google.common.base.Predicates; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.ParameterBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.schema.ModelRef; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Parameter; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.util.ArrayList; +import java.util.List; + +/** + * api页面 /swagger-ui.html + * 如controller在不同的包中,@ComponentScan(basePackages = {"me.aurora.app.rest","..."}) + * @author jie + * @date 2018-11-23 + */ + +@Configuration +@EnableSwagger2 +@ComponentScan(basePackages = {"me.zhengjie.core.rest","me.zhengjie.system.rest","me.zhengjie.system.monitor"}) +public class SwaggerConfig { + + @Bean + public Docket createRestApi() { + ParameterBuilder ticketPar = new ParameterBuilder(); + List pars = new ArrayList(); + ticketPar.name("Authorization").description("token") + .modelRef(new ModelRef("string")) + .parameterType("header") + .defaultValue("Bearer ") + .required(true) + .build(); + pars.add(ticketPar.build()); + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .paths(Predicates.not(PathSelectors.regex("/error.*"))) + .build() + .globalOperationParameters(pars); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("elune 接口文档") + .version("1.0") + .build(); + } + +} diff --git a/src/main/java/me/zhengjie/common/utils/IpUtil.java b/src/main/java/me/zhengjie/common/utils/IpUtil.java new file mode 100644 index 000000000..ed9076f65 --- /dev/null +++ b/src/main/java/me/zhengjie/common/utils/IpUtil.java @@ -0,0 +1,24 @@ +package me.zhengjie.common.utils; + +import javax.servlet.http.HttpServletRequest; + +/** + * 获取ip地址 + * @author X-rapido + * @date 2018-11-24 + */ +public class IpUtil { + public static String getIP(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return "0:0:0:0:0:0:0:1".equals(ip)?"127.0.0.1":ip; + } +} diff --git a/src/main/java/me/zhengjie/common/utils/PageUtil.java b/src/main/java/me/zhengjie/common/utils/PageUtil.java new file mode 100644 index 000000000..2b616d76e --- /dev/null +++ b/src/main/java/me/zhengjie/common/utils/PageUtil.java @@ -0,0 +1,51 @@ +package me.zhengjie.common.utils; + +import org.springframework.data.domain.Page; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 分页工具 + * + * @author jie + * @date 2018-12-10 + */ +public class PageUtil { + + /** + * List 分页 + * @param page + * @param size + * @param list + * @return + */ + public static List toPage(int page, int size , List list) { + int fromIndex = page * size; + int toIndex = page * size + size; + + if(fromIndex > list.size()){ + return new ArrayList(); + } else if(toIndex >= list.size()) { + return list.subList(fromIndex,list.size()); + } else { + return list.subList(fromIndex,toIndex); + } + } + + /** + * Page 数据处理,预防redis反序列化报错 + * @param page + * @return + */ + public static Map toPage(Page page) { + Map map = new HashMap(); + + map.put("content",page.getContent()); + map.put("totalElements",page.getTotalElements()); + + return map; + } + +} diff --git a/src/main/java/me/zhengjie/common/utils/RequestHolder.java b/src/main/java/me/zhengjie/common/utils/RequestHolder.java new file mode 100644 index 000000000..c7b55767f --- /dev/null +++ b/src/main/java/me/zhengjie/common/utils/RequestHolder.java @@ -0,0 +1,17 @@ +package me.zhengjie.common.utils; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import javax.servlet.http.HttpServletRequest; + +/** + * 获取 HttpServletRequest + * @author jie + * @date 2018-11-24 + */ +public class RequestHolder { + + public static HttpServletRequest getHttpServletRequest() { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + } +} diff --git a/src/main/java/me/zhengjie/common/utils/TimeUtil.java b/src/main/java/me/zhengjie/common/utils/TimeUtil.java new file mode 100644 index 000000000..2b7fca93c --- /dev/null +++ b/src/main/java/me/zhengjie/common/utils/TimeUtil.java @@ -0,0 +1,25 @@ +package me.zhengjie.common.utils; + +import java.util.Calendar; +import java.util.Date; + +/** + * @author jie + * @date 2018-12-13 + * + * 日期工具 + */ +public class TimeUtil { + + public static String getWeekDay(){ + String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date()); + + int w = cal.get(Calendar.DAY_OF_WEEK) - 1; + if (w < 0){ + w = 0; + } + return weekDays[w]; + } +} diff --git a/src/main/java/me/zhengjie/common/utils/ValidationUtil.java b/src/main/java/me/zhengjie/common/utils/ValidationUtil.java new file mode 100644 index 000000000..6edb4b86b --- /dev/null +++ b/src/main/java/me/zhengjie/common/utils/ValidationUtil.java @@ -0,0 +1,38 @@ +package me.zhengjie.common.utils; + +import me.zhengjie.common.exception.BadRequestException; +import java.util.Optional; + +/** + * 验证工具 + * @author jie + * @date 2018-11-23 + */ +public class ValidationUtil { + + /** + * 验证空 + * @param optional + */ + public static void isNull(Optional optional, String entity,String parameter , Object value){ + if(!optional.isPresent()){ + String msg = entity + + " 不存在 " + +"{ "+ parameter +":"+ value.toString() +" }"; + throw new BadRequestException(msg); + } + } + + /** + * 验证是否为邮箱 + * @param string + * @return + */ + public static boolean isEmail(String string) { + if (string == null){ + return false; + } + String regEx1 = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; + return string.matches(regEx1); + } +} diff --git a/src/main/java/me/zhengjie/core/config/CorsConfig.java b/src/main/java/me/zhengjie/core/config/CorsConfig.java new file mode 100644 index 000000000..bef66a4b6 --- /dev/null +++ b/src/main/java/me/zhengjie/core/config/CorsConfig.java @@ -0,0 +1,37 @@ +package me.zhengjie.core.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 跨域请求 + * + * @author jie + * @date 2018-11-30 + */ +@Configuration +@EnableWebMvc +public class CorsConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + //设置允许跨域的路径 + registry.addMapping("/**") + //设置允许跨域请求的域名 + .allowedOrigins("*") + //是否允许证书 不再默认开启 + .allowCredentials(true) + //设置允许的方法 + .allowedMethods("*") + //跨域允许时间 + .maxAge(3600); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0); + } +} diff --git a/src/main/java/me/zhengjie/core/config/WebSecurityConfig.java b/src/main/java/me/zhengjie/core/config/WebSecurityConfig.java new file mode 100644 index 000000000..bee8ec384 --- /dev/null +++ b/src/main/java/me/zhengjie/core/config/WebSecurityConfig.java @@ -0,0 +1,119 @@ +package me.zhengjie.core.config; + +import me.zhengjie.core.security.JwtAuthenticationEntryPoint; +import me.zhengjie.core.security.JwtAuthorizationTokenFilter; +import me.zhengjie.core.service.JwtUserDetailsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private JwtAuthenticationEntryPoint unauthorizedHandler; + + @Autowired + private JwtUserDetailsService jwtUserDetailsService; + + /** + * 自定义基于JWT的安全过滤器 + */ + @Autowired + JwtAuthorizationTokenFilter authenticationTokenFilter; + + @Value("${jwt.header}") + private String tokenHeader; + + @Value("${jwt.auth.path}") + private String authenticationPath; + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .userDetailsService(jwtUserDetailsService) + .passwordEncoder(passwordEncoderBean()); + } + + @Bean + public PasswordEncoder passwordEncoderBean() { + return new BCryptPasswordEncoder(); + } + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + httpSecurity + + // 禁用 CSRF + .csrf().disable() + + // 授权异常 + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() + + // 不创建会话 + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + + .authorizeRequests() + + .antMatchers("/auth/**").permitAll() + + .antMatchers("/druid/**").anonymous() + // swagger start + .antMatchers("/swagger-ui.html").anonymous() + .antMatchers("/swagger-resources/**").anonymous() + .antMatchers("/webjars/**").anonymous() + .antMatchers("/*/api-docs").anonymous() + // swagger end + + .antMatchers(HttpMethod.OPTIONS, "/**").anonymous() + + // 所有请求都需要认证 + .anyRequest().authenticated(); + + httpSecurity + .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + } + + @Override + public void configure(WebSecurity web) throws Exception { + // AuthenticationTokenFilter will ignore the below paths + web + .ignoring() + .antMatchers( + HttpMethod.POST, + authenticationPath + ) + + // allow anonymous resource requests + .and() + .ignoring() + .antMatchers( + HttpMethod.GET, + "/*.html", + "/**/*.html", + "/**/*.css", + "/**/*.js" + ); + } +} diff --git a/src/main/java/me/zhengjie/core/rest/AuthenticationController.java b/src/main/java/me/zhengjie/core/rest/AuthenticationController.java new file mode 100644 index 000000000..477a2a277 --- /dev/null +++ b/src/main/java/me/zhengjie/core/rest/AuthenticationController.java @@ -0,0 +1,80 @@ +package me.zhengjie.core.rest; + +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.common.aop.log.Log; +import me.zhengjie.core.security.AuthenticationToken; +import me.zhengjie.core.security.AuthorizationUser; +import me.zhengjie.core.utils.JwtTokenUtil; +import me.zhengjie.core.security.JwtUser; +import me.zhengjie.core.utils.EncryptUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AccountExpiredException; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; + +/** + * @author jie + * @date 2018-11-23 + * 授权、根据token获取用户详细信息 + */ +@Slf4j +@RestController +@RequestMapping("auth") +public class AuthenticationController { + + @Value("${jwt.header}") + private String tokenHeader; + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private JwtTokenUtil jwtTokenUtil; + + @Autowired + @Qualifier("jwtUserDetailsService") + private UserDetailsService userDetailsService; + + /** + * 登录授权 + * @param authorizationUser + * @return + */ + @Log(description = "用户登录") + @PostMapping(value = "${jwt.auth.path}") + public ResponseEntity authenticationLogin(@RequestBody AuthorizationUser authorizationUser){ + + final UserDetails userDetails = userDetailsService.loadUserByUsername(authorizationUser.getUsername()); + + if(!userDetails.getPassword().equals(EncryptUtils.encryptPassword(authorizationUser.getPassword()))){ + throw new AccountExpiredException("密码错误"); + } + + if(!userDetails.isEnabled()){ + throw new AccountExpiredException("账号已停用,请联系管理员"); + } + + // 生成令牌 + final String token = jwtTokenUtil.generateToken(userDetails); + + // 返回 token + return ResponseEntity.ok(new AuthenticationToken(token)); + } + + /** + * 获取用户信息 + * @param request + * @return + */ + @GetMapping(value = "${jwt.auth.account}") + public ResponseEntity getUserInfo(HttpServletRequest request){ + JwtUser jwtUser = (JwtUser)userDetailsService.loadUserByUsername(jwtTokenUtil.getUserName(request)); + return ResponseEntity.ok(jwtUser); + } +} diff --git a/src/main/java/me/zhengjie/core/security/AuthenticationToken.java b/src/main/java/me/zhengjie/core/security/AuthenticationToken.java new file mode 100644 index 000000000..8e0c6253a --- /dev/null +++ b/src/main/java/me/zhengjie/core/security/AuthenticationToken.java @@ -0,0 +1,17 @@ +package me.zhengjie.core.security; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import java.io.Serializable; + +/** + * @author jie + * @date 2018-11-23 + * 返回token + */ +@Getter +@AllArgsConstructor +public class AuthenticationToken implements Serializable { + + private final String token; +} diff --git a/src/main/java/me/zhengjie/core/security/AuthorizationUser.java b/src/main/java/me/zhengjie/core/security/AuthorizationUser.java new file mode 100644 index 000000000..f434beda6 --- /dev/null +++ b/src/main/java/me/zhengjie/core/security/AuthorizationUser.java @@ -0,0 +1,26 @@ +package me.zhengjie.core.security; + +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; + +/** + * @author jie + * @date 2018-11-30 + */ +@Getter +@Setter +public class AuthorizationUser { + + @NotBlank + private String username; + + @NotBlank + private String password; + + @Override + public String toString() { + return "{username=" + username + ", password= ******}"; + } +} diff --git a/src/main/java/me/zhengjie/core/security/JwtAuthenticationEntryPoint.java b/src/main/java/me/zhengjie/core/security/JwtAuthenticationEntryPoint.java new file mode 100644 index 000000000..69d689af3 --- /dev/null +++ b/src/main/java/me/zhengjie/core/security/JwtAuthenticationEntryPoint.java @@ -0,0 +1,26 @@ +package me.zhengjie.core.security; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.Serializable; + +@Component +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { + + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, + HttpServletResponse response, + AuthenticationException authException) throws IOException { + /** + * 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应 + */ + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException==null?"Unauthorized":authException.getMessage()); + } +} diff --git a/src/main/java/me/zhengjie/core/security/JwtAuthorizationTokenFilter.java b/src/main/java/me/zhengjie/core/security/JwtAuthorizationTokenFilter.java new file mode 100644 index 000000000..d3df5639b --- /dev/null +++ b/src/main/java/me/zhengjie/core/security/JwtAuthorizationTokenFilter.java @@ -0,0 +1,76 @@ +package me.zhengjie.core.security; + +import io.jsonwebtoken.ExpiredJwtException; +import me.zhengjie.core.utils.JwtTokenUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +public class JwtAuthorizationTokenFilter extends OncePerRequestFilter { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final UserDetailsService userDetailsService; + private final JwtTokenUtil jwtTokenUtil; + private final String tokenHeader; + + public JwtAuthorizationTokenFilter(@Qualifier("jwtUserDetailsService") UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil, @Value("${jwt.header}") String tokenHeader) { + this.userDetailsService = userDetailsService; + this.jwtTokenUtil = jwtTokenUtil; + this.tokenHeader = tokenHeader; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { + logger.debug("processing authentication for '{}'", request.getRequestURL()); + + final String requestHeader = request.getHeader(this.tokenHeader); + + String username = null; + String authToken = null; + if (requestHeader != null && requestHeader.startsWith("Bearer ")) { + authToken = requestHeader.substring(7); + try { + username = jwtTokenUtil.getUsernameFromToken(authToken); + } catch (ExpiredJwtException e) { + logger.error(e.getMessage()); + } + } else { + logger.warn("couldn't find bearer string, will ignore the header"); + } + + logger.debug("checking authentication for user '{}'", username); + + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + logger.debug("security context was null, so authorizating user"); + + // It is not compelling necessary to load the use details from the database. You could also store the information + // in the token and read it from it. It's up to you ;) + UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); + + // For simple validation it is completely sufficient to just check the token integrity. You don't have to call + // the database compellingly. Again it's up to you ;) + if (jwtTokenUtil.validateToken(authToken, userDetails)) { + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + logger.info("authorizated user '{}', setting security context", username); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + chain.doFilter(request, response); + } +} diff --git a/src/main/java/me/zhengjie/core/security/JwtUser.java b/src/main/java/me/zhengjie/core/security/JwtUser.java new file mode 100644 index 000000000..4967f9cd4 --- /dev/null +++ b/src/main/java/me/zhengjie/core/security/JwtUser.java @@ -0,0 +1,78 @@ +package me.zhengjie.core.security; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import java.util.*; + +/** + * @author jie + * @date 2018-11-23 + */ +@Getter +@AllArgsConstructor +public class JwtUser implements UserDetails { + + @JsonIgnore + private final Long id; + + private final String username; + + @JsonIgnore + private final String password; + + private final String avatar; + + private final String email; + + @JsonIgnore + private final Collection authorities; + + private final boolean enabled; + + @JsonIgnore + private final Date lastPasswordResetDate; + + @JsonIgnore + @Override + public boolean isAccountNonExpired() { + return true; + } + + @JsonIgnore + @Override + public boolean isAccountNonLocked() { + return true; + } + + @JsonIgnore + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @JsonIgnore + @Override + public String getPassword() { + return password; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + /** + * 在我们保存权限的时候加上了前缀ROLE_,因此在这里需要处理下数据 + * @return + */ + public Collection getRoles() { + Set roles = new LinkedHashSet<>(); + for (GrantedAuthority authority : authorities) { + roles.add(authority.getAuthority().substring(5)); + } + return roles; + } +} diff --git a/src/main/java/me/zhengjie/core/service/JwtUserDetailsService.java b/src/main/java/me/zhengjie/core/service/JwtUserDetailsService.java new file mode 100644 index 000000000..5c11a18b8 --- /dev/null +++ b/src/main/java/me/zhengjie/core/service/JwtUserDetailsService.java @@ -0,0 +1,84 @@ +package me.zhengjie.core.service; + +import me.zhengjie.common.exception.EntityNotFoundException; +import me.zhengjie.common.utils.ValidationUtil; +import me.zhengjie.core.security.JwtUser; +import me.zhengjie.system.domain.Permission; +import me.zhengjie.system.domain.Role; +import me.zhengjie.system.domain.User; +import me.zhengjie.system.repository.PermissionRepository; +import me.zhengjie.system.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author jie + * @date 2018-11-22 + */ +@Service +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class JwtUserDetailsService implements UserDetailsService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private PermissionRepository permissionRepository; + + @Override + public UserDetails loadUserByUsername(String username){ + + User user = null; + if(ValidationUtil.isEmail(username)){ + user = userRepository.findByEmail(username); + } else { + user = userRepository.findByUsername(username); + } + + if (user == null) { + throw new EntityNotFoundException(User.class, "name", username); + } else { + return create(user); + } + } + + public UserDetails create(User user) { + return new JwtUser( + user.getId(), + user.getUsername(), + user.getPassword(), + user.getAvatar(), + user.getEmail(), + mapToGrantedAuthorities(user.getRoles(),permissionRepository), + user.getEnabled(), + user.getLastPasswordResetTime() + ); + } + + private static List mapToGrantedAuthorities(Set roles,PermissionRepository permissionRepository) { + + Set permissions = new HashSet<>(); + for (Role role : roles) { + Set roleSet = new HashSet<>(); + roleSet.add(role); + permissions.addAll(permissionRepository.findByRoles(roleSet)); + } + + return permissions.stream() + .map(permission -> new SimpleGrantedAuthority("ROLE_"+permission.getName())) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/me/zhengjie/core/utils/EncryptUtils.java b/src/main/java/me/zhengjie/core/utils/EncryptUtils.java new file mode 100644 index 000000000..4522b156b --- /dev/null +++ b/src/main/java/me/zhengjie/core/utils/EncryptUtils.java @@ -0,0 +1,24 @@ +package me.zhengjie.core.utils; + +import org.springframework.util.DigestUtils; + +/** + * 加密 + * @author jie + * @date 2018-11-23 + */ +public class EncryptUtils { + + /** + * 密码加密 + * @param password + * @return + */ + public static String encryptPassword(String password){ + return DigestUtils.md5DigestAsHex(password.getBytes()); + } + + public static void main(String[] args) { + System.out.println(encryptPassword("e10adc3949ba59abbe56e057f20f883e")); + } +} diff --git a/src/main/java/me/zhengjie/core/utils/JwtTokenUtil.java b/src/main/java/me/zhengjie/core/utils/JwtTokenUtil.java new file mode 100644 index 000000000..e7ea56cf5 --- /dev/null +++ b/src/main/java/me/zhengjie/core/utils/JwtTokenUtil.java @@ -0,0 +1,154 @@ +package me.zhengjie.core.utils; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.impl.DefaultClock; +import me.zhengjie.common.exception.BadRequestException; +import me.zhengjie.core.security.JwtUser; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AccountExpiredException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import static org.springframework.http.HttpStatus.UNAUTHORIZED; + +@Component +public class JwtTokenUtil implements Serializable { + + static final String CLAIM_KEY_USERNAME = "sub"; + static final String CLAIM_KEY_CREATED = "iat"; + private static final long serialVersionUID = -3301605591108950415L; + private Clock clock = DefaultClock.INSTANCE; + + @Value("${jwt.secret}") + private String secret; + + @Value("${jwt.expiration}") + private Long expiration; + + @Value("${jwt.header}") + private String tokenHeader; + + public String getUsernameFromToken(String token) { + return getClaimFromToken(token, Claims::getSubject); + } + + public Date getIssuedAtDateFromToken(String token) { + return getClaimFromToken(token, Claims::getIssuedAt); + } + + public Date getExpirationDateFromToken(String token) { + return getClaimFromToken(token, Claims::getExpiration); + } + + public T getClaimFromToken(String token, Function claimsResolver) { + final Claims claims = getAllClaimsFromToken(token); + return claimsResolver.apply(claims); + } + + private Claims getAllClaimsFromToken(String token) { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } + + private Boolean isTokenExpired(String token) { + final Date expiration = getExpirationDateFromToken(token); + return expiration.before(clock.now()); + } + + private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) { + return (lastPasswordReset != null && created.before(lastPasswordReset)); + } + + private Boolean ignoreTokenExpiration(String token) { + // here you specify tokens, for that the expiration is ignored + return false; + } + + public String generateToken(UserDetails userDetails) { + Map claims = new HashMap<>(); + return doGenerateToken(claims, userDetails.getUsername()); + } + + private String doGenerateToken(Map claims, String subject) { + final Date createdDate = clock.now(); + final Date expirationDate = calculateExpirationDate(createdDate); + + return Jwts.builder() + .setClaims(claims) + .setSubject(subject) + .setIssuedAt(createdDate) + .setExpiration(expirationDate) + .signWith(SignatureAlgorithm.HS512, secret) + .compact(); + } + + public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) { + final Date created = getIssuedAtDateFromToken(token); + return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset) + && (!isTokenExpired(token) || ignoreTokenExpiration(token)); + } + + public String refreshToken(String token) { + final Date createdDate = clock.now(); + final Date expirationDate = calculateExpirationDate(createdDate); + + final Claims claims = getAllClaimsFromToken(token); + claims.setIssuedAt(createdDate); + claims.setExpiration(expirationDate); + + return Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret) + .compact(); + } + + public Boolean validateToken(String token, UserDetails userDetails) { + JwtUser user = (JwtUser) userDetails; + final String username = getUsernameFromToken(token); + final Date created = getIssuedAtDateFromToken(token); + //final Date expiration = getExpirationDateFromToken(token); + return ( + username.equals(user.getUsername()) + && !isTokenExpired(token) + && !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate()) + ); + } + public String getUserName(HttpServletRequest request){ + + String authToken = request.getHeader(tokenHeader); + + if(StringUtils.isEmpty(authToken)||authToken.length()<7){ + throw new AccountExpiredException("令牌已过期或无效"); + } + + final String token = authToken.substring(7); + String username = null; + try { + username = getUsernameFromToken(token); + } catch (ExpiredJwtException e){ + throw new AccountExpiredException("令牌已过期或无效"); + } + + if(StringUtils.isEmpty(username)){ + throw new AccountExpiredException("令牌已过期或无效"); + } + + return username; + } + + private Date calculateExpirationDate(Date createdDate) { + return new Date(createdDate.getTime() + expiration); + } +} + diff --git a/src/main/java/me/zhengjie/monitor/domain/Logging.java b/src/main/java/me/zhengjie/monitor/domain/Logging.java new file mode 100644 index 000000000..327285616 --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/domain/Logging.java @@ -0,0 +1,73 @@ +package me.zhengjie.monitor.domain; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.CreationTimestamp; +import javax.persistence.*; +import java.sql.Timestamp; + +/** + * @author jie + * @date 2018-11-24 + */ +@Entity +@Data +@Table(name = "log") +@NoArgsConstructor +public class Logging { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** + * 操作用户 + */ + private String username; + + /** + * 描述 + */ + private String description; + + /** + * 方法名 + */ + private String method; + + /** + * 参数 + */ + private String params; + + /** + * 日志类型 + */ + private String logType; + + /** + * 请求ip + */ + private String requestIp; + + /** + * 请求耗时 + */ + private Long time; + + /** + * 异常详细 + */ + private String exceptionDetail; + + /** + * 创建日期 + */ + @CreationTimestamp + private Timestamp createTime; + + public Logging(String logType, Long time) { + this.logType = logType; + this.time = time; + } +} diff --git a/src/main/java/me/zhengjie/monitor/domain/Visits.java b/src/main/java/me/zhengjie/monitor/domain/Visits.java new file mode 100644 index 000000000..9d3b27864 --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/domain/Visits.java @@ -0,0 +1,36 @@ +package me.zhengjie.monitor.domain; + +import lombok.Data; +import org.hibernate.annotations.CreationTimestamp; + +import javax.persistence.*; +import java.sql.Timestamp; + +/** + * pv 与 ip 统计 + * + * @author jie + * @date 2018-12-13 + */ +@Entity +@Data +@Table(name = "visits") +public class Visits { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String date; + + @Column(name = "pv_counts") + private Long pvCounts; + + @Column(name = "ip_counts") + private Long ipCounts; + + @CreationTimestamp + private Timestamp createTime; + + private String weekDay; +} diff --git a/src/main/java/me/zhengjie/monitor/domain/vo/RedisVo.java b/src/main/java/me/zhengjie/monitor/domain/vo/RedisVo.java new file mode 100644 index 000000000..1868b6aa3 --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/domain/vo/RedisVo.java @@ -0,0 +1,23 @@ +package me.zhengjie.monitor.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +/** + * @author jie + * @date 2018-12-10 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RedisVo { + + @NotBlank + private String key; + + @NotBlank + private String value; +} diff --git a/src/main/java/me/zhengjie/monitor/repository/LoggingRepository.java b/src/main/java/me/zhengjie/monitor/repository/LoggingRepository.java new file mode 100644 index 000000000..b7c4b29be --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/repository/LoggingRepository.java @@ -0,0 +1,26 @@ +package me.zhengjie.monitor.repository; + +import me.zhengjie.monitor.domain.Logging; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * @author jie + * @date 2018-11-24 + */ +@Repository +public interface LoggingRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 获取一个时间段的IP记录 + * @param date1 + * @param date2 + * @return + */ + @Query(value = "select count(*) FROM (select * FROM log where createTime between ?1 and ?2 GROUP BY requestIp) as s",nativeQuery = true) + Long findIp(String date1, String date2); +} diff --git a/src/main/java/me/zhengjie/monitor/repository/VisitsRepository.java b/src/main/java/me/zhengjie/monitor/repository/VisitsRepository.java new file mode 100644 index 000000000..373952e00 --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/repository/VisitsRepository.java @@ -0,0 +1,34 @@ +package me.zhengjie.monitor.repository; + +import me.zhengjie.monitor.domain.Visits; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * @author jie + * @date 2018-12-13 + */ +@Repository +public interface VisitsRepository extends JpaRepository { + + /** + * findByDate + * @param date + * @return + */ + Visits findByDate(String date); + + /** + * 获得一个时间段的记录 + * @param date1 + * @param date2 + * @return + */ + @Query(value = "select * FROM visits where " + + "createTime between ?1 and ?2",nativeQuery = true) + List findAllVisits(String date1, String date2); +} diff --git a/src/main/java/me/zhengjie/monitor/rest/LoggingController.java b/src/main/java/me/zhengjie/monitor/rest/LoggingController.java new file mode 100644 index 000000000..20fb21b0b --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/rest/LoggingController.java @@ -0,0 +1,30 @@ +package me.zhengjie.monitor.rest; + +import me.zhengjie.monitor.domain.Logging; +import me.zhengjie.monitor.service.query.LoggingQueryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author jie + * @date 2018-11-24 + */ +@RestController +@RequestMapping("api") +public class LoggingController { + + @Autowired + private LoggingQueryService loggingQueryService; + + @GetMapping(value = "logs") + @PreAuthorize("hasAnyRole('ADMIN','USER')") + public ResponseEntity getLogs(Logging logging, Pageable pageable){ + return new ResponseEntity(loggingQueryService.queryAll(logging,pageable), HttpStatus.OK); + } +} diff --git a/src/main/java/me/zhengjie/monitor/rest/RedisController.java b/src/main/java/me/zhengjie/monitor/rest/RedisController.java new file mode 100644 index 000000000..f28c1de80 --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/rest/RedisController.java @@ -0,0 +1,63 @@ +package me.zhengjie.monitor.rest; + +import me.zhengjie.common.aop.log.Log; +import me.zhengjie.monitor.domain.vo.RedisVo; +import me.zhengjie.monitor.service.RedisService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * @author jie + * @date 2018-12-10 + */ +@RestController +@RequestMapping("api") +public class RedisController { + + @Autowired + private RedisService redisService; + + @Log(description = "查询Redis缓存") + @GetMapping(value = "/redis") + @PreAuthorize("hasAnyRole('ADMIN','REDIS_ALL','REDIS_SELECT')") + public ResponseEntity getRedis(String key, Pageable pageable){ + return new ResponseEntity(redisService.findByKey(key,pageable), HttpStatus.OK); + } + + @Log(description = "新增Redis缓存") + @PostMapping(value = "/redis") + @PreAuthorize("hasAnyRole('ADMIN','REDIS_ALL','REDIS_CREATE')") + public ResponseEntity create(@Validated @RequestBody RedisVo resources){ + redisService.save(resources); + return new ResponseEntity(HttpStatus.CREATED); + } + + @Log(description = "修改Redis缓存") + @PutMapping(value = "/redis") + @PreAuthorize("hasAnyRole('ADMIN','REDIS_ALL','REDIS_EDIT')") + public ResponseEntity update(@Validated @RequestBody RedisVo resources){ + redisService.save(resources); + return new ResponseEntity(HttpStatus.NO_CONTENT); + } + + @Log(description = "删除Redis缓存") + @DeleteMapping(value = "/redis/{key}") + @PreAuthorize("hasAnyRole('ADMIN','REDIS_ALL','REDIS_DELETE')") + public ResponseEntity delete(@PathVariable String key){ + redisService.delete(key); + return new ResponseEntity(HttpStatus.OK); + } + + @Log(description = "清空Redis缓存") + @DeleteMapping(value = "/redis/all") + @PreAuthorize("hasAnyRole('ADMIN','REDIS_ALL','REDIS_DELETE')") + public ResponseEntity deleteAll(){ + redisService.flushdb(); + return new ResponseEntity(HttpStatus.OK); + } +} diff --git a/src/main/java/me/zhengjie/monitor/rest/VisitsController.java b/src/main/java/me/zhengjie/monitor/rest/VisitsController.java new file mode 100644 index 000000000..7b74e31f4 --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/rest/VisitsController.java @@ -0,0 +1,39 @@ +package me.zhengjie.monitor.rest; + +import me.zhengjie.common.utils.RequestHolder; +import me.zhengjie.monitor.service.VisitsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author jie + * @date 2018-12-13 + */ +@RestController +@RequestMapping("api") +public class VisitsController { + + @Autowired + private VisitsService visitsService; + + @PostMapping(value = "/visits") + public ResponseEntity create(){ + visitsService.save(RequestHolder.getHttpServletRequest()); + return new ResponseEntity(HttpStatus.CREATED); + } + + @GetMapping(value = "/visits") + public ResponseEntity get(){ + return new ResponseEntity(visitsService.get(),HttpStatus.OK); + } + + @GetMapping(value = "/visits/chartData") + public ResponseEntity getChartData(){ + return new ResponseEntity(visitsService.getChartData(),HttpStatus.OK); + } +} diff --git a/src/main/java/me/zhengjie/monitor/service/LoggingService.java b/src/main/java/me/zhengjie/monitor/service/LoggingService.java new file mode 100644 index 000000000..1cc9b8751 --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/service/LoggingService.java @@ -0,0 +1,20 @@ +package me.zhengjie.monitor.service; + +import me.zhengjie.monitor.domain.Logging; +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.scheduling.annotation.Async; + +/** + * @author jie + * @date 2018-11-24 + */ +public interface LoggingService { + + /** + * 新增日志 + * @param joinPoint + * @param logging + */ + @Async + void save(ProceedingJoinPoint joinPoint, Logging logging); +} diff --git a/src/main/java/me/zhengjie/monitor/service/RedisService.java b/src/main/java/me/zhengjie/monitor/service/RedisService.java new file mode 100644 index 000000000..1bd77de4d --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/service/RedisService.java @@ -0,0 +1,37 @@ +package me.zhengjie.monitor.service; + +import me.zhengjie.monitor.domain.vo.RedisVo; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +/** + * 可自行扩展 + * @author jie + * @date 2018-12-10 + */ +public interface RedisService { + + /** + * findById + * @param key + * @return + */ + public Page findByKey(String key, Pageable pageable); + + /** + * create + * @param redisVo + */ + public void save(RedisVo redisVo); + + /** + * delete + * @param key + */ + public void delete(String key); + + /** + * 清空所有缓存 + */ + public void flushdb(); +} diff --git a/src/main/java/me/zhengjie/monitor/service/VisitsService.java b/src/main/java/me/zhengjie/monitor/service/VisitsService.java new file mode 100644 index 000000000..adf6ffd24 --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/service/VisitsService.java @@ -0,0 +1,31 @@ +package me.zhengjie.monitor.service; + +import org.springframework.scheduling.annotation.Async; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author jie + * @date 2018-12-13 + */ +public interface VisitsService { + + /** + * 新增记录 + * @param request + */ + @Async + void save(HttpServletRequest request); + + /** + * 获取数据 + * @return + */ + Object get(); + + /** + * getChartData + * @return + */ + Object getChartData(); +} diff --git a/src/main/java/me/zhengjie/monitor/service/impl/LoggingServiceImpl.java b/src/main/java/me/zhengjie/monitor/service/impl/LoggingServiceImpl.java new file mode 100644 index 000000000..e1d05800c --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/service/impl/LoggingServiceImpl.java @@ -0,0 +1,91 @@ +package me.zhengjie.monitor.service.impl; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import me.zhengjie.common.utils.IpUtil; +import me.zhengjie.common.utils.RequestHolder; +import me.zhengjie.core.security.AuthorizationUser; +import me.zhengjie.core.utils.JwtTokenUtil; +import me.zhengjie.monitor.domain.Logging; +import me.zhengjie.monitor.repository.LoggingRepository; +import me.zhengjie.monitor.service.LoggingService; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; + +/** + * @author jie + * @date 2018-11-24 + */ +@Service +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class LoggingServiceImpl implements LoggingService { + + @Autowired + private LoggingRepository loggingRepository; + + @Value("${jwt.header}") + private String tokenHeader; + + @Autowired + private JwtTokenUtil jwtTokenUtil; + + private final String LOGINPATH = "authenticationLogin"; + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(ProceedingJoinPoint joinPoint, Logging logging){ + + // 获取request + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + me.zhengjie.common.aop.log.Log log = method.getAnnotation(me.zhengjie.common.aop.log.Log.class); + + // 描述 + if (log != null) { + logging.setDescription(log.description()); + } + + // 方法路径 + String methodName = joinPoint.getTarget().getClass().getName()+"."+signature.getName()+"()"; + + String params = "{"; + //参数值 + Object[] argValues = joinPoint.getArgs(); + //参数名称 + String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames(); + // 用户名 + String username = ""; + + if(argValues != null){ + for (int i = 0; i < argValues.length; i++) { + params += " " + argNames[i] + ": " + argValues[i]; + } + } + + // 获取IP地址 + logging.setRequestIp(IpUtil.getIP(request)); + + if(!LOGINPATH.equals(signature.getName())){ + UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + username = userDetails.getUsername(); + } else { + AuthorizationUser user = JSONUtil.toBean(new JSONObject(argValues[0]),AuthorizationUser.class); + username = user.getUsername(); + + } + logging.setMethod(methodName); + logging.setUsername(username); + logging.setParams(params + " }"); + loggingRepository.save(logging); + } +} diff --git a/src/main/java/me/zhengjie/monitor/service/impl/RedisServiceImpl.java b/src/main/java/me/zhengjie/monitor/service/impl/RedisServiceImpl.java new file mode 100644 index 000000000..b573c978e --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/service/impl/RedisServiceImpl.java @@ -0,0 +1,92 @@ +package me.zhengjie.monitor.service.impl; + +import me.zhengjie.common.utils.PageUtil; +import me.zhengjie.monitor.domain.vo.RedisVo; +import me.zhengjie.monitor.service.RedisService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import java.util.*; + +/** + * @author jie + * @date 2018-12-10 + */ +@Service +public class RedisServiceImpl implements RedisService { + + @Autowired + JedisPool pool; + + @Override + public Page findByKey(String key, Pageable pageable){ + Jedis jedis = null; + try{ + jedis = pool.getResource(); + List redisVos = new ArrayList<>(); + + if(!key.equals("*")){ + key = "*" + key + "*"; + } + for (String s : jedis.keys(key)) { + RedisVo redisVo = new RedisVo(s,jedis.get(s)); + redisVos.add(redisVo); + } + Page page = new PageImpl( + PageUtil.toPage(pageable.getPageNumber(),pageable.getPageSize(),redisVos), + pageable, + redisVos.size()); + return page; + }finally{ + if(null != jedis){ + jedis.close(); // 释放资源还给连接池 + } + } + + } + + @Override + public void save(RedisVo redisVo) { + Jedis jedis = null; + try{ + jedis = pool.getResource(); + jedis.set(redisVo.getKey(),redisVo.getValue()); + }finally{ + if(null != jedis){ + jedis.close(); // 释放资源还给连接池 + } + } + } + + @Override + public void delete(String key) { + Jedis jedis = null; + try{ + jedis = pool.getResource(); + jedis.del(key); + }finally{ + if(null != jedis){ + jedis.close(); // 释放资源还给连接池 + } + } + + } + + @Override + public void flushdb() { + Jedis jedis = null; + try{ + jedis = pool.getResource(); + jedis.flushDB(); + }finally{ + if(null != jedis){ + jedis.close(); // 释放资源还给连接池 + } + } + + } +} diff --git a/src/main/java/me/zhengjie/monitor/service/impl/VisitsServiceImpl.java b/src/main/java/me/zhengjie/monitor/service/impl/VisitsServiceImpl.java new file mode 100644 index 000000000..03cf67228 --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/service/impl/VisitsServiceImpl.java @@ -0,0 +1,87 @@ +package me.zhengjie.monitor.service.impl; + +import me.zhengjie.common.utils.IpUtil; +import me.zhengjie.common.utils.RequestHolder; +import me.zhengjie.common.utils.TimeUtil; +import me.zhengjie.monitor.domain.Logging; +import me.zhengjie.monitor.domain.Visits; +import me.zhengjie.monitor.repository.LoggingRepository; +import me.zhengjie.monitor.repository.VisitsRepository; +import me.zhengjie.monitor.service.VisitsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletRequest; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author jie + * @date 2018-12-13 + */ +@Service +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class VisitsServiceImpl implements VisitsService { + + @Autowired + private VisitsRepository visitsRepository; + + @Autowired + private LoggingRepository loggingRepository; + + @Override + public void save(HttpServletRequest request) { + LocalDate localDate = LocalDate.now(); + Visits visits = visitsRepository.findByDate(localDate.toString()); + if(visits != null){ + visits.setPvCounts(visits.getPvCounts()+1); + long ipCounts = loggingRepository.findIp(localDate.toString(), localDate.plusDays(1).toString()); + visits.setIpCounts(ipCounts); + }else { + visits = new Visits(); + visits.setWeekDay(TimeUtil.getWeekDay()); + visits.setPvCounts(1L); + visits.setIpCounts(1L); + visits.setDate(localDate.toString()); + } + visitsRepository.save(visits); + } + + @Override + public Object get() { + Map map = new HashMap(); + LocalDate localDate = LocalDate.now(); + Visits visits = visitsRepository.findByDate(localDate.toString()); + if(visits == null){ + save(RequestHolder.getHttpServletRequest()); + } + List list = visitsRepository.findAllVisits(localDate.minusDays(6).toString(),localDate.plusDays(1).toString()); + + long recentVisits = 0, recentIp = 0; + for (Visits data : list) { + recentVisits += data.getPvCounts(); + recentIp += data.getIpCounts(); + } + map.put("newVisits",visits.getPvCounts()); + map.put("newIp",visits.getIpCounts()); + map.put("recentVisits",recentVisits); + map.put("recentIp",recentIp); + return map; + } + + @Override + public Object getChartData() { + Map map = new HashMap(); + LocalDate localDate = LocalDate.now(); + List list = visitsRepository.findAllVisits(localDate.minusDays(6).toString(),localDate.plusDays(1).toString()); + map.put("weekDays",list.stream().map(Visits::getWeekDay).collect(Collectors.toList())); + map.put("visitsData",list.stream().map(Visits::getPvCounts).collect(Collectors.toList())); + map.put("ipData",list.stream().map(Visits::getIpCounts).collect(Collectors.toList())); + return map; + } +} diff --git a/src/main/java/me/zhengjie/monitor/service/query/LoggingQueryService.java b/src/main/java/me/zhengjie/monitor/service/query/LoggingQueryService.java new file mode 100644 index 000000000..6406ae23d --- /dev/null +++ b/src/main/java/me/zhengjie/monitor/service/query/LoggingQueryService.java @@ -0,0 +1,67 @@ +package me.zhengjie.monitor.service.query; + +import me.zhengjie.monitor.domain.Logging; +import me.zhengjie.monitor.repository.LoggingRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.List; + +/** + * @author jie + * @date 2018-11-24 + */ +@Service +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class LoggingQueryService { + + @Autowired + private LoggingRepository loggingRepository; + + public Page queryAll(Logging logging, Pageable pageable){ + + return loggingRepository.findAll(new Spec(logging),pageable); + } + + public List queryAll(Logging logging){ + + return loggingRepository.findAll(new Spec(logging)); + } + + class Spec implements Specification { + + private Logging logging; + + public Spec(Logging logging){ + this.logging = logging; + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) { + + List list = new ArrayList(); + + + if(!ObjectUtils.isEmpty(logging.getUsername())){ + list.add(cb.like(root.get("username").as(String.class),"%"+logging.getUsername()+"%")); + } + + if (!ObjectUtils.isEmpty(logging.getLogType())) { + list.add(cb.equal(root.get("logType").as(String.class), logging.getLogType())); + } + + Predicate[] p = new Predicate[list.size()]; + return cb.and(list.toArray(p)); + } + } +} diff --git a/src/main/java/me/zhengjie/system/domain/Menu.java b/src/main/java/me/zhengjie/system/domain/Menu.java new file mode 100644 index 000000000..9845f8843 --- /dev/null +++ b/src/main/java/me/zhengjie/system/domain/Menu.java @@ -0,0 +1,55 @@ +package me.zhengjie.system.domain; + +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import java.sql.Timestamp; +import java.util.Set; + +/** + * @author jie + * @date 2018-12-17 + */ +@Entity +@Getter +@Setter +@Table(name = "menu") +public class Menu { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank + private String name; + + @Column(unique = true) + private Long soft; + + @Column(name = "path") + private String path; + + private String component; + + private String icon; + + /** + * 上级菜单ID + */ + @Column(name = "pid",nullable = false) + private Long pid; + + /** + * 是否为外链 true/false + */ + private Boolean iFrame; + + @ManyToMany + @JoinTable(name = "menus_roles", joinColumns = {@JoinColumn(name = "menu_id",referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "id")}) + private Set roles; + + @CreationTimestamp + private Timestamp createTime; +} diff --git a/src/main/java/me/zhengjie/system/domain/Permission.java b/src/main/java/me/zhengjie/system/domain/Permission.java new file mode 100644 index 000000000..592ec9468 --- /dev/null +++ b/src/main/java/me/zhengjie/system/domain/Permission.java @@ -0,0 +1,58 @@ +package me.zhengjie.system.domain; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.Set; + +/** + * @author jie + * @date 2018-12-03 + */ +@Entity +@Getter +@Setter +@Table(name = "permission") +public class Permission implements Serializable{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank + private String name; + + /** + * 上级类目 + */ + @NotNull + @Column(name = "pid",nullable = false) + private Long pid; + + @NotBlank + private String alias; + + @JsonIgnore + @ManyToMany(mappedBy = "permissions") + private Set roles; + + @CreationTimestamp + private Timestamp createTime; + + @Override + public String toString() { + return "Permission{" + + "id=" + id + + ", name='" + name + '\'' + + ", pid=" + pid + + ", alias='" + alias + '\'' + + ", createTime=" + createTime + + '}'; + } +} diff --git a/src/main/java/me/zhengjie/system/domain/Role.java b/src/main/java/me/zhengjie/system/domain/Role.java new file mode 100644 index 000000000..16ecab6a6 --- /dev/null +++ b/src/main/java/me/zhengjie/system/domain/Role.java @@ -0,0 +1,60 @@ +package me.zhengjie.system.domain; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.Set; + +/** + * 角色 + * @author jie + * @date 2018-11-22 + */ +@Entity +@Table(name = "role") +@Getter +@Setter +public class Role implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + @NotBlank + private String name; + + @Column + private String remark; + + @JsonIgnore + @ManyToMany(mappedBy = "roles") + private Set users; + + @ManyToMany + @JoinTable(name = "roles_permissions", joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "permission_id",referencedColumnName = "id")}) + private Set permissions; + + @JsonIgnore + @ManyToMany(mappedBy = "roles") + private Set menus; + + @Column + @CreationTimestamp + private Timestamp createTime; + + @Override + public String toString() { + return "Role{" + + "id=" + id + + ", name='" + name + '\'' + + ", remark='" + remark + '\'' + + ", createDateTime=" + createTime + + '}'; + } +} diff --git a/src/main/java/me/zhengjie/system/domain/User.java b/src/main/java/me/zhengjie/system/domain/User.java new file mode 100644 index 000000000..b871ab38a --- /dev/null +++ b/src/main/java/me/zhengjie/system/domain/User.java @@ -0,0 +1,65 @@ +package me.zhengjie.system.domain; + +import java.io.Serializable; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; +import java.sql.Timestamp; +import java.util.Date; +import java.util.Set; + +/** + * @author jie + * @date 2018-11-22 + */ +@Entity +@Getter +@Setter +@Table(name="user") +public class User implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank + private String username; + + private String avatar; + + @NotBlank + @Pattern(regexp = "([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}",message = "格式错误") + private String email; + + @NotNull + private Boolean enabled; + + private String password; + + @CreationTimestamp + private Timestamp createTime; + + private Date lastPasswordResetTime; + + @ManyToMany + @JoinTable(name = "users_roles", joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "id")}) + private Set roles; + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", username='" + username + '\'' + + ", avatar='" + avatar + '\'' + + ", email='" + email + '\'' + + ", enabled=" + enabled + + ", password='" + password + '\'' + + ", createTime=" + createTime + + ", lastPasswordResetTime=" + lastPasswordResetTime + + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/me/zhengjie/system/domain/vo/MenuMetaVo.java b/src/main/java/me/zhengjie/system/domain/vo/MenuMetaVo.java new file mode 100644 index 000000000..cb7fa14c5 --- /dev/null +++ b/src/main/java/me/zhengjie/system/domain/vo/MenuMetaVo.java @@ -0,0 +1,17 @@ +package me.zhengjie.system.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * @author jie + * @date 2018-12-20 + */ +@Data +@AllArgsConstructor +public class MenuMetaVo { + + private String title; + + private String icon; +} diff --git a/src/main/java/me/zhengjie/system/domain/vo/MenuVo.java b/src/main/java/me/zhengjie/system/domain/vo/MenuVo.java new file mode 100644 index 000000000..0c32098c4 --- /dev/null +++ b/src/main/java/me/zhengjie/system/domain/vo/MenuVo.java @@ -0,0 +1,27 @@ +package me.zhengjie.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import java.util.List; + +/** + * 构建前端路由时用到 + * @author jie + * @date 2018-12-20 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class MenuVo { + + private String name; + + private String path; + + private String component; + + private Boolean alwaysShow; + + private MenuMetaVo meta; + + private List children; +} diff --git a/src/main/java/me/zhengjie/system/repository/MenuRepository.java b/src/main/java/me/zhengjie/system/repository/MenuRepository.java new file mode 100644 index 000000000..22ed3f0cf --- /dev/null +++ b/src/main/java/me/zhengjie/system/repository/MenuRepository.java @@ -0,0 +1,36 @@ +package me.zhengjie.system.repository; + +import me.zhengjie.system.domain.Menu; +import me.zhengjie.system.domain.Role; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import java.util.List; +import java.util.Set; + +/** + * @author jie + * @date 2018-12-17 + */ +public interface MenuRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * findByName + * @param name + * @return + */ + Menu findByName(String name); + + /** + * findByRoles + * @param roleSet + * @return + */ + Set findByRoles(Set roleSet); + + /** + * findByPid + * @param pid + * @return + */ + List findByPid(long pid); +} diff --git a/src/main/java/me/zhengjie/system/repository/PermissionRepository.java b/src/main/java/me/zhengjie/system/repository/PermissionRepository.java new file mode 100644 index 000000000..f4c3588d0 --- /dev/null +++ b/src/main/java/me/zhengjie/system/repository/PermissionRepository.java @@ -0,0 +1,36 @@ +package me.zhengjie.system.repository; + +import me.zhengjie.system.domain.Permission; +import me.zhengjie.system.domain.Role; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import java.util.List; +import java.util.Set; + +/** + * @author jie + * @date 2018-12-03 + */ +public interface PermissionRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * findByName + * @param name + * @return + */ + Permission findByName(String name); + + /** + * findByRoles + * @param roleSet + * @return + */ + Set findByRoles(Set roleSet); + + /** + * findByPid + * @param pid + * @return + */ + List findByPid(long pid); +} diff --git a/src/main/java/me/zhengjie/system/repository/RoleRepository.java b/src/main/java/me/zhengjie/system/repository/RoleRepository.java new file mode 100644 index 000000000..ede7e3fe5 --- /dev/null +++ b/src/main/java/me/zhengjie/system/repository/RoleRepository.java @@ -0,0 +1,19 @@ +package me.zhengjie.system.repository; + +import me.zhengjie.system.domain.Role; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** + * @author jie + * @date 2018-12-03 + */ +public interface RoleRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * findByName + * @param name + * @return + */ + Role findByName(String name); +} diff --git a/src/main/java/me/zhengjie/system/repository/UserRepository.java b/src/main/java/me/zhengjie/system/repository/UserRepository.java new file mode 100644 index 000000000..f8a58c61e --- /dev/null +++ b/src/main/java/me/zhengjie/system/repository/UserRepository.java @@ -0,0 +1,26 @@ +package me.zhengjie.system.repository; + +import me.zhengjie.system.domain.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** + * @author jie + * @date 2018-11-22 + */ +public interface UserRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * findByUsername + * @param username + * @return + */ + User findByUsername(String username); + + /** + * findByEmail + * @param email + * @return + */ + User findByEmail(String email); +} diff --git a/src/main/java/me/zhengjie/system/rest/MenuController.java b/src/main/java/me/zhengjie/system/rest/MenuController.java new file mode 100644 index 000000000..51fce6b02 --- /dev/null +++ b/src/main/java/me/zhengjie/system/rest/MenuController.java @@ -0,0 +1,107 @@ +package me.zhengjie.system.rest; + +import me.zhengjie.common.aop.log.Log; +import me.zhengjie.common.exception.BadRequestException; +import me.zhengjie.core.utils.JwtTokenUtil; +import me.zhengjie.system.domain.Menu; +import me.zhengjie.system.domain.User; +import me.zhengjie.system.service.MenuService; +import me.zhengjie.system.service.UserService; +import me.zhengjie.system.service.dto.MenuDTO; +import me.zhengjie.system.service.query.MenuQueryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * @author jie + * @date 2018-12-03 + */ +@RestController +@RequestMapping("api") +public class MenuController { + + @Autowired + private JwtTokenUtil jwtTokenUtil; + + @Autowired + private MenuService menuService; + + @Autowired + private MenuQueryService menuQueryService; + + @Autowired + private UserService userService; + + private static final String ENTITY_NAME = "menu"; + + @GetMapping(value = "/menus/{id}") + @PreAuthorize("hasAnyRole('ADMIN','MENU_ALL','MENU_SELECT')") + public ResponseEntity getMenus(@PathVariable Long id){ + return new ResponseEntity(menuService.findById(id), HttpStatus.OK); + } + + /** + * 构建前端路由所需要的菜单 + * @return + */ + @GetMapping(value = "/menus/build") + public ResponseEntity buildMenus(HttpServletRequest request){ + User user = userService.findByName(jwtTokenUtil.getUserName(request)); + + List menuDTOList = menuService.findByRoles(user.getRoles()); + return new ResponseEntity(menuService.buildMenus((List)menuService.buildTree(menuDTOList).get("content")),HttpStatus.OK); + } + + /** + * 返回全部的菜单 + * @return + */ + @GetMapping(value = "/menus/tree") + @PreAuthorize("hasAnyRole('ADMIN','MENU_ALL','MENU_SELECT')") + public ResponseEntity getMenuTree(){ + return new ResponseEntity(menuService.getMenuTree(menuService.findByPid(0L)),HttpStatus.OK); + } + + @Log(description = "查询菜单") + @GetMapping(value = "/menus") + @PreAuthorize("hasAnyRole('ADMIN','MENU_ALL','MENU_SELECT')") + public ResponseEntity getMenus(@RequestParam(required = false) String name){ + List menuDTOList = menuQueryService.queryAll(name); + return new ResponseEntity(menuService.buildTree(menuDTOList),HttpStatus.OK); + } + + @Log(description = "新增菜单") + @PostMapping(value = "/menus") + @PreAuthorize("hasAnyRole('ADMIN','PERMISSION_ALL','PERMISSION_CREATE')") + public ResponseEntity create(@Validated @RequestBody Menu resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + return new ResponseEntity(menuService.create(resources),HttpStatus.CREATED); + } + + @Log(description = "修改菜单") + @PutMapping(value = "/menus") + @PreAuthorize("hasAnyRole('ADMIN','PERMISSION_ALL','PERMISSION_EDIT')") + public ResponseEntity update(@Validated @RequestBody Menu resources){ + if (resources.getId() == null) { + throw new BadRequestException(ENTITY_NAME +" ID Can not be empty"); + } + menuService.update(resources); + return new ResponseEntity(HttpStatus.NO_CONTENT); + } + + @Log(description = "删除菜单") + @DeleteMapping(value = "/menus/{id}") + @PreAuthorize("hasAnyRole('ADMIN','PERMISSION_ALL','PERMISSION_DELETE')") + public ResponseEntity delete(@PathVariable Long id){ + menuService.delete(id); + return new ResponseEntity(HttpStatus.OK); + } +} diff --git a/src/main/java/me/zhengjie/system/rest/PermissionController.java b/src/main/java/me/zhengjie/system/rest/PermissionController.java new file mode 100644 index 000000000..5b64e5e79 --- /dev/null +++ b/src/main/java/me/zhengjie/system/rest/PermissionController.java @@ -0,0 +1,86 @@ +package me.zhengjie.system.rest; + +import me.zhengjie.common.aop.log.Log; +import me.zhengjie.common.exception.BadRequestException; +import me.zhengjie.system.domain.Permission; +import me.zhengjie.system.service.PermissionService; +import me.zhengjie.system.service.dto.PermissionDTO; +import me.zhengjie.system.service.query.PermissionQueryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author jie + * @date 2018-12-03 + */ +@RestController +@RequestMapping("api") +public class PermissionController { + + @Autowired + private PermissionService permissionService; + + @Autowired + private PermissionQueryService permissionQueryService; + + private static final String ENTITY_NAME = "permission"; + + @GetMapping(value = "/permissions/{id}") + @PreAuthorize("hasAnyRole('ADMIN','PERMISSION_ALL','PERMISSION_SELECT')") + public ResponseEntity getPermissions(@PathVariable Long id){ + return new ResponseEntity(permissionService.findById(id), HttpStatus.OK); + } + + /** + * 返回全部的权限,新增角色时下拉选择 + * @return + */ + @GetMapping(value = "/permissions/tree") + @PreAuthorize("hasAnyRole('ADMIN','PERMISSION_ALL','ROLE_ALL','ROLE_ADD','ROLE_EDIT')") + public ResponseEntity getRoleTree(){ + return new ResponseEntity(permissionService.getPermissionTree(permissionService.findByPid(0L)),HttpStatus.OK); + } + + @Log(description = "查询权限") + @GetMapping(value = "/permissions") + @PreAuthorize("hasAnyRole('ADMIN','PERMISSION_ALL','PERMISSION_SELECT')") + public ResponseEntity getPermissions(@RequestParam(required = false) String name){ + List permissionDTOS = permissionQueryService.queryAll(name); + return new ResponseEntity(permissionService.buildTree(permissionDTOS),HttpStatus.OK); + } + + @Log(description = "新增权限") + @PostMapping(value = "/permissions") + @PreAuthorize("hasAnyRole('ADMIN','PERMISSION_ALL','PERMISSION_CREATE')") + public ResponseEntity create(@Validated @RequestBody Permission resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + return new ResponseEntity(permissionService.create(resources),HttpStatus.CREATED); + } + + @Log(description = "修改权限") + @PutMapping(value = "/permissions") + @PreAuthorize("hasAnyRole('ADMIN','PERMISSION_ALL','PERMISSION_EDIT')") + public ResponseEntity update(@Validated @RequestBody Permission resources){ + if (resources.getId() == null) { + throw new BadRequestException(ENTITY_NAME +" ID Can not be empty"); + } + permissionService.update(resources); + return new ResponseEntity(HttpStatus.NO_CONTENT); + } + + @Log(description = "删除权限") + @DeleteMapping(value = "/permissions/{id}") + @PreAuthorize("hasAnyRole('ADMIN','PERMISSION_ALL','PERMISSION_DELETE')") + public ResponseEntity delete(@PathVariable Long id){ + permissionService.delete(id); + return new ResponseEntity(HttpStatus.OK); + } +} diff --git a/src/main/java/me/zhengjie/system/rest/RoleController.java b/src/main/java/me/zhengjie/system/rest/RoleController.java new file mode 100644 index 000000000..9c2afc74b --- /dev/null +++ b/src/main/java/me/zhengjie/system/rest/RoleController.java @@ -0,0 +1,84 @@ +package me.zhengjie.system.rest; + +import me.zhengjie.common.aop.log.Log; +import me.zhengjie.common.exception.BadRequestException; +import me.zhengjie.system.domain.Role; +import me.zhengjie.system.service.RoleService; +import me.zhengjie.system.service.dto.RoleDTO; +import me.zhengjie.system.service.query.RoleQueryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * @author jie + * @date 2018-12-03 + */ +@RestController +@RequestMapping("api") +public class RoleController { + + @Autowired + private RoleService roleService; + + @Autowired + private RoleQueryService roleQueryService; + + private static final String ENTITY_NAME = "role"; + + @GetMapping(value = "/roles/{id}") + @PreAuthorize("hasAnyRole('ADMIN','ROLE_ALL','ROLE_SELECT')") + public ResponseEntity getRoles(@PathVariable Long id){ + return new ResponseEntity(roleService.findById(id), HttpStatus.OK); + } + + /** + * 返回全部的角色,新增用户时下拉选择 + * @return + */ + @GetMapping(value = "/roles/tree") + @PreAuthorize("hasAnyRole('ADMIN','ROLE_ALL','USER_ALL','USER_ADD','USER_EDIT')") + public ResponseEntity getRoleTree(){ + return new ResponseEntity(roleService.getRoleTree(),HttpStatus.OK); + } + + @Log(description = "查询角色") + @GetMapping(value = "/roles") + @PreAuthorize("hasAnyRole('ADMIN','ROLE_ALL','ROLE_SELECT')") + public ResponseEntity getRoles(RoleDTO resources, Pageable pageable){ + return new ResponseEntity(roleQueryService.queryAll(resources,pageable),HttpStatus.OK); + } + + @Log(description = "新增角色") + @PostMapping(value = "/roles") + @PreAuthorize("hasAnyRole('ADMIN','ROLE_ALL','ROLE_CREATE')") + public ResponseEntity create(@Validated @RequestBody Role resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + return new ResponseEntity(roleService.create(resources),HttpStatus.CREATED); + } + + @Log(description = "修改角色") + @PutMapping(value = "/roles") + @PreAuthorize("hasAnyRole('ADMIN','ROLE_ALL','ROLE_EDIT')") + public ResponseEntity update(@Validated @RequestBody Role resources){ + if (resources.getId() == null) { + throw new BadRequestException(ENTITY_NAME +" ID Can not be empty"); + } + roleService.update(resources); + return new ResponseEntity(HttpStatus.NO_CONTENT); + } + + @Log(description = "删除角色") + @DeleteMapping(value = "/roles/{id}") + @PreAuthorize("hasAnyRole('ADMIN','ROLE_ALL','ROLE_DELETE')") + public ResponseEntity delete(@PathVariable Long id){ + roleService.delete(id); + return new ResponseEntity(HttpStatus.OK); + } +} diff --git a/src/main/java/me/zhengjie/system/rest/UserController.java b/src/main/java/me/zhengjie/system/rest/UserController.java new file mode 100644 index 000000000..481998b64 --- /dev/null +++ b/src/main/java/me/zhengjie/system/rest/UserController.java @@ -0,0 +1,74 @@ +package me.zhengjie.system.rest; + +import me.zhengjie.common.aop.log.Log; +import me.zhengjie.common.exception.BadRequestException; +import me.zhengjie.system.domain.User; +import me.zhengjie.system.service.UserService; +import me.zhengjie.system.service.dto.UserDTO; +import me.zhengjie.system.service.query.UserQueryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * @author jie + * @date 2018-11-23 + */ +@RestController +@RequestMapping("api") +public class UserController { + + @Autowired + private UserService userService; + + @Autowired + private UserQueryService userQueryService; + + private static final String ENTITY_NAME = "user"; + + @GetMapping(value = "/users/{id}") + @PreAuthorize("hasAnyRole('ADMIN','USER_ALL','USER_SELECT')") + public ResponseEntity getUser(@PathVariable Long id){ + return new ResponseEntity(userService.findById(id), HttpStatus.OK); + } + + @Log(description = "查询用户") + @GetMapping(value = "/users") + @PreAuthorize("hasAnyRole('ADMIN','USER_ALL','USER_SELECT')") + public ResponseEntity getUsers(UserDTO userDTO, Pageable pageable){ + return new ResponseEntity(userQueryService.queryAll(userDTO,pageable),HttpStatus.OK); + } + + @Log(description = "新增用户") + @PostMapping(value = "/users") + @PreAuthorize("hasAnyRole('ADMIN','USER_ALL','USER_CREATE')") + public ResponseEntity create(@Validated @RequestBody User resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + return new ResponseEntity(userService.create(resources),HttpStatus.CREATED); + } + + @Log(description = "修改用户") + @PutMapping(value = "/users") + @PreAuthorize("hasAnyRole('ADMIN','USER_ALL','USER_EDIT')") + public ResponseEntity update(@Validated @RequestBody User resources){ + if (resources.getId() == null) { + throw new BadRequestException(ENTITY_NAME +" ID Can not be empty"); + } + userService.update(resources); + return new ResponseEntity(HttpStatus.NO_CONTENT); + } + + @Log(description = "删除用户") + @DeleteMapping(value = "/users/{id}") + @PreAuthorize("hasAnyRole('ADMIN','USER_ALL','USER_DELETE')") + public ResponseEntity delete(@PathVariable Long id){ + userService.delete(id); + return new ResponseEntity(HttpStatus.OK); + } +} diff --git a/src/main/java/me/zhengjie/system/service/MenuService.java b/src/main/java/me/zhengjie/system/service/MenuService.java new file mode 100644 index 000000000..f527c7c32 --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/MenuService.java @@ -0,0 +1,85 @@ +package me.zhengjie.system.service; + +import me.zhengjie.system.domain.Menu; +import me.zhengjie.system.domain.Role; +import me.zhengjie.system.service.dto.MenuDTO; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author jie + * @date 2018-12-17 + */ +@CacheConfig(cacheNames = "menu") +public interface MenuService { + + /** + * get + * @param id + * @return + */ + @Cacheable(key = "#p0") + MenuDTO findById(long id); + + /** + * create + * @param resources + * @return + */ + @CacheEvict(allEntries = true) + MenuDTO create(Menu resources); + + /** + * update + * @param resources + */ + @CacheEvict(allEntries = true) + void update(Menu resources); + + /** + * delete + * @param id + */ + @CacheEvict(allEntries = true) + void delete(Long id); + + /** + * permission tree + * @return + */ + @Cacheable(key = "'tree'") + Object getMenuTree(List menus); + + /** + * findByPid + * @param pid + * @return + */ + @Cacheable(key = "'pid:'+#p0") + List findByPid(long pid); + + /** + * build Tree + * @param menuDTOS + * @return + */ + Map buildTree(List menuDTOS); + + /** + * findByRoles + * @param roles + * @return + */ + List findByRoles(Set roles); + + /** + * buildMenus + * @param byRoles + * @return + */ + Object buildMenus(List byRoles); +} diff --git a/src/main/java/me/zhengjie/system/service/PermissionService.java b/src/main/java/me/zhengjie/system/service/PermissionService.java new file mode 100644 index 000000000..2889b7d4d --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/PermissionService.java @@ -0,0 +1,68 @@ +package me.zhengjie.system.service; + +import me.zhengjie.system.domain.Permission; +import me.zhengjie.system.service.dto.PermissionDTO; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import java.util.List; + +/** + * @author jie + * @date 2018-12-08 + */ +@CacheConfig(cacheNames = "permission") +public interface PermissionService { + + /** + * get + * @param id + * @return + */ + @Cacheable(key = "#p0") + PermissionDTO findById(long id); + + /** + * create + * @param resources + * @return + */ + @CacheEvict(allEntries = true) + PermissionDTO create(Permission resources); + + /** + * update + * @param resources + */ + @CacheEvict(allEntries = true) + void update(Permission resources); + + /** + * delete + * @param id + */ + @CacheEvict(allEntries = true) + void delete(Long id); + + /** + * permission tree + * @return + */ + @Cacheable(key = "'tree'") + Object getPermissionTree(List permissions); + + /** + * findByPid + * @param pid + * @return + */ + @Cacheable(key = "'pid:'+#p0") + List findByPid(long pid); + + /** + * build Tree + * @param permissionDTOS + * @return + */ + Object buildTree(List permissionDTOS); +} diff --git a/src/main/java/me/zhengjie/system/service/RoleService.java b/src/main/java/me/zhengjie/system/service/RoleService.java new file mode 100644 index 000000000..c62a9cf9a --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/RoleService.java @@ -0,0 +1,53 @@ +package me.zhengjie.system.service; + +import me.zhengjie.system.domain.Role; +import me.zhengjie.system.service.dto.RoleDTO; +import me.zhengjie.system.service.dto.UserDTO; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; + +/** + * @author jie + * @date 2018-12-03 + */ +@CacheConfig(cacheNames = "role") +public interface RoleService { + + /** + * get + * @param id + * @return + */ + @Cacheable(key = "#p0") + RoleDTO findById(long id); + + /** + * create + * @param resources + * @return + */ + @CacheEvict(allEntries = true) + RoleDTO create(Role resources); + + /** + * update + * @param resources + */ + @CacheEvict(allEntries = true) + void update(Role resources); + + /** + * delete + * @param id + */ + @CacheEvict(allEntries = true) + void delete(Long id); + + /** + * role tree + * @return + */ + @Cacheable(key = "'tree'") + Object getRoleTree(); +} diff --git a/src/main/java/me/zhengjie/system/service/UserService.java b/src/main/java/me/zhengjie/system/service/UserService.java new file mode 100644 index 000000000..966bd064c --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/UserService.java @@ -0,0 +1,53 @@ +package me.zhengjie.system.service; + +import me.zhengjie.system.domain.User; +import me.zhengjie.system.service.dto.UserDTO; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; + +/** + * @author jie + * @date 2018-11-23 + */ +@CacheConfig(cacheNames = "user") +public interface UserService { + + /** + * get + * @param id + * @return + */ + @Cacheable(key = "#p0") + UserDTO findById(long id); + + /** + * create + * @param resources + * @return + */ + @CacheEvict(allEntries = true) + UserDTO create(User resources); + + /** + * update + * @param resources + */ + @CacheEvict(allEntries = true) + void update(User resources); + + /** + * delete + * @param id + */ + @CacheEvict(allEntries = true) + void delete(Long id); + + /** + * findByName + * @param userName + * @return + */ + @Cacheable(key = "'findByName'+#p0") + User findByName(String userName); +} diff --git a/src/main/java/me/zhengjie/system/service/dto/MenuDTO.java b/src/main/java/me/zhengjie/system/service/dto/MenuDTO.java new file mode 100644 index 000000000..6cb15f61e --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/dto/MenuDTO.java @@ -0,0 +1,36 @@ +package me.zhengjie.system.service.dto; + +import lombok.Data; +import java.sql.Timestamp; +import java.util.List; +import java.util.Set; + +/** + * @author jie + * @date 2018-12-17 + */ +@Data +public class MenuDTO { + + private Long id; + + private String name; + + private Long soft; + + private String path; + + private String component; + + private Long pid; + + private Boolean iFrame; + + private String icon; + + private Set roles; + + private List children; + + private Timestamp createTime; +} diff --git a/src/main/java/me/zhengjie/system/service/dto/PermissionDTO.java b/src/main/java/me/zhengjie/system/service/dto/PermissionDTO.java new file mode 100644 index 000000000..913784b0f --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/dto/PermissionDTO.java @@ -0,0 +1,37 @@ +package me.zhengjie.system.service.dto; + +import lombok.Data; +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.List; + +/** + * @author jie + * @date 2018-12-03 + */ +@Data +public class PermissionDTO implements Serializable{ + + private Long id; + + private String name; + + private Long pid; + + private String alias; + + private Timestamp createTime; + + private List children; + + @Override + public String toString() { + return "Permission{" + + "id=" + id + + ", name='" + name + '\'' + + ", pid=" + pid + + ", alias='" + alias + '\'' + + ", createTime=" + createTime + + '}'; + } +} diff --git a/src/main/java/me/zhengjie/system/service/dto/RoleDTO.java b/src/main/java/me/zhengjie/system/service/dto/RoleDTO.java new file mode 100644 index 000000000..9bdcf010d --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/dto/RoleDTO.java @@ -0,0 +1,24 @@ +package me.zhengjie.system.service.dto; + +import lombok.Data; +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.Set; + +/** + * @author jie + * @date 2018-11-23 + */ +@Data +public class RoleDTO implements Serializable { + + private Long id; + + private String name; + + private String remark; + + private Set permissions; + + private Timestamp createTime; +} diff --git a/src/main/java/me/zhengjie/system/service/dto/UserDTO.java b/src/main/java/me/zhengjie/system/service/dto/UserDTO.java new file mode 100644 index 000000000..e14e01dd8 --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/dto/UserDTO.java @@ -0,0 +1,35 @@ +package me.zhengjie.system.service.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.Date; +import java.util.Set; + +/** + * @author jie + * @date 2018-11-23 + */ +@Data +public class UserDTO implements Serializable { + + private Long id; + + private String username; + + private String avatar; + + private String email; + + private Boolean enabled; + + @JsonIgnore + private String password; + + private Timestamp createTime; + + private Date lastPasswordResetTime; + + private Set roles; +} diff --git a/src/main/java/me/zhengjie/system/service/impl/MenuServiceImpl.java b/src/main/java/me/zhengjie/system/service/impl/MenuServiceImpl.java new file mode 100644 index 000000000..ae38901e6 --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/impl/MenuServiceImpl.java @@ -0,0 +1,155 @@ +package me.zhengjie.system.service.impl; + +import cn.hutool.core.util.StrUtil; +import me.zhengjie.common.exception.EntityExistException; +import me.zhengjie.common.utils.ValidationUtil; +import me.zhengjie.system.domain.Menu; +import me.zhengjie.system.domain.Role; +import me.zhengjie.system.domain.vo.MenuMetaVo; +import me.zhengjie.system.domain.vo.MenuVo; +import me.zhengjie.system.repository.MenuRepository; +import me.zhengjie.system.service.MenuService; +import me.zhengjie.system.service.dto.MenuDTO; +import me.zhengjie.system.service.mapper.MenuMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class MenuServiceImpl implements MenuService { + + @Autowired + private MenuRepository menuRepository; + + @Autowired + private MenuMapper menuMapper; + + @Override + public MenuDTO findById(long id) { + Optional menu = menuRepository.findById(id); + ValidationUtil.isNull(menu,"Menu","id",id); + return menuMapper.toDto(menu.get()); + } + + @Override + public List findByRoles(Set roles) { + Set menus = menuRepository.findByRoles(roles); + return menus.stream().map(menuMapper::toDto).collect(Collectors.toList()); + } + + @Override + public MenuDTO create(Menu resources) { + if(menuRepository.findByName(resources.getName()) != null){ + throw new EntityExistException(Menu.class,"name",resources.getName()); + } + return menuMapper.toDto(menuRepository.save(resources)); + } + + @Override + public void update(Menu resources) { + Optional optionalPermission = menuRepository.findById(resources.getId()); + ValidationUtil.isNull(optionalPermission,"Permission","id",resources.getId()); + + Menu menu = optionalPermission.get(); + Menu menu1 = menuRepository.findByName(resources.getName()); + + if(menu1 != null && !menu1.getId().equals(menu.getId())){ + throw new EntityExistException(Menu.class,"name",resources.getName()); + } + + menu.setName(resources.getName()); + menu.setIcon(resources.getIcon()); + menu.setIFrame(resources.getIFrame()); + menu.setPid(resources.getPid()); + menu.setSoft(resources.getSoft()); + menuRepository.save(menu); + } + + @Override + public void delete(Long id) { + List menuList = menuRepository.findByPid(id); + for (Menu menu : menuList) { + menuRepository.delete(menu); + } + menuRepository.deleteById(id); + } + + @Override + public Object getMenuTree(List menus) { + List> list = new LinkedList<>(); + menus.forEach(menu -> { + if (menu!=null){ + Map map = new HashMap<>(); + map.put("id",menu.getId()); + map.put("label",menu.getName()); + list.add(map); + } + } + ); + return list; + } + + @Override + public List findByPid(long pid) { + return menuRepository.findByPid(pid); + } + + @Override + public Map buildTree(List menuDTOS) { + List trees = new ArrayList(); + + for (MenuDTO menuDTO : menuDTOS) { + + if ("0".equals(menuDTO.getPid().toString())) { + trees.add(menuDTO); + } + + for (MenuDTO it : menuDTOS) { + if (it.getPid().equals(menuDTO.getId())) { + if (menuDTO.getChildren() == null) { + menuDTO.setChildren(new ArrayList()); + } + menuDTO.getChildren().add(it); + } + } + } + + Integer totalElements = menuDTOS!=null?menuDTOS.size():0; + Map map = new HashMap(); + map.put("content",trees.size() == 0?menuDTOS:trees); + map.put("totalElements",totalElements); + return map; + } + + @Override + public List buildMenus(List menuDTOS) { + List list = new LinkedList<>(); + menuDTOS.forEach(menuDTO -> { + if (menuDTO!=null){ + List menuDTOList = menuDTO.getChildren(); + MenuVo menuVo = new MenuVo(); + menuVo.setName(menuDTO.getName()); + menuVo.setPath(menuDTO.getPath()); + if(!menuDTO.getIFrame()){ + if(menuDTO.getPid().equals(0L)){ + //一级目录需要加斜杠,不然访问不了 + menuVo.setPath("/" + menuDTO.getPath()); + } + menuVo.setComponent(StrUtil.isEmpty(menuDTO.getComponent())?"Layout":menuDTO.getComponent()); + } + menuVo.setMeta(new MenuMetaVo(menuDTO.getName(),menuDTO.getIcon())); + if(menuDTOList!=null && menuDTOList.size()!=0){ + menuVo.setAlwaysShow(true); + menuVo.setChildren(buildMenus(menuDTOList)); + } + list.add(menuVo); + } + } + ); + return list; + } +} diff --git a/src/main/java/me/zhengjie/system/service/impl/PermissionServiceImpl.java b/src/main/java/me/zhengjie/system/service/impl/PermissionServiceImpl.java new file mode 100644 index 000000000..ed85b0677 --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/impl/PermissionServiceImpl.java @@ -0,0 +1,127 @@ +package me.zhengjie.system.service.impl; + +import me.zhengjie.common.exception.EntityExistException; +import me.zhengjie.common.utils.ValidationUtil; +import me.zhengjie.system.domain.Permission; +import me.zhengjie.system.repository.PermissionRepository; +import me.zhengjie.system.service.PermissionService; +import me.zhengjie.system.service.dto.PermissionDTO; +import me.zhengjie.system.service.mapper.PermissionMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import java.util.*; + +/** + * @author jie + * @date 2018-12-03 + */ +@Service +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class PermissionServiceImpl implements PermissionService { + + @Autowired + private PermissionRepository permissionRepository; + + @Autowired + private PermissionMapper permissionMapper; + + @Override + public PermissionDTO findById(long id) { + Optional permission = permissionRepository.findById(id); + ValidationUtil.isNull(permission,"Permission","id",id); + return permissionMapper.toDto(permission.get()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public PermissionDTO create(Permission resources) { + if(permissionRepository.findByName(resources.getName()) != null){ + throw new EntityExistException(Permission.class,"name",resources.getName()); + } + return permissionMapper.toDto(permissionRepository.save(resources)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Permission resources) { + Optional optionalPermission = permissionRepository.findById(resources.getId()); + ValidationUtil.isNull(optionalPermission,"Permission","id",resources.getId()); + + Permission permission = optionalPermission.get(); + Permission permission1 = permissionRepository.findByName(resources.getName()); + + if(permission1 != null && !permission1.getId().equals(permission.getId())){ + throw new EntityExistException(Permission.class,"name",resources.getName()); + } + + permission.setName(resources.getName()); + permission.setAlias(resources.getAlias()); + permission.setPid(resources.getPid()); + permissionRepository.save(permission); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long id) { + List permissionList = permissionRepository.findByPid(id); + for (Permission permission : permissionList) { + permissionRepository.delete(permission); + } + permissionRepository.deleteById(id); + } + + @Override + public Object getPermissionTree(List permissions) { + List> list = new LinkedList<>(); + permissions.forEach(permission -> { + if (permission!=null){ + List permissionList = permissionRepository.findByPid(permission.getId()); + Map map = new HashMap<>(); + map.put("id",permission.getId()); + map.put("label",permission.getAlias()); + if(permissionList!=null && permissionList.size()!=0){ + map.put("children",getPermissionTree(permissionList)); + } + list.add(map); + } + } + ); + return list; + } + + @Override + public List findByPid(long pid) { + return permissionRepository.findByPid(pid); + } + + @Override + public Object buildTree(List permissionDTOS) { + + List trees = new ArrayList(); + + for (PermissionDTO permissionDTO : permissionDTOS) { + + if ("0".equals(permissionDTO.getPid().toString())) { + trees.add(permissionDTO); + } + + for (PermissionDTO it : permissionDTOS) { + if (it.getPid().equals(permissionDTO.getId())) { + if (permissionDTO.getChildren() == null) { + permissionDTO.setChildren(new ArrayList()); + } + permissionDTO.getChildren().add(it); + } + } + } + + Integer totalElements = permissionDTOS!=null?permissionDTOS.size():0; + + Map map = new HashMap(); + map.put("content",trees.size() == 0?permissionDTOS:trees); + map.put("totalElements",totalElements); + return map; + } +} diff --git a/src/main/java/me/zhengjie/system/service/impl/RoleServiceImpl.java b/src/main/java/me/zhengjie/system/service/impl/RoleServiceImpl.java new file mode 100644 index 000000000..f85e0456c --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/impl/RoleServiceImpl.java @@ -0,0 +1,86 @@ +package me.zhengjie.system.service.impl; + +import me.zhengjie.common.exception.EntityExistException; +import me.zhengjie.common.utils.ValidationUtil; +import me.zhengjie.system.domain.Role; +import me.zhengjie.system.repository.RoleRepository; +import me.zhengjie.system.service.RoleService; +import me.zhengjie.system.service.dto.RoleDTO; +import me.zhengjie.system.service.mapper.RoleMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * @author jie + * @date 2018-12-03 + */ +@Service +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class RoleServiceImpl implements RoleService { + + @Autowired + private RoleRepository roleRepository; + + @Autowired + private RoleMapper roleMapper; + + @Override + public RoleDTO findById(long id) { + Optional role = roleRepository.findById(id); + ValidationUtil.isNull(role,"Role","id",id); + return roleMapper.toDto(role.get()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public RoleDTO create(Role resources) { + if(roleRepository.findByName(resources.getName()) != null){ + throw new EntityExistException(Role.class,"username",resources.getName()); + } + return roleMapper.toDto(roleRepository.save(resources)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Role resources) { + Optional optionalRole = roleRepository.findById(resources.getId()); + ValidationUtil.isNull(optionalRole,"Role","id",resources.getId()); + + Role role = optionalRole.get(); + Role role1 = roleRepository.findByName(resources.getName()); + + if(role1 != null && !role1.getId().equals(role.getId())){ + throw new EntityExistException(Role.class,"username",resources.getName()); + } + + role.setName(resources.getName()); + role.setRemark(resources.getRemark()); + role.setPermissions(resources.getPermissions()); + roleRepository.save(role); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long id) { + roleRepository.deleteById(id); + } + + @Override + public Object getRoleTree() { + + List roleList = roleRepository.findAll(); + + List> list = new ArrayList<>(); + for (Role role : roleList) { + Map map = new HashMap<>(); + map.put("id",role.getId()); + map.put("label",role.getName()); + list.add(map); + } + return list; + } +} diff --git a/src/main/java/me/zhengjie/system/service/impl/UserServiceImpl.java b/src/main/java/me/zhengjie/system/service/impl/UserServiceImpl.java new file mode 100644 index 000000000..80b325e72 --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/impl/UserServiceImpl.java @@ -0,0 +1,115 @@ +package me.zhengjie.system.service.impl; + +import me.zhengjie.common.exception.BadRequestException; +import me.zhengjie.common.exception.EntityExistException; +import me.zhengjie.common.exception.EntityNotFoundException; +import me.zhengjie.common.utils.ValidationUtil; +import me.zhengjie.core.utils.EncryptUtils; +import me.zhengjie.system.domain.User; +import me.zhengjie.system.repository.UserRepository; +import me.zhengjie.system.service.UserService; +import me.zhengjie.system.service.dto.UserDTO; +import me.zhengjie.system.service.mapper.UserMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import java.util.Optional; + +/** + * @author jie + * @date 2018-11-23 + */ +@Service +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class UserServiceImpl implements UserService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserMapper userMapper; + + @Override + public UserDTO findById(long id) { + Optional user = userRepository.findById(id); + ValidationUtil.isNull(user,"User","id",id); + return userMapper.toDto(user.get()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public UserDTO create(User resources) { + + if(userRepository.findByUsername(resources.getUsername())!=null){ + throw new EntityExistException(User.class,"username",resources.getUsername()); + } + + if(userRepository.findByEmail(resources.getEmail())!=null){ + throw new EntityExistException(User.class,"email",resources.getEmail()); + } + + if(resources.getRoles() == null || resources.getRoles().size() == 0){ + throw new BadRequestException("角色不能为空"); + } + + // 默认密码 123456 + resources.setPassword("14e1b600b1fd579f47433b88e8d85291"); + resources.setAvatar("https://i.loli.net/2018/12/06/5c08894d8de21.jpg"); + return userMapper.toDto(userRepository.save(resources)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(User resources) { + + Optional userOptional = userRepository.findById(resources.getId()); + ValidationUtil.isNull(userOptional,"User","id",resources.getId()); + + User user = userOptional.get(); + + User user1 = userRepository.findByUsername(user.getUsername()); + User user2 = userRepository.findByEmail(user.getEmail()); + + if(resources.getRoles() == null || resources.getRoles().size() == 0){ + throw new BadRequestException("角色不能为空"); + } + + if(user1 !=null&&!user.getId().equals(user1.getId())){ + throw new EntityExistException(User.class,"username",resources.getUsername()); + } + + if(user2!=null&&!user.getId().equals(user2.getId())){ + throw new EntityExistException(User.class,"email",resources.getEmail()); + } + + user.setUsername(resources.getUsername()); + user.setEmail(resources.getEmail()); + user.setEnabled(resources.getEnabled()); + user.setRoles(resources.getRoles()); + + userRepository.save(user); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long id) { + userRepository.deleteById(id); + } + + @Override + public User findByName(String userName) { + User user = null; + if(ValidationUtil.isEmail(userName)){ + user = userRepository.findByEmail(userName); + } else { + user = userRepository.findByUsername(userName); + } + + if (user == null) { + throw new EntityNotFoundException(User.class, "name", userName); + } else { + return user; + } + } +} diff --git a/src/main/java/me/zhengjie/system/service/mapper/MenuMapper.java b/src/main/java/me/zhengjie/system/service/mapper/MenuMapper.java new file mode 100644 index 000000000..82eaf8d74 --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/mapper/MenuMapper.java @@ -0,0 +1,16 @@ +package me.zhengjie.system.service.mapper; + +import me.zhengjie.common.mapper.EntityMapper; +import me.zhengjie.system.domain.Menu; +import me.zhengjie.system.service.dto.MenuDTO; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author jie + * @date 2018-12-17 + */ +@Mapper(componentModel = "spring",uses = {RoleMapper.class},unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface MenuMapper extends EntityMapper { + +} diff --git a/src/main/java/me/zhengjie/system/service/mapper/PermissionMapper.java b/src/main/java/me/zhengjie/system/service/mapper/PermissionMapper.java new file mode 100644 index 000000000..b76b8f272 --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/mapper/PermissionMapper.java @@ -0,0 +1,18 @@ +package me.zhengjie.system.service.mapper; + +import me.zhengjie.common.mapper.EntityMapper; +import me.zhengjie.system.domain.Permission; +import me.zhengjie.system.domain.Role; +import me.zhengjie.system.service.dto.PermissionDTO; +import me.zhengjie.system.service.dto.RoleDTO; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author jie + * @date 2018-11-23 + */ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface PermissionMapper extends EntityMapper { + +} diff --git a/src/main/java/me/zhengjie/system/service/mapper/RoleMapper.java b/src/main/java/me/zhengjie/system/service/mapper/RoleMapper.java new file mode 100644 index 000000000..5c098e8b1 --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/mapper/RoleMapper.java @@ -0,0 +1,16 @@ +package me.zhengjie.system.service.mapper; + +import me.zhengjie.common.mapper.EntityMapper; +import me.zhengjie.system.domain.Role; +import me.zhengjie.system.service.dto.RoleDTO; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author jie + * @date 2018-11-23 + */ +@Mapper(componentModel = "spring", uses = {PermissionMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface RoleMapper extends EntityMapper { + +} diff --git a/src/main/java/me/zhengjie/system/service/mapper/UserMapper.java b/src/main/java/me/zhengjie/system/service/mapper/UserMapper.java new file mode 100644 index 000000000..276165557 --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/mapper/UserMapper.java @@ -0,0 +1,16 @@ +package me.zhengjie.system.service.mapper; + +import me.zhengjie.common.mapper.EntityMapper; +import me.zhengjie.system.domain.User; +import me.zhengjie.system.service.dto.UserDTO; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author jie + * @date 2018-11-23 + */ +@Mapper(componentModel = "spring",uses = {RoleMapper.class},unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface UserMapper extends EntityMapper { + +} diff --git a/src/main/java/me/zhengjie/system/service/query/MenuQueryService.java b/src/main/java/me/zhengjie/system/service/query/MenuQueryService.java new file mode 100644 index 000000000..b9ab5fc18 --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/query/MenuQueryService.java @@ -0,0 +1,68 @@ +package me.zhengjie.system.service.query; + +import me.zhengjie.system.domain.Menu; +import me.zhengjie.system.repository.MenuRepository; +import me.zhengjie.system.service.mapper.MenuMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.List; + +/** + * @author jie + * @date 2018-12-17 + */ +@Service +@CacheConfig(cacheNames = "menu") +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class MenuQueryService { + + @Autowired + private MenuRepository menuRepository; + + @Autowired + private MenuMapper menuMapper; + + /** + * 不分页 + */ + @Cacheable(key = "'queryAll:'+#p0") + public List queryAll(String name){ + return menuMapper.toDto(menuRepository.findAll(new Spec(name))); + } + + class Spec implements Specification { + + private String name; + + public Spec(String name) { + this.name = name; + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) { + + List list = new ArrayList(); + + if(!ObjectUtils.isEmpty(name)){ + /** + * 模糊 + */ + list.add(cb.like(root.get("name").as(String.class),"%"+name+"%")); + } + + Predicate[] p = new Predicate[list.size()]; + return cb.and(list.toArray(p)); + } + } +} diff --git a/src/main/java/me/zhengjie/system/service/query/PermissionQueryService.java b/src/main/java/me/zhengjie/system/service/query/PermissionQueryService.java new file mode 100644 index 000000000..40ade2c40 --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/query/PermissionQueryService.java @@ -0,0 +1,68 @@ +package me.zhengjie.system.service.query; + +import me.zhengjie.system.domain.Permission; +import me.zhengjie.system.repository.PermissionRepository; +import me.zhengjie.system.service.mapper.PermissionMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.List; + +/** + * @author jie + * @date 2018-12-03 + */ +@Service +@CacheConfig(cacheNames = "permission") +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class PermissionQueryService { + + @Autowired + private PermissionRepository permissionRepository; + + @Autowired + private PermissionMapper permissionMapper; + + /** + * 不分页 + */ + @Cacheable(key = "'queryAll:'+#p0") + public List queryAll(String name){ + return permissionMapper.toDto(permissionRepository.findAll(new Spec(name))); + } + + class Spec implements Specification { + + private String name; + + public Spec(String name) { + this.name = name; + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) { + + List list = new ArrayList(); + + if(!ObjectUtils.isEmpty(name)){ + /** + * 模糊 + */ + list.add(cb.like(root.get("name").as(String.class),"%"+name+"%")); + } + + Predicate[] p = new Predicate[list.size()]; + return cb.and(list.toArray(p)); + } + } +} diff --git a/src/main/java/me/zhengjie/system/service/query/RoleQueryService.java b/src/main/java/me/zhengjie/system/service/query/RoleQueryService.java new file mode 100644 index 000000000..86e4e466c --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/query/RoleQueryService.java @@ -0,0 +1,81 @@ +package me.zhengjie.system.service.query; + +import me.zhengjie.common.utils.PageUtil; +import me.zhengjie.system.domain.Role; +import me.zhengjie.system.repository.RoleRepository; +import me.zhengjie.system.service.dto.RoleDTO; +import me.zhengjie.system.service.mapper.RoleMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.List; + +/** + * @author jie + * @date 2018-12-03 + */ +@Service +@CacheConfig(cacheNames = "role") +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class RoleQueryService { + + @Autowired + private RoleRepository roleRepository; + + @Autowired + private RoleMapper roleMapper; + + /** + * 分页 + */ + @Cacheable(keyGenerator = "keyGenerator") + public Object queryAll(RoleDTO role, Pageable pageable){ + Page page = roleRepository.findAll(new Spec(role),pageable); + return PageUtil.toPage(page.map(roleMapper::toDto)); + } + + /** + * 不分页 + */ + @Cacheable(keyGenerator = "keyGenerator") + public Object queryAll(RoleDTO role){ + return roleMapper.toDto(roleRepository.findAll(new Spec(role))); + } + + class Spec implements Specification { + + private RoleDTO role; + + public Spec(RoleDTO role){ + this.role = role; + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) { + + List list = new ArrayList(); + + if(!ObjectUtils.isEmpty(role.getName())){ + /** + * 模糊 + */ + list.add(cb.like(root.get("name").as(String.class),"%"+role.getName()+"%")); + } + + Predicate[] p = new Predicate[list.size()]; + return cb.and(list.toArray(p)); + } + } +} diff --git a/src/main/java/me/zhengjie/system/service/query/UserQueryService.java b/src/main/java/me/zhengjie/system/service/query/UserQueryService.java new file mode 100644 index 000000000..fdb27910f --- /dev/null +++ b/src/main/java/me/zhengjie/system/service/query/UserQueryService.java @@ -0,0 +1,100 @@ +package me.zhengjie.system.service.query; + +import me.zhengjie.common.utils.PageUtil; +import me.zhengjie.system.domain.User; +import me.zhengjie.system.repository.UserRepository; +import me.zhengjie.system.service.dto.UserDTO; +import me.zhengjie.system.service.mapper.UserMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; +import javax.persistence.criteria.*; +import java.util.ArrayList; +import java.util.List; + +/** + * @author jie + * @date 2018-11-22 + */ +@Service +@CacheConfig(cacheNames = "user") +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class UserQueryService { + + @Autowired + private UserRepository userRepo; + + @Autowired + private UserMapper userMapper; + + /** + * 分页 + */ + @Cacheable(keyGenerator = "keyGenerator") + public Object queryAll(UserDTO user, Pageable pageable){ + Page page = userRepo.findAll(new Spec(user),pageable); + return PageUtil.toPage(page.map(userMapper::toDto)); + } + + /** + * 不分页 + */ + @Cacheable(keyGenerator = "keyGenerator") + public Object queryAll(UserDTO user){ + return userMapper.toDto(userRepo.findAll(new Spec(user))); + } + + class Spec implements Specification { + + private UserDTO user; + + public Spec(UserDTO user){ + this.user = user; + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) { + + List list = new ArrayList(); + + if(!ObjectUtils.isEmpty(user.getId())){ + /** + * 相等 + */ + list.add(cb.equal(root.get("id").as(Long.class),user.getId())); + } + + if(!ObjectUtils.isEmpty(user.getEnabled())){ + /** + * 相等 + */ + list.add(cb.equal(root.get("enabled").as(Boolean.class),user.getEnabled())); + } + + + if(!ObjectUtils.isEmpty(user.getUsername())){ + /** + * 模糊 + */ + list.add(cb.like(root.get("username").as(String.class),"%"+user.getUsername()+"%")); + } + + if(!ObjectUtils.isEmpty(user.getEmail())){ + /** + * 模糊 + */ + list.add(cb.like(root.get("email").as(String.class),"%"+user.getEmail()+"%")); + } + + Predicate[] p = new Predicate[list.size()]; + return cb.and(list.toArray(p)); + } + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 000000000..2860b4550 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,90 @@ +server: + port: 80 + +#配置数据源 +spring: + datasource: + druid: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy + url: jdbc:log4jdbc:mysql://localhost:3306/eladmin?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false + username: root + password: 123456 + + # 初始化连接大小 + initial-size: 5 + # 最小空闲连接数 + min-idle: 5 + max-active: 20 + max-wait: 30000 + # 可关闭的空闲连接间隔时间 + time-between-eviction-runs-millis: 60000 + # 配置连接在池中的最小生存时间 + min-evictable-idle-time-millis: 300000 + validation-query: select '1' from dual + test-while-idle: true + test-on-borrow: false + test-on-return: false + # 打开PSCache,并且指定每个连接上PSCache的大小 + pool-prepared-statements: true + max-open-prepared-statements: 20 + max-pool-prepared-statement-per-connection-size: 20 + # 配置监控统计拦截的filters + filters: stat + + stat-view-servlet: + url-pattern: /druid/* + reset-enable: false + login-username: admin + login-password: 123456 + + web-stat-filter: + url-pattern: /* + exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" + + #配置 Jpa + jpa: + properties: + hibernate: + show_sql: true + dialect: org.hibernate.dialect.MySQL5InnoDBDialect + hibernate: + ddl-auto: update + naming: + physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl + open-in-view: true + + jackson: + time-zone: GMT+8 + + redis: + #数据库索引 + database: 0 + host: 127.0.0.1 + port: 6379 + password: + jedis: + pool: + #最大连接数 + max-active: 100 + #最大阻塞等待时间(负数表示没限制) + max-wait: 2000 + #最大空闲 + max-idle: 500 + #最小空闲 + min-idle: 8 + #连接超时时间 + timeout: 5000 + +#jwt +jwt: + header: Authorization + secret: mySecret + # token 过期时间 1个小时 + expiration: 3600000 +# expiration: 60000 + auth: + # 授权路径 + path: /login + # 获取用户信息 + account: /info \ No newline at end of file diff --git a/src/main/resources/log4jdbc.log4j2.properties b/src/main/resources/log4jdbc.log4j2.properties new file mode 100644 index 000000000..5cb6f9914 --- /dev/null +++ b/src/main/resources/log4jdbc.log4j2.properties @@ -0,0 +1,2 @@ +# If you use SLF4J. First, you need to tell log4jdbc-log4j2 that you want to use the SLF4J logger +log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 000000000..b125bbf15 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,59 @@ + + + elune + + + + + + %black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %gray(%msg%n) + utf-8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/me/zhengjie/EladminApplicationTests.java b/src/test/java/me/zhengjie/EladminApplicationTests.java new file mode 100644 index 000000000..057696d92 --- /dev/null +++ b/src/test/java/me/zhengjie/EladminApplicationTests.java @@ -0,0 +1,34 @@ +package me.zhengjie; + +import me.zhengjie.monitor.domain.vo.RedisVo; +import me.zhengjie.monitor.service.RedisService; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Calendar; +import java.util.Date; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class EladminApplicationTests { + + @Autowired + private RedisService redisService; + + @Test + public static void main(String[] args) { + String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date()); + + int w = cal.get(Calendar.DAY_OF_WEEK) - 1; + if (w < 0){ + w = 0; + } + System.out.println(weekDays[w]); + } + +}