import * as Notifications from 'expo-notifications'; import { Platform } from 'react-native'; // 配置通知处理方式 Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: true, shouldSetBadge: true, shouldShowBanner: true, shouldShowList: true, }), }); export interface NotificationData { title: string; body: string; data?: Record; sound?: boolean; priority?: 'default' | 'normal' | 'high'; vibrate?: number[]; } export class NotificationService { private static instance: NotificationService; private isInitialized = false; private constructor() {} public static getInstance(): NotificationService { if (!NotificationService.instance) { NotificationService.instance = new NotificationService(); } return NotificationService.instance; } /** * 初始化推送通知服务 */ async initialize(): Promise { if (this.isInitialized) return; try { // 请求通知权限 const { status: existingStatus } = await Notifications.getPermissionsAsync(); let finalStatus = existingStatus; if (existingStatus !== 'granted') { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } if (finalStatus !== 'granted') { console.warn('推送通知权限未授予'); return; } // 获取推送令牌(用于远程推送,本地推送不需要) if (Platform.OS !== 'web') { const token = await Notifications.getExpoPushTokenAsync({ projectId: 'your-project-id', // 需要替换为实际的Expo项目ID }); console.log('推送令牌:', token.data); } // 设置通知监听器 this.setupNotificationListeners(); this.isInitialized = true; console.log('推送通知服务初始化成功'); } catch (error) { console.error('推送通知服务初始化失败:', error); } } /** * 设置通知监听器 */ private setupNotificationListeners(): void { // 监听通知接收 Notifications.addNotificationReceivedListener((notification) => { console.log('收到通知:', notification); }); // 监听通知点击 Notifications.addNotificationResponseReceivedListener((response) => { console.log('用户点击了通知:', response); // 这里可以处理通知点击后的逻辑 this.handleNotificationResponse(response); }); } /** * 处理通知响应 */ private handleNotificationResponse(response: Notifications.NotificationResponse): void { const { notification } = response; const data = notification.request.content.data; // 根据通知类型处理不同的逻辑 if (data?.type === 'workout_reminder') { // 处理运动提醒 console.log('用户点击了运动提醒通知'); } else if (data?.type === 'goal_achievement') { // 处理目标达成通知 console.log('用户点击了目标达成通知'); } else if (data?.type === 'mood_checkin') { // 处理心情打卡提醒 console.log('用户点击了心情打卡提醒'); } } /** * 发送本地推送通知 */ async scheduleLocalNotification( notification: NotificationData, trigger?: Notifications.NotificationTriggerInput ): Promise { try { const notificationId = await Notifications.scheduleNotificationAsync({ content: { title: notification.title, body: notification.body, data: notification.data || {}, sound: notification.sound ? 'default' : undefined, priority: notification.priority || 'default', vibrate: notification.vibrate, }, trigger: trigger || null, // null表示立即发送 }); console.log('本地通知已安排,ID:', notificationId); return notificationId; } catch (error) { console.error('安排本地通知失败:', error); throw error; } } /** * 发送立即通知 */ async sendImmediateNotification(notification: NotificationData): Promise { return this.scheduleLocalNotification(notification); } /** * 安排定时通知 */ async scheduleNotificationAtDate( notification: NotificationData, date: Date ): Promise { try { const notificationId = await Notifications.scheduleNotificationAsync({ content: { title: notification.title, body: notification.body, data: notification.data || {}, sound: notification.sound ? 'default' : undefined, priority: notification.priority || 'default', vibrate: notification.vibrate, }, trigger: { date: date.getTime(), } as any, }); console.log('定时通知已安排,ID:', notificationId); return notificationId; } catch (error) { console.error('安排定时通知失败:', error); throw error; } } /** * 安排重复通知(仅支持秒级别) */ async scheduleRepeatingNotification( notification: NotificationData, interval: { seconds?: number; minutes?: number; hours?: number; days?: number; weeks?: number; months?: number; years?: number; } ): Promise { try { // 计算总秒数 const totalSeconds = (interval.seconds || 0) + (interval.minutes || 0) * 60 + (interval.hours || 0) * 3600 + (interval.days || 0) * 86400 + (interval.weeks || 0) * 604800 + (interval.months || 0) * 2592000 + (interval.years || 0) * 31536000; if (totalSeconds <= 0) { throw new Error('重复间隔必须大于0'); } const notificationId = await Notifications.scheduleNotificationAsync({ content: { title: notification.title, body: notification.body, data: notification.data || {}, sound: notification.sound ? 'default' : undefined, priority: notification.priority || 'default', vibrate: notification.vibrate, }, trigger: { seconds: totalSeconds, repeats: true, } as any, }); console.log('重复通知已安排,ID:', notificationId); return notificationId; } catch (error) { console.error('安排重复通知失败:', error); throw error; } } /** * 取消特定通知 */ async cancelNotification(notificationId: string): Promise { try { await Notifications.cancelScheduledNotificationAsync(notificationId); console.log('通知已取消:', notificationId); } catch (error) { console.error('取消通知失败:', error); throw error; } } /** * 取消所有通知 */ async cancelAllNotifications(): Promise { try { await Notifications.cancelAllScheduledNotificationsAsync(); console.log('所有通知已取消'); } catch (error) { console.error('取消所有通知失败:', error); throw error; } } /** * 获取所有已安排的通知 */ async getAllScheduledNotifications(): Promise { try { const notifications = await Notifications.getAllScheduledNotificationsAsync(); return notifications; } catch (error) { console.error('获取已安排通知失败:', error); throw error; } } /** * 获取通知权限状态 */ async getPermissionStatus(): Promise { try { const { status } = await Notifications.getPermissionsAsync(); return status; } catch (error) { console.error('获取通知权限状态失败:', error); throw error; } } /** * 请求通知权限 */ async requestPermission(): Promise { try { const { status } = await Notifications.requestPermissionsAsync(); return status; } catch (error) { console.error('请求通知权限失败:', error); throw error; } } } // 导出单例实例 export const notificationService = NotificationService.getInstance(); // 预定义的推送通知类型 export const NotificationTypes = { WORKOUT_REMINDER: 'workout_reminder', GOAL_ACHIEVEMENT: 'goal_achievement', MOOD_CHECKIN: 'mood_checkin', NUTRITION_REMINDER: 'nutrition_reminder', PROGRESS_UPDATE: 'progress_update', } as const; // 便捷方法 export const sendWorkoutReminder = (title: string, body: string, date?: Date) => { const notification: NotificationData = { title, body, data: { type: NotificationTypes.WORKOUT_REMINDER }, sound: true, priority: 'high', }; if (date) { return notificationService.scheduleNotificationAtDate(notification, date); } else { return notificationService.sendImmediateNotification(notification); } }; export const sendGoalAchievement = (title: string, body: string) => { const notification: NotificationData = { title, body, data: { type: NotificationTypes.GOAL_ACHIEVEMENT }, sound: true, priority: 'high', }; return notificationService.sendImmediateNotification(notification); }; export const sendMoodCheckinReminder = (title: string, body: string, date?: Date) => { const notification: NotificationData = { title, body, data: { type: NotificationTypes.MOOD_CHECKIN }, sound: true, priority: 'normal', }; if (date) { return notificationService.scheduleNotificationAtDate(notification, date); } else { return notificationService.sendImmediateNotification(notification); } };