## 线程环境自定义租户ID 1. 常规场景下,多租户的ID是从token获取的,而token通过request请求的header传递至后端解析。 2. 租户插件底层从request获取到tenantId进行隔离。 3. 但是如果遇到多线程、定时任务、RPC远程调用等场景是获取不到request的,原本的多租户插件也就失效了。 4. 针对这种情况我们提供了TenantUtil,可以在不同的场景自定义租户ID,包括租户的数据查询以及数据提交。 ## 操作说明 1. 两种写法的对比如下,通过TenantUtil可以自定义当前线程的租户ID ~~~ // 常规写法 Notice detail = noticeService.getOne(Condition.getQueryWrapper(notice)); // 常规写法 Boolean temp = noticeService.save(notice); ​ // 线程自定义写法 Notice detail = TenantUtil.use("自定义租户ID", () ->  noticeService.getOne(Condition.getQueryWrapper(notice)) ); // 线程自定义写法 Boolean temp = TenantUtil.use("自定义租户ID", () ->  noticeService.save(notice) ); ~~~ 2. 若某些场景需要忽略租户id,可以看到所有或者另外自定义,可以采用如下方式定义 ~~~ // 常规写法 Notice detail = noticeService.list(Condition.getQueryWrapper(notice)); ​ // 线程自定义写法 Notice detail = TenantUtil.ignore(() ->  noticeService.list(Condition.getQueryWrapper(notice)) ); ~~~ 3. 实现原理如下,通过本地线程设置租户id,接着在租户插件优先获取本地线程的值,若没有才从request获取租户的值。 ~~~ private static final ThreadLocal<String> tl = new ThreadLocal<>(); ​ /** * 获取租户id * * @return 租户id */ public static String getTenantId() { String tenantId = tl.get(); if (tenantId != null) { return tenantId; } return AuthUtil.getTenantId(); } ​ /** * 使用租户 id 执行函数 * * @param tenantId tenantId * @param runnable Runnable */ public static void use(String tenantId, Runnable runnable) { Assert.hasText(tenantId, "参数 tenantId 为空"); tl.set(tenantId); try { runnable.run(); } finally { tl.remove(); } } ~~~ ~~~ @Slf4j @RequiredArgsConstructor public class BladeTenantHandler implements TenantLineHandler, SmartInitializingSingleton { ​ /** * 获取租户ID * * @return 租户ID */ @Override public Expression getTenantId() { return new StringValue(Func.toStr(TenantUtil.getTenantId(), BladeConstant.ADMIN_TENANT_ID)); } ​ } ~~~ ## 说明 1. 通过TenantUtil可以自定义线程的租户ID。 2. 针对特殊应用场景不受request环境的影响,也可以实现跨租户查询的功能。 3. 注意⚠️:通过此工具可以操作访问任何租户的数据,**编码必须做好风险管理,否则会因为设计不当导致数据错乱等严重问题。**