|
|
@@ -11,31 +11,29 @@ import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
import org.springframework.util.StringUtils;
|
|
|
+import org.springframework.web.servlet.HandlerMapping;
|
|
|
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
|
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.util.Map;
|
|
|
|
|
|
/**
|
|
|
* 内容审计模块数据源切换拦截器
|
|
|
* <p>
|
|
|
- * 通过请求头 datasource-type 控制数据源切换:
|
|
|
- * <ul>
|
|
|
- * <li><b>saas</b> - 切换到指定租户的数据库(需配合请求参数 tenantId 指定租户ID)</li>
|
|
|
- * <li><b>admin</b> - 使用总库 MASTER 数据源(默认行为,不切库)</li>
|
|
|
- * </ul>
|
|
|
- * <p>
|
|
|
- * 请求结束后自动清除租户数据源上下文,恢复到 MASTER。
|
|
|
+ * 按以下优先级获取租户ID并切换数据源:
|
|
|
+ * <ol>
|
|
|
+ * <li>请求参数 {@code tenantId}</li>
|
|
|
+ * <li>请求头 {@code tenant-id}</li>
|
|
|
+ * <li>URL 路径变量 {@code {tenantId}} / {@code {companyId}}</li>
|
|
|
+ * </ol>
|
|
|
+ * 无法获取则保持 MASTER 数据源,请求结束后自动清理恢复到 MASTER。
|
|
|
*/
|
|
|
@Component
|
|
|
public class AdminTenantDataSourceInterceptor extends HandlerInterceptorAdapter {
|
|
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(AdminTenantDataSourceInterceptor.class);
|
|
|
|
|
|
- /** 请求头: 数据源类型, saas=租户库, admin=总库 */
|
|
|
- private static final String HEADER_DS_TYPE = "datasource-type";
|
|
|
- /** 数据源类型: 租户库 */
|
|
|
- private static final String DS_TYPE_SAAS = "saas";
|
|
|
/** 请求头: 租户ID */
|
|
|
private static final String HEADER_TENANT_ID = "tenant-id";
|
|
|
|
|
|
@@ -47,14 +45,19 @@ public class AdminTenantDataSourceInterceptor extends HandlerInterceptorAdapter
|
|
|
|
|
|
@Override
|
|
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
|
|
-
|
|
|
- // saas 模式: 优先从请求参数获取租户ID, 兜底从请求头获取
|
|
|
+
|
|
|
String tenantIdStr = request.getParameter("tenantId");
|
|
|
if (!StringUtils.hasText(tenantIdStr)) {
|
|
|
tenantIdStr = request.getHeader(HEADER_TENANT_ID);
|
|
|
}
|
|
|
if (!StringUtils.hasText(tenantIdStr)) {
|
|
|
- log.warn("datasource-type=saas 但未传 tenantId 参数, 无法切库");
|
|
|
+ Map<String, String> pathVars = (Map<String, String>) request.getAttribute(
|
|
|
+ HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
|
|
+ if (pathVars != null) {
|
|
|
+ tenantIdStr = pathVars.get("tenantId");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!StringUtils.hasText(tenantIdStr)) {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
@@ -77,12 +80,8 @@ public class AdminTenantDataSourceInterceptor extends HandlerInterceptorAdapter
|
|
|
@Override
|
|
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
|
|
Object handler, Exception ex) {
|
|
|
- // 仅 saas 模式需要清理租户数据源上下文
|
|
|
- String dsType = request.getHeader(HEADER_DS_TYPE);
|
|
|
- if (DS_TYPE_SAAS.equals(dsType)) {
|
|
|
- RedisTenantContext.clear();
|
|
|
- tenantDataSourceManager.clear();
|
|
|
- DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
|
|
|
- }
|
|
|
+ RedisTenantContext.clear();
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
+ DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
|
|
|
}
|
|
|
-}
|
|
|
+}
|