ticket
This commit is contained in:
@@ -48,8 +48,9 @@ export enum PermissionEnum {
|
|||||||
MANAGE_TICKETS = 'manage_tickets',
|
MANAGE_TICKETS = 'manage_tickets',
|
||||||
|
|
||||||
// Notif Permsissions
|
// Notif Permsissions
|
||||||
NEW_REQUEST_NOTIFICATION='new_request_notification',
|
NEW_REQUEST_NOTIFICATION = 'new_request_notification',
|
||||||
NEW_PAYMENT_NOTIFICATION='new_payment_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.CREATE_USER]: "",
|
||||||
[PermissionEnum.UPDATE_USER]: "",
|
[PermissionEnum.UPDATE_USER]: "",
|
||||||
[PermissionEnum.DELETE_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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,11 +6,20 @@ import { Ticket } from './entities/ticket.entity';
|
|||||||
import { TicketRepository } from './repositories/ticket.repository';
|
import { TicketRepository } from './repositories/ticket.repository';
|
||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
|
import { AdminModule } from '../admin/admin.module';
|
||||||
|
import { NotificationsModule } from '../notification/notifications.module';
|
||||||
|
import { TicketListeners } from './listeners/ticket.listeners';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [MikroOrmModule.forFeature([Ticket]), AuthModule,JwtModule],
|
imports: [
|
||||||
|
MikroOrmModule.forFeature([Ticket]),
|
||||||
|
AuthModule,
|
||||||
|
JwtModule,
|
||||||
|
AdminModule,
|
||||||
|
NotificationsModule,
|
||||||
|
],
|
||||||
controllers: [TicketController],
|
controllers: [TicketController],
|
||||||
providers: [TicketService, TicketRepository],
|
providers: [TicketService, TicketRepository, TicketListeners],
|
||||||
exports: [TicketService],
|
exports: [TicketService],
|
||||||
})
|
})
|
||||||
export class TicketModule {}
|
export class TicketModule {}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common';
|
import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { CreateTicketDto } from './dto/create-ticket.dto';
|
import { CreateTicketDto } from './dto/create-ticket.dto';
|
||||||
import { UpdateTicketDto } from './dto/update-ticket.dto';
|
import { UpdateTicketDto } from './dto/update-ticket.dto';
|
||||||
import { UpdateTicketStatusDto } from './dto/update-ticket-status.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 { Admin } from 'src/modules/admin/entities/admin.entity';
|
||||||
import { User } from 'src/modules/user/entities/user.entity';
|
import { User } from 'src/modules/user/entities/user.entity';
|
||||||
import { TicketStatusEnum } from './enum/status.enum';
|
import { TicketStatusEnum } from './enum/status.enum';
|
||||||
|
import { TicketCreatedByUserEvent } from './events/ticket.events';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TicketService {
|
export class TicketService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly em: EntityManager,
|
private readonly em: EntityManager,
|
||||||
private readonly ticketRepository: TicketRepository,
|
private readonly ticketRepository: TicketRepository,
|
||||||
|
private readonly eventEmitter: EventEmitter2,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async createAsAdmin(createTicketDto: CreateTicketDto, adminId: string) {
|
async createAsAdmin(createTicketDto: CreateTicketDto, adminId: string) {
|
||||||
@@ -83,6 +86,13 @@ export class TicketService {
|
|||||||
status: TicketStatusEnum.OPEN,
|
status: TicketStatusEnum.OPEN,
|
||||||
});
|
});
|
||||||
await this.em.persistAndFlush(ticket);
|
await this.em.persistAndFlush(ticket);
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
this.eventEmitter.emit(
|
||||||
|
TicketCreatedByUserEvent.name,
|
||||||
|
new TicketCreatedByUserEvent(ticket.id, ticket.subject ?? null, userId),
|
||||||
|
);
|
||||||
|
}
|
||||||
return ticket;
|
return ticket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user