自定义Provider使用SqsService时无法解析SQS_OPTIONS依赖求助
问题分析
报错核心原因是SQS_OPTIONS是@ssut/nestjs-sqs的SqsModule内部提供的私有依赖,你的CoreModule未导入SqsModule,导致Nest无法在模块上下文中找到该依赖。同时要实现本地/生产队列切换的解耦目标,需要完善QueueResolver的多实现逻辑。
解决方案步骤
1. 完善QueueResolver的多实现
补充本地开发用的绕过队列实现,同时添加根据环境动态选择Provider的逻辑:
// queue.resolver.ts import { SqsService } from '@ssut/nestjs-sqs'; import { SqsOptions } from '@ssut/nestjs-sqs/dist/sqs.types'; import { DiscoveryService } from '@golevelup/nestjs-discovery'; import { SQS_OPTIONS } from '@ssut/nestjs-sqs/dist/sqs.constants'; import { ConfigService } from '@nestjs/config'; export interface QueueResolver { send(queue: string, message: any): Promise<any>; } // 本地开发用:绕过队列直接处理消息 export class LocalQueueResolver implements QueueResolver { async send(queue: string, message: any): Promise<any> { console.log(`[LocalQueue] 跳过队列,直接处理: 队列=${queue}, 内容=${JSON.stringify(message)}`); // 可根据需求直接调用消息处理逻辑,或仅打印日志 return Promise.resolve(message); } } // SQS队列实现 export class SqsQueueResolver implements QueueResolver { constructor(private readonly sqsService: SqsService) {} async send(queue: string, message: any): Promise<any> { return this.sqsService.send(queue, message); } } // 动态选择队列实现的工厂 export const QueueProvider = { provide: 'QueueResolver', useFactory: ( configService: ConfigService, sqsOptions: SqsOptions, discoveryService: DiscoveryService, ) => { const env = configService.get<string>('NODE_ENV'); if (env === 'development') { return new LocalQueueResolver(); } // 生产环境用SQS,未来切换Cloud Tasks只需替换此处实例化逻辑 const sqsService = new SqsService(sqsOptions, discoveryService); return new SqsQueueResolver(sqsService); }, inject: [ConfigService, SQS_OPTIONS, DiscoveryService], };
2. 修改CoreModule,导入SqsModule并导出QueueResolver
必须导入SqsModule来提供SQS_OPTIONS依赖,同时导出QueueResolver供其他模块使用:
// core.module.ts import { Global, Module } from '@nestjs/common'; import { MailProvider } from 'core/mail/mail.provider'; import * as path from 'path'; import { MailerModule } from '@nestjs-modules/mailer'; import { ConfigService } from '@nestjs/config'; import { PugAdapter } from '@nestjs-modules/mailer/dist/adapters/pug.adapter'; import { QueueProvider } from './queue/queue.resolver'; import { SqsModule } from '@ssut/nestjs-sqs'; // 导入SqsModule @Global() @Module({ imports: [ MailerModule.forRootAsync({ useFactory: async (configService: ConfigService) => ({ transport: { host: configService.get('MAIL').host, secure: false, port: configService.get('MAIL').port, auth: { user: configService.get('MAIL').auth.user, pass: configService.get('MAIL').auth.pass, }, }, defaults: { from: `"${configService.get('MAIL').from.name}" <${configService.get('MAIL').from.email}>`, }, template: { dir: path.join(__dirname, './mail/templates'), adapter: new PugAdapter(), options: { strict: true, }, }, }), inject: [ConfigService], }), // 导入并配置SqsModule,注册SQS_OPTIONS依赖 SqsModule.forRootAsync({ useFactory: async (configService: ConfigService) => ({ consumers: [], // 根据业务需求配置消费者 producers: [ { name: configService.get('SQS.QUEUE_NAME'), queueUrl: configService.get('SQS.QUEUE_URL'), region: configService.get('SQS.REGION'), }, ], }), inject: [ConfigService], }), ], providers: [MailProvider, QueueProvider], exports: [MailProvider, 'QueueResolver'], // 导出QueueResolver }) export class CoreModule {}
3. 业务模块中使用QueueResolver
在需要发送队列消息的服务中,直接注入QueueResolver即可,无需关心底层实现:
// example.service.ts import { Inject, Injectable } from '@nestjs/common'; import { QueueResolver } from 'core/queue/queue.resolver'; @Injectable() export class ExampleService { constructor(@Inject('QueueResolver') private readonly queueResolver: QueueResolver) {} async sendQueueMessage() { await this.queueResolver.send('business-queue', { data: 'test content' }); } }
关键说明
SQS_OPTIONS是SqsModule内部注册的依赖,必须通过导入配置好的SqsModule,才能让Nest在模块上下文中找到该依赖,解决注入报错。- 通过环境变量动态切换队列实现,本地开发跳过队列直接处理,生产用SQS;未来切换Cloud Tasks时,只需新增
CloudTasksQueueResolver类,修改工厂函数的实例化逻辑即可,完全符合解耦需求。
内容的提问来源于stack exchange,提问作者aasis504
相关产品推荐
相关产品推荐

