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 1 ### 请求 /login 接口 => 成功
2 2 POST {{baseUrl}}/login
3 3 Content-Type: application/json
4   -tenant-id: 0
  4 +tenant-id: 1
5 5  
6 6 {
7 7 "username": "admin",
... ... @@ -13,10 +13,13 @@ tenant-id: 0
13 13 ### 请求 /get-permission-info 接口 => 成功
14 14 GET {{baseUrl}}/get-permission-info
15 15 Authorization: Bearer {{token}}
  16 +tenant-id: 1
16 17  
17 18 ### 请求 /list-menus 接口 => 成功
18 19 GET {{baseUrl}}/list-menus
19 20 Authorization: Bearer {{token}}
  21 +#Authorization: Bearer 0d161f69c9ac4c7f836e1b850715a7b0
  22 +tenant-id: 1
20 23  
21 24 ### 请求 /druid/xxx 接口 => 失败 TODO 临时测试
22 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 21 @ApiImplicitParam(name = "name", value = "租户名", required = true, example = "芋道源码", dataTypeClass = Long.class)
22 22 public CommonResult<Long> getTenantIdByName(@RequestParam("name") String name) {
23 23 if (Objects.equals("芋道源码", name)) {
24   - return CommonResult.success(0L);
  24 + return CommonResult.success(1L);
25 25 }
26 26 return CommonResult.success(null);
27 27 }
... ...
yudao-admin-server/src/main/resources/application.yaml
... ... @@ -22,7 +22,7 @@ spring:
22 22  
23 23 # MyBatis Plus 的配置项
24 24 mybatis-plus:
25   -# 在 mybatis-config/mybatis-config.xml 中设置
  25 +# 在 mybatis-config/mybatis-config.xml 中设置 TODO jason:看看有没其它解决方案
26 26 # configuration:
27 27 # map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
28 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 <settings>
7 7 <setting name="lazyLoadingEnabled" value="false" />
8 8 <setting name="mapUnderscoreToCamelCase" value="true"/>
  9 + <setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
9 10 </settings>
10 11 <typeAliases>
11 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 1 package cn.iocoder.yudao.framework.mq.core.message;
2 2  
  3 +import lombok.Data;
  4 +
3 5 import java.util.HashMap;
4 6 import java.util.Map;
5 7  
... ... @@ -8,12 +10,13 @@ import java.util.Map;
8 10 *
9 11 * @author 芋道源码
10 12 */
  13 +@Data
11 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 21 public String getHeader(String key) {
19 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 40 *
41 41 * @param interceptor 链
42 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 46 List<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors());
46   - inners.add(0, inner);
  47 + inners.add(index, inner);
47 48 interceptor.setInterceptors(inners);
48 49 }
49 50  
... ...
yudao-framework/yudao-spring-boot-starter-tenant/pom.xml
... ... @@ -38,6 +38,12 @@
38 38 <groupId>cn.iocoder.boot</groupId>
39 39 <artifactId>yudao-spring-boot-starter-job</artifactId>
40 40 </dependency>
  41 +
  42 + <!-- 消息队列相关 -->
  43 + <dependency>
  44 + <groupId>cn.iocoder.boot</groupId>
  45 + <artifactId>yudao-spring-boot-starter-mq</artifactId>
  46 + </dependency>
41 47 </dependencies>
42 48  
43 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 4 import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
5 5 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
6 6 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
7   -import org.springframework.beans.BeansException;
8   -import org.springframework.beans.factory.config.BeanPostProcessor;
9 7 import org.springframework.boot.context.properties.EnableConfigurationProperties;
10 8 import org.springframework.context.annotation.Bean;
  9 +import org.springframework.context.annotation.Configuration;
11 10  
12 11 /**
13 12 * 多租户针对 DB 的自动配置
14 13 *
15 14 * @author 芋道源码
16 15 */
  16 +@Configuration
17 17 @EnableConfigurationProperties(TenantProperties.class)
18 18 public class YudaoTenantDatabaseAutoConfiguration {
19 19  
20 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 3 * 1. DB:基于 MyBatis Plus 多租户的功能实现。
4   - * 2. Web:请求 HTTP API 时,Header 带上 tenant-id 租户编号。
  4 + * 2. Web:请求 HTTP API 时,解析 Header 的 tenant-id 租户编号,添加到租户上下文。
5 5 * 3. Job:在 JobHandler 执行任务时,会按照每个租户,都独立并行执行一次。
6   - * 4. MQ:TODO
  6 + * 4. MQ:在 Producer 发送消息时,Header 带上 tenant-id 租户编号;在 Consumer 消费消息时,将 Header 的 tenant-id 租户编号,添加到租户上下文。
7 7 * 5. Async:异步需要保证 ThreadLocal 的传递性,通过使用阿里开源的 TransmittableThreadLocal 实现。相关的改造点,可见:
8 8 * 1)Spring Async:
9 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 1 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2 2 cn.iocoder.yudao.framework.tenant.config.YudaoTenantDatabaseAutoConfiguration,\
3 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
... ...