初始化git项目

master
mfay 5 years ago
parent 110271ec17
commit 08874d07cd
  1. 33
      awardBE/.gitignore
  2. 118
      awardBE/.mvn/wrapper/MavenWrapperDownloader.java
  3. BIN
      awardBE/.mvn/wrapper/maven-wrapper.jar
  4. 2
      awardBE/.mvn/wrapper/maven-wrapper.properties
  5. 310
      awardBE/mvnw
  6. 182
      awardBE/mvnw.cmd
  7. 167
      awardBE/pom.xml
  8. 36
      awardBE/src/main/java/edu/ncst/award/AwardApplication.java
  9. 34
      awardBE/src/main/java/edu/ncst/award/config/CustomAccessDeniedHandler.java
  10. 47
      awardBE/src/main/java/edu/ncst/award/config/JsonConfig.java
  11. 204
      awardBE/src/main/java/edu/ncst/award/config/SecurityConfig.java
  12. 109
      awardBE/src/main/java/edu/ncst/award/config/security/DataBaseUrlVoter.java
  13. 125
      awardBE/src/main/java/edu/ncst/award/config/security/filter/AuthCheckFilter.java
  14. 255
      awardBE/src/main/java/edu/ncst/award/config/security/filter/LoginFilter.java
  15. 39
      awardBE/src/main/java/edu/ncst/award/config/security/token/JwtAuthenticationToken.java
  16. 39
      awardBE/src/main/java/edu/ncst/award/config/security/token/MobileCaptchaAuthenticationToken.java
  17. 40
      awardBE/src/main/java/edu/ncst/award/controller/AuthController.java
  18. 76
      awardBE/src/main/java/edu/ncst/award/controller/CaptchaController.java
  19. 123
      awardBE/src/main/java/edu/ncst/award/controller/MenuController.java
  20. 146
      awardBE/src/main/java/edu/ncst/award/controller/TestController.java
  21. 56
      awardBE/src/main/java/edu/ncst/award/controller/UserController.java
  22. 41
      awardBE/src/main/java/edu/ncst/award/exception/BusinessException.java
  23. 63
      awardBE/src/main/java/edu/ncst/award/exception/CodeMsg.java
  24. 54
      awardBE/src/main/java/edu/ncst/award/exception/handler/InterfaceExceptionHandler.java
  25. 36
      awardBE/src/main/java/edu/ncst/award/mapper/system/MenuMapper.java
  26. 39
      awardBE/src/main/java/edu/ncst/award/mapper/system/ResourceMapper.java
  27. 51
      awardBE/src/main/java/edu/ncst/award/mapper/system/UserMapper.java
  28. 40
      awardBE/src/main/java/edu/ncst/award/model/BaseEntity.java
  29. 50
      awardBE/src/main/java/edu/ncst/award/model/log/LoginLog.java
  30. 59
      awardBE/src/main/java/edu/ncst/award/model/system/MajorCategory.java
  31. 87
      awardBE/src/main/java/edu/ncst/award/model/system/Menu.java
  32. 58
      awardBE/src/main/java/edu/ncst/award/model/system/Resource.java
  33. 76
      awardBE/src/main/java/edu/ncst/award/model/system/Role.java
  34. 84
      awardBE/src/main/java/edu/ncst/award/model/system/User.java
  35. 19
      awardBE/src/main/java/edu/ncst/award/service/ILoginLogService.java
  36. 61
      awardBE/src/main/java/edu/ncst/award/service/IMenuService.java
  37. 25
      awardBE/src/main/java/edu/ncst/award/service/IResourceService.java
  38. 5
      awardBE/src/main/java/edu/ncst/award/service/ISystemService.java
  39. 19
      awardBE/src/main/java/edu/ncst/award/service/IUserService.java
  40. 34
      awardBE/src/main/java/edu/ncst/award/service/impl/log/LoginLogServiceImpl.java
  41. 320
      awardBE/src/main/java/edu/ncst/award/service/impl/system/MenuServiceImpl.java
  42. 387
      awardBE/src/main/java/edu/ncst/award/service/impl/system/ResourceServiceImpl.java
  43. 15
      awardBE/src/main/java/edu/ncst/award/service/impl/system/SystemService.java
  44. 69
      awardBE/src/main/java/edu/ncst/award/service/impl/system/UserServiceImpl.java
  45. 79
      awardBE/src/main/java/edu/ncst/award/utils/CaptchaUtil.java
  46. 29
      awardBE/src/main/java/edu/ncst/award/utils/CookieUtils.java
  47. 91
      awardBE/src/main/java/edu/ncst/award/utils/ResponseUtils.java
  48. 55
      awardBE/src/main/java/edu/ncst/award/utils/ResultUtils.java
  49. 55
      awardBE/src/main/java/edu/ncst/award/utils/SessionUtils.java
  50. 27
      awardBE/src/main/java/edu/ncst/award/utils/URLUtils.java
  51. 30
      awardBE/src/main/java/edu/ncst/award/vo/ListResultVO.java
  52. 61
      awardBE/src/main/java/edu/ncst/award/vo/Query.java
  53. 19
      awardBE/src/main/java/edu/ncst/award/vo/ResultVO.java
  54. 20
      awardBE/src/main/java/edu/ncst/award/vo/Sorter.java
  55. 41
      awardBE/src/main/java/edu/ncst/award/vo/param/CurrentUserDTO.java
  56. 13
      awardBE/src/main/java/edu/ncst/award/vo/param/ResetMenuDTO.java
  57. 32
      awardBE/src/main/java/edu/ncst/award/vo/param/UserLoginDTO.java
  58. 29
      awardBE/src/main/java/edu/ncst/award/vo/param/query/LoginLogQuery.java
  59. 14
      awardBE/src/main/java/edu/ncst/award/vo/param/query/UserQuery.java
  60. 32
      awardBE/src/main/java/edu/ncst/award/vo/result/UserInfoVO.java
  61. 61
      awardBE/src/main/resources/application.yml
  62. 57
      awardBE/src/main/resources/logback.xml
  63. 13
      awardBE/src/test/java/edu/ncst/award/RewardsApplyApplicationTests.java
  64. 16
      awardFE/.editorconfig
  65. 8
      awardFE/.eslintignore
  66. 8
      awardFE/.eslintrc.js
  67. 42
      awardFE/.gitignore
  68. 23
      awardFE/.prettierignore
  69. 5
      awardFE/.prettierrc.js
  70. 5
      awardFE/.stylelintrc.js
  71. 57
      awardFE/README.md
  72. 15
      awardFE/config/config.dev.ts
  73. 67
      awardFE/config/config.ts
  74. 61
      awardFE/config/defaultSettings.ts
  75. 593
      awardFE/config/oneapi.json
  76. 35
      awardFE/config/proxy.ts
  77. 122
      awardFE/config/routes.ts
  78. 9
      awardFE/jest.config.js
  79. 11
      awardFE/jsconfig.json
  80. 174
      awardFE/mock/listTableList.ts
  81. 107
      awardFE/mock/notices.ts
  82. 5
      awardFE/mock/route.ts
  83. 203
      awardFE/mock/user.ts
  84. 53046
      awardFE/package-lock.json
  85. 116
      awardFE/package.json
  86. 22
      awardFE/playwright.config.ts
  87. 9
      awardFE/pont-config.json
  88. 192
      awardFE/pontTemplate.ts
  89. 1
      awardFE/public/CNAME
  90. BIN
      awardFE/public/favicon.ico
  91. BIN
      awardFE/public/icons/icon-128x128.png
  92. BIN
      awardFE/public/icons/icon-192x192.png
  93. BIN
      awardFE/public/icons/icon-512x512.png
  94. 1
      awardFE/public/logo.svg
  95. 5
      awardFE/public/pro_icon.svg
  96. BIN
      awardFE/src/assets/avatar.jpg
  97. BIN
      awardFE/src/assets/background.jpeg
  98. BIN
      awardFE/src/assets/logo.gif
  99. 1
      awardFE/src/assets/logo.svg
  100. 36
      awardFE/src/components/Authorized/Authorized.tsx
  101. Some files were not shown because too many files have changed in this diff Show More

33
awardBE/.gitignore vendored

@ -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();
}
}

Binary file not shown.

@ -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

310
awardBE/mvnw vendored

@ -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 "$@"

182
awardBE/mvnw.cmd vendored

@ -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>-->
<!-- &lt;!&ndash; https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui &ndash;&gt;-->
<!-- <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,
},
};

42
awardFE/.gitignore vendored

@ -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,122 @@
export default [
{
path: '/',
component: '../layouts/BlankLayout',
routes: [
{
path: '/user',
component: '../layouts/UserLayout',
routes: [
{
path: '/user',
routes: [
{
name: 'login',
path: '/user/login',
component: './user/Login',
icon: 'smile',
},
],
},
],
},
{
path: '/',
component: '../layouts/BasicLayout',
// Routes: ['src/pages/Authorized'],
wrappers: ['../pages/Authorized'],
// authority: ['admin', 'user'],
routes: [
{
path: '/welcome',
name: 'welcome',
icon: 'smile',
component: './Admin',
},
{
path: '/admin',
name: 'admin',
icon: 'crown',
access: 'canAdmin',
routes: [
{
path: '/admin/account',
name: 'account',
icon: 'smile',
component: './sysAdmin',
},
{
path: '/admin/role',
name: 'role',
icon: 'smile',
component: './sysAdmin',
},
{
path: '/admin/units',
name: 'units',
icon: 'smile',
component: './sysAdmin',
},
{
path: '/admin/depts',
name: 'depts',
icon: 'smile',
component: './sysAdmin',
},
{
path: '/admin/institutions',
name: 'institutions',
icon: 'smile',
component: './sysAdmin',
},
{
path: '/admin/subjects',
name: 'subjects',
icon: 'smile',
component: './sysAdmin',
},
{
path: '/admin/fields',
name: 'fields',
icon: 'smile',
component: './sysAdmin',
},
],
},
{
name: 'project.declare',
path: '/projectdeclare',
component: './projectDeclare',
icon: 'smile',
},
{
name: 'project.admin.review',
path: '/adminreview',
component: './sysAdmin',
icon: 'smile',
},
{
name: 'project.expert.review',
path: '/expertreview',
component: './sysAdmin',
icon: 'smile',
},
{
name: 'project.expert.assign',
path: '/expertassign',
component: './sysAdmin',
icon: 'smile',
},
{
path: '/',
redirect: '/welcome',
},
{
path: '/404',
component: './404',
},
],
},
],
},
];

@ -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,
};

File diff suppressed because it is too large Load Diff

@ -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,22 @@
// playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
use: {
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
],
};
export default config;

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" version="1.1" viewBox="0 0 200 200"><title>Group 28 Copy 5</title><desc>Created with Sketch.</desc><defs><linearGradient id="linearGradient-1" x1="62.102%" x2="108.197%" y1="0%" y2="37.864%"><stop offset="0%" stop-color="#4285EB"/><stop offset="100%" stop-color="#2EC7FF"/></linearGradient><linearGradient id="linearGradient-2" x1="69.644%" x2="54.043%" y1="0%" y2="108.457%"><stop offset="0%" stop-color="#29CDFF"/><stop offset="37.86%" stop-color="#148EFF"/><stop offset="100%" stop-color="#0A60FF"/></linearGradient><linearGradient id="linearGradient-3" x1="69.691%" x2="16.723%" y1="-12.974%" y2="117.391%"><stop offset="0%" stop-color="#FA816E"/><stop offset="41.473%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient><linearGradient id="linearGradient-4" x1="68.128%" x2="30.44%" y1="-35.691%" y2="114.943%"><stop offset="0%" stop-color="#FA8E7D"/><stop offset="51.264%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient></defs><g id="Page-1" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="logo" transform="translate(-20.000000, -20.000000)"><g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)"><g id="Group-27-Copy-3"><g id="Group-25" fill-rule="nonzero"><g id="2"><path id="Shape" fill="url(#linearGradient-1)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/><path id="Shape" fill="url(#linearGradient-2)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/></g><path id="Shape" fill="url(#linearGradient-3)" d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z"/></g><ellipse id="Combined-Shape" cx="100.519" cy="100.437" fill="url(#linearGradient-4)" rx="23.6" ry="23.581"/></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

@ -0,0 +1,5 @@
<svg width="42" height="42" xmlns="http://www.w3.org/2000/svg">
<g>
<path fill="#070707" d="m6.717392,13.773912l5.6,0c2.8,0 4.7,1.9 4.7,4.7c0,2.8 -2,4.7 -4.9,4.7l-2.5,0l0,4.3l-2.9,0l0,-13.7zm2.9,2.2l0,4.9l1.9,0c1.6,0 2.6,-0.9 2.6,-2.4c0,-1.6 -0.9,-2.4 -2.6,-2.4l-1.9,0l0,-0.1zm8.9,11.5l2.7,0l0,-5.7c0,-1.4 0.8,-2.3 2.2,-2.3c0.4,0 0.8,0.1 1,0.2l0,-2.4c-0.2,-0.1 -0.5,-0.1 -0.8,-0.1c-1.2,0 -2.1,0.7 -2.4,2l-0.1,0l0,-1.9l-2.7,0l0,10.2l0.1,0zm11.7,0.1c-3.1,0 -5,-2 -5,-5.3c0,-3.3 2,-5.3 5,-5.3s5,2 5,5.3c0,3.4 -1.9,5.3 -5,5.3zm0,-2.1c1.4,0 2.2,-1.1 2.2,-3.2c0,-2 -0.8,-3.2 -2.2,-3.2c-1.4,0 -2.2,1.2 -2.2,3.2c0,2.1 0.8,3.2 2.2,3.2z" class="st0" id="Ant-Design-Pro"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" version="1.1" viewBox="0 0 200 200"><title>Group 28 Copy 5</title><desc>Created with Sketch.</desc><defs><linearGradient id="linearGradient-1" x1="62.102%" x2="108.197%" y1="0%" y2="37.864%"><stop offset="0%" stop-color="#4285EB"/><stop offset="100%" stop-color="#2EC7FF"/></linearGradient><linearGradient id="linearGradient-2" x1="69.644%" x2="54.043%" y1="0%" y2="108.457%"><stop offset="0%" stop-color="#29CDFF"/><stop offset="37.86%" stop-color="#148EFF"/><stop offset="100%" stop-color="#0A60FF"/></linearGradient><linearGradient id="linearGradient-3" x1="69.691%" x2="16.723%" y1="-12.974%" y2="117.391%"><stop offset="0%" stop-color="#FA816E"/><stop offset="41.473%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient><linearGradient id="linearGradient-4" x1="68.128%" x2="30.44%" y1="-35.691%" y2="114.943%"><stop offset="0%" stop-color="#FA8E7D"/><stop offset="51.264%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient></defs><g id="Page-1" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="logo" transform="translate(-20.000000, -20.000000)"><g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)"><g id="Group-27-Copy-3"><g id="Group-25" fill-rule="nonzero"><g id="2"><path id="Shape" fill="url(#linearGradient-1)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/><path id="Shape" fill="url(#linearGradient-2)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/></g><path id="Shape" fill="url(#linearGradient-3)" d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z"/></g><ellipse id="Combined-Shape" cx="100.519" cy="100.437" fill="url(#linearGradient-4)" rx="23.6" ry="23.581"/></g></g></g></g></svg>

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;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save