Files
digital-pilates/utils/health.ts
richarjiang c12329bc96 feat: 移除目标管理演示页面并优化相关组件
- 删除目标管理演示页面的代码,简化项目结构
- 更新底部导航,移除目标管理演示页面的路由
- 调整相关组件的样式和逻辑,确保界面一致性
- 优化颜色常量的使用,提升视觉效果
2025-08-22 21:24:31 +08:00

349 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<boolean> {
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<TodayHealthData> {
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<number>((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<number>((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<number>((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<number>((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<number | null>((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<HealthActivitySummary | null>((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<number | null>((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) {
// 血氧饱和度通常在0-100之间验证数据有效性
const value = Number(latestOxygen.value);
if (value >= 0 && value <= 100) {
resolve(Number(value.toFixed(1)));
} else {
console.warn('血氧饱和度数据异常:', value);
resolve(null);
}
} else {
resolve(null);
}
});
}),
// 获取心率数据
new Promise<number | null>((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<TodayHealthData> {
return fetchHealthDataForDate(new Date());
}
// 新增专门获取HRV数据的函数
export async function fetchHRVForDate(date: Date): Promise<number | null> {
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<number | null>((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<number | null> {
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);
});
});
}