fix(tenant): simplify evolution-service tenant configuration
- Use SimpleTenantFinder for evolution-service middleware (same as other services) - Keep TenantFinderService available via TenantModule for super-admin - Add useExternalFinder option to TenantContextModule - Remove @Global() decorator from TenantContextModule class Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
7ff701369b
commit
1ac183fc0c
|
|
@ -5,6 +5,7 @@ import { ScheduleModule } from '@nestjs/schedule';
|
|||
import {
|
||||
TenantContextModule,
|
||||
TenantContextMiddleware,
|
||||
SimpleTenantFinder,
|
||||
DEFAULT_TENANT_ID,
|
||||
} from '@iconsulting/shared';
|
||||
import { EvolutionModule } from './evolution/evolution.module';
|
||||
|
|
@ -12,7 +13,6 @@ import { AdminModule } from './admin/admin.module';
|
|||
import { HealthModule } from './health/health.module';
|
||||
import { AnalyticsModule } from './analytics/analytics.module';
|
||||
import { TenantModule } from './infrastructure/tenant/tenant.module';
|
||||
import { TenantFinderService } from './infrastructure/tenant/tenant-finder.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -25,17 +25,6 @@ import { TenantFinderService } from './infrastructure/tenant/tenant-finder.servi
|
|||
// 定时任务模块
|
||||
ScheduleModule.forRoot(),
|
||||
|
||||
// Tenant context module (global) - uses custom TenantFinderService
|
||||
TenantContextModule.forRoot({
|
||||
tenantFinder: TenantFinderService,
|
||||
middlewareOptions: {
|
||||
allowDefaultTenant: true,
|
||||
defaultTenantId: DEFAULT_TENANT_ID,
|
||||
excludePaths: ['/health', '/health/*', '/super-admin/*'],
|
||||
},
|
||||
isGlobal: true,
|
||||
}),
|
||||
|
||||
// 数据库连接
|
||||
TypeOrmModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
|
|
@ -54,9 +43,20 @@ import { TenantFinderService } from './infrastructure/tenant/tenant-finder.servi
|
|||
}),
|
||||
}),
|
||||
|
||||
// 租户模块
|
||||
// 租户模块 (提供 TenantFinderService 用于 super-admin 租户管理)
|
||||
TenantModule,
|
||||
|
||||
// Tenant context module (global) - 使用 SimpleTenantFinder
|
||||
TenantContextModule.forRoot({
|
||||
tenantFinder: SimpleTenantFinder,
|
||||
middlewareOptions: {
|
||||
allowDefaultTenant: true,
|
||||
defaultTenantId: DEFAULT_TENANT_ID,
|
||||
excludePaths: ['/health', '/health/*', '/super-admin/*'],
|
||||
},
|
||||
isGlobal: true,
|
||||
}),
|
||||
|
||||
// Health check
|
||||
HealthModule,
|
||||
|
||||
|
|
|
|||
|
|
@ -2,17 +2,10 @@ import { Module } from '@nestjs/common';
|
|||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { TenantORM } from '../database/postgres/entities/tenant.orm';
|
||||
import { TenantFinderService } from './tenant-finder.service';
|
||||
import { TENANT_FINDER } from '@iconsulting/shared';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([TenantORM])],
|
||||
providers: [
|
||||
TenantFinderService,
|
||||
{
|
||||
provide: TENANT_FINDER,
|
||||
useExisting: TenantFinderService,
|
||||
},
|
||||
],
|
||||
exports: [TenantFinderService, TENANT_FINDER],
|
||||
providers: [TenantFinderService],
|
||||
exports: [TenantFinderService],
|
||||
})
|
||||
export class TenantModule {}
|
||||
|
|
|
|||
|
|
@ -32,4 +32,8 @@ export type { TenantAwareEntity } from './base-tenant.repository.js';
|
|||
|
||||
// Module
|
||||
export { TenantContextModule } from './tenant-context.module.js';
|
||||
export type { TenantModuleOptions, TenantModuleAsyncOptions } from './tenant-context.module.js';
|
||||
export type {
|
||||
TenantModuleOptions,
|
||||
TenantModuleAsyncOptions,
|
||||
TenantModuleAsyncOptionsWithFinder,
|
||||
} from './tenant-context.module.js';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { DynamicModule, Global, Module, Provider, Type } from '@nestjs/common';
|
||||
import { DynamicModule, Module, Provider, Type } from '@nestjs/common';
|
||||
import { TenantContextService } from './tenant-context.service.js';
|
||||
import {
|
||||
TenantContextMiddleware,
|
||||
|
|
@ -13,9 +13,24 @@ import { TenantGuard } from './tenant.guard.js';
|
|||
*/
|
||||
export interface TenantModuleOptions {
|
||||
/**
|
||||
* 租户查找器类
|
||||
* 租户查找器类 (用 useClass 创建新实例)
|
||||
*/
|
||||
tenantFinder: Type<ITenantFinder>;
|
||||
tenantFinder?: Type<ITenantFinder>;
|
||||
/**
|
||||
* 使用已存在的租户查找器 (用 useExisting 复用已注册的实例)
|
||||
* 当 tenantFinder 需要依赖注入其他服务时使用此选项
|
||||
*/
|
||||
tenantFinderExisting?: Type<ITenantFinder>;
|
||||
/**
|
||||
* 使用外部提供的 TENANT_FINDER
|
||||
* 当此选项为 true 时,必须在其他模块中提供 TENANT_FINDER token
|
||||
* 且该模块必须在 AppModule 中先于 TenantContextModule 导入
|
||||
*/
|
||||
useExternalFinder?: boolean;
|
||||
/**
|
||||
* 导入的模块 (当使用 tenantFinderExisting 时需要导入提供该服务的模块)
|
||||
*/
|
||||
imports?: any[];
|
||||
/**
|
||||
* 中间件选项
|
||||
*/
|
||||
|
|
@ -27,7 +42,7 @@ export interface TenantModuleOptions {
|
|||
}
|
||||
|
||||
/**
|
||||
* 租户模块异步配置选项
|
||||
* 租户模块异步配置选项 (返回 TenantModuleOptions)
|
||||
*/
|
||||
export interface TenantModuleAsyncOptions {
|
||||
/**
|
||||
|
|
@ -48,6 +63,29 @@ export interface TenantModuleAsyncOptions {
|
|||
isGlobal?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户模块异步配置选项 (直接注入 TenantFinder 实例)
|
||||
* 用于 tenantFinder 需要依赖注入其他服务的场景
|
||||
*/
|
||||
export interface TenantModuleAsyncOptionsWithFinder {
|
||||
/**
|
||||
* 导入的模块 (必须包含提供 TenantFinder 的模块)
|
||||
*/
|
||||
imports: any[];
|
||||
/**
|
||||
* 注入的依赖 (必须包含 TenantFinder 类)
|
||||
*/
|
||||
inject: any[];
|
||||
/**
|
||||
* 工厂函数 (第一个注入的依赖应该是 TenantFinder 实例)
|
||||
*/
|
||||
useFactory: (tenantFinder: ITenantFinder, ...args: any[]) => Promise<TenantMiddlewareOptions> | TenantMiddlewareOptions;
|
||||
/**
|
||||
* 是否全局注册
|
||||
*/
|
||||
isGlobal?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户上下文模块
|
||||
*
|
||||
|
|
@ -74,7 +112,6 @@ export interface TenantModuleAsyncOptions {
|
|||
* isGlobal: true,
|
||||
* })
|
||||
*/
|
||||
@Global()
|
||||
@Module({})
|
||||
export class TenantContextModule {
|
||||
/**
|
||||
|
|
@ -84,10 +121,6 @@ export class TenantContextModule {
|
|||
const providers: Provider[] = [
|
||||
TenantContextService,
|
||||
TenantGuard,
|
||||
{
|
||||
provide: TENANT_FINDER,
|
||||
useClass: options.tenantFinder,
|
||||
},
|
||||
{
|
||||
provide: 'TENANT_MIDDLEWARE_OPTIONS',
|
||||
useValue: options.middlewareOptions || {},
|
||||
|
|
@ -103,10 +136,29 @@ export class TenantContextModule {
|
|||
},
|
||||
];
|
||||
|
||||
// 构建 exports 列表
|
||||
const exports: any[] = [TenantContextService, TenantGuard, TenantContextMiddleware];
|
||||
|
||||
// 如果不使用外部 finder,则自己提供并导出 TENANT_FINDER
|
||||
if (!options.useExternalFinder) {
|
||||
const tenantFinderProvider: Provider = options.tenantFinderExisting
|
||||
? {
|
||||
provide: TENANT_FINDER,
|
||||
useExisting: options.tenantFinderExisting,
|
||||
}
|
||||
: {
|
||||
provide: TENANT_FINDER,
|
||||
useClass: options.tenantFinder!,
|
||||
};
|
||||
providers.push(tenantFinderProvider);
|
||||
exports.push(TENANT_FINDER);
|
||||
}
|
||||
|
||||
return {
|
||||
module: TenantContextModule,
|
||||
imports: options.imports || [],
|
||||
providers,
|
||||
exports: [TenantContextService, TenantGuard, TenantContextMiddleware, TENANT_FINDER],
|
||||
exports,
|
||||
global: options.isGlobal ?? true,
|
||||
};
|
||||
}
|
||||
|
|
@ -127,6 +179,9 @@ export class TenantContextModule {
|
|||
provide: TENANT_FINDER,
|
||||
useFactory: async (moduleOptions: TenantModuleOptions) => {
|
||||
// 动态实例化租户查找器
|
||||
if (!moduleOptions.tenantFinder) {
|
||||
throw new Error('tenantFinder is required in async configuration');
|
||||
}
|
||||
return new moduleOptions.tenantFinder();
|
||||
},
|
||||
inject: ['TENANT_MODULE_OPTIONS'],
|
||||
|
|
@ -156,4 +211,53 @@ export class TenantContextModule {
|
|||
global: options.isGlobal ?? true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步注册租户模块 (直接注入 TenantFinder 实例)
|
||||
* 用于 TenantFinder 需要依赖注入其他服务 (如 Repository) 的场景
|
||||
*
|
||||
* @example
|
||||
* TenantContextModule.forRootAsyncWithFinder({
|
||||
* imports: [TenantModule],
|
||||
* inject: [TenantFinderService],
|
||||
* useFactory: (tenantFinder) => ({
|
||||
* allowDefaultTenant: true,
|
||||
* defaultTenantId: DEFAULT_TENANT_ID,
|
||||
* }),
|
||||
* isGlobal: true,
|
||||
* })
|
||||
*/
|
||||
static forRootAsyncWithFinder(options: TenantModuleAsyncOptionsWithFinder): DynamicModule {
|
||||
const providers: Provider[] = [
|
||||
TenantContextService,
|
||||
TenantGuard,
|
||||
{
|
||||
provide: TENANT_FINDER,
|
||||
useFactory: (tenantFinder: ITenantFinder) => tenantFinder,
|
||||
inject: [options.inject[0]], // 第一个注入项是 TenantFinder
|
||||
},
|
||||
{
|
||||
provide: 'TENANT_MIDDLEWARE_OPTIONS',
|
||||
useFactory: options.useFactory,
|
||||
inject: options.inject,
|
||||
},
|
||||
{
|
||||
provide: TenantContextMiddleware,
|
||||
useFactory: (
|
||||
tenantContext: TenantContextService,
|
||||
tenantFinder: ITenantFinder,
|
||||
middlewareOptions: TenantMiddlewareOptions,
|
||||
) => new TenantContextMiddleware(tenantContext, tenantFinder, middlewareOptions),
|
||||
inject: [TenantContextService, TENANT_FINDER, 'TENANT_MIDDLEWARE_OPTIONS'],
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
module: TenantContextModule,
|
||||
imports: options.imports,
|
||||
providers,
|
||||
exports: [TenantContextService, TenantGuard, TenantContextMiddleware, TENANT_FINDER],
|
||||
global: options.isGlobal ?? true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue