feat(tenant): 租户-项目两级架构 Phase 0+1 — 基础框架层
Phase 0 技术验证: - ProjectBaseDO extends TenantBaseDO,新增 projectId 字段 - ProjectContextHolder (TransmittableThreadLocal) 项目上下文管理 - ProjectDatabaseInterceptor 实现 TenantLineHandler,返回 project_id 列 - 注册第二个 TenantLineInnerInterceptor,通过 @Qualifier 保证初始化顺序 - DualInterceptorTest 11 个用例验证双拦截器 SQL 注入(SELECT/INSERT/UPDATE/DELETE + JOIN + 子查询) Phase 1 基础框架层: - @ProjectIgnore 注解 + ProjectIgnoreAspect (SpEL 条件支持) - ProjectUtils 工具类 (execute/executeIgnore) - ProjectContextWebFilter 从请求 Header 解析 project-id - WebFrameworkUtils 扩展 HEADER_PROJECT_ID + getProjectId() - WebFilterOrderEnum 新增 PROJECT_CONTEXT_FILTER、PROJECT_SECURITY_FILTER - RPC: TenantRequestInterceptor 自动透传 project-id - MQ: Kafka/RocketMQ/RabbitMQ/Redis 全部支持 project-id 发送与消费 - @ProjectJob + ProjectJobAspect (@Order(2) 内层,配合 @TenantJob 使用) - TenantJobAspect 补充 @Order(1) 外层标记 - ProjectDO + UserProjectDO + Mapper + ProjectService + ProjectController - ProjectCommonApi (Feign) + ProjectApiImpl + ProjectFrameworkServiceImpl (Guava 缓存) - TenantServiceImpl.createTenant() 联动创建默认项目 - ErrorCodeConstants 新增 1-002-030-xxx 项目错误码 Review 修复: - Bean 初始化顺序: projectLineInnerInterceptor 依赖 @Qualifier 确保顺序 - computeIgnoreTable: @ProjectIgnore 检查优先于 isAssignableFrom - ProjectFrameworkServiceImpl 注册为 Spring Bean - RocketMQ SendHook: project-id 独立于 tenantId 传播 - createDefaultProject 移入 TenantUtils.execute 事务块内 - 全部 MQ/RPC 统一使用 HEADER_PROJECT_ID 常量 - ProjectJobAspect 增加租户上下文防御校验 - 移除 ProjectDO/UserProjectDO 无效的 @KeySequence - ProjectServiceImpl/ProjectApiImpl 移除冗余 TenantUtils.execute 嵌套 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
package com.viewsh.framework.common.biz.system.project;
|
||||
|
||||
import com.viewsh.framework.common.enums.RpcConstants;
|
||||
import com.viewsh.framework.common.pojo.CommonResult;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FeignClient(name = RpcConstants.SYSTEM_NAME)
|
||||
@Tag(name = "RPC 服务 - 项目")
|
||||
public interface ProjectCommonApi {
|
||||
|
||||
String PREFIX = RpcConstants.SYSTEM_PREFIX + "/project";
|
||||
|
||||
@GetMapping(PREFIX + "/id-list")
|
||||
@Operation(summary = "获得当前租户下所有项目编号")
|
||||
CommonResult<List<Long>> getProjectIdList();
|
||||
|
||||
@GetMapping(PREFIX + "/valid")
|
||||
@Operation(summary = "校验项目是否合法")
|
||||
@Parameter(name = "id", description = "项目编号", required = true, example = "1024")
|
||||
CommonResult<Boolean> validProject(@RequestParam("id") Long id);
|
||||
|
||||
}
|
||||
@@ -1,38 +1,42 @@
|
||||
package com.viewsh.framework.common.enums;
|
||||
|
||||
/**
|
||||
* Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
|
||||
*
|
||||
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enum 包下
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface WebFilterOrderEnum {
|
||||
|
||||
int CORS_FILTER = Integer.MIN_VALUE;
|
||||
|
||||
int TRACE_FILTER = CORS_FILTER + 1;
|
||||
|
||||
int ENV_TAG_FILTER = TRACE_FILTER + 1;
|
||||
|
||||
int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500;
|
||||
|
||||
int API_ENCRYPT_FILTER = REQUEST_BODY_CACHE_FILTER + 1;
|
||||
|
||||
// OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等
|
||||
|
||||
int TENANT_CONTEXT_FILTER = - 104; // 需要保证在 ApiAccessLogFilter 前面
|
||||
|
||||
int API_ACCESS_LOG_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面
|
||||
|
||||
int XSS_FILTER = -102; // 需要保证在 RequestBodyCacheFilter 后面
|
||||
|
||||
// Spring Security Filter 默认为 -100,可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类
|
||||
|
||||
int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面
|
||||
|
||||
int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面
|
||||
|
||||
int DEMO_FILTER = Integer.MAX_VALUE;
|
||||
|
||||
}
|
||||
package com.viewsh.framework.common.enums;
|
||||
|
||||
/**
|
||||
* Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
|
||||
*
|
||||
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enum 包下
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface WebFilterOrderEnum {
|
||||
|
||||
int CORS_FILTER = Integer.MIN_VALUE;
|
||||
|
||||
int TRACE_FILTER = CORS_FILTER + 1;
|
||||
|
||||
int ENV_TAG_FILTER = TRACE_FILTER + 1;
|
||||
|
||||
int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500;
|
||||
|
||||
int API_ENCRYPT_FILTER = REQUEST_BODY_CACHE_FILTER + 1;
|
||||
|
||||
// OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等
|
||||
|
||||
int TENANT_CONTEXT_FILTER = -104; // 需要保证在 ApiAccessLogFilter 前面
|
||||
|
||||
int PROJECT_CONTEXT_FILTER = -103; // 需要保证在 ApiAccessLogFilter 前面,紧跟租户上下文之后
|
||||
|
||||
int API_ACCESS_LOG_FILTER = -102; // 需要保证在 RequestBodyCacheFilter 后面
|
||||
|
||||
int XSS_FILTER = -101; // 需要保证在 RequestBodyCacheFilter 后面
|
||||
|
||||
// Spring Security Filter 默认为 -100,可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类
|
||||
|
||||
int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面
|
||||
|
||||
int PROJECT_SECURITY_FILTER = -98; // 需要保证在 Spring Security 过滤器后面,紧跟租户安全过滤之后
|
||||
|
||||
int FLOWABLE_FILTER = -97; // 需要保证在 Spring Security 过滤后面
|
||||
|
||||
int DEMO_FILTER = Integer.MAX_VALUE;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user