Răsfoiți Sursa

!78 同步dev分支
Merge pull request !78 from 疯狂的狮子Li/dev

疯狂的狮子Li 3 ani în urmă
părinte
comite
0375fd319c
100 a modificat fișierele cu 2782 adăugiri și 3334 ștergeri
  1. 24 33
      README.md
  2. 15 16
      docker/docker-compose.yml
  3. 33 9
      pom.xml
  4. 1 1
      ruoyi-admin/pom.xml
  5. 3 3
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
  6. 0 88
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
  7. 50 50
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
  8. 4 4
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
  9. 4 4
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
  10. 8 9
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java
  11. 5 6
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
  12. 6 7
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java
  13. 7 8
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
  14. 5 6
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
  15. 2 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java
  16. 3 4
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
  17. 8 20
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java
  18. 3 4
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java
  19. 108 0
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java
  20. 30 8
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java
  21. 13 20
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
  22. 10 6
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
  23. 38 0
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java
  24. 8 9
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
  25. 38 19
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
  26. 5 54
      ruoyi-admin/src/main/resources/application-dev.yml
  27. 5 54
      ruoyi-admin/src/main/resources/application-prod.yml
  28. 24 5
      ruoyi-admin/src/main/resources/application.yml
  29. 2 1
      ruoyi-admin/src/main/resources/i18n/messages.properties
  30. 1 1
      ruoyi-admin/src/main/resources/i18n/messages_en_US.properties
  31. 2 1
      ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
  32. 26 0
      ruoyi-admin/src/main/resources/spy.properties
  33. 11 1
      ruoyi-common/pom.xml
  34. 0 165
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java
  35. 30 0
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/ExcelDictFormat.java
  36. 0 18
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java
  37. 40 0
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java
  38. 29 23
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java
  39. 1 33
      ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java
  40. 13 10
      ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
  41. 12 0
      ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java
  42. 69 0
      ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelDictConvert.java
  43. 36 2
      ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java
  44. 39 17
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java
  45. 56 38
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java
  46. 103 75
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java
  47. 109 81
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java
  48. 63 45
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java
  49. 125 84
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
  50. 132 100
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java
  51. 157 135
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
  52. 18 2
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
  53. 11 0
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java
  54. 7 8
      ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/methods/InsertAll.java
  55. 20 0
      ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java
  56. 0 43
      ruoyi-common/src/main/java/com/ruoyi/common/exception/CustomException.java
  57. 58 0
      ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java
  58. 73 0
      ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java
  59. 4 4
      ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java
  60. 2 2
      ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java
  61. 2 2
      ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java
  62. 48 48
      ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java
  63. 9 32
      ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java
  64. 3 4
      ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
  65. 185 187
      ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java
  66. 5 7
      ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java
  67. 3 4
      ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java
  68. 35 5
      ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
  69. 2 3
      ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java
  70. 354 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
  71. 0 76
      ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java
  72. 0 239
      ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
  73. 1 75
      ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
  74. 0 102
      ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java
  75. 0 59
      ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java
  76. 2 2
      ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
  77. 138 1060
      ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
  78. 8 9
      ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java
  79. 5 5
      ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java
  80. 1 1
      ruoyi-demo/pom.xml
  81. 9 2
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java
  82. 5 4
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
  83. 29 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestI18nController.java
  84. 3 3
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
  85. 13 10
      ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java
  86. 9 6
      ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestTreeVo.java
  87. 4 4
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java
  88. 3 3
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java
  89. 1 1
      ruoyi-extend/pom.xml
  90. 1 1
      ruoyi-extend/ruoyi-monitor-admin/pom.xml
  91. 1 1
      ruoyi-framework/pom.xml
  92. 12 13
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
  93. 2 2
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java
  94. 5 6
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
  95. 116 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java
  96. 4 4
      ruoyi-framework/src/main/java/com/ruoyi/framework/captcha/UnsignedMathGenerator.java
  97. 1 11
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java
  98. 2 2
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java
  99. 4 3
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java
  100. 48 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java

+ 24 - 33
README.md

@@ -4,8 +4,8 @@
 [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE)
 [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
 <br>
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-2.6.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
-[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.4-blue.svg)]()
+[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-3.0.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
+[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.5-blue.svg)]()
 [![JDK-8+](https://img.shields.io/badge/JDK-8+-green.svg)]()
 [![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()
 
@@ -21,51 +21,39 @@ RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级 定期
 * 权限认证框架 Spring Security、Jwt,支持多终端认证系统
 * 关系数据库 MySQL 适配 8.X 
 * 缓存数据库 Redis 适配 6.X
-* 数据库开发框架 Mybatis-Plus 快速 CRUD 增加开发效率 插件化支持各类需求
+* 数据库框架 Mybatis-Plus 快速 CRUD 增加开发效率 插件化支持各类需求
+* 数据库框架 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构
+* 数据库框架 Redis客户端 采用 Redisson 性能更强
+* 数据库框架 性能分析插件 p6spy 更强劲的 SQL 分析
+* 序列化框架 统一使用 jackson 高效可靠
 * 网络框架 Feign、OkHttp3 接口化管理 HTTP 请求
-* 工具类框架 Hutool、Lombok 减少代码冗余 增加安全性
+* 分布式锁 Lock4j 注解锁、工具锁 多种多样
+* 文件存储 OSS 对象存储模块 支持(Minio、七牛、阿里、腾讯)
 * 监控框架 spring-boot-admin 全方位服务监控
 * 校验框架 validation 增强接口安全性 严谨性
+* Excel框架 Alibaba EasyExcel 性能优异 扩展性强
 * 文档框架 knife4j 美化接口文档
-* 序列化框架 统一使用 jackson 高效可靠
+* 工具类框架 Hutool、Lombok 减少代码冗余 增加安全性
 * 代码生成器 一键生成前后端代码
-* 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构
-* Redis客户端 采用 Redisson 性能更强
-* 分布式锁 Lock4j 注解锁、工具锁 多种多样
 * 部署方式 Docker 容器编排 一键部署业务集群
-* 文件存储 OSS 对象存储模块 支持(Minio、七牛、阿里、腾讯)
+* 国际化 Spring 标准国际化方解决方案
 
 ## 参考文档
 
 使用框架前请仔细阅读文档重点注意事项
 <br>
 >[初始化项目 必看](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于初始化项目?sort_id=4164117)
+>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于初始化项目?sort_id=4164117](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于初始化项目?sort_id=4164117)
 > 
 >[部署项目 必看](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于应用部署?sort_id=4219382)
+>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于应用部署?sort_id=4219382](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于应用部署?sort_id=4219382)
 > 
 >[参考文档 Wiki](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
+>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
 
-## 提问四部曲
-### 一、查阅wiki
-优先在`wiki->重点事项`,查找是否有相关问题及解决方案,尤其是框架更新后产生的问题,多会在wiki中提及
-
-> [参考文档 Wiki](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
-
-### 二、借助issues
-尝试issues中搜索问题关键字(记得选择已完成),看看是否有其他人提出相同问题
-- `如果有`那么依据评论中的解决方案自行尝试解决
-- `如果没有`那么提交一个新的issues描述清楚你的问题,需要包含以下内容(优质的issues,能够帮助作者更高效的帮你解决问题):
-    - 出现问题的模块或功能或类,总之你要说清楚在哪出的问题
-    - 描述产生问题的相关操作流程,以便复现快速解决
-    - 报错的日志截图,一定是截图,不要复制一堆报错的文本
-> [issues](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/issues)
-
-### 三、百度
-大家都懂,不多描述,将关键的报错信息CC->CV到百度中看看大佬们怎么解决的
-> [百度](http://www.baidu.com)
+## 软件架构图
 
-### 四、加群
-以上三点已经能解决大家绝大部分问题了,如果还有问题没能通过这几种方式解决,那么加群,大家一起在群里探讨一下
+![Plus部署架构图](https://images.gitee.com/uploads/images/2021/0729/112230_4295e5ce_1766278.png "Plus部署架构图.png")
 
 ## 贡献代码
 
@@ -79,7 +67,8 @@ RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级 定期
 * ORM框架 使用 Mybatis-Plus 简化CRUD (不支持主子表)
 * Bean简化 使用 Lombok 简化 get set toString 等等
 * 容器改动 Tomcat 改为 并发性能更好的 undertow
-* 分页移除 pagehelper 改为 Mybatis-Plus 分页
+* 移除 pagehelper 改为 Mybatis-Plus 分页
+* 集成 p6spy 更强劲的 SQL 分析
 * 升级 swagger 为 knife4j
 * 集成 Hutool 5.X 并重写RuoYi部分功能
 * 集成 Feign 接口化管理 Http 请求(如三方请求 支付,短信,推送等)
@@ -90,7 +79,8 @@ RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级 定期
 * 集成 dynamic-datasource 多数据源(默认支持MySQL,其他种类需自行适配)
 * 集成 Lock4j 实现分布式 注解锁、工具锁 多种多样
 * 增加 Docker 容器编排 打包插件与部署脚本
-* 移除 本地文件上传 改为 OSS对象存储 支持(Minio、七牛、阿里、腾讯)
+* 移除 通用上传下载 改为 OSS对象存储 支持(Minio、七牛、阿里、腾讯)
+* 移除 RuoYi自带 Excel 工具 改为 EasyExcel 工具
 
 ### 代码改动
 
@@ -112,8 +102,9 @@ RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级 定期
 * 单模块 fast 分支 [RuoYi-Vue-Plus-fast](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/)
 * Oracle 模块 oracle 分支 [RuoYi-Vue-Plus-oracle](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/oracle/)
 
-## 扫码加群 一起交流
-![输入图片说明](https://images.gitee.com/uploads/images/2021/0625/160026_11d949aa_1766278.jpeg "07f7121fab14e57e03e5f6a35eff6ce.jpg")
+## 加群与捐献
+>[加群与捐献](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598)
+>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598)
 
 ## 捐献作者
 作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭  

+ 15 - 16
docker/docker-compose.yml

@@ -18,8 +18,6 @@ services:
       - /docker/mysql/data/:/var/lib/mysql/
       # 配置挂载
       - /docker/mysql/conf/:/etc/mysql/conf.d/
-      # 主机本机时间文件映射 与本机时间同步
-      - /etc/localtime:/etc/localtime:ro
     command:
       # 将mysql8.0默认密码策略 修改为 原先 策略 (mysql8.0对其默认策略做了更改 会导致密码无法匹配)
       --default-authentication-plugin=mysql_native_password
@@ -37,6 +35,9 @@ services:
     # 如果需要指定版本 就把 latest 换成版本号
     image: nginx:latest
     container_name: nginx-web
+    environment:
+      # 时区上海
+      TZ: Asia/Shanghai
     ports:
       - 80:80
       - 443:443
@@ -49,8 +50,6 @@ services:
       - /docker/nginx/html:/usr/share/nginx/html
       # 日志目录
       - /docker/nginx/log:/var/log/nginx
-      # 主机本机时间文件映射 与本机时间同步
-      - /etc/localtime:/etc/localtime:ro
     privileged: true
     restart: always
     networks:
@@ -62,16 +61,13 @@ services:
     ports:
       - 6379:6379
     environment:
-      # 设置环境变量 时区上海 编码UTF-8
+      # 时区上海
       TZ: Asia/Shanghai
-      LANG: en_US.UTF-8
     volumes:
       # 配置文件
       - /docker/redis/conf/redis.conf:/redis.conf:rw
       # 数据文件
       - /docker/redis/data:/data:rw
-      # 主机本机时间文件映射 与本机时间同步
-      - /etc/localtime:/etc/localtime:ro
     command: "redis-server --appendonly yes"
     privileged: true
     restart: always
@@ -88,6 +84,8 @@ services:
       # 控制台端口
       - 9001:9001
     environment:
+      # 时区上海
+      TZ: Asia/Shanghai
       # 管理后台用户名
       MINIO_ACCESS_KEY: ruoyi
       # 管理后台密码,最小8个字符
@@ -97,8 +95,6 @@ services:
       - /docker/minio/data:/data
       # 映射配置目录
       - /docker/minio/config:/root/.minio/
-      # 主机本机时间文件映射 与本机时间同步
-      - /etc/localtime:/etc/localtime:ro
     command: server --console-address ':9001' /data  # 指定容器中的目录 /data
     privileged: true
     restart: always
@@ -107,9 +103,10 @@ services:
         ipv4_address: 172.30.0.54
 
   ruoyi-server1:
-    image: "ruoyi/ruoyi-server:2.6.0"
+    image: "ruoyi/ruoyi-server:3.0.0"
     environment:
-      - TZ=Asia/Shanghai
+      # 时区上海
+      TZ: Asia/Shanghai
     volumes:
       # 配置文件
       - /docker/server1/logs/:/ruoyi/server/logs/
@@ -121,9 +118,10 @@ services:
         ipv4_address: 172.30.0.60
 
   ruoyi-server2:
-    image: "ruoyi/ruoyi-server:2.6.0"
+    image: "ruoyi/ruoyi-server:3.0.0"
     environment:
-      - TZ=Asia/Shanghai
+      # 时区上海
+      TZ: Asia/Shanghai
     volumes:
       # 配置文件
       - /docker/server2/logs/:/ruoyi/server/logs/
@@ -135,9 +133,10 @@ services:
         ipv4_address: 172.30.0.61
 
   ruoyi-monitor-admin:
-    image: "ruoyi/ruoyi-monitor-admin:2.6.0"
+    image: "ruoyi/ruoyi-monitor-admin:3.0.0"
     environment:
-      - TZ=Asia/Shanghai
+      # 时区上海
+      TZ: Asia/Shanghai
     privileged: true
     restart: always
     networks:

+ 33 - 9
pom.xml

@@ -6,14 +6,14 @@
 
     <groupId>com.ruoyi</groupId>
     <artifactId>ruoyi-vue-plus</artifactId>
-    <version>2.6.0</version>
+    <version>3.0.0</version>
 
     <name>RuoYi-Vue-Plus</name>
     <url>https://gitee.com/JavaLionLi/RuoYi-Vue-Plus</url>
     <description>RuoYi-Vue-Plus后台管理系统</description>
 
     <properties>
-        <ruoyi-vue-plus.version>2.6.0</ruoyi-vue-plus.version>
+        <ruoyi-vue-plus.version>3.0.0</ruoyi-vue-plus.version>
         <spring-boot.version>2.5.3</spring-boot.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
@@ -22,16 +22,19 @@
         <druid.version>1.2.6</druid.version>
         <knife4j.version>3.0.3</knife4j.version>
         <poi.version>4.1.2</poi.version>
+        <easyexcel.version>2.2.10</easyexcel.version>
         <velocity.version>1.7</velocity.version>
         <jwt.version>0.9.1</jwt.version>
         <mybatis-plus.version>3.4.3</mybatis-plus.version>
-        <hutool.version>5.7.6</hutool.version>
+        <p6spy.version>3.9.1</p6spy.version>
+        <hutool.version>5.7.7</hutool.version>
         <feign.version>3.0.3</feign.version>
-        <feign-okhttp.version>11.0</feign-okhttp.version>
-        <spring-boot-admin.version>2.4.3</spring-boot-admin.version>
+        <feign-okhttp.version>11.2</feign-okhttp.version>
+        <okhttp.version>4.9.1</okhttp.version>
+        <spring-boot-admin.version>2.5.0</spring-boot-admin.version>
         <redisson.version>3.16.1</redisson.version>
         <lock4j.version>2.2.1</lock4j.version>
-        <datasource.version>3.4.1</datasource.version>
+        <dynamic-ds.version>3.4.1</dynamic-ds.version>
 
         <!-- OSS 配置 -->
         <qiniu.version>7.8.0</qiniu.version>
@@ -79,6 +82,12 @@
                 <version>${poi.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>easyexcel</artifactId>
+                <version>${easyexcel.version}</version>
+            </dependency>
+
             <!-- velocity代码生成使用模板 -->
             <dependency>
                 <groupId>org.apache.velocity</groupId>
@@ -97,7 +106,7 @@
             <dependency>
                 <groupId>com.baomidou</groupId>
                 <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
-                <version>${datasource.version}</version>
+                <version>${dynamic-ds.version}</version>
             </dependency>
 
             <dependency>
@@ -110,6 +119,12 @@
                 <artifactId>mybatis-plus-extension</artifactId>
                 <version>${mybatis-plus.version}</version>
             </dependency>
+            <!-- sql性能分析插件 -->
+            <dependency>
+                <groupId>p6spy</groupId>
+                <artifactId>p6spy</artifactId>
+                <version>${p6spy.version}</version>
+            </dependency>
 
             <dependency>
                 <groupId>cn.hutool</groupId>
@@ -129,6 +144,12 @@
                 <version>${feign-okhttp.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>com.squareup.okhttp3</groupId>
+                <artifactId>okhttp</artifactId>
+                <version>${okhttp.version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>de.codecentric</groupId>
                 <artifactId>spring-boot-admin-starter-server</artifactId>
@@ -248,7 +269,7 @@
         <repository>
             <id>public</id>
             <name>aliyun nexus</name>
-            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
+            <url>https://maven.aliyun.com/repository/public/</url>
             <releases>
                 <enabled>true</enabled>
             </releases>
@@ -259,7 +280,7 @@
         <pluginRepository>
             <id>public</id>
             <name>aliyun nexus</name>
-            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
+            <url>https://maven.aliyun.com/repository/public/</url>
             <releases>
                 <enabled>true</enabled>
             </releases>
@@ -276,6 +297,7 @@
                 <!-- 环境标识,需要与配置文件的名称相对应 -->
                 <profiles.active>local</profiles.active>
                 <logging.level>debug</logging.level>
+                <endpoints.include>'*'</endpoints.include>
             </properties>
         </profile>
         <profile>
@@ -284,6 +306,7 @@
                 <!-- 环境标识,需要与配置文件的名称相对应 -->
                 <profiles.active>dev</profiles.active>
                 <logging.level>debug</logging.level>
+                <endpoints.include>'*'</endpoints.include>
             </properties>
             <activation>
                 <!-- 默认环境 -->
@@ -295,6 +318,7 @@
             <properties>
                 <profiles.active>prod</profiles.active>
                 <logging.level>warn</logging.level>
+                <endpoints.include>health,info</endpoints.include>
             </properties>
         </profile>
 

+ 1 - 1
ruoyi-admin/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>2.6.0</version>
+        <version>3.0.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <packaging>jar</packaging>

+ 3 - 3
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java

@@ -8,7 +8,7 @@ import cn.hutool.captcha.generator.CodeGenerator;
 import cn.hutool.captcha.generator.RandomGenerator;
 import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.StrUtil;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.redis.RedisCache;
@@ -107,9 +107,9 @@ public class CaptchaController {
 
 	private String getCodeResult(String capStr) {
 		int numberLength = captchaProperties.getNumberLength();
-		int a = Convert.toInt(StrUtil.sub(capStr, 0, numberLength).trim());
+		int a = Convert.toInt(StringUtils.substring(capStr, 0, numberLength).trim());
 		char operator = capStr.charAt(numberLength);
-		int b = Convert.toInt(StrUtil.sub(capStr, numberLength + 1, numberLength + 1 + numberLength).trim());
+		int b = Convert.toInt(StringUtils.substring(capStr, numberLength + 1, numberLength + 1 + numberLength).trim());
 		switch (operator) {
 			case '*':
 				return a * b + "";

+ 0 - 88
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java

@@ -1,88 +0,0 @@
-package com.ruoyi.web.controller.common;
-
-import cn.hutool.core.util.StrUtil;
-import com.ruoyi.common.config.RuoYiConfig;
-import com.ruoyi.common.constant.Constants;
-import com.ruoyi.common.utils.file.FileUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.File;
-
-/**
- * 通用请求处理
- *
- * @author ruoyi
- */
-@RestController
-public class CommonController
-{
-    private static final Logger log = LoggerFactory.getLogger(CommonController.class);
-
-    /**
-     * 通用下载请求
-     *
-     * @param fileName 文件名称
-     * @param delete 是否删除
-     */
-    @GetMapping("common/download")
-    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
-    {
-        try
-        {
-            if (!FileUtils.checkAllowDownload(fileName))
-            {
-                throw new Exception(StrUtil.format("文件名称({})非法,不允许下载。 ", fileName));
-            }
-            String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
-            String filePath = RuoYiConfig.getDownloadPath() + fileName;
-			File file = new File(filePath);
-            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
-            FileUtils.setAttachmentResponseHeader(response, realFileName);
-			FileUtils.writeToStream(file, response.getOutputStream());
-            if (delete)
-            {
-				FileUtils.del(file);
-            }
-        }
-        catch (Exception e)
-        {
-            log.error("下载文件失败", e);
-        }
-    }
-
-    /**
-     * 本地资源通用下载
-     */
-    @GetMapping("/common/download/resource")
-    public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
-            throws Exception
-    {
-        try
-        {
-            if (!FileUtils.checkAllowDownload(resource))
-            {
-                throw new Exception(StrUtil.format("资源文件({})非法,不允许下载。 ", resource));
-            }
-            // 本地资源路径
-            String localPath = RuoYiConfig.getProfile();
-            // 数据库资源地址
-            String downloadPath = localPath + StrUtil.subAfter(resource, Constants.RESOURCE_PREFIX,false);
-            // 下载名称
-            String downloadName = StrUtil.subAfter(downloadPath, "/",true);
-            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
-			File file = new File(downloadPath);
-            FileUtils.setAttachmentResponseHeader(response, downloadName);
-            FileUtils.writeToStream(file, response.getOutputStream());
-        }
-        catch (Exception e)
-        {
-            log.error("下载文件失败", e);
-        }
-    }
-}

+ 50 - 50
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java

@@ -1,50 +1,50 @@
-package com.ruoyi.web.controller.monitor;
-
-import cn.hutool.core.util.StrUtil;
-import com.ruoyi.common.core.domain.AjaxResult;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.redis.core.RedisCallback;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.*;
-
-/**
- * 缓存监控
- * 
- * @author ruoyi
- */
-@RestController
-@RequestMapping("/monitor/cache")
-public class CacheController
-{
-    @Autowired
-    private RedisTemplate<String, String> redisTemplate;
-
-    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
-    @GetMapping()
-    public AjaxResult getInfo() throws Exception
-    {
-        Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info());
-        Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));
-        Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize());
-
-        Map<String, Object> result = new HashMap<>(3);
-        result.put("info", info);
-        result.put("dbSize", dbSize);
-
-        List<Map<String, String>> pieList = new ArrayList<>();
-        commandStats.stringPropertyNames().forEach(key -> {
-            Map<String, String> data = new HashMap<>(2);
-            String property = commandStats.getProperty(key);
-            data.put("name", StrUtil.removePrefix(key, "cmdstat_"));
-            data.put("value", StrUtil.subBetween(property, "calls=", ",usec"));
-            pieList.add(data);
-        });
-        result.put("commandStats", pieList);
-        return AjaxResult.success(result);
-    }
-}
+package com.ruoyi.web.controller.monitor;
+
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.core.domain.AjaxResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.*;
+
+/**
+ * 缓存监控
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/monitor/cache")
+public class CacheController
+{
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
+    @GetMapping()
+    public AjaxResult getInfo() throws Exception
+    {
+        Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info());
+        Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));
+        Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize());
+
+        Map<String, Object> result = new HashMap<>(3);
+        result.put("info", info);
+        result.put("dbSize", dbSize);
+
+        List<Map<String, String>> pieList = new ArrayList<>();
+        commandStats.stringPropertyNames().forEach(key -> {
+            Map<String, String> data = new HashMap<>(2);
+            String property = commandStats.getProperty(key);
+            data.put("name", StringUtils.removeStart(key, "cmdstat_"));
+            data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
+            pieList.add(data);
+        });
+        result.put("commandStats", pieList);
+        return AjaxResult.success(result);
+    }
+}

+ 4 - 4
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java

@@ -12,11 +12,12 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 
 /**
  * 系统访问记录
- * 
+ *
  * @author ruoyi
  */
 @RestController
@@ -36,11 +37,10 @@ public class SysLogininforController extends BaseController
     @Log(title = "登录日志", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
     @GetMapping("/export")
-    public AjaxResult export(SysLogininfor logininfor)
+    public void export(SysLogininfor logininfor, HttpServletResponse response)
     {
         List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
-        ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
-        return util.exportExcel(list, "登录日志");
+		ExcelUtil.exportExcel(list, "登录日志", SysLogininfor.class, response);
     }
 
     @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")

+ 4 - 4
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java

@@ -12,11 +12,12 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 
 /**
  * 操作日志记录
- * 
+ *
  * @author ruoyi
  */
 @RestController
@@ -36,11 +37,10 @@ public class SysOperlogController extends BaseController
     @Log(title = "操作日志", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('monitor:operlog:export')")
     @GetMapping("/export")
-    public AjaxResult export(SysOperLog operLog)
+    public void export(SysOperLog operLog, HttpServletResponse response)
     {
         List<SysOperLog> list = operLogService.selectOperLogList(operLog);
-        ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
-        return util.exportExcel(list, "操作日志");
+		ExcelUtil.exportExcel(list, "操作日志", SysOperLog.class, response);
     }
 
     @Log(title = "操作日志", businessType = BusinessType.DELETE)

+ 8 - 9
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java

@@ -1,7 +1,5 @@
 package com.ruoyi.web.controller.monitor;
 
-import cn.hutool.core.lang.Validator;
-import cn.hutool.core.util.StrUtil;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.core.controller.BaseController;
@@ -11,6 +9,7 @@ import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.core.redis.RedisCache;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.PageUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.domain.SysUserOnline;
 import com.ruoyi.system.service.ISysUserOnlineService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -24,7 +23,7 @@ import java.util.List;
 
 /**
  * 在线用户监控
- * 
+ *
  * @author ruoyi
  */
 @RestController
@@ -46,23 +45,23 @@ public class SysUserOnlineController extends BaseController
         for (String key : keys)
         {
             LoginUser user = redisCache.getCacheObject(key);
-            if (Validator.isNotEmpty(ipaddr) && Validator.isNotEmpty(userName))
+            if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
             {
-                if (StrUtil.equals(ipaddr, user.getIpaddr()) && StrUtil.equals(userName, user.getUsername()))
+                if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
                 {
                     userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
                 }
             }
-            else if (Validator.isNotEmpty(ipaddr))
+            else if (StringUtils.isNotEmpty(ipaddr))
             {
-                if (StrUtil.equals(ipaddr, user.getIpaddr()))
+                if (StringUtils.equals(ipaddr, user.getIpaddr()))
                 {
                     userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
                 }
             }
-            else if (Validator.isNotEmpty(userName) && Validator.isNotNull(user.getUser()))
+            else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
             {
-                if (StrUtil.equals(userName, user.getUsername()))
+                if (StringUtils.equals(userName, user.getUsername()))
                 {
                     userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
                 }

+ 5 - 6
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java

@@ -7,7 +7,6 @@ import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.system.domain.SysConfig;
 import com.ruoyi.system.service.ISysConfigService;
@@ -16,6 +15,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 
 /**
@@ -43,11 +43,10 @@ public class SysConfigController extends BaseController
     @Log(title = "参数管理", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('system:config:export')")
     @GetMapping("/export")
-    public AjaxResult export(SysConfig config)
+    public void export(SysConfig config, HttpServletResponse response)
     {
         List<SysConfig> list = configService.selectConfigList(config);
-        ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
-        return util.exportExcel(list, "参数数据");
+		ExcelUtil.exportExcel(list, "参数数据", SysConfig.class, response);
     }
 
     /**
@@ -82,7 +81,7 @@ public class SysConfigController extends BaseController
         {
             return AjaxResult.error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
         }
-        config.setCreateBy(SecurityUtils.getUsername());
+        config.setCreateBy(getUsername());
         return toAjax(configService.insertConfig(config));
     }
 
@@ -98,7 +97,7 @@ public class SysConfigController extends BaseController
         {
             return AjaxResult.error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
         }
-        config.setUpdateBy(SecurityUtils.getUsername());
+        config.setUpdateBy(getUsername());
         return toAjax(configService.updateConfig(config));
     }
 

+ 6 - 7
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java

@@ -1,15 +1,14 @@
 package com.ruoyi.web.controller.system;
 
-import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.ArrayUtil;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.constant.UserConstants;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysDept;
 import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.service.ISysDeptService;
-import org.apache.commons.lang3.ArrayUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
@@ -56,7 +55,7 @@ public class SysDeptController extends BaseController
         {
             SysDept d = (SysDept) it.next();
             if (d.getDeptId().intValue() == deptId
-                    || ArrayUtils.contains(StrUtil.splitToArray(d.getAncestors(), ","), deptId + ""))
+                    || ArrayUtil.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""))
             {
                 it.remove();
             }
@@ -109,7 +108,7 @@ public class SysDeptController extends BaseController
         {
             return AjaxResult.error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
         }
-        dept.setCreateBy(SecurityUtils.getUsername());
+        dept.setCreateBy(getUsername());
         return toAjax(deptService.insertDept(dept));
     }
 
@@ -129,12 +128,12 @@ public class SysDeptController extends BaseController
         {
             return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
         }
-        else if (StrUtil.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
+        else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
                 && deptService.selectNormalChildrenDeptById(dept.getDeptId()) > 0)
         {
             return AjaxResult.error("该部门包含未停用的子部门!");
         }
-        dept.setUpdateBy(SecurityUtils.getUsername());
+        dept.setUpdateBy(getUsername());
         return toAjax(deptService.updateDept(dept));
     }
 

+ 7 - 8
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java

@@ -1,13 +1,12 @@
 package com.ruoyi.web.controller.system;
 
-import cn.hutool.core.lang.Validator;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysDictData;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.system.service.ISysDictDataService;
 import com.ruoyi.system.service.ISysDictTypeService;
@@ -16,6 +15,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -44,11 +44,10 @@ public class SysDictDataController extends BaseController
     @Log(title = "字典数据", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('system:dict:export')")
     @GetMapping("/export")
-    public AjaxResult export(SysDictData dictData)
+    public void export(SysDictData dictData, HttpServletResponse response)
     {
         List<SysDictData> list = dictDataService.selectDictDataList(dictData);
-        ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
-        return util.exportExcel(list, "字典数据");
+		ExcelUtil.exportExcel(list, "字典数据", SysDictData.class, response);
     }
 
     /**
@@ -68,7 +67,7 @@ public class SysDictDataController extends BaseController
     public AjaxResult dictType(@PathVariable String dictType)
     {
         List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
-        if (Validator.isNull(data))
+        if (StringUtils.isNull(data))
         {
             data = new ArrayList<SysDictData>();
         }
@@ -83,7 +82,7 @@ public class SysDictDataController extends BaseController
     @PostMapping
     public AjaxResult add(@Validated @RequestBody SysDictData dict)
     {
-        dict.setCreateBy(SecurityUtils.getUsername());
+        dict.setCreateBy(getUsername());
         return toAjax(dictDataService.insertDictData(dict));
     }
 
@@ -95,7 +94,7 @@ public class SysDictDataController extends BaseController
     @PutMapping
     public AjaxResult edit(@Validated @RequestBody SysDictData dict)
     {
-        dict.setUpdateBy(SecurityUtils.getUsername());
+        dict.setUpdateBy(getUsername());
         return toAjax(dictDataService.updateDictData(dict));
     }
 

+ 5 - 6
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java

@@ -7,7 +7,6 @@ import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysDictType;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.system.service.ISysDictTypeService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -15,6 +14,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 
 /**
@@ -39,11 +39,10 @@ public class SysDictTypeController extends BaseController
     @Log(title = "字典类型", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('system:dict:export')")
     @GetMapping("/export")
-    public AjaxResult export(SysDictType dictType)
+    public void export(SysDictType dictType, HttpServletResponse response)
     {
         List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
-        ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
-        return util.exportExcel(list, "字典类型");
+		ExcelUtil.exportExcel(list, "字典类型", SysDictType.class, response);
     }
 
     /**
@@ -68,7 +67,7 @@ public class SysDictTypeController extends BaseController
         {
             return AjaxResult.error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
         }
-        dict.setCreateBy(SecurityUtils.getUsername());
+        dict.setCreateBy(getUsername());
         return toAjax(dictTypeService.insertDictType(dict));
     }
 
@@ -84,7 +83,7 @@ public class SysDictTypeController extends BaseController
         {
             return AjaxResult.error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
         }
-        dict.setUpdateBy(SecurityUtils.getUsername());
+        dict.setUpdateBy(getUsername());
         return toAjax(dictTypeService.updateDictType(dict));
     }
 

+ 2 - 2
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java

@@ -1,6 +1,6 @@
 package com.ruoyi.web.controller.system;
 
-import cn.hutool.core.util.StrUtil;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.config.RuoYiConfig;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -24,6 +24,6 @@ public class SysIndexController
     @RequestMapping("/")
     public String index()
     {
-        return StrUtil.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
+        return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
     }
 }

+ 3 - 4
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java

@@ -6,6 +6,7 @@ import com.ruoyi.common.core.domain.entity.SysMenu;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.domain.model.LoginBody;
 import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.ServletUtils;
 import com.ruoyi.framework.web.service.SysLoginService;
 import com.ruoyi.framework.web.service.SysPermissionService;
@@ -88,10 +89,8 @@ public class SysLoginController
     @GetMapping("getRouters")
     public AjaxResult getRouters()
     {
-        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        // 用户信息
-        SysUser user = loginUser.getUser();
-        List<SysMenu> menus = menuService.selectMenuTreeByUserId(user.getUserId());
+        Long userId = SecurityUtils.getUserId();
+        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
         return AjaxResult.success(menuService.buildMenus(menus));
     }
 }

+ 8 - 20
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java

@@ -1,16 +1,12 @@
 package com.ruoyi.web.controller.system;
 
-import cn.hutool.core.lang.Validator;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.constant.UserConstants;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysMenu;
-import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.SecurityUtils;
-import com.ruoyi.common.utils.ServletUtils;
-import com.ruoyi.framework.web.service.TokenService;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.service.ISysMenuService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -33,9 +29,6 @@ public class SysMenuController extends BaseController
     @Autowired
     private ISysMenuService menuService;
 
-    @Autowired
-    private TokenService tokenService;
-
     /**
      * 获取菜单列表
      */
@@ -43,9 +36,7 @@ public class SysMenuController extends BaseController
     @GetMapping("/list")
     public AjaxResult list(SysMenu menu)
     {
-        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        Long userId = loginUser.getUser().getUserId();
-        List<SysMenu> menus = menuService.selectMenuList(menu, userId);
+        List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
         return AjaxResult.success(menus);
     }
 
@@ -65,9 +56,7 @@ public class SysMenuController extends BaseController
     @GetMapping("/treeselect")
     public AjaxResult treeselect(SysMenu menu)
     {
-        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        Long userId = loginUser.getUser().getUserId();
-        List<SysMenu> menus = menuService.selectMenuList(menu, userId);
+        List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
         return AjaxResult.success(menuService.buildMenuTreeSelect(menus));
     }
 
@@ -77,8 +66,7 @@ public class SysMenuController extends BaseController
     @GetMapping(value = "/roleMenuTreeselect/{roleId}")
     public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId)
     {
-        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        List<SysMenu> menus = menuService.selectMenuList(loginUser.getUser().getUserId());
+		List<SysMenu> menus = menuService.selectMenuList(getUserId());
 		Map<String,Object> ajax = new HashMap<>();
         ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
         ajax.put("menus", menuService.buildMenuTreeSelect(menus));
@@ -97,11 +85,11 @@ public class SysMenuController extends BaseController
         {
             return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
         }
-        else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !Validator.isUrl(menu.getPath()))
+        else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
         {
             return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
         }
-        menu.setCreateBy(SecurityUtils.getUsername());
+        menu.setCreateBy(getUsername());
         return toAjax(menuService.insertMenu(menu));
     }
 
@@ -117,7 +105,7 @@ public class SysMenuController extends BaseController
         {
             return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
         }
-        else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !Validator.isUrl(menu.getPath()))
+        else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
         {
             return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
         }
@@ -125,7 +113,7 @@ public class SysMenuController extends BaseController
         {
             return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
         }
-        menu.setUpdateBy(SecurityUtils.getUsername());
+        menu.setUpdateBy(getUsername());
         return toAjax(menuService.updateMenu(menu));
     }
 

+ 3 - 4
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java

@@ -17,13 +17,12 @@ import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.system.domain.SysNotice;
 import com.ruoyi.system.service.ISysNoticeService;
 
 /**
  * 公告 信息操作处理
- * 
+ *
  * @author ruoyi
  */
 @RestController
@@ -61,7 +60,7 @@ public class SysNoticeController extends BaseController
     @PostMapping
     public AjaxResult add(@Validated @RequestBody SysNotice notice)
     {
-        notice.setCreateBy(SecurityUtils.getUsername());
+        notice.setCreateBy(getUsername());
         return toAjax(noticeService.insertNotice(notice));
     }
 
@@ -73,7 +72,7 @@ public class SysNoticeController extends BaseController
     @PutMapping
     public AjaxResult edit(@Validated @RequestBody SysNotice notice)
     {
-        notice.setUpdateBy(SecurityUtils.getUsername());
+        notice.setUpdateBy(getUsername());
         return toAjax(noticeService.updateNotice(notice));
     }
 

+ 108 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java

@@ -0,0 +1,108 @@
+package com.ruoyi.web.controller.system;
+
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.annotation.RepeatSubmit;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.core.validate.AddGroup;
+import com.ruoyi.common.core.validate.EditGroup;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.system.domain.bo.SysOssConfigBo;
+import com.ruoyi.system.domain.vo.SysOssConfigVo;
+import com.ruoyi.system.service.ISysOssConfigService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Arrays;
+
+/**
+ * 云存储配置Controller
+ *
+ * @author Lion Li
+ * @author 孤舟烟雨
+ * @date 2021-08-13
+ */
+@Validated
+@Api(value = "云存储配置控制器", tags = {"云存储配置管理"})
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+@RestController
+@RequestMapping("/system/oss/config")
+public class SysOssConfigController extends BaseController {
+
+	private final ISysOssConfigService iSysOssConfigService;
+
+	/**
+	 * 查询云存储配置列表
+	 */
+	@ApiOperation("查询云存储配置列表")
+	@PreAuthorize("@ss.hasPermi('system:oss:list')")
+	@GetMapping("/list")
+	public TableDataInfo<SysOssConfigVo> list(@Validated SysOssConfigBo bo) {
+		return iSysOssConfigService.queryPageList(bo);
+	}
+
+	/**
+	 * 获取云存储配置详细信息
+	 */
+	@ApiOperation("获取云存储配置详细信息")
+	@PreAuthorize("@ss.hasPermi('system:oss:query')")
+	@GetMapping("/{ossConfigId}")
+	public AjaxResult<SysOssConfigVo> getInfo(@NotNull(message = "主键不能为空")
+											  @PathVariable("ossConfigId") Integer ossConfigId) {
+		return AjaxResult.success(iSysOssConfigService.queryById(ossConfigId));
+	}
+
+	/**
+	 * 新增云存储配置
+	 */
+	@ApiOperation("新增云存储配置")
+	@PreAuthorize("@ss.hasPermi('system:oss:add')")
+	@Log(title = "云存储配置", businessType = BusinessType.INSERT)
+	@RepeatSubmit()
+	@PostMapping()
+	public AjaxResult<Void> add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) {
+		return toAjax(iSysOssConfigService.insertByBo(bo) ? 1 : 0);
+	}
+
+	/**
+	 * 修改云存储配置
+	 */
+	@ApiOperation("修改云存储配置")
+	@PreAuthorize("@ss.hasPermi('system:oss:edit')")
+	@Log(title = "云存储配置", businessType = BusinessType.UPDATE)
+	@RepeatSubmit()
+	@PutMapping()
+	public AjaxResult<Void> edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) {
+		return toAjax(iSysOssConfigService.updateByBo(bo) ? 1 : 0);
+	}
+
+	/**
+	 * 删除云存储配置
+	 */
+	@ApiOperation("删除云存储配置")
+	@PreAuthorize("@ss.hasPermi('system:oss:remove')")
+	@Log(title = "云存储配置", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ossConfigIds}")
+	public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
+								   @PathVariable Long[] ossConfigIds) {
+		return toAjax(iSysOssConfigService.deleteWithValidByIds(Arrays.asList(ossConfigIds), true) ? 1 : 0);
+	}
+
+	/**
+	 * 状态修改
+	 */
+	@PreAuthorize("@ss.hasPermi('system:oss:edit')")
+	@Log(title = "云存储状态修改", businessType = BusinessType.UPDATE)
+	@PutMapping("/changeStatus")
+	public AjaxResult changeStatus(@RequestBody SysOssConfigBo bo) {
+		return toAjax(iSysOssConfigService.updateOssConfigStatus(bo));
+	}
+}

+ 30 - 8
ruoyi-oss/src/main/java/com/ruoyi/system/controller/SysOssController.java → ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java

@@ -1,20 +1,26 @@
-package com.ruoyi.system.controller;
+package com.ruoyi.web.controller.system;
 
 
 import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.HttpUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.annotation.RepeatSubmit;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.exception.CustomException;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.JsonUtils;
 import com.ruoyi.common.utils.file.FileUtils;
-import com.ruoyi.system.domain.bo.SysOssBo;
+import com.ruoyi.oss.constant.CloudConstant;
+import com.ruoyi.system.domain.SysConfig;
 import com.ruoyi.system.domain.SysOss;
-import com.ruoyi.system.service.ISysOssService;
+import com.ruoyi.system.domain.bo.SysOssBo;
 import com.ruoyi.system.domain.vo.SysOssVo;
+import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.service.ISysOssService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
@@ -49,6 +55,7 @@ import java.util.Map;
 public class SysOssController extends BaseController {
 
 	private final ISysOssService iSysOssService;
+	private final ISysConfigService iSysConfigService;
 
 	/**
 	 * 查询OSS云存储列表
@@ -72,8 +79,8 @@ public class SysOssController extends BaseController {
 	@RepeatSubmit
 	@PostMapping("/upload")
 	public AjaxResult<Map<String, String>> upload(@RequestPart("file") MultipartFile file) {
-		if (file.isEmpty()) {
-			throw new CustomException("上传文件不能为空");
+		if (ObjectUtil.isNull(file)) {
+			throw new ServiceException("上传文件不能为空");
 		}
 		SysOss oss = iSysOssService.upload(file);
 		Map<String, String> map = new HashMap<>(2);
@@ -87,8 +94,8 @@ public class SysOssController extends BaseController {
 	@GetMapping("/download/{ossId}")
 	public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
 		SysOss sysOss = iSysOssService.getById(ossId);
-		if (sysOss == null) {
-			throw new CustomException("文件数据不存在!");
+		if (ObjectUtil.isNull(sysOss)) {
+			throw new ServiceException("文件数据不存在!");
 		}
 		response.reset();
 		response.addHeader("Access-Control-Allow-Origin", "*");
@@ -111,4 +118,19 @@ public class SysOssController extends BaseController {
 		return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true) ? 1 : 0);
 	}
 
+	/**
+	 * 变更图片列表预览状态
+	 */
+	@ApiOperation("变更图片列表预览状态")
+	@PreAuthorize("@ss.hasPermi('system:oss:edit')")
+	@Log(title = "OSS云存储" , businessType = BusinessType.UPDATE)
+	@PutMapping("/changePreviewListResource")
+	public AjaxResult<Void> changePreviewListResource(@RequestBody String body) {
+		Map<String, Boolean> map = JsonUtils.parseMap(body);
+		SysConfig config = iSysConfigService.getOne(new LambdaQueryWrapper<SysConfig>()
+			.eq(SysConfig::getConfigKey, CloudConstant.PEREVIEW_LIST_RESOURCE_KEY));
+		config.setConfigValue(map.get("previewListResource").toString());
+		return toAjax(iSysConfigService.updateConfig(config));
+	}
+
 }

+ 13 - 20
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java

@@ -1,31 +1,25 @@
 package com.ruoyi.web.controller.system;
 
-import java.util.List;
-import org.springframework.beans.factory.annotation.Autowired;
-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 com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.constant.UserConstants;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.system.domain.SysPost;
 import com.ruoyi.system.service.ISysPostService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
 
 /**
  * 岗位信息操作处理
- * 
+ *
  * @author ruoyi
  */
 @RestController
@@ -44,15 +38,14 @@ public class SysPostController extends BaseController
     {
         return postService.selectPagePostList(post);
     }
-    
+
     @Log(title = "岗位管理", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('system:post:export')")
     @GetMapping("/export")
-    public AjaxResult export(SysPost post)
+    public void export(SysPost post, HttpServletResponse response)
     {
         List<SysPost> list = postService.selectPostList(post);
-        ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
-        return util.exportExcel(list, "岗位数据");
+		ExcelUtil.exportExcel(list, "岗位数据", SysPost.class, response);
     }
 
     /**
@@ -81,7 +74,7 @@ public class SysPostController extends BaseController
         {
             return AjaxResult.error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
         }
-        post.setCreateBy(SecurityUtils.getUsername());
+        post.setCreateBy(getUsername());
         return toAjax(postService.insertPost(post));
     }
 
@@ -101,7 +94,7 @@ public class SysPostController extends BaseController
         {
             return AjaxResult.error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
         }
-        post.setUpdateBy(SecurityUtils.getUsername());
+        post.setUpdateBy(getUsername());
         return toAjax(postService.updatePost(post));
     }
 

+ 10 - 6
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java

@@ -1,8 +1,6 @@
 package com.ruoyi.web.controller.system;
 
-import cn.hutool.core.util.StrUtil;
 import com.ruoyi.common.annotation.Log;
-import com.ruoyi.common.config.RuoYiConfig;
 import com.ruoyi.common.constant.UserConstants;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
@@ -11,8 +9,10 @@ import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.ServletUtils;
-import com.ruoyi.common.utils.file.FileUploadUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.framework.web.service.TokenService;
+import com.ruoyi.system.domain.SysOss;
+import com.ruoyi.system.service.ISysOssService;
 import com.ruoyi.system.service.ISysUserService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -37,6 +37,9 @@ public class SysProfileController extends BaseController
     @Autowired
     private TokenService tokenService;
 
+    @Autowired
+	private ISysOssService iSysOssService;
+
     /**
      * 个人信息
      */
@@ -59,12 +62,12 @@ public class SysProfileController extends BaseController
     @PutMapping
     public AjaxResult updateProfile(@RequestBody SysUser user)
     {
-        if (StrUtil.isNotEmpty(user.getPhonenumber())
+        if (StringUtils.isNotEmpty(user.getPhonenumber())
                 && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
         {
             return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
         }
-        if (StrUtil.isNotEmpty(user.getEmail())
+        if (StringUtils.isNotEmpty(user.getEmail())
                 && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
         {
             return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
@@ -124,7 +127,8 @@ public class SysProfileController extends BaseController
         if (!file.isEmpty())
         {
             LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-            String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file);
+			SysOss oss = iSysOssService.upload(file);
+			String avatar = oss.getUrl();
             if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
             {
 				Map<String,Object> ajax = new HashMap<>();

+ 38 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java

@@ -0,0 +1,38 @@
+package com.ruoyi.web.controller.system;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.model.RegisterBody;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.web.service.SysRegisterService;
+import com.ruoyi.system.service.ISysConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 注册验证
+ *
+ * @author ruoyi
+ */
+@RestController
+public class SysRegisterController extends BaseController
+{
+    @Autowired
+    private SysRegisterService registerService;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @PostMapping("/register")
+    public AjaxResult register(@RequestBody RegisterBody user)
+    {
+        if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser"))))
+        {
+            return error("当前系统没有开启注册功能!");
+        }
+        String msg = registerService.register(user);
+        return StringUtils.isEmpty(msg) ? success() : error(msg);
+    }
+}

+ 8 - 9
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java

@@ -1,6 +1,5 @@
 package com.ruoyi.web.controller.system;
 
-import cn.hutool.core.lang.Validator;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.constant.UserConstants;
 import com.ruoyi.common.core.controller.BaseController;
@@ -10,8 +9,8 @@ import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.web.service.SysPermissionService;
 import com.ruoyi.framework.web.service.TokenService;
@@ -23,6 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 
 /**
@@ -56,11 +56,10 @@ public class SysRoleController extends BaseController
     @Log(title = "角色管理", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('system:role:export')")
     @GetMapping("/export")
-    public AjaxResult export(SysRole role)
+    public void export(SysRole role, HttpServletResponse response)
     {
         List<SysRole> list = roleService.selectRoleList(role);
-        ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
-        return util.exportExcel(list, "角色数据");
+		ExcelUtil.exportExcel(list, "角色数据", SysRole.class, response);
     }
 
     /**
@@ -89,7 +88,7 @@ public class SysRoleController extends BaseController
         {
             return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
         }
-        role.setCreateBy(SecurityUtils.getUsername());
+        role.setCreateBy(getUsername());
         return toAjax(roleService.insertRole(role));
 
     }
@@ -111,13 +110,13 @@ public class SysRoleController extends BaseController
         {
             return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
         }
-        role.setUpdateBy(SecurityUtils.getUsername());
+        role.setUpdateBy(getUsername());
 
         if (roleService.updateRole(role) > 0)
         {
             // 更新缓存用户权限
             LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-            if (Validator.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
+            if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
             {
                 loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
                 loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
@@ -149,7 +148,7 @@ public class SysRoleController extends BaseController
     public AjaxResult changeStatus(@RequestBody SysRole role)
     {
         roleService.checkRoleAllowed(role);
-        role.setUpdateBy(SecurityUtils.getUsername());
+        role.setUpdateBy(getUsername());
         return toAjax(roleService.updateRoleStatus(role));
     }
 

+ 38 - 19
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java

@@ -1,10 +1,13 @@
 package com.ruoyi.web.controller.system;
 
-import cn.hutool.core.lang.Validator;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.constant.UserConstants;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.entity.SysDept;
 import com.ruoyi.common.core.domain.entity.SysRole;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.domain.model.LoginUser;
@@ -12,8 +15,11 @@ import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.web.service.TokenService;
+import com.ruoyi.system.domain.vo.SysUserExportVo;
+import com.ruoyi.system.domain.vo.SysUserImportVo;
 import com.ruoyi.system.service.ISysPostService;
 import com.ruoyi.system.service.ISysRoleService;
 import com.ruoyi.system.service.ISysUserService;
@@ -23,6 +29,8 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -62,11 +70,19 @@ public class SysUserController extends BaseController
     @Log(title = "用户管理", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('system:user:export')")
     @GetMapping("/export")
-    public AjaxResult export(SysUser user)
+    public void export(SysUser user, HttpServletResponse response)
     {
         List<SysUser> list = userService.selectUserList(user);
-        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
-        return util.exportExcel(list, "用户数据");
+		List<SysUserExportVo> listVo = BeanUtil.copyToList(list, SysUserExportVo.class);
+		for (int i = 0; i < list.size(); i++) {
+			SysDept dept = list.get(i).getDept();
+			SysUserExportVo vo = listVo.get(i);
+			if (ObjectUtil.isNotEmpty(dept)) {
+				vo.setDeptName(dept.getDeptName());
+				vo.setLeader(dept.getLeader());
+			}
+		}
+		ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response);
     }
 
     @Log(title = "用户管理", businessType = BusinessType.IMPORT)
@@ -74,19 +90,18 @@ public class SysUserController extends BaseController
     @PostMapping("/importData")
     public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
     {
-        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
-        List<SysUser> userList = util.importExcel(file.getInputStream());
-        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+		List<SysUserImportVo> userListVo = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class);
+		List<SysUser> userList = BeanUtil.copyToList(userListVo, SysUser.class);
+		LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String operName = loginUser.getUsername();
         String message = userService.importUser(userList, updateSupport, operName);
         return AjaxResult.success(message);
     }
 
     @GetMapping("/importTemplate")
-    public AjaxResult importTemplate()
+    public void importTemplate(HttpServletResponse response)
     {
-        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
-        return util.importTemplateExcel("用户数据");
+		ExcelUtil.exportExcel(new ArrayList<>(), "用户数据", SysUserImportVo.class, response);
     }
 
     /**
@@ -100,7 +115,7 @@ public class SysUserController extends BaseController
         List<SysRole> roles = roleService.selectRoleAll();
         ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
         ajax.put("posts", postService.selectPostAll());
-        if (Validator.isNotNull(userId))
+        if (StringUtils.isNotNull(userId))
         {
             ajax.put("user", userService.selectUserById(userId));
             ajax.put("postIds", postService.selectPostListByUserId(userId));
@@ -121,17 +136,17 @@ public class SysUserController extends BaseController
         {
             return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
         }
-        else if (Validator.isNotEmpty(user.getPhonenumber())
+        else if (StringUtils.isNotEmpty(user.getPhonenumber())
                 && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
         {
             return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
         }
-        else if (Validator.isNotEmpty(user.getEmail())
+        else if (StringUtils.isNotEmpty(user.getEmail())
                 && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
         {
             return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
         }
-        user.setCreateBy(SecurityUtils.getUsername());
+        user.setCreateBy(getUsername());
         user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
         return toAjax(userService.insertUser(user));
     }
@@ -145,17 +160,17 @@ public class SysUserController extends BaseController
     public AjaxResult edit(@Validated @RequestBody SysUser user)
     {
         userService.checkUserAllowed(user);
-        if (Validator.isNotEmpty(user.getPhonenumber())
+        if (StringUtils.isNotEmpty(user.getPhonenumber())
                 && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
         {
             return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
         }
-        else if (Validator.isNotEmpty(user.getEmail())
+        else if (StringUtils.isNotEmpty(user.getEmail())
                 && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
         {
             return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
         }
-        user.setUpdateBy(SecurityUtils.getUsername());
+        user.setUpdateBy(getUsername());
         return toAjax(userService.updateUser(user));
     }
 
@@ -167,6 +182,10 @@ public class SysUserController extends BaseController
     @DeleteMapping("/{userIds}")
     public AjaxResult remove(@PathVariable Long[] userIds)
     {
+        if (ArrayUtil.contains(userIds, getUserId()))
+        {
+            return error("当前用户不能删除");
+        }
         return toAjax(userService.deleteUserByIds(userIds));
     }
 
@@ -180,7 +199,7 @@ public class SysUserController extends BaseController
     {
         userService.checkUserAllowed(user);
         user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
-        user.setUpdateBy(SecurityUtils.getUsername());
+        user.setUpdateBy(getUsername());
         return toAjax(userService.resetPwd(user));
     }
 
@@ -193,7 +212,7 @@ public class SysUserController extends BaseController
     public AjaxResult changeStatus(@RequestBody SysUser user)
     {
         userService.checkUserAllowed(user);
-        user.setUpdateBy(SecurityUtils.getUsername());
+        user.setUpdateBy(getUsername());
         return toAjax(userService.updateUserStatus(user));
     }
 

+ 5 - 54
ruoyi-admin/src/main/resources/application-dev.yml

@@ -4,7 +4,9 @@ spring:
     type: com.alibaba.druid.pool.DruidDataSource
     # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
     dynamic:
-      #设置默认的数据源或者数据源组,默认值即为 master
+      # 性能分析插件(有性能损耗 不建议生产环境使用)
+      p6spy: true
+      # 设置默认的数据源或者数据源组,默认值即为 master
       primary: master
       datasource:
         # 主库数据源
@@ -122,62 +124,11 @@ spring:
     admin:
       # Spring Boot Admin Client 客户端的相关配置
       client:
+        # 增加客户端开关
+        enabled: true
         # 设置 Spring Boot Admin Server 地址
         url: http://localhost:9090/admin
         instance:
           prefer-ip: true # 注册实例时,优先使用 IP
         username: ruoyi
         password: 123456
-
-# Actuator 监控端点的配置项
-management:
-  endpoints:
-    web:
-      # Actuator 提供的 API 接口的根目录。默认为 /actuator
-      base-path: /actuator
-      exposure:
-        # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
-        # 生产环境不建议放开所有 根据项目需求放开即可
-        include: '*'
-  endpoint:
-    logfile:
-      external-file: ./logs/sys-console.log
-
---- # OSS 云存储(界面 <参数设置> 可切换)
-cloud-storage:
-  # minio配置
-  minio:
-    endpoint: http://localhost:9000
-    accessKey: ruoyi
-    secretKey: ruoyi123
-    bucketName: ruoyi
-  # 七牛云配置
-  qiniu:
-    domain: http://XXX.XXXX.com
-    prefix:
-    accessKey: XXXXXXXXXXXXXXX
-    secretKey: XXXXXXXXXXXXXXX
-    bucketName: ruoyi
-    isHttps: false
-    # z0 华东  z1 华北  z2 华南  na0 北美  as0 东南亚
-    # 不填为自动获取(性能低 易出问题)
-    region: z0
-  # 阿里云配置
-  aliyun:
-    endpoint: http://oss-cn-beijing.aliyuncs.com
-    prefix:
-    accessKeyId: XXXXXXXXXXXXXXX
-    accessKeySecret: XXXXXXXXXXXXXXX
-    bucketName: ruoyi
-  # 腾讯云配置
-  qcloud:
-    endpoint: http://cos.ap-beijing.myqcloud.com
-    prefix:
-    secretId: XXXXXXXXXXXXXXX
-    secretKey: XXXXXXXXXXXXXXX
-    # 腾讯云bucket名规则 格式为 BucketName-APPID 此处填写的存储桶名称必须为此格式
-    bucketName: ruoyi-1250000000
-    isHttps: false
-    # 地域名参考官方文档
-    # https://cloud.tencent.com/document/product/436/6224
-    region: ap-beijing

+ 5 - 54
ruoyi-admin/src/main/resources/application-prod.yml

@@ -4,7 +4,9 @@ spring:
     type: com.alibaba.druid.pool.DruidDataSource
     # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
     dynamic:
-      #设置默认的数据源或者数据源组,默认值即为 master
+      # 性能分析插件(有性能损耗 不建议生产环境使用)
+      p6spy: false
+      # 设置默认的数据源或者数据源组,默认值即为 master
       primary: master
       datasource:
         # 主库数据源
@@ -122,62 +124,11 @@ spring:
     admin:
       # Spring Boot Admin Client 客户端的相关配置
       client:
+        # 增加客户端开关
+        enabled: true
         # 设置 Spring Boot Admin Server 地址
         url: http://172.30.0.90:9090/admin
         instance:
           prefer-ip: true # 注册实例时,优先使用 IP
         username: ruoyi
         password: 123456
-
-# Actuator 监控端点的配置项
-management:
-  endpoints:
-    web:
-      # Actuator 提供的 API 接口的根目录。默认为 /actuator
-      base-path: /actuator
-      exposure:
-        # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
-        # 生产环境不建议放开所有 根据项目需求放开即可
-        include: health,info
-  endpoint:
-    logfile:
-      external-file: ./logs/sys-console.log
-
---- # OSS 云存储(界面 <参数设置> 可切换)
-cloud-storage:
-  # minio配置
-  minio:
-    endpoint: http://172.30.0.54:9000
-    accessKey: ruoyi
-    secretKey: ruoyi123
-    bucketName: ruoyi
-  # 七牛云配置
-  qiniu:
-    domain: http://XXX.XXXX.com
-    prefix:
-    accessKey: XXXXXXXXXXXXXXX
-    secretKey: XXXXXXXXXXXXXXX
-    bucketName: ruoyi
-    isHttps: false
-    # z0 华东  z1 华北  z2 华南  na0 北美  as0 东南亚
-    # 不填为自动获取(性能低 易出问题)
-    region: z0
-  # 阿里云配置
-  aliyun:
-    endpoint: http://oss-cn-beijing.aliyuncs.com
-    prefix:
-    accessKeyId: XXXXXXXXXXXXXXX
-    accessKeySecret: XXXXXXXXXXXXXXX
-    bucketName: ruoyi
-  # 腾讯云配置
-  qcloud:
-    endpoint: http://cos.ap-beijing.myqcloud.com
-    prefix:
-    secretId: XXXXXXXXXXXXXXX
-    secretKey: XXXXXXXXXXXXXXX
-    # 腾讯云bucket名规则 格式为 BucketName-APPID 此处填写的存储桶名称必须为此格式
-    bucketName: ruoyi-1250000000
-    isHttps: false
-    # 地域名参考官方文档
-    # https://cloud.tencent.com/document/product/436/6224
-    region: ap-beijing

+ 24 - 5
ruoyi-admin/src/main/resources/application.yml

@@ -8,8 +8,6 @@ ruoyi:
   copyrightYear: 2021
   # 实例演示开关
   demoEnabled: true
-  # 文件路径
-  profile: ./ruoyi/uploadPath
   # 获取ip地址开关
   addressEnabled: true
 
@@ -107,6 +105,11 @@ token:
   # 令牌有效期(默认30分钟)
   expireTime: 30
 
+# 重复提交
+repeat-submit:
+  # 全局间隔时间(毫秒)
+  intervalTime: 1000
+
 # MyBatisPlus配置
 # https://baomidou.com/config/
 mybatis-plus:
@@ -159,8 +162,10 @@ mybatis-plus:
     localCacheScope: SESSION
     # 开启Mybatis二级缓存,默认为 true
     cacheEnabled: false
-    # 更详细的日志输出 会有性能损耗
-    # logImpl: org.apache.ibatis.logging.stdout.StdOutImpl
+    # 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl
+    # 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl
+    # 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl
+    logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl
   global-config:
     # 是否打印 Logo banner
     banner: true
@@ -223,7 +228,7 @@ xss:
   # 过滤开关
   enabled: true
   # 排除链接(多个用逗号分隔)
-  excludes: /system/notice/*
+  excludes: /system/notice
   # 匹配链接
   urlPatterns: /system/*,/monitor/*,/tool/*
 
@@ -284,6 +289,20 @@ lock4j:
   # 分布式锁的超时时间,默认为 30 毫秒
   expire: 30000
 
+--- # Actuator 监控端点的配置项
+management:
+  endpoints:
+    web:
+      # Actuator 提供的 API 接口的根目录。默认为 /actuator
+      base-path: /actuator
+      exposure:
+        # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+        # 生产环境不建议放开所有 根据项目需求放开即可
+        include: @endpoints.include@
+  endpoint:
+    logfile:
+      external-file: ./logs/sys-console.log
+
 --- # 定时任务配置
 spring:
   quartz:

+ 2 - 1
ruoyi-admin/src/main/resources/i18n/messages.properties

@@ -15,10 +15,11 @@ length.not.valid=长度必须在{min}到{max}个字符之间
 
 user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
 user.password.not.valid=* 5-50个字符
- 
+
 user.email.not.valid=邮箱格式错误
 user.mobile.phone.number.not.valid=手机号格式错误
 user.login.success=登录成功
+user.register.success=注册成功
 user.notfound=请重新登录
 user.forcelogout=管理员强制退出,请重新登录
 user.unknown.error=未知错误,请重新登录

+ 1 - 1
ruoyi-admin/src/main/resources/i18n/messages_en_US.properties

@@ -16,8 +16,8 @@ user.password.not.valid=
 user.email.not.valid=
 user.mobile.phone.number.not.valid=
 user.login.success=
+user.register.success=register success
 user.notfound=
-user.forcelogout=
 user.unknown.error=
 
 ##文件上传消息

+ 2 - 1
ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties

@@ -15,10 +15,11 @@ length.not.valid=长度必须在{min}到{max}个字符之间
 
 user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
 user.password.not.valid=* 5-50个字符
- 
+
 user.email.not.valid=邮箱格式错误
 user.mobile.phone.number.not.valid=手机号格式错误
 user.login.success=登录成功
+user.register.success=注册成功
 user.notfound=请重新登录
 user.forcelogout=管理员强制退出,请重新登录
 user.unknown.error=未知错误,请重新登录

+ 26 - 0
ruoyi-admin/src/main/resources/spy.properties

@@ -0,0 +1,26 @@
+# p6spy 性能分析插件配置文件
+modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
+# 自定义日志打印
+logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
+#日志输出到控制台
+appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
+# 使用日志系统记录 sql
+#appender=com.p6spy.engine.spy.appender.Slf4JLogger
+# 设置 p6spy driver 代理
+#deregisterdrivers=true
+# 取消JDBC URL前缀
+useprefix=true
+# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
+excludecategories=info,debug,result,commit,resultset
+# 日期格式
+dateformat=yyyy-MM-dd HH:mm:ss
+# 实际驱动可多个
+#driverlist=org.h2.Driver
+# 是否开启慢SQL记录
+outagedetection=true
+# 慢SQL记录标准 2 秒
+outagedetectioninterval=2
+# 是否过滤 Log
+filter=true
+# 过滤 Log 时所排除的表名列表,以逗号分隔
+exclude=QRTZ_

+ 11 - 1
ruoyi-common/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>2.6.0</version>
+        <version>3.0.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -59,6 +59,11 @@
             <artifactId>poi-ooxml</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+        </dependency>
+
         <!-- yml解析器 -->
         <dependency>
             <groupId>org.yaml</groupId>
@@ -147,6 +152,11 @@
             <groupId>com.baomidou</groupId>
             <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
         </dependency>
+        <!-- sql性能分析插件 -->
+        <dependency>
+            <groupId>p6spy</groupId>
+            <artifactId>p6spy</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>com.baomidou</groupId>

+ 0 - 165
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java

@@ -1,165 +0,0 @@
-package com.ruoyi.common.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.math.BigDecimal;
-
-/**
- * 自定义导出Excel数据注解
- * 
- * @author ruoyi
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.FIELD)
-public @interface Excel
-{
-    /**
-     * 导出时在excel中排序
-     */
-    public int sort() default Integer.MAX_VALUE;
-
-    /**
-     * 导出到Excel中的名字.
-     */
-    public String name() default "";
-
-    /**
-     * 日期格式, 如: yyyy-MM-dd
-     */
-    public String dateFormat() default "";
-
-    /**
-     * 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
-     */
-    public String dictType() default "";
-
-    /**
-     * 读取内容转表达式 (如: 0=男,1=女,2=未知)
-     */
-    public String readConverterExp() default "";
-
-    /**
-     * 分隔符,读取字符串组内容
-     */
-    public String separator() default ",";
-
-    /**
-     * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
-     */
-    public int scale() default -1;
-
-    /**
-     * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
-     */
-    public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
-
-    /**
-     * 导出类型(0数字 1字符串)
-     */
-    public ColumnType cellType() default ColumnType.STRING;
-
-    /**
-     * 导出时在excel中每个列的高度 单位为字符
-     */
-    public double height() default 14;
-
-    /**
-     * 导出时在excel中每个列的宽 单位为字符
-     */
-    public double width() default 16;
-
-    /**
-     * 文字后缀,如% 90 变成90%
-     */
-    public String suffix() default "";
-
-    /**
-     * 当值为空时,字段的默认值
-     */
-    public String defaultValue() default "";
-
-    /**
-     * 提示信息
-     */
-    public String prompt() default "";
-
-    /**
-     * 设置只能选择不能输入的列内容.
-     */
-    public String[] combo() default {};
-
-    /**
-     * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
-     */
-    public boolean isExport() default true;
-
-    /**
-     * 另一个类中的属性名称,支持多级获取,以小数点隔开
-     */
-    public String targetAttr() default "";
-
-    /**
-     * 是否自动统计数据,在最后追加一行统计数据总和
-     */
-    public boolean isStatistics() default false;
-
-    /**
-     * 导出字段对齐方式(0:默认;1:靠左;2:居中;3:靠右)
-     */
-    Align align() default Align.AUTO;
-
-    public enum Align
-    {
-        AUTO(0), LEFT(1), CENTER(2), RIGHT(3);
-        private final int value;
-
-        Align(int value)
-        {
-            this.value = value;
-        }
-
-        public int value()
-        {
-            return this.value;
-        }
-    }
-
-    /**
-     * 字段类型(0:导出导入;1:仅导出;2:仅导入)
-     */
-    Type type() default Type.ALL;
-
-    public enum Type
-    {
-        ALL(0), EXPORT(1), IMPORT(2);
-        private final int value;
-
-        Type(int value)
-        {
-            this.value = value;
-        }
-
-        public int value()
-        {
-            return this.value;
-        }
-    }
-
-    public enum ColumnType
-    {
-        NUMERIC(0), STRING(1), IMAGE(2);
-        private final int value;
-
-        ColumnType(int value)
-        {
-            this.value = value;
-        }
-
-        public int value()
-        {
-            return this.value;
-        }
-    }
-}

+ 30 - 0
ruoyi-common/src/main/java/com/ruoyi/common/annotation/ExcelDictFormat.java

@@ -0,0 +1,30 @@
+package com.ruoyi.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 字典格式化
+ *
+ * @author Lion Li
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ExcelDictFormat {
+
+	/**
+	 * 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
+	 */
+	String dictType() default "";
+
+	/**
+	 * 读取内容转表达式 (如: 0=男,1=女,2=未知)
+	 */
+	String readConverterExp() default "";
+
+	/**
+	 * 分隔符,读取字符串组内容
+	 */
+	String separator() default ",";
+
+}

+ 0 - 18
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java

@@ -1,18 +0,0 @@
-package com.ruoyi.common.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Excel注解集
- * 
- * @author ruoyi
- */
-@Target(ElementType.FIELD)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface Excels
-{
-    Excel[] value();
-}

+ 40 - 0
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java

@@ -0,0 +1,40 @@
+package com.ruoyi.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.enums.LimitType;
+
+/**
+ * 限流注解
+ * 
+ * @author ruoyi
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RateLimiter
+{
+    /**
+     * 限流key
+     */
+    public String key() default Constants.RATE_LIMIT_KEY;
+
+    /**
+     * 限流时间,单位秒
+     */
+    public int time() default 60;
+
+    /**
+     * 限流次数
+     */
+    public int count() default 100;
+
+    /**
+     * 限流类型
+     */
+    public LimitType limitType() default LimitType.DEFAULT;
+}

+ 29 - 23
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java

@@ -1,23 +1,29 @@
-package com.ruoyi.common.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * 自定义注解防止表单重复提交
- * 
- * @author ruoyi
- *
- */
-@Inherited
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface RepeatSubmit
-{
-
-}
+package com.ruoyi.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 自定义注解防止表单重复提交
+ *
+ * @author Lion Li
+ */
+@Inherited
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RepeatSubmit {
+
+	/**
+	 * 默认使用全局配置
+	 */
+	int intervalTime() default 0;
+
+	TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
+
+}

+ 1 - 33
ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java

@@ -9,7 +9,7 @@ import org.springframework.stereotype.Component;
 
 /**
  * 读取项目相关配置
- * 
+ *
  * @author ruoyi
  */
 
@@ -32,45 +32,13 @@ public class RuoYiConfig
     /** 实例演示开关 */
     private boolean demoEnabled;
 
-    /** 上传路径 */
-    @Getter
-    private static String profile;
-
     /** 获取地址开关 */
     @Getter
     private static boolean addressEnabled;
 
-    public void setProfile(String profile)
-    {
-        RuoYiConfig.profile = profile;
-    }
-
     public void setAddressEnabled(boolean addressEnabled)
     {
         RuoYiConfig.addressEnabled = addressEnabled;
     }
 
-    /**
-     * 获取头像上传路径
-     */
-    public static String getAvatarPath()
-    {
-        return getProfile() + "/avatar";
-    }
-
-    /**
-     * 获取下载路径
-     */
-    public static String getDownloadPath()
-    {
-        return getProfile() + "/download/";
-    }
-
-    /**
-     * 获取上传路径
-     */
-    public static String getUploadPath()
-    {
-        return getProfile() + "/upload";
-    }
 }

+ 13 - 10
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java

@@ -1,5 +1,7 @@
 package com.ruoyi.common.constant;
 
+import io.jsonwebtoken.Claims;
+
 /**
  * 通用常量信息
  *
@@ -47,6 +49,11 @@ public class Constants
      */
     public static final String LOGOUT = "Logout";
 
+    /**
+     * 注册
+     */
+    public static final String REGISTER = "Register";
+
     /**
      * 登录失败
      */
@@ -67,6 +74,11 @@ public class Constants
      */
     public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
 
+    /**
+     * 限流 redis key
+     */
+    public static final String RATE_LIMIT_KEY = "rate_limit:";
+
     /**
      * 验证码有效期(分钟)
      */
@@ -95,7 +107,7 @@ public class Constants
     /**
      * 用户名称
      */
-    public static final String JWT_USERNAME = "sub";
+    public static final String JWT_USERNAME = Claims.SUBJECT;
 
     /**
      * 用户头像
@@ -122,18 +134,9 @@ public class Constants
      */
     public static final String SYS_DICT_KEY = "sys_dict:";
 
-    /**
-     * 资源映射路径 前缀
-     */
-    public static final String RESOURCE_PREFIX = "/profile";
-
 	/**
 	 * RMI 远程方法调用
 	 */
 	public static final String LOOKUP_RMI = "rmi://";
 
-	/**
-	 * 资源映射路径 前缀
-	 */
-	public static final String REDIS_LOCK_KEY = "redis_lock:";
 }

+ 12 - 0
ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java

@@ -63,4 +63,16 @@ public class UserConstants
     /** 校验返回结果码 */
     public final static String UNIQUE = "0";
     public final static String NOT_UNIQUE = "1";
+
+    /**
+     * 用户名长度限制
+     */
+    public static final int USERNAME_MIN_LENGTH = 2;
+    public static final int USERNAME_MAX_LENGTH = 20;
+
+    /**
+     * 密码长度限制
+     */
+    public static final int PASSWORD_MIN_LENGTH = 5;
+    public static final int PASSWORD_MAX_LENGTH = 20;
 }

+ 69 - 0
ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelDictConvert.java

@@ -0,0 +1,69 @@
+package com.ruoyi.common.convert;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.convert.Convert;
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.CellData;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+import com.ruoyi.common.annotation.ExcelDictFormat;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import java.lang.reflect.Field;
+
+/**
+ * 字典格式化转换处理
+ *
+ * @author Lion Li
+ */
+@Slf4j
+public class ExcelDictConvert implements Converter<Object> {
+
+	@Override
+	public Class<Object> supportJavaTypeKey() {
+		return Object.class;
+	}
+
+	@Override
+	public CellDataTypeEnum supportExcelTypeKey() {
+		return null;
+	}
+
+	@Override
+	public Object convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
+		ExcelDictFormat anno = getAnnotation(contentProperty.getField());
+		String type = anno.dictType();
+		String label = cellData.getStringValue();
+		String value;
+		if (StringUtils.isBlank(type)) {
+			value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator());
+		} else {
+			value = ExcelUtil.reverseDictByExp(label, type, anno.separator());
+		}
+		return Convert.convert(contentProperty.getField().getType(), value);
+	}
+
+	@Override
+	public CellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
+		if (StringUtils.isNull(object)) {
+			return new CellData<>("");
+		}
+		ExcelDictFormat anno = getAnnotation(contentProperty.getField());
+		String type = anno.dictType();
+		String value = Convert.toStr(object);
+		String label;
+		if (StringUtils.isBlank(type)) {
+			label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator());
+		} else {
+			label = ExcelUtil.convertDictByExp(value, type, anno.separator());
+		}
+		return new CellData<>(label);
+	}
+
+	private ExcelDictFormat getAnnotation(Field field) {
+		return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class);
+	}
+}

+ 36 - 2
ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java

@@ -1,7 +1,9 @@
 package com.ruoyi.common.core.controller;
 
-import cn.hutool.core.util.StrUtil;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -73,6 +75,38 @@ public class BaseController
      */
     public String redirect(String url)
     {
-        return StrUtil.format("redirect:{}", url);
+        return StringUtils.format("redirect:{}", url);
+    }
+
+    /**
+     * 获取用户缓存信息
+     */
+    public LoginUser getLoginUser()
+    {
+        return SecurityUtils.getLoginUser();
+    }
+
+    /**
+     * 获取登录用户id
+     */
+    public Long getUserId()
+    {
+        return getLoginUser().getUserId();
+    }
+
+    /**
+     * 获取登录部门id
+     */
+    public Long getDeptId()
+    {
+        return getLoginUser().getDeptId();
+    }
+
+    /**
+     * 获取登录用户名
+     */
+    public String getUsername()
+    {
+        return getLoginUser().getUsername();
     }
 }

+ 39 - 17
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java

@@ -1,5 +1,6 @@
 package com.ruoyi.common.core.domain;
 
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
@@ -18,29 +19,50 @@ import java.util.Map;
 @Data
 @NoArgsConstructor
 @Accessors(chain = true)
-public class BaseEntity implements Serializable
-{
-    private static final long serialVersionUID = 1L;
+public class BaseEntity implements Serializable {
 
-    /** 搜索值 */
-    private String searchValue;
+	private static final long serialVersionUID = 1L;
 
-    /** 创建者 */
-    private String createBy;
+	/**
+	 * 搜索值
+	 */
+	@ApiModelProperty(value = "搜索值")
+	private String searchValue;
 
-    /** 创建时间 */
-    private Date createTime;
+	/**
+	 * 创建者
+	 */
+	@ApiModelProperty(value = "创建者")
+	private String createBy;
 
-    /** 更新者 */
-    private String updateBy;
+	/**
+	 * 创建时间
+	 */
+	@ApiModelProperty(value = "创建时间")
+	private Date createTime;
 
-    /** 更新时间 */
-    private Date updateTime;
+	/**
+	 * 更新者
+	 */
+	@ApiModelProperty(value = "更新者")
+	private String updateBy;
 
-    /** 备注 */
-    private String remark;
+	/**
+	 * 更新时间
+	 */
+	@ApiModelProperty(value = "更新时间")
+	private Date updateTime;
 
-    /** 请求参数 */
-    private Map<String, Object> params = new HashMap<>();
+	/**
+	 * 备注
+	 */
+	@ApiModelProperty(value = "备注")
+	private String remark;
+
+	/**
+	 * 请求参数
+	 */
+	@ApiModelProperty(value = "请求参数")
+	private Map<String, Object> params = new HashMap<>();
 
 }

+ 56 - 38
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java

@@ -1,38 +1,56 @@
-package com.ruoyi.common.core.domain;
-
-import lombok.*;
-import lombok.experimental.Accessors;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Tree基类
- * 
- * @author ruoyi
- */
-
-@EqualsAndHashCode(callSuper = true)
-@Data
-@NoArgsConstructor
-@Accessors(chain = true)
-public class TreeEntity extends BaseEntity
-{
-    private static final long serialVersionUID = 1L;
-
-    /** 父菜单名称 */
-    private String parentName;
-
-    /** 父菜单ID */
-    private Long parentId;
-
-    /** 显示顺序 */
-    private Integer orderNum;
-
-    /** 祖级列表 */
-    private String ancestors;
-
-    /** 子部门 */
-    private List<?> children = new ArrayList<>();
-
-}
+package com.ruoyi.common.core.domain;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tree基类
+ *
+ * @author ruoyi
+ */
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+public class TreeEntity extends BaseEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 父菜单名称
+	 */
+	@ApiModelProperty(value = "父菜单名称")
+	private String parentName;
+
+	/**
+	 * 父菜单ID
+	 */
+	@ApiModelProperty(value = "父菜单ID")
+	private Long parentId;
+
+	/**
+	 * 显示顺序
+	 */
+	@ApiModelProperty(value = "显示顺序")
+	private Integer orderNum;
+
+	/**
+	 * 祖级列表
+	 */
+	@ApiModelProperty(value = "祖级列表")
+	private String ancestors;
+
+	/**
+	 * 子部门
+	 */
+	@ApiModelProperty(value = "子部门")
+	private List<?> children = new ArrayList<>();
+
+}

+ 103 - 75
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java

@@ -1,7 +1,6 @@
 package com.ruoyi.common.core.domain.entity;
 
 import com.baomidou.mybatisplus.annotation.*;
-import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
@@ -14,7 +13,7 @@ import java.util.*;
 
 /**
  * 部门表 sys_dept
- * 
+ *
  * @author ruoyi
  */
 
@@ -22,78 +21,107 @@ import java.util.*;
 @NoArgsConstructor
 @Accessors(chain = true)
 @TableName("sys_dept")
-public class SysDept implements Serializable
-{
-    private static final long serialVersionUID = 1L;
-
-    /** 部门ID */
-    @TableId(value = "dept_id",type = IdType.AUTO)
-    private Long deptId;
-
-    /** 父部门ID */
-    private Long parentId;
-
-    /** 祖级列表 */
-    private String ancestors;
-
-    /** 部门名称 */
-    @NotBlank(message = "部门名称不能为空")
-    @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
-    private String deptName;
-
-    /** 显示顺序 */
-    @NotBlank(message = "显示顺序不能为空")
-    private String orderNum;
-
-    /** 负责人 */
-    private String leader;
-
-    /** 联系电话 */
-    @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
-    private String phone;
-
-    /** 邮箱 */
-    @Email(message = "邮箱格式不正确")
-    @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
-    private String email;
-
-    /** 部门状态:0正常,1停用 */
-    private String status;
-
-    /** 删除标志(0代表存在 2代表删除) */
-    @TableLogic
-    private String delFlag;
-
-    /** 父部门名称 */
-    @TableField(exist = false)
-    private String parentName;
-
-    /** 创建者 */
-    @TableField(fill = FieldFill.INSERT)
-    private String createBy;
-
-    /** 创建时间 */
-    @TableField(fill = FieldFill.INSERT)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date createTime;
-
-    /** 更新者 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private String updateBy;
-
-    /** 更新时间 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date updateTime;
-    
-    /** 子部门 */
-    @TableField(exist = false)
-    private List<SysDept> children = new ArrayList<SysDept>();
-
-    /**
-     * 请求参数
-     */
-    @TableField(exist = false)
-    private Map<String, Object> params = new HashMap<>();
+public class SysDept implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 部门ID
+	 */
+	@TableId(value = "dept_id", type = IdType.AUTO)
+	private Long deptId;
+
+	/**
+	 * 父部门ID
+	 */
+	private Long parentId;
+
+	/**
+	 * 祖级列表
+	 */
+	private String ancestors;
+
+	/**
+	 * 部门名称
+	 */
+	@NotBlank(message = "部门名称不能为空")
+	@Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
+	private String deptName;
+
+	/**
+	 * 显示顺序
+	 */
+	@NotBlank(message = "显示顺序不能为空")
+	private String orderNum;
+
+	/**
+	 * 负责人
+	 */
+	private String leader;
+
+	/**
+	 * 联系电话
+	 */
+	@Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
+	private String phone;
+
+	/**
+	 * 邮箱
+	 */
+	@Email(message = "邮箱格式不正确")
+	@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
+	private String email;
+
+	/**
+	 * 部门状态:0正常,1停用
+	 */
+	private String status;
+
+	/**
+	 * 删除标志(0代表存在 2代表删除)
+	 */
+	@TableLogic
+	private String delFlag;
+
+	/**
+	 * 父部门名称
+	 */
+	@TableField(exist = false)
+	private String parentName;
+
+	/**
+	 * 创建者
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private String createBy;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private Date createTime;
+
+	/**
+	 * 更新者
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private String updateBy;
+
+	/**
+	 * 更新时间
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private Date updateTime;
+
+	/**
+	 * 子部门
+	 */
+	@TableField(exist = false)
+	private List<SysDept> children = new ArrayList<SysDept>();
+
+	/**
+	 * 请求参数
+	 */
+	@TableField(exist = false)
+	private Map<String, Object> params = new HashMap<>();
 
 }

+ 109 - 81
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java

@@ -1,10 +1,11 @@
 package com.ruoyi.common.core.domain.entity;
 
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.baomidou.mybatisplus.annotation.*;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.ruoyi.common.annotation.Excel;
-import com.ruoyi.common.annotation.Excel.ColumnType;
+import com.ruoyi.common.annotation.ExcelDictFormat;
 import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.convert.ExcelDictConvert;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
@@ -18,7 +19,7 @@ import java.util.Map;
 
 /**
  * 字典数据表 sys_dict_data
- * 
+ *
  * @author ruoyi
  */
 
@@ -26,82 +27,109 @@ import java.util.Map;
 @NoArgsConstructor
 @Accessors(chain = true)
 @TableName("sys_dict_data")
-public class SysDictData implements Serializable
-{
-    private static final long serialVersionUID = 1L;
-
-    /** 字典编码 */
-    @Excel(name = "字典编码", cellType = ColumnType.NUMERIC)
-    @TableId(value = "dict_code",type = IdType.AUTO)
-    private Long dictCode;
-
-    /** 字典排序 */
-    @Excel(name = "字典排序", cellType = ColumnType.NUMERIC)
-    private Long dictSort;
-
-    /** 字典标签 */
-    @Excel(name = "字典标签")
-    @NotBlank(message = "字典标签不能为空")
-    @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符")
-    private String dictLabel;
-
-    /** 字典键值 */
-    @Excel(name = "字典键值")
-    @NotBlank(message = "字典键值不能为空")
-    @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符")
-    private String dictValue;
-
-    /** 字典类型 */
-    @Excel(name = "字典类型")
-    @NotBlank(message = "字典类型不能为空")
-    @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符")
-    private String dictType;
-
-    /** 样式属性(其他样式扩展) */
-    @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符")
-    private String cssClass;
-
-    /** 表格字典样式 */
-    private String listClass;
-
-    /** 是否默认(Y是 N否) */
-    @Excel(name = "是否默认", readConverterExp = "Y=是,N=否")
-    private String isDefault;
-
-    /** 状态(0正常 1停用) */
-    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
-    private String status;
-
-    /** 创建者 */
-    @TableField(fill = FieldFill.INSERT)
-    private String createBy;
-
-    /** 创建时间 */
-    @TableField(fill = FieldFill.INSERT)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date createTime;
-
-    /** 更新者 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private String updateBy;
-
-    /** 更新时间 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date updateTime;
-
-    /** 备注 */
-    private String remark;
-
-    /**
-     * 请求参数
-     */
-    @TableField(exist = false)
-    private Map<String, Object> params = new HashMap<>();
-
-    public boolean getDefault()
-    {
-        return UserConstants.YES.equals(this.isDefault) ? true : false;
-    }
+@ExcelIgnoreUnannotated
+public class SysDictData implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 字典编码
+	 */
+	@ExcelProperty(value = "字典编码")
+	@TableId(value = "dict_code", type = IdType.AUTO)
+	private Long dictCode;
+
+	/**
+	 * 字典排序
+	 */
+	@ExcelProperty(value = "字典排序")
+	private Long dictSort;
+
+	/**
+	 * 字典标签
+	 */
+	@ExcelProperty(value = "字典标签")
+	@NotBlank(message = "字典标签不能为空")
+	@Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符")
+	private String dictLabel;
+
+	/**
+	 * 字典键值
+	 */
+	@ExcelProperty(value = "字典键值")
+	@NotBlank(message = "字典键值不能为空")
+	@Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符")
+	private String dictValue;
+
+	/**
+	 * 字典类型
+	 */
+	@ExcelProperty(value = "字典类型")
+	@NotBlank(message = "字典类型不能为空")
+	@Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符")
+	private String dictType;
+
+	/**
+	 * 样式属性(其他样式扩展)
+	 */
+	@Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符")
+	private String cssClass;
+
+	/**
+	 * 表格字典样式
+	 */
+	private String listClass;
+
+	/**
+	 * 是否默认(Y是 N否)
+	 */
+	@ExcelProperty(value = "是否默认", converter = ExcelDictConvert.class)
+	@ExcelDictFormat(dictType = "sys_yes_no")
+	private String isDefault;
+
+	/**
+	 * 状态(0正常 1停用)
+	 */
+	@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+	@ExcelDictFormat(dictType = "sys_common_status")
+	private String status;
+
+	/**
+	 * 创建者
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private String createBy;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private Date createTime;
+
+	/**
+	 * 更新者
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private String updateBy;
+
+	/**
+	 * 更新时间
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private Date updateTime;
+
+	/**
+	 * 备注
+	 */
+	private String remark;
+
+	/**
+	 * 请求参数
+	 */
+	@TableField(exist = false)
+	private Map<String, Object> params = new HashMap<>();
+
+	public boolean getDefault() {
+		return UserConstants.YES.equals(this.isDefault) ? true : false;
+	}
 
 }

+ 63 - 45
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java

@@ -1,9 +1,10 @@
 package com.ruoyi.common.core.domain.entity;
 
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.baomidou.mybatisplus.annotation.*;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.ruoyi.common.annotation.Excel;
-import com.ruoyi.common.annotation.Excel.ColumnType;
+import com.ruoyi.common.annotation.ExcelDictFormat;
+import com.ruoyi.common.convert.ExcelDictConvert;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
@@ -17,7 +18,7 @@ import java.util.Map;
 
 /**
  * 字典类型表 sys_dict_type
- * 
+ *
  * @author ruoyi
  */
 
@@ -25,56 +26,73 @@ import java.util.Map;
 @NoArgsConstructor
 @Accessors(chain = true)
 @TableName("sys_dict_type")
-public class SysDictType implements Serializable
-{
-    private static final long serialVersionUID = 1L;
+@ExcelIgnoreUnannotated
+public class SysDictType implements Serializable {
+	private static final long serialVersionUID = 1L;
 
-    /** 字典主键 */
-    @Excel(name = "字典主键", cellType = ColumnType.NUMERIC)
-    @TableId(value = "dict_id",type = IdType.AUTO)
-    private Long dictId;
+	/**
+	 * 字典主键
+	 */
+	@ExcelProperty(value = "字典主键")
+	@TableId(value = "dict_id", type = IdType.AUTO)
+	private Long dictId;
 
-    /** 字典名称 */
-    @Excel(name = "字典名称")
-    @NotBlank(message = "字典名称不能为空")
-    @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符")
-    private String dictName;
+	/**
+	 * 字典名称
+	 */
+	@ExcelProperty(value = "字典名称")
+	@NotBlank(message = "字典名称不能为空")
+	@Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符")
+	private String dictName;
 
-    /** 字典类型 */
-    @Excel(name = "字典类型")
-    @NotBlank(message = "字典类型不能为空")
-    @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
-    private String dictType;
+	/**
+	 * 字典类型
+	 */
+	@ExcelProperty(value = "字典类型")
+	@NotBlank(message = "字典类型不能为空")
+	@Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
+	private String dictType;
 
-    /** 状态(0正常 1停用) */
-    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
-    private String status;
+	/**
+	 * 状态(0正常 1停用)
+	 */
+	@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+	@ExcelDictFormat(dictType = "sys_common_status")
+	private String status;
 
-    /** 创建者 */
-    @TableField(fill = FieldFill.INSERT)
-    private String createBy;
+	/**
+	 * 创建者
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private String createBy;
 
-    /** 创建时间 */
-    @TableField(fill = FieldFill.INSERT)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date createTime;
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private Date createTime;
 
-    /** 更新者 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private String updateBy;
+	/**
+	 * 更新者
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private String updateBy;
 
-    /** 更新时间 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date updateTime;
+	/**
+	 * 更新时间
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private Date updateTime;
 
-    /** 备注 */
-    private String remark;
+	/**
+	 * 备注
+	 */
+	private String remark;
 
-    /**
-     * 请求参数
-     */
-    @TableField(exist = false)
-    private Map<String, Object> params = new HashMap<>();
+	/**
+	 * 请求参数
+	 */
+	@TableField(exist = false)
+	private Map<String, Object> params = new HashMap<>();
 
 }

+ 125 - 84
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java

@@ -13,7 +13,7 @@ import java.util.*;
 
 /**
  * 菜单权限表 sys_menu
- * 
+ *
  * @author ruoyi
  */
 
@@ -21,88 +21,129 @@ import java.util.*;
 @NoArgsConstructor
 @Accessors(chain = true)
 @TableName("sys_menu")
-public class SysMenu implements Serializable
-{
-    private static final long serialVersionUID = 1L;
-
-    /** 菜单ID */
-    @TableId(value = "menu_id",type = IdType.AUTO)
-    private Long menuId;
-
-    /** 菜单名称 */
-    @NotBlank(message = "菜单名称不能为空")
-    @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
-    private String menuName;
-
-    /** 父菜单名称 */
-    @TableField(exist = false)
-    private String parentName;
-
-    /** 父菜单ID */
-    private Long parentId;
-
-    /** 显示顺序 */
-    @NotBlank(message = "显示顺序不能为空")
-    private String orderNum;
-
-    /** 路由地址 */
-    @Size(min = 0, max = 200, message = "路由地址不能超过200个字符")
-    private String path;
-
-    /** 组件路径 */
-    @Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
-    private String component;
-
-    /** 是否为外链(0是 1否) */
-    private String isFrame;
-
-    /** 是否缓存(0缓存 1不缓存) */
-    private String isCache;
-
-    /** 类型(M目录 C菜单 F按钮) */
-    @NotBlank(message = "菜单类型不能为空")
-    private String menuType;
-
-    /** 显示状态(0显示 1隐藏) */
-    private String visible;
-    
-    /** 菜单状态(0显示 1隐藏) */
-    private String status;
-
-    /** 权限字符串 */
-    @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
-    private String perms;
-
-    /** 菜单图标 */
-    private String icon;
-
-    /** 创建者 */
-    @TableField(fill = FieldFill.INSERT)
-    private String createBy;
-
-    /** 创建时间 */
-    @TableField(fill = FieldFill.INSERT)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date createTime;
-
-    /** 更新者 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private String updateBy;
-
-    /** 更新时间 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date updateTime;
-
-    /** 备注 */
-    private String remark;
-
-    /** 请求参数 */
-    @TableField(exist = false)
-    private Map<String, Object> params = new HashMap<>();
-
-    /** 子菜单 */
-    @TableField(exist = false)
-    private List<SysMenu> children = new ArrayList<SysMenu>();
+public class SysMenu implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 菜单ID
+	 */
+	@TableId(value = "menu_id", type = IdType.AUTO)
+	private Long menuId;
+
+	/**
+	 * 菜单名称
+	 */
+	@NotBlank(message = "菜单名称不能为空")
+	@Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
+	private String menuName;
+
+	/**
+	 * 父菜单名称
+	 */
+	@TableField(exist = false)
+	private String parentName;
+
+	/**
+	 * 父菜单ID
+	 */
+	private Long parentId;
+
+	/**
+	 * 显示顺序
+	 */
+	@NotBlank(message = "显示顺序不能为空")
+	private String orderNum;
+
+	/**
+	 * 路由地址
+	 */
+	@Size(min = 0, max = 200, message = "路由地址不能超过200个字符")
+	private String path;
+
+	/**
+	 * 组件路径
+	 */
+	@Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
+	private String component;
+
+	/**
+	 * 是否为外链(0是 1否)
+	 */
+	private String isFrame;
+
+	/**
+	 * 是否缓存(0缓存 1不缓存)
+	 */
+	private String isCache;
+
+	/**
+	 * 类型(M目录 C菜单 F按钮)
+	 */
+	@NotBlank(message = "菜单类型不能为空")
+	private String menuType;
+
+	/**
+	 * 显示状态(0显示 1隐藏)
+	 */
+	private String visible;
+
+	/**
+	 * 菜单状态(0显示 1隐藏)
+	 */
+	private String status;
+
+	/**
+	 * 权限字符串
+	 */
+	@Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
+	private String perms;
+
+	/**
+	 * 菜单图标
+	 */
+	private String icon;
+
+	/**
+	 * 创建者
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private String createBy;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	private Date createTime;
+
+	/**
+	 * 更新者
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private String updateBy;
+
+	/**
+	 * 更新时间
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	private Date updateTime;
+
+	/**
+	 * 备注
+	 */
+	private String remark;
+
+	/**
+	 * 请求参数
+	 */
+	@TableField(exist = false)
+	private Map<String, Object> params = new HashMap<>();
+
+	/**
+	 * 子菜单
+	 */
+	@TableField(exist = false)
+	private List<SysMenu> children = new ArrayList<SysMenu>();
 
 }

+ 132 - 100
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java

@@ -1,9 +1,10 @@
 package com.ruoyi.common.core.domain.entity;
 
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.baomidou.mybatisplus.annotation.*;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.ruoyi.common.annotation.Excel;
-import com.ruoyi.common.annotation.Excel.ColumnType;
+import com.ruoyi.common.annotation.ExcelDictFormat;
+import com.ruoyi.common.convert.ExcelDictConvert;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
@@ -25,102 +26,133 @@ import java.util.Map;
 @NoArgsConstructor
 @Accessors(chain = true)
 @TableName("sys_role")
-public class SysRole implements Serializable
-{
-    private static final long serialVersionUID = 1L;
-
-    /** 角色ID */
-    @Excel(name = "角色序号", cellType = ColumnType.NUMERIC)
-    @TableId(value = "role_id",type = IdType.AUTO)
-    private Long roleId;
-
-    /** 角色名称 */
-    @Excel(name = "角色名称")
-    @NotBlank(message = "角色名称不能为空")
-    @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符")
-    private String roleName;
-
-    /** 角色权限 */
-    @Excel(name = "角色权限")
-    @NotBlank(message = "权限字符不能为空")
-    @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符")
-    private String roleKey;
-
-    /** 角色排序 */
-    @Excel(name = "角色排序")
-    @NotBlank(message = "显示顺序不能为空")
-    private String roleSort;
-
-    /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */
-    @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
-    private String dataScope;
-
-    /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */
-    private boolean menuCheckStrictly;
-
-    /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */
-    private boolean deptCheckStrictly;
-
-    /** 角色状态(0正常 1停用) */
-    @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用")
-    private String status;
-
-    /** 删除标志(0代表存在 2代表删除) */
-    @TableLogic
-    private String delFlag;
-
-    /** 创建者 */
-    @TableField(fill = FieldFill.INSERT)
-    private String createBy;
-
-    /** 创建时间 */
-    @TableField(fill = FieldFill.INSERT)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date createTime;
-
-    /** 更新者 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private String updateBy;
-
-    /** 更新时间 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date updateTime;
-
-    /** 备注 */
-    private String remark;
-
-    /**
-     * 请求参数
-     */
-    @TableField(exist = false)
-    private Map<String, Object> params = new HashMap<>();
-
-    /** 用户是否存在此角色标识 默认不存在 */
-    @TableField(exist = false)
-    private boolean flag = false;
-
-    /** 菜单组 */
-    @TableField(exist = false)
-    private Long[] menuIds;
-
-    /** 部门组(数据权限) */
-    @TableField(exist = false)
-    private Long[] deptIds;
-
-    public SysRole(Long roleId)
-    {
-        this.roleId = roleId;
-    }
-
-    public boolean isAdmin()
-    {
-        return isAdmin(this.roleId);
-    }
-
-    public static boolean isAdmin(Long roleId)
-    {
-        return roleId != null && 1L == roleId;
-    }
+@ExcelIgnoreUnannotated
+public class SysRole implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 角色ID
+	 */
+	@ExcelProperty(value = "角色序号")
+	@TableId(value = "role_id", type = IdType.AUTO)
+	private Long roleId;
+
+	/**
+	 * 角色名称
+	 */
+	@ExcelProperty(value = "角色名称")
+	@NotBlank(message = "角色名称不能为空")
+	@Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符")
+	private String roleName;
+
+	/**
+	 * 角色权限
+	 */
+	@ExcelProperty(value = "角色权限")
+	@NotBlank(message = "权限字符不能为空")
+	@Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符")
+	private String roleKey;
+
+	/**
+	 * 角色排序
+	 */
+	@ExcelProperty(value = "角色排序")
+	@NotBlank(message = "显示顺序不能为空")
+	private String roleSort;
+
+	/**
+	 * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限)
+	 */
+	@ExcelProperty(value = "数据范围", converter = ExcelDictConvert.class)
+	@ExcelDictFormat(readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
+	private String dataScope;
+
+	/**
+	 * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示)
+	 */
+	private boolean menuCheckStrictly;
+
+	/**
+	 * 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 )
+	 */
+	private boolean deptCheckStrictly;
+
+	/**
+	 * 角色状态(0正常 1停用)
+	 */
+	@ExcelProperty(value = "角色状态", converter = ExcelDictConvert.class)
+	@ExcelDictFormat(dictType = "sys_common_status")
+	private String status;
+
+	/**
+	 * 删除标志(0代表存在 2代表删除)
+	 */
+	@TableLogic
+	private String delFlag;
+
+	/**
+	 * 创建者
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private String createBy;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private Date createTime;
+
+	/**
+	 * 更新者
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private String updateBy;
+
+	/**
+	 * 更新时间
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private Date updateTime;
+
+	/**
+	 * 备注
+	 */
+	private String remark;
+
+	/**
+	 * 请求参数
+	 */
+	@TableField(exist = false)
+	private Map<String, Object> params = new HashMap<>();
+
+	/**
+	 * 用户是否存在此角色标识 默认不存在
+	 */
+	@TableField(exist = false)
+	private boolean flag = false;
+
+	/**
+	 * 菜单组
+	 */
+	@TableField(exist = false)
+	private Long[] menuIds;
+
+	/**
+	 * 部门组(数据权限)
+	 */
+	@TableField(exist = false)
+	private Long[] deptIds;
+
+	public SysRole(Long roleId) {
+		this.roleId = roleId;
+	}
+
+	public boolean isAdmin() {
+		return isAdmin(this.roleId);
+	}
+
+	public static boolean isAdmin(Long roleId) {
+		return roleId != null && 1L == roleId;
+	}
 
 }

+ 157 - 135
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java

@@ -1,13 +1,8 @@
 package com.ruoyi.common.core.domain.entity;
 
 import com.baomidou.mybatisplus.annotation.*;
-import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.ruoyi.common.annotation.Excel;
-import com.ruoyi.common.annotation.Excel.ColumnType;
-import com.ruoyi.common.annotation.Excel.Type;
-import com.ruoyi.common.annotation.Excels;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
@@ -31,140 +26,167 @@ import java.util.Map;
 @NoArgsConstructor
 @Accessors(chain = true)
 @TableName("sys_user")
-public class SysUser implements Serializable
-{
-    private static final long serialVersionUID = 1L;
-
-    /** 用户ID */
-    @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
-    @TableId(value = "user_id",type = IdType.AUTO)
-    private Long userId;
-
-    /** 部门ID */
-    @Excel(name = "部门编号", type = Type.IMPORT)
-    private Long deptId;
-
-    /** 用户账号 */
-    @NotBlank(message = "用户账号不能为空")
-    @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
-    @Excel(name = "登录名称")
-    private String userName;
-
-    /** 用户昵称 */
-    @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
-    @Excel(name = "用户名称")
-    private String nickName;
-
-    /** 用户邮箱 */
-    @Email(message = "邮箱格式不正确")
-    @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
-    @Excel(name = "用户邮箱")
-    private String email;
-
-    /** 手机号码 */
-    @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
-    @Excel(name = "手机号码")
-    private String phonenumber;
-
-    /** 用户性别 */
-    @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知")
-    private String sex;
-
-    /** 用户头像 */
-    private String avatar;
-
-    /** 密码 */
-    private String password;
-
-    @JsonIgnore
-    @JsonProperty
-    public String getPassword() {
-        return password;
-    }
-
-    /** 盐加密 */
-    private String salt;
-
-    /** 帐号状态(0正常 1停用) */
-    @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")
-    private String status;
-
-    /** 删除标志(0代表存在 2代表删除) */
-    @TableLogic
-    private String delFlag;
-
-    /** 最后登录IP */
-    @Excel(name = "最后登录IP", type = Type.EXPORT)
-    private String loginIp;
-
-    /** 最后登录时间 */
-    @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
-    private Date loginDate;
-
-    /** 创建者 */
-    @TableField(fill = FieldFill.INSERT)
-    private String createBy;
-
-    /** 创建时间 */
-    @TableField(fill = FieldFill.INSERT)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date createTime;
-
-    /** 更新者 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private String updateBy;
-
-    /** 更新时间 */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date updateTime;
-
-    /** 备注 */
-    private String remark;
-
-    /**
-     * 请求参数
-     */
-    @TableField(exist = false)
-    private Map<String, Object> params = new HashMap<>();
-
-    /** 部门对象 */
-    @Excels({
-        @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
-        @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT)
-    })
-    @TableField(exist = false)
-    private SysDept dept;
-
-    /** 角色对象 */
-    @TableField(exist = false)
-    private List<SysRole> roles;
-
-    /** 角色组 */
-    @TableField(exist = false)
-    private Long[] roleIds;
-
-    /** 岗位组 */
-    @TableField(exist = false)
-    private Long[] postIds;
-
-	/** 角色ID */
+public class SysUser implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 用户ID
+	 */
+	@TableId(value = "user_id", type = IdType.AUTO)
+	private Long userId;
+
+	/**
+	 * 部门ID
+	 */
+	private Long deptId;
+
+	/**
+	 * 用户账号
+	 */
+	@NotBlank(message = "用户账号不能为空")
+	@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
+	private String userName;
+
+	/**
+	 * 用户昵称
+	 */
+	@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
+	private String nickName;
+
+	/**
+	 * 用户邮箱
+	 */
+	@Email(message = "邮箱格式不正确")
+	@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
+	private String email;
+
+	/**
+	 * 手机号码
+	 */
+	private String phonenumber;
+
+	/**
+	 * 用户性别
+	 */
+	private String sex;
+
+	/**
+	 * 用户头像
+	 */
+	private String avatar;
+
+	/**
+	 * 密码
+	 */
+	private String password;
+
+	@JsonIgnore
+	@JsonProperty
+	public String getPassword() {
+		return password;
+	}
+
+	/**
+	 * 盐加密
+	 */
+	private String salt;
+
+	/**
+	 * 帐号状态(0正常 1停用)
+	 */
+	private String status;
+
+	/**
+	 * 删除标志(0代表存在 2代表删除)
+	 */
+	@TableLogic
+	private String delFlag;
+
+	/**
+	 * 最后登录IP
+	 */
+	private String loginIp;
+
+	/**
+	 * 最后登录时间
+	 */
+	private Date loginDate;
+
+	/**
+	 * 创建者
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private String createBy;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private Date createTime;
+
+	/**
+	 * 更新者
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private String updateBy;
+
+	/**
+	 * 更新时间
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private Date updateTime;
+
+	/**
+	 * 备注
+	 */
+	private String remark;
+
+	/**
+	 * 请求参数
+	 */
+	@TableField(exist = false)
+	private Map<String, Object> params = new HashMap<>();
+
+	/**
+	 * 部门对象
+	 */
+	@TableField(exist = false)
+	private SysDept dept;
+
+	/**
+	 * 角色对象
+	 */
+	@TableField(exist = false)
+	private List<SysRole> roles;
+
+	/**
+	 * 角色组
+	 */
+	@TableField(exist = false)
+	private Long[] roleIds;
+
+	/**
+	 * 岗位组
+	 */
+	@TableField(exist = false)
+	private Long[] postIds;
+
+	/**
+	 * 角色ID
+	 */
 	@TableField(exist = false)
 	private Long roleId;
 
-    public SysUser(Long userId)
-    {
-        this.userId = userId;
-    }
+	public SysUser(Long userId) {
+		this.userId = userId;
+	}
 
-    public boolean isAdmin()
-    {
-        return isAdmin(this.userId);
-    }
+	public boolean isAdmin() {
+		return isAdmin(this.userId);
+	}
 
-    public static boolean isAdmin(Long userId)
-    {
-        return userId != null && 1L == userId;
-    }
+	public static boolean isAdmin(Long userId) {
+		return userId != null && 1L == userId;
+	}
 
 }

+ 18 - 2
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java

@@ -23,6 +23,16 @@ public class LoginUser implements UserDetails
 {
     private static final long serialVersionUID = 1L;
 
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
     /**
      * 用户唯一标识
      */
@@ -74,6 +84,14 @@ public class LoginUser implements UserDetails
         this.permissions = permissions;
     }
 
+    public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
+    {
+        this.userId = userId;
+        this.deptId = deptId;
+        this.user = user;
+        this.permissions = permissions;
+    }
+
     @JsonIgnore
     @Override
     public String getPassword()
@@ -81,7 +99,6 @@ public class LoginUser implements UserDetails
         return user.getPassword();
     }
 
-	@JsonIgnore
     @Override
     public String getUsername()
     {
@@ -134,7 +151,6 @@ public class LoginUser implements UserDetails
         return true;
     }
 
-	@JsonIgnore
     @Override
     public Collection<? extends GrantedAuthority> getAuthorities()
     {

+ 11 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java

@@ -0,0 +1,11 @@
+package com.ruoyi.common.core.domain.model;
+
+/**
+ * 用户注册对象
+ * 
+ * @author ruoyi
+ */
+public class RegisterBody extends LoginBody
+{
+
+}

+ 7 - 8
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/methods/InsertAll.java

@@ -1,11 +1,10 @@
 package com.ruoyi.common.core.mybatisplus.methods;
 
-import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.core.enums.SqlMethod;
 import com.baomidou.mybatisplus.core.injector.AbstractMethod;
 import com.baomidou.mybatisplus.core.metadata.TableInfo;
 import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import com.ruoyi.common.utils.StringUtils;
 import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
 import org.apache.ibatis.executor.keygen.KeyGenerator;
 import org.apache.ibatis.executor.keygen.NoKeyGenerator;
@@ -25,11 +24,11 @@ public class InsertAll extends AbstractMethod {
 		final String fieldSql = prepareFieldSql(tableInfo);
 		final String valueSql = prepareValuesSqlForMysqlBatch(tableInfo);
 		KeyGenerator keyGenerator = new NoKeyGenerator();
-		SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
+		String sqlMethod = "insertAll";
 		String keyProperty = null;
 		String keyColumn = null;
 		// 表包含主键处理逻辑,如果不包含主键当普通字段处理
-		if (StrUtil.isNotBlank(tableInfo.getKeyProperty())) {
+		if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {
 			if (tableInfo.getIdType() == IdType.AUTO) {
 				/** 自增主键 */
 				keyGenerator = new Jdbc3KeyGenerator();
@@ -37,7 +36,7 @@ public class InsertAll extends AbstractMethod {
 				keyColumn = tableInfo.getKeyColumn();
 			} else {
 				if (null != tableInfo.getKeySequence()) {
-					keyGenerator = TableInfoHelper.genKeyGenerator(getMethod(sqlMethod), tableInfo, builderAssistant);
+					keyGenerator = TableInfoHelper.genKeyGenerator(sqlMethod, tableInfo, builderAssistant);
 					keyProperty = tableInfo.getKeyProperty();
 					keyColumn = tableInfo.getKeyColumn();
 				}
@@ -45,12 +44,12 @@ public class InsertAll extends AbstractMethod {
 		}
 		final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
 		SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
-		return this.addInsertMappedStatement(mapperClass, modelClass, "insertAll", sqlSource, keyGenerator, keyProperty, keyColumn);
+		return this.addInsertMappedStatement(mapperClass, modelClass, sqlMethod, sqlSource, keyGenerator, keyProperty, keyColumn);
 	}
 
 	private String prepareFieldSql(TableInfo tableInfo) {
 		StringBuilder fieldSql = new StringBuilder();
-		if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) {
+		if (StringUtils.isNotBlank(tableInfo.getKeyColumn())) {
 			fieldSql.append(tableInfo.getKeyColumn()).append(",");
 		}
 		tableInfo.getFieldList().forEach(x -> fieldSql.append(x.getColumn()).append(","));
@@ -63,7 +62,7 @@ public class InsertAll extends AbstractMethod {
 	private String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) {
 		final StringBuilder valueSql = new StringBuilder();
 		valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
-		if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) {
+		if (StringUtils.isNotBlank(tableInfo.getKeyColumn())) {
 			valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
 		}
 		tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));

+ 20 - 0
ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java

@@ -0,0 +1,20 @@
+package com.ruoyi.common.enums;
+
+/**
+ * 限流类型
+ *
+ * @author ruoyi
+ */
+
+public enum LimitType
+{
+    /**
+     * 默认策略全局限流
+     */
+    DEFAULT,
+
+    /**
+     * 根据请求者IP进行限流
+     */
+    IP
+}

+ 0 - 43
ruoyi-common/src/main/java/com/ruoyi/common/exception/CustomException.java

@@ -1,43 +0,0 @@
-package com.ruoyi.common.exception;
-
-/**
- * 自定义异常
- * 
- * @author ruoyi
- */
-public class CustomException extends RuntimeException
-{
-    private static final long serialVersionUID = 1L;
-
-    private Integer code;
-
-    private String message;
-
-    public CustomException(String message)
-    {
-        this.message = message;
-    }
-
-    public CustomException(String message, Integer code)
-    {
-        this.message = message;
-        this.code = code;
-    }
-
-    public CustomException(String message, Throwable e)
-    {
-        super(message, e);
-        this.message = message;
-    }
-
-    @Override
-    public String getMessage()
-    {
-        return message;
-    }
-
-    public Integer getCode()
-    {
-        return code;
-    }
-}

+ 58 - 0
ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java

@@ -0,0 +1,58 @@
+package com.ruoyi.common.exception;
+
+/**
+ * 全局异常
+ * 
+ * @author ruoyi
+ */
+public class GlobalException extends RuntimeException
+{
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 错误提示
+     */
+    private String message;
+
+    /**
+     * 错误明细,内部调试错误
+     *
+     * 和 {@link CommonResult#getDetailMessage()} 一致的设计
+     */
+    private String detailMessage;
+
+    /**
+     * 空构造方法,避免反序列化问题
+     */
+    public GlobalException()
+    {
+    }
+
+    public GlobalException(String message)
+    {
+        this.message = message;
+    }
+
+    public String getDetailMessage()
+    {
+        return detailMessage;
+    }
+
+    public GlobalException setDetailMessage(String detailMessage)
+    {
+        this.detailMessage = detailMessage;
+        return this;
+    }
+
+    public String getMessage()
+    {
+        return message;
+    }
+
+    public GlobalException setMessage(String message)
+    {
+        this.message = message;
+        return this;
+    }
+}

+ 73 - 0
ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java

@@ -0,0 +1,73 @@
+package com.ruoyi.common.exception;
+
+/**
+ * 业务异常
+ * 
+ * @author ruoyi
+ */
+public final class ServiceException extends RuntimeException
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 错误码
+     */
+    private Integer code;
+
+    /**
+     * 错误提示
+     */
+    private String message;
+
+    /**
+     * 错误明细,内部调试错误
+     *
+     * 和 {@link CommonResult#getDetailMessage()} 一致的设计
+     */
+    private String detailMessage;
+
+    /**
+     * 空构造方法,避免反序列化问题
+     */
+    public ServiceException()
+    {
+    }
+
+    public ServiceException(String message)
+    {
+        this.message = message;
+    }
+
+    public ServiceException(String message, Integer code)
+    {
+        this.message = message;
+        this.code = code;
+    }
+
+    public String getDetailMessage()
+    {
+        return detailMessage;
+    }
+
+    public String getMessage()
+    {
+        return message;
+    }
+
+    public Integer getCode()
+    {
+        return code;
+    }
+
+    public ServiceException setMessage(String message)
+    {
+        this.message = message;
+        return this;
+    }
+
+    public ServiceException setDetailMessage(String detailMessage)
+    {
+        this.detailMessage = detailMessage;
+        return this;
+    }
+}

+ 4 - 4
ruoyi-common/src/main/java/com/ruoyi/common/exception/BaseException.java → ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java

@@ -1,11 +1,11 @@
-package com.ruoyi.common.exception;
+package com.ruoyi.common.exception.base;
 
-import cn.hutool.core.lang.Validator;
 import com.ruoyi.common.utils.MessageUtils;
+import com.ruoyi.common.utils.StringUtils;
 
 /**
  * 基础异常
- * 
+ *
  * @author ruoyi
  */
 public class BaseException extends RuntimeException
@@ -64,7 +64,7 @@ public class BaseException extends RuntimeException
     public String getMessage()
     {
         String message = null;
-        if (!Validator.isEmpty(code))
+        if (!StringUtils.isEmpty(code))
         {
             message = MessageUtils.message(code, args);
         }

+ 2 - 2
ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java

@@ -1,10 +1,10 @@
 package com.ruoyi.common.exception.file;
 
-import com.ruoyi.common.exception.BaseException;
+import com.ruoyi.common.exception.base.BaseException;
 
 /**
  * 文件信息异常类
- * 
+ *
  * @author ruoyi
  */
 public class FileException extends BaseException

+ 2 - 2
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java

@@ -1,10 +1,10 @@
 package com.ruoyi.common.exception.user;
 
-import com.ruoyi.common.exception.BaseException;
+import com.ruoyi.common.exception.base.BaseException;
 
 /**
  * 用户信息异常类
- * 
+ *
  * @author ruoyi
  */
 public class UserException extends BaseException

+ 48 - 48
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java

@@ -1,48 +1,48 @@
-package com.ruoyi.common.filter;
-
-import cn.hutool.core.util.StrUtil;
-import org.springframework.http.MediaType;
-
-import javax.servlet.*;
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-
-/**
- * Repeatable 过滤器
- * 
- * @author ruoyi
- */
-public class RepeatableFilter implements Filter
-{
-    @Override
-    public void init(FilterConfig filterConfig) throws ServletException
-    {
-
-    }
-
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
-            throws IOException, ServletException
-    {
-        ServletRequest requestWrapper = null;
-        if (request instanceof HttpServletRequest
-                && StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE))
-        {
-            requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
-        }
-        if (null == requestWrapper)
-        {
-            chain.doFilter(request, response);
-        }
-        else
-        {
-            chain.doFilter(requestWrapper, response);
-        }
-    }
-
-    @Override
-    public void destroy()
-    {
-
-    }
-}
+package com.ruoyi.common.filter;
+
+import com.ruoyi.common.utils.StringUtils;
+import org.springframework.http.MediaType;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * Repeatable 过滤器
+ *
+ * @author ruoyi
+ */
+public class RepeatableFilter implements Filter
+{
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException
+    {
+
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+            throws IOException, ServletException
+    {
+        ServletRequest requestWrapper = null;
+        if (request instanceof HttpServletRequest
+                && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE))
+        {
+            requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
+        }
+        if (null == requestWrapper)
+        {
+            chain.doFilter(request, response);
+        }
+        else
+        {
+            chain.doFilter(requestWrapper, response);
+        }
+    }
+
+    @Override
+    public void destroy()
+    {
+
+    }
+}

+ 9 - 32
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java

@@ -1,6 +1,6 @@
 package com.ruoyi.common.filter;
 
-import cn.hutool.core.util.StrUtil;
+import com.ruoyi.common.utils.StringUtils;
 
 import javax.servlet.*;
 import javax.servlet.http.HttpServletRequest;
@@ -8,12 +8,10 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * 防止XSS攻击的过滤器
- * 
+ *
  * @author ruoyi
  */
 public class XssFilter implements Filter
@@ -23,17 +21,11 @@ public class XssFilter implements Filter
      */
     public List<String> excludes = new ArrayList<>();
 
-    /**
-     * xss过滤开关
-     */
-    public boolean enabled = false;
-
     @Override
     public void init(FilterConfig filterConfig) throws ServletException
     {
         String tempExcludes = filterConfig.getInitParameter("excludes");
-        String tempEnabled = filterConfig.getInitParameter("enabled");
-        if (StrUtil.isNotEmpty(tempExcludes))
+        if (StringUtils.isNotEmpty(tempExcludes))
         {
             String[] url = tempExcludes.split(",");
             for (int i = 0; url != null && i < url.length; i++)
@@ -41,10 +33,6 @@ public class XssFilter implements Filter
                 excludes.add(url[i]);
             }
         }
-        if (StrUtil.isNotEmpty(tempEnabled))
-        {
-            enabled = Boolean.valueOf(tempEnabled);
-        }
     }
 
     @Override
@@ -64,25 +52,14 @@ public class XssFilter implements Filter
 
     private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
     {
-        if (!enabled)
-        {
-            return true;
-        }
-        if (excludes == null || excludes.isEmpty())
-        {
-            return false;
-        }
         String url = request.getServletPath();
-        for (String pattern : excludes)
+        String method = request.getMethod();
+        // GET DELETE 不过滤
+        if (method == null || method.matches("GET") || method.matches("DELETE"))
         {
-            Pattern p = Pattern.compile("^" + pattern);
-            Matcher m = p.matcher(url);
-            if (m.find())
-            {
-                return true;
-            }
+            return true;
         }
-        return false;
+        return StringUtils.matches(url, excludes);
     }
 
     @Override
@@ -90,4 +67,4 @@ public class XssFilter implements Filter
     {
 
     }
-}
+}

+ 3 - 4
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java

@@ -1,9 +1,8 @@
 package com.ruoyi.common.filter;
 
 import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.lang.Validator;
-import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HtmlUtil;
+import com.ruoyi.common.utils.StringUtils;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
 
@@ -59,7 +58,7 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
 
         // 为空,直接返回
         String json = IoUtil.read(super.getInputStream(), StandardCharsets.UTF_8);
-        if (Validator.isEmpty(json))
+        if (StringUtils.isEmpty(json))
         {
             return super.getInputStream();
         }
@@ -103,6 +102,6 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
     public boolean isJsonRequest()
     {
         String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
-        return StrUtil.startWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
+        return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
     }
 }

+ 185 - 187
ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java

@@ -1,187 +1,185 @@
-package com.ruoyi.common.utils;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.lang.Validator;
-import cn.hutool.core.util.StrUtil;
-import com.ruoyi.common.constant.Constants;
-import com.ruoyi.common.core.domain.entity.SysDictData;
-import com.ruoyi.common.core.redis.RedisCache;
-import com.ruoyi.common.utils.spring.SpringUtils;
-
-import java.util.Collection;
-import java.util.List;
-
-/**
- * 字典工具类
- * 
- * @author ruoyi
- */
-public class DictUtils
-{
-    /**
-     * 分隔符
-     */
-    public static final String SEPARATOR = ",";
-
-    /**
-     * 设置字典缓存
-     * 
-     * @param key 参数键
-     * @param dictDatas 字典数据列表
-     */
-    public static void setDictCache(String key, List<SysDictData> dictDatas)
-    {
-        SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas);
-    }
-
-    /**
-     * 获取字典缓存
-     * 
-     * @param key 参数键
-     * @return dictDatas 字典数据列表
-     */
-    public static List<SysDictData> getDictCache(String key)
-    {
-        Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
-        if (Validator.isNotNull(cacheObj))
-        {
-            List<SysDictData> dictDatas = (List<SysDictData>)cacheObj;
-            return dictDatas;
-        }
-        return null;
-    }
-
-    /**
-     * 根据字典类型和字典值获取字典标签
-     * 
-     * @param dictType 字典类型
-     * @param dictValue 字典值
-     * @return 字典标签
-     */
-    public static String getDictLabel(String dictType, String dictValue)
-    {
-        return getDictLabel(dictType, dictValue, SEPARATOR);
-    }
-
-    /**
-     * 根据字典类型和字典标签获取字典值
-     * 
-     * @param dictType 字典类型
-     * @param dictLabel 字典标签
-     * @return 字典值
-     */
-    public static String getDictValue(String dictType, String dictLabel)
-    {
-        return getDictValue(dictType, dictLabel, SEPARATOR);
-    }
-
-    /**
-     * 根据字典类型和字典值获取字典标签
-     * 
-     * @param dictType 字典类型
-     * @param dictValue 字典值
-     * @param separator 分隔符
-     * @return 字典标签
-     */
-    public static String getDictLabel(String dictType, String dictValue, String separator)
-    {
-        StringBuilder propertyString = new StringBuilder();
-        List<SysDictData> datas = getDictCache(dictType);
-
-        if (StrUtil.containsAny(dictValue, separator) && CollUtil.isNotEmpty(datas))
-        {
-            for (SysDictData dict : datas)
-            {
-                for (String value : dictValue.split(separator))
-                {
-                    if (value.equals(dict.getDictValue()))
-                    {
-                        propertyString.append(dict.getDictLabel() + separator);
-                        break;
-                    }
-                }
-            }
-        }
-        else
-        {
-            for (SysDictData dict : datas)
-            {
-                if (dictValue.equals(dict.getDictValue()))
-                {
-                    return dict.getDictLabel();
-                }
-            }
-        }
-        return StrUtil.strip(propertyString.toString(), null, separator);
-    }
-
-    /**
-     * 根据字典类型和字典标签获取字典值
-     * 
-     * @param dictType 字典类型
-     * @param dictLabel 字典标签
-     * @param separator 分隔符
-     * @return 字典值
-     */
-    public static String getDictValue(String dictType, String dictLabel, String separator)
-    {
-        StringBuilder propertyString = new StringBuilder();
-        List<SysDictData> datas = getDictCache(dictType);
-
-        if (StrUtil.containsAny(dictLabel, separator) && CollUtil.isNotEmpty(datas))
-        {
-            for (SysDictData dict : datas)
-            {
-                for (String label : dictLabel.split(separator))
-                {
-                    if (label.equals(dict.getDictLabel()))
-                    {
-                        propertyString.append(dict.getDictValue() + separator);
-                        break;
-                    }
-                }
-            }
-        }
-        else
-        {
-            for (SysDictData dict : datas)
-            {
-                if (dictLabel.equals(dict.getDictLabel()))
-                {
-                    return dict.getDictValue();
-                }
-            }
-        }
-        return StrUtil.strip(propertyString.toString(), null, separator);
-    }
-
-    /**
-     * 删除指定字典缓存
-     * 
-     * @param key 字典键
-     */
-    public static void removeDictCache(String key)
-    {
-        SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key));
-    }
-
-    /**
-     * 清空字典缓存
-     */
-    public static void clearDictCache()
-    {
-        Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(Constants.SYS_DICT_KEY + "*");
-        SpringUtils.getBean(RedisCache.class).deleteObject(keys);
-    }
-
-    /**
-     * 设置cache key
-     * 
-     * @param configKey 参数键
-     * @return 缓存键key
-     */
-    public static String getCacheKey(String configKey)
-    {
-        return Constants.SYS_DICT_KEY + configKey;
-    }
-}
+package com.ruoyi.common.utils;
+
+import cn.hutool.core.collection.CollUtil;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.core.domain.entity.SysDictData;
+import com.ruoyi.common.core.redis.RedisCache;
+import com.ruoyi.common.utils.spring.SpringUtils;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 字典工具类
+ *
+ * @author ruoyi
+ */
+public class DictUtils
+{
+    /**
+     * 分隔符
+     */
+    public static final String SEPARATOR = ",";
+
+    /**
+     * 设置字典缓存
+     *
+     * @param key 参数键
+     * @param dictDatas 字典数据列表
+     */
+    public static void setDictCache(String key, List<SysDictData> dictDatas)
+    {
+        SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas);
+    }
+
+    /**
+     * 获取字典缓存
+     *
+     * @param key 参数键
+     * @return dictDatas 字典数据列表
+     */
+    public static List<SysDictData> getDictCache(String key)
+    {
+        Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
+        if (StringUtils.isNotNull(cacheObj))
+        {
+            List<SysDictData> dictDatas = (List<SysDictData>)cacheObj;
+            return dictDatas;
+        }
+        return null;
+    }
+
+    /**
+     * 根据字典类型和字典值获取字典标签
+     *
+     * @param dictType 字典类型
+     * @param dictValue 字典值
+     * @return 字典标签
+     */
+    public static String getDictLabel(String dictType, String dictValue)
+    {
+        return getDictLabel(dictType, dictValue, SEPARATOR);
+    }
+
+    /**
+     * 根据字典类型和字典标签获取字典值
+     *
+     * @param dictType 字典类型
+     * @param dictLabel 字典标签
+     * @return 字典值
+     */
+    public static String getDictValue(String dictType, String dictLabel)
+    {
+        return getDictValue(dictType, dictLabel, SEPARATOR);
+    }
+
+    /**
+     * 根据字典类型和字典值获取字典标签
+     *
+     * @param dictType 字典类型
+     * @param dictValue 字典值
+     * @param separator 分隔符
+     * @return 字典标签
+     */
+    public static String getDictLabel(String dictType, String dictValue, String separator)
+    {
+        StringBuilder propertyString = new StringBuilder();
+        List<SysDictData> datas = getDictCache(dictType);
+
+        if (StringUtils.containsAny(dictValue, separator) && CollUtil.isNotEmpty(datas))
+        {
+            for (SysDictData dict : datas)
+            {
+                for (String value : dictValue.split(separator))
+                {
+                    if (value.equals(dict.getDictValue()))
+                    {
+                        propertyString.append(dict.getDictLabel() + separator);
+                        break;
+                    }
+                }
+            }
+        }
+        else
+        {
+            for (SysDictData dict : datas)
+            {
+                if (dictValue.equals(dict.getDictValue()))
+                {
+                    return dict.getDictLabel();
+                }
+            }
+        }
+        return StringUtils.stripEnd(propertyString.toString(), separator);
+    }
+
+    /**
+     * 根据字典类型和字典标签获取字典值
+     *
+     * @param dictType 字典类型
+     * @param dictLabel 字典标签
+     * @param separator 分隔符
+     * @return 字典值
+     */
+    public static String getDictValue(String dictType, String dictLabel, String separator)
+    {
+        StringBuilder propertyString = new StringBuilder();
+        List<SysDictData> datas = getDictCache(dictType);
+
+        if (StringUtils.containsAny(dictLabel, separator) && CollUtil.isNotEmpty(datas))
+        {
+            for (SysDictData dict : datas)
+            {
+                for (String label : dictLabel.split(separator))
+                {
+                    if (label.equals(dict.getDictLabel()))
+                    {
+                        propertyString.append(dict.getDictValue() + separator);
+                        break;
+                    }
+                }
+            }
+        }
+        else
+        {
+            for (SysDictData dict : datas)
+            {
+                if (dictLabel.equals(dict.getDictLabel()))
+                {
+                    return dict.getDictValue();
+                }
+            }
+        }
+        return StringUtils.stripEnd(propertyString.toString(), separator);
+    }
+
+    /**
+     * 删除指定字典缓存
+     *
+     * @param key 字典键
+     */
+    public static void removeDictCache(String key)
+    {
+        SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key));
+    }
+
+    /**
+     * 清空字典缓存
+     */
+    public static void clearDictCache()
+    {
+        Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(Constants.SYS_DICT_KEY + "*");
+        SpringUtils.getBean(RedisCache.class).deleteObject(keys);
+    }
+
+    /**
+     * 设置cache key
+     *
+     * @param configKey 参数键
+     * @return 缓存键key
+     */
+    public static String getCacheKey(String configKey)
+    {
+        return Constants.SYS_DICT_KEY + configKey;
+    }
+}

+ 5 - 7
ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java

@@ -1,8 +1,6 @@
 package com.ruoyi.common.utils;
 
-import cn.hutool.core.lang.Validator;
 import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.util.StrUtil;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -33,7 +31,7 @@ public class JsonUtils {
     }
 
     public static String toJsonString(Object object) {
-		if (Validator.isEmpty(object)) {
+		if (StringUtils.isNull(object)) {
 			return null;
 		}
         try {
@@ -44,7 +42,7 @@ public class JsonUtils {
     }
 
     public static <T> T parseObject(String text, Class<T> clazz) {
-        if (StrUtil.isEmpty(text)) {
+        if (StringUtils.isEmpty(text)) {
             return null;
         }
         try {
@@ -66,7 +64,7 @@ public class JsonUtils {
     }
 
     public static <T> T parseObject(String text, TypeReference<T> typeReference) {
-		if (StrUtil.isBlank(text)) {
+		if (StringUtils.isBlank(text)) {
 			return null;
 		}
         try {
@@ -77,7 +75,7 @@ public class JsonUtils {
     }
 
 	public static <T> Map<String, T> parseMap(String text) {
-		if (StrUtil.isBlank(text)) {
+		if (StringUtils.isBlank(text)) {
 			return null;
 		}
 		try {
@@ -88,7 +86,7 @@ public class JsonUtils {
 	}
 
     public static <T> List<T> parseArray(String text, Class<T> clazz) {
-        if (StrUtil.isEmpty(text)) {
+        if (StringUtils.isEmpty(text)) {
             return new ArrayList<>();
         }
         try {

+ 3 - 4
ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java

@@ -1,6 +1,5 @@
 package com.ruoyi.common.utils;
 
-import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HttpStatus;
 import com.baomidou.mybatisplus.core.metadata.OrderItem;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -59,7 +58,7 @@ public class PageUtils {
         String orderByColumn = ServletUtils.getParameter(ORDER_BY_COLUMN);
         String isAsc = ServletUtils.getParameter(IS_ASC);
         PagePlus<T, K> page = new PagePlus<>(pageNum, pageSize);
-        if (StrUtil.isNotBlank(orderByColumn)) {
+        if (StringUtils.isNotBlank(orderByColumn)) {
             String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
             if ("asc".equals(isAsc)) {
                 page.addOrder(OrderItem.asc(orderBy));
@@ -91,9 +90,9 @@ public class PageUtils {
 			isAsc = "desc";
 		}
         Page<T> page = new Page<>(pageNum, pageSize);
-        if (StrUtil.isNotBlank(orderByColumn)) {
+        if (StringUtils.isNotBlank(orderByColumn)) {
             String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
-			orderBy = StrUtil.toUnderlineCase(orderBy);
+			orderBy = StringUtils.toUnderScoreCase(orderBy);
 			if ("asc".equals(isAsc)) {
                 page.addOrder(OrderItem.asc(orderBy));
             } else if ("desc".equals(isAsc)) {

+ 35 - 5
ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java

@@ -5,15 +5,45 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import com.ruoyi.common.core.domain.model.LoginUser;
-import com.ruoyi.common.exception.CustomException;
+import com.ruoyi.common.exception.ServiceException;
 
 /**
  * 安全服务工具类
- * 
+ *
  * @author ruoyi
  */
 public class SecurityUtils
 {
+    /**
+     * 用户ID
+     **/
+    public static Long getUserId()
+    {
+        try
+        {
+            return getLoginUser().getUserId();
+        }
+        catch (Exception e)
+        {
+            throw new ServiceException("获取用户ID异常", HttpStatus.HTTP_UNAUTHORIZED);
+        }
+    }
+
+    /**
+     * 获取部门ID
+     **/
+    public static Long getDeptId()
+    {
+        try
+        {
+            return getLoginUser().getDeptId();
+        }
+        catch (Exception e)
+        {
+            throw new ServiceException("获取部门ID异常", HttpStatus.HTTP_UNAUTHORIZED);
+        }
+    }
+
     /**
      * 获取用户账户
      **/
@@ -25,7 +55,7 @@ public class SecurityUtils
         }
         catch (Exception e)
         {
-            throw new CustomException("获取用户账户异常", HttpStatus.HTTP_UNAUTHORIZED);
+            throw new ServiceException("获取用户账户异常", HttpStatus.HTTP_UNAUTHORIZED);
         }
     }
 
@@ -40,7 +70,7 @@ public class SecurityUtils
         }
         catch (Exception e)
         {
-            throw new CustomException("获取用户信息异常", HttpStatus.HTTP_UNAUTHORIZED);
+            throw new ServiceException("获取用户信息异常", HttpStatus.HTTP_UNAUTHORIZED);
         }
     }
 
@@ -79,7 +109,7 @@ public class SecurityUtils
 
     /**
      * 是否为管理员
-     * 
+     *
      * @param userId 用户ID
      * @return 结果
      */

+ 2 - 3
ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java

@@ -1,7 +1,6 @@
 package com.ruoyi.common.utils;
 
 import cn.hutool.core.convert.Convert;
-import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.servlet.ServletUtil;
 import cn.hutool.http.HttpStatus;
 import org.springframework.http.MediaType;
@@ -112,12 +111,12 @@ public class ServletUtils extends ServletUtil {
 		}
 
 		String uri = request.getRequestURI();
-		if (StrUtil.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
+		if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
 			return true;
 		}
 
 		String ajax = request.getParameter("__ajax");
-		if (StrUtil.equalsAnyIgnoreCase(ajax, "json", "xml")) {
+		if (StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml")) {
 			return true;
 		}
 		return false;

+ 354 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java

@@ -0,0 +1,354 @@
+package com.ruoyi.common.utils;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Validator;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.ReUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.util.*;
+
+/**
+ * 字符串工具类
+ *
+ * @author ruoyi
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils {
+
+	/**
+	 * 获取参数不为空值
+	 *
+	 * @param value defaultValue 要判断的value
+	 * @return value 返回值
+	 */
+	public static <T> T nvl(T value, T defaultValue) {
+		return ObjectUtil.defaultIfNull(value, defaultValue);
+	}
+
+	/**
+	 * 获取参数不为空值
+	 *
+	 * @param str defaultValue 要判断的value
+	 * @return value 返回值
+	 */
+	public static String blankToDefault(String str, String defaultValue) {
+		return StrUtil.blankToDefault(str, defaultValue);
+	}
+
+	/**
+	 * * 判断一个Collection是否为空, 包含List,Set,Queue
+	 *
+	 * @param coll 要判断的Collection
+	 * @return true:为空 false:非空
+	 */
+	public static boolean isEmpty(Collection<?> coll) {
+		return CollUtil.isEmpty(coll);
+	}
+
+	/**
+	 * * 判断一个Collection是否非空,包含List,Set,Queue
+	 *
+	 * @param coll 要判断的Collection
+	 * @return true:非空 false:空
+	 */
+	public static boolean isNotEmpty(Collection<?> coll) {
+		return !isEmpty(coll);
+	}
+
+	/**
+	 * * 判断一个对象数组是否为空
+	 *
+	 * @param objects 要判断的对象数组
+	 *                * @return true:为空 false:非空
+	 */
+	public static boolean isEmpty(Object[] objects) {
+		return ArrayUtil.isEmpty(objects);
+	}
+
+	/**
+	 * * 判断一个对象数组是否非空
+	 *
+	 * @param objects 要判断的对象数组
+	 * @return true:非空 false:空
+	 */
+	public static boolean isNotEmpty(Object[] objects) {
+		return !isEmpty(objects);
+	}
+
+	/**
+	 * * 判断一个对象是否为空
+	 *
+	 * @param object 要判断的对象数组
+	 *                * @return true:为空 false:非空
+	 */
+	public static boolean isEmpty(Object object) {
+		return ObjectUtil.isEmpty(object);
+	}
+
+	/**
+	 * * 判断一个对象是否非空
+	 *
+	 * @param object 要判断的对象数组
+	 * @return true:非空 false:空
+	 */
+	public static boolean isNotEmpty(Object object) {
+		return !isEmpty(object);
+	}
+
+	/**
+	 * * 判断一个Map是否为空
+	 *
+	 * @param map 要判断的Map
+	 * @return true:为空 false:非空
+	 */
+	public static boolean isEmpty(Map<?, ?> map) {
+		return MapUtil.isEmpty(map);
+	}
+
+	/**
+	 * * 判断一个Map是否为空
+	 *
+	 * @param map 要判断的Map
+	 * @return true:非空 false:空
+	 */
+	public static boolean isNotEmpty(Map<?, ?> map) {
+		return !isEmpty(map);
+	}
+
+	/**
+	 * * 判断一个字符串是否为空串
+	 *
+	 * @param str String
+	 * @return true:为空 false:非空
+	 */
+	public static boolean isEmpty(String str) {
+		return StrUtil.isEmpty(str);
+	}
+
+	/**
+	 * * 判断一个字符串是否为非空串
+	 *
+	 * @param str String
+	 * @return true:非空串 false:空串
+	 */
+	public static boolean isNotEmpty(String str) {
+		return !isEmpty(str);
+	}
+
+	/**
+	 * * 判断一个对象是否为空
+	 *
+	 * @param object Object
+	 * @return true:为空 false:非空
+	 */
+	public static boolean isNull(Object object) {
+		return ObjectUtil.isNull(object);
+	}
+
+	/**
+	 * * 判断一个对象是否非空
+	 *
+	 * @param object Object
+	 * @return true:非空 false:空
+	 */
+	public static boolean isNotNull(Object object) {
+		return !isNull(object);
+	}
+
+	/**
+	 * * 判断一个对象是否是数组类型(Java基本型别的数组)
+	 *
+	 * @param object 对象
+	 * @return true:是数组 false:不是数组
+	 */
+	public static boolean isArray(Object object) {
+		return ArrayUtil.isArray(object);
+	}
+
+	/**
+	 * 去空格
+	 */
+	public static String trim(String str) {
+		return StrUtil.trim(str);
+	}
+
+	/**
+	 * 截取字符串
+	 *
+	 * @param str   字符串
+	 * @param start 开始
+	 * @return 结果
+	 */
+	public static String substring(final String str, int start) {
+		return substring(str, start, str.length());
+	}
+
+	/**
+	 * 截取字符串
+	 *
+	 * @param str   字符串
+	 * @param start 开始
+	 * @param end   结束
+	 * @return 结果
+	 */
+	public static String substring(final String str, int start, int end) {
+		return StrUtil.sub(str, start, end);
+	}
+
+	/**
+	 * 格式化文本, {} 表示占位符<br>
+	 * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
+	 * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
+	 * 例:<br>
+	 * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
+	 * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
+	 * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
+	 *
+	 * @param template 文本模板,被替换的部分用 {} 表示
+	 * @param params   参数值
+	 * @return 格式化后的文本
+	 */
+	public static String format(String template, Object... params) {
+		return StrUtil.format(template, params);
+	}
+
+	/**
+	 * 是否为http(s)://开头
+	 *
+	 * @param link 链接
+	 * @return 结果
+	 */
+	public static boolean ishttp(String link) {
+		return Validator.isUrl(link);
+	}
+
+	/**
+	 * 字符串转set
+	 *
+	 * @param str 字符串
+	 * @param sep 分隔符
+	 * @return set集合
+	 */
+	public static Set<String> str2Set(String str, String sep) {
+		return new HashSet<>(str2List(str, sep, true, false));
+	}
+
+	/**
+	 * 字符串转list
+	 *
+	 * @param str         字符串
+	 * @param sep         分隔符
+	 * @param filterBlank 过滤纯空白
+	 * @param trim        去掉首尾空白
+	 * @return list集合
+	 */
+	public static List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
+		List<String> list = new ArrayList<>();
+		if (isEmpty(str)) {
+			return list;
+		}
+
+		// 过滤空白字符串
+		if (filterBlank && isBlank(str)) {
+			return list;
+		}
+		String[] split = str.split(sep);
+		for (String string : split) {
+			if (filterBlank && isBlank(string)) {
+				continue;
+			}
+			if (trim) {
+				string = trim(string);
+			}
+			list.add(string);
+		}
+
+		return list;
+	}
+
+	/**
+	 * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
+	 *
+	 * @param cs                  指定字符串
+	 * @param searchCharSequences 需要检查的字符串数组
+	 * @return 是否包含任意一个字符串
+	 */
+	public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
+		return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences);
+	}
+
+	/**
+	 * 驼峰转下划线命名
+	 */
+	public static String toUnderScoreCase(String str) {
+		return StrUtil.toUnderlineCase(str);
+	}
+
+	/**
+	 * 是否包含字符串
+	 *
+	 * @param str  验证字符串
+	 * @param strs 字符串组
+	 * @return 包含返回true
+	 */
+	public static boolean inStringIgnoreCase(String str, String... strs) {
+		return StrUtil.equalsAnyIgnoreCase(str, strs);
+	}
+
+	/**
+	 * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
+	 *
+	 * @param name 转换前的下划线大写方式命名的字符串
+	 * @return 转换后的驼峰式命名的字符串
+	 */
+	public static String convertToCamelCase(String name) {
+		return StrUtil.upperFirst(StrUtil.toCamelCase(name));
+	}
+
+	/**
+	 * 驼峰式命名法 例如:user_name->userName
+	 */
+	public static String toCamelCase(String s) {
+		return StrUtil.toCamelCase(s);
+	}
+
+	/**
+	 * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
+	 *
+	 * @param str  指定字符串
+	 * @param strs 需要检查的字符串数组
+	 * @return 是否匹配
+	 */
+	public static boolean matches(String str, List<String> strs) {
+		if (isEmpty(str) || isEmpty(strs)) {
+			return false;
+		}
+		for (String pattern : strs) {
+			if (isMatch(pattern, str)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * 判断url是否与规则配置:
+	 * ? 表示单个字符;
+	 * * 表示一层路径内的任意字符串,不可跨层级;
+	 * ** 表示任意层路径;
+	 *
+	 * @param pattern 匹配规则
+	 * @param url     需要匹配的url
+	 * @return
+	 */
+	public static boolean isMatch(String pattern, String url) {
+		return ReUtil.isMatch(pattern, url);
+	}
+
+	@SuppressWarnings("unchecked")
+	public static <T> T cast(Object obj) {
+		return (T) obj;
+	}
+}

+ 0 - 76
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java

@@ -1,76 +0,0 @@
-package com.ruoyi.common.utils.file;
-
-import java.io.File;
-import org.apache.commons.lang3.StringUtils;
-
-/**
- * 文件类型工具类
- *
- * @author ruoyi
- */
-public class FileTypeUtils
-{
-    /**
-     * 获取文件类型
-     * <p>
-     * 例如: ruoyi.txt, 返回: txt
-     * 
-     * @param file 文件名
-     * @return 后缀(不含".")
-     */
-    public static String getFileType(File file)
-    {
-        if (null == file)
-        {
-            return StringUtils.EMPTY;
-        }
-        return getFileType(file.getName());
-    }
-
-    /**
-     * 获取文件类型
-     * <p>
-     * 例如: ruoyi.txt, 返回: txt
-     *
-     * @param fileName 文件名
-     * @return 后缀(不含".")
-     */
-    public static String getFileType(String fileName)
-    {
-        int separatorIndex = fileName.lastIndexOf(".");
-        if (separatorIndex < 0)
-        {
-            return "";
-        }
-        return fileName.substring(separatorIndex + 1).toLowerCase();
-    }
-
-    /**
-     * 获取文件类型
-     * 
-     * @param photoByte 文件字节码
-     * @return 后缀(不含".")
-     */
-    public static String getFileExtendName(byte[] photoByte)
-    {
-        String strFileExtendName = "JPG";
-        if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
-                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
-        {
-            strFileExtendName = "GIF";
-        }
-        else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
-        {
-            strFileExtendName = "JPG";
-        }
-        else if ((photoByte[0] == 66) && (photoByte[1] == 77))
-        {
-            strFileExtendName = "BMP";
-        }
-        else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
-        {
-            strFileExtendName = "PNG";
-        }
-        return strFileExtendName;
-    }
-}

+ 0 - 239
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java

@@ -1,239 +0,0 @@
-package com.ruoyi.common.utils.file;
-
-import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.lang.Validator;
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.StrUtil;
-import com.ruoyi.common.config.RuoYiConfig;
-import com.ruoyi.common.constant.Constants;
-import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
-import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
-import com.ruoyi.common.exception.file.InvalidExtensionException;
-import com.ruoyi.common.utils.DateUtils;
-import org.apache.commons.io.FilenameUtils;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * 文件上传工具类
- *
- * @author ruoyi
- */
-public class FileUploadUtils
-{
-    /**
-     * 默认大小 50M
-     */
-    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
-
-    /**
-     * 默认的文件名最大长度 100
-     */
-    public static final int DEFAULT_FILE_NAME_LENGTH = 100;
-
-    /**
-     * 默认上传的地址
-     */
-    private static String defaultBaseDir = RuoYiConfig.getProfile();
-
-    public static void setDefaultBaseDir(String defaultBaseDir)
-    {
-        FileUploadUtils.defaultBaseDir = defaultBaseDir;
-    }
-
-    public static String getDefaultBaseDir()
-    {
-        return defaultBaseDir;
-    }
-
-    /**
-     * 以默认配置进行文件上传
-     *
-     * @param file 上传的文件
-     * @return 文件名称
-     * @throws Exception
-     */
-    public static final String upload(MultipartFile file) throws IOException
-    {
-        try
-        {
-            return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
-        }
-        catch (Exception e)
-        {
-            throw new IOException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * 根据文件路径上传
-     *
-     * @param baseDir 相对应用的基目录
-     * @param file 上传的文件
-     * @return 文件名称
-     * @throws IOException
-     */
-    public static final String upload(String baseDir, MultipartFile file) throws IOException
-    {
-        try
-        {
-            return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
-        }
-        catch (Exception e)
-        {
-            throw new IOException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * 文件上传
-     *
-     * @param baseDir 相对应用的基目录
-     * @param file 上传的文件
-     * @param allowedExtension 上传文件类型
-     * @return 返回上传成功的文件名
-     * @throws FileSizeLimitExceededException 如果超出最大大小
-     * @throws FileNameLengthLimitExceededException 文件名太长
-     * @throws IOException 比如读写文件出错时
-     * @throws InvalidExtensionException 文件校验异常
-     */
-    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
-            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
-            InvalidExtensionException
-    {
-        int fileNamelength = file.getOriginalFilename().length();
-        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
-        {
-            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
-        }
-
-        assertAllowed(file, allowedExtension);
-
-        String fileName = extractFilename(file);
-
-        File desc = getAbsoluteFile(baseDir, fileName);
-		desc = FileUtil.touch(desc);
-		FileUtil.writeFromStream(file.getInputStream(), desc);
-        String pathFileName = getPathFileName(baseDir, fileName);
-        return pathFileName;
-    }
-
-    /**
-     * 编码文件名
-     */
-    public static final String extractFilename(MultipartFile file)
-    {
-        String fileName = file.getOriginalFilename();
-        String extension = getExtension(file);
-        fileName = DateUtils.datePath() + "/" + IdUtil.fastUUID() + "." + extension;
-        return fileName;
-    }
-
-    private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
-    {
-        File desc = new File(uploadDir + File.separator + fileName);
-
-        if (!desc.exists())
-        {
-            if (!desc.getParentFile().exists())
-            {
-                desc.getParentFile().mkdirs();
-            }
-        }
-        return desc;
-    }
-
-    private static final String getPathFileName(String uploadDir, String fileName) throws IOException
-    {
-        int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
-        String currentDir = StrUtil.subSuf(uploadDir, dirLastIndex);
-        String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
-        return pathFileName;
-    }
-
-    /**
-     * 文件大小校验
-     *
-     * @param file 上传的文件
-     * @return
-     * @throws FileSizeLimitExceededException 如果超出最大大小
-     * @throws InvalidExtensionException
-     */
-    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
-            throws FileSizeLimitExceededException, InvalidExtensionException
-    {
-        long size = file.getSize();
-        if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE)
-        {
-            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
-        }
-
-        String fileName = file.getOriginalFilename();
-        String extension = getExtension(file);
-        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
-        {
-            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
-            {
-                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
-                        fileName);
-            }
-            else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
-            {
-                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
-                        fileName);
-            }
-            else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
-            {
-                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
-                        fileName);
-            }
-            else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION)
-            {
-                throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
-                        fileName);
-            }
-            else
-            {
-                throw new InvalidExtensionException(allowedExtension, extension, fileName);
-            }
-        }
-
-    }
-
-    /**
-     * 判断MIME类型是否是允许的MIME类型
-     *
-     * @param extension
-     * @param allowedExtension
-     * @return
-     */
-    public static final boolean isAllowedExtension(String extension, String[] allowedExtension)
-    {
-        for (String str : allowedExtension)
-        {
-            if (str.equalsIgnoreCase(extension))
-            {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * 获取文件名的后缀
-     *
-     * @param file 表单文件
-     * @return 后缀名
-     */
-    public static final String getExtension(MultipartFile file)
-    {
-        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
-        if (Validator.isEmpty(extension))
-        {
-            extension = MimeTypeUtils.getExtension(file.getContentType());
-        }
-        return extension;
-    }
-}

+ 1 - 75
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java

@@ -1,12 +1,9 @@
 package com.ruoyi.common.utils.file;
 
 import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.util.StrUtil;
 
-import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.io.*;
+import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 
@@ -17,77 +14,6 @@ import java.nio.charset.StandardCharsets;
  */
 public class FileUtils extends FileUtil
 {
-    public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
-
-    /**
-     * 文件名称验证
-     *
-     * @param filename 文件名称
-     * @return true 正常 false 非法
-     */
-    public static boolean isValidFilename(String filename)
-    {
-        return filename.matches(FILENAME_PATTERN);
-    }
-
-    /**
-     * 检查文件是否可下载
-     *
-     * @param resource 需要下载的文件
-     * @return true 正常 false 非法
-     */
-    public static boolean checkAllowDownload(String resource)
-    {
-        // 禁止目录上跳级别
-        if (StrUtil.contains(resource, ".."))
-        {
-            return false;
-        }
-
-        // 检查允许下载的文件规则
-        if (ArrayUtil.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))
-        {
-            return true;
-        }
-
-        // 不在允许下载的文件规则
-        return false;
-    }
-
-    /**
-     * 下载文件名重新编码
-     *
-     * @param request 请求对象
-     * @param fileName 文件名
-     * @return 编码后的文件名
-     */
-    public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException
-    {
-        final String agent = request.getHeader("USER-AGENT");
-        String filename = fileName;
-        if (agent.contains("MSIE"))
-        {
-            // IE浏览器
-            filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
-            filename = filename.replace("+", " ");
-        }
-        else if (agent.contains("Firefox"))
-        {
-            // 火狐浏览器
-            filename = new String(fileName.getBytes(), "ISO8859-1");
-        }
-        else if (agent.contains("Chrome"))
-        {
-            // google浏览器
-            filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
-        }
-        else
-        {
-            // 其它浏览器
-            filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
-        }
-        return filename;
-    }
 
     /**
      * 下载文件名重新编码

+ 0 - 102
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java

@@ -1,102 +0,0 @@
-package com.ruoyi.common.utils.file;
-
-import cn.hutool.core.util.StrUtil;
-import com.ruoyi.common.config.RuoYiConfig;
-import com.ruoyi.common.constant.Constants;
-import org.apache.poi.util.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.Arrays;
-
-/**
- * 图片处理工具类
- *
- * @author ruoyi
- */
-public class ImageUtils
-{
-    private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
-
-    public static byte[] getImage(String imagePath)
-    {
-        InputStream is = getFile(imagePath);
-        try
-        {
-            return IOUtils.toByteArray(is);
-        }
-        catch (Exception e)
-        {
-            log.error("图片加载异常 {}", e);
-            return null;
-        }
-        finally
-        {
-            IOUtils.closeQuietly(is);
-        }
-    }
-
-    public static InputStream getFile(String imagePath)
-    {
-        try
-        {
-            byte[] result = readFile(imagePath);
-            result = Arrays.copyOf(result, result.length);
-            return new ByteArrayInputStream(result);
-        }
-        catch (Exception e)
-        {
-            log.error("获取图片异常 {}", e);
-        }
-        return null;
-    }
-
-    /**
-     * 读取文件为字节数据
-     * 
-     * @param key 地址
-     * @return 字节数据
-     */
-    public static byte[] readFile(String url)
-    {
-        InputStream in = null;
-        ByteArrayOutputStream baos = null;
-        try
-        {
-            if (url.startsWith("http"))
-            {
-                // 网络地址
-                URL urlObj = new URL(url);
-                URLConnection urlConnection = urlObj.openConnection();
-                urlConnection.setConnectTimeout(30 * 1000);
-                urlConnection.setReadTimeout(60 * 1000);
-                urlConnection.setDoInput(true);
-                in = urlConnection.getInputStream();
-            }
-            else
-            {
-                // 本机地址
-                String localPath = RuoYiConfig.getProfile();
-                String downloadPath = localPath + StrUtil.subAfter(url, Constants.RESOURCE_PREFIX,false);
-                in = new FileInputStream(downloadPath);
-            }
-            return IOUtils.toByteArray(in);
-        }
-        catch (Exception e)
-        {
-            log.error("获取文件路径异常 {}", e);
-            return null;
-        }
-        finally
-        {
-            IOUtils.closeQuietly(in);
-            IOUtils.closeQuietly(baos);
-        }
-    }
-}

+ 0 - 59
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java

@@ -1,59 +0,0 @@
-package com.ruoyi.common.utils.file;
-
-/**
- * 媒体类型工具类
- * 
- * @author ruoyi
- */
-public class MimeTypeUtils
-{
-    public static final String IMAGE_PNG = "image/png";
-
-    public static final String IMAGE_JPG = "image/jpg";
-
-    public static final String IMAGE_JPEG = "image/jpeg";
-
-    public static final String IMAGE_BMP = "image/bmp";
-
-    public static final String IMAGE_GIF = "image/gif";
-    
-    public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };
-
-    public static final String[] FLASH_EXTENSION = { "swf", "flv" };
-
-    public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
-            "asf", "rm", "rmvb" };
-
-    public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };
-
-    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
-            // 图片
-            "bmp", "gif", "jpg", "jpeg", "png",
-            // word excel powerpoint
-            "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
-            // 压缩文件
-            "rar", "zip", "gz", "bz2",
-            // 视频格式
-            "mp4", "avi", "rmvb",
-            // pdf
-            "pdf" };
-
-    public static String getExtension(String prefix)
-    {
-        switch (prefix)
-        {
-            case IMAGE_PNG:
-                return "png";
-            case IMAGE_JPG:
-                return "jpg";
-            case IMAGE_JPEG:
-                return "jpeg";
-            case IMAGE_BMP:
-                return "bmp";
-            case IMAGE_GIF:
-                return "gif";
-            default:
-                return "";
-        }
-    }
-}

+ 2 - 2
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java

@@ -1,12 +1,12 @@
 package com.ruoyi.common.utils.ip;
 
 import cn.hutool.core.net.NetUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HtmlUtil;
 import cn.hutool.http.HttpUtil;
 import com.ruoyi.common.config.RuoYiConfig;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.utils.JsonUtils;
+import com.ruoyi.common.utils.StringUtils;
 import lombok.extern.slf4j.Slf4j;
 
 import java.util.Map;
@@ -38,7 +38,7 @@ public class AddressUtils {
 					.body("ip=" + ip + "&json=true", Constants.GBK)
 					.execute()
 					.body();
-				if (StrUtil.isEmpty(rspStr)) {
+				if (StringUtils.isEmpty(rspStr)) {
 					log.error("获取地理位置异常 {}", ip);
 					return UNKNOWN;
 				}

+ 138 - 1060
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java

@@ -1,1072 +1,150 @@
 package com.ruoyi.common.utils.poi;
 
-import cn.hutool.core.convert.Convert;
-import cn.hutool.core.lang.Validator;
-import cn.hutool.core.util.StrUtil;
-import com.ruoyi.common.annotation.Excel;
-import com.ruoyi.common.annotation.Excel.ColumnType;
-import com.ruoyi.common.annotation.Excel.Type;
-import com.ruoyi.common.annotation.Excels;
-import com.ruoyi.common.config.RuoYiConfig;
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.exception.CustomException;
-import com.ruoyi.common.utils.DateUtils;
+import cn.hutool.core.util.IdUtil;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
 import com.ruoyi.common.utils.DictUtils;
-import com.ruoyi.common.utils.file.FileTypeUtils;
-import com.ruoyi.common.utils.file.ImageUtils;
-import com.ruoyi.common.utils.reflect.ReflectUtils;
-import org.apache.poi.ss.usermodel.*;
-import org.apache.poi.ss.util.CellRangeAddressList;
-import org.apache.poi.xssf.streaming.SXSSFWorkbook;
-import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
-import org.apache.poi.xssf.usermodel.XSSFDataValidation;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.file.FileUtils;
 
-import java.io.*;
-import java.lang.reflect.Field;
-import java.math.BigDecimal;
-import java.text.DecimalFormat;
-import java.util.*;
-import java.util.stream.Collectors;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
 
 /**
  * Excel相关处理
  *
  * @author ruoyi
  */
-public class ExcelUtil<T>
-{
-    private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
+public class ExcelUtil {
+
+	/**
+	 * 对excel表单默认第一个索引名转换成list(EasyExcel)
+	 *
+	 * @param is 输入流
+	 * @return 转换后集合
+	 */
+	public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
+		return EasyExcel.read(is).autoCloseStream(false).sheet().doReadSync();
+	}
+
+	/**
+	 * 对list数据源将其里面的数据导入到excel表单(EasyExcel)
+	 *
+	 * @param list      导出数据集合
+	 * @param sheetName 工作表的名称
+	 * @return 结果
+	 */
+	public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
+		try {
+			String filename = encodingFilename(sheetName);
+			response.reset();
+			response.addHeader("Access-Control-Allow-Origin", "*");
+			response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
+			FileUtils.setAttachmentResponseHeader(response, URLEncoder.encode(filename, StandardCharsets.UTF_8.toString()));
+			response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
+			ServletOutputStream os = response.getOutputStream();
+			EasyExcel.write(os, clazz)
+				.autoCloseStream(false)
+				// 自动适配
+				.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+				.sheet(sheetName).doWrite(list);
+		} catch (IOException e) {
+			throw new RuntimeException("导出Excel异常");
+		}
+	}
+
+	/**
+	 * 解析导出值 0=男,1=女,2=未知
+	 *
+	 * @param propertyValue 参数值
+	 * @param converterExp  翻译注解
+	 * @param separator     分隔符
+	 * @return 解析后值
+	 */
+	public static String convertByExp(String propertyValue, String converterExp, String separator) {
+		StringBuilder propertyString = new StringBuilder();
+		String[] convertSource = converterExp.split(",");
+		for (String item : convertSource) {
+			String[] itemArray = item.split("=");
+			if (StringUtils.containsAny(separator, propertyValue)) {
+				for (String value : propertyValue.split(separator)) {
+					if (itemArray[0].equals(value)) {
+						propertyString.append(itemArray[1] + separator);
+						break;
+					}
+				}
+			} else {
+				if (itemArray[0].equals(propertyValue)) {
+					return itemArray[1];
+				}
+			}
+		}
+		return StringUtils.stripEnd(propertyString.toString(), separator);
+	}
+
+	/**
+	 * 反向解析值 男=0,女=1,未知=2
+	 *
+	 * @param propertyValue 参数值
+	 * @param converterExp  翻译注解
+	 * @param separator     分隔符
+	 * @return 解析后值
+	 */
+	public static String reverseByExp(String propertyValue, String converterExp, String separator) {
+		StringBuilder propertyString = new StringBuilder();
+		String[] convertSource = converterExp.split(",");
+		for (String item : convertSource) {
+			String[] itemArray = item.split("=");
+			if (StringUtils.containsAny(separator, propertyValue)) {
+				for (String value : propertyValue.split(separator)) {
+					if (itemArray[1].equals(value)) {
+						propertyString.append(itemArray[0] + separator);
+						break;
+					}
+				}
+			} else {
+				if (itemArray[1].equals(propertyValue)) {
+					return itemArray[0];
+				}
+			}
+		}
+		return StringUtils.stripEnd(propertyString.toString(), separator);
+	}
+
+	/**
+	 * 解析字典值
+	 *
+	 * @param dictValue 字典值
+	 * @param dictType  字典类型
+	 * @param separator 分隔符
+	 * @return 字典标签
+	 */
+	public static String convertDictByExp(String dictValue, String dictType, String separator) {
+		return DictUtils.getDictLabel(dictType, dictValue, separator);
+	}
+
+	/**
+	 * 反向解析值字典值
+	 *
+	 * @param dictLabel 字典标签
+	 * @param dictType  字典类型
+	 * @param separator 分隔符
+	 * @return 字典值
+	 */
+	public static String reverseDictByExp(String dictLabel, String dictType, String separator) {
+		return DictUtils.getDictValue(dictType, dictLabel, separator);
+	}
+
+	/**
+	 * 编码文件名
+	 */
+	public static String encodingFilename(String filename) {
+		return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
+	}
 
-    /**
-     * Excel sheet最大行数,默认65536
-     */
-    public static final int sheetSize = 65536;
-
-    /**
-     * 工作表名称
-     */
-    private String sheetName;
-
-    /**
-     * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
-     */
-    private Type type;
-
-    /**
-     * 工作薄对象
-     */
-    private Workbook wb;
-
-    /**
-     * 工作表对象
-     */
-    private Sheet sheet;
-
-    /**
-     * 样式列表
-     */
-    private Map<String, CellStyle> styles;
-
-    /**
-     * 导入导出数据列表
-     */
-    private List<T> list;
-
-    /**
-     * 注解列表
-     */
-    private List<Object[]> fields;
-
-    /**
-     * 最大高度
-     */
-    private short maxHeight;
-
-    /**
-     * 统计列表
-     */
-    private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
-
-    /**
-     * 数字格式
-     */
-    private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
-
-    /**
-     * 实体对象
-     */
-    public Class<T> clazz;
-
-    public ExcelUtil(Class<T> clazz)
-    {
-        this.clazz = clazz;
-    }
-
-    public void init(List<T> list, String sheetName, Type type)
-    {
-        if (list == null)
-        {
-            list = new ArrayList<T>();
-        }
-        this.list = list;
-        this.sheetName = sheetName;
-        this.type = type;
-        createExcelField();
-        createWorkbook();
-    }
-
-    /**
-     * 对excel表单默认第一个索引名转换成list
-     *
-     * @param is 输入流
-     * @return 转换后集合
-     */
-    public List<T> importExcel(InputStream is) throws Exception
-    {
-        return importExcel(StrUtil.EMPTY, is);
-    }
-
-    /**
-     * 对excel表单指定表格索引名转换成list
-     *
-     * @param sheetName 表格索引名
-     * @param is 输入流
-     * @return 转换后集合
-     */
-    public List<T> importExcel(String sheetName, InputStream is) throws Exception
-    {
-        this.type = Type.IMPORT;
-        this.wb = WorkbookFactory.create(is);
-        List<T> list = new ArrayList<T>();
-        Sheet sheet = null;
-        if (Validator.isNotEmpty(sheetName))
-        {
-            // 如果指定sheet名,则取指定sheet中的内容.
-            sheet = wb.getSheet(sheetName);
-        }
-        else
-        {
-            // 如果传入的sheet名不存在则默认指向第1个sheet.
-            sheet = wb.getSheetAt(0);
-        }
-
-        if (sheet == null)
-        {
-            throw new IOException("文件sheet不存在");
-        }
-
-        int rows = sheet.getPhysicalNumberOfRows();
-
-        if (rows > 0)
-        {
-            // 定义一个map用于存放excel列的序号和field.
-            Map<String, Integer> cellMap = new HashMap<String, Integer>();
-            // 获取表头
-            Row heard = sheet.getRow(0);
-            for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
-            {
-                Cell cell = heard.getCell(i);
-                if (Validator.isNotNull(cell))
-                {
-                    String value = this.getCellValue(heard, i).toString();
-                    cellMap.put(value, i);
-                }
-                else
-                {
-                    cellMap.put(null, i);
-                }
-            }
-            // 有数据时才处理 得到类的所有field.
-            Field[] allFields = clazz.getDeclaredFields();
-            // 定义一个map用于存放列的序号和field.
-            Map<Integer, Field> fieldsMap = new HashMap<Integer, Field>();
-            for (int col = 0; col < allFields.length; col++)
-            {
-                Field field = allFields[col];
-                Excel attr = field.getAnnotation(Excel.class);
-                if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
-                {
-                    // 设置类的私有字段属性可访问.
-                    field.setAccessible(true);
-                    Integer column = cellMap.get(attr.name());
-                    if (column != null)
-                    {
-                        fieldsMap.put(column, field);
-                    }
-                }
-            }
-            for (int i = 1; i < rows; i++)
-            {
-                // 从第2行开始取数据,默认第一行是表头.
-                Row row = sheet.getRow(i);
-                if(row == null)
-                {
-                    continue;
-                }
-                T entity = null;
-                for (Map.Entry<Integer, Field> entry : fieldsMap.entrySet())
-                {
-                    Object val = this.getCellValue(row, entry.getKey());
-
-                    // 如果不存在实例则新建.
-                    entity = (entity == null ? clazz.newInstance() : entity);
-                    // 从map中得到对应列的field.
-                    Field field = fieldsMap.get(entry.getKey());
-                    // 取得类型,并根据对象类型设置值.
-                    Class<?> fieldType = field.getType();
-                    if (String.class == fieldType)
-                    {
-                        String s = Convert.toStr(val);
-                        if (StrUtil.endWith(s, ".0"))
-                        {
-                            val = StrUtil.subBefore(s, ".0",false);
-                        }
-                        else
-                        {
-                            String dateFormat = field.getAnnotation(Excel.class).dateFormat();
-                            if (Validator.isNotEmpty(dateFormat))
-                            {
-                                val = DateUtils.parseDateToStr(dateFormat, (Date) val);
-                            }
-                            else
-                            {
-                                val = Convert.toStr(val);
-                            }
-                        }
-                    }
-                    else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && Validator.isNumber(Convert.toStr(val)))
-                    {
-                        val = Convert.toInt(val);
-                    }
-                    else if (Long.TYPE == fieldType || Long.class == fieldType)
-                    {
-                        val = Convert.toLong(val);
-                    }
-                    else if (Double.TYPE == fieldType || Double.class == fieldType)
-                    {
-                        val = Convert.toDouble(val);
-                    }
-                    else if (Float.TYPE == fieldType || Float.class == fieldType)
-                    {
-                        val = Convert.toFloat(val);
-                    }
-                    else if (BigDecimal.class == fieldType)
-                    {
-                        val = Convert.toBigDecimal(val);
-                    }
-                    else if (Date.class == fieldType)
-                    {
-                        if (val instanceof String)
-                        {
-                            val = DateUtils.parseDate(val);
-                        }
-                        else if (val instanceof Double)
-                        {
-                            val = DateUtil.getJavaDate((Double) val);
-                        }
-                    }
-                    else if (Boolean.TYPE == fieldType || Boolean.class == fieldType)
-                    {
-                        val = Convert.toBool(val, false);
-                    }
-                    if (Validator.isNotNull(fieldType))
-                    {
-                        Excel attr = field.getAnnotation(Excel.class);
-                        String propertyName = field.getName();
-                        if (Validator.isNotEmpty(attr.targetAttr()))
-                        {
-                            propertyName = field.getName() + "." + attr.targetAttr();
-                        }
-                        else if (Validator.isNotEmpty(attr.readConverterExp()))
-                        {
-                            val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
-                        }
-                        else if (Validator.isNotEmpty(attr.dictType()))
-                        {
-                            val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
-                        }
-                        ReflectUtils.invokeSetter(entity, propertyName, val);
-                    }
-                }
-                list.add(entity);
-            }
-        }
-        return list;
-    }
-
-    /**
-     * 对list数据源将其里面的数据导入到excel表单
-     *
-     * @param list 导出数据集合
-     * @param sheetName 工作表的名称
-     * @return 结果
-     */
-    public AjaxResult exportExcel(List<T> list, String sheetName)
-    {
-        this.init(list, sheetName, Type.EXPORT);
-        return exportExcel();
-    }
-
-    /**
-     * 对list数据源将其里面的数据导入到excel表单
-     *
-     * @param sheetName 工作表的名称
-     * @return 结果
-     */
-    public AjaxResult importTemplateExcel(String sheetName)
-    {
-        this.init(null, sheetName, Type.IMPORT);
-        return exportExcel();
-    }
-
-    /**
-     * 对list数据源将其里面的数据导入到excel表单
-     *
-     * @return 结果
-     */
-    public AjaxResult exportExcel()
-    {
-        OutputStream out = null;
-        try
-        {
-            // 取出一共有多少个sheet.
-            double sheetNo = Math.ceil(list.size() / sheetSize);
-            for (int index = 0; index <= sheetNo; index++)
-            {
-                createSheet(sheetNo, index);
-
-                // 产生一行
-                Row row = sheet.createRow(0);
-                int column = 0;
-                // 写入各个字段的列头名称
-                for (Object[] os : fields)
-                {
-                    Excel excel = (Excel) os[1];
-                    this.createCell(excel, row, column++);
-                }
-                if (Type.EXPORT.equals(type))
-                {
-                    fillExcelData(index, row);
-                    addStatisticsRow();
-                }
-            }
-            String filename = encodingFilename(sheetName);
-            out = new FileOutputStream(getAbsoluteFile(filename));
-            wb.write(out);
-            return AjaxResult.success(filename);
-        }
-        catch (Exception e)
-        {
-            log.error("导出Excel异常{}", e.getMessage());
-            throw new CustomException("导出Excel失败,请联系网站管理员!");
-        }
-        finally
-        {
-            if (wb != null)
-            {
-                try
-                {
-                    wb.close();
-                }
-                catch (IOException e1)
-                {
-                    e1.printStackTrace();
-                }
-            }
-            if (out != null)
-            {
-                try
-                {
-                    out.close();
-                }
-                catch (IOException e1)
-                {
-                    e1.printStackTrace();
-                }
-            }
-        }
-    }
-
-    /**
-     * 填充excel数据
-     *
-     * @param index 序号
-     * @param row 单元格行
-     */
-    public void fillExcelData(int index, Row row)
-    {
-        int startNo = index * sheetSize;
-        int endNo = Math.min(startNo + sheetSize, list.size());
-        for (int i = startNo; i < endNo; i++)
-        {
-            row = sheet.createRow(i + 1 - startNo);
-            // 得到导出对象.
-            T vo = (T) list.get(i);
-            int column = 0;
-            for (Object[] os : fields)
-            {
-                Field field = (Field) os[0];
-                Excel excel = (Excel) os[1];
-                // 设置实体类私有属性可访问
-                field.setAccessible(true);
-                this.addCell(excel, row, vo, field, column++);
-            }
-        }
-    }
-
-    /**
-     * 创建表格样式
-     *
-     * @param wb 工作薄对象
-     * @return 样式列表
-     */
-    private Map<String, CellStyle> createStyles(Workbook wb)
-    {
-        // 写入各条记录,每条记录对应excel表中的一行
-        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
-        CellStyle style = wb.createCellStyle();
-        style.setAlignment(HorizontalAlignment.CENTER);
-        style.setVerticalAlignment(VerticalAlignment.CENTER);
-        style.setBorderRight(BorderStyle.THIN);
-        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
-        style.setBorderLeft(BorderStyle.THIN);
-        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
-        style.setBorderTop(BorderStyle.THIN);
-        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
-        style.setBorderBottom(BorderStyle.THIN);
-        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
-        Font dataFont = wb.createFont();
-        dataFont.setFontName("Arial");
-        dataFont.setFontHeightInPoints((short) 10);
-        style.setFont(dataFont);
-        styles.put("data", style);
-
-        style = wb.createCellStyle();
-        style.cloneStyleFrom(styles.get("data"));
-        style.setAlignment(HorizontalAlignment.CENTER);
-        style.setVerticalAlignment(VerticalAlignment.CENTER);
-        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
-        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
-        Font headerFont = wb.createFont();
-        headerFont.setFontName("Arial");
-        headerFont.setFontHeightInPoints((short) 10);
-        headerFont.setBold(true);
-        headerFont.setColor(IndexedColors.WHITE.getIndex());
-        style.setFont(headerFont);
-        styles.put("header", style);
-
-        style = wb.createCellStyle();
-        style.setAlignment(HorizontalAlignment.CENTER);
-        style.setVerticalAlignment(VerticalAlignment.CENTER);
-        Font totalFont = wb.createFont();
-        totalFont.setFontName("Arial");
-        totalFont.setFontHeightInPoints((short) 10);
-        style.setFont(totalFont);
-        styles.put("total", style);
-
-        style = wb.createCellStyle();
-        style.cloneStyleFrom(styles.get("data"));
-        style.setAlignment(HorizontalAlignment.LEFT);
-        styles.put("data1", style);
-
-        style = wb.createCellStyle();
-        style.cloneStyleFrom(styles.get("data"));
-        style.setAlignment(HorizontalAlignment.CENTER);
-        styles.put("data2", style);
-
-        style = wb.createCellStyle();
-        style.cloneStyleFrom(styles.get("data"));
-        style.setAlignment(HorizontalAlignment.RIGHT);
-        styles.put("data3", style);
-
-        return styles;
-    }
-
-    /**
-     * 创建单元格
-     */
-    public Cell createCell(Excel attr, Row row, int column)
-    {
-        // 创建列
-        Cell cell = row.createCell(column);
-        // 写入列信息
-        cell.setCellValue(attr.name());
-        setDataValidation(attr, row, column);
-        cell.setCellStyle(styles.get("header"));
-        return cell;
-    }
-
-    /**
-     * 设置单元格信息
-     *
-     * @param value 单元格值
-     * @param attr 注解相关
-     * @param cell 单元格信息
-     */
-    public void setCellVo(Object value, Excel attr, Cell cell)
-    {
-        if (ColumnType.STRING == attr.cellType())
-        {
-            cell.setCellValue(Validator.isNull(value) ? attr.defaultValue() : value + attr.suffix());
-        }
-        else if (ColumnType.NUMERIC == attr.cellType())
-        {
-            if (Validator.isNotNull(value))
-            {
-                cell.setCellValue(StrUtil.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));
-            }
-        }
-        else if (ColumnType.IMAGE == attr.cellType())
-        {
-            ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1),
-                    cell.getRow().getRowNum() + 1);
-            String imagePath = Convert.toStr(value);
-            if (Validator.isNotEmpty(imagePath))
-            {
-                byte[] data = ImageUtils.getImage(imagePath);
-                getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
-                        cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
-            }
-        }
-    }
-
-    /**
-     * 获取画布
-     */
-    public static Drawing<?> getDrawingPatriarch(Sheet sheet)
-    {
-        if (sheet.getDrawingPatriarch() == null)
-        {
-            sheet.createDrawingPatriarch();
-        }
-        return sheet.getDrawingPatriarch();
-    }
-
-    /**
-     * 获取图片类型,设置图片插入类型
-     */
-    public int getImageType(byte[] value)
-    {
-        String type = FileTypeUtils.getFileExtendName(value);
-        if ("JPG".equalsIgnoreCase(type))
-        {
-            return Workbook.PICTURE_TYPE_JPEG;
-        }
-        else if ("PNG".equalsIgnoreCase(type))
-        {
-            return Workbook.PICTURE_TYPE_PNG;
-        }
-        return Workbook.PICTURE_TYPE_JPEG;
-    }
-
-    /**
-     * 创建表格样式
-     */
-    public void setDataValidation(Excel attr, Row row, int column)
-    {
-        if (attr.name().indexOf("注:") >= 0)
-        {
-            sheet.setColumnWidth(column, 6000);
-        }
-        else
-        {
-            // 设置列宽
-            sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
-        }
-        // 如果设置了提示信息则鼠标放上去提示.
-        if (Validator.isNotEmpty(attr.prompt()))
-        {
-            // 这里默认设了2-101列提示.
-            setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column);
-        }
-        // 如果设置了combo属性则本列只能选择不能输入
-        if (attr.combo().length > 0)
-        {
-            // 这里默认设了2-101列只能选择不能输入.
-            setXSSFValidation(sheet, attr.combo(), 1, 100, column, column);
-        }
-    }
-
-    /**
-     * 添加单元格
-     */
-    public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
-    {
-        Cell cell = null;
-        try
-        {
-            // 设置行高
-            row.setHeight(maxHeight);
-            // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
-            if (attr.isExport())
-            {
-                // 创建cell
-                cell = row.createCell(column);
-                int align = attr.align().value();
-                cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : "")));
-
-                // 用于读取对象中的属性
-                Object value = getTargetValue(vo, field, attr);
-                String dateFormat = attr.dateFormat();
-                String readConverterExp = attr.readConverterExp();
-                String separator = attr.separator();
-                String dictType = attr.dictType();
-                if (Validator.isNotEmpty(dateFormat) && Validator.isNotNull(value))
-                {
-                    cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));
-                }
-                else if (Validator.isNotEmpty(readConverterExp) && Validator.isNotNull(value))
-                {
-                    cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));
-                }
-                else if (Validator.isNotEmpty(dictType) && Validator.isNotNull(value))
-                {
-                    cell.setCellValue(convertDictByExp(Convert.toStr(value), dictType, separator));
-                }
-                else if (value instanceof BigDecimal && -1 != attr.scale())
-                {
-                    cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString());
-                }
-                else
-                {
-                    // 设置列类型
-                    setCellVo(value, attr, cell);
-                }
-                addStatisticsData(column, Convert.toStr(value), attr);
-            }
-        }
-        catch (Exception e)
-        {
-            log.error("导出Excel失败{}", e);
-        }
-        return cell;
-    }
-
-    /**
-     * 设置 POI XSSFSheet 单元格提示
-     *
-     * @param sheet 表单
-     * @param promptTitle 提示标题
-     * @param promptContent 提示内容
-     * @param firstRow 开始行
-     * @param endRow 结束行
-     * @param firstCol 开始列
-     * @param endCol 结束列
-     */
-    public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow,
-            int firstCol, int endCol)
-    {
-        DataValidationHelper helper = sheet.getDataValidationHelper();
-        DataValidationConstraint constraint = helper.createCustomConstraint("DD1");
-        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
-        DataValidation dataValidation = helper.createValidation(constraint, regions);
-        dataValidation.createPromptBox(promptTitle, promptContent);
-        dataValidation.setShowPromptBox(true);
-        sheet.addValidationData(dataValidation);
-    }
-
-    /**
-     * 设置某些列的值只能输入预制的数据,显示下拉框.
-     *
-     * @param sheet 要设置的sheet.
-     * @param textlist 下拉框显示的内容
-     * @param firstRow 开始行
-     * @param endRow 结束行
-     * @param firstCol 开始列
-     * @param endCol 结束列
-     * @return 设置好的sheet.
-     */
-    public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol)
-    {
-        DataValidationHelper helper = sheet.getDataValidationHelper();
-        // 加载下拉列表内容
-        DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist);
-        // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
-        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
-        // 数据有效性对象
-        DataValidation dataValidation = helper.createValidation(constraint, regions);
-        // 处理Excel兼容性问题
-        if (dataValidation instanceof XSSFDataValidation)
-        {
-            dataValidation.setSuppressDropDownArrow(true);
-            dataValidation.setShowErrorBox(true);
-        }
-        else
-        {
-            dataValidation.setSuppressDropDownArrow(false);
-        }
-
-        sheet.addValidationData(dataValidation);
-    }
-
-    /**
-     * 解析导出值 0=男,1=女,2=未知
-     *
-     * @param propertyValue 参数值
-     * @param converterExp 翻译注解
-     * @param separator 分隔符
-     * @return 解析后值
-     */
-    public static String convertByExp(String propertyValue, String converterExp, String separator)
-    {
-        StringBuilder propertyString = new StringBuilder();
-        String[] convertSource = converterExp.split(",");
-        for (String item : convertSource)
-        {
-            String[] itemArray = item.split("=");
-            if (StrUtil.containsAny(propertyValue, separator))
-            {
-                for (String value : propertyValue.split(separator))
-                {
-                    if (itemArray[0].equals(value))
-                    {
-                        propertyString.append(itemArray[1] + separator);
-                        break;
-                    }
-                }
-            }
-            else
-            {
-                if (itemArray[0].equals(propertyValue))
-                {
-                    return itemArray[1];
-                }
-            }
-        }
-        return StrUtil.strip(propertyString.toString(), null,separator);
-    }
-
-    /**
-     * 反向解析值 男=0,女=1,未知=2
-     *
-     * @param propertyValue 参数值
-     * @param converterExp 翻译注解
-     * @param separator 分隔符
-     * @return 解析后值
-     */
-    public static String reverseByExp(String propertyValue, String converterExp, String separator)
-    {
-        StringBuilder propertyString = new StringBuilder();
-        String[] convertSource = converterExp.split(",");
-        for (String item : convertSource)
-        {
-            String[] itemArray = item.split("=");
-            if (StrUtil.containsAny(propertyValue, separator))
-            {
-                for (String value : propertyValue.split(separator))
-                {
-                    if (itemArray[1].equals(value))
-                    {
-                        propertyString.append(itemArray[0] + separator);
-                        break;
-                    }
-                }
-            }
-            else
-            {
-                if (itemArray[1].equals(propertyValue))
-                {
-                    return itemArray[0];
-                }
-            }
-        }
-        return StrUtil.strip(propertyString.toString(), null,separator);
-    }
-
-    /**
-     * 解析字典值
-     *
-     * @param dictValue 字典值
-     * @param dictType 字典类型
-     * @param separator 分隔符
-     * @return 字典标签
-     */
-    public static String convertDictByExp(String dictValue, String dictType, String separator)
-    {
-        return DictUtils.getDictLabel(dictType, dictValue, separator);
-    }
-
-    /**
-     * 反向解析值字典值
-     *
-     * @param dictLabel 字典标签
-     * @param dictType 字典类型
-     * @param separator 分隔符
-     * @return 字典值
-     */
-    public static String reverseDictByExp(String dictLabel, String dictType, String separator)
-    {
-        return DictUtils.getDictValue(dictType, dictLabel, separator);
-    }
-
-    /**
-     * 合计统计信息
-     */
-    private void addStatisticsData(Integer index, String text, Excel entity)
-    {
-        if (entity != null && entity.isStatistics())
-        {
-            Double temp = 0D;
-            if (!statistics.containsKey(index))
-            {
-                statistics.put(index, temp);
-            }
-            try
-            {
-                temp = Double.valueOf(text);
-            }
-            catch (NumberFormatException e)
-            {
-            }
-            statistics.put(index, statistics.get(index) + temp);
-        }
-    }
-
-    /**
-     * 创建统计行
-     */
-    public void addStatisticsRow()
-    {
-        if (statistics.size() > 0)
-        {
-            Cell cell = null;
-            Row row = sheet.createRow(sheet.getLastRowNum() + 1);
-            Set<Integer> keys = statistics.keySet();
-            cell = row.createCell(0);
-            cell.setCellStyle(styles.get("total"));
-            cell.setCellValue("合计");
-
-            for (Integer key : keys)
-            {
-                cell = row.createCell(key);
-                cell.setCellStyle(styles.get("total"));
-                cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
-            }
-            statistics.clear();
-        }
-    }
-
-    /**
-     * 编码文件名
-     */
-    public String encodingFilename(String filename)
-    {
-        filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx";
-        return filename;
-    }
-
-    /**
-     * 获取下载路径
-     *
-     * @param filename 文件名称
-     */
-    public String getAbsoluteFile(String filename)
-    {
-        String downloadPath = RuoYiConfig.getDownloadPath() + filename;
-        File desc = new File(downloadPath);
-        if (!desc.getParentFile().exists())
-        {
-            desc.getParentFile().mkdirs();
-        }
-        return downloadPath;
-    }
-
-    /**
-     * 获取bean中的属性值
-     *
-     * @param vo 实体对象
-     * @param field 字段
-     * @param excel 注解
-     * @return 最终的属性值
-     * @throws Exception
-     */
-    private Object getTargetValue(T vo, Field field, Excel excel) throws Exception
-    {
-        Object o = field.get(vo);
-        if (Validator.isNotEmpty(excel.targetAttr()))
-        {
-            String target = excel.targetAttr();
-            if (target.contains("."))
-            {
-                String[] targets = target.split("[.]");
-                for (String name : targets)
-                {
-                    o = getValue(o, name);
-                }
-            }
-            else
-            {
-                o = getValue(o, target);
-            }
-        }
-        return o;
-    }
-
-    /**
-     * 以类的属性的get方法方法形式获取值
-     *
-     * @param o
-     * @param name
-     * @return value
-     * @throws Exception
-     */
-    private Object getValue(Object o, String name) throws Exception
-    {
-        if (Validator.isNotNull(o) && Validator.isNotEmpty(name))
-        {
-            Class<?> clazz = o.getClass();
-            Field field = clazz.getDeclaredField(name);
-            field.setAccessible(true);
-            o = field.get(o);
-        }
-        return o;
-    }
-
-    /**
-     * 得到所有定义字段
-     */
-    private void createExcelField()
-    {
-        this.fields = new ArrayList<Object[]>();
-        List<Field> tempFields = new ArrayList<>();
-        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
-        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
-        for (Field field : tempFields)
-        {
-            // 单注解
-            if (field.isAnnotationPresent(Excel.class))
-            {
-                putToField(field, field.getAnnotation(Excel.class));
-            }
-
-            // 多注解
-            if (field.isAnnotationPresent(Excels.class))
-            {
-                Excels attrs = field.getAnnotation(Excels.class);
-                Excel[] excels = attrs.value();
-                for (Excel excel : excels)
-                {
-                    putToField(field, excel);
-                }
-            }
-        }
-        this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
-        this.maxHeight = getRowHeight();
-    }
-
-    /**
-     * 根据注解获取最大行高
-     */
-    public short getRowHeight()
-    {
-        double maxHeight = 0;
-        for (Object[] os : this.fields)
-        {
-            Excel excel = (Excel) os[1];
-            maxHeight = maxHeight > excel.height() ? maxHeight : excel.height();
-        }
-        return (short) (maxHeight * 20);
-    }
-
-    /**
-     * 放到字段集合中
-     */
-    private void putToField(Field field, Excel attr)
-    {
-        if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
-        {
-            this.fields.add(new Object[] { field, attr });
-        }
-    }
-
-    /**
-     * 创建一个工作簿
-     */
-    public void createWorkbook()
-    {
-        this.wb = new SXSSFWorkbook(500);
-    }
-
-    /**
-     * 创建工作表
-     *
-     * @param sheetNo sheet数量
-     * @param index 序号
-     */
-    public void createSheet(double sheetNo, int index)
-    {
-        this.sheet = wb.createSheet();
-        this.styles = createStyles(wb);
-        // 设置工作表的名称.
-        if (sheetNo == 0)
-        {
-            wb.setSheetName(index, sheetName);
-        }
-        else
-        {
-            wb.setSheetName(index, sheetName + index);
-        }
-    }
-
-    /**
-     * 获取单元格值
-     *
-     * @param row 获取的行
-     * @param column 获取单元格列号
-     * @return 单元格值
-     */
-    public Object getCellValue(Row row, int column)
-    {
-        if (row == null)
-        {
-            return row;
-        }
-        Object val = "";
-        try
-        {
-            Cell cell = row.getCell(column);
-            if (Validator.isNotNull(cell))
-            {
-                if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA)
-                {
-                    val = cell.getNumericCellValue();
-                    if (DateUtil.isCellDateFormatted(cell))
-                    {
-                        val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
-                    }
-                    else
-                    {
-                        if ((Double) val % 1 != 0)
-                        {
-                            val = new BigDecimal(val.toString());
-                        }
-                        else
-                        {
-                            val = new DecimalFormat("0").format(val);
-                        }
-                    }
-                }
-                else if (cell.getCellType() == CellType.STRING)
-                {
-                    val = cell.getStringCellValue();
-                }
-                else if (cell.getCellType() == CellType.BOOLEAN)
-                {
-                    val = cell.getBooleanCellValue();
-                }
-                else if (cell.getCellType() == CellType.ERROR)
-                {
-                    val = cell.getErrorCellValue();
-                }
-
-            }
-        }
-        catch (Exception e)
-        {
-            return val;
-        }
-        return val;
-    }
 }

+ 8 - 9
ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java

@@ -1,10 +1,9 @@
 package com.ruoyi.common.utils.reflect;
 
 import cn.hutool.core.util.ReflectUtil;
-import cn.hutool.core.util.StrUtil;
+import com.ruoyi.common.utils.StringUtils;
 
 import java.lang.reflect.Method;
-import java.util.List;
 
 /**
  * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
@@ -25,8 +24,8 @@ public class ReflectUtils extends ReflectUtil {
 	@SuppressWarnings("unchecked")
 	public static <E> E invokeGetter(Object obj, String propertyName) {
 		Object object = obj;
-		for (String name : StrUtil.split(propertyName, ".")) {
-			String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(name);
+		for (String name : StringUtils.split(propertyName, ".")) {
+			String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
 			object = invoke(object, getterMethodName);
 		}
 		return (E) object;
@@ -38,13 +37,13 @@ public class ReflectUtils extends ReflectUtil {
 	 */
 	public static <E> void invokeSetter(Object obj, String propertyName, E value) {
 		Object object = obj;
-		List<String> names = StrUtil.split(propertyName, ".");
-		for (int i = 0; i < names.size(); i++) {
-			if (i < names.size() - 1) {
-				String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(names.get(i));
+		String[] names = StringUtils.split(propertyName, ".");
+		for (int i = 0; i < names.length; i++) {
+			if (i < names.length - 1) {
+				String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
 				object = invoke(object, getterMethodName);
 			} else {
-				String setterMethodName = SETTER_PREFIX + StrUtil.upperFirst(names.get(i));
+				String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
 				Method method = getMethodByName(object.getClass(), setterMethodName);
 				invoke(object, method, value);
 			}

+ 5 - 5
ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java

@@ -1,11 +1,11 @@
 package com.ruoyi.common.utils.sql;
 
-import cn.hutool.core.lang.Validator;
-import com.ruoyi.common.exception.BaseException;
+import com.ruoyi.common.exception.UtilException;
+import com.ruoyi.common.utils.StringUtils;
 
 /**
  * sql操作工具类
- * 
+ *
  * @author ruoyi
  */
 public class SqlUtil
@@ -20,9 +20,9 @@ public class SqlUtil
      */
     public static String escapeOrderBySql(String value)
     {
-        if (Validator.isNotEmpty(value) && !isValidOrderBySql(value))
+        if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value))
         {
-            throw new BaseException("参数不符合规范,不能进行查询");
+            throw new UtilException("参数不符合规范,不能进行查询");
         }
         return value;
     }

+ 1 - 1
ruoyi-demo/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>2.6.0</version>
+        <version>3.0.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 9 - 2
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java

@@ -5,6 +5,8 @@ import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.demo.domain.TestDemo;
 import com.ruoyi.demo.service.ITestDemoService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -21,6 +23,7 @@ import java.util.List;
  * @author Lion Li
  * @date 2021-05-30
  */
+@Api(value = "测试批量方法", tags = {"测试批量方法"})
 @RequiredArgsConstructor(onConstructor_ = @Autowired)
 @RestController
 @RequestMapping("/demo/batch")
@@ -31,7 +34,9 @@ public class TestBatchController extends BaseController {
     /**
      * 新增批量方法 ( 全量覆盖填充 )
      */
+	@ApiOperation(value = "新增批量方法")
     @PostMapping()
+//	@DataSource(DataSourceType.SLAVE)
     public AjaxResult<Void> add() {
 		List<TestDemo> list = new ArrayList<>();
 		for (int i = 0; i < 1000; i++) {
@@ -41,10 +46,12 @@ public class TestBatchController extends BaseController {
     }
 
     /**
-     * 修改批量方法
+     * 删除批量方法
      */
+	@ApiOperation(value = "删除批量方法")
     @DeleteMapping()
-    public AjaxResult<Void> edit() {
+//	@DataSource(DataSourceType.SLAVE)
+    public AjaxResult<Void> remove() {
         return toAjax(iTestDemoService.remove(new LambdaQueryWrapper<TestDemo>()
 			.eq(TestDemo::getOrderNum, -1L)) ? 1 : 0);
     }

+ 5 - 4
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java

@@ -20,10 +20,12 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * 测试单表Controller
@@ -67,10 +69,9 @@ public class TestDemoController extends BaseController {
     @PreAuthorize("@ss.hasPermi('demo:demo:export')")
     @Log(title = "测试单表", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
-    public AjaxResult<TestDemoVo> export(@Validated TestDemoBo bo) {
+    public void export(@Validated TestDemoBo bo, HttpServletResponse response) {
         List<TestDemoVo> list = iTestDemoService.queryList(bo);
-        ExcelUtil<TestDemoVo> util = new ExcelUtil<TestDemoVo>(TestDemoVo.class);
-        return util.exportExcel(list, "测试单表");
+		ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, response);
     }
 
     /**
@@ -90,7 +91,7 @@ public class TestDemoController extends BaseController {
     @ApiOperation("新增测试单表")
     @PreAuthorize("@ss.hasPermi('demo:demo:add')")
     @Log(title = "测试单表", businessType = BusinessType.INSERT)
-    @RepeatSubmit
+    @RepeatSubmit(intervalTime = 2, timeUnit = TimeUnit.SECONDS)
     @PostMapping()
     public AjaxResult<Void> add(@Validated(AddGroup.class) @RequestBody TestDemoBo bo) {
         return toAjax(iTestDemoService.insertByBo(bo) ? 1 : 0);

+ 29 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestI18nController.java

@@ -0,0 +1,29 @@
+package com.ruoyi.demo.controller;
+
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.MessageUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+/**
+ * 测试国际化
+ *
+ * @author Lion Li
+ */
+@RestController
+@RequestMapping("/demo/i18n")
+public class TestI18nController {
+
+	/**
+	 * 通过code获取国际化内容
+	 * code为 messages.properties 中的 key
+	 *
+	 * 测试使用 user.register.success
+	 */
+	@GetMapping()
+	public AjaxResult<Void> get(String code) {
+		return AjaxResult.success(MessageUtils.message(code));
+	}
+}

+ 3 - 3
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java

@@ -19,6 +19,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
 import java.util.Arrays;
@@ -57,10 +58,9 @@ public class TestTreeController extends BaseController {
     @PreAuthorize("@ss.hasPermi('demo:tree:export')")
     @Log(title = "测试树表", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
-    public AjaxResult<TestTreeVo> export(@Validated TestTreeBo bo) {
+    public void export(@Validated TestTreeBo bo, HttpServletResponse response) {
         List<TestTreeVo> list = iTestTreeService.queryList(bo);
-        ExcelUtil<TestTreeVo> util = new ExcelUtil<TestTreeVo>(TestTreeVo.class);
-        return util.exportExcel(list, "测试树表");
+		ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response);
     }
 
     /**

+ 13 - 10
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java

@@ -1,9 +1,11 @@
 package com.ruoyi.demo.domain.vo;
 
-import com.ruoyi.common.annotation.Excel;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+
 import java.util.Date;
 
 
@@ -16,6 +18,7 @@ import java.util.Date;
  */
 @Data
 @ApiModel("测试单表视图对象")
+@ExcelIgnoreUnannotated
 public class TestDemoVo {
 
 	private static final long serialVersionUID = 1L;
@@ -29,63 +32,63 @@ public class TestDemoVo {
     /**
      * 部门id
      */
-	@Excel(name = "部门id")
+	@ExcelProperty(value = "部门id")
 	@ApiModelProperty("部门id")
 	private Long deptId;
 
     /**
      * 用户id
      */
-	@Excel(name = "用户id")
+	@ExcelProperty(value = "用户id")
 	@ApiModelProperty("用户id")
 	private Long userId;
 
     /**
      * 排序号
      */
-	@Excel(name = "排序号")
+	@ExcelProperty(value = "排序号")
 	@ApiModelProperty("排序号")
 	private Long orderNum;
 
     /**
      * key键
      */
-	@Excel(name = "key键")
+	@ExcelProperty(value = "key键")
 	@ApiModelProperty("key键")
 	private String testKey;
 
     /**
      * 值
      */
-	@Excel(name = "值")
+	@ExcelProperty(value = "值")
 	@ApiModelProperty("值")
 	private String value;
 
     /**
      * 创建时间
      */
-	@Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
+	@ExcelProperty(value = "创建时间")
 	@ApiModelProperty("创建时间")
 	private Date createTime;
 
     /**
      * 创建人
      */
-	@Excel(name = "创建人")
+	@ExcelProperty(value = "创建人")
 	@ApiModelProperty("创建人")
 	private String createBy;
 
     /**
      * 更新时间
      */
-	@Excel(name = "更新时间" , width = 30, dateFormat = "yyyy-MM-dd")
+	@ExcelProperty(value = "更新时间")
 	@ApiModelProperty("更新时间")
 	private Date updateTime;
 
     /**
      * 更新人
      */
-	@Excel(name = "更新人")
+	@ExcelProperty(value = "更新人")
 	@ApiModelProperty("更新人")
 	private String updateBy;
 

+ 9 - 6
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestTreeVo.java

@@ -1,9 +1,11 @@
 package com.ruoyi.demo.domain.vo;
 
-import com.ruoyi.common.annotation.Excel;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+
 import java.util.Date;
 
 
@@ -16,6 +18,7 @@ import java.util.Date;
  */
 @Data
 @ApiModel("测试树表视图对象")
+@ExcelIgnoreUnannotated
 public class TestTreeVo {
 
 	private static final long serialVersionUID = 1L;
@@ -29,35 +32,35 @@ public class TestTreeVo {
     /**
      * 父id
      */
-	@Excel(name = "父id")
+	@ExcelProperty(value = "父id")
 	@ApiModelProperty("父id")
 	private Long parentId;
 
     /**
      * 部门id
      */
-	@Excel(name = "部门id")
+	@ExcelProperty(value = "部门id")
 	@ApiModelProperty("部门id")
 	private Long deptId;
 
     /**
      * 用户id
      */
-	@Excel(name = "用户id")
+	@ExcelProperty(value = "用户id")
 	@ApiModelProperty("用户id")
 	private Long userId;
 
     /**
      * 树节点名
      */
-	@Excel(name = "树节点名")
+	@ExcelProperty(value = "树节点名")
 	@ApiModelProperty("树节点名")
 	private String treeName;
 
     /**
      * 创建时间
      */
-	@Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
+	@ExcelProperty(value = "创建时间")
 	@ApiModelProperty("创建时间")
 	private Date createTime;
 

+ 4 - 4
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java

@@ -1,7 +1,7 @@
 package com.ruoyi.demo.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.util.StrUtil;
+import com.ruoyi.common.utils.StringUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -62,11 +62,11 @@ public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDem
 		Map<String, Object> params = bo.getParams();
 		Object dataScope = params.get("dataScope");
 		LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
-		lqw.like(StrUtil.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
-		lqw.eq(StrUtil.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
+		lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
+		lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
 		lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
 			TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
-		lqw.apply(dataScope != null && StrUtil.isNotBlank(dataScope.toString()),
+		lqw.apply(dataScope != null && StringUtils.isNotBlank(dataScope.toString()),
 			dataScope != null ? dataScope.toString() : null);
 		return lqw;
 	}

+ 3 - 3
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java

@@ -1,7 +1,7 @@
 package com.ruoyi.demo.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.util.StrUtil;
+import com.ruoyi.common.utils.StringUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.ruoyi.common.annotation.DataScope;
@@ -42,10 +42,10 @@ public class TestTreeServiceImpl extends ServicePlusImpl<TestTreeMapper, TestTre
 		Map<String, Object> params = bo.getParams();
 		Object dataScope = params.get("dataScope");
 		LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery();
-		lqw.like(StrUtil.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
+		lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
 		lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
 			TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
-		lqw.apply(dataScope != null && StrUtil.isNotBlank(dataScope.toString()),
+		lqw.apply(dataScope != null && StringUtils.isNotBlank(dataScope.toString()),
 			dataScope != null ? dataScope.toString() : null);
 		return lqw;
 	}

+ 1 - 1
ruoyi-extend/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>2.6.0</version>
+        <version>3.0.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>ruoyi-extend</artifactId>

+ 1 - 1
ruoyi-extend/ruoyi-monitor-admin/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-extend</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>2.6.0</version>
+        <version>3.0.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <packaging>jar</packaging>

+ 1 - 1
ruoyi-framework/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>2.6.0</version>
+        <version>3.0.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 12 - 13
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java

@@ -1,13 +1,12 @@
 package com.ruoyi.framework.aspectj;
 
-import cn.hutool.core.lang.Validator;
-import cn.hutool.core.util.StrUtil;
 import com.ruoyi.common.annotation.DataScope;
 import com.ruoyi.common.core.domain.BaseEntity;
 import com.ruoyi.common.core.domain.entity.SysRole;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.reflect.ReflectUtils;
 import com.ruoyi.common.utils.spring.SpringUtils;
 import com.ruoyi.framework.web.service.TokenService;
@@ -80,10 +79,10 @@ public class DataScopeAspect {
 		}
 		// 获取当前的用户
 		LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
-		if (Validator.isNotNull(loginUser)) {
+		if (StringUtils.isNotNull(loginUser)) {
 			SysUser currentUser = loginUser.getUser();
 			// 如果是超级管理员,则不过滤数据
-			if (Validator.isNotNull(currentUser) && !currentUser.isAdmin()) {
+			if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) {
 				dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
 					controllerDataScope.userAlias(), controllerDataScope.isUser());
 			}
@@ -101,8 +100,8 @@ public class DataScopeAspect {
 		StringBuilder sqlString = new StringBuilder();
 
 		// 将 "." 提取出,不写别名为单表查询,写别名为多表查询
-		deptAlias = StrUtil.isNotBlank(deptAlias) ? deptAlias + "." : "";
-		userAlias = StrUtil.isNotBlank(userAlias) ? userAlias + "." : "";
+		deptAlias = StringUtils.isNotBlank(deptAlias) ? deptAlias + "." : "";
+		userAlias = StringUtils.isNotBlank(userAlias) ? userAlias + "." : "";
 
 		for (SysRole role : user.getRoles()) {
 			String dataScope = role.getDataScope();
@@ -110,19 +109,19 @@ public class DataScopeAspect {
 				sqlString = new StringBuilder();
 				break;
 			} else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
-				sqlString.append(StrUtil.format(
+				sqlString.append(StringUtils.format(
 					" OR {}dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ",
 					deptAlias, role.getRoleId()));
 			} else if (DATA_SCOPE_DEPT.equals(dataScope)) {
-				sqlString.append(StrUtil.format(" OR {}dept_id = {} ",
+				sqlString.append(StringUtils.format(" OR {}dept_id = {} ",
 					deptAlias, user.getDeptId()));
 			} else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
-				sqlString.append(StrUtil.format(
+				sqlString.append(StringUtils.format(
 					" OR {}dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
 					deptAlias, user.getDeptId(), user.getDeptId()));
 			} else if (DATA_SCOPE_SELF.equals(dataScope)) {
 				if (isUser) {
-					sqlString.append(StrUtil.format(" OR {}user_id = {} ",
+					sqlString.append(StringUtils.format(" OR {}user_id = {} ",
 						userAlias, user.getUserId()));
 				} else {
 					// 数据权限为仅本人且没有userAlias别名不查询任何数据
@@ -131,7 +130,7 @@ public class DataScopeAspect {
 			}
 		}
 
-		if (StrUtil.isNotBlank(sqlString.toString())) {
+		if (StringUtils.isNotBlank(sqlString.toString())) {
 			putDataScope(joinPoint, sqlString.substring(4));
 		}
 	}
@@ -155,14 +154,14 @@ public class DataScopeAspect {
 	 */
 	private void clearDataScope(final JoinPoint joinPoint) {
 		Object params = joinPoint.getArgs()[0];
-		if (Validator.isNotNull(params)) {
+		if (StringUtils.isNotNull(params)) {
 			putDataScope(joinPoint, "");
 		}
 	}
 
 	private static void putDataScope(JoinPoint joinPoint, String sql) {
 		Object params = joinPoint.getArgs()[0];
-		if (Validator.isNotNull(params)) {
+		if (StringUtils.isNotNull(params)) {
 			if (params instanceof BaseEntity) {
 				BaseEntity baseEntity = (BaseEntity) params;
 				baseEntity.getParams().put(DATA_SCOPE, sql);

+ 2 - 2
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java

@@ -1,8 +1,8 @@
 package com.ruoyi.framework.aspectj;
 
-import cn.hutool.core.lang.Validator;
 import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
 import com.ruoyi.common.annotation.DataSource;
+import com.ruoyi.common.utils.StringUtils;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
@@ -33,7 +33,7 @@ public class DataSourceAspect {
 	public Object around(ProceedingJoinPoint point) throws Throwable {
 		DataSource dataSource = getDataSource(point);
 
-		if (Validator.isNotNull(dataSource)) {
+		if (StringUtils.isNotNull(dataSource)) {
 			DynamicDataSourceContextHolder.poll();
 			String source = dataSource.value().getSource();
 			DynamicDataSourceContextHolder.push(source);

+ 5 - 6
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java

@@ -1,13 +1,12 @@
 package com.ruoyi.framework.aspectj;
 
-import cn.hutool.core.lang.Validator;
-import cn.hutool.core.util.StrUtil;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.enums.BusinessStatus;
 import com.ruoyi.common.enums.HttpMethod;
 import com.ruoyi.common.utils.JsonUtils;
 import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.spring.SpringUtils;
 import com.ruoyi.framework.web.service.AsyncService;
 import com.ruoyi.framework.web.service.TokenService;
@@ -104,7 +103,7 @@ public class LogAspect
             if (e != null)
             {
                 operLog.setStatus(BusinessStatus.FAIL.ordinal());
-                operLog.setErrorMsg(StrUtil.sub(e.getMessage(), 0, 2000));
+                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
             }
             // 设置方法名称
             String className = joinPoint.getTarget().getClass().getName();
@@ -161,12 +160,12 @@ public class LogAspect
         if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
         {
             String params = argsArrayToString(joinPoint.getArgs());
-            operLog.setOperParam(StrUtil.sub(params, 0, 2000));
+            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
         }
         else
         {
             Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
-            operLog.setOperParam(StrUtil.sub(paramsMap.toString(), 0, 2000));
+            operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
         }
     }
 
@@ -195,7 +194,7 @@ public class LogAspect
         if (paramsArray != null && paramsArray.length > 0)
         {
 			for (Object o : paramsArray) {
-				if (Validator.isNotNull(o) && !isFilterObject(o)) {
+				if (StringUtils.isNotNull(o) && !isFilterObject(o)) {
 					params.append(JsonUtils.toJsonString(o)).append(" ");
 				}
 			}

+ 116 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java

@@ -0,0 +1,116 @@
+package com.ruoyi.framework.aspectj;
+
+import com.ruoyi.common.annotation.RateLimiter;
+import com.ruoyi.common.enums.LimitType;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.RedisScript;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 限流处理
+ *
+ * @author ruoyi
+ */
+@Aspect
+@Component
+public class RateLimiterAspect
+{
+    private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
+
+    private RedisTemplate<Object, Object> redisTemplate;
+
+    private RedisScript<Long> limitScript;
+
+    @Autowired
+    public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate)
+    {
+        this.redisTemplate = redisTemplate;
+    }
+
+    @Autowired
+    public void setLimitScript(RedisScript<Long> limitScript)
+    {
+        this.limitScript = limitScript;
+    }
+
+    // 配置织入点
+    @Pointcut("@annotation(com.ruoyi.common.annotation.RateLimiter)")
+    public void rateLimiterPointCut()
+    {
+    }
+
+    @Before("rateLimiterPointCut()")
+    public void doBefore(JoinPoint point) throws Throwable
+    {
+        RateLimiter rateLimiter = getAnnotationRateLimiter(point);
+        String key = rateLimiter.key();
+        int time = rateLimiter.time();
+        int count = rateLimiter.count();
+
+        String combineKey = getCombineKey(rateLimiter, point);
+        List<Object> keys = Collections.singletonList(combineKey);
+        try
+        {
+            Long number = redisTemplate.execute(limitScript, keys, count, time);
+            if (StringUtils.isNull(number) || number.intValue() > count)
+            {
+                throw new ServiceException("访问过于频繁,请稍后再试");
+            }
+            log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), key);
+        }
+        catch (ServiceException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException("服务器限流异常,请稍后再试");
+        }
+    }
+
+    /**
+     * 是否存在注解,如果存在就获取
+     */
+    private RateLimiter getAnnotationRateLimiter(JoinPoint joinPoint)
+    {
+        Signature signature = joinPoint.getSignature();
+        MethodSignature methodSignature = (MethodSignature) signature;
+        Method method = methodSignature.getMethod();
+
+        if (method != null)
+        {
+            return method.getAnnotation(RateLimiter.class);
+        }
+        return null;
+    }
+
+    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
+    {
+        StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
+        if (rateLimiter.limitType() == LimitType.IP)
+        {
+            stringBuffer.append(ServletUtils.getClientIP());
+        }
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        Method method = signature.getMethod();
+        Class<?> targetClass = method.getDeclaringClass();
+        stringBuffer.append("-").append(targetClass.getName()).append("- ").append(method.getName());
+        return stringBuffer.toString();
+    }
+}

+ 4 - 4
ruoyi-framework/src/main/java/com/ruoyi/framework/captcha/UnsignedMathGenerator.java

@@ -4,7 +4,7 @@ import cn.hutool.captcha.generator.CodeGenerator;
 import cn.hutool.core.math.Calculator;
 import cn.hutool.core.util.CharUtil;
 import cn.hutool.core.util.RandomUtil;
-import cn.hutool.core.util.StrUtil;
+import com.ruoyi.common.utils.StringUtils;
 
 /**
  * 无符号计算生成器
@@ -45,8 +45,8 @@ public class UnsignedMathGenerator implements CodeGenerator {
 		int max = RandomUtil.randomInt(min, limit);
 		String number1 = Integer.toString(max);
 		String number2 = Integer.toString(min);
-		number1 = StrUtil.padAfter(number1, this.numberLength, CharUtil.SPACE);
-		number2 = StrUtil.padAfter(number2, this.numberLength, CharUtil.SPACE);
+		number1 = StringUtils.rightPad(number1, this.numberLength, CharUtil.SPACE);
+		number2 = StringUtils.rightPad(number2, this.numberLength, CharUtil.SPACE);
 
 		return number1 + RandomUtil.randomChar(operators) + number2 + '=';
 	}
@@ -80,6 +80,6 @@ public class UnsignedMathGenerator implements CodeGenerator {
 	 * @return 最大值
 	 */
 	private int getLimit() {
-		return Integer.parseInt("1" + StrUtil.repeat('0', this.numberLength));
+		return Integer.parseInt("1" + StringUtils.repeat('0', this.numberLength));
 	}
 }

+ 1 - 11
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java

@@ -1,12 +1,8 @@
 package com.ruoyi.framework.config;
 
-import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
-import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 
-import java.util.TimeZone;
-
 /**
  * 程序注解配置
  *
@@ -16,11 +12,5 @@ import java.util.TimeZone;
 // 表示通过aop框架暴露该代理对象,AopContext能够访问
 @EnableAspectJAutoProxy(exposeProxy = true)
 public class ApplicationConfig {
-    /**
-     * 时区配置
-     */
-    @Bean
-    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
-        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
-    }
+
 }

+ 2 - 2
ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java

@@ -1,6 +1,6 @@
 package com.ruoyi.framework.config;
 
-import com.ruoyi.common.exception.CustomException;
+import com.ruoyi.common.exception.ServiceException;
 import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -41,7 +41,7 @@ public class AsyncConfig extends AsyncConfigurerSupport {
     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
         return (throwable, method, objects) -> {
             throwable.printStackTrace();
-            throw new CustomException(
+            throw new ServiceException(
                     "Exception message - " + throwable.getMessage()
                     + ", Method name - " + method.getName()
                     + ", Parameter value - " + Arrays.toString(objects));

+ 4 - 3
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java

@@ -1,10 +1,11 @@
 package com.ruoyi.framework.config;
 
-import cn.hutool.core.util.StrUtil;
 import com.ruoyi.common.filter.RepeatableFilter;
 import com.ruoyi.common.filter.XssFilter;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.framework.config.properties.XssProperties;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -19,6 +20,7 @@ import java.util.Map;
  * @author Lion Li
  */
 @Configuration
+@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
 public class FilterConfig {
 
     @Autowired
@@ -30,12 +32,11 @@ public class FilterConfig {
         FilterRegistrationBean registration = new FilterRegistrationBean();
         registration.setDispatcherTypes(DispatcherType.REQUEST);
         registration.setFilter(new XssFilter());
-        registration.addUrlPatterns(StrUtil.splitToArray(xssProperties.getUrlPatterns(), ","));
+        registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), ","));
         registration.setName("xssFilter");
         registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
         Map<String, String> initParameters = new HashMap<String, String>();
         initParameters.put("excludes", xssProperties.getExcludes());
-        initParameters.put("enabled", xssProperties.getEnabled());
         registration.setInitParameters(initParameters);
         return registration;
     }

+ 48 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java

@@ -0,0 +1,48 @@
+package com.ruoyi.framework.config;
+
+import cn.hutool.core.util.StrUtil;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.LocaleResolver;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Locale;
+
+/**
+ * 国际化配置
+ *
+ * @author Lion Li
+ */
+@Configuration
+public class I18nConfig {
+
+	@Bean
+	public LocaleResolver localeResolver() {
+		return new I18nLocaleResolver();
+	}
+
+	/**
+	 * 获取请求头国际化信息
+	 */
+	static class I18nLocaleResolver implements LocaleResolver {
+
+		@NotNull
+		@Override
+		public Locale resolveLocale(HttpServletRequest httpServletRequest) {
+			String language = httpServletRequest.getHeader("content-language");
+			Locale locale = Locale.getDefault();
+			if (StrUtil.isNotBlank(language)) {
+				String[] split = language.split("_");
+				locale = new Locale(split[0], split[1]);
+			}
+			return locale;
+		}
+
+		@Override
+		public void setLocale(@NotNull HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
+
+		}
+	}
+}

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff