import dayjs from 'dayjs'; import AppleHealthKit from 'react-native-health'; // 睡眠阶段枚举(与 HealthKit 保持一致) export enum SleepStage { InBed = 'INBED', Asleep = 'ASLEEP', Awake = 'AWAKE', Core = 'CORE', Deep = 'DEEP', REM = 'REM' } // 睡眠质量评级 export enum SleepQuality { Poor = 'poor', // 差 Fair = 'fair', // 一般 Good = 'good', // 良好 Excellent = 'excellent' // 优秀 } // 睡眠样本数据类型 export type SleepSample = { startDate: string; endDate: string; value: SleepStage; sourceName?: string; sourceId?: string; }; // 睡眠阶段统计 export type SleepStageStats = { stage: SleepStage; duration: number; // 分钟 percentage: number; // 百分比 quality: SleepQuality; }; // 心率数据类型 export type HeartRateData = { timestamp: string; value: number; // BPM }; // 睡眠详情数据类型 export type SleepDetailData = { // 基础睡眠信息 sleepScore: number; // 睡眠得分 0-100 totalSleepTime: number; // 总睡眠时间(分钟) sleepQualityPercentage: number; // 睡眠质量百分比 // 睡眠时间信息 bedtime: string; // 上床时间 wakeupTime: string; // 起床时间 timeInBed: number; // 在床时间(分钟) // 睡眠阶段统计 sleepStages: SleepStageStats[]; // 心率数据 averageHeartRate: number | null; // 平均心率 sleepHeartRateData: HeartRateData[]; // 睡眠期间心率数据 // 睡眠效率 sleepEfficiency: number; // 睡眠效率百分比 (总睡眠时间/在床时间) // 建议和评价 qualityDescription: string; // 睡眠质量描述 recommendation: string; // 睡眠建议 }; // 日期范围工具函数 function createSleepDateRange(date: Date): { startDate: string; endDate: string } { // 睡眠数据通常跨越两天,从前一天18:00到当天12:00 return { startDate: dayjs(date).subtract(1, 'day').hour(18).minute(0).second(0).millisecond(0).toISOString(), endDate: dayjs(date).hour(12).minute(0).second(0).millisecond(0).toISOString() }; } // 获取睡眠样本数据 async function fetchSleepSamples(date: Date): Promise { return new Promise((resolve) => { const options = createSleepDateRange(date); AppleHealthKit.getSleepSamples(options, (err, results) => { if (err) { console.error('获取睡眠样本失败:', err); resolve([]); return; } if (!results || !Array.isArray(results)) { console.warn('睡眠样本数据为空'); resolve([]); return; } console.log('获取到睡眠样本:', results.length); resolve(results as unknown as SleepSample[]); }); }); } // 获取睡眠期间心率数据 async function fetchSleepHeartRateData(bedtime: string, wakeupTime: string): Promise { return new Promise((resolve) => { const options = { startDate: bedtime, endDate: wakeupTime, ascending: true }; AppleHealthKit.getHeartRateSamples(options, (err, results) => { if (err) { console.error('获取睡眠心率数据失败:', err); resolve([]); return; } if (!results || !Array.isArray(results)) { resolve([]); return; } const heartRateData: HeartRateData[] = results.map(sample => ({ timestamp: sample.startDate, value: Math.round(sample.value) })); console.log('获取到睡眠心率数据:', heartRateData.length, '个样本'); resolve(heartRateData); }); }); } // 计算睡眠阶段统计 function calculateSleepStageStats(samples: SleepSample[]): SleepStageStats[] { const stageMap = new Map(); // 计算每个阶段的总时长 samples.forEach(sample => { const startTime = dayjs(sample.startDate); const endTime = dayjs(sample.endDate); const duration = endTime.diff(startTime, 'minute'); const currentDuration = stageMap.get(sample.value) || 0; stageMap.set(sample.value, currentDuration + duration); }); // 计算总睡眠时间(排除在床时间) const totalSleepTime = Array.from(stageMap.entries()) .filter(([stage]) => stage !== SleepStage.InBed) .reduce((total, [, duration]) => total + duration, 0); // 生成统计数据 const stats: SleepStageStats[] = []; stageMap.forEach((duration, stage) => { if (stage === SleepStage.InBed || stage === SleepStage.Awake) return; const percentage = totalSleepTime > 0 ? (duration / totalSleepTime) * 100 : 0; let quality: SleepQuality; // 根据睡眠阶段和比例判断质量 switch (stage) { case SleepStage.Deep: quality = percentage >= 15 ? SleepQuality.Excellent : percentage >= 10 ? SleepQuality.Good : percentage >= 5 ? SleepQuality.Fair : SleepQuality.Poor; break; case SleepStage.REM: quality = percentage >= 20 ? SleepQuality.Excellent : percentage >= 15 ? SleepQuality.Good : percentage >= 10 ? SleepQuality.Fair : SleepQuality.Poor; break; case SleepStage.Core: quality = percentage >= 45 ? SleepQuality.Excellent : percentage >= 35 ? SleepQuality.Good : percentage >= 25 ? SleepQuality.Fair : SleepQuality.Poor; break; default: quality = SleepQuality.Fair; } stats.push({ stage, duration, percentage: Math.round(percentage), quality }); }); // 按持续时间排序 return stats.sort((a, b) => b.duration - a.duration); } // 计算睡眠得分 function calculateSleepScore(sleepStages: SleepStageStats[], sleepEfficiency: number, totalSleepTime: number): number { let score = 0; // 睡眠时长得分 (30分) const idealSleepHours = 8 * 60; // 8小时 const sleepDurationScore = Math.min(30, (totalSleepTime / idealSleepHours) * 30); score += sleepDurationScore; // 睡眠效率得分 (25分) const efficiencyScore = (sleepEfficiency / 100) * 25; score += efficiencyScore; // 深度睡眠得分 (25分) const deepSleepStage = sleepStages.find(stage => stage.stage === SleepStage.Deep); const deepSleepScore = deepSleepStage ? Math.min(25, (deepSleepStage.percentage / 20) * 25) : 0; score += deepSleepScore; // REM睡眠得分 (20分) const remSleepStage = sleepStages.find(stage => stage.stage === SleepStage.REM); const remSleepScore = remSleepStage ? Math.min(20, (remSleepStage.percentage / 25) * 20) : 0; score += remSleepScore; return Math.round(Math.min(100, score)); } // 获取睡眠质量描述和建议 function getSleepQualityInfo(sleepScore: number): { description: string; recommendation: string } { if (sleepScore >= 85) { return { description: '你身心愉悦并且精力充沛', recommendation: '恭喜你获得优质的睡眠!如果你感到精力充沛,可以考虑中等强度的运动,以维持健康的生活方式,并进一步减轻压力,以获得最佳睡眠。' }; } else if (sleepScore >= 70) { return { description: '睡眠质量良好,精神状态不错', recommendation: '你的睡眠质量还不错,但还有改善空间。建议保持规律的睡眠时间,睡前避免使用电子设备,营造安静舒适的睡眠环境。' }; } else if (sleepScore >= 50) { return { description: '睡眠质量一般,可能影响日间表现', recommendation: '你的睡眠需要改善。建议制定固定的睡前例行程序,限制咖啡因摄入,确保卧室温度适宜,考虑进行轻度运动来改善睡眠质量。' }; } else { return { description: '睡眠质量较差,建议重视睡眠健康', recommendation: '你的睡眠质量需要严重关注。建议咨询医生或睡眠专家,检查是否有睡眠障碍,同时改善睡眠环境和习惯,避免睡前刺激性活动。' }; } } // 获取睡眠阶段中文名称 export function getSleepStageDisplayName(stage: SleepStage): string { switch (stage) { case SleepStage.Deep: return '深度'; case SleepStage.Core: return '核心'; case SleepStage.REM: return '快速眼动'; case SleepStage.Asleep: return '浅睡'; case SleepStage.Awake: return '清醒'; case SleepStage.InBed: return '在床'; default: return '未知'; } } // 获取睡眠质量颜色 export function getSleepStageColor(stage: SleepStage): string { switch (stage) { case SleepStage.Deep: return '#1E40AF'; // 深蓝色 case SleepStage.Core: return '#3B82F6'; // 蓝色 case SleepStage.REM: return '#8B5CF6'; // 紫色 case SleepStage.Asleep: return '#06B6D4'; // 青色 case SleepStage.Awake: return '#F59E0B'; // 橙色 case SleepStage.InBed: return '#6B7280'; // 灰色 default: return '#9CA3AF'; } } // 主函数:获取完整的睡眠详情数据 export async function fetchSleepDetailForDate(date: Date): Promise { try { console.log('开始获取睡眠详情数据...', date); // 获取睡眠样本数据 const sleepSamples = await fetchSleepSamples(date); if (sleepSamples.length === 0) { console.warn('没有找到睡眠数据'); return null; } // 找到上床时间和起床时间 const inBedSamples = sleepSamples.filter(sample => sample.value === SleepStage.InBed); const bedtime = inBedSamples.length > 0 ? inBedSamples[0].startDate : sleepSamples[0].startDate; const wakeupTime = inBedSamples.length > 0 ? inBedSamples[inBedSamples.length - 1].endDate : sleepSamples[sleepSamples.length - 1].endDate; // 计算在床时间 const timeInBed = dayjs(wakeupTime).diff(dayjs(bedtime), 'minute'); // 计算睡眠阶段统计 const sleepStages = calculateSleepStageStats(sleepSamples); // 计算总睡眠时间 const totalSleepTime = sleepStages.reduce((total, stage) => total + stage.duration, 0); // 计算睡眠效率 const sleepEfficiency = timeInBed > 0 ? Math.round((totalSleepTime / timeInBed) * 100) : 0; // 获取睡眠期间心率数据 const sleepHeartRateData = await fetchSleepHeartRateData(bedtime, wakeupTime); // 计算平均心率 const averageHeartRate = sleepHeartRateData.length > 0 ? Math.round(sleepHeartRateData.reduce((sum, data) => sum + data.value, 0) / sleepHeartRateData.length) : null; // 计算睡眠得分 const sleepScore = calculateSleepScore(sleepStages, sleepEfficiency, totalSleepTime); // 获取质量描述和建议 const qualityInfo = getSleepQualityInfo(sleepScore); const sleepDetailData: SleepDetailData = { sleepScore, totalSleepTime, sleepQualityPercentage: sleepScore, // 使用睡眠得分作为质量百分比 bedtime, wakeupTime, timeInBed, sleepStages, averageHeartRate, sleepHeartRateData, sleepEfficiency, qualityDescription: qualityInfo.description, recommendation: qualityInfo.recommendation }; console.log('睡眠详情数据获取完成:', sleepDetailData); return sleepDetailData; } catch (error) { console.error('获取睡眠详情数据失败:', error); return null; } } // 格式化睡眠时间显示 export function formatSleepTime(minutes: number): string { const hours = Math.floor(minutes / 60); const mins = minutes % 60; if (hours > 0 && mins > 0) { return `${hours}h ${mins}m`; } else if (hours > 0) { return `${hours}h`; } else { return `${mins}m`; } } // 格式化时间显示 (HH:MM) export function formatTime(dateString: string): string { return dayjs(dateString).format('HH:mm'); }