浏览代码

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

# Conflicts:
#	pom.xml
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.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-common/pom.xml
#	ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
#	ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
#	ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
#	ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
#	ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
#	ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java
#	ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java
#	ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TokenServiceImpl.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserDetailsServiceImpl.java
疯狂的狮子li 3 年之前
父节点
当前提交
dd37247e65
共有 100 个文件被更改,包括 2160 次插入900 次删除
  1. 4 4
      README.md
  2. 42 27
      pom.xml
  3. 3 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
  4. 3 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
  5. 1 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java
  6. 3 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
  7. 3 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
  8. 3 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
  9. 3 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java
  10. 3 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java
  11. 3 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java
  12. 3 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
  13. 4 1
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
  14. 7 6
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
  15. 3 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
  16. 3 1
      ruoyi-admin/src/main/resources/application-dev.yml
  17. 3 1
      ruoyi-admin/src/main/resources/application-prod.yml
  18. 3 5
      ruoyi-admin/src/main/resources/application.yml
  19. 4 4
      ruoyi-admin/src/main/resources/i18n/messages.properties
  20. 4 4
      ruoyi-admin/src/main/resources/i18n/messages_en_US.properties
  21. 4 4
      ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
  22. 33 25
      ruoyi-common/pom.xml
  23. 26 0
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataColumn.java
  24. 18 0
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataPermission.java
  25. 4 0
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java
  26. 2 0
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java
  27. 35 19
      ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
  28. 41 41
      ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java
  29. 24 24
      ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java
  30. 93 0
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/PageQuery.java
  31. 2 0
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
  32. 7 4
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
  33. 77 0
      ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/BaseMapperPlus.java
  34. 21 41
      ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/IServicePlus.java
  35. 25 40
      ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/ServicePlusImpl.java
  36. 2 0
      ruoyi-common/src/main/java/com/ruoyi/common/core/page/PagePlus.java
  37. 27 0
      ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java
  38. 72 0
      ruoyi-common/src/main/java/com/ruoyi/common/enums/DataScopeType.java
  39. 2 0
      ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java
  40. 2 2
      ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java
  41. 1 1
      ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java
  42. 1 1
      ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java
  43. 1 1
      ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java
  44. 45 0
      ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java
  45. 76 22
      ruoyi-common/src/main/java/com/ruoyi/common/utils/BeanCopyUtils.java
  46. 3 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
  47. 0 158
      ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java
  48. 11 7
      ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java
  49. 8 3
      ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java
  50. 41 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java
  51. 89 40
      ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisUtils.java
  52. 5 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
  53. 3 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java
  54. 3 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
  55. 7 5
      ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java
  56. 5 7
      ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeBuildUtils.java
  57. 6 2
      ruoyi-common/src/main/java/com/ruoyi/common/utils/ValidatorUtils.java
  58. 3 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
  59. 3 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
  60. 3 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
  61. 3 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java
  62. 25 1
      ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java
  63. 26 0
      ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java
  64. 21 0
      ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java
  65. 7 3
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java
  66. 4 3
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
  67. 3 29
      ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java
  68. 0 25
      ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java
  69. 39 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
  70. 6 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java
  71. 3 2
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java
  72. 10 16
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java
  73. 4 8
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java
  74. 2 4
      ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/config/SecurityConfig.java
  75. 1 0
      ruoyi-extend/ruoyi-xxl-job-admin/pom.xml
  76. 5 5
      ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java
  77. 6 6
      ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java
  78. 15 5
      ruoyi-framework/pom.xml
  79. 2 5
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
  80. 2 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java
  81. 1 1
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java
  82. 30 8
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java
  83. 154 147
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java
  84. 1 1
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
  85. 4 29
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/TLogConfig.java
  86. 192 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java
  87. 108 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/PlusDataPermissionInterceptor.java
  88. 23 16
      ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/PlusWebInvokeTimeInterceptor.java
  89. 5 4
      ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java
  90. 2 0
      ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java
  91. 2 0
      ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java
  92. 12 8
      ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
  93. 3 2
      ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java
  94. 2 5
      ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java
  95. 12 12
      ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml
  96. 0 24
      ruoyi-generator/src/main/resources/vm/java/bo.java.vm
  97. 3 2
      ruoyi-generator/src/main/resources/vm/java/controller.java.vm
  98. 2 1
      ruoyi-generator/src/main/resources/vm/java/service.java.vm
  99. 8 6
      ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm
  100. 476 0
      ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm

+ 4 - 4
README.md

@@ -8,21 +8,21 @@
 [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.5-blue.svg)]()
 [![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]()
 [![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()
-[![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/) | 高可读性 扩展性(推荐使用) |
 | 单体分支 | RuoYi-Vue-Plus-fast | [fast分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/) | 单体应用结构 |
+| Vue3分支 | RuoYi-Vue-Plus-UI | [UI地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus-UI) | 由于组件还未完善 仅供学习 |
 | 原框架 | RuoYi-Vue | [RuoYi-Vue官网](http://ruoyi.vip/) | 定期同步需要的功能 |
 | 前端开发框架 | Vue、Element UI | [Element UI官网](https://element.eleme.cn/#/zh-CN) | |
 | 后端开发框架 | SpringBoot | [SpringBoot官网](https://spring.io/projects/spring-boot/#learn) | |
-| 容器框架 | Undertow | [Undertow官网](https://undertow.io/) | 基于 Netty 的高性能容器 |
+| 容器框架 | Undertow | [Undertow官网](https://undertow.io/) | 基于 XNIO 的高性能容器 |
 | 权限认证框架 | Spring Security、Jwt | [SpringSecurity官网](https://spring.io/projects/spring-security#learn) | 支持多终端认证系统 |
 | 关系数据库 | MySQL | [MySQL官网](https://dev.mysql.com/) | 适配 8.X 最低 5.7 |
 | 缓存数据库 | Redis | [Redis官网](https://redis.io/) | 适配 6.X 最低 4.X |

+ 42 - 27
pom.xml

@@ -14,7 +14,7 @@
 
     <properties>
         <ruoyi-vue-plus.version>3.4.0</ruoyi-vue-plus.version>
-        <spring-boot.version>2.5.7</spring-boot.version>
+        <spring-boot.version>2.5.8</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,27 +24,28 @@
         <swagger-annotations.version>1.5.22</swagger-annotations.version>
         <poi.version>4.1.2</poi.version>
         <easyexcel.version>2.2.11</easyexcel.version>
+        <cglib.version>3.3.0</cglib.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.16</hutool.version>
+        <hutool.version>5.7.17</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>
+        <spring-boot-admin.version>2.5.5</spring-boot-admin.version>
+        <redisson.version>3.16.6</redisson.version>
         <lock4j.version>2.2.1</lock4j.version>
-        <dynamic-ds.version>3.4.1</dynamic-ds.version>
-        <tlog.version>1.3.4</tlog.version>
+        <dynamic-ds.version>3.5.0</dynamic-ds.version>
+        <tlog.version>1.3.6</tlog.version>
         <xxl-job.version>2.3.0</xxl-job.version>
 
         <!-- jdk11 缺失依赖 jaxb-->
         <jaxb.version>3.0.1</jaxb.version>
 
         <!-- OSS 配置 -->
-        <qiniu.version>7.8.0</qiniu.version>
+        <qiniu.version>7.9.0</qiniu.version>
         <aliyun.oss.version>3.13.1</aliyun.oss.version>
         <qcloud.cos.version>5.6.58</qcloud.cos.version>
-        <minio.version>8.3.3</minio.version>
+        <minio.version>8.3.4</minio.version>
 
         <!-- docker 配置 -->
         <docker.registry.url>localhost</docker.registry.url>
@@ -115,6 +116,12 @@
                 </exclusions>
             </dependency>
 
+            <dependency>
+                <groupId>cglib</groupId>
+                <artifactId>cglib</artifactId>
+                <version>${cglib.version}</version>
+            </dependency>
+
             <!-- velocity代码生成使用模板 -->
             <dependency>
                 <groupId>org.apache.velocity</groupId>
@@ -168,7 +175,31 @@
 
             <dependency>
                 <groupId>cn.hutool</groupId>
-                <artifactId>hutool-all</artifactId>
+                <artifactId>hutool-core</artifactId>
+                <version>${hutool.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.hutool</groupId>
+                <artifactId>hutool-http</artifactId>
+                <version>${hutool.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.hutool</groupId>
+                <artifactId>hutool-captcha</artifactId>
+                <version>${hutool.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.hutool</groupId>
+                <artifactId>hutool-extra</artifactId>
+                <version>${hutool.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.hutool</groupId>
+                <artifactId>hutool-jwt</artifactId>
                 <version>${hutool.version}</version>
             </dependency>
 
@@ -210,29 +241,13 @@
 
             <dependency>
                 <groupId>com.yomahub</groupId>
-                <artifactId>tlog-spring-boot-configuration</artifactId>
+                <artifactId>tlog-web-spring-boot-starter</artifactId>
                 <version>${tlog.version}</version>
             </dependency>
 
             <dependency>
                 <groupId>com.yomahub</groupId>
-                <artifactId>tlog-webroot</artifactId>
-                <version>${tlog.version}</version>
-                <exclusions>
-                    <exclusion>
-                        <artifactId>javassist</artifactId>
-                        <groupId>org.javassist</groupId>
-                    </exclusion>
-                    <exclusion>
-                        <artifactId>guava</artifactId>
-                        <groupId>com.google.guava</groupId>
-                    </exclusion>
-                </exclusions>
-            </dependency>
-
-            <dependency>
-                <groupId>com.yomahub</groupId>
-                <artifactId>tlog-xxl-job</artifactId>
+                <artifactId>tlog-xxljob-spring-boot-starter</artifactId>
                 <version>${tlog.version}</version>
             </dependency>
 

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

@@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -36,8 +37,8 @@ public class SysLogininforController extends BaseController {
     @ApiOperation("查询系统访问记录列表")
     @SaCheckPermission("monitor:logininfor:list")
     @GetMapping("/list")
-    public TableDataInfo<SysLogininfor> list(SysLogininfor logininfor) {
-        return logininforService.selectPageLogininforList(logininfor);
+    public TableDataInfo<SysLogininfor> list(SysLogininfor logininfor, PageQuery pageQuery) {
+        return logininforService.selectPageLogininforList(logininfor, pageQuery);
     }
 
     @ApiOperation("导出系统访问记录列表")

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

@@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -36,8 +37,8 @@ public class SysOperlogController extends BaseController {
     @ApiOperation("查询操作日志记录列表")
     @SaCheckPermission("monitor:operlog:list")
     @GetMapping("/list")
-    public TableDataInfo<SysOperLog> list(SysOperLog operLog) {
-        return operLogService.selectPageOperLogList(operLog);
+    public TableDataInfo<SysOperLog> list(SysOperLog operLog, PageQuery pageQuery) {
+        return operLogService.selectPageOperLogList(operLog, pageQuery);
     }
 
     @ApiOperation("导出操作日志记录列表")

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

@@ -12,7 +12,6 @@ import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.dto.UserOnlineDTO;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.PageUtils;
 import com.ruoyi.common.utils.RedisUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.domain.SysUserOnline;
@@ -70,7 +69,7 @@ public class SysUserOnlineController extends BaseController {
         Collections.reverse(userOnlineDTOList);
         userOnlineDTOList.removeAll(Collections.singleton(null));
         List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class);
-        return PageUtils.buildDataInfo(userOnlineList);
+        return TableDataInfo.build(userOnlineList);
     }
 
     /**

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

@@ -5,6 +5,7 @@ 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.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -41,8 +42,8 @@ public class SysConfigController extends BaseController {
     @ApiOperation("获取参数配置列表")
     @SaCheckPermission("system:config:list")
     @GetMapping("/list")
-    public TableDataInfo<SysConfig> list(SysConfig config) {
-        return configService.selectPageConfigList(config);
+    public TableDataInfo<SysConfig> list(SysConfig config, PageQuery pageQuery) {
+        return configService.selectPageConfigList(config, pageQuery);
     }
 
     @ApiOperation("导出参数配置列表")

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

@@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.domain.entity.SysDictData;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
@@ -41,8 +42,8 @@ public class SysDictDataController extends BaseController {
     @ApiOperation("查询字典数据列表")
     @SaCheckPermission("system:dict:list")
     @GetMapping("/list")
-    public TableDataInfo<SysDictData> list(SysDictData dictData) {
-        return dictDataService.selectPageDictDataList(dictData);
+    public TableDataInfo<SysDictData> list(SysDictData dictData, PageQuery pageQuery) {
+        return dictDataService.selectPageDictDataList(dictData, pageQuery);
     }
 
     @ApiOperation("导出字典数据列表")

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

@@ -5,6 +5,7 @@ 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.PageQuery;
 import com.ruoyi.common.core.domain.entity.SysDictType;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
@@ -38,8 +39,8 @@ public class SysDictTypeController extends BaseController {
     @ApiOperation("查询字典类型列表")
     @SaCheckPermission("system:dict:list")
     @GetMapping("/list")
-    public TableDataInfo<SysDictType> list(SysDictType dictType) {
-        return dictTypeService.selectPageDictTypeList(dictType);
+    public TableDataInfo<SysDictType> list(SysDictType dictType, PageQuery pageQuery) {
+        return dictTypeService.selectPageDictTypeList(dictType, pageQuery);
     }
 
     @ApiOperation("导出字典类型列表")

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

@@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.system.domain.SysNotice;
@@ -36,8 +37,8 @@ public class SysNoticeController extends BaseController {
     @ApiOperation("获取通知公告列表")
     @SaCheckPermission("system:notice:list")
     @GetMapping("/list")
-    public TableDataInfo<SysNotice> list(SysNotice notice) {
-        return noticeService.selectPageNoticeList(notice);
+    public TableDataInfo<SysNotice> list(SysNotice notice, PageQuery pageQuery) {
+        return noticeService.selectPageNoticeList(notice, pageQuery);
     }
 
     /**

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

@@ -5,6 +5,7 @@ import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.annotation.RepeatSubmit;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.core.validate.AddGroup;
 import com.ruoyi.common.core.validate.EditGroup;
@@ -47,8 +48,8 @@ public class SysOssConfigController extends BaseController {
     @ApiOperation("查询对象存储配置列表")
     @SaCheckPermission("system:oss:list")
     @GetMapping("/list")
-    public TableDataInfo<SysOssConfigVo> list(@Validated(QueryGroup.class) SysOssConfigBo bo) {
-        return iSysOssConfigService.queryPageList(bo);
+    public TableDataInfo<SysOssConfigVo> list(@Validated(QueryGroup.class) SysOssConfigBo bo, PageQuery pageQuery) {
+        return iSysOssConfigService.queryPageList(bo, pageQuery);
     }
 
     /**

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

@@ -11,6 +11,7 @@ import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.annotation.RepeatSubmit;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.core.validate.QueryGroup;
 import com.ruoyi.common.enums.BusinessType;
@@ -61,8 +62,8 @@ public class SysOssController extends BaseController {
     @ApiOperation("查询OSS对象存储列表")
     @SaCheckPermission("system:oss:list")
     @GetMapping("/list")
-    public TableDataInfo<SysOssVo> list(@Validated(QueryGroup.class) SysOssBo bo) {
-        return iSysOssService.queryPageList(bo);
+    public TableDataInfo<SysOssVo> list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) {
+        return iSysOssService.queryPageList(bo, pageQuery);
     }
 
     /**

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

@@ -5,6 +5,7 @@ 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.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -41,8 +42,8 @@ public class SysPostController extends BaseController {
     @ApiOperation("获取岗位列表")
     @SaCheckPermission("system:post:list")
     @GetMapping("/list")
-    public TableDataInfo<SysPost> list(SysPost post) {
-        return postService.selectPagePostList(post);
+    public TableDataInfo<SysPost> list(SysPost post, PageQuery pageQuery) {
+        return postService.selectPagePostList(post, pageQuery);
     }
 
     @ApiOperation("导出岗位列表")

+ 4 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java

@@ -11,7 +11,10 @@ import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.domain.SysOss;
 import com.ruoyi.system.service.ISysOssService;
 import com.ruoyi.system.service.ISysUserService;
-import io.swagger.annotations.*;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;

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

@@ -5,6 +5,7 @@ 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.PageQuery;
 import com.ruoyi.common.core.domain.entity.SysRole;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.domain.model.LoginUser;
@@ -45,8 +46,8 @@ public class SysRoleController extends BaseController {
     @ApiOperation("查询角色信息列表")
     @SaCheckPermission("system:role:list")
     @GetMapping("/list")
-    public TableDataInfo<SysRole> list(SysRole role) {
-        return roleService.selectPageRoleList(role);
+    public TableDataInfo<SysRole> list(SysRole role, PageQuery pageQuery) {
+        return roleService.selectPageRoleList(role, pageQuery);
     }
 
     @ApiOperation("导出角色信息列表")
@@ -165,8 +166,8 @@ public class SysRoleController extends BaseController {
     @ApiOperation("查询已分配用户角色列表")
     @SaCheckPermission("system:role:list")
     @GetMapping("/authUser/allocatedList")
-    public TableDataInfo<SysUser> allocatedList(SysUser user) {
-        return userService.selectAllocatedList(user);
+    public TableDataInfo<SysUser> allocatedList(SysUser user, PageQuery pageQuery) {
+        return userService.selectAllocatedList(user, pageQuery);
     }
 
     /**
@@ -175,8 +176,8 @@ public class SysRoleController extends BaseController {
     @ApiOperation("查询未分配用户角色列表")
     @SaCheckPermission("system:role:list")
     @GetMapping("/authUser/unallocatedList")
-    public TableDataInfo<SysUser> unallocatedList(SysUser user) {
-        return userService.selectUnallocatedList(user);
+    public TableDataInfo<SysUser> unallocatedList(SysUser user, PageQuery pageQuery) {
+        return userService.selectUnallocatedList(user, pageQuery);
     }
 
     /**

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

@@ -8,6 +8,7 @@ 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.PageQuery;
 import com.ruoyi.common.core.domain.entity.SysDept;
 import com.ruoyi.common.core.domain.entity.SysRole;
 import com.ruoyi.common.core.domain.entity.SysUser;
@@ -59,8 +60,8 @@ public class SysUserController extends BaseController {
     @ApiOperation("获取用户列表")
     @SaCheckPermission("system:user:list")
     @GetMapping("/list")
-    public TableDataInfo<SysUser> list(SysUser user) {
-        return userService.selectPageUserList(user);
+    public TableDataInfo<SysUser> list(SysUser user, PageQuery pageQuery) {
+        return userService.selectPageUserList(user, pageQuery);
     }
 
     @ApiOperation("导出用户列表")

+ 3 - 1
ruoyi-admin/src/main/resources/application-dev.yml

@@ -51,7 +51,9 @@ spring:
         # 主库数据源
         master:
           driverClassName: com.mysql.cj.jdbc.Driver
-          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
+          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
+          # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
+          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
           username: root
           password: root
         # 从库数据源

+ 3 - 1
ruoyi-admin/src/main/resources/application-prod.yml

@@ -58,7 +58,9 @@ spring:
         # 主库数据源
         master:
           driverClassName: com.mysql.cj.jdbc.Driver
-          url: jdbc:mysql://172.30.0.36:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
+          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
+          # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
+          url: jdbc:mysql://172.30.0.36:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
           username: root
           password: root
         # 从库数据源

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

@@ -11,7 +11,7 @@ ruoyi:
   # 获取ip地址开关
   addressEnabled: true
   # 缓存懒加载
-  cacheLazy: true
+  cacheLazy: false
 
 captcha:
   # 页面 <参数设置> 可开启关闭 验证码校验
@@ -53,10 +53,6 @@ logging:
     org.springframework: warn
   config: classpath:logback.xml
 
-# tlog 全局访问性能拦截
-tlog:
-  enable-invoke-time-print: true
-
 # Spring配置
 spring:
   application:
@@ -142,6 +138,8 @@ security:
     - /*/api-docs
     # druid 监控配置
     - /druid/**
+  # 用户放行
+  permit-all:
     # actuator 监控配置
     - /actuator
     - /actuator/**

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

@@ -2,12 +2,12 @@
 not.null=* 必须填写
 user.jcaptcha.error=验证码错误
 user.jcaptcha.expire=验证码已失效
-user.not.exists=用户不存在/密码错误
+user.not.exists=对不起, 您的账号:{0} 不存在.
 user.password.not.match=用户不存在/密码错误
 user.password.retry.limit.count=密码输入错误{0}次
-user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟
-user.password.delete=对不起,您的账号已被删除
-user.blocked=用户已封禁,请联系管理员
+user.password.retry.limit.exceed=密码错误次数过多,帐户锁定{0}分钟
+user.password.delete=对不起,您的账号:{0} 已被删除
+user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
 role.blocked=角色已封禁,请联系管理员
 user.logout.success=退出成功
 length.not.valid=长度必须在{min}到{max}个字符之间

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

@@ -2,12 +2,12 @@
 not.null=* Required fill in
 user.jcaptcha.error=Captcha error
 user.jcaptcha.expire=Captcha invalid
-user.not.exists=User does not exist/Password error
+user.not.exists=Sorry, your account: {0} does not exist
 user.password.not.match=User does not exist/Password error
 user.password.retry.limit.count=Password input error {0} times
-user.password.retry.limit.exceed=Password input error {0} times, account locked for 10 minutes
-user.password.delete=Sorry, your account has been deleted
-user.blocked=User disabled,please contact administrators
+user.password.retry.limit.exceed=Too many password errors, account locked for {0} minutes
+user.password.delete=Sorry, your account:{0} has been deleted
+user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator
 role.blocked=Role disabled,please contact administrators
 user.logout.success=Exit successful
 length.not.valid=The length must be between {min} and {max} characters

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

@@ -2,12 +2,12 @@
 not.null=* 必须填写
 user.jcaptcha.error=验证码错误
 user.jcaptcha.expire=验证码已失效
-user.not.exists=用户不存在/密码错误
+user.not.exists=对不起, 您的账号:{0} 不存在.
 user.password.not.match=用户不存在/密码错误
 user.password.retry.limit.count=密码输入错误{0}次
-user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟
-user.password.delete=对不起,您的账号已被删除
-user.blocked=用户已封禁,请联系管理员
+user.password.retry.limit.exceed=密码错误次数过多,帐户锁定{0}分钟
+user.password.delete=对不起,您的账号:{0} 已被删除
+user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
 role.blocked=角色已封禁,请联系管理员
 user.logout.success=退出成功
 length.not.valid=长度必须在{min}到{max}个字符之间

+ 33 - 25
ruoyi-common/pom.xml

@@ -74,18 +74,17 @@
             <artifactId>easyexcel</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib</artifactId>
+        </dependency>
+
         <!-- yml解析器 -->
         <dependency>
             <groupId>org.yaml</groupId>
             <artifactId>snakeyaml</artifactId>
         </dependency>
 
-<!--        &lt;!&ndash;Token生成与解析&ndash;&gt;-->
-<!--        <dependency>-->
-<!--            <groupId>io.jsonwebtoken</groupId>-->
-<!--            <artifactId>jjwt</artifactId>-->
-<!--        </dependency>-->
-
         <!-- jdk11 缺失依赖 jaxb-->
         <dependency>
             <groupId>com.sun.xml.bind</groupId>
@@ -106,18 +105,41 @@
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-extension</artifactId>
         </dependency>
+
+        <!-- dynamic-datasource 多数据源-->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-core</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>cn.hutool</groupId>
-            <artifactId>hutool-all</artifactId>
+            <artifactId>hutool-http</artifactId>
         </dependency>
+
         <dependency>
-            <groupId>org.projectlombok</groupId>
-            <artifactId>lombok</artifactId>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-captcha</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-jwt</artifactId>
         </dependency>
 
         <dependency>
-            <groupId>de.codecentric</groupId>
-            <artifactId>spring-boot-admin-starter-client</artifactId>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-extra</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
         </dependency>
 
         <dependency>
@@ -130,10 +152,6 @@
             <artifactId>swagger-annotations</artifactId>
         </dependency>
 
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-actuator</artifactId>
-        </dependency>
         <!--  自动生成YML配置关联JSON文件  -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -151,16 +169,6 @@
             <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
         </dependency>
 
-        <dependency>
-            <groupId>com.yomahub</groupId>
-            <artifactId>tlog-spring-boot-configuration</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>com.yomahub</groupId>
-            <artifactId>tlog-webroot</artifactId>
-        </dependency>
-
     </dependencies>
 
 </project>

+ 26 - 0
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataColumn.java

@@ -0,0 +1,26 @@
+package com.ruoyi.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 数据权限
+ *
+ * @author Lion Li
+ * @version 3.5.0
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataColumn {
+
+    /**
+     * 占位符关键字
+     */
+    String key() default "deptName";
+
+    /**
+     * 占位符替换值
+     */
+    String value() default "dept_id";
+
+}

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

@@ -0,0 +1,18 @@
+package com.ruoyi.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 数据权限组
+ *
+ * @author Lion Li
+ * @version 3.5.0
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataPermission {
+
+    DataColumn[] value();
+
+}

+ 4 - 0
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java

@@ -6,11 +6,14 @@ import java.lang.annotation.*;
  * 数据权限过滤注解
  *
  * @author ruoyi
+ * @deprecated 3.6.0 移除 {@link com.ruoyi.common.annotation.DataPermission}
  */
 @Target(ElementType.METHOD)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
+@Deprecated
 public @interface DataScope {
+
     /**
      * 部门表的别名
      */
@@ -25,4 +28,5 @@ public @interface DataScope {
      * 是否过滤用户权限
      */
     boolean isUser() default false;
+
 }

+ 2 - 0
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java

@@ -10,11 +10,13 @@ import java.lang.annotation.*;
  * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
  *
  * @author ruoyi
+ * @deprecated 3.6.0 移除 使用原生注解处理 方法更全 {@link com.baomidou.dynamic.datasource.annotation.DS}
  */
 @Target({ElementType.METHOD, ElementType.TYPE})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Inherited
+@Deprecated
 public @interface DataSource {
     /**
      * 切换数据源名称

+ 35 - 19
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java

@@ -5,61 +5,62 @@ package com.ruoyi.common.constant;
  *
  * @author ruoyi
  */
-public class Constants {
+public interface Constants {
+
     /**
      * UTF-8 字符集
      */
-    public static final String UTF8 = "UTF-8";
+    String UTF8 = "UTF-8";
 
     /**
      * GBK 字符集
      */
-    public static final String GBK = "GBK";
+    String GBK = "GBK";
 
     /**
      * http请求
      */
-    public static final String HTTP = "http://";
+    String HTTP = "http://";
 
     /**
      * https请求
      */
-    public static final String HTTPS = "https://";
+    String HTTPS = "https://";
 
     /**
      * 通用成功标识
      */
-    public static final String SUCCESS = "0";
+    String SUCCESS = "0";
 
     /**
      * 通用失败标识
      */
-    public static final String FAIL = "1";
+    String FAIL = "1";
 
     /**
      * 登录成功
      */
-    public static final String LOGIN_SUCCESS = "Success";
+    String LOGIN_SUCCESS = "Success";
 
     /**
      * 注销
      */
-    public static final String LOGOUT = "Logout";
+    String LOGOUT = "Logout";
 
     /**
      * 注册
      */
-    public static final String REGISTER = "Register";
+    String REGISTER = "Register";
 
     /**
      * 登录失败
      */
-    public static final String LOGIN_FAIL = "Error";
+    String LOGIN_FAIL = "Error";
 
     /**
      * 验证码 redis key
      */
-    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
+    String CAPTCHA_CODE_KEY = "captcha_codes:";
 
     /**
      * 登录用户 redis key
@@ -74,36 +75,51 @@ public class Constants {
     /**
      * 防重提交 redis key
      */
-    public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
+    String REPEAT_SUBMIT_KEY = "repeat_submit:";
 
     /**
      * 限流 redis key
      */
-    public static final String RATE_LIMIT_KEY = "rate_limit:";
+    String RATE_LIMIT_KEY = "rate_limit:";
 
     /**
      * 验证码有效期(分钟)
      */
-    public static final Integer CAPTCHA_EXPIRATION = 2;
+    Integer CAPTCHA_EXPIRATION = 2;
+
+    /**
+     * 登陆错误 redis key
+     */
+    String LOGIN_ERROR = "login_error:";
+
+    /**
+     * 登录错误次数
+     */
+    Integer LOGIN_ERROR_NUMBER = 5;
+
+    /**
+     * 登录错误限制时间(分钟)
+     */
+    Integer LOGIN_ERROR_LIMIT_TIME = 10;
 
     /**
      * 令牌
      */
-    public static final String TOKEN = "token";
+    String TOKEN = "token";
 
     /**
      * 令牌前缀
      */
-    public static final String LOGIN_USER_KEY = "login_user_key";
+    String LOGIN_USER_KEY = "login_user_key";
 
     /**
      * 参数管理 cache key
      */
-    public static final String SYS_CONFIG_KEY = "sys_config:";
+    String SYS_CONFIG_KEY = "sys_config:";
 
     /**
      * 字典管理 cache key
      */
-    public static final String SYS_DICT_KEY = "sys_dict:";
+    String SYS_DICT_KEY = "sys_dict:";
 
 }

+ 41 - 41
ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java

@@ -5,184 +5,184 @@ package com.ruoyi.common.constant;
  *
  * @author ruoyi
  */
-public class GenConstants {
+public interface GenConstants {
     /**
      * 单表(增删改查)
      */
-    public static final String TPL_CRUD = "crud";
+    String TPL_CRUD = "crud";
 
     /**
      * 树表(增删改查)
      */
-    public static final String TPL_TREE = "tree";
+    String TPL_TREE = "tree";
 
     /**
      * 主子表(增删改查)
      */
-    public static final String TPL_SUB = "sub";
+    String TPL_SUB = "sub";
 
     /**
      * 树编码字段
      */
-    public static final String TREE_CODE = "treeCode";
+    String TREE_CODE = "treeCode";
 
     /**
      * 树父编码字段
      */
-    public static final String TREE_PARENT_CODE = "treeParentCode";
+    String TREE_PARENT_CODE = "treeParentCode";
 
     /**
      * 树名称字段
      */
-    public static final String TREE_NAME = "treeName";
+    String TREE_NAME = "treeName";
 
     /**
      * 上级菜单ID字段
      */
-    public static final String PARENT_MENU_ID = "parentMenuId";
+    String PARENT_MENU_ID = "parentMenuId";
 
     /**
      * 上级菜单名称字段
      */
-    public static final String PARENT_MENU_NAME = "parentMenuName";
+    String PARENT_MENU_NAME = "parentMenuName";
 
     /**
      * 数据库字符串类型
      */
-    public static final String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"};
+    String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"};
 
     /**
      * 数据库文本类型
      */
-    public static final String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"};
+    String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"};
 
     /**
      * 数据库时间类型
      */
-    public static final String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"};
+    String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"};
 
     /**
      * 数据库数字类型
      */
-    public static final String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer",
-            "bit", "bigint", "float", "double", "decimal"};
+    String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer",
+        "bit", "bigint", "float", "double", "decimal"};
 
     /**
      * BO对象 不需要添加字段
      */
-    public static final String[] COLUMNNAME_NOT_ADD = {"create_by", "create_time", "del_flag", "update_by",
-            "update_time", "version"};
+    String[] COLUMNNAME_NOT_ADD = {"create_by", "create_time", "del_flag", "update_by",
+        "update_time", "version"};
 
     /**
      * BO对象 不需要编辑字段
      */
-    public static final String[] COLUMNNAME_NOT_EDIT = {"create_by", "create_time", "del_flag", "update_by",
-            "update_time", "version"};
+    String[] COLUMNNAME_NOT_EDIT = {"create_by", "create_time", "del_flag", "update_by",
+        "update_time", "version"};
 
     /**
      * VO对象 不需要返回字段
      */
-    public static final String[] COLUMNNAME_NOT_LIST = {"create_by", "create_time", "del_flag", "update_by",
-            "update_time", "version"};
+    String[] COLUMNNAME_NOT_LIST = {"create_by", "create_time", "del_flag", "update_by",
+        "update_time", "version"};
 
     /**
      * BO对象 不需要查询字段
      */
-    public static final String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by",
-            "update_time", "remark", "version"};
+    String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by",
+        "update_time", "remark", "version"};
 
     /**
      * Entity基类字段
      */
-    public static final String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime"};
+    String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime"};
 
     /**
      * Tree基类字段
      */
-    public static final String[] TREE_ENTITY = {"parentName", "parentId", "children"};
+    String[] TREE_ENTITY = {"parentName", "parentId", "children"};
 
     /**
      * 文本框
      */
-    public static final String HTML_INPUT = "input";
+    String HTML_INPUT = "input";
 
     /**
      * 文本域
      */
-    public static final String HTML_TEXTAREA = "textarea";
+    String HTML_TEXTAREA = "textarea";
 
     /**
      * 下拉框
      */
-    public static final String HTML_SELECT = "select";
+    String HTML_SELECT = "select";
 
     /**
      * 单选框
      */
-    public static final String HTML_RADIO = "radio";
+    String HTML_RADIO = "radio";
 
     /**
      * 复选框
      */
-    public static final String HTML_CHECKBOX = "checkbox";
+    String HTML_CHECKBOX = "checkbox";
 
     /**
      * 日期控件
      */
-    public static final String HTML_DATETIME = "datetime";
+    String HTML_DATETIME = "datetime";
 
     /**
      * 图片上传控件
      */
-    public static final String HTML_IMAGE_UPLOAD = "imageUpload";
+    String HTML_IMAGE_UPLOAD = "imageUpload";
 
     /**
      * 文件上传控件
      */
-    public static final String HTML_FILE_UPLOAD = "fileUpload";
+    String HTML_FILE_UPLOAD = "fileUpload";
 
     /**
      * 富文本控件
      */
-    public static final String HTML_EDITOR = "editor";
+    String HTML_EDITOR = "editor";
 
     /**
      * 字符串类型
      */
-    public static final String TYPE_STRING = "String";
+    String TYPE_STRING = "String";
 
     /**
      * 整型
      */
-    public static final String TYPE_INTEGER = "Integer";
+    String TYPE_INTEGER = "Integer";
 
     /**
      * 长整型
      */
-    public static final String TYPE_LONG = "Long";
+    String TYPE_LONG = "Long";
 
     /**
      * 浮点型
      */
-    public static final String TYPE_DOUBLE = "Double";
+    String TYPE_DOUBLE = "Double";
 
     /**
      * 高精度计算类型
      */
-    public static final String TYPE_BIGDECIMAL = "BigDecimal";
+    String TYPE_BIGDECIMAL = "BigDecimal";
 
     /**
      * 时间类型
      */
-    public static final String TYPE_DATE = "Date";
+    String TYPE_DATE = "Date";
 
     /**
      * 模糊查询
      */
-    public static final String QUERY_LIKE = "LIKE";
+    String QUERY_LIKE = "LIKE";
 
     /**
      * 需要
      */
-    public static final String REQUIRE = "1";
+    String REQUIRE = "1";
 }

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

@@ -5,108 +5,108 @@ package com.ruoyi.common.constant;
  *
  * @author ruoyi
  */
-public class UserConstants {
+public interface UserConstants {
 
     /**
      * 平台内系统用户的唯一标志
      */
-    public static final String SYS_USER = "SYS_USER";
+    String SYS_USER = "SYS_USER";
 
     /**
      * 正常状态
      */
-    public static final String NORMAL = "0";
+    String NORMAL = "0";
 
     /**
      * 异常状态
      */
-    public static final String EXCEPTION = "1";
+    String EXCEPTION = "1";
 
     /**
      * 用户封禁状态
      */
-    public static final String USER_DISABLE = "1";
+    String USER_DISABLE = "1";
 
     /**
      * 角色封禁状态
      */
-    public static final String ROLE_DISABLE = "1";
+    String ROLE_DISABLE = "1";
 
     /**
      * 部门正常状态
      */
-    public static final String DEPT_NORMAL = "0";
+    String DEPT_NORMAL = "0";
 
     /**
      * 部门停用状态
      */
-    public static final String DEPT_DISABLE = "1";
+    String DEPT_DISABLE = "1";
 
     /**
      * 字典正常状态
      */
-    public static final String DICT_NORMAL = "0";
+    String DICT_NORMAL = "0";
 
     /**
      * 是否为系统默认(是)
      */
-    public static final String YES = "Y";
+    String YES = "Y";
 
     /**
      * 是否菜单外链(是)
      */
-    public static final String YES_FRAME = "0";
+    String YES_FRAME = "0";
 
     /**
      * 是否菜单外链(否)
      */
-    public static final String NO_FRAME = "1";
+    String NO_FRAME = "1";
 
     /**
      * 菜单类型(目录)
      */
-    public static final String TYPE_DIR = "M";
+    String TYPE_DIR = "M";
 
     /**
      * 菜单类型(菜单)
      */
-    public static final String TYPE_MENU = "C";
+    String TYPE_MENU = "C";
 
     /**
      * 菜单类型(按钮)
      */
-    public static final String TYPE_BUTTON = "F";
+    String TYPE_BUTTON = "F";
 
     /**
      * Layout组件标识
      */
-    public final static String LAYOUT = "Layout";
+    String LAYOUT = "Layout";
 
     /**
      * ParentView组件标识
      */
-    public final static String PARENT_VIEW = "ParentView";
+    String PARENT_VIEW = "ParentView";
 
     /**
      * InnerLink组件标识
      */
-    public final static String INNER_LINK = "InnerLink";
+    String INNER_LINK = "InnerLink";
 
     /**
      * 校验返回结果码
      */
-    public final static String UNIQUE = "0";
-    public final static String NOT_UNIQUE = "1";
+    String UNIQUE = "0";
+    String NOT_UNIQUE = "1";
 
     /**
      * 用户名长度限制
      */
-    public static final int USERNAME_MIN_LENGTH = 2;
-    public static final int USERNAME_MAX_LENGTH = 20;
+    int USERNAME_MIN_LENGTH = 2;
+    int USERNAME_MAX_LENGTH = 20;
 
     /**
      * 密码长度限制
      */
-    public static final int PASSWORD_MIN_LENGTH = 5;
-    public static final int PASSWORD_MAX_LENGTH = 20;
+    int PASSWORD_MIN_LENGTH = 5;
+    int PASSWORD_MAX_LENGTH = 20;
 }

+ 93 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/PageQuery.java

@@ -0,0 +1,93 @@
+package com.ruoyi.common.core.domain;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.sql.SqlUtil;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * 分页查询实体类
+ *
+ * @author Lion Li
+ */
+
+@Data
+@Accessors(chain = true)
+public class PageQuery implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+    /**
+     * 分页大小
+     */
+    @ApiModelProperty("分页大小")
+    private Integer pageSize;
+
+    /**
+     * 当前页数
+     */
+    @ApiModelProperty("当前页数")
+    private Integer pageNum;
+
+    /**
+     * 排序列
+     */
+    @ApiModelProperty("排序列")
+    private String orderByColumn;
+
+    /**
+     * 排序的方向desc或者asc
+     */
+    @ApiModelProperty(value = "排序的方向", example = "asc,desc")
+    private String isAsc;
+
+    /**
+     * 当前记录起始索引 默认值
+     */
+    public static final int DEFAULT_PAGE_NUM = 1;
+
+    /**
+     * 每页显示记录数 默认值 默认查全部
+     */
+    public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
+
+    public <T> Page<T> build() {
+        Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);
+        Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);
+        if (pageNum <= 0) {
+            pageNum = DEFAULT_PAGE_NUM;
+        }
+        Page<T> page = new Page<>(pageNum, pageSize);
+        OrderItem orderItem = buildOrderItem();
+        if (ObjectUtil.isNotNull(orderItem)) {
+            page.addOrder(orderItem);
+        }
+        return page;
+    }
+
+    private OrderItem buildOrderItem() {
+        // 兼容前端排序类型
+        if ("ascending".equals(isAsc)) {
+            isAsc = "asc";
+        } else if ("descending".equals(isAsc)) {
+            isAsc = "desc";
+        }
+        if (StringUtils.isNotBlank(orderByColumn)) {
+            String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
+            orderBy = StringUtils.toUnderScoreCase(orderBy);
+            if ("asc".equals(isAsc)) {
+                return OrderItem.asc(orderBy);
+            } else if ("desc".equals(isAsc)) {
+                return OrderItem.desc(orderBy);
+            }
+        }
+        return null;
+    }
+
+}

+ 2 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java

@@ -1,5 +1,6 @@
 package com.ruoyi.common.core.domain.entity;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.ruoyi.common.core.domain.TreeEntity;
@@ -65,6 +66,7 @@ public class SysMenu extends TreeEntity {
      * 路由参数
      */
 	@ApiModelProperty(value = "路由参数")
+    @TableField("`query`")
     private String query;
 
 	/**

+ 7 - 4
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.*;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.ruoyi.common.core.domain.BaseEntity;
+import com.ruoyi.common.xss.Xss;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -48,15 +49,17 @@ public class SysUser extends BaseEntity {
 	 * 用户账号
 	 */
 	@ApiModelProperty(value = "用户账号")
-	@NotBlank(message = "用户账号不能为空")
+    @Xss(message = "用户账号不能包含脚本字符")
+    @NotBlank(message = "用户账号不能为空")
 	@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
 	private String userName;
 
 	/**
 	 * 用户昵称
 	 */
-	@ApiModelProperty(value = "用户昵称")
-	@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
+    @ApiModelProperty(value = "用户昵称")
+    @Xss(message = "用户昵称不能包含脚本字符")
+    @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
 	private String nickName;
 
 	/**
@@ -162,7 +165,7 @@ public class SysUser extends BaseEntity {
 	private Long[] postIds;
 
 	/**
-	 * 角色ID
+	 * 数据权限 当前角色ID
 	 */
 	@ApiModelProperty(value = "角色ID")
 	@TableField(exist = false)

+ 77 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/BaseMapperPlus.java

@@ -1,9 +1,18 @@
 package com.ruoyi.common.core.mybatisplus.core;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.utils.BeanCopyUtils;
 import org.apache.ibatis.annotations.Param;
 
+import java.io.Serializable;
 import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
 /**
  * 自定义 Mapper 接口, 实现 自定义扩展
@@ -18,4 +27,72 @@ public interface BaseMapperPlus<T> extends BaseMapper<T> {
 	 */
 	int insertAll(@Param("list") Collection<T> batchList);
 
+    /**
+     * 根据 ID 查询
+     */
+    default <V> V selectVoById(Serializable id, Class<V> voClass){
+        T obj = this.selectById(id);
+        if (ObjectUtil.isNull(obj)) {
+            return null;
+        }
+        return BeanCopyUtils.copy(obj, voClass);
+    }
+
+    /**
+     * 查询(根据ID 批量查询)
+     */
+    default <V> List<V> selectVoBatchIds(Collection<? extends Serializable> idList, Class<V> voClass){
+        List<T> list = this.selectBatchIds(idList);
+        if (CollUtil.isEmpty(list)) {
+            return CollUtil.newArrayList();
+        }
+        return BeanCopyUtils.copyList(list, voClass);
+    }
+
+    /**
+     * 查询(根据 columnMap 条件)
+     */
+    default <V> List<V> selectVoByMap(Map<String, Object> map, Class<V> voClass){
+        List<T> list = this.selectByMap(map);
+        if (CollUtil.isEmpty(list)) {
+            return CollUtil.newArrayList();
+        }
+        return BeanCopyUtils.copyList(list, voClass);
+    }
+
+    /**
+     * 根据 entity 条件,查询一条记录
+     */
+    default <V> V selectVoOne(Wrapper<T> wrapper, Class<V> voClass) {
+        T obj = this.selectOne(wrapper);
+        if (ObjectUtil.isNull(obj)) {
+            return null;
+        }
+        return BeanCopyUtils.copy(obj, voClass);
+    }
+
+    /**
+     * 根据 entity 条件,查询全部记录
+     */
+    default <V> List<V> selectVoList(Wrapper<T> wrapper, Class<V> voClass) {
+        List<T> list = this.selectList(wrapper);
+        if (CollUtil.isEmpty(list)) {
+            return CollUtil.newArrayList();
+        }
+        return BeanCopyUtils.copyList(list, voClass);
+    }
+
+    /**
+     * 分页查询VO
+     */
+    default <V, P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<V> voClass) {
+        IPage<T> pageData = this.selectPage(page, wrapper);
+        IPage<V> voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal());
+        if (CollUtil.isEmpty(pageData.getRecords())) {
+            return (P) voPage;
+        }
+        voPage.setRecords(BeanCopyUtils.copyList(pageData.getRecords(), voClass));
+        return (P) voPage;
+    }
+
 }

+ 21 - 41
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/IServicePlus.java

@@ -1,7 +1,7 @@
 package com.ruoyi.common.core.mybatisplus.core;
 
-import cn.hutool.core.bean.copier.CopyOptions;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ruoyi.common.core.page.PagePlus;
@@ -23,14 +23,9 @@ public interface IServicePlus<T, V> extends IService<T> {
 
 	/**
 	 * @param id          主键id
-	 * @param copyOptions copy条件
 	 * @return V对象
 	 */
-	V getVoById(Serializable id, CopyOptions copyOptions);
-
-	default V getVoById(Serializable id) {
-		return getVoById(id, new CopyOptions());
-	}
+	V getVoById(Serializable id);
 
 	/**
 	 * @param convertor 自定义转换器
@@ -41,14 +36,9 @@ public interface IServicePlus<T, V> extends IService<T> {
 
 	/**
 	 * @param idList      id列表
-	 * @param copyOptions copy条件
 	 * @return V对象
 	 */
-	List<V> listVoByIds(Collection<? extends Serializable> idList, CopyOptions copyOptions);
-
-	default List<V> listVoByIds(Collection<? extends Serializable> idList) {
-		return listVoByIds(idList, new CopyOptions());
-	}
+	List<V> listVoByIds(Collection<? extends Serializable> idList);
 
 	/**
 	 * @param convertor 自定义转换器
@@ -64,14 +54,9 @@ public interface IServicePlus<T, V> extends IService<T> {
 
 	/**
 	 * @param columnMap   表字段 map 对象
-	 * @param copyOptions copy条件
 	 * @return V对象
 	 */
-	List<V> listVoByMap(Map<String, Object> columnMap, CopyOptions copyOptions);
-
-	default List<V> listVoByMap(Map<String, Object> columnMap) {
-		return listVoByMap(columnMap, new CopyOptions());
-	}
+	List<V> listVoByMap(Map<String, Object> columnMap);
 
 	/**
 	 * @param convertor 自定义转换器
@@ -87,14 +72,9 @@ public interface IServicePlus<T, V> extends IService<T> {
 
 	/**
 	 * @param queryWrapper 查询条件
-	 * @param copyOptions  copy条件
 	 * @return V对象
 	 */
-	V getVoOne(Wrapper<T> queryWrapper, CopyOptions copyOptions);
-
-	default V getVoOne(Wrapper<T> queryWrapper) {
-		return getVoOne(queryWrapper, new CopyOptions());
-	}
+	V getVoOne(Wrapper<T> queryWrapper);
 
 	/**
 	 * @param convertor 自定义转换器
@@ -105,14 +85,9 @@ public interface IServicePlus<T, V> extends IService<T> {
 
 	/**
 	 * @param queryWrapper 查询条件
-	 * @param copyOptions  copy条件
 	 * @return V对象
 	 */
-	List<V> listVo(Wrapper<T> queryWrapper, CopyOptions copyOptions);
-
-	default List<V> listVo(Wrapper<T> queryWrapper) {
-		return listVo(queryWrapper, new CopyOptions());
-	}
+	List<V> listVo(Wrapper<T> queryWrapper);
 
 	/**
 	 * @param convertor 自定义转换器
@@ -139,31 +114,36 @@ public interface IServicePlus<T, V> extends IService<T> {
 	/**
 	 * @param page         分页对象
 	 * @param queryWrapper 查询条件
-	 * @param copyOptions  copy条件
 	 * @return V对象
+     * @deprecated 3.6.0 移除 请使用 {@link ServicePlusImpl#pageVo(IPage, Wrapper)}
 	 */
-	PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper, CopyOptions copyOptions);
-
-	default PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper) {
-		return pageVo(page, queryWrapper, new CopyOptions());
-	}
-
-	/**
-	 * @param convertor 自定义转换器
-	 */
+    @Deprecated
+	PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper);
+
+    /**
+     * @param convertor 自定义转换器
+     * @deprecated 3.6.0 移除 请使用 {@link ServicePlusImpl#pageVo(IPage, Wrapper)}
+     */
+    @Deprecated
 	default PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper,
 								  Function<Collection<T>, List<V>> convertor) {
 		PagePlus<T, V> result = getBaseMapper().selectPage(page, queryWrapper);
 		return result.setRecordsVo(convertor.apply(result.getRecords()));
 	}
 
+    /**
+     * @deprecated 3.6.0 移除 请使用 {@link ServicePlusImpl#pageVo(IPage, Wrapper)}
+     */
+    @Deprecated
 	default PagePlus<T, V> pageVo(PagePlus<T, V> page) {
 		return pageVo(page, Wrappers.emptyWrapper());
 	}
 
 	/**
 	 * @param convertor 自定义转换器
+     * @deprecated 3.6.0 移除 请使用 {@link ServicePlusImpl#pageVo(IPage, Wrapper)}
 	 */
+    @Deprecated
 	default PagePlus<T, V> pageVo(PagePlus<T, V> page, Function<Collection<T>, List<V>> convertor) {
 		return pageVo(page, Wrappers.emptyWrapper(), convertor);
 	}

+ 25 - 40
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/ServicePlusImpl.java

@@ -1,9 +1,9 @@
 package com.ruoyi.common.core.mybatisplus.core;
 
-import cn.hutool.core.bean.copier.CopyOptions;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.metadata.TableInfo;
 import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
 import com.baomidou.mybatisplus.core.toolkit.Assert;
@@ -161,81 +161,66 @@ public class ServicePlusImpl<M extends BaseMapperPlus<T>, T, V> extends ServiceI
 
 	/**
 	 * 根据 ID 查询
-	 *
-	 * @param id 主键ID
 	 */
 	@Override
-	public V getVoById(Serializable id, CopyOptions copyOptions) {
-		T t = getBaseMapper().selectById(id);
-		return BeanCopyUtils.oneCopy(t, copyOptions, voClass);
+	public V getVoById(Serializable id) {
+        return getBaseMapper().selectVoById(id, voClass);
 	}
 
 	/**
 	 * 查询(根据ID 批量查询)
-	 *
-	 * @param idList 主键ID列表
 	 */
 	@Override
-	public List<V> listVoByIds(Collection<? extends Serializable> idList, CopyOptions copyOptions) {
-		List<T> list = getBaseMapper().selectBatchIds(idList);
-		if (list == null) {
-			return null;
-		}
-		return BeanCopyUtils.listCopy(list, copyOptions, voClass);
+	public List<V> listVoByIds(Collection<? extends Serializable> idList) {
+        return getBaseMapper().selectVoBatchIds(idList, voClass);
 	}
 
 	/**
 	 * 查询(根据 columnMap 条件)
-	 *
-	 * @param columnMap 表字段 map 对象
 	 */
 	@Override
-	public List<V> listVoByMap(Map<String, Object> columnMap, CopyOptions copyOptions) {
-		List<T> list = getBaseMapper().selectByMap(columnMap);
-		if (list == null) {
-			return null;
-		}
-		return BeanCopyUtils.listCopy(list, copyOptions, voClass);
+	public List<V> listVoByMap(Map<String, Object> columnMap) {
+        return getBaseMapper().selectVoByMap(columnMap, voClass);
 	}
 
 	/**
 	 * 根据 Wrapper,查询一条记录 <br/>
 	 * <p>结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>
-	 *
-	 * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 	 */
 	@Override
-	public V getVoOne(Wrapper<T> queryWrapper, CopyOptions copyOptions) {
-		T t = getOne(queryWrapper, true);
-		return BeanCopyUtils.oneCopy(t, copyOptions, voClass);
+	public V getVoOne(Wrapper<T> queryWrapper) {
+        return getBaseMapper().selectVoOne(queryWrapper, voClass);
 	}
 
 	/**
 	 * 查询列表
-	 *
-	 * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 	 */
 	@Override
-	public List<V> listVo(Wrapper<T> queryWrapper, CopyOptions copyOptions) {
-		List<T> list = getBaseMapper().selectList(queryWrapper);
-		if (list == null) {
-			return null;
-		}
-		return BeanCopyUtils.listCopy(list, copyOptions, voClass);
+	public List<V> listVo(Wrapper<T> queryWrapper) {
+        return getBaseMapper().selectVoList(queryWrapper, voClass);
 	}
 
 	/**
 	 * 翻页查询
-	 *
-	 * @param page         翻页对象
-	 * @param queryWrapper 实体对象封装操作类
+     * @deprecated 3.6.0 移除 请使用 {@link #pageVo(IPage, Wrapper)}
 	 */
 	@Override
-	public PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper, CopyOptions copyOptions) {
+    @Deprecated
+	public PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper) {
 		PagePlus<T, V> result = getBaseMapper().selectPage(page, queryWrapper);
-		List<V> volist = BeanCopyUtils.listCopy(result.getRecords(), copyOptions, voClass);
+		List<V> volist = BeanCopyUtils.copyList(result.getRecords(), voClass);
 		result.setRecordsVo(volist);
 		return result;
 	}
 
+    /**
+     * 翻页查询
+     *
+     * @param page         翻页对象
+     * @param queryWrapper 实体对象封装操作类
+     */
+    public <P extends IPage<V>> P pageVo(IPage<T> page, Wrapper<T> queryWrapper) {
+        return getBaseMapper().selectVoPage(page, queryWrapper, voClass);
+    }
+
 }

+ 2 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/page/PagePlus.java

@@ -16,9 +16,11 @@ import java.util.List;
  * @param <T> 数据库实体
  * @param <K> vo实体
  * @author Lion Li
+ * @deprecated 3.6.0 删除 请使用 {@link com.ruoyi.common.core.domain.PageQuery#build()}
  */
 @Data
 @Accessors(chain = true)
+@Deprecated
 public class PagePlus<T,K> implements IPage<T> {
 
     /**

+ 27 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java

@@ -1,5 +1,7 @@
 package com.ruoyi.common.core.page;
 
+import cn.hutool.http.HttpStatus;
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -57,4 +59,29 @@ public class TableDataInfo<T> implements Serializable {
 		this.total = total;
 	}
 
+    public static <T> TableDataInfo<T> build(IPage<T> page) {
+        TableDataInfo<T> rspData = new TableDataInfo<>();
+        rspData.setCode(HttpStatus.HTTP_OK);
+        rspData.setMsg("查询成功");
+        rspData.setRows(page.getRecords());
+        rspData.setTotal(page.getTotal());
+        return rspData;
+    }
+
+    public static <T> TableDataInfo<T> build(List<T> list) {
+        TableDataInfo<T> rspData = new TableDataInfo<>();
+        rspData.setCode(HttpStatus.HTTP_OK);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(list.size());
+        return rspData;
+    }
+
+    public static <T> TableDataInfo<T> build() {
+        TableDataInfo<T> rspData = new TableDataInfo<>();
+        rspData.setCode(HttpStatus.HTTP_OK);
+        rspData.setMsg("查询成功");
+        return rspData;
+    }
+
 }

+ 72 - 0
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataScopeType.java

@@ -0,0 +1,72 @@
+package com.ruoyi.common.enums;
+
+import com.ruoyi.common.utils.StringUtils;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 数据权限类型
+ *
+ * 语法支持 spel 模板表达式
+ *
+ * 内置数据 user 当前用户 内容参考 SysUser
+ * 如需扩展数据 可使用 {@link com.ruoyi.common.helper.DataPermissionHelper} 操作
+ * 内置服务 sdss 系统数据权限服务 内容参考 SysDataScopeService
+ * 如需扩展更多自定义服务 可以参考 sdss 自行编写
+ *
+ * @author Lion Li
+ * @version 3.5.0
+ */
+@Getter
+@AllArgsConstructor
+public enum DataScopeType {
+
+    /**
+     * 全部数据权限
+     */
+    ALL("1", "", ""),
+
+    /**
+     * 自定数据权限
+     */
+    CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", ""),
+
+    /**
+     * 部门数据权限
+     */
+    DEPT("3", " #{#deptName} = #{#user.deptId} ", ""),
+
+    /**
+     * 部门及以下数据权限
+     */
+    DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", ""),
+
+    /**
+     * 仅本人数据权限
+     */
+    SELF("5", " #{#userName} = #{#user.userId} " , " 1 = 0 ");
+
+    private final String code;
+
+    /**
+     * 语法 采用 spel 模板表达式
+     */
+    private final String sqlTemplate;
+
+    /**
+     * 不满足 sqlTemplate 则填充
+     */
+    private final String elseSql;
+
+    public static DataScopeType findCode(String code) {
+        if (StringUtils.isBlank(code)) {
+            return null;
+        }
+        for (DataScopeType type : values()) {
+            if (type.getCode().equals(code)) {
+                return type;
+            }
+        }
+        return null;
+    }
+}

+ 2 - 0
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java

@@ -7,8 +7,10 @@ import lombok.Getter;
  * 数据源
  *
  * @author Lion Li
+ * @deprecated 3.6.0 移除
  */
 @AllArgsConstructor
+@Deprecated
 public enum DataSourceType {
     /**
      * 主库

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

@@ -5,7 +5,7 @@ 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.JsonUtils;
 import com.ruoyi.common.utils.ValidatorUtils;
 import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -84,7 +84,7 @@ public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements
     @Override
     public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
         this.headMap = headMap;
-        log.debug("解析到一条表头数据: {}", JSON.toJSONString(headMap));
+        log.debug("解析到一条表头数据: {}", JsonUtils.toJsonString(headMap));
     }
 
     @Override

+ 1 - 1
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java

@@ -9,6 +9,6 @@ public class CaptchaException extends UserException {
     private static final long serialVersionUID = 1L;
 
     public CaptchaException() {
-        super("user.jcaptcha.error", null);
+        super("user.jcaptcha.error");
     }
 }

+ 1 - 1
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java

@@ -9,6 +9,6 @@ public class CaptchaExpireException extends UserException {
     private static final long serialVersionUID = 1L;
 
     public CaptchaExpireException() {
-        super("user.jcaptcha.expire", null);
+        super("user.jcaptcha.expire");
     }
 }

+ 1 - 1
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java

@@ -9,6 +9,6 @@ public class UserPasswordNotMatchException extends UserException {
     private static final long serialVersionUID = 1L;
 
     public UserPasswordNotMatchException() {
-        super("user.password.not.match", null);
+        super("user.password.not.match");
     }
 }

+ 45 - 0
ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java

@@ -0,0 +1,45 @@
+package com.ruoyi.common.helper;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.ruoyi.common.utils.ServletUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 数据权限助手
+ *
+ * @author Lion Li
+ * @version 3.5.0
+ */
+@SuppressWarnings("unchecked cast")
+public class DataPermissionHelper {
+
+    private static final String DATA_PERMISSION_KEY = "data:permission";
+
+    public static <T> T getVariable(String key) {
+        Map<String, Object> context = getContext();
+        return (T) context.get(key);
+    }
+
+
+
+    public static void setVariable(String key, Object value) {
+        Map<String, Object> context = getContext();
+        context.put(key, value);
+    }
+
+    public static Map<String, Object> getContext() {
+        HttpServletRequest request = ServletUtils.getRequest();
+        Object attribute = request.getAttribute(DATA_PERMISSION_KEY);
+        if (ObjectUtil.isNull(attribute)) {
+            request.setAttribute(DATA_PERMISSION_KEY, new HashMap<>());
+            attribute = request.getAttribute(DATA_PERMISSION_KEY);
+        }
+        if (attribute instanceof Map) {
+            return (Map<String, Object>) attribute;
+        }
+        throw new NullPointerException("data permission context type exception");
+    }
+}

+ 76 - 22
ruoyi-common/src/main/java/com/ruoyi/common/utils/BeanCopyUtils.java

@@ -1,66 +1,120 @@
 package com.ruoyi.common.utils;
 
-import cn.hutool.core.bean.copier.BeanCopier;
-import cn.hutool.core.bean.copier.CopyOptions;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.extra.cglib.CglibUtil;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 
 import java.util.List;
-import java.util.stream.Collectors;
+import java.util.Map;
 
 /**
- * bean深拷贝工具
+ * bean深拷贝工具(基于 cglib 性能优异)
  *
  * @author Lion Li
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class BeanCopyUtils {
 
     /**
      * 单对象基于class创建拷贝
      *
-     * @param source      数据来源实体
-     * @param copyOptions copy条件
-     * @param desc        描述对象 转换后的对象
+     * @param source 数据来源实体
+     * @param desc   描述对象 转换后的对象
      * @return desc
      */
-    public static <T, V> V oneCopy(T source, CopyOptions copyOptions, Class<V> desc) {
-        V v = ReflectUtil.newInstanceIfPossible(desc);
-        return oneCopy(source, copyOptions, v);
+    public static <T, V> V copy(T source, Class<V> desc) {
+        if (ObjectUtil.isNull(source)) {
+            return null;
+        }
+        if (ObjectUtil.isNull(desc)) {
+            return null;
+        }
+        return CglibUtil.copy(source, desc);
     }
 
     /**
      * 单对象基于对象创建拷贝
      *
-     * @param source      数据来源实体
-     * @param copyOptions copy条件
-     * @param desc        转换后的对象
+     * @param source 数据来源实体
+     * @param desc   转换后的对象
      * @return desc
      */
-    public static <T, V> V oneCopy(T source, CopyOptions copyOptions, V desc) {
+    public static <T, V> V copy(T source, V desc) {
         if (ObjectUtil.isNull(source)) {
             return null;
         }
-        return BeanCopier.create(source, desc, copyOptions).copy();
+        if (ObjectUtil.isNull(desc)) {
+            return null;
+        }
+        CglibUtil.copy(source, desc);
+        return desc;
     }
 
     /**
      * 列表对象基于class创建拷贝
      *
-     * @param sourceList  数据来源实体列表
-     * @param copyOptions copy条件
-     * @param desc        描述对象 转换后的对象
+     * @param sourceList 数据来源实体列表
+     * @param desc       描述对象 转换后的对象
      * @return desc
      */
-    public static <T, V> List<V> listCopy(List<T> sourceList, CopyOptions copyOptions, Class<V> desc) {
+    public static <T, V> List<V> copyList(List<T> sourceList, Class<V> desc) {
         if (ObjectUtil.isNull(sourceList)) {
             return null;
         }
         if (CollUtil.isEmpty(sourceList)) {
             return CollUtil.newArrayList();
         }
-        return sourceList.stream()
-                .map(source -> oneCopy(source, copyOptions, desc))
-                .collect(Collectors.toList());
+        return CglibUtil.copyList(sourceList, () -> ReflectUtil.newInstanceIfPossible(desc));
+    }
+
+    /**
+     * bean拷贝到map
+     *
+     * @param bean 数据来源实体
+     * @return map对象
+     */
+    public static <T> Map<String, Object> copyToMap(T bean) {
+        if (ObjectUtil.isNull(bean)) {
+            return null;
+        }
+        return CglibUtil.toMap(bean);
+    }
+
+    /**
+     * map拷贝到bean
+     *
+     * @param map       数据来源
+     * @param beanClass bean类
+     * @return bean对象
+     */
+    public static <T> T mapToBean(Map<String, Object> map, Class<T> beanClass) {
+        if (MapUtil.isEmpty(map)) {
+            return null;
+        }
+        if (ObjectUtil.isNull(beanClass)) {
+            return null;
+        }
+        return CglibUtil.toBean(map, beanClass);
+    }
+
+    /**
+     * map拷贝到bean
+     *
+     * @param map  数据来源
+     * @param bean bean对象
+     * @return bean对象
+     */
+    public static <T> T mapToBean(Map<String, Object> map, T bean) {
+        if (MapUtil.isEmpty(map)) {
+            return null;
+        }
+        if (ObjectUtil.isNull(bean)) {
+            return null;
+        }
+        return CglibUtil.fillBean(map, bean);
     }
 }

+ 3 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java

@@ -1,5 +1,7 @@
 package com.ruoyi.common.utils;
 
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 import org.apache.commons.lang3.time.DateFormatUtils;
 
 import java.lang.management.ManagementFactory;
@@ -12,6 +14,7 @@ import java.util.Date;
  *
  * @author ruoyi
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
 
     public static String YYYY = "yyyy";

+ 0 - 158
ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java

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

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

@@ -21,14 +21,18 @@ import java.util.Map;
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class JsonUtils {
 
-    private static ObjectMapper objectMapper = SpringUtils.getBean(ObjectMapper.class);
+    private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);
+
+    public static ObjectMapper getObjectMapper() {
+        return OBJECT_MAPPER;
+    }
 
     public static String toJsonString(Object object) {
         if (StringUtils.isNull(object)) {
             return null;
         }
         try {
-            return objectMapper.writeValueAsString(object);
+            return OBJECT_MAPPER.writeValueAsString(object);
         } catch (JsonProcessingException e) {
             throw new RuntimeException(e);
         }
@@ -39,7 +43,7 @@ public class JsonUtils {
             return null;
         }
         try {
-            return objectMapper.readValue(text, clazz);
+            return OBJECT_MAPPER.readValue(text, clazz);
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
@@ -50,7 +54,7 @@ public class JsonUtils {
             return null;
         }
         try {
-            return objectMapper.readValue(bytes, clazz);
+            return OBJECT_MAPPER.readValue(bytes, clazz);
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
@@ -61,7 +65,7 @@ public class JsonUtils {
             return null;
         }
         try {
-            return objectMapper.readValue(text, typeReference);
+            return OBJECT_MAPPER.readValue(text, typeReference);
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
@@ -72,7 +76,7 @@ public class JsonUtils {
             return null;
         }
         try {
-            return objectMapper.readValue(text, new TypeReference<Map<String, T>>() {
+            return OBJECT_MAPPER.readValue(text, new TypeReference<Map<String, T>>() {
             });
         } catch (IOException e) {
             throw new RuntimeException(e);
@@ -84,7 +88,7 @@ public class JsonUtils {
             return new ArrayList<>();
         }
         try {
-            return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
+            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
         } catch (IOException e) {
             throw new RuntimeException(e);
         }

+ 8 - 3
ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java

@@ -1,15 +1,21 @@
 package com.ruoyi.common.utils;
 
 import com.ruoyi.common.utils.spring.SpringUtils;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 import org.springframework.context.MessageSource;
 import org.springframework.context.i18n.LocaleContextHolder;
 
 /**
  * 获取i18n资源文件
  *
- * @author ruoyi
+ * @author Lion Li
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class MessageUtils {
+
+    private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class);
+
     /**
      * 根据消息键和参数 获取消息 委托给spring messageSource
      *
@@ -18,7 +24,6 @@ public class MessageUtils {
      * @return 获取国际化翻译值
      */
     public static String message(String code, Object... args) {
-        MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
-        return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
+        return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale());
     }
 }

+ 41 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java

@@ -2,11 +2,15 @@ package com.ruoyi.common.utils;
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.HttpStatus;
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.metadata.OrderItem;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.page.PagePlus;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.utils.sql.SqlUtil;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 
 import java.util.List;
 
@@ -14,37 +18,46 @@ import java.util.List;
  * 分页工具
  *
  * @author Lion Li
+ * @deprecated 3.6.0 删除 请使用 {@link PageQuery} 与 {@link TableDataInfo}
  */
+@Deprecated
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class PageUtils {
 
     /**
      * 当前记录起始索引
      */
+    @Deprecated
     public static final String PAGE_NUM = "pageNum";
 
     /**
      * 每页显示记录数
      */
+    @Deprecated
     public static final String PAGE_SIZE = "pageSize";
 
     /**
      * 排序列
      */
+    @Deprecated
     public static final String ORDER_BY_COLUMN = "orderByColumn";
 
     /**
      * 排序的方向 "desc" 或者 "asc".
      */
+    @Deprecated
     public static final String IS_ASC = "isAsc";
 
     /**
      * 当前记录起始索引 默认值
      */
+    @Deprecated
     public static final int DEFAULT_PAGE_NUM = 1;
 
     /**
      * 每页显示记录数 默认值 默认查全部
      */
+    @Deprecated
     public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
 
     /**
@@ -53,7 +66,10 @@ public class PageUtils {
      * @param <T> domain 实体
      * @param <K> vo 实体
      * @return 分页对象
+     * @deprecated 3.6.0 删除 请使用 {@link PageQuery#build()}
+     * 由于使用 Servlet 获取只能从 param 获取 灵活性降低 故将传参操作交给用户
      */
+    @Deprecated
     public static <T, K> PagePlus<T, K> buildPagePlus() {
         Integer pageNum = ServletUtils.getParameterToInt(PAGE_NUM, DEFAULT_PAGE_NUM);
         Integer pageSize = ServletUtils.getParameterToInt(PAGE_SIZE, DEFAULT_PAGE_SIZE);
@@ -70,6 +86,7 @@ public class PageUtils {
         return page;
     }
 
+    @Deprecated
     public static <T> Page<T> buildPage() {
         return buildPage(null, null);
     }
@@ -79,7 +96,10 @@ public class PageUtils {
      *
      * @param <T> domain 实体
      * @return 分页对象
+     * @deprecated 3.6.0 删除 请使用 {@link PageQuery#build()}
+     * 由于使用 Servlet 获取只能从 param 获取 灵活性降低 故将传参操作交给用户
      */
+    @Deprecated
     public static <T> Page<T> buildPage(String defaultOrderByColumn, String defaultIsAsc) {
         Integer pageNum = ServletUtils.getParameterToInt(PAGE_NUM, DEFAULT_PAGE_NUM);
         Integer pageSize = ServletUtils.getParameterToInt(PAGE_SIZE, DEFAULT_PAGE_SIZE);
@@ -115,6 +135,15 @@ public class PageUtils {
         return null;
     }
 
+    /**
+     * 构建 MP 普通分页对象
+     *
+     * @param <T> domain 实体
+     * @return 分页对象
+     * @deprecated 3.6.0 删除 请使用 {@link PageQuery#build()}
+     * 由于使用 Servlet 获取只能从 param 获取 灵活性降低 故将传参操作交给用户
+     */
+    @Deprecated
     public static <T, K> TableDataInfo<K> buildDataInfo(PagePlus<T, K> page) {
         TableDataInfo<K> rspData = new TableDataInfo<>();
         rspData.setCode(HttpStatus.HTTP_OK);
@@ -124,6 +153,10 @@ public class PageUtils {
         return rspData;
     }
 
+    /**
+     * @deprecated 3.6.0 删除 请使用 {@link TableDataInfo#build(IPage)}
+     */
+    @Deprecated
     public static <T> TableDataInfo<T> buildDataInfo(Page<T> page) {
         TableDataInfo<T> rspData = new TableDataInfo<>();
         rspData.setCode(HttpStatus.HTTP_OK);
@@ -133,6 +166,10 @@ public class PageUtils {
         return rspData;
     }
 
+    /**
+     * @deprecated 3.6.0 删除 请使用 {@link TableDataInfo#build(List)}
+     */
+    @Deprecated
     public static <T> TableDataInfo<T> buildDataInfo(List<T> list) {
         TableDataInfo<T> rspData = new TableDataInfo<>();
         rspData.setCode(HttpStatus.HTTP_OK);
@@ -142,6 +179,10 @@ public class PageUtils {
         return rspData;
     }
 
+    /**
+     * @deprecated 3.6.0 删除 请使用 {@link TableDataInfo#build()}
+     */
+    @Deprecated
     public static <T> TableDataInfo<T> buildDataInfo() {
         TableDataInfo<T> rspData = new TableDataInfo<>();
         rspData.setCode(HttpStatus.HTTP_OK);

+ 89 - 40
ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisUtils.java

@@ -1,6 +1,6 @@
 package com.ruoyi.common.utils;
 
-import com.google.common.collect.Lists;
+import cn.hutool.core.collection.IterUtil;
 import com.ruoyi.common.utils.spring.SpringUtils;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
@@ -23,7 +23,7 @@ import java.util.function.Consumer;
 @SuppressWarnings(value = {"unchecked", "rawtypes"})
 public class RedisUtils {
 
-    private static RedissonClient client = SpringUtils.getBean(RedissonClient.class);
+    private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
 
     /**
      * 限流
@@ -35,7 +35,7 @@ public class RedisUtils {
      * @return -1 表示失败
      */
     public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) {
-        RRateLimiter rateLimiter = client.getRateLimiter(key);
+        RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);
         rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS);
         if (rateLimiter.tryAcquire()) {
             return rateLimiter.availablePermits();
@@ -45,10 +45,10 @@ public class RedisUtils {
     }
 
     /**
-     * 获取实例id
+     * 获取客户端实例
      */
-    public static String getClientId() {
-        return client.getId();
+    public static RedissonClient getClient() {
+        return CLIENT;
     }
 
     /**
@@ -59,13 +59,13 @@ public class RedisUtils {
      * @param consumer   自定义处理
      */
     public static <T> void publish(String channelKey, T msg, Consumer<T> consumer) {
-        RTopic topic = client.getTopic(channelKey);
+        RTopic topic = CLIENT.getTopic(channelKey);
         topic.publish(msg);
         consumer.accept(msg);
     }
 
     public static <T> void publish(String channelKey, T msg) {
-        RTopic topic = client.getTopic(channelKey);
+        RTopic topic = CLIENT.getTopic(channelKey);
         topic.publish(msg);
     }
 
@@ -77,7 +77,7 @@ public class RedisUtils {
      * @param consumer   自定义处理
      */
     public static <T> void subscribe(String channelKey, Class<T> clazz, Consumer<T> consumer) {
-        RTopic topic = client.getTopic(channelKey);
+        RTopic topic = CLIENT.getTopic(channelKey);
         topic.addListener(clazz, (channel, msg) -> consumer.accept(msg));
     }
 
@@ -94,13 +94,13 @@ public class RedisUtils {
     /**
      * 缓存基本的对象,保留当前对象 TTL 有效期
      *
-     * @param key   缓存的键值
-     * @param value 缓存的值
+     * @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);
+        RBucket<Object> bucket = CLIENT.getBucket(key);
         if (isSaveTtl) {
             try {
                 bucket.setAndKeepTTL(value);
@@ -123,11 +123,24 @@ public class RedisUtils {
      * @param timeUnit 时间颗粒度
      */
     public static <T> void setCacheObject(final String key, final T value, final long timeout, final TimeUnit timeUnit) {
-        RBucket<T> result = client.getBucket(key);
+        RBucket<T> result = CLIENT.getBucket(key);
         result.set(value);
         result.expire(timeout, timeUnit);
     }
 
+    /**
+     * 注册对象监听器
+     *
+     * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
+     *
+     * @param key      缓存的键值
+     * @param listener 监听器配置
+     */
+    public static <T> void addObjectListener(final String key, final ObjectListener listener) {
+        RBucket<T> result = CLIENT.getBucket(key);
+        result.addListener(listener);
+    }
+
     /**
      * 设置有效时间
      *
@@ -148,7 +161,7 @@ public class RedisUtils {
      * @return true=设置成功;false=设置失败
      */
     public static boolean expire(final String key, final long timeout, final TimeUnit unit) {
-        RBucket rBucket = client.getBucket(key);
+        RBucket rBucket = CLIENT.getBucket(key);
         return rBucket.expire(timeout, unit);
     }
 
@@ -159,7 +172,7 @@ public class RedisUtils {
      * @return 缓存键值对应的数据
      */
     public static <T> T getCacheObject(final String key) {
-        RBucket<T> rBucket = client.getBucket(key);
+        RBucket<T> rBucket = CLIENT.getBucket(key);
         return rBucket.get();
     }
 
@@ -170,29 +183,26 @@ public class RedisUtils {
      * @return 剩余存活时间
      */
     public static <T> long getTimeToLive(final String key) {
-        RBucket<T> rBucket = client.getBucket(key);
+        RBucket<T> rBucket = CLIENT.getBucket(key);
         return rBucket.remainTimeToLive();
     }
 
     /**
      * 删除单个对象
      *
-     * @param key
+     * @param key 缓存的键值
      */
     public static boolean deleteObject(final String key) {
-        return client.getBucket(key).delete();
+        return CLIENT.getBucket(key).delete();
     }
 
-    /* */
-
     /**
      * 删除集合对象
      *
      * @param collection 多个对象
-     * @return
      */
     public static void deleteObject(final Collection collection) {
-        RBatch batch = client.createBatch();
+        RBatch batch = CLIENT.createBatch();
         collection.forEach(t -> {
             batch.getBucket(t.toString()).deleteAsync();
         });
@@ -207,10 +217,23 @@ public class RedisUtils {
      * @return 缓存的对象
      */
     public static <T> boolean setCacheList(final String key, final List<T> dataList) {
-        RList<T> rList = client.getList(key);
+        RList<T> rList = CLIENT.getList(key);
         return rList.addAll(dataList);
     }
 
+    /**
+     * 注册List监听器
+     *
+     * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
+     *
+     * @param key      缓存的键值
+     * @param listener 监听器配置
+     */
+    public static <T> void addListListener(final String key, final ObjectListener listener) {
+        RList<T> rList = CLIENT.getList(key);
+        rList.addListener(listener);
+    }
+
     /**
      * 获得缓存的list对象
      *
@@ -218,7 +241,7 @@ public class RedisUtils {
      * @return 缓存键值对应的数据
      */
     public static <T> List<T> getCacheList(final String key) {
-        RList<T> rList = client.getList(key);
+        RList<T> rList = CLIENT.getList(key);
         return rList.readAll();
     }
 
@@ -230,42 +253,68 @@ public class RedisUtils {
      * @return 缓存数据的对象
      */
     public static <T> boolean setCacheSet(final String key, final Set<T> dataSet) {
-        RSet<T> rSet = client.getSet(key);
+        RSet<T> rSet = CLIENT.getSet(key);
         return rSet.addAll(dataSet);
     }
 
+    /**
+     * 注册Set监听器
+     *
+     * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
+     *
+     * @param key      缓存的键值
+     * @param listener 监听器配置
+     */
+    public static <T> void addSetListener(final String key, final ObjectListener listener) {
+        RSet<T> rSet = CLIENT.getSet(key);
+        rSet.addListener(listener);
+    }
+
     /**
      * 获得缓存的set
      *
-     * @param key
-     * @return
+     * @param key 缓存的key
+     * @return set对象
      */
     public static <T> Set<T> getCacheSet(final String key) {
-        RSet<T> rSet = client.getSet(key);
+        RSet<T> rSet = CLIENT.getSet(key);
         return rSet.readAll();
     }
 
     /**
      * 缓存Map
      *
-     * @param key
-     * @param dataMap
+     * @param key     缓存的键值
+     * @param dataMap 缓存的数据
      */
     public static <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
         if (dataMap != null) {
-            RMap<String, T> rMap = client.getMap(key);
+            RMap<String, T> rMap = CLIENT.getMap(key);
             rMap.putAll(dataMap);
         }
     }
 
+    /**
+     * 注册Map监听器
+     *
+     * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
+     *
+     * @param key      缓存的键值
+     * @param listener 监听器配置
+     */
+    public static <T> void addMapListener(final String key, final ObjectListener listener) {
+        RMap<String, T> rMap = CLIENT.getMap(key);
+        rMap.addListener(listener);
+    }
+
     /**
      * 获得缓存的Map
      *
-     * @param key
-     * @return
+     * @param key 缓存的键值
+     * @return map对象
      */
     public static <T> Map<String, T> getCacheMap(final String key) {
-        RMap<String, T> rMap = client.getMap(key);
+        RMap<String, T> rMap = CLIENT.getMap(key);
         return rMap.getAll(rMap.keySet());
     }
 
@@ -277,7 +326,7 @@ public class RedisUtils {
      * @param value 值
      */
     public static <T> void setCacheMapValue(final String key, final String hKey, final T value) {
-        RMap<String, T> rMap = client.getMap(key);
+        RMap<String, T> rMap = CLIENT.getMap(key);
         rMap.put(hKey, value);
     }
 
@@ -289,7 +338,7 @@ public class RedisUtils {
      * @return Hash中的对象
      */
     public static <T> T getCacheMapValue(final String key, final String hKey) {
-        RMap<String, T> rMap = client.getMap(key);
+        RMap<String, T> rMap = CLIENT.getMap(key);
         return rMap.get(hKey);
     }
 
@@ -301,7 +350,7 @@ public class RedisUtils {
      * @return Hash中的对象
      */
     public static <T> T delCacheMapValue(final String key, final String hKey) {
-        RMap<String, T> rMap = client.getMap(key);
+        RMap<String, T> rMap = CLIENT.getMap(key);
         return rMap.remove(hKey);
     }
 
@@ -313,7 +362,7 @@ public class RedisUtils {
      * @return Hash对象集合
      */
     public static <K, V> Map<K, V> getMultiCacheMapValue(final String key, final Set<K> hKeys) {
-        RMap<K, V> rMap = client.getMap(key);
+        RMap<K, V> rMap = CLIENT.getMap(key);
         return rMap.getAll(hKeys);
     }
 
@@ -324,7 +373,7 @@ public class RedisUtils {
      * @return 对象列表
      */
     public static Collection<String> keys(final String pattern) {
-        Iterable<String> iterable = client.getKeys().getKeysByPattern(pattern);
-        return Lists.newArrayList(iterable);
+        Iterable<String> iterable = CLIENT.getKeys().getKeysByPattern(pattern);
+        return IterUtil.toList(iterable);
     }
 }

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

@@ -5,6 +5,10 @@ import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.service.UserService;
 import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.spring.SpringUtils;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 
 /**
@@ -12,6 +16,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  *
  * @author Long Li
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class SecurityUtils {
 
     /**

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

@@ -3,6 +3,8 @@ package com.ruoyi.common.utils;
 import cn.hutool.core.convert.Convert;
 import cn.hutool.extra.servlet.ServletUtil;
 import cn.hutool.http.HttpStatus;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 import org.springframework.http.MediaType;
 import org.springframework.web.context.request.RequestAttributes;
 import org.springframework.web.context.request.RequestContextHolder;
@@ -19,6 +21,7 @@ import java.nio.charset.StandardCharsets;
  *
  * @author ruoyi
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class ServletUtils extends ServletUtil {
 
     /**

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

@@ -7,6 +7,8 @@ import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ReUtil;
 import cn.hutool.core.util.StrUtil;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 
 import java.util.*;
 
@@ -15,6 +17,7 @@ import java.util.*;
  *
  * @author Lion Li
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class StringUtils extends org.apache.commons.lang3.StringUtils {
 
 	/**

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

@@ -1,7 +1,8 @@
 package com.ruoyi.common.utils;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 
 import java.util.concurrent.*;
 
@@ -10,8 +11,9 @@ import java.util.concurrent.*;
  *
  * @author ruoyi
  */
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class Threads {
-    private static final Logger logger = LoggerFactory.getLogger(Threads.class);
 
     /**
      * sleep等待,单位为毫秒
@@ -38,7 +40,7 @@ public class Threads {
                 if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
                     pool.shutdownNow();
                     if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
-                        logger.info("Pool did not terminate");
+                        log.info("Pool did not terminate");
                     }
                 }
             } catch (InterruptedException ie) {
@@ -67,7 +69,7 @@ public class Threads {
             }
         }
         if (t != null) {
-            logger.error(t.getMessage(), t);
+            log.error(t.getMessage(), t);
         }
     }
 }

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

@@ -4,6 +4,8 @@ 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 lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 
 import java.util.List;
 
@@ -12,6 +14,7 @@ import java.util.List;
  *
  * @author Lion Li
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class TreeBuildUtils extends TreeUtil {
 
     /**
@@ -19,13 +22,8 @@ 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);
+    public static <T> List<Tree<Long>> build(List<T> list, Long parentId, NodeParser<T, Long> nodeParser) {
+        return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser);
     }
 
 }

+ 6 - 2
ruoyi-common/src/main/java/com/ruoyi/common/utils/ValidatorUtils.java

@@ -1,8 +1,11 @@
 package com.ruoyi.common.utils;
 
+import com.ruoyi.common.utils.spring.SpringUtils;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
 import javax.validation.ConstraintViolation;
 import javax.validation.ConstraintViolationException;
-import javax.validation.Validation;
 import javax.validation.Validator;
 import java.util.Set;
 
@@ -11,9 +14,10 @@ import java.util.Set;
  *
  * @author Lion Li
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class ValidatorUtils {
 
-	private static final Validator VALID = Validation.buildDefaultValidatorFactory().getValidator();
+	private static final Validator VALID = SpringUtils.getBean(Validator.class);
 
 	public static <T> void validate(T object, Class<?>... groups) {
         Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);

+ 3 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java

@@ -1,6 +1,8 @@
 package com.ruoyi.common.utils.file;
 
 import cn.hutool.core.io.FileUtil;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 
 import javax.servlet.http.HttpServletResponse;
 import java.io.UnsupportedEncodingException;
@@ -12,6 +14,7 @@ import java.nio.charset.StandardCharsets;
  *
  * @author Lion Li
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class FileUtils extends FileUtil {
 
     /**

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

@@ -7,6 +7,8 @@ import com.ruoyi.common.config.RuoYiConfig;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.utils.JsonUtils;
 import com.ruoyi.common.utils.StringUtils;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 
 import java.util.Map;
@@ -17,6 +19,7 @@ import java.util.Map;
  * @author Lion Li
  */
 @Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class AddressUtils {
 
     // IP地址查询

+ 3 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java

@@ -9,6 +9,8 @@ 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;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletResponse;
@@ -21,6 +23,7 @@ import java.util.List;
  *
  * @author Lion Li
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class ExcelUtil {
 
     /**

+ 3 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java

@@ -2,6 +2,8 @@ package com.ruoyi.common.utils.reflect;
 
 import cn.hutool.core.util.ReflectUtil;
 import com.ruoyi.common.utils.StringUtils;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 
 import java.lang.reflect.Method;
 
@@ -11,6 +13,7 @@ import java.lang.reflect.Method;
  * @author Lion Li
  */
 @SuppressWarnings("rawtypes")
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class ReflectUtils extends ReflectUtil {
 
     private static final String SETTER_PREFIX = "set";

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

@@ -2,17 +2,26 @@ package com.ruoyi.common.utils.sql;
 
 import com.ruoyi.common.exception.UtilException;
 import com.ruoyi.common.utils.StringUtils;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 
 /**
  * sql操作工具类
  *
  * @author ruoyi
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class SqlUtil {
+
+    /**
+     * 定义常用的 sql关键字
+     */
+    public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
+
     /**
      * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
      */
-    public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
+    public static final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
 
     /**
      * 检查字符,防止注入绕过
@@ -30,4 +39,19 @@ public class SqlUtil {
     public static boolean isValidOrderBySql(String value) {
         return value.matches(SQL_PATTERN);
     }
+
+    /**
+     * SQL关键字检查
+     */
+    public static void filterKeyword(String value) {
+        if (StringUtils.isEmpty(value)) {
+            return;
+        }
+        String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
+        for (String sqlKeyword : sqlKeywords) {
+            if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
+                throw new UtilException("参数存在SQL注入风险");
+            }
+        }
+    }
 }

+ 26 - 0
ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java

@@ -0,0 +1,26 @@
+package com.ruoyi.common.xss;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 自定义xss校验注解
+ *
+ * @author Lion Li
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
+@Constraint(validatedBy = {XssValidator.class})
+public @interface Xss {
+
+    String message() default "不允许任何脚本运行";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+
+}

+ 21 - 0
ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java

@@ -0,0 +1,21 @@
+package com.ruoyi.common.xss;
+
+import cn.hutool.core.util.ReUtil;
+import cn.hutool.http.HtmlUtil;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * 自定义xss校验注解实现
+ *
+ * @author Lion Li
+ */
+public class XssValidator implements ConstraintValidator<Xss, String> {
+
+    @Override
+    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
+        return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value);
+    }
+
+}

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

@@ -33,10 +33,12 @@ public class TestBatchController extends BaseController {
 
     /**
      * 新增批量方法 可完美替代 saveBatch 秒级插入上万数据 (对mysql负荷较大)
+     *
+     * 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度
      */
     @ApiOperation(value = "新增批量方法")
     @PostMapping("/add")
-//	@DataSource(DataSourceType.SLAVE)
+//	@DS("slave")
     public AjaxResult<Void> add() {
         List<TestDemo> list = new ArrayList<>();
         for (int i = 0; i < 1000; i++) {
@@ -47,10 +49,12 @@ public class TestBatchController extends BaseController {
 
     /**
      * 新增或更新 可完美替代 saveOrUpdateBatch 高性能
+     *
+     * 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度
      */
     @ApiOperation(value = "新增或更新批量方法")
     @PostMapping("/addOrUpdate")
-//	@DataSource(DataSourceType.SLAVE)
+//	@DS("slave")
     public AjaxResult<Void> addOrUpdate() {
         List<TestDemo> list = new ArrayList<>();
         for (int i = 0; i < 1000; i++) {
@@ -72,7 +76,7 @@ public class TestBatchController extends BaseController {
      */
     @ApiOperation(value = "删除批量方法")
     @DeleteMapping()
-//	@DataSource(DataSourceType.SLAVE)
+//	@DS("slave")
     public AjaxResult<Void> remove() {
         return toAjax(iTestDemoService.remove(new LambdaQueryWrapper<TestDemo>()
             .eq(TestDemo::getOrderNum, -1L)) ? 1 : 0);

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

@@ -6,6 +6,7 @@ import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.annotation.RepeatSubmit;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.core.validate.AddGroup;
 import com.ruoyi.common.core.validate.EditGroup;
@@ -54,8 +55,8 @@ public class TestDemoController extends BaseController {
     @ApiOperation("查询测试单表列表")
     @SaCheckPermission("demo:demo:list")
     @GetMapping("/list")
-    public TableDataInfo<TestDemoVo> list(@Validated(QueryGroup.class) TestDemoBo bo) {
-        return iTestDemoService.queryPageList(bo);
+    public TableDataInfo<TestDemoVo> list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
+        return iTestDemoService.queryPageList(bo, pageQuery);
     }
 
 	/**
@@ -65,7 +66,7 @@ public class TestDemoController extends BaseController {
 	@SaCheckPermission("demo:demo:list")
 	@GetMapping("/page")
 	public TableDataInfo<TestDemoVo> page(@Validated(QueryGroup.class) TestDemoBo bo) {
-		return iTestDemoService.customPageList(bo);
+		return iTestDemoService.customPageList(bo, pageQuery);
 	}
 
     @ApiOperation("导入测试-校验")

+ 3 - 29
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java

@@ -1,16 +1,15 @@
 package com.ruoyi.demo.domain.bo;
 
+import com.ruoyi.common.core.domain.BaseEntity;
 import com.ruoyi.common.core.validate.AddGroup;
 import com.ruoyi.common.core.validate.EditGroup;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
-import javax.validation.constraints.*;
-
-import java.util.Date;
 
-import com.ruoyi.common.core.domain.BaseEntity;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
 
 /**
  * 测试单表业务对象 test_demo
@@ -66,29 +65,4 @@ public class TestDemoBo extends BaseEntity {
     @NotBlank(message = "值不能为空", groups = { AddGroup.class, EditGroup.class })
     private String value;
 
-
-    /**
-     * 分页大小
-     */
-    @ApiModelProperty("分页大小")
-    private Integer pageSize;
-
-    /**
-     * 当前页数
-     */
-    @ApiModelProperty("当前页数")
-    private Integer pageNum;
-
-    /**
-     * 排序列
-     */
-    @ApiModelProperty("排序列")
-    private String orderByColumn;
-
-    /**
-     * 排序的方向desc或者asc
-     */
-    @ApiModelProperty(value = "排序的方向", example = "asc,desc")
-    private String isAsc;
-
 }

+ 0 - 25
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java

@@ -51,29 +51,4 @@ public class TestTreeBo extends TreeEntity {
     @NotBlank(message = "树节点名不能为空", groups = { AddGroup.class, EditGroup.class })
     private String treeName;
 
-
-    /**
-     * 分页大小
-     */
-    @ApiModelProperty("分页大小")
-    private Integer pageSize;
-
-    /**
-     * 当前页数
-     */
-    @ApiModelProperty("当前页数")
-    private Integer pageNum;
-
-    /**
-     * 排序列
-     */
-    @ApiModelProperty("排序列")
-    private String orderByColumn;
-
-    /**
-     * 排序的方向desc或者asc
-     */
-    @ApiModelProperty(value = "排序的方向", example = "asc,desc")
-    private String isAsc;
-
 }

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

@@ -1,12 +1,20 @@
 package com.ruoyi.demo.mapper;
 
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.annotation.DataColumn;
+import com.ruoyi.common.annotation.DataPermission;
 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.Param;
 
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+
 /**
  * 测试单表Mapper接口
  *
@@ -15,6 +23,37 @@ import org.apache.ibatis.annotations.Param;
  */
 public interface TestDemoMapper extends BaseMapperPlus<TestDemo> {
 
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
     Page<TestDemoVo> customPageList(@Param("page") Page<TestDemo> page, @Param("ew") Wrapper<TestDemo> wrapper);
 
+    @Override
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    <P extends IPage<TestDemo>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<TestDemo> queryWrapper);
+
+    @Override
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    List<TestDemo> selectList(@Param(Constants.WRAPPER) Wrapper<TestDemo> queryWrapper);
+
+    @Override
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    int updateById(@Param(Constants.ENTITY) TestDemo entity);
+
+    @Override
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
 }

+ 6 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java

@@ -1,5 +1,7 @@
 package com.ruoyi.demo.mapper;
 
+import com.ruoyi.common.annotation.DataColumn;
+import com.ruoyi.common.annotation.DataPermission;
 import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
 import com.ruoyi.demo.domain.TestTree;
 
@@ -9,6 +11,10 @@ import com.ruoyi.demo.domain.TestTree;
  * @author Lion Li
  * @date 2021-07-26
  */
+@DataPermission({
+    @DataColumn(key = "deptName", value = "dept_id"),
+    @DataColumn(key = "userName", value = "user_id")
+})
 public interface TestTreeMapper extends BaseMapperPlus<TestTree> {
 
 }

+ 3 - 2
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java

@@ -1,5 +1,6 @@
 package com.ruoyi.demo.service;
 
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.demo.domain.TestDemo;
 import com.ruoyi.demo.domain.vo.TestDemoVo;
 import com.ruoyi.demo.domain.bo.TestDemoBo;
@@ -26,12 +27,12 @@ public interface ITestDemoService extends IServicePlus<TestDemo, TestDemoVo> {
 	/**
 	 * 查询列表
 	 */
-    TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo);
+    TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo, PageQuery pageQuery);
 
 	/**
 	 * 自定义分页查询
 	 */
-	TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo);
+	TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo, PageQuery pageQuery);
 
     /**
 	 * 查询列表

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

@@ -1,15 +1,13 @@
 package com.ruoyi.demo.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
-import com.ruoyi.common.utils.StringUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.common.annotation.DataScope;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
-import com.ruoyi.common.core.page.PagePlus;
 import com.ruoyi.common.core.page.TableDataInfo;
-import com.ruoyi.common.utils.PageUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.demo.domain.TestDemo;
 import com.ruoyi.demo.domain.bo.TestDemoBo;
 import com.ruoyi.demo.domain.vo.TestDemoVo;
@@ -35,24 +33,23 @@ public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDem
 		return getVoById(id);
 	}
 
-	@DataScope(isUser = true)
 	@Override
-	public TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo) {
-		PagePlus<TestDemo, TestDemoVo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo));
-		return PageUtils.buildDataInfo(result);
+	public TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
+        Page<TestDemoVo> result = pageVo(pageQuery.build(), lqw);
+		return TableDataInfo.build(result);
 	}
 
 	/**
 	 * 自定义分页查询
 	 */
-	@DataScope(isUser = true)
 	@Override
-	public TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo) {
-		Page<TestDemoVo> result = baseMapper.customPageList(PageUtils.buildPage(), buildQueryWrapper(bo));
-		return PageUtils.buildDataInfo(result);
+	public TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
+		Page<TestDemoVo> result = baseMapper.customPageList(pageQuery.build(), lqw);
+		return TableDataInfo.build(result);
 	}
 
-	@DataScope(isUser = true)
 	@Override
 	public List<TestDemoVo> queryList(TestDemoBo bo) {
 		return listVo(buildQueryWrapper(bo));
@@ -60,14 +57,11 @@ public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDem
 
 	private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoBo bo) {
 		Map<String, Object> params = bo.getParams();
-		Object dataScope = params.get("dataScope");
 		LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
 		lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
 		lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
 		lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
 			TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
-		lqw.apply(dataScope != null && StringUtils.isNotBlank(dataScope.toString()),
-			dataScope != null ? dataScope.toString() : null);
 		return lqw;
 	}
 

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

@@ -3,7 +3,6 @@ package com.ruoyi.demo.service.impl;
 import cn.hutool.core.bean.BeanUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.ruoyi.common.annotation.DataScope;
 import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.demo.domain.TestTree;
@@ -23,7 +22,7 @@ import java.util.Map;
  * @author Lion Li
  * @date 2021-07-26
  */
-//@DataSource(DataSourceType.SLAVE) // 切换从库查询
+// @DS("slave") // 切换从库查询
 @Service
 public class TestTreeServiceImpl extends ServicePlusImpl<TestTreeMapper, TestTree, TestTreeVo> implements ITestTreeService {
 
@@ -32,22 +31,19 @@ public class TestTreeServiceImpl extends ServicePlusImpl<TestTreeMapper, TestTre
 		return getVoById(id);
 	}
 
-//	@DataSource(DataSourceType.SLAVE) // 切换从库查询
-	@DataScope(isUser = true)
+//	@DS("slave") // 切换从库查询
 	@Override
 	public List<TestTreeVo> queryList(TestTreeBo bo) {
-		return listVo(buildQueryWrapper(bo));
+        LambdaQueryWrapper<TestTree> lqw = buildQueryWrapper(bo);
+        return listVo(lqw);
 	}
 
 	private LambdaQueryWrapper<TestTree> buildQueryWrapper(TestTreeBo bo) {
 		Map<String, Object> params = bo.getParams();
-		Object dataScope = params.get("dataScope");
 		LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery();
 		lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
 		lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
 			TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
-		lqw.apply(dataScope != null && StringUtils.isNotBlank(dataScope.toString()),
-			dataScope != null ? dataScope.toString() : null);
 		return lqw;
 	}
 

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

@@ -2,7 +2,6 @@ package com.ruoyi.monitor.admin.config;
 
 import de.codecentric.boot.admin.server.config.AdminServerProperties;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@@ -15,7 +14,6 @@ import org.springframework.security.web.authentication.SavedRequestAwareAuthenti
  */
 @Configuration
 @EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true)
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
 	private final String adminContextPath;
@@ -34,8 +32,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
 			//授予对所有静态资产和登录页面的公共访问权限。
 			.antMatchers(adminContextPath + "/assets/**").permitAll()
 			.antMatchers(adminContextPath + "/login").permitAll()
-            .antMatchers("/actuator").anonymous()
-            .antMatchers("/actuator/**").anonymous()
+            .antMatchers("/actuator").permitAll()
+            .antMatchers("/actuator/**").permitAll()
 			//必须对每个其他请求进行身份验证
 			.anyRequest().authenticated().and()
 			//配置登录和注销

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

@@ -102,6 +102,7 @@
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-resources-plugin</artifactId>
+                <version>2.6</version>
 				<configuration>
 					<nonFilteredFileExtensions>
 						<nonFilteredFileExtension>ttf</nonFilteredFileExtension>

+ 5 - 5
ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java

@@ -3,8 +3,8 @@ package com.xxl.job.admin.controller.interceptor;
 import com.xxl.job.admin.core.util.FtlUtil;
 import com.xxl.job.admin.core.util.I18nUtil;
 import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.AsyncHandlerInterceptor;
 import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
@@ -17,7 +17,7 @@ import java.util.HashMap;
  * @author xuxueli 2015-12-12 18:09:04
  */
 @Component
-public class CookieInterceptor extends HandlerInterceptorAdapter {
+public class CookieInterceptor implements AsyncHandlerInterceptor {
 
 	@Override
 	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@@ -36,8 +36,8 @@ public class CookieInterceptor extends HandlerInterceptorAdapter {
 		if (modelAndView != null) {
 			modelAndView.addObject("I18nUtil", FtlUtil.generateStaticModel(I18nUtil.class.getName()));
 		}
-		
-		super.postHandle(request, response, handler, modelAndView);
+
+		AsyncHandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
 	}
-	
+
 }

+ 6 - 6
ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java

@@ -6,7 +6,7 @@ import com.xxl.job.admin.core.util.I18nUtil;
 import com.xxl.job.admin.service.LoginService;
 import org.springframework.stereotype.Component;
 import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+import org.springframework.web.servlet.AsyncHandlerInterceptor;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
@@ -18,16 +18,16 @@ import javax.servlet.http.HttpServletResponse;
  * @author xuxueli 2015-12-12 18:09:04
  */
 @Component
-public class PermissionInterceptor extends HandlerInterceptorAdapter {
+public class PermissionInterceptor implements AsyncHandlerInterceptor {
 
 	@Resource
 	private LoginService loginService;
 
 	@Override
 	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-		
+
 		if (!(handler instanceof HandlerMethod)) {
-			return super.preHandle(request, response, handler);
+			return AsyncHandlerInterceptor.super.preHandle(request, response, handler);
 		}
 
 		// if need login
@@ -53,7 +53,7 @@ public class PermissionInterceptor extends HandlerInterceptorAdapter {
 			request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser);
 		}
 
-		return super.preHandle(request, response, handler);
+		return AsyncHandlerInterceptor.super.preHandle(request, response, handler);
 	}
-	
+
 }

+ 15 - 5
ruoyi-framework/pom.xml

@@ -46,23 +46,33 @@
             <artifactId>druid-spring-boot-starter</artifactId>
         </dependency>
 
-        <!-- dynamic-datasource 多数据源-->
-        <dependency>
-            <groupId>com.baomidou</groupId>
-            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
-        </dependency>
         <!-- sql性能分析插件 -->
         <dependency>
             <groupId>p6spy</groupId>
             <artifactId>p6spy</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>de.codecentric</groupId>
+            <artifactId>spring-boot-admin-starter-client</artifactId>
+        </dependency>
+
         <!-- 系统模块-->
         <dependency>
             <groupId>com.ruoyi</groupId>
             <artifactId>ruoyi-common</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.yomahub</groupId>
+            <artifactId>tlog-web-spring-boot-starter</artifactId>
+        </dependency>
+
     </dependencies>
 
 </project>

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

@@ -12,15 +12,15 @@ import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Before;
 import org.springframework.stereotype.Component;
 
-import java.util.Map;
-
 /**
  * 数据过滤处理
  *
  * @author Lion Li
+ * @deprecated 3.6.0 移除 {@link com.ruoyi.framework.handler.PlusDataPermissionHandler}
  */
 @Aspect
 @Component
+@Deprecated
 public class DataScopeAspect {
 
 	/**
@@ -131,9 +131,6 @@ public class DataScopeAspect {
 			if (params instanceof BaseEntity) {
 				BaseEntity baseEntity = (BaseEntity) params;
 				baseEntity.getParams().put(DATA_SCOPE, sql);
-			} else {
-				Map<String, Object> invoke = ReflectUtils.invokeGetter(params, "params");
-				invoke.put(DATA_SCOPE, sql);
 			}
 		}
 	}

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

@@ -18,10 +18,12 @@ import java.util.Objects;
  * 多数据源处理
  *
  * @author Lion Li
+ * @deprecated 3.6.0 移除 使用原生方法处理 功能更全
  */
 @Aspect
 @Order(-500)
 @Component
+@Deprecated
 public class DataSourceAspect {
 
 	@Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"

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

@@ -54,7 +54,7 @@ public class RateLimiterAspect {
             stringBuffer.append(ServletUtils.getClientIP()).append("-");
         } else if (rateLimiter.limitType() == LimitType.CLUSTER){
             // 获取客户端实例id
-            stringBuffer.append(RedisUtils.getClientId()).append("-");
+            stringBuffer.append(RedisUtils.getClient().getId()).append("-");
         }
         MethodSignature signature = (MethodSignature) point.getSignature();
         Method method = signature.getMethod();

+ 30 - 8
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java

@@ -1,6 +1,9 @@
 package com.ruoyi.framework.config;
 
+import cn.hutool.core.net.NetUtil;
 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
+import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
 import com.baomidou.mybatisplus.core.injector.AbstractMethod;
 import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
 import com.baomidou.mybatisplus.core.injector.ISqlInjector;
@@ -10,6 +13,7 @@ import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInt
 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
 import com.ruoyi.common.core.mybatisplus.methods.InsertAll;
 import com.ruoyi.framework.handler.CreateAndUpdateMetaObjectHandler;
+import com.ruoyi.framework.interceptor.PlusDataPermissionInterceptor;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -30,6 +34,8 @@ public class MybatisPlusConfig {
 	@Bean
 	public MybatisPlusInterceptor mybatisPlusInterceptor() {
 		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        // 数据权限处理
+        interceptor.addInnerInterceptor(dataPermissionInterceptor());
 		// 分页插件
 		interceptor.addInnerInterceptor(paginationInnerInterceptor());
 		// 乐观锁插件
@@ -37,6 +43,13 @@ public class MybatisPlusConfig {
 		return interceptor;
 	}
 
+    /**
+     * 数据权限拦截器
+     */
+    public PlusDataPermissionInterceptor dataPermissionInterceptor() {
+        return new PlusDataPermissionInterceptor();
+    }
+
 	/**
 	 * 分页插件,自动识别数据库类型
 	 */
@@ -79,24 +92,33 @@ public class MybatisPlusConfig {
 		};
 	}
 
+    /**
+     * 使用网卡信息绑定雪花生成器
+     * 防止集群雪花ID重复
+     */
+    @Bean
+    public IdentifierGenerator idGenerator() {
+        return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
+    }
+
 	/**
 	 * PaginationInnerInterceptor 分页插件,自动识别数据库类型
-	 * https://baomidou.com/guide/interceptor-pagination.html
+	 * https://baomidou.com/pages/97710a/
 	 * OptimisticLockerInnerInterceptor 乐观锁插件
-	 * https://baomidou.com/guide/interceptor-optimistic-locker.html
+	 * https://baomidou.com/pages/0d93c0/
 	 * MetaObjectHandler 元对象字段填充控制器
-	 * https://baomidou.com/guide/auto-fill-metainfo.html
+	 * https://baomidou.com/pages/4c6bcf/
 	 * ISqlInjector sql注入器
-	 * https://baomidou.com/guide/sql-injector.html
+	 * https://baomidou.com/pages/42ea4a/
 	 * BlockAttackInnerInterceptor 如果是对全表的删除或更新操作,就会终止该操作
-	 * https://baomidou.com/guide/interceptor-block-attack.html
+	 * https://baomidou.com/pages/f9a237/
 	 * IllegalSQLInnerInterceptor sql性能规范插件(垃圾SQL拦截)
 	 * IdentifierGenerator 自定义主键策略
-	 * https://baomidou.com/guide/id-generator.html
+	 * https://baomidou.com/pages/568eb2/
 	 * TenantLineInnerInterceptor 多租户插件
-	 * https://baomidou.com/guide/interceptor-tenant-line.html
+	 * https://baomidou.com/pages/aef2f2/
 	 * DynamicTableNameInnerInterceptor 动态表名插件
-	 * https://baomidou.com/guide/interceptor-dynamic-table-name.html
+	 * https://baomidou.com/pages/2a45ff/
 	 */
 
 }

+ 154 - 147
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java

@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * redis配置
@@ -34,158 +35,164 @@ import java.util.Map;
 @EnableCaching
 public class RedisConfig extends CachingConfigurerSupport {
 
-	private static final String REDIS_PROTOCOL_PREFIX = "redis://";
-	private static final String REDISS_PROTOCOL_PREFIX = "rediss://";
+    private static final String REDIS_PROTOCOL_PREFIX = "redis://";
+    private static final String REDISS_PROTOCOL_PREFIX = "rediss://";
 
-	@Autowired
-	private RedisProperties redisProperties;
+    @Autowired
+    private RedisProperties redisProperties;
 
-	@Autowired
-	private RedissonProperties redissonProperties;
+    @Autowired
+    private RedissonProperties redissonProperties;
 
-	@Bean(destroyMethod = "shutdown")
-	@ConditionalOnMissingBean(RedissonClient.class)
-	public RedissonClient redisson() throws IOException {
-		String prefix = REDIS_PROTOCOL_PREFIX;
-		if (redisProperties.isSsl()) {
-			prefix = REDISS_PROTOCOL_PREFIX;
-		}
-		Config config = new Config();
-		config.setThreads(redissonProperties.getThreads())
-			.setNettyThreads(redissonProperties.getNettyThreads())
-			.setCodec(JsonJacksonCodec.INSTANCE)
-			.setTransportMode(redissonProperties.getTransportMode());
+    @Bean(destroyMethod = "shutdown")
+    @ConditionalOnMissingBean(RedissonClient.class)
+    public RedissonClient redisson() throws IOException {
+        String prefix = REDIS_PROTOCOL_PREFIX;
+        if (redisProperties.isSsl()) {
+            prefix = REDISS_PROTOCOL_PREFIX;
+        }
+        Config config = new Config();
+        config.setThreads(redissonProperties.getThreads())
+            .setNettyThreads(redissonProperties.getNettyThreads())
+            .setCodec(JsonJacksonCodec.INSTANCE)
+            .setTransportMode(redissonProperties.getTransportMode());
 
-		RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
-		if (ObjectUtil.isNotNull(singleServerConfig)) {
-			// 使用单机模式
-			config.useSingleServer()
-					.setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
-					.setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
-					.setDatabase(redisProperties.getDatabase())
-					.setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
-					.setTimeout(singleServerConfig.getTimeout())
-					.setRetryAttempts(singleServerConfig.getRetryAttempts())
-					.setRetryInterval(singleServerConfig.getRetryInterval())
-					.setSubscriptionsPerConnection(singleServerConfig.getSubscriptionsPerConnection())
-					.setClientName(singleServerConfig.getClientName())
-					.setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
-					.setSubscriptionConnectionMinimumIdleSize(singleServerConfig.getSubscriptionConnectionMinimumIdleSize())
-					.setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
-					.setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
-					.setConnectionPoolSize(singleServerConfig.getConnectionPoolSize())
-					.setDnsMonitoringInterval(singleServerConfig.getDnsMonitoringInterval());
-		}
-		// 集群配置方式 参考下方注释
-		RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
-		if (ObjectUtil.isNotNull(clusterServersConfig)) {
-			// 使用集群模式
-			config.useClusterServers()
-					.setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
-					.setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
-					.setTimeout(clusterServersConfig.getTimeout())
-					.setRetryAttempts(clusterServersConfig.getRetryAttempts())
-					.setRetryInterval(clusterServersConfig.getRetryInterval())
-					.setSubscriptionsPerConnection(clusterServersConfig.getSubscriptionsPerConnection())
-					.setClientName(clusterServersConfig.getClientName())
-					.setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
-					.setPingConnectionInterval(clusterServersConfig.getPingConnectionInterval())
-					.setSubscriptionConnectionMinimumIdleSize(clusterServersConfig.getSubscriptionConnectionMinimumIdleSize())
-					.setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
-					.setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
-					.setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
-					.setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
-					.setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
-					.setDnsMonitoringInterval(clusterServersConfig.getDnsMonitoringInterval())
-					.setFailedSlaveReconnectionInterval(clusterServersConfig.getFailedSlaveReconnectionInterval())
-					.setScanInterval(clusterServersConfig.getScanInterval())
-					.setReadMode(clusterServersConfig.getReadMode())
-					.setSubscriptionMode(clusterServersConfig.getSubscriptionMode())
-					.setNodeAddresses(redisProperties.getCluster().getNodes());
-		}
-		RedissonClient redissonClient = Redisson.create(config);
-		log.info("初始化 redis 配置");
-		return redissonClient;
-	}
+        RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
+        if (ObjectUtil.isNotNull(singleServerConfig)) {
+            // 使用单机模式
+            config.useSingleServer()
+                .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
+                .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
+                .setDatabase(redisProperties.getDatabase())
+                .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
+                .setTimeout(singleServerConfig.getTimeout())
+                .setRetryAttempts(singleServerConfig.getRetryAttempts())
+                .setRetryInterval(singleServerConfig.getRetryInterval())
+                .setSubscriptionsPerConnection(singleServerConfig.getSubscriptionsPerConnection())
+                .setClientName(singleServerConfig.getClientName())
+                .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
+                .setSubscriptionConnectionMinimumIdleSize(singleServerConfig.getSubscriptionConnectionMinimumIdleSize())
+                .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
+                .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
+                .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize())
+                .setDnsMonitoringInterval(singleServerConfig.getDnsMonitoringInterval());
+        }
+        // 集群配置方式 参考下方注释
+        RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
+        if (ObjectUtil.isNotNull(clusterServersConfig)) {
+            // 使用集群模式
+            String finalPrefix = prefix;
+            List<String> nodes = redisProperties.getCluster().getNodes()
+                .stream()
+                .map(node -> finalPrefix + node)
+                .collect(Collectors.toList());
 
-	/**
-	 * 整合spring-cache
-	 */
-	@Bean
-	public CacheManager cacheManager(RedissonClient redissonClient) {
-		List<RedissonProperties.CacheGroup> cacheGroup = redissonProperties.getCacheGroup();
-		Map<String, CacheConfig> config = new HashMap<>();
-		for (RedissonProperties.CacheGroup group : cacheGroup) {
-			CacheConfig cacheConfig = new CacheConfig(group.getTtl(), group.getMaxIdleTime());
-			cacheConfig.setMaxSize(group.getMaxSize());
-			config.put(group.getGroupId(), cacheConfig);
-		}
-		return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE);
-	}
+            config.useClusterServers()
+                .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
+                .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
+                .setTimeout(clusterServersConfig.getTimeout())
+                .setRetryAttempts(clusterServersConfig.getRetryAttempts())
+                .setRetryInterval(clusterServersConfig.getRetryInterval())
+                .setSubscriptionsPerConnection(clusterServersConfig.getSubscriptionsPerConnection())
+                .setClientName(clusterServersConfig.getClientName())
+                .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
+                .setPingConnectionInterval(clusterServersConfig.getPingConnectionInterval())
+                .setSubscriptionConnectionMinimumIdleSize(clusterServersConfig.getSubscriptionConnectionMinimumIdleSize())
+                .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
+                .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
+                .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
+                .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
+                .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
+                .setDnsMonitoringInterval(clusterServersConfig.getDnsMonitoringInterval())
+                .setFailedSlaveReconnectionInterval(clusterServersConfig.getFailedSlaveReconnectionInterval())
+                .setScanInterval(clusterServersConfig.getScanInterval())
+                .setReadMode(clusterServersConfig.getReadMode())
+                .setSubscriptionMode(clusterServersConfig.getSubscriptionMode())
+                .setNodeAddresses(nodes);
+        }
+        RedissonClient redissonClient = Redisson.create(config);
+        log.info("初始化 redis 配置");
+        return redissonClient;
+    }
 
-	/**
-	 * redis集群配置 yml
-	 *
-	 * --- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉)
-	 * spring:
-	 *   redis:
-	 *     cluster:
-	 *       nodes:
-	 *         - 192.168.0.100:6379
-	 *         - 192.168.0.101:6379
-	 *         - 192.168.0.102:6379
-	 *     # 密码
-	 *     password:
-	 *     # 连接超时时间
-	 *     timeout: 10s
-	 *     # 是否开启ssl
-	 *     ssl: false
-	 *
-	 * redisson:
-	 *   # 线程池数量
-	 *   threads: 16
-	 *   # Netty线程池数量
-	 *   nettyThreads: 32
-	 *   # 传输模式
-	 *   transportMode: "NIO"
-	 *   # 集群配置
-	 *   clusterServersConfig:
-	 *     # 客户端名称
-	 *     clientName: ${ruoyi.name}
-	 *     # master最小空闲连接数
-	 *     masterConnectionMinimumIdleSize: 32
-	 *     # master连接池大小
-	 *     masterConnectionPoolSize: 64
-	 *     # slave最小空闲连接数
-	 *     slaveConnectionMinimumIdleSize: 32
-	 *     # slave连接池大小
-	 *     slaveConnectionPoolSize: 64
-	 *     # 连接空闲超时,单位:毫秒
-	 *     idleConnectionTimeout: 10000
-	 *     # ping连接间隔
-	 *     pingConnectionInterval: 1000
-	 *     # 命令等待超时,单位:毫秒
-	 *     timeout: 3000
-	 *     # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。
-	 *     retryAttempts: 3
-	 *     # 命令重试发送时间间隔,单位:毫秒
-	 *     retryInterval: 1500
-	 *     # 从可用服务器的内部列表中排除 Redis Slave 重新连接尝试的间隔。
-	 *     failedSlaveReconnectionInterval: 3000
-	 *     # 发布和订阅连接池最小空闲连接数
-	 *     subscriptionConnectionMinimumIdleSize: 1
-	 *     # 发布和订阅连接池大小
-	 *     subscriptionConnectionPoolSize: 50
-	 *     # 单个连接最大订阅数量
-	 *     subscriptionsPerConnection: 5
-	 *     # 扫描间隔
-	 *     scanInterval: 1000
-	 *     # DNS监测时间间隔,单位:毫秒
-	 *     dnsMonitoringInterval: 5000
-	 *     # 读取模式
-	 *     readMode: "SLAVE"
-	 *     # 订阅模式
-	 *     subscriptionMode: "MASTER"
-	 */
+    /**
+     * 整合spring-cache
+     */
+    @Bean
+    public CacheManager cacheManager(RedissonClient redissonClient) {
+        List<RedissonProperties.CacheGroup> cacheGroup = redissonProperties.getCacheGroup();
+        Map<String, CacheConfig> config = new HashMap<>();
+        for (RedissonProperties.CacheGroup group : cacheGroup) {
+            CacheConfig cacheConfig = new CacheConfig(group.getTtl(), group.getMaxIdleTime());
+            cacheConfig.setMaxSize(group.getMaxSize());
+            config.put(group.getGroupId(), cacheConfig);
+        }
+        return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE);
+    }
+
+    /**
+     * redis集群配置 yml
+     *
+     * --- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉)
+     * spring:
+     *   redis:
+     *     cluster:
+     *       nodes:
+     *         - 192.168.0.100:6379
+     *         - 192.168.0.101:6379
+     *         - 192.168.0.102:6379
+     *     # 密码
+     *     password:
+     *     # 连接超时时间
+     *     timeout: 10s
+     *     # 是否开启ssl
+     *     ssl: false
+     *
+     * redisson:
+     *   # 线程池数量
+     *   threads: 16
+     *   # Netty线程池数量
+     *   nettyThreads: 32
+     *   # 传输模式
+     *   transportMode: "NIO"
+     *   # 集群配置
+     *   clusterServersConfig:
+     *     # 客户端名称
+     *     clientName: ${ruoyi.name}
+     *     # master最小空闲连接数
+     *     masterConnectionMinimumIdleSize: 32
+     *     # master连接池大小
+     *     masterConnectionPoolSize: 64
+     *     # slave最小空闲连接数
+     *     slaveConnectionMinimumIdleSize: 32
+     *     # slave连接池大小
+     *     slaveConnectionPoolSize: 64
+     *     # 连接空闲超时,单位:毫秒
+     *     idleConnectionTimeout: 10000
+     *     # ping连接间隔
+     *     pingConnectionInterval: 1000
+     *     # 命令等待超时,单位:毫秒
+     *     timeout: 3000
+     *     # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。
+     *     retryAttempts: 3
+     *     # 命令重试发送时间间隔,单位:毫秒
+     *     retryInterval: 1500
+     *     # 从可用服务器的内部列表中排除 Redis Slave 重新连接尝试的间隔。
+     *     failedSlaveReconnectionInterval: 3000
+     *     # 发布和订阅连接池最小空闲连接数
+     *     subscriptionConnectionMinimumIdleSize: 1
+     *     # 发布和订阅连接池大小
+     *     subscriptionConnectionPoolSize: 50
+     *     # 单个连接最大订阅数量
+     *     subscriptionsPerConnection: 5
+     *     # 扫描间隔
+     *     scanInterval: 1000
+     *     # DNS监测时间间隔,单位:毫秒
+     *     dnsMonitoringInterval: 5000
+     *     # 读取模式
+     *     readMode: "SLAVE"
+     *     # 订阅模式
+     *     subscriptionMode: "MASTER"
+     */
 
 }

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

@@ -1,6 +1,6 @@
 package com.ruoyi.framework.config;
 
-import com.ruoyi.framework.Interceptor.PlusWebInvokeTimeInterceptor;
+import com.ruoyi.framework.interceptor.PlusWebInvokeTimeInterceptor;
 import com.yomahub.tlog.web.interceptor.TLogWebInterceptor;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;

+ 4 - 29
ruoyi-framework/src/main/java/com/ruoyi/framework/config/TLogConfig.java

@@ -1,13 +1,8 @@
 package com.ruoyi.framework.config;
 
-import com.yomahub.tlog.core.aop.AspectLogAop;
-import com.yomahub.tlog.spring.TLogPropertyInit;
-import com.yomahub.tlog.spring.TLogSpringAware;
-import com.yomahub.tlog.springboot.property.TLogProperty;
-import org.springframework.context.annotation.Bean;
+import com.yomahub.tlog.springboot.TLogWebAutoConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Import;
-import org.springframework.core.annotation.Order;
 
 /**
  * 整合 TLog 框架配置
@@ -15,29 +10,9 @@ import org.springframework.core.annotation.Order;
  * @author Lion Li
  * @since 3.3.0
  */
-@Order(-999)
 @Configuration
-@Import(TLogProperty.class)
+// 排除 web 自动配置 自定义实现
+@EnableAutoConfiguration(exclude = TLogWebAutoConfiguration.class)
 public class TLogConfig {
 
-    @Bean
-    public TLogPropertyInit tLogPropertyInit(TLogProperty tLogProperty) {
-        TLogPropertyInit tLogPropertyInit = new TLogPropertyInit();
-        tLogPropertyInit.setPattern(tLogProperty.getPattern());
-        tLogPropertyInit.setEnableInvokeTimePrint(tLogProperty.enableInvokeTimePrint());
-        tLogPropertyInit.setIdGenerator(tLogProperty.getIdGenerator());
-        tLogPropertyInit.setMdcEnable(tLogProperty.getMdcEnable());
-        return tLogPropertyInit;
-    }
-
-    @Bean
-    public TLogSpringAware tLogSpringAware(){
-        return new TLogSpringAware();
-    }
-
-    @Bean
-    public AspectLogAop aspectLogAop() {
-        return new AspectLogAop();
-    }
-
 }

+ 192 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java

@@ -0,0 +1,192 @@
+package com.ruoyi.framework.handler;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.collection.ConcurrentHashSet;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ClassUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.ruoyi.common.annotation.DataColumn;
+import com.ruoyi.common.annotation.DataPermission;
+import com.ruoyi.common.core.domain.entity.SysRole;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.service.UserService;
+import com.ruoyi.common.enums.DataScopeType;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.helper.DataPermissionHelper;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.Parenthesis;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import org.springframework.context.expression.BeanFactoryResolver;
+import org.springframework.expression.BeanResolver;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.ParserContext;
+import org.springframework.expression.common.TemplateParserContext;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * 数据权限过滤
+ *
+ * @author Lion Li
+ * @version 3.5.0
+ */
+@Slf4j
+public class PlusDataPermissionHandler {
+
+    /**
+     * 方法或类(名称) 与 注解的映射关系缓存
+     */
+    private final Map<String, DataPermission> dataPermissionCacheMap = new ConcurrentHashMap<>();
+
+    /**
+     * 无效注解方法缓存用于快速返回
+     */
+    private final Set<String> inavlidCacheSet = new ConcurrentHashSet<>();
+
+    /**
+     * spel 解析器
+     */
+    private final ExpressionParser parser = new SpelExpressionParser();
+    private final ParserContext parserContext = new TemplateParserContext();
+    /**
+     * bean解析器 用于处理 spel 表达式中对 bean 的调用
+     */
+    private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory());
+
+
+    public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) {
+        DataColumn[] dataColumns = findAnnotation(mappedStatementId);
+        if (ArrayUtil.isEmpty(dataColumns)) {
+            inavlidCacheSet.add(mappedStatementId);
+            return where;
+        }
+        SysUser currentUser = DataPermissionHelper.getVariable("user");
+        if (ObjectUtil.isNull(currentUser)) {
+            currentUser = SpringUtils.getBean(UserService.class).selectUserById(SecurityUtils.getUserId());
+            DataPermissionHelper.setVariable("user", currentUser);
+        }
+        // 如果是超级管理员,则不过滤数据
+        if (ObjectUtil.isNull(currentUser) || currentUser.isAdmin()) {
+            return where;
+        }
+        String dataFilterSql = buildDataFilter(dataColumns, isSelect);
+        if (StringUtils.isBlank(dataFilterSql)) {
+            return where;
+        }
+        try {
+            Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql);
+            // 数据权限使用单独的括号 防止与其他条件冲突
+            Parenthesis parenthesis = new Parenthesis(expression);
+            if (ObjectUtil.isNotNull(where)) {
+                return new AndExpression(where, parenthesis);
+            } else {
+                return parenthesis;
+            }
+        } catch (JSQLParserException e) {
+            throw new ServiceException("数据权限解析异常 => " + e.getMessage());
+        }
+    }
+
+    /**
+     * 构造数据过滤sql
+     */
+    private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) {
+        StringBuilder sqlString = new StringBuilder();
+        // 更新或删除需满足所有条件
+        String joinStr = isSelect ? " OR " : " AND ";
+        SysUser user = DataPermissionHelper.getVariable("user");
+        StandardEvaluationContext context = new StandardEvaluationContext();
+        context.setBeanResolver(beanResolver);
+        DataPermissionHelper.getContext().forEach(context::setVariable);
+        for (SysRole role : user.getRoles()) {
+            user.setRoleId(role.getRoleId());
+            // 获取角色权限泛型
+            DataScopeType type = DataScopeType.findCode(role.getDataScope());
+            if (ObjectUtil.isNull(type)) {
+                throw new ServiceException("角色数据范围异常 => " + role.getDataScope());
+            }
+            // 全部数据权限直接返回
+            if (type == DataScopeType.ALL) {
+                return "";
+            }
+            boolean isSuccess = false;
+            for (DataColumn dataColumn : dataColumns) {
+                // 不包含 key 变量 则不处理
+                if (!StringUtils.contains(type.getSqlTemplate(), "#" + dataColumn.key())) {
+                    continue;
+                }
+                // 设置注解变量 key 为表达式变量 value 为变量值
+                context.setVariable(dataColumn.key(), dataColumn.value());
+
+                // 解析sql模板并填充
+                String sql = parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class);
+                sqlString.append(joinStr).append(sql);
+                isSuccess = true;
+            }
+            // 未处理成功则填充兜底方案
+            if (!isSuccess) {
+                sqlString.append(joinStr).append(type.getElseSql());
+            }
+        }
+
+        if (StringUtils.isNotBlank(sqlString.toString())) {
+            return sqlString.substring(joinStr.length());
+        }
+        return "";
+    }
+
+    private DataColumn[] findAnnotation(String mappedStatementId) {
+        StringBuilder sb = new StringBuilder(mappedStatementId);
+        int index = sb.lastIndexOf(".");
+        String clazzName = sb.substring(0, index);
+        String methodName = sb.substring(index + 1, sb.length());
+        Class<?> clazz = ClassUtil.loadClass(clazzName);
+        List<Method> methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz))
+            .filter(method -> method.getName().equals(methodName)).collect(Collectors.toList());
+        DataPermission dataPermission;
+        // 获取方法注解
+        for (Method method : methods) {
+            dataPermission = dataPermissionCacheMap.get(method.getName());
+            if (ObjectUtil.isNotNull(dataPermission)) {
+                return dataPermission.value();
+            }
+            if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) {
+                dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class);
+                dataPermissionCacheMap.put(method.getName(), dataPermission);
+                return dataPermission.value();
+            }
+        }
+        dataPermission = dataPermissionCacheMap.get(clazz.getName());
+        if (ObjectUtil.isNotNull(dataPermission)) {
+            return dataPermission.value();
+        }
+        // 获取类注解
+        if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) {
+            dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class);
+            dataPermissionCacheMap.put(clazz.getName(), dataPermission);
+            return dataPermission.value();
+        }
+        return null;
+    }
+
+    /**
+     * 是否为无效方法 无数据权限
+     */
+    public boolean isInvalid(String mappedStatementId) {
+        return inavlidCacheSet.contains(mappedStatementId);
+    }
+}

+ 108 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/PlusDataPermissionInterceptor.java

@@ -0,0 +1,108 @@
+package com.ruoyi.framework.interceptor;
+
+import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
+import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
+import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
+import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
+import com.ruoyi.framework.handler.PlusDataPermissionHandler;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.statement.delete.Delete;
+import net.sf.jsqlparser.statement.select.PlainSelect;
+import net.sf.jsqlparser.statement.select.Select;
+import net.sf.jsqlparser.statement.select.SelectBody;
+import net.sf.jsqlparser.statement.select.SetOperationList;
+import net.sf.jsqlparser.statement.update.Update;
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * 数据权限拦截器
+ *
+ * @author Lion Li
+ * @version 3.5.0
+ */
+public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {
+
+    private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler();
+
+    @Override
+    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
+        // 检查忽略注解
+        if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
+            return;
+        }
+        // 检查是否无效 无数据权限注解
+        if (dataPermissionHandler.isInvalid(ms.getId())) {
+            return;
+        }
+        // 解析 sql 分配对应方法
+        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
+        mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));
+    }
+
+    @Override
+    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
+        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
+        MappedStatement ms = mpSh.mappedStatement();
+        SqlCommandType sct = ms.getSqlCommandType();
+        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
+            if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
+                return;
+            }
+            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
+            mpBs.sql(parserMulti(mpBs.sql(), ms.getId()));
+        }
+    }
+
+    @Override
+    protected void processSelect(Select select, int index, String sql, Object obj) {
+        SelectBody selectBody = select.getSelectBody();
+        if (selectBody instanceof PlainSelect) {
+            this.setWhere((PlainSelect) selectBody, (String) obj);
+        } else if (selectBody instanceof SetOperationList) {
+            SetOperationList setOperationList = (SetOperationList) selectBody;
+            List<SelectBody> selectBodyList = setOperationList.getSelects();
+            selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj));
+        }
+    }
+
+    @Override
+    protected void processUpdate(Update update, int index, String sql, Object obj) {
+        Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false);
+        if (null != sqlSegment) {
+            update.setWhere(sqlSegment);
+        }
+    }
+
+    @Override
+    protected void processDelete(Delete delete, int index, String sql, Object obj) {
+        Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false);
+        if (null != sqlSegment) {
+            delete.setWhere(sqlSegment);
+        }
+    }
+
+    /**
+     * 设置 where 条件
+     *
+     * @param plainSelect       查询对象
+     * @param mappedStatementId 执行方法id
+     */
+    protected void setWhere(PlainSelect plainSelect, String mappedStatementId) {
+        Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true);
+        if (null != sqlSegment) {
+            plainSelect.setWhere(sqlSegment);
+        }
+    }
+
+}
+

+ 23 - 16
ruoyi-framework/src/main/java/com/ruoyi/framework/Interceptor/PlusWebInvokeTimeInterceptor.java → ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/PlusWebInvokeTimeInterceptor.java

@@ -1,48 +1,55 @@
-package com.ruoyi.framework.Interceptor;
+package com.ruoyi.framework.interceptor;
 
+import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.map.MapUtil;
 import com.alibaba.ttl.TransmittableThreadLocal;
+import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
 import com.ruoyi.common.utils.JsonUtils;
 import com.ruoyi.common.utils.StringUtils;
-import com.yomahub.tlog.context.TLogContext;
-import com.yomahub.tlog.web.interceptor.AbsTLogWebHandlerMethodInterceptor;
-import com.yomahub.tlog.web.wrapper.RequestWrapper;
+import com.ruoyi.common.utils.spring.SpringUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.time.StopWatch;
 import org.springframework.http.MediaType;
+import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.ModelAndView;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
 import java.util.Map;
 
 /**
- * 重写Tlog web的调用时间统计拦截器
+ * web的调用时间统计拦截器
+ * dev环境有效
  *
  * @author Lion Li
  * @since 3.3.0
  */
 @Slf4j
-public class PlusWebInvokeTimeInterceptor extends AbsTLogWebHandlerMethodInterceptor {
+public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
 
     private final TransmittableThreadLocal<StopWatch> invokeTimeTL = new TransmittableThreadLocal<>();
 
     @Override
-    public boolean preHandleByHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        if (TLogContext.enableInvokeTimePrint()) {
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        if (!"prod".equals(SpringUtils.getActiveProfile())) {
             String url = request.getMethod() + " " + request.getRequestURI();
 
             // 打印请求参数
             if (isJsonRequest(request)) {
-                    String jsonParam = new RequestWrapper(request).getBodyString();
-                    log.info("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam);
+                String jsonParam = "";
+                if (request instanceof RepeatedlyRequestWrapper) {
+                    BufferedReader reader = request.getReader();
+                    jsonParam = IoUtil.read(reader);
+                }
+                log.debug("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam);
             } else {
                 Map<String, String[]> parameterMap = request.getParameterMap();
                 if (MapUtil.isNotEmpty(parameterMap)) {
                     String parameters = JsonUtils.toJsonString(parameterMap);
-                    log.info("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters);
+                    log.debug("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters);
                 } else {
-                    log.info("[PLUS]开始请求 => URL[{}],无参数", url);
+                    log.debug("[PLUS]开始请求 => URL[{}],无参数", url);
                 }
             }
 
@@ -54,16 +61,16 @@ public class PlusWebInvokeTimeInterceptor extends AbsTLogWebHandlerMethodInterce
     }
 
     @Override
-    public void postHandleByHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
 
     }
 
     @Override
-    public void afterCompletionByHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
-        if (TLogContext.enableInvokeTimePrint()) {
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        if (!"prod".equals(SpringUtils.getActiveProfile())) {
             StopWatch stopWatch = invokeTimeTL.get();
             stopWatch.stop();
-            log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime());
+            log.debug("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime());
             invokeTimeTL.remove();
         }
     }

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

@@ -6,6 +6,7 @@ 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;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.generator.domain.GenTable;
@@ -46,8 +47,8 @@ public class GenController extends BaseController {
     @ApiOperation("查询代码生成列表")
     @SaCheckPermission("tool:gen:list")
     @GetMapping("/list")
-    public TableDataInfo<GenTable> genList(GenTable genTable) {
-        return genTableService.selectPageGenTableList(genTable);
+    public TableDataInfo<GenTable> genList(GenTable genTable, PageQuery pageQuery) {
+        return genTableService.selectPageGenTableList(genTable, pageQuery);
     }
 
     /**
@@ -73,8 +74,8 @@ public class GenController extends BaseController {
     @ApiOperation("查询数据库列表")
     @SaCheckPermission("tool:gen:list")
     @GetMapping("/db/list")
-    public TableDataInfo<GenTable> dataList(GenTable genTable) {
-        return genTableService.selectPageDbTableList(genTable);
+    public TableDataInfo<GenTable> dataList(GenTable genTable, PageQuery pageQuery) {
+        return genTableService.selectPageDbTableList(genTable, pageQuery);
     }
 
     /**

+ 2 - 0
ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java

@@ -1,5 +1,6 @@
 package com.ruoyi.generator.mapper;
 
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
 import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
 import com.ruoyi.generator.domain.GenTableColumn;
 
@@ -10,6 +11,7 @@ import java.util.List;
  *
  * @author Lion Li
  */
+@InterceptorIgnore(dataPermission = "true")
 public interface GenTableColumnMapper extends BaseMapperPlus<GenTableColumn> {
     /**
      * 根据表名称查询列信息

+ 2 - 0
ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java

@@ -1,5 +1,6 @@
 package com.ruoyi.generator.mapper;
 
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
 import com.ruoyi.generator.domain.GenTable;
@@ -12,6 +13,7 @@ import java.util.List;
  *
  * @author Lion Li
  */
+@InterceptorIgnore(dataPermission = "true")
 public interface GenTableMapper extends BaseMapperPlus<GenTable> {
 
 

+ 12 - 8
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java

@@ -4,8 +4,10 @@ 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.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.constant.GenConstants;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.exception.ServiceException;
@@ -63,13 +65,15 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
     }
 
     @Override
-    public TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable) {
-        return PageUtils.buildDataInfo(baseMapper.selectPageGenTableList(PageUtils.buildPage(), genTable));
+    public TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable, PageQuery pageQuery) {
+        Page<GenTable> page = baseMapper.selectPageGenTableList(pageQuery.build(), genTable);
+        return TableDataInfo.build(page);
     }
 
     @Override
-    public TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable) {
-        return PageUtils.buildDataInfo(baseMapper.selectPageDbTableList(PageUtils.buildPage(), genTable));
+    public TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable, PageQuery pageQuery) {
+        Page<GenTable> page = baseMapper.selectPageDbTableList(pageQuery.build(), genTable);
+        return TableDataInfo.build(page);
     }
 
     /**
@@ -122,7 +126,7 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
      * @return 结果
      */
     @Override
-    @Transactional
+    @Transactional(rollbackFor = Exception.class)
     public void updateGenTable(GenTable genTable) {
         String options = JsonUtils.toJsonString(genTable.getParams());
         genTable.setOptions(options);
@@ -141,7 +145,7 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
      * @return 结果
      */
     @Override
-    @Transactional
+    @Transactional(rollbackFor = Exception.class)
     public void deleteGenTableByIds(Long[] tableIds) {
         List<Long> ids = Arrays.asList(tableIds);
         removeByIds(ids);
@@ -154,7 +158,7 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
      * @param tableList 导入表列表
      */
     @Override
-    @Transactional
+    @Transactional(rollbackFor = Exception.class)
     public void importGenTable(List<GenTable> tableList) {
         String operName = LoginUtils.getUsername();
         try {
@@ -268,7 +272,7 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
      * @param tableName 表名称
      */
     @Override
-    @Transactional
+    @Transactional(rollbackFor = Exception.class)
     public void synchDb(String tableName) {
         GenTable table = baseMapper.selectGenTableByName(tableName);
         List<GenTableColumn> tableColumns = table.getColumns();

+ 3 - 2
ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java

@@ -1,6 +1,7 @@
 package com.ruoyi.generator.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.generator.domain.GenTable;
 
@@ -15,10 +16,10 @@ import java.util.Map;
 public interface IGenTableService extends IService<GenTable> {
 
 
-    TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable);
+    TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable, PageQuery pageQuery);
 
 
-    TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable);
+    TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable, PageQuery pageQuery);
 
     /**
      * 查询业务列表

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

@@ -9,10 +9,7 @@ import com.ruoyi.generator.domain.GenTable;
 import com.ruoyi.generator.domain.GenTableColumn;
 import org.apache.velocity.VelocityContext;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * 模板处理工具类
@@ -244,7 +241,7 @@ public class VelocityUtils {
      */
     public static String getDicts(GenTable genTable) {
         List<GenTableColumn> columns = genTable.getColumns();
-        List<String> dicts = new ArrayList<String>();
+        Set<String> dicts = new HashSet<String>();
         for (GenTableColumn column : columns) {
             if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny(
                     column.getHtmlType(),

+ 12 - 12
ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml

@@ -27,7 +27,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		<result property="remark"         column="remark"            />
 		<collection  property="columns"  javaType="java.util.List"  resultMap="GenTableColumnResult" />
 	</resultMap>
-	
+
 	<resultMap type="GenTableColumn" id="GenTableColumnResult">
         <id     property="columnId"       column="column_id"      />
         <result property="tableId"        column="table_id"       />
@@ -52,7 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="updateBy"       column="update_by"      />
         <result property="updateTime"     column="update_time"    />
     </resultMap>
-	
+
 	<sql id="selectGenTableVo">
         select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table
     </sql>
@@ -78,7 +78,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	<select id="selectPageDbTableList" parameterType="GenTable" resultMap="GenTableResult">
 		select table_name, table_comment, create_time, update_time from information_schema.tables
 		where table_schema = (select database())
-		AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%'
+		AND table_name NOT LIKE 'xxl_job_%' AND table_name NOT LIKE 'gen_%'
 		AND table_name NOT IN (select table_name from gen_table)
 		<if test="genTable.tableName != null and genTable.tableName != ''">
 			AND lower(table_name) like lower(concat('%', #{genTable.tableName}, '%'))
@@ -117,7 +117,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	<select id="selectDbTableList" parameterType="GenTable" resultMap="GenTableResult">
 		select table_name, table_comment, create_time, update_time from information_schema.tables
 		where table_schema = (select database())
-		AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%'
+		AND table_name NOT LIKE 'xxl_job_%' AND table_name NOT LIKE 'gen_%'
 		AND table_name NOT IN (select table_name from gen_table)
 		<if test="tableName != null and tableName != ''">
 			AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
@@ -133,22 +133,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		</if>
         order by create_time desc
 	</select>
-	
+
 	<select id="selectDbTableListByNames" resultMap="GenTableResult">
 		select table_name, table_comment, create_time, update_time from information_schema.tables
-		where table_name NOT LIKE 'qrtz_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
+		where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
 		and table_name in
 	    <foreach collection="array" item="name" open="(" separator="," close=")">
  			#{name}
-        </foreach> 
+        </foreach>
 	</select>
-	
+
 	<select id="selectTableByName" parameterType="String" resultMap="GenTableResult">
 		select table_name, table_comment, create_time, update_time from information_schema.tables
 		where table_comment <![CDATA[ <> ]]> '' and table_schema = (select database())
 		and table_name = #{tableName}
 	</select>
-	
+
 	<select id="selectGenTableById" parameterType="Long" resultMap="GenTableResult">
 	    SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
 			   c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
@@ -156,7 +156,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 			 LEFT JOIN gen_table_column c ON t.table_id = c.table_id
 		where t.table_id = #{tableId} order by c.sort
 	</select>
-	
+
 	<select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult">
 	    SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
 			   c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
@@ -164,7 +164,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 			 LEFT JOIN gen_table_column c ON t.table_id = c.table_id
 		where t.table_name = #{tableName} order by c.sort
 	</select>
-	
+
 	<select id="selectGenTableAll" parameterType="String" resultMap="GenTableResult">
 	    SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark,
 			   c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
@@ -173,4 +173,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		order by c.sort
 	</select>
 
-</mapper>
+</mapper>

+ 0 - 24
ruoyi-generator/src/main/resources/vm/java/bo.java.vm

@@ -63,28 +63,4 @@ public class ${ClassName}Bo extends ${Entity} {
 #end
 #end
 
-    /**
-     * 分页大小
-     */
-    @ApiModelProperty("分页大小")
-    private Integer pageSize;
-
-    /**
-     * 当前页数
-     */
-    @ApiModelProperty("当前页数")
-    private Integer pageNum;
-
-    /**
-     * 排序列
-     */
-    @ApiModelProperty("排序列")
-    private String orderByColumn;
-
-    /**
-     * 排序的方向desc或者asc
-     */
-    @ApiModelProperty(value = "排序的方向", example = "asc,desc")
-    private String isAsc;
-
 }

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

@@ -14,6 +14,7 @@ import org.springframework.validation.annotation.Validated;
 import com.ruoyi.common.annotation.RepeatSubmit;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.validate.AddGroup;
 import com.ruoyi.common.core.validate.EditGroup;
@@ -53,8 +54,8 @@ public class ${ClassName}Controller extends BaseController {
     @SaCheckPermission("${permissionPrefix}:list")
     @GetMapping("/list")
 #if($table.crud || $table.sub)
-    public TableDataInfo<${ClassName}Vo> list(@Validated(QueryGroup.class) ${ClassName}Bo bo) {
-        return i${ClassName}Service.queryPageList(bo);
+    public TableDataInfo<${ClassName}Vo> list(@Validated(QueryGroup.class) ${ClassName}Bo bo, PageQuery pageQuery) {
+        return i${ClassName}Service.queryPageList(bo, pageQuery);
     }
 #elseif($table.tree)
     public AjaxResult<List<${ClassName}Vo>> list(@Validated(QueryGroup.class) ${ClassName}Bo bo) {

+ 2 - 1
ruoyi-generator/src/main/resources/vm/java/service.java.vm

@@ -6,6 +6,7 @@ import ${packageName}.domain.bo.${ClassName}Bo;
 import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
 #if($table.crud || $table.sub)
 import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.core.domain.PageQuery;
 #end
 
 import java.util.Collection;
@@ -28,7 +29,7 @@ public interface I${ClassName}Service extends IServicePlus<${ClassName}, ${Class
 	/**
 	 * 查询列表
 	 */
-    TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo);
+    TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery);
 #end
 
 	/**

+ 8 - 6
ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm

@@ -3,12 +3,12 @@ package ${packageName}.service.impl;
 import cn.hutool.core.bean.BeanUtil;
 import com.ruoyi.common.utils.StringUtils;
 #if($table.crud || $table.sub)
-import com.ruoyi.common.utils.PageUtils;
-import com.ruoyi.common.core.page.PagePlus;
 import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.core.domain.PageQuery;
 #end
 import org.springframework.stereotype.Service;
 import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import ${packageName}.domain.bo.${ClassName}Bo;
@@ -37,15 +37,17 @@ public class ${ClassName}ServiceImpl extends ServicePlusImpl<${ClassName}Mapper,
 
 #if($table.crud || $table.sub)
     @Override
-    public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo) {
-        PagePlus<${ClassName}, ${ClassName}Vo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo));
-        return PageUtils.buildDataInfo(result);
+    public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo);
+        Page<${ClassName}Vo> result = pageVo(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
     }
 #end
 
     @Override
     public List<${ClassName}Vo> queryList(${ClassName}Bo bo) {
-        return listVo(buildQueryWrapper(bo));
+        LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo);
+        return listVo(lqw);
     }
 
     private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}Bo bo) {

+ 476 - 0
ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm

@@ -0,0 +1,476 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
+#foreach($column in $columns)
+#if($column.query)
+#set($dictType=$column.dictType)
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.htmlType == "input" || $column.htmlType == "textarea")
+      <el-form-item label="${comment}" prop="${column.javaField}">
+        <el-input
+          v-model="queryParams.${column.javaField}"
+          placeholder="请输入${comment}"
+          clearable
+          size="small"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+      <el-form-item label="${comment}" prop="${column.javaField}">
+        <el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
+          <el-option
+            v-for="dict in ${dictType}"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+      <el-form-item label="${comment}" prop="${column.javaField}">
+        <el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
+          <el-option label="请选择字典生成" value="" />
+        </el-select>
+      </el-form-item>
+#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
+      <el-form-item label="${comment}" prop="${column.javaField}">
+        <el-date-picker clearable size="small"
+          v-model="queryParams.${column.javaField}"
+          type="date"
+          value-format="YYYY-MM-DD"
+          placeholder="选择${comment}">
+        </el-date-picker>
+      </el-form-item>
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+      <el-form-item label="${comment}">
+        <el-date-picker
+          v-model="daterange${AttrName}"
+          size="small"
+          style="width: 240px"
+          value-format="YYYY-MM-DD"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+#end
+#end
+#end
+      <el-form-item>
+	    <el-button type="primary" icon="Search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="Refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="Plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['${moduleName}:${businessName}:add']"
+        >新增</el-button>
+      </el-col>
+      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table
+      v-loading="loading"
+      :data="${businessName}List"
+      row-key="${treeCode}"
+      default-expand-all
+      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+    >
+#foreach($column in $columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk)
+#elseif($column.list && $column.htmlType == "datetime")
+      <el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
+        <template #default="scope">
+          <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+#elseif($column.list && $column.dictType && "" != $column.dictType)
+      <el-table-column label="${comment}" align="center" prop="${javaField}">
+        <template #default="scope">
+#if($column.htmlType == "checkbox")
+          <dict-tag :options="${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/>
+#else
+          <dict-tag :options="${column.dictType}" :value="scope.row.${javaField}"/>
+#end
+        </template>
+      </el-table-column>
+#elseif($column.list && "" != $javaField)
+#if(${foreach.index} == 1)
+      <el-table-column label="${comment}" prop="${javaField}" />
+#else
+      <el-table-column label="${comment}" align="center" prop="${javaField}" />
+#end
+#end
+#end
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template #default="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="Edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['${moduleName}:${businessName}:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="Plus"
+            @click="handleAdd(scope.row)"
+            v-hasPermi="['${moduleName}:${businessName}:add']"
+          >新增</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="Delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['${moduleName}:${businessName}:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 添加或修改${functionName}对话框 -->
+    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
+      <el-form ref="${businessName}Ref" :model="form" :rules="rules" label-width="80px">
+#foreach($column in $columns)
+#set($field=$column.javaField)
+#if($column.insert && !$column.pk)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($dictType=$column.dictType)
+#if("" != $treeParentCode && $column.javaField == $treeParentCode)
+        <el-form-item label="${comment}" prop="${treeParentCode}">
+          <tree-select
+            v-model:value="form.${treeParentCode}"
+            :options="${businessName}Options"
+            :objMap="{ value: '${treeCode}', label: '${treeName}', children: 'children' }"
+            placeholder="请选择${comment}"
+          />
+        </el-form-item>
+#elseif($column.htmlType == "input")
+        <el-form-item label="${comment}" prop="${field}">
+          <el-input v-model="form.${field}" placeholder="请输入${comment}" />
+        </el-form-item>
+#elseif($column.htmlType == "imageUpload")
+        <el-form-item label="${comment}">
+          <imageUpload v-model="form.${field}"/>
+        </el-form-item>
+#elseif($column.htmlType == "fileUpload")
+        <el-form-item label="${comment}">
+          <fileUpload v-model="form.${field}"/>
+        </el-form-item>
+#elseif($column.htmlType == "editor")
+        <el-form-item label="${comment}">
+          <editor v-model="form.${field}" :min-height="192"/>
+        </el-form-item>
+#elseif($column.htmlType == "select" && "" != $dictType)
+        <el-form-item label="${comment}" prop="${field}">
+          <el-select v-model="form.${field}" placeholder="请选择${comment}">
+            <el-option
+              v-for="dict in ${dictType}"
+              :key="dict.value"
+              :label="dict.label"
+              #if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end
+
+            ></el-option>
+          </el-select>
+        </el-form-item>
+#elseif($column.htmlType == "select" && $dictType)
+        <el-form-item label="${comment}" prop="${field}">
+          <el-select v-model="form.${field}" placeholder="请选择${comment}">
+            <el-option label="请选择字典生成" value="" />
+          </el-select>
+        </el-form-item>
+#elseif($column.htmlType == "checkbox" && "" != $dictType)
+        <el-form-item label="${comment}">
+          <el-checkbox-group v-model="form.${field}">
+            <el-checkbox
+              v-for="dict in ${dictType}"
+              :key="dict.value"
+              :label="dict.value">
+              {{dict.label}}
+            </el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+#elseif($column.htmlType == "checkbox" && $dictType)
+        <el-form-item label="${comment}">
+          <el-checkbox-group v-model="form.${field}">
+            <el-checkbox>请选择字典生成</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+#elseif($column.htmlType == "radio" && "" != $dictType)
+        <el-form-item label="${comment}">
+          <el-radio-group v-model="form.${field}">
+            <el-radio
+              v-for="dict in ${dictType}"
+              :key="dict.value"
+              #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end
+
+            >{{dict.label}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+#elseif($column.htmlType == "radio" && $dictType)
+        <el-form-item label="${comment}">
+          <el-radio-group v-model="form.${field}">
+            <el-radio label="1">请选择字典生成</el-radio>
+          </el-radio-group>
+        </el-form-item>
+#elseif($column.htmlType == "datetime")
+        <el-form-item label="${comment}" prop="${field}">
+          <el-date-picker clearable size="small"
+            v-model="form.${field}"
+            type="datetime"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            placeholder="选择${comment}">
+          </el-date-picker>
+        </el-form-item>
+#elseif($column.htmlType == "textarea")
+        <el-form-item label="${comment}" prop="${field}">
+          <el-input v-model="form.${field}" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+#end
+#end
+#end
+#end
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="${BusinessName}">
+import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
+
+const { proxy } = getCurrentInstance();
+#if(${dicts} != '')
+#set($dictsNoSymbol=$dicts.replace("'", ""))
+const { ${dictsNoSymbol} } = proxy.useDict(${dicts});
+#end
+
+const ${businessName}List = ref([]);
+const ${businessName}Options = ref([]);
+const open = ref(false);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const title = ref("");
+#foreach ($column in $columns)
+#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+const daterange${AttrName} = ref([]);
+#end
+#end
+
+const data = reactive({
+  form: {},
+  queryParams: {
+#foreach ($column in $columns)
+#if($column.query)
+    $column.javaField: undefined#if($foreach.count != $columns.size()),#end
+#end
+#end
+  },
+  rules: {
+#foreach ($column in $columns)
+#if($column.required)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+    $column.javaField: [
+      { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }
+    ]#if($foreach.count != $columns.size()),#end
+#end
+#end
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询${functionName}列表 */
+function getList() {
+  loading.value = true;
+#foreach ($column in $columns)
+#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+  queryParams.value.params = {};
+#break
+#end
+#end
+#foreach ($column in $columns)
+#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+  if (null != daterange${AttrName} && '' != daterange${AttrName}) {
+    queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0];
+    queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1];
+  }
+#end
+#end
+  list${BusinessName}(queryParams.value).then(response => {
+    ${businessName}List.value = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}");
+    loading.value = false;
+  });
+}
+
+/** 查询${functionName}下拉树结构 */
+async function getTreeselect() {
+  await list${BusinessName}().then(response => {
+    ${businessName}Options.value = [];
+    const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };
+    data.children = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}");
+    ${businessName}Options.value.push(data);
+  });
+}
+
+// 取消按钮
+function cancel() {
+  open.value = false;
+  reset();
+}
+
+// 表单重置
+function reset() {
+  form.value = {
+#foreach ($column in $columns)
+#if($column.htmlType == "radio")
+    $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($foreach.count != $columns.size()),#end
+#else
+    $column.javaField: null#if($foreach.count != $columns.size()),#end
+#end
+#end
+  };
+  proxy.resetForm("${businessName}Ref");
+}
+
+/** 搜索按钮操作 */
+function handleQuery() {
+  getList();
+}
+
+/** 重置按钮操作 */
+function resetQuery() {
+#foreach ($column in $columns)
+#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+  daterange${AttrName}.value = [];
+#end
+#end
+  proxy.resetForm("queryRef");
+  handleQuery();
+}
+
+/** 新增按钮操作 */
+async function handleAdd(row) {
+  reset();
+  await getTreeselect();
+  if (row != null && row.${treeCode}) {
+    form.value.${treeParentCode} = row.${treeCode};
+  } else {
+    form.value.${treeParentCode} = 0;
+  }
+  open.value = true;
+  title.value = "添加${functionName}";
+}
+
+/** 修改按钮操作 */
+async function handleUpdate(row) {
+  loading.value = true;
+  reset();
+  await getTreeselect();
+  if (row != null) {
+    form.value.${treeParentCode} = row.${treeCode};
+  }
+  get${BusinessName}(row.${pkColumn.javaField}).then(response => {
+    loading.value = false;
+    form.value = response.data;
+#foreach ($column in $columns)
+#if($column.htmlType == "checkbox")
+    form.value.$column.javaField = form.value.${column.javaField}.split(",");
+#end
+#end
+    open.value = true;
+    title.value = "修改${functionName}";
+  });
+}
+
+/** 提交按钮 */
+function submitForm() {
+  proxy.#[[$]]#refs["${businessName}Ref"].validate(valid => {
+    if (valid) {
+      buttonLoading.value = true;
+#foreach ($column in $columns)
+#if($column.htmlType == "checkbox")
+      form.value.$column.javaField = form.value.${column.javaField}.join(",");
+#end
+#end
+      if (form.value.${pkColumn.javaField} != null) {
+        update${BusinessName}(form.value).then(response => {
+          proxy.#[[$modal]]#.msgSuccess("修改成功");
+          open.value = false;
+          getList();
+        }).finally(() => {
+          buttonLoading.value = false;
+        });
+      } else {
+        add${BusinessName}(form.value).then(response => {
+          proxy.#[[$modal]]#.msgSuccess("新增成功");
+          open.value = false;
+          getList();
+        }).finally(() => {
+          buttonLoading.value = false;
+        });
+      }
+    }
+  });
+}
+
+/** 删除按钮操作 */
+function handleDelete(row) {
+  proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?').then(function() {
+    loading.value = true;
+    return del${BusinessName}(row.${pkColumn.javaField});
+  }).then(() => {
+    loading.value = false;
+    getList();
+    proxy.#[[$modal]]#.msgSuccess("删除成功");
+  }).finally(() => {
+    loading.value = false;
+  });
+}
+
+getList();
+</script>

部分文件因为文件数量过多而无法显示