feat(health): 重构营养卡片数据获取逻辑,支持基础代谢与运动消耗分离

- 新增 fetchCompleteNutritionCardData 异步 action,统一拉取营养、健康与基础代谢数据
- NutritionRadarCard 改用 Redux 数据源,移除 props 透传,自动根据日期刷新
- BasalMetabolismCard 新增详情弹窗,展示 BMR 计算公式、正常区间及提升策略
- StepsCard 与 StepsCardOptimized 引入 InteractionManager 与动画懒加载,减少 UI 阻塞
- HealthKitManager 新增饮水读写接口,支持将饮水记录同步至 HealthKit
- 移除 statistics 页面冗余 mock 与 nutrition/health 重复请求,缓存时间统一为 5 分钟
This commit is contained in:
richarjiang
2025-09-23 10:01:50 +08:00
parent d082c66b72
commit e6dfd4d59a
11 changed files with 1115 additions and 203 deletions

View File

@@ -309,7 +309,7 @@ export async function fetchStepCount(date: Date): Promise<number> {
}
// 使用样本数据获取每小时步数
// 使用样本数据获取每小时步数 - 优化版本,减少计算复杂度
export async function fetchHourlyStepSamples(date: Date): Promise<HourlyStepData[]> {
try {
const options = createDateRange(date);
@@ -318,22 +318,25 @@ export async function fetchHourlyStepSamples(date: Date): Promise<HourlyStepData
if (result && result.data && Array.isArray(result.data)) {
logSuccess('每小时步数样本', result);
// 初始化24小时数据
const hourlyData: HourlyStepData[] = Array.from({ length: 24 }, (_, i) => ({
hour: i,
steps: 0
}));
// 优化:使用更高效的数据结构
const hourlyMap = new Map<number, number>();
// 将每小时的步数样本数据映射到对应的小时
// 优化:批量处理数据,减少重复验证
result.data.forEach((sample: any) => {
if (sample && sample.hour !== undefined && sample.value !== undefined) {
const hour = sample.hour;
if (hour >= 0 && hour < 24) {
hourlyData[hour].steps = Math.round(sample.value);
}
if (sample?.hour >= 0 && sample?.hour < 24 && sample?.value !== undefined) {
hourlyMap.set(sample.hour, Math.round(sample.value));
}
});
// 生成最终数组
const hourlyData: HourlyStepData[] = [];
for (let i = 0; i < 24; i++) {
hourlyData.push({
hour: i,
steps: hourlyMap.get(i) || 0
});
}
return hourlyData;
} else {
logWarning('每小时步数', '为空或格式错误');
@@ -467,7 +470,7 @@ async function fetchActiveEnergyBurned(options: HealthDataOptions): Promise<numb
}
}
async function fetchBasalEnergyBurned(options: HealthDataOptions): Promise<number> {
export async function fetchBasalEnergyBurned(options: HealthDataOptions): Promise<number> {
try {
const result = await HealthKitManager.getBasalEnergyBurned(options);
@@ -765,24 +768,45 @@ export async function testOxygenSaturationData(_date: Date = dayjs().toDate()):
}
}
// 添加饮水记录到 HealthKit (暂未实现)
export async function saveWaterIntakeToHealthKit(_amount: number, _recordedAt?: string): Promise<boolean> {
// 添加饮水记录到 HealthKit
export async function saveWaterIntakeToHealthKit(amount: number, recordedAt?: string): Promise<boolean> {
try {
// Note: Water intake saving would need to be implemented in native module
console.log('饮水记录保存到HealthKit暂未实现');
return true; // Return true for now to not break existing functionality
console.log('开始保存饮水记录到HealthKit...', { amount, recordedAt });
const options = {
amount: amount,
recordedAt: recordedAt || new Date().toISOString()
};
const result = await HealthKitManager.saveWaterIntakeToHealthKit(options);
if (result && result.success) {
console.log('饮水记录保存成功:', result);
return true;
} else {
console.error('饮水记录保存失败:', result);
return false;
}
} catch (error) {
console.error('添加饮水记录到 HealthKit 失败:', error);
return false;
}
}
// 获取 HealthKit 中的饮水记录 (暂未实现)
export async function getWaterIntakeFromHealthKit(_options: HealthDataOptions): Promise<any[]> {
// 获取 HealthKit 中的饮水记录
export async function getWaterIntakeFromHealthKit(options: HealthDataOptions): Promise<any[]> {
try {
// Note: Water intake fetching would need to be implemented in native module
console.log('从HealthKit获取饮水记录暂未实现');
return [];
console.log('开始从HealthKit获取饮水记录...', options);
const result = await HealthKitManager.getWaterIntakeFromHealthKit(options);
if (result && result.data && Array.isArray(result.data)) {
console.log('成功获取饮水记录:', result);
return result.data;
} else {
console.log('饮水记录为空或格式错误:', result);
return [];
}
} catch (error) {
console.error('获取 HealthKit 饮水记录失败:', error);
return [];