From 2c0f2f89521b160983a51b4c4858a8a9c490639e Mon Sep 17 00:00:00 2001 From: zhangjf Date: Thu, 19 Feb 2026 20:45:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9C=8D=E5=8A=A1=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E5=A2=9E=E5=8A=A0=E5=A4=9A?= =?UTF-8?q?=E7=A7=9F=E6=88=B7=E6=B7=B7=E5=90=88=E6=A8=A1=E5=BC=8F=E8=B4=9F?= =?UTF-8?q?=E8=BD=BD=E5=9D=87=E8=A1=A1=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## application-docker.yml 更新 ### fund-sys - Nacos 元数据配置:tenant-id, tenant-group - 租户路由配置:启用租户感知负载均衡 - VIP 租户列表定义 - 监控标签:tenant_id, tenant_group, instance_type - 日志格式:增加 tenantId MDC ### fund-gateway - 全局跨域配置 - 多租户路由配置(所有服务的VIP租户列表) - 监控和日志增强 ## TenantRoutingProperties 更新 - 添加 @ConfigurationProperties 支持 YAML 配置绑定 - 新增 vip-tenants 列表属性 - 新增 fallback-to-shared 回退策略属性 - 新增 getVipTenants(), isVipTenant(), isFallbackToShared() 方法 --- .../config/TenantRoutingProperties.java | 118 +++++++++++++++--- .../src/main/resources/application-docker.yml | 59 ++++++++- .../src/main/resources/application-docker.yml | 40 +++++- 3 files changed, 196 insertions(+), 21 deletions(-) diff --git a/fund-common/src/main/java/com/fundplatform/common/config/TenantRoutingProperties.java b/fund-common/src/main/java/com/fundplatform/common/config/TenantRoutingProperties.java index f6b2ce7..3f3e459 100644 --- a/fund-common/src/main/java/com/fundplatform/common/config/TenantRoutingProperties.java +++ b/fund-common/src/main/java/com/fundplatform/common/config/TenantRoutingProperties.java @@ -3,14 +3,32 @@ package com.fundplatform.common.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.HashMap; +import java.util.*; + +/** + * 多租户路由配置属性 + * + *

支持从 YAML 配置文件读取租户路由配置

+ * + *

配置示例:

+ *
+ * tenant:
+ *   routing:
+ *     enabled: true
+ *     default-tenant-id: 1
+ *     services:
+ *       fund-sys:
+ *         vip-tenants:
+ *           - TENANT_VIP_001
+ *         fallback-to-shared: true
+ * 
+ */ +@Component +@ConfigurationProperties(prefix = "tenant.routing") public class TenantRoutingProperties { /** 是否启用租户路由 */ - private boolean enabled = false; + private boolean enabled = true; /** 租户 ID 请求头 */ private String tenantHeader = "X-Tenant-Id"; @@ -21,6 +39,9 @@ public class TenantRoutingProperties { /** 服务组分隔符 */ private String groupSeparator = "TENANT_"; + /** 默认租户 ID(当未指定时使用) */ + private String defaultTenantId = "1"; + /** 共享服务列表(不区分租户,所有租户共用) */ private List sharedServices = Arrays.asList( "fund-gateway", @@ -28,10 +49,10 @@ public class TenantRoutingProperties { "fund-file" ); - /** 默认租户 ID(当未指定时使用) */ - private String defaultTenantId = "1"; - /** 租户服务配置映射 */ + private Map services = new HashMap<>(); + + /** 租户服务配置(旧版兼容) */ private Map tenantConfigs = new HashMap<>(); // Getters and Setters @@ -68,6 +89,14 @@ public class TenantRoutingProperties { this.groupSeparator = groupSeparator; } + public String getDefaultTenantId() { + return defaultTenantId; + } + + public void setDefaultTenantId(String defaultTenantId) { + this.defaultTenantId = defaultTenantId; + } + public List getSharedServices() { return sharedServices; } @@ -76,12 +105,12 @@ public class TenantRoutingProperties { this.sharedServices = sharedServices; } - public String getDefaultTenantId() { - return defaultTenantId; + public Map getServices() { + return services; } - public void setDefaultTenantId(String defaultTenantId) { - this.defaultTenantId = defaultTenantId; + public void setServices(Map services) { + this.services = services; } public Map getTenantConfigs() { @@ -109,21 +138,76 @@ public class TenantRoutingProperties { return sharedServices.contains(serviceName); } + /** + * 获取服务的 VIP 租户列表 + */ + public List getVipTenants(String serviceName) { + TenantServiceConfig config = services.get(serviceName); + if (config != null && config.getVipTenants() != null) { + return config.getVipTenants(); + } + return Collections.emptyList(); + } + + /** + * 判断租户是否为某个服务的 VIP 租户 + */ + public boolean isVipTenant(String serviceName, String tenantGroup) { + if (tenantGroup == null || tenantGroup.isEmpty()) { + return false; + } + List vipTenants = getVipTenants(serviceName); + return vipTenants.contains(tenantGroup); + } + + /** + * 判断服务是否启用了共享实例回退 + */ + public boolean isFallbackToShared(String serviceName) { + TenantServiceConfig config = services.get(serviceName); + if (config != null) { + return config.isFallbackToShared(); + } + return true; // 默认启用回退 + } + /** * 租户服务配置 */ public static class TenantServiceConfig { + /** VIP 租户列表(优先路由到专属实例) */ + private List vipTenants = new ArrayList<>(); + + /** 是否回退到共享实例 */ + private boolean fallbackToShared = true; + /** 租户 ID */ private String tenantId; /** 服务实例配置 */ - private Map services = new HashMap<>(); + private Map instances = new HashMap<>(); /** 数据库配置(一库一租户模式) */ private DatabaseConfig database; // Getters and Setters + public List getVipTenants() { + return vipTenants; + } + + public void setVipTenants(List vipTenants) { + this.vipTenants = vipTenants != null ? vipTenants : new ArrayList<>(); + } + + public boolean isFallbackToShared() { + return fallbackToShared; + } + + public void setFallbackToShared(boolean fallbackToShared) { + this.fallbackToShared = fallbackToShared; + } + public String getTenantId() { return tenantId; } @@ -132,12 +216,12 @@ public class TenantRoutingProperties { this.tenantId = tenantId; } - public Map getServices() { - return services; + public Map getInstances() { + return instances; } - public void setServices(Map services) { - this.services = services; + public void setInstances(Map instances) { + this.instances = instances; } public DatabaseConfig getDatabase() { diff --git a/fund-gateway/src/main/resources/application-docker.yml b/fund-gateway/src/main/resources/application-docker.yml index 56b4b87..fb36099 100644 --- a/fund-gateway/src/main/resources/application-docker.yml +++ b/fund-gateway/src/main/resources/application-docker.yml @@ -17,12 +17,22 @@ spring: namespace: ${NACOS_NAMESPACE:} group: DEFAULT_GROUP enabled: true + metadata: + service-type: gateway gateway: discovery: locator: enabled: true lower-case-service-id: true + # 全局跨域配置 + globalcors: + cors-configurations: + '[/**]': + allowed-origins: "*" + allowed-methods: "*" + allowed-headers: "*" + allow-credentials: true routes: - id: fund-sys uri: lb://fund-sys @@ -30,6 +40,7 @@ spring: - Path=/api/sys/** filters: - StripPrefix=1 + # 租户感知负载均衡(自动添加 tenant-group 请求头) - id: fund-cust uri: lb://fund-cust predicates: @@ -75,8 +86,46 @@ spring: # JWT 配置 jwt: - secret: YourSecretKeyForJWTTokenGenerationMustBeAtLeast256BitsLong - expiration: 86400000 + secret: ${JWT_SECRET:YourSecretKeyForJWTTokenGenerationMustBeAtLeast256BitsLong} + expiration: ${JWT_EXPIRATION:86400000} + +# ==================== 多租户混合模式配置 ==================== +tenant: + routing: + # 启用租户感知负载均衡 + enabled: true + # 默认租户ID + default-tenant-id: 1 + # 租户服务配置(定义每个服务的VIP租户) + services: + fund-sys: + vip-tenants: + - TENANT_VIP_001 + - TENANT_VIP_002 + fallback-to-shared: true + fund-cust: + vip-tenants: + - TENANT_VIP_001 + fallback-to-shared: true + fund-proj: + vip-tenants: + - TENANT_VIP_001 + fallback-to-shared: true + fund-req: + vip-tenants: [] + fallback-to-shared: true + fund-exp: + vip-tenants: [] + fallback-to-shared: true + fund-receipt: + vip-tenants: [] + fallback-to-shared: true + fund-report: + vip-tenants: [] + fallback-to-shared: true + fund-file: + vip-tenants: [] + fallback-to-shared: true # Actuator 监控端点配置 management: @@ -98,6 +147,7 @@ management: metrics: tags: application: ${spring.application.name} + service-type: gateway distribution: percentiles-histogram: http.server.requests: true @@ -109,5 +159,8 @@ logging: level: root: INFO com.fundplatform: DEBUG + # 多租户负载均衡日志 + com.fundplatform.common.loadbalancer: DEBUG + com.fundplatform.gateway.filter: DEBUG pattern: - console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n" + console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{tenantId}] %-5level %logger{36} - %msg%n" diff --git a/fund-sys/src/main/resources/application-docker.yml b/fund-sys/src/main/resources/application-docker.yml index 642df80..3144570 100644 --- a/fund-sys/src/main/resources/application-docker.yml +++ b/fund-sys/src/main/resources/application-docker.yml @@ -38,6 +38,12 @@ spring: namespace: ${NACOS_NAMESPACE:} group: DEFAULT_GROUP enabled: true + # 多租户元数据配置(支持混合模式负载均衡) + metadata: + tenant-id: ${TENANT_ID:1} + tenant-group: ${TENANT_GROUP:} + # 服务实例权重(VIP实例可配置更高权重) + weight: ${NACOS_WEIGHT:1} # MyBatis Plus 配置 mybatis-plus: @@ -49,6 +55,31 @@ mybatis-plus: logic-delete-value: 1 logic-not-delete-value: 0 +# ==================== 多租户混合模式配置 ==================== +tenant: + routing: + # 启用租户感知负载均衡 + enabled: true + # 默认租户ID + default-tenant-id: 1 + # 租户服务配置 + services: + fund-sys: + # VIP租户列表(优先路由到专属实例) + vip-tenants: + - TENANT_VIP_001 + - TENANT_VIP_002 + # 共享实例回退策略:当无专属实例时回退到共享实例 + fallback-to-shared: true + fund-cust: + vip-tenants: + - TENANT_VIP_001 + fallback-to-shared: true + fund-proj: + vip-tenants: + - TENANT_VIP_001 + fallback-to-shared: true + # Actuator 监控端点配置 management: endpoints: @@ -69,6 +100,10 @@ management: metrics: tags: application: ${spring.application.name} + # 多租户监控标签 + tenant_id: ${TENANT_ID:1} + tenant_group: ${TENANT_GROUP:shared} + instance_type: ${TENANT_GROUP:shared} distribution: percentiles-histogram: http.server.requests: true @@ -80,5 +115,8 @@ logging: level: root: INFO com.fundplatform: DEBUG + # 多租户负载均衡日志 + com.fundplatform.common.loadbalancer: DEBUG + com.fundplatform.common.nacos: DEBUG pattern: - console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n" + console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{tenantId}] %-5level %logger{36} - %msg%n"