239 lines
6.3 KiB
TypeScript
239 lines
6.3 KiB
TypeScript
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<NotificationResponseDto> {
|
|
// 构建目标配置
|
|
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<NotificationResponseDto> {
|
|
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<NotificationResponseDto[]> {
|
|
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<NotificationResponseDto> {
|
|
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<void> {
|
|
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<NotificationListResponseDto> {
|
|
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<UnreadCountResponseDto> {
|
|
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 };
|
|
}
|
|
}
|