Commit 1ce2c09f4790924fa985317cf7b1f24a78b69616
1 parent
a2315826
增加 Tenant MQ 的支持
Showing
12 changed files
with
95 additions
and
31 deletions
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 |