Commit 1ce2c09f4790924fa985317cf7b1f24a78b69616

Authored by YunaiV
1 parent a2315826

增加 Tenant MQ 的支持

yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http
1 ### 请求 /login 接口 => 成功 1 ### 请求 /login 接口 => 成功
2 POST {{baseUrl}}/login 2 POST {{baseUrl}}/login
3 Content-Type: application/json 3 Content-Type: application/json
4 -tenant-id: 0 4 +tenant-id: 1
5 5
6 { 6 {
7 "username": "admin", 7 "username": "admin",
@@ -13,10 +13,13 @@ tenant-id: 0 @@ -13,10 +13,13 @@ tenant-id: 0
13 ### 请求 /get-permission-info 接口 => 成功 13 ### 请求 /get-permission-info 接口 => 成功
14 GET {{baseUrl}}/get-permission-info 14 GET {{baseUrl}}/get-permission-info
15 Authorization: Bearer {{token}} 15 Authorization: Bearer {{token}}
  16 +tenant-id: 1
16 17
17 ### 请求 /list-menus 接口 => 成功 18 ### 请求 /list-menus 接口 => 成功
18 GET {{baseUrl}}/list-menus 19 GET {{baseUrl}}/list-menus
19 Authorization: Bearer {{token}} 20 Authorization: Bearer {{token}}
  21 +#Authorization: Bearer 0d161f69c9ac4c7f836e1b850715a7b0
  22 +tenant-id: 1
20 23
21 ### 请求 /druid/xxx 接口 => 失败 TODO 临时测试 24 ### 请求 /druid/xxx 接口 => 失败 TODO 临时测试
22 GET http://127.0.0.1:8080/druid/123 25 GET http://127.0.0.1:8080/druid/123
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/tenant/SysTenantController.java
@@ -21,7 +21,7 @@ public class SysTenantController { @@ -21,7 +21,7 @@ public class SysTenantController {
21 @ApiImplicitParam(name = "name", value = "租户名", required = true, example = "芋道源码", dataTypeClass = Long.class) 21 @ApiImplicitParam(name = "name", value = "租户名", required = true, example = "芋道源码", dataTypeClass = Long.class)
22 public CommonResult<Long> getTenantIdByName(@RequestParam("name") String name) { 22 public CommonResult<Long> getTenantIdByName(@RequestParam("name") String name) {
23 if (Objects.equals("芋道源码", name)) { 23 if (Objects.equals("芋道源码", name)) {
24 - return CommonResult.success(0L); 24 + return CommonResult.success(1L);
25 } 25 }
26 return CommonResult.success(null); 26 return CommonResult.success(null);
27 } 27 }
yudao-admin-server/src/main/resources/application.yaml
@@ -22,7 +22,7 @@ spring: @@ -22,7 +22,7 @@ spring:
22 22
23 # MyBatis Plus 的配置项 23 # MyBatis Plus 的配置项
24 mybatis-plus: 24 mybatis-plus:
25 -# 在 mybatis-config/mybatis-config.xml 中设置 25 +# 在 mybatis-config/mybatis-config.xml 中设置 TODO jason:看看有没其它解决方案
26 # configuration: 26 # configuration:
27 # map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 27 # map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
28 # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志 28 # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志
yudao-admin-server/src/main/resources/mybatis-config/mybatis-config.xml
@@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
6 <settings> 6 <settings>
7 <setting name="lazyLoadingEnabled" value="false" /> 7 <setting name="lazyLoadingEnabled" value="false" />
8 <setting name="mapUnderscoreToCamelCase" value="true"/> 8 <setting name="mapUnderscoreToCamelCase" value="true"/>
  9 + <setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
9 </settings> 10 </settings>
10 <typeAliases> 11 <typeAliases>
11 <typeAlias type="org.activiti.engine.impl.persistence.ByteArrayRefTypeHandler" alias="ByteArrayRefTypeHandler"/> 12 <typeAlias type="org.activiti.engine.impl.persistence.ByteArrayRefTypeHandler" alias="ByteArrayRefTypeHandler"/>
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java
1 package cn.iocoder.yudao.framework.mq.core.message; 1 package cn.iocoder.yudao.framework.mq.core.message;
2 2
  3 +import lombok.Data;
  4 +
3 import java.util.HashMap; 5 import java.util.HashMap;
4 import java.util.Map; 6 import java.util.Map;
5 7
@@ -8,12 +10,13 @@ import java.util.Map; @@ -8,12 +10,13 @@ import java.util.Map;
8 * 10 *
9 * @author 芋道源码 11 * @author 芋道源码
10 */ 12 */
  13 +@Data
11 public abstract class AbstractRedisMessage { 14 public abstract class AbstractRedisMessage {
12 15
13 /** 16 /**
14 * 头 17 * 头
15 */ 18 */
16 - private final Map<String, String> headers = new HashMap<>(); 19 + private Map<String, String> headers = new HashMap<>();
17 20
18 public String getHeader(String key) { 21 public String getHeader(String key) {
19 return headers.get(key); 22 return headers.get(key);
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 { @@ -40,10 +40,11 @@ public class MyBatisUtils {
40 * 40 *
41 * @param interceptor 链 41 * @param interceptor 链
42 * @param inner 拦截器 42 * @param inner 拦截器
  43 + * @param index 位置
43 */ 44 */
44 - public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner) { 45 + public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner, int index) {
45 List<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors()); 46 List<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors());
46 - inners.add(0, inner); 47 + inners.add(index, inner);
47 interceptor.setInterceptors(inners); 48 interceptor.setInterceptors(inners);
48 } 49 }
49 50
yudao-framework/yudao-spring-boot-starter-tenant/pom.xml
@@ -38,6 +38,12 @@ @@ -38,6 +38,12 @@
38 <groupId>cn.iocoder.boot</groupId> 38 <groupId>cn.iocoder.boot</groupId>
39 <artifactId>yudao-spring-boot-starter-job</artifactId> 39 <artifactId>yudao-spring-boot-starter-job</artifactId>
40 </dependency> 40 </dependency>
  41 +
  42 + <!-- 消息队列相关 -->
  43 + <dependency>
  44 + <groupId>cn.iocoder.boot</groupId>
  45 + <artifactId>yudao-spring-boot-starter-mq</artifactId>
  46 + </dependency>
41 </dependencies> 47 </dependencies>
42 48
43 </project> 49 </project>
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; @@ -4,40 +4,27 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
4 import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; 4 import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
5 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 5 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
6 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; 6 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
7 -import org.springframework.beans.BeansException;  
8 -import org.springframework.beans.factory.config.BeanPostProcessor;  
9 import org.springframework.boot.context.properties.EnableConfigurationProperties; 7 import org.springframework.boot.context.properties.EnableConfigurationProperties;
10 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.Bean;
  9 +import org.springframework.context.annotation.Configuration;
11 10
12 /** 11 /**
13 * 多租户针对 DB 的自动配置 12 * 多租户针对 DB 的自动配置
14 * 13 *
15 * @author 芋道源码 14 * @author 芋道源码
16 */ 15 */
  16 +@Configuration
17 @EnableConfigurationProperties(TenantProperties.class) 17 @EnableConfigurationProperties(TenantProperties.class)
18 public class YudaoTenantDatabaseAutoConfiguration { 18 public class YudaoTenantDatabaseAutoConfiguration {
19 19
20 @Bean 20 @Bean
21 - public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties) {  
22 - return new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties));  
23 - }  
24 -  
25 - @Bean  
26 - public BeanPostProcessor mybatisPlusInterceptorBeanPostProcessor(TenantLineInnerInterceptor tenantLineInnerInterceptor) {  
27 - return new BeanPostProcessor() {  
28 -  
29 - @Override  
30 - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
31 - if (!(bean instanceof MybatisPlusInterceptor)) {  
32 - return bean;  
33 - }  
34 - // 将 TenantDatabaseInterceptor 添加到最前面  
35 - MybatisPlusInterceptor interceptor = (MybatisPlusInterceptor) bean;  
36 - MyBatisUtils.addInterceptor(interceptor, tenantLineInnerInterceptor);  
37 - return bean;  
38 - }  
39 -  
40 - }; 21 + public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties,
  22 + MybatisPlusInterceptor interceptor) {
  23 + TenantLineInnerInterceptor inner = new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties));
  24 + // 添加到 interceptor 中
  25 + // 需要加在首个,主要是为了在分页插件前面。这个是 MyBatis Plus 的规定
  26 + MyBatisUtils.addInterceptor(interceptor, inner, 0);
  27 + return inner;
41 } 28 }
42 29
43 } 30 }
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantMQAutoConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.tenant.config;
  2 +
  3 +import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor;
  4 +import org.springframework.context.annotation.Bean;
  5 +import org.springframework.context.annotation.Configuration;
  6 +
  7 +/**
  8 + * 多租户针对 MQ 的自动配置
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Configuration
  13 +public class YudaoTenantMQAutoConfiguration {
  14 +
  15 + @Bean
  16 + public TenantRedisMessageInterceptor tenantRedisMessageInterceptor() {
  17 + return new TenantRedisMessageInterceptor();
  18 + }
  19 +
  20 +}
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.tenant.core.mq;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor;
  5 +import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage;
  6 +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
  7 +
  8 +/**
  9 + * 多租户 {@link AbstractRedisMessage} 拦截器
  10 + *
  11 + * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中
  12 + * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public class TenantRedisMessageInterceptor implements RedisMessageInterceptor {
  17 +
  18 + private static final String HEADER_TENANT_ID = "tenant-id";
  19 +
  20 + @Override
  21 + public void sendMessageBefore(AbstractRedisMessage message) {
  22 + Long tenantId = TenantContextHolder.getTenantId();
  23 + if (tenantId != null) {
  24 + message.addHeader(HEADER_TENANT_ID, tenantId.toString());
  25 + }
  26 + }
  27 +
  28 + @Override
  29 + public void consumeMessageBefore(AbstractRedisMessage message) {
  30 + String tenantIdStr = message.getHeader(HEADER_TENANT_ID);
  31 + if (StrUtil.isNotEmpty(tenantIdStr)) {
  32 + TenantContextHolder.setTenantId(Long.valueOf(tenantIdStr));
  33 + }
  34 + }
  35 +
  36 + @Override
  37 + public void consumeMessageAfter(AbstractRedisMessage message) {
  38 + // 注意,Consumer 是一个逻辑的入口,所以不考虑原本上下文就存在租户编号的情况
  39 + TenantContextHolder.clear();
  40 + }
  41 +
  42 +}
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java
1 /** 1 /**
2 * 多租户,支持如下层面: 2 * 多租户,支持如下层面:
3 * 1. DB:基于 MyBatis Plus 多租户的功能实现。 3 * 1. DB:基于 MyBatis Plus 多租户的功能实现。
4 - * 2. Web:请求 HTTP API 时,Header 带上 tenant-id 租户编号。 4 + * 2. Web:请求 HTTP API 时,解析 Header 的 tenant-id 租户编号,添加到租户上下文。
5 * 3. Job:在 JobHandler 执行任务时,会按照每个租户,都独立并行执行一次。 5 * 3. Job:在 JobHandler 执行任务时,会按照每个租户,都独立并行执行一次。
6 - * 4. MQ:TODO 6 + * 4. MQ:在 Producer 发送消息时,Header 带上 tenant-id 租户编号;在 Consumer 消费消息时,将 Header 的 tenant-id 租户编号,添加到租户上下文。
7 * 5. Async:异步需要保证 ThreadLocal 的传递性,通过使用阿里开源的 TransmittableThreadLocal 实现。相关的改造点,可见: 7 * 5. Async:异步需要保证 ThreadLocal 的传递性,通过使用阿里开源的 TransmittableThreadLocal 实现。相关的改造点,可见:
8 * 1)Spring Async: 8 * 1)Spring Async:
9 * {@link cn.iocoder.yudao.framework.quartz.config.YudaoAsyncAutoConfiguration#threadPoolTaskExecutorBeanPostProcessor()} 9 * {@link cn.iocoder.yudao.framework.quartz.config.YudaoAsyncAutoConfiguration#threadPoolTaskExecutorBeanPostProcessor()}
yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories
1 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 1 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2 cn.iocoder.yudao.framework.tenant.config.YudaoTenantDatabaseAutoConfiguration,\ 2 cn.iocoder.yudao.framework.tenant.config.YudaoTenantDatabaseAutoConfiguration,\
3 cn.iocoder.yudao.framework.tenant.config.YudaoTenantWebAutoConfiguration,\ 3 cn.iocoder.yudao.framework.tenant.config.YudaoTenantWebAutoConfiguration,\
4 - cn.iocoder.yudao.framework.tenant.config.YudaoTenantJobAutoConfiguration 4 + cn.iocoder.yudao.framework.tenant.config.YudaoTenantJobAutoConfiguration,\
  5 + cn.iocoder.yudao.framework.tenant.config.YudaoTenantMQAutoConfiguration