197 lines
5.6 KiB
TypeScript
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,
|
|
}))
|
|
}
|
|
}
|