rwadurian/backend/services/admin-service/src/api/controllers/customer-service-contact.co...

197 lines
5.6 KiB
TypeScript

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<ContactResponseDto[]> {
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<ContactResponseDto> {
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<ContactResponseDto> {
const existing = await this.prisma.customerServiceContact.findUnique({ where: { id } })
if (!existing) {
throw new NotFoundException('Contact not found')
}
const data: Record<string, unknown> = {}
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<void> {
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<ContactResponseDto[]> {
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,
}))
}
}