import { Controller, Get, Post, Put, Delete, Body, Param, HttpCode, HttpStatus, NotFoundException, BadRequestException, Logger, } from '@nestjs/common' import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger' import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service' import { ContactType } from '@prisma/client' // ===== DTOs ===== interface CreateContactDto { type: string label: string value: string sortOrder: number isEnabled?: boolean } interface UpdateContactDto { type?: string label?: string value?: string sortOrder?: number isEnabled?: boolean } interface ContactResponseDto { id: string type: ContactType label: string value: string sortOrder: number isEnabled: boolean createdAt: Date updatedAt: Date } // ============================================================================= // Admin Controller (需要认证) // ============================================================================= @ApiTags('Customer Service Contact Management') @Controller('admin/customer-service-contacts') export class AdminCustomerServiceContactController { private readonly logger = new Logger(AdminCustomerServiceContactController.name) constructor(private readonly prisma: PrismaService) {} @Get() @ApiBearerAuth() @ApiOperation({ summary: '查询客服联系方式列表 (全部)' }) async list(): Promise { const contacts = await this.prisma.customerServiceContact.findMany({ orderBy: [{ sortOrder: 'asc' }], }) return contacts.map(this.toDto) } @Post() @ApiBearerAuth() @ApiOperation({ summary: '新增客服联系方式' }) async create(@Body() body: CreateContactDto): Promise { const contactType = body.type as ContactType if (!Object.values(ContactType).includes(contactType)) { throw new BadRequestException(`Invalid type: ${body.type}. Must be WECHAT or QQ`) } if (!body.label || !body.value) { throw new BadRequestException('label and value are required') } if (body.sortOrder === undefined || body.sortOrder < 0) { throw new BadRequestException('sortOrder must be a non-negative integer') } const contact = await this.prisma.customerServiceContact.create({ data: { type: contactType, label: body.label, value: body.value, sortOrder: body.sortOrder, isEnabled: body.isEnabled ?? true, }, }) this.logger.log(`Created customer service contact: id=${contact.id}, type=${contact.type}, label=${contact.label}`) return this.toDto(contact) } @Put(':id') @ApiBearerAuth() @HttpCode(HttpStatus.OK) @ApiOperation({ summary: '更新客服联系方式' }) async update( @Param('id') id: string, @Body() body: UpdateContactDto, ): Promise { const existing = await this.prisma.customerServiceContact.findUnique({ where: { id } }) if (!existing) { throw new NotFoundException('Contact not found') } const data: Record = {} if (body.type !== undefined) { const contactType = body.type as ContactType if (!Object.values(ContactType).includes(contactType)) { throw new BadRequestException(`Invalid type: ${body.type}`) } data.type = contactType } if (body.label !== undefined) data.label = body.label if (body.value !== undefined) data.value = body.value if (body.sortOrder !== undefined) data.sortOrder = body.sortOrder if (body.isEnabled !== undefined) data.isEnabled = body.isEnabled const updated = await this.prisma.customerServiceContact.update({ where: { id }, data, }) return this.toDto(updated) } @Delete(':id') @ApiBearerAuth() @HttpCode(HttpStatus.NO_CONTENT) @ApiOperation({ summary: '删除客服联系方式' }) async delete(@Param('id') id: string): Promise { const existing = await this.prisma.customerServiceContact.findUnique({ where: { id } }) if (!existing) { throw new NotFoundException('Contact not found') } await this.prisma.customerServiceContact.delete({ where: { id } }) this.logger.log(`Deleted customer service contact: id=${id}, type=${existing.type}`) } private toDto(contact: { id: string type: ContactType label: string value: string sortOrder: number isEnabled: boolean createdAt: Date updatedAt: Date }): ContactResponseDto { return { id: contact.id, type: contact.type, label: contact.label, value: contact.value, sortOrder: contact.sortOrder, isEnabled: contact.isEnabled, createdAt: contact.createdAt, updatedAt: contact.updatedAt, } } } // ============================================================================= // Public Controller (移动端调用,无需认证) // ============================================================================= @ApiTags('Customer Service Contacts (Public)') @Controller('customer-service-contacts') export class PublicCustomerServiceContactController { constructor(private readonly prisma: PrismaService) {} @Get() @ApiOperation({ summary: '获取已启用的客服联系方式 (移动端)' }) async list(): Promise { const contacts = await this.prisma.customerServiceContact.findMany({ where: { isEnabled: true }, orderBy: [{ sortOrder: 'asc' }], }) return contacts.map((c) => ({ id: c.id, type: c.type, label: c.label, value: c.value, sortOrder: c.sortOrder, isEnabled: c.isEnabled, createdAt: c.createdAt, updatedAt: c.updatedAt, })) } }