feat(notifications): 新增晚餐和心情提醒功能,支持HRV压力检测和后台处理

- 新增晚餐提醒(18:00)和心情提醒(21:00)的定时通知
- 实现基于HRV数据的压力检测和智能鼓励通知
- 添加后台任务处理支持,修改iOS后台模式为processing
- 优化营养记录页面使用Redux状态管理,支持实时数据更新
- 重构卡路里计算公式,移除目标卡路里概念,改为基代+运动-饮食
- 新增营养目标动态计算功能,基于用户身体数据智能推荐
- 完善通知点击跳转逻辑,支持多种提醒类型的路由处理
This commit is contained in:
richarjiang
2025-09-01 10:29:13 +08:00
parent fe634ba258
commit a34ca556e8
12 changed files with 867 additions and 189 deletions

78
utils/nutrition.ts Normal file
View File

@@ -0,0 +1,78 @@
import dayjs from 'dayjs';
export interface NutritionGoals {
calories: number;
proteinGoal: number;
fatGoal: number;
carbsGoal: number;
fiberGoal: number;
sodiumGoal: number;
}
export interface UserProfileForNutrition {
weight?: string;
height?: string;
birthDate?: Date;
gender?: 'male' | 'female';
}
/**
* 计算用户的营养目标
* 基于Mifflin-St Jeor公式计算基础代谢率然后计算各营养素目标
*/
export const calculateNutritionGoals = (userProfile?: UserProfileForNutrition): NutritionGoals => {
const weight = parseFloat(userProfile?.weight || '70'); // 默认70kg
const height = parseFloat(userProfile?.height || '170'); // 默认170cm
const age = userProfile?.birthDate ?
dayjs().diff(dayjs(userProfile.birthDate), 'year') : 25; // 默认25岁
const isWoman = userProfile?.gender === 'female';
// 基础代谢率计算Mifflin-St Jeor Equation
let bmr;
if (isWoman) {
bmr = 10 * weight + 6.25 * height - 5 * age - 161;
} else {
bmr = 10 * weight + 6.25 * height - 5 * age + 5;
}
// 总热量需求(假设轻度活动)
const totalCalories = bmr * 1.375;
// 计算营养素目标
const proteinGoal = weight * 1.6; // 1.6g/kg
const fatGoal = totalCalories * 0.25 / 9; // 25%来自脂肪9卡/克
const carbsGoal = (totalCalories - proteinGoal * 4 - fatGoal * 9) / 4; // 剩余来自碳水
// 纤维目标成人推荐25-35g/天
const fiberGoal = 25;
// 钠目标WHO推荐<2300mg/天
const sodiumGoal = 2300;
return {
calories: Math.round(totalCalories),
proteinGoal: Math.round(proteinGoal * 10) / 10,
fatGoal: Math.round(fatGoal * 10) / 10,
carbsGoal: Math.round(carbsGoal * 10) / 10,
fiberGoal,
sodiumGoal,
};
};
/**
* 计算剩余可摄入卡路里
* 公式:还能吃 = 基础代谢 + 运动消耗 - 已摄入饮食
*/
export const calculateRemainingCalories = (params: {
basalMetabolism: number;
activeCalories: number;
consumedCalories: number;
}): number => {
const { basalMetabolism, activeCalories, consumedCalories } = params;
// 总消耗 = 基础代谢 + 运动消耗
const totalBurned = basalMetabolism + activeCalories;
// 剩余可摄入 = 总消耗 - 已摄入
return totalBurned - consumedCalories;
};