import { Body, Controller, Get, NotFoundException, Param, Patch, Post, Query, UseGuards, } from '@nestjs/common'; import { HqAuthGuard } from '../../common/guards/hq-auth.guard'; import { SuperAdminGuard } from '../../common/guards/super-admin.guard'; import { AuthUser } from '../../common/guards/jwt-auth.guard'; import { CurrentUser } from '../../common/decorators/current-user.decorator'; import { HqOperation } from '../../common/hq-operation/hq-operation.decorator'; import { HqOperationAction } from '../../common/hq-operation/hq-operation.constants'; import { PrismaService } from '../../common/prisma/prisma.module'; import { SupportTicketService } from '../common/support-ticket.service'; import { SupportTicketReviewAiService } from '../dev-plan/support-ticket-review-ai.service'; import { CreateSupportTicketDto, RejectSupportTicketDto, SupportTicketListQueryDto, SupportTicketRemarkDto, UpdateSupportTicketDto, BatchUpdateSupportTicketStatusDto, BatchCreateSupportTicketTasksDto, BatchPublishSupportTicketsDto, } from '../common/dto/support-ticket.dto'; import { BatchReviewConfirmDto, BatchReviewPreviewDto, ReviewSupportTicketDto, } from '../dev-plan/dto/dev-plan.dto'; @Controller('admin/support-tickets') @UseGuards(HqAuthGuard) export class AdminSupportTicketsController { constructor( private readonly service: SupportTicketService, private readonly reviewAi: SupportTicketReviewAiService, private readonly prisma: PrismaService, ) {} private async resolveHqAccount(user: AuthUser) { const account = await this.prisma.hqAccount.findUnique({ where: { id: user.actorId }, select: { id: true, name: true }, }); if (!account) throw new NotFoundException('HQ 账户不存在'); return account; } @Get() list(@Query() query: SupportTicketListQueryDto) { return this.service.list(query); } @Post() @HqOperation({ action: HqOperationAction.SUPPORT_TICKET_CREATE, refType: 'SUPPORT_TICKET', batch: true, includeBody: true, }) async create(@CurrentUser() user: AuthUser, @Body() body: CreateSupportTicketDto) { const account = await this.resolveHqAccount(user); return this.service.create(body, account); } @Post('batch-review/preview') @UseGuards(SuperAdminGuard) async batchReviewPreview(@Body() body: BatchReviewPreviewDto) { return this.reviewAi.preview(body.ticketIds); } @Post('batch-review/confirm') @UseGuards(SuperAdminGuard) @HqOperation({ action: HqOperationAction.SUPPORT_TICKET_BATCH_REVIEW, refType: 'SUPPORT_TICKET', includeBody: true, batch: true, }) async batchReviewConfirm(@CurrentUser() user: AuthUser, @Body() body: BatchReviewConfirmDto) { const account = await this.resolveHqAccount(user); return this.service.batchReviewConfirm(account, body.items); } @Post('batch-update-status') @UseGuards(SuperAdminGuard) async batchUpdateStatus( @CurrentUser() user: AuthUser, @Body() body: BatchUpdateSupportTicketStatusDto, ) { const account = await this.resolveHqAccount(user); const profile = await this.prisma.hqAccount.findUnique({ where: { id: user.actorId }, select: { adminRole: true }, }); return this.service.batchUpdateStatus( body.ticketIds.map(BigInt), body.status as never, { isSuperAdmin: profile?.adminRole === 'SUPER_ADMIN', rejectReason: body.rejectReason, note: body.note, reviewer: account, }, ); } @Post('batch-create-tasks') @UseGuards(SuperAdminGuard) async batchCreateTasks( @CurrentUser() user: AuthUser, @Body() body: BatchCreateSupportTicketTasksDto, ) { const account = await this.resolveHqAccount(user); return this.service.batchCreateTasks(body.ticketIds.map(BigInt), account); } @Post('batch-publish') @UseGuards(SuperAdminGuard) async batchPublish( @CurrentUser() user: AuthUser, @Body() body: BatchPublishSupportTicketsDto, ) { const account = await this.resolveHqAccount(user); return this.service.batchPublish(body.ticketIds.map(BigInt), body, account); } @Get(':id') detail(@Param('id') id: string) { return this.service.detail(BigInt(id)); } @Patch(':id') async update(@Param('id') id: string, @Body() body: UpdateSupportTicketDto) { return this.service.update(BigInt(id), body); } @Post(':id/review') @UseGuards(SuperAdminGuard) @HqOperation({ action: HqOperationAction.SUPPORT_TICKET_REVIEW, refType: 'SUPPORT_TICKET', refIdParam: 'id', includeBody: true, }) async review( @CurrentUser() user: AuthUser, @Param('id') id: string, @Body() body: ReviewSupportTicketDto, ) { const account = await this.resolveHqAccount(user); return this.service.review(BigInt(id), account, body); } @Post(':id/approve') @UseGuards(SuperAdminGuard) @HqOperation({ action: HqOperationAction.SUPPORT_TICKET_APPROVE, refType: 'SUPPORT_TICKET', refIdParam: 'id', includeBody: true, }) async approve( @CurrentUser() user: AuthUser, @Param('id') id: string, @Body() body: SupportTicketRemarkDto, ) { const account = await this.resolveHqAccount(user); return this.service.approve(BigInt(id), account, body); } @Post(':id/reject') @UseGuards(SuperAdminGuard) @HqOperation({ action: HqOperationAction.SUPPORT_TICKET_REJECT, refType: 'SUPPORT_TICKET', refIdParam: 'id', includeBody: true, }) async reject( @CurrentUser() user: AuthUser, @Param('id') id: string, @Body() body: RejectSupportTicketDto, ) { const account = await this.resolveHqAccount(user); return this.service.reject(BigInt(id), account, body); } @Post(':id/start-testing') @HqOperation({ action: HqOperationAction.SUPPORT_TICKET_START_TESTING, refType: 'SUPPORT_TICKET', refIdParam: 'id', includeBody: true, }) startTesting(@Param('id') id: string, @Body() body: SupportTicketRemarkDto) { return this.service.startTesting(BigInt(id), body); } @Post(':id/pass') @HqOperation({ action: HqOperationAction.SUPPORT_TICKET_PASS, refType: 'SUPPORT_TICKET', refIdParam: 'id', includeBody: true, }) pass(@Param('id') id: string, @Body() body: SupportTicketRemarkDto) { return this.service.pass(BigInt(id), body); } }