196 lines
6.0 KiB
TypeScript
196 lines
6.0 KiB
TypeScript
import type { 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.SleepAnalysis,
|
||
AppleHealthKit.Constants.Permissions.HeartRateVariability,
|
||
],
|
||
write: [],
|
||
},
|
||
};
|
||
|
||
export type TodayHealthData = {
|
||
steps: number;
|
||
activeEnergyBurned: number; // kilocalories
|
||
sleepDuration: number; // 睡眠时长(分钟)
|
||
hrv: number | null; // 心率变异性 (ms)
|
||
};
|
||
|
||
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 = 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;
|
||
|
||
console.log('查询选项:', options);
|
||
|
||
// 并行获取所有健康数据
|
||
const [steps, calories, sleepDuration, hrv] = await Promise.all([
|
||
// 获取步数
|
||
new Promise<number>((resolve) => {
|
||
AppleHealthKit.getStepCount(options, (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.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(latestHrv.value);
|
||
} else {
|
||
resolve(null);
|
||
}
|
||
});
|
||
})
|
||
]);
|
||
|
||
console.log('指定日期健康数据获取完成:', { steps, calories, sleepDuration, hrv });
|
||
return { steps, activeEnergyBurned: calories, sleepDuration, hrv };
|
||
}
|
||
|
||
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());
|
||
} |