feat: 支持 push
This commit is contained in:
673
docs/push-service-architecture.md
Normal file
673
docs/push-service-architecture.md
Normal file
@@ -0,0 +1,673 @@
|
||||
# iOS推送服务架构和接口设计
|
||||
|
||||
## 1. 服务架构概览
|
||||
|
||||
### 1.1 整体架构图
|
||||
```mermaid
|
||||
graph TB
|
||||
A[iOS App] --> B[APNs]
|
||||
B --> C[NestJS Push Service]
|
||||
C --> D[APNs Provider]
|
||||
D --> B
|
||||
C --> E[Push Token Service]
|
||||
C --> F[Push Template Service]
|
||||
C --> G[Push Message Service]
|
||||
C --> H[Database]
|
||||
E --> H
|
||||
F --> H
|
||||
G --> H
|
||||
```
|
||||
|
||||
### 1.2 模块依赖关系
|
||||
```mermaid
|
||||
graph LR
|
||||
A[PushNotificationsModule] --> B[PushNotificationsController]
|
||||
A --> C[PushNotificationsService]
|
||||
A --> D[ApnsProvider]
|
||||
A --> E[PushTokenService]
|
||||
A --> F[PushTemplateService]
|
||||
A --> G[PushMessageService]
|
||||
A --> H[DatabaseModule]
|
||||
A --> I[ConfigModule]
|
||||
```
|
||||
|
||||
## 2. 核心服务类设计
|
||||
|
||||
### 2.1 PushNotificationsService
|
||||
```typescript
|
||||
@Injectable()
|
||||
export class PushNotificationsService {
|
||||
constructor(
|
||||
private readonly apnsProvider: ApnsProvider,
|
||||
private readonly pushTokenService: PushTokenService,
|
||||
private readonly pushTemplateService: PushTemplateService,
|
||||
private readonly pushMessageService: PushMessageService,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
// 发送单个推送
|
||||
async sendNotification(userId: string, notification: PushNotificationDto): Promise<PushResponseDto>
|
||||
|
||||
// 批量发送推送
|
||||
async sendBatchNotifications(userIds: string[], notification: PushNotificationDto): Promise<BatchPushResponseDto>
|
||||
|
||||
// 使用模板发送推送
|
||||
async sendNotificationByTemplate(userId: string, templateKey: string, data: any): Promise<PushResponseDto>
|
||||
|
||||
// 发送静默推送
|
||||
async sendSilentNotification(userId: string, payload: any): Promise<PushResponseDto>
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 ApnsProvider
|
||||
```typescript
|
||||
@Injectable()
|
||||
export class ApnsProvider {
|
||||
private provider: apn.Provider;
|
||||
private multiProvider: apn.MultiProvider;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
this.initializeProvider();
|
||||
}
|
||||
|
||||
// 初始化APNs连接
|
||||
private initializeProvider(): void
|
||||
|
||||
// 发送单个通知
|
||||
async send(notification: apn.Notification, deviceTokens: string[]): Promise<apn.Results>
|
||||
|
||||
// 批量发送通知
|
||||
async sendBatch(notifications: apn.Notification[], deviceTokens: string[]): Promise<apn.Results>
|
||||
|
||||
// 管理推送通道
|
||||
async manageChannels(notification: apn.Notification, bundleId: string, action: string): Promise<any>
|
||||
|
||||
// 广播实时活动通知
|
||||
async broadcast(notification: apn.Notification, bundleId: string): Promise<any>
|
||||
|
||||
// 关闭连接
|
||||
shutdown(): void
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 PushTokenService
|
||||
```typescript
|
||||
@Injectable()
|
||||
export class PushTokenService {
|
||||
constructor(
|
||||
@InjectModel(UserPushToken) private readonly pushTokenModel: typeof UserPushToken,
|
||||
) {}
|
||||
|
||||
// 注册设备令牌
|
||||
async registerToken(userId: string, tokenData: RegisterDeviceTokenDto): Promise<UserPushToken>
|
||||
|
||||
// 更新设备令牌
|
||||
async updateToken(userId: string, tokenData: UpdateDeviceTokenDto): Promise<UserPushToken>
|
||||
|
||||
// 注销设备令牌
|
||||
async unregisterToken(userId: string, deviceToken: string): Promise<void>
|
||||
|
||||
// 获取用户的所有有效令牌
|
||||
async getActiveTokens(userId: string): Promise<UserPushToken[]>
|
||||
|
||||
// 清理无效令牌
|
||||
async cleanupInvalidTokens(): Promise<number>
|
||||
|
||||
// 验证令牌有效性
|
||||
async validateToken(deviceToken: string): Promise<boolean>
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 PushTemplateService
|
||||
```typescript
|
||||
@Injectable()
|
||||
export class PushTemplateService {
|
||||
constructor(
|
||||
@InjectModel(PushTemplate) private readonly templateModel: typeof PushTemplate,
|
||||
) {}
|
||||
|
||||
// 创建推送模板
|
||||
async createTemplate(templateData: CreatePushTemplateDto): Promise<PushTemplate>
|
||||
|
||||
// 更新推送模板
|
||||
async updateTemplate(id: string, templateData: UpdatePushTemplateDto): Promise<PushTemplate>
|
||||
|
||||
// 删除推送模板
|
||||
async deleteTemplate(id: string): Promise<void>
|
||||
|
||||
// 获取模板
|
||||
async getTemplate(templateKey: string): Promise<PushTemplate>
|
||||
|
||||
// 获取所有模板
|
||||
async getAllTemplates(): Promise<PushTemplate[]>
|
||||
|
||||
// 渲染模板
|
||||
async renderTemplate(templateKey: string, data: any): Promise<RenderedTemplate>
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 PushMessageService
|
||||
```typescript
|
||||
@Injectable()
|
||||
export class PushMessageService {
|
||||
constructor(
|
||||
@InjectModel(PushMessage) private readonly messageModel: typeof PushMessage,
|
||||
) {}
|
||||
|
||||
// 创建推送消息记录
|
||||
async createMessage(messageData: CreatePushMessageDto): Promise<PushMessage>
|
||||
|
||||
// 更新消息状态
|
||||
async updateMessageStatus(id: string, status: PushMessageStatus, response?: any): Promise<void>
|
||||
|
||||
// 获取消息历史
|
||||
async getMessageHistory(userId: string, options: QueryOptions): Promise<PushMessage[]>
|
||||
|
||||
// 获取消息统计
|
||||
async getMessageStats(userId?: string, timeRange?: TimeRange): Promise<PushStats>
|
||||
|
||||
// 清理过期消息
|
||||
async cleanupExpiredMessages(): Promise<number>
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 数据传输对象(DTO)设计
|
||||
|
||||
### 3.1 推送令牌相关DTO
|
||||
```typescript
|
||||
// 注册设备令牌
|
||||
export class RegisterDeviceTokenDto {
|
||||
@ApiProperty({ description: '设备推送令牌' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
deviceToken: string;
|
||||
|
||||
@ApiProperty({ description: '设备类型', enum: DeviceType })
|
||||
@IsEnum(DeviceType)
|
||||
deviceType: DeviceType;
|
||||
|
||||
@ApiProperty({ description: '应用版本', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
appVersion?: string;
|
||||
|
||||
@ApiProperty({ description: '操作系统版本', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
osVersion?: string;
|
||||
|
||||
@ApiProperty({ description: '设备名称', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
deviceName?: string;
|
||||
}
|
||||
|
||||
// 更新设备令牌
|
||||
export class UpdateDeviceTokenDto {
|
||||
@ApiProperty({ description: '新的设备推送令牌' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
newDeviceToken: string;
|
||||
|
||||
@ApiProperty({ description: '应用版本', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
appVersion?: string;
|
||||
|
||||
@ApiProperty({ description: '操作系统版本', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
osVersion?: string;
|
||||
|
||||
@ApiProperty({ description: '设备名称', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
deviceName?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 推送消息相关DTO
|
||||
```typescript
|
||||
// 发送推送通知
|
||||
export class SendPushNotificationDto {
|
||||
@ApiProperty({ description: '用户ID列表' })
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
userIds: string[];
|
||||
|
||||
@ApiProperty({ description: '推送标题' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: '推送内容' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
body: string;
|
||||
|
||||
@ApiProperty({ description: '自定义数据', required: false })
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
payload?: any;
|
||||
|
||||
@ApiProperty({ description: '推送类型', enum: PushType, required: false })
|
||||
@IsEnum(PushType)
|
||||
@IsOptional()
|
||||
pushType?: PushType;
|
||||
|
||||
@ApiProperty({ description: '优先级', required: false })
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
priority?: number;
|
||||
|
||||
@ApiProperty({ description: '过期时间(秒)', required: false })
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
expiry?: number;
|
||||
|
||||
@ApiProperty({ description: '折叠ID', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
collapseId?: string;
|
||||
}
|
||||
|
||||
// 使用模板发送推送
|
||||
export class SendPushByTemplateDto {
|
||||
@ApiProperty({ description: '用户ID列表' })
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
userIds: string[];
|
||||
|
||||
@ApiProperty({ description: '模板键' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
templateKey: string;
|
||||
|
||||
@ApiProperty({ description: '模板数据' })
|
||||
@IsObject()
|
||||
@IsNotEmpty()
|
||||
data: any;
|
||||
|
||||
@ApiProperty({ description: '自定义数据', required: false })
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
payload?: any;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 推送模板相关DTO
|
||||
```typescript
|
||||
// 创建推送模板
|
||||
export class CreatePushTemplateDto {
|
||||
@ApiProperty({ description: '模板键' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
templateKey: string;
|
||||
|
||||
@ApiProperty({ description: '模板标题' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: '模板内容' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
body: string;
|
||||
|
||||
@ApiProperty({ description: '负载模板', required: false })
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
payloadTemplate?: any;
|
||||
|
||||
@ApiProperty({ description: '推送类型', enum: PushType, required: false })
|
||||
@IsEnum(PushType)
|
||||
@IsOptional()
|
||||
pushType?: PushType;
|
||||
|
||||
@ApiProperty({ description: '优先级', required: false })
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
priority?: number;
|
||||
}
|
||||
|
||||
// 更新推送模板
|
||||
export class UpdatePushTemplateDto {
|
||||
@ApiProperty({ description: '模板标题', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
title?: string;
|
||||
|
||||
@ApiProperty({ description: '模板内容', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
body?: string;
|
||||
|
||||
@ApiProperty({ description: '负载模板', required: false })
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
payloadTemplate?: any;
|
||||
|
||||
@ApiProperty({ description: '推送类型', enum: PushType, required: false })
|
||||
@IsEnum(PushType)
|
||||
@IsOptional()
|
||||
pushType?: PushType;
|
||||
|
||||
@ApiProperty({ description: '优先级', required: false })
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
priority?: number;
|
||||
|
||||
@ApiProperty({ description: '是否激活', required: false })
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
isActive?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 响应DTO
|
||||
```typescript
|
||||
// 推送响应
|
||||
export class PushResponseDto {
|
||||
@ApiProperty({ description: '响应代码' })
|
||||
code: ResponseCode;
|
||||
|
||||
@ApiProperty({ description: '响应消息' })
|
||||
message: string;
|
||||
|
||||
@ApiProperty({ description: '推送结果' })
|
||||
data: {
|
||||
success: boolean;
|
||||
sentCount: number;
|
||||
failedCount: number;
|
||||
results: PushResult[];
|
||||
};
|
||||
}
|
||||
|
||||
// 批量推送响应
|
||||
export class BatchPushResponseDto {
|
||||
@ApiProperty({ description: '响应代码' })
|
||||
code: ResponseCode;
|
||||
|
||||
@ApiProperty({ description: '响应消息' })
|
||||
message: string;
|
||||
|
||||
@ApiProperty({ description: '批量推送结果' })
|
||||
data: {
|
||||
totalUsers: number;
|
||||
totalTokens: number;
|
||||
successCount: number;
|
||||
failedCount: number;
|
||||
results: PushResult[];
|
||||
};
|
||||
}
|
||||
|
||||
// 推送结果
|
||||
export class PushResult {
|
||||
@ApiProperty({ description: '用户ID' })
|
||||
userId: string;
|
||||
|
||||
@ApiProperty({ description: '设备令牌' })
|
||||
deviceToken: string;
|
||||
|
||||
@ApiProperty({ description: '是否成功' })
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({ description: '错误信息', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
error?: string;
|
||||
|
||||
@ApiProperty({ description: 'APNs响应', required: false })
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
apnsResponse?: any;
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 控制器接口设计
|
||||
|
||||
### 4.1 PushNotificationsController
|
||||
```typescript
|
||||
@Controller('push-notifications')
|
||||
@ApiTags('推送通知')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class PushNotificationsController {
|
||||
constructor(private readonly pushNotificationsService: PushNotificationsService) {}
|
||||
|
||||
// 注册设备令牌
|
||||
@Post('register-token')
|
||||
@ApiOperation({ summary: '注册设备推送令牌' })
|
||||
@ApiResponse({ status: 200, description: '注册成功', type: ResponseDto })
|
||||
async registerToken(
|
||||
@CurrentUser() user: AccessTokenPayload,
|
||||
@Body() registerTokenDto: RegisterDeviceTokenDto,
|
||||
): Promise<ResponseDto> {
|
||||
return this.pushNotificationsService.registerToken(user.sub, registerTokenDto);
|
||||
}
|
||||
|
||||
// 更新设备令牌
|
||||
@Put('update-token')
|
||||
@ApiOperation({ summary: '更新设备推送令牌' })
|
||||
@ApiResponse({ status: 200, description: '更新成功', type: ResponseDto })
|
||||
async updateToken(
|
||||
@CurrentUser() user: AccessTokenPayload,
|
||||
@Body() updateTokenDto: UpdateDeviceTokenDto,
|
||||
): Promise<ResponseDto> {
|
||||
return this.pushNotificationsService.updateToken(user.sub, updateTokenDto);
|
||||
}
|
||||
|
||||
// 注销设备令牌
|
||||
@Delete('unregister-token')
|
||||
@ApiOperation({ summary: '注销设备推送令牌' })
|
||||
@ApiResponse({ status: 200, description: '注销成功', type: ResponseDto })
|
||||
async unregisterToken(
|
||||
@CurrentUser() user: AccessTokenPayload,
|
||||
@Body() body: { deviceToken: string },
|
||||
): Promise<ResponseDto> {
|
||||
return this.pushNotificationsService.unregisterToken(user.sub, body.deviceToken);
|
||||
}
|
||||
|
||||
// 发送推送通知
|
||||
@Post('send')
|
||||
@ApiOperation({ summary: '发送推送通知' })
|
||||
@ApiResponse({ status: 200, description: '发送成功', type: PushResponseDto })
|
||||
async sendNotification(
|
||||
@Body() sendNotificationDto: SendPushNotificationDto,
|
||||
): Promise<PushResponseDto> {
|
||||
return this.pushNotificationsService.sendNotification(sendNotificationDto);
|
||||
}
|
||||
|
||||
// 使用模板发送推送
|
||||
@Post('send-by-template')
|
||||
@ApiOperation({ summary: '使用模板发送推送' })
|
||||
@ApiResponse({ status: 200, description: '发送成功', type: PushResponseDto })
|
||||
async sendNotificationByTemplate(
|
||||
@Body() sendByTemplateDto: SendPushByTemplateDto,
|
||||
): Promise<PushResponseDto> {
|
||||
return this.pushNotificationsService.sendNotificationByTemplate(sendByTemplateDto);
|
||||
}
|
||||
|
||||
// 批量发送推送
|
||||
@Post('send-batch')
|
||||
@ApiOperation({ summary: '批量发送推送' })
|
||||
@ApiResponse({ status: 200, description: '发送成功', type: BatchPushResponseDto })
|
||||
async sendBatchNotifications(
|
||||
@Body() sendBatchDto: SendPushNotificationDto,
|
||||
): Promise<BatchPushResponseDto> {
|
||||
return this.pushNotificationsService.sendBatchNotifications(sendBatchDto);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 PushTemplateController
|
||||
```typescript
|
||||
@Controller('push-notifications/templates')
|
||||
@ApiTags('推送模板')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class PushTemplateController {
|
||||
constructor(private readonly pushTemplateService: PushTemplateService) {}
|
||||
|
||||
// 获取所有模板
|
||||
@Get()
|
||||
@ApiOperation({ summary: '获取所有推送模板' })
|
||||
@ApiResponse({ status: 200, description: '获取成功', type: [PushTemplate] })
|
||||
async getAllTemplates(): Promise<PushTemplate[]> {
|
||||
return this.pushTemplateService.getAllTemplates();
|
||||
}
|
||||
|
||||
// 获取单个模板
|
||||
@Get(':templateKey')
|
||||
@ApiOperation({ summary: '获取推送模板' })
|
||||
@ApiResponse({ status: 200, description: '获取成功', type: PushTemplate })
|
||||
async getTemplate(
|
||||
@Param('templateKey') templateKey: string,
|
||||
): Promise<PushTemplate> {
|
||||
return this.pushTemplateService.getTemplate(templateKey);
|
||||
}
|
||||
|
||||
// 创建模板
|
||||
@Post()
|
||||
@ApiOperation({ summary: '创建推送模板' })
|
||||
@ApiResponse({ status: 201, description: '创建成功', type: PushTemplate })
|
||||
async createTemplate(
|
||||
@Body() createTemplateDto: CreatePushTemplateDto,
|
||||
): Promise<PushTemplate> {
|
||||
return this.pushTemplateService.createTemplate(createTemplateDto);
|
||||
}
|
||||
|
||||
// 更新模板
|
||||
@Put(':id')
|
||||
@ApiOperation({ summary: '更新推送模板' })
|
||||
@ApiResponse({ status: 200, description: '更新成功', type: PushTemplate })
|
||||
async updateTemplate(
|
||||
@Param('id') id: string,
|
||||
@Body() updateTemplateDto: UpdatePushTemplateDto,
|
||||
): Promise<PushTemplate> {
|
||||
return this.pushTemplateService.updateTemplate(id, updateTemplateDto);
|
||||
}
|
||||
|
||||
// 删除模板
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: '删除推送模板' })
|
||||
@ApiResponse({ status: 200, description: '删除成功' })
|
||||
async deleteTemplate(@Param('id') id: string): Promise<void> {
|
||||
return this.pushTemplateService.deleteTemplate(id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 接口使用示例
|
||||
|
||||
### 5.1 注册设备令牌
|
||||
```bash
|
||||
POST /api/push-notifications/register-token
|
||||
Authorization: Bearer <access_token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"deviceToken": "a9d0ed10e9cfd022a61cb08753f49c5a0b0dfb383697bf9f9d750a1003da19c7",
|
||||
"deviceType": "IOS",
|
||||
"appVersion": "1.0.0",
|
||||
"osVersion": "iOS 15.0",
|
||||
"deviceName": "iPhone 13"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 发送推送通知
|
||||
```bash
|
||||
POST /api/push-notifications/send
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"userIds": ["user_123", "user_456"],
|
||||
"title": "训练提醒",
|
||||
"body": "您今天的普拉提训练还未完成,快来打卡吧!",
|
||||
"payload": {
|
||||
"type": "training_reminder",
|
||||
"trainingId": "training_123"
|
||||
},
|
||||
"pushType": "ALERT",
|
||||
"priority": 10
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 使用模板发送推送
|
||||
```bash
|
||||
POST /api/push-notifications/send-by-template
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"userIds": ["user_123"],
|
||||
"templateKey": "training_reminder",
|
||||
"data": {
|
||||
"userName": "张三",
|
||||
"trainingName": "核心力量训练"
|
||||
},
|
||||
"payload": {
|
||||
"type": "training_reminder",
|
||||
"trainingId": "training_123"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 错误处理
|
||||
|
||||
### 6.1 错误类型定义
|
||||
```typescript
|
||||
export enum PushErrorCode {
|
||||
INVALID_DEVICE_TOKEN = 'INVALID_DEVICE_TOKEN',
|
||||
DEVICE_TOKEN_NOT_FOR_TOPIC = 'DEVICE_TOKEN_NOT_FOR_TOPIC',
|
||||
TOO_MANY_REQUESTS = 'TOO_MANY_REQUESTS',
|
||||
INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR',
|
||||
TEMPLATE_NOT_FOUND = 'TEMPLATE_NOT_FOUND',
|
||||
USER_NOT_FOUND = 'USER_NOT_FOUND',
|
||||
INVALID_PAYLOAD = 'INVALID_PAYLOAD',
|
||||
}
|
||||
|
||||
export class PushException extends HttpException {
|
||||
constructor(
|
||||
errorCode: PushErrorCode,
|
||||
message: string,
|
||||
statusCode: HttpStatus = HttpStatus.BAD_REQUEST,
|
||||
) {
|
||||
super({ code: errorCode, message }, statusCode);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 全局异常处理
|
||||
```typescript
|
||||
@Catch(PushException, Error)
|
||||
export class PushExceptionFilter implements ExceptionFilter {
|
||||
catch(exception: unknown, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
|
||||
if (exception instanceof PushException) {
|
||||
response.status(exception.getStatus()).json(exception.getResponse());
|
||||
} else {
|
||||
response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: '推送服务内部错误',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 性能优化策略
|
||||
|
||||
### 7.1 连接池管理
|
||||
- 使用HTTP/2连接池提高并发性能
|
||||
- 实现连接复用和心跳保活
|
||||
- 动态调整连接池大小
|
||||
|
||||
### 7.2 批量处理优化
|
||||
- 实现批量推送减少网络请求
|
||||
- 使用队列系统处理大量推送请求
|
||||
- 实现推送优先级和限流机制
|
||||
|
||||
### 7.3 缓存策略
|
||||
- 缓存用户设备令牌减少数据库查询
|
||||
- 缓存推送模板提高渲染性能
|
||||
- 实现分布式缓存支持集群部署
|
||||
Reference in New Issue
Block a user