Browse Source

:tada: 3.2.0.RELEASE 新增灵活数据权限特性

smallchill 3 years ago
parent
commit
3468b0f694
48 changed files with 1539 additions and 56 deletions
  1. 4 9
      README.md
  2. 1 1
      blade-core-boot/pom.xml
  3. 45 0
      blade-core-boot/src/main/java/org/springblade/core/boot/tenant/BladeTenantInterceptor.java
  4. 20 8
      blade-core-boot/src/main/java/org/springblade/core/boot/tenant/TenantConfiguration.java
  5. 1 1
      blade-core-boot/src/main/resources/bootstrap.yml
  6. 1 1
      blade-core-cloud/pom.xml
  7. 37 0
      blade-core-datascope/pom.xml
  8. 60 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/annotation/DataAuth.java
  9. 64 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/config/DataScopeConfiguration.java
  10. 64 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/constant/DataScopeConstant.java
  11. 75 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/enums/DataScopeEnum.java
  12. 35 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/exception/DataScopeException.java
  13. 83 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/handler/BladeDataScopeHandler.java
  14. 114 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/handler/BladeScopeModelHandler.java
  15. 39 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/handler/DataScopeHandler.java
  16. 54 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/handler/ScopeModelHandler.java
  17. 138 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/interceptor/DataScopeInterceptor.java
  18. 67 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/model/DataScopeModel.java
  19. 48 0
      blade-core-datascope/src/main/java/org/springblade/core/datascope/props/DataScopeProperties.java
  20. 1 1
      blade-core-develop/pom.xml
  21. 1 1
      blade-core-launch/pom.xml
  22. 1 1
      blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java
  23. 1 0
      blade-core-launch/src/main/java/org/springblade/core/launch/constant/TokenConstant.java
  24. 1 1
      blade-core-log/pom.xml
  25. 1 1
      blade-core-mybatis/pom.xml
  26. 45 10
      blade-core-mybatis/src/main/java/org/springblade/core/mp/config/MybatisPlusConfiguration.java
  27. 56 0
      blade-core-mybatis/src/main/java/org/springblade/core/mp/intercept/QueryInterceptor.java
  28. 48 0
      blade-core-mybatis/src/main/java/org/springblade/core/mp/plugins/BladePaginationInterceptor.java
  29. 50 0
      blade-core-mybatis/src/main/java/org/springblade/core/mp/plugins/QueryInterceptorExecutor.java
  30. 1 1
      blade-core-mybatis/src/main/java/org/springblade/core/mp/props/MybatisPlusProperties.java
  31. 1 1
      blade-core-oss/pom.xml
  32. 1 1
      blade-core-report/pom.xml
  33. 1 1
      blade-core-secure/pom.xml
  34. 5 0
      blade-core-secure/src/main/java/org/springblade/core/secure/BladeUser.java
  35. 3 0
      blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java
  36. 1 1
      blade-core-social/pom.xml
  37. 1 1
      blade-core-swagger/pom.xml
  38. 1 1
      blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java
  39. 1 1
      blade-core-test/pom.xml
  40. 1 1
      blade-core-tool/pom.xml
  41. 2 0
      blade-core-tool/src/main/java/org/springblade/core/tool/config/XssConfiguration.java
  42. 5 0
      blade-core-tool/src/main/java/org/springblade/core/tool/constant/BladeConstant.java
  43. 165 0
      blade-core-tool/src/main/java/org/springblade/core/tool/utils/CacheUtil.java
  44. 4 0
      blade-core-tool/src/main/java/org/springblade/core/tool/utils/DateUtil.java
  45. 143 0
      blade-core-tool/src/main/java/org/springblade/core/tool/utils/PlaceholderUtil.java
  46. 35 0
      blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java
  47. 1 1
      blade-core-transaction/pom.xml
  48. 12 11
      pom.xml

+ 4 - 9
README.md

@@ -1,7 +1,7 @@
  <p align="center">
   <img src="https://img.shields.io/badge/license-LGPL%20v3-blue.svg" alt="Build Status">
    <img src="https://img.shields.io/badge/Spring%20Cloud-2020-blue.svg" alt="Coverage Status">
-   <img src="https://img.shields.io/badge/Spring%20Boot-2.5.2-blue.svg" alt="Downloads">
+   <img src="https://img.shields.io/badge/Spring%20Boot-2.5.6-blue.svg" alt="Downloads">
  </p>  
 
 ## SpringBlade微服务开发平台
@@ -45,7 +45,8 @@ blade-tool
 * 交流二群:`751253339`(满)
 * 交流三群:`784729540`(满)
 * 交流四群:`1034621754`(满)
-* 交流五群:`946350912`
+* 交流五群:`946350912`(满)
+* 交流六群: `511624269`
 
 ## 在线演示
 * Sword演示地址:[https://sword.bladex.vip](https://sword.bladex.vip)
@@ -166,10 +167,4 @@ LGPL是GPL的一个为主要为类库使用设计的开源协议。和GPL要求
 ## 鸣谢
 * mica([Mica](https://github.com/lets-mica/mica))
 * 如梦技术([DreamLu](https://www.dreamlu.net/))
-* pigx([Pig Microservice](https://www.pig4cloud.com/zh-cn/))
-* avue([avue](https://avue.top/))
-* gitee.ltd([gitee.ltd](https://gitee.ltd/))
-* 鲸宵(<a href="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/jx.png" target="_blank">鲸宵</a>)
-
-## 关注我们
-![](https://images.gitee.com/uploads/images/2019/0330/065148_f0ada806_410595.jpeg)
+* avue([avue](https://avuejs.com/))

+ 1 - 1
blade-core-boot/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.springblade</groupId>
         <artifactId>blade-tool</artifactId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 45 - 0
blade-core-boot/src/main/java/org/springblade/core/boot/tenant/BladeTenantInterceptor.java

@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.boot.tenant;
+
+import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
+import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * 租户拦截器
+ *
+ * @author Chill
+ */
+@Data
+@ToString(callSuper = true)
+@EqualsAndHashCode(callSuper = true)
+public class BladeTenantInterceptor extends TenantLineInnerInterceptor {
+
+	/**
+	 * 租户处理器
+	 */
+	private TenantLineHandler tenantLineHandler;
+
+	@Override
+	public void setTenantLineHandler(TenantLineHandler tenantLineHandler) {
+		super.setTenantLineHandler(tenantLineHandler);
+		this.tenantLineHandler = tenantLineHandler;
+	}
+
+}

+ 20 - 8
blade-core-boot/src/main/java/org/springblade/core/boot/tenant/TenantConfiguration.java

@@ -16,13 +16,15 @@
 package org.springblade.core.boot.tenant;
 
 import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
+import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
 import lombok.AllArgsConstructor;
-import org.springblade.core.boot.config.MybatisPlusConfiguration;
+import org.springblade.core.mp.config.MybatisPlusConfiguration;
 import org.springframework.boot.autoconfigure.AutoConfigureBefore;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
 
 /**
  * 多租户配置类
@@ -36,19 +38,29 @@ import org.springframework.context.annotation.Configuration;
 public class TenantConfiguration {
 
 	/**
-	 * 多租户配置类
+	 * 自定义多租户处理器
+	 *
+	 * @param tenantProperties 多租户配置类
+	 * @return TenantHandler
 	 */
-	private final BladeTenantProperties properties;
+	@Bean
+	@Primary
+	public TenantLineHandler bladeTenantHandler(BladeTenantProperties tenantProperties) {
+		return new BladeTenantHandler(tenantProperties);
+	}
 
 	/**
-	 * 自定义租户处理器
+	 * 自定义租户拦截
 	 *
-	 * @return TenantHandler
+	 * @param tenantHandler 多租户处理器
+	 * @return BladeTenantInterceptor
 	 */
 	@Bean
-	@ConditionalOnMissingBean(TenantLineHandler.class)
-	public TenantLineHandler bladeTenantHandler() {
-		return new BladeTenantHandler(properties);
+	@Primary
+	public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantLineHandler tenantHandler) {
+		BladeTenantInterceptor tenantInterceptor = new BladeTenantInterceptor();
+		tenantInterceptor.setTenantLineHandler(tenantHandler);
+		return tenantInterceptor;
 	}
 
 	/**

+ 1 - 1
blade-core-boot/src/main/resources/bootstrap.yml

@@ -101,7 +101,7 @@ mybatis-plus:
 swagger:
   title: SpringBlade 接口文档系统
   description: SpringBlade 接口文档系统
-  version: 3.1.0
+  version: 3.2.0
   license: Powered By SpringBlade
   licenseUrl: https://bladex.vip
   terms-of-service-url: https://bladex.vip

+ 1 - 1
blade-core-cloud/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 37 - 0
blade-core-datascope/pom.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>blade-tool</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>3.2.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-core-datascope</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${blade.tool.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!--Mybatis-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-mybatis</artifactId>
+            <version>${blade.tool.version}</version>
+        </dependency>
+        <!--Jdbc-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>tomcat-jdbc</artifactId>
+                    <groupId>org.apache.tomcat</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+
+</project>

+ 60 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/annotation/DataAuth.java

@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.annotation;
+
+import org.springblade.core.datascope.constant.DataScopeConstant;
+import org.springblade.core.datascope.enums.DataScopeEnum;
+
+import java.lang.annotation.*;
+
+/**
+ * 数据权限定义
+ *
+ * @author Chill
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface DataAuth {
+
+	/**
+	 * 资源编号
+	 */
+	String code() default "";
+
+	/**
+	 * 数据权限对应字段
+	 */
+	String column() default DataScopeConstant.DEFAULT_COLUMN;
+
+	/**
+	 * 数据权限规则
+	 */
+	DataScopeEnum type() default DataScopeEnum.ALL;
+
+	/**
+	 * 可见字段
+	 */
+	String field() default "*";
+
+	/**
+	 * 数据权限规则值域
+	 */
+	String value() default "";
+
+}
+

+ 64 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/config/DataScopeConfiguration.java

@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.config;
+
+import lombok.AllArgsConstructor;
+import org.springblade.core.datascope.handler.BladeDataScopeHandler;
+import org.springblade.core.datascope.handler.BladeScopeModelHandler;
+import org.springblade.core.datascope.handler.DataScopeHandler;
+import org.springblade.core.datascope.handler.ScopeModelHandler;
+import org.springblade.core.datascope.interceptor.DataScopeInterceptor;
+import org.springblade.core.datascope.props.DataScopeProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+/**
+ * 数据权限配置类
+ *
+ * @author Chill
+ */
+@Configuration(proxyBeanMethods = false)
+@AllArgsConstructor
+@EnableConfigurationProperties(DataScopeProperties.class)
+public class DataScopeConfiguration {
+
+	private final JdbcTemplate jdbcTemplate;
+
+	@Bean
+	@ConditionalOnMissingBean(ScopeModelHandler.class)
+	public ScopeModelHandler scopeModelHandler() {
+		return new BladeScopeModelHandler(jdbcTemplate);
+	}
+
+	@Bean
+	@ConditionalOnBean(ScopeModelHandler.class)
+	@ConditionalOnMissingBean(DataScopeHandler.class)
+	public DataScopeHandler dataScopeHandler(ScopeModelHandler scopeModelHandler) {
+		return new BladeDataScopeHandler(scopeModelHandler);
+	}
+
+	@Bean
+	@ConditionalOnBean(DataScopeHandler.class)
+	@ConditionalOnMissingBean(DataScopeInterceptor.class)
+	public DataScopeInterceptor interceptor(DataScopeHandler dataScopeHandler, DataScopeProperties dataScopeProperties) {
+		return new DataScopeInterceptor(dataScopeHandler, dataScopeProperties);
+	}
+
+}

+ 64 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/constant/DataScopeConstant.java

@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.constant;
+
+import org.springblade.core.tool.utils.StringUtil;
+
+/**
+ * 数据权限常量
+ *
+ * @author Chill
+ */
+public interface DataScopeConstant {
+
+	String DEFAULT_COLUMN = "create_dept";
+
+	/**
+	 * 获取部门数据
+	 */
+	String DATA_BY_DEPT = "select id from blade_dept where ancestors like concat(concat('%', ?),'%') and is_deleted = 0";
+
+	/**
+	 * 根据resourceCode获取数据权限配置
+	 */
+	String DATA_BY_CODE = "select resource_code, scope_column, scope_field, scope_type, scope_value from blade_scope_data where resource_code = ?";
+
+	/**
+	 * 根据mapperId获取数据权限配置
+	 *
+	 * @param size 数量
+	 * @return String
+	 */
+	static String dataByMapper(int size) {
+		return "select resource_code, scope_column, scope_field, scope_type, scope_value from blade_scope_data where scope_class = ? and id in (select scope_id from blade_role_scope where role_id in (" + buildHolder(size) + "))";
+	}
+
+	/**
+	 * 获取Sql占位符
+	 *
+	 * @param size 数量
+	 * @return String
+	 */
+	static String buildHolder(int size) {
+		StringBuilder builder = StringUtil.builder();
+		for (int i = 0; i < size; i++) {
+			builder.append("?,");
+		}
+		return StringUtil.removeSuffix(builder.toString(), ",");
+	}
+
+
+}

+ 75 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/enums/DataScopeEnum.java

@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 数据权限类型
+ *
+ * @author lengleng, Chill
+ */
+@Getter
+@AllArgsConstructor
+public enum DataScopeEnum {
+	/**
+	 * 全部数据
+	 */
+	ALL(1, "全部"),
+
+	/**
+	 * 本人可见
+	 */
+	OWN(2, "本人可见"),
+
+	/**
+	 * 所在机构可见
+	 */
+	OWN_DEPT(3, "所在机构可见"),
+
+	/**
+	 * 所在机构及子级可见
+	 */
+	OWN_DEPT_CHILD(4, "所在机构及子级可见"),
+
+	/**
+	 * 自定义
+	 */
+	CUSTOM(5, "自定义");
+
+	/**
+	 * 类型
+	 */
+	private final int type;
+	/**
+	 * 描述
+	 */
+	private final String description;
+
+	public static DataScopeEnum of(Integer dataScopeType) {
+		if (dataScopeType == null) {
+			return null;
+		}
+		DataScopeEnum[] values = DataScopeEnum.values();
+		for (DataScopeEnum scopeTypeEnum : values) {
+			if (scopeTypeEnum.type == dataScopeType) {
+				return scopeTypeEnum;
+			}
+		}
+		return null;
+	}
+}

+ 35 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/exception/DataScopeException.java

@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.exception;
+
+/**
+ * 数据权限异常
+ *
+ * @author L.cm
+ */
+public class DataScopeException extends RuntimeException {
+
+	public DataScopeException() {
+	}
+
+	public DataScopeException(String message) {
+		super(message);
+	}
+
+	public DataScopeException(Throwable cause) {
+		super(cause);
+	}
+}

+ 83 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/handler/BladeDataScopeHandler.java

@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.handler;
+
+import lombok.RequiredArgsConstructor;
+import org.springblade.core.datascope.enums.DataScopeEnum;
+import org.springblade.core.datascope.model.DataScopeModel;
+import org.springblade.core.secure.BladeUser;
+import org.springblade.core.tool.constant.RoleConstant;
+import org.springblade.core.tool.utils.BeanUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.PlaceholderUtil;
+import org.springblade.core.tool.utils.StringUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 默认数据权限规则
+ *
+ * @author Chill
+ */
+@RequiredArgsConstructor
+public class BladeDataScopeHandler implements DataScopeHandler {
+
+	private final ScopeModelHandler scopeModelHandler;
+
+	@Override
+	public String sqlCondition(String mapperId, DataScopeModel dataScope, BladeUser bladeUser, String originalSql) {
+
+		//数据权限资源编号
+		String code = dataScope.getResourceCode();
+
+		//根据mapperId从数据库中获取对应模型
+		DataScopeModel dataScopeDb = scopeModelHandler.getDataScopeByMapper(mapperId, bladeUser.getRoleId());
+
+		//mapperId配置未取到则从数据库中根据资源编号获取
+		if (dataScopeDb == null && StringUtil.isNotBlank(code)) {
+			dataScopeDb = scopeModelHandler.getDataScopeByCode(code);
+		}
+
+		//未从数据库找到对应配置则采用默认
+		dataScope = (dataScopeDb != null) ? dataScopeDb : dataScope;
+
+		//判断数据权限类型并组装对应Sql
+		Integer scopeRule = Objects.requireNonNull(dataScope).getScopeType();
+		DataScopeEnum scopeTypeEnum = DataScopeEnum.of(scopeRule);
+		List<Long> ids = new ArrayList<>();
+		String whereSql = "where scope.{} in ({})";
+		if (DataScopeEnum.ALL == scopeTypeEnum || StringUtil.containsAny(bladeUser.getRoleName(), RoleConstant.ADMIN)) {
+			return null;
+		} else if (DataScopeEnum.CUSTOM == scopeTypeEnum) {
+			whereSql = PlaceholderUtil.getDefaultResolver().resolveByMap(dataScope.getScopeValue(), BeanUtil.toMap(bladeUser));
+		} else if (DataScopeEnum.OWN == scopeTypeEnum) {
+			ids.add(bladeUser.getUserId());
+		} else if (DataScopeEnum.OWN_DEPT == scopeTypeEnum) {
+			ids.addAll(Func.toLongList(bladeUser.getDeptId()));
+		} else if (DataScopeEnum.OWN_DEPT_CHILD == scopeTypeEnum) {
+			List<Long> deptIds = Func.toLongList(bladeUser.getDeptId());
+			ids.addAll(deptIds);
+			deptIds.forEach(deptId -> {
+				List<Long> deptIdList = scopeModelHandler.getDeptAncestors(deptId);
+				ids.addAll(deptIdList);
+			});
+		}
+		return StringUtil.format("select {} from ({}) scope " + whereSql, Func.toStr(dataScope.getScopeField(), "*"), originalSql, dataScope.getScopeColumn(), StringUtil.join(ids));
+	}
+
+}

+ 114 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/handler/BladeScopeModelHandler.java

@@ -0,0 +1,114 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.handler;
+
+import lombok.RequiredArgsConstructor;
+import org.springblade.core.datascope.constant.DataScopeConstant;
+import org.springblade.core.datascope.model.DataScopeModel;
+import org.springblade.core.tool.utils.*;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.springblade.core.tool.utils.CacheUtil.SYS_CACHE;
+
+
+/**
+ * BladeScopeModelHandler
+ *
+ * @author Chill
+ */
+@RequiredArgsConstructor
+public class BladeScopeModelHandler implements ScopeModelHandler {
+
+	private static final String SCOPE_CACHE_CODE = "dataScope:code:";
+	private static final String SCOPE_CACHE_CLASS = "dataScope:class:";
+	private static final String DEPT_CACHE_ANCESTORS = "dept:ancestors:";
+	private static final DataScopeModel SEARCHED_DATA_SCOPE_MODEL = new DataScopeModel(Boolean.TRUE);
+
+	private final JdbcTemplate jdbcTemplate;
+
+	/**
+	 * 获取数据权限
+	 *
+	 * @param mapperId 数据权限mapperId
+	 * @param roleId   用户角色集合
+	 * @return DataScopeModel
+	 */
+	@Override
+	public DataScopeModel getDataScopeByMapper(String mapperId, String roleId) {
+		List<Object> args = new ArrayList<>(Collections.singletonList(mapperId));
+		List<Long> roleIds = Func.toLongList(roleId);
+		args.addAll(roleIds);
+		// 增加searched字段防止未配置的参数重复读库导致缓存击穿
+		// 后续若有新增配置则会清空缓存重新加载
+		DataScopeModel dataScope = CacheUtil.get(SYS_CACHE, SCOPE_CACHE_CLASS, mapperId + StringPool.COLON + roleId, DataScopeModel.class);
+		if (dataScope == null || !dataScope.getSearched()) {
+			List<DataScopeModel> list = jdbcTemplate.query(DataScopeConstant.dataByMapper(roleIds.size()), args.toArray(), new BeanPropertyRowMapper<>(DataScopeModel.class));
+			if (CollectionUtil.isNotEmpty(list)) {
+				dataScope = list.iterator().next();
+				dataScope.setSearched(Boolean.TRUE);
+			} else {
+				dataScope = SEARCHED_DATA_SCOPE_MODEL;
+			}
+			CacheUtil.put(SYS_CACHE, SCOPE_CACHE_CLASS, mapperId + StringPool.COLON + roleId, dataScope);
+		}
+		return StringUtil.isNotBlank(dataScope.getResourceCode()) ? dataScope : null;
+	}
+
+	/**
+	 * 获取数据权限
+	 *
+	 * @param code 数据权限资源编号
+	 * @return DataScopeModel
+	 */
+	@Override
+	public DataScopeModel getDataScopeByCode(String code) {
+		DataScopeModel dataScope = CacheUtil.get(SYS_CACHE, SCOPE_CACHE_CODE, code, DataScopeModel.class);
+		// 增加searched字段防止未配置的参数重复读库导致缓存击穿
+		// 后续若有新增配置则会清空缓存重新加载
+		if (dataScope == null || !dataScope.getSearched()) {
+			List<DataScopeModel> list = jdbcTemplate.query(DataScopeConstant.DATA_BY_CODE, new Object[]{code}, new BeanPropertyRowMapper<>(DataScopeModel.class));
+			if (CollectionUtil.isNotEmpty(list)) {
+				dataScope = list.iterator().next();
+				dataScope.setSearched(Boolean.TRUE);
+			} else {
+				dataScope = SEARCHED_DATA_SCOPE_MODEL;
+			}
+			CacheUtil.put(SYS_CACHE, SCOPE_CACHE_CODE, code, dataScope);
+		}
+		return StringUtil.isNotBlank(dataScope.getResourceCode()) ? dataScope : null;
+	}
+
+	/**
+	 * 获取部门子级
+	 *
+	 * @param deptId 部门id
+	 * @return deptIds
+	 */
+	@Override
+	public List<Long> getDeptAncestors(Long deptId) {
+		List ancestors = CacheUtil.get(SYS_CACHE, DEPT_CACHE_ANCESTORS, deptId, List.class);
+		if (CollectionUtil.isEmpty(ancestors)) {
+			ancestors = jdbcTemplate.queryForList(DataScopeConstant.DATA_BY_DEPT, new Object[]{deptId}, Long.class);
+			CacheUtil.put(SYS_CACHE, DEPT_CACHE_ANCESTORS, deptId, ancestors);
+		}
+		return ancestors;
+	}
+}

+ 39 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/handler/DataScopeHandler.java

@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.handler;
+
+import org.springblade.core.datascope.model.DataScopeModel;
+import org.springblade.core.secure.BladeUser;
+
+/**
+ * 数据权限规则
+ *
+ * @author Chill
+ */
+public interface DataScopeHandler {
+
+	/**
+	 * 获取过滤sql
+	 *
+	 * @param mapperId    数据查询类
+	 * @param dataScope   数据权限类
+	 * @param bladeUser   当前用户信息
+	 * @param originalSql 原始Sql
+	 * @return sql
+	 */
+	String sqlCondition(String mapperId, DataScopeModel dataScope, BladeUser bladeUser, String originalSql);
+
+}

+ 54 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/handler/ScopeModelHandler.java

@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.handler;
+
+import org.springblade.core.datascope.model.DataScopeModel;
+
+import java.util.List;
+
+/**
+ * 获取数据权限模型统一接口
+ *
+ * @author Chill
+ */
+public interface ScopeModelHandler {
+
+	/**
+	 * 获取数据权限
+	 *
+	 * @param mapperId 数据权限mapperId
+	 * @param roleId   用户角色集合
+	 * @return DataScopeModel
+	 */
+	DataScopeModel getDataScopeByMapper(String mapperId, String roleId);
+
+	/**
+	 * 获取数据权限
+	 *
+	 * @param code 数据权限资源编号
+	 * @return DataScopeModel
+	 */
+	DataScopeModel getDataScopeByCode(String code);
+
+	/**
+	 * 获取部门子级
+	 *
+	 * @param deptId 部门id
+	 * @return deptIds
+	 */
+	List<Long> getDeptAncestors(Long deptId);
+
+}

+ 138 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/interceptor/DataScopeInterceptor.java

@@ -0,0 +1,138 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.interceptor;
+
+import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.mapping.StatementType;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+import org.springblade.core.datascope.annotation.DataAuth;
+import org.springblade.core.datascope.handler.DataScopeHandler;
+import org.springblade.core.datascope.model.DataScopeModel;
+import org.springblade.core.datascope.props.DataScopeProperties;
+import org.springblade.core.mp.intercept.QueryInterceptor;
+import org.springblade.core.secure.BladeUser;
+import org.springblade.core.secure.utils.SecureUtil;
+import org.springblade.core.tool.utils.ClassUtil;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springblade.core.tool.utils.StringUtil;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+
+/**
+ * mybatis 数据权限拦截器
+ *
+ * @author L.cm, Chill
+ */
+@Slf4j
+@RequiredArgsConstructor
+@SuppressWarnings({"rawtypes"})
+public class DataScopeInterceptor implements QueryInterceptor {
+
+	private final ConcurrentMap<String, DataAuth> dataAuthMap = new ConcurrentHashMap<>(8);
+
+	private final DataScopeHandler dataScopeHandler;
+	private final DataScopeProperties dataScopeProperties;
+
+	@Override
+	public void intercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
+		//未启用则放行
+		if (!dataScopeProperties.getEnabled()) {
+			return;
+		}
+
+		//未取到用户则放行
+		BladeUser bladeUser = SecureUtil.getUser();
+		if (bladeUser == null) {
+			return;
+		}
+
+		if (SqlCommandType.SELECT != ms.getSqlCommandType() || StatementType.CALLABLE == ms.getStatementType()) {
+			return;
+		}
+
+		String originalSql = boundSql.getSql();
+
+		//查找注解中包含DataAuth类型的参数
+		DataAuth dataAuth = findDataAuthAnnotation(ms);
+
+		//注解为空并且数据权限方法名未匹配到,则放行
+		String mapperId = ms.getId();
+		String className = mapperId.substring(0, mapperId.lastIndexOf(StringPool.DOT));
+		String mapperName = ClassUtil.getShortName(className);
+		String methodName = mapperId.substring(mapperId.lastIndexOf(StringPool.DOT) + 1);
+		boolean mapperSkip = dataScopeProperties.getMapperKey().stream().noneMatch(methodName::contains)
+			|| dataScopeProperties.getMapperExclude().stream().anyMatch(mapperName::contains);
+		if (dataAuth == null && mapperSkip) {
+			return;
+		}
+
+		//创建数据权限模型
+		DataScopeModel dataScope = new DataScopeModel();
+
+		//若注解不为空,则配置注解项
+		if (dataAuth != null) {
+			dataScope.setResourceCode(dataAuth.code());
+			dataScope.setScopeColumn(dataAuth.column());
+			dataScope.setScopeType(dataAuth.type().getType());
+			dataScope.setScopeField(dataAuth.field());
+			dataScope.setScopeValue(dataAuth.value());
+		}
+
+		//获取数据权限规则对应的筛选Sql
+		String sqlCondition = dataScopeHandler.sqlCondition(mapperId, dataScope, bladeUser, originalSql);
+		if (!StringUtil.isBlank(sqlCondition)) {
+			PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
+			mpBoundSql.sql(sqlCondition);
+		}
+	}
+
+	/**
+	 * 获取数据权限注解信息
+	 *
+	 * @param mappedStatement mappedStatement
+	 * @return DataAuth
+	 */
+	private DataAuth findDataAuthAnnotation(MappedStatement mappedStatement) {
+		String id = mappedStatement.getId();
+		return dataAuthMap.computeIfAbsent(id, (key) -> {
+			String className = key.substring(0, key.lastIndexOf(StringPool.DOT));
+			String mapperBean = StringUtil.firstCharToLower(ClassUtil.getShortName(className));
+			Object mapper = SpringUtil.getBean(mapperBean);
+			String methodName = key.substring(key.lastIndexOf(StringPool.DOT) + 1);
+			Class<?>[] interfaces = ClassUtil.getAllInterfaces(mapper);
+			for (Class<?> mapperInterface : interfaces) {
+				for (Method method : mapperInterface.getDeclaredMethods()) {
+					if (methodName.equals(method.getName()) && method.isAnnotationPresent(DataAuth.class)) {
+						return method.getAnnotation(DataAuth.class);
+					}
+				}
+			}
+			return null;
+		});
+	}
+
+}

+ 67 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/model/DataScopeModel.java

@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springblade.core.datascope.constant.DataScopeConstant;
+import org.springblade.core.datascope.enums.DataScopeEnum;
+
+import java.io.Serializable;
+
+/**
+ * 数据权限实体类
+ *
+ * @author Chill
+ */
+@Data
+@NoArgsConstructor
+public class DataScopeModel implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 构造器创建
+	 */
+	public DataScopeModel(Boolean searched) {
+		this.searched = searched;
+	}
+
+	/**
+	 * 是否已查询
+	 */
+	private Boolean searched = Boolean.FALSE;
+	/**
+	 * 资源编号
+	 */
+	private String resourceCode;
+	/**
+	 * 数据权限字段
+	 */
+	private String scopeColumn = DataScopeConstant.DEFAULT_COLUMN;
+	/**
+	 * 数据权限规则
+	 */
+	private Integer scopeType = DataScopeEnum.ALL.getType();
+	/**
+	 * 可见字段
+	 */
+	private String scopeField;
+	/**
+	 * 数据权限规则值
+	 */
+	private String scopeValue;
+}

+ 48 - 0
blade-core-datascope/src/main/java/org/springblade/core/datascope/props/DataScopeProperties.java

@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.datascope.props;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 数据权限参数配置类
+ *
+ * @author Chill
+ */
+@Data
+@ConfigurationProperties(prefix = "blade.data-scope")
+public class DataScopeProperties {
+
+	/**
+	 * 开启数据权限
+	 */
+	private Boolean enabled = true;
+	/**
+	 * mapper方法匹配关键字
+	 */
+	private List<String> mapperKey = Arrays.asList("page", "Page", "list", "List");
+
+	/**
+	 * mapper过滤
+	 */
+	private List<String> mapperExclude = Collections.singletonList("FlowMapper");
+
+}

+ 1 - 1
blade-core-develop/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 1
blade-core-launch/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 1 - 1
blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java

@@ -25,7 +25,7 @@ public interface AppConstant {
 	/**
 	 * 应用版本
 	 */
-	String APPLICATION_VERSION = "3.1.0";
+	String APPLICATION_VERSION = "3.2.0";
 
 	/**
 	 * 基础包

+ 1 - 0
blade-core-launch/src/main/java/org/springblade/core/launch/constant/TokenConstant.java

@@ -33,6 +33,7 @@ public interface TokenConstant {
 	String ACCOUNT = "account";
 	String USER_ID = "user_id";
 	String ROLE_ID = "role_id";
+	String DEPT_ID = "dept_id";
 	String USER_NAME = "user_name";
 	String ROLE_NAME = "role_name";
 	String TENANT_ID = "tenant_id";

+ 1 - 1
blade-core-log/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 1 - 1
blade-core-mybatis/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 45 - 10
blade-core-boot/src/main/java/org/springblade/core/boot/config/MybatisPlusConfiguration.java → blade-core-mybatis/src/main/java/org/springblade/core/mp/config/MybatisPlusConfiguration.java

@@ -13,21 +13,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.springblade.core.boot.config;
+package org.springblade.core.mp.config;
 
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
-import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
 import lombok.AllArgsConstructor;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.StringValue;
 import org.mybatis.spring.annotation.MapperScan;
-import org.springblade.core.boot.props.MybatisPlusProperties;
+import org.springblade.core.mp.intercept.QueryInterceptor;
+import org.springblade.core.mp.plugins.BladePaginationInterceptor;
 import org.springblade.core.mp.plugins.SqlLogInterceptor;
+import org.springblade.core.mp.props.MybatisPlusProperties;
+import org.springblade.core.secure.utils.SecureUtil;
+import org.springblade.core.tool.constant.BladeConstant;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.ObjectUtil;
+import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.AnnotationAwareOrderComparator;
 
 /**
  * mybatisplus 配置
@@ -40,22 +49,48 @@ import org.springframework.context.annotation.Configuration;
 @EnableConfigurationProperties(MybatisPlusProperties.class)
 public class MybatisPlusConfiguration {
 
-	private final TenantLineHandler tenantLineHandler;
+
+	/**
+	 * 租户拦截器
+	 */
+	@Bean
+	@ConditionalOnMissingBean(TenantLineInnerInterceptor.class)
+	public TenantLineInnerInterceptor tenantLineInnerInterceptor() {
+		return new TenantLineInnerInterceptor(new TenantLineHandler() {
+			@Override
+			public Expression getTenantId() {
+				return new StringValue(Func.toStr(SecureUtil.getTenantId(), BladeConstant.ADMIN_TENANT_ID));
+			}
+
+			@Override
+			public boolean ignoreTable(String tableName) {
+				return true;
+			}
+		});
+	}
 
 	/**
 	 * mybatis-plus 拦截器集合
 	 */
 	@Bean
 	@ConditionalOnMissingBean(MybatisPlusInterceptor.class)
-	public MybatisPlusInterceptor mybatisPlusInterceptor(MybatisPlusProperties mybatisPlusProperties) {
+	public MybatisPlusInterceptor mybatisPlusInterceptor(ObjectProvider<QueryInterceptor[]> queryInterceptors,
+														 TenantLineInnerInterceptor tenantLineInnerInterceptor,
+														 MybatisPlusProperties mybatisPlusProperties) {
 		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
 		// 配置租户拦截器
-		interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(tenantLineHandler));
+		interceptor.addInnerInterceptor(tenantLineInnerInterceptor);
 		// 配置分页拦截器
-		PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
-		paginationInnerInterceptor.setMaxLimit(mybatisPlusProperties.getPageLimit());
-		paginationInnerInterceptor.setOverflow(mybatisPlusProperties.getOverflow());
-		interceptor.addInnerInterceptor(paginationInnerInterceptor);
+		BladePaginationInterceptor paginationInterceptor = new BladePaginationInterceptor();
+		// 配置自定义查询拦截器
+		QueryInterceptor[] queryInterceptorArray = queryInterceptors.getIfAvailable();
+		if (ObjectUtil.isNotEmpty(queryInterceptorArray)) {
+			AnnotationAwareOrderComparator.sort(queryInterceptorArray);
+			paginationInterceptor.setQueryInterceptors(queryInterceptorArray);
+		}
+		paginationInterceptor.setMaxLimit(mybatisPlusProperties.getPageLimit());
+		paginationInterceptor.setOverflow(mybatisPlusProperties.getOverflow());
+		interceptor.addInnerInterceptor(paginationInterceptor);
 		return interceptor;
 	}
 

+ 56 - 0
blade-core-mybatis/src/main/java/org/springblade/core/mp/intercept/QueryInterceptor.java

@@ -0,0 +1,56 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+
+package org.springblade.core.mp.intercept;
+
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+import org.springframework.core.Ordered;
+
+/**
+ * 自定义 mybatis plus 查询拦截器
+ *
+ * @author L.cm
+ */
+@SuppressWarnings({"rawtypes"})
+public interface QueryInterceptor extends Ordered {
+
+	/**
+	 * 拦截处理
+	 *
+	 * @param executor
+	 * @param ms
+	 * @param parameter
+	 * @param rowBounds
+	 * @param resultHandler
+	 * @param boundSql
+	 */
+	void intercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql);
+
+	/**
+	 * 排序
+	 *
+	 * @return int
+	 */
+	@Override
+	default int getOrder() {
+		return Ordered.LOWEST_PRECEDENCE;
+	}
+}

+ 48 - 0
blade-core-mybatis/src/main/java/org/springblade/core/mp/plugins/BladePaginationInterceptor.java

@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.mp.plugins;
+
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import lombok.Setter;
+import lombok.SneakyThrows;
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+import org.springblade.core.mp.intercept.QueryInterceptor;
+
+/**
+ * 拓展分页拦截器
+ *
+ * @author Chill
+ */
+@Setter
+public class BladePaginationInterceptor extends PaginationInnerInterceptor {
+
+	/**
+	 * 查询拦截器
+	 */
+	private QueryInterceptor[] queryInterceptors;
+
+	@SneakyThrows
+	@Override
+	public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
+		QueryInterceptorExecutor.exec(queryInterceptors, executor, ms, parameter, rowBounds, resultHandler, boundSql);
+		return super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
+	}
+
+}

+ 50 - 0
blade-core-mybatis/src/main/java/org/springblade/core/mp/plugins/QueryInterceptorExecutor.java

@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
+ * <p>
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.gnu.org/licenses/lgpl.html
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.mp.plugins;
+
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+import org.springblade.core.mp.intercept.QueryInterceptor;
+import org.springblade.core.tool.utils.ObjectUtil;
+
+/**
+ * 查询拦截器执行器
+ *
+ * <p>
+ * 目的:抽取此方法是为了后期方便同步更新 {@link BladePaginationInterceptor}
+ * </p>
+ *
+ * @author L.cm
+ */
+@SuppressWarnings({"rawtypes"})
+public class QueryInterceptorExecutor {
+
+	/**
+	 * 执行查询拦截器
+	 */
+	static void exec(QueryInterceptor[] interceptors, Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws Throwable {
+		if (ObjectUtil.isEmpty(interceptors)) {
+			return;
+		}
+		for (QueryInterceptor interceptor : interceptors) {
+			interceptor.intercept(executor, ms, parameter, rowBounds, resultHandler, boundSql);
+		}
+	}
+
+}

+ 1 - 1
blade-core-boot/src/main/java/org/springblade/core/boot/props/MybatisPlusProperties.java → blade-core-mybatis/src/main/java/org/springblade/core/mp/props/MybatisPlusProperties.java

@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.springblade.core.boot.props;
+package org.springblade.core.mp.props;
 
 import lombok.Data;
 import org.springframework.boot.context.properties.ConfigurationProperties;

+ 1 - 1
blade-core-oss/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 1
blade-core-report/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 1
blade-core-secure/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 5 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/BladeUser.java

@@ -45,6 +45,11 @@ public class BladeUser implements Serializable {
 	 */
 	@ApiModelProperty(hidden = true)
 	private String tenantId;
+	/**
+	 * 部门id
+	 */
+	@ApiModelProperty(hidden = true)
+	private String deptId;
 	/**
 	 * 昵称
 	 */

+ 3 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java

@@ -48,6 +48,7 @@ public class SecureUtil {
 	private final static String ACCOUNT = TokenConstant.ACCOUNT;
 	private final static String USER_ID = TokenConstant.USER_ID;
 	private final static String ROLE_ID = TokenConstant.ROLE_ID;
+	private final static String DEPT_ID = TokenConstant.DEPT_ID;
 	private final static String USER_NAME = TokenConstant.USER_NAME;
 	private final static String ROLE_NAME = TokenConstant.ROLE_NAME;
 	private final static String TENANT_ID = TokenConstant.TENANT_ID;
@@ -98,6 +99,7 @@ public class SecureUtil {
 		Long userId = Func.toLong(claims.get(SecureUtil.USER_ID));
 		String tenantId = Func.toStr(claims.get(SecureUtil.TENANT_ID));
 		String roleId = Func.toStr(claims.get(SecureUtil.ROLE_ID));
+		String deptId = Func.toStr(claims.get(SecureUtil.DEPT_ID));
 		String account = Func.toStr(claims.get(SecureUtil.ACCOUNT));
 		String roleName = Func.toStr(claims.get(SecureUtil.ROLE_NAME));
 		String userName = Func.toStr(claims.get(SecureUtil.USER_NAME));
@@ -107,6 +109,7 @@ public class SecureUtil {
 		bladeUser.setTenantId(tenantId);
 		bladeUser.setAccount(account);
 		bladeUser.setRoleId(roleId);
+		bladeUser.setDeptId(deptId);
 		bladeUser.setRoleName(roleName);
 		bladeUser.setUserName(userName);
 		return bladeUser;

+ 1 - 1
blade-core-social/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 1
blade-core-swagger/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 1 - 1
blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java

@@ -55,7 +55,7 @@ public class SwaggerProperties {
 	/**
 	 * 版本
 	 **/
-	private String version = "3.1.0";
+	private String version = "3.2.0";
 	/**
 	 * 许可证
 	 **/

+ 1 - 1
blade-core-test/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.springblade</groupId>
         <artifactId>blade-tool</artifactId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 1
blade-core-tool/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.springblade</groupId>
         <artifactId>blade-tool</artifactId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 2 - 0
blade-core-tool/src/main/java/org/springblade/core/tool/config/XssConfiguration.java

@@ -44,6 +44,8 @@ public class XssConfiguration {
 
 	/**
 	 * 防XSS注入
+	 *
+	 * @return FilterRegistrationBean
 	 */
 	@Bean
 	public FilterRegistrationBean<XssFilter> xssFilterRegistration() {

+ 5 - 0
blade-core-tool/src/main/java/org/springblade/core/tool/constant/BladeConstant.java

@@ -65,6 +65,11 @@ public interface BladeConstant {
 	int DB_ADMIN_NON_LOCKED = 0;
 	int DB_ADMIN_LOCKED = 1;
 
+	/**
+	 * 顶级父节点id
+	 */
+	Long TOP_PARENT_ID = 0L;
+
 	/**
 	 * 管理员对应的租户ID
 	 */

+ 165 - 0
blade-core-tool/src/main/java/org/springblade/core/tool/utils/CacheUtil.java

@@ -0,0 +1,165 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.tool.utils;
+
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.lang.Nullable;
+
+import java.util.concurrent.Callable;
+
+/**
+ * 缓存工具类
+ *
+ * @author Chill
+ */
+public class CacheUtil {
+
+	public static final String SYS_CACHE = "blade:sys";
+
+	private static CacheManager cacheManager;
+
+	/**
+	 * 获取缓存工具
+	 *
+	 * @return CacheManager
+	 */
+	private static CacheManager getCacheManager() {
+		if (cacheManager == null) {
+			cacheManager = SpringUtil.getBean(CacheManager.class);
+		}
+		return cacheManager;
+	}
+
+	/**
+	 * 获取缓存对象
+	 *
+	 * @param cacheName 缓存名
+	 * @return Cache
+	 */
+	public static Cache getCache(String cacheName) {
+		return getCacheManager().getCache(cacheName);
+	}
+
+	/**
+	 * 获取缓存
+	 *
+	 * @param cacheName 缓存名
+	 * @param keyPrefix 缓存键前缀
+	 * @param key       缓存键值
+	 * @return Cache
+	 */
+	@Nullable
+	public static Object get(String cacheName, String keyPrefix, Object key) {
+		if (Func.hasEmpty(cacheName, keyPrefix, key)) {
+			return null;
+		}
+		return getCache(cacheName).get(keyPrefix.concat(String.valueOf(key))).get();
+	}
+
+	/**
+	 * 获取缓存
+	 *
+	 * @param cacheName 缓存名
+	 * @param keyPrefix 缓存键前缀
+	 * @param key       缓存键值
+	 * @param type      转换类型
+	 * @param <T>       类型
+	 * @return Cache
+	 */
+	@Nullable
+	public static <T> T get(String cacheName, String keyPrefix, Object key, @Nullable Class<T> type) {
+		if (Func.hasEmpty(cacheName, keyPrefix, key)) {
+			return null;
+		}
+		return getCache(cacheName).get(keyPrefix.concat(String.valueOf(key)), type);
+	}
+
+	/**
+	 * 获取缓存
+	 *
+	 * @param cacheName   缓存名
+	 * @param keyPrefix   缓存键前缀
+	 * @param key         缓存键值
+	 * @param valueLoader 重载对象
+	 * @param <T>         类型
+	 * @return Cache
+	 */
+	@Nullable
+	public static <T> T get(String cacheName, String keyPrefix, Object key, Callable<T> valueLoader) {
+		if (Func.hasEmpty(cacheName, keyPrefix, key)) {
+			return null;
+		}
+		try {
+			Cache.ValueWrapper valueWrapper = getCache(cacheName).get(keyPrefix.concat(String.valueOf(key)));
+			Object value = null;
+			if (valueWrapper == null) {
+				T call = valueLoader.call();
+				if (Func.isNotEmpty(call)) {
+					getCache(cacheName).put(keyPrefix.concat(String.valueOf(key)), call);
+					value = call;
+				}
+			} else {
+				value = valueWrapper.get();
+			}
+			return (T) value;
+		} catch (Exception ex) {
+			ex.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * 设置缓存
+	 *
+	 * @param cacheName 缓存名
+	 * @param keyPrefix 缓存键前缀
+	 * @param key       缓存键值
+	 * @param value     缓存值
+	 */
+	public static void put(String cacheName, String keyPrefix, Object key, @Nullable Object value) {
+		getCache(cacheName).put(keyPrefix.concat(String.valueOf(key)), value);
+	}
+
+	/**
+	 * 清除缓存
+	 *
+	 * @param cacheName 缓存名
+	 * @param keyPrefix 缓存键前缀
+	 * @param key       缓存键值
+	 */
+	public static void evict(String cacheName, String keyPrefix, Object key) {
+		if (Func.hasEmpty(cacheName, keyPrefix, key)) {
+			return;
+		}
+		getCache(cacheName).evict(keyPrefix.concat(String.valueOf(key)));
+	}
+
+	/**
+	 * 清空缓存
+	 *
+	 * @param cacheName 缓存名
+	 */
+	public static void clear(String cacheName) {
+		if (Func.isEmpty(cacheName)) {
+			return;
+		}
+		getCache(cacheName).clear();
+	}
+
+}
+

+ 4 - 0
blade-core-tool/src/main/java/org/springblade/core/tool/utils/DateUtil.java

@@ -405,6 +405,7 @@ public class DateUtil {
 	 *
 	 * @param dateStr 时间字符串
 	 * @param pattern 表达式
+	 * @param query   移动查询
 	 * @return 时间
 	 */
 	public static <T> T parse(String dateStr, String pattern, TemporalQuery<T> query) {
@@ -453,6 +454,9 @@ public class DateUtil {
 
 	/**
 	 * Converts local date time to Calendar.
+	 *
+	 * @param localDateTime LocalDateTime
+	 * @return Calendar
 	 */
 	public static Calendar toCalendar(final LocalDateTime localDateTime) {
 		return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault()));

+ 143 - 0
blade-core-tool/src/main/java/org/springblade/core/tool/utils/PlaceholderUtil.java

@@ -0,0 +1,143 @@
+package org.springblade.core.tool.utils;
+
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+/**
+ * 占位符解析器
+ *
+ * @author meilin.huang, chill
+ */
+public class PlaceholderUtil {
+	/**
+	 * 默认前缀占位符
+	 */
+	public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
+
+	/**
+	 * 默认后缀占位符
+	 */
+	public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
+
+	/**
+	 * 默认单例解析器
+	 */
+	private static final PlaceholderUtil DEFAULT_RESOLVER = new PlaceholderUtil();
+
+	/**
+	 * 占位符前缀
+	 */
+	private String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
+
+	/**
+	 * 占位符后缀
+	 */
+	private String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
+
+
+	private PlaceholderUtil() {
+	}
+
+	private PlaceholderUtil(String placeholderPrefix, String placeholderSuffix) {
+		this.placeholderPrefix = placeholderPrefix;
+		this.placeholderSuffix = placeholderSuffix;
+	}
+
+	/**
+	 * 获取默认的占位符解析器,即占位符前缀为"${", 后缀为"}"
+	 *
+	 * @return PlaceholderUtil
+	 */
+	public static PlaceholderUtil getDefaultResolver() {
+		return DEFAULT_RESOLVER;
+	}
+
+	public static PlaceholderUtil getResolver(String placeholderPrefix, String placeholderSuffix) {
+		return new PlaceholderUtil(placeholderPrefix, placeholderSuffix);
+	}
+
+	/**
+	 * 解析带有指定占位符的模板字符串,默认占位符为前缀:${  后缀:}
+	 *
+	 * @param content 要解析的带有占位符的模板字符串
+	 * @param values  按照模板占位符索引位置设置对应的值
+	 * @return {String}
+	 */
+	public String resolve(String content, String... values) {
+		int start = content.indexOf(this.placeholderPrefix);
+		if (start == -1) {
+			return content;
+		}
+		//值索引
+		int valueIndex = 0;
+		StringBuilder result = new StringBuilder(content);
+		while (start != -1) {
+			int end = result.indexOf(this.placeholderSuffix);
+			String replaceContent = values[valueIndex++];
+			result.replace(start, end + this.placeholderSuffix.length(), replaceContent);
+			start = result.indexOf(this.placeholderPrefix, start + replaceContent.length());
+		}
+		return result.toString();
+	}
+
+	/**
+	 * 解析带有指定占位符的模板字符串,默认占位符为前缀:${  后缀:}
+	 *
+	 * @param content 要解析的带有占位符的模板字符串
+	 * @param values  按照模板占位符索引位置设置对应的值
+	 * @return {String}
+	 */
+	public String resolve(String content, Object[] values) {
+		return resolve(content, Stream.of(values).map(String::valueOf).toArray(String[]::new));
+	}
+
+	/**
+	 * 根据替换规则来替换指定模板中的占位符值
+	 *
+	 * @param content 要解析的字符串
+	 * @param rule    解析规则回调
+	 * @return {String}
+	 */
+	public String resolveByRule(String content, Function<String, String> rule) {
+		int start = content.indexOf(this.placeholderPrefix);
+		if (start == -1) {
+			return content;
+		}
+		StringBuilder result = new StringBuilder(content);
+		while (start != -1) {
+			int end = result.indexOf(this.placeholderSuffix, start + 1);
+			//获取占位符属性值,如${id}, 即获取id
+			String placeholder = result.substring(start + this.placeholderPrefix.length(), end);
+			//替换整个占位符内容,即将${id}值替换为替换规则回调中的内容
+			String replaceContent = placeholder.trim().isEmpty() ? "" : rule.apply(placeholder);
+			result.replace(start, end + this.placeholderSuffix.length(), replaceContent);
+			start = result.indexOf(this.placeholderPrefix, start + replaceContent.length());
+		}
+		return result.toString();
+	}
+
+	/**
+	 * 替换模板中占位符内容,占位符的内容即为map key对应的值,key为占位符中的内容
+	 *
+	 * @param content  模板内容
+	 * @param valueMap 值映射
+	 * @return 替换完成后的字符串
+	 */
+	public String resolveByMap(String content, final Map<String, Object> valueMap) {
+		return resolveByRule(content, placeholderValue -> String.valueOf(valueMap.get(placeholderValue)));
+	}
+
+	/**
+	 * 根据properties文件替换占位符内容
+	 *
+	 * @param content    模板内容
+	 * @param properties 配置
+	 * @return {String}
+	 */
+	public String resolveByProperties(String content, final Properties properties) {
+		return resolveByRule(content, properties::getProperty);
+	}
+
+}

+ 35 - 0
blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java

@@ -1438,5 +1438,40 @@ public class StringUtil extends org.springframework.util.StringUtils {
 		return sb.toString().toLowerCase();
 	}
 
+
+
+	/**
+	 * 首字母变小写
+	 *
+	 * @param str 字符串
+	 * @return {String}
+	 */
+	public static String firstCharToLower(String str) {
+		char firstChar = str.charAt(0);
+		if (firstChar >= CharPool.UPPER_A && firstChar <= CharPool.UPPER_Z) {
+			char[] arr = str.toCharArray();
+			arr[0] += (CharPool.LOWER_A - CharPool.UPPER_A);
+			return new String(arr);
+		}
+		return str;
+	}
+
+	/**
+	 * 首字母变大写
+	 *
+	 * @param str 字符串
+	 * @return {String}
+	 */
+	public static String firstCharToUpper(String str) {
+		char firstChar = str.charAt(0);
+		if (firstChar >= CharPool.LOWER_A && firstChar <= CharPool.LOWER_Z) {
+			char[] arr = str.toCharArray();
+			arr[0] -= (CharPool.LOWER_A - CharPool.UPPER_A);
+			return new String(arr);
+		}
+		return str;
+	}
+
+
 }
 

+ 1 - 1
blade-core-transaction/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>3.1.0</version>
+        <version>3.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 12 - 11
pom.xml

@@ -5,7 +5,7 @@
 
     <groupId>org.springblade</groupId>
     <artifactId>blade-tool</artifactId>
-    <version>3.1.0</version>
+    <version>3.2.0</version>
     <packaging>pom</packaging>
     <name>blade-tool</name>
     <description>
@@ -36,26 +36,26 @@
     </scm>
 
     <properties>
-        <blade.tool.version>3.1.0</blade.tool.version>
+        <blade.tool.version>3.2.0</blade.tool.version>
 
         <java.version>1.8</java.version>
-        <maven.plugin.version>3.8.0</maven.plugin.version>
+        <maven.plugin.version>3.8.1</maven.plugin.version>
         <swagger.version>2.10.5</swagger.version>
         <swagger.models.version>1.6.2</swagger.models.version>
-        <knife4j.version>2.0.8</knife4j.version>
-        <mybatis.plus.version>3.4.3.1</mybatis.plus.version>
+        <knife4j.version>2.0.9</knife4j.version>
+        <mybatis.plus.version>3.4.3.4</mybatis.plus.version>
         <mybatis.plus.generator.version>3.4.1</mybatis.plus.generator.version>
         <protostuff.version>1.6.0</protostuff.version>
         <disruptor.version>3.4.2</disruptor.version>
-        <spring.boot.admin.version>2.3.1</spring.boot.admin.version>
+        <spring.boot.admin.version>2.5.3</spring.boot.admin.version>
         <mica.auto.version>1.2.5</mica.auto.version>
         <alibaba.cloud.version>2021.1</alibaba.cloud.version>
-        <alibaba.nacos.version>2.0.2</alibaba.nacos.version>
+        <alibaba.nacos.version>2.0.3</alibaba.nacos.version>
         <alibaba.seata.version>1.4.2</alibaba.seata.version>
         <spring.plugin.version>2.0.0.RELEASE</spring.plugin.version>
 
-        <spring.boot.version>2.5.2</spring.boot.version>
-        <spring.cloud.version>2020.0.3</spring.cloud.version>
+        <spring.boot.version>2.5.6</spring.boot.version>
+        <spring.cloud.version>2020.0.4</spring.cloud.version>
         <spring.platform.version>Cairo-SR8</spring.platform.version>
 
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -77,6 +77,7 @@
         <module>blade-core-oss</module>
         <module>blade-core-transaction</module>
         <module>blade-core-report</module>
+        <module>blade-core-datascope</module>
     </modules>
 
     <dependencyManagement>
@@ -278,7 +279,7 @@
                         </executions>
                     </plugin>
                     <!-- Nexus -->
-                    <plugin>
+                    <!--<plugin>
                         <groupId>org.sonatype.plugins</groupId>
                         <artifactId>nexus-staging-maven-plugin</artifactId>
                         <version>1.6.8</version>
@@ -288,7 +289,7 @@
                             <nexusUrl>https://oss.sonatype.org/</nexusUrl>
                             <autoReleaseAfterClose>true</autoReleaseAfterClose>
                         </configuration>
-                    </plugin>
+                    </plugin>-->
                 </plugins>
             </build>
             <distributionManagement>