Browse Source

发布 v2.3.0

疯狂的狮子li 4 years ago
parent
commit
c90ca8c2a6
74 changed files with 3832 additions and 462 deletions
  1. 5 2
      README.md
  2. 9 1
      pom.xml
  3. 35 10
      ruoyi-admin/src/main/resources/application-dev.yml
  4. 35 10
      ruoyi-admin/src/main/resources/application-prod.yml
  5. 6 0
      ruoyi-common/pom.xml
  6. 6 1
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java
  7. 208 223
      ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
  8. 50 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/bo/ChkjTestAddBo.java
  9. 61 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/bo/ChkjTestEditBo.java
  10. 53 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/bo/ChkjTestQueryBo.java
  11. 43 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoAddBo.java
  12. 52 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoEditBo.java
  13. 42 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoQueryBo.java
  14. 39 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeAddBo.java
  15. 47 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeEditBo.java
  16. 42 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeQueryBo.java
  17. 108 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/ChkjTestController.java
  18. 108 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
  19. 107 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
  20. 55 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/domain/ChkjTest.java
  21. 70 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java
  22. 67 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java
  23. 14 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/ChkjTestMapper.java
  24. 14 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
  25. 14 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java
  26. 57 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/IChkjTestService.java
  27. 57 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java
  28. 52 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java
  29. 93 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/ChkjTestServiceImpl.java
  30. 95 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java
  31. 84 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java
  32. 57 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/vo/ChkjTestVo.java
  33. 76 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestDemoVo.java
  34. 55 0
      ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestTreeVo.java
  35. 19 0
      ruoyi-demo/src/main/resources/mapper/demo/ChkjTestMapper.xml
  36. 23 0
      ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml
  37. 22 0
      ruoyi-demo/src/main/resources/mapper/demo/TestTreeMapper.xml
  38. 124 144
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
  39. 41 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java
  40. 55 28
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java
  41. 101 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java
  42. 1 0
      ruoyi-generator/src/main/resources/vm/java/addBo.java.vm
  43. 5 2
      ruoyi-generator/src/main/resources/vm/java/domain.java.vm
  44. 1 0
      ruoyi-generator/src/main/resources/vm/java/service.java.vm
  45. 3 1
      ruoyi-generator/src/main/resources/vm/java/vo.java.vm
  46. 8 0
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java
  47. 10 0
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java
  48. 10 0
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java
  49. 10 0
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java
  50. 10 0
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java
  51. 2 4
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java
  52. 1 1
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
  53. 2 1
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java
  54. 2 4
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java
  55. 2 6
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
  56. 6 10
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
  57. 16 1
      ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
  58. 8 1
      ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml
  59. 6 2
      ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml
  60. 8 1
      ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml
  61. 6 2
      ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
  62. 8 1
      ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml
  63. 8 1
      ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml
  64. 1 1
      ruoyi-ui/package.json
  65. 53 0
      ruoyi-ui/src/api/demo/demo.js
  66. 53 0
      ruoyi-ui/src/api/demo/test.js
  67. 53 0
      ruoyi-ui/src/api/demo/tree.js
  68. 339 0
      ruoyi-ui/src/views/demo/demo/index.vue
  69. 361 0
      ruoyi-ui/src/views/demo/test/index.vue
  70. 282 0
      ruoyi-ui/src/views/demo/tree/index.vue
  71. 10 0
      ruoyi-ui/src/views/index.vue
  72. 67 0
      ry.bat
  73. 8 4
      ry.sh
  74. 171 0
      sql/test.sql

+ 5 - 2
README.md

@@ -28,6 +28,7 @@
 * 集成 Feign 接口化管理 Http 请求(如三方请求 支付,短信,推送等)
 * 移除 自带服务监控 改为 spring-boot-admin 全方位监控
 * 增加 demo 模块示例(给不会增加模块的小伙伴做参考)
+* 增加 redisson 支持分布式锁 功能更强大
 
 ### 代码改动
 
@@ -38,6 +39,7 @@
 * 代码生成模板 增加 文档注解 与 校验注解 简化通用操作
 * 项目修改为 maven多环境配置
 * 项目配置修改为 application.yml 统一管理
+* 数据权限修改为 适配支持单表、多表
 
 ### 其他
 
@@ -73,8 +75,9 @@
 * 代码生成选择树表生成即可
 
 关于数据权限
-* 参考(用户,角色,部门)列表接口用法
-* 目前仅支持 XML 方式使用(后续修改为支持MP)
+* 创建表 需预留 dept_id 字段 如需用户权限 还需预留 user_id 字段
+* 支持 Mybatis-Plus 方式注入 参考 demo 模块用法(需导入 test.sql 文件)
+* 支持 XML 方式注入 参考 system 模块用法
 
 关于vue与boot整合部署  
 * [前端静态资源如何整合到后端访问](https://doc.ruoyi.vip/ruoyi-vue/other/faq.html#前端静态资源如何整合到后端访问)

+ 9 - 1
pom.xml

@@ -13,7 +13,7 @@
     <description>RuoYi-Vue-Plus后台管理系统</description>
 
     <properties>
-        <ruoyi-vue-plus.version>2.2.1</ruoyi-vue-plus.version>
+        <ruoyi-vue-plus.version>2.3.0</ruoyi-vue-plus.version>
         <spring-boot.version>2.3.11.RELEASE</spring-boot.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
@@ -31,6 +31,7 @@
         <feign.version>2.2.6.RELEASE</feign.version>
         <feign-okhttp.version>11.0</feign-okhttp.version>
         <spring-boot-admin.version>2.3.1</spring-boot-admin.version>
+        <redisson.version>3.15.2</redisson.version>
     </properties>
 
     <!-- 依赖声明 -->
@@ -180,6 +181,13 @@
                 <version>${ruoyi-vue-plus.version}</version>
             </dependency>
 
+            <!--redisson-->
+            <dependency>
+                <groupId>org.redisson</groupId>
+                <artifactId>redisson-spring-boot-starter</artifactId>
+                <version>${redisson.version}</version>
+            </dependency>
+
         </dependencies>
     </dependencyManagement>
 

+ 35 - 10
ruoyi-admin/src/main/resources/application-dev.yml

@@ -67,13 +67,38 @@ spring:
     password:
     # 连接超时时间
     timeout: 10s
-    lettuce:
-      pool:
-        # 连接池中的最小空闲连接
-        min-idle: 0
-        # 连接池中的最大空闲连接
-        max-idle: 8
-        # 连接池的最大数据库连接数
-        max-active: 8
-        # #连接池最大阻塞等待时间(使用负值表示没有限制)
-        max-wait: -1ms
+    # 是否开启ssl
+    ssl: false
+
+--- # redisson 客户端配置
+redisson:
+  # 线程池数量
+  threads: 16
+  # Netty线程池数量
+  nettyThreads: 32
+  # 传输模式
+  transportMode: "NIO"
+  # 单节点配置
+  singleServerConfig:
+    # 客户端名称
+    clientName: ${ruoyi-vue-plus.name}
+    # 最小空闲连接数
+    connectionMinimumIdleSize: 32
+    # 连接池大小
+    connectionPoolSize: 64
+    # 连接空闲超时,单位:毫秒
+    idleConnectionTimeout: 10000
+    # 命令等待超时,单位:毫秒
+    timeout: 3000
+    # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。
+    retryAttempts: 3
+    # 命令重试发送时间间隔,单位:毫秒
+    retryInterval: 1500
+    # 发布和订阅连接的最小空闲连接数
+    subscriptionConnectionMinimumIdleSize: 1
+    # 发布和订阅连接池大小
+    subscriptionConnectionPoolSize: 50
+    # 单个连接最大订阅数量
+    subscriptionsPerConnection: 5
+    # DNS监测时间间隔,单位:毫秒
+    dnsMonitoringInterval: 5000

+ 35 - 10
ruoyi-admin/src/main/resources/application-prod.yml

@@ -67,13 +67,38 @@ spring:
     password:
     # 连接超时时间
     timeout: 10s
-    lettuce:
-      pool:
-        # 连接池中的最小空闲连接
-        min-idle: 0
-        # 连接池中的最大空闲连接
-        max-idle: 8
-        # 连接池的最大数据库连接数
-        max-active: 8
-        # #连接池最大阻塞等待时间(使用负值表示没有限制)
-        max-wait: -1ms
+    # 是否开启ssl
+    ssl: false
+
+--- # redisson 客户端配置
+redisson:
+  # 线程池数量
+  threads: 16
+  # Netty线程池数量
+  nettyThreads: 32
+  # 传输模式
+  transportMode: "NIO"
+  # 单节点配置
+  singleServerConfig:
+    # 客户端名称
+    clientName: ${ruoyi-vue-plus.name}
+    # 最小空闲连接数
+    connectionMinimumIdleSize: 32
+    # 连接池大小
+    connectionPoolSize: 64
+    # 连接空闲超时,单位:毫秒
+    idleConnectionTimeout: 10000
+    # 命令等待超时,单位:毫秒
+    timeout: 3000
+    # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。
+    retryAttempts: 3
+    # 命令重试发送时间间隔,单位:毫秒
+    retryInterval: 1500
+    # 发布和订阅连接的最小空闲连接数
+    subscriptionConnectionMinimumIdleSize: 1
+    # 发布和订阅连接池大小
+    subscriptionConnectionPoolSize: 50
+    # 单个连接最大订阅数量
+    subscriptionsPerConnection: 5
+    # DNS监测时间间隔,单位:毫秒
+    dnsMonitoringInterval: 5000

+ 6 - 0
ruoyi-common/pom.xml

@@ -151,6 +151,12 @@
             <artifactId>spring-boot-configuration-processor</artifactId>
         </dependency>
 
+        <!--redisson-->
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson-spring-boot-starter</artifactId>
+        </dependency>
+
     </dependencies>
 
 </project>

+ 6 - 1
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java

@@ -8,7 +8,7 @@ import java.lang.annotation.Target;
 
 /**
  * 数据权限过滤注解
- * 
+ *
  * @author ruoyi
  */
 @Target(ElementType.METHOD)
@@ -25,4 +25,9 @@ public @interface DataScope
      * 用户表的别名
      */
     public String userAlias() default "";
+
+	/**
+	 * 是否过滤用户权限
+	 */
+	public boolean isUser() default false;
 }

+ 208 - 223
ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java

@@ -1,234 +1,219 @@
 package com.ruoyi.common.core.redis;
 
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
+import com.google.common.collect.Lists;
+import org.redisson.api.*;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.redis.core.BoundSetOperations;
-import org.springframework.data.redis.core.HashOperations;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.data.redis.core.ValueOperations;
 import org.springframework.stereotype.Component;
 
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
 /**
  * spring redis 工具类
  *
- * @author ruoyi
+ * @author shenxinquan
  **/
-@SuppressWarnings(value = { "unchecked", "rawtypes" })
+@SuppressWarnings(value = {"unchecked", "rawtypes"})
 @Component
-public class RedisCache
-{
-    @Autowired
-    public RedisTemplate redisTemplate;
-
-    /**
-     * 缓存基本的对象,Integer、String、实体类等
-     *
-     * @param key 缓存的键值
-     * @param value 缓存的值
-     */
-    public <T> void setCacheObject(final String key, final T value)
-    {
-        redisTemplate.opsForValue().set(key, value);
-    }
-
-    /**
-     * 缓存基本的对象,Integer、String、实体类等
-     *
-     * @param key 缓存的键值
-     * @param value 缓存的值
-     * @param timeout 时间
-     * @param timeUnit 时间颗粒度
-     */
-    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
-    {
-        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
-    }
-
-    /**
-     * 设置有效时间
-     *
-     * @param key Redis键
-     * @param timeout 超时时间
-     * @return true=设置成功;false=设置失败
-     */
-    public boolean expire(final String key, final long timeout)
-    {
-        return expire(key, timeout, TimeUnit.SECONDS);
-    }
-
-    /**
-     * 设置有效时间
-     *
-     * @param key Redis键
-     * @param timeout 超时时间
-     * @param unit 时间单位
-     * @return true=设置成功;false=设置失败
-     */
-    public boolean expire(final String key, final long timeout, final TimeUnit unit)
-    {
-        return redisTemplate.expire(key, timeout, unit);
-    }
-
-    /**
-     * 获得缓存的基本对象。
-     *
-     * @param key 缓存键值
-     * @return 缓存键值对应的数据
-     */
-    public <T> T getCacheObject(final String key)
-    {
-        ValueOperations<String, T> operation = redisTemplate.opsForValue();
-        return operation.get(key);
-    }
-
-    /**
-     * 删除单个对象
-     *
-     * @param key
-     */
-    public boolean deleteObject(final String key)
-    {
-        return redisTemplate.delete(key);
-    }
-
-    /**
-     * 删除集合对象
-     *
-     * @param collection 多个对象
-     * @return
-     */
-    public long deleteObject(final Collection collection)
-    {
-        return redisTemplate.delete(collection);
-    }
-
-    /**
-     * 缓存List数据
-     *
-     * @param key 缓存的键值
-     * @param dataList 待缓存的List数据
-     * @return 缓存的对象
-     */
-    public <T> long setCacheList(final String key, final List<T> dataList)
-    {
-        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
-        return count == null ? 0 : count;
-    }
-
-    /**
-     * 获得缓存的list对象
-     *
-     * @param key 缓存的键值
-     * @return 缓存键值对应的数据
-     */
-    public <T> List<T> getCacheList(final String key)
-    {
-        return redisTemplate.opsForList().range(key, 0, -1);
-    }
-
-    /**
-     * 缓存Set
-     *
-     * @param key 缓存键值
-     * @param dataSet 缓存的数据
-     * @return 缓存数据的对象
-     */
-    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
-    {
-        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
-        Iterator<T> it = dataSet.iterator();
-        while (it.hasNext())
-        {
-            setOperation.add(it.next());
-        }
-        return setOperation;
-    }
-
-    /**
-     * 获得缓存的set
-     *
-     * @param key
-     * @return
-     */
-    public <T> Set<T> getCacheSet(final String key)
-    {
-        return redisTemplate.opsForSet().members(key);
-    }
-
-    /**
-     * 缓存Map
-     *
-     * @param key
-     * @param dataMap
-     */
-    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
-    {
-        if (dataMap != null) {
-            redisTemplate.opsForHash().putAll(key, dataMap);
-        }
-    }
-
-    /**
-     * 获得缓存的Map
-     *
-     * @param key
-     * @return
-     */
-    public <T> Map<String, T> getCacheMap(final String key)
-    {
-        return redisTemplate.opsForHash().entries(key);
-    }
-
-    /**
-     * 往Hash中存入数据
-     *
-     * @param key Redis键
-     * @param hKey Hash键
-     * @param value 值
-     */
-    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
-    {
-        redisTemplate.opsForHash().put(key, hKey, value);
-    }
-
-    /**
-     * 获取Hash中的数据
-     *
-     * @param key Redis键
-     * @param hKey Hash键
-     * @return Hash中的对象
-     */
-    public <T> T getCacheMapValue(final String key, final String hKey)
-    {
-        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
-        return opsForHash.get(key, hKey);
-    }
-
-    /**
-     * 获取多个Hash中的数据
-     *
-     * @param key Redis键
-     * @param hKeys Hash键集合
-     * @return Hash对象集合
-     */
-    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
-    {
-        return redisTemplate.opsForHash().multiGet(key, hKeys);
-    }
-
-    /**
-     * 获得缓存的基本对象列表
-     *
-     * @param pattern 字符串前缀
-     * @return 对象列表
-     */
-    public Collection<String> keys(final String pattern)
-    {
-        return redisTemplate.keys(pattern);
-    }
+public class RedisCache {
+
+	@Autowired
+	private RedissonClient redissonClient;
+
+	/**
+	 * 缓存基本的对象,Integer、String、实体类等
+	 *
+	 * @param key   缓存的键值
+	 * @param value 缓存的值
+	 */
+	public <T> void setCacheObject(final String key, final T value) {
+		redissonClient.getBucket(key).set(value);
+	}
+
+	/**
+	 * 缓存基本的对象,Integer、String、实体类等
+	 *
+	 * @param key      缓存的键值
+	 * @param value    缓存的值
+	 * @param timeout  时间
+	 * @param timeUnit 时间颗粒度
+	 */
+	public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
+		RBucket<T> result = redissonClient.getBucket(key);
+		result.set(value);
+		result.expire(timeout, timeUnit);
+	}
+
+	/**
+	 * 设置有效时间
+	 *
+	 * @param key     Redis键
+	 * @param timeout 超时时间
+	 * @return true=设置成功;false=设置失败
+	 */
+	public boolean expire(final String key, final long timeout) {
+		return expire(key, timeout, TimeUnit.SECONDS);
+	}
+
+	/**
+	 * 设置有效时间
+	 *
+	 * @param key     Redis键
+	 * @param timeout 超时时间
+	 * @param unit    时间单位
+	 * @return true=设置成功;false=设置失败
+	 */
+	public boolean expire(final String key, final long timeout, final TimeUnit unit) {
+		RBucket rBucket = redissonClient.getBucket(key);
+		return rBucket.expire(timeout, unit);
+	}
+
+	/**
+	 * 获得缓存的基本对象。
+	 *
+	 * @param key 缓存键值
+	 * @return 缓存键值对应的数据
+	 */
+	public <T> T getCacheObject(final String key) {
+		RBucket<T> rBucket = redissonClient.getBucket(key);
+		return rBucket.get();
+	}
+
+	/**
+	 * 删除单个对象
+	 *
+	 * @param key
+	 */
+	public boolean deleteObject(final String key) {
+		return redissonClient.getBucket(key).delete();
+	}
+
+	/* */
+
+	/**
+	 * 删除集合对象
+	 *
+	 * @param collection 多个对象
+	 * @return
+	 */
+	public long deleteObject(final Collection collection) {
+		return redissonClient.getKeys().delete(Arrays.toString(collection.toArray()));
+	}
+
+	/**
+	 * 缓存List数据
+	 *
+	 * @param key      缓存的键值
+	 * @param dataList 待缓存的List数据
+	 * @return 缓存的对象
+	 */
+	public <T> boolean setCacheList(final String key, final List<T> dataList) {
+		RList<T> rList = redissonClient.getList(key);
+		return rList.addAll(dataList);
+	}
+
+	/**
+	 * 获得缓存的list对象
+	 *
+	 * @param key 缓存的键值
+	 * @return 缓存键值对应的数据
+	 */
+	public <T> List<T> getCacheList(final String key) {
+		RList<T> rList = redissonClient.getList(key);
+		return rList.readAll();
+	}
+
+	/**
+	 * 缓存Set
+	 *
+	 * @param key     缓存键值
+	 * @param dataSet 缓存的数据
+	 * @return 缓存数据的对象
+	 */
+	public <T> boolean setCacheSet(final String key, final Set<T> dataSet) {
+		RSet<T> rSet = redissonClient.getSet(key);
+		return rSet.addAll(dataSet);
+	}
+
+	/**
+	 * 获得缓存的set
+	 *
+	 * @param key
+	 * @return
+	 */
+	public <T> Set<T> getCacheSet(final String key) {
+		RSet<T> rSet = redissonClient.getSet(key);
+		return rSet.readAll();
+	}
+
+	/**
+	 * 缓存Map
+	 *
+	 * @param key
+	 * @param dataMap
+	 */
+	public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
+		if (dataMap != null) {
+			RMap<String, T> rMap = redissonClient.getMap(key);
+			rMap.putAll(dataMap);
+		}
+	}
+
+	/**
+	 * 获得缓存的Map
+	 *
+	 * @param key
+	 * @return
+	 */
+	public <T> Map<String, T> getCacheMap(final String key) {
+		RMap<String, T> rMap = redissonClient.getMap(key);
+		return rMap.getAll(rMap.keySet());
+	}
+
+	/**
+	 * 往Hash中存入数据
+	 *
+	 * @param key   Redis键
+	 * @param hKey  Hash键
+	 * @param value 值
+	 */
+	public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
+		RMap<String, T> rMap = redissonClient.getMap(key);
+		rMap.put(hKey, value);
+	}
+
+	/**
+	 * 获取Hash中的数据
+	 *
+	 * @param key  Redis键
+	 * @param hKey Hash键
+	 * @return Hash中的对象
+	 */
+	public <T> T getCacheMapValue(final String key, final String hKey) {
+		RMap<String, T> rMap = redissonClient.getMap(key);
+		return rMap.get(hKey);
+	}
+
+	/**
+	 * 获取多个Hash中的数据
+	 *
+	 * @param key   Redis键
+	 * @param hKeys Hash键集合
+	 * @return Hash对象集合
+	 */
+	public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
+		RListMultimap rListMultimap = redissonClient.getListMultimap(key);
+		return rListMultimap.getAll(hKeys);
+	}
+
+	/**
+	 * 获得缓存的基本对象列表
+	 *
+	 * @param pattern 字符串前缀
+	 * @return 对象列表
+	 */
+	public Collection<String> keys(final String pattern) {
+		Iterable<String> iterable = redissonClient.getKeys().getKeysByPattern(pattern);
+		return Lists.newArrayList(iterable);
+	}
 }

+ 50 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/ChkjTestAddBo.java

@@ -0,0 +1,50 @@
+package com.ruoyi.demo.bo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+
+
+
+/**
+ * 测试添加对象 chkj_test
+ *
+ * @author Lion Li
+ * @date 2021-05-14
+ */
+@Data
+@ApiModel("测试添加对象")
+public class ChkjTestAddBo {
+
+    /** key键 */
+    @ApiModelProperty("key键")
+    @NotBlank(message = "key键不能为空")
+    private String testKey;
+    /** 值 */
+    @ApiModelProperty("值")
+    @NotBlank(message = "值不能为空")
+    private String value;
+    /** 版本 */
+    @ApiModelProperty("版本")
+    private Long version;
+    /** 创建时间 */
+    @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+    /** 删除标志 */
+    @ApiModelProperty("删除标志")
+    private Long deleted;
+    /** 父id */
+    @ApiModelProperty("父id")
+    @NotNull(message = "父id不能为空")
+    private Long parentId;
+    /** 排序号 */
+    @ApiModelProperty("排序号")
+    @NotNull(message = "排序号不能为空")
+    private Long orderNum;
+}

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

@@ -0,0 +1,61 @@
+package com.ruoyi.demo.bo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+
+
+/**
+ * 测试编辑对象 chkj_test
+ *
+ * @author Lion Li
+ * @date 2021-05-14
+ */
+@Data
+@ApiModel("测试编辑对象")
+public class ChkjTestEditBo {
+
+
+    /** 主键 */
+    @ApiModelProperty("主键")
+    @NotNull(message = "主键不能为空")
+    private Long id;
+
+    /** key键 */
+    @ApiModelProperty("key键")
+    @NotBlank(message = "key键不能为空")
+    private String testKey;
+
+    /** 值 */
+    @ApiModelProperty("值")
+    @NotBlank(message = "值不能为空")
+    private String value;
+
+    /** 版本 */
+    @ApiModelProperty("版本")
+    private Long version;
+
+    /** 创建时间 */
+    @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    /** 删除标志 */
+    @ApiModelProperty("删除标志")
+    private Long deleted;
+
+    /** 父id */
+    @ApiModelProperty("父id")
+    @NotNull(message = "父id不能为空")
+    private Long parentId;
+
+    /** 排序号 */
+    @ApiModelProperty("排序号")
+    @NotNull(message = "排序号不能为空")
+    private Long orderNum;
+}

+ 53 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/ChkjTestQueryBo.java

@@ -0,0 +1,53 @@
+package com.ruoyi.demo.bo;
+
+import com.ruoyi.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 测试分页查询对象 chkj_test
+ *
+ * @author Lion Li
+ * @date 2021-05-14
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel("测试分页查询对象")
+public class ChkjTestQueryBo extends BaseEntity {
+
+	/** 分页大小 */
+	@ApiModelProperty("分页大小")
+	private Integer pageSize;
+	/** 当前页数 */
+	@ApiModelProperty("当前页数")
+	private Integer pageNum;
+	/** 排序列 */
+	@ApiModelProperty("排序列")
+	private String orderByColumn;
+	/** 排序的方向desc或者asc */
+	@ApiModelProperty(value = "排序的方向", example = "asc,desc")
+	private String isAsc;
+
+
+	/** key键 */
+	@ApiModelProperty("key键")
+	private String testKey;
+	/** 值 */
+	@ApiModelProperty("值")
+	private String value;
+	/** 版本 */
+	@ApiModelProperty("版本")
+	private Long version;
+	/** 删除标志 */
+	@ApiModelProperty("删除标志")
+	private Long deleted;
+	/** 父id */
+	@ApiModelProperty("父id")
+	private Long parentId;
+	/** 排序号 */
+	@ApiModelProperty("排序号")
+	private Long orderNum;
+
+}

+ 43 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoAddBo.java

@@ -0,0 +1,43 @@
+package com.ruoyi.demo.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+
+
+/**
+ * 测试单表添加对象 test_demo
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Data
+@ApiModel("测试单表添加对象")
+public class TestDemoAddBo {
+
+    /** 部门id */
+    @ApiModelProperty("部门id")
+    private Long deptId;
+
+    /** 用户id */
+    @ApiModelProperty("用户id")
+    private Long userId;
+
+    /** 排序号 */
+    @ApiModelProperty("排序号")
+    private Long orderNum;
+
+    /** key键 */
+    @ApiModelProperty("key键")
+    @NotBlank(message = "key键不能为空")
+    private String testKey;
+
+    /** 值 */
+    @ApiModelProperty("值")
+    @NotBlank(message = "值不能为空")
+    private String value;
+
+}

+ 52 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoEditBo.java

@@ -0,0 +1,52 @@
+package com.ruoyi.demo.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+
+/**
+ * 测试单表编辑对象 test_demo
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Data
+@ApiModel("测试单表编辑对象")
+public class TestDemoEditBo {
+
+
+    /** 主键 */
+    @ApiModelProperty("主键")
+    private Long id;
+
+
+    /** 部门id */
+    @ApiModelProperty("部门id")
+    private Long deptId;
+
+
+    /** 用户id */
+    @ApiModelProperty("用户id")
+    private Long userId;
+
+
+    /** 排序号 */
+    @ApiModelProperty("排序号")
+    private Long orderNum;
+
+
+    /** key键 */
+    @ApiModelProperty("key键")
+    @NotBlank(message = "key键不能为空")
+    private String testKey;
+
+
+    /** 值 */
+    @ApiModelProperty("值")
+    @NotBlank(message = "值不能为空")
+    private String value;
+
+}

+ 42 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoQueryBo.java

@@ -0,0 +1,42 @@
+package com.ruoyi.demo.bo;
+
+import com.ruoyi.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 测试单表分页查询对象 test_demo
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel("测试单表分页查询对象")
+public class TestDemoQueryBo extends BaseEntity {
+
+	/** 分页大小 */
+	@ApiModelProperty("分页大小")
+	private Integer pageSize;
+	/** 当前页数 */
+	@ApiModelProperty("当前页数")
+	private Integer pageNum;
+	/** 排序列 */
+	@ApiModelProperty("排序列")
+	private String orderByColumn;
+	/** 排序的方向desc或者asc */
+	@ApiModelProperty(value = "排序的方向", example = "asc,desc")
+	private String isAsc;
+
+
+	/** key键 */
+	@ApiModelProperty("key键")
+	private String testKey;
+	/** 值 */
+	@ApiModelProperty("值")
+	private String value;
+
+}

+ 39 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeAddBo.java

@@ -0,0 +1,39 @@
+package com.ruoyi.demo.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import java.util.Date;
+import javax.validation.constraints.*;
+
+
+
+/**
+ * 测试树表添加对象 test_tree
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Data
+@ApiModel("测试树表添加对象")
+public class TestTreeAddBo {
+
+    /** 父id */
+    @ApiModelProperty("父id")
+    private Long parentId;
+
+    /** 部门id */
+    @ApiModelProperty("部门id")
+    private Long deptId;
+
+    /** 用户id */
+    @ApiModelProperty("用户id")
+    private Long userId;
+
+    /** 树节点名 */
+    @ApiModelProperty("树节点名")
+    @NotBlank(message = "树节点名不能为空")
+    private String treeName;
+
+}

+ 47 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeEditBo.java

@@ -0,0 +1,47 @@
+package com.ruoyi.demo.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import java.util.Date;
+import javax.validation.constraints.*;
+
+
+/**
+ * 测试树表编辑对象 test_tree
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Data
+@ApiModel("测试树表编辑对象")
+public class TestTreeEditBo {
+
+
+    /** 主键 */
+    @ApiModelProperty("主键")
+    private Long id;
+
+
+    /** 父id */
+    @ApiModelProperty("父id")
+    private Long parentId;
+
+
+    /** 部门id */
+    @ApiModelProperty("部门id")
+    private Long deptId;
+
+
+    /** 用户id */
+    @ApiModelProperty("用户id")
+    private Long userId;
+
+
+    /** 树节点名 */
+    @ApiModelProperty("树节点名")
+    @NotBlank(message = "树节点名不能为空")
+    private String treeName;
+
+}

+ 42 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestTreeQueryBo.java

@@ -0,0 +1,42 @@
+package com.ruoyi.demo.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 测试树表分页查询对象 test_tree
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel("测试树表分页查询对象")
+public class TestTreeQueryBo extends BaseEntity {
+
+	/** 分页大小 */
+	@ApiModelProperty("分页大小")
+	private Integer pageSize;
+	/** 当前页数 */
+	@ApiModelProperty("当前页数")
+	private Integer pageNum;
+	/** 排序列 */
+	@ApiModelProperty("排序列")
+	private String orderByColumn;
+	/** 排序的方向desc或者asc */
+	@ApiModelProperty(value = "排序的方向", example = "asc,desc")
+	private String isAsc;
+
+
+	/** 树节点名 */
+	@ApiModelProperty("树节点名")
+	private String treeName;
+
+}

+ 108 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/ChkjTestController.java

@@ -0,0 +1,108 @@
+package com.ruoyi.demo.controller;
+
+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.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.demo.bo.ChkjTestAddBo;
+import com.ruoyi.demo.bo.ChkjTestEditBo;
+import com.ruoyi.demo.bo.ChkjTestQueryBo;
+import com.ruoyi.demo.service.IChkjTestService;
+import com.ruoyi.demo.vo.ChkjTestVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 测试Controller
+ * 
+ * @author Lion Li
+ * @date 2021-05-14
+ */
+@Api(value = "测试控制器", tags = {"测试管理"})
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+@RestController
+@RequestMapping("/demo/test")
+public class ChkjTestController extends BaseController {
+
+    private final IChkjTestService iChkjTestService;
+
+    /**
+     * 查询测试列表
+     */
+    @ApiOperation("查询测试列表")
+    @PreAuthorize("@ss.hasPermi('demo:test:list')")
+    @GetMapping("/list")
+    public TableDataInfo<ChkjTestVo> list(@Validated ChkjTestQueryBo bo) {
+        return iChkjTestService.queryPageList(bo);
+    }
+
+    /**
+     * 导出测试列表
+     */
+    @ApiOperation("导出测试列表")
+    @PreAuthorize("@ss.hasPermi('demo:test:export')")
+    @Log(title = "测试", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult<ChkjTestVo> export(@Validated ChkjTestQueryBo bo) {
+        List<ChkjTestVo> list = iChkjTestService.queryList(bo);
+        ExcelUtil<ChkjTestVo> util = new ExcelUtil<ChkjTestVo>(ChkjTestVo.class);
+        return util.exportExcel(list, "测试");
+    }
+
+    /**
+     * 获取测试详细信息
+     */
+    @ApiOperation("获取测试详细信息")
+    @PreAuthorize("@ss.hasPermi('demo:test:query')")
+    @GetMapping("/{id}")
+    public AjaxResult<ChkjTestVo> getInfo(@NotNull(message = "主键不能为空")
+                                                  @PathVariable("id") Long id) {
+        return AjaxResult.success(iChkjTestService.queryById(id));
+    }
+
+    /**
+     * 新增测试
+     */
+    @ApiOperation("新增测试")
+    @PreAuthorize("@ss.hasPermi('demo:test:add')")
+    @Log(title = "测试", businessType = BusinessType.INSERT)
+    @PostMapping()
+    public AjaxResult<Void> add(@Validated @RequestBody ChkjTestAddBo bo) {
+        return toAjax(iChkjTestService.insertByAddBo(bo) ? 1 : 0);
+    }
+
+    /**
+     * 修改测试
+     */
+    @ApiOperation("修改测试")
+    @PreAuthorize("@ss.hasPermi('demo:test:edit')")
+    @Log(title = "测试", businessType = BusinessType.UPDATE)
+    @PutMapping()
+    public AjaxResult<Void> edit(@Validated @RequestBody ChkjTestEditBo bo) {
+        return toAjax(iChkjTestService.updateByEditBo(bo) ? 1 : 0);
+    }
+
+    /**
+     * 删除测试
+     */
+    @ApiOperation("删除测试")
+    @PreAuthorize("@ss.hasPermi('demo:test:remove')")
+    @Log(title = "测试" , businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
+                                       @PathVariable Long[] ids) {
+        return toAjax(iChkjTestService.deleteWithValidByIds(Arrays.asList(ids), true) ? 1 : 0);
+    }
+}

+ 108 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java

@@ -0,0 +1,108 @@
+package com.ruoyi.demo.controller;
+
+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.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.demo.bo.TestDemoAddBo;
+import com.ruoyi.demo.bo.TestDemoEditBo;
+import com.ruoyi.demo.bo.TestDemoQueryBo;
+import com.ruoyi.demo.service.ITestDemoService;
+import com.ruoyi.demo.vo.TestDemoVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 测试单表Controller
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Api(value = "测试单表控制器", tags = {"测试单表管理"})
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+@RestController
+@RequestMapping("/demo/demo")
+public class TestDemoController extends BaseController {
+
+    private final ITestDemoService iTestDemoService;
+
+    /**
+     * 查询测试单表列表
+     */
+    @ApiOperation("查询测试单表列表")
+    @PreAuthorize("@ss.hasPermi('demo:demo:list')")
+    @GetMapping("/list")
+    public TableDataInfo<TestDemoVo> list(@Validated TestDemoQueryBo bo) {
+        return iTestDemoService.queryPageList(bo);
+    }
+
+    /**
+     * 导出测试单表列表
+     */
+    @ApiOperation("导出测试单表列表")
+    @PreAuthorize("@ss.hasPermi('demo:demo:export')")
+    @Log(title = "测试单表", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult<TestDemoVo> export(@Validated TestDemoQueryBo bo) {
+        List<TestDemoVo> list = iTestDemoService.queryList(bo);
+        ExcelUtil<TestDemoVo> util = new ExcelUtil<TestDemoVo>(TestDemoVo.class);
+        return util.exportExcel(list, "测试单表");
+    }
+
+    /**
+     * 获取测试单表详细信息
+     */
+    @ApiOperation("获取测试单表详细信息")
+    @PreAuthorize("@ss.hasPermi('demo:demo:query')")
+    @GetMapping("/{id}")
+    public AjaxResult<TestDemoVo> getInfo(@NotNull(message = "主键不能为空")
+                                                  @PathVariable("id") Long id) {
+        return AjaxResult.success(iTestDemoService.queryById(id));
+    }
+
+    /**
+     * 新增测试单表
+     */
+    @ApiOperation("新增测试单表")
+    @PreAuthorize("@ss.hasPermi('demo:demo:add')")
+    @Log(title = "测试单表", businessType = BusinessType.INSERT)
+    @PostMapping()
+    public AjaxResult<Void> add(@Validated @RequestBody TestDemoAddBo bo) {
+        return toAjax(iTestDemoService.insertByAddBo(bo) ? 1 : 0);
+    }
+
+    /**
+     * 修改测试单表
+     */
+    @ApiOperation("修改测试单表")
+    @PreAuthorize("@ss.hasPermi('demo:demo:edit')")
+    @Log(title = "测试单表", businessType = BusinessType.UPDATE)
+    @PutMapping()
+    public AjaxResult<Void> edit(@Validated @RequestBody TestDemoEditBo bo) {
+        return toAjax(iTestDemoService.updateByEditBo(bo) ? 1 : 0);
+    }
+
+    /**
+     * 删除测试单表
+     */
+    @ApiOperation("删除测试单表")
+    @PreAuthorize("@ss.hasPermi('demo:demo:remove')")
+    @Log(title = "测试单表" , businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
+                                       @PathVariable Long[] ids) {
+        return toAjax(iTestDemoService.deleteWithValidByIds(Arrays.asList(ids), true) ? 1 : 0);
+    }
+}

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

@@ -0,0 +1,107 @@
+package com.ruoyi.demo.controller;
+
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.demo.bo.TestTreeAddBo;
+import com.ruoyi.demo.bo.TestTreeEditBo;
+import com.ruoyi.demo.bo.TestTreeQueryBo;
+import com.ruoyi.demo.service.ITestTreeService;
+import com.ruoyi.demo.vo.TestTreeVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 测试树表Controller
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Api(value = "测试树表控制器", tags = {"测试树表管理"})
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+@RestController
+@RequestMapping("/demo/tree")
+public class TestTreeController extends BaseController {
+
+    private final ITestTreeService iTestTreeService;
+
+    /**
+     * 查询测试树表列表
+     */
+    @ApiOperation("查询测试树表列表")
+    @PreAuthorize("@ss.hasPermi('demo:tree:list')")
+    @GetMapping("/list")
+    public AjaxResult<List<TestTreeVo>> list(@Validated TestTreeQueryBo bo) {
+        return AjaxResult.success(iTestTreeService.queryList(bo));
+    }
+
+    /**
+     * 导出测试树表列表
+     */
+    @ApiOperation("导出测试树表列表")
+    @PreAuthorize("@ss.hasPermi('demo:tree:export')")
+    @Log(title = "测试树表", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult<TestTreeVo> export(@Validated TestTreeQueryBo bo) {
+        List<TestTreeVo> list = iTestTreeService.queryList(bo);
+        ExcelUtil<TestTreeVo> util = new ExcelUtil<TestTreeVo>(TestTreeVo.class);
+        return util.exportExcel(list, "测试树表");
+    }
+
+    /**
+     * 获取测试树表详细信息
+     */
+    @ApiOperation("获取测试树表详细信息")
+    @PreAuthorize("@ss.hasPermi('demo:tree:query')")
+    @GetMapping("/{id}")
+    public AjaxResult<TestTreeVo> getInfo(@NotNull(message = "主键不能为空")
+                                                  @PathVariable("id") Long id) {
+        return AjaxResult.success(iTestTreeService.queryById(id));
+    }
+
+    /**
+     * 新增测试树表
+     */
+    @ApiOperation("新增测试树表")
+    @PreAuthorize("@ss.hasPermi('demo:tree:add')")
+    @Log(title = "测试树表", businessType = BusinessType.INSERT)
+    @PostMapping()
+    public AjaxResult<Void> add(@Validated @RequestBody TestTreeAddBo bo) {
+        return toAjax(iTestTreeService.insertByAddBo(bo) ? 1 : 0);
+    }
+
+    /**
+     * 修改测试树表
+     */
+    @ApiOperation("修改测试树表")
+    @PreAuthorize("@ss.hasPermi('demo:tree:edit')")
+    @Log(title = "测试树表", businessType = BusinessType.UPDATE)
+    @PutMapping()
+    public AjaxResult<Void> edit(@Validated @RequestBody TestTreeEditBo bo) {
+        return toAjax(iTestTreeService.updateByEditBo(bo) ? 1 : 0);
+    }
+
+    /**
+     * 删除测试树表
+     */
+    @ApiOperation("删除测试树表")
+    @PreAuthorize("@ss.hasPermi('demo:tree:remove')")
+    @Log(title = "测试树表" , businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
+                                       @PathVariable Long[] ids) {
+        return toAjax(iTestTreeService.deleteWithValidByIds(Arrays.asList(ids), true) ? 1 : 0);
+    }
+}

+ 55 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/ChkjTest.java

@@ -0,0 +1,55 @@
+package com.ruoyi.demo.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 测试对象 chkj_test
+ * 
+ * @author Lion Li
+ * @date 2021-05-14
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName("chkj_test")
+public class ChkjTest implements Serializable {
+
+private static final long serialVersionUID=1L;
+
+
+    /** 主键 */
+    @TableId(value = "id")
+    private Long id;
+
+    /** key键 */
+    private String testKey;
+
+    /** 值 */
+    private String value;
+
+    /** 版本 */
+    private Long version;
+
+    /** 创建时间 */
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** 删除标志 */
+    private Long deleted;
+
+    /** 父id */
+    private Long parentId;
+
+    /** 排序号 */
+    private Long orderNum;
+
+}

+ 70 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java

@@ -0,0 +1,70 @@
+package com.ruoyi.demo.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 测试单表对象 test_demo
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName("test_demo")
+public class TestDemo implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+
+    /** 主键 */
+    @TableId(value = "id")
+    private Long id;
+
+    /** 部门id */
+    private Long deptId;
+
+    /** 用户id */
+    private Long userId;
+
+    /** 排序号 */
+    @OrderBy(isDesc = false, sort = 1)
+    private Long orderNum;
+
+    /** key键 */
+    private String testKey;
+
+    /** 值 */
+    private String value;
+
+    /** 版本 */
+    @Version
+    private Long version;
+
+    /** 创建时间 */
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** 创建人 */
+    @TableField(fill = FieldFill.INSERT)
+    private String createBy;
+
+    /** 更新时间 */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Date updateTime;
+
+    /** 更新人 */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private String updateBy;
+
+    /** 删除标志 */
+    @TableLogic
+    private Long delFlag;
+
+}

+ 67 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java

@@ -0,0 +1,67 @@
+package com.ruoyi.demo.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import java.io.Serializable;
+import java.util.Date;
+import java.math.BigDecimal;
+import com.ruoyi.common.annotation.Excel;
+
+/**
+ * 测试树表对象 test_tree
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@TableName("test_tree")
+public class TestTree implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+
+    /** 主键 */
+    @TableId(value = "id")
+    private Long id;
+
+    /** 父id */
+    private Long parentId;
+
+    /** 部门id */
+    private Long deptId;
+
+    /** 用户id */
+    private Long userId;
+
+    /** 树节点名 */
+    private String treeName;
+
+    /** 版本 */
+    @Version
+    private Long version;
+
+    /** 创建时间 */
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** 创建人 */
+    @TableField(fill = FieldFill.INSERT)
+    private String createBy;
+
+    /** 更新时间 */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Date updateTime;
+
+    /** 更新人 */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private String updateBy;
+
+    /** 删除标志 */
+    @TableLogic
+    private Long delFlag;
+
+}

+ 14 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/ChkjTestMapper.java

@@ -0,0 +1,14 @@
+package com.ruoyi.demo.mapper;
+
+import com.ruoyi.demo.domain.ChkjTest;
+import com.ruoyi.common.core.page.BaseMapperPlus;
+
+/**
+ * 测试Mapper接口
+ *
+ * @author Lion Li
+ * @date 2021-05-14
+ */
+public interface ChkjTestMapper extends BaseMapperPlus<ChkjTest> {
+
+}

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

@@ -0,0 +1,14 @@
+package com.ruoyi.demo.mapper;
+
+import com.ruoyi.demo.domain.TestDemo;
+import com.ruoyi.common.core.page.BaseMapperPlus;
+
+/**
+ * 测试单表Mapper接口
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+public interface TestDemoMapper extends BaseMapperPlus<TestDemo> {
+
+}

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

@@ -0,0 +1,14 @@
+package com.ruoyi.demo.mapper;
+
+import com.ruoyi.demo.domain.TestTree;
+import com.ruoyi.common.core.page.BaseMapperPlus;
+
+/**
+ * 测试树表Mapper接口
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+public interface TestTreeMapper extends BaseMapperPlus<TestTree> {
+
+}

+ 57 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/service/IChkjTestService.java

@@ -0,0 +1,57 @@
+package com.ruoyi.demo.service;
+
+import com.ruoyi.demo.domain.ChkjTest;
+import com.ruoyi.demo.vo.ChkjTestVo;
+import com.ruoyi.demo.bo.ChkjTestQueryBo;
+import com.ruoyi.demo.bo.ChkjTestAddBo;
+import com.ruoyi.demo.bo.ChkjTestEditBo;
+import com.ruoyi.common.core.page.IServicePlus;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 测试Service接口
+ *
+ * @author Lion Li
+ * @date 2021-05-14
+ */
+public interface IChkjTestService extends IServicePlus<ChkjTest> {
+	/**
+	 * 查询单个
+	 * @return
+	 */
+	ChkjTestVo queryById(Long id);
+
+	/**
+	 * 查询列表
+	 */
+    TableDataInfo<ChkjTestVo> queryPageList(ChkjTestQueryBo bo);
+	/**
+	 * 查询列表
+	 */
+	List<ChkjTestVo> queryList(ChkjTestQueryBo bo);
+
+	/**
+	 * 根据新增业务对象插入测试
+	 * @param bo 测试新增业务对象
+	 * @return
+	 */
+	Boolean insertByAddBo(ChkjTestAddBo bo);
+
+	/**
+	 * 根据编辑业务对象修改测试
+	 * @param bo 测试编辑业务对象
+	 * @return
+	 */
+	Boolean updateByEditBo(ChkjTestEditBo bo);
+
+	/**
+	 * 校验并删除数据
+	 * @param ids 主键集合
+	 * @param isValid 是否校验,true-删除前校验,false-不校验
+	 * @return
+	 */
+	Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 57 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java

@@ -0,0 +1,57 @@
+package com.ruoyi.demo.service;
+
+import com.ruoyi.demo.domain.TestDemo;
+import com.ruoyi.demo.vo.TestDemoVo;
+import com.ruoyi.demo.bo.TestDemoQueryBo;
+import com.ruoyi.demo.bo.TestDemoAddBo;
+import com.ruoyi.demo.bo.TestDemoEditBo;
+import com.ruoyi.common.core.page.IServicePlus;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 测试单表Service接口
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+public interface ITestDemoService extends IServicePlus<TestDemo> {
+	/**
+	 * 查询单个
+	 * @return
+	 */
+	TestDemoVo queryById(Long id);
+
+	/**
+	 * 查询列表
+	 */
+    TableDataInfo<TestDemoVo> queryPageList(TestDemoQueryBo bo);
+	/**
+	 * 查询列表
+	 */
+	List<TestDemoVo> queryList(TestDemoQueryBo bo);
+
+	/**
+	 * 根据新增业务对象插入测试单表
+	 * @param bo 测试单表新增业务对象
+	 * @return
+	 */
+	Boolean insertByAddBo(TestDemoAddBo bo);
+
+	/**
+	 * 根据编辑业务对象修改测试单表
+	 * @param bo 测试单表编辑业务对象
+	 * @return
+	 */
+	Boolean updateByEditBo(TestDemoEditBo bo);
+
+	/**
+	 * 校验并删除数据
+	 * @param ids 主键集合
+	 * @param isValid 是否校验,true-删除前校验,false-不校验
+	 * @return
+	 */
+	Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 52 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java

@@ -0,0 +1,52 @@
+package com.ruoyi.demo.service;
+
+import com.ruoyi.common.core.page.IServicePlus;
+import com.ruoyi.demo.bo.TestTreeAddBo;
+import com.ruoyi.demo.bo.TestTreeEditBo;
+import com.ruoyi.demo.bo.TestTreeQueryBo;
+import com.ruoyi.demo.domain.TestTree;
+import com.ruoyi.demo.vo.TestTreeVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 测试树表Service接口
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+public interface ITestTreeService extends IServicePlus<TestTree> {
+	/**
+	 * 查询单个
+	 * @return
+	 */
+	TestTreeVo queryById(Long id);
+
+	/**
+	 * 查询列表
+	 */
+	List<TestTreeVo> queryList(TestTreeQueryBo bo);
+
+	/**
+	 * 根据新增业务对象插入测试树表
+	 * @param bo 测试树表新增业务对象
+	 * @return
+	 */
+	Boolean insertByAddBo(TestTreeAddBo bo);
+
+	/**
+	 * 根据编辑业务对象修改测试树表
+	 * @param bo 测试树表编辑业务对象
+	 * @return
+	 */
+	Boolean updateByEditBo(TestTreeEditBo bo);
+
+	/**
+	 * 校验并删除数据
+	 * @param ids 主键集合
+	 * @param isValid 是否校验,true-删除前校验,false-不校验
+	 * @return
+	 */
+	Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 93 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/ChkjTestServiceImpl.java

@@ -0,0 +1,93 @@
+package com.ruoyi.demo.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.core.page.PagePlus;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.utils.PageUtils;
+import com.ruoyi.demo.bo.ChkjTestAddBo;
+import com.ruoyi.demo.bo.ChkjTestEditBo;
+import com.ruoyi.demo.bo.ChkjTestQueryBo;
+import com.ruoyi.demo.domain.ChkjTest;
+import com.ruoyi.demo.mapper.ChkjTestMapper;
+import com.ruoyi.demo.service.IChkjTestService;
+import com.ruoyi.demo.vo.ChkjTestVo;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 测试Service业务层处理
+ *
+ * @author Lion Li
+ * @date 2021-05-14
+ */
+@Service
+public class ChkjTestServiceImpl extends ServiceImpl<ChkjTestMapper, ChkjTest> implements IChkjTestService {
+
+    @Override
+    public ChkjTestVo queryById(Long id){
+        return getVoById(id, obj -> BeanUtil.toBean(obj, ChkjTestVo.class));
+    }
+
+    @Override
+    public TableDataInfo<ChkjTestVo> queryPageList(ChkjTestQueryBo bo) {
+        PagePlus<ChkjTest, ChkjTestVo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo), ChkjTestVo.class);
+        return PageUtils.buildDataInfo(result);
+    }
+
+    @Override
+    public List<ChkjTestVo> queryList(ChkjTestQueryBo bo) {
+        return listVo(buildQueryWrapper(bo), ChkjTestVo.class);
+    }
+
+    private LambdaQueryWrapper<ChkjTest> buildQueryWrapper(ChkjTestQueryBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<ChkjTest> lqw = Wrappers.lambdaQuery();
+        lqw.like(StrUtil.isNotBlank(bo.getTestKey()), ChkjTest::getTestKey, bo.getTestKey());
+        lqw.eq(StrUtil.isNotBlank(bo.getValue()), ChkjTest::getValue, bo.getValue());
+        lqw.eq(bo.getVersion() != null, ChkjTest::getVersion, bo.getVersion());
+        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
+            ChkjTest::getCreateTime ,params.get("beginCreateTime"), params.get("endCreateTime"));
+        lqw.eq(bo.getDeleted() != null, ChkjTest::getDeleted, bo.getDeleted());
+        lqw.eq(bo.getParentId() != null, ChkjTest::getParentId, bo.getParentId());
+        lqw.eq(bo.getOrderNum() != null, ChkjTest::getOrderNum, bo.getOrderNum());
+        return lqw;
+    }
+
+    @Override
+    public Boolean insertByAddBo(ChkjTestAddBo bo) {
+        ChkjTest add = BeanUtil.toBean(bo, ChkjTest.class);
+        validEntityBeforeSave(add);
+        return save(add);
+    }
+
+    @Override
+    public Boolean updateByEditBo(ChkjTestEditBo bo) {
+        ChkjTest update = BeanUtil.toBean(bo, ChkjTest.class);
+        validEntityBeforeSave(update);
+        return updateById(update);
+    }
+
+    /**
+     * 保存前的数据校验
+     *
+     * @param entity 实体类数据
+     */
+    private void validEntityBeforeSave(ChkjTest entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return removeByIds(ids);
+    }
+}

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

@@ -0,0 +1,95 @@
+package com.ruoyi.demo.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.annotation.DataScope;
+import com.ruoyi.common.core.page.PagePlus;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.utils.PageUtils;
+import com.ruoyi.demo.bo.TestDemoAddBo;
+import com.ruoyi.demo.bo.TestDemoEditBo;
+import com.ruoyi.demo.bo.TestDemoQueryBo;
+import com.ruoyi.demo.domain.TestDemo;
+import com.ruoyi.demo.mapper.TestDemoMapper;
+import com.ruoyi.demo.service.ITestDemoService;
+import com.ruoyi.demo.vo.TestDemoVo;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 测试单表Service业务层处理
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Service
+public class TestDemoServiceImpl extends ServiceImpl<TestDemoMapper, TestDemo> implements ITestDemoService {
+
+	@Override
+	public TestDemoVo queryById(Long id) {
+		return getVoById(id, TestDemoVo.class);
+	}
+
+	@DataScope(isUser = true)
+	@Override
+	public TableDataInfo<TestDemoVo> queryPageList(TestDemoQueryBo bo) {
+		PagePlus<TestDemo, TestDemoVo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo), TestDemoVo.class);
+		return PageUtils.buildDataInfo(result);
+	}
+
+	@DataScope(isUser = true)
+	@Override
+	public List<TestDemoVo> queryList(TestDemoQueryBo bo) {
+		return listVo(buildQueryWrapper(bo), TestDemoVo.class);
+	}
+
+	private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoQueryBo bo) {
+		Map<String, Object> params = bo.getParams();
+		Object dataScope = params.get("dataScope");
+		LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
+		lqw.like(StrUtil.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
+		lqw.eq(StrUtil.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
+		lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
+			TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
+		lqw.apply(dataScope != null && StrUtil.isNotBlank(dataScope.toString()),
+			dataScope != null ? dataScope.toString() : null);
+		return lqw;
+	}
+
+	@Override
+	public Boolean insertByAddBo(TestDemoAddBo bo) {
+		TestDemo add = BeanUtil.toBean(bo, TestDemo.class);
+		validEntityBeforeSave(add);
+		return save(add);
+	}
+
+	@Override
+	public Boolean updateByEditBo(TestDemoEditBo bo) {
+		TestDemo update = BeanUtil.toBean(bo, TestDemo.class);
+		validEntityBeforeSave(update);
+		return updateById(update);
+	}
+
+	/**
+	 * 保存前的数据校验
+	 *
+	 * @param entity 实体类数据
+	 */
+	private void validEntityBeforeSave(TestDemo entity) {
+		//TODO 做一些数据校验,如唯一约束
+	}
+
+	@Override
+	public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+		if (isValid) {
+			//TODO 做一些业务上的校验,判断是否需要校验
+		}
+		return removeByIds(ids);
+	}
+}

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

@@ -0,0 +1,84 @@
+package com.ruoyi.demo.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.annotation.DataScope;
+import com.ruoyi.demo.bo.TestTreeAddBo;
+import com.ruoyi.demo.bo.TestTreeEditBo;
+import com.ruoyi.demo.bo.TestTreeQueryBo;
+import com.ruoyi.demo.domain.TestTree;
+import com.ruoyi.demo.mapper.TestTreeMapper;
+import com.ruoyi.demo.service.ITestTreeService;
+import com.ruoyi.demo.vo.TestTreeVo;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 测试树表Service业务层处理
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Service
+public class TestTreeServiceImpl extends ServiceImpl<TestTreeMapper, TestTree> implements ITestTreeService {
+
+	@Override
+	public TestTreeVo queryById(Long id) {
+		return getVoById(id, TestTreeVo.class);
+	}
+
+	@DataScope(isUser = true)
+	@Override
+	public List<TestTreeVo> queryList(TestTreeQueryBo bo) {
+		return listVo(buildQueryWrapper(bo), TestTreeVo.class);
+	}
+
+	private LambdaQueryWrapper<TestTree> buildQueryWrapper(TestTreeQueryBo bo) {
+		Map<String, Object> params = bo.getParams();
+		Object dataScope = params.get("dataScope");
+		LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery();
+		lqw.like(StrUtil.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
+		lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
+			TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
+		lqw.apply(dataScope != null && StrUtil.isNotBlank(dataScope.toString()),
+			dataScope != null ? dataScope.toString() : null);
+		return lqw;
+	}
+
+	@Override
+	public Boolean insertByAddBo(TestTreeAddBo bo) {
+		TestTree add = BeanUtil.toBean(bo, TestTree.class);
+		validEntityBeforeSave(add);
+		return save(add);
+	}
+
+	@Override
+	public Boolean updateByEditBo(TestTreeEditBo bo) {
+		TestTree update = BeanUtil.toBean(bo, TestTree.class);
+		validEntityBeforeSave(update);
+		return updateById(update);
+	}
+
+	/**
+	 * 保存前的数据校验
+	 *
+	 * @param entity 实体类数据
+	 */
+	private void validEntityBeforeSave(TestTree entity) {
+		//TODO 做一些数据校验,如唯一约束
+	}
+
+	@Override
+	public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+		if (isValid) {
+			//TODO 做一些业务上的校验,判断是否需要校验
+		}
+		return removeByIds(ids);
+	}
+}

+ 57 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/vo/ChkjTestVo.java

@@ -0,0 +1,57 @@
+package com.ruoyi.demo.vo;
+
+import com.ruoyi.common.annotation.Excel;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import java.util.Date;
+
+
+
+/**
+ * 测试视图对象 mall_package
+ *
+ * @author Lion Li
+ * @date 2021-05-14
+ */
+@Data
+@ApiModel("测试视图对象")
+public class ChkjTestVo {
+	private static final long serialVersionUID = 1L;
+
+	/** 主键 */
+	@ApiModelProperty("主键")
+	private Long id;
+
+	/** key键 */
+	@Excel(name = "key键")
+	@ApiModelProperty("key键")
+	private String testKey;
+	/** 值 */
+	@Excel(name = "值")
+	@ApiModelProperty("值")
+	private String value;
+	/** 版本 */
+	@Excel(name = "版本")
+	@ApiModelProperty("版本")
+	private Long version;
+	/** 创建时间 */
+	@Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	@ApiModelProperty("创建时间")
+	private Date createTime;
+	/** 删除标志 */
+	@Excel(name = "删除标志")
+	@ApiModelProperty("删除标志")
+	private Long deleted;
+	/** 父id */
+	@Excel(name = "父id")
+	@ApiModelProperty("父id")
+	private Long parentId;
+	/** 排序号 */
+	@Excel(name = "排序号")
+	@ApiModelProperty("排序号")
+	private Long orderNum;
+
+}

+ 76 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestDemoVo.java

@@ -0,0 +1,76 @@
+package com.ruoyi.demo.vo;
+
+import com.ruoyi.common.annotation.Excel;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import java.util.Date;
+
+
+
+/**
+ * 测试单表视图对象 test_demo
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Data
+@ApiModel("测试单表视图对象")
+public class TestDemoVo {
+
+	private static final long serialVersionUID = 1L;
+
+	/** 主键 */
+	@ApiModelProperty("主键")
+	private Long id;
+
+	/** 部门id */
+	@Excel(name = "部门id")
+	@ApiModelProperty("部门id")
+	private Long deptId;
+
+	/** 用户id */
+	@Excel(name = "用户id")
+	@ApiModelProperty("用户id")
+	private Long userId;
+
+	/** 排序号 */
+	@Excel(name = "排序号")
+	@ApiModelProperty("排序号")
+	private Long orderNum;
+
+	/** key键 */
+	@Excel(name = "key键")
+	@ApiModelProperty("key键")
+	private String testKey;
+
+	/** 值 */
+	@Excel(name = "值")
+	@ApiModelProperty("值")
+	private String value;
+
+	/** 创建时间 */
+	@Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	@ApiModelProperty("创建时间")
+	private Date createTime;
+
+	/** 创建人 */
+	@Excel(name = "创建人")
+	@ApiModelProperty("创建人")
+	private String createBy;
+
+	/** 更新时间 */
+	@Excel(name = "更新时间" , width = 30, dateFormat = "yyyy-MM-dd")
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	@ApiModelProperty("更新时间")
+	private Date updateTime;
+
+	/** 更新人 */
+	@Excel(name = "更新人")
+	@ApiModelProperty("更新人")
+	private String updateBy;
+
+
+}

+ 55 - 0
ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestTreeVo.java

@@ -0,0 +1,55 @@
+package com.ruoyi.demo.vo;
+
+import com.ruoyi.common.annotation.Excel;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import java.util.Date;
+
+
+
+/**
+ * 测试树表视图对象 test_tree
+ *
+ * @author Lion Li
+ * @date 2021-05-30
+ */
+@Data
+@ApiModel("测试树表视图对象")
+public class TestTreeVo {
+
+	private static final long serialVersionUID = 1L;
+
+	/** 主键 */
+	@ApiModelProperty("主键")
+	private Long id;
+
+	/** 父id */
+	@Excel(name = "父id")
+	@ApiModelProperty("父id")
+	private Long parentId;
+
+	/** 部门id */
+	@Excel(name = "部门id")
+	@ApiModelProperty("部门id")
+	private Long deptId;
+
+	/** 用户id */
+	@Excel(name = "用户id")
+	@ApiModelProperty("用户id")
+	private Long userId;
+
+	/** 树节点名 */
+	@Excel(name = "树节点名")
+	@ApiModelProperty("树节点名")
+	private String treeName;
+
+	/** 创建时间 */
+	@Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	@ApiModelProperty("创建时间")
+	private Date createTime;
+
+
+}

+ 19 - 0
ruoyi-demo/src/main/resources/mapper/demo/ChkjTestMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.demo.mapper.ChkjTestMapper">
+
+    <resultMap type="com.ruoyi.demo.domain.ChkjTest" id="ChkjTestResult">
+        <result property="id" column="id"/>
+        <result property="testKey" column="test_key"/>
+        <result property="value" column="value"/>
+        <result property="version" column="version"/>
+        <result property="createTime" column="create_time"/>
+        <result property="deleted" column="deleted"/>
+        <result property="parentId" column="parent_id"/>
+        <result property="orderNum" column="order_num"/>
+    </resultMap>
+
+
+</mapper>

+ 23 - 0
ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.demo.mapper.TestDemoMapper">
+
+    <resultMap type="com.ruoyi.demo.domain.TestDemo" id="TestDemoResult">
+        <result property="id" column="id"/>
+        <result property="deptId" column="dept_id"/>
+        <result property="userId" column="user_id"/>
+        <result property="orderNum" column="order_num"/>
+        <result property="testKey" column="test_key"/>
+        <result property="value" column="value"/>
+        <result property="version" column="version"/>
+        <result property="createTime" column="create_time"/>
+        <result property="createBy" column="create_by"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="delFlag" column="del_flag"/>
+    </resultMap>
+
+
+</mapper>

+ 22 - 0
ruoyi-demo/src/main/resources/mapper/demo/TestTreeMapper.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.demo.mapper.TestTreeMapper">
+
+    <resultMap type="com.ruoyi.demo.domain.TestTree" id="TestTreeResult">
+        <result property="id" column="id"/>
+        <result property="parentId" column="parent_id"/>
+        <result property="deptId" column="dept_id"/>
+        <result property="userId" column="user_id"/>
+        <result property="treeName" column="tree_name"/>
+        <result property="version" column="version"/>
+        <result property="createTime" column="create_time"/>
+        <result property="createBy" column="create_by"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="delFlag" column="del_flag"/>
+    </resultMap>
+
+
+</mapper>

+ 124 - 144
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java

@@ -24,167 +24,147 @@ import java.util.Map;
 /**
  * 数据过滤处理
  *
- * @author ruoyi
+ * @author Lion Li
  */
 @Aspect
 @Component
-public class DataScopeAspect
-{
-    /**
-     * 全部数据权限
-     */
-    public static final String DATA_SCOPE_ALL = "1";
-
-    /**
-     * 自定数据权限
-     */
-    public static final String DATA_SCOPE_CUSTOM = "2";
-
-    /**
-     * 部门数据权限
-     */
-    public static final String DATA_SCOPE_DEPT = "3";
-
-    /**
-     * 部门及以下数据权限
-     */
-    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
-
-    /**
-     * 仅本人数据权限
-     */
-    public static final String DATA_SCOPE_SELF = "5";
-
-    /**
-     * 数据权限过滤关键字
-     */
-    public static final String DATA_SCOPE = "dataScope";
-
-    // 配置织入点
-    @Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)")
-    public void dataScopePointCut()
-    {
-    }
-
-    @Before("dataScopePointCut()")
-    public void doBefore(JoinPoint point) throws Throwable
-    {
+public class DataScopeAspect {
+
+	/**
+	 * 全部数据权限
+	 */
+	public static final String DATA_SCOPE_ALL = "1";
+
+	/**
+	 * 自定数据权限
+	 */
+	public static final String DATA_SCOPE_CUSTOM = "2";
+
+	/**
+	 * 部门数据权限
+	 */
+	public static final String DATA_SCOPE_DEPT = "3";
+
+	/**
+	 * 部门及以下数据权限
+	 */
+	public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
+
+	/**
+	 * 仅本人数据权限
+	 */
+	public static final String DATA_SCOPE_SELF = "5";
+
+	/**
+	 * 数据权限过滤关键字
+	 */
+	public static final String DATA_SCOPE = "dataScope";
+
+	// 配置织入点
+	@Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)")
+	public void dataScopePointCut() {
+	}
+
+	@Before("dataScopePointCut()")
+	public void doBefore(JoinPoint point) throws Throwable {
 		clearDataScope(point);
-        handleDataScope(point);
-    }
-
-    protected void handleDataScope(final JoinPoint joinPoint)
-    {
-        // 获得注解
-        DataScope controllerDataScope = getAnnotationLog(joinPoint);
-        if (controllerDataScope == null)
-        {
-            return;
-        }
-        // 获取当前的用户
-        LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
-        if (Validator.isNotNull(loginUser))
-        {
-            SysUser currentUser = loginUser.getUser();
-            // 如果是超级管理员,则不过滤数据
-            if (Validator.isNotNull(currentUser) && !currentUser.isAdmin())
-            {
-                dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
-                        controllerDataScope.userAlias());
-            }
-        }
-    }
-
-    /**
-     * 数据范围过滤
-     *
-     * @param joinPoint 切点
-     * @param user 用户
-     * @param userAlias 别名
-     */
-    public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias)
-    {
-        StringBuilder sqlString = new StringBuilder();
-
-        for (SysRole role : user.getRoles())
-        {
-            String dataScope = role.getDataScope();
-            if (DATA_SCOPE_ALL.equals(dataScope))
-            {
-                sqlString = new StringBuilder();
-                break;
-            }
-            else if (DATA_SCOPE_CUSTOM.equals(dataScope))
-            {
-                sqlString.append(StrUtil.format(
-                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
-                        role.getRoleId()));
-            }
-            else if (DATA_SCOPE_DEPT.equals(dataScope))
-            {
-                sqlString.append(StrUtil.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
-            }
-            else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
-            {
-                sqlString.append(StrUtil.format(
-                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
-                        deptAlias, user.getDeptId(), user.getDeptId()));
-            }
-            else if (DATA_SCOPE_SELF.equals(dataScope))
-            {
-                if (StrUtil.isNotBlank(userAlias))
-                {
-                    sqlString.append(StrUtil.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
-                }
-                else
-                {
-                    // 数据权限为仅本人且没有userAlias别名不查询任何数据
-                    sqlString.append(" OR 1=0 ");
-                }
-            }
-        }
-
-        if (StrUtil.isNotBlank(sqlString.toString()))
-        {
-			putDataScope(joinPoint, "AND (" + sqlString.substring(4) + ")");
+		handleDataScope(point);
+	}
+
+	protected void handleDataScope(final JoinPoint joinPoint) {
+		// 获得注解
+		DataScope controllerDataScope = getAnnotationLog(joinPoint);
+		if (controllerDataScope == null) {
+			return;
+		}
+		// 获取当前的用户
+		LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
+		if (Validator.isNotNull(loginUser)) {
+			SysUser currentUser = loginUser.getUser();
+			// 如果是超级管理员,则不过滤数据
+			if (Validator.isNotNull(currentUser) && !currentUser.isAdmin()) {
+				dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
+					controllerDataScope.userAlias(), controllerDataScope.isUser());
+			}
+		}
+	}
+
+	/**
+	 * 数据范围过滤
+	 *
+	 * @param joinPoint 切点
+	 * @param user      用户
+	 * @param userAlias 别名
+	 */
+	public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, boolean isUser) {
+		StringBuilder sqlString = new StringBuilder();
+
+		// 将 "." 提取出,不写别名为单表查询,写别名为多表查询
+		deptAlias = StrUtil.isNotBlank(deptAlias) ? deptAlias + "." : "";
+		userAlias = StrUtil.isNotBlank(userAlias) ? userAlias + "." : "";
+
+		for (SysRole role : user.getRoles()) {
+			String dataScope = role.getDataScope();
+			if (DATA_SCOPE_ALL.equals(dataScope)) {
+				sqlString = new StringBuilder();
+				break;
+			} else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
+				sqlString.append(StrUtil.format(
+					" OR {}dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ",
+					deptAlias, role.getRoleId()));
+			} else if (DATA_SCOPE_DEPT.equals(dataScope)) {
+				sqlString.append(StrUtil.format(" OR {}dept_id = {} ",
+					deptAlias, user.getDeptId()));
+			} else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
+				sqlString.append(StrUtil.format(
+					" OR {}dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
+					deptAlias, user.getDeptId(), user.getDeptId()));
+			} else if (DATA_SCOPE_SELF.equals(dataScope)) {
+				if (isUser) {
+					sqlString.append(StrUtil.format(" OR {}user_id = {} ",
+						userAlias, user.getUserId()));
+				} else {
+					// 数据权限为仅本人且没有userAlias别名不查询任何数据
+					sqlString.append(" OR 1=0 ");
+				}
+			}
+		}
+
+		if (StrUtil.isNotBlank(sqlString.toString())) {
+			putDataScope(joinPoint, sqlString.substring(4));
+		}
+	}
+
+	/**
+	 * 是否存在注解,如果存在就获取
+	 */
+	private DataScope getAnnotationLog(JoinPoint joinPoint) {
+		Signature signature = joinPoint.getSignature();
+		MethodSignature methodSignature = (MethodSignature) signature;
+		Method method = methodSignature.getMethod();
+
+		if (method != null) {
+			return method.getAnnotation(DataScope.class);
 		}
-    }
-
-    /**
-     * 是否存在注解,如果存在就获取
-     */
-    private DataScope getAnnotationLog(JoinPoint joinPoint)
-    {
-        Signature signature = joinPoint.getSignature();
-        MethodSignature methodSignature = (MethodSignature) signature;
-        Method method = methodSignature.getMethod();
-
-        if (method != null)
-        {
-            return method.getAnnotation(DataScope.class);
-        }
-        return null;
-    }
+		return null;
+	}
 
 	/**
 	 * 拼接权限sql前先清空params.dataScope参数防止注入
 	 */
-	private void clearDataScope(final JoinPoint joinPoint)
-	{
+	private void clearDataScope(final JoinPoint joinPoint) {
 		Object params = joinPoint.getArgs()[0];
-		if (Validator.isNotNull(params))
-		{
+		if (Validator.isNotNull(params)) {
 			putDataScope(joinPoint, "");
 		}
 	}
 
 	private static void putDataScope(JoinPoint joinPoint, String sql) {
 		Object params = joinPoint.getArgs()[0];
-		if (Validator.isNotNull(params))
-		{
-			if(params instanceof BaseEntity) {
+		if (Validator.isNotNull(params)) {
+			if (params instanceof BaseEntity) {
 				BaseEntity baseEntity = (BaseEntity) params;
-				baseEntity.getParams().put(DATA_SCOPE, "");
+				baseEntity.getParams().put(DATA_SCOPE, sql);
 			} else {
 				try {
 					Method getParams = params.getClass().getDeclaredMethod("getParams", null);

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

@@ -0,0 +1,41 @@
+package com.ruoyi.framework.config;
+
+import com.ruoyi.common.exception.CustomException;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
+
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+@EnableAsync
+@Configuration
+public class AsyncConfig extends AsyncConfigurerSupport {
+
+    /**
+     * 异步执行需要使用权限框架自带的包装线程池  保证权限信息的传递
+     */
+    @Override
+    public Executor getAsyncExecutor() {
+        return new DelegatingSecurityContextExecutorService(
+        	Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
+    }
+
+    /**
+     * 异步执行异常处理
+     */
+    @Override
+    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+        return (throwable, method, objects) -> {
+            throwable.printStackTrace();
+            throw new CustomException(
+                    "Exception message - " + throwable.getMessage()
+                    + ", Method name - " + method.getName()
+                    + ", Parameter value - " + Arrays.toString(objects));
+        };
+    }
+
+}

+ 55 - 28
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java

@@ -1,42 +1,69 @@
 package com.ruoyi.framework.config;
 
-import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
+import com.ruoyi.framework.config.properties.RedissonProperties;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.codec.JsonJacksonCodec;
+import org.redisson.config.Config;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
 import org.springframework.cache.annotation.CachingConfigurerSupport;
 import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.data.redis.connection.RedisConnectionFactory;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.io.IOException;
 
 /**
  * redis配置
  *
- * @author ruoyi
+ * @author Lion Li
  */
 @Configuration
 @EnableCaching
-public class RedisConfig extends CachingConfigurerSupport
-{
-    @Bean
-    @SuppressWarnings(value = { "unchecked", "rawtypes" })
-    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
-    {
-        RedisTemplate<Object, Object> template = new RedisTemplate<>();
-        template.setConnectionFactory(connectionFactory);
-
-        GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer();
-		StringRedisSerializer keySerializer = new StringRedisSerializer();
-
-        // 使用StringRedisSerializer来序列化和反序列化redis的key值
-		template.setKeySerializer(keySerializer);
-        template.setValueSerializer(serializer);
-
-        // Hash的key也采用StringRedisSerializer的序列化方式
-        template.setHashKeySerializer(keySerializer);
-        template.setHashValueSerializer(serializer);
-
-        template.afterPropertiesSet();
-        return template;
-    }
+public class RedisConfig extends CachingConfigurerSupport {
+
+	private static final String REDIS_PROTOCOL_PREFIX = "redis://";
+	private static final String REDISS_PROTOCOL_PREFIX = "rediss://";
+
+	@Autowired
+	private RedisProperties redisProperties;
+
+	@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());
+
+		RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
+		// 使用单机模式
+		config.useSingleServer()
+			.setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
+			.setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
+			.setDatabase(redisProperties.getDatabase())
+			.setPassword(redisProperties.getPassword())
+			.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());
+		return Redisson.create(config);
+	}
 }

+ 101 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java

@@ -0,0 +1,101 @@
+package com.ruoyi.framework.config.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.redisson.client.codec.Codec;
+import org.redisson.config.TransportMode;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Redisson 配置属性
+ *
+ * @author Lion Li
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "redisson")
+public class RedissonProperties {
+
+	/**
+	 * 线程池数量,默认值 = 当前处理核数量 * 2
+	 */
+	private int threads;
+
+	/**
+	 * Netty线程池数量,默认值 = 当前处理核数量 * 2
+	 */
+	private int nettyThreads;
+
+	/**
+	 * 传输模式
+	 */
+	private TransportMode transportMode;
+
+	/**
+	 * 单机服务配置
+	 */
+	private SingleServerConfig singleServerConfig;
+
+	@Data
+	@NoArgsConstructor
+	public static class SingleServerConfig {
+
+		/**
+		 * 客户端名称
+		 */
+		private String clientName;
+
+		/**
+		 * 最小空闲连接数
+		 */
+		private int connectionMinimumIdleSize;
+
+		/**
+		 * 连接池大小
+		 */
+		private int connectionPoolSize;
+
+		/**
+		 * 连接空闲超时,单位:毫秒
+		 */
+		private int idleConnectionTimeout;
+
+		/**
+		 * 命令等待超时,单位:毫秒
+		 */
+		private int timeout;
+
+		/**
+		 * 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。
+		 */
+		private int retryAttempts;
+
+		/**
+		 * 命令重试发送时间间隔,单位:毫秒
+		 */
+		private int retryInterval;
+
+		/**
+		 * 发布和订阅连接的最小空闲连接数
+		 */
+		private int subscriptionConnectionMinimumIdleSize;
+
+		/**
+		 * 发布和订阅连接池大小
+		 */
+		private int subscriptionConnectionPoolSize;
+
+		/**
+		 * 单个连接最大订阅数量
+		 */
+		private int subscriptionsPerConnection;
+
+		/**
+		 * DNS监测时间间隔,单位:毫秒
+		 */
+		private int dnsMonitoringInterval;
+
+	}
+
+}

+ 1 - 0
ruoyi-generator/src/main/resources/vm/java/addBo.java.vm

@@ -24,6 +24,7 @@ public class ${ClassName}AddBo {
 
 #foreach ($column in $columns)
 #if($column.isInsert && $column.isPk!=1)
+
     /** $column.columnComment */
     @ApiModelProperty("$column.columnComment")
 #if($column.javaType == 'Date')

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

@@ -11,7 +11,7 @@ import com.ruoyi.common.annotation.Excel;
 
 /**
  * ${functionName}对象 ${tableName}
- * 
+ *
  * @author ${author}
  * @date ${datetime}
  */
@@ -21,7 +21,7 @@ import com.ruoyi.common.annotation.Excel;
 @TableName("${tableName}")
 public class ${ClassName} implements Serializable {
 
-private static final long serialVersionUID=1L;
+    private static final long serialVersionUID=1L;
 
 #foreach ($column in $columns)
 
@@ -35,6 +35,9 @@ private static final long serialVersionUID=1L;
 #if($column.javaField=='delFlag')
     @TableLogic
 #end
+#if($column.javaField=='version')
+    @Version
+#end
 #if($column.isPk==1)
     @TableId(value = "$column.columnName")
 #end

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

@@ -32,6 +32,7 @@ public interface I${ClassName}Service extends IServicePlus<${ClassName}> {
 	 */
     TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}QueryBo bo);
 #end
+
 	/**
 	 * 查询列表
 	 */

+ 3 - 1
ruoyi-generator/src/main/resources/vm/java/vo.java.vm

@@ -13,7 +13,7 @@ import java.util.Date;
 
 
 /**
- * ${functionName}视图对象 mall_package
+ * ${functionName}视图对象 ${tableName}
  *
  * @author ${author}
  * @date ${datetime}
@@ -21,6 +21,7 @@ import java.util.Date;
 @Data
 @ApiModel("${functionName}视图对象")
 public class ${ClassName}Vo {
+
 	private static final long serialVersionUID = 1L;
 
 	/** $pkColumn.columnComment */
@@ -46,6 +47,7 @@ public class ${ClassName}Vo {
 #end
 	@ApiModelProperty("$column.columnComment")
 	private $column.javaType $column.javaField;
+
 #end
 #end
 

+ 8 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java

@@ -30,4 +30,12 @@ public interface SysDeptMapper extends BaseMapperPlus<SysDept> {
      */
     public List<Integer> selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly);
 
+	/**
+	 * 修改子元素关系
+	 *
+	 * @param depts 子元素
+	 * @return 结果
+	 */
+	public int updateDeptChildren(@Param("depts") List<SysDept> depts);
+
 }

+ 10 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java

@@ -3,6 +3,8 @@ package com.ruoyi.system.mapper;
 import com.ruoyi.common.core.page.BaseMapperPlus;
 import com.ruoyi.system.domain.SysRoleDept;
 
+import java.util.List;
+
 /**
  * 角色与部门关联表 数据层
  *
@@ -10,4 +12,12 @@ import com.ruoyi.system.domain.SysRoleDept;
  */
 public interface SysRoleDeptMapper extends BaseMapperPlus<SysRoleDept> {
 
+	/**
+	 * 批量新增角色部门信息
+	 *
+	 * @param roleDeptList 角色部门列表
+	 * @return 结果
+	 */
+	public int batchRoleDept(List<SysRoleDept> roleDeptList);
+
 }

+ 10 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java

@@ -3,6 +3,8 @@ package com.ruoyi.system.mapper;
 import com.ruoyi.common.core.page.BaseMapperPlus;
 import com.ruoyi.system.domain.SysRoleMenu;
 
+import java.util.List;
+
 /**
  * 角色与菜单关联表 数据层
  *
@@ -10,4 +12,12 @@ import com.ruoyi.system.domain.SysRoleMenu;
  */
 public interface SysRoleMenuMapper extends BaseMapperPlus<SysRoleMenu> {
 
+	/**
+	 * 批量新增角色菜单信息
+	 *
+	 * @param roleMenuList 角色菜单列表
+	 * @return 结果
+	 */
+	public int batchRoleMenu(List<SysRoleMenu> roleMenuList);
+
 }

+ 10 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java

@@ -3,6 +3,8 @@ package com.ruoyi.system.mapper;
 import com.ruoyi.common.core.page.BaseMapperPlus;
 import com.ruoyi.system.domain.SysUserPost;
 
+import java.util.List;
+
 /**
  * 用户与岗位关联表 数据层
  *
@@ -10,4 +12,12 @@ import com.ruoyi.system.domain.SysUserPost;
  */
 public interface SysUserPostMapper extends BaseMapperPlus<SysUserPost> {
 
+	/**
+	 * 批量新增用户岗位信息
+	 *
+	 * @param userPostList 用户角色列表
+	 * @return 结果
+	 */
+	public int batchUserPost(List<SysUserPost> userPostList);
+
 }

+ 10 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java

@@ -3,6 +3,8 @@ package com.ruoyi.system.mapper;
 import com.ruoyi.common.core.page.BaseMapperPlus;
 import com.ruoyi.system.domain.SysUserRole;
 
+import java.util.List;
+
 /**
  * 用户与角色关联表 数据层
  *
@@ -10,4 +12,12 @@ import com.ruoyi.system.domain.SysUserRole;
  */
 public interface SysUserRoleMapper extends BaseMapperPlus<SysUserRole> {
 
+	/**
+	 * 批量新增用户角色信息
+	 *
+	 * @param userRoleList 用户角色列表
+	 * @return 结果
+	 */
+	public int batchUserRole(List<SysUserRole> userRoleList);
+
 }

+ 2 - 4
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java

@@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.PostConstruct;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -32,9 +33,6 @@ import java.util.Map;
 @Service
 public class SysConfigServiceImpl extends ServiceImpl<SysConfigMapper, SysConfig> implements ISysConfigService {
 
-	@Autowired
-	private SysConfigMapper configMapper;
-
 	@Autowired
 	private RedisCache redisCache;
 
@@ -160,9 +158,9 @@ public class SysConfigServiceImpl extends ServiceImpl<SysConfigMapper, SysConfig
 			if (StrUtil.equals(UserConstants.YES, config.getConfigType())) {
 				throw new CustomException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey()));
 			}
-			configMapper.deleteById(configId);
 			redisCache.deleteObject(getCacheKey(config.getConfigKey()));
 		}
+		baseMapper.deleteBatchIds(Arrays.asList(configIds));
 	}
 
 	/**

+ 1 - 1
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java

@@ -244,7 +244,7 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
             child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
         }
         if (children.size() > 0) {
-            updateBatchById(children);
+            baseMapper.updateDeptChildren(children);
         }
     }
 

+ 2 - 1
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java

@@ -11,6 +11,7 @@ import com.ruoyi.system.mapper.SysDictDataMapper;
 import com.ruoyi.system.service.ISysDictDataService;
 import org.springframework.stereotype.Service;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -83,10 +84,10 @@ public class SysDictDataServiceImpl extends ServiceImpl<SysDictDataMapper, SysDi
 	public void deleteDictDataByIds(Long[] dictCodes) {
 		for (Long dictCode : dictCodes) {
 			SysDictData data = selectDictDataById(dictCode);
-			baseMapper.deleteById(dictCode);
 			List<SysDictData> dictDatas = baseMapper.selectDictDataByType(data.getDictType());
 			DictUtils.setDictCache(data.getDictType(), dictDatas);
 		}
+		baseMapper.deleteBatchIds(Arrays.asList(dictCodes));
 	}
 
 	/**

+ 2 - 4
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java

@@ -21,6 +21,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.PostConstruct;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
@@ -32,9 +33,6 @@ import java.util.Map;
 @Service
 public class SysDictTypeServiceImpl extends ServiceImpl<SysDictTypeMapper, SysDictType> implements ISysDictTypeService {
 
-	@Autowired
-	private SysDictTypeMapper dictTypeMapper;
-
 	@Autowired
 	private SysDictDataMapper dictDataMapper;
 
@@ -149,9 +147,9 @@ public class SysDictTypeServiceImpl extends ServiceImpl<SysDictTypeMapper, SysDi
 				.eq(SysDictData::getDictType, dictType.getDictType())) > 0) {
 				throw new CustomException(String.format("%1$s已分配,不能删除", dictType.getDictName()));
 			}
-			dictTypeMapper.deleteById(dictId);
 			DictUtils.removeDictCache(dictType.getDictType());
 		}
+		baseMapper.deleteBatchIds(Arrays.asList(dictIds));
 	}
 
 	/**

+ 2 - 6
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java

@@ -240,9 +240,7 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
             list.add(rm);
         }
         if (list.size() > 0) {
-            for (SysRoleMenu sysRoleMenu : list) {
-                rows += roleMenuMapper.insert(sysRoleMenu);
-            }
+			rows = roleMenuMapper.batchRoleMenu(list);
         }
         return rows;
     }
@@ -263,9 +261,7 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
             list.add(rd);
         }
         if (list.size() > 0) {
-            for (SysRoleDept sysRoleDept : list) {
-                rows += roleDeptMapper.insert(sysRoleDept);
-            }
+			rows = roleDeptMapper.batchRoleDept(list);
         }
         return rows;
     }

+ 6 - 10
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java

@@ -52,7 +52,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
     private ISysConfigService configService;
 
     @Override
-    @DataScope(deptAlias = "d", userAlias = "u")
+    @DataScope(deptAlias = "d", userAlias = "u", isUser = true)
     public TableDataInfo<SysUser> selectPageUserList(SysUser user) {
         return PageUtils.buildDataInfo(baseMapper.selectPageUserList(PageUtils.buildPage(), user));
     }
@@ -64,7 +64,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
      * @return 用户信息集合信息
      */
     @Override
-    @DataScope(deptAlias = "d", userAlias = "u")
+    @DataScope(deptAlias = "d", userAlias = "u", isUser = true)
     public List<SysUser> selectUserList(SysUser user) {
         return baseMapper.selectUserList(user);
     }
@@ -100,7 +100,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
     @Override
     public String selectUserRoleGroup(String userName) {
         List<SysRole> list = roleMapper.selectRolesByUserName(userName);
-        StringBuffer idsStr = new StringBuffer();
+        StringBuilder idsStr = new StringBuilder();
         for (SysRole role : list) {
             idsStr.append(role.getRoleName()).append(",");
         }
@@ -119,7 +119,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
     @Override
     public String selectUserPostGroup(String userName) {
         List<SysPost> list = postMapper.selectPostsByUserName(userName);
-        StringBuffer idsStr = new StringBuffer();
+        StringBuilder idsStr = new StringBuilder();
         for (SysPost post : list) {
             idsStr.append(post.getPostName()).append(",");
         }
@@ -311,9 +311,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
                 list.add(ur);
             }
             if (list.size() > 0) {
-                for (SysUserRole sysUserRole : list) {
-                    userRoleMapper.insert(sysUserRole);
-                }
+				userRoleMapper.batchUserRole(list);
             }
         }
     }
@@ -335,9 +333,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
                 list.add(up);
             }
             if (list.size() > 0) {
-                for (SysUserPost sysUserPost : list) {
-                    userPostMapper.insert(sysUserPost);
-                }
+				userPostMapper.batchUserPost(list);
             }
         }
     }

+ 16 - 1
ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml

@@ -40,7 +40,9 @@
             AND status = #{status}
         </if>
         <!-- 数据范围过滤 -->
-        ${params.dataScope}
+        <if test="params.dataScope != null and params.dataScope != ''">
+            AND ( ${params.dataScope} )
+        </if>
         order by d.parent_id, d.order_num
     </select>
 
@@ -55,4 +57,17 @@
         order by d.parent_id, d.order_num
     </select>
 
+    <update id="updateDeptChildren" parameterType="java.util.List">
+        update sys_dept set ancestors =
+        <foreach collection="depts" item="item" index="index"
+                 separator=" " open="case dept_id" close="end">
+            when #{item.deptId} then #{item.ancestors}
+        </foreach>
+        where dept_id in
+        <foreach collection="depts" item="item" index="index"
+                 separator="," open="(" close=")">
+            #{item.deptId}
+        </foreach>
+    </update>
+
 </mapper>

+ 8 - 1
ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml

@@ -9,4 +9,11 @@
         <result property="deptId" column="dept_id"/>
     </resultMap>
 
-</mapper> 
+    <insert id="batchRoleDept">
+        insert into sys_role_dept(role_id, dept_id) values
+        <foreach item="item" index="index" collection="list" separator=",">
+            (#{item.roleId},#{item.deptId})
+        </foreach>
+    </insert>
+
+</mapper>

+ 6 - 2
ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml

@@ -58,7 +58,9 @@
             and date_format(r.create_time,'%y%m%d') &lt;= date_format(#{role.params.endTime},'%y%m%d')
         </if>
         <!-- 数据范围过滤 -->
-        ${role.params.dataScope}
+        <if test="role.params.dataScope != null and role.params.dataScope != ''">
+            AND ( ${role.params.dataScope} )
+        </if>
         order by r.role_sort
     </select>
 
@@ -81,7 +83,9 @@
             and date_format(r.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
         </if>
         <!-- 数据范围过滤 -->
-        ${params.dataScope}
+        <if test="params.dataScope != null and params.dataScope != ''">
+            AND ( ${params.dataScope} )
+        </if>
         order by r.role_sort
     </select>
 

+ 8 - 1
ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml

@@ -9,4 +9,11 @@
         <result property="menuId" column="menu_id"/>
     </resultMap>
 
-</mapper> 
+    <insert id="batchRoleMenu">
+        insert into sys_role_menu(role_id, menu_id) values
+        <foreach item="item" index="index" collection="list" separator=",">
+            (#{item.roleId},#{item.menuId})
+        </foreach>
+    </insert>
+
+</mapper>

+ 6 - 2
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

@@ -106,7 +106,9 @@
             ancestors) ))
         </if>
         <!-- 数据范围过滤 -->
-        ${user.params.dataScope}
+        <if test="user.params.dataScope != null and user.params.dataScope != ''">
+            AND ( ${user.params.dataScope} )
+        </if>
     </select>
 
     <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
@@ -135,7 +137,9 @@
             ancestors) ))
         </if>
         <!-- 数据范围过滤 -->
-        ${params.dataScope}
+        <if test="params.dataScope != null and params.dataScope != ''">
+            AND ( ${params.dataScope} )
+        </if>
     </select>
 
     <select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">

+ 8 - 1
ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml

@@ -9,4 +9,11 @@
         <result property="postId" column="post_id"/>
     </resultMap>
 
-</mapper> 
+    <insert id="batchUserPost">
+        insert into sys_user_post(user_id, post_id) values
+        <foreach item="item" index="index" collection="list" separator=",">
+            (#{item.userId},#{item.postId})
+        </foreach>
+    </insert>
+
+</mapper>

+ 8 - 1
ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml

@@ -9,4 +9,11 @@
         <result property="roleId" column="role_id"/>
     </resultMap>
 
-</mapper> 
+    <insert id="batchUserRole">
+        insert into sys_user_role(user_id, role_id) values
+        <foreach item="item" index="index" collection="list" separator=",">
+            (#{item.userId},#{item.roleId})
+        </foreach>
+    </insert>
+
+</mapper>

+ 1 - 1
ruoyi-ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "ruoyi-vue-plus",
-  "version": "2.2.1",
+  "version": "2.3.0",
   "description": "RuoYi-Vue-Plus后台管理系统",
   "author": "LionLi",
   "license": "MIT",

+ 53 - 0
ruoyi-ui/src/api/demo/demo.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询测试单表列表
+export function listDemo(query) {
+  return request({
+    url: '/demo/demo/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询测试单表详细
+export function getDemo(id) {
+  return request({
+    url: '/demo/demo/' + id,
+    method: 'get'
+  })
+}
+
+// 新增测试单表
+export function addDemo(data) {
+  return request({
+    url: '/demo/demo',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改测试单表
+export function updateDemo(data) {
+  return request({
+    url: '/demo/demo',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除测试单表
+export function delDemo(id) {
+  return request({
+    url: '/demo/demo/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出测试单表
+export function exportDemo(query) {
+  return request({
+    url: '/demo/demo/export',
+    method: 'get',
+    params: query
+  })
+}

+ 53 - 0
ruoyi-ui/src/api/demo/test.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询测试列表
+export function listTest(query) {
+  return request({
+    url: '/demo/test/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询测试详细
+export function getTest(id) {
+  return request({
+    url: '/demo/test/' + id,
+    method: 'get'
+  })
+}
+
+// 新增测试
+export function addTest(data) {
+  return request({
+    url: '/demo/test',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改测试
+export function updateTest(data) {
+  return request({
+    url: '/demo/test',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除测试
+export function delTest(id) {
+  return request({
+    url: '/demo/test/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出测试
+export function exportTest(query) {
+  return request({
+    url: '/demo/test/export',
+    method: 'get',
+    params: query
+  })
+}

+ 53 - 0
ruoyi-ui/src/api/demo/tree.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询测试树表列表
+export function listTree(query) {
+  return request({
+    url: '/demo/tree/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询测试树表详细
+export function getTree(id) {
+  return request({
+    url: '/demo/tree/' + id,
+    method: 'get'
+  })
+}
+
+// 新增测试树表
+export function addTree(data) {
+  return request({
+    url: '/demo/tree',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改测试树表
+export function updateTree(data) {
+  return request({
+    url: '/demo/tree',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除测试树表
+export function delTree(id) {
+  return request({
+    url: '/demo/tree/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出测试树表
+export function exportTree(query) {
+  return request({
+    url: '/demo/tree/export',
+    method: 'get',
+    params: query
+  })
+}

+ 339 - 0
ruoyi-ui/src/views/demo/demo/index.vue

@@ -0,0 +1,339 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="key键" prop="testKey">
+        <el-input
+          v-model="queryParams.testKey"
+          placeholder="请输入key键"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="值" prop="value">
+        <el-input
+          v-model="queryParams.value"
+          placeholder="请输入值"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="创建时间">
+        <el-date-picker
+          v-model="daterangeCreateTime"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-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="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['demo:demo:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['demo:demo:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['demo:demo:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['demo:demo:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="demoList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="主键" align="center" prop="id" v-if="false"/>
+      <el-table-column label="部门id" align="center" prop="deptId" />
+      <el-table-column label="用户id" align="center" prop="userId" />
+      <el-table-column label="排序号" align="center" prop="orderNum" />
+      <el-table-column label="key键" align="center" prop="testKey" />
+      <el-table-column label="值" align="center" prop="value" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建人" align="center" prop="createBy" />
+      <el-table-column label="更新时间" align="center" prop="updateTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="更新人" align="center" prop="updateBy" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['demo:demo:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['demo:demo:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改测试单表对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="部门id" prop="deptId">
+          <el-input v-model="form.deptId" placeholder="请输入部门id" />
+        </el-form-item>
+        <el-form-item label="用户id" prop="userId">
+          <el-input v-model="form.userId" placeholder="请输入用户id" />
+        </el-form-item>
+        <el-form-item label="排序号" prop="orderNum">
+          <el-input v-model="form.orderNum" placeholder="请输入排序号" />
+        </el-form-item>
+        <el-form-item label="key键" prop="testKey">
+          <el-input v-model="form.testKey" placeholder="请输入key键" />
+        </el-form-item>
+        <el-form-item label="值" prop="value">
+          <el-input v-model="form.value" placeholder="请输入值" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listDemo, getDemo, delDemo, addDemo, updateDemo, exportDemo } from "@/api/demo/demo";
+
+export default {
+  name: "Demo",
+  components: {
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 测试单表表格数据
+      demoList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 创建时间时间范围
+      daterangeCreateTime: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        testKey: undefined,
+        value: undefined,
+        createTime: undefined,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        testKey: [
+          { required: true, message: "key键不能为空", trigger: "blur" }
+        ],
+        value: [
+          { required: true, message: "值不能为空", trigger: "blur" }
+        ],
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询测试单表列表 */
+    getList() {
+      this.loading = true;
+      this.queryParams.params = {};
+      if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {
+        this.queryParams.params["beginCreateTime"] = this.daterangeCreateTime[0];
+        this.queryParams.params["endCreateTime"] = this.daterangeCreateTime[1];
+      }
+      listDemo(this.queryParams).then(response => {
+        this.demoList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: undefined,
+        deptId: undefined,
+        userId: undefined,
+        orderNum: undefined,
+        testKey: undefined,
+        value: undefined,
+        version: undefined,
+        createTime: undefined,
+        createBy: undefined,
+        updateTime: undefined,
+        updateBy: undefined,
+        delFlag: undefined
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.daterangeCreateTime = [];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加测试单表";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getDemo(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改测试单表";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateDemo(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addDemo(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除测试单表编号为"' + ids + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return delDemo(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      })
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有测试单表数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportDemo(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      })
+    }
+  }
+};
+</script>

+ 361 - 0
ruoyi-ui/src/views/demo/test/index.vue

@@ -0,0 +1,361 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="key键" prop="testKey">
+        <el-input
+          v-model="queryParams.testKey"
+          placeholder="请输入key键"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="值" prop="value">
+        <el-input
+          v-model="queryParams.value"
+          placeholder="请输入值"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="版本" prop="version">
+        <el-input
+          v-model="queryParams.version"
+          placeholder="请输入版本"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="创建时间">
+        <el-date-picker
+          v-model="daterangeCreateTime"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item label="删除标志" prop="deleted">
+        <el-input
+          v-model="queryParams.deleted"
+          placeholder="请输入删除标志"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="父id" prop="parentId">
+        <el-input
+          v-model="queryParams.parentId"
+          placeholder="请输入父id"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="排序号" prop="orderNum">
+        <el-input
+          v-model="queryParams.orderNum"
+          placeholder="请输入排序号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-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="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['demo:test:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['demo:test:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['demo:test:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['demo:test:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="testList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="主键" align="center" prop="id" v-if="false"/>
+      <el-table-column label="key键" align="center" prop="testKey" />
+      <el-table-column label="值" align="center" prop="value" />
+      <el-table-column label="版本" align="center" prop="version" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="删除标志" align="center" prop="deleted" />
+      <el-table-column label="父id" align="center" prop="parentId" />
+      <el-table-column label="排序号" align="center" prop="orderNum" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['demo:test:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['demo:test:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改测试对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="key键" prop="testKey">
+          <el-input v-model="form.testKey" placeholder="请输入key键" />
+        </el-form-item>
+        <el-form-item label="值" prop="value">
+          <el-input v-model="form.value" placeholder="请输入值" />
+        </el-form-item>
+        <el-form-item label="版本" prop="version">
+          <el-input v-model="form.version" placeholder="请输入版本" />
+        </el-form-item>
+        <el-form-item label="删除标志" prop="deleted">
+          <el-input v-model="form.deleted" placeholder="请输入删除标志" />
+        </el-form-item>
+        <el-form-item label="父id" prop="parentId">
+          <el-input v-model="form.parentId" placeholder="请输入父id" />
+        </el-form-item>
+        <el-form-item label="排序号" prop="orderNum">
+          <el-input v-model="form.orderNum" placeholder="请输入排序号" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listTest, getTest, delTest, addTest, updateTest, exportTest } from "@/api/demo/test";
+
+export default {
+  name: "Test",
+  components: {
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 测试表格数据
+      testList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 创建时间时间范围
+      daterangeCreateTime: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        testKey: undefined,
+        value: undefined,
+        version: undefined,
+        createTime: undefined,
+        deleted: undefined,
+        parentId: undefined,
+        orderNum: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询测试列表 */
+    getList() {
+      this.loading = true;
+      this.queryParams.params = {};
+      if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {
+        this.queryParams.params["beginCreateTime"] = this.daterangeCreateTime[0];
+        this.queryParams.params["endCreateTime"] = this.daterangeCreateTime[1];
+      }
+      listTest(this.queryParams).then(response => {
+        this.testList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: undefined,
+        testKey: undefined,
+        value: undefined,
+        version: undefined,
+        createTime: undefined,
+        deleted: undefined,
+        parentId: undefined,
+        orderNum: undefined
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.daterangeCreateTime = [];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加测试";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getTest(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改测试";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateTest(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addTest(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除测试编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delTest(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        })
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有测试数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return exportTest(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+        })
+    }
+  }
+};
+</script>

+ 282 - 0
ruoyi-ui/src/views/demo/tree/index.vue

@@ -0,0 +1,282 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="树节点名" prop="treeName">
+        <el-input
+          v-model="queryParams.treeName"
+          placeholder="请输入树节点名"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="创建时间">
+        <el-date-picker
+          v-model="daterangeCreateTime"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-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="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['demo:tree:add']"
+        >新增</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table
+      v-loading="loading"
+      :data="treeList"
+      row-key="id"
+      default-expand-all
+      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+    >
+      <el-table-column label="父id" prop="parentId" />
+      <el-table-column label="部门id" align="center" prop="deptId" />
+      <el-table-column label="用户id" align="center" prop="userId" />
+      <el-table-column label="树节点名" align="center" prop="treeName" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['demo:tree:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-plus"
+            @click="handleAdd(scope.row)"
+            v-hasPermi="['demo:tree:add']"
+          >新增</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['demo:tree:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 添加或修改测试树表对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="父id" prop="parentId">
+          <treeselect v-model="form.parentId" :options="treeOptions" :normalizer="normalizer" placeholder="请选择父id" />
+        </el-form-item>
+        <el-form-item label="部门id" prop="deptId">
+          <el-input v-model="form.deptId" placeholder="请输入部门id" />
+        </el-form-item>
+        <el-form-item label="用户id" prop="userId">
+          <el-input v-model="form.userId" placeholder="请输入用户id" />
+        </el-form-item>
+        <el-form-item label="树节点名" prop="treeName">
+          <el-input v-model="form.treeName" placeholder="请输入树节点名" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listTree, getTree, delTree, addTree, updateTree, exportTree } from "@/api/demo/tree";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+  name: "Tree",
+  components: {
+    Treeselect
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 测试树表表格数据
+      treeList: [],
+      // 测试树表树选项
+      treeOptions: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 创建时间时间范围
+      daterangeCreateTime: [],
+      // 查询参数
+      queryParams: {
+        treeName: null,
+        createTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        treeName: [
+          { required: true, message: "树节点名不能为空", trigger: "blur" }
+        ],
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询测试树表列表 */
+    getList() {
+      this.loading = true;
+      this.queryParams.params = {};
+      if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {
+        this.queryParams.params["beginCreateTime"] = this.daterangeCreateTime[0];
+        this.queryParams.params["endCreateTime"] = this.daterangeCreateTime[1];
+      }
+      listTree(this.queryParams).then(response => {
+        this.treeList = this.handleTree(response.data, "id", "parentId");
+        this.loading = false;
+      });
+    },
+    /** 转换测试树表数据结构 */
+    normalizer(node) {
+      if (node.children && !node.children.length) {
+        delete node.children;
+      }
+      return {
+        id: node.id,
+        label: node.treeName,
+        children: node.children
+      };
+    },
+    /** 查询测试树表下拉树结构 */
+    getTreeselect() {
+      listTree().then(response => {
+        this.treeOptions = [];
+        const data = { id: 0, treeName: '顶级节点', children: [] };
+        data.children = this.handleTree(response.data, "id", "parentId");
+        this.treeOptions.push(data);
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        parentId: null,
+        deptId: null,
+        userId: null,
+        treeName: null,
+        version: null,
+        createTime: null,
+        createBy: null,
+        updateTime: null,
+        updateBy: null,
+        delFlag: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.daterangeCreateTime = [];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 新增按钮操作 */
+    handleAdd(row) {
+      this.reset();
+      this.getTreeselect();
+      if (row != null && row.id) {
+        this.form.parentId = row.id;
+      } else {
+        this.form.parentId = 0;
+      }
+      this.open = true;
+      this.title = "添加测试树表";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      this.getTreeselect();
+      if (row != null) {
+        this.form.parentId = row.id;
+      }
+      getTree(row.id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改测试树表";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateTree(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addTree(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      this.$confirm('是否确认删除测试树表编号为"' + row.id + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return delTree(row.id);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      })
+    }
+  }
+};
+</script>

+ 10 - 0
ruoyi-ui/src/views/index.vue

@@ -80,6 +80,16 @@
             <span>更新日志</span>
           </div>
           <el-collapse accordion>
+            <el-collapse-item title="v2.3.0 - 2021-6-1">
+              <ol>
+                <li>add 升级 luttuce 为 redisson 性能更强 工具更全</li>
+                <li>add 增加测试数据sql文件</li>
+                <li>add 增加demo模块 单表演示案例(包含数据权限)</li>
+                <li>update 完美修复 数据权限功能(支持单表多表过滤)</li>
+                <li>update 优化代码生成模板</li>
+                <li>update 优化 system 模块 批量操作性能</li>
+              </ol>
+            </el-collapse-item>
             <el-collapse-item title="v2.2.1 - 2021-5-29">
               <ol>
                 <li>add 增加 security 权限框架 @Async 异步注解配置</li>

+ 67 - 0
ry.bat

@@ -0,0 +1,67 @@
+@echo off
+
+rem jar平级目录
+set AppName=ruoyi-admin.jar
+
+rem JVM参数
+set JVM_OPTS="-Dname=%AppName%  -Duser.timezone=Asia/Shanghai -Xms512M -Xmx512M -XX:PermSize=256M -XX:MaxPermSize=512M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps  -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
+
+
+ECHO. 
+	ECHO.  [1] 启动%AppName%
+	ECHO.  [2] 关闭%AppName%
+	ECHO.  [3] 重启%AppName%
+	ECHO.  [4] 启动状态 %AppName%
+	ECHO.  [5] 退 出 
+ECHO. 
+
+ECHO.请输入选择项目的序号:
+set /p ID=
+	IF "%id%"=="1" GOTO start 
+	IF "%id%"=="2" GOTO stop 
+	IF "%id%"=="3" GOTO restart 
+	IF "%id%"=="4" GOTO status
+	IF "%id%"=="5" EXIT
+PAUSE 
+:start
+    for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
+		set pid=%%a
+		set image_name=%%b
+	)
+	if  defined pid (
+		echo %%is running 
+		PAUSE 
+	) 
+
+start javaw -jar %JAVA_OPTS% ruoyi-admin.jar
+
+echo  starting……
+echo  Start %AppName% success...
+goto:eof
+
+rem 函数stop通过jps命令查找pid并结束进程
+:stop
+	for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
+		set pid=%%a
+		set image_name=%%b
+	)
+	if not defined pid (echo process %AppName% does not exists) else (
+		echo prepare to kill %image_name%
+		echo start kill %pid% ...
+		rem 根据进程ID,kill进程
+		taskkill /f /pid %pid%
+	)
+goto:eof
+:restart
+	call :stop
+    call :start
+goto:eof
+:status
+	for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
+		set pid=%%a
+		set image_name=%%b
+	)
+	if not defined pid (echo process %AppName% is dead ) else (
+		echo %image_name% is running
+	)
+goto:eof

+ 8 - 4
ry.sh

@@ -1,8 +1,12 @@
-#!/bin/bash
-
+#!/bin/sh
+# author ruoyi
+# ./ry.sh start 启动
+# ./ry.sh stop 停止
+# ./ry.sh restart 重启
+# ./ry.sh start 状态
 AppName=ruoyi-admin.jar
 
-#JVM参数
+# JVM参数
 JVM_OPTS="-Dname=$AppName  -Duser.timezone=Asia/Shanghai -Xms512M -Xmx512M -XX:PermSize=256M -XX:MaxPermSize=512M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps  -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
 APP_HOME=`pwd`
 LOG_PATH=$APP_HOME/logs/$AppName.log
@@ -34,7 +38,7 @@ function start()
 function stop()
 {
     echo "Stop $AppName"
-	
+
 	PID=""
 	query(){
 		PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`

+ 171 - 0
sql/test.sql

@@ -0,0 +1,171 @@
+DROP TABLE if EXISTS test_demo;
+CREATE TABLE test_demo
+(
+    id          int(0) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    dept_id     int(0) NULL DEFAULT NULL COMMENT '部门id',
+    user_id     int(0) NULL DEFAULT NULL COMMENT '用户id',
+    order_num   int(0) NULL DEFAULT 0 COMMENT '排序号',
+    test_key    varchar(255) NULL DEFAULT NULL COMMENT 'key键',
+    value       varchar(255) NULL DEFAULT NULL COMMENT '值',
+    version     int(0) NULL DEFAULT 0 COMMENT '版本',
+    create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+    create_by   varchar(64) NULL DEFAULT NULL COMMENT '创建人',
+    update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+    update_by   varchar(64) NULL DEFAULT NULL COMMENT '更新人',
+    del_flag    int(0) NULL DEFAULT NULL COMMENT '删除标志',
+    PRIMARY KEY (id) USING BTREE
+) ENGINE = InnoDB COMMENT = '测试单表';
+
+DROP TABLE if EXISTS test_tree;
+CREATE TABLE test_tree
+(
+    id          int(0) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    parent_id   int(0) NULL DEFAULT 0 COMMENT '父id',
+    dept_id     int(0) NULL DEFAULT NULL COMMENT '部门id',
+    user_id     int(0) NULL DEFAULT NULL COMMENT '用户id',
+    tree_name   varchar(255) NULL DEFAULT NULL COMMENT '值',
+    version     int(0) NULL DEFAULT 0 COMMENT '版本',
+    create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+    create_by   varchar(64) NULL DEFAULT NULL COMMENT '创建人',
+    update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+    update_by   varchar(64) NULL DEFAULT NULL COMMENT '更新人',
+    del_flag    int(0) NULL DEFAULT NULL COMMENT '删除标志',
+    PRIMARY KEY (id) USING BTREE
+) ENGINE = InnoDB COMMENT = '测试树表';
+
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (5, '测试菜单', 0, 5, 'demo', NULL, 1, 0, 'M', '0', '0', NULL, 'star', 'admin', '2021-05-30 00:34:26', NULL, NULL, '');
+
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 'admin', '2021-05-30 00:39:23', '', NULL, '测试单表菜单');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 'admin', '2021-05-30 00:39:23', '', NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 'admin', '2021-05-30 00:39:23', '', NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 'admin', '2021-05-30 00:39:23', '', NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 'admin', '2021-05-30 00:39:23', '', NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 'admin', '2021-05-30 00:39:23', '', NULL, '');
+
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 'admin', '2021-05-30 00:39:30', '', NULL, '测试树表菜单');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 'admin', '2021-05-30 00:39:30', '', NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 'admin', '2021-05-30 00:39:30', '', NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 'admin', '2021-05-30 00:39:30', '', NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 'admin', '2021-05-30 00:39:30', '', NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 'admin', '2021-05-30 00:39:30', '', NULL, '');
+
+INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (3, '本部门及以下', 'test1', 3, '4', 1, 1, '0', '0', 'admin', '2021-05-08 22:31:37', 'admin', '2021-05-08 22:32:03', NULL);
+INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (4, '仅本人', 'test2', 4, '5', 1, 1, '0', '0', 'admin', '2021-05-30 01:14:52', 'admin', '2021-05-30 01:18:38', NULL);
+
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 5);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 100);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 101);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 102);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 103);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 104);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 105);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 106);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 107);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 108);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 500);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 501);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1001);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1002);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1003);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1004);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1005);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1006);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1007);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1008);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1009);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1010);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1011);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1012);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1013);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1014);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1015);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1016);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1017);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1018);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1019);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1020);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1021);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1022);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1023);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1024);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1025);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1026);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1027);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1028);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1029);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1030);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1031);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1032);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1033);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1034);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1035);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1036);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1037);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1038);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1039);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1040);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1041);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1042);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1043);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1044);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1045);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1500);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1501);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1502);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1503);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1504);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1505);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1506);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1507);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1508);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1509);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1510);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1511);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 5);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1500);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1501);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1502);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1503);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1504);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1505);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1506);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1507);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1508);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1509);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1510);
+INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1511);
+
+INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (3, 108, 'test', '本部门及以下 密码666', '00', '', '', '0', '', '$2a$10$M6tZRpUZbWKq11O/z6YISePQc./Jhru8E18mmVJTr9aV8whzfjacC', '0', '0', '127.0.0.1', '2021-05-30 02:00:37', 'admin', '2021-04-22 09:50:41', 'test', '2021-05-30 02:00:37', NULL);
+INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (4, 102, 'test1', '仅本人 密码666', '00', '', '', '0', '', '$2a$10$yBSXp5Ba1m402cxXTPSy4eXUO8CXCGvXfquNVP/XMWwZ8nf9GaoMy', '0', '0', '127.0.0.1', '2021-05-30 01:48:03', 'admin', '2021-05-30 01:16:02', 'test1', '2021-05-30 01:48:03', NULL);
+
+INSERT INTO sys_user_role(user_id, role_id) VALUES (3, 3);
+INSERT INTO sys_user_role(user_id, role_id) VALUES (4, 4);
+
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 102, 4, 1, '测试数据权限', '测试', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 102, 3, 2, '子节点1', '111', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 102, 3, 3, '子节点2', '222', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 108, 4, 4, '测试数据', 'demo', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 108, 3, 13, '子节点11', '1111', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 108, 3, 12, '子节点22', '2222', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 108, 3, 11, '子节点33', '3333', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 108, 3, 10, '子节点44', '4444', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 108, 3, 9, '子节点55', '5555', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 108, 3, 8, '子节点66', '6666', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 108, 3, 7, '子节点77', '7777', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 108, 3, 6, '子节点88', '8888', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 108, 3, 5, '子节点99', '9999', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 0, 102, 4, '测试数据权限', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 1, 102, 3, '子节点1', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 2, 102, 3, '子节点2', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 0, 108, 4, '测试树1', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 4, 108, 3, '子节点11', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 4, 108, 3, '子节点22', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 4, 108, 3, '子节点33', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 5, 108, 3, '子节点44', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 6, 108, 3, '子节点55', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 7, 108, 3, '子节点66', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 7, 108, 3, '子节点77', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 10, 108, 3, '子节点88', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);
+INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 10, 108, 3, '子节点99', 0, '2021-06-01 10:00:00', 'admin', NULL, NULL, 0);