|
@@ -0,0 +1,357 @@
|
|
|
|
|
+# Epic-008: Server和Web多租户集成 - Brownfield Enhancement
|
|
|
|
|
+
|
|
|
|
|
+## Epic Goal
|
|
|
|
|
+
|
|
|
|
|
+将当前的单租户系统直接改为多租户系统,基于史诗007已完成的多租户包,在server和web中集成完整的租户数据隔离和租户管理功能,为后续按需拼装单租户或多租户系统提供基础。
|
|
|
|
|
+
|
|
|
|
|
+**核心目标:直接改造现有单租户系统为多租户系统,按照现有包集成模式实现。**
|
|
|
|
|
+
|
|
|
|
|
+## Epic Description
|
|
|
|
|
+
|
|
|
|
|
+### Existing System Context
|
|
|
|
|
+
|
|
|
|
|
+**Current relevant functionality:**
|
|
|
|
|
+- 史诗007已完成11个多租户业务包的复制和26个前端管理界面包的创建
|
|
|
|
|
+- 现有server使用单租户包路由(如`@d8d/user-module`),支持基础的用户、认证、文件等模块
|
|
|
|
|
+- 多租户认证模块包(`@d8d/auth-module-mt`)中已实现租户上下文管理,在认证中间件中设置`c.set('tenantId', user.tenantId)`
|
|
|
|
|
+- web应用已集成用户管理UI包(`@d8d/user-management-ui`),展示包集成模式
|
|
|
|
|
+- web中routes.tsx使用`import { UserManagement } from '@d8d/user-management-ui'`模式
|
|
|
|
|
+- web中api_init.ts使用`userClientManager.init('/api/v1/users')`初始化客户端
|
|
|
|
|
+- 共享包(shared-crud、shared-types、shared-utils)在两套系统中复用
|
|
|
|
|
+- 数据库已支持多租户表结构(带_mt后缀的表)
|
|
|
|
|
+
|
|
|
|
|
+**Current limitations:**
|
|
|
|
|
+- server只能运行单租户模式,不支持多租户
|
|
|
|
|
+- web应用只有用户管理使用包模式,其他管理界面仍为本地实现
|
|
|
|
|
+- 缺乏租户上下文管理和租户数据隔离
|
|
|
|
|
+- 认证系统不支持多租户用户隔离
|
|
|
|
|
+- 前端路由不支持租户感知
|
|
|
|
|
+
|
|
|
|
|
+**Technology stack:**
|
|
|
|
|
+- Backend: Node.js, TypeScript, Hono, TypeORM, PostgreSQL
|
|
|
|
|
+- Frontend: React 19, TypeScript, TanStack Query, React Hook Form
|
|
|
|
|
+- Authentication: JWT, Redis session management
|
|
|
|
|
+- Database: PostgreSQL with tenant isolation
|
|
|
|
|
+- Package Management: pnpm workspace
|
|
|
|
|
+
|
|
|
|
|
+**Integration points:**
|
|
|
|
|
+- 现有server路由注册机制
|
|
|
|
|
+- 认证中间件和用户上下文
|
|
|
|
|
+- 数据库连接和实体管理
|
|
|
|
|
+- 前端API客户端和路由配置
|
|
|
|
|
+- 共享UI组件包
|
|
|
|
|
+
|
|
|
|
|
+### Enhancement Details
|
|
|
|
|
+
|
|
|
|
|
+**What's being added/changed:**
|
|
|
|
|
+- 将server从单租户包改为多租户包(如`@d8d/user-module` → `@d8d/user-module-mt`)
|
|
|
|
|
+- 租户上下文管理,支持租户ID在请求链中传递
|
|
|
|
|
+- 多租户认证中间件,支持租户感知的用户认证
|
|
|
|
|
+- web应用全部改为使用多租户UI包(如`@d8d/user-management-ui-mt`)
|
|
|
|
|
+- 前端API客户端支持租户上下文传递
|
|
|
|
|
+- 租户数据隔离验证和权限控制
|
|
|
|
|
+
|
|
|
|
|
+**Enhanced architecture:**
|
|
|
|
|
+```
|
|
|
|
|
+packages/
|
|
|
|
|
+├── server/ # 改为多租户系统
|
|
|
|
|
+│ ├── src/
|
|
|
|
|
+│ │ └── index.ts (改为多租户包路由)
|
|
|
|
|
+├── web/ # 全部改为多租户UI包
|
|
|
|
|
+│ ├── src/
|
|
|
|
|
+│ │ ├── client/
|
|
|
|
|
+│ │ │ ├── admin/
|
|
|
|
|
+│ │ │ │ └── routes.tsx (改为导入多租户UI包)
|
|
|
|
|
+│ │ │ ├── api_init.ts (改为初始化多租户客户端)
|
|
|
|
|
+│ │ │ └── pages/ (移除本地实现,全部使用包)
|
|
|
|
|
+└── 多租户包 (史诗007已完成)
|
|
|
|
|
+ ├── @d8d/user-module-mt
|
|
|
|
|
+ ├── @d8d/auth-module-mt (已包含租户上下文管理)
|
|
|
|
|
+ ├── @d8d/file-module-mt
|
|
|
|
|
+ ├── @d8d/user-management-ui-mt
|
|
|
|
|
+ ├── @d8d/order-management-ui-mt
|
|
|
|
|
+ └── ... (其他多租户UI包)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**How it integrates:**
|
|
|
|
|
+- 按照现有server包导入模式,将单租户包替换为多租户包
|
|
|
|
|
+- 多租户模块包直接依赖多租户认证模块的认证中间件,其中已包含租户上下文管理
|
|
|
|
|
+- 按照web中用户管理UI包的集成模式,将其他管理界面改为使用多租户UI包
|
|
|
|
|
+- 保持现有API接口不变,新增租户相关API
|
|
|
|
|
+- 前端复用现有UI组件,新增租户管理功能
|
|
|
|
|
+- 共享包在两套系统中继续复用
|
|
|
|
|
+
|
|
|
|
|
+**Success criteria:**
|
|
|
|
|
+- server直接使用多租户包,无需环境变量切换
|
|
|
|
|
+- 多租户模式下租户数据完全隔离
|
|
|
|
|
+- web应用全部使用多租户UI包,移除本地实现
|
|
|
|
|
+- 租户管理界面功能完整,支持租户CRUD操作
|
|
|
|
|
+- 现有单租户功能不受影响,保持向后兼容
|
|
|
|
|
+- 性能影响小于5%,租户过滤使用数据库索引
|
|
|
|
|
+- 完整的测试覆盖和文档
|
|
|
|
|
+
|
|
|
|
|
+## Stories
|
|
|
|
|
+
|
|
|
|
|
+### 阶段 1: Server多租户集成
|
|
|
|
|
+
|
|
|
|
|
+1. **Story 1:** Server多租户包替换 - 将server中的单租户包替换为多租户包(如`@d8d/user-module` → `@d8d/user-module-mt`),多租户模块包直接依赖多租户认证模块的认证中间件
|
|
|
|
|
+
|
|
|
|
|
+2. **Story 2:** 多租户数据实体集成 - 更新数据库实体初始化,使用多租户实体(带_mt后缀),确保租户数据隔离
|
|
|
|
|
+
|
|
|
|
|
+### 阶段 2: Web多租户UI包集成
|
|
|
|
|
+
|
|
|
|
|
+3. **Story 3:** Web多租户UI包全面集成 - 按照现有用户管理UI包的集成模式,将web中所有管理界面改为使用多租户UI包(如`@d8d/user-management-ui-mt`),移除本地实现
|
|
|
|
|
+
|
|
|
|
|
+4. **Story 4:** 前端租户上下文和API客户端增强 - 实现前端的租户上下文管理,增强API客户端支持租户上下文传递,提供租户切换和租户感知的UI组件
|
|
|
|
|
+
|
|
|
|
|
+### 阶段 3: 系统集成和验证
|
|
|
|
|
+
|
|
|
|
|
+5. **Story 5:** 多租户系统集成测试和验证 - 进行完整的系统集成测试,验证租户数据隔离、权限控制和性能表现,确保系统稳定性和可靠性
|
|
|
|
|
+
|
|
|
|
|
+## Compatibility Requirements
|
|
|
|
|
+
|
|
|
|
|
+- [x] 现有单租户APIs保持不变,多租户为可选功能
|
|
|
|
|
+- [x] 数据库schema变更向后兼容,多租户使用独立表
|
|
|
|
|
+- [x] 现有UI组件和设计模式保持不变
|
|
|
|
|
+- [x] 性能影响最小化,租户过滤使用数据库索引
|
|
|
|
|
+- [x] 配置向后兼容,现有配置继续有效
|
|
|
|
|
+
|
|
|
|
|
+## Risk Mitigation
|
|
|
|
|
+
|
|
|
|
|
+**Primary Risk:** 租户上下文管理复杂,可能影响现有请求处理
|
|
|
|
|
+**Mitigation:** 使用AsyncLocalStorage管理租户上下文,确保线程安全
|
|
|
|
|
+**Rollback Plan:** 租户功能为可选配置,可随时禁用回退到单租户模式
|
|
|
|
|
+
|
|
|
|
|
+**Primary Risk:** 多租户路由切换可能引入路由冲突
|
|
|
|
|
+**Mitigation:** 清晰的命名空间分离,单租户和多租户路由独立管理
|
|
|
|
|
+**Rollback Plan:** 保持单租户路由不变,多租户路由可独立移除
|
|
|
|
|
+
|
|
|
|
|
+**Primary Risk:** 前端租户管理界面与现有功能冲突
|
|
|
|
|
+**Mitigation:** 租户管理作为独立模块,与现有管理界面分离
|
|
|
|
|
+**Rollback Plan:** 租户管理界面可独立禁用,不影响现有功能
|
|
|
|
|
+
|
|
|
|
|
+**Primary Risk:** 性能影响,租户过滤增加查询复杂度
|
|
|
|
|
+**Mitigation:** 数据库索引优化,查询条件合并,性能基准测试
|
|
|
|
|
+**Rollback Plan:** 租户过滤可配置禁用,回退到无租户过滤模式
|
|
|
|
|
+
|
|
|
|
|
+## Definition of Done
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 所有故事完成且验收标准满足
|
|
|
|
|
+- [ ] server支持单租户/多租户模式动态切换
|
|
|
|
|
+- [ ] 租户数据隔离验证通过
|
|
|
|
|
+- [ ] 租户管理界面功能完整
|
|
|
|
|
+- [ ] 现有单租户功能通过回归测试验证
|
|
|
|
|
+- [ ] 性能基准测试通过,无明显性能下降
|
|
|
|
|
+- [ ] 完整的单元测试和集成测试覆盖
|
|
|
|
|
+- [ ] 使用文档和配置说明完整
|
|
|
|
|
+- [ ] 向后兼容性验证通过
|
|
|
|
|
+
|
|
|
|
|
+## 架构设计详情
|
|
|
|
|
+
|
|
|
|
|
+### Server多租户包集成
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 重构后的server入口 - 直接使用多租户包
|
|
|
|
|
+// 当前实际写法(packages/server/src/index.ts:4-6)
|
|
|
|
|
+import { userRoutes as userModuleRoutes } from '@d8d/user-module' // 当前:单租户包
|
|
|
|
|
+import { authRoutes as authModuleRoutes } from '@d8d/auth-module' // 当前:单租户包
|
|
|
|
|
+import { fileRoutes as fileModuleRoutes } from '@d8d/file-module' // 当前:单租户包
|
|
|
|
|
+
|
|
|
|
|
+// 改为多租户包
|
|
|
|
|
+import { userRoutes as userModuleRoutes } from '@d8d/user-module-mt' // 改为:多租户包
|
|
|
|
|
+import { authRoutes as authModuleRoutes } from '@d8d/auth-module-mt' // 改为:多租户包
|
|
|
|
|
+import { fileRoutes as fileModuleRoutes } from '@d8d/file-module-mt' // 改为:多租户包
|
|
|
|
|
+
|
|
|
|
|
+// 多租户认证模块包中已实现租户上下文(packages/auth-module-mt/src/middleware/auth.middleware.mt.ts:44-46)
|
|
|
|
|
+// 在认证中间件中设置租户上下文:c.set('tenantId', user.tenantId)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Web多租户UI包集成
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// web/src/client/admin/routes.tsx - 改为使用多租户UI包
|
|
|
|
|
+import { UserManagement } from '@d8d/user-management-ui-mt' // 改为多租户UI包
|
|
|
|
|
+import { OrderManagement } from '@d8d/order-management-ui-mt' // 改为多租户UI包
|
|
|
|
|
+import { GoodsManagement } from '@d8d/goods-management-ui-mt' // 改为多租户UI包
|
|
|
|
|
+// ... 其他管理界面包
|
|
|
|
|
+
|
|
|
|
|
+// 路由配置
|
|
|
|
|
+{
|
|
|
|
|
+ path: 'users',
|
|
|
|
|
+ element: <UserManagement />, // 使用多租户UI包
|
|
|
|
|
+},
|
|
|
|
|
+{
|
|
|
|
|
+ path: 'orders',
|
|
|
|
|
+ element: <OrderManagement />, // 使用多租户UI包
|
|
|
|
|
+},
|
|
|
|
|
+{
|
|
|
|
|
+ path: 'goods',
|
|
|
|
|
+ element: <GoodsManagement />, // 使用多租户UI包
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### API客户端初始化
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// web/src/client/api_init.ts - 改为初始化多租户客户端
|
|
|
|
|
+import { userClientManager } from '@d8d/user-management-ui-mt/api' // 改为多租户包
|
|
|
|
|
+import { orderClientManager } from '@d8d/order-management-ui-mt/api' // 改为多租户包
|
|
|
|
|
+import { goodsClientManager } from '@d8d/goods-management-ui-mt/api' // 改为多租户包
|
|
|
|
|
+
|
|
|
|
|
+// 初始化多租户客户端
|
|
|
|
|
+userClientManager.init('/api/v1/users')
|
|
|
|
|
+orderClientManager.init('/api/v1/orders')
|
|
|
|
|
+goodsClientManager.init('/api/v1/goods')
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 前端租户上下文管理
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 租户上下文Hook
|
|
|
|
|
+export function useTenant() {
|
|
|
|
|
+ const [currentTenant, setCurrentTenant] = useState<Tenant | null>(null);
|
|
|
|
|
+
|
|
|
|
|
+ const switchTenant = useCallback(async (tenantId: number) => {
|
|
|
|
|
+ // 切换租户逻辑
|
|
|
|
|
+ const response = await tenantClient.switchTenant({ tenantId });
|
|
|
|
|
+ setCurrentTenant(response.data);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新API客户端租户上下文
|
|
|
|
|
+ updateApiClientTenant(tenantId);
|
|
|
|
|
+ }, []);
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ currentTenant,
|
|
|
|
|
+ switchTenant,
|
|
|
|
|
+ isMultiTenant: !!currentTenant
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// API客户端租户上下文
|
|
|
|
|
+export function createApiClient(baseURL: string, tenantId?: number) {
|
|
|
|
|
+ const client = rpcClient(baseURL);
|
|
|
|
|
+
|
|
|
|
|
+ // 添加租户上下文到请求头
|
|
|
|
|
+ if (tenantId) {
|
|
|
|
|
+ client.$default.headers.set('X-Tenant-ID', tenantId.toString());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return client;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 租户管理界面
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 租户管理页面组件
|
|
|
|
|
+export function TenantsPage() {
|
|
|
|
|
+ const { data: tenants, isLoading } = useQuery({
|
|
|
|
|
+ queryKey: ['tenants'],
|
|
|
|
|
+ queryFn: () => tenantClient.index.$get()
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const createMutation = useMutation({
|
|
|
|
|
+ mutationFn: (data: CreateTenantDto) => tenantClient.index.$post({ json: data }),
|
|
|
|
|
+ onSuccess: () => {
|
|
|
|
|
+ // 刷新租户列表
|
|
|
|
|
+ queryClient.invalidateQueries({ queryKey: ['tenants'] });
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="container mx-auto p-6">
|
|
|
|
|
+ <div className="flex justify-between items-center mb-6">
|
|
|
|
|
+ <h1 className="text-2xl font-bold">租户管理</h1>
|
|
|
|
|
+ <Button onClick={() => setShowCreateDialog(true)}>
|
|
|
|
|
+ 创建租户
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <DataTable
|
|
|
|
|
+ data={tenants?.data || []}
|
|
|
|
|
+ columns={tenantColumns}
|
|
|
|
|
+ isLoading={isLoading}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <TenantCreateDialog
|
|
|
|
|
+ open={showCreateDialog}
|
|
|
|
|
+ onOpenChange={setShowCreateDialog}
|
|
|
|
|
+ onSubmit={createMutation.mutate}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ );
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 实施策略
|
|
|
|
|
+
|
|
|
|
|
+### 阶段化实施
|
|
|
|
|
+1. **第一阶段**: Server多租户集成(多租户包替换、租户上下文、认证增强)
|
|
|
|
|
+2. **第二阶段**: Web多租户UI包集成(全面使用多租户UI包、移除本地实现)
|
|
|
|
|
+3. **第三阶段**: 系统集成和验证(集成测试、性能测试、文档)
|
|
|
|
|
+
|
|
|
|
|
+### 数据迁移策略
|
|
|
|
|
+- 多租户系统使用全新数据,不迁移现有单租户数据
|
|
|
|
|
+- 提供数据导出导入工具
|
|
|
|
|
+- 保持两套系统数据独立
|
|
|
|
|
+
|
|
|
|
|
+## 技术实现要点
|
|
|
|
|
+
|
|
|
|
|
+### 向后兼容性保证
|
|
|
|
|
+1. **包替换**: 直接替换单租户包为多租户包,保持相同API接口
|
|
|
|
|
+2. **API 不变**: 现有单租户API接口和行为保持不变
|
|
|
|
|
+3. **数据独立**: 多租户使用独立的数据表
|
|
|
|
|
+4. **性能隔离**: 两套系统性能互不影响
|
|
|
|
|
+
|
|
|
|
|
+### 安全性考虑
|
|
|
|
|
+1. **租户隔离**: 严格的租户数据隔离机制
|
|
|
|
|
+2. **权限控制**: 租户间数据访问权限控制
|
|
|
|
|
+3. **输入验证**: 所有用户输入进行严格的验证和转义
|
|
|
|
|
+4. **审计日志**: 租户操作审计日志记录
|
|
|
|
|
+
|
|
|
|
|
+### 性能优化
|
|
|
|
|
+1. **数据库索引**: 确保租户ID字段有合适的索引
|
|
|
|
|
+2. **查询优化**: 租户过滤条件与现有查询条件合并
|
|
|
|
|
+3. **连接池**: 优化数据库连接池配置
|
|
|
|
|
+4. **缓存策略**: 租户级缓存策略
|
|
|
|
|
+
|
|
|
|
|
+## 测试策略
|
|
|
|
|
+
|
|
|
|
|
+### 单元测试
|
|
|
|
|
+- 租户上下文管理测试
|
|
|
|
|
+- 多租户包集成测试
|
|
|
|
|
+- 认证中间件增强测试
|
|
|
|
|
+- 前端租户Hook测试
|
|
|
|
|
+
|
|
|
|
|
+### 集成测试
|
|
|
|
|
+- 租户数据隔离验证测试
|
|
|
|
|
+- 租户管理界面功能测试
|
|
|
|
|
+- 性能基准测试
|
|
|
|
|
+
|
|
|
|
|
+### 回归测试
|
|
|
|
|
+- 现有单租户功能回归测试
|
|
|
|
|
+- 向后兼容性验证测试
|
|
|
|
|
+- API接口兼容性测试
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## Story Manager Handoff
|
|
|
|
|
+
|
|
|
|
|
+"请为这个brownfield epic开发详细的用户故事。关键考虑因素:
|
|
|
|
|
+
|
|
|
|
|
+- 这是基于史诗007已完成的多租户包,在现有Node.js + TypeScript + Hono + TypeORM + PostgreSQL + React技术栈上的集成
|
|
|
|
|
+- 集成点:现有server包导入模式(packages/server/src/index.ts)、web中用户管理UI包的集成模式
|
|
|
|
|
+- 需要遵循的现有模式:server直接包导入、web中UI包集成、Hono中间件、TypeORM实体、React组件、TanStack Query数据获取
|
|
|
|
|
+- 关键发现:多租户模块包直接依赖多租户认证模块的认证中间件,其中已实现租户上下文管理(`c.set('tenantId', user.tenantId)`),无需额外添加租户中间件
|
|
|
|
|
+- 关键兼容性要求:现有单租户系统完全不变、直接替换为多租户包、性能影响最小化
|
|
|
|
|
+- 每个故事必须包含验证现有功能保持完整的回归测试
|
|
|
|
|
+
|
|
|
|
|
+该epic应按照现有包集成模式,将server和web直接改为多租户系统,为后续按需拼装不同系统提供基础。"
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+**🤖 Generated with [Claude Code](https://claude.ai/code)**
|
|
|
|
|
+via [Happy](https://happy.engineering)
|
|
|
|
|
+
|
|
|
|
|
+**Co-Authored-By: Claude <noreply@anthropic.com>**
|
|
|
|
|
+**Co-Authored-By: Happy <yesreply@happy.engineering>**
|