dy-hu
3 years ago
commit
e5fed2de9f
1079 changed files with 135273 additions and 0 deletions
-
9.gitignore
-
28.run/yshop-admin_Dockerfile.run.xml
-
25.run/yshop-app_Dockerfile.run.xml
-
BINAlibaba-PuHuiTi-Regular.otf
-
57README.md
-
BINfx.jpg
-
270pom.xml
-
BINposter.jpg
-
64shell/jar-run2.sh
-
1shell/log.sh
-
1shell/start.sh
-
81shell/startup.sh
-
7shell/stop.sh
-
81shell/yshop.sh
-
BINsimsunb.ttf
-
11450sql/.yshop.version/yshop-V1.1.pdman.json
-
11402sql/.yshop.version/yshop-V1.2.pdman.json
-
11247sql/.yshop.version/yshop-base.pdman.json
-
136sql/3.0升级3.1sql.sql
-
52sql/3.1升级3.2sql.sql
-
4sql/sql执行顺序说明.txt
-
12704sql/yshop.pdman.json
-
7410sql/yshop3.2.sql
-
50sql/新增售后相关sql增量sql
-
18yshop-admin/Dockerfile
-
112yshop-admin/pom.xml
-
69yshop-admin/src/main/java/co/yixiang/AppRun.java
-
69yshop-admin/src/main/java/co/yixiang/config/ConfigurerAdapter.java
-
47yshop-admin/src/main/java/co/yixiang/config/CorsFilter.java
-
95yshop-admin/src/main/java/co/yixiang/config/DataScope.java
-
22yshop-admin/src/main/java/co/yixiang/config/MybatisPlusConfig.java
-
24yshop-admin/src/main/java/co/yixiang/config/WebSocketConfig.java
-
60yshop-admin/src/main/java/co/yixiang/config/thread/AsyncTaskExecutePool.java
-
29yshop-admin/src/main/java/co/yixiang/config/thread/AsyncTaskProperties.java
-
53yshop-admin/src/main/java/co/yixiang/config/thread/TheadFactoryName.java
-
32yshop-admin/src/main/java/co/yixiang/config/thread/ThreadPoolExecutorUtil.java
-
36yshop-admin/src/main/java/co/yixiang/handler/ApiErr.java
-
43yshop-admin/src/main/java/co/yixiang/handler/ApiError.java
-
130yshop-admin/src/main/java/co/yixiang/handler/GlobalExceptionHandler.java
-
26yshop-admin/src/main/java/co/yixiang/modules/monitor/config/VisitsInitialization.java
-
35yshop-admin/src/main/java/co/yixiang/modules/monitor/domain/Visits.java
-
24yshop-admin/src/main/java/co/yixiang/modules/monitor/domain/vo/RedisVo.java
-
34yshop-admin/src/main/java/co/yixiang/modules/monitor/rest/LimitController.java
-
55yshop-admin/src/main/java/co/yixiang/modules/monitor/rest/RedisController.java
-
47yshop-admin/src/main/java/co/yixiang/modules/monitor/rest/VisitsController.java
-
44yshop-admin/src/main/java/co/yixiang/modules/monitor/service/RedisService.java
-
38yshop-admin/src/main/java/co/yixiang/modules/monitor/service/VisitsService.java
-
87yshop-admin/src/main/java/co/yixiang/modules/monitor/service/impl/RedisServiceImpl.java
-
102yshop-admin/src/main/java/co/yixiang/modules/monitor/service/impl/VisitsServiceImpl.java
-
15yshop-admin/src/main/java/co/yixiang/modules/monitor/service/mapper/VisitsMapper.java
-
44yshop-admin/src/main/java/co/yixiang/modules/quartz/config/JobRunner.java
-
62yshop-admin/src/main/java/co/yixiang/modules/quartz/config/QuartzConfig.java
-
65yshop-admin/src/main/java/co/yixiang/modules/quartz/domain/QuartzJob.java
-
65yshop-admin/src/main/java/co/yixiang/modules/quartz/domain/QuartzLog.java
-
150yshop-admin/src/main/java/co/yixiang/modules/quartz/rest/QuartzJobController.java
-
67yshop-admin/src/main/java/co/yixiang/modules/quartz/service/QuartzJobService.java
-
47yshop-admin/src/main/java/co/yixiang/modules/quartz/service/QuartzLogService.java
-
47yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzJobDto.java
-
32yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzJobQueryCriteria.java
-
46yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzLogDto.java
-
16yshop-admin/src/main/java/co/yixiang/modules/quartz/service/dto/QuartzLogQueryCriteria.java
-
168yshop-admin/src/main/java/co/yixiang/modules/quartz/service/impl/QuartzJobServiceImpl.java
-
100yshop-admin/src/main/java/co/yixiang/modules/quartz/service/impl/QuartzLogServiceImpl.java
-
19yshop-admin/src/main/java/co/yixiang/modules/quartz/service/mapper/QuartzJobMapper.java
-
19yshop-admin/src/main/java/co/yixiang/modules/quartz/service/mapper/QuartzLogMapper.java
-
27yshop-admin/src/main/java/co/yixiang/modules/quartz/task/TestTask.java
-
22yshop-admin/src/main/java/co/yixiang/modules/quartz/task/VisitsTask.java
-
78yshop-admin/src/main/java/co/yixiang/modules/quartz/utils/ExecutionJob.java
-
173yshop-admin/src/main/java/co/yixiang/modules/quartz/utils/QuartzManage.java
-
49yshop-admin/src/main/java/co/yixiang/modules/quartz/utils/QuartzRunnable.java
-
139yshop-admin/src/main/java/co/yixiang/modules/security/config/SecurityConfig.java
-
43yshop-admin/src/main/java/co/yixiang/modules/security/config/SecurityProperties.java
-
161yshop-admin/src/main/java/co/yixiang/modules/security/rest/AuthController.java
-
83yshop-admin/src/main/java/co/yixiang/modules/security/rest/OnlineController.java
-
27yshop-admin/src/main/java/co/yixiang/modules/security/security/JwtAccessDeniedHandler.java
-
29yshop-admin/src/main/java/co/yixiang/modules/security/security/JwtAuthenticationEntryPoint.java
-
29yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenConfigurer.java
-
75yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenFilter.java
-
118yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenProvider.java
-
238yshop-admin/src/main/java/co/yixiang/modules/security/security/TokenUtil.java
-
35yshop-admin/src/main/java/co/yixiang/modules/security/security/vo/AuthUser.java
-
90yshop-admin/src/main/java/co/yixiang/modules/security/security/vo/JwtUser.java
-
189yshop-admin/src/main/java/co/yixiang/modules/security/service/OnlineUserService.java
-
71yshop-admin/src/main/java/co/yixiang/modules/security/service/UserDetailsServiceImpl.java
-
56yshop-admin/src/main/java/co/yixiang/modules/system/domain/Dept.java
-
49yshop-admin/src/main/java/co/yixiang/modules/system/domain/Dict.java
-
56yshop-admin/src/main/java/co/yixiang/modules/system/domain/DictDetail.java
-
59yshop-admin/src/main/java/co/yixiang/modules/system/domain/Job.java
-
92yshop-admin/src/main/java/co/yixiang/modules/system/domain/Menu.java
-
67yshop-admin/src/main/java/co/yixiang/modules/system/domain/Role.java
-
36yshop-admin/src/main/java/co/yixiang/modules/system/domain/RolesDepts.java
-
36yshop-admin/src/main/java/co/yixiang/modules/system/domain/RolesMenus.java
-
122yshop-admin/src/main/java/co/yixiang/modules/system/domain/User.java
-
54yshop-admin/src/main/java/co/yixiang/modules/system/domain/UserAvatar.java
-
36yshop-admin/src/main/java/co/yixiang/modules/system/domain/UsersRoles.java
-
26yshop-admin/src/main/java/co/yixiang/modules/system/domain/vo/MenuMetaVo.java
-
38yshop-admin/src/main/java/co/yixiang/modules/system/domain/vo/MenuVo.java
-
21yshop-admin/src/main/java/co/yixiang/modules/system/domain/vo/UserPassVo.java
-
137yshop-admin/src/main/java/co/yixiang/modules/system/rest/DeptController.java
-
108yshop-admin/src/main/java/co/yixiang/modules/system/rest/DictController.java
@ -0,0 +1,9 @@ |
|||||
|
*.classpath |
||||
|
*.project |
||||
|
*.iml |
||||
|
*.factorypath |
||||
|
target |
||||
|
.idea/ |
||||
|
*.log |
||||
|
logs |
||||
|
*.DS_Store |
@ -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> |
@ -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> |
@ -0,0 +1,57 @@ |
|||||
|
<h1 style="text-align: center">商城</h1> |
||||
|
|
||||
|
|
||||
|
#### 项目简介 |
||||
|
基于当前流行技术组合的前后端分离商城系统: SpringBoot2+Jpa+MybatisPlus+SpringSecurity+jwt+redis+Vue的前后端分离的商城系统, 包含商城、拼团、砍价、商户管理、 秒杀、优惠券、积分、分销、会员、充值、到店核销等功能。 |
||||
|
|
||||
|
|
||||
|
## 商城功能 |
||||
|
|
||||
|
* 一:商品模块:商品添加、规格设置,商品上下架等 |
||||
|
* 二:订单模块:下单、购物车、支付,发货、收货、评价、退款等 |
||||
|
* 三:营销模块:积分、优惠券、分销、砍价、拼团、秒杀(、到店核销等 |
||||
|
* 四:微信模块:自定义菜单、自动回复、微信授权、图文管理、模板消息推送 |
||||
|
* 五:配置模块:各种配置 |
||||
|
* 六:用户模块:登陆、注册、会员卡等 |
||||
|
* 七:其他等 |
||||
|
|
||||
|
|
||||
|
#### 项目结构 |
||||
|
项目采用分模块开发方式 |
||||
|
- app 移动端API模块(H5+uniapp端的API) |
||||
|
- admin 管理后台模块 |
||||
|
- weixin 微信相关模块 |
||||
|
- mall 商城公共模块 |
||||
|
- shop 后台商城模块 |
||||
|
- message 消息队列模块 |
||||
|
- common 公共模块 |
||||
|
- logging 日志模块 |
||||
|
- tools 第三方工具模块 |
||||
|
- generator 代码生成模块 |
||||
|
- mproot mybatisPlus配置模块 |
||||
|
|
||||
|
|
||||
|
## 技术选型 |
||||
|
* 1 后端使用技术 |
||||
|
* 1.1 SpringBoot2 |
||||
|
* 1.2 mybatis、MyBatis-Plus |
||||
|
* 1.3 SpringSecurity |
||||
|
* 1.5 Druid |
||||
|
* 1.6 Slf4j |
||||
|
* 1.7 Fastjson |
||||
|
* 1.8 JWT |
||||
|
* 1.9 Redis |
||||
|
* 1.10 Quartz |
||||
|
* 1.11 Mysql |
||||
|
* 1.12 swagger |
||||
|
* 1.13 WxJava |
||||
|
* 1.14 Lombok |
||||
|
* 1.15 Hutool |
||||
|
* 1.16 Mapstruct |
||||
|
* 1.17 Redisson |
||||
|
|
||||
|
* 前端使用技术 |
||||
|
* 2.1 Vue 全家桶 |
||||
|
* 2.2 Element |
||||
|
* 2.3 uniapp |
||||
|
|
After Width: 600 | Height: 1000 | Size: 95 KiB |
@ -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.8</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> |
After Width: 690 | Height: 1130 | Size: 29 KiB |
@ -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 |
@ -0,0 +1 @@ |
|||||
|
tail -f nohup.out |
@ -0,0 +1 @@ |
|||||
|
nohup java -jar yshop-app-3.2.jar --spring.profiles.active=prod & |
@ -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 |
@ -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 |
@ -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 |
11450
sql/.yshop.version/yshop-V1.1.pdman.json
File diff suppressed because it is too large
View File
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
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
File diff suppressed because it is too large
View File
@ -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; |
@ -0,0 +1,52 @@ |
|||||
|
|
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- 字段修改 |
||||
|
-- ---------------------------- |
||||
|
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'; |
||||
|
|
||||
|
ALTER TABLE yx_store_visit modify column id bigint(20) NOT NULL; |
||||
|
|
||||
|
ALTER TABLE yx_store_visit modify column product_id bigint(20) NULL DEFAULT NULL COMMENT '产品ID'; |
||||
|
|
||||
|
ALTER TABLE yx_store_visit modify column uid bigint(20) NULL DEFAULT NULL COMMENT '用户ID'; |
||||
|
|
||||
|
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); |
||||
|
|
||||
|
|
||||
|
|
@ -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操作!!! |
12704
sql/yshop.pdman.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
7410
sql/yshop3.2.sql
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,50 @@ |
|||||
|
CREATE TABLE `yx_store_after_sales_status` ( |
||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT, |
||||
|
`store_after_sales_id` bigint(20) DEFAULT NULL COMMENT '售后id', |
||||
|
`change_type` tinyint(1) DEFAULT NULL COMMENT '操作类型', |
||||
|
`change_message` varchar(128) COLLATE utf8_bin DEFAULT NULL COMMENT '操作备注', |
||||
|
`change_time` datetime DEFAULT NULL COMMENT '操作时间', |
||||
|
`operator` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '操作人', |
||||
|
PRIMARY KEY (`id`) |
||||
|
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='售后订单操作详情表'; |
||||
|
|
||||
|
CREATE TABLE `yx_store_after_sales_item` ( |
||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id', |
||||
|
`store_after_sales_id` bigint(20) DEFAULT NULL COMMENT '售后id', |
||||
|
`product_id` bigint(20) DEFAULT NULL COMMENT '商品id', |
||||
|
`cart_info` text COLLATE utf8_bin NOT NULL COMMENT '退货东西的详情信息', |
||||
|
`is_del` bit(1) DEFAULT NULL COMMENT '逻辑删除', |
||||
|
PRIMARY KEY (`id`) |
||||
|
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='售后子表'; |
||||
|
|
||||
|
|
||||
|
CREATE TABLE `yx_store_after_sales` ( |
||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT, |
||||
|
`order_code` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '订单号', |
||||
|
`refund_amount` decimal(32,8) DEFAULT NULL COMMENT '退款金额', |
||||
|
`service_type` tinyint(1) DEFAULT NULL COMMENT '服务类型0仅退款1退货退款', |
||||
|
`reasons` text COLLATE utf8_bin COMMENT '申请原因', |
||||
|
`explains` text COLLATE utf8_bin COMMENT '说明', |
||||
|
`explain_img` text COLLATE utf8_bin COMMENT '说明图片->多个用逗号分割', |
||||
|
`shipper_code` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '物流公司编码', |
||||
|
`delivery_sn` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '物流单号', |
||||
|
`delivery_name` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '物流名称', |
||||
|
`state` tinyint(1) DEFAULT NULL COMMENT '状态 0已提交等待平台审核 1平台已审核 等待用户发货/退款 2 用户已发货 3退款成功', |
||||
|
`sales_state` tinyint(1) DEFAULT NULL COMMENT '售后状态-0正常1用户取消2商家拒绝', |
||||
|
`create_time` datetime DEFAULT NULL COMMENT '添加时间', |
||||
|
`is_del` tinyint(1) DEFAULT NULL COMMENT '逻辑删除', |
||||
|
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id', |
||||
|
`consignee` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '商家收货人', |
||||
|
`phone_number` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '商家手机号', |
||||
|
`address` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '商家地址', |
||||
|
PRIMARY KEY (`id`) |
||||
|
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='售后记录表'; |
||||
|
|
||||
|
|
||||
|
ALTER TABLE yx_store_order_cart_info ADD COLUMN is_after_sales tinyint(1) unsigned zerofill DEFAULT '0' COMMENT '是否能售后 0不能1能'; |
||||
|
|
||||
|
|
||||
|
INSERT INTO `menu` VALUES (266, b'0', '售后', 'shop/afterSeals/index', 53, 44, 'order', 'afterSeals', b'0', b'0', 'AfterSeals', '2021-06-30 15:23:38', 'yxStoreAfterSales:list', 1, '2021-06-30 15:33:14', 0); |
||||
|
INSERT INTO `menu` VALUES (267, b'0', '新增', NULL, 266, 999, NULL, NULL, b'0', b'0', '-', '2021-06-30 15:34:17', 'yxStoreAfterSales:add', 2, NULL, 0); |
||||
|
INSERT INTO `menu` VALUES (268, b'0', '修改', NULL, 266, 999, NULL, NULL, b'0', b'0', '-', '2021-06-30 15:34:39', 'yxStoreAfterSales:edit', 2, NULL, 0); |
||||
|
INSERT INTO `menu` VALUES (269, b'0', '删除', NULL, 266, 999, NULL, NULL, b'0', b'0', '-', '2021-06-30 15:34:55', 'yxStoreAfterSales:del', 2, NULL, 0); |
@ -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 |
@ -0,0 +1,112 @@ |
|||||
|
<?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> |
||||
|
<!--weixinpay--> |
||||
|
<dependency> |
||||
|
<groupId>co.yixiang</groupId> |
||||
|
<artifactId>yshop-weixin</artifactId> |
||||
|
<version>3.2</version> |
||||
|
<exclusions> |
||||
|
<exclusion> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-security</artifactId> |
||||
|
</exclusion> |
||||
|
</exclusions> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.security</groupId> |
||||
|
<artifactId>spring-security-core</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.security</groupId> |
||||
|
<artifactId>spring-security-web</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.security</groupId> |
||||
|
<artifactId>spring-security-config</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> |
@ -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"; |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
// } |
||||
|
//} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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(); |
||||
|
} |
||||
|
} |
@ -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(); |
||||
|
} |
||||
|
|
||||
|
} |
@ -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()); |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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() |
||||
|
); |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
@ -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; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
@ -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())); |
||||
|
} |
||||
|
} |
@ -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("--------------- 初始化站点统计完成 ---------------"); |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
@ -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; |
||||
|
} |
@ -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(); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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(); |
||||
|
} |
@ -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(); |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -0,0 +1,102 @@ |
|||||
|
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())); |
||||
|
if (visits == null) { |
||||
|
visits = new Visits(); |
||||
|
visits.setPvCounts(1L); |
||||
|
} else { |
||||
|
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; |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
@ -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("--------------------定时任务注入完成---------------------"); |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
@ -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; |
||||
|
} |
@ -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; |
||||
|
|
||||
|
} |
@ -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; |
||||
|
} |
@ -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; |
||||
|
} |
@ -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{ |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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> { |
||||
|
|
||||
|
} |
@ -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> { |
||||
|
|
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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(); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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("定时任务暂停失败"); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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 + " "; |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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()); |
||||
|
} |
||||
|
} |
@ -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()); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
||||
|
*/ |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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= ******}"; |
||||
|
} |
||||
|
} |
@ -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()); |
||||
|
} |
||||
|
} |
@ -0,0 +1,189 @@ |
|||||
|
/** |
||||
|
* Copyright (C) 2018-2021 |
||||
|
* All rights reserved, Designed By www.yixiang.co |
||||
|
*/ |
||||
|
package co.yixiang.modules.security.service; |
||||
|
|
||||
|
import cn.hutool.core.util.StrUtil; |
||||
|
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.*; |
||||
|
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.*; |
||||
|
|
||||
|
/** |
||||
|
* @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[] split = StrUtil.split(key, ":"); |
||||
|
String keyt = ShopConstants.YSHOP_APP_LOGIN_USER + split[0] + ":" + EncryptUtils.desDecrypt(split[1]); |
||||
|
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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
@ -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() |
||||
|
); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
@ -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; |
||||
|
} |
@ -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; |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue