基于用户的多租户复制包方案,通过复制现有业务包并添加租户标记和租户ID支持,形成两套业务包:单租户版本和多租户版本,在server中按需进行依赖拼装,实现多租户支持。
@d8d/user-module-mt, @d8d/file-module-mt, @d8d/auth-module-mt, @d8d/geo-areas-mt, @d8d/delivery-address-module-mt, @d8d/merchant-module-mt, @d8d/supplier-module-mt, @d8d/goods-module-mt, @d8d/orders-module-mt, @d8d/advertisements-module-mt@d8d/shared-ui-components,包含46个基础UI组件@d8d/tenant-management-ui,基于现有用户管理界面实现,依赖租户模块包 @d8d/tenant-module-mt@d8d/auth-management-ui,基于现有认证管理界面实现,依赖认证模块包 @d8d/auth-module@d8d/user-management-ui,基于现有用户管理界面实现,依赖用户模块包 @d8d/user-module@d8d/advertisement-type-management-ui,基于现有广告分类管理界面实现,依赖广告模块包 @d8d/advertisements-module@d8d/order-management-ui,基于现有订单管理界面实现,依赖订单模块包 @d8d/orders-module,包含完整的订单CRUD操作和状态管理@d8d/goods-category-management-ui,基于现有商品分类管理界面实现,依赖商品模块包 @d8d/goods-module@d8d/area-management-ui,基于现有区域管理界面实现,依赖地理区域模块包 @d8d/geo-areas@d8d/supplier-management-ui,基于现有供应商管理界面实现,依赖供应商模块包 @d8d/supplier-module@d8d/goods-management-ui,基于现有商品管理界面实现,依赖商品模块包 @d8d/goods-module,包含完整的商品CRUD操作、库存管理和价格管理@d8d/auth-management-ui, @d8d/user-management-ui, @d8d/advertisement-management-ui, @d8d/advertisement-type-management-ui, @d8d/order-management-ui, @d8d/goods-management-ui, @d8d/goods-category-management-ui, @d8d/supplier-management-ui, @d8d/merchant-management-ui, @d8d/file-management-ui, @d8d/delivery-address-management-ui, @d8d/area-management-ui, @d8d/tenant-config-management-ui@d8d/auth-management-ui-mt, @d8d/user-management-ui-mt, @d8d/advertisement-management-ui-mt, @d8d/advertisement-type-management-ui-mt, @d8d/order-management-ui-mt, @d8d/goods-management-ui-mt, @d8d/goods-category-management-ui-mt, @d8d/supplier-management-ui-mt, @d8d/merchant-management-ui-mt, @d8d/file-management-ui-mt, @d8d/delivery-address-management-ui-mt, @d8d/area-management-ui-mt, @d8d/tenant-config-management-ui-mtCurrent relevant functionality:
共享包 (保持不变):
@d8d/shared-crud: 通用CRUD操作和用户跟踪字段@d8d/shared-types: 共享类型定义@d8d/shared-utils: 共享工具函数@d8d/shared-test-util: 共享测试工具业务包 (需要复制):
@d8d/user-module: 用户管理、认证、角色权限@d8d/orders-module: 订单创建、支付、物流管理@d8d/goods-module: 商品管理、分类、库存@d8d/merchant-module: 商户管理@d8d/supplier-module: 供应商管理@d8d/delivery-address-module: 配送地址管理@d8d/file-module: 文件上传管理@d8d/geo-areas: 地理区域管理@d8d/auth-module: 认证模块@d8d/mini-payment: 小程序支付@d8d/advertisements-module: 广告模块Current limitations:
Technology stack:
Integration points:
What's being added/changed:
Enhanced architecture:
packages/
├── shared-crud/ (保持不变)
├── shared-types/ (保持不变)
├── shared-utils/ (保持不变)
├── shared-test-util/ (保持不变)
├──
├── 单租户业务包 (现有包):
│ ├── user-module/
│ ├── orders-module/
│ ├── goods-module/
│ └── ...
├──
└── 多租户业务包 (新复制包):
├── user-module-mt/ (多租户版本)
├── orders-module-mt/ (多租户版本)
├── goods-module-mt/ (多租户版本)
└── ...
How it integrates:
Success criteria:
Story 1: 租户基础包创建和租户管理
@d8d/merchant-module 为 @d8d/tenant-module-mt 租户管理模块Story 2: 用户模块多租户复制和租户支持 ✅ 已完成
@d8d/user-module 为 @d8d/user-module-mtStory 3: 文件模块多租户复制和租户支持 ✅ 已完成
@d8d/file-module 为 @d8d/file-module-mtStory 4: 认证模块多租户复制和租户支持 ✅ 已完成
@d8d/auth-module 为 @d8d/auth-module-mtStory 5: 地理区域模块多租户复制和租户支持 ✅ 已完成
@d8d/geo-areas 为 @d8d/geo-areas-mtfileParallelism: falseStory 6: 地址模块多租户复制和租户支持 ✅ 已完成
@d8d/delivery-address-module 为 @d8d/delivery-address-module-mtStory 7: 商户模块多租户复制和租户支持 ✅ 已完成
@d8d/merchant-module 为 @d8d/merchant-module-mtStory 8: 供应商模块多租户复制和租户支持 ✅ 已完成
@d8d/supplier-module 为 @d8d/supplier-module-mtStory 9: 商品模块多租户复制和租户支持 ✅ 已完成
@d8d/goods-module 为 @d8d/goods-module-mtStory 10: 订单模块多租户复制和租户支持 ✅ 已完成
@d8d/orders-module 为 @d8d/orders-module-mtStory 11: 小程序支付模块多租户复制和租户支持
@d8d/mini-payment 为 @d8d/mini-payment-mtStory 12: 广告模块多租户复制和租户支持 ✅ 已完成
@d8d/advertisements-module 为 @d8d/advertisements-module-mt管理界面包架构说明:
@d8d/{module}-management-ui@d8d/{module}-management-ui-mt@d8d/shared-ui-componentsStory 13: 共享UI组件包创建 ✅ 已完成
@d8d/shared-ui-componentsStory 14: 租户管理界面独立包实现 ✅ 已完成
web/src/client/admin/pages/Users.tsx 为租户管理界面@d8d/tenant-management-ui@d8d/shared-ui-componentsStory 15: 单租户认证管理界面独立包实现 ✅ 已完成
web/src/client/admin/pages/Login.tsx 为单租户认证管理界面包web/src/client/admin/hooks/AuthProvider.tsx 为单租户认证包@d8d/auth-management-ui@d8d/shared-ui-components@d8d/auth-moduleStory 16: 多租户认证管理界面独立包实现
packages/auth-management-ui/ 为 packages/auth-management-ui-mt/@d8d/auth-management-ui-mt,更新依赖为 @d8d/auth-module-mtStory 17: 单租户用户管理界面独立包实现 ✅ 已完成
web/src/client/admin/pages/Users.tsx 为单租户用户管理界面包@d8d/user-management-ui@d8d/shared-ui-components@d8d/user-modulepackages/user-management-ui/src/components/UserSelector.tsx@d8d/file-management-ui 中的文件选择器组件替代头像选择器Story 18: 多租户用户管理界面独立包实现
packages/user-management-ui/ 为 packages/user-management-ui-mt/@d8d/user-management-ui-mt,更新依赖为 @d8d/user-module-mtStory 19: 单租户广告管理界面独立包实现
web/src/client/admin/pages/Advertisements.tsx 为单租户广告管理界面包@d8d/advertisement-management-ui@d8d/shared-ui-components@d8d/advertisements-module@d8d/advertisement-type-management-ui 中的广告分类选择器组件@d8d/file-management-ui 中的文件选择器组件Story 20: 多租户广告管理界面独立包实现
packages/advertisement-management-ui/ 为 packages/advertisement-management-ui-mt/@d8d/advertisement-management-ui-mt,更新依赖为 @d8d/advertisements-module-mtStory 21: 单租户广告分类管理界面独立包实现
web/src/client/admin/pages/AdvertisementTypes.tsx 为单租户广告分类管理界面包@d8d/advertisement-type-management-ui@d8d/shared-ui-components@d8d/advertisements-moduleStory 22: 多租户广告分类管理界面独立包实现
packages/advertisement-type-management-ui/ 为 packages/advertisement-type-management-ui-mt/@d8d/advertisement-type-management-ui-mt,更新依赖为 @d8d/advertisements-module-mtStory 23: 单租户订单管理界面独立包实现 ✅ 已完成
web/src/client/admin/pages/Orders.tsx 为单租户订单管理界面包@d8d/order-management-ui@d8d/shared-ui-components@d8d/orders-moduleStory 24: 多租户订单管理界面独立包实现
packages/order-management-ui/ 为 packages/order-management-ui-mt/@d8d/order-management-ui-mt,更新依赖为 @d8d/orders-module-mtStory 25: 单租户商品管理界面独立包实现
web/src/client/admin/pages/Goods.tsx 为单租户商品管理界面包@d8d/goods-management-ui@d8d/shared-ui-components@d8d/goods-module@d8d/goods-category-management-ui 中的商品分类选择器组件@d8d/merchant-management-ui 中的商户选择器组件@d8d/supplier-management-ui 中的供应商选择器组件@d8d/file-management-ui 中的文件选择器组件Story 26: 多租户商品管理界面独立包实现
packages/goods-management-ui/ 为 packages/goods-management-ui-mt/@d8d/goods-management-ui-mt,更新依赖为 @d8d/goods-module-mtStory 27: 单租户商品分类管理界面独立包实现 ✅ 已完成
web/src/client/admin/pages/CategoriesTreePage.tsx 为单租户商品分类管理界面包@d8d/goods-category-management-ui@d8d/shared-ui-components@d8d/goods-module@d8d/file-management-ui 中的文件选择器组件packages/goods-category-management-ui/src/components/GoodsCategorySelector.tsxpackages/goods-category-management-ui/src/components/GoodsCategoryCascadeSelector.tsxStory 28: 多租户商品分类管理界面独立包实现
packages/goods-category-management-ui/ 为 packages/goods-category-management-ui-mt/@d8d/goods-category-management-ui-mt,更新依赖为 @d8d/goods-module-mtStory 29: 单租户供应商管理界面独立包实现
web/src/client/admin/pages/Suppliers.tsx 为单租户供应商管理界面包@d8d/supplier-management-ui@d8d/shared-ui-components@d8d/supplier-moduleStory 30: 多租户供应商管理界面独立包实现
packages/supplier-management-ui/ 为 packages/supplier-management-ui-mt/@d8d/supplier-management-ui-mt,更新依赖为 @d8d/supplier-module-mtStory 31: 单租户商户管理界面独立包实现
web/src/client/admin/pages/Merchants.tsx 为单租户商户管理界面包@d8d/merchant-management-ui@d8d/shared-ui-components@d8d/merchant-moduleStory 32: 多租户商户管理界面独立包实现
packages/merchant-management-ui/ 为 packages/merchant-management-ui-mt/@d8d/merchant-management-ui-mt,更新依赖为 @d8d/merchant-module-mtStory 33: 单租户文件管理界面独立包实现
web/src/client/admin/pages/Files.tsx 为单租户文件管理界面包@d8d/file-management-ui@d8d/shared-ui-components@d8d/file-modulepackages/file-management-ui/src/components/FileSelector.tsxStory 34: 多租户文件管理界面独立包实现
packages/file-management-ui/ 为 packages/file-management-ui-mt/@d8d/file-management-ui-mt,更新依赖为 @d8d/file-module-mtStory 35: 单租户地址管理界面独立包实现
web/src/client/admin/pages/DeliveryAddresses.tsx 为单租户地址管理界面包@d8d/delivery-address-management-ui@d8d/shared-ui-components@d8d/delivery-address-module@d8d/user-management-ui 中的用户选择器组件Story 36: 多租户地址管理界面独立包实现
web/src/client/admin/pages/DeliveryAddresses.tsx 为多租户地址管理界面包@d8d/delivery-address-management-ui-mt@d8d/shared-ui-components@d8d/delivery-address-module-mt@d8d/user-management-ui-mt 中的用户选择器组件Story 37: 单租户区域管理界面独立包实现
web/src/client/admin/pages/AreasTreePage.tsx 为单租户区域管理界面包@d8d/area-management-ui@d8d/shared-ui-components@d8d/geo-areaspackages/area-management-ui/src/components/AreaSelect.tsxpackages/area-management-ui/src/components/AreaSelect4Level.tsxStory 38: 多租户区域管理界面独立包实现
web/src/client/admin/pages/AreasTreePage.tsx 为多租户区域管理界面包@d8d/area-management-ui-mt@d8d/shared-ui-components@d8d/geo-areas-mt@d8d/file-management-ui 中的 FileSelector 组件AreaSelect): 省份 → 城市 → 区县AreaSelect4Level): 省份 → 城市 → 区县 → 乡镇Primary Risk: 代码重复度高,维护成本增加 Mitigation: 共享包保持不变,业务逻辑变化控制在最小范围 Rollback Plan: 多租户包为新增,可随时移除,不影响单租户系统
Primary Risk: 包依赖关系复杂化 Mitigation: 清晰的命名约定和文档,server依赖管理简化 Rollback Plan: 保持单租户包不变,多租户包可独立移除
Primary Risk: 数据库schema变更影响现有数据 Mitigation: 多租户包使用新的数据库表或字段,不修改现有表 Rollback Plan: 多租户数据可独立清理,不影响单租户数据
Primary Risk: 性能影响 Mitigation: 租户过滤使用数据库索引,性能基准测试 Rollback Plan: 多租户系统可独立关闭,不影响单租户性能
// 单租户包 (现有)
@d8d/user-module
@d8d/orders-module
@d8d/goods-module
@d8d/merchant-module
@d8d/supplier-module
@d8d/delivery-address-module
@d8d/file-module
@d8d/geo-areas
@d8d/auth-module
@d8d/mini-payment
@d8d/advertisements-module
// 多租户包 (新增,-mt后缀)
@d8d/tenant-module-mt (从商户包复制修改)
@d8d/user-module-mt
@d8d/orders-module-mt
@d8d/goods-module-mt
@d8d/merchant-module-mt
@d8d/supplier-module-mt
@d8d/delivery-address-module-mt
@d8d/file-module-mt
@d8d/geo-areas-mt
@d8d/auth-module-mt
@d8d/mini-payment-mt
@d8d/advertisements-module-mt
// 从商户包复制后修改为租户包
// 商户实体 -> 租户实体
@Entity('tenants_mt')
export class TenantEntityMt {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'name', type: 'varchar', length: 100 })
name: string; // 租户名称
@Column({ name: 'code', type: 'varchar', length: 50, unique: true })
code: string; // 租户代码
@Column({ name: 'status', type: 'varchar', length: 20, default: 'active' })
status: string; // 租户状态
@Column({ name: 'config', type: 'jsonb', nullable: true })
config: Record<string, any>; // 租户配置
// ... 其他字段根据租户需求调整
}
// 多租户用户实体示例
@Entity('users_mt') // 使用不同的表名避免冲突
@TenantAware()
export class UserEntityMt {
@PrimaryGeneratedColumn()
id: number;
@TenantId()
@Column({ name: 'tenant_id', type: 'int', unsigned: true })
tenantId: number;
@Column({ name: 'username', type: 'varchar', length: 50 })
username: string;
// ... 其他字段保持不变
}
// 多租户用户服务
@Service()
export class UserServiceMt {
constructor(
@InjectRepository(UserEntityMt)
private userRepository: Repository<UserEntityMt>,
private tenantContext: TenantContext
) {}
async getUsers(page: number = 1, pageSize: number = 10): Promise<[UserEntityMt[], number]> {
const tenantId = this.tenantContext.getCurrentTenantId();
return this.userRepository.findAndCount({
where: { tenantId },
skip: (page - 1) * pageSize,
take: pageSize,
order: { id: 'DESC' }
});
}
async createUser(data: CreateUserDto): Promise<UserEntityMt> {
const tenantId = this.tenantContext.getCurrentTenantId();
const user = this.userRepository.create({
...data,
tenantId
});
return this.userRepository.save(user);
}
}
// 多租户用户路由(使用原有的认证中间件名字)
const userRoutesMt = new Hono()
.use('*', authMiddleware) // 保持原有中间件名字
.get('/', async (c) => {
const userService = c.get('userServiceMt') as UserServiceMt;
const [users, total] = await userService.getUsers();
return c.json({ code: 200, data: { users, total } });
})
.post('/', async (c) => {
const userService = c.get('userServiceMt') as UserServiceMt;
const data = await c.req.json();
const user = await userService.createUser(data);
return c.json({ code: 201, data: user });
});
export { userRoutesMt };
// 单租户server配置
export function createSingleTenantServer(): Hono {
const app = new Hono();
// 加载单租户包
app.route('/api/users', userRoutes);
app.route('/api/orders', ordersRoutes);
app.route('/api/goods', goodsRoutes);
// ... 其他单租户路由
return app;
}
// 多租户server配置
export function createMultiTenantServer(): Hono {
const app = new Hono();
// 加载多租户包
app.route('/api/users', userRoutesMt);
app.route('/api/orders', ordersRoutesMt);
app.route('/api/goods', goodsRoutesMt);
// ... 其他多租户路由
return app;
}
// 根据配置选择server
export function createServer(): Hono {
const isMultiTenant = process.env.MULTI_TENANT_ENABLED === 'true';
if (isMultiTenant) {
return createMultiTenantServer();
} else {
return createSingleTenantServer();
}
}
// 租户上下文
export class TenantContext {
private static readonly tenantIdStorage = new AsyncLocalStorage<number>();
static getCurrentTenantId(): number | undefined {
return this.tenantIdStorage.getStore();
}
static runWithTenant<T>(tenantId: number, fn: () => Promise<T>): Promise<T> {
return this.tenantIdStorage.run(tenantId, fn);
}
}
// 多租户认证中间件(保持原有中间件名字)
export const authMiddleware: MiddlewareHandler = async (c, next) => {
// 1. 执行原有认证逻辑
const token = c.req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return c.json({ code: 401, message: '未授权访问' }, 401);
}
// 2. 验证token并获取用户信息
const user = await verifyToken(token);
if (!user) {
return c.json({ code: 401, message: '无效token' }, 401);
}
// 3. 设置用户上下文
c.set('user', user);
// 4. 设置租户上下文(从用户信息中提取租户ID)
const tenantId = user.tenantId;
if (tenantId) {
await TenantContext.runWithTenant(tenantId, async () => {
await next();
});
} else {
await next();
}
};
-- 多租户表使用不同的表名或schema
-- 方案1: 使用不同表名
CREATE TABLE users_mt (
id SERIAL PRIMARY KEY,
tenant_id INTEGER NOT NULL,
username VARCHAR(50) NOT NULL,
-- ... 其他字段
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 方案2: 使用不同schema
CREATE SCHEMA IF NOT EXISTS tenant_1;
CREATE TABLE tenant_1.users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL,
-- ... 其他字段
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建索引优化租户查询
CREATE INDEX idx_users_mt_tenant_id ON users_mt(tenant_id);
CREATE INDEX idx_orders_mt_tenant_id ON orders_mt(tenant_id);
CREATE INDEX idx_goods_mt_tenant_id ON goods_mt(tenant_id);
// 多租户包的package.json
{
"name": "@d8d/user-module-mt",
"version": "1.0.0",
"dependencies": {
"@d8d/shared-crud": "workspace:*",
"@d8d/shared-types": "workspace:*",
"@d8d/shared-utils": "workspace:*"
// 不依赖其他业务包,避免循环依赖
}
}
"请为此brownfield epic开发详细的用户故事。关键考虑因素:
该epic应在提供多租户支持的同时,通过复制包方案保持系统完整性和风险可控性。"
数据库同步冲突
fileParallelism: false 替代 maxWorkers: 1duplicate key value violates unique constraint 错误TypeScript类型检查
as any 绕过类型检查,保持运行时验证多租户模块依赖
.mt.ts 后缀租户ID字段管理
共享CRUD库租户验证顺序
目录结构错误
前端包依赖管理
测试路径别名问题
认证管理界面包测试策略
.mt.ts 后缀区分多租户文件fileParallelism: false 避免数据库冲突FormField mock: 正确处理render函数,确保子组件正确渲染
RPC客户端架构设计
客户端管理器设计
// 单例模式确保全局唯一实例
class UserClientManager {
private static instance: UserClientManager;
private client: ReturnType<typeof rpcClient<typeof userRoutes>> | null = null;
// 初始化客户端
public init(baseUrl: string = '/'): ReturnType<typeof rpcClient<typeof userRoutes>> {
return this.client = rpcClient<typeof userRoutes>(baseUrl);
}
// 获取客户端实例(延迟初始化)
public get(): ReturnType<typeof rpcClient<typeof userRoutes>> {
if (!this.client) {
return this.init()
}
return this.client;
}
// 重置客户端(用于测试或重新初始化)
public reset(): void {
this.client = null;
}
}
类型安全调用
// 使用Hono类型推断确保类型安全
type CreateUserRequest = InferRequestType<typeof userClient.index.$post>['json'];
type UpdateUserRequest = InferRequestType<typeof userClient[':id']['$put']>['json'];
type UserResponse = InferResponseType<typeof userClient.index.$get, 200>['data'][0];
// 直接使用后端定义的schema
const createUserFormSchema = CreateUserDto;
const updateUserFormSchema = UpdateUserDto;
API调用模式
// 使用客户端管理器进行API调用
const res = await userClientManager.get().index.$get({
query: {
page: searchParams.page,
pageSize: searchParams.limit,
keyword: searchParams.keyword,
filters: Object.keys(filterParams).length > 0 ? JSON.stringify(filterParams) : undefined
}
});
// 创建用户
const res = await userClientManager.get().index.$post({
json: data
});
主应用集成
// 在主应用中初始化客户端
import { userClientManager } from '@d8d/user-management-ui/api';
userClientManager.init('/api/v1/users');
多租户复制包方案为用户提供了明确的实施路径:
虽然存在代码重复和维护成本增加的权衡,但该方案在风险控制、实施简单性和团队接受度方面具有明显优势,特别适合需要快速实现多租户支持且对现有系统稳定性要求极高的场景。
当前进展: 阶段1已100%完成,阶段2已100%完成,阶段3完成100%,阶段4完成42.3%,总体进度61.5%,所有已创建的多租户包测试通过且构建成功。租户管理界面独立包已完成,包含完整的租户CRUD操作、配置管理功能,所有18个测试通过,构建成功。认证管理界面独立包已完成,包含完整的登录表单、认证状态管理功能,所有11个测试通过,构建成功。用户管理界面独立包已完成,包含完整的用户CRUD操作和角色权限管理,所有测试通过,构建成功。广告分类管理界面独立包已完成,包含完整的广告分类CRUD操作,所有测试通过,构建成功。订单管理界面独立包已完成,包含完整的订单CRUD操作和状态管理,所有测试通过,构建成功。商品分类管理界面独立包已完成,包含完整的商品分类CRUD操作和树形结构管理,所有4个集成测试通过,构建成功。商品管理界面独立包已完成,包含完整的商品CRUD操作、库存管理和价格管理,依赖商品分类、商户、供应商和文件选择器组件,构建成功。前端包依赖共享UI组件包,解决了组件导出和测试路径问题,确保管理界面独立包可独立使用。新增26个管理界面独立包故事,每个管理界面都区分单租户和多租户版本,形成独立的开发故事,确保架构清晰和可维护性。认证管理界面包作为基础依赖包,确保其他管理界面包可正常使用。
🤖 Generated with Claude Code via Happy
Co-Authored-By: Claude noreply@anthropic.com Co-Authored-By: Happy yesreply@happy.engineering
最后更新: 2025-11-17 (更新商品管理UI包完成状态和进度统计)