/** * 睡眠通知服务 * * 负责在睡眠分析完成后发送通知,提供睡眠质量评估和建议 */ import i18n from '@/i18n'; import { logger } from '@/utils/logger'; import dayjs from 'dayjs'; import * as Notifications from 'expo-notifications'; import { SleepAnalysisData } from './sleepMonitor'; const t = (key: string, options?: Record) => i18n.t(`sleepNotification.${key}`, options); /** * 分析睡眠数据并发送通知 */ export async function analyzeSleepAndSendNotification( analysis: SleepAnalysisData ): Promise { try { logger.info('开始分析睡眠并发送通知:', { score: analysis.sleepScore, quality: analysis.quality, duration: analysis.totalSleepHours, }); // 构建通知内容 const notification = buildSleepNotification(analysis); // 发送通知 await Notifications.scheduleNotificationAsync({ content: notification, trigger: null, // 立即发送 }); logger.info('睡眠分析通知已发送'); } catch (error) { logger.error('发送睡眠分析通知失败:', error); throw error; } } /** * 构建睡眠通知内容 */ function buildSleepNotification(analysis: SleepAnalysisData): Notifications.NotificationContentInput { const { sleepScore, quality, totalSleepHours, sleepEfficiency } = analysis; // 根据质量等级选择emoji和标题 const qualityConfig = getQualityConfig(quality); // 构建通知标题 const title = `${qualityConfig.emoji} ${qualityConfig.title}`; // 构建通知正文 const sleepDuration = formatSleepDuration(totalSleepHours); const body = t('body', { duration: sleepDuration, efficiency: sleepEfficiency.toFixed(0), score: sleepScore }); // 获取建议 const suggestion = getSleepSuggestion(analysis); // 计算睡眠日期 // 睡眠详情页面使用的日期逻辑是:传入的日期会查询从前一天18:00到当天12:00的数据 // 所以我们应该传递醒来的日期(sessionEnd),这样用户点击通知后能看到正确的睡眠数据 const sleepDate = analysis.sessionEnd ? dayjs(analysis.sessionEnd).format('YYYY-MM-DD') : dayjs().format('YYYY-MM-DD'); return { title, body: `${body}\n${suggestion}`, data: { type: 'sleep_analysis', score: sleepScore, quality, date: sleepDate, // 添加日期参数,用于点击通知后跳转 analysis: JSON.stringify(analysis), url: '/sleep-detail', // 点击通知跳转到睡眠详情页 }, sound: 'default', badge: 1, }; } /** * 获取质量配置 */ function getQualityConfig(quality: string): { emoji: string; title: string; } { const configs: Record = { excellent: { emoji: '🥳', title: t('quality.excellent'), }, good: { emoji: '☀️', title: t('quality.good'), }, fair: { emoji: '🌤️', title: t('quality.fair'), }, poor: { emoji: '🌛', title: t('quality.poor'), }, very_poor: { emoji: '🫂', title: t('quality.veryPoor'), }, }; return configs[quality] || { emoji: '🛏️', title: t('quality.default'), }; } /** * 格式化睡眠时长 */ function formatSleepDuration(hours: number): string { const h = Math.floor(hours); const m = Math.round((hours - h) * 60); if (m === 0) { return t('duration.hoursOnly', { hours: h }); } return t('duration.hoursAndMinutes', { hours: h, minutes: m }); } /** * 获取睡眠建议 */ function getSleepSuggestion(analysis: SleepAnalysisData): string { const { quality, totalSleepHours, deepSleepPercentage, remSleepPercentage, sleepEfficiency } = analysis; // 优秀或良好的睡眠 - 给予鼓励 if (quality === 'excellent' || quality === 'good') { const tips = [ t('tips.excellent.keepItUp'), t('tips.excellent.greatJob'), t('tips.excellent.energized'), t('tips.excellent.proud'), ]; return `✨ ${tips[Math.floor(Math.random() * tips.length)]}`; } // 根据具体问题给出温暖的建议 const suggestions: string[] = []; if (totalSleepHours < 7) { suggestions.push(t('tips.suggestions.shortSleep')); } else if (totalSleepHours > 9) { suggestions.push(t('tips.suggestions.longSleep')); } if (deepSleepPercentage < 13) { suggestions.push(t('tips.suggestions.lowDeepSleep')); } if (remSleepPercentage < 20) { suggestions.push(t('tips.suggestions.lowRemSleep')); } if (sleepEfficiency < 85) { suggestions.push(t('tips.suggestions.lowEfficiency')); } // 如果有具体建议,返回第一条;否则返回通用建议 if (suggestions.length > 0) { return `💡 ${suggestions[0]}`; } return `💡 ${t('tips.general')}`; }