From 1ce2c09f4790924fa985317cf7b1f24a78b69616 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 6 Dec 2021 01:32:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20Tenant=20MQ=20=E7=9A=84?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/auth/SysAuthController.http | 5 ++- .../tenant/SysTenantController.java | 2 +- .../src/main/resources/application.yaml | 2 +- .../mybatis-config/mybatis-config.xml | 1 + .../mq/core/message/AbstractRedisMessage.java | 5 ++- .../mybatis/core/util/MyBatisUtils.java | 5 ++- .../yudao-spring-boot-starter-tenant/pom.xml | 6 +++ .../YudaoTenantDatabaseAutoConfiguration.java | 31 ++++---------- .../YudaoTenantMQAutoConfiguration.java | 20 +++++++++ .../mq/TenantRedisMessageInterceptor.java | 42 +++++++++++++++++++ .../yudao/framework/tenant/package-info.java | 4 +- .../main/resources/META-INF/spring.factories | 3 +- 12 files changed, 95 insertions(+), 31 deletions(-) create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantMQAutoConfiguration.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http index 580b897f8..c98984b69 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http @@ -1,7 +1,7 @@ ### 请求 /login 接口 => 成功 POST {{baseUrl}}/login Content-Type: application/json -tenant-id: 0 +tenant-id: 1 { "username": "admin", @@ -13,10 +13,13 @@ tenant-id: 0 ### 请求 /get-permission-info 接口 => 成功 GET {{baseUrl}}/get-permission-info Authorization: Bearer {{token}} +tenant-id: 1 ### 请求 /list-menus 接口 => 成功 GET {{baseUrl}}/list-menus Authorization: Bearer {{token}} +#Authorization: Bearer 0d161f69c9ac4c7f836e1b850715a7b0 +tenant-id: 1 ### 请求 /druid/xxx 接口 => 失败 TODO 临时测试 GET http://127.0.0.1:8080/druid/123 diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/tenant/SysTenantController.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/tenant/SysTenantController.java index 9d91b740e..fc44b1ede 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/tenant/SysTenantController.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/tenant/SysTenantController.java @@ -21,7 +21,7 @@ public class SysTenantController { @ApiImplicitParam(name = "name", value = "租户名", required = true, example = "芋道源码", dataTypeClass = Long.class) public CommonResult getTenantIdByName(@RequestParam("name") String name) { if (Objects.equals("芋道源码", name)) { - return CommonResult.success(0L); + return CommonResult.success(1L); } return CommonResult.success(null); } diff --git a/yudao-admin-server/src/main/resources/application.yaml b/yudao-admin-server/src/main/resources/application.yaml index 6590d3c4c..18b3b2eb7 100644 --- a/yudao-admin-server/src/main/resources/application.yaml +++ b/yudao-admin-server/src/main/resources/application.yaml @@ -22,7 +22,7 @@ spring: # MyBatis Plus 的配置项 mybatis-plus: -# 在 mybatis-config/mybatis-config.xml 中设置 +# 在 mybatis-config/mybatis-config.xml 中设置 TODO jason:看看有没其它解决方案 # configuration: # map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志 diff --git a/yudao-admin-server/src/main/resources/mybatis-config/mybatis-config.xml b/yudao-admin-server/src/main/resources/mybatis-config/mybatis-config.xml index 4f290bc98..ddc3d8951 100644 --- a/yudao-admin-server/src/main/resources/mybatis-config/mybatis-config.xml +++ b/yudao-admin-server/src/main/resources/mybatis-config/mybatis-config.xml @@ -6,6 +6,7 @@ + diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java index 70a668154..f02e89d6f 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java +++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.framework.mq.core.message; +import lombok.Data; + import java.util.HashMap; import java.util.Map; @@ -8,12 +10,13 @@ import java.util.Map; * * @author 芋道源码 */ +@Data public abstract class AbstractRedisMessage { /** * 头 */ - private final Map headers = new HashMap<>(); + private Map headers = new HashMap<>(); public String getHeader(String key) { return headers.get(key); diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java index 8789ea574..f7f0cad1c 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java @@ -40,10 +40,11 @@ public class MyBatisUtils { * * @param interceptor 链 * @param inner 拦截器 + * @param index 位置 */ - public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner) { + public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner, int index) { List inners = new ArrayList<>(interceptor.getInterceptors()); - inners.add(0, inner); + inners.add(index, inner); interceptor.setInterceptors(inners); } diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/pom.xml b/yudao-framework/yudao-spring-boot-starter-tenant/pom.xml index 6588eaf1b..23d897828 100644 --- a/yudao-framework/yudao-spring-boot-starter-tenant/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-tenant/pom.xml @@ -38,6 +38,12 @@ cn.iocoder.boot yudao-spring-boot-starter-job + + + + cn.iocoder.boot + yudao-spring-boot-starter-mq + diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java index 7149e39a2..1c7d8a7b3 100644 --- a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java @@ -4,40 +4,27 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; /** * 多租户针对 DB 的自动配置 * * @author 芋道源码 */ +@Configuration @EnableConfigurationProperties(TenantProperties.class) public class YudaoTenantDatabaseAutoConfiguration { @Bean - public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties) { - return new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties)); - } - - @Bean - public BeanPostProcessor mybatisPlusInterceptorBeanPostProcessor(TenantLineInnerInterceptor tenantLineInnerInterceptor) { - return new BeanPostProcessor() { - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (!(bean instanceof MybatisPlusInterceptor)) { - return bean; - } - // 将 TenantDatabaseInterceptor 添加到最前面 - MybatisPlusInterceptor interceptor = (MybatisPlusInterceptor) bean; - MyBatisUtils.addInterceptor(interceptor, tenantLineInnerInterceptor); - return bean; - } - - }; + public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties, + MybatisPlusInterceptor interceptor) { + TenantLineInnerInterceptor inner = new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties)); + // 添加到 interceptor 中 + // 需要加在首个,主要是为了在分页插件前面。这个是 MyBatis Plus 的规定 + MyBatisUtils.addInterceptor(interceptor, inner, 0); + return inner; } } diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantMQAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantMQAutoConfiguration.java new file mode 100644 index 000000000..6a465b91e --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantMQAutoConfiguration.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.framework.tenant.config; + +import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 多租户针对 MQ 的自动配置 + * + * @author 芋道源码 + */ +@Configuration +public class YudaoTenantMQAutoConfiguration { + + @Bean + public TenantRedisMessageInterceptor tenantRedisMessageInterceptor() { + return new TenantRedisMessageInterceptor(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java new file mode 100644 index 000000000..15c72f992 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.framework.tenant.core.mq; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; +import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; + +/** + * 多租户 {@link AbstractRedisMessage} 拦截器 + * + * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 + * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中 + * + * @author 芋道源码 + */ +public class TenantRedisMessageInterceptor implements RedisMessageInterceptor { + + private static final String HEADER_TENANT_ID = "tenant-id"; + + @Override + public void sendMessageBefore(AbstractRedisMessage message) { + Long tenantId = TenantContextHolder.getTenantId(); + if (tenantId != null) { + message.addHeader(HEADER_TENANT_ID, tenantId.toString()); + } + } + + @Override + public void consumeMessageBefore(AbstractRedisMessage message) { + String tenantIdStr = message.getHeader(HEADER_TENANT_ID); + if (StrUtil.isNotEmpty(tenantIdStr)) { + TenantContextHolder.setTenantId(Long.valueOf(tenantIdStr)); + } + } + + @Override + public void consumeMessageAfter(AbstractRedisMessage message) { + // 注意,Consumer 是一个逻辑的入口,所以不考虑原本上下文就存在租户编号的情况 + TenantContextHolder.clear(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java index b2be77ae1..0856628b0 100644 --- a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java @@ -1,9 +1,9 @@ /** * 多租户,支持如下层面: * 1. DB:基于 MyBatis Plus 多租户的功能实现。 - * 2. Web:请求 HTTP API 时,Header 带上 tenant-id 租户编号。 + * 2. Web:请求 HTTP API 时,解析 Header 的 tenant-id 租户编号,添加到租户上下文。 * 3. Job:在 JobHandler 执行任务时,会按照每个租户,都独立并行执行一次。 - * 4. MQ:TODO + * 4. MQ:在 Producer 发送消息时,Header 带上 tenant-id 租户编号;在 Consumer 消费消息时,将 Header 的 tenant-id 租户编号,添加到租户上下文。 * 5. Async:异步需要保证 ThreadLocal 的传递性,通过使用阿里开源的 TransmittableThreadLocal 实现。相关的改造点,可见: * 1)Spring Async: * {@link cn.iocoder.yudao.framework.quartz.config.YudaoAsyncAutoConfiguration#threadPoolTaskExecutorBeanPostProcessor()} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories index 52ea04fb2..5340df3a8 100644 --- a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories @@ -1,4 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.iocoder.yudao.framework.tenant.config.YudaoTenantDatabaseAutoConfiguration,\ cn.iocoder.yudao.framework.tenant.config.YudaoTenantWebAutoConfiguration,\ - cn.iocoder.yudao.framework.tenant.config.YudaoTenantJobAutoConfiguration + cn.iocoder.yudao.framework.tenant.config.YudaoTenantJobAutoConfiguration,\ + cn.iocoder.yudao.framework.tenant.config.YudaoTenantMQAutoConfiguration