02d89e6385
Add knowledge document GET/PUT and admin edit UI; auto-create support tickets on client 400 validation errors across user/shop/partner apps; batch create tasks and publish from support tickets; restore mini-user store env single-column layout. Co-authored-by: Cursor <cursoragent@cursor.com>
217 lines
6.2 KiB
TypeScript
217 lines
6.2 KiB
TypeScript
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);
|
|
}
|
|
}
|