@ -0,0 +1,33 @@ |
|||||||
|
HELP.md |
||||||
|
target/ |
||||||
|
!.mvn/wrapper/maven-wrapper.jar |
||||||
|
!**/src/main/** |
||||||
|
!**/src/test/** |
||||||
|
|
||||||
|
### STS ### |
||||||
|
.apt_generated |
||||||
|
.classpath |
||||||
|
.factorypath |
||||||
|
.project |
||||||
|
.settings |
||||||
|
.springBeans |
||||||
|
.sts4-cache |
||||||
|
|
||||||
|
### IntelliJ IDEA ### |
||||||
|
.idea |
||||||
|
*.iws |
||||||
|
*.iml |
||||||
|
*.ipr |
||||||
|
|
||||||
|
### NetBeans ### |
||||||
|
/nbproject/private/ |
||||||
|
/nbbuild/ |
||||||
|
/dist/ |
||||||
|
/nbdist/ |
||||||
|
/.nb-gradle/ |
||||||
|
build/ |
||||||
|
|
||||||
|
### VS Code ### |
||||||
|
.vscode/ |
||||||
|
|
||||||
|
*.DS_Store |
||||||
@ -0,0 +1,118 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2007-present the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import java.net.*; |
||||||
|
import java.io.*; |
||||||
|
import java.nio.channels.*; |
||||||
|
import java.util.Properties; |
||||||
|
|
||||||
|
public class MavenWrapperDownloader { |
||||||
|
|
||||||
|
private static final String WRAPPER_VERSION = "0.5.6"; |
||||||
|
/** |
||||||
|
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. |
||||||
|
*/ |
||||||
|
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" |
||||||
|
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to |
||||||
|
* use instead of the default one. |
||||||
|
*/ |
||||||
|
private static final String MAVEN_WRAPPER_PROPERTIES_PATH = |
||||||
|
".mvn/wrapper/maven-wrapper.properties"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Path where the maven-wrapper.jar will be saved to. |
||||||
|
*/ |
||||||
|
private static final String MAVEN_WRAPPER_JAR_PATH = |
||||||
|
".mvn/wrapper/maven-wrapper.jar"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Name of the property which should be used to override the default download url for the wrapper. |
||||||
|
*/ |
||||||
|
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; |
||||||
|
|
||||||
|
public static void main(String args[]) { |
||||||
|
System.out.println("- Downloader started"); |
||||||
|
File baseDirectory = new File(args[0]); |
||||||
|
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); |
||||||
|
|
||||||
|
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
||||||
|
// wrapperUrl parameter.
|
||||||
|
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); |
||||||
|
String url = DEFAULT_DOWNLOAD_URL; |
||||||
|
if (mavenWrapperPropertyFile.exists()) { |
||||||
|
FileInputStream mavenWrapperPropertyFileInputStream = null; |
||||||
|
try { |
||||||
|
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); |
||||||
|
Properties mavenWrapperProperties = new Properties(); |
||||||
|
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); |
||||||
|
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); |
||||||
|
} catch (IOException e) { |
||||||
|
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); |
||||||
|
} finally { |
||||||
|
try { |
||||||
|
if (mavenWrapperPropertyFileInputStream != null) { |
||||||
|
mavenWrapperPropertyFileInputStream.close(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
// Ignore ...
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
System.out.println("- Downloading from: " + url); |
||||||
|
|
||||||
|
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); |
||||||
|
if (!outputFile.getParentFile().exists()) { |
||||||
|
if (!outputFile.getParentFile().mkdirs()) { |
||||||
|
System.out.println( |
||||||
|
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); |
||||||
|
} |
||||||
|
} |
||||||
|
System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); |
||||||
|
try { |
||||||
|
downloadFileFromURL(url, outputFile); |
||||||
|
System.out.println("Done"); |
||||||
|
System.exit(0); |
||||||
|
} catch (Throwable e) { |
||||||
|
System.out.println("- Error downloading"); |
||||||
|
e.printStackTrace(); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void downloadFileFromURL(String urlString, File destination) throws Exception { |
||||||
|
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { |
||||||
|
String username = System.getenv("MVNW_USERNAME"); |
||||||
|
char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); |
||||||
|
Authenticator.setDefault(new Authenticator() { |
||||||
|
@Override |
||||||
|
protected PasswordAuthentication getPasswordAuthentication() { |
||||||
|
return new PasswordAuthentication(username, password); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
URL website = new URL(urlString); |
||||||
|
ReadableByteChannel rbc; |
||||||
|
rbc = Channels.newChannel(website.openStream()); |
||||||
|
FileOutputStream fos = new FileOutputStream(destination); |
||||||
|
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); |
||||||
|
fos.close(); |
||||||
|
rbc.close(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,2 @@ |
|||||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip |
||||||
|
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar |
||||||
@ -0,0 +1,310 @@ |
|||||||
|
#!/bin/sh |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one |
||||||
|
# or more contributor license agreements. See the NOTICE file |
||||||
|
# distributed with this work for additional information |
||||||
|
# regarding copyright ownership. The ASF licenses this file |
||||||
|
# to you under the Apache License, Version 2.0 (the |
||||||
|
# "License"); you may not use this file except in compliance |
||||||
|
# with the License. You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, |
||||||
|
# software distributed under the License is distributed on an |
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||||
|
# KIND, either express or implied. See the License for the |
||||||
|
# specific language governing permissions and limitations |
||||||
|
# under the License. |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
# Maven Start Up Batch script |
||||||
|
# |
||||||
|
# Required ENV vars: |
||||||
|
# ------------------ |
||||||
|
# JAVA_HOME - location of a JDK home dir |
||||||
|
# |
||||||
|
# Optional ENV vars |
||||||
|
# ----------------- |
||||||
|
# M2_HOME - location of maven2's installed home dir |
||||||
|
# MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||||
|
# e.g. to debug Maven itself, use |
||||||
|
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||||
|
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
if [ -z "$MAVEN_SKIP_RC" ] ; then |
||||||
|
|
||||||
|
if [ -f /etc/mavenrc ] ; then |
||||||
|
. /etc/mavenrc |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -f "$HOME/.mavenrc" ] ; then |
||||||
|
. "$HOME/.mavenrc" |
||||||
|
fi |
||||||
|
|
||||||
|
fi |
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false. |
||||||
|
cygwin=false; |
||||||
|
darwin=false; |
||||||
|
mingw=false |
||||||
|
case "`uname`" in |
||||||
|
CYGWIN*) cygwin=true ;; |
||||||
|
MINGW*) mingw=true;; |
||||||
|
Darwin*) darwin=true |
||||||
|
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home |
||||||
|
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html |
||||||
|
if [ -z "$JAVA_HOME" ]; then |
||||||
|
if [ -x "/usr/libexec/java_home" ]; then |
||||||
|
export JAVA_HOME="`/usr/libexec/java_home`" |
||||||
|
else |
||||||
|
export JAVA_HOME="/Library/Java/Home" |
||||||
|
fi |
||||||
|
fi |
||||||
|
;; |
||||||
|
esac |
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then |
||||||
|
if [ -r /etc/gentoo-release ] ; then |
||||||
|
JAVA_HOME=`java-config --jre-home` |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$M2_HOME" ] ; then |
||||||
|
## resolve links - $0 may be a link to maven's home |
||||||
|
PRG="$0" |
||||||
|
|
||||||
|
# need this for relative symlinks |
||||||
|
while [ -h "$PRG" ] ; do |
||||||
|
ls=`ls -ld "$PRG"` |
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'` |
||||||
|
if expr "$link" : '/.*' > /dev/null; then |
||||||
|
PRG="$link" |
||||||
|
else |
||||||
|
PRG="`dirname "$PRG"`/$link" |
||||||
|
fi |
||||||
|
done |
||||||
|
|
||||||
|
saveddir=`pwd` |
||||||
|
|
||||||
|
M2_HOME=`dirname "$PRG"`/.. |
||||||
|
|
||||||
|
# make it fully qualified |
||||||
|
M2_HOME=`cd "$M2_HOME" && pwd` |
||||||
|
|
||||||
|
cd "$saveddir" |
||||||
|
# echo Using m2 at $M2_HOME |
||||||
|
fi |
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched |
||||||
|
if $cygwin ; then |
||||||
|
[ -n "$M2_HOME" ] && |
||||||
|
M2_HOME=`cygpath --unix "$M2_HOME"` |
||||||
|
[ -n "$JAVA_HOME" ] && |
||||||
|
JAVA_HOME=`cygpath --unix "$JAVA_HOME"` |
||||||
|
[ -n "$CLASSPATH" ] && |
||||||
|
CLASSPATH=`cygpath --path --unix "$CLASSPATH"` |
||||||
|
fi |
||||||
|
|
||||||
|
# For Mingw, ensure paths are in UNIX format before anything is touched |
||||||
|
if $mingw ; then |
||||||
|
[ -n "$M2_HOME" ] && |
||||||
|
M2_HOME="`(cd "$M2_HOME"; pwd)`" |
||||||
|
[ -n "$JAVA_HOME" ] && |
||||||
|
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then |
||||||
|
javaExecutable="`which javac`" |
||||||
|
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then |
||||||
|
# readlink(1) is not available as standard on Solaris 10. |
||||||
|
readLink=`which readlink` |
||||||
|
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then |
||||||
|
if $darwin ; then |
||||||
|
javaHome="`dirname \"$javaExecutable\"`" |
||||||
|
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" |
||||||
|
else |
||||||
|
javaExecutable="`readlink -f \"$javaExecutable\"`" |
||||||
|
fi |
||||||
|
javaHome="`dirname \"$javaExecutable\"`" |
||||||
|
javaHome=`expr "$javaHome" : '\(.*\)/bin'` |
||||||
|
JAVA_HOME="$javaHome" |
||||||
|
export JAVA_HOME |
||||||
|
fi |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then |
||||||
|
if [ -n "$JAVA_HOME" ] ; then |
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
||||||
|
# IBM's JDK on AIX uses strange locations for the executables |
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java" |
||||||
|
else |
||||||
|
JAVACMD="$JAVA_HOME/bin/java" |
||||||
|
fi |
||||||
|
else |
||||||
|
JAVACMD="`which java`" |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then |
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2 |
||||||
|
echo " We cannot execute $JAVACMD" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then |
||||||
|
echo "Warning: JAVA_HOME environment variable is not set." |
||||||
|
fi |
||||||
|
|
||||||
|
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher |
||||||
|
|
||||||
|
# traverses directory structure from process work directory to filesystem root |
||||||
|
# first directory with .mvn subdirectory is considered project base directory |
||||||
|
find_maven_basedir() { |
||||||
|
|
||||||
|
if [ -z "$1" ] |
||||||
|
then |
||||||
|
echo "Path not specified to find_maven_basedir" |
||||||
|
return 1 |
||||||
|
fi |
||||||
|
|
||||||
|
basedir="$1" |
||||||
|
wdir="$1" |
||||||
|
while [ "$wdir" != '/' ] ; do |
||||||
|
if [ -d "$wdir"/.mvn ] ; then |
||||||
|
basedir=$wdir |
||||||
|
break |
||||||
|
fi |
||||||
|
# workaround for JBEAP-8937 (on Solaris 10/Sparc) |
||||||
|
if [ -d "${wdir}" ]; then |
||||||
|
wdir=`cd "$wdir/.."; pwd` |
||||||
|
fi |
||||||
|
# end of workaround |
||||||
|
done |
||||||
|
echo "${basedir}" |
||||||
|
} |
||||||
|
|
||||||
|
# concatenates all lines of a file |
||||||
|
concat_lines() { |
||||||
|
if [ -f "$1" ]; then |
||||||
|
echo "$(tr -s '\n' ' ' < "$1")" |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
BASE_DIR=`find_maven_basedir "$(pwd)"` |
||||||
|
if [ -z "$BASE_DIR" ]; then |
||||||
|
exit 1; |
||||||
|
fi |
||||||
|
|
||||||
|
########################################################################################## |
||||||
|
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||||
|
# This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||||
|
########################################################################################## |
||||||
|
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Found .mvn/wrapper/maven-wrapper.jar" |
||||||
|
fi |
||||||
|
else |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." |
||||||
|
fi |
||||||
|
if [ -n "$MVNW_REPOURL" ]; then |
||||||
|
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" |
||||||
|
else |
||||||
|
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" |
||||||
|
fi |
||||||
|
while IFS="=" read key value; do |
||||||
|
case "$key" in (wrapperUrl) jarUrl="$value"; break ;; |
||||||
|
esac |
||||||
|
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Downloading from: $jarUrl" |
||||||
|
fi |
||||||
|
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" |
||||||
|
if $cygwin; then |
||||||
|
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` |
||||||
|
fi |
||||||
|
|
||||||
|
if command -v wget > /dev/null; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Found wget ... using wget" |
||||||
|
fi |
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then |
||||||
|
wget "$jarUrl" -O "$wrapperJarPath" |
||||||
|
else |
||||||
|
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" |
||||||
|
fi |
||||||
|
elif command -v curl > /dev/null; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Found curl ... using curl" |
||||||
|
fi |
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then |
||||||
|
curl -o "$wrapperJarPath" "$jarUrl" -f |
||||||
|
else |
||||||
|
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f |
||||||
|
fi |
||||||
|
|
||||||
|
else |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo "Falling back to using Java to download" |
||||||
|
fi |
||||||
|
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" |
||||||
|
# For Cygwin, switch paths to Windows format before running javac |
||||||
|
if $cygwin; then |
||||||
|
javaClass=`cygpath --path --windows "$javaClass"` |
||||||
|
fi |
||||||
|
if [ -e "$javaClass" ]; then |
||||||
|
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo " - Compiling MavenWrapperDownloader.java ..." |
||||||
|
fi |
||||||
|
# Compiling the Java class |
||||||
|
("$JAVA_HOME/bin/javac" "$javaClass") |
||||||
|
fi |
||||||
|
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||||
|
# Running the downloader |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo " - Running MavenWrapperDownloader.java ..." |
||||||
|
fi |
||||||
|
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") |
||||||
|
fi |
||||||
|
fi |
||||||
|
fi |
||||||
|
fi |
||||||
|
########################################################################################## |
||||||
|
# End of extension |
||||||
|
########################################################################################## |
||||||
|
|
||||||
|
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} |
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then |
||||||
|
echo $MAVEN_PROJECTBASEDIR |
||||||
|
fi |
||||||
|
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" |
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java |
||||||
|
if $cygwin; then |
||||||
|
[ -n "$M2_HOME" ] && |
||||||
|
M2_HOME=`cygpath --path --windows "$M2_HOME"` |
||||||
|
[ -n "$JAVA_HOME" ] && |
||||||
|
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` |
||||||
|
[ -n "$CLASSPATH" ] && |
||||||
|
CLASSPATH=`cygpath --path --windows "$CLASSPATH"` |
||||||
|
[ -n "$MAVEN_PROJECTBASEDIR" ] && |
||||||
|
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` |
||||||
|
fi |
||||||
|
|
||||||
|
# Provide a "standardized" way to retrieve the CLI args that will |
||||||
|
# work with both Windows and non-Windows executions. |
||||||
|
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" |
||||||
|
export MAVEN_CMD_LINE_ARGS |
||||||
|
|
||||||
|
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||||
|
|
||||||
|
exec "$JAVACMD" \ |
||||||
|
$MAVEN_OPTS \ |
||||||
|
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ |
||||||
|
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ |
||||||
|
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" |
||||||
@ -0,0 +1,182 @@ |
|||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one |
||||||
|
@REM or more contributor license agreements. See the NOTICE file |
||||||
|
@REM distributed with this work for additional information |
||||||
|
@REM regarding copyright ownership. The ASF licenses this file |
||||||
|
@REM to you under the Apache License, Version 2.0 (the |
||||||
|
@REM "License"); you may not use this file except in compliance |
||||||
|
@REM with the License. You may obtain a copy of the License at |
||||||
|
@REM |
||||||
|
@REM https://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
@REM |
||||||
|
@REM Unless required by applicable law or agreed to in writing, |
||||||
|
@REM software distributed under the License is distributed on an |
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||||
|
@REM KIND, either express or implied. See the License for the |
||||||
|
@REM specific language governing permissions and limitations |
||||||
|
@REM under the License. |
||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
@REM Maven Start Up Batch script |
||||||
|
@REM |
||||||
|
@REM Required ENV vars: |
||||||
|
@REM JAVA_HOME - location of a JDK home dir |
||||||
|
@REM |
||||||
|
@REM Optional ENV vars |
||||||
|
@REM M2_HOME - location of maven2's installed home dir |
||||||
|
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands |
||||||
|
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending |
||||||
|
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||||
|
@REM e.g. to debug Maven itself, use |
||||||
|
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||||
|
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||||
|
@REM ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' |
||||||
|
@echo off |
||||||
|
@REM set title of command window |
||||||
|
title %0 |
||||||
|
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' |
||||||
|
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% |
||||||
|
|
||||||
|
@REM set %HOME% to equivalent of $HOME |
||||||
|
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") |
||||||
|
|
||||||
|
@REM Execute a user defined script before this one |
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre |
||||||
|
@REM check for pre script, once with legacy .bat ending and once with .cmd ending |
||||||
|
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" |
||||||
|
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" |
||||||
|
:skipRcPre |
||||||
|
|
||||||
|
@setlocal |
||||||
|
|
||||||
|
set ERROR_CODE=0 |
||||||
|
|
||||||
|
@REM To isolate internal variables from possible post scripts, we use another setlocal |
||||||
|
@setlocal |
||||||
|
|
||||||
|
@REM ==== START VALIDATION ==== |
||||||
|
if not "%JAVA_HOME%" == "" goto OkJHome |
||||||
|
|
||||||
|
echo. |
||||||
|
echo Error: JAVA_HOME not found in your environment. >&2 |
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||||
|
echo location of your Java installation. >&2 |
||||||
|
echo. |
||||||
|
goto error |
||||||
|
|
||||||
|
:OkJHome |
||||||
|
if exist "%JAVA_HOME%\bin\java.exe" goto init |
||||||
|
|
||||||
|
echo. |
||||||
|
echo Error: JAVA_HOME is set to an invalid directory. >&2 |
||||||
|
echo JAVA_HOME = "%JAVA_HOME%" >&2 |
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||||
|
echo location of your Java installation. >&2 |
||||||
|
echo. |
||||||
|
goto error |
||||||
|
|
||||||
|
@REM ==== END VALIDATION ==== |
||||||
|
|
||||||
|
:init |
||||||
|
|
||||||
|
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". |
||||||
|
@REM Fallback to current working directory if not found. |
||||||
|
|
||||||
|
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% |
||||||
|
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir |
||||||
|
|
||||||
|
set EXEC_DIR=%CD% |
||||||
|
set WDIR=%EXEC_DIR% |
||||||
|
:findBaseDir |
||||||
|
IF EXIST "%WDIR%"\.mvn goto baseDirFound |
||||||
|
cd .. |
||||||
|
IF "%WDIR%"=="%CD%" goto baseDirNotFound |
||||||
|
set WDIR=%CD% |
||||||
|
goto findBaseDir |
||||||
|
|
||||||
|
:baseDirFound |
||||||
|
set MAVEN_PROJECTBASEDIR=%WDIR% |
||||||
|
cd "%EXEC_DIR%" |
||||||
|
goto endDetectBaseDir |
||||||
|
|
||||||
|
:baseDirNotFound |
||||||
|
set MAVEN_PROJECTBASEDIR=%EXEC_DIR% |
||||||
|
cd "%EXEC_DIR%" |
||||||
|
|
||||||
|
:endDetectBaseDir |
||||||
|
|
||||||
|
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig |
||||||
|
|
||||||
|
@setlocal EnableExtensions EnableDelayedExpansion |
||||||
|
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a |
||||||
|
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% |
||||||
|
|
||||||
|
:endReadAdditionalConfig |
||||||
|
|
||||||
|
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" |
||||||
|
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" |
||||||
|
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||||
|
|
||||||
|
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" |
||||||
|
|
||||||
|
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( |
||||||
|
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B |
||||||
|
) |
||||||
|
|
||||||
|
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||||
|
@REM This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||||
|
if exist %WRAPPER_JAR% ( |
||||||
|
if "%MVNW_VERBOSE%" == "true" ( |
||||||
|
echo Found %WRAPPER_JAR% |
||||||
|
) |
||||||
|
) else ( |
||||||
|
if not "%MVNW_REPOURL%" == "" ( |
||||||
|
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" |
||||||
|
) |
||||||
|
if "%MVNW_VERBOSE%" == "true" ( |
||||||
|
echo Couldn't find %WRAPPER_JAR%, downloading it ... |
||||||
|
echo Downloading from: %DOWNLOAD_URL% |
||||||
|
) |
||||||
|
|
||||||
|
powershell -Command "&{"^ |
||||||
|
"$webclient = new-object System.Net.WebClient;"^ |
||||||
|
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ |
||||||
|
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ |
||||||
|
"}"^ |
||||||
|
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ |
||||||
|
"}" |
||||||
|
if "%MVNW_VERBOSE%" == "true" ( |
||||||
|
echo Finished downloading %WRAPPER_JAR% |
||||||
|
) |
||||||
|
) |
||||||
|
@REM End of extension |
||||||
|
|
||||||
|
@REM Provide a "standardized" way to retrieve the CLI args that will |
||||||
|
@REM work with both Windows and non-Windows executions. |
||||||
|
set MAVEN_CMD_LINE_ARGS=%* |
||||||
|
|
||||||
|
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* |
||||||
|
if ERRORLEVEL 1 goto error |
||||||
|
goto end |
||||||
|
|
||||||
|
:error |
||||||
|
set ERROR_CODE=1 |
||||||
|
|
||||||
|
:end |
||||||
|
@endlocal & set ERROR_CODE=%ERROR_CODE% |
||||||
|
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost |
||||||
|
@REM check for post script, once with legacy .bat ending and once with .cmd ending |
||||||
|
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" |
||||||
|
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" |
||||||
|
:skipRcPost |
||||||
|
|
||||||
|
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' |
||||||
|
if "%MAVEN_BATCH_PAUSE%" == "on" pause |
||||||
|
|
||||||
|
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% |
||||||
|
|
||||||
|
exit /B %ERROR_CODE% |
||||||
@ -0,0 +1,167 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<parent> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-parent</artifactId> |
||||||
|
<version>2.6.1</version> |
||||||
|
<relativePath/> <!-- lookup parent from repository --> |
||||||
|
</parent> |
||||||
|
<groupId>edu.ncst</groupId> |
||||||
|
<artifactId>award</artifactId> |
||||||
|
<version>0.0.1-SNAPSHOT</version> |
||||||
|
<name>award</name> |
||||||
|
<description>award project for Spring Boot</description> |
||||||
|
<properties> |
||||||
|
<java.version>1.8</java.version> |
||||||
|
</properties> |
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-web</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.mybatis.spring.boot</groupId> |
||||||
|
<artifactId>mybatis-spring-boot-starter</artifactId> |
||||||
|
<version>2.2.0</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-devtools</artifactId> |
||||||
|
<scope>runtime</scope> |
||||||
|
<optional>true</optional> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>mysql</groupId> |
||||||
|
<artifactId>mysql-connector-java</artifactId> |
||||||
|
<scope>runtime</scope> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.projectlombok</groupId> |
||||||
|
<artifactId>lombok</artifactId> |
||||||
|
<optional>true</optional> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-test</artifactId> |
||||||
|
<scope>test</scope> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>com.alibaba</groupId> |
||||||
|
<artifactId>fastjson</artifactId> |
||||||
|
<version>1.2.35</version> |
||||||
|
</dependency> |
||||||
|
<!-- MyBatis --> |
||||||
|
<dependency> |
||||||
|
<groupId>org.mybatis.spring.boot</groupId> |
||||||
|
<artifactId>mybatis-spring-boot-starter</artifactId> |
||||||
|
<version>2.1.3</version> |
||||||
|
</dependency> |
||||||
|
<!-- MyBatis Plus --> |
||||||
|
<dependency> |
||||||
|
<groupId>com.baomidou</groupId> |
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId> |
||||||
|
<version>3.3.2</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>com.baomidou</groupId> |
||||||
|
<artifactId>mybatis-plus-generator</artifactId> |
||||||
|
<version>3.3.2</version> |
||||||
|
</dependency> |
||||||
|
<!-- 数据校验 --> |
||||||
|
<dependency> |
||||||
|
<groupId>javax.validation</groupId> |
||||||
|
<artifactId>validation-api</artifactId> |
||||||
|
<version>2.0.1.Final</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.hibernate</groupId> |
||||||
|
<artifactId>hibernate-validator</artifactId> |
||||||
|
<version>6.0.13.Final</version> |
||||||
|
</dependency> |
||||||
|
<!-- JWT --> |
||||||
|
<dependency> |
||||||
|
<groupId>io.jsonwebtoken</groupId> |
||||||
|
<artifactId>jjwt</artifactId> |
||||||
|
<version>0.9.0</version> |
||||||
|
</dependency> |
||||||
|
<!-- Redis --> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId> |
||||||
|
</dependency> |
||||||
|
<!-- AOP --> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-aop</artifactId> |
||||||
|
</dependency> |
||||||
|
<!-- DevTools --> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework</groupId> |
||||||
|
<artifactId>springloaded</artifactId> |
||||||
|
<version>1.2.8.RELEASE</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-devtools</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-security</artifactId> |
||||||
|
</dependency> |
||||||
|
<!-- swagger --> |
||||||
|
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 --> |
||||||
|
<!-- <dependency>--> |
||||||
|
<!-- <groupId>io.springfox</groupId>--> |
||||||
|
<!-- <artifactId>springfox-swagger2</artifactId>--> |
||||||
|
<!-- <version>2.9.2</version>--> |
||||||
|
<!-- </dependency>--> |
||||||
|
<!-- <!– https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui –>--> |
||||||
|
<!-- <dependency>--> |
||||||
|
<!-- <groupId>io.springfox</groupId>--> |
||||||
|
<!-- <artifactId>springfox-swagger-ui</artifactId>--> |
||||||
|
<!-- <version>2.9.2</version>--> |
||||||
|
<!-- </dependency>--> |
||||||
|
|
||||||
|
<!-- <dependency>--> |
||||||
|
<!-- <groupId>io.springfox</groupId>--> |
||||||
|
<!-- <artifactId>springfox-boot-starter</artifactId>--> |
||||||
|
<!-- <version>3.0.0</version>--> |
||||||
|
<!-- </dependency>--> |
||||||
|
<dependency> |
||||||
|
<groupId>com.spring4all</groupId> |
||||||
|
<artifactId>swagger-spring-boot-starter</artifactId> |
||||||
|
<version>1.9.1.RELEASE</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>com.google.guava</groupId> |
||||||
|
<artifactId>guava</artifactId> |
||||||
|
<version>25.1-jre</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId> |
||||||
|
<optional>true</optional> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<excludes> |
||||||
|
<exclude> |
||||||
|
<groupId>org.projectlombok</groupId> |
||||||
|
<artifactId>lombok</artifactId> |
||||||
|
</exclude> |
||||||
|
</excludes> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
</project> |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
package edu.ncst.award; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; |
||||||
|
import org.mybatis.spring.annotation.MapperScan; |
||||||
|
import org.springframework.boot.SpringApplication; |
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||||
|
import com.spring4all.swagger.EnableSwagger2Doc; |
||||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; |
||||||
|
import org.springframework.cache.annotation.EnableCaching; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.EnableAspectJAutoProxy; |
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling; |
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; |
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement; |
||||||
|
|
||||||
|
|
||||||
|
@EnableSwagger2Doc |
||||||
|
@SpringBootApplication |
||||||
|
@MapperScan(basePackages = {"edu.ncst.award.mapper"}) |
||||||
|
@EnableTransactionManagement |
||||||
|
@EnableCaching |
||||||
|
@EnableAspectJAutoProxy |
||||||
|
public class AwardApplication { |
||||||
|
/** |
||||||
|
* 分页插件 |
||||||
|
*/ |
||||||
|
@Bean |
||||||
|
public PaginationInterceptor paginationInterceptor() { |
||||||
|
return new PaginationInterceptor(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
SpringApplication.run(AwardApplication.class, args); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
package edu.ncst.award.config; |
||||||
|
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.security.access.AccessDeniedException; |
||||||
|
import org.springframework.security.web.access.AccessDeniedHandler; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
|
||||||
|
import javax.servlet.ServletException; |
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
@Service |
||||||
|
public class CustomAccessDeniedHandler implements AccessDeniedHandler { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private ObjectMapper objectMapper; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { |
||||||
|
response.setContentType("application/json;charset=UTF-8"); |
||||||
|
Map map = new HashMap(); |
||||||
|
map.put("code",403); |
||||||
|
map.put("msg", "无访问权限"); |
||||||
|
map.put("data",""); |
||||||
|
response.setContentType("application/json"); |
||||||
|
response.setStatus(HttpServletResponse.SC_OK); |
||||||
|
response.getWriter().write(objectMapper.writeValueAsString(map)); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,47 @@ |
|||||||
|
|
||||||
|
package edu.ncst.award.config; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.serializer.SerializerFeature; |
||||||
|
import com.alibaba.fastjson.support.config.FastJsonConfig; |
||||||
|
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.http.MediaType; |
||||||
|
import org.springframework.http.converter.HttpMessageConverter; |
||||||
|
|
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Configuration |
||||||
|
public class JsonConfig { |
||||||
|
|
||||||
|
|
||||||
|
@Bean |
||||||
|
public HttpMessageConverter configureMessageConverters() { |
||||||
|
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); |
||||||
|
FastJsonConfig config = new FastJsonConfig(); |
||||||
|
config.setSerializerFeatures( |
||||||
|
// 保留map空的字段
|
||||||
|
SerializerFeature.WriteMapNullValue, |
||||||
|
// 将String类型的null转成""
|
||||||
|
SerializerFeature.WriteNullStringAsEmpty, |
||||||
|
// 将Number类型的null转成0
|
||||||
|
SerializerFeature.WriteNullNumberAsZero, |
||||||
|
// 将List类型的null转成[]
|
||||||
|
SerializerFeature.WriteNullListAsEmpty, |
||||||
|
// 将Boolean类型的null转成false
|
||||||
|
SerializerFeature.WriteNullBooleanAsFalse, |
||||||
|
// 避免循环引用
|
||||||
|
SerializerFeature.DisableCircularReferenceDetect); |
||||||
|
|
||||||
|
converter.setFastJsonConfig(config); |
||||||
|
converter.setDefaultCharset(Charset.forName("UTF-8")); |
||||||
|
List<MediaType> mediaTypeList = new ArrayList<>(); |
||||||
|
// 解决中文乱码问题,相当于在Controller上的@RequestMapping中加了个属性produces = "application/json"
|
||||||
|
mediaTypeList.add(MediaType.APPLICATION_JSON); |
||||||
|
converter.setSupportedMediaTypes(mediaTypeList); |
||||||
|
return converter; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,204 @@ |
|||||||
|
package edu.ncst.award.config; |
||||||
|
|
||||||
|
import org.springframework.security.web.access.ExceptionTranslationFilter; |
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||||
|
import edu.ncst.award.config.security.DataBaseUrlVoter; |
||||||
|
import edu.ncst.award.config.security.filter.AuthCheckFilter; |
||||||
|
import edu.ncst.award.config.security.filter.LoginFilter; |
||||||
|
import edu.ncst.award.exception.CodeMsg; |
||||||
|
import edu.ncst.award.service.ILoginLogService; |
||||||
|
import edu.ncst.award.service.impl.system.UserServiceImpl; |
||||||
|
import edu.ncst.award.utils.ResultUtils; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.data.redis.core.RedisTemplate; |
||||||
|
import org.springframework.http.HttpMethod; |
||||||
|
import org.springframework.security.access.AccessDecisionManager; |
||||||
|
import org.springframework.security.access.AccessDecisionVoter; |
||||||
|
import org.springframework.security.access.vote.AuthenticatedVoter; |
||||||
|
import org.springframework.security.access.vote.RoleVoter; |
||||||
|
import org.springframework.security.access.vote.UnanimousBased; |
||||||
|
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.access.expression.WebExpressionVoter; |
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
||||||
|
|
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 安全配置 |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
@EnableWebSecurity |
||||||
|
@EnableGlobalMethodSecurity(securedEnabled=true) |
||||||
|
public class SecurityConfig extends WebSecurityConfigurerAdapter { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private UserServiceImpl userService; |
||||||
|
@Autowired |
||||||
|
private ILoginLogService loginLogService; |
||||||
|
@Autowired |
||||||
|
private DataBaseUrlVoter dataBaseUrlVoter; |
||||||
|
@Autowired |
||||||
|
private RedisTemplate<String, String> redisCacheTemplate; |
||||||
|
/** |
||||||
|
* 认证失败处理类 |
||||||
|
*/ |
||||||
|
@Autowired |
||||||
|
CustomAccessDeniedHandler accessDeniedHandler; |
||||||
|
|
||||||
|
//org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
|
||||||
|
/** |
||||||
|
* 解决 无法直接注入 AuthenticationManager |
||||||
|
* |
||||||
|
* @return |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
@Bean |
||||||
|
@Override |
||||||
|
public AuthenticationManager authenticationManagerBean() throws Exception |
||||||
|
{ |
||||||
|
return super.authenticationManagerBean(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 权限白名单 |
||||||
|
* 注意:该名单下匹配的 URL 不会经过鉴权,因此也无法获取当前用户的信息 |
||||||
|
*/ |
||||||
|
private final String[] WHITE_LIST = { |
||||||
|
"/v2/api-docs", |
||||||
|
"/configuration/ui", |
||||||
|
"/swagger-resources/**", |
||||||
|
"/configuration/**", |
||||||
|
"/swagger-ui.html", |
||||||
|
"/webjars/**", |
||||||
|
"/api/captcha.jpg", |
||||||
|
"/websocket/**", |
||||||
|
"/api/image/get/**", |
||||||
|
"/common/download/**", |
||||||
|
"/test/**", |
||||||
|
"/*.html", |
||||||
|
"/**/*.html", |
||||||
|
"/**/*.css", |
||||||
|
"/**/*.js", |
||||||
|
"/error*" |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* 密码加密器 |
||||||
|
*/ |
||||||
|
@Bean |
||||||
|
public PasswordEncoder passwordEncoderBean() { |
||||||
|
return new BCryptPasswordEncoder(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 权限投票访问决策管理器 |
||||||
|
*/ |
||||||
|
@Bean |
||||||
|
public AccessDecisionManager accessDecisionManager() { |
||||||
|
List<AccessDecisionVoter<? extends Object>> decisionVoters |
||||||
|
= Arrays.asList( |
||||||
|
new WebExpressionVoter(), |
||||||
|
new RoleVoter(),//主要用来判断当前请求是否具备该接口所需要的角色
|
||||||
|
dataBaseUrlVoter, |
||||||
|
new AuthenticatedVoter()); |
||||||
|
return new UnanimousBased(decisionVoters);//要求所有 AccessDecisionVoter 均返回肯定的结果时,才代表授予权限。
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception { |
||||||
|
auth.userDetailsService(userService); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* anyRequest | 匹配所有请求路径 |
||||||
|
* access | SpringEl表达式结果为true时可以访问 |
||||||
|
* anonymous | 匿名可以访问 |
||||||
|
* denyAll | 用户不能访问 |
||||||
|
* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) |
||||||
|
* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 |
||||||
|
* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 |
||||||
|
* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 |
||||||
|
* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 |
||||||
|
* hasRole | 如果有参数,参数表示角色,则其角色可以访问 |
||||||
|
* permitAll | 用户可以任意访问 |
||||||
|
* rememberMe | 允许通过remember-me登录的用户访问 |
||||||
|
* authenticated | 用户登录后可访问 |
||||||
|
*/ |
||||||
|
//权限验证 基于资源配置
|
||||||
|
// HttpSecurity 及WebSecurity 作用是不一样的:
|
||||||
|
// WebSecurity 主要针对的全局的忽略规则,
|
||||||
|
// HttpSecurity主要是权限控制规则。
|
||||||
|
@Override |
||||||
|
protected void configure(HttpSecurity http) throws Exception { |
||||||
|
|
||||||
|
//允许跨域
|
||||||
|
http.cors().and().anonymous().and() |
||||||
|
// CRSF禁用,因为不使用session
|
||||||
|
.csrf().disable() |
||||||
|
.formLogin().disable().httpBasic().disable() |
||||||
|
.authorizeRequests() |
||||||
|
.accessDecisionManager(accessDecisionManager())//访问决策管理器
|
||||||
|
.antMatchers("/api/login").permitAll() |
||||||
|
.antMatchers("/system/init").permitAll() |
||||||
|
// 除上面外的所有请求全部需要鉴权认证
|
||||||
|
.anyRequest().authenticated() |
||||||
|
.and().exceptionHandling().accessDeniedHandler(accessDeniedHandler).and() |
||||||
|
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); |
||||||
|
|
||||||
|
http.addFilterAfter(new AuthCheckFilter(redisCacheTemplate), UsernamePasswordAuthenticationFilter.class) |
||||||
|
.addFilterAt( |
||||||
|
new LoginFilter(authenticationManager(), loginLogService, redisCacheTemplate), UsernamePasswordAuthenticationFilter.class |
||||||
|
) |
||||||
|
.exceptionHandling()//登录认证失败
|
||||||
|
.authenticationEntryPoint(((request, response, authException) -> { |
||||||
|
response.setCharacterEncoding("utf-8"); |
||||||
|
response.setContentType("application/json;charset=utf-8"); |
||||||
|
|
||||||
|
PrintWriter out = response.getWriter(); |
||||||
|
out.write(new ObjectMapper().writeValueAsString(ResultUtils.error(CodeMsg.USER_NEED_LOGIN))); |
||||||
|
out.flush(); |
||||||
|
out.close(); |
||||||
|
})); |
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
http.logout() |
||||||
|
.logoutUrl("/logout") |
||||||
|
.deleteCookies("JSESSIONID") |
||||||
|
.logoutSuccessHandler(((request, response, authentication) -> { |
||||||
|
PrintWriter out = response.getWriter(); |
||||||
|
response.setContentType("application/json"); |
||||||
|
out.write(new ObjectMapper().writeValueAsString(ResultUtils.success("注销成功"))); |
||||||
|
out.flush(); |
||||||
|
out.close(); |
||||||
|
})); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// protected void configure(HttpSecurity httpSecurity) throws Exception{
|
||||||
|
// //调试阶段
|
||||||
|
// httpSecurity.csrf().disable().authorizeRequests();
|
||||||
|
// httpSecurity.authorizeRequests().anyRequest()
|
||||||
|
// .permitAll().and().logout().permitAll();
|
||||||
|
// }
|
||||||
|
|
||||||
|
//用于配置全局的某些通用事物,例如静态资源等
|
||||||
|
@Override |
||||||
|
public void configure(WebSecurity web) throws Exception { |
||||||
|
web.ignoring().antMatchers(WHITE_LIST); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,109 @@ |
|||||||
|
package edu.ncst.award.config.security; |
||||||
|
|
||||||
|
import edu.ncst.award.model.system.Role; |
||||||
|
import edu.ncst.award.service.IResourceService; |
||||||
|
import edu.ncst.award.utils.URLUtils; |
||||||
|
import edu.ncst.award.vo.result.UserInfoVO; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.security.access.AccessDecisionVoter; |
||||||
|
import org.springframework.security.access.ConfigAttribute; |
||||||
|
import org.springframework.security.core.Authentication; |
||||||
|
import org.springframework.security.core.GrantedAuthority; |
||||||
|
import org.springframework.security.web.FilterInvocation; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
import org.springframework.util.AntPathMatcher; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.List; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* URL 鉴权投票器 |
||||||
|
*/ |
||||||
|
@Component |
||||||
|
public class DataBaseUrlVoter implements AccessDecisionVoter<FilterInvocation> { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private IResourceService resourceService; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean supports(ConfigAttribute attribute) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean supports(Class<?> clazz) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int vote(Authentication authentication, FilterInvocation fi, Collection<ConfigAttribute> attributes) { |
||||||
|
if (authentication == null) { |
||||||
|
return ACCESS_DENIED; // 反对票
|
||||||
|
} |
||||||
|
|
||||||
|
String url = fi.getRequestUrl(); // 当前请求的URL
|
||||||
|
String urlWithoutQueryString = URLUtils.removeQueryString(url); |
||||||
|
|
||||||
|
// Current User Info
|
||||||
|
Object details = authentication.getDetails(); |
||||||
|
|
||||||
|
if (details instanceof UserInfoVO) { |
||||||
|
UserInfoVO userInfoVO = (UserInfoVO) details; |
||||||
|
AntPathMatcher antPathMatcher = new AntPathMatcher(); |
||||||
|
|
||||||
|
//获取不限用户角色的资源
|
||||||
|
List<String> permitAllResourceURLs = resourceService.getPermitAllResourceURLs(); |
||||||
|
// Permit All Resource
|
||||||
|
for (String permitUrl : permitAllResourceURLs) { |
||||||
|
boolean match = antPathMatcher.match(permitUrl, urlWithoutQueryString); |
||||||
|
if (match) { |
||||||
|
return ACCESS_GRANTED; // 赞同票
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 判断用户角色信息
|
||||||
|
// 反对票:1、没有角色信息 2、角色未绑定该资源
|
||||||
|
// 赞成票:用户是超级管理员
|
||||||
|
|
||||||
|
List<Role> roles = userInfoVO.getRoles(); |
||||||
|
if (roles == null || roles.isEmpty()) { |
||||||
|
return ACCESS_DENIED; // 反对票
|
||||||
|
} |
||||||
|
|
||||||
|
List<String> roleNames = roles.stream().map(Role::getName).collect(Collectors.toList()); |
||||||
|
|
||||||
|
// Super Admin User Always Pass
|
||||||
|
if (roleNames.contains("SUPER_ADMIN")) { |
||||||
|
return ACCESS_GRANTED; // 赞同票
|
||||||
|
} |
||||||
|
|
||||||
|
List<String> rolesResourceList = resourceService.getRolesResourceList(roleNames);//获取目标角色绑定的资源
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Role Resource
|
||||||
|
if (rolesResourceList == null) { |
||||||
|
return ACCESS_DENIED; // 反对票
|
||||||
|
} |
||||||
|
|
||||||
|
for (String allowUrl : rolesResourceList) { |
||||||
|
boolean match = antPathMatcher.match(allowUrl, urlWithoutQueryString); |
||||||
|
if (match) { |
||||||
|
return ACCESS_GRANTED; // 赞同票
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ACCESS_DENIED; |
||||||
|
} |
||||||
|
|
||||||
|
Collection<? extends GrantedAuthority> extractAuthorities( |
||||||
|
Authentication authentication) { |
||||||
|
return authentication.getAuthorities(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,125 @@ |
|||||||
|
package edu.ncst.award.config.security.filter; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||||
|
import edu.ncst.award.config.security.token.JwtAuthenticationToken; |
||||||
|
import edu.ncst.award.exception.CodeMsg; |
||||||
|
import edu.ncst.award.model.system.Role; |
||||||
|
import edu.ncst.award.utils.ResponseUtils; |
||||||
|
import edu.ncst.award.utils.ResultUtils; |
||||||
|
import edu.ncst.award.vo.ResultVO; |
||||||
|
import edu.ncst.award.vo.result.UserInfoVO; |
||||||
|
import io.jsonwebtoken.Claims; |
||||||
|
import io.jsonwebtoken.ExpiredJwtException; |
||||||
|
import io.jsonwebtoken.Jws; |
||||||
|
import io.jsonwebtoken.Jwts; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.data.redis.core.RedisTemplate; |
||||||
|
import org.springframework.security.core.GrantedAuthority; |
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority; |
||||||
|
import org.springframework.security.core.context.SecurityContextHolder; |
||||||
|
import org.springframework.web.filter.OncePerRequestFilter; |
||||||
|
|
||||||
|
import javax.servlet.FilterChain; |
||||||
|
import javax.servlet.ServletException; |
||||||
|
import javax.servlet.http.Cookie; |
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 用户凭证检查过滤器 |
||||||
|
* 并不是所有的container都像我们期望的只过滤一次,servlet版本不同,表现也不同 |
||||||
|
* OncePerRequestFilter可以保证一次请求只通过一次过滤器。推荐自定义的授权过滤器继承OncePerRequestFilter |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class AuthCheckFilter extends OncePerRequestFilter { |
||||||
|
private final RedisTemplate<String, String> stringRedisTemplate; |
||||||
|
|
||||||
|
public AuthCheckFilter(RedisTemplate<String, String> stringRedisTemplate) { |
||||||
|
super(); |
||||||
|
this.stringRedisTemplate = stringRedisTemplate; |
||||||
|
} |
||||||
|
@Override |
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { |
||||||
|
String jwtToken = null; |
||||||
|
|
||||||
|
// 优先从 Cookie 中获取 jwtTOKEN
|
||||||
|
Cookie[] cookies = request.getCookies(); |
||||||
|
|
||||||
|
log.info("开始鉴权====cookies====" +cookies); |
||||||
|
boolean cookieHasToken = false; |
||||||
|
if (cookies != null) { |
||||||
|
for (Cookie cookie : cookies) { |
||||||
|
if ("SYS_TOKEN".equals(cookie.getName())) { |
||||||
|
jwtToken = cookie.getValue(); |
||||||
|
cookieHasToken = true; |
||||||
|
log.info("获取到token====cookies===="); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 如果 Cookie 中不存在 TOKEN,则尝试从 Header 中获取 TOKEN
|
||||||
|
if (!cookieHasToken) { |
||||||
|
String authorization = request.getHeader("Authorization"); |
||||||
|
log.info("开始鉴权====header Authorization====" +authorization); |
||||||
|
if (authorization != null && authorization.startsWith("Bearer ")) { |
||||||
|
jwtToken = authorization.replace("Bearer ", ""); |
||||||
|
log.info("获取到token====header===="); |
||||||
|
} else { |
||||||
|
log.info("header中无token,进入过滤器链"); |
||||||
|
filterChain.doFilter(request, response); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (jwtToken == null || "".equals(jwtToken)) { |
||||||
|
log.error("鉴权TOKEN:未获取,用户需要登录"); |
||||||
|
ResultVO<Object> resultVO = ResultUtils.error(CodeMsg.USER_NEED_LOGIN); |
||||||
|
ResponseUtils.output(response, resultVO); |
||||||
|
} |
||||||
|
else { |
||||||
|
log.info("鉴权TOKEN:" +"Bearer " + jwtToken); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
// 解析TOKEN
|
||||||
|
Jws<Claims> jws = Jwts.parser().setSigningKey("ncst20220218") |
||||||
|
.parseClaimsJws(jwtToken); |
||||||
|
|
||||||
|
Claims claims = jws.getBody(); |
||||||
|
String userName = claims.getSubject(); |
||||||
|
//可以通过控制redis数据从服务端强制下线用户
|
||||||
|
if(!stringRedisTemplate.hasKey("jwt_token:"+userName)){ |
||||||
|
throw new ExpiredJwtException(null,jws.getBody(),"登录失效"); |
||||||
|
} |
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
String userInfoJSON = (String) claims.get("userInfo"); |
||||||
|
ObjectMapper objectMapper = new ObjectMapper(); |
||||||
|
UserInfoVO userInfoVO = objectMapper.readValue(userInfoJSON, UserInfoVO.class); |
||||||
|
|
||||||
|
// 获取角色列表
|
||||||
|
List<Role> roles = userInfoVO.getRoles(); |
||||||
|
List<GrantedAuthority> authorities = new ArrayList<>(); |
||||||
|
|
||||||
|
if (roles != null && !roles.isEmpty()) { |
||||||
|
for (Role role : roles) { |
||||||
|
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName())); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 将 TOKEN 存到 CONTEXT 当中
|
||||||
|
JwtAuthenticationToken token = new JwtAuthenticationToken(userName, authorities, userInfoVO); |
||||||
|
SecurityContextHolder.getContext().setAuthentication(token); |
||||||
|
} catch (ExpiredJwtException e) { |
||||||
|
ResultVO<Object> resultVO = ResultUtils.error(CodeMsg.USER_TOKEN_EXPIRED);// 用户凭证过期
|
||||||
|
ResponseUtils.output(response, resultVO); |
||||||
|
} finally { |
||||||
|
filterChain.doFilter(request, response); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,255 @@ |
|||||||
|
package edu.ncst.award.config.security.filter; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||||
|
import edu.ncst.award.config.security.token.MobileCaptchaAuthenticationToken; |
||||||
|
import edu.ncst.award.exception.BusinessException; |
||||||
|
import edu.ncst.award.exception.CodeMsg; |
||||||
|
import edu.ncst.award.model.system.Role; |
||||||
|
import edu.ncst.award.model.system.User; |
||||||
|
import edu.ncst.award.service.ILoginLogService; |
||||||
|
import edu.ncst.award.utils.CookieUtils; |
||||||
|
import edu.ncst.award.utils.ResultUtils; |
||||||
|
import edu.ncst.award.vo.ResultVO; |
||||||
|
import edu.ncst.award.vo.param.UserLoginDTO; |
||||||
|
import edu.ncst.award.vo.result.UserInfoVO; |
||||||
|
import io.jsonwebtoken.Jwts; |
||||||
|
import io.jsonwebtoken.SignatureAlgorithm; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.beans.BeanUtils; |
||||||
|
import org.springframework.data.redis.core.RedisTemplate; |
||||||
|
import org.springframework.http.HttpMethod; |
||||||
|
import org.springframework.security.authentication.*; |
||||||
|
import org.springframework.security.core.Authentication; |
||||||
|
import org.springframework.security.core.AuthenticationException; |
||||||
|
import org.springframework.security.core.GrantedAuthority; |
||||||
|
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; |
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; |
||||||
|
|
||||||
|
import javax.servlet.FilterChain; |
||||||
|
import javax.servlet.ServletException; |
||||||
|
import javax.servlet.http.Cookie; |
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.util.Collection; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.List; |
||||||
|
import java.util.UUID; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* 统一登录入口 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class LoginFilter extends AbstractAuthenticationProcessingFilter {//OncePerRequestFilter
|
||||||
|
|
||||||
|
private static final String loginUrl = "/api/login"; |
||||||
|
|
||||||
|
private final ILoginLogService loginLogService; |
||||||
|
|
||||||
|
private final RedisTemplate<String, String> stringRedisTemplate; |
||||||
|
|
||||||
|
private String tempToken=""; |
||||||
|
private final int MAX_ATTEMPT = 20; |
||||||
|
|
||||||
|
public LoginFilter(AuthenticationManager authenticationManager, ILoginLogService loginLogService, RedisTemplate<String, String> stringRedisTemplate) { |
||||||
|
super(new AntPathRequestMatcher(loginUrl, HttpMethod.POST.name())); |
||||||
|
setAuthenticationManager(authenticationManager); |
||||||
|
this.loginLogService = loginLogService; |
||||||
|
this.stringRedisTemplate = stringRedisTemplate; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 尝试登录 |
||||||
|
* 根据登录表单的类型(type)将登录凭证分发给对应的 AuthenticationProvider |
||||||
|
* 当前filter的认证逻辑 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Authentication attemptAuthentication( |
||||||
|
HttpServletRequest request, HttpServletResponse response |
||||||
|
) throws AuthenticationException, IOException, ServletException { |
||||||
|
UserLoginDTO paramUserLoginVO = new ObjectMapper().readValue(request.getInputStream(), UserLoginDTO.class); |
||||||
|
AuthenticationManager authenticationManager = getAuthenticationManager(); |
||||||
|
|
||||||
|
request.setAttribute("userLoginVO", paramUserLoginVO); |
||||||
|
// 登录类型
|
||||||
|
String loginType = paramUserLoginVO.getType(); |
||||||
|
log.info("尝试登录===="+ paramUserLoginVO); |
||||||
|
|
||||||
|
if ("ACCOUNT".equalsIgnoreCase(loginType)) { |
||||||
|
// 校验 TOKEN
|
||||||
|
Cookie tempTokenCookie = CookieUtils.getCookie(request, "TEMP_TOKEN");//cookie中的TEMP_TOKEN携带验证码的sessionid,去redis获取有效期内的验证码
|
||||||
|
|
||||||
|
|
||||||
|
if (tempTokenCookie == null || "".equals(tempTokenCookie.getValue())) { |
||||||
|
throw new InternalAuthenticationServiceException("无效的请求"); |
||||||
|
} |
||||||
|
|
||||||
|
tempToken = tempTokenCookie.getValue(); |
||||||
|
Boolean tokenIsValid = stringRedisTemplate.hasKey("tempToken:" + tempToken); |
||||||
|
|
||||||
|
if (tokenIsValid == null || !tokenIsValid) { |
||||||
|
throw new InternalAuthenticationServiceException("无效的请求"); |
||||||
|
} |
||||||
|
// 校验验证码
|
||||||
|
String rightCaptcha = stringRedisTemplate.opsForValue().get("captcha:" + tempToken); |
||||||
|
String loginCaptcha = paramUserLoginVO.getLoginCaptcha(); |
||||||
|
|
||||||
|
if (loginCaptcha == null || "".equals(loginCaptcha)) { |
||||||
|
throw new BusinessException(CodeMsg.CAPTCHA_IS_EMPTY); |
||||||
|
} else if (rightCaptcha == null || "".equals(rightCaptcha)) { |
||||||
|
throw new BusinessException(CodeMsg.CAPTCHA_IS_EXPIRED); |
||||||
|
} else if (!loginCaptcha.equalsIgnoreCase(rightCaptcha)) { |
||||||
|
throw new BusinessException(CodeMsg.CAPTCHA_IS_NOT_RIGHT); |
||||||
|
} |
||||||
|
// 用户名密码登录,用户名可以是用户账号名、手机号或电子邮箱
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(paramUserLoginVO.getUsername(), paramUserLoginVO.getPassword()); |
||||||
|
request.setAttribute("token", token); |
||||||
|
|
||||||
|
return authenticationManager.authenticate(token); |
||||||
|
} else if ("MOBILE".equalsIgnoreCase(loginType)) { |
||||||
|
// 手机号验证码登录
|
||||||
|
MobileCaptchaAuthenticationToken token = new MobileCaptchaAuthenticationToken(paramUserLoginVO.getPhoneNumber(), paramUserLoginVO.getMobileCaptcha()); |
||||||
|
request.setAttribute("token", token); |
||||||
|
|
||||||
|
return authenticationManager.authenticate(token); |
||||||
|
} else { |
||||||
|
throw new AuthenticationServiceException("登录方式错误"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 登录成功 |
||||||
|
* 无论以何种方式登录,登录成功后都向客户端返回 JWT,前端将该 JWT 临时存储到 localStorage 当中,以便后续的请求在请求头中携带 JWT。 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected void successfulAuthentication( |
||||||
|
HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult |
||||||
|
) throws IOException { |
||||||
|
Object principal = authResult.getPrincipal(); |
||||||
|
UserInfoVO userInfoVO = new UserInfoVO(); |
||||||
|
|
||||||
|
if (authResult instanceof UsernamePasswordAuthenticationToken) { |
||||||
|
userInfoVO.setLoginType("ACCOUNT"); |
||||||
|
} else if (authResult instanceof MobileCaptchaAuthenticationToken) { |
||||||
|
userInfoVO.setLoginType("MOBILE"); |
||||||
|
} |
||||||
|
|
||||||
|
if (principal instanceof User) { |
||||||
|
User user = (User) principal; |
||||||
|
BeanUtils.copyProperties(user, userInfoVO); |
||||||
|
} |
||||||
|
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper(); |
||||||
|
String userInfoJSON = objectMapper.writeValueAsString(userInfoVO); |
||||||
|
|
||||||
|
UUID uuid = UUID.randomUUID(); |
||||||
|
String jwtTokenId = uuid.toString(); |
||||||
|
// 生成 jwt
|
||||||
|
String jwt = Jwts.builder() |
||||||
|
.claim("userInfo", userInfoJSON) |
||||||
|
.setSubject(authResult.getName()) |
||||||
|
.setExpiration(new Date(System.currentTimeMillis() + 10 * 24 * 60 * 60 * 1000)) // 10天有效
|
||||||
|
.signWith(SignatureAlgorithm.HS512, "ncst20220218") |
||||||
|
.setId(jwtTokenId) |
||||||
|
.compact(); |
||||||
|
|
||||||
|
stringRedisTemplate.opsForValue().set("jwt_token:"+authResult.getName(),jwt,10, TimeUnit.DAYS); |
||||||
|
// stringRedisTemplate.delete("user_attempts:"+request.getRemoteAddr());
|
||||||
|
// 记录登录日志
|
||||||
|
loginLogService.addLoginLog(userInfoVO.getId(), true); |
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
ResultVO<String> result = ResultUtils.success("登录成功", jwt); |
||||||
|
response.setContentType("application/json;charset=utf-8"); |
||||||
|
Cookie cookieToken = new Cookie("SYS_TOKEN", jwt); |
||||||
|
cookieToken.setHttpOnly(true); |
||||||
|
response.addCookie(cookieToken); |
||||||
|
|
||||||
|
log.info("登录成功"); |
||||||
|
PrintWriter out = response.getWriter(); |
||||||
|
out.print(new ObjectMapper().writeValueAsString(result)); |
||||||
|
out.flush(); |
||||||
|
out.close(); |
||||||
|
} |
||||||
|
|
||||||
|
// 将权限列表转换成逗号分隔的字符串
|
||||||
|
private String convertRoleToString(List<Role> roleList) { |
||||||
|
StringBuilder roleStrings = new StringBuilder(); |
||||||
|
|
||||||
|
if (roleList == null) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
for (Role role : roleList) { |
||||||
|
roleStrings.append(role.getName()).append(","); |
||||||
|
} |
||||||
|
|
||||||
|
if (roleStrings.length() > 0) { |
||||||
|
roleStrings.deleteCharAt(roleStrings.length() - 1); |
||||||
|
} |
||||||
|
|
||||||
|
return roleStrings.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
// 将权限列表转换成逗号分隔的字符串
|
||||||
|
private String convertAuthorityToString(Collection<? extends GrantedAuthority> authorities) { |
||||||
|
StringBuilder authorityString = new StringBuilder(); |
||||||
|
|
||||||
|
for (GrantedAuthority authority : authorities) { |
||||||
|
authorityString.append(authority.getAuthority()).append(","); |
||||||
|
} |
||||||
|
|
||||||
|
if (authorityString.length() > 0) { |
||||||
|
authorityString.deleteCharAt(authorityString.length() - 1); |
||||||
|
} |
||||||
|
|
||||||
|
return authorityString.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 登录失败 |
||||||
|
* 向前端返回登录失败的错误信息 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { |
||||||
|
ResultVO<?> resultVO = new ResultVO<>(); |
||||||
|
log.debug("登录失败:"+failed); |
||||||
|
if (failed instanceof LockedException) { |
||||||
|
resultVO = ResultUtils.error(CodeMsg.USER_IS_LOCKED); |
||||||
|
} else if (failed instanceof CredentialsExpiredException) { |
||||||
|
resultVO = ResultUtils.error(CodeMsg.USER_CREDENTIALS_EXPIRED); |
||||||
|
} else if (failed instanceof AccountExpiredException) { |
||||||
|
resultVO = ResultUtils.error(CodeMsg.USER_ACCOUNT_EXPIRED); |
||||||
|
} else if (failed instanceof DisabledException) { |
||||||
|
resultVO = ResultUtils.error(CodeMsg.USER_IS_DISABLED); |
||||||
|
} else if (failed instanceof AuthenticationException) { |
||||||
|
if (failed instanceof BusinessException) { |
||||||
|
stringRedisTemplate.delete("captcha:" + tempToken); |
||||||
|
BusinessException businessException = (BusinessException) failed; |
||||||
|
resultVO = ResultUtils.error(businessException.getError()); |
||||||
|
|
||||||
|
}else{ |
||||||
|
Throwable cause = failed.getCause(); |
||||||
|
if (cause instanceof BusinessException) { |
||||||
|
BusinessException businessException = (BusinessException) cause; |
||||||
|
resultVO = ResultUtils.error(businessException.getError()); |
||||||
|
} else { |
||||||
|
resultVO = ResultUtils.error(CodeMsg.USER_BAD_CREDENTIALS); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
resultVO = ResultUtils.error(CodeMsg.LOGIN_FAIL); |
||||||
|
} |
||||||
|
|
||||||
|
// 拦截后直接将错误信息反馈给前端,不经过全局异常处理器(GlobalExceptionHandler)处理
|
||||||
|
response.setCharacterEncoding("utf-8"); |
||||||
|
response.setContentType("application/json;charset=utf-8"); |
||||||
|
PrintWriter out = response.getWriter(); |
||||||
|
out.write(new ObjectMapper().writeValueAsString(resultVO)); |
||||||
|
out.flush(); |
||||||
|
out.close(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
package edu.ncst.award.config.security.token; |
||||||
|
|
||||||
|
import edu.ncst.award.vo.result.UserInfoVO; |
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken; |
||||||
|
import org.springframework.security.core.GrantedAuthority; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* JWT认证凭证(Security Context) |
||||||
|
*/ |
||||||
|
public class JwtAuthenticationToken extends AbstractAuthenticationToken { |
||||||
|
|
||||||
|
private final Object principal; |
||||||
|
|
||||||
|
private final Object details; |
||||||
|
|
||||||
|
public JwtAuthenticationToken(Object principal, List<GrantedAuthority> authorities, UserInfoVO userInfoVO) { |
||||||
|
super(authorities); |
||||||
|
this.principal = principal; |
||||||
|
this.details = userInfoVO; |
||||||
|
this.setAuthenticated(true); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getCredentials() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getPrincipal() { |
||||||
|
return this.principal; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getDetails() { |
||||||
|
return this.details; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
package edu.ncst.award.config.security.token; |
||||||
|
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken; |
||||||
|
import org.springframework.security.core.GrantedAuthority; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
|
||||||
|
/** |
||||||
|
* 手机验证码方式登录凭证 |
||||||
|
*/ |
||||||
|
public class MobileCaptchaAuthenticationToken extends AbstractAuthenticationToken { |
||||||
|
|
||||||
|
// 手机号码
|
||||||
|
private String phoneNumber; |
||||||
|
|
||||||
|
// 验证码
|
||||||
|
private String captchaCode; |
||||||
|
|
||||||
|
public MobileCaptchaAuthenticationToken(String phoneNumber, String captchaCode) { |
||||||
|
super(null); |
||||||
|
this.phoneNumber = phoneNumber; |
||||||
|
this.captchaCode = captchaCode; |
||||||
|
} |
||||||
|
|
||||||
|
public MobileCaptchaAuthenticationToken(Collection<? extends GrantedAuthority> authorities) { |
||||||
|
super(authorities); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getCredentials() { |
||||||
|
return this.phoneNumber; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getPrincipal() { |
||||||
|
return this.captchaCode; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
package edu.ncst.award.controller; |
||||||
|
|
||||||
|
|
||||||
|
import edu.ncst.award.utils.ResultUtils; |
||||||
|
import edu.ncst.award.vo.ResultVO; |
||||||
|
import io.swagger.annotations.Api; |
||||||
|
import io.swagger.annotations.ApiOperation; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.http.ResponseEntity; |
||||||
|
import org.springframework.web.bind.annotation.*; |
||||||
|
|
||||||
|
import javax.annotation.security.PermitAll; |
||||||
|
import javax.servlet.http.Cookie; |
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
|
||||||
|
@Api(tags = "AuthController") |
||||||
|
@RestController |
||||||
|
@RequestMapping("/api/auth") |
||||||
|
public class AuthController { |
||||||
|
|
||||||
|
|
||||||
|
@ApiOperation("注销登录") |
||||||
|
@GetMapping("/logout") |
||||||
|
public ResultVO<?> logout(HttpServletRequest request, HttpServletResponse response) { |
||||||
|
Cookie[] cookies = request.getCookies(); |
||||||
|
|
||||||
|
if (cookies != null) { |
||||||
|
for (Cookie cookie : cookies) { |
||||||
|
cookie.setValue(""); |
||||||
|
cookie.setPath("/api"); |
||||||
|
cookie.setMaxAge(0); |
||||||
|
response.addCookie(cookie); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ResultUtils.success("注销成功", null); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,76 @@ |
|||||||
|
package edu.ncst.award.controller; |
||||||
|
|
||||||
|
import edu.ncst.award.utils.CaptchaUtil; |
||||||
|
import edu.ncst.award.utils.CookieUtils; |
||||||
|
import io.swagger.annotations.Api; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate; |
||||||
|
import org.springframework.stereotype.Controller; |
||||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
|
||||||
|
import javax.imageio.ImageIO; |
||||||
|
import javax.servlet.ServletOutputStream; |
||||||
|
import javax.servlet.http.Cookie; |
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import java.awt.image.BufferedImage; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.UUID; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
@Api(description = "验证码") |
||||||
|
@Controller |
||||||
|
@RequestMapping("/api") |
||||||
|
public class CaptchaController { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private StringRedisTemplate stringRedisTemplate; |
||||||
|
|
||||||
|
@GetMapping("/captcha.jpg") |
||||||
|
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException { |
||||||
|
Cookie tempTokenCookie = CookieUtils.getCookie(request, "TEMP_TOKEN"); |
||||||
|
String tempToken = null; |
||||||
|
Boolean tokenIsValid = false; |
||||||
|
|
||||||
|
// 校验 TOKEN 是否存在
|
||||||
|
if (tempTokenCookie != null && tempTokenCookie.getValue() != null && !"".equals(tempTokenCookie.getValue())) { |
||||||
|
tempToken = tempTokenCookie.getValue(); |
||||||
|
} |
||||||
|
|
||||||
|
// 校验 TOKEN 是否有效
|
||||||
|
if (tempToken != null) { |
||||||
|
tokenIsValid = stringRedisTemplate.hasKey("tempToken:" + tempToken); |
||||||
|
} |
||||||
|
|
||||||
|
// 如果没有 TOKEN 或者 TOKEN 失效都会生成一个新的TOKEN
|
||||||
|
if (tokenIsValid == null || !tokenIsValid) { |
||||||
|
UUID uuid = UUID.randomUUID(); |
||||||
|
tempToken = uuid.toString(); |
||||||
|
|
||||||
|
// 将临时 TOKEN 存入Redis,设置24小时的过期时间
|
||||||
|
stringRedisTemplate.opsForValue().set("tempToken:" + tempToken, "1", 24, TimeUnit.HOURS); |
||||||
|
|
||||||
|
// 将临时TOKEN存入Cookie同步设置24小时过期时间
|
||||||
|
Cookie newCookie = new Cookie("TEMP_TOKEN", tempToken); |
||||||
|
newCookie.setMaxAge(24 * 59 * 60); |
||||||
|
newCookie.setHttpOnly(true); |
||||||
|
|
||||||
|
response.addCookie(newCookie); |
||||||
|
} |
||||||
|
|
||||||
|
// 生成验证码
|
||||||
|
response.setHeader("Cache-Control", "no-store, no-cache"); |
||||||
|
response.setContentType("image/jpeg"); |
||||||
|
|
||||||
|
BufferedImage image = CaptchaUtil.createImage(); |
||||||
|
String randomText = CaptchaUtil.drawRandomText(image); |
||||||
|
|
||||||
|
// 将验证码存入 Redis,设置两分钟的过期时间
|
||||||
|
stringRedisTemplate.opsForValue().set("captcha:" + tempToken, randomText, 2, TimeUnit.MINUTES); |
||||||
|
ServletOutputStream out = response.getOutputStream(); |
||||||
|
|
||||||
|
ImageIO.write(image, "jpeg", out); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,123 @@ |
|||||||
|
package edu.ncst.award.controller; |
||||||
|
|
||||||
|
import edu.ncst.award.model.system.Menu; |
||||||
|
import edu.ncst.award.service.IMenuService; |
||||||
|
import edu.ncst.award.utils.ResponseUtils; |
||||||
|
import edu.ncst.award.utils.ResultUtils; |
||||||
|
import edu.ncst.award.vo.ListResultVO; |
||||||
|
import edu.ncst.award.vo.Query; |
||||||
|
import edu.ncst.award.vo.ResultVO; |
||||||
|
import edu.ncst.award.vo.param.ResetMenuDTO; |
||||||
|
import io.swagger.annotations.Api; |
||||||
|
import io.swagger.annotations.ApiOperation; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.http.ResponseEntity; |
||||||
|
import org.springframework.web.bind.annotation.*; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
; |
||||||
|
|
||||||
|
/** |
||||||
|
* 菜单 控制器 |
||||||
|
*/ |
||||||
|
@Api(tags = "MenuController") |
||||||
|
@Slf4j |
||||||
|
@RestController |
||||||
|
@RequestMapping("/api/menu") |
||||||
|
public class MenuController { |
||||||
|
|
||||||
|
private final IMenuService menuService; |
||||||
|
|
||||||
|
public MenuController(IMenuService menuService) { |
||||||
|
this.menuService = menuService; |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("获取菜单列表") |
||||||
|
@GetMapping("/list") |
||||||
|
public ResultVO<ListResultVO<Menu>> getMenuList(Query query) { |
||||||
|
ListResultVO<Menu> menuList = menuService.getMenuList(query); |
||||||
|
|
||||||
|
return ResultUtils.success("ok", menuList); |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("获取菜单树") |
||||||
|
@GetMapping("/tree") |
||||||
|
public ResultVO<List<Menu>> getMenuTree() { |
||||||
|
List<Menu> menuTree = menuService.getMenuTree(); |
||||||
|
return ResultUtils.success("ok", menuTree); |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("获取指定角色能够访问的菜单ID列表") |
||||||
|
@GetMapping("/ids/role") |
||||||
|
public ResultVO<List<Integer>> getRoleMenuIds(Integer roleId) { |
||||||
|
List<Integer> menuIds = menuService.getRoleMenuIds(roleId,true); |
||||||
|
return ResultUtils.success("ok", menuIds); |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("获取菜单详情") |
||||||
|
@PostMapping("/detail") |
||||||
|
public ResultVO<Menu> getMenuDetail(@RequestParam Integer id) { |
||||||
|
Menu menuDetail = menuService.getMenuDetail(id); |
||||||
|
|
||||||
|
return ResultUtils.success("ok", menuDetail); |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("添加菜单") |
||||||
|
@PostMapping("/add") |
||||||
|
public ResultVO<Menu> addMenu(@RequestBody Menu menu) { |
||||||
|
log.info("将要添加的菜单:" + menu); |
||||||
|
Menu result = menuService.addMenu(menu); |
||||||
|
|
||||||
|
return ResultUtils.success("ok", result); |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("修改菜单") |
||||||
|
@PostMapping("/modify") |
||||||
|
public ResultVO<Menu> modifyMenu(@RequestBody Menu menu) { |
||||||
|
Menu result = menuService.modifyMenu(menu); |
||||||
|
|
||||||
|
return ResultUtils.success("ok", result); |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("删除菜单") |
||||||
|
@PostMapping("/delete") |
||||||
|
public ResultVO<Menu> deleteMenu(@RequestParam Integer id) { |
||||||
|
menuService.deleteMenu(id); |
||||||
|
|
||||||
|
return ResultUtils.success("ok", null); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@ApiOperation("获取指定用户可访问的菜单列表") |
||||||
|
@GetMapping("/user/{userId}") |
||||||
|
public ResultVO<ListResultVO<Menu>> getUserMenuList(@PathVariable Integer userId) { |
||||||
|
return ResultUtils.success("ok", null); |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("获取当前用户可访问的菜单列表") |
||||||
|
@GetMapping("/user/current") |
||||||
|
public ResultVO<List<Menu>> getCurrentUserMenuTree() { |
||||||
|
List<Menu> currentUserMenuTree = menuService.getCurrentUserMenuTree(); |
||||||
|
|
||||||
|
return ResultUtils.success("ok", currentUserMenuTree); |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("获取当前用户可访问的菜单列表") |
||||||
|
@GetMapping("/user/authority") |
||||||
|
public ResultVO<List<String>> getCurrentUserMenuAuthority() { |
||||||
|
List<String> currentUserMenuTree = menuService.getCurrentUserMenuAuthority(); |
||||||
|
|
||||||
|
return ResultUtils.success("ok", currentUserMenuTree); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@ApiOperation("重置菜单元信息") |
||||||
|
@PostMapping("/reset") |
||||||
|
public ResponseEntity<?> resetMenuMeta(@RequestBody ResetMenuDTO resetMenuDTO) { |
||||||
|
menuService.resetMenuMeta(resetMenuDTO); |
||||||
|
|
||||||
|
return ResponseUtils.success("ok", null); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,146 @@ |
|||||||
|
package edu.ncst.award.controller; |
||||||
|
|
||||||
|
|
||||||
|
import edu.ncst.award.exception.CodeMsg; |
||||||
|
import edu.ncst.award.model.system.MajorCategory; |
||||||
|
import edu.ncst.award.model.system.Resource; |
||||||
|
import edu.ncst.award.service.IUserService; |
||||||
|
import edu.ncst.award.utils.ResponseUtils; |
||||||
|
import edu.ncst.award.utils.ResultUtils; |
||||||
|
import edu.ncst.award.vo.ListResultVO; |
||||||
|
import edu.ncst.award.vo.ResultVO; |
||||||
|
import edu.ncst.award.vo.param.query.UserQuery; |
||||||
|
import edu.ncst.award.vo.result.UserInfoVO; |
||||||
|
import io.swagger.annotations.Api; |
||||||
|
import io.swagger.annotations.ApiOperation; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.http.ResponseEntity; |
||||||
|
import org.springframework.web.bind.annotation.*; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
@RestController |
||||||
|
@RequestMapping("/test") |
||||||
|
@Api(tags = "TestController") |
||||||
|
@Slf4j |
||||||
|
public class TestController { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private IUserService userService; |
||||||
|
|
||||||
|
@ApiOperation("get传参,简单类型,requestParam注解") |
||||||
|
@GetMapping("/user/detail") |
||||||
|
@ResponseBody |
||||||
|
public ResponseEntity getUserInfo(@RequestParam(name = "phone", required = false) String param, @RequestParam String name){ |
||||||
|
if(param==null){ |
||||||
|
return ResponseUtils.error(CodeMsg.PARAM_ERROR); |
||||||
|
} |
||||||
|
UserQuery queryVO = new UserQuery(); |
||||||
|
queryVO.setPhone(param); |
||||||
|
UserInfoVO user = userService.getUserInfo(queryVO); |
||||||
|
return ResponseUtils.success(user); |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("post传参,简单类型,requestParam注解") |
||||||
|
@PostMapping("/user/detail00") |
||||||
|
@ResponseBody |
||||||
|
public ResponseEntity getUserInfo00(@RequestParam(name = "param", required = false) String param,@RequestParam String name){ |
||||||
|
if(param==null){ |
||||||
|
return ResponseUtils.error(CodeMsg.PARAM_ERROR); |
||||||
|
} |
||||||
|
UserQuery queryVO = new UserQuery(); |
||||||
|
queryVO.setPhone(param); |
||||||
|
UserInfoVO user = userService.getUserInfo(queryVO); |
||||||
|
return ResponseUtils.success(user); |
||||||
|
} |
||||||
|
@ApiOperation("get方法传参,类接收,无注解") |
||||||
|
@GetMapping("/user/detail1") |
||||||
|
@ResponseBody |
||||||
|
public UserInfoVO getUserInfo1(UserQuery param){ |
||||||
|
if(param.getPhone()==null){ |
||||||
|
String s = param.getPhone().toString(); |
||||||
|
} |
||||||
|
UserInfoVO user = userService.getUserInfo(param); |
||||||
|
user.setFullName("getUserInfo1"); |
||||||
|
return user; |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("post方法传参,类接收,无注解") |
||||||
|
@PostMapping("/user/detail11") |
||||||
|
@ResponseBody |
||||||
|
public UserInfoVO getUserInfo11(UserQuery param){ |
||||||
|
if(param.getPhone()==null){ |
||||||
|
String s = param.getPhone().toString(); |
||||||
|
} |
||||||
|
UserInfoVO user = userService.getUserInfo(param); |
||||||
|
user.setFullName("getUserInfo1"); |
||||||
|
return user; |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("get方法传参,类接收,requestparam注解") |
||||||
|
@GetMapping("/user/detail2") |
||||||
|
@ResponseBody |
||||||
|
public UserInfoVO getUserInfo2(@RequestParam UserQuery param){ |
||||||
|
if(param.getPhone()==null){ |
||||||
|
String s = param.getPhone().toString(); |
||||||
|
} |
||||||
|
UserInfoVO user = userService.getUserInfo(param); |
||||||
|
user.setFullName("getUserInfo2"); |
||||||
|
return user; |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("post方法传参,类接收,requestparam注解") |
||||||
|
@PostMapping("/user/detail22") |
||||||
|
@ResponseBody |
||||||
|
public UserInfoVO getUserInfo22(@RequestParam UserQuery param){ |
||||||
|
if(param.getPhone()==null){ |
||||||
|
String s = param.getPhone().toString(); |
||||||
|
} |
||||||
|
UserInfoVO user = userService.getUserInfo(param); |
||||||
|
user.setFullName("getUserInfo22"); |
||||||
|
return user; |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("get方法传参,类接收,requestbody注解") |
||||||
|
@GetMapping("/user/detail3") |
||||||
|
@ResponseBody |
||||||
|
public UserInfoVO getUserInfo3(@RequestBody UserQuery param){ |
||||||
|
if(param.getPhone()==null){ |
||||||
|
String s = param.getPhone().toString(); |
||||||
|
} |
||||||
|
UserInfoVO user = userService.getUserInfo(param); |
||||||
|
user.setFullName("getUserInfo3"); |
||||||
|
return user; |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("post方法传参,类接收,requestbody注解") |
||||||
|
@PostMapping("/user/detail4") |
||||||
|
@ResponseBody |
||||||
|
public UserInfoVO getUserInfo4(@RequestBody UserQuery param){ |
||||||
|
if(param.getPhone()==null){ |
||||||
|
String s = param.getPhone().toString(); |
||||||
|
} |
||||||
|
UserInfoVO user = userService.getUserInfo(param); |
||||||
|
user.setFullName("getUserInfo3"); |
||||||
|
return user; |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("post方法传参,类接收,requestbody注解") |
||||||
|
@PostMapping("/user/detail5") |
||||||
|
@ResponseBody |
||||||
|
public ListResultVO<MajorCategory> getUserInfo5(@RequestBody MajorCategory param){ |
||||||
|
ListResultVO<MajorCategory> listResultVO = new ListResultVO<>(); |
||||||
|
ArrayList l = new ArrayList(); |
||||||
|
l.add(param); |
||||||
|
listResultVO.setList(l); |
||||||
|
return listResultVO; |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("post方法传参,类接收,requestbody注解") |
||||||
|
@PostMapping("/user/detailResource") |
||||||
|
@ResponseBody |
||||||
|
public ResultVO<Resource> getUserInfoResource(@RequestBody Resource param){ |
||||||
|
return ResultUtils.success(param); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
package edu.ncst.award.controller; |
||||||
|
|
||||||
|
|
||||||
|
import edu.ncst.award.model.system.User; |
||||||
|
import edu.ncst.award.service.IUserService; |
||||||
|
import edu.ncst.award.utils.ResponseUtils; |
||||||
|
import edu.ncst.award.utils.ResultUtils; |
||||||
|
import edu.ncst.award.vo.ListResultVO; |
||||||
|
import edu.ncst.award.vo.ResultVO; |
||||||
|
import edu.ncst.award.vo.param.CurrentUserDTO; |
||||||
|
import edu.ncst.award.vo.param.query.UserQuery; |
||||||
|
import io.swagger.annotations.Api; |
||||||
|
import io.swagger.annotations.ApiOperation; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.http.ResponseEntity; |
||||||
|
import org.springframework.validation.BindingResult; |
||||||
|
import org.springframework.web.bind.annotation.*; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
; |
||||||
|
|
||||||
|
/** |
||||||
|
* 用户 控制器 |
||||||
|
*/ |
||||||
|
@Api(description = "用户管理", tags = "UserController") |
||||||
|
@Slf4j |
||||||
|
@RestController |
||||||
|
@RequestMapping("/api/user") |
||||||
|
public class UserController { |
||||||
|
|
||||||
|
private final IUserService userService; |
||||||
|
|
||||||
|
public UserController(IUserService userService) { |
||||||
|
this.userService = userService; |
||||||
|
} |
||||||
|
|
||||||
|
@ApiOperation("获取用户列表") |
||||||
|
@GetMapping("/list") |
||||||
|
public ResultVO<ListResultVO<User>> getUserList(UserQuery query) { |
||||||
|
ListResultVO<User> userList = userService.getUserList(query); |
||||||
|
|
||||||
|
return ResultUtils.success("ok", userList); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@ApiOperation("获取当前用户信息") |
||||||
|
@GetMapping("/current") |
||||||
|
public ResponseEntity<?> getCurrentUser() { |
||||||
|
CurrentUserDTO currentUserDTO = userService.getCurrentUser(); |
||||||
|
|
||||||
|
return ResponseUtils.success(currentUserDTO); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,41 @@ |
|||||||
|
package edu.ncst.award.exception; |
||||||
|
|
||||||
|
import org.springframework.security.core.AuthenticationException; |
||||||
|
|
||||||
|
public class BusinessException extends AuthenticationException { |
||||||
|
|
||||||
|
private CodeMsg codeMsg; |
||||||
|
|
||||||
|
public BusinessException(String msg){ |
||||||
|
super(msg); |
||||||
|
codeMsg = CodeMsg.BUSINESS_ERROR.fillArgs(msg); |
||||||
|
} |
||||||
|
|
||||||
|
public BusinessException(CodeMsg msg){ |
||||||
|
super(msg.getMsg()); |
||||||
|
if(msg==null){ |
||||||
|
msg=CodeMsg.BUSINESS_ERROR.fillArgs("未知错误"); |
||||||
|
} |
||||||
|
|
||||||
|
this.codeMsg = msg; |
||||||
|
} |
||||||
|
public CodeMsg getError() { |
||||||
|
if(codeMsg==null){ |
||||||
|
codeMsg=CodeMsg.BUSINESS_ERROR.fillArgs("未知错误"); |
||||||
|
} |
||||||
|
return codeMsg; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getMessage() { |
||||||
|
if(codeMsg==null){ |
||||||
|
String msg = super.getMessage(); |
||||||
|
if(msg==null){ |
||||||
|
return CodeMsg.BUSINESS_ERROR.fillArgs("未知错误").getMsg(); |
||||||
|
} else{ |
||||||
|
return msg; |
||||||
|
} |
||||||
|
} |
||||||
|
return codeMsg.getMsg(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,63 @@ |
|||||||
|
package edu.ncst.award.exception; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
@Data |
||||||
|
public class CodeMsg { |
||||||
|
|
||||||
|
public static final CodeMsg SUCCESS = new CodeMsg(0, "成功"); |
||||||
|
public static final CodeMsg SUCCESS_MSG = new CodeMsg(0, "%s"); |
||||||
|
public final static CodeMsg SERVER_ERROR = new CodeMsg(500, "服务器错误:%s"); |
||||||
|
public static final CodeMsg PARAM_ERROR = new CodeMsg(501, "参数错误:%s"); |
||||||
|
public static final CodeMsg GLOBAL_ERROR = new CodeMsg(502, "全局错误:%s"); |
||||||
|
public static final CodeMsg BUSINESS_ERROR = new CodeMsg(503, "业务错误:%s");; |
||||||
|
|
||||||
|
public final static CodeMsg USER_NEED_LOGIN = new CodeMsg(10000, "尚未登录,请登录。"); |
||||||
|
public final static CodeMsg USERNAME_REQUIRE_NOT_NULL = new CodeMsg(10001, "用户名不能为空"); |
||||||
|
public final static CodeMsg PASSWORD_REQUIRE_NOT_NULL = new CodeMsg(10002, "用户密码不能为空"); |
||||||
|
public final static CodeMsg USER_NOT_FOUND = new CodeMsg(10003, "该用户不存在。"); |
||||||
|
public final static CodeMsg USERNAME_OR_PASSWORD_ERROR = new CodeMsg(10004, "用户名或密码错误"); |
||||||
|
public final static CodeMsg USER_IS_LOCKED = new CodeMsg(10005, "当前IP已被锁定,请1小时后再试。"); |
||||||
|
public final static CodeMsg USER_CREDENTIALS_EXPIRED = new CodeMsg(10006, "用户登录凭证过期,请重新登录。"); |
||||||
|
public final static CodeMsg USER_ACCOUNT_EXPIRED = new CodeMsg(10007, "用户账号过期,请联系管理员。"); |
||||||
|
public final static CodeMsg USER_IS_DISABLED = new CodeMsg(10008, "该用户已被禁用,请联系管理员。"); |
||||||
|
public final static CodeMsg USER_BAD_CREDENTIALS = new CodeMsg(10009, "无效的登录凭证,请尝试重新登录"); |
||||||
|
public final static CodeMsg USER_TOKEN_EXPIRED = new CodeMsg(10010, "用户凭证已过期,请联系管理员。"); |
||||||
|
public final static CodeMsg NOT_GET_CURRENT_USER_INFO = new CodeMsg(10011, "无法获取当前用户信息。"); |
||||||
|
public final static CodeMsg CAN_NOT_DATA_ACCESS = new CodeMsg(10012, "当前用户没有访问相关数据的权限。"); |
||||||
|
public final static CodeMsg LOGIN_FAIL = new CodeMsg(10015, "登录失败,未知原因。"); |
||||||
|
public final static CodeMsg CAPTCHA_IS_NOT_RIGHT = new CodeMsg(10018, "验证码错误。"); |
||||||
|
public final static CodeMsg CAPTCHA_IS_EMPTY = new CodeMsg(10019, "验证码不能为空。"); |
||||||
|
public final static CodeMsg CAPTCHA_IS_EXPIRED = new CodeMsg(10020, "验证码已过期。"); |
||||||
|
public static final CodeMsg TEMP_TOKEN_NOT_FOUND = new CodeMsg(10021,"无法获取临时授权"); |
||||||
|
public static final CodeMsg TEMP_TOKEN_EXPIRED = new CodeMsg(10022,"临时授权已过期"); |
||||||
|
|
||||||
|
/** |
||||||
|
* 基本操作 101XX |
||||||
|
*/ |
||||||
|
public final static CodeMsg FETCH_ERROR = new CodeMsg(10100, "获取数据失败"); |
||||||
|
public final static CodeMsg ADD_ERROR = new CodeMsg(10101, "添加失败%s"); |
||||||
|
public final static CodeMsg MODIFY_ERROR = new CodeMsg(10102, "修改失败"); |
||||||
|
public final static CodeMsg DELETE_ERROR = new CodeMsg(10103, "删除失败"); |
||||||
|
public final static CodeMsg DELETE_ERROR_RELATION_DATA = new CodeMsg(101031, "存在关联业务数据,删除或禁用失败"); |
||||||
|
public final static CodeMsg REPEAT_RECORD = new CodeMsg(10104, "字段与已有记录重复"); |
||||||
|
public final static CodeMsg ENABLE_OR_DISABLE_ERROR = new CodeMsg(10105, "启用或禁用出错。"); |
||||||
|
public final static CodeMsg CAN_FOUND_DICT = new CodeMsg(10106, "找不到字典。"); |
||||||
|
public final static CodeMsg FORM_ERROR = new CodeMsg(10107, "表单参数错误"); |
||||||
|
public static final CodeMsg MORE_THAN_ONE_ITEM = new CodeMsg(10108, "多余一条数据被查出"); |
||||||
|
public static final CodeMsg ALREADY_EXIST_ITEM = new CodeMsg(10109, "数据重复"); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private int code; |
||||||
|
private String msg; |
||||||
|
|
||||||
|
public CodeMsg(int i, String s) { |
||||||
|
this.code=i; |
||||||
|
this.msg=s; |
||||||
|
} |
||||||
|
public CodeMsg fillArgs(Object... args) { |
||||||
|
String message = String.format(msg, args); |
||||||
|
return new CodeMsg(code, message); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
package edu.ncst.award.exception.handler; |
||||||
|
|
||||||
|
import edu.ncst.award.exception.BusinessException; |
||||||
|
import edu.ncst.award.exception.CodeMsg; |
||||||
|
import edu.ncst.award.utils.ResponseUtils; |
||||||
|
import org.springframework.http.ResponseEntity; |
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler; |
||||||
|
import org.springframework.web.bind.annotation.ResponseBody; |
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice; |
||||||
|
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
/** |
||||||
|
* 自定义异常处理器 |
||||||
|
* |
||||||
|
* @author ieflex |
||||||
|
*/ |
||||||
|
@RestControllerAdvice |
||||||
|
@Slf4j |
||||||
|
public class InterfaceExceptionHandler { |
||||||
|
|
||||||
|
/** |
||||||
|
* 接口 业务异常 |
||||||
|
*/ |
||||||
|
@ResponseBody |
||||||
|
@ExceptionHandler(BusinessException.class) |
||||||
|
public ResponseEntity businessInterfaceException(BusinessException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
return ResponseUtils.error(CodeMsg.BUSINESS_ERROR.fillArgs(e.getError())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 拦截所有运行时的全局异常 |
||||||
|
*/ |
||||||
|
@ExceptionHandler(RuntimeException.class) |
||||||
|
@ResponseBody |
||||||
|
public ResponseEntity runtimeException(RuntimeException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
// 返回 JOSN
|
||||||
|
return ResponseUtils.error(CodeMsg.GLOBAL_ERROR.fillArgs(e.toString())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 系统异常捕获处理 |
||||||
|
*/ |
||||||
|
@ExceptionHandler(Exception.class) |
||||||
|
@ResponseBody |
||||||
|
public ResponseEntity exception(Exception e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
return ResponseUtils.error(CodeMsg.SERVER_ERROR.fillArgs(e.toString())); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
package edu.ncst.award.mapper.system; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
import edu.ncst.award.model.system.Menu; |
||||||
|
import org.apache.ibatis.annotations.Param; |
||||||
|
import org.springframework.stereotype.Repository; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 菜单 Mapper 接口 |
||||||
|
*/ |
||||||
|
@Repository |
||||||
|
public interface MenuMapper extends BaseMapper<Menu> { |
||||||
|
|
||||||
|
int batchInsert(List<Menu> menuList); |
||||||
|
|
||||||
|
int batchInsertOrUpdate(List<Menu> menuList); |
||||||
|
|
||||||
|
Page<Menu> getMenuList(Page<Menu> pageRequest, @Param("parentId") Integer parentId); |
||||||
|
|
||||||
|
List<Menu> getMenuTree(@Param("parentId") Integer parentId); |
||||||
|
|
||||||
|
// 清空菜单项
|
||||||
|
int removeAllMenuItem(); |
||||||
|
|
||||||
|
// 获取指定角色能够访问的菜单ID
|
||||||
|
List<Integer> getRoleMenuIds(Integer roleId); |
||||||
|
|
||||||
|
List<String> getCurrentUserMenuAuthority(List<String> roleNames); |
||||||
|
|
||||||
|
List<Integer> getParentMenuid(List<Integer> menuIds); |
||||||
|
|
||||||
|
List<Integer> getRoleMenuIdsWithParent(Integer roleId); |
||||||
|
} |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
package edu.ncst.award.mapper.system; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||||
|
import edu.ncst.award.model.system.Resource; |
||||||
|
import org.springframework.stereotype.Repository; |
||||||
|
import org.springframework.web.bind.annotation.RequestParam; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Repository |
||||||
|
public interface ResourceMapper extends BaseMapper<Resource> { |
||||||
|
|
||||||
|
List<String> getRolesResourceUrls(List<String> roleNames); |
||||||
|
|
||||||
|
int batchReplaceOrInsert(List<Resource> resources); |
||||||
|
|
||||||
|
int batchInsert(List<Resource> resources); |
||||||
|
|
||||||
|
int replaceOrInsert(@RequestParam("resource") Resource resource); |
||||||
|
|
||||||
|
Integer getIdByName(String name); |
||||||
|
|
||||||
|
int insertNotExisted(@RequestParam("resource") Resource resource); |
||||||
|
|
||||||
|
List<Integer> getRoleResourceIds(Integer roleId); |
||||||
|
|
||||||
|
List<Resource> getResourceTree(); |
||||||
|
|
||||||
|
List<Resource> getPermitAllResourceList(); |
||||||
|
|
||||||
|
List<Resource> getUserScopeResourceList(); |
||||||
|
|
||||||
|
List<String> getPermitAllResourceURLs(); |
||||||
|
|
||||||
|
List<String> getUserScopeResourceURLs(); |
||||||
|
|
||||||
|
// 所有用户都可以访问的查询接口
|
||||||
|
List<Integer> getAllResourceIds(); |
||||||
|
} |
||||||
@ -0,0 +1,51 @@ |
|||||||
|
package edu.ncst.award.mapper.system; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Constants; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
import edu.ncst.award.model.system.User; |
||||||
|
import org.apache.ibatis.annotations.Param; |
||||||
|
import org.apache.ibatis.annotations.Update; |
||||||
|
import org.springframework.stereotype.Repository; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 用户 Mapper 接口 |
||||||
|
*/ |
||||||
|
@Repository |
||||||
|
|
||||||
|
public interface UserMapper extends BaseMapper<User> { |
||||||
|
|
||||||
|
Page<Integer> getUserPage( |
||||||
|
@Param("pageRequest") Page<User> pageRequest, |
||||||
|
@Param(Constants.WRAPPER) QueryWrapper queryWrapper |
||||||
|
); |
||||||
|
|
||||||
|
List<User> getUserList( @Param(Constants.WRAPPER) QueryWrapper queryWrapper); |
||||||
|
|
||||||
|
User getUserDetail(Integer userId); |
||||||
|
|
||||||
|
User getUserByAccount(String account); |
||||||
|
|
||||||
|
User getUserByOpenId(String openId); |
||||||
|
|
||||||
|
@Update("UPDATE sys_user SET enable = #{ enable } WHERE id = #{ userId }") |
||||||
|
int setUserEnable(Integer userId, Boolean enable); |
||||||
|
|
||||||
|
// 更新用户的最后登录记录
|
||||||
|
int updateUserLastLoginRecord( |
||||||
|
@Param("userId") Integer userId, |
||||||
|
@Param("lastLoginIp") String lastLoginIp, |
||||||
|
@Param("lastLoginTime") Date lastLoginTime |
||||||
|
); |
||||||
|
|
||||||
|
// 绑定用户微信号
|
||||||
|
int bindUserOpenId(Integer userId, String openId); |
||||||
|
|
||||||
|
int getUserByUnitID(Integer unitID); |
||||||
|
|
||||||
|
void batchUpdatePassword(List<User> newuser); |
||||||
|
} |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
package edu.ncst.award.model; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableLogic; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
@Data |
||||||
|
public abstract class BaseEntity implements Serializable { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "创建时间") |
||||||
|
@TableField(value = "create_time", fill = FieldFill.INSERT) |
||||||
|
private Date createTime; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "创建人") |
||||||
|
@TableField(value = "create_user_id", fill = FieldFill.INSERT) |
||||||
|
private Integer createUserId; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "最后更新时间") |
||||||
|
@TableField(value = "last_modify_time", fill = FieldFill.INSERT_UPDATE) |
||||||
|
private Date lastModifyTime; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "最后更新者") |
||||||
|
@TableField(value = "last_modify_user_id", fill = FieldFill.INSERT_UPDATE) |
||||||
|
private Integer lastModifyUserId; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "删除时间") |
||||||
|
@TableField("delete_time") |
||||||
|
@TableLogic |
||||||
|
private Date deleteTime; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "删除者") |
||||||
|
@TableField("delete_user_id") |
||||||
|
private Integer deleteUserId; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
package edu.ncst.award.model.log; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||||
|
import edu.ncst.award.model.system.User; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.EqualsAndHashCode; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
/** |
||||||
|
* 登录日志 |
||||||
|
*/ |
||||||
|
@ApiModel(value="LoginLog", description="登录日志") |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = false) |
||||||
|
@TableName("log_login_log") |
||||||
|
public class LoginLog implements Serializable { |
||||||
|
|
||||||
|
private static final long serialVersionUID=1L; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "ID") |
||||||
|
@TableId(value = "id", type = IdType.AUTO) |
||||||
|
private Integer id; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "登录方式") |
||||||
|
private String type; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "用户ID") |
||||||
|
private Integer userId; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "登錄用户") |
||||||
|
@TableField(exist = false) |
||||||
|
private User user; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "登录凭证", notes = "用户名、手机号、电子邮箱或微信登录凭证code") |
||||||
|
private String principal; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "IP地址") |
||||||
|
private String ipAddress; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "登录时间") |
||||||
|
private Date loginTime; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,59 @@ |
|||||||
|
package edu.ncst.award.model.system; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||||
|
import edu.ncst.award.model.BaseEntity; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.EqualsAndHashCode; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 专业分类 |
||||||
|
*/ |
||||||
|
@ApiModel(value="MajorCategory", description="专业分类") |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = true) |
||||||
|
@TableName(value = "major_category", autoResultMap = true) |
||||||
|
public class MajorCategory extends BaseEntity implements Serializable { |
||||||
|
|
||||||
|
private static final long serialVersionUID = -1167360738461144523L; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "分类ID") |
||||||
|
@TableId(value = "id", type = IdType.AUTO) |
||||||
|
private Integer id; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "分类编码") |
||||||
|
private String code; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "分类名称") |
||||||
|
private String name; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否启用") |
||||||
|
private Boolean enable; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "分类级别") |
||||||
|
private Integer level; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "上级分类ID") |
||||||
|
private Integer parentId; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "备注") |
||||||
|
private String remark; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "下级分类列表") |
||||||
|
@TableField(exist = false) |
||||||
|
private List<MajorCategory> children; |
||||||
|
|
||||||
|
public MajorCategory(Integer id) { |
||||||
|
this.id = id; |
||||||
|
} |
||||||
|
|
||||||
|
public MajorCategory() { |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,87 @@ |
|||||||
|
package edu.ncst.award.model.system; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
/** |
||||||
|
* 菜单 |
||||||
|
*/ |
||||||
|
@ApiModel(value="Menu", description="菜单") |
||||||
|
@Data |
||||||
|
@TableName("sys_menu") |
||||||
|
public class Menu implements Serializable { |
||||||
|
|
||||||
|
private static final long serialVersionUID=1L; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "菜单ID") |
||||||
|
@TableId(value = "id", type = IdType.AUTO) |
||||||
|
private Integer id; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "菜单名称") |
||||||
|
private String name; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "菜单标题") |
||||||
|
private String title; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "菜单图标") |
||||||
|
private String icon; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "菜单链接") |
||||||
|
private String url; |
||||||
|
|
||||||
|
// @ApiModelProperty(value = "路由名称")
|
||||||
|
// private String routeName;
|
||||||
|
//
|
||||||
|
// @ApiModelProperty(value = "路由路径")
|
||||||
|
// private String routePath;
|
||||||
|
//
|
||||||
|
// @ApiModelProperty(value = "路由组件")
|
||||||
|
// private String routeComponentName;
|
||||||
|
//
|
||||||
|
// @ApiModelProperty(value = "是否开启路由缓存")
|
||||||
|
// private Boolean routeCache;
|
||||||
|
|
||||||
|
// @ApiModelProperty(value = "关联权限ID")
|
||||||
|
// private Integer authorityId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "上级菜单") |
||||||
|
private Integer parentId; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "下级菜单") |
||||||
|
@TableField(exist = false) |
||||||
|
private List<Menu> children; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否显示在菜单上") |
||||||
|
private Boolean showInMenu = true; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "显示排序") |
||||||
|
private Integer sort = 0; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "备注") |
||||||
|
private String remark; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "删除时间") |
||||||
|
@TableLogic |
||||||
|
private Date deleteTime; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (this == o) return true; |
||||||
|
if (o == null || getClass() != o.getClass()) return false; |
||||||
|
Menu menu = (Menu) o; |
||||||
|
return Objects.equals(name, menu.name); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int hashCode() { |
||||||
|
return Objects.hash(name); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,58 @@ |
|||||||
|
package edu.ncst.award.model.system; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.EqualsAndHashCode; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@ApiModel(value="Resource", description="资源") |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = false) |
||||||
|
@TableName("sys_resource") |
||||||
|
public class Resource implements Serializable { |
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "资源ID") |
||||||
|
@TableId(value = "id", type = IdType.AUTO) |
||||||
|
private Integer id; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "显示文本") |
||||||
|
private String label; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "资源名称(方法签名)") |
||||||
|
private String name; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "请求方法类型") |
||||||
|
private String method; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "链接") |
||||||
|
private String url; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "允许所有访问", notes = "不需要用户登录") |
||||||
|
private Boolean permitAll = false; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "用户作用域") |
||||||
|
private Boolean userScope = false; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "上级菜单ID") |
||||||
|
private Integer parentId; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "上级菜单ID") |
||||||
|
@TableField(exist = false) |
||||||
|
private List<Resource> children; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "排序") |
||||||
|
private Integer sort; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否對所有角色開放") |
||||||
|
private Boolean isForAll = true; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,76 @@ |
|||||||
|
package edu.ncst.award.model.system; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableLogic; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||||
|
import edu.ncst.award.model.BaseEntity; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.EqualsAndHashCode; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
/** |
||||||
|
* 角色 |
||||||
|
*/ |
||||||
|
@ApiModel(value="Role", description="角色") |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = false) |
||||||
|
@TableName("sys_role") |
||||||
|
public class Role extends BaseEntity { |
||||||
|
|
||||||
|
private static final long serialVersionUID=1L; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "角色ID") |
||||||
|
@TableId(value = "id", type = IdType.AUTO) |
||||||
|
private Integer id; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "角色名称") |
||||||
|
private String name; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "显示文本") |
||||||
|
private String label; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否启用") |
||||||
|
private Boolean enable; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "删除时间") |
||||||
|
@TableLogic |
||||||
|
@JsonIgnore |
||||||
|
private Date deleteTime; |
||||||
|
|
||||||
|
public Role(Integer id) { |
||||||
|
this.id = id; |
||||||
|
} |
||||||
|
|
||||||
|
public Role() { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (this == o) return true; |
||||||
|
|
||||||
|
if (o == null || getClass() != o.getClass()) return false; |
||||||
|
|
||||||
|
Role role = (Role) o; |
||||||
|
|
||||||
|
return Objects.equals(name, role.name); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int hashCode() { |
||||||
|
return Objects.hash(name); |
||||||
|
} |
||||||
|
|
||||||
|
public static Role forName(String name) { |
||||||
|
Role role = new Role(); |
||||||
|
role.setName(name); |
||||||
|
return role; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,84 @@ |
|||||||
|
package edu.ncst.award.model.system; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||||
|
import edu.ncst.award.model.BaseEntity; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.EqualsAndHashCode; |
||||||
|
import org.springframework.security.core.GrantedAuthority; |
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority; |
||||||
|
import org.springframework.security.core.userdetails.UserDetails; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collection; |
||||||
|
import java.util.List; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
@ApiModel(value="User", description="用户") |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = false) |
||||||
|
@TableName("sys_user") |
||||||
|
public class User extends BaseEntity implements Serializable, UserDetails { |
||||||
|
@ApiModelProperty(value = "用户id") |
||||||
|
private Integer id; |
||||||
|
@ApiModelProperty(value = "用户密码") |
||||||
|
private String password; |
||||||
|
@ApiModelProperty(value = "用户名") |
||||||
|
private String username; |
||||||
|
@ApiModelProperty(value = "用户真实名") |
||||||
|
private String fullName; |
||||||
|
@ApiModelProperty(value = "地址信息") |
||||||
|
private String address; |
||||||
|
@ApiModelProperty(value = "手机号") |
||||||
|
private String phone; |
||||||
|
@ApiModelProperty(value = "所属单位") |
||||||
|
private Integer unitId; |
||||||
|
@ApiModelProperty(value = "角色列表") |
||||||
|
@TableField(exist = false) |
||||||
|
private List<Role> roles; |
||||||
|
@ApiModelProperty(value = "是否启用 1:启用 0:禁用") |
||||||
|
private Boolean enable; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() { |
||||||
|
List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); |
||||||
|
|
||||||
|
if (this.roles != null && this.roles.size() > 0) { |
||||||
|
List<GrantedAuthority> roleAuthority = roles.stream() |
||||||
|
.map((role) -> new SimpleGrantedAuthority("ROLE_" + role.getName())) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
grantedAuthorities.addAll(roleAuthority); |
||||||
|
} |
||||||
|
|
||||||
|
return grantedAuthorities; |
||||||
|
} |
||||||
|
|
||||||
|
@JsonIgnore |
||||||
|
public boolean isAccountNonExpired() { |
||||||
|
return true; |
||||||
|
}//用户是否未过期
|
||||||
|
|
||||||
|
@JsonIgnore |
||||||
|
@Override |
||||||
|
public boolean isAccountNonLocked() { |
||||||
|
return true; |
||||||
|
}//用户是否未锁定
|
||||||
|
|
||||||
|
@JsonIgnore |
||||||
|
@Override |
||||||
|
public boolean isCredentialsNonExpired() { |
||||||
|
return true; |
||||||
|
}//凭证是否未过期
|
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isEnabled() { |
||||||
|
return this.enable; |
||||||
|
}//是否启用
|
||||||
|
} |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
package edu.ncst.award.service; |
||||||
|
|
||||||
|
import edu.ncst.award.model.log.LoginLog; |
||||||
|
import edu.ncst.award.vo.ListResultVO; |
||||||
|
import edu.ncst.award.vo.param.query.LoginLogQuery; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
|
||||||
|
/** |
||||||
|
* 登录日志 服务类 |
||||||
|
*/ |
||||||
|
public interface ILoginLogService { |
||||||
|
|
||||||
|
// 查询登录日志列表
|
||||||
|
ListResultVO<LoginLog> getLoginLogList(LoginLogQuery query); |
||||||
|
|
||||||
|
// 添加登录日志
|
||||||
|
void addLoginLog(Integer userId, Boolean success); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
package edu.ncst.award.service; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||||
|
import edu.ncst.award.model.system.Menu; |
||||||
|
import edu.ncst.award.vo.ListResultVO; |
||||||
|
import edu.ncst.award.vo.Query; |
||||||
|
import edu.ncst.award.vo.param.ResetMenuDTO; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 菜单 服务类 |
||||||
|
*/ |
||||||
|
public interface IMenuService extends IService<Menu> { |
||||||
|
|
||||||
|
// 获取菜单列表
|
||||||
|
ListResultVO<Menu> getMenuList(Query query); |
||||||
|
|
||||||
|
// 获取菜单详情
|
||||||
|
Menu getMenuDetail(Integer id); |
||||||
|
|
||||||
|
// 添加菜单
|
||||||
|
Menu addMenu(Menu menu); |
||||||
|
|
||||||
|
// 修改菜单
|
||||||
|
Menu modifyMenu(Menu menu); |
||||||
|
|
||||||
|
// 删除菜单
|
||||||
|
boolean deleteMenu(Integer id); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 导入菜单元信息 |
||||||
|
* @param resetMenuDTO |
||||||
|
*/ |
||||||
|
void resetMenuMeta(ResetMenuDTO resetMenuDTO); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取菜单树 |
||||||
|
* @return 菜单树 |
||||||
|
*/ |
||||||
|
List<Menu> getMenuTree(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前用户菜单树 |
||||||
|
* @return 菜单树 |
||||||
|
*/ |
||||||
|
List<Menu> getCurrentUserMenuTree(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前用户菜单权限列表 |
||||||
|
* @return 菜单树 |
||||||
|
*/ |
||||||
|
List<String> getCurrentUserMenuAuthority(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取指定角色能够访问的菜单ID |
||||||
|
* @param roleId |
||||||
|
*/ |
||||||
|
List<Integer> getRoleMenuIds(Integer roleId, boolean withParent); |
||||||
|
} |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
package edu.ncst.award.service; |
||||||
|
|
||||||
|
import edu.ncst.award.model.system.Resource; |
||||||
|
import edu.ncst.award.vo.ListResultVO; |
||||||
|
import edu.ncst.award.vo.Query; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public interface IResourceService { |
||||||
|
void reimportInterfaceMeta(); |
||||||
|
|
||||||
|
ListResultVO<Resource> getResourceList(Query query); |
||||||
|
|
||||||
|
List<Resource> getResourceTree(); |
||||||
|
|
||||||
|
List<Integer> getRoleResourceIds(Integer roleId); |
||||||
|
|
||||||
|
List<String> getRolesResourceList(List<String> roleNames); |
||||||
|
|
||||||
|
List<Resource> getPermitAllResourceList(); |
||||||
|
|
||||||
|
List<String> getPermitAllResourceURLs(); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
package edu.ncst.award.service; |
||||||
|
|
||||||
|
public interface ISystemService { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
package edu.ncst.award.service; |
||||||
|
|
||||||
|
import edu.ncst.award.model.system.User; |
||||||
|
import edu.ncst.award.vo.ListResultVO; |
||||||
|
import edu.ncst.award.vo.param.CurrentUserDTO; |
||||||
|
import edu.ncst.award.vo.param.query.UserQuery; |
||||||
|
import edu.ncst.award.vo.result.UserInfoVO; |
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
|
||||||
|
|
||||||
|
public interface IUserService { |
||||||
|
UserInfoVO getUserInfo(UserQuery query); |
||||||
|
|
||||||
|
ListResultVO<User> getUserList(UserQuery query); |
||||||
|
|
||||||
|
CurrentUserDTO getCurrentUser(); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
package edu.ncst.award.service.impl.log; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
|
||||||
|
import edu.ncst.award.model.log.LoginLog; |
||||||
|
import edu.ncst.award.service.ILoginLogService; |
||||||
|
import edu.ncst.award.vo.ListResultVO; |
||||||
|
import edu.ncst.award.vo.param.query.LoginLogQuery; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.security.core.Authentication; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* 登录日志 服务实现类 |
||||||
|
*/ |
||||||
|
@Service |
||||||
|
public class LoginLogServiceImpl implements ILoginLogService { |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public ListResultVO<LoginLog> getLoginLogList(LoginLogQuery query) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addLoginLog(Integer userId, Boolean success) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,320 @@ |
|||||||
|
package edu.ncst.award.service.impl.system; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||||
|
import edu.ncst.award.exception.BusinessException; |
||||||
|
import edu.ncst.award.exception.CodeMsg; |
||||||
|
import edu.ncst.award.mapper.system.MenuMapper; |
||||||
|
import edu.ncst.award.model.system.Menu; |
||||||
|
import edu.ncst.award.model.system.Role; |
||||||
|
import edu.ncst.award.service.IMenuService; |
||||||
|
import edu.ncst.award.utils.SessionUtils; |
||||||
|
import edu.ncst.award.vo.ListResultVO; |
||||||
|
import edu.ncst.award.vo.Query; |
||||||
|
import edu.ncst.award.vo.param.ResetMenuDTO; |
||||||
|
import edu.ncst.award.vo.result.UserInfoVO; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.ibatis.session.SqlSession; |
||||||
|
import org.springframework.beans.BeanUtils; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
import org.springframework.transaction.annotation.Transactional; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* 菜单 服务实现类 |
||||||
|
*/ |
||||||
|
@Service |
||||||
|
@Slf4j |
||||||
|
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IMenuService { |
||||||
|
|
||||||
|
private final MenuMapper menuMapper; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private SqlSession sqlSession; |
||||||
|
|
||||||
|
public MenuServiceImpl(MenuMapper menuMapper) { |
||||||
|
this.menuMapper = menuMapper; |
||||||
|
} |
||||||
|
|
||||||
|
// 获取菜单列表
|
||||||
|
@Override |
||||||
|
public ListResultVO<Menu> getMenuList(Query query) { |
||||||
|
QueryWrapper<Menu> queryWrapper = new QueryWrapper<>(); |
||||||
|
Page<Menu> pageRequest = query.toPageRequest(); |
||||||
|
Page<Menu> page = menuMapper.getMenuList(pageRequest, null); |
||||||
|
|
||||||
|
// 封装返回结果
|
||||||
|
ListResultVO<Menu> listResultVO = new ListResultVO<>(); |
||||||
|
listResultVO.setList(page.getRecords()); |
||||||
|
listResultVO.setTotal(page.getTotal()); |
||||||
|
listResultVO.setCurrent(page.getCurrent()); |
||||||
|
|
||||||
|
return listResultVO; |
||||||
|
} |
||||||
|
|
||||||
|
// 获取菜单详情
|
||||||
|
@Override |
||||||
|
public Menu getMenuDetail(Integer id) { |
||||||
|
Menu menu = menuMapper.selectById(id); |
||||||
|
return menu; |
||||||
|
} |
||||||
|
|
||||||
|
// 添加菜单
|
||||||
|
@Transactional |
||||||
|
@Override |
||||||
|
public Menu addMenu(Menu menu) { |
||||||
|
int insert = menuMapper.insert(menu); |
||||||
|
Menu result = getMenuDetail(menu.getId()); |
||||||
|
if (insert == 1) { |
||||||
|
return result; |
||||||
|
} else { |
||||||
|
throw new BusinessException(CodeMsg.ADD_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 修改菜单
|
||||||
|
@Transactional |
||||||
|
@Override |
||||||
|
public Menu modifyMenu(Menu menu) { |
||||||
|
int update = menuMapper.updateById(menu); |
||||||
|
Menu result = getMenuDetail(menu.getId()); |
||||||
|
|
||||||
|
if (update == 1) { |
||||||
|
return result; |
||||||
|
} else { |
||||||
|
throw new BusinessException(CodeMsg.MODIFY_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 删除菜单
|
||||||
|
@Transactional |
||||||
|
@Override |
||||||
|
public boolean deleteMenu(Integer id) { |
||||||
|
int delete = menuMapper.deleteById(id); |
||||||
|
|
||||||
|
if (delete == 1) { |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
throw new BusinessException(CodeMsg.DELETE_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 导入菜单 |
||||||
|
* 说明:该操作会将原有的菜单元信息全部清空,然后再导入新的菜单元信息 |
||||||
|
* @param resetMenuDTO 重置菜单表单 |
||||||
|
*/ |
||||||
|
@Transactional |
||||||
|
@Override |
||||||
|
public void resetMenuMeta(ResetMenuDTO resetMenuDTO) { |
||||||
|
List<Menu> oldTree = getMenuTree(); |
||||||
|
List<Menu> newTree = resetMenuDTO.getMenus(); |
||||||
|
Map<String, Menu> waitDeleteMap = new HashMap<>(); |
||||||
|
|
||||||
|
findWaitDeleteMenu(oldTree, newTree, waitDeleteMap); |
||||||
|
diffMenuMeta(oldTree, newTree, waitDeleteMap, null); |
||||||
|
|
||||||
|
// 批量删除不存在的菜单
|
||||||
|
Collection<Menu> deleteMenus = waitDeleteMap.values(); |
||||||
|
List<Integer> menuIds = deleteMenus.stream().map(Menu::getId).collect(Collectors.toList()); |
||||||
|
|
||||||
|
if (!menuIds.isEmpty()) { |
||||||
|
menuMapper.deleteBatchIds(menuIds); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void findWaitDeleteMenu(List<Menu> oldTree, List<Menu> newTree, Map<String, Menu> waitDeleteMap) { |
||||||
|
HashMap<String, Menu> newMenuMap = new HashMap<>(); |
||||||
|
|
||||||
|
for (Menu menu : newTree) { |
||||||
|
newMenuMap.put(menu.getName(), menu); |
||||||
|
} |
||||||
|
|
||||||
|
// 旧菜单列表存在 但 新菜单列表不存在 加入到待删除列表(旧有新无)
|
||||||
|
for (Menu oldMenu : oldTree) { |
||||||
|
String key = oldMenu.getName(); |
||||||
|
|
||||||
|
if (!newMenuMap.containsKey(key)) { |
||||||
|
waitDeleteMap.put(key, oldMenu); |
||||||
|
} else { |
||||||
|
Menu newMenu = newMenuMap.get(key); |
||||||
|
List<Menu> children = newMenu.getChildren(); |
||||||
|
|
||||||
|
if (children != null && children.size() > 0) { |
||||||
|
findWaitDeleteMenu(oldMenu.getChildren(), children, waitDeleteMap); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void diffMenuMeta(List<Menu> oldTree, List<Menu> newTree, Map<String, Menu> waitDeleteMap, Integer parentId) { |
||||||
|
if (oldTree == null || newTree == null) return; |
||||||
|
|
||||||
|
HashMap<String, Menu> oldMenuMap = new HashMap<>(); |
||||||
|
|
||||||
|
for (Menu menu : oldTree) { |
||||||
|
oldMenuMap.put(menu.getName(), menu); |
||||||
|
} |
||||||
|
|
||||||
|
// 遍历新菜单列表
|
||||||
|
for (Menu newMenu : newTree) { |
||||||
|
List<Menu> children = newMenu.getChildren(); |
||||||
|
List<Menu> oldChildren = new ArrayList<>(); |
||||||
|
String key = newMenu.getName(); |
||||||
|
log.info(newMenu.toString()); |
||||||
|
log.info(key); |
||||||
|
Integer menuId; |
||||||
|
|
||||||
|
if (newMenu.getName() == null || "".equals(newMenu.getName())) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (oldMenuMap.containsKey(key)) { |
||||||
|
// 旧菜单列表存在 且 新菜单列表存在 执行更新操作(旧有新有)
|
||||||
|
Menu oldMenu = oldMenuMap.get(key); |
||||||
|
oldChildren = oldMenu.getChildren(); |
||||||
|
menuId = oldMenu.getId(); |
||||||
|
|
||||||
|
// 更新旧菜单数据
|
||||||
|
BeanUtils.copyProperties(newMenu, oldMenu,"id", "children"); |
||||||
|
oldMenu.setParentId(parentId); |
||||||
|
|
||||||
|
// 属性完全相同(除ID,新菜单没有ID可以进行比较)时没有必要进行更新操作
|
||||||
|
if (!isMenuEquals(oldMenu, newMenu)) { |
||||||
|
menuMapper.updateById(oldMenu); |
||||||
|
} |
||||||
|
} else if (waitDeleteMap.containsKey(key)) { |
||||||
|
Menu oldMenu = waitDeleteMap.remove(key); |
||||||
|
oldChildren = oldMenu.getChildren(); |
||||||
|
menuId = oldMenu.getId(); |
||||||
|
|
||||||
|
// 更新旧菜单数据
|
||||||
|
BeanUtils.copyProperties(newMenu, oldMenu, "id", "children"); |
||||||
|
oldMenu.setParentId(parentId); |
||||||
|
|
||||||
|
if (!isMenuEquals(oldMenu, newMenu)) { |
||||||
|
menuMapper.updateById(oldMenu); |
||||||
|
} |
||||||
|
} else { |
||||||
|
log.info(newMenu.toString()); |
||||||
|
// 旧菜单列表不存在 但 新菜单列表存在 执行插入操作(旧无新有)
|
||||||
|
newMenu.setParentId(parentId); |
||||||
|
menuMapper.insert(newMenu); |
||||||
|
menuId = newMenu.getId(); |
||||||
|
} |
||||||
|
|
||||||
|
if (children != null && children.size() > 0) { |
||||||
|
diffMenuMeta(oldChildren, children, waitDeleteMap, menuId); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断两个菜单中的字段是否相等 |
||||||
|
*/ |
||||||
|
private boolean isMenuEquals(Menu oldMenu, Menu newMenu) { |
||||||
|
if (oldMenu == null && newMenu == null) { |
||||||
|
return true; |
||||||
|
} else if (oldMenu == null || newMenu == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return Objects.equals(oldMenu.getName(), newMenu.getName()) && |
||||||
|
Objects.equals(oldMenu.getTitle(), newMenu.getTitle()) && |
||||||
|
Objects.equals(oldMenu.getIcon(), newMenu.getIcon()) && |
||||||
|
Objects.equals(oldMenu.getUrl(), newMenu.getUrl()) && |
||||||
|
Objects.equals(oldMenu.getShowInMenu(), newMenu.getShowInMenu()) && |
||||||
|
Objects.equals(oldMenu.getParentId(), newMenu.getParentId()) && |
||||||
|
Objects.equals(oldMenu.getSort(), newMenu.getSort()) && |
||||||
|
Objects.equals(oldMenu.getRemark(), newMenu.getRemark()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取菜单树 |
||||||
|
* @return 菜单树 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public List<Menu> getMenuTree() { |
||||||
|
return menuMapper.getMenuTree(null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前菜单树 |
||||||
|
* @return 菜单树 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public List<Menu> getCurrentUserMenuTree() { |
||||||
|
UserInfoVO userInfoVO = SessionUtils.getCurrentUser(); |
||||||
|
List<Role> roles = userInfoVO.getRoles(); |
||||||
|
Set<Integer> allowMenuIdSet = new HashSet<>(); |
||||||
|
|
||||||
|
if (roles != null) { |
||||||
|
for (Role role : roles) { |
||||||
|
List<Integer> roleMenuIds = getRoleMenuIds(role.getId(),true); |
||||||
|
allowMenuIdSet.addAll(roleMenuIds); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
List<Menu> menuTree = getMenuTree(); |
||||||
|
|
||||||
|
return filterMenuList(menuTree, allowMenuIdSet); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 过滤掉不允许访问的菜单 |
||||||
|
*/ |
||||||
|
private List<Menu> filterMenuList(List<Menu> menuTree, Set<Integer> allowMenuIdSet) { |
||||||
|
List<Menu> allowMenuTree = new ArrayList<>(); |
||||||
|
|
||||||
|
if (menuTree == null || menuTree.size() == 0) { |
||||||
|
return menuTree; |
||||||
|
} |
||||||
|
|
||||||
|
for (Menu menu : menuTree) { |
||||||
|
if (allowMenuIdSet.contains(menu.getId())) { |
||||||
|
|
||||||
|
if (menu.getChildren() != null && menu.getChildren().size() > 0) { |
||||||
|
menu.setChildren(filterMenuList(menu.getChildren(), allowMenuIdSet)); |
||||||
|
} |
||||||
|
|
||||||
|
allowMenuTree.add(menu); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return allowMenuTree; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取指定角色能够访问的菜单ID |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public List<Integer> getRoleMenuIds(Integer roleId , boolean withParent) { |
||||||
|
if(withParent){ |
||||||
|
return menuMapper.getRoleMenuIdsWithParent(roleId); |
||||||
|
}else{ |
||||||
|
return menuMapper.getRoleMenuIds(roleId); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前用户 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public List<String> getCurrentUserMenuAuthority() { |
||||||
|
UserInfoVO userInfoVO = SessionUtils.getCurrentUser(); |
||||||
|
List<Role> roles = userInfoVO.getRoles(); |
||||||
|
|
||||||
|
if (roles != null && !roles.isEmpty()) { |
||||||
|
List<String> roleNames = roles.stream().map(Role::getName).collect(Collectors.toList()); |
||||||
|
return menuMapper.getCurrentUserMenuAuthority(roleNames); |
||||||
|
} else { |
||||||
|
return new ArrayList<>(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,387 @@ |
|||||||
|
package edu.ncst.award.service.impl.system; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
import edu.ncst.award.exception.BusinessException; |
||||||
|
import edu.ncst.award.exception.CodeMsg; |
||||||
|
import edu.ncst.award.mapper.system.ResourceMapper; |
||||||
|
import edu.ncst.award.model.system.Resource; |
||||||
|
import edu.ncst.award.service.IResourceService; |
||||||
|
import edu.ncst.award.vo.ListResultVO; |
||||||
|
import edu.ncst.award.vo.Query; |
||||||
|
import io.swagger.annotations.Api; |
||||||
|
import io.swagger.annotations.ApiOperation; |
||||||
|
import org.springframework.beans.BeanUtils; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.cache.annotation.CacheEvict; |
||||||
|
import org.springframework.cache.annotation.Cacheable; |
||||||
|
import org.springframework.cache.annotation.Caching; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
import org.springframework.transaction.annotation.Transactional; |
||||||
|
import org.springframework.web.bind.annotation.*; |
||||||
|
|
||||||
|
import javax.annotation.security.PermitAll; |
||||||
|
import java.io.File; |
||||||
|
import java.io.FileFilter; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.lang.reflect.Modifier; |
||||||
|
import java.net.URI; |
||||||
|
import java.net.URISyntaxException; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
@Service |
||||||
|
public class ResourceServiceImpl implements IResourceService { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private ResourceMapper resourceMapper; |
||||||
|
|
||||||
|
// 欲扫描的包路径
|
||||||
|
private static final String SCAN_PACKAGE = "edu.ncst.equmanager.controller"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 重新导入接口元信息 |
||||||
|
*/ |
||||||
|
@Caching( |
||||||
|
evict = { |
||||||
|
@CacheEvict(cacheNames = "permitAllResourceList"), |
||||||
|
@CacheEvict(cacheNames = "userScopeResourceList"), |
||||||
|
@CacheEvict(cacheNames = "permitAllResourceURLs"), |
||||||
|
@CacheEvict(cacheNames = "userScopeResourceURLs") |
||||||
|
} |
||||||
|
) |
||||||
|
@Transactional |
||||||
|
@Override |
||||||
|
public void reimportInterfaceMeta() { |
||||||
|
List<Resource> newInterfaceMetas = getSystemInterfaceMetas(); |
||||||
|
Set<String> newInterfaceNameSet = new HashSet<>(); |
||||||
|
|
||||||
|
QueryWrapper<Resource> queryWrapper = new QueryWrapper<>(); |
||||||
|
List<Resource> oldInterfaceMetas = resourceMapper.selectList(queryWrapper); |
||||||
|
HashMap<String, Resource> oldInterfaceNameMap = new HashMap<>(); |
||||||
|
List<Resource> insertResourceList = new ArrayList<>(); |
||||||
|
|
||||||
|
// 待删除接口列表
|
||||||
|
List<Integer> deleteInterfaceIds = new ArrayList<>(); |
||||||
|
|
||||||
|
for (Resource newInterfaceMeta : newInterfaceMetas) { |
||||||
|
newInterfaceNameSet.add(newInterfaceMeta.getName()); |
||||||
|
} |
||||||
|
|
||||||
|
for (Resource oldInterfaceMeta : oldInterfaceMetas) { |
||||||
|
oldInterfaceNameMap.put(oldInterfaceMeta.getName(), oldInterfaceMeta); |
||||||
|
|
||||||
|
if (!newInterfaceNameSet.contains(oldInterfaceMeta.getName())) { |
||||||
|
// 旧有新无,进行删除操作
|
||||||
|
deleteInterfaceIds.add(oldInterfaceMeta.getId()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 检查数据库中的接口是否还存在于当前系统当中
|
||||||
|
for (Resource newInterface : newInterfaceMetas) { |
||||||
|
String key = newInterface.getName(); |
||||||
|
|
||||||
|
if (oldInterfaceNameMap.containsKey(key)) { |
||||||
|
// 新有旧有,进行 UPDATE
|
||||||
|
Resource oldResource = oldInterfaceNameMap.get(key); |
||||||
|
|
||||||
|
if (isResourceChange(oldResource, newInterface)) { |
||||||
|
BeanUtils.copyProperties(newInterface, oldResource, "id"); |
||||||
|
resourceMapper.updateById(oldResource); |
||||||
|
} |
||||||
|
} else { |
||||||
|
// 新有旧无,进行 INSERT, 如果上级资源ID为空,说明是资源分组,资源分组已经在getSystemInterfaceMetas方法中插入到数据库
|
||||||
|
if (newInterface.getParentId() != null) { |
||||||
|
insertResourceList.add(newInterface); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
if (!deleteInterfaceIds.isEmpty()) { |
||||||
|
resourceMapper.deleteBatchIds(deleteInterfaceIds); |
||||||
|
} |
||||||
|
|
||||||
|
// 批量插入
|
||||||
|
if (!insertResourceList.isEmpty()) { |
||||||
|
resourceMapper.batchInsert(insertResourceList); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取系统当前所有接口的元信息 |
||||||
|
*/ |
||||||
|
public List<Resource> getSystemInterfaceMetas() { |
||||||
|
List<Class<?>> classes = findClassLocal(); |
||||||
|
List<Resource> resources = new ArrayList<>(); |
||||||
|
|
||||||
|
for (Class<?> aClass : classes) { |
||||||
|
RequestMapping baseRequestMapping = aClass.getAnnotation(RequestMapping.class); |
||||||
|
Api apiAnnotation = aClass.getAnnotation(Api.class); |
||||||
|
|
||||||
|
String baseUrl = ""; // Base URL
|
||||||
|
String groupName = ""; // 分组标识名
|
||||||
|
Resource parentResource = null; // 上级资源
|
||||||
|
|
||||||
|
if (baseRequestMapping != null) { |
||||||
|
String[] urls = baseRequestMapping.value(); |
||||||
|
if (urls.length > 0) { |
||||||
|
baseUrl = urls[0]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (apiAnnotation != null && !apiAnnotation.hidden()) { |
||||||
|
parentResource = new Resource(); |
||||||
|
groupName = aClass.getName(); |
||||||
|
|
||||||
|
parentResource.setLabel(apiAnnotation.description()); |
||||||
|
parentResource.setName(groupName); |
||||||
|
parentResource.setUrl(baseUrl); |
||||||
|
parentResource.setMethod("ALL"); |
||||||
|
|
||||||
|
Integer parentResourceId = resourceMapper.getIdByName(groupName); |
||||||
|
|
||||||
|
if (parentResourceId == null) { |
||||||
|
resourceMapper.insert(parentResource); |
||||||
|
} else { |
||||||
|
parentResource.setId(parentResourceId); |
||||||
|
resourceMapper.updateById(parentResource); |
||||||
|
} |
||||||
|
|
||||||
|
resources.add(parentResource); |
||||||
|
} |
||||||
|
|
||||||
|
// 获取该类的所有方法 (不包括父类)
|
||||||
|
Method[] methods = aClass.getDeclaredMethods(); |
||||||
|
|
||||||
|
for (Method method : methods) { |
||||||
|
if (!Modifier.isPublic(method.getModifiers())) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
ApiOperation operationAnnotation = method.getAnnotation(ApiOperation.class); |
||||||
|
// LoginUserScope scopeAnnotation = method.getAnnotation(LoginUserScope.class);
|
||||||
|
PermitAll permitAllAnnotation = method.getAnnotation(PermitAll.class); |
||||||
|
|
||||||
|
if (operationAnnotation == null || operationAnnotation.hidden()) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
List<String> interfaceMethods = new ArrayList<>(); |
||||||
|
String url = getInterfaceMethods(method, interfaceMethods); |
||||||
|
String interfaceName = operationAnnotation.value(); |
||||||
|
|
||||||
|
// 添加到接口信息列表
|
||||||
|
if (!"".equals(interfaceName) && url != null) { |
||||||
|
for (String requestMethod : interfaceMethods) { |
||||||
|
Resource resource = new Resource(); |
||||||
|
|
||||||
|
resource.setLabel(interfaceName); |
||||||
|
resource.setName(groupName + "." + method.getName()); |
||||||
|
resource.setMethod(requestMethod); |
||||||
|
resource.setUrl(baseUrl + url); |
||||||
|
|
||||||
|
if (permitAllAnnotation != null) { |
||||||
|
resource.setPermitAll(true); |
||||||
|
} |
||||||
|
|
||||||
|
// if (scopeAnnotation != null) {
|
||||||
|
// resource.setUserScope(true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (parentResource != null) { |
||||||
|
resource.setParentId(parentResource.getId()); |
||||||
|
} |
||||||
|
|
||||||
|
resources.add(resource); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return resources; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断资源元信息是否发生变更 |
||||||
|
*/ |
||||||
|
private boolean isResourceChange(Resource newResource, Resource oldResource) { |
||||||
|
if (newResource == null && oldResource == null) return true; |
||||||
|
if (newResource == null || oldResource == null) return false; |
||||||
|
|
||||||
|
return !Objects.equals(newResource.getName(), oldResource.getName()) || |
||||||
|
!Objects.equals(newResource.getParentId(), oldResource.getParentId()) || |
||||||
|
!Objects.equals(newResource.getLabel(), oldResource.getLabel()) || |
||||||
|
!Objects.equals(newResource.getMethod(), oldResource.getMethod()) || |
||||||
|
!Objects.equals(newResource.getPermitAll(), oldResource.getPermitAll()) || |
||||||
|
!Objects.equals(newResource.getUrl(), oldResource.getUrl()) || |
||||||
|
!Objects.equals(newResource.getUserScope(), oldResource.getUserScope()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取接口的请求方法和URL |
||||||
|
*/ |
||||||
|
private String getInterfaceMethods(Method method, List<String> interfaceMethods) { |
||||||
|
RequestMapping requestMapping = method.getDeclaredAnnotation(RequestMapping.class); |
||||||
|
GetMapping getMapping = method.getDeclaredAnnotation(GetMapping.class); |
||||||
|
PostMapping postMapping = method.getDeclaredAnnotation(PostMapping.class); |
||||||
|
PutMapping putMapping = method.getDeclaredAnnotation(PutMapping.class); |
||||||
|
DeleteMapping deleteMapping = method.getDeclaredAnnotation(DeleteMapping.class); |
||||||
|
|
||||||
|
String url = null; |
||||||
|
|
||||||
|
if (requestMapping != null) { |
||||||
|
String[] urls = requestMapping.value(); |
||||||
|
if (urls.length > 0) { |
||||||
|
url = urls[0]; |
||||||
|
} |
||||||
|
|
||||||
|
// 获取请求方法类型
|
||||||
|
RequestMethod[] requestMethods = requestMapping.method(); |
||||||
|
|
||||||
|
if (requestMethods.length > 0) { |
||||||
|
for (RequestMethod requestMethod : requestMethods) { |
||||||
|
interfaceMethods.add(requestMethod.name()); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if (getMapping != null) { |
||||||
|
String[] urls = getMapping.value(); |
||||||
|
if (urls.length > 0) { |
||||||
|
url = urls[0]; |
||||||
|
} |
||||||
|
interfaceMethods.add("GET"); |
||||||
|
} else if (postMapping != null) { |
||||||
|
String[] urls = postMapping.value(); |
||||||
|
if (urls.length > 0) { |
||||||
|
url = urls[0]; |
||||||
|
} |
||||||
|
interfaceMethods.add("POST"); |
||||||
|
} else if (putMapping != null) { |
||||||
|
String[] urls = putMapping.value(); |
||||||
|
if (urls.length > 0) { |
||||||
|
url = urls[0]; |
||||||
|
} |
||||||
|
interfaceMethods.add("PUT"); |
||||||
|
} else if (deleteMapping != null) { |
||||||
|
String[] urls = deleteMapping.value(); |
||||||
|
if (urls.length > 0) { |
||||||
|
url = urls[0]; |
||||||
|
} |
||||||
|
interfaceMethods.add("DELETE"); |
||||||
|
} |
||||||
|
|
||||||
|
return url; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 扫描指定包下的所有类 |
||||||
|
* @return 类对象列表 |
||||||
|
*/ |
||||||
|
private List<Class<?>> findClassLocal() { |
||||||
|
ClassLoader classLoader = SystemService.class.getClassLoader();// 类加载器
|
||||||
|
List<Class<?>> result = new ArrayList<>(); |
||||||
|
|
||||||
|
findClassLocalHelper(SCAN_PACKAGE, classLoader, result); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
private void findClassLocalHelper(String packageName, ClassLoader classLoader, List<Class<?>> result) { |
||||||
|
URI uri = null; |
||||||
|
|
||||||
|
try { |
||||||
|
uri = Objects.requireNonNull(classLoader.getResource(packageName.replace(".", "/"))).toURI(); |
||||||
|
} catch (URISyntaxException e) { |
||||||
|
throw new BusinessException(CodeMsg.SERVER_ERROR); |
||||||
|
} |
||||||
|
|
||||||
|
File file = new File(uri); |
||||||
|
file.listFiles(new FileFilter() { |
||||||
|
@Override |
||||||
|
public boolean accept(File pathname) { |
||||||
|
if (pathname.isDirectory()) { |
||||||
|
findClassLocalHelper(packageName + "." + pathname.getName(), classLoader, result); |
||||||
|
} |
||||||
|
|
||||||
|
if (pathname.getName().endsWith(".class")) { |
||||||
|
Class<?> clazz = null; |
||||||
|
|
||||||
|
try { |
||||||
|
clazz = classLoader.loadClass(packageName + "." + pathname.getName().replace(".class", "")); |
||||||
|
} catch (ClassNotFoundException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
|
||||||
|
result.add(clazz); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取资源列表 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public ListResultVO<Resource> getResourceList(Query query) { |
||||||
|
Page<Resource> pageRequest = query.toPageRequest(); |
||||||
|
QueryWrapper<Resource> queryWrapper = new QueryWrapper<>(); |
||||||
|
Page<Resource> resourcePage = resourceMapper.selectPage(pageRequest, queryWrapper); |
||||||
|
|
||||||
|
ListResultVO<Resource> listResultVO = new ListResultVO<>(); |
||||||
|
listResultVO.setList(resourcePage.getRecords()); |
||||||
|
listResultVO.setTotal(resourcePage.getTotal()); |
||||||
|
|
||||||
|
return listResultVO; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取资源树 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public List<Resource> getResourceTree() { |
||||||
|
return resourceMapper.getResourceTree(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取角色能够访问的资源列表 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public List<Integer> getRoleResourceIds(Integer roleId) { |
||||||
|
return resourceMapper.getRoleResourceIds(roleId); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取多个角色能够访问的资源列表 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public List<String> getRolesResourceList(List<String> roleNames) { |
||||||
|
//resourceMapper.getRolesResourceUrls(roleNames);
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取不受限的接口 |
||||||
|
*/ |
||||||
|
@Cacheable(cacheNames = "permitAllResourceList") |
||||||
|
@Override |
||||||
|
public List<Resource> getPermitAllResourceList() { |
||||||
|
return resourceMapper.getPermitAllResourceList(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 获取不受限的URL |
||||||
|
*/ |
||||||
|
@Cacheable(cacheNames = "permitAllResourceURLs") |
||||||
|
@Override |
||||||
|
public List<String> getPermitAllResourceURLs() { |
||||||
|
List<String> strings = new ArrayList<>(); |
||||||
|
strings.add("/api/user/current"); |
||||||
|
strings.add("/api/auth/logout"); |
||||||
|
//resourceMapper.getPermitAllResourceURLs();
|
||||||
|
return strings; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
package edu.ncst.award.service.impl.system; |
||||||
|
|
||||||
|
import edu.ncst.award.service.ISystemService; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
|
||||||
|
/** |
||||||
|
* 系统核心服务 |
||||||
|
*/ |
||||||
|
@Service |
||||||
|
public class SystemService implements ISystemService { |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,69 @@ |
|||||||
|
package edu.ncst.award.service.impl.system; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||||
|
import edu.ncst.award.mapper.system.UserMapper; |
||||||
|
import edu.ncst.award.model.system.Role; |
||||||
|
import edu.ncst.award.model.system.User; |
||||||
|
import edu.ncst.award.service.IUserService; |
||||||
|
import edu.ncst.award.vo.ListResultVO; |
||||||
|
import edu.ncst.award.vo.param.CurrentUserDTO; |
||||||
|
import edu.ncst.award.vo.param.query.UserQuery; |
||||||
|
import edu.ncst.award.vo.result.UserInfoVO; |
||||||
|
import org.springframework.security.core.userdetails.UserDetails; |
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService; |
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Service |
||||||
|
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService, UserDetailsService { |
||||||
|
@Override |
||||||
|
public UserInfoVO getUserInfo(UserQuery query) { |
||||||
|
UserInfoVO vo = new UserInfoVO(); |
||||||
|
vo.setAddress("address 1111111"); |
||||||
|
vo.setUsername("zhangsan"); |
||||||
|
vo.setFullName("张三"); |
||||||
|
return vo; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ListResultVO<User> getUserList(UserQuery query) { |
||||||
|
User user = new User(); |
||||||
|
user.setPassword("$2a$10$1cDN8G8bEpqkvOHZUxrAk.qvnZzP8IAuCWzezPXCdZYOL4jKVgEb."); |
||||||
|
user.setAddress("address 1111111"); |
||||||
|
user.setUsername("zhangsan"); |
||||||
|
user.setFullName("张三"); |
||||||
|
user.setEnable(true); |
||||||
|
ListResultVO listResultVO = new ListResultVO(); |
||||||
|
List list = new ArrayList<>(); |
||||||
|
list.add(user); |
||||||
|
listResultVO.setList(list); |
||||||
|
return listResultVO; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CurrentUserDTO getCurrentUser() { |
||||||
|
CurrentUserDTO user = new CurrentUserDTO(); |
||||||
|
user.setUsername("zhangsan"); |
||||||
|
user.setNickname("张三"); |
||||||
|
return user; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { |
||||||
|
User user = new User(); |
||||||
|
user.setPassword("$2a$10$1cDN8G8bEpqkvOHZUxrAk.qvnZzP8IAuCWzezPXCdZYOL4jKVgEb."); |
||||||
|
user.setAddress("address 1111111"); |
||||||
|
user.setUsername("zhangsan"); |
||||||
|
user.setFullName("张三"); |
||||||
|
Role role=new Role(); |
||||||
|
role.setName("NORMAL"); |
||||||
|
List<Role> roles = new ArrayList<>(); |
||||||
|
roles.add(role); |
||||||
|
user.setRoles(roles); |
||||||
|
user.setEnable(true); |
||||||
|
return user; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,79 @@ |
|||||||
|
package edu.ncst.award.utils; |
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass; |
||||||
|
|
||||||
|
import java.awt.*; |
||||||
|
import java.awt.image.BufferedImage; |
||||||
|
import java.util.Random; |
||||||
|
|
||||||
|
@UtilityClass |
||||||
|
public class CaptchaUtil { |
||||||
|
|
||||||
|
private int width = 200; |
||||||
|
private int height = 50; |
||||||
|
|
||||||
|
public BufferedImage createImage() { |
||||||
|
return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); |
||||||
|
} |
||||||
|
|
||||||
|
public String drawRandomText(BufferedImage verifyImg) { |
||||||
|
Graphics2D graphics = (Graphics2D) verifyImg.getGraphics(); |
||||||
|
|
||||||
|
graphics.setColor(Color.WHITE); |
||||||
|
graphics.fillRect(0, 0, width, height); |
||||||
|
Font font = new Font("Bitmap", Font.PLAIN, 54); |
||||||
|
graphics.setFont(font); |
||||||
|
|
||||||
|
String baseNumLetter = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; |
||||||
|
StringBuilder stringBuffer = new StringBuilder(); |
||||||
|
|
||||||
|
int x = 10; |
||||||
|
String ch = ""; |
||||||
|
Random random = new Random(); |
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) { |
||||||
|
graphics.setColor(getRandomColor()); |
||||||
|
|
||||||
|
// 设置字体旋转角度(控制小于30度)
|
||||||
|
int degree = random.nextInt() % 30; |
||||||
|
int dot = random.nextInt(baseNumLetter.length()); |
||||||
|
|
||||||
|
ch = baseNumLetter.charAt(dot) + ""; |
||||||
|
stringBuffer.append(ch); |
||||||
|
|
||||||
|
// 正向旋转
|
||||||
|
graphics.rotate(degree * Math.PI / 180, x, 45); |
||||||
|
graphics.drawString(ch, x, 45); |
||||||
|
|
||||||
|
// 反向旋转
|
||||||
|
graphics.rotate(-degree * Math.PI / 180, x, 45); |
||||||
|
x += 48; |
||||||
|
} |
||||||
|
|
||||||
|
// 绘制干扰线
|
||||||
|
for (int i = 0; i < 6; i++) { |
||||||
|
graphics.setColor(getRandomColor()); |
||||||
|
graphics.drawLine(random.nextInt(width), random.nextInt(height), random.nextInt(width), random.nextInt(height)); |
||||||
|
} |
||||||
|
|
||||||
|
// 绘制噪点
|
||||||
|
for (int i = 0; i < 30; i++) { |
||||||
|
int x1 = random.nextInt(width); |
||||||
|
int y1 = random.nextInt(height); |
||||||
|
|
||||||
|
graphics.setColor(getRandomColor()); |
||||||
|
graphics.fillRect(x1, y1, 2, 1); |
||||||
|
} |
||||||
|
|
||||||
|
return stringBuffer.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* 取随机颜色 |
||||||
|
*/ |
||||||
|
private Color getRandomColor() { |
||||||
|
Random random = new Random(); |
||||||
|
return new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
package edu.ncst.award.utils; |
||||||
|
|
||||||
|
import javax.servlet.http.Cookie; |
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
|
||||||
|
public class CookieUtils { |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取指定的 Cookie |
||||||
|
*/ |
||||||
|
public static Cookie getCookie(HttpServletRequest request, String name) { |
||||||
|
if (name == null || "".equalsIgnoreCase(name)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
Cookie[] cookies = request.getCookies(); |
||||||
|
|
||||||
|
if (cookies != null) { |
||||||
|
for (Cookie cookie : cookies) { |
||||||
|
if (cookie.getName() != null && name.equals(cookie.getName())) { |
||||||
|
return cookie; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,91 @@ |
|||||||
|
package edu.ncst.award.utils; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||||
|
import edu.ncst.award.exception.CodeMsg; |
||||||
|
import edu.ncst.award.vo.ResultVO; |
||||||
|
import org.springframework.http.ResponseEntity; |
||||||
|
import org.springframework.validation.BindingResult; |
||||||
|
import org.springframework.validation.FieldError; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStream; |
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 消息类响应工具类 |
||||||
|
*/ |
||||||
|
public class ResponseUtils { |
||||||
|
|
||||||
|
// 成功
|
||||||
|
// 成功
|
||||||
|
public static <T> ResponseEntity<?> success(String msg, T data) { |
||||||
|
ResultVO<T> resultVO = new ResultVO<>(); |
||||||
|
resultVO.setCode(0); |
||||||
|
resultVO.setMsg(msg); |
||||||
|
resultVO.setData(data); |
||||||
|
|
||||||
|
return ResponseEntity.ok(resultVO); |
||||||
|
} |
||||||
|
|
||||||
|
public static ResponseEntity<?> success(Object data) { |
||||||
|
return success("success", data); |
||||||
|
} |
||||||
|
|
||||||
|
public static ResponseEntity<?> success(String msg) { |
||||||
|
return success(msg, null); |
||||||
|
} |
||||||
|
|
||||||
|
public static ResponseEntity<?> success() { |
||||||
|
return success("success", null); |
||||||
|
} |
||||||
|
|
||||||
|
// 失败
|
||||||
|
public static <T> ResponseEntity<?> error(Integer code, String msg) { |
||||||
|
ResultVO<T> resultVO = new ResultVO<>(); |
||||||
|
resultVO.setCode(code); |
||||||
|
resultVO.setMsg(msg); |
||||||
|
return ResponseEntity.ok().body(resultVO); |
||||||
|
} |
||||||
|
|
||||||
|
public static ResponseEntity<?> error(CodeMsg codeMsg) { |
||||||
|
return error(codeMsg.getCode(), codeMsg.getMsg()); |
||||||
|
} |
||||||
|
|
||||||
|
// 输出字段错误信息
|
||||||
|
public static <T> ResponseEntity<?> fieldError(BindingResult bindingResult) { |
||||||
|
ResultVO<T> resultVO = new ResultVO<>(); |
||||||
|
String msg = "未知字段错误"; |
||||||
|
|
||||||
|
if (bindingResult.hasFieldErrors()) { |
||||||
|
List<FieldError> fieldErrors = bindingResult.getFieldErrors(); |
||||||
|
int count = bindingResult.getFieldErrorCount(); |
||||||
|
|
||||||
|
if (count > 0) { |
||||||
|
msg = fieldErrors.get(0).getDefaultMessage(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
resultVO.setMsg(msg); |
||||||
|
|
||||||
|
return ResponseEntity.ok().body(resultVO); |
||||||
|
} |
||||||
|
|
||||||
|
// 调用
|
||||||
|
public static void output(HttpServletResponse response, Object data) { |
||||||
|
try(OutputStream out = response.getOutputStream()) { |
||||||
|
response.setContentType("application/json;charset=utf-8"); |
||||||
|
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper(); |
||||||
|
String s = objectMapper.writeValueAsString(data); |
||||||
|
PrintWriter writer = new PrintWriter(out); |
||||||
|
writer.print(s); |
||||||
|
writer.flush(); |
||||||
|
writer.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,55 @@ |
|||||||
|
package edu.ncst.award.utils; |
||||||
|
|
||||||
|
import edu.ncst.award.exception.CodeMsg; |
||||||
|
import edu.ncst.award.vo.ResultVO; |
||||||
|
|
||||||
|
public class ResultUtils { |
||||||
|
|
||||||
|
// 成功
|
||||||
|
public static <T> ResultVO<T> success(String msg, T data) { |
||||||
|
ResultVO<T> resultVO = new ResultVO<>(); |
||||||
|
resultVO.setCode(0); |
||||||
|
resultVO.setMsg(msg); |
||||||
|
resultVO.setData(data); |
||||||
|
|
||||||
|
return resultVO; |
||||||
|
} |
||||||
|
public static <T> ResultVO<T> success(String msg) { |
||||||
|
ResultVO<T> resultVO = new ResultVO<>(); |
||||||
|
resultVO.setCode(0); |
||||||
|
resultVO.setMsg(msg); |
||||||
|
resultVO.setData(null); |
||||||
|
|
||||||
|
return resultVO; |
||||||
|
} |
||||||
|
|
||||||
|
// 成功
|
||||||
|
public static <T> ResultVO<T> success(T data) { |
||||||
|
ResultVO<T> resultVO = new ResultVO<>(); |
||||||
|
resultVO.setCode(0); |
||||||
|
resultVO.setMsg("success"); |
||||||
|
resultVO.setData(data); |
||||||
|
|
||||||
|
return resultVO; |
||||||
|
} |
||||||
|
// 错误
|
||||||
|
public static <T> ResultVO<T> error(CodeMsg codeMsg) { |
||||||
|
ResultVO<T> resultVO = new ResultVO<>(); |
||||||
|
resultVO.setCode(codeMsg.getCode()); |
||||||
|
resultVO.setMsg(codeMsg.getMsg()); |
||||||
|
resultVO.setData(null); |
||||||
|
|
||||||
|
return resultVO; |
||||||
|
} |
||||||
|
|
||||||
|
// 错误
|
||||||
|
public static <T> ResultVO<T> error(String codeMsg) { |
||||||
|
ResultVO<T> resultVO = new ResultVO<>(); |
||||||
|
resultVO.setCode(500); |
||||||
|
resultVO.setMsg(codeMsg); |
||||||
|
resultVO.setData(null); |
||||||
|
|
||||||
|
return resultVO; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,55 @@ |
|||||||
|
package edu.ncst.award.utils; |
||||||
|
|
||||||
|
import edu.ncst.award.exception.BusinessException; |
||||||
|
import edu.ncst.award.exception.CodeMsg; |
||||||
|
import edu.ncst.award.service.IUserService; |
||||||
|
import edu.ncst.award.vo.result.UserInfoVO; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.security.core.context.SecurityContext; |
||||||
|
import org.springframework.security.core.context.SecurityContextHolder; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
import org.springframework.web.context.request.RequestContextHolder; |
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
/** |
||||||
|
* 会话工具类 |
||||||
|
*/ |
||||||
|
@Component |
||||||
|
public class SessionUtils { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private IUserService userService; |
||||||
|
|
||||||
|
// 获取当前用户
|
||||||
|
public static UserInfoVO getCurrentUser() { |
||||||
|
SecurityContext context = SecurityContextHolder.getContext(); |
||||||
|
|
||||||
|
if (context == null || context.getAuthentication() == null || context.getAuthentication().getDetails() == null) { |
||||||
|
throw new BusinessException(CodeMsg.NOT_GET_CURRENT_USER_INFO); |
||||||
|
} |
||||||
|
|
||||||
|
return (UserInfoVO) context.getAuthentication().getDetails(); |
||||||
|
} |
||||||
|
|
||||||
|
// 获取当前用户标识
|
||||||
|
public static Object getCurrentPrincipal() { |
||||||
|
SecurityContext context = SecurityContextHolder.getContext(); |
||||||
|
|
||||||
|
return context.getAuthentication().getPrincipal(); |
||||||
|
} |
||||||
|
|
||||||
|
// 获取当前 Request
|
||||||
|
public static HttpServletRequest getRequest() { |
||||||
|
return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); |
||||||
|
} |
||||||
|
|
||||||
|
// 获取当前 Response
|
||||||
|
public static HttpServletResponse getResponse() { |
||||||
|
return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
package edu.ncst.award.utils; |
||||||
|
|
||||||
|
public class URLUtils { |
||||||
|
|
||||||
|
public static String removeQueryString(String url) { |
||||||
|
int a = url.indexOf("?"); |
||||||
|
int b = url.indexOf("#"); |
||||||
|
int deleteStart = 0; |
||||||
|
|
||||||
|
if (a == -1 && b == -1) { |
||||||
|
deleteStart = -1; |
||||||
|
} else if (a != -1 && b != -1) { |
||||||
|
deleteStart = Math.min(a, b); |
||||||
|
} else if (a == -1) { |
||||||
|
deleteStart = b; |
||||||
|
} else { |
||||||
|
deleteStart = a; |
||||||
|
} |
||||||
|
|
||||||
|
if (deleteStart != -1) { |
||||||
|
return url.substring(0, deleteStart); |
||||||
|
} else { |
||||||
|
return url; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,30 @@ |
|||||||
|
package edu.ncst.award.vo; |
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.EqualsAndHashCode; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@ApiModel(value = "ListResultVO", description = "分页列表数据") |
||||||
|
@Data |
||||||
|
@EqualsAndHashCode(callSuper = false) |
||||||
|
public class ListResultVO<T> implements Serializable { |
||||||
|
|
||||||
|
private static final long serialVersionUID = -8896090499552990782L; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "列表数据") |
||||||
|
private List<T> list; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "记录总数") |
||||||
|
private Long total; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "每页记录数") |
||||||
|
private Long pageSize; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "当前页码") |
||||||
|
private Long current; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
package edu.ncst.award.vo; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.StringUtils; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
|
||||||
|
@ApiModel(description = "列表查询参数") |
||||||
|
@Data |
||||||
|
public class Query implements Serializable { |
||||||
|
|
||||||
|
private static final long serialVersionUID = 4617734867110140874L; |
||||||
|
|
||||||
|
// 排序方式
|
||||||
|
private String sorter; |
||||||
|
|
||||||
|
// 页码
|
||||||
|
private Integer current = 1; |
||||||
|
|
||||||
|
// 每页记录数
|
||||||
|
private Integer pageSize = 10; |
||||||
|
|
||||||
|
// 转换为 Pageable
|
||||||
|
public <T> Page<T> toPageRequest() { |
||||||
|
return new Page<>(current, pageSize); |
||||||
|
} |
||||||
|
|
||||||
|
// 转换为 Sorter 对象
|
||||||
|
public Sorter toSorter() { |
||||||
|
if (sorter == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
if (sorter.endsWith("_ascend")) { |
||||||
|
String key = sorter.replace("_ascend", ""); |
||||||
|
String column = StringUtils.camelToUnderline(key); |
||||||
|
Sorter _sorter = new Sorter(); |
||||||
|
|
||||||
|
_sorter.setColumn(column); |
||||||
|
_sorter.setKey(key); |
||||||
|
_sorter.setIsAsc(true); |
||||||
|
|
||||||
|
return _sorter; |
||||||
|
} else if (sorter.endsWith("_descend")){ |
||||||
|
String key = sorter.replace("_descend", ""); |
||||||
|
String column = StringUtils.camelToUnderline(key); |
||||||
|
Sorter _sorter = new Sorter(); |
||||||
|
|
||||||
|
_sorter.setColumn(column); |
||||||
|
_sorter.setKey(key); |
||||||
|
_sorter.setIsAsc(false); |
||||||
|
|
||||||
|
return _sorter; |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
package edu.ncst.award.vo; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
/** |
||||||
|
* 统一的接口返回数据格式 |
||||||
|
*/ |
||||||
|
@ApiModel(value = "ResultVO", description = "返回信息") |
||||||
|
@Data |
||||||
|
public class ResultVO<T> { |
||||||
|
private Integer code=0; |
||||||
|
private String msg="success"; |
||||||
|
// 数据
|
||||||
|
@ApiModelProperty(value = "数据", dataType = "Object",name = "data") |
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY) |
||||||
|
private T data; |
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
package edu.ncst.award.vo; |
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
@ApiModel(description = "排序器") |
||||||
|
@Data |
||||||
|
public class Sorter { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "排序关键字") |
||||||
|
private String key; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否为升序") |
||||||
|
private Boolean isAsc = true; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "排序列名", notes = "根据该列进行排序") |
||||||
|
private String column; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,41 @@ |
|||||||
|
package edu.ncst.award.vo.param; |
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
@ApiModel(description = "当前用户信息") |
||||||
|
@Data |
||||||
|
public class CurrentUserDTO { |
||||||
|
|
||||||
|
@ApiModelProperty("头像链接") |
||||||
|
private String avatar; |
||||||
|
|
||||||
|
@ApiModelProperty("姓名") |
||||||
|
private String username; |
||||||
|
|
||||||
|
@ApiModelProperty("昵称") |
||||||
|
private String nickname; |
||||||
|
|
||||||
|
@ApiModelProperty("全名") |
||||||
|
private String fullname; |
||||||
|
|
||||||
|
@ApiModelProperty("角色") |
||||||
|
private String role; |
||||||
|
|
||||||
|
@ApiModelProperty("头衔") |
||||||
|
private String title; |
||||||
|
|
||||||
|
@ApiModelProperty("用户组") |
||||||
|
private String group; |
||||||
|
|
||||||
|
@ApiModelProperty("用户ID") |
||||||
|
private Integer userId; |
||||||
|
|
||||||
|
@ApiModelProperty("所在单位ID") |
||||||
|
private Integer unitId; |
||||||
|
|
||||||
|
@ApiModelProperty("所在单位名称") |
||||||
|
private Integer unitName; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
package edu.ncst.award.vo.param; |
||||||
|
|
||||||
|
import edu.ncst.award.model.system.Menu; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Data |
||||||
|
public class ResetMenuDTO { |
||||||
|
|
||||||
|
private List<Menu> menus; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
package edu.ncst.award.vo.param; |
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
@ApiModel(description = "用户登录表单") |
||||||
|
@Data |
||||||
|
public class UserLoginDTO { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "登录方式", notes = "可选:MOBILE / PASSWORD") |
||||||
|
private String type; |
||||||
|
|
||||||
|
@ApiModelProperty("用户名") |
||||||
|
private String username; |
||||||
|
|
||||||
|
@ApiModelProperty("密码") |
||||||
|
private String password; |
||||||
|
|
||||||
|
@ApiModelProperty("手机号") |
||||||
|
private String phoneNumber; |
||||||
|
|
||||||
|
@ApiModelProperty("手机验证码") |
||||||
|
private String mobileCaptcha; |
||||||
|
|
||||||
|
@ApiModelProperty("验证码") |
||||||
|
private String loginCaptcha; |
||||||
|
|
||||||
|
@ApiModelProperty("临时TOKEN") |
||||||
|
private String tempToken; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
package edu.ncst.award.vo.param.query; |
||||||
|
|
||||||
|
import edu.ncst.award.vo.Query; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
import org.springframework.format.annotation.DateTimeFormat; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
@ApiModel(description = "登录日志查询对象") |
||||||
|
@Data |
||||||
|
public class LoginLogQuery extends Query { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "关键字", notes = "用于对用户名、用户ID、用户姓名进行模糊查询") |
||||||
|
private String keyword; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "登录类型") |
||||||
|
private String type; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "开始日期") |
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
||||||
|
private Date startTime; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "结束日期") |
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
||||||
|
private Date endTime; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
package edu.ncst.award.vo.param.query; |
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
@Data |
||||||
|
@ApiModel(description = "用户信息查询") |
||||||
|
public class UserQuery { |
||||||
|
@ApiModelProperty("手机号") |
||||||
|
private String phone; |
||||||
|
@ApiModelProperty("姓名") |
||||||
|
private String name; |
||||||
|
} |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
package edu.ncst.award.vo.result; |
||||||
|
|
||||||
|
import edu.ncst.award.model.system.Role; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Data |
||||||
|
@ApiModel(description = "用户信息") |
||||||
|
public class UserInfoVO { |
||||||
|
@ApiModelProperty(value = "用户id") |
||||||
|
private Integer id; |
||||||
|
@ApiModelProperty(value = "用户名") |
||||||
|
private String username; |
||||||
|
@ApiModelProperty(value = "用户真实名") |
||||||
|
private String fullName; |
||||||
|
@ApiModelProperty(value = "地址信息") |
||||||
|
private String address; |
||||||
|
@ApiModelProperty(value = "手机号") |
||||||
|
private String phone; |
||||||
|
@ApiModelProperty(value = "所属单位") |
||||||
|
private Integer unitId; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "角色列表") |
||||||
|
private List<Role> roles; |
||||||
|
@ApiModelProperty(value = "登录方式") |
||||||
|
private String loginType; |
||||||
|
@ApiModelProperty(value = "是否启用") |
||||||
|
private boolean enable; |
||||||
|
} |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
spring: |
||||||
|
datasource: |
||||||
|
url: jdbc:mysql://localhost:3307/award_sys?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai |
||||||
|
username: smart |
||||||
|
password: smart3306 |
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver |
||||||
|
hikari: |
||||||
|
max-lifetime: 300000 |
||||||
|
devtools: |
||||||
|
restart: |
||||||
|
enabled: true #设置开启热部署 |
||||||
|
additional-paths: src/main/java #重启目录 |
||||||
|
exclude: WEB-INF/** |
||||||
|
application: |
||||||
|
name: springfox-swagger |
||||||
|
mvc: |
||||||
|
pathmatch: |
||||||
|
matching-strategy: ant_path_matcher |
||||||
|
async: |
||||||
|
request-timeout: 120000 #设置restful的api超时时间为60秒 |
||||||
|
server: |
||||||
|
port: 8083 |
||||||
|
tomcat: |
||||||
|
connection-timeout: 300000 |
||||||
|
servlet: |
||||||
|
encoding: |
||||||
|
charset: utf-8 |
||||||
|
#mybatis: |
||||||
|
# mapper-locations: classpath:mapping/*.xml |
||||||
|
# type-aliases-package: edu.ncst.award.model |
||||||
|
|
||||||
|
swagger: |
||||||
|
enable: true |
||||||
|
title: API文档 |
||||||
|
description: 科技奖申报系统API文档 |
||||||
|
base-package: edu.ncst.award.controller |
||||||
|
exclude-path: /error/** |
||||||
|
termsOfServiceUrl: https://meander.net.cn |
||||||
|
contact: |
||||||
|
name: sage |
||||||
|
url: www.meander.net.cn |
||||||
|
email: sage@meander.net.cn |
||||||
|
|
||||||
|
logging: |
||||||
|
config: classpath:logback.xml |
||||||
|
level: |
||||||
|
edu.ncst.equmanager: info |
||||||
|
org.springframework: info |
||||||
|
java.sql: info |
||||||
|
# |
||||||
|
## MyBatis Plus |
||||||
|
#mybatis-plus: |
||||||
|
# global-config: |
||||||
|
# db-config: |
||||||
|
# logic-delete-field: deleteAt |
||||||
|
# logic-delete-value: now() |
||||||
|
# logic-not-delete-value: null |
||||||
|
# banner: false # 关闭控制台的 LOGO |
||||||
|
# mapper-locations: classpath*:/mapping/**/*.xml |
||||||
|
# configuration: |
||||||
|
# log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl |
||||||
@ -0,0 +1,57 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<configuration debug="false"> |
||||||
|
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径--> |
||||||
|
<property name="LOG_PATH" value="D:/log"/> |
||||||
|
<!-- 定义日志格式 --> |
||||||
|
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS,CTT} [%-5level] [%thread] [%-30.30logger{30}] %msg%n"/> |
||||||
|
<property name="MAX_FILE_SIZE" value="10MB"/> |
||||||
|
<property name="MAX_HISTORY" value="30"/> |
||||||
|
<property name="CONTEXT_NAME" value="sbgl"/> |
||||||
|
|
||||||
|
<contextName>${CONTEXT_NAME}</contextName> |
||||||
|
<!-- 彩色日志 --> |
||||||
|
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" /> |
||||||
|
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" /> |
||||||
|
<!-- 控制台输出 --> |
||||||
|
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> |
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> |
||||||
|
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> |
||||||
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS,CTT} [%thread] %-5level %logger{50} - %msg%n</pattern> |
||||||
|
</encoder> |
||||||
|
</appender> |
||||||
|
<!-- 按照每天生成日志文件 --> |
||||||
|
<!-- 控制台日志样式 --> |
||||||
|
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS,CTT}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr([%15.15t]){faint} [%X{requestId}] %clr(%-40.40logger{39}){cyan} [%L] %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> |
||||||
|
<!-- 文件日志样式 --> |
||||||
|
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS,CTT}} ${LOG_LEVEL_PATTERN:-%5p} [%t] [%X{requestId}] %-40.40logger{39} %L : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> |
||||||
|
|
||||||
|
<!-- 禁用logback自身日志输出 --> |
||||||
|
<statusListener class="ch.qos.logback.core.status.NopStatusListener" /> |
||||||
|
|
||||||
|
<!-- 控制台 --> |
||||||
|
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> |
||||||
|
<encoder> |
||||||
|
<pattern>${CONSOLE_LOG_PATTERN}</pattern> |
||||||
|
</encoder> |
||||||
|
</appender> |
||||||
|
|
||||||
|
<!-- 运行日志文件 --> |
||||||
|
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
||||||
|
<encoder> |
||||||
|
<pattern>${FILE_LOG_PATTERN}</pattern> |
||||||
|
</encoder> |
||||||
|
<file>${LOG_PATH}/sbgl.log</file> |
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> |
||||||
|
<fileNamePattern>${LOG_PATH}/sbgl-%d{yyyy-MM-dd}.%i.log</fileNamePattern> |
||||||
|
<maxFileSize>${MAX_FILE_SIZE}</maxFileSize> |
||||||
|
<maxHistory>${MAX_HISTORY}</maxHistory> |
||||||
|
</rollingPolicy> |
||||||
|
</appender> |
||||||
|
<!-- |
||||||
|
日志输出级别 |
||||||
|
--> |
||||||
|
<root level="INFO"> |
||||||
|
<appender-ref ref="CONSOLE"/> |
||||||
|
<appender-ref ref="FILE"/> |
||||||
|
</root> |
||||||
|
</configuration> |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
package edu.ncst.award; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.springframework.boot.test.context.SpringBootTest; |
||||||
|
|
||||||
|
@SpringBootTest |
||||||
|
class AwardApplicationTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
void contextLoads() { |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
# http://editorconfig.org |
||||||
|
root = true |
||||||
|
|
||||||
|
[*] |
||||||
|
indent_style = space |
||||||
|
indent_size = 2 |
||||||
|
end_of_line = lf |
||||||
|
charset = utf-8 |
||||||
|
trim_trailing_whitespace = true |
||||||
|
insert_final_newline = true |
||||||
|
|
||||||
|
[*.md] |
||||||
|
trim_trailing_whitespace = false |
||||||
|
|
||||||
|
[Makefile] |
||||||
|
indent_style = tab |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
/lambda/ |
||||||
|
/scripts |
||||||
|
/config |
||||||
|
.history |
||||||
|
public |
||||||
|
dist |
||||||
|
.umi |
||||||
|
mock |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
module.exports = { |
||||||
|
extends: [require.resolve('@umijs/fabric/dist/eslint')], |
||||||
|
globals: { |
||||||
|
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true, |
||||||
|
page: true, |
||||||
|
REACT_APP_ENV: true, |
||||||
|
}, |
||||||
|
}; |
||||||
@ -0,0 +1,42 @@ |
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. |
||||||
|
|
||||||
|
# dependencies |
||||||
|
**/node_modules |
||||||
|
# roadhog-api-doc ignore |
||||||
|
/src/utils/request-temp.js |
||||||
|
_roadhog-api-doc |
||||||
|
|
||||||
|
# production |
||||||
|
/dist |
||||||
|
/.vscode |
||||||
|
|
||||||
|
# misc |
||||||
|
.DS_Store |
||||||
|
npm-debug.log* |
||||||
|
yarn-error.log |
||||||
|
|
||||||
|
/coverage |
||||||
|
.idea |
||||||
|
yarn.lock |
||||||
|
# package-lock.json |
||||||
|
*bak |
||||||
|
.vscode |
||||||
|
|
||||||
|
# visual studio code |
||||||
|
.history |
||||||
|
*.log |
||||||
|
functions/* |
||||||
|
.temp/** |
||||||
|
|
||||||
|
# umi |
||||||
|
.umi |
||||||
|
.umi-production |
||||||
|
|
||||||
|
# screenshot |
||||||
|
screenshot |
||||||
|
.firebase |
||||||
|
.eslintcache |
||||||
|
|
||||||
|
build |
||||||
|
|
||||||
|
.mocks/ |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
**/*.svg |
||||||
|
package.json |
||||||
|
.umi |
||||||
|
.umi-production |
||||||
|
/dist |
||||||
|
.dockerignore |
||||||
|
.DS_Store |
||||||
|
.eslintignore |
||||||
|
*.png |
||||||
|
*.toml |
||||||
|
docker |
||||||
|
.editorconfig |
||||||
|
Dockerfile* |
||||||
|
.gitignore |
||||||
|
.prettierignore |
||||||
|
LICENSE |
||||||
|
.eslintcache |
||||||
|
*.lock |
||||||
|
yarn-error.log |
||||||
|
.history |
||||||
|
CNAME |
||||||
|
/build |
||||||
|
/public |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
const fabric = require('@umijs/fabric'); |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
...fabric.prettier, |
||||||
|
}; |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
const fabric = require('@umijs/fabric'); |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
...fabric.stylelint, |
||||||
|
}; |
||||||
@ -0,0 +1,57 @@ |
|||||||
|
# Award Manage |
||||||
|
|
||||||
|
This project is initialized with [Award Manage](https://pro.ant.design). Follow is the quick guide for how to use. |
||||||
|
|
||||||
|
## Environment Prepare |
||||||
|
|
||||||
|
Install `node_modules`: |
||||||
|
|
||||||
|
```bash |
||||||
|
npm install |
||||||
|
``` |
||||||
|
|
||||||
|
or |
||||||
|
|
||||||
|
```bash |
||||||
|
yarn |
||||||
|
``` |
||||||
|
|
||||||
|
## Provided Scripts |
||||||
|
|
||||||
|
Award Manage provides some useful script to help you quick start and build with web project, code style check and test. |
||||||
|
|
||||||
|
Scripts provided in `package.json`. It's safe to modify or add additional script: |
||||||
|
|
||||||
|
### Start project |
||||||
|
|
||||||
|
```bash |
||||||
|
npm start |
||||||
|
``` |
||||||
|
|
||||||
|
### Build project |
||||||
|
|
||||||
|
```bash |
||||||
|
npm run build |
||||||
|
``` |
||||||
|
|
||||||
|
### Check code style |
||||||
|
|
||||||
|
```bash |
||||||
|
npm run lint |
||||||
|
``` |
||||||
|
|
||||||
|
You can also use script to auto fix some lint error: |
||||||
|
|
||||||
|
```bash |
||||||
|
npm run lint:fix |
||||||
|
``` |
||||||
|
|
||||||
|
### Test code |
||||||
|
|
||||||
|
```bash |
||||||
|
npm test |
||||||
|
``` |
||||||
|
|
||||||
|
## More |
||||||
|
|
||||||
|
You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro). |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
// https://umijs.org/config/
|
||||||
|
import { defineConfig } from 'umi'; |
||||||
|
|
||||||
|
export default defineConfig({ |
||||||
|
plugins: [ |
||||||
|
// https://github.com/zthxxx/react-dev-inspector
|
||||||
|
'react-dev-inspector/plugins/umi/react-inspector', |
||||||
|
], |
||||||
|
// https://github.com/zthxxx/react-dev-inspector#inspector-loader-props
|
||||||
|
inspectorConfig: { |
||||||
|
exclude: [], |
||||||
|
babelPlugins: [], |
||||||
|
babelOptions: {}, |
||||||
|
}, |
||||||
|
}); |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
// https://umijs.org/config/
|
||||||
|
import { defineConfig } from 'umi'; |
||||||
|
import { join } from 'path'; |
||||||
|
|
||||||
|
import defaultSettings from './defaultSettings'; |
||||||
|
import proxy from './proxy'; |
||||||
|
import routes from './routes'; |
||||||
|
|
||||||
|
const { REACT_APP_ENV } = process.env; |
||||||
|
|
||||||
|
export default defineConfig({ |
||||||
|
hash: true, |
||||||
|
antd: {}, |
||||||
|
dva: { |
||||||
|
hmr: true, |
||||||
|
}, |
||||||
|
|
||||||
|
// https://umijs.org/zh-CN/plugins/plugin-locale
|
||||||
|
locale: { |
||||||
|
// default zh-CN
|
||||||
|
default: 'zh-CN', |
||||||
|
antd: true, |
||||||
|
// default true, when it is true, will use `navigator.language` overwrite default
|
||||||
|
baseNavigator: false, |
||||||
|
}, |
||||||
|
dynamicImport: { |
||||||
|
loading: '@ant-design/pro-layout/es/PageLoading', |
||||||
|
}, |
||||||
|
targets: { |
||||||
|
ie: 11, |
||||||
|
}, |
||||||
|
// umi routes: https://umijs.org/docs/routing
|
||||||
|
routes, |
||||||
|
// Theme for antd: https://ant.design/docs/react/customize-theme-cn
|
||||||
|
theme: { |
||||||
|
'primary-color': defaultSettings.primaryColor, |
||||||
|
}, |
||||||
|
// esbuild is father build tools
|
||||||
|
// https://umijs.org/plugins/plugin-esbuild
|
||||||
|
esbuild: {}, |
||||||
|
title: false, |
||||||
|
ignoreMomentLocale: true, |
||||||
|
proxy: proxy[REACT_APP_ENV || 'dev'], |
||||||
|
manifest: { |
||||||
|
basePath: '/', |
||||||
|
}, |
||||||
|
// Fast Refresh 热更新
|
||||||
|
fastRefresh: {}, |
||||||
|
openAPI: [ |
||||||
|
{ |
||||||
|
requestLibPath: "import { request } from 'umi'", |
||||||
|
// 或者使用在线的版本
|
||||||
|
// schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json"
|
||||||
|
schemaPath: join(__dirname, 'oneapi.json'), |
||||||
|
mock: false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
requestLibPath: "import { request } from 'umi'", |
||||||
|
schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json', |
||||||
|
projectName: 'swagger', |
||||||
|
}, |
||||||
|
], |
||||||
|
nodeModulesTransform: { type: 'none' }, |
||||||
|
mfsu: {}, |
||||||
|
webpack5: {}, |
||||||
|
exportStatic: {}, |
||||||
|
}); |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
import { MenuTheme } from 'antd/es/menu/MenuContext'; |
||||||
|
|
||||||
|
export type ContentWidth = 'Fluid' | 'Fixed'; |
||||||
|
|
||||||
|
export interface DefaultSettings { |
||||||
|
/** |
||||||
|
* theme for nav menu |
||||||
|
*/ |
||||||
|
navTheme: MenuTheme; |
||||||
|
/** |
||||||
|
* primary color of ant design |
||||||
|
*/ |
||||||
|
primaryColor: string; |
||||||
|
/** |
||||||
|
* nav menu position: `sidemenu` or `topmenu` |
||||||
|
*/ |
||||||
|
layout: 'sidemenu' | 'topmenu'; |
||||||
|
/** |
||||||
|
* layout of content: `Fluid` or `Fixed`, only works when layout is topmenu |
||||||
|
*/ |
||||||
|
contentWidth: ContentWidth; |
||||||
|
/** |
||||||
|
* sticky header |
||||||
|
*/ |
||||||
|
fixedHeader: boolean; |
||||||
|
/** |
||||||
|
* auto hide header |
||||||
|
*/ |
||||||
|
autoHideHeader: boolean; |
||||||
|
/** |
||||||
|
* sticky siderbar |
||||||
|
*/ |
||||||
|
fixSiderbar: boolean; |
||||||
|
menu: { locale: boolean }; |
||||||
|
title: string; |
||||||
|
pwa: boolean; |
||||||
|
// Your custom iconfont Symbol script Url
|
||||||
|
// eg://at.alicdn.com/t/font_1039637_btcrd5co4w.js
|
||||||
|
// 注意:如果需要图标多色,Iconfont 图标项目里要进行批量去色处理
|
||||||
|
// Usage: https://github.com/ant-design/ant-design-pro/pull/3517
|
||||||
|
iconfontUrl: string; |
||||||
|
colorWeak: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
export default { |
||||||
|
navTheme: 'light', |
||||||
|
// 拂晓蓝
|
||||||
|
primaryColor: '#1890ff', |
||||||
|
layout: 'sidemenu', |
||||||
|
contentWidth: 'Fluid', |
||||||
|
fixedHeader: false, |
||||||
|
autoHideHeader: false, |
||||||
|
fixSiderbar: true, |
||||||
|
colorWeak: false, |
||||||
|
menu: { |
||||||
|
locale: true, |
||||||
|
}, |
||||||
|
title: '科技奖评审系统', |
||||||
|
pwa: false, |
||||||
|
iconfontUrl: '', |
||||||
|
} as DefaultSettings; |
||||||
@ -0,0 +1,593 @@ |
|||||||
|
{ |
||||||
|
"openapi": "3.0.1", |
||||||
|
"info": { |
||||||
|
"title": "Award Manage", |
||||||
|
"version": "1.0.0" |
||||||
|
}, |
||||||
|
"servers": [ |
||||||
|
{ |
||||||
|
"url": "http://localhost:8000/" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"url": "https://localhost:8000/" |
||||||
|
} |
||||||
|
], |
||||||
|
"paths": { |
||||||
|
"/api/currentUser": { |
||||||
|
"get": { |
||||||
|
"tags": ["api"], |
||||||
|
"description": "获取当前的用户", |
||||||
|
"operationId": "currentUser", |
||||||
|
"responses": { |
||||||
|
"200": { |
||||||
|
"description": "Success", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/CurrentUser" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"401": { |
||||||
|
"description": "Error", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/ErrorResponse" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"x-swagger-router-controller": "api" |
||||||
|
}, |
||||||
|
"/api/login/captcha": { |
||||||
|
"post": { |
||||||
|
"description": "发送验证码", |
||||||
|
"operationId": "getFakeCaptcha", |
||||||
|
"tags": ["login"], |
||||||
|
"parameters": [ |
||||||
|
{ |
||||||
|
"name": "phone", |
||||||
|
"in": "query", |
||||||
|
"description": "手机号", |
||||||
|
"schema": { |
||||||
|
"type": "string" |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
"responses": { |
||||||
|
"200": { |
||||||
|
"description": "Success", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/FakeCaptcha" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"/api/login/outLogin": { |
||||||
|
"post": { |
||||||
|
"description": "登录接口", |
||||||
|
"operationId": "outLogin", |
||||||
|
"tags": ["login"], |
||||||
|
"responses": { |
||||||
|
"200": { |
||||||
|
"description": "Success", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"type": "object" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"401": { |
||||||
|
"description": "Error", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/ErrorResponse" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"x-swagger-router-controller": "api" |
||||||
|
}, |
||||||
|
"/api/login/account": { |
||||||
|
"post": { |
||||||
|
"tags": ["login"], |
||||||
|
"description": "登录接口", |
||||||
|
"operationId": "login", |
||||||
|
"requestBody": { |
||||||
|
"description": "登录系统", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/LoginParams" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"required": true |
||||||
|
}, |
||||||
|
"responses": { |
||||||
|
"200": { |
||||||
|
"description": "Success", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/LoginResult" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"401": { |
||||||
|
"description": "Error", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/ErrorResponse" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"x-codegen-request-body-name": "body" |
||||||
|
}, |
||||||
|
"x-swagger-router-controller": "api" |
||||||
|
}, |
||||||
|
"/api/notices": { |
||||||
|
"summary": "getNotices", |
||||||
|
"description": "NoticeIconItem", |
||||||
|
"get": { |
||||||
|
"tags": ["api"], |
||||||
|
"operationId": "getNotices", |
||||||
|
"responses": { |
||||||
|
"200": { |
||||||
|
"description": "Success", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/NoticeIconList" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"/api/rule": { |
||||||
|
"get": { |
||||||
|
"tags": ["rule"], |
||||||
|
"description": "获取规则列表", |
||||||
|
"operationId": "rule", |
||||||
|
"parameters": [ |
||||||
|
{ |
||||||
|
"name": "current", |
||||||
|
"in": "query", |
||||||
|
"description": "当前的页码", |
||||||
|
"schema": { |
||||||
|
"type": "number" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "pageSize", |
||||||
|
"in": "query", |
||||||
|
"description": "页面的容量", |
||||||
|
"schema": { |
||||||
|
"type": "number" |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
"responses": { |
||||||
|
"200": { |
||||||
|
"description": "Success", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/RuleList" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"401": { |
||||||
|
"description": "Error", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/ErrorResponse" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"post": { |
||||||
|
"tags": ["rule"], |
||||||
|
"description": "新建规则", |
||||||
|
"operationId": "addRule", |
||||||
|
"responses": { |
||||||
|
"200": { |
||||||
|
"description": "Success", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/RuleListItem" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"401": { |
||||||
|
"description": "Error", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/ErrorResponse" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"put": { |
||||||
|
"tags": ["rule"], |
||||||
|
"description": "新建规则", |
||||||
|
"operationId": "updateRule", |
||||||
|
"responses": { |
||||||
|
"200": { |
||||||
|
"description": "Success", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/RuleListItem" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"401": { |
||||||
|
"description": "Error", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/ErrorResponse" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"delete": { |
||||||
|
"tags": ["rule"], |
||||||
|
"description": "删除规则", |
||||||
|
"operationId": "removeRule", |
||||||
|
"responses": { |
||||||
|
"200": { |
||||||
|
"description": "Success", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"type": "object" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"401": { |
||||||
|
"description": "Error", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"$ref": "#/components/schemas/ErrorResponse" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"x-swagger-router-controller": "api" |
||||||
|
}, |
||||||
|
"/swagger": { |
||||||
|
"x-swagger-pipe": "swagger_raw" |
||||||
|
} |
||||||
|
}, |
||||||
|
"components": { |
||||||
|
"schemas": { |
||||||
|
"CurrentUser": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"name": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"avatar": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"userid": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"email": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"signature": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"title": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"group": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"tags": { |
||||||
|
"type": "array", |
||||||
|
"items": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"key": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"label": { |
||||||
|
"type": "string" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"notifyCount": { |
||||||
|
"type": "integer", |
||||||
|
"format": "int32" |
||||||
|
}, |
||||||
|
"unreadCount": { |
||||||
|
"type": "integer", |
||||||
|
"format": "int32" |
||||||
|
}, |
||||||
|
"country": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"access": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"geographic": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"province": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"label": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"key": { |
||||||
|
"type": "string" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"city": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"label": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"key": { |
||||||
|
"type": "string" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"address": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"phone": { |
||||||
|
"type": "string" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"LoginResult": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"status": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"type": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"currentAuthority": { |
||||||
|
"type": "string" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"PageParams": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"current": { |
||||||
|
"type": "number" |
||||||
|
}, |
||||||
|
"pageSize": { |
||||||
|
"type": "number" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"RuleListItem": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"key": { |
||||||
|
"type": "integer", |
||||||
|
"format": "int32" |
||||||
|
}, |
||||||
|
"disabled": { |
||||||
|
"type": "boolean" |
||||||
|
}, |
||||||
|
"href": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"avatar": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"name": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"owner": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"desc": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"callNo": { |
||||||
|
"type": "integer", |
||||||
|
"format": "int32" |
||||||
|
}, |
||||||
|
"status": { |
||||||
|
"type": "integer", |
||||||
|
"format": "int32" |
||||||
|
}, |
||||||
|
"updatedAt": { |
||||||
|
"type": "string", |
||||||
|
"format": "datetime" |
||||||
|
}, |
||||||
|
"createdAt": { |
||||||
|
"type": "string", |
||||||
|
"format": "datetime" |
||||||
|
}, |
||||||
|
"progress": { |
||||||
|
"type": "integer", |
||||||
|
"format": "int32" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"RuleList": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"data": { |
||||||
|
"type": "array", |
||||||
|
"items": { |
||||||
|
"$ref": "#/components/schemas/RuleListItem" |
||||||
|
} |
||||||
|
}, |
||||||
|
"total": { |
||||||
|
"type": "integer", |
||||||
|
"description": "列表的内容总数", |
||||||
|
"format": "int32" |
||||||
|
}, |
||||||
|
"success": { |
||||||
|
"type": "boolean" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"FakeCaptcha": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"code": { |
||||||
|
"type": "integer", |
||||||
|
"format": "int32" |
||||||
|
}, |
||||||
|
"status": { |
||||||
|
"type": "string" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"LoginParams": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"username": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"password": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"autoLogin": { |
||||||
|
"type": "boolean" |
||||||
|
}, |
||||||
|
"type": { |
||||||
|
"type": "string" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"ErrorResponse": { |
||||||
|
"required": ["errorCode"], |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"errorCode": { |
||||||
|
"type": "string", |
||||||
|
"description": "业务约定的错误码" |
||||||
|
}, |
||||||
|
"errorMessage": { |
||||||
|
"type": "string", |
||||||
|
"description": "业务上的错误信息" |
||||||
|
}, |
||||||
|
"success": { |
||||||
|
"type": "boolean", |
||||||
|
"description": "业务上的请求是否成功" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"NoticeIconList": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"data": { |
||||||
|
"type": "array", |
||||||
|
"items": { |
||||||
|
"$ref": "#/components/schemas/NoticeIconItem" |
||||||
|
} |
||||||
|
}, |
||||||
|
"total": { |
||||||
|
"type": "integer", |
||||||
|
"description": "列表的内容总数", |
||||||
|
"format": "int32" |
||||||
|
}, |
||||||
|
"success": { |
||||||
|
"type": "boolean" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"NoticeIconItemType": { |
||||||
|
"title": "NoticeIconItemType", |
||||||
|
"description": "已读未读列表的枚举", |
||||||
|
"type": "string", |
||||||
|
"properties": {}, |
||||||
|
"enum": ["notification", "message", "event"] |
||||||
|
}, |
||||||
|
"NoticeIconItem": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"id": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"extra": { |
||||||
|
"type": "string", |
||||||
|
"format": "any" |
||||||
|
}, |
||||||
|
"key": { "type": "string" }, |
||||||
|
"read": { |
||||||
|
"type": "boolean" |
||||||
|
}, |
||||||
|
"avatar": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"title": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"status": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"datetime": { |
||||||
|
"type": "string", |
||||||
|
"format": "date" |
||||||
|
}, |
||||||
|
"description": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"type": { |
||||||
|
"extensions": { |
||||||
|
"x-is-enum": true |
||||||
|
}, |
||||||
|
"$ref": "#/components/schemas/NoticeIconItemType" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
/** |
||||||
|
* 在生产环境 代理是无法生效的,所以这里没有生产环境的配置 |
||||||
|
* ------------------------------- |
||||||
|
* The agent cannot take effect in the production environment |
||||||
|
* so there is no configuration of the production environment |
||||||
|
* For details, please see |
||||||
|
* https://pro.ant.design/docs/deploy
|
||||||
|
*/ |
||||||
|
export default { |
||||||
|
dev: { |
||||||
|
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
|
||||||
|
'/api/': { |
||||||
|
// 要代理的地址
|
||||||
|
// target: 'https://preview.pro.ant.design',
|
||||||
|
// 配置了这个可以从 http 代理到 https
|
||||||
|
// 依赖 origin 的功能可能需要这个,比如 cookie
|
||||||
|
target: 'http://localhost:8083', |
||||||
|
changeOrigin: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
test: { |
||||||
|
'/api/': { |
||||||
|
target: 'https://proapi.azurewebsites.net', |
||||||
|
changeOrigin: true, |
||||||
|
pathRewrite: { '^': '' }, |
||||||
|
}, |
||||||
|
}, |
||||||
|
pre: { |
||||||
|
'/api/': { |
||||||
|
target: 'your pre url', |
||||||
|
changeOrigin: true, |
||||||
|
pathRewrite: { '^': '' }, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
module.exports = { |
||||||
|
testURL: 'http://localhost:8000', |
||||||
|
verbose: false, |
||||||
|
extraSetupFiles: ['./tests/setupTests.js'], |
||||||
|
globals: { |
||||||
|
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false, |
||||||
|
localStorage: null, |
||||||
|
}, |
||||||
|
}; |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
{ |
||||||
|
"compilerOptions": { |
||||||
|
"jsx": "react-jsx", |
||||||
|
"emitDecoratorMetadata": true, |
||||||
|
"experimentalDecorators": true, |
||||||
|
"baseUrl": ".", |
||||||
|
"paths": { |
||||||
|
"@/*": ["./src/*"] |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,174 @@ |
|||||||
|
import { Request, Response } from 'express'; |
||||||
|
import moment from 'moment'; |
||||||
|
import { parse } from 'url'; |
||||||
|
|
||||||
|
// mock tableListDataSource
|
||||||
|
const genList = (current: number, pageSize: number) => { |
||||||
|
const tableListDataSource: APID.RuleListItem[] = []; |
||||||
|
|
||||||
|
for (let i = 0; i < pageSize; i += 1) { |
||||||
|
const index = (current - 1) * 10 + i; |
||||||
|
tableListDataSource.push({ |
||||||
|
key: index, |
||||||
|
disabled: i % 6 === 0, |
||||||
|
href: 'https://ant.design', |
||||||
|
avatar: [ |
||||||
|
'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', |
||||||
|
'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', |
||||||
|
][i % 2], |
||||||
|
name: `TradeCode ${index}`, |
||||||
|
owner: '曲丽丽', |
||||||
|
desc: '这是一段描述', |
||||||
|
callNo: Math.floor(Math.random() * 1000), |
||||||
|
status: Math.floor(Math.random() * 10) % 4, |
||||||
|
updatedAt: moment().format('YYYY-MM-DD'), |
||||||
|
createdAt: moment().format('YYYY-MM-DD'), |
||||||
|
progress: Math.ceil(Math.random() * 100), |
||||||
|
}); |
||||||
|
} |
||||||
|
tableListDataSource.reverse(); |
||||||
|
return tableListDataSource; |
||||||
|
}; |
||||||
|
|
||||||
|
let tableListDataSource = genList(1, 100); |
||||||
|
|
||||||
|
function getRule(req: Request, res: Response, u: string) { |
||||||
|
let realUrl = u; |
||||||
|
if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') { |
||||||
|
realUrl = req.url; |
||||||
|
} |
||||||
|
const { current = 1, pageSize = 10 } = req.query; |
||||||
|
const params = parse(realUrl, true).query as unknown as APID.PageParams & |
||||||
|
APID.RuleListItem & { |
||||||
|
sorter: any; |
||||||
|
filter: any; |
||||||
|
}; |
||||||
|
|
||||||
|
let dataSource = [...tableListDataSource].slice( |
||||||
|
((current as number) - 1) * (pageSize as number), |
||||||
|
(current as number) * (pageSize as number), |
||||||
|
); |
||||||
|
if (params.sorter) { |
||||||
|
const sorter = JSON.parse(params.sorter); |
||||||
|
dataSource = dataSource.sort((prev, next) => { |
||||||
|
let sortNumber = 0; |
||||||
|
Object.keys(sorter).forEach((key) => { |
||||||
|
if (sorter[key] === 'descend') { |
||||||
|
if (prev[key] - next[key] > 0) { |
||||||
|
sortNumber += -1; |
||||||
|
} else { |
||||||
|
sortNumber += 1; |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
if (prev[key] - next[key] > 0) { |
||||||
|
sortNumber += 1; |
||||||
|
} else { |
||||||
|
sortNumber += -1; |
||||||
|
} |
||||||
|
}); |
||||||
|
return sortNumber; |
||||||
|
}); |
||||||
|
} |
||||||
|
if (params.filter) { |
||||||
|
const filter = JSON.parse(params.filter as any) as { |
||||||
|
[key: string]: string[]; |
||||||
|
}; |
||||||
|
if (Object.keys(filter).length > 0) { |
||||||
|
dataSource = dataSource.filter((item) => { |
||||||
|
return Object.keys(filter).some((key) => { |
||||||
|
if (!filter[key]) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
if (filter[key].includes(`${item[key]}`)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (params.name) { |
||||||
|
dataSource = dataSource.filter((data) => data?.name?.includes(params.name || '')); |
||||||
|
} |
||||||
|
const result = { |
||||||
|
data: dataSource, |
||||||
|
total: tableListDataSource.length, |
||||||
|
success: true, |
||||||
|
pageSize, |
||||||
|
current: parseInt(`${params.current}`, 10) || 1, |
||||||
|
}; |
||||||
|
|
||||||
|
return res.json(result); |
||||||
|
} |
||||||
|
|
||||||
|
function postRule(req: Request, res: Response, u: string, b: Request) { |
||||||
|
let realUrl = u; |
||||||
|
if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') { |
||||||
|
realUrl = req.url; |
||||||
|
} |
||||||
|
|
||||||
|
const body = (b && b.body) || req.body; |
||||||
|
const { method, name, desc, key } = body; |
||||||
|
|
||||||
|
switch (method) { |
||||||
|
/* eslint no-case-declarations:0 */ |
||||||
|
case 'delete': |
||||||
|
tableListDataSource = tableListDataSource.filter((item) => key.indexOf(item.key) === -1); |
||||||
|
break; |
||||||
|
case 'post': |
||||||
|
(() => { |
||||||
|
const i = Math.ceil(Math.random() * 10000); |
||||||
|
const newRule: APID.RuleListItem = { |
||||||
|
key: tableListDataSource.length, |
||||||
|
href: 'https://ant.design', |
||||||
|
avatar: [ |
||||||
|
'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', |
||||||
|
'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', |
||||||
|
][i % 2], |
||||||
|
name, |
||||||
|
owner: '曲丽丽', |
||||||
|
desc, |
||||||
|
callNo: Math.floor(Math.random() * 1000), |
||||||
|
status: Math.floor(Math.random() * 10) % 2, |
||||||
|
updatedAt: moment().format('YYYY-MM-DD'), |
||||||
|
createdAt: moment().format('YYYY-MM-DD'), |
||||||
|
progress: Math.ceil(Math.random() * 100), |
||||||
|
}; |
||||||
|
tableListDataSource.unshift(newRule); |
||||||
|
return res.json(newRule); |
||||||
|
})(); |
||||||
|
return; |
||||||
|
|
||||||
|
case 'update': |
||||||
|
(() => { |
||||||
|
let newRule = {}; |
||||||
|
tableListDataSource = tableListDataSource.map((item) => { |
||||||
|
if (item.key === key) { |
||||||
|
newRule = { ...item, desc, name }; |
||||||
|
return { ...item, desc, name }; |
||||||
|
} |
||||||
|
return item; |
||||||
|
}); |
||||||
|
return res.json(newRule); |
||||||
|
})(); |
||||||
|
return; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
const result = { |
||||||
|
list: tableListDataSource, |
||||||
|
pagination: { |
||||||
|
total: tableListDataSource.length, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
res.json(result); |
||||||
|
} |
||||||
|
|
||||||
|
export default { |
||||||
|
'GET /api/rule': getRule, |
||||||
|
'POST /api/rule': postRule, |
||||||
|
}; |
||||||
@ -0,0 +1,107 @@ |
|||||||
|
import { Request, Response } from 'express'; |
||||||
|
|
||||||
|
const getNotices = (req: Request, res: Response) => { |
||||||
|
res.json({ |
||||||
|
data: [ |
||||||
|
{ |
||||||
|
id: '000000001', |
||||||
|
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', |
||||||
|
title: '你收到了 14 份新周报', |
||||||
|
datetime: '2017-08-09', |
||||||
|
type: 'notification', |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: '000000002', |
||||||
|
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png', |
||||||
|
title: '你推荐的 曲妮妮 已通过第三轮面试', |
||||||
|
datetime: '2017-08-08', |
||||||
|
type: 'notification', |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: '000000003', |
||||||
|
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png', |
||||||
|
title: '这种模板可以区分多种通知类型', |
||||||
|
datetime: '2017-08-07', |
||||||
|
read: true, |
||||||
|
type: 'notification', |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: '000000004', |
||||||
|
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', |
||||||
|
title: '左侧图标用于区分不同的类型', |
||||||
|
datetime: '2017-08-07', |
||||||
|
type: 'notification', |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: '000000005', |
||||||
|
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', |
||||||
|
title: '内容不要超过两行字,超出时自动截断', |
||||||
|
datetime: '2017-08-07', |
||||||
|
type: 'notification', |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: '000000006', |
||||||
|
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', |
||||||
|
title: '曲丽丽 评论了你', |
||||||
|
description: '描述信息描述信息描述信息', |
||||||
|
datetime: '2017-08-07', |
||||||
|
type: 'message', |
||||||
|
clickClose: true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: '000000007', |
||||||
|
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', |
||||||
|
title: '朱偏右 回复了你', |
||||||
|
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像', |
||||||
|
datetime: '2017-08-07', |
||||||
|
type: 'message', |
||||||
|
clickClose: true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: '000000008', |
||||||
|
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', |
||||||
|
title: '标题', |
||||||
|
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像', |
||||||
|
datetime: '2017-08-07', |
||||||
|
type: 'message', |
||||||
|
clickClose: true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: '000000009', |
||||||
|
title: '任务名称', |
||||||
|
description: '任务需要在 2017-01-12 20:00 前启动', |
||||||
|
extra: '未开始', |
||||||
|
status: 'todo', |
||||||
|
type: 'event', |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: '000000010', |
||||||
|
title: '第三方紧急代码变更', |
||||||
|
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务', |
||||||
|
extra: '马上到期', |
||||||
|
status: 'urgent', |
||||||
|
type: 'event', |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: '000000011', |
||||||
|
title: '信息安全考试', |
||||||
|
description: '指派竹尔于 2017-01-09 前完成更新并发布', |
||||||
|
extra: '已耗时 8 天', |
||||||
|
status: 'doing', |
||||||
|
type: 'event', |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: '000000012', |
||||||
|
title: 'ABCD 版本发布', |
||||||
|
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务', |
||||||
|
extra: '进行中', |
||||||
|
status: 'processing', |
||||||
|
type: 'event', |
||||||
|
}, |
||||||
|
], |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
export default { |
||||||
|
'GET /api/notices': getNotices, |
||||||
|
}; |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
export default { |
||||||
|
'/api/auth_routes': { |
||||||
|
'/form/advanced-form': { authority: ['admin', 'user'] }, |
||||||
|
}, |
||||||
|
}; |
||||||
@ -0,0 +1,203 @@ |
|||||||
|
import { Request, Response } from 'express'; |
||||||
|
|
||||||
|
const waitTime = (time: number = 100) => { |
||||||
|
return new Promise((resolve) => { |
||||||
|
setTimeout(() => { |
||||||
|
resolve(true); |
||||||
|
}, time); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
async function getFakeCaptcha(req: Request, res: Response) { |
||||||
|
await waitTime(2000); |
||||||
|
return res.json('captcha-xxx'); |
||||||
|
} |
||||||
|
|
||||||
|
const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION } = process.env; |
||||||
|
|
||||||
|
/** |
||||||
|
* 当前用户的权限,如果为空代表没登录 |
||||||
|
* current user access, if is '', user need login |
||||||
|
* 如果是 pro 的预览,默认是有权限的 |
||||||
|
*/ |
||||||
|
let access = ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site' ? 'admin' : ''; |
||||||
|
|
||||||
|
const getAccess = () => { |
||||||
|
return access; |
||||||
|
}; |
||||||
|
|
||||||
|
// 代码中会兼容本地 service mock 以及部署站点的静态数据
|
||||||
|
export default { |
||||||
|
// 支持值为 Object 和 Array
|
||||||
|
'GET /api/currentUser': (req: Request, res: Response) => { |
||||||
|
if (!getAccess()) { |
||||||
|
res.status(401).send({ |
||||||
|
data: { |
||||||
|
isLogin: false, |
||||||
|
}, |
||||||
|
errorCode: '401', |
||||||
|
errorMessage: '请先登录!', |
||||||
|
success: true, |
||||||
|
}); |
||||||
|
return; |
||||||
|
} |
||||||
|
res.send({ |
||||||
|
success: true, |
||||||
|
data: { |
||||||
|
name: 'Serati Ma', |
||||||
|
avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png', |
||||||
|
userid: '00000001', |
||||||
|
email: 'antdesign@alipay.com', |
||||||
|
signature: '海纳百川,有容乃大', |
||||||
|
title: '交互专家', |
||||||
|
group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED', |
||||||
|
tags: [ |
||||||
|
{ |
||||||
|
key: '0', |
||||||
|
label: '很有想法的', |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: '1', |
||||||
|
label: '专注设计', |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: '2', |
||||||
|
label: '辣~', |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: '3', |
||||||
|
label: '大长腿', |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: '4', |
||||||
|
label: '川妹子', |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: '5', |
||||||
|
label: '海纳百川', |
||||||
|
}, |
||||||
|
], |
||||||
|
notifyCount: 12, |
||||||
|
unreadCount: 11, |
||||||
|
country: 'China', |
||||||
|
access: getAccess(), |
||||||
|
geographic: { |
||||||
|
province: { |
||||||
|
label: '浙江省', |
||||||
|
key: '330000', |
||||||
|
}, |
||||||
|
city: { |
||||||
|
label: '杭州市', |
||||||
|
key: '330100', |
||||||
|
}, |
||||||
|
}, |
||||||
|
address: '西湖区工专路 77 号', |
||||||
|
phone: '0752-268888888', |
||||||
|
}, |
||||||
|
}); |
||||||
|
}, |
||||||
|
// GET POST 可省略
|
||||||
|
'GET /api/users': [ |
||||||
|
{ |
||||||
|
key: '1', |
||||||
|
name: 'John Brown', |
||||||
|
age: 32, |
||||||
|
address: 'New York No. 1 Lake Park', |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: '2', |
||||||
|
name: 'Jim Green', |
||||||
|
age: 42, |
||||||
|
address: 'London No. 1 Lake Park', |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: '3', |
||||||
|
name: 'Joe Black', |
||||||
|
age: 32, |
||||||
|
address: 'Sidney No. 1 Lake Park', |
||||||
|
}, |
||||||
|
], |
||||||
|
'POST /api/login/account': async (req: Request, res: Response) => { |
||||||
|
const { password, username, type } = req.body; |
||||||
|
await waitTime(2000); |
||||||
|
if (password === 'ant.design' && username === 'admin') { |
||||||
|
res.send({ |
||||||
|
status: 'ok', |
||||||
|
type, |
||||||
|
currentAuthority: 'admin', |
||||||
|
}); |
||||||
|
access = 'admin'; |
||||||
|
return; |
||||||
|
} |
||||||
|
if (password === 'ant.design' && username === 'user') { |
||||||
|
res.send({ |
||||||
|
status: 'ok', |
||||||
|
type, |
||||||
|
currentAuthority: 'user', |
||||||
|
}); |
||||||
|
access = 'user'; |
||||||
|
return; |
||||||
|
} |
||||||
|
if (type === 'mobile') { |
||||||
|
res.send({ |
||||||
|
status: 'ok', |
||||||
|
type, |
||||||
|
currentAuthority: 'admin', |
||||||
|
}); |
||||||
|
access = 'admin'; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
res.send({ |
||||||
|
status: 'error', |
||||||
|
type, |
||||||
|
currentAuthority: 'guest', |
||||||
|
}); |
||||||
|
access = 'guest'; |
||||||
|
}, |
||||||
|
'POST /api/login/outLogin': (req: Request, res: Response) => { |
||||||
|
access = ''; |
||||||
|
res.send({ data: {}, success: true }); |
||||||
|
}, |
||||||
|
'POST /api/register': (req: Request, res: Response) => { |
||||||
|
res.send({ status: 'ok', currentAuthority: 'user', success: true }); |
||||||
|
}, |
||||||
|
'GET /api/500': (req: Request, res: Response) => { |
||||||
|
res.status(500).send({ |
||||||
|
timestamp: 1513932555104, |
||||||
|
status: 500, |
||||||
|
error: 'error', |
||||||
|
message: 'error', |
||||||
|
path: '/base/category/list', |
||||||
|
}); |
||||||
|
}, |
||||||
|
'GET /api/404': (req: Request, res: Response) => { |
||||||
|
res.status(404).send({ |
||||||
|
timestamp: 1513932643431, |
||||||
|
status: 404, |
||||||
|
error: 'Not Found', |
||||||
|
message: 'No message available', |
||||||
|
path: '/base/category/list/2121212', |
||||||
|
}); |
||||||
|
}, |
||||||
|
'GET /api/403': (req: Request, res: Response) => { |
||||||
|
res.status(403).send({ |
||||||
|
timestamp: 1513932555104, |
||||||
|
status: 403, |
||||||
|
error: 'Forbidden', |
||||||
|
message: 'Forbidden', |
||||||
|
path: '/base/category/list', |
||||||
|
}); |
||||||
|
}, |
||||||
|
'GET /api/401': (req: Request, res: Response) => { |
||||||
|
res.status(401).send({ |
||||||
|
timestamp: 1513932555104, |
||||||
|
status: 401, |
||||||
|
error: 'Unauthorized', |
||||||
|
message: 'Unauthorized', |
||||||
|
path: '/base/category/list', |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
'GET /api/login/captcha': getFakeCaptcha, |
||||||
|
}; |
||||||
@ -0,0 +1,116 @@ |
|||||||
|
{ |
||||||
|
"name": "ant-design-pro", |
||||||
|
"version": "5.2.0", |
||||||
|
"private": true, |
||||||
|
"description": "An out-of-box UI solution for enterprise applications", |
||||||
|
"scripts": { |
||||||
|
"analyze": "cross-env ANALYZE=1 umi build", |
||||||
|
"build": "umi build", |
||||||
|
"deploy": "npm run build && npm run gh-pages", |
||||||
|
"dev": "npm run start:dev", |
||||||
|
"gh-pages": "gh-pages -d dist", |
||||||
|
"i18n-remove": "pro i18n-remove --locale=zh-CN --write", |
||||||
|
"postinstall": "umi g tmp", |
||||||
|
"lint": "umi g tmp && npm run lint:js && npm run lint:style && npm run lint:prettier && npm run tsc", |
||||||
|
"lint-staged": "lint-staged", |
||||||
|
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ", |
||||||
|
"lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style", |
||||||
|
"lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src", |
||||||
|
"lint:prettier": "prettier -c --write \"src/**/*\" --end-of-line auto", |
||||||
|
"lint:style": "stylelint --fix \"src/**/*.less\" --syntax less", |
||||||
|
"openapi": "umi openapi", |
||||||
|
"playwright": "playwright install && playwright test", |
||||||
|
"precommit": "lint-staged", |
||||||
|
"prettier": "prettier -c --write \"src/**/*\"", |
||||||
|
"serve": "umi-serve", |
||||||
|
"start": "cross-env UMI_ENV=dev umi dev", |
||||||
|
"start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev umi dev", |
||||||
|
"start:no-mock": "cross-env MOCK=none UMI_ENV=dev umi dev", |
||||||
|
"start:no-ui": "cross-env UMI_UI=none UMI_ENV=dev umi dev", |
||||||
|
"start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev umi dev", |
||||||
|
"start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev umi dev", |
||||||
|
"test": "umi test", |
||||||
|
"test:component": "umi test ./src/components", |
||||||
|
"test:e2e": "node ./tests/run-tests.js", |
||||||
|
"tsc": "tsc --noEmit" |
||||||
|
}, |
||||||
|
"lint-staged": { |
||||||
|
"**/*.less": "stylelint --syntax less", |
||||||
|
"**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js", |
||||||
|
"**/*.{js,jsx,tsx,ts,less,md,json}": [ |
||||||
|
"prettier --write" |
||||||
|
] |
||||||
|
}, |
||||||
|
"browserslist": [ |
||||||
|
"> 1%", |
||||||
|
"last 2 versions", |
||||||
|
"not ie <= 10" |
||||||
|
], |
||||||
|
"dependencies": { |
||||||
|
"@ant-design/icons": "^4.7.0", |
||||||
|
"@ant-design/pro-descriptions": "^1.10.0", |
||||||
|
"@ant-design/pro-form": "^1.52.0", |
||||||
|
"@ant-design/pro-layout": "^6.32.0", |
||||||
|
"@ant-design/pro-table": "^2.61.0", |
||||||
|
"@umijs/route-utils": "^2.0.0", |
||||||
|
"antd": "^4.17.0", |
||||||
|
"classnames": "^2.3.0", |
||||||
|
"lodash": "^4.17.0", |
||||||
|
"md5": "^2.3.0", |
||||||
|
"moment": "^2.29.0", |
||||||
|
"omit.js": "^2.0.2", |
||||||
|
"rc-menu": "^9.1.0", |
||||||
|
"rc-util": "^5.16.0", |
||||||
|
"react": "^17.0.0", |
||||||
|
"react-dev-inspector": "^1.7.0", |
||||||
|
"react-dom": "^17.0.0", |
||||||
|
"react-helmet-async": "^1.2.0", |
||||||
|
"umi": "^3.5.0", |
||||||
|
"umi-request": "^1.4.0", |
||||||
|
"use-merge-value": "^1.0.2" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@ant-design/pro-cli": "^2.1.0", |
||||||
|
"@playwright/test": "^1.17.0", |
||||||
|
"@types/express": "^4.17.0", |
||||||
|
"@types/history": "^4.7.0", |
||||||
|
"@types/jest": "^26.0.0", |
||||||
|
"@types/lodash": "^4.14.0", |
||||||
|
"@types/react": "^17.0.0", |
||||||
|
"@types/react-dom": "^17.0.0", |
||||||
|
"@types/react-helmet": "^6.1.0", |
||||||
|
"@umijs/fabric": "^2.8.0", |
||||||
|
"@umijs/openapi": "^1.3.0", |
||||||
|
"@umijs/plugin-blocks": "^2.2.0", |
||||||
|
"@umijs/plugin-esbuild": "^1.4.0", |
||||||
|
"@umijs/plugin-openapi": "^1.3.0", |
||||||
|
"@umijs/preset-ant-design-pro": "^1.3.0", |
||||||
|
"@umijs/preset-dumi": "^1.1.0", |
||||||
|
"@umijs/preset-react": "^1.8.17", |
||||||
|
"@umijs/yorkie": "^2.0.5", |
||||||
|
"carlo": "^0.9.46", |
||||||
|
"cross-env": "^7.0.0", |
||||||
|
"cross-port-killer": "^1.3.0", |
||||||
|
"detect-installer": "^1.0.0", |
||||||
|
"enzyme": "^3.11.0", |
||||||
|
"eslint": "^7.32.0", |
||||||
|
"express": "^4.17.0", |
||||||
|
"gh-pages": "^3.2.0", |
||||||
|
"jsdom-global": "^3.0.0", |
||||||
|
"lint-staged": "^10.0.0", |
||||||
|
"mockjs": "^1.1.0", |
||||||
|
"pont-engine": "1.0.13", |
||||||
|
"prettier": "^2.5.0", |
||||||
|
"puppeteer-core": "^8.0.0", |
||||||
|
"stylelint": "^13.0.0", |
||||||
|
"swagger-ui-react": "^3.52.0", |
||||||
|
"typescript": "^4.5.0", |
||||||
|
"umi-serve": "^1.9.10" |
||||||
|
}, |
||||||
|
"engines": { |
||||||
|
"node": ">=10.0.0" |
||||||
|
}, |
||||||
|
"gitHooks": { |
||||||
|
"commit-msg": "fabric verify-commit" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
{ |
||||||
|
"originUrl": "http://localhost:8083/v2/api-docs", |
||||||
|
"templatePath": "./pontTemplate", |
||||||
|
"outDir": "./src/services", |
||||||
|
"surrounding": "typescript", |
||||||
|
"mocks": { |
||||||
|
"enable": true |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,192 @@ |
|||||||
|
import * as Pont from 'pont-engine'; |
||||||
|
import { CodeGenerator, Interface, Property, Mod, Surrounding } from 'pont-engine'; |
||||||
|
|
||||||
|
/** |
||||||
|
* 生成代码 |
||||||
|
*/ |
||||||
|
export default class MyGenerator extends CodeGenerator { |
||||||
|
/** |
||||||
|
* 类型声明 api.d.ts |
||||||
|
*/ |
||||||
|
|
||||||
|
/** 获取公共的类型定义代码 */ |
||||||
|
// getCommonDeclaration() {
|
||||||
|
// return `
|
||||||
|
// import { RequestOptionsInit } from 'umi-request';
|
||||||
|
// `;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/** 获取所有基类的类型定义代码,一个 namespace */ |
||||||
|
getBaseClassesInDeclaration() { |
||||||
|
const content = `namespace ${this.dataSource.name || 'defs'} {
|
||||||
|
${this.dataSource.baseClasses |
||||||
|
.map( |
||||||
|
(base) => ` |
||||||
|
export ${this.getBaseClassInDeclaration(base)} |
||||||
|
`,
|
||||||
|
) |
||||||
|
.join('\n')} |
||||||
|
} |
||||||
|
`;
|
||||||
|
|
||||||
|
return content; |
||||||
|
} |
||||||
|
|
||||||
|
/** 获取接口内容的类型定义代码 */ |
||||||
|
getInterfaceContentInDeclaration(inter: Interface) { |
||||||
|
// 请求体(Body)类型参数列表
|
||||||
|
const bodyParams = inter.parameters.filter((param) => param.in === 'body'); |
||||||
|
// 路径参数(PathVariable)类型参数列表
|
||||||
|
const pathParams = inter.parameters.filter((param) => param.in === 'path'); |
||||||
|
// 查询字符串(QueryString)类型参数列表
|
||||||
|
const queryParams = inter.parameters.filter((param) => param.in === 'query'); |
||||||
|
|
||||||
|
// 获取请求体类型参数类代码
|
||||||
|
const bodyParamsCode = inter.getBodyParamsCode(); |
||||||
|
// 获取查询字符串参数类代码
|
||||||
|
const paramsCode = inter.getParamsCode(); |
||||||
|
// 获取路径参数类代码
|
||||||
|
const pathParamsCode = this.getPathParamsCodeInDeclaration(pathParams); |
||||||
|
|
||||||
|
// 接口方法参数列表
|
||||||
|
const requestArgs: string[] = []; |
||||||
|
|
||||||
|
pathParams.length > 0 && requestArgs.push(`pathParams: PathParams`); |
||||||
|
queryParams.length > 0 && requestArgs.push(`params: Params`); |
||||||
|
bodyParams.length > 0 && |
||||||
|
requestArgs.push(`bodyParams:${bodyParams.length > 0 ? bodyParamsCode : ''}`); |
||||||
|
requestArgs.push('options?: any'); |
||||||
|
|
||||||
|
const requestParams = requestArgs.join(', '); |
||||||
|
|
||||||
|
// 返回数据类型
|
||||||
|
const { responseType } = inter; |
||||||
|
|
||||||
|
return ` |
||||||
|
${queryParams.length > 0 ? paramsCode : ''} |
||||||
|
${pathParams.length > 0 ? pathParamsCode : ''} |
||||||
|
|
||||||
|
export type ResponseType = Promise<${responseType}> |
||||||
|
|
||||||
|
export const URL:string; |
||||||
|
|
||||||
|
export function request(${requestParams}): ResponseType; |
||||||
|
`;
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 接口实现 /mods/<module_name>/<method_name>.ts |
||||||
|
*/ |
||||||
|
|
||||||
|
/** 获取接口实现内容的代码 */ |
||||||
|
getInterfaceContent(inter: Interface) { |
||||||
|
// 接口请求类型
|
||||||
|
const method = inter.method.toUpperCase(); |
||||||
|
|
||||||
|
// 请求体(Body)类型参数列表
|
||||||
|
const bodyParams = inter.parameters.filter((param) => param.in === 'body'); |
||||||
|
// 路径参数(PathVariable)类型参数列表
|
||||||
|
const pathParams = inter.parameters.filter((param) => param.in === 'path'); |
||||||
|
// 查询字符串(QueryString)类型参数列表
|
||||||
|
const queryParams = inter.parameters.filter((param) => param.in === 'query'); |
||||||
|
|
||||||
|
// 获取请求体类型参数类代码
|
||||||
|
const bodyParamsCode = inter.getBodyParamsCode(); |
||||||
|
// 获取查询字符串参数类代码
|
||||||
|
const paramsCode = this.getParamsCode(queryParams); |
||||||
|
// 获取路径参数类代码
|
||||||
|
const pathParamsCode = this.getPathParamsCode(pathParams); |
||||||
|
|
||||||
|
// 请求的 MIME 类型
|
||||||
|
const contentType = |
||||||
|
inter.consumes && inter.consumes.length ? inter.consumes[0] : 'application/json'; |
||||||
|
|
||||||
|
// 接口方法参数列表
|
||||||
|
const requestArgs: string[] = []; |
||||||
|
|
||||||
|
pathParams.length > 0 && requestArgs.push(`pathParams: PathParams`); |
||||||
|
queryParams.length > 0 && requestArgs.push(`params: Params`); |
||||||
|
bodyParams.length > 0 && |
||||||
|
requestArgs.push(`bodyParams:${bodyParams.length > 0 ? bodyParamsCode : ''}`); |
||||||
|
requestArgs.push('options?: RequestOptionsInit'); |
||||||
|
|
||||||
|
const requestParams = requestArgs.join(', '); |
||||||
|
|
||||||
|
// 数据源前缀
|
||||||
|
let dataSourcePrefix = ''; |
||||||
|
if (this.dataSource.name) { |
||||||
|
dataSourcePrefix = `/${this.dataSource.name}`; |
||||||
|
} |
||||||
|
|
||||||
|
return ` |
||||||
|
import fetch from '../../../utils/request'; |
||||||
|
import { RequestOptionsInit } from 'umi-request'; |
||||||
|
|
||||||
|
${pathParams.length > 0 ? `export ${pathParamsCode}` : ''} |
||||||
|
${queryParams.length > 0 ? `export ${paramsCode}` : ''} |
||||||
|
|
||||||
|
export const URL = '${dataSourcePrefix}${inter.path}'; |
||||||
|
|
||||||
|
/** |
||||||
|
* @desc ${inter.description} |
||||||
|
*/ |
||||||
|
export function request (${requestParams}) { |
||||||
|
const fetchOption = Object.assign ({ |
||||||
|
method: '${method}', |
||||||
|
headers: { |
||||||
|
'Content-Type': '${contentType}' |
||||||
|
}, |
||||||
|
${queryParams.length > 0 ? 'params: params,' : ''} |
||||||
|
${bodyParamsCode ? 'data: bodyParams' : ''} |
||||||
|
}, |
||||||
|
options) |
||||||
|
return fetch('${dataSourcePrefix}${inter.path}', fetchOption); |
||||||
|
} |
||||||
|
`;
|
||||||
|
} |
||||||
|
|
||||||
|
// 生成参数类(Params)
|
||||||
|
getParamsCode(params: Property[]): string { |
||||||
|
if (!Array.isArray(params) || params.length === 0) return ''; |
||||||
|
|
||||||
|
return ` |
||||||
|
export class Params { |
||||||
|
${params |
||||||
|
.map((param) => { |
||||||
|
return `${param.toPropertyCodeWithInitValue('')}`; |
||||||
|
}) |
||||||
|
.join('\n')} |
||||||
|
} |
||||||
|
`;
|
||||||
|
} |
||||||
|
|
||||||
|
// 生成路径参数类(PathParams)
|
||||||
|
getPathParamsCode(pathParams: Property[]): string { |
||||||
|
if (!Array.isArray(pathParams) || pathParams.length === 0) return ''; |
||||||
|
|
||||||
|
return ` |
||||||
|
class PathParams { |
||||||
|
${pathParams |
||||||
|
.map((param) => { |
||||||
|
return `${param.toPropertyCodeWithInitValue('')}`; |
||||||
|
}) |
||||||
|
.join('\n')} |
||||||
|
} |
||||||
|
`;
|
||||||
|
} |
||||||
|
|
||||||
|
// 生成路径参数类(PathParams)
|
||||||
|
getPathParamsCodeInDeclaration(pathParams: Property[]): string { |
||||||
|
if (!Array.isArray(pathParams) || pathParams.length === 0) return ''; |
||||||
|
|
||||||
|
return ` |
||||||
|
class PathParams { |
||||||
|
${pathParams |
||||||
|
.map((param) => { |
||||||
|
return `${param.toPropertyCode(Surrounding.typeScript, param.required)}`; |
||||||
|
}) |
||||||
|
.join('\n')} |
||||||
|
} |
||||||
|
`;
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
preview.pro.ant.design |
||||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 677 B |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
@ -0,0 +1,36 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { Result } from 'antd'; |
||||||
|
import type { IAuthorityType } from './CheckPermissions'; |
||||||
|
import check from './CheckPermissions'; |
||||||
|
|
||||||
|
import type AuthorizedRoute from './AuthorizedRoute'; |
||||||
|
import type Secured from './Secured'; |
||||||
|
|
||||||
|
interface AuthorizedProps { |
||||||
|
authority: IAuthorityType; |
||||||
|
noMatch?: React.ReactNode; |
||||||
|
} |
||||||
|
|
||||||
|
type IAuthorizedType = React.FunctionComponent<AuthorizedProps> & { |
||||||
|
Secured: typeof Secured; |
||||||
|
check: typeof check; |
||||||
|
AuthorizedRoute: typeof AuthorizedRoute; |
||||||
|
}; |
||||||
|
|
||||||
|
const Authorized: React.FunctionComponent<AuthorizedProps> = ({ |
||||||
|
children, |
||||||
|
authority, |
||||||
|
noMatch = ( |
||||||
|
<Result |
||||||
|
status="403" |
||||||
|
title="403" |
||||||
|
subTitle="Sorry, you are not authorized to access this page." |
||||||
|
/> |
||||||
|
), |
||||||
|
}) => { |
||||||
|
const childrenRender: React.ReactNode = typeof children === 'undefined' ? null : children; |
||||||
|
const dom = check(authority, childrenRender, noMatch); |
||||||
|
return <>{dom}</>; |
||||||
|
}; |
||||||
|
|
||||||
|
export default Authorized as IAuthorizedType; |
||||||