From a1fe186c5f4e594de2698c30966315ba90ab06c4 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Tue, 24 Feb 2026 12:02:41 +0330 Subject: [PATCH] send notif after chat --- src/app.module.ts | 1 - src/modules/chat/DTO/create-chat.dto.ts | 2 +- src/modules/chat/chat.module.ts | 13 +-- src/modules/chat/events/chat.events.ts | 6 ++ src/modules/chat/listeners/chat.listeners.ts | 84 ++++++++++++++++++++ src/modules/chat/providers/chat.service.ts | 10 ++- 6 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 src/modules/chat/events/chat.events.ts create mode 100644 src/modules/chat/listeners/chat.listeners.ts diff --git a/src/app.module.ts b/src/app.module.ts index a3f74ea..e70c811 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -44,7 +44,6 @@ import { TicketModule } from './modules/ticket/ticket.module'; ProductModule, RolesModule, PaymentModule, - // OrderModule, NotificationsModule, EventEmitterModule.forRoot(), PrintModule, diff --git a/src/modules/chat/DTO/create-chat.dto.ts b/src/modules/chat/DTO/create-chat.dto.ts index 212ad6e..d461c96 100755 --- a/src/modules/chat/DTO/create-chat.dto.ts +++ b/src/modules/chat/DTO/create-chat.dto.ts @@ -7,7 +7,7 @@ import { TicketMessageEnum } from "../../../common/enums/message.enum"; export class CreateChatDto { @IsNotEmpty({ message: TicketMessageEnum.MESSAGE_REQUIRED }) @IsString({ message: TicketMessageEnum.MESSAGE_STRING }) - @Length(5, 5000, { message: TicketMessageEnum.MESSAGE_LENGTH }) + @Length(1, 5000, { message: TicketMessageEnum.MESSAGE_LENGTH }) @ApiProperty({ description: "The message content of the ticket" }) content: string; diff --git a/src/modules/chat/chat.module.ts b/src/modules/chat/chat.module.ts index 7905821..10a6a35 100755 --- a/src/modules/chat/chat.module.ts +++ b/src/modules/chat/chat.module.ts @@ -7,17 +7,20 @@ import { MikroOrmModule } from "@mikro-orm/nestjs"; import { AdminModule } from "../admin/admin.module"; import { UserModule } from "../user/user.module"; import { JwtModule } from "@nestjs/jwt"; - - +import { Request } from "../request/entities/request.entity"; +import { Order } from "../order/entities/order.entity"; +import { NotificationsModule } from "../notification/notifications.module"; +import { ChatListeners } from "./listeners/chat.listeners"; @Module({ imports: [ - MikroOrmModule.forFeature([Chat]), + MikroOrmModule.forFeature([Chat, Request, Order]), AdminModule, UserModule, - JwtModule.register({}) + JwtModule.register({}), + NotificationsModule, ], - providers: [ChattService, ChatRepository], + providers: [ChattService, ChatRepository, ChatListeners], controllers: [ChatController], exports: [ChattService, ChatRepository], }) diff --git a/src/modules/chat/events/chat.events.ts b/src/modules/chat/events/chat.events.ts new file mode 100644 index 0000000..b967afa --- /dev/null +++ b/src/modules/chat/events/chat.events.ts @@ -0,0 +1,6 @@ +export class ChatCreatedByAdminEvent { + constructor( + public readonly refId: string, + public readonly chatId: string, + ) {} +} diff --git a/src/modules/chat/listeners/chat.listeners.ts b/src/modules/chat/listeners/chat.listeners.ts new file mode 100644 index 0000000..8f70663 --- /dev/null +++ b/src/modules/chat/listeners/chat.listeners.ts @@ -0,0 +1,84 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { EntityManager } from '@mikro-orm/postgresql'; +import { Request } from 'src/modules/request/entities/request.entity'; +import { Order } from 'src/modules/order/entities/order.entity'; +import { NotificationService } from 'src/modules/notification/services/notification.service'; +import { NotifChannelEnum, NotifTitle } from 'src/modules/notification/interfaces/notification.interface'; +import { ChatCreatedByAdminEvent } from '../events/chat.events'; + +@Injectable() +export class ChatListeners { + private readonly logger = new Logger(ChatListeners.name); + + constructor( + private readonly em: EntityManager, + private readonly notificationService: NotificationService, + ) {} + + @OnEvent(ChatCreatedByAdminEvent.name) + async handleChatCreatedByAdmin(event: ChatCreatedByAdminEvent) { + try { + this.logger.log( + `Chat created by admin: refId=${event.refId}, chatId=${event.chatId}`, + ); + + const user = await this.findUserByRefId(event.refId); + if (!user) { + this.logger.warn( + `No user found for refId=${event.refId}, skipping notification`, + ); + return; + } + + const body = 'پیام جدید از پشتیبانی برای شما ارسال شده است.'; + + await this.notificationService.sendNotification({ + channels: [NotifChannelEnum.SMS], + recipients: [{ userId: user.id }], + message: { + title: NotifTitle.NEW_TICKET, + content: body, + sms: { + templateId: '', + parameters: {}, + }, + }, + }); + + this.logger.log( + `Notification sent to user ${user.id} for admin chat on refId=${event.refId}`, + ); + } catch (error) { + this.logger.error( + `Failed to send notification for chat created by admin: refId=${event.refId}`, + error instanceof Error ? error.stack : String(error), + ); + } + } + + /** + * Find the user associated with refId: first search in requests, then in orders. + */ + private async findUserByRefId(refId: string): Promise<{ id: string } | null> { + const request = await this.em.findOne( + Request, + { id: refId }, + { populate: ['user'], fields: ['id', 'user'] }, + ); + if (request?.user?.id) { + return { id: request.user.id }; + } + + const order = await this.em.findOne( + Order, + { id: refId }, + { populate: ['user'], fields: ['id', 'user'] }, + ); + if (order?.user?.id) { + return { id: order.user.id }; + } + + return null; + } +} diff --git a/src/modules/chat/providers/chat.service.ts b/src/modules/chat/providers/chat.service.ts index ba0a2a3..88657ff 100755 --- a/src/modules/chat/providers/chat.service.ts +++ b/src/modules/chat/providers/chat.service.ts @@ -9,6 +9,8 @@ import { UpdateChatDto } from "../DTO/update-chat.dto"; import { Chat } from "../entities/ticket.entity"; import { AdminService } from "src/modules/admin/providers/admin.service"; import { UserService } from "src/modules/user/providers/user.service"; +import { EventEmitter2 } from "@nestjs/event-emitter"; +import { ChatCreatedByAdminEvent } from "../events/chat.events"; @Injectable() export class ChattService { @@ -18,18 +20,22 @@ export class ChattService { private readonly adminService: AdminService, private readonly userService: UserService, private readonly em: EntityManager, + private readonly eventEmitter: EventEmitter2, ) { } async createChatAsAdmin(refId: string, dto: CreateChatDto, adminId: string) { - // const orderItem = await this.findOrderItemOrFail(refId) - let admin: null | Admin = null admin = await this.adminService.findOrFail(adminId) const ticket = await this.createChat(refId, dto, undefined, admin) + this.eventEmitter.emit( + ChatCreatedByAdminEvent.name, + new ChatCreatedByAdminEvent(refId, ticket.id), + ); + return ticket }