租户策略
· 版本至少 1.6.0-SNAPSHOT
1.添加 policy 策略配置
pai:
tenant:
# 全局开关,默认false 不启用
enabled: true
# 租户字段,默认tenant_id
column: tenant_id
# 全局租户策略,默认v1(当前企业),v2(运营企业)
policy: v1
# 忽略表集合,即不需要过滤租户字段的表
ignore-tables:
- user
- role
2.添加注解配置切换策略
@TenantPolicy 注解属性
value: 版本(Policy.V1,Policy.V2 )v1表示当前的企业/园区id,v2表示企业/园区的运营企业的集团id,默认值:Policy.V1
type: 方法类型(type = MethodType.ENT,type = MethodType.PARK)该值表示通过企业id还是园区id去寻找最后的租户id,默认值:MethodType.ENT
@TenantPolicy(value = Policy.V1,type = MethodType.ENT)
#获取当前用户所在企业的id作为租户id
@TenantPolicy(value = Policy.V2,type = MethodType.ENT)
#获取当前用户所在企业的运营企业的集团id作为租户id
@TenantPolicy(value = Policy.V1,type = MethodType.PARK)
#获取当前用户所在园区的id作为租户id
@TenantPolicy(value = Policy.V2,type = MethodType.PARK)
#获取当前用户所在园区的运营企业的集团id作为租户id
3.注意事项
- 使用注解的时候,尽量在调用方法链中不要出现多个@TenantPolicy注解,因为是在同一线程中获取参数配置,后配置的后导致覆盖,谨慎使用。
- 如果真的出现很复杂的情况,以上方法仍然无法解决问题,建议租户配置忽略表,自行过滤数据。
4.实现逻辑
tips: 当企业集团数据变更的时候,利用监听nacos share-tenant-config.yaml 配置文件变化来清理本地缓存数据。
操作实例
1. 添加配置
pai:
tenant:
# 全局开关,默认false 不启用
enabled: true
# 租户字段,默认tenant_id
column: tenant_id
# 租户策略,默认v1(当前企业),v2(运营企业)
policy: v1
# 忽略表集合,即不需要过滤租户字段的表
ignore-tables:
- user
- role
2. Maven依赖
正常情况下骨架已自动引入,如没有可自动引入,如下
<dependency>
<groupId>cn.flyrise</groupId>
<artifactId>pai-common-mybatis</artifactId>
<version>版本号</version>
</dependency>
3. 添加表字段
通过开发中心表建模工具添加租户字段,名称与配置保持一致,建议类型为varchar(32) ,长度可根据实际业务来调整
数据处理
a. 新增时会根据当前登录用户自动获取并填,如无登录用户则为null
b. 查询时会自动加上过滤条件
c. 空字符串的时候不会覆盖,尤其是swagger上面。
说明:
多租户 != 权限过滤,不要乱用,租户之间是完全隔离的!!!
启用多租户后所有执行的method的sql都会进行处理.
自写的sql请按规范书写(sql涉及到多个表的每个表都要给别名,特别是 inner join 的要写标准的 inner join)
4. 临时取消租户条件
在mapper中的方法添加注释@InterceptorIgnore(tenantLine = "true")
实现忽略开关
@Mapper
@Repository
public interface UserMapper extends BaseMapper<UserPO> {
@Select("${sql}")
@ResultType(ArrayList.class)
@InterceptorIgnore(tenantLine = "true")
List<UserPO> nativeQuery(@Param("sql") String sql);
@ResultType(ArrayList.class)
@InterceptorIgnore(tenantLine = "true")
List<UserPO> testQuery();
}
<?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="cn.flyrise.pai.demo.dao.UserMapper">
<select id="testQuery" resultType="cn.flyrise.pai.demo.model.po.UserPO">
select id, name, password, park_id, status from demo_user
</select>
</mapper>
执行效果:
4.1 使用代码忽略租户
4.1.1 版本
目前只有以下版本支持在业务逻辑中使用代码/注解自动忽略租户条件
<parent>
<groupId>cn.flyrise</groupId>
<artifactId>pai</artifactId>
<version>1.11.0-ts-SNAPSHOT</version>
<relativePath/>
</parent>
<parent>
<groupId>cn.flyrise</groupId>
<artifactId>pai</artifactId>
<version>2.1.0.0-SNAPSHOT</version>
<relativePath/>
</parent>
4.1.2 忽略租户的方法
当某些方法需要在Mq、xxlJob、多线程中执行的时候,可以使用下面方法忽略租户隔离,避免重复写xml以及Dao文件
1、可以通过@IgnoreTenant注解
2、通过Mybatis-plus 提供的InterceptorIgnoreHelper工具类
3、通过封装的TenantUtil工具类
4.1.3 使用 @IgnoreTenant 注解
- 在方法上添加@IgnoreTenant,注意,只有支持AOP扫描的方法才能使用,如果是方法中有异步线程,一步线程中有去执行sql的,这部分sql并不会忽略租户,需要在异步方法前后做忽略租户、结束忽略租户的处理。
- 同一个接口中如果调用的多个方法中都包含有@IgnoreTenant 注解,可能会导致某些方法忽略不了租户,建议在Controller/最外层方法上使用这个注解
@IgnoreTenant
@XxlJob("发票状态检查调度器")
@XxlJobTask(desc = "检查状态为开票中的发票信息,根据结果更新状态,每分钟执行一次", cron = "0 */1 * * * ?", author = "pai_finance")
public ReturnT<String> checkStatus(String param){
}
4.1.4 使用TenantUtil工具类
//有返回值
T t = TenantUtil.ignoreTenant(()->{
return this.bankService.get(id);
})
//有返回值,异步方法。需使用Future接收对象
Future<T> future = TenantUtil.ignoreTenantAsync(()->{
return this.service.get(id);
});
//无返回值
TenantUtil.ignoreTenant(()->{
this.bankService.get(id);
})
//无返回值,异步
TenantUtil.ignoreTenantAsync(()->{
this.bankService.get(id);
})
⚠️注意事项
1、升级pai版本后如果代码中有使用maper.count(),需要自行把int 类型改成Long 类型
改之前: baseMapper.selectCount(Query)
改之后: baseMapper.selectCount(Query).intValue()
2、使用上面的方法需要注意的是:不要忘记写 InterceptorIgnoreHelper.clearIgnoreStrategy();
3、fegin调用是无效的,Api接口还是要定义的,在inner方法中加上开始忽略租户、关闭忽略租户的方法。
4、异步调用的过程中,注意关闭忽略租户的位置是否正确。
5、一个Controller 接口中的方法使用了多个@ignoreTenant注解,可能会导致租户忽略失效,例如下方代码
//最外层使用了忽略租户的注解
@IgnoreTenant
@GetMapping("demo")
public Reply sync(){
//这里注解只是表示这个方法使用了@IgnoreTenant注解
@IgnoreTenant
service.IgnoreTeanntMethod();
service.getById("1");
//这个时候 getById() 这个方法不会忽略租户,因为上面的IgnoreTeanntMethod() 已经把忽略租户的策略关闭了
return Reply.success();
}
6、建议是在Controller/最外层代码写忽略租户的注解