import dayjs from 'dayjs'; import HealthKitManager, { HealthKitUtils } from './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; }; // 完整睡眠详情数据 export type CompleteSleepData = { sleepScore: number; totalSleepTime: number; sleepQualityPercentage: number; bedtime: string; wakeupTime: string; timeInBed: number; sleepStages: SleepStageStats[]; rawSleepSamples: SleepSample[]; averageHeartRate: number | null; sleepHeartRateData: HeartRateData[]; sleepEfficiency: number; qualityDescription: string; recommendation: string; }; /** * 创建睡眠日期范围 (从前一天 18:00 到当天 12:00) */ const createSleepDateRange = (date: Date): { startDate: Date; endDate: Date } => { return { startDate: dayjs(date).subtract(1, 'day').hour(18).minute(0).second(0).millisecond(0).toDate(), endDate: dayjs(date).hour(12).minute(0).second(0).millisecond(0).toDate() }; }; /** * 映射 HealthKit 睡眠值到自定义枚举 * react-native-health 库返回的睡眠分析值是字符串: * "INBED" = InBed (在床时间) * "ASLEEP" = Asleep (睡着但未分阶段) * "AWAKE" = Awake (醒来时间) * "CORE" = Core (核心睡眠/浅度睡眠) * "DEEP" = Deep (深度睡眠) * "REM" = REM (快速眼动睡眠) */ const mapHealthKitSleepValue = (value: string): SleepStage => { switch (value) { case 'INBED': return SleepStage.InBed; case 'ASLEEP': return SleepStage.Asleep; case 'AWAKE': return SleepStage.Awake; case 'CORE': return SleepStage.Core; case 'DEEP': return SleepStage.Deep; case 'REM': return SleepStage.REM; default: console.warn(`[Sleep] 未识别的睡眠阶段值: ${value}`); return SleepStage.Asleep; } }; /** * 获取睡眠样本数据 */ export const fetchSleepSamples = async (date: Date): Promise => { try { const dateRange = createSleepDateRange(date); console.log('[Sleep] 查询睡眠数据范围:', { startDate: dayjs(dateRange.startDate).format('YYYY-MM-DD HH:mm:ss'), endDate: dayjs(dateRange.endDate).format('YYYY-MM-DD HH:mm:ss') }); const options = { startDate: dateRange.startDate.toISOString(), endDate: dateRange.endDate.toISOString(), }; // return new Promise((resolve) => { // HealthKitManager.(options, (error: string, results: any[]) => { // if (error) { // console.error('[Sleep] 获取睡眠数据失败:', error); // resolve([]); // 返回空数组而非拒绝,以便于处理 // return; // } // if (!results || results.length === 0) { // console.warn('[Sleep] 未找到睡眠数据'); // resolve([]); // return; // } // console.log('[Sleep] 获取到原始睡眠样本:', results.length, '条'); // // 过滤并转换数据格式 // const sleepSamples: SleepSample[] = results // .filter(sample => { // const sampleStart = new Date(sample.startDate).getTime(); // const sampleEnd = new Date(sample.endDate).getTime(); // const rangeStart = dateRange.startDate.getTime(); // const rangeEnd = dateRange.endDate.getTime(); // return (sampleStart >= rangeStart && sampleStart < rangeEnd) || // (sampleStart < rangeEnd && sampleEnd > rangeStart); // }) // .map(sample => { // console.log('[Sleep] 原始睡眠样本:', { // startDate: sample.startDate, // endDate: sample.endDate, // value: sample.value, // sourceName: sample.sourceName // }); // return { // startDate: sample.startDate, // endDate: sample.endDate, // value: mapHealthKitSleepValue(sample.value), // sourceName: sample.sourceName, // sourceId: sample.sourceId || sample.uuid // }; // }); // console.log('[Sleep] 过滤后的睡眠样本:', sleepSamples.length, '条'); // resolve(sleepSamples); // }); // }); } catch (error) { console.error('[Sleep] 获取睡眠样本失败:', error); return []; } }; /** * 获取睡眠期间心率数据 */ export const fetchSleepHeartRateData = async (bedtime: string, wakeupTime: string): Promise => { try { const options = { startDate: bedtime, endDate: wakeupTime, }; // return new Promise((resolve) => { // HealthKit.getHeartRateSamples(options, (error: string, results: any[]) => { // if (error) { // console.error('[Sleep] 获取心率数据失败:', error); // resolve([]); // return; // } // if (!results || results.length === 0) { // console.log('[Sleep] 未找到心率数据'); // resolve([]); // return; // } // const heartRateData: HeartRateData[] = results // .filter(sample => { // const sampleTime = new Date(sample.startDate).getTime(); // const bedtimeMs = new Date(bedtime).getTime(); // const wakeupTimeMs = new Date(wakeupTime).getTime(); // return sampleTime >= bedtimeMs && sampleTime <= wakeupTimeMs; // }) // .map(sample => ({ // timestamp: sample.startDate, // value: Math.round(sample.value) // })) // .sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()); // console.log('[Sleep] 获取到睡眠心率数据:', heartRateData.length, '个样本'); // resolve(heartRateData); // }); // }); } catch (error) { console.error('[Sleep] 获取睡眠心率数据失败:', error); return []; } }; /** * 计算睡眠阶段统计 */ export const calculateSleepStageStats = (samples: SleepSample[]): SleepStageStats[] => { console.log('[Sleep] 开始计算睡眠阶段统计,原始样本数:', samples.length); const stageMap = new Map(); // 统计各阶段持续时间 samples.forEach(sample => { const startTime = dayjs(sample.startDate); const endTime = dayjs(sample.endDate); const duration = endTime.diff(startTime, 'minute'); console.log(`[Sleep] 阶段: ${sample.value}, 持续时间: ${duration}分钟`); const currentDuration = stageMap.get(sample.value) || 0; stageMap.set(sample.value, currentDuration + duration); }); console.log('[Sleep] 阶段时间统计:', Array.from(stageMap.entries())); // 计算实际睡眠时间(排除在床时间,但包含醒来时间) const actualSleepTime = Array.from(stageMap.entries()) .filter(([stage]) => stage !== SleepStage.InBed) .reduce((total, [, duration]) => total + duration, 0); console.log('[Sleep] 实际睡眠时间(包含醒来):', actualSleepTime, '分钟'); const stats: SleepStageStats[] = []; stageMap.forEach((duration, stage) => { // 不处理在床时间,但处理所有其他阶段包括醒来时间 if (stage === SleepStage.InBed) return; const percentage = actualSleepTime > 0 ? (duration / actualSleepTime) * 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; case SleepStage.Asleep: // 未分阶段的睡眠时间,按中等质量处理 quality = SleepQuality.Fair; break; case SleepStage.Awake: // 醒来时间越少越好 quality = percentage <= 5 ? SleepQuality.Excellent : percentage <= 10 ? SleepQuality.Good : percentage <= 15 ? SleepQuality.Fair : SleepQuality.Poor; break; default: quality = SleepQuality.Fair; } stats.push({ stage, duration, percentage: Math.round(percentage), quality }); console.log(`[Sleep] 阶段统计: ${stage}, 时长: ${duration}分钟, 百分比: ${Math.round(percentage)}%, 质量: ${quality}`); }); const sortedStats = stats.sort((a, b) => b.duration - a.duration); console.log('[Sleep] 最终睡眠阶段统计:', sortedStats); return sortedStats; }; /** * 计算睡眠得分 */ export const calculateSleepScore = ( sleepStages: SleepStageStats[], sleepEfficiency: number, totalSleepTime: number ): number => { let score = 0; // 睡眠时长得分 (30%) const idealSleepHours = 8 * 60; 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)); }; /** * 获取睡眠质量描述和建议 */ export const 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 const fetchCompleteSleepData = async (date: Date): Promise => { try { console.log('[Sleep] 开始获取完整睡眠数据...', dayjs(date).format('YYYY-MM-DD')); // 检查HealthKit是否可用 if (!HealthKitUtils.isAvailable()) { console.log('HealthKit不可用,可能运行在Android设备或模拟器上'); return null; } const res = await HealthKitManager.requestAuthorization() const status = await HealthKitManager.getAuthorizationStatus() console.log('status~~~', status); // await ensureHealthPermissions() // 获取睡眠样本 const sleepSamples = await fetchSleepSamples(date); if (sleepSamples.length === 0) { console.warn('[Sleep] 没有找到睡眠数据'); return null; } // 计算入睡和起床时间 const sortedSamples = sleepSamples.sort((a, b) => new Date(a.startDate).getTime() - new Date(b.startDate).getTime() ); const bedtime = sortedSamples[0].startDate; const wakeupTime = sortedSamples[sortedSamples.length - 1].endDate; console.log('[Sleep] 计算睡眠时间范围:'); console.log('- 入睡时间:', dayjs(bedtime).format('YYYY-MM-DD HH:mm:ss')); console.log('- 起床时间:', dayjs(wakeupTime).format('YYYY-MM-DD HH:mm:ss')); // 计算在床时间 let timeInBed: number; const inBedSamples = sleepSamples.filter(sample => sample.value === SleepStage.InBed); if (inBedSamples.length > 0) { const sortedInBedSamples = inBedSamples.sort((a, b) => new Date(a.startDate).getTime() - new Date(b.startDate).getTime() ); const inBedStart = sortedInBedSamples[0].startDate; const inBedEnd = sortedInBedSamples[sortedInBedSamples.length - 1].endDate; timeInBed = dayjs(inBedEnd).diff(dayjs(inBedStart), 'minute'); console.log('[Sleep] 在床时间:', timeInBed, '分钟'); } else { timeInBed = dayjs(wakeupTime).diff(dayjs(bedtime), 'minute'); console.log('[Sleep] 使用睡眠时间作为在床时间:', timeInBed, '分钟'); } // 计算睡眠阶段统计 const sleepStages = calculateSleepStageStats(sleepSamples); // 计算总睡眠时间(排除在床时间和醒来时间) const actualSleepStages = sleepStages.filter(stage => stage.stage !== SleepStage.InBed ); const totalSleepTime = actualSleepStages.reduce((total, stage) => total + stage.duration, 0); // 重新计算睡眠效率 const sleepEfficiency = timeInBed > 0 ? Math.round((totalSleepTime / timeInBed) * 100) : 0; console.log('[Sleep] 睡眠效率计算:'); console.log('- 总睡眠时间(不含醒来):', totalSleepTime, '分钟'); console.log('- 在床时间:', timeInBed, '分钟'); console.log('- 睡眠效率:', sleepEfficiency, '%'); // 获取睡眠期间心率数据 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); console.log('[Sleep] 睡眠数据处理完成:'); console.log('- 总睡眠时间:', totalSleepTime, '分钟'); console.log('- 睡眠效率:', sleepEfficiency, '%'); console.log('- 睡眠得分:', sleepScore); return { sleepScore, totalSleepTime, sleepQualityPercentage: sleepScore, bedtime, wakeupTime, timeInBed, sleepStages, rawSleepSamples: sleepSamples, averageHeartRate, sleepHeartRateData, sleepEfficiency, qualityDescription: qualityInfo.description, recommendation: qualityInfo.recommendation }; } catch (error) { console.error('[Sleep] 获取完整睡眠数据失败:', error); return null; } }; /** * 工具函数:格式化睡眠时间显示 */ export const 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`; } }; /** * 工具函数:格式化时间显示 */ export const formatTime = (dateString: string): string => { return dayjs(dateString).format('HH:mm'); }; /** * 工具函数:获取睡眠阶段颜色 */ export const getSleepStageColor = (stage: SleepStage): string => { switch (stage) { case SleepStage.Deep: return '#3B82F6'; case SleepStage.Core: return '#8B5CF6'; case SleepStage.REM: case SleepStage.Asleep: return '#EC4899'; case SleepStage.Awake: return '#F59E0B'; case SleepStage.InBed: return '#6B7280'; default: return '#9CA3AF'; } };