import dayjs from 'dayjs'; import type { HealthActivitySummary, HealthKitPermissions } from 'react-native-health'; import AppleHealthKit from 'react-native-health'; type HealthDataOptions = { startDate: string; endDate: string; }; 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); }); }); } // 日期工具函数 function createDateRange(date: Date): HealthDataOptions { return { startDate: dayjs(date).startOf('day').toDate().toISOString(), endDate: dayjs(date).endOf('day').toDate().toISOString() }; } // 睡眠时长计算 function calculateSleepDuration(samples: any[]): number { return samples.reduce((total, sample) => { if (sample && sample.startDate && sample.endDate) { const startTime = dayjs(sample.startDate).valueOf(); const endTime = dayjs(sample.endDate).valueOf(); const durationMinutes = (endTime - startTime) / (1000 * 60); return total + durationMinutes; } return total; }, 0); } // 通用错误处理 function logError(operation: string, error: any): void { console.error(`获取${operation}失败:`, error); } function logWarning(operation: string, message: string): void { console.warn(`${operation}数据${message}`); } function logSuccess(operation: string, data: any): void { console.log(`${operation}数据:`, data); } // 数值验证和转换 function validateOxygenSaturation(value: any): number | null { if (value === undefined || value === null) return null; let numValue = Number(value); // 如果值小于1,可能是小数形式(0.0-1.0),需要转换为百分比 if (numValue > 0 && numValue < 1) { numValue = numValue * 100; } // 血氧饱和度通常在0-100之间,验证数据有效性 if (numValue >= 0 && numValue <= 100) { return Number(numValue.toFixed(1)); } console.warn('血氧饱和度数据异常:', numValue); return null; } function validateHeartRate(value: any): number | null { if (value === undefined || value === null) return null; const numValue = Number(value); // 心率通常在30-200之间,验证数据有效性 if (numValue >= 30 && numValue <= 200) { return Math.round(numValue); } console.warn('心率数据异常:', numValue); return null; } // 健康数据获取函数 async function fetchStepCount(date: Date): Promise { return new Promise((resolve) => { AppleHealthKit.getStepCount({ date: dayjs(date).toDate().toISOString() }, (err, res) => { if (err) { logError('步数', err); return resolve(0); } if (!res) { logWarning('步数', '为空'); return resolve(0); } logSuccess('步数', res); resolve(res.value || 0); }); }); } async function fetchActiveEnergyBurned(options: HealthDataOptions): Promise { return new Promise((resolve) => { AppleHealthKit.getActiveEnergyBurned(options, (err, res) => { if (err) { logError('消耗卡路里', err); return resolve(0); } if (!res || !Array.isArray(res) || res.length === 0) { logWarning('卡路里', '为空或格式错误'); return resolve(0); } logSuccess('卡路里', res); const total = res.reduce((acc: number, item: any) => acc + (item?.value || 0), 0); resolve(total); }); }); } async function fetchBasalEnergyBurned(options: HealthDataOptions): Promise { return new Promise((resolve) => { AppleHealthKit.getBasalEnergyBurned(options, (err, res) => { if (err) { logError('基础代谢', err); return resolve(0); } if (!res || !Array.isArray(res) || res.length === 0) { logWarning('基础代谢', '为空或格式错误'); return resolve(0); } logSuccess('基础代谢', res); const total = res.reduce((acc: number, item: any) => acc + (item?.value || 0), 0); resolve(total); }); }); } async function fetchSleepDuration(options: HealthDataOptions): Promise { return new Promise((resolve) => { AppleHealthKit.getSleepSamples(options, (err, res) => { if (err) { logError('睡眠数据', err); return resolve(0); } if (!res || !Array.isArray(res) || res.length === 0) { logWarning('睡眠', '为空或格式错误'); return resolve(0); } logSuccess('睡眠', res); resolve(calculateSleepDuration(res)); }); }); } async function fetchHeartRateVariability(options: HealthDataOptions): Promise { return new Promise((resolve) => { AppleHealthKit.getHeartRateVariabilitySamples(options, (err, res) => { if (err) { logError('HRV数据', err); return resolve(null); } if (!res || !Array.isArray(res) || res.length === 0) { logWarning('HRV', '为空或格式错误'); return resolve(null); } logSuccess('HRV', res); const latestHrv = res[res.length - 1]; if (latestHrv && latestHrv.value) { resolve(Math.round(latestHrv.value * 1000)); } else { resolve(null); } }); }); } async function fetchActivitySummary(options: HealthDataOptions): Promise { return new Promise((resolve) => { AppleHealthKit.getActivitySummary( options, (err: Object, results: HealthActivitySummary[]) => { if (err) { logError('ActivitySummary', err); return resolve(null); } if (!results || results.length === 0) { logWarning('ActivitySummary', '为空'); return resolve(null); } logSuccess('ActivitySummary', results[0]); resolve(results[0]); }, ); }); } async function fetchOxygenSaturation(options: HealthDataOptions): Promise { return new Promise((resolve) => { AppleHealthKit.getOxygenSaturationSamples(options, (err, res) => { if (err) { logError('血氧饱和度', err); return resolve(null); } if (!res || !Array.isArray(res) || res.length === 0) { logWarning('血氧饱和度', '为空或格式错误'); return resolve(null); } logSuccess('血氧饱和度', res); const latestOxygen = res[res.length - 1]; return resolve(validateOxygenSaturation(latestOxygen?.value)); }); }); } async function fetchHeartRate(options: HealthDataOptions): Promise { return new Promise((resolve) => { AppleHealthKit.getHeartRateSamples(options, (err, res) => { if (err) { logError('心率', err); return resolve(null); } if (!res || !Array.isArray(res) || res.length === 0) { logWarning('心率', '为空或格式错误'); return resolve(null); } logSuccess('心率', res); const latestHeartRate = res[res.length - 1]; return resolve(validateHeartRate(latestHeartRate?.value)); }); }); } // 默认健康数据 function getDefaultHealthData(): TodayHealthData { return { steps: 0, activeEnergyBurned: 0, basalEnergyBurned: 0, sleepDuration: 0, hrv: null, activeCalories: 0, activeCaloriesGoal: 350, exerciseMinutes: 0, exerciseMinutesGoal: 30, standHours: 0, standHoursGoal: 12, oxygenSaturation: null, heartRate: null }; } export async function fetchHealthDataForDate(date: Date): Promise { try { console.log('开始获取指定日期健康数据...', date); const options = createDateRange(date); console.log('查询选项:', options); // 并行获取所有健康数据 const [ steps, activeEnergyBurned, basalEnergyBurned, sleepDuration, hrv, activitySummary, oxygenSaturation, heartRate ] = await Promise.all([ fetchStepCount(date), fetchActiveEnergyBurned(options), fetchBasalEnergyBurned(options), fetchSleepDuration(options), fetchHeartRateVariability(options), fetchActivitySummary(options), fetchOxygenSaturation(options), fetchHeartRate(options) ]); console.log('指定日期健康数据获取完成:', { steps, activeEnergyBurned, basalEnergyBurned, sleepDuration, hrv, activitySummary, oxygenSaturation, heartRate }); return { steps, activeEnergyBurned, basalEnergyBurned, sleepDuration, hrv, activeCalories: Math.round(activitySummary?.activeEnergyBurned || 0), activeCaloriesGoal: Math.round(activitySummary?.activeEnergyBurnedGoal || 350), exerciseMinutes: Math.round(activitySummary?.appleExerciseTime || 0), exerciseMinutesGoal: Math.round(activitySummary?.appleExerciseTimeGoal || 30), standHours: Math.round(activitySummary?.appleStandHours || 0), standHoursGoal: Math.round(activitySummary?.appleStandHoursGoal || 12), oxygenSaturation, heartRate }; } catch (error) { console.error('获取指定日期健康数据失败:', error); return getDefaultHealthData(); } } export async function fetchTodayHealthData(): Promise { return fetchHealthDataForDate(dayjs().toDate()); } export async function fetchHRVForDate(date: Date): Promise { console.log('开始获取指定日期HRV数据...', date); const options = createDateRange(date); return fetchHeartRateVariability(options); } export async function fetchTodayHRV(): Promise { return fetchHRVForDate(dayjs().toDate()); } // 更新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 = dayjs().toDate()): Promise { console.log('=== 开始测试血氧饱和度数据获取 ==='); const options = createDateRange(date); 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?.value !== undefined && latestOxygen?.value !== null) { const processedValue = validateOxygenSaturation(latestOxygen.value); console.log('处理前的值:', latestOxygen.value); console.log('最终处理后的值:', processedValue); console.log('数据有效性检查:', processedValue !== null ? '有效' : '无效'); } console.log('=== 血氧饱和度数据测试完成 ==='); resolve(); }); }); }