# iOS推送功能使用指南 ## 概述 本文档详细介绍了如何使用iOS远程推送功能,包括API接口使用、配置说明和代码示例。 ## 环境配置 ### 1. 环境变量配置 在`.env`文件中添加以下配置: ```bash # APNs配置 APNS_KEY_ID=your_key_id APNS_TEAM_ID=your_team_id APNS_KEY_PATH=path/to/APNsAuthKey_XXXXXXXXXX.p8 APNS_BUNDLE_ID=com.yourcompany.yourapp APNS_ENVIRONMENT=production # or sandbox # 推送服务配置 APNS_CLIENT_COUNT=2 APNS_CONNECTION_RETRY_LIMIT=3 APNS_HEARTBEAT=60000 APNS_REQUEST_TIMEOUT=5000 ``` ### 2. APNs认证文件 1. 登录 [Apple Developer Portal](https://developer.apple.com) 2. 导航到 "Certificates, Identifiers & Profiles" 3. 选择 "Keys" 4. 创建新的密钥,并启用 "Apple Push Notifications service" 5. 下载`.p8`格式的私钥文件 6. 将私钥文件安全地存储在服务器上 ### 3. 数据库迁移 执行以下SQL脚本创建推送相关的数据表: ```bash mysql -u username -p database_name < sql-scripts/push-notifications-tables-create.sql ``` ## API接口使用 ### 1. 设备令牌管理 #### 注册设备令牌 ```bash POST /api/push-notifications/register-token Authorization: Bearer Content-Type: application/json { "deviceToken": "a9d0ed10e9cfd022a61cb08753f49c5a0b0dfb383697bf9f9d750a1003da19c7", "deviceType": "IOS", "appVersion": "1.0.0", "osVersion": "iOS 15.0", "deviceName": "iPhone 13" } ``` **响应示例:** ```json { "code": 0, "message": "设备令牌注册成功", "data": { "success": true, "tokenId": "uuid-token-id" } } ``` #### 更新设备令牌 ```bash PUT /api/push-notifications/update-token Authorization: Bearer Content-Type: application/json { "currentDeviceToken": "old-device-token", "newDeviceToken": "new-device-token", "appVersion": "1.0.1", "osVersion": "iOS 15.1" } ``` #### 注销设备令牌 ```bash DELETE /api/push-notifications/unregister-token Authorization: Bearer Content-Type: application/json { "deviceToken": "device-token-to-unregister" } ``` ### 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, "sound": "default", "badge": 1 } ``` **响应示例:** ```json { "code": 0, "message": "推送发送成功", "data": { "success": true, "sentCount": 2, "failedCount": 0, "results": [ { "userId": "user_123", "deviceToken": "device-token-1", "success": true }, { "userId": "user_456", "deviceToken": "device-token-2", "success": true } ] } } ``` #### 使用模板发送推送 ```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" } } ``` #### 批量发送推送 ```bash POST /api/push-notifications/send-batch Content-Type: application/json { "userIds": ["user_123", "user_456", "user_789"], "title": "系统通知", "body": "系统将于今晚22:00进行维护,请提前保存您的工作。", "payload": { "type": "system_maintenance", "maintenanceTime": "22:00" } } ``` #### 发送静默推送 ```bash POST /api/push-notifications/send-silent Content-Type: application/json { "userId": "user_123", "payload": { "type": "data_sync", "syncData": true } } ``` ### 3. 推送模板管理 #### 获取所有模板 ```bash GET /api/push-notifications/templates Authorization: Bearer ``` #### 创建推送模板 ```bash POST /api/push-notifications/templates Authorization: Bearer Content-Type: application/json { "templateKey": "custom_reminder", "title": "自定义提醒", "body": "您好{{userName}},{{reminderContent}}", "payloadTemplate": { "type": "custom_reminder", "reminderId": "{{reminderId}}" }, "pushType": "ALERT", "priority": 8 } ``` #### 更新推送模板 ```bash PUT /api/push-notifications/templates/:id Authorization: Bearer Content-Type: application/json { "title": "更新后的标题", "body": "更新后的内容:{{userName}},{{reminderContent}}", "isActive": true } ``` #### 删除推送模板 ```bash DELETE /api/push-notifications/templates/:id Authorization: Bearer ``` ## 代码示例 ### 1. 在业务服务中使用推送功能 ```typescript import { Injectable } from '@nestjs/common'; import { PushNotificationsService } from '../push-notifications/push-notifications.service'; @Injectable() export class TrainingService { constructor( private readonly pushNotificationsService: PushNotificationsService, ) {} async sendTrainingReminder(userId: string, trainingName: string): Promise { // 使用模板发送推送 await this.pushNotificationsService.sendNotificationByTemplate({ userIds: [userId], templateKey: 'training_reminder', data: { userName: '用户', // 可以从用户服务获取 trainingName, }, payload: { type: 'training_reminder', trainingId: 'training_123', }, }); } async sendWorkoutCompletionNotification(userId: string, workoutName: string, calories: number): Promise { // 直接发送推送 await this.pushNotificationsService.sendNotification({ userIds: [userId], title: '训练完成', body: `太棒了!您已完成${workoutName}训练,消耗了${calories}卡路里。`, payload: { type: 'workout_completed', workoutId: 'workout_123', calories, }, sound: 'celebration.caf', badge: 1, }); } } ``` ### 2. 在控制器中处理设备令牌注册 ```typescript import { Controller, Post, Body, UseGuards } from '@nestjs/common'; import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { PushNotificationsService } from '../push-notifications/push-notifications.service'; import { RegisterDeviceTokenDto } from '../push-notifications/dto/register-device-token.dto'; import { CurrentUser } from '../common/decorators/current-user.decorator'; import { AccessTokenPayload } from '../users/services/apple-auth.service'; import { JwtAuthGuard } from '../common/guards/jwt-auth.guard'; @ApiTags('用户设备') @Controller('user/device') @UseGuards(JwtAuthGuard) export class UserDeviceController { constructor( private readonly pushNotificationsService: PushNotificationsService, ) {} @Post('register-token') @ApiOperation({ summary: '注册设备推送令牌' }) async registerDeviceToken( @CurrentUser() user: AccessTokenPayload, @Body() registerTokenDto: RegisterDeviceTokenDto, ) { return this.pushNotificationsService.registerToken(user.sub, registerTokenDto); } } ``` ## 预定义推送模板 系统已预置以下推送模板: ### 1. 训练提醒 (training_reminder) - **用途**: 提醒用户完成训练 - **变量**: `{{userName}}`, `{{trainingName}}` - **示例**: "您好张三,您今天的核心力量训练还未完成,快来打卡吧!" ### 2. 饮食记录提醒 (diet_record_reminder) - **用途**: 提醒用户记录饮食 - **变量**: `{{userName}}` - **示例**: "您好张三,您还没有记录今天的饮食,记得及时记录哦!" ### 3. 挑战进度 (challenge_progress) - **用途**: 通知用户挑战进度 - **变量**: `{{challengeName}}`, `{{progress}}` - **示例**: "恭喜您!您已完成30天挑战的50%,继续加油!" ### 4. 会员到期提醒 (membership_expiring) - **用途**: 提醒用户会员即将到期 - **变量**: `{{userName}}`, `{{days}}` - **示例**: "您好张三,您的会员将在7天后到期,请及时续费以免影响使用。" ### 5. 会员已到期 (membership_expired) - **用途**: 通知用户会员已到期 - **变量**: `{{userName}}` - **示例**: "您好张三,您的会员已到期,请续费以继续享受会员服务。" ### 6. 成就解锁 (achievement_unlocked) - **用途**: 庆祝用户解锁成就 - **变量**: `{{achievementName}}` - **示例**: "恭喜您解锁了"连续训练7天"成就!" ### 7. 训练完成 (workout_completed) - **用途**: 确认用户完成训练 - **变量**: `{{workoutName}}`, `{{calories}}` - **示例**: "太棒了!您已完成核心力量训练,消耗了150卡路里。" ## 错误处理 ### 常见错误码 | 错误码 | 描述 | 解决方案 | |--------|------|----------| | 400 | 请求参数错误 | 检查请求参数格式和必填字段 | | 401 | 未授权访问 | 确保提供了有效的访问令牌 | | 404 | 资源不存在 | 检查用户ID或模板键是否正确 | | 429 | 请求频率过高 | 降低请求频率,实现退避重试 | | 500 | 服务器内部错误 | 检查服务器日志,联系技术支持 | ### APNs错误处理 系统会自动处理以下APNs错误: - **Unregistered**: 自动停用无效的设备令牌 - **BadDeviceToken**: 记录错误并停用令牌 - **DeviceTokenNotForTopic**: 记录错误日志 - **TooManyRequests**: 实现退避重试机制 - **InternalServerError**: 自动重试 ## 监控和日志 ### 1. 推送状态监控 可以通过以下方式监控推送状态: ```typescript // 获取推送统计 const stats = await this.pushMessageService.getMessageStats(); console.log(`推送成功率: ${stats.successRate}%`); console.log(`错误分布:`, stats.errorBreakdown); ``` ### 2. 日志查看 推送相关日志包含以下信息: - 推送请求和响应 - APNs连接状态 - 错误详情和堆栈跟踪 - 性能指标 ## 最佳实践 ### 1. 推送时机 - 避免在深夜或凌晨发送推送 - 根据用户时区调整推送时间 - 尊重用户的推送偏好设置 ### 2. 推送内容 - 保持推送内容简洁明了 - 使用个性化内容提高用户参与度 - 避免发送过于频繁的推送 ### 3. 性能优化 - 使用批量推送减少网络请求 - 实现推送优先级管理 - 定期清理无效的设备令牌 ### 4. 安全考虑 - 保护用户隐私数据 - 实现推送内容审核机制 - 使用HTTPS进行API通信 ## 故障排除 ### 1. 推送不生效 - 检查APNs配置是否正确 - 验证设备令牌是否有效 - 确认Bundle ID是否匹配 ### 2. 推送延迟 - 检查网络连接状态 - 验证APNs服务器状态 - 调整推送优先级设置 ### 3. 设备令牌失效 - 实现令牌自动更新机制 - 定期清理无效令牌 - 监控令牌失效率 ## 扩展功能 ### 1. 推送统计分析 - 实现推送打开率统计 - 分析用户行为数据 - 优化推送策略 ### 2. A/B测试 - 实现推送内容A/B测试 - 比较不同推送策略效果 - 优化推送转化率 ### 3. 多平台支持 - 扩展Android推送功能 - 统一推送接口设计 - 实现平台特定功能 ## 总结 iOS推送功能已完全集成到系统中,提供了完整的推送令牌管理、消息发送和模板系统。通过遵循本指南,您可以轻松地在应用中实现各种推送场景,提升用户体验和参与度。 如有任何问题或需要进一步的技术支持,请参考相关文档或联系开发团队。