feat(notifications): 新增晚餐和心情提醒功能,支持HRV压力检测和后台处理
- 新增晚餐提醒(18:00)和心情提醒(21:00)的定时通知 - 实现基于HRV数据的压力检测和智能鼓励通知 - 添加后台任务处理支持,修改iOS后台模式为processing - 优化营养记录页面使用Redux状态管理,支持实时数据更新 - 重构卡路里计算公式,移除目标卡路里概念,改为基代+运动-饮食 - 新增营养目标动态计算功能,基于用户身体数据智能推荐 - 完善通知点击跳转逻辑,支持多种提醒类型的路由处理
This commit is contained in:
78
utils/nutrition.ts
Normal file
78
utils/nutrition.ts
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user