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 { // 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"); // } }