Prechádzať zdrojové kódy

Merge remote-tracking branch 'origin/dev' into satoken

# Conflicts:
#	pom.xml
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
#	ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
#	ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
#	ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
#	ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
#	ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
#	ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
#	ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
#	ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java
#	ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java
#	ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/PermissionService.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserDetailsServiceImpl.java
疯狂的狮子li 3 rokov pred
rodič
commit
42295ef2ac
100 zmenil súbory, kde vykonal 1310 pridanie a 1218 odobranie
  1. 7 7
      README.md
  2. 11 43
      pom.xml
  3. 1 0
      ruoyi-admin/Dockerfile
  4. 0 6
      ruoyi-admin/pom.xml
  5. 4 1
      ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
  6. 1 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
  7. 1 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
  8. 5 6
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
  9. 7 6
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java
  10. 5 4
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
  11. 4 3
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
  12. 3 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
  13. 6 5
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java
  14. 3 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java
  15. 5 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java
  16. 8 9
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java
  17. 4 3
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
  18. 8 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
  19. 12 5
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
  20. 14 14
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
  21. 7 0
      ruoyi-admin/src/main/resources/application-prod.yml
  22. 18 47
      ruoyi-admin/src/main/resources/application.yml
  23. 6 24
      ruoyi-admin/src/main/resources/logback.xml
  24. 0 2
      ruoyi-admin/src/main/resources/spy.properties
  25. 0 28
      ruoyi-common/pom.xml
  26. 5 0
      ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java
  27. 0 9
      ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
  28. 0 51
      ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java
  29. 4 2
      ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelDictConvert.java
  30. 0 67
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java
  31. 0 91
      ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/cache/MybatisPlusRedisCache.java
  32. 18 0
      ruoyi-common/src/main/java/com/ruoyi/common/core/service/ConfigService.java
  33. 57 0
      ruoyi-common/src/main/java/com/ruoyi/common/core/service/DictService.java
  34. 5 0
      ruoyi-common/src/main/java/com/ruoyi/common/core/service/LogininforService.java
  35. 6 0
      ruoyi-common/src/main/java/com/ruoyi/common/core/service/OperLogService.java
  36. 108 0
      ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java
  37. 73 0
      ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java
  38. 14 0
      ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelListener.java
  39. 26 0
      ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelResult.java
  40. 0 61
      ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java
  41. 0 35
      ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java
  42. 4 3
      ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java
  43. 7 2
      ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java
  44. 24 1
      ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisUtils.java
  45. 31 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeBuildUtils.java
  46. 25 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/ValidatorUtils.java
  47. 125 119
      ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
  48. 0 37
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/FeignTestController.java
  49. 67 67
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java
  50. 43 53
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java
  51. 17 16
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisPubSubController.java
  52. 30 30
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java
  53. 13 13
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/Swagger3DemoController.java
  54. 27 27
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java
  55. 36 11
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
  56. 44 1
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestI18nController.java
  57. 8 5
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
  58. 61 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoImportVo.java
  59. 0 27
      ruoyi-demo/src/main/java/com/ruoyi/demo/feign/FeignTestService.java
  60. 0 13
      ruoyi-demo/src/main/java/com/ruoyi/demo/feign/constant/FeignTestConstant.java
  61. 0 28
      ruoyi-demo/src/main/java/com/ruoyi/demo/feign/fallback/FeignTestFallback.java
  62. 0 1
      ruoyi-demo/src/main/java/com/ruoyi/demo/feign/fallback/package-info.java
  63. 0 1
      ruoyi-demo/src/main/java/com/ruoyi/demo/feign/package-info.java
  64. 0 4
      ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
  65. 0 0
      ruoyi-demo/src/main/resources/mapper/demo/package-info.md
  66. 3 0
      ruoyi-demo/src/main/resources/mapper/package-info.md
  67. 6 0
      ruoyi-extend/ruoyi-monitor-admin/pom.xml
  68. 2 0
      ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/config/SecurityConfig.java
  69. 14 0
      ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application-dev.yml
  70. 14 0
      ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application-prod.yml
  71. 20 0
      ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
  72. 5 0
      ruoyi-extend/ruoyi-xxl-job-admin/pom.xml
  73. 15 0
      ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-dev.yml
  74. 15 0
      ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-prod.yml
  75. 14 3
      ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml
  76. 1 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
  77. 59 7
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
  78. 0 95
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/FeignConfig.java
  79. 1 1
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java
  80. 1 3
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java
  81. 5 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
  82. 0 6
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/TLogConfig.java
  83. 19 7
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/ValidatorConfig.java
  84. 1 1
      ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java
  85. 8 2
      ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java
  86. 1 1
      ruoyi-generator/pom.xml
  87. 2 2
      ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java
  88. 5 3
      ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
  89. 1 2
      ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java
  90. 2 1
      ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java
  91. 3 0
      ruoyi-generator/src/main/resources/mapper/package-info.md
  92. 5 2
      ruoyi-generator/src/main/resources/vm/java/controller.java.vm
  93. 9 9
      ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm
  94. 12 14
      ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
  95. 3 3
      ruoyi-oss/src/main/java/com/ruoyi/oss/constant/OssConstant.java
  96. 11 11
      ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/OssEnumd.java
  97. 28 22
      ruoyi-oss/src/main/java/com/ruoyi/oss/factory/OssFactory.java
  98. 1 8
      ruoyi-oss/src/main/java/com/ruoyi/oss/properties/OssProperties.java
  99. 1 11
      ruoyi-oss/src/main/java/com/ruoyi/oss/service/IOssStrategy.java
  100. 5 7
      ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractOssStrategy.java

+ 7 - 7
README.md

@@ -10,12 +10,14 @@
 [![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()
 [![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
 
-RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼容原框架)
+> RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼容原框架)
+
+> 系统演示: [传送门](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/系统演示?sort_id=4836388)
 
 | 功能介绍 | 使用技术 | 文档地址 | 特性注意事项 |
 |---|---|---|---|
 | 当前框架 | RuoYi-Vue-Plus | [RuoYi-Vue-Plus文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages) | 重写RuoYi-Vue全方位升级(不兼容原框架) |
-| satoken分支 | RuoYi-Vue-Plus-satoken | [satoken分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/) | 使用satoken重构权限鉴权(仅供学习不推荐上生产) |
+| satoken分支 | RuoYi-Vue-Plus-satoken | [satoken分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/) | 使用satoken重构权限鉴权(公测 可尝试上生产) |
 | 单体分支 | RuoYi-Vue-Plus-fast | [fast分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/) | 单体应用结构 |
 | 原框架 | RuoYi-Vue | [RuoYi-Vue官网](http://ruoyi.vip/) | 定期同步需要的功能 |
 | 前端开发框架 | Vue、Element UI | [Element UI官网](https://element.eleme.cn/#/zh-CN) | |
@@ -28,17 +30,16 @@ RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼
 | 数据库框架 | p6spy | [p6spy官网](https://p6spy.readthedocs.io/) | 更强劲的 SQL 分析 |
 | 多数据源框架 | dynamic-datasource | [dynamic-ds文档](https://www.kancloud.cn/tracy5546/dynamic-datasource/content) | 支持主从与多种类数据库异构 |
 | 序列化框架 | Jackson | [Jackson官网](https://github.com/FasterXML/jackson) | 统一使用 jackson 高效可靠 |
-| 网络框架 | Feign、OkHttp3 | [Feign官网](https://github.com/OpenFeign/feign) | 接口化管理 HTTP 请求 |
 | Redis客户端 | Redisson | [Redisson文档](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | 支持单机、集群配置 |
 | 分布式限流 | Redisson | [Redisson文档](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | 全局、请求IP、集群ID 多种限流 |
 | 分布式锁 | Lock4j | [Lock4j官网](https://gitee.com/baomidou/lock4j) | 注解锁、工具锁 多种多样 |
-| 分布式幂等 | Lock4j | [Lock4j文档](https://gitee.com/baomidou/lock4j) | 基于分布式锁实现 |
+| 分布式幂等 | Redisson | [Lock4j文档](https://gitee.com/baomidou/lock4j) | 拦截重复提交 |
 | 分布式日志 | TLog | [TLog文档](https://yomahub.com/tlog/docs) | 支持跟踪链路日志记录、性能分析、链路排查 |
 | 分布式任务调度 | Xxl-Job | [Xxl-Job官网](https://www.xuxueli.com/xxl-job/) | 高性能 高可靠 易扩展 |
 | 文件存储 | Minio | [Minio文档](https://docs.min.io/) | 本地存储 |
 | 文件存储 | 七牛、阿里、腾讯 | [OSS使用文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4359146&doc_id=1469725) | 云存储 |
 | 监控框架 | SpringBoot-Admin | [SpringBoot-Admin文档](https://codecentric.github.io/spring-boot-admin/current/) | 全方位服务监控 |
-| 校验框架 | Validation | [Validation文档](https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/) | 增强接口安全性、严谨性 |
+| 校验框架 | Validation | [Validation文档](https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/) | 增强接口安全性、严谨性 支持国际化 |
 | Excel框架 | Alibaba EasyExcel | [EasyExcel文档](https://www.yuque.com/easyexcel/doc/easyexcel) | 性能优异 扩展性强 |
 | 文档框架 | Knife4j | [Knife4j文档](https://doc.xiaominfo.com/knife4j/documentation/) | 美化接口文档 |
 | 工具类框架 | Hutool、Lombok | [Hutool文档](https://www.hutool.cn/docs/) | 减少代码冗余 增加安全性 |
@@ -61,8 +62,7 @@ RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼
 
 ## 软件架构图
 
-![Plus部署架构图](https://images.gitee.com/uploads/images/2021/0729/112230_4295e5ce_1766278.png "Plus部署架构图.png")
-
+![Plus部署架构图](https://images.gitee.com/uploads/images/2021/1112/202137_673ac5d2_1766278.png "Plus部署架构图.png")
 ## 贡献代码
 
 欢迎各路英雄豪杰 `PR` 代码 请提交到 `dev` 开发分支 统一测试发版

+ 11 - 43
pom.xml

@@ -14,7 +14,7 @@
 
     <properties>
         <ruoyi-vue-plus.version>3.3.0</ruoyi-vue-plus.version>
-        <spring-boot.version>2.5.6</spring-boot.version>
+        <spring-boot.version>2.5.7</spring-boot.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
         <java.version>1.8</java.version>
@@ -24,19 +24,17 @@
         <swagger-annotations.version>1.5.22</swagger-annotations.version>
         <poi.version>4.1.2</poi.version>
         <easyexcel.version>2.2.11</easyexcel.version>
-        <velocity.version>1.7</velocity.version>
+        <velocity.version>2.3</velocity.version>
         <satoken.version>1.28.0</satoken.version>
         <mybatis-plus.version>3.4.3.4</mybatis-plus.version>
         <p6spy.version>3.9.1</p6spy.version>
-        <hutool.version>5.7.15</hutool.version>
-        <feign.version>3.0.3</feign.version>
-        <feign-okhttp.version>11.6</feign-okhttp.version>
-        <okhttp.version>4.9.1</okhttp.version>
-        <spring-boot-admin.version>2.5.2</spring-boot-admin.version>
-        <redisson.version>3.16.3</redisson.version>
+        <hutool.version>5.7.16</hutool.version>
+        <okhttp.version>4.9.2</okhttp.version>
+        <spring-boot-admin.version>2.5.4</spring-boot-admin.version>
+        <redisson.version>3.16.4</redisson.version>
         <lock4j.version>2.2.1</lock4j.version>
         <dynamic-ds.version>3.4.1</dynamic-ds.version>
-        <tlog.version>1.3.3</tlog.version>
+        <tlog.version>1.3.4</tlog.version>
         <xxl-job.version>2.3.0</xxl-job.version>
 
         <!-- jdk11 缺失依赖 jaxb-->
@@ -120,7 +118,7 @@
             <!-- velocity代码生成使用模板 -->
             <dependency>
                 <groupId>org.apache.velocity</groupId>
-                <artifactId>velocity</artifactId>
+                <artifactId>velocity-engine-core</artifactId>
                 <version>${velocity.version}</version>
             </dependency>
 
@@ -174,25 +172,6 @@
                 <version>${hutool.version}</version>
             </dependency>
 
-            <!-- @deprecated 由于使用人数较少 决定与 3.4.0 版本移除 -->
-            <dependency>
-                <groupId>org.springframework.cloud</groupId>
-                <artifactId>spring-cloud-starter-openfeign</artifactId>
-                <version>${feign.version}</version>
-                <exclusions>
-                    <exclusion>
-                        <artifactId>feign-core</artifactId>
-                        <groupId>io.github.openfeign</groupId>
-                    </exclusion>
-                </exclusions>
-            </dependency>
-            <!-- @deprecated 由于使用人数较少 决定与 3.4.0 版本移除 -->
-            <dependency>
-                <groupId>io.github.openfeign</groupId>
-                <artifactId>feign-okhttp</artifactId>
-                <version>${feign-okhttp.version}</version>
-            </dependency>
-
             <dependency>
                 <groupId>com.squareup.okhttp3</groupId>
                 <artifactId>okhttp</artifactId>
@@ -251,25 +230,12 @@
                 </exclusions>
             </dependency>
 
-            <dependency>
-                <groupId>com.yomahub</groupId>
-                <artifactId>tlog-feign</artifactId>
-                <version>${tlog.version}</version>
-            </dependency>
-
             <dependency>
                 <groupId>com.yomahub</groupId>
                 <artifactId>tlog-xxl-job</artifactId>
                 <version>${tlog.version}</version>
             </dependency>
 
-            <!-- 定时任务 @deprecated 3.4.0删除 迁移至xxl-job -->
-            <dependency>
-                <groupId>com.ruoyi</groupId>
-                <artifactId>ruoyi-quartz</artifactId>
-                <version>${ruoyi-vue-plus.version}</version>
-            </dependency>
-
             <!-- 定时任务 -->
             <dependency>
                 <groupId>com.ruoyi</groupId>
@@ -326,7 +292,6 @@
         <module>ruoyi-admin</module>
         <module>ruoyi-framework</module>
         <module>ruoyi-system</module>
-        <module>ruoyi-quartz</module>
         <module>ruoyi-job</module>
         <module>ruoyi-generator</module>
         <module>ruoyi-common</module>
@@ -391,6 +356,7 @@
                 <!-- 环境标识,需要与配置文件的名称相对应 -->
                 <profiles.active>local</profiles.active>
                 <logging.level>debug</logging.level>
+                <knife4j.production>false</knife4j.production>
                 <endpoints.include>'*'</endpoints.include>
             </properties>
         </profile>
@@ -400,6 +366,7 @@
                 <!-- 环境标识,需要与配置文件的名称相对应 -->
                 <profiles.active>dev</profiles.active>
                 <logging.level>debug</logging.level>
+                <knife4j.production>false</knife4j.production>
                 <endpoints.include>'*'</endpoints.include>
             </properties>
             <activation>
@@ -412,6 +379,7 @@
             <properties>
                 <profiles.active>prod</profiles.active>
                 <logging.level>warn</logging.level>
+                <knife4j.production>true</knife4j.production>
                 <endpoints.include>health, info, logfile</endpoints.include>
             </properties>
         </profile>

+ 1 - 0
ruoyi-admin/Dockerfile

@@ -4,6 +4,7 @@ MAINTAINER Lion Li
 
 RUN mkdir -p /ruoyi/server
 RUN mkdir -p /ruoyi/server/logs
+RUN mkdir -p /ruoyi/server/temp
 
 WORKDIR /ruoyi/server
 

+ 0 - 6
ruoyi-admin/pom.xml

@@ -41,12 +41,6 @@
             <artifactId>ruoyi-system</artifactId>
         </dependency>
 
-        <!-- 定时任务 @deprecated 3.4.0删除 迁移至xxl-job -->
-<!--        <dependency>-->
-<!--            <groupId>com.ruoyi</groupId>-->
-<!--            <artifactId>ruoyi-quartz</artifactId>-->
-<!--        </dependency>-->
-
         <dependency>
             <groupId>com.ruoyi</groupId>
             <artifactId>ruoyi-job</artifactId>

+ 4 - 1
ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java

@@ -2,6 +2,7 @@ package com.ruoyi;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
 
 /**
  * 启动程序
@@ -14,7 +15,9 @@ public class RuoYiApplication {
 
     public static void main(String[] args) {
         System.setProperty("spring.devtools.restart.enabled", "false");
-        SpringApplication.run(RuoYiApplication.class, args);
+        SpringApplication application = new SpringApplication(RuoYiApplication.class);
+        application.setApplicationStartup(new BufferingApplicationStartup(2048));
+        application.run(args);
         System.out.println("(♥◠‿◠)ノ゙  RuoYi-Vue-Plus启动成功   ლ(´ڡ`ლ)゙");
     }
 

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

@@ -43,7 +43,7 @@ public class SysLogininforController extends BaseController {
     @ApiOperation("导出系统访问记录列表")
     @Log(title = "登录日志", businessType = BusinessType.EXPORT)
     @SaCheckPermission("monitor:logininfor:export")
-    @GetMapping("/export")
+    @PostMapping("/export")
     public void export(SysLogininfor logininfor, HttpServletResponse response) {
         List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
         ExcelUtil.exportExcel(list, "登录日志", SysLogininfor.class, response);

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

@@ -43,7 +43,7 @@ public class SysOperlogController extends BaseController {
     @ApiOperation("导出操作日志记录列表")
     @Log(title = "操作日志", businessType = BusinessType.EXPORT)
     @SaCheckPermission("monitor:operlog:export")
-    @GetMapping("/export")
+    @PostMapping("/export")
     public void export(SysOperLog operLog, HttpServletResponse response) {
         List<SysOperLog> list = operLogService.selectOperLogList(operLog);
         ExcelUtil.exportExcel(list, "操作日志", SysOperLog.class, response);

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

@@ -2,7 +2,6 @@ package com.ruoyi.web.controller.system;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
 import com.ruoyi.common.annotation.Log;
-import com.ruoyi.common.annotation.RepeatSubmit;
 import com.ruoyi.common.constant.UserConstants;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
@@ -13,6 +12,7 @@ import com.ruoyi.system.domain.SysConfig;
 import com.ruoyi.system.service.ISysConfigService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -48,7 +48,7 @@ public class SysConfigController extends BaseController {
     @ApiOperation("导出参数配置列表")
     @Log(title = "参数管理", businessType = BusinessType.EXPORT)
     @SaCheckPermission("system:config:export")
-    @GetMapping("/export")
+    @PostMapping("/export")
     public void export(SysConfig config, HttpServletResponse response) {
         List<SysConfig> list = configService.selectConfigList(config);
         ExcelUtil.exportExcel(list, "参数数据", SysConfig.class, response);
@@ -60,7 +60,7 @@ public class SysConfigController extends BaseController {
     @ApiOperation("根据参数编号获取详细信息")
     @SaCheckPermission("system:config:query")
     @GetMapping(value = "/{configId}")
-    public AjaxResult<SysConfig> getInfo(@PathVariable Long configId) {
+    public AjaxResult<SysConfig> getInfo(@ApiParam("参数ID") @PathVariable Long configId) {
         return AjaxResult.success(configService.selectConfigById(configId));
     }
 
@@ -69,7 +69,7 @@ public class SysConfigController extends BaseController {
      */
     @ApiOperation("根据参数键名查询参数值")
     @GetMapping(value = "/configKey/{configKey}")
-    public AjaxResult<Void> getConfigKey(@PathVariable String configKey) {
+    public AjaxResult<Void> getConfigKey(@ApiParam("参数Key") @PathVariable String configKey) {
         return AjaxResult.success(configService.selectConfigByKey(configKey));
     }
 
@@ -80,7 +80,6 @@ public class SysConfigController extends BaseController {
     @SaCheckPermission("system:config:add")
     @Log(title = "参数管理", businessType = BusinessType.INSERT)
     @PostMapping
-    @RepeatSubmit
     public AjaxResult<Void> add(@Validated @RequestBody SysConfig config) {
         if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) {
             return AjaxResult.error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
@@ -109,7 +108,7 @@ public class SysConfigController extends BaseController {
     @SaCheckPermission("system:config:remove")
     @Log(title = "参数管理", businessType = BusinessType.DELETE)
     @DeleteMapping("/{configIds}")
-    public AjaxResult<Void> remove(@PathVariable Long[] configIds) {
+    public AjaxResult<Void> remove(@ApiParam("参数ID串") @PathVariable Long[] configIds) {
         configService.deleteConfigByIds(configIds);
         return success();
     }

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

@@ -1,18 +1,19 @@
 package com.ruoyi.web.controller.system;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.lang.tree.Tree;
 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.TreeSelect;
 import com.ruoyi.common.core.domain.entity.SysDept;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.service.ISysDeptService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -53,7 +54,7 @@ public class SysDeptController extends BaseController {
     @ApiOperation("查询部门列表(排除节点)")
     @SaCheckPermission("system:dept:list")
     @GetMapping("/list/exclude/{deptId}")
-    public AjaxResult<List<SysDept>> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
+    public AjaxResult<List<SysDept>> excludeChild(@ApiParam("部门ID") @PathVariable(value = "deptId", required = false) Long deptId) {
         List<SysDept> depts = deptService.selectDeptList(new SysDept());
         depts.removeIf(d -> d.getDeptId().equals(deptId)
                 || ArrayUtil.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
@@ -66,7 +67,7 @@ public class SysDeptController extends BaseController {
     @ApiOperation("根据部门编号获取详细信息")
     @SaCheckPermission("system:dept:query")
     @GetMapping(value = "/{deptId}")
-    public AjaxResult<SysDept> getInfo(@PathVariable Long deptId) {
+    public AjaxResult<SysDept> getInfo(@ApiParam("部门ID") @PathVariable Long deptId) {
         deptService.checkDeptDataScope(deptId);
         return AjaxResult.success(deptService.selectDeptById(deptId));
     }
@@ -76,7 +77,7 @@ public class SysDeptController extends BaseController {
      */
     @ApiOperation("获取部门下拉树列表")
     @GetMapping("/treeselect")
-    public AjaxResult<List<TreeSelect>> treeselect(SysDept dept) {
+    public AjaxResult<List<Tree<Long>>> treeselect(SysDept dept) {
         List<SysDept> depts = deptService.selectDeptList(dept);
         return AjaxResult.success(deptService.buildDeptTreeSelect(depts));
     }
@@ -86,7 +87,7 @@ public class SysDeptController extends BaseController {
      */
     @ApiOperation("加载对应角色部门列表树")
     @GetMapping(value = "/roleDeptTreeselect/{roleId}")
-    public AjaxResult<Map<String, Object>> roleDeptTreeselect(@PathVariable("roleId") Long roleId) {
+    public AjaxResult<Map<String, Object>> roleDeptTreeselect(@ApiParam("角色ID") @PathVariable("roleId") Long roleId) {
         List<SysDept> depts = deptService.selectDeptList(new SysDept());
         Map<String, Object> ajax = new HashMap<>();
         ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
@@ -134,7 +135,7 @@ public class SysDeptController extends BaseController {
     @SaCheckPermission("system:dept:remove")
     @Log(title = "部门管理", businessType = BusinessType.DELETE)
     @DeleteMapping("/{deptId}")
-    public AjaxResult<Void> remove(@PathVariable Long deptId) {
+    public AjaxResult<Void> remove(@ApiParam("部门ID串") @PathVariable Long deptId) {
         if (deptService.hasChildByDeptId(deptId)) {
             return AjaxResult.error("存在下级部门,不允许删除");
         }

+ 5 - 4
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java

@@ -13,6 +13,7 @@ import com.ruoyi.system.service.ISysDictDataService;
 import com.ruoyi.system.service.ISysDictTypeService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -47,7 +48,7 @@ public class SysDictDataController extends BaseController {
     @ApiOperation("导出字典数据列表")
     @Log(title = "字典数据", businessType = BusinessType.EXPORT)
     @SaCheckPermission("system:dict:export")
-    @GetMapping("/export")
+    @PostMapping("/export")
     public void export(SysDictData dictData, HttpServletResponse response) {
         List<SysDictData> list = dictDataService.selectDictDataList(dictData);
         ExcelUtil.exportExcel(list, "字典数据", SysDictData.class, response);
@@ -59,7 +60,7 @@ public class SysDictDataController extends BaseController {
     @ApiOperation("查询字典数据详细")
     @SaCheckPermission("system:dict:query")
     @GetMapping(value = "/{dictCode}")
-    public AjaxResult<SysDictData> getInfo(@PathVariable Long dictCode) {
+    public AjaxResult<SysDictData> getInfo(@ApiParam("字典code") @PathVariable Long dictCode) {
         return AjaxResult.success(dictDataService.selectDictDataById(dictCode));
     }
 
@@ -68,7 +69,7 @@ public class SysDictDataController extends BaseController {
      */
     @ApiOperation("根据字典类型查询字典数据信息")
     @GetMapping(value = "/type/{dictType}")
-    public AjaxResult<List<SysDictData>> dictType(@PathVariable String dictType) {
+    public AjaxResult<List<SysDictData>> dictType(@ApiParam("字典类型") @PathVariable String dictType) {
         List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
         if (StringUtils.isNull(data)) {
             data = new ArrayList<>();
@@ -105,7 +106,7 @@ public class SysDictDataController extends BaseController {
     @SaCheckPermission("system:dict:remove")
     @Log(title = "字典类型", businessType = BusinessType.DELETE)
     @DeleteMapping("/{dictCodes}")
-    public AjaxResult<Void> remove(@PathVariable Long[] dictCodes) {
+    public AjaxResult<Void> remove(@ApiParam("字典code串") @PathVariable Long[] dictCodes) {
         dictDataService.deleteDictDataByIds(dictCodes);
         return success();
     }

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

@@ -12,6 +12,7 @@ import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.system.service.ISysDictTypeService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -44,7 +45,7 @@ public class SysDictTypeController extends BaseController {
     @ApiOperation("导出字典类型列表")
     @Log(title = "字典类型", businessType = BusinessType.EXPORT)
     @SaCheckPermission("system:dict:export")
-    @GetMapping("/export")
+    @PostMapping("/export")
     public void export(SysDictType dictType, HttpServletResponse response) {
         List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
         ExcelUtil.exportExcel(list, "字典类型", SysDictType.class, response);
@@ -56,7 +57,7 @@ public class SysDictTypeController extends BaseController {
     @ApiOperation("查询字典类型详细")
     @SaCheckPermission("system:dict:query")
     @GetMapping(value = "/{dictId}")
-    public AjaxResult<SysDictType> getInfo(@PathVariable Long dictId) {
+    public AjaxResult<SysDictType> getInfo(@ApiParam("字典ID") @PathVariable Long dictId) {
         return AjaxResult.success(dictTypeService.selectDictTypeById(dictId));
     }
 
@@ -95,7 +96,7 @@ public class SysDictTypeController extends BaseController {
     @SaCheckPermission("system:dict:remove")
     @Log(title = "字典类型", businessType = BusinessType.DELETE)
     @DeleteMapping("/{dictIds}")
-    public AjaxResult<Void> remove(@PathVariable Long[] dictIds) {
+    public AjaxResult<Void> remove(@ApiParam("字典ID串") @PathVariable Long[] dictIds) {
         dictTypeService.deleteDictTypeByIds(dictIds);
         return success();
     }

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

@@ -10,6 +10,7 @@ import com.ruoyi.common.core.domain.model.LoginBody;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.system.domain.vo.RouterVo;
 import com.ruoyi.system.service.ISysMenuService;
+import com.ruoyi.system.service.ISysUserService;
 import com.ruoyi.system.service.SysLoginService;
 import com.ruoyi.system.service.SysPermissionService;
 import io.swagger.annotations.Api;
@@ -33,13 +34,14 @@ import java.util.Set;
  * @author Lion Li
  */
 @Validated
-@Api(value = "数据字典信息控制器", tags = {"数据字典信息管理"})
+@Api(value = "登录验证控制器", tags = {"登录验证管理"})
 @RequiredArgsConstructor(onConstructor_ = @Autowired)
 @RestController
 public class SysLoginController {
 
     private final SysLoginService loginService;
     private final ISysMenuService menuService;
+    private final ISysUserService userService;
     private final SysPermissionService permissionService;
 
     /**

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

@@ -1,17 +1,18 @@
 package com.ruoyi.web.controller.system;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.lang.tree.Tree;
 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.TreeSelect;
 import com.ruoyi.common.core.domain.entity.SysMenu;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.service.ISysMenuService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -52,7 +53,7 @@ public class SysMenuController extends BaseController {
     @ApiOperation("根据菜单编号获取详细信息")
     @SaCheckPermission("system:menu:query")
     @GetMapping(value = "/{menuId}")
-    public AjaxResult<SysMenu> getInfo(@PathVariable Long menuId) {
+    public AjaxResult<SysMenu> getInfo(@ApiParam("菜单ID") @PathVariable Long menuId) {
         return AjaxResult.success(menuService.selectMenuById(menuId));
     }
 
@@ -61,7 +62,7 @@ public class SysMenuController extends BaseController {
      */
     @ApiOperation("获取菜单下拉树列表")
     @GetMapping("/treeselect")
-    public AjaxResult<List<TreeSelect>> treeselect(SysMenu menu) {
+    public AjaxResult<List<Tree<Long>>> treeselect(SysMenu menu) {
         List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
         return AjaxResult.success(menuService.buildMenuTreeSelect(menus));
     }
@@ -71,7 +72,7 @@ public class SysMenuController extends BaseController {
      */
     @ApiOperation("加载对应角色菜单列表树")
     @GetMapping(value = "/roleMenuTreeselect/{roleId}")
-    public AjaxResult<Map<String, Object>> roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
+    public AjaxResult<Map<String, Object>> roleMenuTreeselect(@ApiParam("角色ID") @PathVariable("roleId") Long roleId) {
         List<SysMenu> menus = menuService.selectMenuList(getUserId());
         Map<String, Object> ajax = new HashMap<>();
         ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
@@ -120,7 +121,7 @@ public class SysMenuController extends BaseController {
     @SaCheckPermission("system:menu:remove")
     @Log(title = "菜单管理", businessType = BusinessType.DELETE)
     @DeleteMapping("/{menuId}")
-    public AjaxResult<Void> remove(@PathVariable("menuId") Long menuId) {
+    public AjaxResult<Void> remove(@ApiParam("菜单ID") @PathVariable("menuId") Long menuId) {
         if (menuService.hasChildByMenuId(menuId)) {
             return AjaxResult.error("存在子菜单,不允许删除");
         }

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

@@ -13,6 +13,7 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.RequiredArgsConstructor;
 
 /**
@@ -45,7 +46,7 @@ public class SysNoticeController extends BaseController {
     @ApiOperation("根据通知公告编号获取详细信息")
     @SaCheckPermission("system:notice:query")
     @GetMapping(value = "/{noticeId}")
-    public AjaxResult<SysNotice> getInfo(@PathVariable Long noticeId) {
+    public AjaxResult<SysNotice> getInfo(@ApiParam("公告ID") @PathVariable Long noticeId) {
         return AjaxResult.success(noticeService.selectNoticeById(noticeId));
     }
 
@@ -78,7 +79,7 @@ public class SysNoticeController extends BaseController {
     @SaCheckPermission("system:notice:remove")
     @Log(title = "通知公告", businessType = BusinessType.DELETE)
     @DeleteMapping("/{noticeIds}")
-    public AjaxResult<Void> remove(@PathVariable Long[] noticeIds) {
+    public AjaxResult<Void> remove(@ApiParam("公告ID串") @PathVariable Long[] noticeIds) {
         return toAjax(noticeService.deleteNoticeByIds(noticeIds));
     }
 }

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

@@ -15,6 +15,7 @@ import com.ruoyi.system.domain.vo.SysOssConfigVo;
 import com.ruoyi.system.service.ISysOssConfigService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -56,7 +57,8 @@ public class SysOssConfigController extends BaseController {
     @ApiOperation("获取对象存储配置详细信息")
     @SaCheckPermission("system:oss:query")
     @GetMapping("/{ossConfigId}")
-    public AjaxResult<SysOssConfigVo> getInfo(@NotNull(message = "主键不能为空")
+    public AjaxResult<SysOssConfigVo> getInfo(@ApiParam("OSS配置ID")
+                                              @NotNull(message = "主键不能为空")
                                               @PathVariable("ossConfigId") Integer ossConfigId) {
         return AjaxResult.success(iSysOssConfigService.queryById(ossConfigId));
     }
@@ -92,7 +94,8 @@ public class SysOssConfigController extends BaseController {
     @SaCheckPermission("system:oss:remove")
     @Log(title = "对象存储配置", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ossConfigIds}")
-    public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
+    public AjaxResult<Void> remove(@ApiParam("OSS配置ID串")
+                                   @NotEmpty(message = "主键不能为空")
                                    @PathVariable Long[] ossConfigIds) {
         return toAjax(iSysOssConfigService.deleteWithValidByIds(Arrays.asList(ossConfigIds), true) ? 1 : 0);
     }

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

@@ -17,17 +17,14 @@ import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.JsonUtils;
 import com.ruoyi.common.utils.file.FileUtils;
-import com.ruoyi.oss.constant.CloudConstant;
+import com.ruoyi.oss.constant.OssConstant;
 import com.ruoyi.system.domain.SysConfig;
 import com.ruoyi.system.domain.SysOss;
 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;
-import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.MediaType;
@@ -37,6 +34,7 @@ import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.constraints.NotEmpty;
+import java.io.File;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -72,7 +70,7 @@ public class SysOssController extends BaseController {
      */
     @ApiOperation("上传OSS对象存储")
     @ApiImplicitParams({
-            @ApiImplicitParam(name = "file", value = "文件", dataType = "java.io.File", required = true),
+        @ApiImplicitParam(name = "file", value = "文件", dataTypeClass = File.class, required = true),
     })
     @SaCheckPermission("system:oss:upload")
     @Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
@@ -92,7 +90,7 @@ public class SysOssController extends BaseController {
     @ApiOperation("下载OSS对象存储")
     @SaCheckPermission("system:oss:download")
     @GetMapping("/download/{ossId}")
-    public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
+    public void download(@ApiParam("OSS对象ID") @PathVariable Long ossId, HttpServletResponse response) throws IOException {
         SysOss sysOss = iSysOssService.getById(ossId);
         if (ObjectUtil.isNull(sysOss)) {
             throw new ServiceException("文件数据不存在!");
@@ -120,7 +118,8 @@ public class SysOssController extends BaseController {
     @SaCheckPermission("system:oss:remove")
     @Log(title = "OSS对象存储", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ossIds}")
-    public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
+    public AjaxResult<Void> remove(@ApiParam("OSS对象ID串")
+                                   @NotEmpty(message = "主键不能为空")
                                    @PathVariable Long[] ossIds) {
         return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true) ? 1 : 0);
     }
@@ -135,7 +134,7 @@ public class SysOssController extends BaseController {
     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));
+            .eq(SysConfig::getConfigKey, OssConstant.PEREVIEW_LIST_RESOURCE_KEY));
         config.setConfigValue(map.get("previewListResource").toString());
         return toAjax(iSysConfigService.updateConfig(config));
     }

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

@@ -12,6 +12,7 @@ import com.ruoyi.system.domain.SysPost;
 import com.ruoyi.system.service.ISysPostService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -47,7 +48,7 @@ public class SysPostController extends BaseController {
     @ApiOperation("导出岗位列表")
     @Log(title = "岗位管理", businessType = BusinessType.EXPORT)
     @SaCheckPermission("system:post:export")
-    @GetMapping("/export")
+    @PostMapping("/export")
     public void export(SysPost post, HttpServletResponse response) {
         List<SysPost> list = postService.selectPostList(post);
         ExcelUtil.exportExcel(list, "岗位数据", SysPost.class, response);
@@ -59,7 +60,7 @@ public class SysPostController extends BaseController {
     @ApiOperation("根据岗位编号获取详细信息")
     @SaCheckPermission("system:post:query")
     @GetMapping(value = "/{postId}")
-    public AjaxResult<SysPost> getInfo(@PathVariable Long postId) {
+    public AjaxResult<SysPost> getInfo(@ApiParam("岗位ID") @PathVariable Long postId) {
         return AjaxResult.success(postService.selectPostById(postId));
     }
 
@@ -102,7 +103,7 @@ public class SysPostController extends BaseController {
     @SaCheckPermission("system:post:remove")
     @Log(title = "岗位管理", businessType = BusinessType.DELETE)
     @DeleteMapping("/{postIds}")
-    public AjaxResult<Void> remove(@PathVariable Long[] postIds) {
+    public AjaxResult<Void> remove(@ApiParam("岗位ID串") @PathVariable Long[] postIds) {
         return toAjax(postService.deletePostByIds(postIds));
     }
 

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

@@ -18,6 +18,7 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.io.File;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -43,7 +44,7 @@ public class SysProfileController extends BaseController {
     @GetMapping
     public AjaxResult<Map<String, Object>> profile() {
         SysUser user = userService.getById(getUserId());
-		Map<String,Object> ajax = new HashMap<>();
+		Map<String, Object> ajax = new HashMap<>();
 		ajax.put("user", user);
         ajax.put("roleGroup", userService.selectUserRoleGroup(user.getUserName()));
         ajax.put("postGroup", userService.selectUserPostGroup(user.getUserName()));
@@ -66,6 +67,7 @@ public class SysProfileController extends BaseController {
             return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
         }
         user.setUserId(getUserId());
+        user.setUserName(null);
         user.setPassword(null);
         if (userService.updateUserProfile(user) > 0) {
             return AjaxResult.success();
@@ -77,6 +79,10 @@ public class SysProfileController extends BaseController {
      * 重置密码
      */
     @ApiOperation("重置密码")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "oldPassword", value = "旧密码", paramType = "query", dataTypeClass = String.class),
+        @ApiImplicitParam(name = "newPassword", value = "新密码", paramType = "query", dataTypeClass = String.class)
+    })
     @Log(title = "个人信息", businessType = BusinessType.UPDATE)
     @PutMapping("/updatePwd")
     public AjaxResult<Void> updatePwd(String oldPassword, String newPassword) {
@@ -100,7 +106,7 @@ public class SysProfileController extends BaseController {
      */
     @ApiOperation("头像上传")
     @ApiImplicitParams({
-            @ApiImplicitParam(name = "file", value = "用户头像", dataType = "java.io.File", required = true),
+        @ApiImplicitParam(name = "avatarfile", value = "用户头像", dataTypeClass = File.class, required = true),
     })
     @Log(title = "用户头像", businessType = BusinessType.UPDATE)
     @PostMapping("/avatar")

+ 12 - 5
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java

@@ -13,8 +13,7 @@ import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.system.domain.SysUserRole;
 import com.ruoyi.system.service.ISysRoleService;
 import com.ruoyi.system.service.ISysUserService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -48,7 +47,7 @@ public class SysRoleController extends BaseController {
     @ApiOperation("导出角色信息列表")
     @Log(title = "角色管理", businessType = BusinessType.EXPORT)
     @SaCheckPermission("system:role:export")
-    @GetMapping("/export")
+    @PostMapping("/export")
     public void export(SysRole role, HttpServletResponse response) {
         List<SysRole> list = roleService.selectRoleList(role);
         ExcelUtil.exportExcel(list, "角色数据", SysRole.class, response);
@@ -60,7 +59,7 @@ public class SysRoleController extends BaseController {
     @ApiOperation("根据角色编号获取详细信息")
     @SaCheckPermission("system:role:query")
     @GetMapping(value = "/{roleId}")
-    public AjaxResult<SysRole> getInfo(@PathVariable Long roleId) {
+    public AjaxResult<SysRole> getInfo(@ApiParam("角色ID") @PathVariable Long roleId) {
         roleService.checkRoleDataScope(roleId);
         return AjaxResult.success(roleService.selectRoleById(roleId));
     }
@@ -134,7 +133,7 @@ public class SysRoleController extends BaseController {
     @SaCheckPermission("system:role:remove")
     @Log(title = "角色管理", businessType = BusinessType.DELETE)
     @DeleteMapping("/{roleIds}")
-    public AjaxResult<Void> remove(@PathVariable Long[] roleIds) {
+    public AjaxResult<Void> remove(@ApiParam("岗位ID串") @PathVariable Long[] roleIds) {
         return toAjax(roleService.deleteRoleByIds(roleIds));
     }
 
@@ -183,6 +182,10 @@ public class SysRoleController extends BaseController {
      * 批量取消授权用户
      */
     @ApiOperation("批量取消授权用户")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "roleId", value = "角色ID", paramType = "query", dataTypeClass = String.class),
+        @ApiImplicitParam(name = "userIds", value = "用户ID串", paramType = "query", dataTypeClass = String.class)
+    })
     @SaCheckPermission("system:role:edit")
     @Log(title = "角色管理", businessType = BusinessType.GRANT)
     @PutMapping("/authUser/cancelAll")
@@ -194,6 +197,10 @@ public class SysRoleController extends BaseController {
      * 批量选择用户授权
      */
     @ApiOperation("批量选择用户授权")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "roleId", value = "角色ID", paramType = "query", dataTypeClass = String.class),
+        @ApiImplicitParam(name = "userIds", value = "用户ID串", paramType = "query", dataTypeClass = String.class)
+    })
     @SaCheckPermission("system:role:edit")
     @Log(title = "角色管理", businessType = BusinessType.GRANT)
     @PutMapping("/authUser/selectAll")

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

@@ -13,18 +13,17 @@ import com.ruoyi.common.core.domain.entity.SysRole;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.excel.ExcelResult;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.system.domain.vo.SysUserExportVo;
 import com.ruoyi.system.domain.vo.SysUserImportVo;
+import com.ruoyi.system.listener.SysUserImportListener;
 import com.ruoyi.system.service.ISysPostService;
 import com.ruoyi.system.service.ISysRoleService;
 import com.ruoyi.system.service.ISysUserService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiImplicitParam;
-import io.swagger.annotations.ApiImplicitParams;
-import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -67,7 +66,7 @@ public class SysUserController extends BaseController {
     @ApiOperation("导出用户列表")
     @Log(title = "用户管理", businessType = BusinessType.EXPORT)
     @SaCheckPermission("system:user:export")
-    @GetMapping("/export")
+    @PostMapping("/export")
     public void export(SysUser user, HttpServletResponse response) {
         List<SysUser> list = userService.selectUserList(user);
         List<SysUserExportVo> listVo = BeanUtil.copyToList(list, SysUserExportVo.class);
@@ -90,15 +89,12 @@ public class SysUserController extends BaseController {
     @SaCheckPermission("system:user:import")
     @PostMapping("/importData")
     public AjaxResult<Void> importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception {
-		List<SysUserImportVo> userListVo = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class);
-		List<SysUser> userList = BeanUtil.copyToList(userListVo, SysUser.class);
-        String operName = userService.getById(getUserId()).getUserName();
-        String message = userService.importUser(userList, updateSupport, operName);
-        return AjaxResult.success(message);
+        ExcelResult<SysUserImportVo> result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport));
+        return AjaxResult.success(result.getAnalysis());
     }
 
     @ApiOperation("下载导入模板")
-    @GetMapping("/importTemplate")
+    @PostMapping("/importTemplate")
     public void importTemplate(HttpServletResponse response) {
         ExcelUtil.exportExcel(new ArrayList<>(), "用户数据", SysUserImportVo.class, response);
     }
@@ -109,7 +105,7 @@ public class SysUserController extends BaseController {
     @ApiOperation("根据用户编号获取详细信息")
     @SaCheckPermission("system:user:query")
     @GetMapping(value = {"/", "/{userId}" })
-    public AjaxResult<Map<String, Object>> getInfo(@PathVariable(value = "userId", required = false) Long userId) {
+    public AjaxResult<Map<String, Object>> getInfo(@ApiParam("用户ID") @PathVariable(value = "userId", required = false) Long userId) {
 		userService.checkUserDataScope(userId);
         Map<String, Object> ajax = new HashMap<>();
         List<SysRole> roles = roleService.selectRoleAll();
@@ -170,7 +166,7 @@ public class SysUserController extends BaseController {
     @SaCheckPermission("system:user:remove")
     @Log(title = "用户管理", businessType = BusinessType.DELETE)
     @DeleteMapping("/{userIds}")
-    public AjaxResult<Void> remove(@PathVariable Long[] userIds) {
+    public AjaxResult<Void> remove(@ApiParam("角色ID串") @PathVariable Long[] userIds) {
         if (ArrayUtil.contains(userIds, getUserId())) {
             return error("当前用户不能删除");
         }
@@ -208,7 +204,7 @@ public class SysUserController extends BaseController {
     @ApiOperation("根据用户编号获取授权角色")
     @SaCheckPermission("system:user:query")
     @GetMapping("/authRole/{userId}")
-    public AjaxResult<Map<String, Object>> authRole(@PathVariable("userId") Long userId) {
+    public AjaxResult<Map<String, Object>> authRole(@ApiParam("用户ID") @PathVariable("userId") Long userId) {
         SysUser user = userService.selectUserById(userId);
         List<SysRole> roles = roleService.selectRolesByUserId(userId);
         Map<String, Object> ajax = new HashMap<>();
@@ -221,6 +217,10 @@ public class SysUserController extends BaseController {
      * 用户授权角色
      */
     @ApiOperation("用户授权角色")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "userId", value = "用户Id", paramType = "query", dataTypeClass = String.class),
+        @ApiImplicitParam(name = "roleIds", value = "角色ID串", paramType = "query", dataTypeClass = String.class)
+    })
     @SaCheckPermission("system:user:edit")
     @Log(title = "用户管理", businessType = BusinessType.GRANT)
     @PutMapping("/authRole")

+ 7 - 0
ruoyi-admin/src/main/resources/application-prod.yml

@@ -1,3 +1,10 @@
+--- # 配置临时路径存储
+spring:
+  servlet:
+    multipart:
+      # 临时文件存储位置 避免临时文件被系统清理报错
+      location: /ruoyi/server/temp
+
 --- # 监控配置
 spring:
   boot:

+ 18 - 47
ruoyi-admin/src/main/resources/application.yml

@@ -10,6 +10,8 @@ ruoyi:
   demoEnabled: true
   # 获取ip地址开关
   addressEnabled: true
+  # 缓存懒加载
+  cacheLazy: true
 
 captcha:
   # 页面 <参数设置> 可开启关闭 验证码校验
@@ -234,6 +236,22 @@ swagger:
     - name: 3.代码生成模块
       basePackage: com.ruoyi.generator
 
+knife4j:
+  # 是否开启Knife4j增强模式
+  enable: true
+  # 是否开启生产环境保护策略
+  production: @knife4j.production@
+  # 前端Ui的个性化配置属性
+  setting:
+    # 默认语言
+    language: zh-CN
+    # 是否显示Footer
+    enableFooter: false
+    # 是否开启动态参数调试功能
+    enableDynamicParameter: true
+    # 是否在每个Debug调试栏后显示刷新变量按钮
+    enableReloadCacheParameter: true
+
 # 防止XSS攻击
 xss:
   # 过滤开关
@@ -262,22 +280,6 @@ thread-pool:
   # ABORT_POLICY 中止
   rejectedExecutionHandler: CALLER_RUNS_POLICY
 
-# feign 相关配置
-feign:
-  # 不支持多包, 如有需要可在注解配置 或 提升扫包等级
-  # 例如 com.**.**.feign
-  package: com.ruoyi.**.feign
-  # 开启压缩
-  compression:
-    request:
-      enabled: true
-    response:
-      enabled: true
-  okhttp:
-    enabled: true
-  circuitbreaker:
-    enabled: true
-
 --- # redisson 缓存配置
 redisson:
   cacheGroup:
@@ -313,34 +315,3 @@ management:
   endpoint:
     logfile:
       external-file: ./logs/sys-console.log
-
---- # 定时任务配置
-spring:
-  quartz:
-    scheduler-name: RuoyiScheduler
-    startup-delay: 1s
-    overwrite-existing-jobs: true
-    auto-startup: true
-    job-store-type: jdbc
-    properties:
-      org:
-        quartz:
-          # Scheduler 相关配置
-          scheduler:
-            instanceName: RuoyiScheduler
-            instanceId: AUTO
-          # 线程池相关配置
-          threadPool:
-            class: org.quartz.simpl.SimpleThreadPool
-            threadCount: 20
-            threadPriority: 5
-          # JobStore 集群配置
-          jobStore:
-            class: org.quartz.impl.jdbcjobstore.JobStoreTX
-            isClustered: true
-            clusterCheckinInterval: 15000
-            txIsolationLevelSerializable: true
-            misfireThreshold: 60000
-            tablePrefix: QRTZ_
-            # sqlserver 启用
-            # selectWithLockSQL: SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?

+ 6 - 24
ruoyi-admin/src/main/resources/logback.xml

@@ -31,7 +31,7 @@
             <level>INFO</level>
         </filter>
     </appender>
-	
+
 	<!-- 系统日志输出 -->
 	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
 	    <file>${log.path}/sys-info.log</file>
@@ -54,7 +54,7 @@
             <onMismatch>DENY</onMismatch>
         </filter>
 	</appender>
-	
+
 	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
 	    <file>${log.path}/sys-error.log</file>
         <!-- 循环政策:基于时间创建日志文件 -->
@@ -76,21 +76,7 @@
             <onMismatch>DENY</onMismatch>
         </filter>
     </appender>
-	
-	<!-- 用户访问日志输出  -->
-    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
-		<file>${log.path}/sys-user.log</file>
-        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
-            <!-- 按天回滚 daily -->
-            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
-            <!-- 日志最大的历史 60天 -->
-            <maxHistory>60</maxHistory>
-        </rollingPolicy>
-        <encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
-            <pattern>${log.pattern}</pattern>
-        </encoder>
-    </appender>
-	
+
 	<!-- 系统模块日志级别控制  -->
 	<logger name="com.ruoyi" level="info" />
 	<!-- Spring日志级别控制  -->
@@ -99,16 +85,12 @@
 	<root level="info">
 		<appender-ref ref="console" />
 	</root>
-	
+
 	<!--系统操作日志-->
     <root level="info">
         <appender-ref ref="file_info" />
         <appender-ref ref="file_error" />
         <appender-ref ref="file_console" />
     </root>
-	
-	<!--系统用户操作日志-->
-    <logger name="sys-user" level="info">
-        <appender-ref ref="sys-user"/>
-    </logger>
-</configuration> 
+
+</configuration>

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

@@ -22,5 +22,3 @@ outagedetection=true
 outagedetectioninterval=2
 # 是否过滤 Log
 filter=true
-# 过滤 Log 时所排除的表名列表,以逗号分隔
-exclude=QRTZ_

+ 0 - 28
ruoyi-common/pom.xml

@@ -87,18 +87,6 @@
             <artifactId>jaxb-impl</artifactId>
         </dependency>
 
-        <!-- redis 缓存操作 -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-data-redis</artifactId>
-        </dependency>
-
-        <!-- pool 对象池 -->
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-pool2</artifactId>
-        </dependency>
-
         <!-- servlet包 -->
         <dependency>
             <groupId>javax.servlet</groupId>
@@ -122,18 +110,6 @@
             <artifactId>lombok</artifactId>
         </dependency>
 
-        <!-- @deprecated 由于使用人数较少 决定与 3.4.0 版本移除 -->
-        <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-openfeign</artifactId>
-        </dependency>
-
-        <!-- @deprecated 由于使用人数较少 决定与 3.4.0 版本移除 -->
-        <dependency>
-            <groupId>io.github.openfeign</groupId>
-            <artifactId>feign-okhttp</artifactId>
-        </dependency>
-
         <dependency>
             <groupId>de.codecentric</groupId>
             <artifactId>spring-boot-admin-starter-client</artifactId>
@@ -180,10 +156,6 @@
             <artifactId>tlog-webroot</artifactId>
         </dependency>
 
-        <dependency>
-            <groupId>com.yomahub</groupId>
-            <artifactId>tlog-feign</artifactId>
-        </dependency>
     </dependencies>
 
 </project>

+ 5 - 0
ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java

@@ -38,6 +38,11 @@ public class RuoYiConfig {
      */
     private boolean demoEnabled;
 
+    /**
+     * 缓存懒加载
+     */
+    private boolean cacheLazy;
+
     /**
      * 获取地址开关
      */

+ 0 - 9
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java

@@ -106,13 +106,4 @@ public class Constants {
      */
     public static final String SYS_DICT_KEY = "sys_dict:";
 
-    /**
-     * RMI 远程方法调用
-     */
-    public static final String LOOKUP_RMI = "rmi://";
-
-    /**
-     * LDAP 远程方法调用
-     */
-    public static final String LOOKUP_LDAP = "ldap://";
 }

+ 0 - 51
ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java

@@ -1,51 +0,0 @@
-package com.ruoyi.common.constant;
-
-/**
- * 任务调度通用常量
- *
- * @deprecated 3.4.0删除 迁移至xxl-job
- * @author ruoyi
- */
-public class ScheduleConstants
-{
-    public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
-
-    /** 执行目标key */
-    public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
-
-    /** 默认 */
-    public static final String MISFIRE_DEFAULT = "0";
-
-    /** 立即触发执行 */
-    public static final String MISFIRE_IGNORE_MISFIRES = "1";
-
-    /** 触发一次执行 */
-    public static final String MISFIRE_FIRE_AND_PROCEED = "2";
-
-    /** 不触发立即执行 */
-    public static final String MISFIRE_DO_NOTHING = "3";
-
-    public enum Status
-    {
-        /**
-         * 正常
-         */
-        NORMAL("0"),
-        /**
-         * 暂停
-         */
-        PAUSE("1");
-
-        private String value;
-
-        private Status(String value)
-        {
-            this.value = value;
-        }
-
-        public String getValue()
-        {
-            return value;
-        }
-    }
-}

+ 4 - 2
ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelDictConvert.java

@@ -8,8 +8,10 @@ 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.core.service.DictService;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.utils.spring.SpringUtils;
 import lombok.extern.slf4j.Slf4j;
 
 import java.lang.reflect.Field;
@@ -41,7 +43,7 @@ public class ExcelDictConvert implements Converter<Object> {
 		if (StringUtils.isBlank(type)) {
 			value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator());
 		} else {
-			value = ExcelUtil.reverseDictByExp(label, type, anno.separator());
+			value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator());
 		}
 		return Convert.convert(contentProperty.getField().getType(), value);
 	}
@@ -58,7 +60,7 @@ public class ExcelDictConvert implements Converter<Object> {
 		if (StringUtils.isBlank(type)) {
 			label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator());
 		} else {
-			label = ExcelUtil.convertDictByExp(value, type, anno.separator());
+			label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator());
 		}
 		return new CellData<>(label);
 	}

+ 0 - 67
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java

@@ -1,67 +0,0 @@
-package com.ruoyi.common.core.domain;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.ruoyi.common.core.domain.entity.SysDept;
-import com.ruoyi.common.core.domain.entity.SysMenu;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Treeselect树结构实体类
- *
- * @author Lion Li
- */
-
-@Data
-@NoArgsConstructor
-@Accessors(chain = true)
-@ApiModel("树结构实体类")
-public class TreeSelect implements Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 节点ID
-     */
-    @ApiModelProperty(value = "节点ID")
-    private Long id;
-
-    /**
-     * 节点名称
-     */
-    @ApiModelProperty(value = "节点名称")
-    private String label;
-
-    /**
-     * 子节点
-     */
-    @ApiModelProperty(value = "子节点")
-    @JsonInclude(JsonInclude.Include.NON_EMPTY)
-    private List<TreeSelect> children;
-
-    public TreeSelect(SysDept dept) {
-        this.id = dept.getDeptId();
-        this.label = dept.getDeptName();
-        this.children = dept.getChildren()
-                .stream()
-                .map(d -> new TreeSelect((SysDept) d))
-                .collect(Collectors.toList());
-    }
-
-    public TreeSelect(SysMenu menu) {
-        this.id = menu.getMenuId();
-        this.label = menu.getMenuName();
-        this.children = menu.getChildren()
-                .stream()
-                .map(d -> new TreeSelect((SysMenu) d))
-                .collect(Collectors.toList());
-    }
-
-}

+ 0 - 91
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/cache/MybatisPlusRedisCache.java

@@ -1,91 +0,0 @@
-package com.ruoyi.common.core.mybatisplus.cache;
-
-import cn.hutool.extra.spring.SpringUtil;
-import com.ruoyi.common.utils.RedisUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.ibatis.cache.Cache;
-import org.springframework.data.redis.connection.RedisServerCommands;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.util.CollectionUtils;
-
-import java.util.Collection;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * mybatis-redis 二级缓存
- *
- * 使用方法 配置文件开启 mybatis-plus 二级缓存
- * 在 XxxMapper.java 类上添加注解 @CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
- *
- * @deprecated 3.4.0删除 推荐使用spirng-cache
- * @author Lion Li
- */
-@Slf4j
-public class MybatisPlusRedisCache implements Cache {
-
-	private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
-
-	private String id;
-
-	public MybatisPlusRedisCache(final String id) {
-		if (id == null) {
-			throw new IllegalArgumentException("Cache instances require an ID");
-		}
-		this.id = id;
-	}
-
-	@Override
-	public String getId() {
-		return this.id;
-	}
-
-	@Override
-	public void putObject(Object key, Object value) {
-		if (value != null) {
-			RedisUtils.setCacheObject(key.toString(), value);
-		}
-	}
-
-	@Override
-	public Object getObject(Object key) {
-		try {
-			if (key != null) {
-				return RedisUtils.getCacheObject(key.toString());
-			}
-		} catch (Exception e) {
-			e.printStackTrace();
-			log.error("缓存出错");
-		}
-		return null;
-	}
-
-	@Override
-	public Object removeObject(Object key) {
-		if (key != null) {
-			RedisUtils.deleteObject(key.toString());
-		}
-		return null;
-	}
-
-	@Override
-	public void clear() {
-		log.debug("清空缓存");
-		Collection<String> keys = RedisUtils.keys("*:" + this.id + "*");
-		if (!CollectionUtils.isEmpty(keys)) {
-			RedisUtils.deleteObject(keys);
-		}
-	}
-
-	@Override
-	public int getSize() {
-		RedisTemplate<String, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
-		Long size = redisTemplate.execute(RedisServerCommands::dbSize);
-		return size.intValue();
-	}
-
-	@Override
-	public ReadWriteLock getReadWriteLock() {
-		return this.readWriteLock;
-	}
-}

+ 18 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/service/ConfigService.java

@@ -0,0 +1,18 @@
+package com.ruoyi.common.core.service;
+
+/**
+ * 通用 参数配置服务
+ *
+ * @author Lion Li
+ */
+public interface ConfigService {
+
+    /**
+     * 根据参数 key 获取参数值
+     *
+     * @param configKey 参数 key
+     * @return 参数值
+     */
+    String getConfigValue(String configKey);
+
+}

+ 57 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/service/DictService.java

@@ -0,0 +1,57 @@
+package com.ruoyi.common.core.service;
+
+/**
+ * 通用 字典服务
+ *
+ * @author Lion Li
+ */
+public interface DictService {
+
+    /**
+     * 分隔符
+     */
+    String SEPARATOR = ",";
+
+    /**
+     * 根据字典类型和字典值获取字典标签
+     *
+     * @param dictType  字典类型
+     * @param dictValue 字典值
+     * @return 字典标签
+     */
+    default String getDictLabel(String dictType, String dictValue) {
+        return getDictLabel(dictType, dictValue, SEPARATOR);
+    }
+
+    /**
+     * 根据字典类型和字典标签获取字典值
+     *
+     * @param dictType  字典类型
+     * @param dictLabel 字典标签
+     * @return 字典值
+     */
+    default String getDictValue(String dictType, String dictLabel) {
+        return getDictValue(dictType, dictLabel, SEPARATOR);
+    }
+
+    /**
+     * 根据字典类型和字典值获取字典标签
+     *
+     * @param dictType  字典类型
+     * @param dictValue 字典值
+     * @param separator 分隔符
+     * @return 字典标签
+     */
+    String getDictLabel(String dictType, String dictValue, String separator);
+
+    /**
+     * 根据字典类型和字典标签获取字典值
+     *
+     * @param dictType  字典类型
+     * @param dictLabel 字典标签
+     * @param separator 分隔符
+     * @return 字典值
+     */
+    String getDictValue(String dictType, String dictLabel, String separator);
+
+}

+ 5 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/service/LogininforService.java

@@ -2,6 +2,11 @@ package com.ruoyi.common.core.service;
 
 import javax.servlet.http.HttpServletRequest;
 
+/**
+ * 通用 系统访问日志
+ *
+ * @author Lion Li
+ */
 public interface LogininforService {
 
     void recordLogininfor(String username, String status, String message,

+ 6 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/service/OperLogService.java

@@ -3,7 +3,13 @@ package com.ruoyi.common.core.service;
 import com.ruoyi.common.core.domain.dto.OperLogDTO;
 import org.springframework.scheduling.annotation.Async;
 
+/**
+ * 通用 操作日志
+ *
+ * @author Lion Li
+ */
 public interface OperLogService {
+
     @Async
     void recordOper(OperLogDTO operLogDTO);
 }

+ 108 - 0
ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java

@@ -0,0 +1,108 @@
+package com.ruoyi.common.excel;
+
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.alibaba.excel.exception.ExcelAnalysisException;
+import com.alibaba.excel.exception.ExcelDataConvertException;
+import com.alibaba.fastjson.JSON;
+import com.ruoyi.common.utils.ValidatorUtils;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Excel 导入监听
+ *
+ * @author Yjoioooo
+ * @author Lion Li
+ */
+@Slf4j
+@NoArgsConstructor
+public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements ExcelListener<T> {
+
+    /**
+     * 是否Validator检验,默认为是
+     */
+    private Boolean isValidate = Boolean.TRUE;
+
+    /**
+     * excel 表头数据
+     */
+    private Map<Integer, String> headMap;
+
+    /**
+     * 导入回执
+     */
+    private ExcelResult<T> excelResult;
+
+    public DefaultExcelListener(boolean isValidate) {
+        this.excelResult = new DefautExcelResult<>();
+        this.isValidate = isValidate;
+    }
+
+    /**
+     * 处理异常
+     *
+     * @param exception ExcelDataConvertException
+     * @param context   Excel 上下文
+     */
+    @Override
+    public void onException(Exception exception, AnalysisContext context) throws Exception {
+        String errMsg = null;
+        if (exception instanceof ExcelDataConvertException) {
+            // 如果是某一个单元格的转换异常 能获取到具体行号
+            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
+            Integer rowIndex = excelDataConvertException.getRowIndex();
+            Integer columnIndex = excelDataConvertException.getColumnIndex();
+            errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常<br/>",
+                rowIndex + 1, columnIndex + 1, headMap.get(columnIndex));
+            if (log.isDebugEnabled()) {
+                log.error(errMsg);
+            }
+        }
+        if (exception instanceof ConstraintViolationException) {
+            ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception;
+            Set<ConstraintViolation<?>> constraintViolations = constraintViolationException.getConstraintViolations();
+            String constraintViolationsMsg = constraintViolations.stream()
+                .map(ConstraintViolation::getMessage)
+                .collect(Collectors.joining(", "));
+            errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg);
+            if (log.isDebugEnabled()) {
+                log.error(errMsg);
+            }
+        }
+        excelResult.getErrorList().add(errMsg);
+        throw new ExcelAnalysisException(errMsg);
+    }
+
+    @Override
+    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
+        this.headMap = headMap;
+        log.debug("解析到一条表头数据: {}", JSON.toJSONString(headMap));
+    }
+
+    @Override
+    public void invoke(T data, AnalysisContext context) {
+        if (isValidate) {
+            ValidatorUtils.validate(data);
+        }
+        excelResult.getList().add(data);
+    }
+
+    @Override
+    public void doAfterAllAnalysed(AnalysisContext context) {
+        log.debug("所有数据解析完成!");
+    }
+
+    @Override
+    public ExcelResult<T> getExcelResult() {
+        return excelResult;
+    }
+
+}

+ 73 - 0
ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java

@@ -0,0 +1,73 @@
+package com.ruoyi.common.excel;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 默认excel返回对象
+ *
+ * @author Yjoioooo
+ * @author Lion Li
+ */
+public class DefautExcelResult<T> implements ExcelResult<T> {
+
+    /**
+     * 数据对象list
+     */
+    @Setter
+    private List<T> list;
+
+    /**
+     * 错误信息列表
+     */
+    @Setter
+    private List<String> errorList;
+
+    public DefautExcelResult() {
+        this.list = new ArrayList<>();
+        this.errorList = new ArrayList<>();
+    }
+
+    public DefautExcelResult(List<T> list, List<String> errorList) {
+        this.list = list;
+        this.errorList = errorList;
+    }
+
+    public DefautExcelResult(ExcelResult<T> excelResult) {
+        this.list = excelResult.getList();
+        this.errorList = excelResult.getErrorList();
+    }
+
+    @Override
+    public List<T> getList() {
+        return list;
+    }
+
+    @Override
+    public List<String> getErrorList() {
+        return errorList;
+    }
+
+    /**
+     * 获取导入回执
+     *
+     * @return 导入回执
+     */
+    @Override
+    public String getAnalysis() {
+        int successCount = list.size();
+        int errorCount = errorList.size();
+        if (successCount == 0) {
+            return "读取失败,未解析到数据";
+        } else {
+            if (errorCount == 0) {
+                return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount);
+            } else {
+                return "";
+            }
+        }
+    }
+}

+ 14 - 0
ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelListener.java

@@ -0,0 +1,14 @@
+package com.ruoyi.common.excel;
+
+import com.alibaba.excel.read.listener.ReadListener;
+
+/**
+ * Excel 导入监听
+ *
+ * @author Lion Li
+ */
+public interface ExcelListener<T> extends ReadListener<T> {
+
+    ExcelResult<T> getExcelResult();
+
+}

+ 26 - 0
ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelResult.java

@@ -0,0 +1,26 @@
+package com.ruoyi.common.excel;
+
+import java.util.List;
+
+/**
+ * excel返回对象
+ *
+ * @author Lion Li
+ */
+public interface ExcelResult<T> {
+
+    /**
+     * 对象列表
+     */
+    List<T> getList();
+
+    /**
+     * 错误列表
+     */
+    List<String> getErrorList();
+
+    /**
+     * 导入回执
+     */
+    String getAnalysis();
+}

+ 0 - 61
ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java

@@ -1,61 +0,0 @@
-package com.ruoyi.common.exception.file;
-
-import lombok.*;
-import org.apache.commons.fileupload.FileUploadException;
-
-import java.util.Arrays;
-
-/**
- * 文件上传 误异常类
- *
- * @author ruoyi
- */
-@Data
-@EqualsAndHashCode(callSuper = true)
-@NoArgsConstructor
-public class InvalidExtensionException extends FileUploadException {
-    private static final long serialVersionUID = 1L;
-
-    private String[] allowedExtension;
-    private String extension;
-    private String filename;
-
-    public InvalidExtensionException(String[] allowedExtension, String extension, String filename) {
-        super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]");
-        this.allowedExtension = allowedExtension;
-        this.extension = extension;
-        this.filename = filename;
-    }
-
-    public static class InvalidImageExtensionException extends InvalidExtensionException {
-        private static final long serialVersionUID = 1L;
-
-        public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) {
-            super(allowedExtension, extension, filename);
-        }
-    }
-
-    public static class InvalidFlashExtensionException extends InvalidExtensionException {
-        private static final long serialVersionUID = 1L;
-
-        public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) {
-            super(allowedExtension, extension, filename);
-        }
-    }
-
-    public static class InvalidMediaExtensionException extends InvalidExtensionException {
-        private static final long serialVersionUID = 1L;
-
-        public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) {
-            super(allowedExtension, extension, filename);
-        }
-    }
-
-    public static class InvalidVideoExtensionException extends InvalidExtensionException {
-        private static final long serialVersionUID = 1L;
-
-        public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) {
-            super(allowedExtension, extension, filename);
-        }
-    }
-}

+ 0 - 35
ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java

@@ -1,35 +0,0 @@
-package com.ruoyi.common.exception.job;
-
-/**
- * 计划策略异常
- *
- * @deprecated 3.4.0删除 迁移至xxl-job
- * @author ruoyi
- */
-public class TaskException extends Exception
-{
-    private static final long serialVersionUID = 1L;
-
-    private Code code;
-
-    public TaskException(String msg, Code code)
-    {
-        this(msg, code, null);
-    }
-
-    public TaskException(String msg, Code code, Exception nestedEx)
-    {
-        super(msg, nestedEx);
-        this.code = code;
-    }
-
-    public Code getCode()
-    {
-        return code;
-    }
-
-    public enum Code
-    {
-        TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
-    }
-}

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

@@ -11,7 +11,9 @@ import java.util.List;
  * 字典工具类
  *
  * @author ruoyi
+ * @deprecated 3.5.0 版本删除 迁移至 {@link com.ruoyi.common.core.service.DictService}
  */
+@Deprecated
 public class DictUtils {
 
     /**
@@ -36,9 +38,8 @@ public class DictUtils {
      * @return dictDatas 字典数据列表
      */
     public static List<SysDictData> getDictCache(String key) {
-        Object cacheObj = RedisUtils.getCacheObject(getCacheKey(key));
-        if (StringUtils.isNotNull(cacheObj)) {
-            List<SysDictData> dictDatas = (List<SysDictData>) cacheObj;
+        List<SysDictData> dictDatas = RedisUtils.getCacheObject(getCacheKey(key));
+        if (StringUtils.isNotNull(dictDatas)) {
             return dictDatas;
         }
         return null;

+ 7 - 2
ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java

@@ -1,5 +1,6 @@
 package com.ruoyi.common.utils;
 
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.HttpStatus;
 import com.baomidou.mybatisplus.core.metadata.OrderItem;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -63,7 +64,9 @@ public class PageUtils {
         }
         PagePlus<T, K> page = new PagePlus<>(pageNum, pageSize);
         OrderItem orderItem = buildOrderItem(orderByColumn, isAsc);
-        page.addOrder(orderItem);
+        if (ObjectUtil.isNotNull(orderItem)) {
+            page.addOrder(orderItem);
+        }
         return page;
     }
 
@@ -87,7 +90,9 @@ public class PageUtils {
         }
         Page<T> page = new Page<>(pageNum, pageSize);
         OrderItem orderItem = buildOrderItem(orderByColumn, isAsc);
-        page.addOrder(orderItem);
+        if (ObjectUtil.isNotNull(orderItem)) {
+            page.addOrder(orderItem);
+        }
         return page;
     }
 

+ 24 - 1
ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisUtils.java

@@ -88,7 +88,30 @@ public class RedisUtils {
      * @param value 缓存的值
      */
     public static <T> void setCacheObject(final String key, final T value) {
-        client.getBucket(key).set(value);
+        setCacheObject(key, value, false);
+    }
+
+    /**
+     * 缓存基本的对象,保留当前对象 TTL 有效期
+     *
+     * @param key   缓存的键值
+     * @param value 缓存的值
+     * @param isSaveTtl 是否保留TTL有效期(例如: set之前ttl剩余90 set之后还是为90)
+     * @since Redis 6.X 以上使用 setAndKeepTTL 兼容 5.X 方案
+     */
+    public static <T> void setCacheObject(final String key, final T value, final boolean isSaveTtl) {
+        RBucket<Object> bucket = client.getBucket(key);
+        if (isSaveTtl) {
+            try {
+                bucket.setAndKeepTTL(value);
+            } catch (Exception e) {
+                long timeToLive = bucket.remainTimeToLive();
+                bucket.set(value);
+                bucket.expire(timeToLive, TimeUnit.MILLISECONDS);
+            }
+        } else {
+            bucket.set(value);
+        }
     }
 
     /**

+ 31 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeBuildUtils.java

@@ -0,0 +1,31 @@
+package com.ruoyi.common.utils;
+
+import cn.hutool.core.lang.tree.Tree;
+import cn.hutool.core.lang.tree.TreeNodeConfig;
+import cn.hutool.core.lang.tree.TreeUtil;
+import cn.hutool.core.lang.tree.parser.NodeParser;
+
+import java.util.List;
+
+/**
+ * 扩展 hutool TreeUtil 封装系统树构建
+ *
+ * @author Lion Li
+ */
+public class TreeBuildUtils extends TreeUtil {
+
+    /**
+     * 根据前端定制差异化字段
+     */
+    public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label");
+
+    /**
+     * 默认树父节点id
+     */
+    public static final Long DEFAULT_PARENT_ID = 0L;
+
+    public static <T> List<Tree<Long>> build(List<T> list, NodeParser<T, Long> nodeParser) {
+        return TreeUtil.build(list, DEFAULT_PARENT_ID, DEFAULT_CONFIG, nodeParser);
+    }
+
+}

+ 25 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/ValidatorUtils.java

@@ -0,0 +1,25 @@
+package com.ruoyi.common.utils;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * Validator 校验框架工具
+ *
+ * @author Lion Li
+ */
+public class ValidatorUtils {
+
+	private static final Validator VALID = Validation.buildDefaultValidatorFactory().getValidator();
+
+	public static <T> void validate(T object, Class<?>... groups) {
+        Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);
+        if (!validate.isEmpty()) {
+            throw new ConstraintViolationException("参数校验异常", validate);
+        }
+    }
+
+}

+ 125 - 119
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java

@@ -4,7 +4,9 @@ import cn.hutool.core.util.IdUtil;
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
 import com.ruoyi.common.convert.ExcelBigNumberConvert;
-import com.ruoyi.common.utils.DictUtils;
+import com.ruoyi.common.excel.DefaultExcelListener;
+import com.ruoyi.common.excel.ExcelListener;
+import com.ruoyi.common.excel.ExcelResult;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.file.FileUtils;
 
@@ -21,129 +23,133 @@ import java.util.List;
  */
 public class ExcelUtil {
 
-	/**
-	 * 对excel表单默认第一个索引名转换成list(EasyExcel)
-	 *
-	 * @param is 输入流
-	 * @return 转换后集合
-	 */
-	public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
-		return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
-	}
+    /**
+     * 同步导入(适用于小数据量)
+     *
+     * @param is 输入流
+     * @return 转换后集合
+     */
+    public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
+        return EasyExcel.read(is).head(clazz).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();
-			FileUtils.setAttachmentResponseHeader(response, filename);
-			response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
-			ServletOutputStream os = response.getOutputStream();
-			EasyExcel.write(os, clazz)
-				.autoCloseStream(false)
-				// 自动适配
-				.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
-				// 大数值自动转换 防止失真
-				.registerConverter(new ExcelBigNumberConvert())
-				.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);
-	}
+    /**
+     * 使用校验监听器 异步导入 同步返回
+     *
+     * @param is         输入流
+     * @param clazz      对象类型
+     * @param isValidate 是否 Validator 检验 默认为是
+     * @return 转换后集合
+     */
+    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) {
+        DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate);
+        EasyExcel.read(is, clazz, listener).sheet().doRead();
+        return listener.getExcelResult();
+    }
 
-	/**
-	 * 反向解析值 男=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 is       输入流
+     * @param clazz    对象类型
+     * @param listener 自定义监听器
+     * @return 转换后集合
+     */
+    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, ExcelListener<T> listener) {
+        EasyExcel.read(is, clazz, listener).sheet().doRead();
+        return listener.getExcelResult();
+    }
 
-	/**
-	 * 解析字典值
-	 *
-	 * @param dictValue 字典值
-	 * @param dictType  字典类型
-	 * @param separator 分隔符
-	 * @return 字典标签
-	 */
-	public static String convertDictByExp(String dictValue, String dictType, String separator) {
-		return DictUtils.getDictLabel(dictType, dictValue, separator);
-	}
+    /**
+     * 导出excel
+     *
+     * @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();
+            FileUtils.setAttachmentResponseHeader(response, filename);
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
+            ServletOutputStream os = response.getOutputStream();
+            EasyExcel.write(os, clazz)
+                .autoCloseStream(false)
+                // 自动适配
+                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                // 大数值自动转换 防止失真
+                .registerConverter(new ExcelBigNumberConvert())
+                .sheet(sheetName).doWrite(list);
+        } catch (IOException e) {
+            throw new RuntimeException("导出Excel异常");
+        }
+    }
 
-	/**
-	 * 反向解析值字典值
-	 *
-	 * @param dictLabel 字典标签
-	 * @param dictType  字典类型
-	 * @param separator 分隔符
-	 * @return 字典值
-	 */
-	public static String reverseDictByExp(String dictLabel, String dictType, String separator) {
-		return DictUtils.getDictValue(dictType, dictLabel, separator);
-	}
+    /**
+     * 解析导出值 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);
+    }
 
-	/**
-	 * 编码文件名
-	 */
-	public static String encodingFilename(String filename) {
-		return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
-	}
+    /**
+     * 反向解析值 男=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);
+    }
+
+    /**
+     * 编码文件名
+     */
+    public static String encodingFilename(String filename) {
+        return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
+    }
 
 }

+ 0 - 37
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/FeignTestController.java

@@ -1,37 +0,0 @@
-package com.ruoyi.demo.controller;
-
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.demo.feign.FeignTestService;
-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.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * feign测试controller
- *
- * @author Lion Li
- * @deprecated 由于使用人数较少 决定与 3.4.0 版本移除
- */
-@Api(value = "feign测试", tags = {"feign测试"})
-@RequiredArgsConstructor(onConstructor_ = @Autowired)
-@RestController
-@RequestMapping("/feign/test")
-public class FeignTestController {
-
-    private final FeignTestService feignTestService;
-
-    /**
-     * 搜索数据
-     */
-    @ApiOperation("测试使用feign请求数据")
-    @GetMapping("/search/{wd}")
-    public AjaxResult search(@PathVariable String wd) {
-        String search = feignTestService.search(wd);
-        return AjaxResult.success("操作成功",search);
-    }
-}

+ 67 - 67
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java

@@ -28,75 +28,75 @@ import java.util.concurrent.TimeUnit;
 @RequestMapping("/demo/cache")
 public class RedisCacheController {
 
-	/**
-	 * 测试 @Cacheable
-	 *
-	 * 表示这个方法有了缓存的功能,方法的返回值会被缓存下来
-	 * 下一次调用该方法前,会去检查是否缓存中已经有值
-	 * 如果有就直接返回,不调用方法
-	 * 如果没有,就调用方法,然后把结果缓存起来
-	 * 这个注解「一般用在查询方法上」
-	 *
-	 * 重点说明: 缓存注解严谨与其他筛选数据功能一起使用
-	 * 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题
-	 *
-	 * cacheNames 为配置文件内 groupId
-	 */
-	@ApiOperation("测试 @Cacheable")
-	@Cacheable(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
-	@GetMapping("/test1")
-	public AjaxResult<String> test1(String key, String value){
-		return AjaxResult.success("操作成功", value);
-	}
+    /**
+     * 测试 @Cacheable
+     * <p>
+     * 表示这个方法有了缓存的功能,方法的返回值会被缓存下来
+     * 下一次调用该方法前,会去检查是否缓存中已经有值
+     * 如果有就直接返回,不调用方法
+     * 如果没有,就调用方法,然后把结果缓存起来
+     * 这个注解「一般用在查询方法上」
+     * <p>
+     * 重点说明: 缓存注解严谨与其他筛选数据功能一起使用
+     * 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题
+     * <p>
+     * cacheNames 为配置文件内 groupId
+     */
+    @ApiOperation("测试 @Cacheable")
+    @Cacheable(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
+    @GetMapping("/test1")
+    public AjaxResult<String> test1(String key, String value) {
+        return AjaxResult.success("操作成功", value);
+    }
 
-	/**
-	 * 测试 @CachePut
-	 *
-	 * 加了@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用
-	 * 它「通常用在新增方法上」
-	 *
-	 * cacheNames 为 配置文件内 groupId
-	 */
-	@ApiOperation("测试 @CachePut")
-	@CachePut(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
-	@GetMapping("/test2")
-	public AjaxResult<String> test2(String key, String value){
-		return AjaxResult.success("操作成功", value);
-	}
+    /**
+     * 测试 @CachePut
+     * <p>
+     * 加了@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用
+     * 它「通常用在新增方法上」
+     * <p>
+     * cacheNames 为 配置文件内 groupId
+     */
+    @ApiOperation("测试 @CachePut")
+    @CachePut(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
+    @GetMapping("/test2")
+    public AjaxResult<String> test2(String key, String value) {
+        return AjaxResult.success("操作成功", value);
+    }
 
-	/**
-	 * 测试 @CacheEvict
-	 *
-	 * 使用了CacheEvict注解的方法,会清空指定缓存
-	 * 「一般用在更新或者删除的方法上」
-	 *
-	 * cacheNames 为 配置文件内 groupId
-	 */
-	@ApiOperation("测试 @CacheEvict")
-	@CacheEvict(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
-	@GetMapping("/test3")
-	public AjaxResult<String> test3(String key, String value){
-		return AjaxResult.success("操作成功", value);
-	}
+    /**
+     * 测试 @CacheEvict
+     * <p>
+     * 使用了CacheEvict注解的方法,会清空指定缓存
+     * 「一般用在更新或者删除的方法上」
+     * <p>
+     * cacheNames 为 配置文件内 groupId
+     */
+    @ApiOperation("测试 @CacheEvict")
+    @CacheEvict(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
+    @GetMapping("/test3")
+    public AjaxResult<String> test3(String key, String value) {
+        return AjaxResult.success("操作成功", value);
+    }
 
-	/**
-	 * 测试设置过期时间
-	 * 手动设置过期时间10秒
-	 * 11秒后获取 判断是否相等
-	 */
-	@ApiOperation("测试设置过期时间")
-	@GetMapping("/test6")
-	public AjaxResult<Boolean> test6(String key, String value){
-		RedisUtils.setCacheObject(key, value);
-		boolean flag = RedisUtils.expire(key, 10, TimeUnit.SECONDS);
-		System.out.println("***********" + flag);
-		try {
-			Thread.sleep(11 * 1000);
-		} catch (InterruptedException e) {
-			e.printStackTrace();
-		}
-		Object obj = RedisUtils.getCacheObject(key);
-		return AjaxResult.success("操作成功", value.equals(obj));
-	}
+    /**
+     * 测试设置过期时间
+     * 手动设置过期时间10秒
+     * 11秒后获取 判断是否相等
+     */
+    @ApiOperation("测试设置过期时间")
+    @GetMapping("/test6")
+    public AjaxResult<Boolean> test6(String key, String value) {
+        RedisUtils.setCacheObject(key, value);
+        boolean flag = RedisUtils.expire(key, 10, TimeUnit.SECONDS);
+        System.out.println("***********" + flag);
+        try {
+            Thread.sleep(11 * 1000);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        Object obj = RedisUtils.getCacheObject(key);
+        return AjaxResult.success("操作成功", value.equals(obj));
+    }
 
 }

+ 43 - 53
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java

@@ -9,7 +9,6 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.cache.annotation.Cacheable;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
@@ -28,59 +27,50 @@ import java.time.LocalTime;
 @RequestMapping("/demo/redisLock")
 public class RedisLockController {
 
-	@Autowired
-	private LockTemplate lockTemplate;
+    @Autowired
+    private LockTemplate lockTemplate;
 
-	/**
-	 * 测试lock4j 注解
-	 */
-	@ApiOperation("测试lock4j 注解")
-	@Lock4j(keys = {"#key"})
-	@GetMapping("/testLock4j")
-	public  AjaxResult<String> testLock4j(String key,String value){
-		System.out.println("start:"+key+",time:"+ LocalTime.now().toString());
-		try {
-			Thread.sleep(10000);
-		} catch (InterruptedException e) {
-			e.printStackTrace();
-		}
-		System.out.println("end :"+key+",time:"+LocalTime.now().toString());
-		return AjaxResult.success("操作成功",value);
-	}
+    /**
+     * 测试lock4j 注解
+     */
+    @ApiOperation("测试lock4j 注解")
+    @Lock4j(keys = {"#key"})
+    @GetMapping("/testLock4j")
+    public AjaxResult<String> testLock4j(String key, String value) {
+        System.out.println("start:" + key + ",time:" + LocalTime.now().toString());
+        try {
+            Thread.sleep(10000);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        System.out.println("end :" + key + ",time:" + LocalTime.now().toString());
+        return AjaxResult.success("操作成功", value);
+    }
 
-	/**
-	 * 测试lock4j 工具
-	 */
-	@ApiOperation("测试lock4j 工具")
-	@GetMapping("/testLock4jLockTemaplate")
-	public  AjaxResult<String> testLock4jLockTemaplate(String key,String value){
-		final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class);
-		if (null == lockInfo) {
-			throw new RuntimeException("业务处理中,请稍后再试");
-		}
-		// 获取锁成功,处理业务
-		try {
-			try {
-				Thread.sleep(8000);
-			} catch (InterruptedException e) {
-				//
-			}
-			System.out.println("执行简单方法1 , 当前线程:" + Thread.currentThread().getName());
-		} finally {
-			//释放锁
-			lockTemplate.releaseLock(lockInfo);
-		}
-		//结束
-		return AjaxResult.success("操作成功",value);
-	}
+    /**
+     * 测试lock4j 工具
+     */
+    @ApiOperation("测试lock4j 工具")
+    @GetMapping("/testLock4jLockTemaplate")
+    public AjaxResult<String> testLock4jLockTemaplate(String key, String value) {
+        final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class);
+        if (null == lockInfo) {
+            throw new RuntimeException("业务处理中,请稍后再试");
+        }
+        // 获取锁成功,处理业务
+        try {
+            try {
+                Thread.sleep(8000);
+            } catch (InterruptedException e) {
+                //
+            }
+            System.out.println("执行简单方法1 , 当前线程:" + Thread.currentThread().getName());
+        } finally {
+            //释放锁
+            lockTemplate.releaseLock(lockInfo);
+        }
+        //结束
+        return AjaxResult.success("操作成功", value);
+    }
 
-	/**
-	 * 测试spring-cache注解
-	 */
-	@ApiOperation("测试spring-cache注解")
-	@Cacheable(value = "test", key = "#key")
-	@GetMapping("/testCache")
-	public AjaxResult<String> testCache(String key) {
-		return AjaxResult.success("操作成功", key);
-	}
 }

+ 17 - 16
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisPubSubController.java

@@ -4,6 +4,7 @@ import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.utils.RedisUtils;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -21,22 +22,22 @@ import org.springframework.web.bind.annotation.RestController;
 @RequestMapping("/demo/redis/pubsub")
 public class RedisPubSubController {
 
-	@ApiOperation("发布消息")
-	@GetMapping("/pub")
-	public AjaxResult<Void> pub(String key, String value){
-		RedisUtils.publish(key, value, consumer -> {
-			System.out.println("发布通道 => " + key + ", 发送值 => " + value);
-		});
-		return AjaxResult.success("操作成功");
-	}
+    @ApiOperation("发布消息")
+    @GetMapping("/pub")
+    public AjaxResult<Void> pub(@ApiParam("通道Key") String key, @ApiParam("发送内容") String value) {
+        RedisUtils.publish(key, value, consumer -> {
+            System.out.println("发布通道 => " + key + ", 发送值 => " + value);
+        });
+        return AjaxResult.success("操作成功");
+    }
 
-	@ApiOperation("订阅消息")
-	@GetMapping("/sub")
-	public AjaxResult<Void> sub(String key){
-		RedisUtils.subscribe(key, String.class, msg -> {
-			System.out.println("订阅通道 => " + key + ", 接收值 => " + msg);
-		});
-		return AjaxResult.success("操作成功");
-	}
+    @ApiOperation("订阅消息")
+    @GetMapping("/sub")
+    public AjaxResult<Void> sub(@ApiParam("通道Key") String key) {
+        RedisUtils.subscribe(key, String.class, msg -> {
+            System.out.println("订阅通道 => " + key + ", 接收值 => " + msg);
+        });
+        return AjaxResult.success("操作成功");
+    }
 
 }

+ 30 - 30
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java

@@ -22,37 +22,37 @@ import org.springframework.web.bind.annotation.RestController;
 @RequestMapping("/demo/rateLimiter")
 public class RedisRateLimiterController {
 
-	/**
-	 * 测试全局限流
-	 * 全局影响
-	 */
-	@ApiOperation("测试全局限流")
-	@RateLimiter(count = 2, time = 10)
-	@GetMapping("/test")
-	public  AjaxResult<String> test(String value){
-		return AjaxResult.success("操作成功",value);
-	}
+    /**
+     * 测试全局限流
+     * 全局影响
+     */
+    @ApiOperation("测试全局限流")
+    @RateLimiter(count = 2, time = 10)
+    @GetMapping("/test")
+    public AjaxResult<String> test(String value) {
+        return AjaxResult.success("操作成功", value);
+    }
 
-	/**
-	 * 测试请求IP限流
-	 * 同一IP请求受影响
-	 */
-	@ApiOperation("测试请求IP限流")
-	@RateLimiter(count = 2, time = 10, limitType = LimitType.IP)
-	@GetMapping("/testip")
-	public  AjaxResult<String> testip(String value){
-		return AjaxResult.success("操作成功",value);
-	}
+    /**
+     * 测试请求IP限流
+     * 同一IP请求受影响
+     */
+    @ApiOperation("测试请求IP限流")
+    @RateLimiter(count = 2, time = 10, limitType = LimitType.IP)
+    @GetMapping("/testip")
+    public AjaxResult<String> testip(String value) {
+        return AjaxResult.success("操作成功", value);
+    }
 
-	/**
-	 * 测试集群实例限流
-	 * 启动两个后端服务互不影响
-	 */
-	@ApiOperation("测试集群实例限流")
-	@RateLimiter(count = 2, time = 10, limitType = LimitType.CLUSTER)
-	@GetMapping("/testcluster")
-	public  AjaxResult<String> testcluster(String value){
-		return AjaxResult.success("操作成功",value);
-	}
+    /**
+     * 测试集群实例限流
+     * 启动两个后端服务互不影响
+     */
+    @ApiOperation("测试集群实例限流")
+    @RateLimiter(count = 2, time = 10, limitType = LimitType.CLUSTER)
+    @GetMapping("/testcluster")
+    public AjaxResult<String> testcluster(String value) {
+        return AjaxResult.success("操作成功", value);
+    }
 
 }

+ 13 - 13
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/Swagger3DemoController.java

@@ -21,18 +21,18 @@ import org.springframework.web.multipart.MultipartFile;
 @RequestMapping("/swagger/demo")
 public class Swagger3DemoController {
 
-	/**
-	 * 上传请求
-	 * 必须使用 @RequestPart 注解标注为文件
-	 * dataType 必须为 "java.io.File"
-	 */
-	@ApiOperation(value = "通用上传请求")
-	@ApiImplicitParams({
-		@ApiImplicitParam(name = "file", value = "文件", dataType = "java.io.File", required = true),
-	})
-	@PostMapping(value = "/upload")
-	public AjaxResult<String> upload(@RequestPart("file") MultipartFile file) {
-		return AjaxResult.success("操作成功", file.getOriginalFilename());
-	}
+    /**
+     * 上传请求
+     * 必须使用 @RequestPart 注解标注为文件
+     * dataType 必须为 "java.io.File"
+     */
+    @ApiOperation(value = "通用上传请求")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "file", value = "文件", dataType = "java.io.File", required = true),
+    })
+    @PostMapping(value = "/upload")
+    public AjaxResult<String> upload(@RequestPart("file") MultipartFile file) {
+        return AjaxResult.success("操作成功", file.getOriginalFilename());
+    }
 
 }

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

@@ -34,48 +34,48 @@ public class TestBatchController extends BaseController {
     /**
      * 新增批量方法 可完美替代 saveBatch 秒级插入上万数据 (对mysql负荷较大)
      */
-	@ApiOperation(value = "新增批量方法")
+    @ApiOperation(value = "新增批量方法")
     @PostMapping("/add")
 //	@DataSource(DataSourceType.SLAVE)
     public AjaxResult<Void> add() {
-		List<TestDemo> list = new ArrayList<>();
-		for (int i = 0; i < 1000; i++) {
-			list.add(new TestDemo().setOrderNum(-1L).setTestKey("批量新增").setValue("测试新增"));
-		}
+        List<TestDemo> list = new ArrayList<>();
+        for (int i = 0; i < 1000; i++) {
+            list.add(new TestDemo().setOrderNum(-1L).setTestKey("批量新增").setValue("测试新增"));
+        }
         return toAjax(iTestDemoService.saveAll(list) ? 1 : 0);
     }
 
-	/**
-	 * 新增或更新 可完美替代 saveOrUpdateBatch 高性能
-	 */
-	@ApiOperation(value = "新增或更新批量方法")
-	@PostMapping("/addOrUpdate")
+    /**
+     * 新增或更新 可完美替代 saveOrUpdateBatch 高性能
+     */
+    @ApiOperation(value = "新增或更新批量方法")
+    @PostMapping("/addOrUpdate")
 //	@DataSource(DataSourceType.SLAVE)
-	public AjaxResult<Void> addOrUpdate() {
-		List<TestDemo> list = new ArrayList<>();
-		for (int i = 0; i < 1000; i++) {
-			list.add(new TestDemo().setOrderNum(-1L).setTestKey("批量新增").setValue("测试新增"));
-		}
-		iTestDemoService.saveAll(list);
-		for (int i = 0; i < list.size(); i++) {
-			TestDemo testDemo = list.get(i);
-			testDemo.setTestKey("批量新增或修改").setValue("批量新增或修改");
-			if (i % 2 == 0) {
-				testDemo.setId(null);
-			}
-		}
-		return toAjax(iTestDemoService.saveOrUpdateAll(list) ? 1 : 0);
-	}
+    public AjaxResult<Void> addOrUpdate() {
+        List<TestDemo> list = new ArrayList<>();
+        for (int i = 0; i < 1000; i++) {
+            list.add(new TestDemo().setOrderNum(-1L).setTestKey("批量新增").setValue("测试新增"));
+        }
+        iTestDemoService.saveAll(list);
+        for (int i = 0; i < list.size(); i++) {
+            TestDemo testDemo = list.get(i);
+            testDemo.setTestKey("批量新增或修改").setValue("批量新增或修改");
+            if (i % 2 == 0) {
+                testDemo.setId(null);
+            }
+        }
+        return toAjax(iTestDemoService.saveOrUpdateAll(list) ? 1 : 0);
+    }
 
     /**
      * 删除批量方法
      */
-	@ApiOperation(value = "删除批量方法")
+    @ApiOperation(value = "删除批量方法")
     @DeleteMapping()
 //	@DataSource(DataSourceType.SLAVE)
     public AjaxResult<Void> remove() {
         return toAjax(iTestDemoService.remove(new LambdaQueryWrapper<TestDemo>()
-			.eq(TestDemo::getOrderNum, -1L)) ? 1 : 0);
+            .eq(TestDemo::getOrderNum, -1L)) ? 1 : 0);
     }
 
 }

+ 36 - 11
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java

@@ -1,6 +1,7 @@
 package com.ruoyi.demo.controller;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.bean.BeanUtil;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.annotation.RepeatSubmit;
 import com.ruoyi.common.core.controller.BaseController;
@@ -10,16 +11,20 @@ import com.ruoyi.common.core.validate.AddGroup;
 import com.ruoyi.common.core.validate.EditGroup;
 import com.ruoyi.common.core.validate.QueryGroup;
 import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.ValidatorUtils;
+import com.ruoyi.common.excel.ExcelResult;
 import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.demo.domain.TestDemo;
 import com.ruoyi.demo.domain.bo.TestDemoBo;
+import com.ruoyi.demo.domain.bo.TestDemoImportVo;
 import com.ruoyi.demo.domain.vo.TestDemoVo;
 import com.ruoyi.demo.service.ITestDemoService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.constraints.NotEmpty;
@@ -63,20 +68,35 @@ public class TestDemoController extends BaseController {
 		return iTestDemoService.customPageList(bo);
 	}
 
-	/**
+    @ApiOperation("导入测试-校验")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "file", value = "导入文件", dataType = "java.io.File", required = true),
+    })
+    @Log(title = "测试单表", businessType = BusinessType.IMPORT)
+    @PreAuthorize("@ss.hasPermi('demo:demo:import')")
+    @PostMapping("/importData")
+    public AjaxResult<Void> importData(@RequestPart("file") MultipartFile file) throws Exception {
+        ExcelResult<TestDemoImportVo> excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true);
+        List<TestDemoImportVo> volist = excelResult.getList();
+        List<TestDemo> list = BeanUtil.copyToList(volist, TestDemo.class);
+        iTestDemoService.saveAll(list);
+        return AjaxResult.success(excelResult.getAnalysis());
+    }
+
+    /**
      * 导出测试单表列表
      */
     @ApiOperation("导出测试单表列表")
     @SaCheckPermission("demo:demo:export")
     @Log(title = "测试单表", businessType = BusinessType.EXPORT)
-    @GetMapping("/export")
+    @PostMapping("/export")
     public void export(@Validated TestDemoBo bo, HttpServletResponse response) {
         List<TestDemoVo> list = iTestDemoService.queryList(bo);
-		// 测试雪花id导出
+        // 测试雪花id导出
 //        for (TestDemoVo vo : list) {
 //			vo.setId(1234567891234567893L);
 //		}
-		ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, response);
+        ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, response);
     }
 
     /**
@@ -85,8 +105,9 @@ public class TestDemoController extends BaseController {
     @ApiOperation("获取测试单表详细信息")
     @SaCheckPermission("demo:demo:query")
     @GetMapping("/{id}")
-    public AjaxResult<TestDemoVo> getInfo(@NotNull(message = "主键不能为空")
-                                                  @PathVariable("id") Long id) {
+    public AjaxResult<TestDemoVo> getInfo(@ApiParam("测试ID")
+                                          @NotNull(message = "主键不能为空")
+                                          @PathVariable("id") Long id) {
         return AjaxResult.success(iTestDemoService.queryById(id));
     }
 
@@ -98,7 +119,10 @@ public class TestDemoController extends BaseController {
     @Log(title = "测试单表", businessType = BusinessType.INSERT)
     @RepeatSubmit(interval = 2, timeUnit = TimeUnit.SECONDS, message = "不允许重复提交")
     @PostMapping()
-    public AjaxResult<Void> add(@Validated(AddGroup.class) @RequestBody TestDemoBo bo) {
+    public AjaxResult<Void> add(@RequestBody TestDemoBo bo) {
+        // 使用校验工具对标 @Validated(AddGroup.class) 注解
+        // 用于在非 Controller 的地方校验对象
+        ValidatorUtils.validate(bo, AddGroup.class);
         return toAjax(iTestDemoService.insertByBo(bo) ? 1 : 0);
     }
 
@@ -121,8 +145,9 @@ public class TestDemoController extends BaseController {
     @SaCheckPermission("demo:demo:remove")
     @Log(title = "测试单表" , businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
-    public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
-                                       @PathVariable Long[] ids) {
+    public AjaxResult<Void> remove(@ApiParam("测试ID串")
+                                   @NotEmpty(message = "主键不能为空")
+                                   @PathVariable Long[] ids) {
         return toAjax(iTestDemoService.deleteWithValidByIds(Arrays.asList(ids), true) ? 1 : 0);
     }
 }

+ 44 - 1
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestI18nController.java

@@ -4,16 +4,24 @@ import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.utils.MessageUtils;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.Data;
+import org.hibernate.validator.constraints.Range;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
 
 /**
  * 测试国际化
  *
  * @author Lion Li
  */
+@Validated
 @Api(value = "测试国际化控制器", tags = {"测试国际化管理"})
 @RestController
 @RequestMapping("/demo/i18n")
@@ -27,7 +35,42 @@ public class TestI18nController {
 	 */
 	@ApiOperation("通过code获取国际化内容")
 	@GetMapping()
-	public AjaxResult<Void> get(String code) {
+	public AjaxResult<Void> get(@ApiParam("国际化code") String code) {
 		return AjaxResult.success(MessageUtils.message(code));
 	}
+
+    /**
+     * Validator 校验国际化
+     * 不传值 分别查看异常返回
+     *
+     * 测试使用 not.null
+     */
+    @ApiOperation("Validator 校验国际化")
+    @GetMapping("/test1")
+    public AjaxResult<Void> test1(@NotBlank(message = "{not.null}") String str) {
+        return AjaxResult.success(str);
+    }
+
+    /**
+     * Bean 校验国际化
+     * 不传值 分别查看异常返回
+     *
+     * 测试使用 not.null
+     */
+    @ApiOperation("Bean 校验国际化")
+    @GetMapping("/test2")
+    public AjaxResult<TestI18nBo> test2(@Validated TestI18nBo bo) {
+        return AjaxResult.success(bo);
+    }
+
+    @Data
+    public static class TestI18nBo {
+
+        @NotBlank(message = "{not.null}")
+        private String name;
+
+        @NotNull(message = "{not.null}")
+        @Range(min = 0, max = 100, message = "{length.not.valid}")
+        private Integer age;
+    }
 }

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

@@ -15,6 +15,7 @@ import com.ruoyi.demo.domain.vo.TestTreeVo;
 import com.ruoyi.demo.service.ITestTreeService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -61,7 +62,7 @@ public class TestTreeController extends BaseController {
     @GetMapping("/export")
     public void export(@Validated TestTreeBo bo, HttpServletResponse response) {
         List<TestTreeVo> list = iTestTreeService.queryList(bo);
-		ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response);
+        ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response);
     }
 
     /**
@@ -70,8 +71,9 @@ public class TestTreeController extends BaseController {
     @ApiOperation("获取测试树表详细信息")
     @SaCheckPermission("demo:tree:query")
     @GetMapping("/{id}")
-    public AjaxResult<TestTreeVo> getInfo(@NotNull(message = "主键不能为空")
-                                                  @PathVariable("id") Long id) {
+    public AjaxResult<TestTreeVo> getInfo(@ApiParam("测试树ID")
+                                          @NotNull(message = "主键不能为空")
+                                          @PathVariable("id") Long id) {
         return AjaxResult.success(iTestTreeService.queryById(id));
     }
 
@@ -106,8 +108,9 @@ public class TestTreeController extends BaseController {
     @SaCheckPermission("demo:tree:remove")
     @Log(title = "测试树表" , businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
-    public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
-                                       @PathVariable Long[] ids) {
+    public AjaxResult<Void> remove(@ApiParam("测试树ID串")
+                                   @NotEmpty(message = "主键不能为空")
+                                   @PathVariable Long[] ids) {
         return toAjax(iTestTreeService.deleteWithValidByIds(Arrays.asList(ids), true) ? 1 : 0);
     }
 }

+ 61 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoImportVo.java

@@ -0,0 +1,61 @@
+package com.ruoyi.demo.domain.bo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 测试单表业务对象 test_demo
+ *
+ * @author Lion Li
+ * @date 2021-07-26
+ */
+@Data
+@ApiModel("测试单表业务对象")
+public class TestDemoImportVo {
+
+    /**
+     * 部门id
+     */
+    @ApiModelProperty("部门id")
+    @NotNull(message = "部门id不能为空")
+    @ExcelProperty(value = "部门id")
+    private Long deptId;
+
+    /**
+     * 用户id
+     */
+    @ApiModelProperty("用户id")
+    @NotNull(message = "用户id不能为空")
+    @ExcelProperty(value = "用户id")
+    private Long userId;
+
+    /**
+     * 排序号
+     */
+    @ApiModelProperty("排序号")
+    @NotNull(message = "排序号不能为空")
+    @ExcelProperty(value = "排序号")
+    private Long orderNum;
+
+    /**
+     * key键
+     */
+    @ApiModelProperty("key键")
+    @NotBlank(message = "key键不能为空")
+    @ExcelProperty(value = "key键")
+    private String testKey;
+
+    /**
+     * 值
+     */
+    @ApiModelProperty("值")
+    @NotBlank(message = "值不能为空")
+    @ExcelProperty(value = "值")
+    private String value;
+
+}

+ 0 - 27
ruoyi-demo/src/main/java/com/ruoyi/demo/feign/FeignTestService.java

@@ -1,27 +0,0 @@
-package com.ruoyi.demo.feign;
-
-import com.ruoyi.demo.feign.constant.FeignTestConstant;
-import com.ruoyi.demo.feign.fallback.FeignTestFallback;
-import org.springframework.cloud.openfeign.FeignClient;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-
-/**
- * feign测试service
- * 规范接口 Service 无感调用
- * 常量管理请求路径 更加规范
- * 自定义容错处理 安全可靠 (需自行配置熔断器)
- * 增加 feign 的目的为使 http 请求接口化
- *
- * @author Lion Li
- * @deprecated 由于使用人数较少 决定与 3.4.0 版本移除
- */
-@FeignClient(
-	name = FeignTestConstant.BAIDU_NAME,
-	url = FeignTestConstant.BAIDU_URL,
-	fallback = FeignTestFallback.class)
-public interface FeignTestService {
-
-    @GetMapping("/s")
-    String search(@RequestParam("wd") String wd);
-}

+ 0 - 13
ruoyi-demo/src/main/java/com/ruoyi/demo/feign/constant/FeignTestConstant.java

@@ -1,13 +0,0 @@
-package com.ruoyi.demo.feign.constant;
-
-/**
- * @deprecated 由于使用人数较少 决定与 3.4.0 版本移除
- */
-@Deprecated
-public class FeignTestConstant {
-
-	public static final String BAIDU_NAME = "baidu";
-
-	public static final String BAIDU_URL = "http://www.baidu.com";
-
-}

+ 0 - 28
ruoyi-demo/src/main/java/com/ruoyi/demo/feign/fallback/FeignTestFallback.java

@@ -1,28 +0,0 @@
-package com.ruoyi.demo.feign.fallback;
-
-
-import com.ruoyi.demo.feign.FeignTestService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-/**
- * feign测试fallback
- * 自定义封装结构体熔断
- * 需重写解码器 根据自定义实体 自行解析熔断
- *
- * 熔断器需要自行添加配置
- *
- * @see {com.ruoyi.framework.config.FeignConfig#errorDecoder()}
- * @author Lion Li
- * @deprecated 由于使用人数较少 决定与 3.4.0 版本移除
- */
-@Slf4j
-@Component
-public class FeignTestFallback implements FeignTestService {
-
-    @Override
-    public String search(String wd) {
-        log.error("fallback");
-        return "报错啦";
-    }
-}

+ 0 - 1
ruoyi-demo/src/main/java/com/ruoyi/demo/feign/fallback/package-info.java

@@ -1 +0,0 @@
-package com.ruoyi.demo.feign.fallback;

+ 0 - 1
ruoyi-demo/src/main/java/com/ruoyi/demo/feign/package-info.java

@@ -1 +0,0 @@
-package com.ruoyi.demo.feign;

+ 0 - 4
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java

@@ -2,11 +2,9 @@ package com.ruoyi.demo.mapper;
 
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.common.core.mybatisplus.cache.MybatisPlusRedisCache;
 import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
 import com.ruoyi.demo.domain.TestDemo;
 import com.ruoyi.demo.domain.vo.TestDemoVo;
-import org.apache.ibatis.annotations.CacheNamespace;
 import org.apache.ibatis.annotations.Param;
 
 /**
@@ -15,8 +13,6 @@ import org.apache.ibatis.annotations.Param;
  * @author Lion Li
  * @date 2021-07-26
  */
-// 如使需切换数据源 请勿使用缓存 会造成数据不一致现象
-@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
 public interface TestDemoMapper extends BaseMapperPlus<TestDemo> {
 
     Page<TestDemoVo> customPageList(@Param("page") Page<TestDemo> page, @Param("ew") Wrapper<TestDemo> wrapper);

+ 0 - 0
ruoyi-demo/src/main/resources/mapper/demo/package-info.md


+ 3 - 0
ruoyi-demo/src/main/resources/mapper/package-info.md

@@ -0,0 +1,3 @@
+java包使用 `.` 分割 resource 目录使用 `/` 分割
+<br>
+此文件目的 防止文件夹粘连找不到 `xml` 文件

+ 6 - 0
ruoyi-extend/ruoyi-monitor-admin/pom.xml

@@ -28,6 +28,12 @@
             <groupId>de.codecentric</groupId>
             <artifactId>spring-boot-admin-starter-server</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>de.codecentric</groupId>
+            <artifactId>spring-boot-admin-starter-client</artifactId>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 2 - 0
ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/config/SecurityConfig.java

@@ -34,6 +34,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
 			//授予对所有静态资产和登录页面的公共访问权限。
 			.antMatchers(adminContextPath + "/assets/**").permitAll()
 			.antMatchers(adminContextPath + "/login").permitAll()
+            .antMatchers("/actuator").anonymous()
+            .antMatchers("/actuator/**").anonymous()
 			//必须对每个其他请求进行身份验证
 			.anyRequest().authenticated().and()
 			//配置登录和注销

+ 14 - 0
ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application-dev.yml

@@ -0,0 +1,14 @@
+--- # 监控配置
+spring:
+  boot:
+    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

+ 14 - 0
ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application-prod.yml

@@ -0,0 +1,14 @@
+--- # 监控配置
+spring:
+  boot:
+    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

+ 20 - 0
ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml

@@ -1,6 +1,12 @@
 server:
   port: 9090
+spring:
+  application:
+    name: ruoyi-monitor-admin
+  profiles:
+    active: @profiles.active@
 
+--- # 监控中心服务端配置
 spring:
   security:
     user:
@@ -9,3 +15,17 @@ spring:
   boot:
     admin:
       context-path: /admin
+
+--- # Actuator 监控端点的配置项
+management:
+  endpoints:
+    web:
+      # Actuator 提供的 API 接口的根目录。默认为 /actuator
+      base-path: /actuator
+      exposure:
+        # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+        # 生产环境不建议放开所有 根据项目需求放开即可
+        include: @endpoints.include@
+  endpoint:
+    logfile:
+      external-file: ./logs/ruoyi-monitor-admin.log

+ 5 - 0
ruoyi-extend/ruoyi-xxl-job-admin/pom.xml

@@ -71,6 +71,11 @@
 			<version>${mysql-connector-java.version}</version>
 		</dependency>
 
+        <dependency>
+            <groupId>de.codecentric</groupId>
+            <artifactId>spring-boot-admin-starter-client</artifactId>
+        </dependency>
+
 		<!-- xxl-job-core -->
 		<dependency>
 			<groupId>com.xuxueli</groupId>

+ 15 - 0
ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-dev.yml

@@ -1,3 +1,18 @@
+--- # 监控配置
+spring:
+  boot:
+    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
+
 --- # 数据库配置
 spring:
   datasource:

+ 15 - 0
ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-prod.yml

@@ -1,3 +1,18 @@
+--- # 监控配置
+spring:
+  boot:
+    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
+
 --- # 数据库配置
 spring:
   datasource:

+ 14 - 3
ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml

@@ -4,6 +4,8 @@ server:
   servlet:
     context-path: /xxl-job-admin
 spring:
+  application:
+    name: ruoyi-xxl-job-admin
   profiles:
     active: @profiles.active@
   mvc:
@@ -28,13 +30,22 @@ spring:
     suffix: .ftl
     templateLoaderPath: classpath:/templates/
 
---- # 监控配置
+--- # Actuator 监控端点的配置
 management:
   health:
     mail:
       enabled: false
-  server:
-    base-path: /actuator
+  endpoints:
+    web:
+      # Actuator 提供的 API 接口的根目录。默认为 /actuator
+      base-path: /actuator
+      exposure:
+        # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+        # 生产环境不建议放开所有 根据项目需求放开即可
+        include: @endpoints.include@
+  endpoint:
+    logfile:
+      external-file: ./logs/ruoyi-xxl-job-admin.log
 
 --- # xxljob系统配置
 xxl:

+ 1 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java

@@ -7,6 +7,7 @@ import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.reflect.ReflectUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Before;

+ 59 - 7
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java

@@ -1,13 +1,12 @@
 package com.ruoyi.framework.aspectj;
 
 import cn.dev33.satoken.SaManager;
-import cn.hutool.core.util.StrUtil;
 import cn.hutool.crypto.SecureUtil;
-import com.baomidou.lock.LockInfo;
-import com.baomidou.lock.LockTemplate;
 import com.ruoyi.common.annotation.RepeatSubmit;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.JsonUtils;
+import com.ruoyi.common.utils.RedisUtils;
 import com.ruoyi.common.utils.ServletUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.framework.config.properties.RepeatSubmitProperties;
@@ -18,8 +17,14 @@ import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Before;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * 防止重复提交
@@ -33,7 +38,6 @@ import javax.servlet.http.HttpServletRequest;
 public class RepeatSubmitAspect {
 
     private final RepeatSubmitProperties repeatSubmitProperties;
-    private final LockTemplate lockTemplate;
 
     @Before("@annotation(repeatSubmit)")
     public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable {
@@ -46,7 +50,7 @@ public class RepeatSubmitAspect {
             throw new ServiceException("重复提交间隔时间不能小于'1'秒");
         }
         HttpServletRequest request = ServletUtils.getRequest();
-        String nowParams = StrUtil.join(",", point.getArgs());
+        String nowParams = argsArrayToString(point.getArgs());
 
         // 请求地址(作为存放cache的key值)
         String url = request.getRequestURI();
@@ -59,10 +63,58 @@ public class RepeatSubmitAspect {
         submitKey = SecureUtil.md5(submitKey + ":" + nowParams);
         // 唯一标识(指定key + 消息头)
         String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
-        LockInfo lock = lockTemplate.lock(cacheRepeatKey, interval, interval / 2);
-        if (lock == null) {
+        String key = RedisUtils.getCacheObject(cacheRepeatKey);
+        if (key == null) {
+            RedisUtils.setCacheObject(cacheRepeatKey, "", interval, TimeUnit.MILLISECONDS);
+        } else {
             throw new ServiceException(repeatSubmit.message());
         }
     }
 
+    /**
+     * 参数拼装
+     */
+    private String argsArrayToString(Object[] paramsArray) {
+        StringBuilder params = new StringBuilder();
+        if (paramsArray != null && paramsArray.length > 0) {
+            for (Object o : paramsArray) {
+                if (StringUtils.isNotNull(o) && !isFilterObject(o)) {
+                    try {
+                        params.append(JsonUtils.toJsonString(o)).append(" ");
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+        return params.toString().trim();
+    }
+
+    /**
+     * 判断是否需要过滤的对象。
+     *
+     * @param o 对象信息。
+     * @return 如果是需要过滤的对象,则返回true;否则返回false。
+     */
+    @SuppressWarnings("rawtypes")
+    public boolean isFilterObject(final Object o) {
+        Class<?> clazz = o.getClass();
+        if (clazz.isArray()) {
+            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
+        } else if (Collection.class.isAssignableFrom(clazz)) {
+            Collection collection = (Collection) o;
+            for (Object value : collection) {
+                return value instanceof MultipartFile;
+            }
+        } else if (Map.class.isAssignableFrom(clazz)) {
+            Map map = (Map) o;
+            for (Object value : map.entrySet()) {
+                Map.Entry entry = (Map.Entry) value;
+                return entry.getValue() instanceof MultipartFile;
+            }
+        }
+        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
+            || o instanceof BindingResult;
+    }
+
 }

+ 0 - 95
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FeignConfig.java

@@ -1,95 +0,0 @@
-package com.ruoyi.framework.config;
-
-import feign.*;
-import okhttp3.ConnectionPool;
-import okhttp3.OkHttpClient;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.cloud.openfeign.EnableFeignClients;
-import org.springframework.cloud.openfeign.FeignAutoConfiguration;
-import org.springframework.cloud.openfeign.support.SpringMvcContract;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * openfeign配置类
- *
- * @author Lion Li
- * @deprecated 由于使用人数较少 决定与 3.4.0 版本移除
- */
-@Deprecated
-@EnableFeignClients("${feign.package}")
-@Configuration
-@ConditionalOnClass(Feign.class)
-@AutoConfigureBefore(FeignAutoConfiguration.class)
-public class FeignConfig {
-
-    @Bean
-    public OkHttpClient okHttpClient(){
-        return new OkHttpClient.Builder()
-                .readTimeout(60, TimeUnit.SECONDS)
-                .connectTimeout(60, TimeUnit.SECONDS)
-                .writeTimeout(120, TimeUnit.SECONDS)
-                .connectionPool(new ConnectionPool())
-                .build();
-    }
-
-    @Bean
-    public Contract feignContract() {
-        return new SpringMvcContract();
-    }
-
-    @Bean
-    public Logger.Level feignLoggerLevel() {
-        return Logger.Level.BASIC;
-    }
-
-    @Bean
-    public Request.Options feignRequestOptions() {
-        return new Request.Options(10, TimeUnit.SECONDS, 60,TimeUnit.SECONDS,true);
-    }
-
-    @Bean
-    public Retryer feignRetry() {
-        return new Retryer.Default();
-    }
-
-//	/**
-//	 * 自定义异常解码器
-//	 * 用于自定义返回体异常熔断
-//	 */
-//	@Bean
-//	public ErrorDecoder errorDecoder() {
-//		return new CustomErrorDecoder();
-//	}
-//
-//
-//	/**
-//	 * 自定义返回体解码器
-//	 */
-//	@Slf4j
-//	public static class CustomErrorDecoder implements ErrorDecoder {
-//
-//		@Override
-//		public Exception decode(String methodKey, Response response) {
-//			Exception exception = null;
-//			try {
-//				// 获取原始的返回内容
-//				String json = JsonUtils.toJsonString(response.body().asReader(StandardCharsets.UTF_8));
-//				exception = new RuntimeException(json);
-//				// 将返回内容反序列化为Result,这里应根据自身项目作修改
-//				AjaxResult result = JsonUtils.parseObject(json, AjaxResult.class);
-//				// 业务异常抛出简单的 RuntimeException,保留原来错误信息
-//				if (result.getCode() != 200) {
-//					exception = new RuntimeException(result.getMsg());
-//				}
-//			} catch (IOException e) {
-//				log.error(e.getMessage(), e);
-//			}
-//			return exception;
-//		}
-//	}
-
-}

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

@@ -20,7 +20,6 @@ import java.util.Map;
  * @author Lion Li
  */
 @Configuration
-@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
 public class FilterConfig {
 
     @Autowired
@@ -28,6 +27,7 @@ public class FilterConfig {
 
     @SuppressWarnings({"rawtypes", "unchecked"})
     @Bean
+    @ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
     public FilterRegistrationBean xssFilterRegistration() {
         FilterRegistrationBean registration = new FilterRegistrationBean();
         registration.setDispatcherTypes(DispatcherType.REQUEST);

+ 1 - 3
ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java

@@ -1,7 +1,6 @@
 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;
@@ -28,7 +27,6 @@ public class I18nConfig {
 	 */
 	static class I18nLocaleResolver implements LocaleResolver {
 
-		@NotNull
 		@Override
 		public Locale resolveLocale(HttpServletRequest httpServletRequest) {
 			String language = httpServletRequest.getHeader("content-language");
@@ -41,7 +39,7 @@ public class I18nConfig {
 		}
 
 		@Override
-		public void setLocale(@NotNull HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
+		public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
 
 		}
 	}

+ 5 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java

@@ -2,6 +2,7 @@ package com.ruoyi.framework.config;
 
 import cn.dev33.satoken.config.SaTokenConfig;
 import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
+import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.spring.SpringUtils;
 import com.ruoyi.framework.config.properties.SwaggerProperties;
@@ -34,6 +35,9 @@ public class SwaggerConfig {
     @Autowired
     private SaTokenConfig saTokenConfig;
 
+    @Autowired
+    private OpenApiExtensionResolver openApiExtensionResolver;
+
     /**
      * 创建API
      */
@@ -58,6 +62,7 @@ public class SwaggerConfig {
 					// 设置安全模式,swagger可以设置访问token
 					.securitySchemes(securitySchemes())
 					.securityContexts(securityContexts())
+                    .extensions(openApiExtensionResolver.buildExtensions(group.getName()))
 					.pathMapping(swaggerProperties.getPathMapping());
 			String beanName = StringUtils.substringAfterLast(basePackage, ".") + "Docket";
 			SpringUtils.registerBean(beanName, docket);

+ 0 - 6
ruoyi-framework/src/main/java/com/ruoyi/framework/config/TLogConfig.java

@@ -1,7 +1,6 @@
 package com.ruoyi.framework.config;
 
 import com.yomahub.tlog.core.aop.AspectLogAop;
-import com.yomahub.tlog.feign.filter.TLogFeignFilter;
 import com.yomahub.tlog.spring.TLogPropertyInit;
 import com.yomahub.tlog.spring.TLogSpringAware;
 import com.yomahub.tlog.springboot.property.TLogProperty;
@@ -41,9 +40,4 @@ public class TLogConfig {
         return new AspectLogAop();
     }
 
-    @Bean
-    public TLogFeignFilter tLogFeignFilter() {
-        return new TLogFeignFilter();
-    }
-
 }

+ 19 - 7
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ValidatorConfig.java

@@ -1,12 +1,14 @@
 package com.ruoyi.framework.config;
 
 import org.hibernate.validator.HibernateValidator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.MessageSource;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
 
-import javax.validation.Validation;
 import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
+import java.util.Properties;
 
 /**
  * 校验框架配置类
@@ -16,16 +18,26 @@ import javax.validation.ValidatorFactory;
 @Configuration
 public class ValidatorConfig {
 
+    @Autowired
+    private MessageSource messageSource;
+
     /**
      * 配置校验框架 快速返回模式
      */
     @Bean
     public Validator validator() {
-        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
-                .configure()
-                .failFast(true)
-                .buildValidatorFactory();
-        return validatorFactory.getValidator();
+        LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
+        // 国际化
+        factoryBean.setValidationMessageSource(messageSource);
+        // 设置使用 HibernateValidator 校验器
+        factoryBean.setProviderClass(HibernateValidator.class);
+        Properties properties = new Properties();
+        // 设置 快速异常返回
+        properties.setProperty("hibernate.validator.fail_fast", "true");
+        factoryBean.setValidationProperties(properties);
+        // 加载配置
+        factoryBean.afterPropertiesSet();
+        return factoryBean.getValidator();
     }
 
 }

+ 1 - 1
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java

@@ -14,7 +14,7 @@ import java.util.concurrent.ScheduledExecutorService;
  *
  * @author Lion Li
  */
-@Slf4j(topic = "sys-user")
+@Slf4j
 @Component
 public class ShutdownManager {
 

+ 8 - 2
ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java

@@ -16,7 +16,9 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.validation.ConstraintViolation;
 import javax.validation.ConstraintViolationException;
+import java.util.stream.Collectors;
 
 /**
  * 全局异常处理器
@@ -104,7 +106,9 @@ public class GlobalExceptionHandler {
     @ExceptionHandler(BindException.class)
     public AjaxResult<Void> handleBindException(BindException e) {
         log.error(e.getMessage(), e);
-        String message = e.getAllErrors().get(0).getDefaultMessage();
+        String message = e.getAllErrors().stream()
+            .map(DefaultMessageSourceResolvable::getDefaultMessage)
+            .collect(Collectors.joining(", "));
         return AjaxResult.error(message);
     }
 
@@ -114,7 +118,9 @@ public class GlobalExceptionHandler {
     @ExceptionHandler(ConstraintViolationException.class)
     public AjaxResult<Void> constraintViolationException(ConstraintViolationException e) {
         log.error(e.getMessage(), e);
-        String message = e.getConstraintViolations().iterator().next().getMessage();
+        String message = e.getConstraintViolations().stream()
+            .map(ConstraintViolation::getMessage)
+            .collect(Collectors.joining(", "));
         return AjaxResult.error(message);
     }
 

+ 1 - 1
ruoyi-generator/pom.xml

@@ -20,7 +20,7 @@
         <!--velocity代码生成使用模板 -->
         <dependency>
             <groupId>org.apache.velocity</groupId>
-            <artifactId>velocity</artifactId>
+            <artifactId>velocity-engine-core</artifactId>
         </dependency>
 
         <!-- 通用工具-->

+ 2 - 2
ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java

@@ -2,6 +2,7 @@ package com.ruoyi.generator.controller;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
 import cn.hutool.core.convert.Convert;
+import cn.hutool.core.io.IoUtil;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
@@ -14,7 +15,6 @@ import com.ruoyi.generator.service.IGenTableService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
-import org.apache.commons.io.IOUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -201,6 +201,6 @@ public class GenController extends BaseController {
         response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\"");
         response.addHeader("Content-Length", "" + data.length);
         response.setContentType("application/octet-stream; charset=UTF-8");
-        IOUtils.write(data, response.getOutputStream());
+        IoUtil.write(response.getOutputStream(), false, data);
     }
 }

+ 5 - 3
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java

@@ -2,6 +2,7 @@ package com.ruoyi.generator.service;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.convert.Convert;
+import cn.hutool.core.io.IoUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.constant.GenConstants;
@@ -21,7 +22,7 @@ import com.ruoyi.generator.util.GenUtils;
 import com.ruoyi.generator.util.VelocityInitializer;
 import com.ruoyi.generator.util.VelocityUtils;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.io.IOUtils;
+import org.apache.poi.util.IOUtils;
 import org.apache.velocity.Template;
 import org.apache.velocity.VelocityContext;
 import org.apache.velocity.app.Velocity;
@@ -33,6 +34,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
 import java.util.*;
 import java.util.stream.Collectors;
 import java.util.zip.ZipEntry;
@@ -341,8 +343,8 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
             try {
                 // 添加到zip
                 zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));
-                IOUtils.write(sw.toString(), zip, Constants.UTF8);
-                IOUtils.closeQuietly(sw);
+                IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString());
+                IoUtil.close(sw);
                 zip.flush();
                 zip.closeEntry();
             } catch (IOException e) {

+ 1 - 2
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java

@@ -19,10 +19,9 @@ public class VelocityInitializer {
         Properties p = new Properties();
         try {
             // 加载classpath目录下的vm文件
-            p.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
+            p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
             // 定义字符集
             p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8);
-            p.setProperty(Velocity.OUTPUT_ENCODING, Constants.UTF8);
             // 初始化Velocity引擎,指定配置Properties
             Velocity.init(p);
         } catch (Exception e) {

+ 2 - 1
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java

@@ -247,7 +247,8 @@ public class VelocityUtils {
         List<String> dicts = new ArrayList<String>();
         for (GenTableColumn column : columns) {
             if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny(
-                    column.getHtmlType(), new String[]{GenConstants.HTML_SELECT, GenConstants.HTML_RADIO})) {
+                    column.getHtmlType(),
+                new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) {
                 dicts.add("'" + column.getDictType() + "'");
             }
         }

+ 3 - 0
ruoyi-generator/src/main/resources/mapper/package-info.md

@@ -0,0 +1,3 @@
+java包使用 `.` 分割 resource 目录使用 `/` 分割
+<br>
+此文件目的 防止文件夹粘连找不到 `xml` 文件

+ 5 - 2
ruoyi-generator/src/main/resources/vm/java/controller.java.vm

@@ -28,6 +28,7 @@ import com.ruoyi.common.core.page.TableDataInfo;
 #elseif($table.tree)
 #end
 import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiOperation;
 
 /**
@@ -80,7 +81,8 @@ public class ${ClassName}Controller extends BaseController {
     @ApiOperation("获取${functionName}详细信息")
     @SaCheckPermission("${permissionPrefix}:query")
     @GetMapping("/{${pkColumn.javaField}}")
-    public AjaxResult<${ClassName}Vo> getInfo(@NotNull(message = "主键不能为空")
+    public AjaxResult<${ClassName}Vo> getInfo(@ApiParam("主键")
+                                                  @NotNull(message = "主键不能为空")
                                                   @PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) {
         return AjaxResult.success(i${ClassName}Service.queryById(${pkColumn.javaField}));
     }
@@ -116,7 +118,8 @@ public class ${ClassName}Controller extends BaseController {
     @SaCheckPermission("${permissionPrefix}:remove")
     @Log(title = "${functionName}" , businessType = BusinessType.DELETE)
     @DeleteMapping("/{${pkColumn.javaField}s}")
-    public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
+    public AjaxResult<Void> remove(@ApiParam("主键串")
+                                       @NotEmpty(message = "主键不能为空")
                                        @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
         return toAjax(i${ClassName}Service.deleteWithValidByIds(Arrays.asList(${pkColumn.javaField}s), true) ? 1 : 0);
     }

+ 9 - 9
ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm

@@ -108,7 +108,11 @@
 #elseif($column.list && $column.dictType && "" != $column.dictType)
       <el-table-column label="${comment}" align="center" prop="${javaField}">
         <template slot-scope="scope">
+#if($column.htmlType == "checkbox")
+          <dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/>
+#else
           <dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField}"/>
+#end
         </template>
       </el-table-column>
 #elseif($column.list && "" != $javaField)
@@ -296,8 +300,7 @@ export default {
       queryParams: {
 #foreach ($column in $columns)
 #if($column.query)
-        $column.javaField: null#if($velocityCount != $columns.size()),#end
-
+        $column.javaField: null#if($foreach.count != $columns.size()),#end
 #end
 #end
       },
@@ -315,8 +318,7 @@ export default {
 #end
         $column.javaField: [
           { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }
-        ]#if($velocityCount != $columns.size()),#end
-
+        ]#if($foreach.count != $columns.size()),#end
 #end
 #end
       }
@@ -379,14 +381,12 @@ export default {
       this.form = {
 #foreach ($column in $columns)
 #if($column.htmlType == "radio")
-        $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($velocityCount != $columns.size()),#end
+        $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end
 
 #elseif($column.htmlType == "checkbox")
-        $column.javaField: []#if($velocityCount != $columns.size()),#end
-
+        $column.javaField: []#if($foreach.count != $columns.size()),#end
 #else
-        $column.javaField: null#if($velocityCount != $columns.size()),#end
-
+        $column.javaField: null#if($foreach.count != $columns.size()),#end
 #end
 #end
       };

+ 12 - 14
ruoyi-generator/src/main/resources/vm/vue/index.vue.vm

@@ -108,7 +108,6 @@
           plain
           icon="el-icon-download"
           size="mini"
-          :loading="exportLoading"
           @click="handleExport"
           v-hasPermi="['${moduleName}:${businessName}:export']"
         >导出</el-button>
@@ -137,7 +136,11 @@
 #elseif($column.list && $column.dictType && "" != $column.dictType)
       <el-table-column label="${comment}" align="center" prop="${javaField}">
         <template slot-scope="scope">
+#if($column.htmlType == "checkbox")
+          <dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/>
+#else
           <dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField}"/>
+#end
         </template>
       </el-table-column>
 #elseif($column.list && "" != $javaField)
@@ -324,8 +327,6 @@ export default {
       buttonLoading: false,
       // 遮罩层
       loading: true,
-      // 导出遮罩层
-      exportLoading: false,
       // 选中数组
       ids: [],
 #if($table.sub)
@@ -363,8 +364,7 @@ export default {
         pageSize: 10,
 #foreach ($column in $columns)
 #if($column.query)
-        $column.javaField: undefined#if($velocityCount != $columns.size()),#end
-
+        $column.javaField: undefined#if($foreach.count != $columns.size()),#end
 #end
 #end
       },
@@ -382,8 +382,7 @@ export default {
 #end
         $column.javaField: [
           { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }
-        ]#if($velocityCount != $columns.size()),#end
-
+        ]#if($foreach.count != $columns.size()),#end
 #end
 #end
       }
@@ -427,14 +426,11 @@ export default {
       this.form = {
 #foreach ($column in $columns)
 #if($column.htmlType == "radio")
-        $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($velocityCount != $columns.size()),#end
-
+        $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end
 #elseif($column.htmlType == "checkbox")
-        $column.javaField: []#if($velocityCount != $columns.size()),#end
-
+        $column.javaField: []#if($foreach.count != $columns.size()),#end
 #else
-        $column.javaField: undefined#if($velocityCount != $columns.size()),#end
-
+        $column.javaField: undefined#if($foreach.count != $columns.size()),#end
 #end
 #end
       };
@@ -573,7 +569,9 @@ export default {
 #end
     /** 导出按钮操作 */
     handleExport() {
-        this.#[[$download]]#.excel('/${moduleName}/${businessName}/export', this.queryParams);
+      this.download('${moduleName}/${businessName}/export', {
+        ...this.queryParams
+      }, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`)
     }
   }
 };

+ 3 - 3
ruoyi-oss/src/main/java/com/ruoyi/oss/constant/CloudConstant.java → ruoyi-oss/src/main/java/com/ruoyi/oss/constant/OssConstant.java

@@ -8,7 +8,7 @@ import java.util.List;
  *
  * @author Lion Li
  */
-public class CloudConstant {
+public class OssConstant {
 
 	/**
 	 * OSS模块KEY
@@ -18,12 +18,12 @@ public class CloudConstant {
 	/**
 	 * 对象存储配置KEY
 	 */
-	public static final String CLOUD_STORAGE_CONFIG_KEY = "CloudStorageConfig";
+	public static final String OSS_CONFIG_KEY = "OssConfig";
 
 	/**
 	 * 缓存配置KEY
 	 */
-	public static final String CACHE_CONFIG_KEY = SYS_OSS_KEY + CLOUD_STORAGE_CONFIG_KEY;
+	public static final String CACHE_CONFIG_KEY = SYS_OSS_KEY + OSS_CONFIG_KEY;
 
 	/**
 	 * 预览列表资源开关Key

+ 11 - 11
ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/CloudServiceEnumd.java → ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/OssEnumd.java

@@ -1,10 +1,10 @@
 package com.ruoyi.oss.enumd;
 
 import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.oss.service.impl.AliyunCloudStorageStrategy;
-import com.ruoyi.oss.service.impl.MinioCloudStorageStrategy;
-import com.ruoyi.oss.service.impl.QcloudCloudStorageStrategy;
-import com.ruoyi.oss.service.impl.QiniuCloudStorageStrategy;
+import com.ruoyi.oss.service.impl.AliyunOssStrategy;
+import com.ruoyi.oss.service.impl.MinioOssStrategy;
+import com.ruoyi.oss.service.impl.QcloudOssStrategy;
+import com.ruoyi.oss.service.impl.QiniuOssStrategy;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
@@ -15,34 +15,34 @@ import lombok.Getter;
  */
 @Getter
 @AllArgsConstructor
-public enum CloudServiceEnumd {
+public enum OssEnumd {
 
 	/**
 	 * 七牛云
 	 */
-	QINIU("qiniu", QiniuCloudStorageStrategy.class),
+	QINIU("qiniu", QiniuOssStrategy.class),
 
 	/**
 	 * 阿里云
 	 */
-	ALIYUN("aliyun", AliyunCloudStorageStrategy.class),
+	ALIYUN("aliyun", AliyunOssStrategy.class),
 
 	/**
 	 * 腾讯云
 	 */
-	QCLOUD("qcloud", QcloudCloudStorageStrategy.class),
+	QCLOUD("qcloud", QcloudOssStrategy.class),
 
 	/**
 	 * minio
 	 */
-	MINIO("minio", MinioCloudStorageStrategy.class);
+	MINIO("minio", MinioOssStrategy.class);
 
 	private final String value;
 
 	private final Class<?> serviceClass;
 
 	public static Class<?> getServiceClass(String value) {
-		for (CloudServiceEnumd clazz : values()) {
+		for (OssEnumd clazz : values()) {
 			if (clazz.getValue().equals(value)) {
 				return clazz.getServiceClass();
 			}
@@ -51,7 +51,7 @@ public enum CloudServiceEnumd {
 	}
 
 	public static String getServiceName(String value) {
-		for (CloudServiceEnumd clazz : values()) {
+		for (OssEnumd clazz : values()) {
 			if (clazz.getValue().equals(value)) {
 				return StringUtils.uncapitalize(clazz.getServiceClass().getSimpleName());
 			}

+ 28 - 22
ruoyi-oss/src/main/java/com/ruoyi/oss/factory/OssFactory.java

@@ -1,16 +1,15 @@
 package com.ruoyi.oss.factory;
 
-import cn.hutool.core.convert.Convert;
 import com.ruoyi.common.utils.JsonUtils;
 import com.ruoyi.common.utils.RedisUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.reflect.ReflectUtils;
-import com.ruoyi.oss.constant.CloudConstant;
-import com.ruoyi.oss.enumd.CloudServiceEnumd;
+import com.ruoyi.oss.constant.OssConstant;
+import com.ruoyi.oss.enumd.OssEnumd;
 import com.ruoyi.oss.exception.OssException;
-import com.ruoyi.oss.properties.CloudStorageProperties;
-import com.ruoyi.oss.service.ICloudStorageStrategy;
-import com.ruoyi.oss.service.abstractd.AbstractCloudStorageStrategy;
+import com.ruoyi.oss.properties.OssProperties;
+import com.ruoyi.oss.service.IOssStrategy;
+import com.ruoyi.oss.service.abstractd.AbstractOssStrategy;
 import lombok.extern.slf4j.Slf4j;
 
 import java.util.Map;
@@ -24,24 +23,31 @@ import java.util.concurrent.ConcurrentHashMap;
 @Slf4j
 public class OssFactory {
 
-	static {
-		RedisUtils.subscribe(CloudConstant.CACHE_CONFIG_KEY, String.class, msg -> {
-			refreshService(msg);
-			log.info("订阅刷新OSS配置 => " + msg);
-		});
-	}
-
 	/**
 	 * 服务实例缓存
 	 */
-	private static final Map<String, ICloudStorageStrategy> SERVICES = new ConcurrentHashMap<>();
+	private static final Map<String, IOssStrategy> SERVICES = new ConcurrentHashMap<>();
+
+    /**
+     * 初始化工厂
+     */
+    public static void init() {
+        log.info("初始化OSS工厂");
+        RedisUtils.subscribe(OssConstant.CACHE_CONFIG_KEY, String.class, type -> {
+            // 没有的实例不处理
+            if (SERVICES.containsKey(type)) {
+                refreshService(type);
+                log.info("订阅刷新OSS配置 => " + type);
+            }
+        });
+    }
 
 	/**
 	 * 获取默认实例
 	 */
-	public static ICloudStorageStrategy instance() {
+	public static IOssStrategy instance() {
 		// 获取redis 默认类型
-		String type = Convert.toStr(RedisUtils.getCacheObject(CloudConstant.CACHE_CONFIG_KEY));
+		String type = RedisUtils.getCacheObject(OssConstant.CACHE_CONFIG_KEY);
 		if (StringUtils.isEmpty(type)) {
 			throw new OssException("文件存储服务类型无法找到!");
 		}
@@ -51,8 +57,8 @@ public class OssFactory {
 	/**
 	 * 根据类型获取实例
 	 */
-	public static ICloudStorageStrategy instance(String type) {
-		ICloudStorageStrategy service = SERVICES.get(type);
+	public static IOssStrategy instance(String type) {
+        IOssStrategy service = SERVICES.get(type);
 		if (service == null) {
 			refreshService(type);
 			service = SERVICES.get(type);
@@ -61,14 +67,14 @@ public class OssFactory {
 	}
 
 	private static void refreshService(String type) {
-		Object json = RedisUtils.getCacheObject(CloudConstant.SYS_OSS_KEY + type);
-		CloudStorageProperties properties = JsonUtils.parseObject(json.toString(), CloudStorageProperties.class);
+		Object json = RedisUtils.getCacheObject(OssConstant.SYS_OSS_KEY + type);
+        OssProperties properties = JsonUtils.parseObject(json.toString(), OssProperties.class);
 		if (properties == null) {
 			throw new OssException("系统异常, '" + type + "'配置信息不存在!");
 		}
 		// 获取redis配置信息 创建对象 并缓存
-		ICloudStorageStrategy service = (ICloudStorageStrategy) ReflectUtils.newInstance(CloudServiceEnumd.getServiceClass(type));
-		((AbstractCloudStorageStrategy)service).init(properties);
+        IOssStrategy service = (IOssStrategy) ReflectUtils.newInstance(OssEnumd.getServiceClass(type));
+		((AbstractOssStrategy)service).init(properties);
 		SERVICES.put(type, service);
 	}
 

+ 1 - 8
ruoyi-oss/src/main/java/com/ruoyi/oss/properties/CloudStorageProperties.java → ruoyi-oss/src/main/java/com/ruoyi/oss/properties/OssProperties.java

@@ -2,15 +2,13 @@ package com.ruoyi.oss.properties;
 
 import lombok.Data;
 
-import java.util.Date;
-
 /**
  * OSS对象存储 配置属性
  *
  * @author Lion Li
  */
 @Data
-public class CloudStorageProperties {
+public class OssProperties {
 
 	/**
 	 * 域名
@@ -47,9 +45,4 @@ public class CloudStorageProperties {
 	 */
 	private String isHttps;
 
-	/**
-	 * 更新时间
-	 */
-	private Date updateTime;
-
 }

+ 1 - 11
ruoyi-oss/src/main/java/com/ruoyi/oss/service/ICloudStorageStrategy.java → ruoyi-oss/src/main/java/com/ruoyi/oss/service/IOssStrategy.java

@@ -9,7 +9,7 @@ import java.io.InputStream;
  *
  * @author Lion Li
  */
-public interface ICloudStorageStrategy {
+public interface IOssStrategy {
 
 	void createBucket();
 
@@ -18,15 +18,6 @@ public interface ICloudStorageStrategy {
 	 */
 	String getServiceType();
 
-	/**
-	 * 文件路径
-	 *
-	 * @param prefix 前缀
-	 * @param suffix 后缀
-	 * @return 返回上传路径
-	 */
-	String getPath(String prefix, String suffix);
-
 	/**
 	 * 文件上传
 	 *
@@ -70,5 +61,4 @@ public interface ICloudStorageStrategy {
 	 */
 	UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType);
 
-	String getEndpointLink();
 }

+ 5 - 7
ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractCloudStorageStrategy.java → ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractOssStrategy.java

@@ -5,8 +5,8 @@ import cn.hutool.core.util.IdUtil;
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.oss.entity.UploadResult;
-import com.ruoyi.oss.properties.CloudStorageProperties;
-import com.ruoyi.oss.service.ICloudStorageStrategy;
+import com.ruoyi.oss.properties.OssProperties;
+import com.ruoyi.oss.service.IOssStrategy;
 
 import java.io.InputStream;
 
@@ -15,11 +15,11 @@ import java.io.InputStream;
  *
  * @author Lion Li
  */
-public abstract class AbstractCloudStorageStrategy implements ICloudStorageStrategy {
+public abstract class AbstractOssStrategy implements IOssStrategy {
 
-	protected CloudStorageProperties properties;
+	protected OssProperties properties;
 
-	public abstract void init(CloudStorageProperties properties);
+	public abstract void init(OssProperties properties);
 
 	@Override
 	public abstract void createBucket();
@@ -27,7 +27,6 @@ public abstract class AbstractCloudStorageStrategy implements ICloudStorageStrat
 	@Override
 	public abstract String getServiceType();
 
-	@Override
 	public String getPath(String prefix, String suffix) {
 		// 生成uuid
 		String uuid = IdUtil.fastSimpleUUID();
@@ -57,6 +56,5 @@ public abstract class AbstractCloudStorageStrategy implements ICloudStorageStrat
 	@Override
 	public abstract UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType);
 
-	@Override
 	public abstract String getEndpointLink();
 }

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov