租户策略

· 版本至少 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.注意事项

  1. 使用注解的时候,尽量在调用方法链中不要出现多个@TenantPolicy注解,因为是在同一线程中获取参数配置,后配置的后导致覆盖,谨慎使用。
  2. 如果真的出现很复杂的情况,以上方法仍然无法解决问题,建议租户配置忽略表,自行过滤数据。

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/最外层代码写忽略租户的注解

文档更新时间: 2023-09-15 10:29   作者:伍润源