import { Controller, Get, Post, Put, Delete, Body, Param, Query, HttpCode, HttpStatus, Inject, NotFoundException, } from '@nestjs/common'; import { v4 as uuidv4 } from 'uuid'; import { NOTIFICATION_REPOSITORY, NotificationRepository, } from '../../domain/repositories/notification.repository'; import { NotificationEntity, NotificationTarget, TargetType, } from '../../domain/entities/notification.entity'; import { CreateNotificationDto, UpdateNotificationDto, ListNotificationsDto, UserNotificationsDto, MarkReadDto, } from '../dto/request/notification.dto'; import { NotificationResponseDto, UserNotificationResponseDto, UnreadCountResponseDto, NotificationListResponseDto, } from '../dto/response/notification.dto'; /** * 管理端通知控制器 */ @Controller('admin/notifications') export class AdminNotificationController { constructor( @Inject(NOTIFICATION_REPOSITORY) private readonly notificationRepo: NotificationRepository, ) {} /** * 创建通知 */ @Post() async create(@Body() dto: CreateNotificationDto): Promise { // 构建目标配置 let targetConfig: NotificationTarget | null = null; const targetType = dto.targetType ?? TargetType.ALL; if (dto.targetConfig || targetType !== TargetType.ALL) { targetConfig = { type: targetType, tagIds: dto.targetConfig?.tagIds, segmentId: dto.targetConfig?.segmentId, accountSequences: dto.targetConfig?.accountSequences, }; } const notification = NotificationEntity.create({ id: uuidv4(), title: dto.title, content: dto.content, type: dto.type, priority: dto.priority, targetType, targetConfig, imageUrl: dto.imageUrl, linkUrl: dto.linkUrl, requiresForceRead: dto.requiresForceRead, publishedAt: dto.publishedAt ? new Date(dto.publishedAt) : null, expiresAt: dto.expiresAt ? new Date(dto.expiresAt) : null, createdBy: 'admin', // TODO: 从认证信息获取 }); const saved = await this.notificationRepo.save(notification); return NotificationResponseDto.fromEntity(saved); } /** * 获取通知详情 */ @Get(':id') async findOne(@Param('id') id: string): Promise { const notification = await this.notificationRepo.findById(id); if (!notification) { throw new NotFoundException('Notification not found'); } return NotificationResponseDto.fromEntity(notification); } /** * 获取通知列表(管理端) */ @Get() async findAll(@Query() dto: ListNotificationsDto): Promise { const notifications = await this.notificationRepo.findAll({ type: dto.type, limit: dto.limit, offset: dto.offset, }); return notifications.map(NotificationResponseDto.fromEntity); } /** * 更新通知 */ @Put(':id') async update( @Param('id') id: string, @Body() dto: UpdateNotificationDto, ): Promise { const existing = await this.notificationRepo.findById(id); if (!existing) { throw new NotFoundException('Notification not found'); } // 构建目标配置 let targetConfig: NotificationTarget | null | undefined = undefined; if (dto.targetConfig !== undefined || dto.targetType !== undefined) { const targetType = dto.targetType ?? existing.targetType; if (dto.targetConfig || targetType !== TargetType.ALL) { targetConfig = { type: targetType, tagIds: dto.targetConfig?.tagIds ?? existing.targetConfig?.tagIds, segmentId: dto.targetConfig?.segmentId ?? existing.targetConfig?.segmentId, accountSequences: dto.targetConfig?.accountSequences ?? existing.targetConfig?.accountSequences, }; } else { targetConfig = null; } } const updated = existing.update({ title: dto.title, content: dto.content, type: dto.type, priority: dto.priority, targetType: dto.targetType, targetConfig, imageUrl: dto.imageUrl, linkUrl: dto.linkUrl, isEnabled: dto.isEnabled, requiresForceRead: dto.requiresForceRead, publishedAt: dto.publishedAt !== undefined ? dto.publishedAt ? new Date(dto.publishedAt) : null : undefined, expiresAt: dto.expiresAt !== undefined ? dto.expiresAt ? new Date(dto.expiresAt) : null : undefined, }); const saved = await this.notificationRepo.save(updated); return NotificationResponseDto.fromEntity(saved); } /** * 删除通知 */ @Delete(':id') @HttpCode(HttpStatus.NO_CONTENT) async delete(@Param('id') id: string): Promise { await this.notificationRepo.delete(id); } } /** * 移动端通知控制器 */ @Controller('mobile/notifications') export class MobileNotificationController { constructor( @Inject(NOTIFICATION_REPOSITORY) private readonly notificationRepo: NotificationRepository, ) {} /** * 获取用户的通知列表 */ @Get() async getNotifications( @Query() dto: UserNotificationsDto, ): Promise { const [notifications, unreadCount] = await Promise.all([ this.notificationRepo.findNotificationsForUser({ userSerialNum: dto.userSerialNum, type: dto.type, limit: dto.limit ?? 50, offset: dto.offset ?? 0, }), this.notificationRepo.countUnreadForUser(dto.userSerialNum), ]); return { notifications: notifications.map(UserNotificationResponseDto.fromEntity), total: notifications.length, unreadCount, }; } /** * 获取未读通知数量 */ @Get('unread-count') async getUnreadCount( @Query('userSerialNum') userSerialNum: string, ): Promise { const unreadCount = await this.notificationRepo.countUnreadForUser(userSerialNum); return { unreadCount }; } /** * 标记通知为已读 */ @Post('mark-read') @HttpCode(HttpStatus.OK) async markRead(@Body() dto: MarkReadDto): Promise<{ success: boolean }> { if (dto.notificationId) { await this.notificationRepo.markAsRead(dto.notificationId, dto.userSerialNum); } else { await this.notificationRepo.markAllAsRead(dto.userSerialNum); } return { success: true }; } }