This commit is contained in:
2026-02-25 09:15:07 +03:30
parent 37d3a241e7
commit 65bd50cff7
5 changed files with 102 additions and 5 deletions
+5 -3
View File
@@ -48,8 +48,9 @@ export enum PermissionEnum {
MANAGE_TICKETS = 'manage_tickets',
// Notif Permsissions
NEW_REQUEST_NOTIFICATION='new_request_notification',
NEW_PAYMENT_NOTIFICATION='new_payment_notification',
NEW_REQUEST_NOTIFICATION = 'new_request_notification',
NEW_PAYMENT_NOTIFICATION = 'new_payment_notification',
NEW_TICKET_NOTIFICATION = 'new_ticket_notification',
}
@@ -86,5 +87,6 @@ export const PermissionTitles: Record<PermissionEnum, string> = {
[PermissionEnum.CREATE_USER]: "",
[PermissionEnum.UPDATE_USER]: "",
[PermissionEnum.DELETE_USER]: "",
[PermissionEnum.VIEW_USERS]: ""
[PermissionEnum.VIEW_USERS]: "",
[PermissionEnum.NEW_TICKET_NOTIFICATION]: ""
};
@@ -0,0 +1,7 @@
export class TicketCreatedByUserEvent {
constructor(
public readonly ticketId: string,
public readonly subject: string | null,
public readonly userId: string,
) {}
}
@@ -0,0 +1,69 @@
import { Injectable, Logger } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { EntityManager } from '@mikro-orm/postgresql';
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
import { PermissionEnum } from 'src/common/enums/permission.enum';
import { NotifTitle } from 'src/modules/notification/interfaces/notification.interface';
import { Notification } from 'src/modules/notification/entities/notification.entity';
import { NotificationQueueService } from 'src/modules/notification/services/notification-queue.service';
import { TicketCreatedByUserEvent } from '../events/ticket.events';
@Injectable()
export class TicketListeners {
private readonly logger = new Logger(TicketListeners.name);
constructor(
private readonly adminRepository: AdminRepository,
private readonly em: EntityManager,
private readonly notificationQueueService: NotificationQueueService,
) {}
@OnEvent(TicketCreatedByUserEvent.name)
async handleTicketCreatedByUser(event: InstanceType<typeof TicketCreatedByUserEvent>) {
try {
this.logger.log(
`Ticket created by user: ticketId=${event.ticketId}, userId=${event.userId}`,
);
const admins = await this.adminRepository.findAdminsWithPermission(
PermissionEnum.NEW_TICKET_NOTIFICATION,
);
if (admins.length === 0) {
this.logger.log('No admins with NEW_TICKET_NOTIFICATION permission');
return;
}
const subjectText = event.subject ? `: ${event.subject}` : '';
const body = `تیکت جدید${subjectText}`;
const notifications = admins.map((admin) =>
this.em.create(Notification, {
admin,
title: NotifTitle.NEW_TICKET,
content: body,
}),
);
await this.em.persistAndFlush(notifications);
await this.notificationQueueService.addBulkInAppNotifications(
notifications.map((n) => ({
recipient: { adminId: n.admin!.id },
subject: NotifTitle.NEW_TICKET,
body,
notificationId: n.id,
})),
);
this.logger.log(
`Notification sent to ${admins.length} admin(s) for new ticket ${event.ticketId}`,
);
} catch (error) {
this.logger.error(
`Failed to send notification for ticket ${event.ticketId}`,
error instanceof Error ? error.stack : String(error),
);
}
}
}
+11 -2
View File
@@ -6,11 +6,20 @@ import { Ticket } from './entities/ticket.entity';
import { TicketRepository } from './repositories/ticket.repository';
import { AuthModule } from '../auth/auth.module';
import { JwtModule } from '@nestjs/jwt';
import { AdminModule } from '../admin/admin.module';
import { NotificationsModule } from '../notification/notifications.module';
import { TicketListeners } from './listeners/ticket.listeners';
@Module({
imports: [MikroOrmModule.forFeature([Ticket]), AuthModule,JwtModule],
imports: [
MikroOrmModule.forFeature([Ticket]),
AuthModule,
JwtModule,
AdminModule,
NotificationsModule,
],
controllers: [TicketController],
providers: [TicketService, TicketRepository],
providers: [TicketService, TicketRepository, TicketListeners],
exports: [TicketService],
})
export class TicketModule {}
+10
View File
@@ -1,5 +1,6 @@
import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common';
import { EntityManager } from '@mikro-orm/postgresql';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { CreateTicketDto } from './dto/create-ticket.dto';
import { UpdateTicketDto } from './dto/update-ticket.dto';
import { UpdateTicketStatusDto } from './dto/update-ticket-status.dto';
@@ -12,12 +13,14 @@ import { TicketRepository } from './repositories/ticket.repository';
import { Admin } from 'src/modules/admin/entities/admin.entity';
import { User } from 'src/modules/user/entities/user.entity';
import { TicketStatusEnum } from './enum/status.enum';
import { TicketCreatedByUserEvent } from './events/ticket.events';
@Injectable()
export class TicketService {
constructor(
private readonly em: EntityManager,
private readonly ticketRepository: TicketRepository,
private readonly eventEmitter: EventEmitter2,
) { }
async createAsAdmin(createTicketDto: CreateTicketDto, adminId: string) {
@@ -83,6 +86,13 @@ export class TicketService {
status: TicketStatusEnum.OPEN,
});
await this.em.persistAndFlush(ticket);
if (!parent) {
this.eventEmitter.emit(
TicketCreatedByUserEvent.name,
new TicketCreatedByUserEvent(ticket.id, ticket.subject ?? null, userId),
);
}
return ticket;
}