Explorar el Código

增加 Tenant MQ 的支持

YunaiV hace 3 años
padre
commit
1ce2c09f47
Se han modificado 12 ficheros con 95 adiciones y 31 borrados
  1. 4 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http
  2. 1 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/tenant/SysTenantController.java
  3. 1 1
      yudao-admin-server/src/main/resources/application.yaml
  4. 1 0
      yudao-admin-server/src/main/resources/mybatis-config/mybatis-config.xml
  5. 4 1
      yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java
  6. 3 2
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java
  7. 6 0
      yudao-framework/yudao-spring-boot-starter-tenant/pom.xml
  8. 9 22
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java
  9. 20 0
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantMQAutoConfiguration.java
  10. 42 0
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java
  11. 2 2
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java
  12. 2 1
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories

+ 4 - 1
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

+ 1 - 1
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<Long> getTenantIdByName(@RequestParam("name") String name) {
         if (Objects.equals("芋道源码", name)) {
-            return CommonResult.success(0L);
+            return CommonResult.success(1L);
         }
         return CommonResult.success(null);
     }

+ 1 - 1
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 # 打印日志

+ 1 - 0
yudao-admin-server/src/main/resources/mybatis-config/mybatis-config.xml

@@ -6,6 +6,7 @@
     <settings>
         <setting name="lazyLoadingEnabled" value="false" />
         <setting name="mapUnderscoreToCamelCase" value="true"/>
+        <setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
     </settings>
     <typeAliases>
         <typeAlias type="org.activiti.engine.impl.persistence.ByteArrayRefTypeHandler" alias="ByteArrayRefTypeHandler"/>

+ 4 - 1
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<String, String> headers = new HashMap<>();
+    private Map<String, String> headers = new HashMap<>();
 
     public String getHeader(String key) {
         return headers.get(key);

+ 3 - 2
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<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors());
-        inners.add(0, inner);
+        inners.add(index, inner);
         interceptor.setInterceptors(inners);
     }
 

+ 6 - 0
yudao-framework/yudao-spring-boot-starter-tenant/pom.xml

@@ -38,6 +38,12 @@
             <groupId>cn.iocoder.boot</groupId>
             <artifactId>yudao-spring-boot-starter-job</artifactId>
         </dependency>
+
+        <!-- 消息队列相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-mq</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 9 - 22
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;
     }
 
 }

+ 20 - 0
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();
+    }
+
+}

+ 42 - 0
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();
+    }
+
+}

+ 2 - 2
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()}

+ 2 - 1
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