This commit is contained in:
2026-02-25 10:33:19 +03:30
parent e6dbe216c1
commit 88bc1edb1d
4 changed files with 101 additions and 26 deletions
@@ -35,13 +35,6 @@ export class CreateTicketDto {
@IsString()
content: string;
@ApiProperty({
description: 'Ticket status',
enum: TicketStatusEnum,
default: TicketStatusEnum.OPEN,
})
@IsEnum(TicketStatusEnum)
status: TicketStatusEnum = TicketStatusEnum.OPEN;
@ApiPropertyOptional({
description: 'Attachments',
@@ -12,12 +12,11 @@ export class TicketRepository extends EntityRepository<Ticket> {
super(em, Ticket);
}
async findAllPaginated(dto: SearchTicketQueryDto): Promise<PaginatedResult<Ticket>> {
async findTicketsPaginated(dto: SearchTicketQueryDto): Promise<PaginatedResult<Ticket>> {
const {
page = 1,
limit = 10,
statuses,
parentId,
adminId,
userId,
search,
@@ -29,7 +28,89 @@ export class TicketRepository extends EntityRepository<Ticket> {
const offset = (page - 1) * limit;
const where: FilterQuery<Ticket> = {};
const where: FilterQuery<Ticket> = {parent:null};
if (adminId !== undefined) {
if (adminId === null || adminId === '') {
where.admin = null;
} else {
where.admin = { id: adminId };
}
}
if (userId !== undefined) {
if (userId === null || userId === '') {
where.user = null;
} else {
where.user = { id: userId };
}
}
if (statuses?.length) {
const normalizedStatuses = Array.isArray(statuses) ? statuses : [statuses];
const validStatuses = normalizedStatuses.filter((s): s is TicketStatusEnum => !!s);
if (validStatuses.length === 1) {
where.status = validStatuses[0];
} else if (validStatuses.length > 1) {
where.status = { $in: validStatuses };
}
}
if (from || to) {
where.createdAt = {};
if (from) where.createdAt.$gte = new Date(from);
if (to) where.createdAt.$lte = new Date(to);
}
if (search) {
const searchPattern = `%${search}%`;
where.$or = [
{ subject: { $ilike: searchPattern } },
{ content: { $ilike: searchPattern } },
];
}
const [data, total] = await this.findAndCount(where, {
limit,
offset,
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
populate: ['parent', 'children', 'admin', 'user'],
filters: ['notDeleted'],
});
const totalPages = Math.ceil(total / limit);
return {
data,
meta: {
total,
page,
limit,
totalPages,
},
};
}
async findTicketMessagesPaginated(dto: SearchTicketQueryDto): Promise<PaginatedResult<Ticket>> {
const {
page = 1,
limit = 10,
statuses,
adminId,
userId,
search,
orderBy = 'createdAt',
order = 'desc',
from,
to,
parentId
} = dto;
const offset = (page - 1) * limit;
const where: FilterQuery<Ticket> = {parent:null};
if (parentId !== undefined) {
if (parentId === null || parentId === '') {
+5 -5
View File
@@ -49,9 +49,9 @@ export class TicketController {
@Get('public/tickets/:id')
@UseGuards(AuthGuard)
@ApiOperation({ summary: 'Get one of my tickets by id' })
@ApiOperation({ summary: 'Get tickets messages by parent id' })
findOneAsUser(@Param('id') id: string, @UserId() userId: string) {
return this.ticketService.findOneAsUser(id, userId);
return this.ticketService.findTicketMessagesAsUser(id, userId);
}
@Patch('public/tickets/:id')
@@ -91,15 +91,15 @@ export class TicketController {
@Permissions(PermissionEnum.MANAGE_TICKETS)
@ApiOperation({ summary: 'Get all tickets with pagination' })
findAllAsAdmin(@Query() queryDto: SearchTicketQueryDto) {
return this.ticketService.findAll(queryDto);
return this.ticketService.findTickets(queryDto);
}
@Get('admin/tickets/:id')
@UseGuards(AdminAuthGuard)
@Permissions(PermissionEnum.MANAGE_TICKETS)
@ApiOperation({ summary: 'Get one ticket by id' })
@ApiOperation({ summary: 'Get ticket messages by parent id' })
findOneAsAdmin(@Param('id') id: string) {
return this.ticketService.findOne(id);
return this.ticketService.findTicketMessagesAsAdmin(id);
}
@Patch('admin/tickets/:id')
+12 -11
View File
@@ -55,6 +55,7 @@ export class TicketService {
parent: parent ?? undefined,
admin: admin ?? undefined,
user: user ?? undefined,
status: TicketStatusEnum.OPEN,
});
await this.em.persistAndFlush(ticket);
return ticket;
@@ -96,18 +97,18 @@ export class TicketService {
return ticket;
}
findAll(dto: SearchTicketQueryDto) {
return this.ticketRepository.findAllPaginated(dto);
findTickets(dto: SearchTicketQueryDto) {
return this.ticketRepository.findTicketsPaginated(dto);
}
findMyTickets(userId: string, dto: SearchTicketQueryDto) {
return this.ticketRepository.findAllPaginated({ ...dto, userId });
return this.ticketRepository.findTicketsPaginated({ ...dto, userId });
}
async findOneAsUser(id: string, userId: string) {
const ticket = await this.ticketRepository.findOne(
{ id, user: { id: userId } },
{ populate: ['parent', 'children', 'admin', 'user'], filters: ['notDeleted'] },
async findTicketMessagesAsUser(ticketId: string, userId: string) {
const ticket = await this.ticketRepository.find(
{ parent: { id: ticketId }, user: { id: userId } },
{ populate: ['admin', 'user'], filters: ['notDeleted'] },
);
if (!ticket) {
throw new NotFoundException('Ticket not found');
@@ -115,10 +116,10 @@ export class TicketService {
return ticket;
}
async findOne(id: string) {
const ticket = await this.ticketRepository.findOne(
{ id },
{ populate: ['parent', 'children', 'admin', 'user'], filters: ['notDeleted'] },
async findTicketMessagesAsAdmin(ticketId: string) {
const ticket = await this.ticketRepository.find(
{ parent: { id: ticketId } },
{ populate: ['admin', 'user'], filters: ['notDeleted'] },
);
if (!ticket) {
throw new NotFoundException('Ticket not found');