import dayjs from 'dayjs'; import type { HealthActivitySummary, HealthKitPermissions } from 'react-native-health'; import AppleHealthKit from 'react-native-health'; const PERMISSIONS: HealthKitPermissions = { permissions: { read: [ AppleHealthKit.Constants.Permissions.StepCount, AppleHealthKit.Constants.Permissions.ActiveEnergyBurned, AppleHealthKit.Constants.Permissions.BasalEnergyBurned, AppleHealthKit.Constants.Permissions.SleepAnalysis, AppleHealthKit.Constants.Permissions.HeartRateVariability, AppleHealthKit.Constants.Permissions.ActivitySummary, AppleHealthKit.Constants.Permissions.OxygenSaturation, AppleHealthKit.Constants.Permissions.HeartRate, ], write: [ // 支持体重写入 AppleHealthKit.Constants.Permissions.Weight, ], }, }; export type TodayHealthData = { steps: number; activeEnergyBurned: number; // kilocalories basalEnergyBurned: number; // kilocalories - 基础代谢率 sleepDuration: number; // 睡眠时长(分钟) hrv: number | null; // 心率变异性 (ms) // 健身圆环数据 activeCalories: number; activeCaloriesGoal: number; exerciseMinutes: number; exerciseMinutesGoal: number; standHours: number; standHoursGoal: number; // 新增血氧饱和度和心率数据 oxygenSaturation: number | null; heartRate: number | null; }; export async function ensureHealthPermissions(): Promise { return new Promise((resolve) => { console.log('开始初始化HealthKit...'); AppleHealthKit.initHealthKit(PERMISSIONS, (error) => { if (error) { console.error('HealthKit初始化失败:', error); // 常见错误处理 if (typeof error === 'string') { if (error.includes('not available')) { console.warn('HealthKit不可用 - 可能在模拟器上运行或非iOS设备'); } } resolve(false); return; } console.log('HealthKit初始化成功'); resolve(true); }); }); } export async function fetchHealthDataForDate(date: Date): Promise { console.log('开始获取指定日期健康数据...', date); const start = dayjs(date).startOf('day').toDate(); const end = dayjs(date).endOf('day').toDate(); const options = { startDate: start.toISOString(), endDate: end.toISOString() } as any; const activitySummaryOptions = { startDate: start.toISOString(), endDate: end.toISOString() }; console.log('查询选项:', options); // 并行获取所有健康数据,包括ActivitySummary、血氧饱和度和心率 const [steps, calories, basalMetabolism, sleepDuration, hrv, activitySummary, oxygenSaturation, heartRate] = await Promise.all([ // 获取步数 new Promise((resolve) => { AppleHealthKit.getStepCount({ date: dayjs(date).toISOString() }, (err, res) => { if (err) { console.error('获取步数失败:', err); return resolve(0); } if (!res) { console.warn('步数数据为空'); return resolve(0); } console.log('步数数据:', res); resolve(res.value || 0); }); }), // 获取消耗卡路里 new Promise((resolve) => { AppleHealthKit.getActiveEnergyBurned(options, (err, res) => { if (err) { console.error('获取消耗卡路里失败:', err); return resolve(0); } if (!res || !Array.isArray(res) || res.length === 0) { console.warn('卡路里数据为空或格式错误'); return resolve(0); } console.log('卡路里数据:', res); // 求和该日内的所有记录(单位:千卡) const total = res.reduce((acc: number, item: any) => acc + (item?.value || 0), 0); resolve(total); }); }), // 获取基础代谢率 new Promise((resolve) => { AppleHealthKit.getBasalEnergyBurned(options, (err, res) => { if (err) { console.error('获取基础代谢失败:', err); return resolve(0); } if (!res || !Array.isArray(res) || res.length === 0) { console.warn('基础代谢数据为空或格式错误'); return resolve(0); } console.log('基础代谢数据:', res); // 求和该日内的所有记录(单位:千卡) const total = res.reduce((acc: number, item: any) => acc + (item?.value || 0), 0); resolve(total); }); }), // 获取睡眠时长 new Promise((resolve) => { AppleHealthKit.getSleepSamples(options, (err, res) => { if (err) { console.error('获取睡眠数据失败:', err); return resolve(0); } if (!res || !Array.isArray(res) || res.length === 0) { console.warn('睡眠数据为空或格式错误'); return resolve(0); } console.log('睡眠数据:', res); // 计算总睡眠时间(单位:分钟) let totalSleepDuration = 0; res.forEach((sample: any) => { if (sample && sample.startDate && sample.endDate) { const startTime = new Date(sample.startDate).getTime(); const endTime = new Date(sample.endDate).getTime(); const durationMinutes = (endTime - startTime) / (1000 * 60); totalSleepDuration += durationMinutes; } }); resolve(totalSleepDuration); }); }), // 获取HRV数据 new Promise((resolve) => { AppleHealthKit.getHeartRateVariabilitySamples(options, (err, res) => { if (err) { console.error('获取HRV数据失败:', err); return resolve(null); } if (!res || !Array.isArray(res) || res.length === 0) { console.warn('HRV数据为空或格式错误'); return resolve(null); } console.log('HRV数据:', res); // 获取最新的HRV值 const latestHrv = res[res.length - 1]; if (latestHrv && latestHrv.value) { resolve(Math.round(latestHrv.value * 1000)); } else { resolve(null); } }); }), // 获取ActivitySummary数据(健身圆环数据) new Promise((resolve) => { AppleHealthKit.getActivitySummary( activitySummaryOptions, (err: Object, results: HealthActivitySummary[]) => { if (err) { console.error('获取ActivitySummary失败:', err); return resolve(null); } if (!results || results.length === 0) { console.warn('ActivitySummary数据为空'); return resolve(null); } console.log('ActivitySummary数据:', results[0]); resolve(results[0]); }, ); }), // 获取血氧饱和度数据 new Promise((resolve) => { AppleHealthKit.getOxygenSaturationSamples(options, (err, res) => { if (err) { console.error('获取血氧饱和度失败:', err); return resolve(null); } if (!res || !Array.isArray(res) || res.length === 0) { console.warn('血氧饱和度数据为空或格式错误'); return resolve(null); } console.log('血氧饱和度数据:', res); // 获取最新的血氧饱和度值 const latestOxygen = res[res.length - 1]; if (latestOxygen && latestOxygen.value !== undefined && latestOxygen.value !== null) { let value = Number(latestOxygen.value); // 检查数据格式:如果值小于1,可能是小数形式(0.0-1.0),需要转换为百分比 if (value > 0 && value < 1) { value = value * 100; console.log('血氧饱和度数据从小数转换为百分比:', latestOxygen.value, '->', value); } // 血氧饱和度通常在0-100之间,验证数据有效性 if (value >= 0 && value <= 100) { resolve(Number(value.toFixed(1))); } else { console.warn('血氧饱和度数据异常:', value); resolve(null); } } else { resolve(null); } }); }), // 获取心率数据 new Promise((resolve) => { AppleHealthKit.getHeartRateSamples(options, (err, res) => { if (err) { console.error('获取心率失败:', err); return resolve(null); } if (!res || !Array.isArray(res) || res.length === 0) { console.warn('心率数据为空或格式错误'); return resolve(null); } console.log('心率数据:', res); // 获取最新的心率值 const latestHeartRate = res[res.length - 1]; if (latestHeartRate && latestHeartRate.value !== undefined && latestHeartRate.value !== null) { // 心率通常在30-200之间,验证数据有效性 const value = Number(latestHeartRate.value); if (value >= 30 && value <= 200) { resolve(Math.round(value)); } else { console.warn('心率数据异常:', value); resolve(null); } } else { resolve(null); } }); }) ]); console.log('指定日期健康数据获取完成:', { steps, calories, basalMetabolism, sleepDuration, hrv, activitySummary, oxygenSaturation, heartRate }); return { steps, activeEnergyBurned: calories, basalEnergyBurned: basalMetabolism, sleepDuration, hrv, // 健身圆环数据 activeCalories: activitySummary?.activeEnergyBurned || 0, activeCaloriesGoal: activitySummary?.activeEnergyBurnedGoal || 350, exerciseMinutes: activitySummary?.appleExerciseTime || 0, exerciseMinutesGoal: activitySummary?.appleExerciseTimeGoal || 30, standHours: activitySummary?.appleStandHours || 0, standHoursGoal: activitySummary?.appleStandHoursGoal || 12, // 血氧饱和度和心率数据 oxygenSaturation, heartRate }; } export async function fetchTodayHealthData(): Promise { return fetchHealthDataForDate(new Date()); } // 新增:专门获取HRV数据的函数 export async function fetchHRVForDate(date: Date): Promise { console.log('开始获取指定日期HRV数据...', date); const start = new Date(date); start.setHours(0, 0, 0, 0); const end = new Date(date); end.setHours(23, 59, 59, 999); const options = { startDate: start.toISOString(), endDate: end.toISOString() } as any; return new Promise((resolve) => { AppleHealthKit.getHeartRateVariabilitySamples(options, (err, res) => { if (err) { console.error('获取HRV数据失败:', err); return resolve(null); } if (!res || !Array.isArray(res) || res.length === 0) { console.warn('HRV数据为空或格式错误'); return resolve(null); } console.log('HRV数据:', res); // 获取最新的HRV值 const latestHrv = res[res.length - 1]; if (latestHrv && latestHrv.value) { resolve(latestHrv.value); } else { resolve(null); } }); }); } // 新增:获取今日HRV数据 export async function fetchTodayHRV(): Promise { return fetchHRVForDate(new Date()); } // 更新healthkit中的体重 export async function updateWeight(weight: number) { return new Promise((resolve) => { AppleHealthKit.saveWeight({ value: weight, }, (err, res) => { if (err) { console.error('更新体重失败:', err); return resolve(false); } console.log('体重更新成功:', res); resolve(true); }); }); } // 新增:测试血氧饱和度数据获取 export async function testOxygenSaturationData(date: Date = new Date()): Promise { console.log('=== 开始测试血氧饱和度数据获取 ==='); const start = dayjs(date).startOf('day').toDate(); const end = dayjs(date).endOf('day').toDate(); const options = { startDate: start.toISOString(), endDate: end.toISOString() } as any; return new Promise((resolve) => { AppleHealthKit.getOxygenSaturationSamples(options, (err, res) => { if (err) { console.error('获取血氧饱和度失败:', err); resolve(); return; } console.log('原始血氧饱和度数据:', res); if (!res || !Array.isArray(res) || res.length === 0) { console.warn('血氧饱和度数据为空'); resolve(); return; } // 分析所有数据样本 res.forEach((sample, index) => { console.log(`样本 ${index + 1}:`, { value: sample.value, valueType: typeof sample.value, startDate: sample.startDate, endDate: sample.endDate }); }); // 获取最新的血氧饱和度值 const latestOxygen = res[res.length - 1]; if (latestOxygen && latestOxygen.value !== undefined && latestOxygen.value !== null) { let value = Number(latestOxygen.value); console.log('处理前的值:', latestOxygen.value); console.log('转换为数字后的值:', value); // 检查数据格式:如果值小于1,可能是小数形式(0.0-1.0),需要转换为百分比 if (value > 0 && value < 1) { const originalValue = value; value = value * 100; console.log('血氧饱和度数据从小数转换为百分比:', originalValue, '->', value); } console.log('最终处理后的值:', value); console.log('数据有效性检查:', value >= 0 && value <= 100 ? '有效' : '无效'); } console.log('=== 血氧饱和度数据测试完成 ==='); resolve(); }); }); }