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 {
|
import {
|
||||||
TenantContextModule,
|
TenantContextModule,
|
||||||
TenantContextMiddleware,
|
TenantContextMiddleware,
|
||||||
|
SimpleTenantFinder,
|
||||||
DEFAULT_TENANT_ID,
|
DEFAULT_TENANT_ID,
|
||||||
} from '@iconsulting/shared';
|
} from '@iconsulting/shared';
|
||||||
import { EvolutionModule } from './evolution/evolution.module';
|
import { EvolutionModule } from './evolution/evolution.module';
|
||||||
|
|
@ -12,7 +13,6 @@ import { AdminModule } from './admin/admin.module';
|
||||||
import { HealthModule } from './health/health.module';
|
import { HealthModule } from './health/health.module';
|
||||||
import { AnalyticsModule } from './analytics/analytics.module';
|
import { AnalyticsModule } from './analytics/analytics.module';
|
||||||
import { TenantModule } from './infrastructure/tenant/tenant.module';
|
import { TenantModule } from './infrastructure/tenant/tenant.module';
|
||||||
import { TenantFinderService } from './infrastructure/tenant/tenant-finder.service';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -25,17 +25,6 @@ import { TenantFinderService } from './infrastructure/tenant/tenant-finder.servi
|
||||||
// 定时任务模块
|
// 定时任务模块
|
||||||
ScheduleModule.forRoot(),
|
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({
|
TypeOrmModule.forRootAsync({
|
||||||
imports: [ConfigModule],
|
imports: [ConfigModule],
|
||||||
|
|
@ -54,9 +43,20 @@ import { TenantFinderService } from './infrastructure/tenant/tenant-finder.servi
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// 租户模块
|
// 租户模块 (提供 TenantFinderService 用于 super-admin 租户管理)
|
||||||
TenantModule,
|
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
|
// Health check
|
||||||
HealthModule,
|
HealthModule,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,10 @@ import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { TenantORM } from '../database/postgres/entities/tenant.orm';
|
import { TenantORM } from '../database/postgres/entities/tenant.orm';
|
||||||
import { TenantFinderService } from './tenant-finder.service';
|
import { TenantFinderService } from './tenant-finder.service';
|
||||||
import { TENANT_FINDER } from '@iconsulting/shared';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([TenantORM])],
|
imports: [TypeOrmModule.forFeature([TenantORM])],
|
||||||
providers: [
|
providers: [TenantFinderService],
|
||||||
TenantFinderService,
|
exports: [TenantFinderService],
|
||||||
{
|
|
||||||
provide: TENANT_FINDER,
|
|
||||||
useExisting: TenantFinderService,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
exports: [TenantFinderService, TENANT_FINDER],
|
|
||||||
})
|
})
|
||||||
export class TenantModule {}
|
export class TenantModule {}
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,8 @@ export type { TenantAwareEntity } from './base-tenant.repository.js';
|
||||||
|
|
||||||
// Module
|
// Module
|
||||||
export { TenantContextModule } from './tenant-context.module.js';
|
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 { TenantContextService } from './tenant-context.service.js';
|
||||||
import {
|
import {
|
||||||
TenantContextMiddleware,
|
TenantContextMiddleware,
|
||||||
|
|
@ -13,9 +13,24 @@ import { TenantGuard } from './tenant.guard.js';
|
||||||
*/
|
*/
|
||||||
export interface TenantModuleOptions {
|
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 {
|
export interface TenantModuleAsyncOptions {
|
||||||
/**
|
/**
|
||||||
|
|
@ -48,6 +63,29 @@ export interface TenantModuleAsyncOptions {
|
||||||
isGlobal?: boolean;
|
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,
|
* isGlobal: true,
|
||||||
* })
|
* })
|
||||||
*/
|
*/
|
||||||
@Global()
|
|
||||||
@Module({})
|
@Module({})
|
||||||
export class TenantContextModule {
|
export class TenantContextModule {
|
||||||
/**
|
/**
|
||||||
|
|
@ -84,10 +121,6 @@ export class TenantContextModule {
|
||||||
const providers: Provider[] = [
|
const providers: Provider[] = [
|
||||||
TenantContextService,
|
TenantContextService,
|
||||||
TenantGuard,
|
TenantGuard,
|
||||||
{
|
|
||||||
provide: TENANT_FINDER,
|
|
||||||
useClass: options.tenantFinder,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: 'TENANT_MIDDLEWARE_OPTIONS',
|
provide: 'TENANT_MIDDLEWARE_OPTIONS',
|
||||||
useValue: options.middlewareOptions || {},
|
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 {
|
return {
|
||||||
module: TenantContextModule,
|
module: TenantContextModule,
|
||||||
|
imports: options.imports || [],
|
||||||
providers,
|
providers,
|
||||||
exports: [TenantContextService, TenantGuard, TenantContextMiddleware, TENANT_FINDER],
|
exports,
|
||||||
global: options.isGlobal ?? true,
|
global: options.isGlobal ?? true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -127,6 +179,9 @@ export class TenantContextModule {
|
||||||
provide: TENANT_FINDER,
|
provide: TENANT_FINDER,
|
||||||
useFactory: async (moduleOptions: TenantModuleOptions) => {
|
useFactory: async (moduleOptions: TenantModuleOptions) => {
|
||||||
// 动态实例化租户查找器
|
// 动态实例化租户查找器
|
||||||
|
if (!moduleOptions.tenantFinder) {
|
||||||
|
throw new Error('tenantFinder is required in async configuration');
|
||||||
|
}
|
||||||
return new moduleOptions.tenantFinder();
|
return new moduleOptions.tenantFinder();
|
||||||
},
|
},
|
||||||
inject: ['TENANT_MODULE_OPTIONS'],
|
inject: ['TENANT_MODULE_OPTIONS'],
|
||||||
|
|
@ -156,4 +211,53 @@ export class TenantContextModule {
|
||||||
global: options.isGlobal ?? true,
|
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