Files
negareh-api/src/modules/ticket/providers/tickets.service.ts
T
2026-01-21 19:46:59 +03:30

425 lines
15 KiB
TypeScript
Executable File

import { TicketRepository } from "../repositories/tickets.repository";
import { BadRequestException, Injectable } from "@nestjs/common";
import { CreateTicketDto } from "../DTO/create-ticket.dto";
import { OrderRepository } from "src/modules/order/repositories/order.repository";
import { User } from "src/modules/user/entities/user.entity";
import { UserRepository } from "src/modules/user/repositories/user.repository";
import { AdminRepository } from "src/modules/admin/repositories/admin.repository";
import { Admin } from "src/modules/admin/entities/admin.entity";
import { FindTicketQueryDto } from "../DTO/search-ticket-query.dto";
import { EntityManager } from "@mikro-orm/postgresql";
import { UpdateTicketDto } from "../DTO/update-ticket.dto";
@Injectable()
export class TicketService {
constructor(
private readonly ticketsRepository: TicketRepository,
private readonly orderRepository: OrderRepository,
private readonly userRepository: UserRepository,
private readonly adminRepository: AdminRepository,
private readonly em: EntityManager,
) { }
async createTicket(orderId: string, dto: CreateTicketDto, userId?: string, adminId?: string) {
const { content, attachments } = dto
if (!userId && !adminId) {
throw new BadRequestException("One of userId ord adminId is required")
}
const order = await this.orderRepository.findOne({ id: orderId })
if (!order) {
throw new BadRequestException("Order not found")
}
let user: null | User = null
let admin: null | Admin = null
if (userId) {
user = await this.userRepository.findOne({ id: userId })
if (!user) {
throw new BadRequestException("User not found")
}
}
if (adminId) {
admin = await this.adminRepository.findOne({ id: adminId })
if (!admin) {
throw new BadRequestException("Admin not found")
}
}
if (!user && !admin) {
throw new BadRequestException("One of user or admin is required")
}
const ticket = this.ticketsRepository.create({
content,
attachments,
admin,
user,
order,
})
await this.ticketsRepository.em.persistAndFlush(ticket)
return ticket
}
// //******************************** */
async getTickets(orderId: string, queryDto: FindTicketQueryDto, userId?: string) {
const order = await this.orderRepository.findOne({ id: orderId })
if (!order) {
throw new BadRequestException("order not found!")
}
if (userId) {
if (order.user.id !== userId) {
throw new BadRequestException("This ticket doenst belongs to You !")
}
}
const data = await this.ticketsRepository.findAllPaginated({ ...queryDto, orderId });
return data
}
//******************************** */
async deleteTicket(ticketId: string, userId?: string) {
const ticket = await this.ticketsRepository.findOne({ id: ticketId }, { populate: ['order', 'order.user'] })
if (!ticket) {
throw new BadRequestException('ticket not found!')
}
if (userId && ticket.order.user.id !== userId) {
throw new BadRequestException("You can not Delete this Ticket")
}
await this.em.removeAndFlush(ticket)
return true
}
// //******************************** */
async updateTicket(ticketId: string, dto: UpdateTicketDto, userId?: string) {
const ticket = await this.ticketsRepository.findOne({ id: ticketId }, { populate: ['order', 'order.user'] })
if (!ticket) {
throw new BadRequestException('ticket not found!')
}
if (userId && ticket.order.user.id !== userId) {
throw new BadRequestException("You can not Update this Ticket")
}
this.ticketsRepository.assign(ticket, dto, {
onlyProperties: true,
})
await this.em.flush()
return true
}
// //******************************** */
// async createTicketMessage(ticketId: string, createDto: CreateTicketMessageDto, userId: string, isAdmin: boolean) {
// const queryRunner = this.dataSource.createQueryRunner();
// await queryRunner.connect();
// await queryRunner.startTransaction();
// try {
// const ticket = await this.findTicket(queryRunner, ticketId, userId, isAdmin);
// if (ticket.status === TicketStatus.CLOSED) throw new BadRequestException(TicketMessageEnum.TICKET_CLOSED);
// const isSuperAdmin = await this.usersService.isSuperAdmin(userId);
// if (isAdmin && !isSuperAdmin && ticket.assignedTo?.id !== userId)
// throw new BadRequestException(TicketMessageEnum.TICKET_NOT_ASSIGNED_TO_USER);
// const user = await queryRunner.manager.findOneBy(User, { id: userId });
// if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
// const ticketMessage = queryRunner.manager.create(TicketMessage, {
// content: createDto.content,
// author: user,
// ticket,
// });
// await queryRunner.manager.save(TicketMessage, ticketMessage);
// if (createDto.attachmentUrls?.length) {
// await this.saveAttachments(queryRunner, createDto.attachmentUrls, ticketMessage);
// }
// this.updateTicketStatus(ticket, isAdmin);
// await queryRunner.manager.save(Ticket, ticket);
// if (isAdmin) {
// await this.sendNotificationForAdminResponse(ticket, user);
// }
// await queryRunner.commitTransaction();
// return {
// message: TicketMessageEnum.MESSAGE_CREATED,
// ticketMessage,
// };
// } catch (error) {
// await queryRunner.rollbackTransaction();
// if (error instanceof HttpException) throw error;
// throw new InternalServerErrorException(TicketMessageEnum.ERROR_IN_TICKET_MSG_CREATION);
// } finally {
// await queryRunner.release();
// }
// }
// //******************************** */
// async getTicketMessages(ticketId: string, userId: string, isAdmin: boolean) {
// let ticket: null | Ticket = null;
// //
// if (isAdmin) {
// ticket = await this.ticketsRepository.findTicketById(ticketId);
// } else {
// ticket = await this.ticketsRepository.findTicketById(ticketId, userId);
// }
// if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND);
// const messages = await this.ticketMessagesRepository.findMessagesByTicketId(ticketId);
// const userSupportPlan = await this.supportPlansService.getUserSupportPlanOfUser(ticket.user.id);
// return {
// ticket,
// messages,
// supportPlan: userSupportPlan,
// };
// }
// //******************************** */
// async closeTicketByUser(ticketId: string, isAdmin: boolean, userIpAndHeaders: IUserIpAndHeaders) {
// let ticket: null | Ticket = null;
// if (isAdmin) {
// ticket = await this.ticketsRepository.findTicketById(ticketId);
// } else {
// ticket = await this.ticketsRepository.findTicketById(ticketId, userIpAndHeaders.userId);
// }
// if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND);
// ticket.status = TicketStatus.CLOSED;
// await this.ticketsRepository.save(ticket);
// await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, {
// user: { id: userIpAndHeaders.userId },
// endpoint: "/tickets",
// requestId: userIpAndHeaders.headers["x-request-id"],
// ipAddress: userIpAndHeaders.ip,
// userAgent: userIpAndHeaders.headers["user-agent"],
// actionDescription: `Admin closed ticket: ${ticket.subject}`,
// metadata: {
// ticketId: ticket.id,
// ticketSubject: ticket.subject,
// },
// });
// return {
// message: TicketMessageEnum.CLOSED,
// };
// }
// //******************************** * /
// async openTicketByAdmin(ticketId: string, userIpAndHeaders: IUserIpAndHeaders) {
// const ticket = await this.ticketsRepository.findTicketById(ticketId);
// if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND);
// ticket.status = TicketStatus.PENDING;
// await this.ticketsRepository.save(ticket);
// await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, {
// user: { id: userIpAndHeaders.userId },
// endpoint: "/tickets",
// requestId: userIpAndHeaders.headers["x-request-id"],
// ipAddress: userIpAndHeaders.ip,
// userAgent: userIpAndHeaders.headers["user-agent"],
// actionDescription: `Admin opened ticket: ${ticket.subject}`,
// metadata: {
// ticketId: ticket.id,
// ticketSubject: ticket.subject,
// },
// });
// return {
// message: TicketMessageEnum.OPENED,
// };
// }
// //******************************** */
// async referTicket(ticketId: string, referDto: ReferTicketDto, userIpAndHeaders: IUserIpAndHeaders) {
// const queryRunner = this.dataSource.createQueryRunner();
// try {
// await queryRunner.connect();
// await queryRunner.startTransaction();
// const isSuperAdmin = await this.usersService.isSuperAdmin(userIpAndHeaders.userId);
// // if (!isSuperAdmin) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_ASSIGNED_TO_USER);
// console.log("isSuperAdmin", isSuperAdmin);
// const ticket = await this.ticketsRepository.findTicketById(ticketId);
// if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND);
// const admin = await this.usersService.findOneByIdWithQueryRunner(referDto.adminId, queryRunner);
// ticket.assignedTo = admin;
// await queryRunner.manager.save(this.ticketsRepository.target, ticket);
// await this.notificationQueue.addAssignTicketNotificationForAdmin(admin.id, {
// ticketId: ticket.numericId.toString(),
// subject: ticket.subject,
// date: ticket.createdAt,
// userPhone: admin.phone,
// userEmail: admin.email,
// user: `${ticket.user.firstName} ${ticket.user.lastName}`,
// });
// await queryRunner.commitTransaction();
// await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, {
// user: { id: userIpAndHeaders.userId },
// endpoint: "/tickets",
// requestId: userIpAndHeaders.headers["x-request-id"],
// ipAddress: userIpAndHeaders.ip,
// userAgent: userIpAndHeaders.headers["user-agent"],
// actionDescription: `Admin referred ticket: ${ticket.subject}`,
// metadata: {
// ticketId: ticket.id,
// ticketSubject: ticket.subject,
// },
// });
// return {
// message: TicketMessageEnum.REFERRED,
// ticket,
// };
// } catch (error) {
// await queryRunner.rollbackTransaction();
// throw error;
// } finally {
// await queryRunner.release();
// }
// }
// //******************************** */
// async getUnreadTickets() {
// const unreadTickets = await this.ticketsRepository.count({
// where: { status: TicketStatus.PENDING },
// });
// return unreadTickets;
// }
// //******************************** */
// async countUserTickets(userId: string) {
// const ticketCount = await this.ticketsRepository.count({
// where: { user: { id: userId }, status: Not(TicketStatus.CLOSED) },
// });
// return ticketCount;
// }
// //***************************** */
// private async assignTicketAndNotify(ticket: Ticket, ticketCategory: TicketCategory, user: User, queryRunner: QueryRunner) {
// const assignedUser = await this.referralService.assignAdmin(ticketCategory.group);
// //
// ticket.assignedTo = assignedUser;
// await queryRunner.manager.save(Ticket, ticket);
// await this.notificationQueue.addAssignTicketNotificationForAdmin(assignedUser.id, {
// ticketId: ticket.numericId.toString(),
// subject: ticket.subject,
// date: ticket.createdAt,
// userPhone: user.phone,
// userEmail: user.email,
// user: `${user.firstName} ${user.lastName}`,
// });
// await this.notificationQueue.addTicketNotification(user.id, {
// ticketId: ticket.numericId.toString(),
// subject: ticket.subject,
// date: ticket.createdAt,
// userPhone: user.phone,
// userEmail: user.email,
// });
// await this.notifySuperAdminsForTicket(queryRunner, ticket);
// }
// //***************************** */
// private async findTicket(queryRunner: QueryRunner, ticketId: string, userId: string, isAdmin: boolean): Promise<Ticket> {
// const ticket = isAdmin
// ? await queryRunner.manager.findOne(Ticket, {
// where: { id: ticketId },
// relations: { user: true, assignedTo: true },
// })
// : await queryRunner.manager.findOne(Ticket, {
// where: { id: ticketId, user: { id: userId } },
// relations: { user: true, assignedTo: true },
// });
// if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND);
// return ticket;
// }
// //***************************** */
// private updateTicketStatus(ticket: Ticket, isAdmin: boolean): void {
// if (isAdmin && ticket.status === TicketStatus.PENDING) {
// ticket.status = TicketStatus.ANSWERED;
// } else if (!isAdmin && ticket.status === TicketStatus.ANSWERED) {
// ticket.status = TicketStatus.PENDING;
// }
// }
// //***************************** */
// private async saveAttachments(queryRunner: QueryRunner, attachmentUrls: string[], ticketMessage: TicketMessage) {
// const attachments = attachmentUrls.map((url) =>
// queryRunner.manager.create(TicketMessageAttachment, {
// attachmentUrl: url,
// ticketMessage,
// }),
// );
// await queryRunner.manager.save(TicketMessageAttachment, attachments);
// }
// //***************************** */
// private async sendNotificationForAdminResponse(ticket: Ticket, admin: User) {
// await this.notificationQueue.addAnswerTicketNotification(ticket.user.id, {
// ticketId: ticket.numericId.toString(),
// subject: ticket.subject,
// date: ticket.createdAt,
// userPhone: admin.phone,
// userEmail: admin.email,
// });
// }
// //***************************** */
// private async notifySuperAdminsForTicket(queryRunner: QueryRunner, ticket: Ticket) {
// const superAdmins = await this.adminsService.getSuperAdmins(queryRunner);
// for (const admin of superAdmins) {
// await this.notificationQueue.addNewTicketGlobalNotification(admin.id, {
// ticketId: ticket.numericId.toString(),
// ticketSubject: ticket.subject,
// userPhone: admin.phone,
// userEmail: admin.email,
// fullName: `${ticket.user.firstName} ${ticket.user.lastName}`,
// });
// }
// }
// //***************************** */
// async scheduleAutoCloseJob() {
// await this.ticketQueue.add(
// TICKET.AUTO_CLOSE_JOB_NAME,
// {},
// {
// repeat: { pattern: TICKET.AUTO_CLOSE_REPEAT_PATTERN },
// priority: TICKET.AUTO_CLOSE_PRIORITY,
// },
// );
// this.logger.log("Scheduled auto-close-inactive job for tickets");
// }
}