feat: 支持 healthkit

This commit is contained in:
richarjiang
2025-09-17 18:05:11 +08:00
parent 63ed820e93
commit 6b7776e51d
15 changed files with 1675 additions and 532 deletions

View File

@@ -1,6 +1,5 @@
import dayjs from 'dayjs';
import HealthKit from 'react-native-health';
import { ensureHealthPermissions } from './health';
import HealthKitManager, { HealthKitUtils } from './healthKit';
// 睡眠阶段枚举
export enum SleepStage {
@@ -116,54 +115,54 @@ export const fetchSleepSamples = async (date: Date): Promise<SleepSample[]> => {
endDate: dateRange.endDate.toISOString(),
};
return new Promise((resolve) => {
HealthKit.getSleepSamples(options, (error: string, results: any[]) => {
if (error) {
console.error('[Sleep] 获取睡眠数据失败:', error);
resolve([]); // 返回空数组而非拒绝,以便于处理
return;
}
// 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;
}
// if (!results || results.length === 0) {
// console.warn('[Sleep] 未找到睡眠数据');
// resolve([]);
// return;
// }
console.log('[Sleep] 获取到原始睡眠样本:', results.length, '条');
// 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();
// // 过滤并转换数据格式
// 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 (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
};
});
// 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);
});
});
// console.log('[Sleep] 过滤后的睡眠样本:', sleepSamples.length, '条');
// resolve(sleepSamples);
// });
// });
} catch (error) {
console.error('[Sleep] 获取睡眠样本失败:', error);
@@ -181,37 +180,37 @@ export const fetchSleepHeartRateData = async (bedtime: string, wakeupTime: strin
endDate: wakeupTime,
};
return new Promise((resolve) => {
HealthKit.getHeartRateSamples(options, (error: string, results: any[]) => {
if (error) {
console.error('[Sleep] 获取心率数据失败:', error);
resolve([]);
return;
}
// 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;
}
// 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());
// 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);
});
});
// console.log('[Sleep] 获取到睡眠心率数据:', heartRateData.length, '个样本');
// resolve(heartRateData);
// });
// });
} catch (error) {
console.error('[Sleep] 获取睡眠心率数据失败:', error);
@@ -369,8 +368,14 @@ export const getSleepQualityInfo = (sleepScore: number): { description: string;
export const fetchCompleteSleepData = async (date: Date): Promise<CompleteSleepData | null> => {
try {
console.log('[Sleep] 开始获取完整睡眠数据...', dayjs(date).format('YYYY-MM-DD'));
// 检查HealthKit是否可用
if (!HealthKitUtils.isAvailable()) {
console.log('HealthKit不可用可能运行在Android设备或模拟器上');
return null;
}
await ensureHealthPermissions()
await HealthKitManager.requestAuthorization()
// await ensureHealthPermissions()
// 获取睡眠样本
const sleepSamples = await fetchSleepSamples(date);