Browse Source

后台工程代码3.2版本

master
dy-hu 3 years ago
commit
e5474daf2c
  1. 8
      .gitignore
  2. 28
      .run/yshop-admin_Dockerfile.run.xml
  3. 25
      .run/yshop-app_Dockerfile.run.xml
  4. BIN
      Alibaba-PuHuiTi-Regular.otf
  5. BIN
      fx.jpg
  6. 270
      pom.xml
  7. BIN
      poster.jpg
  8. 64
      shell/jar-run2.sh
  9. 1
      shell/log.sh
  10. 1
      shell/start.sh
  11. 81
      shell/startup.sh
  12. 7
      shell/stop.sh
  13. 81
      shell/yshop.sh
  14. BIN
      simsunb.ttf
  15. 11450
      sql/.yshop.version/yshop-V1.1.pdman.json
  16. 11402
      sql/.yshop.version/yshop-V1.2.pdman.json
  17. 11247
      sql/.yshop.version/yshop-base.pdman.json
  18. 136
      sql/3.0升级3.1sql.sql
  19. 46
      sql/3.1升级3.2sql.sql
  20. 4
      sql/sql执行顺序说明.txt
  21. 12327
      sql/yshop.pdman.json
  22. 7378
      sql/yshop3.2.sql
  23. 18
      yshop-admin/Dockerfile
  24. 89
      yshop-admin/pom.xml
  25. 69
      yshop-admin/src/main/java/co/yixiang/AppRun.java
  26. 69
      yshop-admin/src/main/java/co/yixiang/config/ConfigurerAdapter.java
  27. 47
      yshop-admin/src/main/java/co/yixiang/config/CorsFilter.java
  28. 95
      yshop-admin/src/main/java/co/yixiang/config/DataScope.java
  29. 22
      yshop-admin/src/main/java/co/yixiang/config/MybatisPlusConfig.java
  30. 24
      yshop-admin/src/main/java/co/yixiang/config/WebSocketConfig.java
  31. 60
      yshop-admin/src/main/java/co/yixiang/config/thread/AsyncTaskExecutePool.java
  32. 29
      yshop-admin/src/main/java/co/yixiang/config/thread/AsyncTaskProperties.java
  33. 53
      yshop-admin/src/main/java/co/yixiang/config/thread/TheadFactoryName.java
  34. 32
      yshop-admin/src/main/java/co/yixiang/config/thread/ThreadPoolExecutorUtil.java
  35. 36
      yshop-admin/src/main/java/co/yixiang/handler/ApiErr.java
  36. 43
      yshop-admin/src/main/java/co/yixiang/handler/ApiError.java
  37. 130
      yshop-admin/src/main/java/co/yixiang/handler/GlobalExceptionHandler.java
  38. 26
      yshop-admin/src/main/java/co/yixiang/modules/monitor/config/VisitsInitialization.java
  39. 35
      yshop-admin/src/main/java/co/yixiang/modules/monitor/domain/Visits.java
  40. 24
      yshop-admin/src/main/java/co/yixiang/modules/monitor/domain/vo/RedisVo.java
  41. 34
      yshop-admin/src/main/java/co/yixiang/modules/monitor/rest/LimitController.java
  42. 55
      yshop-admin/src/main/java/co/yixiang/modules/monitor/rest/RedisController.java
  43. 47
      yshop-admin/src/main/java/co/yixiang/modules/monitor/rest/VisitsController.java
  44. 44
      yshop-admin/src/main/java/co/yixiang/modules/monitor/service/RedisService.java
  45. 38
      yshop-admin/src/main/java/co/yixiang/modules/monitor/service/VisitsService.java
  46. 87
      yshop-admin/src/main/java/co/yixiang/modules/monitor/service/impl/RedisServiceImpl.java
  47. 97
      yshop-admin/src/main/java/co/yixiang/modules/monitor/service/impl/VisitsServiceImpl.java
  48. 15
      yshop-admin/src/main/java/co/yixiang/modules/monitor/service/mapper/VisitsMapper.java
  49. 44
      yshop-admin/src/main/java/co/yixiang/modules/quartz/config/JobRunner.java
  50. 62
      yshop-admin/src/main/java/co/yixiang/modules/quartz/config/QuartzConfig.java
  51. 65
      yshop-admin/src/main/java/co/yixiang/modules/quartz/domain/QuartzJob.java
  52. 65
      yshop-admin/src/main/java/co/yixiang/modules/quartz/domain/QuartzLog.java
  53. 150
      yshop-admin/src/main/java/co/yixiang/modules/quartz/rest/QuartzJobController.java
  54. 67
      yshop-admin/src/main/java/co/yixiang/modules/quartz/service/QuartzJobService.java
  55. 47
      yshop-admin/src/main/java/co/yixiang/modules/quartz/service/QuartzLogService.java
  56. 47
      yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzJobDto.java
  57. 32
      yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzJobQueryCriteria.java
  58. 46
      yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzLogDto.java
  59. 16
      yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzLogQueryCriteria.java
  60. 168
      yshop-admin/src/main/java/co/yixiang/modules/quartz/service/impl/QuartzJobServiceImpl.java
  61. 100
      yshop-admin/src/main/java/co/yixiang/modules/quartz/service/impl/QuartzLogServiceImpl.java
  62. 19
      yshop-admin/src/main/java/co/yixiang/modules/quartz/service/mapper/QuartzJobMapper.java
  63. 19
      yshop-admin/src/main/java/co/yixiang/modules/quartz/service/mapper/QuartzLogMapper.java
  64. 27
      yshop-admin/src/main/java/co/yixiang/modules/quartz/task/TestTask.java
  65. 22
      yshop-admin/src/main/java/co/yixiang/modules/quartz/task/VisitsTask.java
  66. 78
      yshop-admin/src/main/java/co/yixiang/modules/quartz/utils/ExecutionJob.java
  67. 173
      yshop-admin/src/main/java/co/yixiang/modules/quartz/utils/QuartzManage.java
  68. 49
      yshop-admin/src/main/java/co/yixiang/modules/quartz/utils/QuartzRunnable.java
  69. 139
      yshop-admin/src/main/java/co/yixiang/modules/security/config/SecurityConfig.java
  70. 43
      yshop-admin/src/main/java/co/yixiang/modules/security/config/SecurityProperties.java
  71. 161
      yshop-admin/src/main/java/co/yixiang/modules/security/rest/AuthController.java
  72. 83
      yshop-admin/src/main/java/co/yixiang/modules/security/rest/OnlineController.java
  73. 27
      yshop-admin/src/main/java/co/yixiang/modules/security/security/JwtAccessDeniedHandler.java
  74. 29
      yshop-admin/src/main/java/co/yixiang/modules/security/security/JwtAuthenticationEntryPoint.java
  75. 29
      yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenConfigurer.java
  76. 75
      yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenFilter.java
  77. 118
      yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenProvider.java
  78. 238
      yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenUtil.java
  79. 35
      yshop-admin/src/main/java/co/yixiang/modules/security/security/vo/AuthUser.java
  80. 90
      yshop-admin/src/main/java/co/yixiang/modules/security/security/vo/JwtUser.java
  81. 198
      yshop-admin/src/main/java/co/yixiang/modules/security/service/OnlineUserService.java
  82. 71
      yshop-admin/src/main/java/co/yixiang/modules/security/service/UserDetailsServiceImpl.java
  83. 56
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/Dept.java
  84. 49
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/Dict.java
  85. 56
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/DictDetail.java
  86. 59
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/Job.java
  87. 92
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/Menu.java
  88. 67
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/Role.java
  89. 36
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/RolesDepts.java
  90. 36
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/RolesMenus.java
  91. 122
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/User.java
  92. 54
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/UserAvatar.java
  93. 36
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/UsersRoles.java
  94. 26
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/vo/MenuMetaVo.java
  95. 38
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/vo/MenuVo.java
  96. 21
      yshop-admin/src/main/java/co/yixiang/modules/system/domain/vo/UserPassVo.java
  97. 137
      yshop-admin/src/main/java/co/yixiang/modules/system/rest/DeptController.java
  98. 108
      yshop-admin/src/main/java/co/yixiang/modules/system/rest/DictController.java
  99. 104
      yshop-admin/src/main/java/co/yixiang/modules/system/rest/DictDetailController.java
  100. 113
      yshop-admin/src/main/java/co/yixiang/modules/system/rest/JobController.java

8
.gitignore

@ -0,0 +1,8 @@
*.classpath
*.project
*.iml
*.factorypath
target
.idea/
*.log
logs

28
.run/yshop-admin_Dockerfile.run.xml

@ -0,0 +1,28 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="yshop-admin/Dockerfile" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="yshop-admin" />
<option name="buildCliOptions" value="" />
<option name="command" value="" />
<option name="containerName" value="" />
<option name="entrypoint" value="" />
<option name="portBindings">
<list>
<DockerPortBindingImpl>
<!--映射端口-->
<option name="containerPort" value="8001" />
<option name="hostIp" value="0.0.0.0" />
<!--容器内部端口-->
<option name="hostPort" value="8001" />
</DockerPortBindingImpl>
</list>
</option>
<!--目录挂载 -v 宿主机:容器 -->
<option name="commandLineOptions" value="-v /home/yshop:/home/yshop -v /home/yshop/logs:/yshop-admin/logs" />
<option name="sourceFilePath" value="yshop-admin/Dockerfile" />
</settings>
</deployment>
<method v="2" />
</configuration>
</component>

25
.run/yshop-app_Dockerfile.run.xml

@ -0,0 +1,25 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="yshop-app/Dockerfile" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="yshop-app" />
<option name="buildCliOptions" value="" />
<option name="command" value="" />
<option name="containerName" value="" />
<option name="entrypoint" value="" />
<option name="portBindings">
<list>
<DockerPortBindingImpl>
<option name="containerPort" value="8008" />
<option name="hostIp" value="0.0.0.0" />
<option name="hostPort" value="8008" />
</DockerPortBindingImpl>
</list>
</option>
<option name="commandLineOptions" value="-v /home/yshop:/home/yshop -v /home/yshop/logs:/yshop-app/logs" />
<option name="sourceFilePath" value="yshop-app/Dockerfile" />
</settings>
</deployment>
<method v="2" />
</configuration>
</component>

BIN
Alibaba-PuHuiTi-Regular.otf

BIN
fx.jpg

After

Width: 600  |  Height: 1000  |  Size: 95 KiB

270
pom.xml

@ -0,0 +1,270 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>co.yixiang</groupId>
<artifactId>yshop</artifactId>
<packaging>pom</packaging>
<version>3.2</version>
<modules>
<module>yshop-common</module>
<module>yshop-logging</module>
<module>yshop-admin</module>
<module>yshop-tools</module>
<module>yshop-generator</module>
<module>yshop-app</module>
<module>yshop-weixin</module>
<module>yshop-shop</module>
<module>yshop-mproot</module>
<module>yshop-mall</module>
<module>yshop-message</module>
</modules>
<name>YSHOP商城管理系统</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<jedis.version>2.9.0</jedis.version>
<swagger.version>2.9.2</swagger.version>
<fastjson.version>1.2.75</fastjson.version>
<druid.version>1.2.4</druid.version>
<hutool.version>5.5.7</hutool.version>
<commons-pool2.version>2.5.0</commons-pool2.version>
<mapstruct.version>1.2.0.Final</mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.6.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- spring cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<!--Spring boot end-->
<!--spring2.0集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>${commons-pool2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- RESTful APIs swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
<!--单应用-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<!--Mysql依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- druid数据源驱动 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!--lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--工具包-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/xerces/xercesImpl -->
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.11.0</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--mapStruct依赖-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>1.20</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 打包时跳过测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<!--阿里云仓库-->
<repository>
<id>aliyun</id>
<name>aliyun-maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://maven.aliyun.com/repository/spring</url>
</repository>
<repository>
<id>central</id>
<name>maven-central</name>
<url>http://central.maven.org/maven2/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

BIN
poster.jpg

After

Width: 690  |  Height: 1130  |  Size: 29 KiB

64
shell/jar-run2.sh

@ -0,0 +1,64 @@
#cription: 启动重启server服务
#端口号,根据此端口号确定PID
PORT=767
#启动命令所在目录
HOME='/home/sszn/task-agc'
#查询出监听了test.jar端口TCP协议的程序
pid=$(ps -ef | grep gdw-agc-task-1.0.0.0.jar | grep -v grep | awk '{print $2}')
start(){
echo "start running cloud-core ............... "
if [ -n "$pid" ]; then
echo "server already start,pid:$pid"
echo "pid:$pid agc-job port:$PORT 服务已经在运行了,请停止后再 执行 sh run.sh start "
return 0
fi
#进入命令所在目录
cd $HOME
# 启动服务控制台日志输出到nohup.out文件中
nohup java -jar gdw-agc-task-1.0.0.0.jar >> /home/sszn/task-agc/log/agc-$(date +%Y-%m-%d).log 2>&1 &
echo "running success agc-job port:$PORT"
echo "agc-job port:$PORT 服务启动成功 ..... "
}
stop(){
echo "stopping running cloud-core ............... "
if [ -z "$pid" ]; then
echo "not find program on port:$PORT"
echo "agc-job port:$PORT 服务已经被关闭了请执行 sh run.sh start "
return 0
fi
#结束程序,使用讯号2,如果不行可以尝试讯号9强制结束
kill -9 $pid
rm -rf $pid
echo "kill program use signal 2,pid:$pid"
}
status(){
if [ -z "$pid" ]; then
echo "not find program on port:$PORT"
else
echo "program is running,pid:$pid"
fi
}
case $1 in
start)
start
;;
stop)
stop
;;
restart)
$0 stop
sleep 2
$0 start
;;
status)
status
;;
*)
echo $"Usage: $0 {start|stop|status}"
exit 0
esac

1
shell/log.sh

@ -0,0 +1 @@
tail -f nohup.out

1
shell/start.sh

@ -0,0 +1 @@
nohup java -jar yshop-app-3.2.jar --spring.profiles.active=prod &

81
shell/startup.sh

@ -0,0 +1,81 @@
#!/usr/bin/env bash
INPUT=$2
FILE_PATH=`readlink -f ${INPUT}`
SERVICE=${INPUT##*/}
SERVICE_NAME=${SERVICE%.*}
DEPLOY_DIR=`pwd`
JVM_OPTS="-server -Xms64m -Xmx128m"
if [[ "$1" = "" ]];
then
echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m"
exit 1
fi
if [[ "$SERVICE" = "" ]];
then
echo -e "\033[0;31m 未输入应用名 \033[0m"
exit 1
fi
LOGS_DIR="$DEPLOY_DIR/logs/$SERVICE_NAME"
echo "$LOGS_DIR"
if [[ ! -d "$LOGS_DIR" ]]; then
mkdir -p ${LOGS_DIR}
fi
LOG_PATH="$LOGS_DIR/stdout.out"
pid=0
start()
{
checkPid
if [[ ! -n "$pid" ]]; then
BUILD_ID=dontKillMe nohup java ${JVM_OPTS} -jar ${FILE_PATH} --spring.profiles.active=prod >> ${LOG_PATH} 2>&1 &
echo "$SERVICE_NAME is starting you can check the $LOG_PATH"
else
echo "$SERVICE_NAME is runing PID: $pid"
fi
}
checkPid()
{
pid=`ps -ef |grep ${FILE_PATH} |grep -v grep |awk '{print $2}'`
}
stop()
{
checkPid
if [[ ! -n "$pid" ]]; then
echo "$SERVICE_NAME not runing"
else
echo "$SERVICE_NAME stop..."
kill -9 ${pid}
fi
}
restart()
{
stop
sleep 2
start
}
status()
{
checkPid
if [[ ! -n "$pid" ]]; then
echo "$SERVICE_NAME not runing"
else
echo "$SERVICE_NAME runing PID: $pid"
fi
}
case $1 in
start) start;;
stop) stop;;
restart) restart;;
status) status;;
*) echo "require start|stop|restart|status" ;;
esac

7
shell/stop.sh

@ -0,0 +1,7 @@
PID=$(ps -ef | grep yshop-app-3.2.jar | grep -v grep | awk '{ print $2 }')
if [ -z "$PID" ];then
echo Application is already stopped
else
echo kill $PID
kill $PID
fi

81
shell/yshop.sh

@ -0,0 +1,81 @@
#!/usr/bin/env bash
INPUT=$2
FILE_PATH=`readlink -f ${INPUT}`
SERVICE=${INPUT##*/}
SERVICE_NAME=${SERVICE%.*}
DEPLOY_DIR=`pwd`
JVM_OPTS="-server -Xms64m -Xmx128m"
if [[ "$1" = "" ]];
then
echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m"
exit 1
fi
if [[ "$SERVICE" = "" ]];
then
echo -e "\033[0;31m 未输入应用名 \033[0m"
exit 1
fi
LOGS_DIR="$DEPLOY_DIR/logs/$SERVICE_NAME"
echo "$LOGS_DIR"
if [[ ! -d "$LOGS_DIR" ]]; then
mkdir -p ${LOGS_DIR}
fi
LOG_PATH="$LOGS_DIR/stdout.out"
pid=0
start()
{
checkPid
if [[ ! -n "$pid" ]]; then
BUILD_ID=dontKillMe nohup java ${JVM_OPTS} -jar ${FILE_PATH} >> ${LOG_PATH} 2>&1 &
echo "$SERVICE_NAME is starting you can check the $LOG_PATH"
else
echo "$SERVICE_NAME is runing PID: $pid"
fi
}
checkPid()
{
pid=`ps -ef |grep ${FILE_PATH} |grep -v grep |awk '{print $2}'`
}
stop()
{
checkPid
if [[ ! -n "$pid" ]]; then
echo "$SERVICE_NAME not runing"
else
echo "$SERVICE_NAME stop..."
kill -9 ${pid}
fi
}
restart()
{
stop
sleep 2
start
}
status()
{
checkPid
if [[ ! -n "$pid" ]]; then
echo "$SERVICE_NAME not runing"
else
echo "$SERVICE_NAME runing PID: $pid"
fi
}
case $1 in
start) start;;
stop) stop;;
restart) restart;;
status) status;;
*) echo "require start|stop|restart|status" ;;
esac

BIN
simsunb.ttf

11450
sql/.yshop.version/yshop-V1.1.pdman.json
File diff suppressed because it is too large
View File

11402
sql/.yshop.version/yshop-V1.2.pdman.json
File diff suppressed because it is too large
View File

11247
sql/.yshop.version/yshop-base.pdman.json
File diff suppressed because it is too large
View File

136
sql/3.0升级3.1sql.sql

@ -0,0 +1,136 @@
-- ----------------------------
-- 字段修改
-- ----------------------------
ALTER TABLE `yx_store_seckill`
CHANGE COLUMN `price` `price` DECIMAL(10,2) UNSIGNED COMMENT '价格' AFTER `info`,
CHANGE COLUMN `cost` `cost` DECIMAL(8,2) UNSIGNED COMMENT '成本' AFTER `price`,
CHANGE COLUMN `ot_price` `ot_price` DECIMAL(10,2) UNSIGNED COMMENT '原价' AFTER `cost`;
ALTER TABLE yx_store_seckill ADD COLUMN spec_type tinyint(1) COMMENT '规格 0单 1多' AFTER time_id;
ALTER TABLE yx_store_seckill ADD COLUMN temp_id INT(10) COMMENT '运费模板id' AFTER spec_type;
ALTER TABLE yx_store_pink ADD COLUMN unique_id VARCHAR(128) COMMENT '库存唯一值' AFTER is_del;
ALTER TABLE yx_store_seckill MODIFY COLUMN give_integral DECIMAL(10,2) UNSIGNED COMMENT '返多少积分';
ALTER TABLE `yx_store_combination`
CHANGE COLUMN `price` `price` DECIMAL(10,2) UNSIGNED NULL DEFAULT '0.00' COMMENT '价格' AFTER `info`,
CHANGE COLUMN `stock` `stock` INT(10) UNSIGNED NULL DEFAULT '0' COMMENT '库存' AFTER `sales`,
CHANGE COLUMN `unit_name` `unit_name` VARCHAR(32) NULL DEFAULT '' COMMENT '单位名' AFTER `browse`;
ALTER TABLE yx_store_combination ADD COLUMN spec_type tinyint(1) COMMENT '规格 0单 1多' AFTER unit_name;
ALTER TABLE yx_store_combination ADD COLUMN temp_id INT(10) COMMENT '运费模板ID' AFTER spec_type;
ALTER TABLE yx_store_combination DROP is_postage;
ALTER TABLE yx_store_combination DROP postage;
ALTER TABLE yx_store_product_attr_value ADD COLUMN pink_price DECIMAL(8,2) NOT NULL COMMENT '拼团价' AFTER brokerage_two;
ALTER TABLE yx_store_product_attr_value ADD COLUMN pink_stock INT(10) NOT NULL COMMENT '拼团库存' AFTER pink_price;
ALTER TABLE yx_store_product_attr_value ADD COLUMN seckill_price DECIMAL(10,2) NOT NULL COMMENT '秒杀价' AFTER pink_stock;
ALTER TABLE yx_store_product_attr_value ADD COLUMN seckill_stock INT(10) NOT NULL COMMENT '秒杀库存' AFTER seckill_price;
ALTER TABLE yx_wechat_template ADD COLUMN type VARCHAR(30) COMMENT '类型:template:模板消息 subscribe:订阅消息' AFTER is_del;
-- ----------------------------
-- 足迹菜单添加
-- ----------------------------
INSERT INTO `yx_system_group_data` VALUES (225, 'yshop_my_menus', '{\"imageArr\":[\"https://consoleapi.xinxintuan.co/file/pic/20200911093912577832.png\"],\"uniapp_url\":\"/pages/shop/GoodsFoot/index\",\"name\":\"我的足迹\",\"id\":225,\"pic\":\"https://consoleapi.xinxintuan.co/file/pic/20200911093912577832.png\",\"sort\":10,\"url\":\"\",\"wxapp_url\":\"\",\"status\":1}', '2020-09-11 09:39:21', '2020-09-11 09:40:49', 10, 1, 0);
-- ----------------------------
-- 菜单添加
-- ----------------------------
INSERT INTO `menu` VALUES (237, b'0', '规格新增、修改', NULL, 233, 1, NULL, NULL, b'0', b'0', NULL, '2020-06-28 16:35:00', 'yxStoreProductRule:add', 2, '2020-07-16 20:09:03', 0);
INSERT INTO `menu` VALUES (238, b'0', '规格删除', NULL, 233, 1, NULL, NULL, b'0', b'0', NULL, '2020-06-28 16:35:00', 'yxStoreProductRule:del', 2, '2020-07-16 20:08:57', 0);
INSERT INTO `menu` VALUES (240, b'0', '新增、修改模板', NULL, 234, 1, NULL, NULL, b'0', b'0', NULL, '2020-06-29 17:16:06', 'yxShippingTemplates:add', 2, '2020-07-16 20:09:11', 0);
INSERT INTO `menu` VALUES (241, b'0', '删除模板', NULL, 234, 2, NULL, NULL, b'0', b'0', NULL, '2020-06-29 17:16:06', 'yxShippingTemplates:del', 2, '2020-07-16 20:09:16', 0);
INSERT INTO `menu` VALUES (242, b'0', '直播管理', 'wechat/live/index', 48, 999, 'weixin', 'wxlive', b'0', b'0', 'Wxlive', '2020-08-10 17:20:54', NULL, 1, NULL, 0);
INSERT INTO `menu` VALUES (243, b'0', '直播商品管理', 'wechat/goods/index', 48, 999, 'weixin', 'wxlivegoods', b'0', b'0', 'WxliveGoods', '2020-08-10 17:20:54', NULL, 1, NULL, 0);
INSERT INTO `menu` VALUES (244, b'0', '拼团商品添加', 'activity/combination/form', 63, 999, NULL, 'combinationAdd', b'0', b'1', 'CombinationAdd', '2020-08-13 21:28:45', 'YXSTORECOMBINATION_EDIT', 1, '2020-08-13 21:31:26', 0);
INSERT INTO `menu` VALUES (245, b'0', '拼团商品修改', 'activity/combination/form', 63, 3, 'anq', 'combinationEdit/:id', b'0', b'1', 'CombinationEdit', '2019-12-24 13:02:23', 'YXSTORECOMBINATION_EDIT', 1, '2020-07-10 16:45:33', 0);
INSERT INTO `menu` VALUES (246, b'0', '秒杀商品添加', 'activity/seckill/form', 63, 999, NULL, 'secKillAdd', b'0', b'1', 'SecKillAdd', '2020-08-13 21:28:45', 'YXSTORESECKILL_EDIT', 1, '2020-08-13 21:31:26', 0);
INSERT INTO `menu` VALUES (247, b'0', '秒杀商品修改', 'activity/seckill/form', 63, 3, 'anq', 'secKillEdit/:id', b'0', b'1', 'SecKillEdit', '2019-12-24 13:02:23', 'YXSTORESECKILL_EDIT', 1, '2020-07-10 16:45:33', 0);
INSERT INTO `menu` VALUES (248, b'0', '多级菜单', NULL, 0, 999, 'menu', 'nested', b'0', b'0', '-', '2020-08-19 11:31:10', NULL, 0, NULL, 0);
INSERT INTO `menu` VALUES (249, b'0', '二级菜单1', 'nested/menu1/index', 248, 999, 'menu', 'menu1', b'0', b'0', '-', '2020-08-19 11:34:34', NULL, 1, NULL, 0);
INSERT INTO `menu` VALUES (250, b'0', '三级菜单1', 'nested/menu1/menu1-1', 249, 999, 'menu', 'menu1-1', b'0', b'0', '-', '2020-08-19 11:35:52', NULL, 1, NULL, 0);
INSERT INTO `menu` VALUES (251, b'0', '三级菜单2', 'nested/menu1/menu1-2', 249, 999, 'menu', 'menu1-2', b'0', b'0', '-', '2020-08-19 11:37:48', NULL, 1, NULL, 0);
INSERT INTO `menu` VALUES (252, b'0', '二级菜单2', 'nested/menu2/index', 248, 999, 'menu', 'menu2', b'0', b'0', '-', '2020-08-19 11:38:35', NULL, 1, NULL, 0);
INSERT INTO `menu` VALUES (253, b'0', '浏览记录', 'monitor/log/mlog', 40, 15, 'log', 'viewlog', b'0', b'0', 'Viewlog', '2020-07-31 09:47:11', 'log:list', 1, '2020-07-31 09:49:39', 0);
INSERT INTO `menu` VALUES (256, b'0', '商品收藏', 'shop/collect/index', 40, 16, 'menu', 'productRelation', b'0', b'0', 'ProductRelation', '2020-09-03 14:32:49', 'yxStoreProductRelation:list', 1, '2020-09-03 16:21:08', 0);
INSERT INTO `menu` VALUES (257, b'0', '用户足迹', 'shop/foot/index', 40, 17, 'list', 'footRelation', b'0', b'0', 'FootRelation', '2020-09-03 16:20:21', 'yxStoreProductRelation:list', 1, '2020-09-03 16:21:16', 0);
INSERT INTO `menu` VALUES (258, b'0', '订单详情', 'shop/order/detail', 53, 999, 'sqlMonitor', 'detail/:id', b'0', b'1', 'Detail', '2020-09-10 07:29:34', NULL, 1, '2020-09-10 08:52:09', 0);
-- ----------------------------
-- 菜单添加权限
-- ----------------------------
INSERT INTO `roles_menus` VALUES (237, 1);
INSERT INTO `roles_menus` VALUES (238, 1);
INSERT INTO `roles_menus` VALUES (240, 1);
INSERT INTO `roles_menus` VALUES (241, 1);
INSERT INTO `roles_menus` VALUES (242, 1);
INSERT INTO `roles_menus` VALUES (243, 1);
INSERT INTO `roles_menus` VALUES (244, 1);
INSERT INTO `roles_menus` VALUES (245, 1);
INSERT INTO `roles_menus` VALUES (246, 1);
INSERT INTO `roles_menus` VALUES (247, 1);
INSERT INTO `roles_menus` VALUES (248, 1);
INSERT INTO `roles_menus` VALUES (249, 1);
INSERT INTO `roles_menus` VALUES (250, 1);
INSERT INTO `roles_menus` VALUES (251, 1);
INSERT INTO `roles_menus` VALUES (252, 1);
INSERT INTO `roles_menus` VALUES (253, 1);
INSERT INTO `roles_menus` VALUES (256, 1);
INSERT INTO `roles_menus` VALUES (257, 1);
INSERT INTO `roles_menus` VALUES (258, 1);
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for yx_wechat_live
-- ----------------------------
DROP TABLE IF EXISTS `yx_wechat_live`;
CREATE TABLE `yx_wechat_live` (
`room_id` bigint(11) NOT NULL COMMENT '直播间id',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '直播间标题',
`cover_imge` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '背景图',
`share_imge` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分享图片',
`live_status` int(9) NULL DEFAULT NULL COMMENT '直播间状态',
`start_time` bigint(11) NULL DEFAULT NULL COMMENT '开始时间',
`end_time` bigint(11) NULL DEFAULT NULL COMMENT '预计结束时间',
`anchor_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '主播昵称',
`anchor_wechat` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '主播微信号',
`anchor_imge` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '主播头像',
`type` tinyint(1) NULL DEFAULT NULL COMMENT '直播间类型 1:推流 0:手机直播',
`screen_type` tinyint(1) NULL DEFAULT NULL COMMENT '横屏、竖屏 【1:横屏,0:竖屏】',
`close_like` tinyint(1) NULL DEFAULT NULL COMMENT '是否关闭点赞 【0:开启,1:关闭】',
`close_comment` tinyint(1) NULL DEFAULT NULL COMMENT '是否关闭评论 【0:开启,1:关闭】',
`close_goods` tinyint(1) NULL DEFAULT NULL COMMENT '是否关闭货架 【0:开启,1:关闭】',
`product_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品id 多个,分割',
`close_replay` tinyint(1) NULL DEFAULT NULL COMMENT '是否关闭回放【0:开启,1:关闭】',
`close_share` tinyint(1) NULL DEFAULT NULL COMMENT '是否关闭分享【0:开启,1:关闭】',
`close_kf` tinyint(1) NULL DEFAULT NULL COMMENT '是否关闭客服【0:开启,1:关闭】',
PRIMARY KEY (`room_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '微信直播表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for yx_wechat_live_goods
-- ----------------------------
DROP TABLE IF EXISTS `yx_wechat_live_goods`;
CREATE TABLE `yx_wechat_live_goods` (
`goods_id` bigint(9) NOT NULL COMMENT '直播商品id',
`product_id` bigint(9) NULL DEFAULT NULL COMMENT '关联商品id',
`cover_imge_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '商品图片',
`url` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '商品小程序路径',
`price_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '价格类型 1:一口价(只需要传入price,price2不传) 2:价格区间(price字段为左边界,price2字段为右边界,price和price2必传) 3:显示折扣价(price字段为原价,price2字段为现价, price和price2必传)',
`price` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`price2` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '商品名称',
`third_party_tag` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '1, 2:表示是为api添加商品,否则是直播控制台添加的商品',
`audit_id` bigint(20) NULL DEFAULT NULL COMMENT '审核单id',
`audit_status` int(1) UNSIGNED ZEROFILL NULL DEFAULT NULL COMMENT '审核状态 0:未审核,1:审核中,2:审核通过,3审核失败',
PRIMARY KEY (`goods_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '微信直播商品表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
drop index uid on yx_store_product_relation;

46
sql/3.1升级3.2sql.sql

@ -0,0 +1,46 @@
-- ----------------------------
-- 字段修改
-- ----------------------------
ALTER TABLE yx_store_product ADD COLUMN is_integral tinyint(1) ZEROFILL NULL DEFAULT 0 COMMENT '是开启积分兑换' AFTER is_del;
ALTER TABLE yx_store_product ADD COLUMN integral int(11) NULL DEFAULT 0 COMMENT '需要多少积分兑换 只在开启积分兑换时生效' AFTER is_integral;
ALTER TABLE yx_store_product_attr_value ADD COLUMN integral INT(10) DEFAULT 0 COMMENT '需要多少积分兑换' AFTER seckill_price;
ALTER TABLE yx_store_order ADD COLUMN `pay_integral` decimal(8, 2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '实际支付积分';
ALTER TABLE yx_user modify column add_ip varchar(100) NULL DEFAULT '' COMMENT '添加ip';
ALTER TABLE yx_user modify column last_ip varchar(100) NULL DEFAULT '' COMMENT '最后一次登录ip';
INSERT INTO `menu` VALUES (264, b'0', '终端装修', NULL, 0, 0, 'theme', 'theme', b'0', b'0', '--', '2021-02-25 19:33:17', '', 1, '2021-02-25 19:33:32', 0);
INSERT INTO `menu` VALUES (265, b'1', '商城装修', NULL, 264, 999, 'theme', 'https://demo2.yixiang.co/container', b'0', b'0', '-', '2021-02-25 19:35:13', NULL, 1, NULL, 0);
INSERT INTO `roles_menus` VALUES (264, 1);
INSERT INTO `roles_menus` VALUES (265, 1);
-- ----------------------------
-- Table structure for yx_store_canvas
-- ----------------------------
DROP TABLE IF EXISTS `yx_store_canvas`;
CREATE TABLE `yx_store_canvas` (
`canvas_id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '画布id',
`terminal` tinyint(1) NOT NULL COMMENT '终端 1-小程序 2-H5 3-APP 4-PC',
`json` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '画布json数据',
`type` tinyint(1) NULL DEFAULT 1 COMMENT '类型 1-系统画布 2-自定义页面 3-商家店铺装修',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '名称',
`shop_id` bigint(20) NULL DEFAULT 0 COMMENT '店铺id,当type=3的时候,值为具体的店铺id,其它情况为0',
`create_time` timestamp(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_time` timestamp(0) NULL DEFAULT NULL COMMENT '修改时间',
`is_del` tinyint(1) NULL DEFAULT NULL COMMENT '删除标识',
PRIMARY KEY (`canvas_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '画布信息表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of yx_store_canvas
-- ----------------------------
INSERT INTO `yx_store_canvas` VALUES (11, 3, '[{\"title\":\"店铺头部\",\"type\":\"header\",\"componentContent\":{\"title\":\"Yshop\"},\"index\":0},{\"title\":\"搜索商品\",\"type\":\"search\",\"componentContent\":{},\"index\":1},{\"title\":\"轮播图\",\"type\":\"banner\",\"componentContent\":{\"bannerData\":[{\"pic\":\"./static/img/banner.f96c3f5.png\",\"name\":\"0\",\"sort\":0,\"url\":\"/\",\"status\":1}]},\"index\":2},{\"title\":\"滚动新闻\",\"type\":\"noticeBar\",\"componentContent\":{\"roll\":[{\"uniapp_url\":\"/pages/shop/news/NewsList/index\",\"url\":\"/news_list\",\"info\":\"yshop基于springboot2+Mybatisplus商城系统,3.0版本重构了代码,新增了运费模板、sku单独管理、商品券等\",\"wxapp_url\":\"/pages/shop/news/NewsList/main\"}]},\"index\":3},{\"title\":\"菜单\",\"type\":\"menu\",\"componentContent\":{\"menus\":[{\"imageArr\":[\"https://image.dayouqiantu.cn/5e85bfa61251d.png\"],\"uniapp_url\":\"/pages/shop/GoodsList/index\",\"name\":\"全部商品\",\"id\":180,\"pic\":\"https://image.dayouqiantu.cn/all.png\",\"sort\":9,\"url\":\"/goods_list\",\"wxapp_url\":\"/pages/shop/GoodsClass/main\",\"status\":1},{\"imageArr\":[\"https://image.dayouqiantu.cn/news.png\"],\"uniapp_url\":\"/pages/shop/news/NewsList/index\",\"name\":\"图文资讯\",\"id\":196,\"pic\":\"https://image.dayouqiantu.cn/xw.png\",\"sort\":8,\"url\":\"/news_list\",\"wxapp_url\":\"/pages/shop/news/NewsList/main\",\"status\":1},{\"imageArr\":[\"https://image.dayouqiantu.cn/5e4e939507b5e.png\"],\"uniapp_url\":\"/pages/shop/GoodsCollection/index\",\"name\":\"我的收藏\",\"id\":197,\"pic\":\"https://image.dayouqiantu.cn/colle.png\",\"sort\":7,\"url\":\"/collection\",\"wxapp_url\":\"/pages/shop/GoodsCollection/main\",\"status\":1},{\"imageArr\":[\"https://image.dayouqiantu.cn/coupon.png\"],\"uniapp_url\":\"\",\"name\":\"优惠券\",\"id\":199,\"pic\":\"https://image.dayouqiantu.cn/cou.png\",\"sort\":6,\"url\":\"/user/get_coupon\",\"wxapp_url\":\"/pages/user/coupon/GetCoupon/main\",\"status\":1},{\"imageArr\":[\"https://image.dayouqiantu.cn/pink.png\"],\"uniapp_url\":\"/pages/activity/GoodsGroup/index\",\"name\":\"拼团专区\",\"id\":200,\"pic\":\"https://image.dayouqiantu.cn/62ac09d2914d36c65b9b59d2147d809a.png\",\"sort\":5,\"url\":\"/activity/group\",\"wxapp_url\":\"/pages/activity/GoodsGroup/main\",\"status\":1},{\"imageArr\":[\"https://image.dayouqiantu.cn/sign.png\"],\"uniapp_url\":\"/pages/user/signIn/Sign/index\",\"name\":\"积分签到\",\"id\":209,\"pic\":\"https://image.dayouqiantu.cn/29ea4acebbf99e7eaf6f85af2b6d79ae.png\",\"sort\":4,\"url\":\"/user/sign\",\"wxapp_url\":\"/pages/user/signIn/Sign/main\",\"status\":1},{\"imageArr\":[\"https://image.dayouqiantu.cn/sekill.png\"],\"uniapp_url\":\"/pages/activity/GoodsSeckill/index\",\"name\":\"秒杀专区\",\"id\":216,\"pic\":\"https://image.dayouqiantu.cn/b0344c148141b50d68db9722708ea49e.png\",\"sort\":3,\"url\":\"/activity/goods_seckill\",\"wxapp_url\":\"/pages/activity/GoodsSeckill/main\",\"status\":1},{\"imageArr\":[\"https://image.dayouqiantu.cn/bargin.png\"],\"uniapp_url\":\"/pages/activity/GoodsBargain/index\",\"name\":\"砍价专区\",\"id\":217,\"pic\":\"https://image.dayouqiantu.cn/bar.png\",\"sort\":2,\"url\":\"/activity/bargain\",\"wxapp_url\":\"/pages/activity/GoodsBargain/main\",\"status\":1}]},\"index\":4},{\"title\":\"广告\",\"type\":\"adv\",\"componentContent\":{\"detail\":{\"list\":[{\"image\":\"https://wx.yixiang.co/static/images/index001.png\",\"url\":\"\",\"uniapp_url\":\"/pages/user/coupon/GetCoupon/index\",\"wxapp_url\":\"/pages/user/coupon/GetCoupon/index\",\"path_type\":1},{\"image\":\"https://wx.yixiang.co/static/images/index002.png\",\"url\":\"\",\"uniapp_url\":\"/pages/shop/GoodsList/index\",\"wxapp_url\":\"/pages/shop/GoodsList/index\",\"path_type\":1},{\"image\":\"https://wx.yixiang.co/static/images/index003.png\",\"url\":\"\",\"uniapp_url\":\"/pages/shop/GoodsList/index?title=\\\"积分商城\\\"&isIntegral=true\",\"wxapp_url\":\"/pages/shop/GoodsList/index?title=\\\"积分商城\\\"&isIntegral=true\",\"path_type\":1}],\"name\":\"\",\"style\":3}},\"index\":5},{\"title\":\"热门榜单\",\"type\":\"hotCommodity\",\"componentContent\":{},\"index\":6},{\"title\":\"为您推荐\",\"type\":\"promotionGood\",\"componentContent\":{},\"index\":7}]', 1, '1', 0, '2021-02-25 19:36:06', '2021-02-25 22:39:55', 0);

4
sql/sql执行顺序说明.txt

@ -0,0 +1,4 @@
1、3.0平滑升级3.2 直接导入3.0升级3.1sql.sql再执行3.1升级3.2sql.sql(特别说明:平滑升级只限于没有改过添加过菜单之类的,如果你已经添加了菜单,请自行解决一下)
2、如果你已经是3.1版本了直接执行3.1升级3.2sql.sql即可
3、如果是全新下载的直接导入yshop3.2.sql
4、注意:操作数据库属于危险行为,请勿用于生产库,请事先做好备份,再执行sql操作!!!

12327
sql/yshop.pdman.json
File diff suppressed because it is too large
View File

7378
sql/yshop3.2.sql
File diff suppressed because it is too large
View File

18
yshop-admin/Dockerfile

@ -0,0 +1,18 @@
FROM moxm/java:1.8-full as builder
MAINTAINER wangiegie@gmail.com
ENV TZ=Asia/Shanghai
ENV JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN mkdir -p /yshop-admin
WORKDIR /yshop-admin
EXPOSE 8001
ADD ./target/yshop-admin-3.2.jar ./
CMD java $JAVA_OPTS -jar yshop-admin-3.2.jar --spring.profiles.active=docker

89
yshop-admin/pom.xml

@ -0,0 +1,89 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>yshop</artifactId>
<groupId>co.yixiang</groupId>
<version>3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yshop-admin</artifactId>
<name>后台管理模块</name>
<properties>
<jjwt.version>0.10.6</jjwt.version>
</properties>
<dependencies>
<!-- 代码生成模块 -->
<dependency>
<groupId>co.yixiang</groupId>
<artifactId>yshop-generator</artifactId>
<version>3.2</version>
<exclusions>
<exclusion>
<groupId>co.yixiang</groupId>
<artifactId>yshop-common</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>co.yixiang</groupId>
<artifactId>yshop-shop</artifactId>
<version>3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!--jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>io.jsonwebtoken</groupId>-->
<!-- <artifactId>jjwt-api</artifactId>-->
<!-- <version>${jjwt.version}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>io.jsonwebtoken</groupId>-->
<!-- <artifactId>jjwt-impl</artifactId>-->
<!-- <version>${jjwt.version}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>io.jsonwebtoken</groupId>-->
<!-- <artifactId>jjwt-jackson</artifactId>-->
<!-- <version>${jjwt.version}</version>-->
<!-- </dependency>-->
<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 跳过单元测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

69
yshop-admin/src/main/java/co/yixiang/AppRun.java

@ -0,0 +1,69 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang;
import co.yixiang.annotation.AnonymousAccess;
import co.yixiang.utils.SpringContextHolder;
import com.binarywang.spring.starter.wxjava.miniapp.config.WxMaAutoConfiguration;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author hupeng
* @date 2018/11/15 9:20:19
*/
@EnableAsync
@RestController
@SpringBootApplication(exclude = {WxMaAutoConfiguration.class})
@EnableTransactionManagement
@MapperScan(basePackages ={"co.yixiang.modules.*.service.mapper", "co.yixiang.config"})
public class AppRun {
public static void main(String[] args) {
SpringApplication.run(AppRun.class, args);
System.out.println(
" __ \n" +
" __ __ ___ / / ___ ___ \n" +
" / // /(_-< / _ \\/ _ \\ / _ \\ \n" +
" \\_, //___//_//_/\\___// .__/ \n" +
"/___/ /_/ \n "+
"\n意象yshop电商系统管理后台启动成功 \n官网:https://www.yixiang.co 提供技术支持゙ \n");
}
@Bean
public SpringContextHolder springContextHolder() {
return new SpringContextHolder();
}
@Bean
public ServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory fa = new TomcatServletWebServerFactory();
fa.addConnectorCustomizers(connector -> connector.setProperty("relaxedQueryChars", "[]{}"));
return fa;
}
/**
* 访问首页提示
* @return /
*/
@GetMapping("/")
@AnonymousAccess
public String index() {
return "Backend service started successfully";
}
}

69
yshop-admin/src/main/java/co/yixiang/config/ConfigurerAdapter.java

@ -0,0 +1,69 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.lang.reflect.Array;
import java.util.Arrays;
/**
* WebMvcConfigurer
*
* @author hupeng
* @date 2018-11-30
*/
@Configuration(proxyBeanMethods = false)
@EnableWebMvc
public class ConfigurerAdapter implements WebMvcConfigurer {
@Value("${file.path}")
private String path;
@Value("${file.avatar}")
private String avatar;
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
// 允许cookies跨域
config.setAllowCredentials(true);
// #允许向该服务器提交请求的URI*表示全部允许在SpringMVC中如果设成*会自动转成当前请求头中的Origin
config.addAllowedOriginPattern("*");
// #允许访问的头信息,*表示全部
config.addAllowedHeader("*");
// 预检请求的缓存时间即在这个时间段里对于相同的跨域请求不会再预检了
config.setMaxAge(18000L);
// 允许提交请求的方法类型*表示全部允许
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String avatarUtl = "file:" + avatar.replace("\\","/");
String pathUtl = "file:" + path.replace("\\","/");
registry.addResourceHandler("/avatar/**").addResourceLocations(avatarUtl).setCachePeriod(0);
registry.addResourceHandler("/file/**").addResourceLocations(pathUtl).setCachePeriod(0);
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0);
}
}

47
yshop-admin/src/main/java/co/yixiang/config/CorsFilter.java

@ -0,0 +1,47 @@
//package co.yixiang.config;
//
///**
// * @author LionCity
// * @date Created in 2020-12-21 13:38
// * @description
// * @modified By
// * @version:
// */
//import java.io.IOException;
//
//import javax.servlet.FilterChain;
//import javax.servlet.ServletException;
//import javax.servlet.http.HttpFilter;
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//
//import co.yixiang.utils.StringUtils;
//import org.springframework.core.annotation.Order;
//import org.springframework.http.HttpHeaders;
//import org.springframework.stereotype.Component;
//
//@Component
//@Order(-9999)
//public class CorsFilter extends HttpFilter {
//
// /**
// *
// */
// private static final long serialVersionUID = -8387103310559517243L;
//
// @Override
// protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
//
// String origin = req.getHeader(HttpHeaders.ORIGIN);
//
// if (!StringUtils.isEmpty(origin)){
// res.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
// res.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "Origin, x-requested-with, Content-Type, Accept, Authorization");
// res.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
// res.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, OPTIONS, DELETE");
// res.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma");
// res.addHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "60");
// }
// super.doFilter(req, res, chain);
// }
//}

95
yshop-admin/src/main/java/co/yixiang/config/DataScope.java

@ -0,0 +1,95 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.config;
import co.yixiang.modules.system.domain.Dept;
import co.yixiang.modules.system.service.DeptService;
import co.yixiang.modules.system.service.RoleService;
import co.yixiang.modules.system.service.UserService;
import co.yixiang.modules.system.service.dto.RoleSmallDto;
import co.yixiang.modules.system.service.dto.UserDto;
import co.yixiang.utils.SecurityUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 数据权限配置
* @author hupeng
* @date 2019-4-1
*/
@Component
public class DataScope {
private final String[] scopeType = {"全部","本级","自定义"};
private final UserService userService;
private final RoleService roleService;
private final DeptService deptService;
public DataScope(UserService userService, RoleService roleService, DeptService deptService) {
this.userService = userService;
this.roleService = roleService;
this.deptService = deptService;
}
public Set<Long> getDeptIds() {
UserDto user = userService.findByName(SecurityUtils.getUsername());
// 用于存储部门id
Set<Long> deptIds = new HashSet<>();
// 查询用户角色
List<RoleSmallDto> roleSet = roleService.findByUsersId(user.getId());
for (RoleSmallDto role : roleSet) {
if (scopeType[0].equals(role.getDataScope())) {
return new HashSet<>() ;
}
// 存储本级的数据权限
if (scopeType[1].equals(role.getDataScope())) {
deptIds.add(user.getDept().getId());
}
// 存储自定义的数据权限
if (scopeType[2].equals(role.getDataScope())) {
Set<Dept> depts = deptService.findByRoleIds(role.getId());
for (Dept dept : depts) {
deptIds.add(dept.getId());
List<Dept> deptChildren = deptService.findByPid(dept.getId());
if (deptChildren != null && deptChildren.size() != 0) {
deptIds.addAll(getDeptChildren(deptChildren));
}
}
}
}
return deptIds;
}
public List<Long> getDeptChildren(List<Dept> deptList) {
List<Long> list = new ArrayList<>();
deptList.forEach(dept -> {
if (dept!=null && dept.getEnabled()){
List<Dept> depts = deptService.findByPid(dept.getId());
if(deptList.size() != 0){
list.addAll(getDeptChildren(depts));
}
list.add(dept.getId());
}
}
);
return list;
}
}

22
yshop-admin/src/main/java/co/yixiang/config/MybatisPlusConfig.java

@ -0,0 +1,22 @@
package co.yixiang.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MybatisPlus配置
*
*/
@Configuration(proxyBeanMethods = false)
public class MybatisPlusConfig {
/**
* mybatis-plus分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}

24
yshop-admin/src/main/java/co/yixiang/config/WebSocketConfig.java

@ -0,0 +1,24 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @author: ZhangHouYing
* @date: 2019-08-24 15:44
*/
@Configuration(proxyBeanMethods = false)
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

60
yshop-admin/src/main/java/co/yixiang/config/thread/AsyncTaskExecutePool.java

@ -0,0 +1,60 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.config.thread;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 异步任务线程池装配类
* @author https://juejin.im/entry/5abb8f6951882555677e9da2
* @date 2019年10月31日15:06:18
*/
@Slf4j
@Configuration(proxyBeanMethods = false)
public class AsyncTaskExecutePool implements AsyncConfigurer {
/** 注入配置类 */
private final AsyncTaskProperties config;
public AsyncTaskExecutePool(AsyncTaskProperties config) {
this.config = config;
}
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程池大小
executor.setCorePoolSize(config.getCorePoolSize());
//最大线程数
executor.setMaxPoolSize(config.getMaxPoolSize());
//队列容量
executor.setQueueCapacity(config.getQueueCapacity());
//活跃时间
executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
//线程名字前缀
executor.setThreadNamePrefix("el-async-");
// setRejectedExecutionHandler当pool已经达到max size的时候如何处理新任务
// CallerRunsPolicy不在新线程中执行任务而是由调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (throwable, method, objects) -> {
log.error("===="+throwable.getMessage()+"====", throwable);
log.error("exception method:"+method.getName());
};
}
}

29
yshop-admin/src/main/java/co/yixiang/config/thread/AsyncTaskProperties.java

@ -0,0 +1,29 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.config.thread;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 线程池配置属性类
* @author https://juejin.im/entry/5abb8f6951882555677e9da2
* @date 2019年10月31日14:58:18
*/
@Data
@Component
@ConfigurationProperties(prefix = "task.pool")
public class AsyncTaskProperties {
private int corePoolSize;
private int maxPoolSize;
private int keepAliveSeconds;
private int queueCapacity;
}

53
yshop-admin/src/main/java/co/yixiang/config/thread/TheadFactoryName.java

@ -0,0 +1,53 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.config.thread;
import org.springframework.stereotype.Component;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 自定义线程名称
* @author hupeng
* @date 2019年10月31日17:49:55
*/
@Component
public class TheadFactoryName implements ThreadFactory {
private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public TheadFactoryName() {
this("el-pool");
}
private TheadFactoryName(String name){
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
//此时namePrefix就是 name + 第几个用这个工厂创建线程池的
this.namePrefix = name +
POOL_NUMBER.getAndIncrement();
}
@Override
public Thread newThread(Runnable r) {
//此时线程的名字 就是 namePrefix + -thread- + 这个线程池中第几个执行的线程
Thread t = new Thread(group, r,
namePrefix + "-thread-"+threadNumber.getAndIncrement(),
0);
if (t.isDaemon()) {
t.setDaemon(false);
}
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}

32
yshop-admin/src/main/java/co/yixiang/config/thread/ThreadPoolExecutorUtil.java

@ -0,0 +1,32 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.config.thread;
import co.yixiang.utils.SpringContextHolder;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 用于获取自定义线程池
* @author hupeng
* @date 2019年10月31日18:16:47
*/
public class ThreadPoolExecutorUtil {
public static ThreadPoolExecutor getPoll(){
AsyncTaskProperties properties = SpringContextHolder.getBean(AsyncTaskProperties.class);
return new ThreadPoolExecutor(
properties.getCorePoolSize(),
properties.getMaxPoolSize(),
properties.getKeepAliveSeconds(),
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(properties.getQueueCapacity()),
new TheadFactoryName()
);
}
}

36
yshop-admin/src/main/java/co/yixiang/handler/ApiErr.java

@ -0,0 +1,36 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.handler;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @author hupeng
* @since 2019-10-02
*/
@Data
class ApiErr {
private Integer status;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime timestamp;
private String msg;
private ApiErr() {
timestamp = LocalDateTime.now();
}
public ApiErr(Integer status, String message) {
this();
this.status = status;
this.msg = message;
}
}

43
yshop-admin/src/main/java/co/yixiang/handler/ApiError.java

@ -0,0 +1,43 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.handler;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @author Zheng Jie
* @date 2018-11-23
*/
@Data
class ApiError {
private Integer status = 400;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime timestamp;
private String message;
private ApiError() {
timestamp = LocalDateTime.now();
}
public static ApiError error(String message){
ApiError apiError = new ApiError();
apiError.setMessage(message);
return apiError;
}
public static ApiError error(Integer status, String message){
ApiError apiError = new ApiError();
apiError.setStatus(status);
apiError.setMessage(message);
return apiError;
}
}

130
yshop-admin/src/main/java/co/yixiang/handler/GlobalExceptionHandler.java

@ -0,0 +1,130 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.handler;
import co.yixiang.exception.BadRequestException;
import co.yixiang.exception.EntityExistException;
import co.yixiang.exception.EntityNotFoundException;
import co.yixiang.exception.ErrorRequestException;
import co.yixiang.utils.ThrowableUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.Objects;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.valueOf;
/**
* @author Zheng Jie
* @date 2018-11-23
*/
@Slf4j
@RestControllerAdvice
@SuppressWarnings("unchecked")
public class GlobalExceptionHandler {
/**
* 处理所有不可知的异常
*/
@ExceptionHandler(Throwable.class)
public ResponseEntity<ApiError> handleException(Throwable e){
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
return buildResponseEntity(ApiError.error(e.getMessage()));
}
/**
* BadCredentialsException
*/
@ExceptionHandler(BadCredentialsException.class)
public ResponseEntity<ApiError> badCredentialsException(BadCredentialsException e){
// 打印堆栈信息
String message = "坏的凭证".equals(e.getMessage()) ? "用户名或密码不正确" : e.getMessage();
log.error(message);
return buildResponseEntity(ApiError.error(message));
}
/**
* 处理自定义异常
*/
@ExceptionHandler(value = BadRequestException.class)
public ResponseEntity<ApiError> badRequestException(BadRequestException e) {
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
return buildResponseEntity(ApiError.error(e.getStatus(),e.getMessage()));
}
/**
* 处理自定义异常
* @param e
* @return
*/
@ExceptionHandler(value = ErrorRequestException.class)
public ResponseEntity<ApiErr> errorRequestException(ErrorRequestException e) {
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
ApiErr apiError = new ApiErr(e.getStatus(),e.getMessage());
return buildResponseEntity2(apiError);
}
/**
* 处理 EntityExist
*/
@ExceptionHandler(value = EntityExistException.class)
public ResponseEntity<ApiError> entityExistException(EntityExistException e) {
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
return buildResponseEntity(ApiError.error(e.getMessage()));
}
/**
* 处理 EntityNotFound
*/
@ExceptionHandler(value = EntityNotFoundException.class)
public ResponseEntity<ApiError> entityNotFoundException(EntityNotFoundException e) {
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
return buildResponseEntity(ApiError.error(NOT_FOUND.value(),e.getMessage()));
}
/**
* 处理所有接口数据验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiError> handleMethodArgumentNotValidException(MethodArgumentNotValidException e){
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
String[] str = Objects.requireNonNull(e.getBindingResult().getAllErrors().get(0).getCodes())[1].split("\\.");
String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
String msg = "不能为空";
if(msg.equals(message)){
message = str[1] + ":" + message;
}
return buildResponseEntity(ApiError.error(message));
}
/**
* 统一返回
*/
private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
return new ResponseEntity<>(apiError, valueOf(apiError.getStatus()));
}
/**
* 统一返回
* @param apiError
* @return
*/
private ResponseEntity<ApiErr> buildResponseEntity2(ApiErr apiError) {
return new ResponseEntity(apiError, HttpStatus.valueOf(apiError.getStatus()));
}
}

26
yshop-admin/src/main/java/co/yixiang/modules/monitor/config/VisitsInitialization.java

@ -0,0 +1,26 @@
package co.yixiang.modules.monitor.config;
import co.yixiang.modules.monitor.service.VisitsService;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
/**
* 初始化站点统计
* @author Zheng Jie
*/
//@Component
public class VisitsInitialization implements ApplicationRunner {
private final VisitsService visitsService;
public VisitsInitialization(VisitsService visitsService) {
this.visitsService = visitsService;
}
@Override
public void run(ApplicationArguments args){
System.out.println("--------------- 初始化站点统计,如果存在今日统计则跳过 ---------------");
visitsService.save();
System.out.println("--------------- 初始化站点统计完成 ---------------");
}
}

35
yshop-admin/src/main/java/co/yixiang/modules/monitor/domain/Visits.java

@ -0,0 +1,35 @@
package co.yixiang.modules.monitor.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* pv ip 统计
*
* @author Zheng Jie
* @date 2018-12-13
*/
@Data
@TableName( "visits")
public class Visits implements Serializable {
@TableId
private Long id;
private String date;
private Long pvCounts;
private Long ipCounts;
@TableField(fill = FieldFill.INSERT)
private Timestamp createTime;
private String weekDay;
}

24
yshop-admin/src/main/java/co/yixiang/modules/monitor/domain/vo/RedisVo.java

@ -0,0 +1,24 @@
package co.yixiang.modules.monitor.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* @author Zheng Jie
* @date 2018-12-10
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RedisVo implements Serializable {
@NotBlank
private String key;
@NotBlank
private String value;
}

34
yshop-admin/src/main/java/co/yixiang/modules/monitor/rest/LimitController.java

@ -0,0 +1,34 @@
package co.yixiang.modules.monitor.rest;
import co.yixiang.annotation.AnonymousAccess;
import co.yixiang.annotation.Limit;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author /
* 接口限流测试类
*/
@RestController
@RequestMapping("/api/limit")
@Api(tags = "系统:限流测试管理")
public class LimitController {
private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger();
/**
* 测试限流注解下面配置说明该接口 60秒内最多只能访问 10次保存到redis的键名为 limit_test
*/
@GetMapping
@AnonymousAccess
@ApiOperation("测试")
@Limit(key = "test", period = 60, count = 10, name = "testLimit", prefix = "limit")
public int testLimit() {
return ATOMIC_INTEGER.incrementAndGet();
}
}

55
yshop-admin/src/main/java/co/yixiang/modules/monitor/rest/RedisController.java

@ -0,0 +1,55 @@
package co.yixiang.modules.monitor.rest;
import co.yixiang.logging.aop.log.Log;
import co.yixiang.modules.aop.ForbidSubmit;
import co.yixiang.modules.monitor.domain.vo.RedisVo;
import co.yixiang.modules.monitor.service.RedisService;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Zheng Jie
* @date 2018-12-10
*/
@Api(tags = "redis缓存管理")
@RestController
@RequestMapping("api")
public class RedisController {
@Autowired
private RedisService redisService;
@Log("查询Redis缓存")
@GetMapping(value = "/redis")
@PreAuthorize("hasAnyRole('ADMIN','REDIS_ALL','REDIS_SELECT')")
public ResponseEntity getRedis(String key, Pageable pageable){
return new ResponseEntity(redisService.findByKey(key,pageable), HttpStatus.OK);
}
@ForbidSubmit
@Log("删除Redis缓存")
@DeleteMapping(value = "/redis")
@PreAuthorize("hasAnyRole('ADMIN','REDIS_ALL','REDIS_DELETE')")
public ResponseEntity delete(@RequestBody RedisVo resources){
redisService.delete(resources.getKey());
return new ResponseEntity(HttpStatus.OK);
}
@ForbidSubmit
@Log("清空Redis缓存")
@DeleteMapping(value = "/redis/all")
@PreAuthorize("hasAnyRole('ADMIN','REDIS_ALL','REDIS_DELETE')")
public ResponseEntity deleteAll(){
redisService.flushdb();
return new ResponseEntity(HttpStatus.OK);
}
}

47
yshop-admin/src/main/java/co/yixiang/modules/monitor/rest/VisitsController.java

@ -0,0 +1,47 @@
package co.yixiang.modules.monitor.rest;
import co.yixiang.modules.monitor.service.VisitsService;
import co.yixiang.utils.RequestHolder;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Zheng Jie
* @date 2018-12-13
*/
@RestController
@RequestMapping("/api/visits")
@Api(tags = "系统:访问记录管理")
public class VisitsController {
private final VisitsService visitsService;
public VisitsController(VisitsService visitsService) {
this.visitsService = visitsService;
}
@PostMapping
@ApiOperation("创建访问记录")
public ResponseEntity<Object> create(){
visitsService.count(RequestHolder.getHttpServletRequest());
return new ResponseEntity<>(HttpStatus.CREATED);
}
@GetMapping
@ApiOperation("查询")
public ResponseEntity<Object> get(){
return new ResponseEntity<>(visitsService.get(),HttpStatus.OK);
}
@GetMapping(value = "/chartData")
@ApiOperation("查询图表数据")
public ResponseEntity<Object> getChartData(){
return new ResponseEntity<>(visitsService.getChartData(),HttpStatus.OK);
}
}

44
yshop-admin/src/main/java/co/yixiang/modules/monitor/service/RedisService.java

@ -0,0 +1,44 @@
package co.yixiang.modules.monitor.service;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
/**
* 可自行扩展
* @author Zheng Jie
* @date 2018-12-10
*/
public interface RedisService {
/**
* findById
* @param key
* @return
*/
Page findByKey(String key, Pageable pageable);
/**
* 查询验证码的值
* @param key
* @return
*/
String getCodeVal(String key);
/**
* 保存验证码
* @param key
* @param val
*/
void saveCode(String key, Object val);
/**
* delete
* @param key
*/
void delete(String key);
/**
* 清空所有缓存
*/
void flushdb();
}

38
yshop-admin/src/main/java/co/yixiang/modules/monitor/service/VisitsService.java

@ -0,0 +1,38 @@
package co.yixiang.modules.monitor.service;
import co.yixiang.common.service.BaseService;
import co.yixiang.modules.monitor.domain.Visits;
import org.springframework.scheduling.annotation.Async;
import javax.servlet.http.HttpServletRequest;
/**
* @author Zheng Jie
* @date 2018-12-13
*/
public interface VisitsService extends BaseService<Visits> {
/**
* 提供给定时任务每天0点执行
*/
void save();
/**
* 新增记录
* @param request /
*/
@Async
void count(HttpServletRequest request);
/**
* 获取数据
* @return /
*/
Object get();
/**
* getChartData
* @return /
*/
Object getChartData();
}

87
yshop-admin/src/main/java/co/yixiang/modules/monitor/service/impl/RedisServiceImpl.java

@ -0,0 +1,87 @@
package co.yixiang.modules.monitor.service.impl;
import co.yixiang.modules.monitor.domain.vo.RedisVo;
import co.yixiang.modules.monitor.service.RedisService;
import co.yixiang.utils.PageUtil;
import co.yixiang.utils.ShopKeyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author Zheng Jie
* @date 2018-12-10
*/
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
RedisTemplate redisTemplate;
@Value("${loginCode.expiration}")
private Long expiration;
@Override
public Page<RedisVo> findByKey(String key, Pageable pageable){
List<RedisVo> redisVos = new ArrayList<>();
if(!"*".equals(key)){
key = "*" + key + "*";
}
for (Object s : redisTemplate.keys(key)) {
// 过滤掉权限的缓存
if (s.toString().indexOf("role::loadPermissionByUser") != -1
|| s.toString().indexOf("user::loadUserByUsername") != -1
|| s.toString().indexOf("wechat") != -1
|| s.toString().indexOf("wxpay") != -1
|| s.toString().indexOf(ShopKeyUtils.getSiteUrl()) != -1) {
continue;
}
DataType dataType = redisTemplate.type(s.toString());
if(!"string".equals(dataType.code())) {
continue;
}
RedisVo redisVo = new RedisVo(s.toString(),redisTemplate.opsForValue().get(s.toString()).toString());
redisVos.add(redisVo);
}
Page<RedisVo> page = new PageImpl<RedisVo>(
PageUtil.toPage(pageable.getPageNumber(),pageable.getPageSize(),redisVos),
pageable,
redisVos.size());
return page;
}
@Override
public void delete(String key) {
redisTemplate.delete(key);
}
@Override
public void flushdb() {
redisTemplate.getConnectionFactory().getConnection().flushDb();
}
@Override
public String getCodeVal(String key) {
try {
String value = redisTemplate.opsForValue().get(key).toString();
return value;
}catch (Exception e){
return "";
}
}
@Override
public void saveCode(String key, Object val) {
redisTemplate.opsForValue().set(key,val);
redisTemplate.expire(key,expiration, TimeUnit.MINUTES);
}
}

97
yshop-admin/src/main/java/co/yixiang/modules/monitor/service/impl/VisitsServiceImpl.java

@ -0,0 +1,97 @@
package co.yixiang.modules.monitor.service.impl;
import co.yixiang.common.service.impl.BaseServiceImpl;
import co.yixiang.logging.service.mapper.LogMapper;
import co.yixiang.modules.monitor.domain.Visits;
import co.yixiang.modules.monitor.service.VisitsService;
import co.yixiang.modules.monitor.service.mapper.VisitsMapper;
import co.yixiang.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author hupeng
* @date 2018-12-13
*/
@Slf4j
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class VisitsServiceImpl extends BaseServiceImpl<VisitsMapper, Visits> implements VisitsService {
private final LogMapper logMapper;
private final VisitsMapper visitsMapper;
public VisitsServiceImpl(LogMapper logMapper, VisitsMapper visitsMapper) {
this.logMapper = logMapper;
this.visitsMapper = visitsMapper;
}
@Override
public void save() {
LocalDate localDate = LocalDate.now();
Visits visits = this.getOne(new LambdaQueryWrapper<Visits>()
.eq(Visits::getDate,localDate.toString()));
if(visits == null){
visits = new Visits();
visits.setWeekDay(StringUtils.getWeekDay());
visits.setPvCounts(1L);
visits.setIpCounts(1L);
visits.setDate(localDate.toString());
this.save(visits);
}
}
@Override
public void count(HttpServletRequest request) {
LocalDate localDate = LocalDate.now();
Visits visits = this.getOne(new LambdaQueryWrapper<Visits>()
.eq(Visits::getDate,localDate.toString()));
visits.setPvCounts(visits.getPvCounts()+1);
long ipCounts = logMapper.findIp(localDate.toString(), localDate.plusDays(1).toString());
visits.setIpCounts(ipCounts);
this.saveOrUpdate(visits);
}
@Override
public Object get() {
Map<String,Object> map = new HashMap<>(4);
LocalDate localDate = LocalDate.now();
Visits visits = this.getOne(new LambdaQueryWrapper<Visits>()
.eq(Visits::getDate,localDate.toString()));
List<Visits> list = visitsMapper.findAllVisits(localDate.minusDays(6).toString(),localDate.plusDays(1).toString());
long recentVisits = 0, recentIp = 0;
for (Visits data : list) {
recentVisits += data.getPvCounts();
recentIp += data.getIpCounts();
}
map.put("newVisits",visits.getPvCounts());
map.put("newIp",visits.getIpCounts());
map.put("recentVisits",recentVisits);
map.put("recentIp",recentIp);
return map;
}
@Override
public Object getChartData() {
Map<String,Object> map = new HashMap<>(3);
// LocalDate localDate = LocalDate.now();
// List<Visits> list = visitsRepository.findAllVisits(localDate.minusDays(6).toString(),localDate.plusDays(1).toString());
// map.put("weekDays",list.stream().map(Visits::getWeekDay).collect(Collectors.toList()));
// map.put("visitsData",list.stream().map(Visits::getPvCounts).collect(Collectors.toList()));
// map.put("ipData",list.stream().map(Visits::getIpCounts).collect(Collectors.toList()));
return map;
}
}

15
yshop-admin/src/main/java/co/yixiang/modules/monitor/service/mapper/VisitsMapper.java

@ -0,0 +1,15 @@
package co.yixiang.modules.monitor.service.mapper;
import co.yixiang.common.mapper.CoreMapper;
import co.yixiang.modules.monitor.domain.Visits;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface VisitsMapper extends CoreMapper<Visits> {
@Select("select * FROM visits where create_time between #{time1} and #{time2}")
List<Visits> findAllVisits(@Param("time1") String time1, @Param("time2")String time2);
}

44
yshop-admin/src/main/java/co/yixiang/modules/quartz/config/JobRunner.java

@ -0,0 +1,44 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.config;
import co.yixiang.modules.quartz.domain.QuartzJob;
import co.yixiang.modules.quartz.service.QuartzJobService;
import co.yixiang.modules.quartz.utils.QuartzManage;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author hupeng
* @date 2019-01-07
*/
@Component
public class JobRunner implements ApplicationRunner {
private final QuartzJobService quartzJobService;
private final QuartzManage quartzManage;
public JobRunner(QuartzJobService quartzJobService, QuartzManage quartzManage) {
this.quartzJobService = quartzJobService;
this.quartzManage = quartzManage;
}
/**
* 项目启动时重新激活启用的定时任务
* @param applicationArguments /
*/
@Override
public void run(ApplicationArguments applicationArguments){
System.out.println("--------------------注入定时任务---------------------");
List<QuartzJob> quartzJobs = quartzJobService.findByIsPauseIsFalse();
quartzJobs.forEach(quartzManage::addJob);
System.out.println("--------------------定时任务注入完成---------------------");
}
}

62
yshop-admin/src/main/java/co/yixiang/modules/quartz/config/QuartzConfig.java

@ -0,0 +1,62 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.config;
import org.quartz.Scheduler;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;
/**
* 定时任务配置
* @author /
* @date 2019-01-07
*/
@Configuration(proxyBeanMethods = false)
public class QuartzConfig {
/**
* 解决Job中注入Spring Bean为null的问题
*/
@Component("quartzJobFactory")
public static class QuartzJobFactory extends AdaptableJobFactory {
private final AutowireCapableBeanFactory capableBeanFactory;
public QuartzJobFactory(AutowireCapableBeanFactory capableBeanFactory) {
this.capableBeanFactory = capableBeanFactory;
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
/**
* 注入scheduler到spring
* @param quartzJobFactory /
* @return Scheduler
* @throws Exception /
*/
@Bean(name = "scheduler")
public Scheduler scheduler(QuartzJobFactory quartzJobFactory) throws Exception {
SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
factoryBean.setJobFactory(quartzJobFactory);
factoryBean.afterPropertiesSet();
Scheduler scheduler=factoryBean.getScheduler();
scheduler.start();
return scheduler;
}
}

65
yshop-admin/src/main/java/co/yixiang/modules/quartz/domain/QuartzJob.java

@ -0,0 +1,65 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import co.yixiang.domain.BaseDomain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author hupeng
* @date 2020-05-13
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("quartz_job")
public class QuartzJob extends BaseDomain {
public static final String JOB_KEY = "JOB_KEY";
/** 定时任务ID */
@TableId
private Long id;
/** Spring Bean名称 */
private String beanName;
/** cron 表达式 */
private String cronExpression;
/** 状态:1暂停、0启用 */
private Boolean isPause;
/** 任务名称 */
private String jobName;
/** 方法名称 */
private String methodName;
/** 参数 */
private String params;
/** 备注 */
private String remark;
public void copy(QuartzJob source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

65
yshop-admin/src/main/java/co/yixiang/modules/quartz/domain/QuartzLog.java

@ -0,0 +1,65 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import co.yixiang.domain.BaseDomain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* @author hupeng
* @date 2020-05-13
*/
@Data
@TableName("quartz_log")
public class QuartzLog extends BaseDomain {
/** 任务日志ID */
@TableId
private Long id;
/** 任务名称 */
private String baenName;
/** cron表达式 */
private String cronExpression;
/** 异常详细 */
private String exceptionDetail;
/** 状态 */
private Boolean isSuccess;
/** 任务名称 */
private String jobName;
/** 方法名称 */
private String methodName;
/** 参数 */
private String params;
/** 耗时(毫秒) */
private Long time;
public void copy(QuartzLog source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

150
yshop-admin/src/main/java/co/yixiang/modules/quartz/rest/QuartzJobController.java

@ -0,0 +1,150 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.rest;
import co.yixiang.dozer.service.IGenerator;
import co.yixiang.exception.BadRequestException;
import co.yixiang.logging.aop.log.Log;
import co.yixiang.modules.aop.ForbidSubmit;
import co.yixiang.modules.quartz.domain.QuartzJob;
import co.yixiang.modules.quartz.service.QuartzJobService;
import co.yixiang.modules.quartz.service.QuartzLogService;
import co.yixiang.modules.quartz.service.dto.QuartzJobDto;
import co.yixiang.modules.quartz.service.dto.QuartzJobQueryCriteria;
import co.yixiang.modules.quartz.service.dto.QuartzLogDto;
import co.yixiang.modules.quartz.service.dto.QuartzLogQueryCriteria;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
/**
* @author hupeng
* @date 2019-01-07
*/
@Slf4j
@RestController
@Api(tags = "系统:定时任务管理")
@RequestMapping("/api/jobs")
public class QuartzJobController {
private static final String ENTITY_NAME = "quartzJob";
private final QuartzJobService quartzJobService;
private final IGenerator generator;
private final QuartzLogService quartzLogService;
public QuartzJobController(QuartzJobService quartzJobService, IGenerator generator, QuartzLogService quartzLogService) {
this.quartzJobService = quartzJobService;
this.generator = generator;
this.quartzLogService = quartzLogService;
}
@Log("查询定时任务")
@ApiOperation("查询定时任务")
@GetMapping
@PreAuthorize("@el.check('admin','timing:list')")
public ResponseEntity<Object> getJobs(QuartzJobQueryCriteria criteria, Pageable pageable){
return new ResponseEntity<>(quartzJobService.queryAll(criteria,pageable), HttpStatus.OK);
}
@Log("导出任务数据")
@ApiOperation("导出任务数据")
@GetMapping(value = "/download")
@PreAuthorize("@el.check('admin','timing:list')")
public void download(HttpServletResponse response, QuartzJobQueryCriteria criteria) throws IOException {
quartzJobService.download(generator.convert(quartzJobService.queryAll(criteria),QuartzJobDto.class), response);
}
@Log("导出日志数据")
@ApiOperation("导出日志数据")
@GetMapping(value = "/logs/download")
@PreAuthorize("@el.check('admin','timing:list')")
public void downloadLog(HttpServletResponse response, QuartzLogQueryCriteria criteria) throws IOException {
quartzLogService.download(generator.convert(quartzLogService.queryAll(criteria), QuartzLogDto.class), response);
}
@ApiOperation("查询任务执行日志")
@GetMapping(value = "/logs")
@PreAuthorize("@el.check('admin','timing:list')")
public ResponseEntity<Object> getJobLogs(QuartzLogQueryCriteria criteria, Pageable pageable){
return new ResponseEntity<>(quartzLogService.queryAll(criteria,pageable), HttpStatus.OK);
}
@ForbidSubmit
@Log("新增定时任务")
@ApiOperation("新增定时任务")
@PostMapping
@PreAuthorize("@el.check('admin','timing:add')")
public ResponseEntity<Object> create(@Validated @RequestBody QuartzJob resources){
if (resources.getId() != null) {
throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID");
}
return new ResponseEntity<>(quartzJobService.save(resources),HttpStatus.CREATED);
}
@ForbidSubmit
@Log("修改定时任务")
@ApiOperation("修改定时任务")
@PutMapping
@PreAuthorize("@el.check('admin','timing:edit')")
public ResponseEntity<Object> update(@Validated @RequestBody QuartzJob resources){
quartzJobService.updateById(resources);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@ForbidSubmit
@Log("更改定时任务状态")
@ApiOperation("更改定时任务状态")
@PutMapping(value = "/{id}")
@PreAuthorize("@el.check('admin','timing:edit')")
public ResponseEntity<Object> updateIsPause(@PathVariable Long id){
quartzJobService.updateIsPause(quartzJobService.getOne(new LambdaQueryWrapper<QuartzJob>()
.eq(QuartzJob::getId,id)));
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@ForbidSubmit
@Log("执行定时任务")
@ApiOperation("执行定时任务")
@PutMapping(value = "/exec/{id}")
@PreAuthorize("@el.check('admin','timing:edit')")
public ResponseEntity<Object> execution(@PathVariable Long id){
quartzJobService.execution(quartzJobService.getOne(new LambdaQueryWrapper<QuartzJob>().eq(QuartzJob::getId,id)));
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@ForbidSubmit
@Log("删除定时任务")
@ApiOperation("删除定时任务")
@DeleteMapping
@PreAuthorize("@el.check('admin','timing:del')")
public ResponseEntity<Object> delete(@RequestBody Integer[] ids){
quartzJobService.removeByIds(new ArrayList<>(Arrays.asList(ids)));
return new ResponseEntity<>(HttpStatus.OK);
}
}

67
yshop-admin/src/main/java/co/yixiang/modules/quartz/service/QuartzJobService.java

@ -0,0 +1,67 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.service;
import co.yixiang.common.service.BaseService;
import co.yixiang.modules.quartz.domain.QuartzJob;
import co.yixiang.modules.quartz.service.dto.QuartzJobDto;
import co.yixiang.modules.quartz.service.dto.QuartzJobQueryCriteria;
import org.springframework.data.domain.Pageable;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* @author hupeng
* @date 2020-05-13
*/
public interface QuartzJobService extends BaseService<QuartzJob>{
/**
* 查询数据分页
* @param criteria 条件
* @param pageable 分页参数
* @return Map<String,Object>
*/
Map<String,Object> queryAll(QuartzJobQueryCriteria criteria, Pageable pageable);
/**
* 查询所有数据不分页
* @param criteria 条件参数
* @return List<QuartzJobDto>
*/
List<QuartzJob> queryAll(QuartzJobQueryCriteria criteria);
/**
* 导出数据
* @param all 待导出的数据
* @param response /
* @throws IOException /
*/
void download(List<QuartzJobDto> all, HttpServletResponse response) throws IOException;
/**
* 更改定时任务状态
* @param quartzJob /
*/
void updateIsPause(QuartzJob quartzJob);
/**
* 立即执行定时任务
* @param quartzJob /
*/
void execution(QuartzJob quartzJob);
/**
* 查询启用的任务
* @return List
*/
List<QuartzJob> findByIsPauseIsFalse();
void removeByIds(List<Integer> idList);
}

47
yshop-admin/src/main/java/co/yixiang/modules/quartz/service/QuartzLogService.java

@ -0,0 +1,47 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.service;
import co.yixiang.common.service.BaseService;
import co.yixiang.modules.quartz.domain.QuartzLog;
import co.yixiang.modules.quartz.service.dto.QuartzLogDto;
import co.yixiang.modules.quartz.service.dto.QuartzLogQueryCriteria;
import org.springframework.data.domain.Pageable;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* @author hupeng
* @date 2020-05-13
*/
public interface QuartzLogService extends BaseService<QuartzLog>{
/**
* 查询数据分页
* @param criteria 条件
* @param pageable 分页参数
* @return Map<String,Object>
*/
Map<String,Object> queryAll(QuartzLogQueryCriteria criteria, Pageable pageable);
/**
* 查询所有数据不分页
* @param criteria 条件参数
* @return List<QuartzLogDto>
*/
List<QuartzLog> queryAll(QuartzLogQueryCriteria criteria);
/**
* 导出数据
* @param all 待导出的数据
* @param response /
* @throws IOException /
*/
void download(List<QuartzLogDto> all, HttpServletResponse response) throws IOException;
}

47
yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzJobDto.java

@ -0,0 +1,47 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.service.dto;
import lombok.Data;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* @author hupeng
* @date 2020-05-13
*/
@Data
public class QuartzJobDto implements Serializable {
/** 定时任务ID */
private Long id;
/** Bean名称 */
private String beanName;
/** cron 表达式 */
private String cronExpression;
/** 状态:1暂停、0启用 */
private Boolean isPause;
/** 任务名称 */
private String jobName;
/** 方法名称 */
private String methodName;
/** 参数 */
private String params;
/** 备注 */
private String remark;
/** 创建日期 */
private Timestamp createTime;
}

32
yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzJobQueryCriteria.java

@ -0,0 +1,32 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.service.dto;
import co.yixiang.annotation.Query;
import lombok.Data;
import java.sql.Timestamp;
import java.util.List;
/**
* @author hupeng
* @date 2020-05-13
*/
@Data
public class QuartzJobQueryCriteria{
@Query(type = Query.Type.INNER_LIKE)
private String jobName;
@Query
private Boolean isSuccess;
@Query
private Boolean isPause;
@Query(type = Query.Type.BETWEEN)
private List<Timestamp> createTime;
}

46
yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzLogDto.java

@ -0,0 +1,46 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.service.dto;
import lombok.Data;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* @author hupeng
* @date 2020-05-13
*/
@Data
public class QuartzLogDto implements Serializable {
/** 定时任务名称 */
private String baenName;
/** Bean名称 */
private Timestamp createTime;
/** cron表达式 */
private String cronExpression;
/** 异常详细 */
private String exceptionDetail;
/** 状态 */
private Boolean isSuccess;
/** 任务名称 */
private String jobName;
/** 方法名称 */
private String methodName;
/** 参数 */
private String params;
/** 耗时(毫秒) */
private Long time;
}

16
yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzLogQueryCriteria.java

@ -0,0 +1,16 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.service.dto;
import lombok.Data;
/**
* @author hupeng
* @date 2020-05-13
*/
@Data
public class QuartzLogQueryCriteria{
}

168
yshop-admin/src/main/java/co/yixiang/modules/quartz/service/impl/QuartzJobServiceImpl.java

@ -0,0 +1,168 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.service.impl;
import co.yixiang.common.service.impl.BaseServiceImpl;
import co.yixiang.common.utils.QueryHelpPlus;
import co.yixiang.dozer.service.IGenerator;
import co.yixiang.exception.BadRequestException;
import co.yixiang.modules.quartz.domain.QuartzJob;
import co.yixiang.modules.quartz.service.QuartzJobService;
import co.yixiang.modules.quartz.service.dto.QuartzJobDto;
import co.yixiang.modules.quartz.service.dto.QuartzJobQueryCriteria;
import co.yixiang.modules.quartz.service.mapper.QuartzJobMapper;
import co.yixiang.modules.quartz.utils.QuartzManage;
import co.yixiang.utils.FileUtil;
import com.github.pagehelper.PageInfo;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
// 默认不使用缓存
//import org.springframework.cache.annotation.CacheConfig;
//import org.springframework.cache.annotation.CacheEvict;
//import org.springframework.cache.annotation.Cacheable;
/**
* @author hupeng
* @date 2020-05-13
*/
@Service
@AllArgsConstructor
//@CacheConfig(cacheNames = "quartzJob")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class QuartzJobServiceImpl extends BaseServiceImpl<QuartzJobMapper, QuartzJob> implements QuartzJobService {
private final IGenerator generator;
private final QuartzManage quartzManage;
@Override
//@Cacheable
public Map<String, Object> queryAll(QuartzJobQueryCriteria criteria, Pageable pageable) {
getPage(pageable);
PageInfo<QuartzJob> page = new PageInfo<>(queryAll(criteria));
Map<String, Object> map = new LinkedHashMap<>(2);
map.put("content", generator.convert(page.getList(), QuartzJobDto.class));
map.put("totalElements", page.getTotal());
return map;
}
@Override
//@Cacheable
public List<QuartzJob> queryAll(QuartzJobQueryCriteria criteria) {
return baseMapper.selectList(QueryHelpPlus.getPredicate(QuartzJob.class, criteria));
}
@Override
public void download(List<QuartzJobDto> all, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (QuartzJobDto quartzJob : all) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("Spring Bean名称", quartzJob.getBeanName());
map.put("cron 表达式", quartzJob.getCronExpression());
map.put("状态:1暂停、0启用", quartzJob.getIsPause());
map.put("任务名称", quartzJob.getJobName());
map.put("方法名称", quartzJob.getMethodName());
map.put("参数", quartzJob.getParams());
map.put("备注", quartzJob.getRemark());
map.put("创建日期", quartzJob.getCreateTime());
map.put("Spring Bean名称", quartzJob.getBeanName());
map.put("cron 表达式", quartzJob.getCronExpression());
map.put("状态:1暂停、0启用", quartzJob.getIsPause());
map.put("任务名称", quartzJob.getJobName());
map.put("方法名称", quartzJob.getMethodName());
map.put("参数", quartzJob.getParams());
map.put("备注", quartzJob.getRemark());
map.put("创建日期", quartzJob.getCreateTime());
map.put("Spring Bean名称", quartzJob.getBeanName());
map.put("cron 表达式", quartzJob.getCronExpression());
map.put("状态:1暂停、0启用", quartzJob.getIsPause());
map.put("任务名称", quartzJob.getJobName());
map.put("方法名称", quartzJob.getMethodName());
map.put("参数", quartzJob.getParams());
map.put("备注", quartzJob.getRemark());
map.put("创建日期", quartzJob.getCreateTime());
list.add(map);
}
FileUtil.downloadExcel(list, response);
}
/**
* 更改定时任务状态
*
* @param quartzJob /
*/
@Override
public void updateIsPause(QuartzJob quartzJob) {
if (quartzJob.getId().equals(1L)) {
throw new BadRequestException("该任务不可操作");
}
if (quartzJob.getIsPause()) {
quartzManage.resumeJob(quartzJob);
quartzJob.setIsPause(false);
} else {
quartzManage.pauseJob(quartzJob);
quartzJob.setIsPause(true);
}
this.saveOrUpdate(quartzJob);
}
@Override
public boolean save(QuartzJob quartzJob) {
quartzManage.addJob(quartzJob);
return retBool(baseMapper.insert(quartzJob));
}
@Override
public boolean updateById(QuartzJob quartzJob) {
quartzManage.updateJobCron(quartzJob);
return retBool(baseMapper.updateById(quartzJob));
}
/**
* 立即执行定时任务
*
* @param quartzJob /
*/
@Override
public void execution(QuartzJob quartzJob) {
if (quartzJob.getId().equals(1L)) {
throw new BadRequestException("该任务不可操作");
}
quartzManage.runJobNow(quartzJob);
}
/**
* 查询启用的任务
*
* @return List
*/
@Override
public List<QuartzJob> findByIsPauseIsFalse() {
QuartzJobQueryCriteria criteria = new QuartzJobQueryCriteria();
criteria.setIsPause(false);
return baseMapper.selectList(QueryHelpPlus.getPredicate(QuartzJob.class, criteria));
}
@Override
public void removeByIds(List<Integer> idList) {
idList.forEach(id -> {
QuartzJob quartzJob = baseMapper.selectById(id);
quartzManage.deleteJob(quartzJob);
});
baseMapper.deleteBatchIds(idList);
}
}

100
yshop-admin/src/main/java/co/yixiang/modules/quartz/service/impl/QuartzLogServiceImpl.java

@ -0,0 +1,100 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.service.impl;
import co.yixiang.common.service.impl.BaseServiceImpl;
import co.yixiang.common.utils.QueryHelpPlus;
import co.yixiang.dozer.service.IGenerator;
import co.yixiang.modules.quartz.domain.QuartzLog;
import co.yixiang.modules.quartz.service.QuartzLogService;
import co.yixiang.modules.quartz.service.dto.QuartzLogDto;
import co.yixiang.modules.quartz.service.dto.QuartzLogQueryCriteria;
import co.yixiang.modules.quartz.service.mapper.QuartzLogMapper;
import co.yixiang.utils.FileUtil;
import com.github.pagehelper.PageInfo;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
// 默认不使用缓存
//import org.springframework.cache.annotation.CacheConfig;
//import org.springframework.cache.annotation.CacheEvict;
//import org.springframework.cache.annotation.Cacheable;
/**
* @author hupeng
* @date 2020-05-13
*/
@Service
@AllArgsConstructor
//@CacheConfig(cacheNames = "quartzLog")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class QuartzLogServiceImpl extends BaseServiceImpl<QuartzLogMapper, QuartzLog> implements QuartzLogService {
private final IGenerator generator;
@Override
//@Cacheable
public Map<String, Object> queryAll(QuartzLogQueryCriteria criteria, Pageable pageable) {
getPage(pageable);
PageInfo<QuartzLog> page = new PageInfo<>(queryAll(criteria));
Map<String, Object> map = new LinkedHashMap<>(2);
map.put("content", generator.convert(page.getList(), QuartzLogDto.class));
map.put("totalElements", page.getTotal());
return map;
}
@Override
//@Cacheable
public List<QuartzLog> queryAll(QuartzLogQueryCriteria criteria){
return baseMapper.selectList(QueryHelpPlus.getPredicate(QuartzLog.class, criteria));
}
/**
* 导出数据
*
* @param all 待导出的数据
* @param response /
* @throws IOException /
*/
@Override
public void download(List<QuartzLogDto> all, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (QuartzLogDto quartzLog : all) {
Map<String,Object> map = new LinkedHashMap<>();
map.put(" baenName", quartzLog.getBaenName());
map.put(" createTime", quartzLog.getCreateTime());
map.put(" cronExpression", quartzLog.getCronExpression());
map.put(" exceptionDetail", quartzLog.getExceptionDetail());
map.put(" isSuccess", quartzLog.getIsSuccess());
map.put(" jobName", quartzLog.getJobName());
map.put(" methodName", quartzLog.getMethodName());
map.put(" params", quartzLog.getParams());
map.put(" time", quartzLog.getTime());
map.put("任务名称", quartzLog.getBaenName());
map.put("Bean名称 ", quartzLog.getCreateTime());
map.put("cron表达式", quartzLog.getCronExpression());
map.put("异常详细 ", quartzLog.getExceptionDetail());
map.put("状态", quartzLog.getIsSuccess());
map.put("任务名称", quartzLog.getJobName());
map.put("方法名称", quartzLog.getMethodName());
map.put("参数", quartzLog.getParams());
map.put("耗时(毫秒)", quartzLog.getTime());
list.add(map);
}
FileUtil.downloadExcel(list, response);
}
}

19
yshop-admin/src/main/java/co/yixiang/modules/quartz/service/mapper/QuartzJobMapper.java

@ -0,0 +1,19 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.service.mapper;
import co.yixiang.common.mapper.CoreMapper;
import co.yixiang.modules.quartz.domain.QuartzJob;
import org.springframework.stereotype.Repository;
/**
* @author hupeng
* @date 2020-05-13
*/
@Repository
public interface QuartzJobMapper extends CoreMapper<QuartzJob> {
}

19
yshop-admin/src/main/java/co/yixiang/modules/quartz/service/mapper/QuartzLogMapper.java

@ -0,0 +1,19 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.service.mapper;
import co.yixiang.common.mapper.CoreMapper;
import co.yixiang.modules.quartz.domain.QuartzLog;
import org.springframework.stereotype.Repository;
/**
* @author hupeng
* @date 2020-05-13
*/
@Repository
public interface QuartzLogMapper extends CoreMapper<QuartzLog> {
}

27
yshop-admin/src/main/java/co/yixiang/modules/quartz/task/TestTask.java

@ -0,0 +1,27 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.task;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 测试用
* @author hupeng
* @date 2019-01-08
*/
@Slf4j
@Component
public class TestTask {
public void run(){
log.info("执行成功");
}
public void run1(String str){
log.info("执行成功,参数为: {}" + str);
}
}

22
yshop-admin/src/main/java/co/yixiang/modules/quartz/task/VisitsTask.java

@ -0,0 +1,22 @@
package co.yixiang.modules.quartz.task;
import co.yixiang.modules.monitor.service.VisitsService;
import org.springframework.stereotype.Component;
/**
* @author Zheng Jie
* @date 2018-12-25
*/
@Component
public class VisitsTask {
private final VisitsService visitsService;
public VisitsTask(VisitsService visitsService) {
this.visitsService = visitsService;
}
public void run(){
visitsService.save();
}
}

78
yshop-admin/src/main/java/co/yixiang/modules/quartz/utils/ExecutionJob.java

@ -0,0 +1,78 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.utils;
import co.yixiang.config.thread.ThreadPoolExecutorUtil;
import co.yixiang.modules.quartz.domain.QuartzJob;
import co.yixiang.modules.quartz.domain.QuartzLog;
import co.yixiang.modules.quartz.service.QuartzJobService;
import co.yixiang.modules.quartz.service.QuartzLogService;
import co.yixiang.utils.SpringContextHolder;
import co.yixiang.utils.ThrowableUtil;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 参考人人开源https://gitee.com/renrenio/renren-security
* @author /
* @date 2019-01-07
*/
@Async
public class ExecutionJob extends QuartzJobBean {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/** 该处仅供参考 */
private final static ThreadPoolExecutor EXECUTOR = ThreadPoolExecutorUtil.getPoll();
@Override
@SuppressWarnings("unchecked")
protected void executeInternal(JobExecutionContext context) {
QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY);
// 获取spring bean
QuartzLogService quartzLogService = SpringContextHolder.getBean(QuartzLogService.class);
QuartzJobService quartzJobService = SpringContextHolder.getBean(QuartzJobService.class);
QuartzLog log = new QuartzLog();
log.setJobName(quartzJob.getJobName());
log.setBaenName(quartzJob.getBeanName());
log.setMethodName(quartzJob.getMethodName());
log.setParams(quartzJob.getParams());
long startTime = System.currentTimeMillis();
log.setCronExpression(quartzJob.getCronExpression());
try {
// 执行任务
logger.info("任务准备执行,任务名称:{}", quartzJob.getJobName());
QuartzRunnable task = new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(),
quartzJob.getParams());
Future<?> future = EXECUTOR.submit(task);
future.get();
long times = System.currentTimeMillis() - startTime;
log.setTime(times);
// 任务状态
log.setIsSuccess(true);
logger.info("任务执行完毕,任务名称:{} 总共耗时:{} 毫秒", quartzJob.getJobName(), times);
} catch (Exception e) {
logger.error("任务执行失败,任务名称:{}" + quartzJob.getJobName(), e);
long times = System.currentTimeMillis() - startTime;
log.setTime(times);
// 任务状态 0成功 1失败
log.setIsSuccess(false);
log.setExceptionDetail(ThrowableUtil.getStackTrace(e));
quartzJob.setIsPause(false);
//更新状态
quartzJobService.updateIsPause(quartzJob);
} finally {
quartzLogService.save(log);
}
}
}

173
yshop-admin/src/main/java/co/yixiang/modules/quartz/utils/QuartzManage.java

@ -0,0 +1,173 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.utils;
import co.yixiang.exception.BadRequestException;
import co.yixiang.modules.quartz.domain.QuartzJob;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerKey;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
import static org.quartz.TriggerBuilder.newTrigger;
/**
* @author hupeng
* @date 2019-01-07
*/
@Slf4j
@Component
public class QuartzManage {
private static final String JOB_NAME = "TASK_";
@Resource(name = "scheduler")
private Scheduler scheduler;
public void addJob(QuartzJob quartzJob){
try {
// 构建job信息
JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class).
withIdentity(JOB_NAME + quartzJob.getId()).build();
//通过触发器名和cron 表达式创建 Trigger
Trigger cronTrigger = newTrigger()
.withIdentity(JOB_NAME + quartzJob.getId())
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression()))
.build();
cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob);
//重置启动时间
((CronTriggerImpl)cronTrigger).setStartTime(new Date());
//执行定时任务
scheduler.scheduleJob(jobDetail,cronTrigger);
// 暂停任务
if (quartzJob.getIsPause()) {
pauseJob(quartzJob);
}
} catch (Exception e){
log.error("创建定时任务失败", e);
throw new BadRequestException("创建定时任务失败");
}
}
/**
* 更新job cron表达式
* @param quartzJob /
*/
public void updateJobCron(QuartzJob quartzJob){
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if(trigger == null){
addJob(quartzJob);
trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
}
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression());
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
//重置启动时间
((CronTriggerImpl)trigger).setStartTime(new Date());
trigger.getJobDataMap().put(QuartzJob.JOB_KEY,quartzJob);
scheduler.rescheduleJob(triggerKey, trigger);
// 暂停任务
if (quartzJob.getIsPause()) {
pauseJob(quartzJob);
}
} catch (Exception e){
log.error("更新定时任务失败", e);
throw new BadRequestException("更新定时任务失败");
}
}
/**
* 删除一个job
* @param quartzJob /
*/
public void deleteJob(QuartzJob quartzJob){
try {
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.pauseJob(jobKey);
scheduler.deleteJob(jobKey);
} catch (Exception e){
log.error("删除定时任务失败", e);
throw new BadRequestException("删除定时任务失败");
}
}
/**
* 恢复一个job
* @param quartzJob /
*/
public void resumeJob(QuartzJob quartzJob){
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if(trigger == null) {
addJob(quartzJob);
}
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.resumeJob(jobKey);
} catch (Exception e){
log.error("恢复定时任务失败", e);
throw new BadRequestException("恢复定时任务失败");
}
}
/**
* 立即执行job
* @param quartzJob /
*/
public void runJobNow(QuartzJob quartzJob){
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if(trigger == null) {
addJob(quartzJob);
}
JobDataMap dataMap = new JobDataMap();
dataMap.put(QuartzJob.JOB_KEY, quartzJob);
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.triggerJob(jobKey,dataMap);
} catch (Exception e){
log.error("定时任务执行失败", e);
throw new BadRequestException("定时任务执行失败");
}
}
/**
* 暂停一个job
* @param quartzJob /
*/
public void pauseJob(QuartzJob quartzJob){
try {
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.pauseJob(jobKey);
} catch (Exception e){
log.error("定时任务暂停失败", e);
throw new BadRequestException("定时任务暂停失败");
}
}
}

49
yshop-admin/src/main/java/co/yixiang/modules/quartz/utils/QuartzRunnable.java

@ -0,0 +1,49 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.quartz.utils;
import co.yixiang.utils.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
/**
* 执行定时任务
* @author /
*/
@Slf4j
public class QuartzRunnable implements Callable {
private Object target;
private Method method;
private String params;
QuartzRunnable(String beanName, String methodName, String params)
throws NoSuchMethodException, SecurityException {
this.target = SpringContextHolder.getBean(beanName);
this.params = params;
if (StringUtils.isNotBlank(params)) {
this.method = target.getClass().getDeclaredMethod(methodName, String.class);
} else {
this.method = target.getClass().getDeclaredMethod(methodName);
}
}
@Override
public Object call() throws Exception {
ReflectionUtils.makeAccessible(method);
if (StringUtils.isNotBlank(params)) {
method.invoke(target, params);
} else {
method.invoke(target);
}
return null;
}
}

139
yshop-admin/src/main/java/co/yixiang/modules/security/config/SecurityConfig.java

@ -0,0 +1,139 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.config;
import co.yixiang.annotation.AnonymousAccess;
import co.yixiang.modules.security.security.JwtAccessDeniedHandler;
import co.yixiang.modules.security.security.JwtAuthenticationEntryPoint;
import co.yixiang.modules.security.security.TokenConfigurer;
import co.yixiang.modules.security.security.TokenUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author hupeng
*/
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final TokenUtil tokenUtil;
private final CorsFilter corsFilter;
private final JwtAuthenticationEntryPoint authenticationErrorHandler;
private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
private final ApplicationContext applicationContext;
public SecurityConfig(TokenUtil tokenUtil, CorsFilter corsFilter, JwtAuthenticationEntryPoint authenticationErrorHandler, JwtAccessDeniedHandler jwtAccessDeniedHandler, ApplicationContext applicationContext) {
this.tokenUtil = tokenUtil;
this.corsFilter = corsFilter;
this.authenticationErrorHandler = authenticationErrorHandler;
this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;
this.applicationContext = applicationContext;
}
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
// 去除 ROLE_ 前缀
return new GrantedAuthorityDefaults("");
}
@Bean
public PasswordEncoder passwordEncoder() {
// 密码加密方式
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// 搜寻匿名标记 url @AnonymousAccess
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
Set<String> anonymousUrls = new HashSet<>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = infoEntry.getValue();
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
if (null != anonymousAccess) {
anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
}
}
httpSecurity
// 禁用 CSRF
.csrf().disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
// 授权异常
.exceptionHandling()
.authenticationEntryPoint(authenticationErrorHandler)
.accessDeniedHandler(jwtAccessDeniedHandler)
// 防止iframe 造成跨域
.and()
.headers()
.frameOptions()
.disable()
// 不创建会话
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 静态资源等等
.antMatchers(
HttpMethod.GET,
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/webSocket/**"
).permitAll()
// swagger 文档
.antMatchers("/swagger-ui.html").permitAll()
.antMatchers("/swagger-resources/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.antMatchers("/*/api-docs").permitAll()
.antMatchers("/v2/api-docs-ext").permitAll()
//.antMatchers("/api/wxmp/**").permitAll()
// 文件
.antMatchers("/avatar/**").permitAll()
.antMatchers("/file/**").permitAll()
// 阿里巴巴 druid
.antMatchers("/druid/**").permitAll()
.antMatchers("/api/canvas/**").permitAll()
// 放行OPTIONS请求
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 自定义匿名访问所有url放行 允许匿名和带权限以及登录用户访问
.antMatchers(anonymousUrls.toArray(new String[0])).permitAll()
// 所有请求都需要认证
.anyRequest().authenticated()
.and().apply(securityConfigurerAdapter());
}
private TokenConfigurer securityConfigurerAdapter() {
return new TokenConfigurer(tokenUtil);
}
}

43
yshop-admin/src/main/java/co/yixiang/modules/security/config/SecurityProperties.java

@ -0,0 +1,43 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* Jwt参数配置
* @author hupeng
* @date 2019年11月28日
*/
@Data
@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = "jwt")
public class SecurityProperties {
/** Request Headers : Authorization */
private String header;
/** 令牌前缀,最后留个空格 Bearer */
private String tokenStartWith;
/** 必须使用最少88位的Base64对该令牌进行编码 */
private String base64Secret;
private String secret;
/** 令牌过期时间 此处单位/毫秒 */
private Long tokenValidityInSeconds;
/** 在线用户 key,根据 key 查询 redis 中在线用户的数据 */
private String onlineKey;
/** 验证码 key */
private String codeKey;
public String getTokenStartWith() {
return tokenStartWith + " ";
}
}

161
yshop-admin/src/main/java/co/yixiang/modules/security/rest/AuthController.java

@ -0,0 +1,161 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.rest;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import co.yixiang.annotation.AnonymousAccess;
import co.yixiang.exception.BadRequestException;
import co.yixiang.logging.aop.log.Log;
import co.yixiang.modules.security.config.SecurityProperties;
import co.yixiang.modules.security.security.TokenUtil;
import co.yixiang.modules.security.security.vo.AuthUser;
import co.yixiang.modules.security.security.vo.JwtUser;
import co.yixiang.modules.security.service.OnlineUserService;
import co.yixiang.utils.RedisUtils;
import co.yixiang.utils.SecurityUtils;
import co.yixiang.utils.StringUtils;
import com.wf.captcha.ArithmeticCaptcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author hupeng
* @date 2018-11-23
* 授权根据token获取用户详细信息
*/
@Slf4j
@RestController
@RequestMapping("/auth")
@Api(tags = "系统:系统授权接口")
public class AuthController {
@Value("${loginCode.expiration}")
private Long expiration;
@Value("${rsa.private_key}")
private String privateKey;
@Value("${single.login}")
private Boolean singleLogin;
private final SecurityProperties properties;
private final RedisUtils redisUtils;
private final UserDetailsService userDetailsService;
private final OnlineUserService onlineUserService;
private final TokenUtil tokenUtil;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
public AuthController(SecurityProperties properties, RedisUtils redisUtils, UserDetailsService userDetailsService, OnlineUserService onlineUserService, TokenUtil tokenUtil, AuthenticationManagerBuilder authenticationManagerBuilder) {
this.properties = properties;
this.redisUtils = redisUtils;
this.userDetailsService = userDetailsService;
this.onlineUserService = onlineUserService;
this.tokenUtil = tokenUtil;
this.authenticationManagerBuilder = authenticationManagerBuilder;
}
@Log("用户登录")
@ApiOperation("登录授权")
@AnonymousAccess
@PostMapping(value = "/login")
public ResponseEntity<Object> login(@Validated @RequestBody AuthUser authUser, HttpServletRequest request){
// 密码解密
RSA rsa = new RSA(privateKey, null);
String password = new String(rsa.decrypt(authUser.getPassword(), KeyType.PrivateKey));
// 查询验证码
String code = (String) redisUtils.get(authUser.getUuid());
// 清除验证码
redisUtils.del(authUser.getUuid());
if (StringUtils.isBlank(code)) {
throw new BadRequestException("验证码不存在或已过期");
}
if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
throw new BadRequestException("验证码错误");
}
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 生成令牌
final UserDetails userDetails = (UserDetails) authentication.getPrincipal();
String token = tokenUtil.generateToken(userDetails);
final JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
// 保存在线信息
onlineUserService.save(jwtUser, token, request);
// 返回 token 用户信息
Map<String,Object> authInfo = new HashMap<String,Object>(2){{
put("token", properties.getTokenStartWith() + token);
put("user", jwtUser);
}};
if(singleLogin){
//踢掉之前已经登录的token
onlineUserService.checkLoginOnUser(authUser.getUsername(),token);
}
return ResponseEntity.ok(authInfo);
}
@ApiOperation("获取用户信息")
@GetMapping(value = "/info")
public ResponseEntity<Object> getUserInfo(){
JwtUser jwtUser = (JwtUser)userDetailsService.loadUserByUsername(SecurityUtils.getUsername());
return ResponseEntity.ok(jwtUser);
}
@AnonymousAccess
@ApiOperation("获取验证码")
@GetMapping(value = "/code")
public ResponseEntity<Object> getCode(){
// 算术类型 https://gitee.com/whvse/EasyCaptcha
ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);
// 几位数运算默认是两位
captcha.setLen(2);
// 获取运算的结果
String result ="";
try {
result = new Double(Double.parseDouble(captcha.text())).intValue()+"";
}catch (Exception e){
result = captcha.text();
}
String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
// 保存
redisUtils.set(uuid, result, expiration, TimeUnit.MINUTES);
// 验证码信息
Map<String,Object> imgResult = new HashMap<String,Object>(2){{
put("img", captcha.toBase64());
put("uuid", uuid);
}};
return ResponseEntity.ok(imgResult);
}
@ApiOperation("退出登录")
@AnonymousAccess
@DeleteMapping(value = "/logout")
public ResponseEntity<Object> logout(HttpServletRequest request){
onlineUserService.logout(tokenUtil.getToken(request));
return new ResponseEntity<>(HttpStatus.OK);
}
}

83
yshop-admin/src/main/java/co/yixiang/modules/security/rest/OnlineController.java

@ -0,0 +1,83 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.rest;
import co.yixiang.logging.aop.log.Log;
import co.yixiang.modules.aop.ForbidSubmit;
import co.yixiang.modules.security.service.OnlineUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
/**
* @author hupeng
*/
@RestController
@RequestMapping("/auth/online")
@Api(tags = "系统:在线用户管理")
public class OnlineController {
private final OnlineUserService onlineUserService;
public OnlineController(OnlineUserService onlineUserService) {
this.onlineUserService = onlineUserService;
}
@ApiOperation("查询在线用户")
@GetMapping
@PreAuthorize("@el.check('auth_online')")
public ResponseEntity<Object> getAll(@RequestParam(value = "filter",defaultValue = "") String filter,
@RequestParam(value = "type",defaultValue = "0") int type,
Pageable pageable){
return new ResponseEntity<>(onlineUserService.getAll(filter, type,pageable),HttpStatus.OK);
}
@Log("导出数据")
@ApiOperation("导出数据")
@GetMapping(value = "/download")
@PreAuthorize("@el.check()")
public void download(HttpServletResponse response,
@RequestParam(value = "filter",defaultValue = "") String filter,
@RequestParam(value = "type",defaultValue = "0") int type) throws IOException {
onlineUserService.download(onlineUserService.getAll(filter,type), response);
}
@ForbidSubmit
@ApiOperation("踢出用户")
@DeleteMapping
@PreAuthorize("@el.check()")
public ResponseEntity<Object> delete(@RequestBody Set<String> keys) throws Exception {
for (String key : keys) {
onlineUserService.kickOut(key);
}
return new ResponseEntity<>(HttpStatus.OK);
}
@ForbidSubmit
@ApiOperation("踢出移动端用户")
@PostMapping("/delete" )
@PreAuthorize("@el.check()")
public ResponseEntity<Object> deletet(@RequestBody Set<String> keys) throws Exception {
for (String key : keys) {
onlineUserService.kickOutT(key);
}
return new ResponseEntity<>(HttpStatus.OK);
}
}

27
yshop-admin/src/main/java/co/yixiang/modules/security/security/JwtAccessDeniedHandler.java

@ -0,0 +1,27 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.security;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author hupeng
*/
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
//当用户在没有授权的情况下访问受保护的REST资源时将调用此方法发送403 Forbidden响应
response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());
}
}

29
yshop-admin/src/main/java/co/yixiang/modules/security/security/JwtAuthenticationEntryPoint.java

@ -0,0 +1,29 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.security;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author hupeng
*/
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
// 当用户尝试访问安全的REST资源而不提供任何凭据时将调用此方法发送401 响应
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException==null?"Unauthorized":authException.getMessage());
}
}

29
yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenConfigurer.java

@ -0,0 +1,29 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.security;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* @author /
*/
public class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final TokenUtil tokenUtil;
public TokenConfigurer(TokenUtil tokenUtil){
this.tokenUtil = tokenUtil;
}
@Override
public void configure(HttpSecurity http) {
TokenFilter customFilter = new TokenFilter(tokenUtil);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}

75
yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenFilter.java

@ -0,0 +1,75 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.security;
import co.yixiang.modules.security.config.SecurityProperties;
import co.yixiang.modules.security.service.OnlineUserService;
import co.yixiang.modules.user.vo.OnlineUser;
import co.yixiang.utils.SpringContextHolder;
import co.yixiang.utils.StringUtils;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author /
*/
@Slf4j
public class TokenFilter extends GenericFilterBean {
private final TokenUtil tokenUtil;
TokenFilter(TokenUtil tokenUtil) {
this.tokenUtil = tokenUtil;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String requestRri = httpServletRequest.getRequestURI();
// 验证 token 是否存在
OnlineUser onlineUser = null;
String authToken = "";
try {
SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class);
authToken = tokenUtil.getToken(httpServletRequest);
if (authToken == null) {
filterChain.doFilter(httpServletRequest, servletResponse);
return;
}
onlineUser = onlineUserService.getOne(properties.getOnlineKey() + authToken);
} catch (ExpiredJwtException e) {
log.error(e.getMessage());
}
String username = StringUtils.isNotBlank(authToken) ? tokenUtil.getUsernameFromToken(authToken) : null;
if (onlineUser != null && username != null && SecurityContextHolder.getContext().getAuthentication() == null && tokenUtil.validateToken(authToken)) {
UserDetails userDetails = tokenUtil.getUserDetails(authToken);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
log.debug("set Authentication to security context for '{}', uri: {}", authentication.getName(), requestRri);
} else {
tokenUtil.removeToken(authToken);
log.debug("no valid JWT token found, uri: {}", requestRri);
}
filterChain.doFilter(httpServletRequest, servletResponse);
}
}

118
yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenProvider.java

@ -0,0 +1,118 @@
package co.yixiang.modules.security.security; /**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*//*
package co.yixiang.modules.security.security;
import co.yixiang.modules.security.config.SecurityProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.stream.Collectors;
*/
/**
* @author /
*//*
@Slf4j
@Component
public class TokenProvider implements InitializingBean {
private final SecurityProperties properties;
private static final String AUTHORITIES_KEY = "auth";
private Key key;
public TokenProvider(SecurityProperties properties) {
this.properties = properties;
}
@Override
public void afterPropertiesSet() {
byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret());
this.key = Keys.hmacShaKeyFor(keyBytes);
}
public String createToken(Authentication authentication) {
String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
long now = (new Date()).getTime();
Date validity = new Date(now + properties.getTokenValidityInSeconds());
return Jwts.builder()
.setSubject(authentication.getName())
.claim(AUTHORITIES_KEY, authorities)
.signWith(key, SignatureAlgorithm.HS512)
.setExpiration(validity)
.compact();
}
Authentication getAuthentication(String token) {
Claims claims = Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token)
.getBody();
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
User principal = new User(claims.getSubject(), "", authorities);
return new UsernamePasswordAuthenticationToken(principal, token, authorities);
}
boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(key).parseClaimsJws(authToken);
return true;
} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
log.info("Invalid JWT signature.");
e.printStackTrace();
} catch (ExpiredJwtException e) {
log.info("Expired JWT token.");
e.printStackTrace();
} catch (UnsupportedJwtException e) {
log.info("Unsupported JWT token.");
e.printStackTrace();
} catch (IllegalArgumentException e) {
log.info("JWT token compact of handler are invalid.");
e.printStackTrace();
}
return false;
}
public String getToken(HttpServletRequest request){
final String requestHeader = request.getHeader(properties.getHeader());
if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) {
return requestHeader.substring(7);
}
return null;
}
}
*/

238
yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenUtil.java

@ -0,0 +1,238 @@
package co.yixiang.modules.security.security;
import co.yixiang.modules.security.config.SecurityProperties;
import co.yixiang.modules.security.security.vo.JwtUser;
import co.yixiang.utils.RedisUtils;
import co.yixiang.utils.StringUtils;
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Base64;
import java.util.Date;
/**
* Token 工具类
*
* @author lioncity
*/
@Component
public class TokenUtil {
@Autowired
private SecurityProperties properties;
/**
* Logger
*/
protected static final Logger LOGGER = LoggerFactory.getLogger(TokenUtil.class);
/**
* 权限缓存前缀
*/
private static final String REDIS_PREFIX_AUTH = "auth:";
/**
* 用户信息缓存前缀
*/
private static final String REDIS_PREFIX_USER = "user-details:";
/**
* redis repository
*/
@Autowired
private RedisUtils redisUtils;
/**
* 获取用户名
*
* @param token Token
* @return String
*/
public String getUsernameFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims != null ? claims.getSubject() : null;
}
/**
* 获取过期时间
*
* @param token Token
* @return Date
*/
public Date getExpiredFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims != null ? claims.getExpiration() : null;
}
/**
* 获得 Claims
*
* @param token Token
* @return Claims
*/
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(properties.getSecret())
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
LOGGER.warn("getClaimsFromToken exception", e);
claims = null;
}
return claims;
}
/**
* 计算过期时间
*
* @return Date
*/
private Date generateExpired() {
return new Date(System.currentTimeMillis() + properties.getTokenValidityInSeconds() * 1000);
}
/**
* 判断 Token 是否过期
*
* @param token Token
* @return Boolean
*/
private Boolean isTokenExpired(String token) {
Date expirationDate = getExpiredFromToken(token);
return expirationDate.before(new Date());
}
/**
* 生成 Token
*
* @param userDetails 用户信息
* @return String
*/
public String generateToken(UserDetails userDetails) {
String secret=properties.getSecret();
String token = Jwts.builder()
.setSubject(userDetails.getUsername())
.setExpiration(generateExpired())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
String key = REDIS_PREFIX_AUTH + userDetails.getUsername() + ":" + token;
redisUtils.set(key, token, properties.getTokenValidityInSeconds() / 1000);
putUserDetails(userDetails);
return token;
}
/**
* 验证 Token
*
* @param token Token
* @return Boolean
*/
public Boolean validateToken(String token) {
final String username = getUsernameFromToken(token);
String key = REDIS_PREFIX_AUTH + username+ ":" + token;
Object data = redisUtils.get(key);
String redisToken = data == null ? null : data.toString();
return StringUtils.isNotEmpty(token) && !isTokenExpired(token) && token.equals(redisToken);
}
/**
* 移除 Token
*
* @param token Token
*/
public void removeToken(String token) {
final String username = getUsernameFromToken(token);
String key = REDIS_PREFIX_AUTH + username+ ":" + token;
redisUtils.del(key);
delUserDetails(username);
}
/**
* 获得用户信息 Json 字符串
*
* @param token Token
* @return String
*/
protected String getUserDetailsString(String token) {
final String username = getUsernameFromToken(token);
String key = REDIS_PREFIX_USER + username;
Object data = redisUtils.get(key);
return data == null ? null : data.toString();
}
/**
* 获得用户信息
*
* @param token Token
* @return UserDetails
*/
public UserDetails getUserDetails(String token) {
String userDetailsString = getUserDetailsString(token);
if (userDetailsString != null) {
return new Gson().fromJson(userDetailsString, JwtUser.class);
}
return null;
}
/**
* 存储用户信息
*
* @param userDetails 用户信息
*/
private void putUserDetails(UserDetails userDetails) {
String key = REDIS_PREFIX_USER + userDetails.getUsername();
redisUtils.set(key, new Gson().toJson(userDetails), properties.getTokenValidityInSeconds() / 1000);
}
/**
* 删除用户信息
*
* @param username 用户名
*/
private void delUserDetails(String username) {
String key = REDIS_PREFIX_USER + username;
redisUtils.del(key);
}
public String getToken(HttpServletRequest request) {
final String requestHeader = request.getHeader(properties.getHeader());
if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) {
return requestHeader.substring(7);
}
return null;
}
public static void main(String[] args) {
String key = Base64.getEncoder().encodeToString("123".getBytes());
Claims claims = Jwts.parser().setSigningKey(key)
.parseClaimsJws("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFwcCIsIndyaXRlIl0sInVpbiI6MSwiZXhwIjoxNTc1MDE1ODgzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiYjdiYjQ1NTQtNTQ4OS00YTg5LWI3NjQtNzNjODI0YzljNGMyIiwiY2xpZW50X2lkIjoibHZoYWliYW8ifQ.x7QZxRAR1wuX_YNLi6EzRJ1iaKr1rIEUgjtYF0oSx5k").getBody();
System.out.println(JSON.toJSONString(claims));
}
}

35
yshop-admin/src/main/java/co/yixiang/modules/security/security/vo/AuthUser.java

@ -0,0 +1,35 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.security.vo;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* @author hupeng
* @date 2018-11-30
*/
@Getter
@Setter
public class AuthUser {
@NotBlank
private String username;
@NotBlank
private String password;
private String code;
private String uuid = "";
@Override
public String toString() {
return "{username=" + username + ", password= ******}";
}
}

90
yshop-admin/src/main/java/co/yixiang/modules/security/security/vo/JwtUser.java

@ -0,0 +1,90 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.security.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Date;
import java.util.stream.Collectors;
/**
* @author hupeng
* @date 2018-11-23
*/
@Getter
@AllArgsConstructor
public class JwtUser implements UserDetails {
private final Long id;
private final String username;
private final String nickName;
private final String sex;
@JsonIgnore
private final String password;
private final String avatar;
private final String email;
private final String phone;
private final String dept;
private final String job;
@JsonIgnore
private final Collection<SimpleGrantedAuthority> authorities;
private final boolean enabled;
private Timestamp createTime;
@JsonIgnore
private final Date lastPasswordResetDate;
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@JsonIgnore
@Override
public String getPassword() {
return password;
}
@Override
public boolean isEnabled() {
return enabled;
}
public Collection getRoles() {
return authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
}
}

198
yshop-admin/src/main/java/co/yixiang/modules/security/service/OnlineUserService.java

@ -0,0 +1,198 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.service;
import co.yixiang.constant.ShopConstants;
import co.yixiang.modules.security.config.SecurityProperties;
import co.yixiang.modules.security.security.vo.JwtUser;
import co.yixiang.modules.user.vo.OnlineUser;
import co.yixiang.utils.EncryptUtils;
import co.yixiang.utils.FileUtil;
import co.yixiang.utils.PageUtil;
import co.yixiang.utils.RedisUtils;
import co.yixiang.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @author hupeng
* @Date 2019年10月26日21:56:27
*/
@Service
@Slf4j
public class OnlineUserService {
private final SecurityProperties properties;
private RedisUtils redisUtils;
public OnlineUserService(SecurityProperties properties, RedisUtils redisUtils) {
this.properties = properties;
this.redisUtils = redisUtils;
}
/**
* 保存在线用户信息
* @param jwtUser /
* @param token /
* @param request /
*/
public void save(JwtUser jwtUser, String token, HttpServletRequest request){
String job = jwtUser.getDept() + "/" + jwtUser.getJob();
String ip = StringUtils.getIp(request);
String browser = StringUtils.getBrowser(request);
String address = StringUtils.getCityInfo(ip);
OnlineUser onlineUser = null;
try {
onlineUser = new OnlineUser(jwtUser.getUsername(), jwtUser.getNickName(), job, browser , ip, address, EncryptUtils.desEncrypt(token), new Date());
} catch (Exception e) {
e.printStackTrace();
}
redisUtils.set(properties.getOnlineKey() + token, onlineUser, properties.getTokenValidityInSeconds()/1000);
}
/**
* 查询全部数据
* @param filter /
* @param pageable /
* @return /
*/
public Map<String,Object> getAll(String filter, int type, Pageable pageable){
List<OnlineUser> onlineUsers = getAll(filter,type);
return PageUtil.toPage(
PageUtil.toPage(pageable.getPageNumber(),pageable.getPageSize(),onlineUsers),
onlineUsers.size()
);
}
/**
* 查询全部数据不分页
* @param filter /
* @return /
*/
public List<OnlineUser> getAll(String filter,int type){
List<String> keys = null;
if(type == 1){
keys = redisUtils.scan(ShopConstants.YSHOP_APP_LOGIN_USER + "*");
}else{
keys = redisUtils.scan(properties.getOnlineKey() + "*");
}
Collections.reverse(keys);
List<OnlineUser> onlineUsers = new ArrayList<>();
for (String key : keys) {
OnlineUser onlineUser = (OnlineUser) redisUtils.get(key);
if(StringUtils.isNotBlank(filter)){
if(onlineUser.toString().contains(filter)){
onlineUsers.add(onlineUser);
}
} else {
onlineUsers.add(onlineUser);
}
}
onlineUsers.sort((o1, o2) -> o2.getLoginTime().compareTo(o1.getLoginTime()));
return onlineUsers;
}
/**
* 踢出用户
* @param key /
* @throws Exception /
*/
public void kickOut(String key) throws Exception {
key = properties.getOnlineKey() + EncryptUtils.desDecrypt(key);
redisUtils.del(key);
}
/**
* 踢出移动端用户
* @param key /
* @throws Exception /
*/
public void kickOutT(String key) throws Exception {
String keyt = ShopConstants.YSHOP_APP_LOGIN_USER + EncryptUtils.desDecrypt(key);
redisUtils.del(keyt);
}
/**
* 退出登录
* @param token /
*/
public void logout(String token) {
String key = properties.getOnlineKey() + token;
redisUtils.del(key);
}
/**
* 导出
* @param all /
* @param response /
* @throws IOException /
*/
public void download(List<OnlineUser> all, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (OnlineUser user : all) {
Map<String,Object> map = new LinkedHashMap<>();
map.put("用户名", user.getUserName());
map.put("用户昵称", user.getNickName());
map.put("登录IP", user.getIp());
map.put("登录地点", user.getAddress());
map.put("浏览器", user.getBrowser());
map.put("登录日期", user.getLoginTime());
list.add(map);
}
FileUtil.downloadExcel(list, response);
}
/**
* 查询用户
* @param key /
* @return /
*/
public OnlineUser getOne(String key) {
return (OnlineUser)redisUtils.get(key);
}
/**
* 检测用户是否在之前已经登录已经登录踢下线
* @param userName 用户名
*/
public void checkLoginOnUser(String userName, String igoreToken){
List<OnlineUser> onlineUsers = getAll(userName,0);
if(onlineUsers ==null || onlineUsers.isEmpty()){
return;
}
for(OnlineUser onlineUser:onlineUsers){
if(onlineUser.getUserName().equals(userName)){
try {
String token =EncryptUtils.desDecrypt(onlineUser.getKey());
if(StringUtils.isNotBlank(igoreToken)&&!igoreToken.equals(token)){
this.kickOut(onlineUser.getKey());
}else if(StringUtils.isBlank(igoreToken)){
this.kickOut(onlineUser.getKey());
}
} catch (Exception e) {
log.error("checkUser is error",e);
}
}
}
}
}

71
yshop-admin/src/main/java/co/yixiang/modules/security/service/UserDetailsServiceImpl.java

@ -0,0 +1,71 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.security.service;
import co.yixiang.exception.BadRequestException;
import co.yixiang.modules.security.security.vo.JwtUser;
import co.yixiang.modules.system.service.RoleService;
import co.yixiang.modules.system.service.UserService;
import co.yixiang.modules.system.service.dto.DeptSmallDto;
import co.yixiang.modules.system.service.dto.JobSmallDto;
import co.yixiang.modules.system.service.dto.UserDto;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
/**
* @author hupeng
* @date 2018-11-22
*/
@Service("userDetailsService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserService userService;
private final RoleService roleService;
public UserDetailsServiceImpl(UserService userService, RoleService roleService) {
this.userService = userService;
this.roleService = roleService;
}
@Override
public UserDetails loadUserByUsername(String username){
UserDto user = userService.findByName(username);
if (user == null) {
throw new BadRequestException("账号不存在");
} else {
if (!user.getEnabled()) {
throw new BadRequestException("账号未激活");
}
return createJwtUser(user);
}
}
private UserDetails createJwtUser(UserDto user) {
return new JwtUser(
user.getId(),
user.getUsername(),
user.getNickName(),
user.getSex(),
user.getPassword(),
user.getAvatar(),
user.getEmail(),
user.getPhone(),
Optional.ofNullable(user.getDept()).map(DeptSmallDto::getName).orElse(null),
Optional.ofNullable(user.getJob()).map(JobSmallDto::getName).orElse(null),
roleService.mapToGrantedAuthorities(user),
user.getEnabled(),
user.getCreateTime(),
user.getLastPasswordResetTime()
);
}
}

56
yshop-admin/src/main/java/co/yixiang/modules/system/domain/Dept.java

@ -0,0 +1,56 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import co.yixiang.domain.BaseDomain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author hupeng
* @date 2020-05-14
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("dept")
public class Dept extends BaseDomain {
/** ID */
@TableId(value = "id",type= IdType.AUTO)
private Long id;
/** 名称 */
@NotBlank(message = "部门名称不能为空")
private String name;
/** 上级部门 */
@NotNull(message = "上级部门不能为空")
private Long pid;
/** 状态 */
private Boolean enabled;
public void copy(Dept source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

49
yshop-admin/src/main/java/co/yixiang/modules/system/domain/Dict.java

@ -0,0 +1,49 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import co.yixiang.domain.BaseDomain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
/**
* @author hupeng
* @date 2020-05-14
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("dict")
public class Dict extends BaseDomain {
/** 字典ID */
@TableId
private Long id;
/** 字典名称 */
@NotBlank(message = "字典名称不能为空")
private String name;
/** 描述 */
private String remark;
public void copy(Dict source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

56
yshop-admin/src/main/java/co/yixiang/modules/system/domain/DictDetail.java

@ -0,0 +1,56 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import co.yixiang.domain.BaseDomain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* @author hupeng
* @date 2020-05-14
*/
@Data
@TableName("dict_detail")
public class DictDetail extends BaseDomain {
/** 字典详细 */
@TableId
private Long id;
/** 字典标签 */
private String label;
/** 字典值 */
private String value;
/** 排序 */
private String sort;
/** 字典id */
private Long dictId;
@TableField(exist = false)
private Dict dict;
public void copy(DictDetail source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

59
yshop-admin/src/main/java/co/yixiang/modules/system/domain/Job.java

@ -0,0 +1,59 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import co.yixiang.domain.BaseDomain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
/**
* @author hupeng
* @date 2020-05-14
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("job")
public class Job extends BaseDomain {
/** 岗位ID */
@TableId
private Long id;
/** 岗位名称 */
@NotBlank(message = "岗位名称不能为空")
private String name;
/** 岗位状态 */
private Boolean enabled;
@TableField(exist = false)
private Dept dept;
/** 岗位排序 */
private Long sort;
/** 部门ID */
private Long deptId;
public void copy(Job source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

92
yshop-admin/src/main/java/co/yixiang/modules/system/domain/Menu.java

@ -0,0 +1,92 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import co.yixiang.domain.BaseDomain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author hupeng
* @date 2020-05-14
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("menu")
public class Menu extends BaseDomain {
/** ID */
@TableId
private Long id;
/** 是否外链 */
private Boolean iFrame;
/** 菜单名称 */
@NotBlank(message = "请填写菜单名称")
private String name;
/** 组件 */
private String component;
/** 上级菜单ID */
@NotNull(message = "上级菜单ID不能为空")
private Long pid;
/** 排序 */
@NotNull(message = "排序不能为空")
private Long sort;
/** 图标 */
private String icon;
/** 链接地址 */
private String path;
/** 缓存 */
private Boolean cache;
/** 是否隐藏 */
private Boolean hidden;
/** 组件名称 */
private String componentName;
/** 权限 */
private String permission;
/** 类型,目录、菜单、按钮 */
private Integer type;
public void copy(Menu source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

67
yshop-admin/src/main/java/co/yixiang/modules/system/domain/Role.java

@ -0,0 +1,67 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import co.yixiang.domain.BaseDomain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import java.util.Set;
/**
* @author hupeng
* @date 2020-05-14
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("role")
public class Role extends BaseDomain {
/** ID */
@TableId
private Long id;
/** 名称 */
@NotBlank(message = "请填写角色名称")
private String name;
/** 备注 */
private String remark;
/** 数据权限 */
private String dataScope;
/** 角色级别 */
private Integer level;
@TableField(exist = false)
private Set<Menu> menus;
@TableField(exist = false)
private Set<Dept> depts;
/** 功能权限 */
private String permission;
public void copy(Role source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

36
yshop-admin/src/main/java/co/yixiang/modules/system/domain/RolesDepts.java

@ -0,0 +1,36 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* @author hupeng
* @date 2020-05-16
*/
@Data
@TableName("roles_depts")
public class RolesDepts implements Serializable {
/** 角色ID */
private Long roleId;
/** 部门ID */
private Long deptId;
public void copy(RolesDepts source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

36
yshop-admin/src/main/java/co/yixiang/modules/system/domain/RolesMenus.java

@ -0,0 +1,36 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* @author hupeng
* @date 2020-05-16
*/
@Data
@TableName("roles_menus")
public class RolesMenus implements Serializable {
/** 菜单ID */
private Long menuId;
/** 角色ID */
private Long roleId;
public void copy(RolesMenus source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

122
yshop-admin/src/main/java/co/yixiang/modules/system/domain/User.java

@ -0,0 +1,122 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import co.yixiang.domain.BaseDomain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import java.sql.Timestamp;
import java.util.Objects;
import java.util.Set;
/**
* @author hupeng
* @date 2020-05-14
*/
@Getter
@Setter
@TableName("user")
public class User extends BaseDomain {
/** 系统用户ID */
@TableId
private Long id;
/** 头像 */
private Long avatarId;
/** 邮箱 */
private String email;
/** 状态:1启用、0禁用 */
private Boolean enabled;
/** 用户头像 */
@TableField(exist = false)
private String avatar;
/** 用户角色 */
@TableField(exist = false)
private Set<Role> roles;
/** 用户职位*/
@TableField(exist = false)
private Job job;
/** 用户部门*/
@TableField(exist = false)
private Dept dept;
/** 密码 */
private String password;
/** 用户名 */
@NotBlank(message = "请填写用户名称")
private String username;
/** 部门名称 */
private Long deptId;
/** 手机号码 */
@NotBlank(message = "请输入手机号码")
private String phone;
/** 岗位名称 */
private Long jobId;
/** 最后修改密码的日期 */
private Timestamp lastPasswordResetTime;
/** 昵称 */
private String nickName;
/** 性别 */
private String sex;
public @interface Update {}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(username, user.username);
}
@Override
public int hashCode() {
return Objects.hash(id, username);
}
public void copy(User source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

54
yshop-admin/src/main/java/co/yixiang/modules/system/domain/UserAvatar.java

@ -0,0 +1,54 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* @author hupeng
* @date 2020-05-14
*/
@Data
@TableName("user_avatar")
public class UserAvatar implements Serializable {
@TableId
private Long id;
/** 真实文件名 */
private String realName;
/** 路径 */
private String path;
/** 大小 */
private String size;
/** 创建时间 */
@TableField(fill= FieldFill.INSERT)
private Timestamp createTime;
public void copy(UserAvatar source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

36
yshop-admin/src/main/java/co/yixiang/modules/system/domain/UsersRoles.java

@ -0,0 +1,36 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
* 注意
* 本软件为www.yixiang.co开发研制未经购买不得使用
* 购买后可获得全部源代码禁止转卖分享上传到码云github等开源平台
* 一经发现盗用分享等行为将追究法律责任后果自负
*/
package co.yixiang.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* @author hupeng
* @date 2020-05-16
*/
@Data
@TableName("users_roles")
public class UsersRoles implements Serializable {
/** 用户ID */
private Long userId;
/** 角色ID */
private Long roleId;
public void copy(UsersRoles source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

26
yshop-admin/src/main/java/co/yixiang/modules/system/domain/vo/MenuMetaVo.java

@ -0,0 +1,26 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.system.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
/**
* @author hupeng
* @date 2018-12-20
*/
@Data
@AllArgsConstructor
public class MenuMetaVo implements Serializable {
private String title;
private String icon;
private Boolean noCache;
}

38
yshop-admin/src/main/java/co/yixiang/modules/system/domain/vo/MenuVo.java

@ -0,0 +1,38 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 构建前端路由时用到
* @author hupeng
* @date 2018-12-20
*/
@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class MenuVo implements Serializable {
private String name;
private String path;
private Boolean hidden;
private String redirect;
private String component;
private Boolean alwaysShow;
private MenuMetaVo meta;
private List<MenuVo> children;
}

21
yshop-admin/src/main/java/co/yixiang/modules/system/domain/vo/UserPassVo.java

@ -0,0 +1,21 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.system.domain.vo;
import lombok.Data;
/**
* 修改密码的 Vo
* @author hupeng
* @date 2019年7月11日13:59:49
*/
@Data
public class UserPassVo {
private String oldPass;
private String newPass;
}

137
yshop-admin/src/main/java/co/yixiang/modules/system/rest/DeptController.java

@ -0,0 +1,137 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.system.rest;
import cn.hutool.core.collection.CollectionUtil;
import co.yixiang.config.DataScope;
import co.yixiang.dozer.service.IGenerator;
import co.yixiang.exception.BadRequestException;
import co.yixiang.logging.aop.log.Log;
import co.yixiang.modules.aop.ForbidSubmit;
import co.yixiang.modules.system.domain.Dept;
import co.yixiang.modules.system.service.DeptService;
import co.yixiang.modules.system.service.dto.DeptDto;
import co.yixiang.modules.system.service.dto.DeptQueryCriteria;
import co.yixiang.utils.ValidationUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @author hupeng
* @date 2019-03-25
*/
@RestController
@Api(tags = "系统:部门管理")
@RequestMapping("/api/dept")
public class DeptController {
private final DeptService deptService;
private final DataScope dataScope;
private final IGenerator generator;
private static final String ENTITY_NAME = "dept";
public DeptController(DeptService deptService, DataScope dataScope, IGenerator generator) {
this.deptService = deptService;
this.dataScope = dataScope;
this.generator = generator;
}
@Log("导出部门数据")
@ApiOperation("导出部门数据")
@GetMapping(value = "/download")
@PreAuthorize("@el.check('admin','dept:list')")
public void download(HttpServletResponse response, DeptQueryCriteria criteria) throws IOException {
deptService.download(generator.convert(deptService.queryAll(criteria), DeptDto.class), response);
}
@Log("查询部门")
@ApiOperation("查询部门")
@GetMapping
@PreAuthorize("@el.check('user:list','admin','dept:list')")
public ResponseEntity<Object> getDepts(DeptQueryCriteria criteria){
// 数据权限
criteria.setIds(dataScope.getDeptIds());
List<DeptDto> deptDtos = generator.convert(deptService.queryAll(criteria),DeptDto.class);
return new ResponseEntity<>(deptService.buildTree(deptDtos),HttpStatus.OK);
}
@Log("新增部门")
@ApiOperation("新增部门")
@PostMapping
@PreAuthorize("@el.check('admin','dept:add')")
public ResponseEntity<Object> create(@Validated @RequestBody Dept resources){
if (resources.getId() != null) {
throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID");
}
return new ResponseEntity<>(deptService.save(resources),HttpStatus.CREATED);
}
@Log("修改部门")
@ApiOperation("修改部门")
@PutMapping
@PreAuthorize("@el.check('admin','dept:edit')")
public ResponseEntity<Object> update(@Validated @RequestBody Dept resources){
if(resources.getId().equals(resources.getPid())) {
throw new BadRequestException("上级不能为自己");
}
Dept dept = deptService.getOne(new LambdaQueryWrapper<Dept>()
.eq(Dept::getId,resources.getId()));
ValidationUtil.isNull( dept.getId(),"Dept","id",resources.getId());
resources.setId(dept.getId());
deptService.saveOrUpdate(resources);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@ForbidSubmit
@Log("删除部门")
@ApiOperation("删除部门")
@DeleteMapping
@PreAuthorize("@el.check('admin','dept:del')")
public ResponseEntity<Object> delete(@RequestBody Set<Long> ids){
List<Long> deptIds = new ArrayList<>();
for (Long id : ids) {
List<Dept> deptList = deptService.findByPid(id);
Dept dept = deptService.getOne(new LambdaQueryWrapper<Dept>().eq(Dept::getId,id));
if(null!=dept){
deptIds.add(dept.getId());
}
if(CollectionUtil.isNotEmpty(deptList)){
for(Dept d:deptList){
deptIds.add(d.getId());
}
}
}
deptService.delDepts(deptIds);
// try {
// deptService.delDepts(deptIds);
// }catch (Throwable e){
// throw new BadRequestException( "所选部门中存在岗位或者角色关联,请取消关联后再试");
// }
return new ResponseEntity<>(HttpStatus.OK);
}
}

108
yshop-admin/src/main/java/co/yixiang/modules/system/rest/DictController.java

@ -0,0 +1,108 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.system.rest;
import co.yixiang.dozer.service.IGenerator;
import co.yixiang.exception.BadRequestException;
import co.yixiang.logging.aop.log.Log;
import co.yixiang.modules.aop.ForbidSubmit;
import co.yixiang.modules.system.domain.Dict;
import co.yixiang.modules.system.service.DictService;
import co.yixiang.modules.system.service.dto.DictDto;
import co.yixiang.modules.system.service.dto.DictQueryCriteria;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author hupeng
* @date 2019-04-10
*/
@Api(tags = "系统:字典管理")
@RestController
@RequestMapping("/api/dict")
public class DictController {
private final DictService dictService;
private final IGenerator generator;
private static final String ENTITY_NAME = "dict";
public DictController(DictService dictService, IGenerator generator) {
this.dictService = dictService;
this.generator = generator;
}
@Log("导出字典数据")
@ApiOperation("导出字典数据")
@GetMapping(value = "/download")
@PreAuthorize("@el.check('admin','dict:list')")
public void download(HttpServletResponse response, DictQueryCriteria criteria) throws IOException {
dictService.download(generator.convert(dictService.queryAll(criteria), DictDto.class), response);
}
@Log("查询字典")
@ApiOperation("查询字典")
@GetMapping(value = "/all")
@PreAuthorize("@el.check('admin','dict:list')")
public ResponseEntity<Object> all(){
return new ResponseEntity<>(dictService.queryAll(new DictQueryCriteria()),HttpStatus.OK);
}
@Log("查询字典")
@ApiOperation("查询字典")
@GetMapping
@PreAuthorize("@el.check('admin','dict:list')")
public ResponseEntity<Object> getDicts(DictQueryCriteria resources, Pageable pageable){
return new ResponseEntity<>(dictService.queryAll(resources,pageable),HttpStatus.OK);
}
@Log("新增字典")
@ApiOperation("新增字典")
@PostMapping
@PreAuthorize("@el.check('admin','dict:add')")
public ResponseEntity<Object> create(@Validated @RequestBody Dict resources){
if (resources.getId() != null) {
throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID");
}
return new ResponseEntity<>(dictService.save(resources),HttpStatus.CREATED);
}
@ForbidSubmit
@Log("修改字典")
@ApiOperation("修改字典")
@PutMapping
@PreAuthorize("@el.check('admin','dict:edit')")
public ResponseEntity<Object> update(@Validated @RequestBody Dict resources){
dictService.saveOrUpdate(resources);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@ForbidSubmit
@Log("删除字典")
@ApiOperation("删除字典")
@DeleteMapping(value = "/{id}")
@PreAuthorize("@el.check('admin','dict:del')")
public ResponseEntity<Object> delete(@PathVariable Long id){
dictService.removeById(id);
return new ResponseEntity<>(HttpStatus.OK);
}
}

104
yshop-admin/src/main/java/co/yixiang/modules/system/rest/DictDetailController.java

@ -0,0 +1,104 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.system.rest;
import co.yixiang.exception.BadRequestException;
import co.yixiang.logging.aop.log.Log;
import co.yixiang.modules.aop.ForbidSubmit;
import co.yixiang.modules.system.domain.DictDetail;
import co.yixiang.modules.system.service.DictDetailService;
import co.yixiang.modules.system.service.dto.DictDetailQueryCriteria;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* @author hupeng
* @date 2019-04-10
*/
@RestController
@Api(tags = "系统:字典详情管理")
@RequestMapping("/api/dictDetail")
public class DictDetailController {
private final DictDetailService dictDetailService;
private static final String ENTITY_NAME = "dictDetail";
public DictDetailController(DictDetailService dictDetailService) {
this.dictDetailService = dictDetailService;
}
@Log("查询字典详情")
@ApiOperation("查询字典详情")
@GetMapping
public ResponseEntity<Object> getDictDetails(DictDetailQueryCriteria criteria,
@PageableDefault(sort = {"sort"}, direction = Sort.Direction.ASC) Pageable pageable){
return new ResponseEntity<>(dictDetailService.queryAll(criteria,pageable),HttpStatus.OK);
}
@Log("查询多个字典详情")
@ApiOperation("查询多个字典详情")
@GetMapping(value = "/map")
public ResponseEntity<Object> getDictDetailMaps(DictDetailQueryCriteria criteria,
@PageableDefault(sort = {"sort"}, direction = Sort.Direction.ASC) Pageable pageable){
String[] names = criteria.getDictName().split(",");
Map<String,Object> map = new HashMap<>(names.length);
for (String name : names) {
criteria.setDictName(name);
map.put(name,dictDetailService.queryAll(criteria,pageable).get("content"));
}
return new ResponseEntity<>(map,HttpStatus.OK);
}
@Log("新增字典详情")
@ApiOperation("新增字典详情")
@PostMapping
@PreAuthorize("@el.check('admin','dict:add')")
public ResponseEntity<Object> create(@Validated @RequestBody DictDetail resources){
if (resources.getId() != null) {
throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID");
}
resources.setDictId(resources.getDict().getId());
return new ResponseEntity<>(dictDetailService.save(resources),HttpStatus.CREATED);
}
@ForbidSubmit
@Log("修改字典详情")
@ApiOperation("修改字典详情")
@PutMapping
@PreAuthorize("@el.check('admin','dict:edit')")
public ResponseEntity<Object> update(@Validated @RequestBody DictDetail resources){
dictDetailService.saveOrUpdate(resources);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@ForbidSubmit
@Log("删除字典详情")
@ApiOperation("删除字典详情")
@DeleteMapping(value = "/{id}")
@PreAuthorize("@el.check('admin','dict:del')")
public ResponseEntity<Object> delete(@PathVariable Long id){
dictDetailService.removeById(id);
return new ResponseEntity<>(HttpStatus.OK);
}
}

113
yshop-admin/src/main/java/co/yixiang/modules/system/rest/JobController.java

@ -0,0 +1,113 @@
/**
* Copyright (C) 2018-2021
* All rights reserved, Designed By www.yixiang.co
*/
package co.yixiang.modules.system.rest;
import co.yixiang.config.DataScope;
import co.yixiang.dozer.service.IGenerator;
import co.yixiang.exception.BadRequestException;
import co.yixiang.logging.aop.log.Log;
import co.yixiang.modules.aop.ForbidSubmit;
import co.yixiang.modules.system.domain.Job;
import co.yixiang.modules.system.service.JobService;
import co.yixiang.modules.system.service.dto.JobDto;
import co.yixiang.modules.system.service.dto.JobQueryCriteria;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
/**
* @author hupeng
* @date 2019-03-29
*/
@Api(tags = "系统:岗位管理")
@RestController
@RequestMapping("/api/job")
public class JobController {
private final JobService jobService;
private final DataScope dataScope;
private final IGenerator generator;
private static final String ENTITY_NAME = "job";
public JobController(JobService jobService, DataScope dataScope, IGenerator generator) {
this.jobService = jobService;
this.dataScope = dataScope;
this.generator = generator;
}
@Log("导出岗位数据")
@ApiOperation("导出岗位数据")
@GetMapping(value = "/download")
@PreAuthorize("@el.check('admin','job:list')")
public void download(HttpServletResponse response, JobQueryCriteria criteria) throws IOException {
jobService.download(generator.convert(jobService.queryAll(criteria), JobDto.class), response);
}
@Log("查询岗位")
@ApiOperation("查询岗位")
@GetMapping
@PreAuthorize("@el.check('admin','job:list','user:list')")
public ResponseEntity<Object> getJobs(JobQueryCriteria criteria, Pageable pageable){
// 数据权限
criteria.setDeptIds(dataScope.getDeptIds());
return new ResponseEntity<>(jobService.queryAll(criteria, pageable),HttpStatus.OK);
}
@Log("新增岗位")
@ApiOperation("新增岗位")
@PostMapping
@PreAuthorize("@el.check('admin','job:add')")
public ResponseEntity<Object> create(@Validated @RequestBody Job resources){
if (resources.getId() != null) {
throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID");
}
resources.setDeptId(resources.getDept().getId());
return new ResponseEntity<>(jobService.save(resources),HttpStatus.CREATED);
}
@ForbidSubmit
@Log("修改岗位")
@ApiOperation("修改岗位")
@PutMapping
@PreAuthorize("@el.check('admin','job:edit')")
public ResponseEntity<Object> update(@Validated @RequestBody Job resources){
resources.setDeptId(resources.getDept().getId());
jobService.saveOrUpdate(resources);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@ForbidSubmit
@Log("删除岗位")
@ApiOperation("删除岗位")
@DeleteMapping
@PreAuthorize("@el.check('admin','job:del')")
public ResponseEntity<Object> delete(@RequestBody Set<Long> ids){
try {
jobService.removeByIds(ids);
}catch (Throwable e){
throw new BadRequestException( "所选岗位存在用户关联,请取消关联后再试");
}
return new ResponseEntity<>(HttpStatus.OK);
}
}

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

Loading…
Cancel
Save