update ticket

This commit is contained in:
2026-01-21 19:37:54 +03:30
parent 6c7dfbef1f
commit 9e233cd81a
5 changed files with 70 additions and 326 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
import { ApiProperty } from "@nestjs/swagger";
import { ArrayMinSize, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator";
import { IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator";
import { TicketMessageEnum, UserMessage } from "../../../common/enums/message.enum";
import { TicketMessageEnum } from "../../../common/enums/message.enum";
export class CreateTicketDto {
@@ -1,3 +0,0 @@
export class SearchTicketCategoryDto {}
+6
View File
@@ -0,0 +1,6 @@
import { PartialType } from "@nestjs/swagger";
import { CreateTicketDto } from "./create-ticket.dto";
export class UpdateTicketDto extends PartialType(CreateTicketDto){}
+37 -267
View File
@@ -10,6 +10,8 @@ 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()
@@ -21,7 +23,7 @@ export class TicketService {
private readonly orderRepository: OrderRepository,
private readonly userRepository: UserRepository,
private readonly adminRepository: AdminRepository,
// private readonly adminsService: AdminsService,
private readonly em: EntityManager,
) { }
async createTicket(orderId: string, dto: CreateTicketDto, userId?: string, adminId?: string) {
@@ -67,271 +69,6 @@ export class TicketService {
return ticket
}
// async onModuleInit() {
// await this.scheduleAutoCloseJob();
// }
// //******************************** */
// async getTicketCategories() {
// const ticketCategories = await this.ticketsCategoryRepository.find({});
// return {
// ticketCategories,
// };
// }
// //******************************** */
// async getCategoriesList(queryDto: SearchTicketCategoryDto) {
// const [categories, count] = await this.ticketsCategoryRepository.findCategoriesList(queryDto);
// return { categories, count, paginate: true };
// }
// //******************************** */
// async createTicketCategory(createDto: CreateTicketCategoryDto, userIpAndHeaders: IUserIpAndHeaders) {
// const { userGroup } = await this.usersService.getUserGroupById(createDto.userGroupId);
// const existCategory = await this.ticketsCategoryRepository.findCategoryByTitle(createDto.title);
// if (existCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_EXIST);
// const ticketCategory = this.ticketsCategoryRepository.create({
// ...createDto,
// group: userGroup,
// });
// await this.ticketsCategoryRepository.save(ticketCategory);
// await this.accessLogService.logCreate("TicketCategory", UserType.ADMIN, {
// user: { id: userIpAndHeaders.userId },
// endpoint: "/tickets/categories",
// requestId: userIpAndHeaders.headers["x-request-id"],
// ipAddress: userIpAndHeaders.ip,
// userAgent: userIpAndHeaders.headers["user-agent"],
// actionDescription: `Admin created ticket category: ${createDto.title}`,
// metadata: {
// categoryId: ticketCategory.id,
// categoryTitle: ticketCategory.title,
// },
// });
// return {
// message: TicketMessageEnum.CATEGORY_CREATED,
// };
// }
// //******************************** */
// async updateCategory(paramDto: ParamDto, updateCategoryDto: UpdateTicketCategoryDto, userIpAndHeaders: IUserIpAndHeaders) {
// const category = await this.ticketsCategoryRepository.findCategoryById(paramDto.id);
// if (!category) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND);
// if (updateCategoryDto.title) {
// const { title } = updateCategoryDto;
// const existCategory = await this.ticketsCategoryRepository.findOneBy({
// title,
// id: Not(paramDto.id),
// });
// if (existCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_EXIST);
// }
// await this.ticketsCategoryRepository.save({
// ...category,
// ...updateCategoryDto,
// });
// await this.accessLogService.logUpdate("TicketCategory", paramDto.id, UserType.ADMIN, {
// user: { id: userIpAndHeaders.userId },
// endpoint: "/tickets/categories",
// requestId: userIpAndHeaders.headers["x-request-id"],
// ipAddress: userIpAndHeaders.ip,
// userAgent: userIpAndHeaders.headers["user-agent"],
// actionDescription: `Admin updated ticket category: ${category.title}`,
// metadata: {
// categoryId: category.id,
// categoryTitle: category.title,
// },
// oldValues: JSON.stringify({ ...category }),
// newValues: JSON.stringify({ ...updateCategoryDto }),
// });
// return {
// message: CommonMessage.UPDATE_SUCCESS,
// };
// }
// //******************************** */
// async toggleCategoryStatus(paramDto: ParamDto, userIpAndHeaders: IUserIpAndHeaders) {
// const category = await this.ticketsCategoryRepository.findCategoryById(paramDto.id);
// if (!category) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND);
// //
// category.isActive = !category.isActive;
// await this.ticketsCategoryRepository.save(category);
// //
// await this.accessLogService.logUpdate("TicketCategory", paramDto.id, UserType.ADMIN, {
// user: { id: userIpAndHeaders.userId },
// endpoint: "/tickets/categories",
// requestId: userIpAndHeaders.headers["x-request-id"],
// ipAddress: userIpAndHeaders.ip,
// userAgent: userIpAndHeaders.headers["user-agent"],
// actionDescription: `Admin toggled ticket category status: ${category.title}`,
// metadata: {
// categoryId: category.id,
// categoryTitle: category.title,
// isActive: category.isActive,
// },
// });
// return {
// message: CommonMessage.UPDATE_SUCCESS,
// isActive: category.isActive,
// };
// }
// //******************************** */
// private async checkTicketLimit(userId: string, queryRunner: QueryRunner): Promise<void> {
// const { userSupportPlan } = await this.supportPlansService.getUserSupportPlanOfUser(userId);
// if (!userSupportPlan) throw new BadRequestException(TicketMessageEnum.NO_ACTIVE_SUPPORT_PLAN);
// const ticketLimitFeature = userSupportPlan.supportPlan.features.find(
// (feature) => feature.featureKey === SupportPlanFeatureKey.TICKET_LIMIT,
// );
// if (!ticketLimitFeature) throw new BadRequestException(TicketMessageEnum.TICKET_LIMIT_NOT_FOUND);
// const ticketLimit = parseInt(ticketLimitFeature.featureValue);
// if (isNaN(ticketLimit)) return;
// const today = dayjs().startOf("day").toDate();
// const ticketCount = await queryRunner.manager.count(this.ticketsRepository.target, {
// where: {
// user: { id: userId },
// createdAt: MoreThanOrEqual(today),
// },
// });
// if (ticketCount >= ticketLimit) throw new BadRequestException(TicketMessageEnum.TICKET_LIMIT_EXCEEDED);
// }
// //******************************** */
// async createTicket(createDto: CreateTicketDto, userId: string) {
// const queryRunner = this.dataSource.createQueryRunner();
// try {
// await queryRunner.connect();
// await queryRunner.startTransaction();
// const [user, ticketCategory] = await Promise.all([
// this.usersService.findOneByIdWithQueryRunner(userId, queryRunner),
// queryRunner.manager.findOne(TicketCategory, { where: { id: createDto.categoryId }, relations: { group: { users: true } } }),
// ]);
// if (!ticketCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND);
// await this.checkTicketLimit(userId, queryRunner);
// let service = null;
// if (createDto.danakServiceId) {
// service = await queryRunner.manager.findOneBy(DanakService, { id: createDto.danakServiceId });
// if (!service) throw new BadRequestException(TicketMessageEnum.SERVICE_NOT_FOUND);
// }
// const ticketMessage: Partial<TicketMessage> = { content: createDto.message, author: user };
// if (createDto.attachmentUrls?.length) {
// ticketMessage.attachments = createDto.attachmentUrls.map((url) => {
// const attachment = new TicketMessageAttachment();
// attachment.attachmentUrl = url;
// return attachment;
// });
// }
// const ticket = queryRunner.manager.create(Ticket, {
// ...createDto,
// user,
// category: ticketCategory,
// messages: [ticketMessage],
// danakService: service,
// });
// await queryRunner.manager.save(Ticket, ticket);
// await this.assignTicketAndNotify(ticket, ticketCategory, user, queryRunner);
// await queryRunner.commitTransaction();
// return {
// message: TicketMessageEnum.CREATED,
// ticketId: ticket.id,
// };
// } catch (error) {
// this.logger.error("Error in create ticket transaction", error);
// await queryRunner.rollbackTransaction();
// throw error;
// } finally {
// await queryRunner.release();
// }
// }
// //******************************** */
// async createTicketByAdmin(createDto: CreateTicketByAdminDto, userIpAndHeaders: IUserIpAndHeaders) {
// const queryRunner = this.dataSource.createQueryRunner();
// try {
// await queryRunner.connect();
// await queryRunner.startTransaction();
// const [user, admin, ticketCategory] = await Promise.all([
// this.usersService.findOneByIdWithQueryRunner(createDto.userId, queryRunner),
// this.usersService.findOneByIdWithQueryRunner(userIpAndHeaders.userId, queryRunner),
// queryRunner.manager.findOne(TicketCategory, { where: { id: createDto.categoryId }, relations: { group: { users: true } } }),
// ]);
// if (!ticketCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND);
// const ticketMessage: Partial<TicketMessage> = { content: createDto.message, author: admin };
// const ticket = queryRunner.manager.create(Ticket, {
// ...createDto,
// user,
// category: ticketCategory,
// messages: [ticketMessage],
// assignedTo: admin,
// });
// await queryRunner.manager.save(Ticket, ticket);
// await this.notificationQueue.addTicketNotification(user.id, {
// ticketId: ticket.numericId.toString(),
// subject: ticket.subject,
// date: ticket.createdAt,
// userPhone: user.phone,
// userEmail: user.email,
// });
// await this.accessLogService.logCreate("Ticket", UserType.ADMIN, {
// user: { id: userIpAndHeaders.userId },
// endpoint: "/tickets",
// requestId: userIpAndHeaders.headers["x-request-id"],
// ipAddress: userIpAndHeaders.ip,
// userAgent: userIpAndHeaders.headers["user-agent"],
// actionDescription: `Admin created ticket: ${ticket.subject}`,
// metadata: {
// ticketId: ticket.id,
// ticketSubject: ticket.subject,
// },
// });
// await queryRunner.commitTransaction();
// return {
// message: TicketMessageEnum.CREATED,
// ticketId: ticket.id,
// };
// } catch (error) {
// await queryRunner.rollbackTransaction();
// throw error;
// } finally {
// await queryRunner.release();
// }
// }
// //******************************** */
@@ -341,7 +78,7 @@ export class TicketService {
if (!order) {
throw new BadRequestException("order not found!")
}
if(userId){
if (userId) {
if (order.user.id !== userId) {
throw new BadRequestException("This ticket doenst belongs to You !")
}
@@ -350,8 +87,41 @@ export class TicketService {
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
}
// //******************************** */
+25 -54
View File
@@ -1,13 +1,11 @@
import { Body, Controller, Get, HttpCode, HttpStatus, Param, Patch, Post, Query } from "@nestjs/common";
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Patch, Post, Query } from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger";
import { CreateTicketDto } from "./DTO/create-ticket.dto";
import { FindTicketQueryDto } from "./DTO/search-ticket-query.dto";
import { TicketService } from "./providers/tickets.service";
import { ParamDto } from "../../common/DTO/param.dto";
import { UserId } from "src/common/decorators";
import { AdminId } from "src/common/decorators/admin-id.decorator";
import { UpdateTicketDto } from "./DTO/update-ticket.dto";
@Controller("tickets")
@ApiTags("Tickets")
@@ -73,8 +71,26 @@ export class TicketsController {
return this.ticketsService.getTickets(orderId, queryDto, userId);
}
@ApiOperation({ summary: "Delete Ticket ==> admin route" })
@Delete('public/ticket/:ticketId')
deleteTicket(@Param('ticketId') ticketId: string, @UserId() userId: string) {
return this.ticketsService.deleteTicket(ticketId, userId);
}
@ApiOperation({ summary: "Delete Ticket ==> admin route" })
@Patch('public/ticket/:ticketId')
updateTicket(@Param('ticketId') ticketId: string, @UserId() userId: string, @Body() body: UpdateTicketDto) {
return this.ticketsService.updateTicket(ticketId, body, userId,);
}
/*=========================== Admin ============================== */
@ApiOperation({ summary: "Delete Ticket ==> admin route" })
@Delete('admin/ticket/:ticketId')
deleteTicketAsAdmin(@Param('ticketId') ticketId: string) {
return this.ticketsService.deleteTicket(ticketId);
}
@Post('admin/ticket/order/:orderId')
@ApiOperation({ summary: "create ticket ==> admin route" })
createTicketAsAdmin(@Param('orderId') orderId: string, @Body() createDto: CreateTicketDto, @AdminId() adminId: string) {
@@ -87,55 +103,10 @@ export class TicketsController {
getTicketsAsAdmin(@Query() queryDto: FindTicketQueryDto, @Param('orderId') orderId: string) {
return this.ticketsService.getTickets(orderId, queryDto);
}
// @ApiOperation({ summary: "Get all tickets ==> admin route" })
// @AdminRoute()
// @PermissionsDec(PermissionEnum.TICKETS)
// @Get("admin")
// getAllTickets(@Query() queryDto: SearchTicketQueryDto, @UserDec("id") adminId: string) {
// return this.ticketsService.getTicketForAdmin(queryDto, adminId);
// }
// @ApiOperation({ summary: "create ticket messages" })
// @Post(":id/messages")
// createTicketMessage(
// @Param() paramDto: ParamDto,
// @Body() createDto: CreateTicketMessageDto,
// @UserDec("id") userId: string,
// @UserDec("isAdmin") isAdmin: boolean,
// ) {
// return this.ticketsService.createTicketMessage(paramDto.id, createDto, userId, isAdmin);
// }
// @ApiOperation({ summary: "Get all ticket messages of user" })
// @Get(":id/messages")
// getTicketMessages(@Param() paramDto: ParamDto, @UserDec("id") userId: string, @UserDec("isAdmin") isAdmin: boolean) {
// return this.ticketsService.getTicketMessages(paramDto.id, userId, isAdmin);
// }
// @ApiOperation({ summary: "close ticket by user or admin" })
// @HttpCode(HttpStatus.OK)
// @Post(":id/close")
// closedTicketByUser(
// @Param() paramDto: ParamDto,
// @UserDec("isAdmin") isAdmin: boolean,
// @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
// ) {
// return this.ticketsService.closeTicketByUser(paramDto.id, isAdmin, userIpAndHeaders);
// }
// @ApiOperation({ summary: "open ticket by admin" })
// @AdminRoute()
// @PermissionsDec(PermissionEnum.TICKETS)
// @Post(":id/open")
// openTicketByAdmin(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
// return this.ticketsService.openTicketByAdmin(paramDto.id, userIpAndHeaders);
// }
// @ApiOperation({ summary: "referer ticket ==> admin route" })
// @AdminRoute()
// @PermissionsDec(PermissionEnum.TICKETS)
// @Post(":id/refer")
// referTicket(@Param() paramDto: ParamDto, @Body() referDto: ReferTicketDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
// return this.ticketsService.referTicket(paramDto.id, referDto, userIpAndHeaders);
// }
@ApiOperation({ summary: "Delete Ticket ==> admin route" })
@Patch('admin/ticket/:ticketId')
updateTicketAsAdmin(@Param('ticketId') ticketId: string, @Body() body: UpdateTicketDto) {
return this.ticketsService.updateTicket(ticketId, body);
}
}