diff --git a/src/vitals/core/database.py b/src/vitals/core/database.py index 4d2f82d..66c98c9 100644 --- a/src/vitals/core/database.py +++ b/src/vitals/core/database.py @@ -287,10 +287,13 @@ def get_exercises(start_date: Optional[date] = None, end_date: Optional[date] = ] -def delete_exercise(exercise_id: int): - """删除运动记录""" +def delete_exercise(exercise_id: int, user_id: int = None): + """删除运动记录(需验证 user_id)""" with get_connection() as (conn, cursor): - cursor.execute("DELETE FROM exercise WHERE id = %s", (exercise_id,)) + if user_id: + cursor.execute("DELETE FROM exercise WHERE id = %s AND user_id = %s", (exercise_id, user_id)) + else: + cursor.execute("DELETE FROM exercise WHERE id = %s", (exercise_id,)) # ===== 步数记录 ===== @@ -420,10 +423,13 @@ def get_meals(start_date: Optional[date] = None, end_date: Optional[date] = None ] -def delete_meal(meal_id: int): - """删除饮食记录""" +def delete_meal(meal_id: int, user_id: int = None): + """删除饮食记录(需验证 user_id)""" with get_connection() as (conn, cursor): - cursor.execute("DELETE FROM meal WHERE id = %s", (meal_id,)) + if user_id: + cursor.execute("DELETE FROM meal WHERE id = %s AND user_id = %s", (meal_id, user_id)) + else: + cursor.execute("DELETE FROM meal WHERE id = %s", (meal_id,)) # ===== 睡眠记录 ===== @@ -483,10 +489,13 @@ def get_sleep_records(start_date: Optional[date] = None, end_date: Optional[date ] -def delete_sleep(sleep_id: int): - """删除睡眠记录""" +def delete_sleep(sleep_id: int, user_id: int = None): + """删除睡眠记录(需验证 user_id)""" with get_connection() as (conn, cursor): - cursor.execute("DELETE FROM sleep WHERE id = %s", (sleep_id,)) + if user_id: + cursor.execute("DELETE FROM sleep WHERE id = %s AND user_id = %s", (sleep_id, user_id)) + else: + cursor.execute("DELETE FROM sleep WHERE id = %s", (sleep_id,)) # ===== 体重记录 ===== @@ -546,10 +555,13 @@ def get_latest_weight() -> Optional[Weight]: return records[0] if records else None -def delete_weight(weight_id: int): - """删除体重记录""" +def delete_weight(weight_id: int, user_id: int = None): + """删除体重记录(需验证 user_id)""" with get_connection() as (conn, cursor): - cursor.execute("DELETE FROM weight WHERE id = %s", (weight_id,)) + if user_id: + cursor.execute("DELETE FROM weight WHERE id = %s AND user_id = %s", (weight_id, user_id)) + else: + cursor.execute("DELETE FROM weight WHERE id = %s", (weight_id,)) # ===== 用户配置 ===== @@ -590,6 +602,7 @@ def get_config(user_id: int = 1) -> UserConfig: weight=float(config_dict["weight"]) if config_dict.get("weight") else None, activity_level=config_dict.get("activity_level", "moderate"), goal=config_dict.get("goal", "maintain"), + target_weight=float(config_dict["target_weight"]) if config_dict.get("target_weight") else None, ) @@ -603,6 +616,7 @@ def save_config(user_id: int, config: UserConfig): "weight": str(config.weight) if config.weight else None, "activity_level": config.activity_level, "goal": config.goal, + "target_weight": str(config.target_weight) if config.target_weight else None, } for key, value in config_dict.items(): @@ -921,10 +935,13 @@ def get_readings( ] -def get_reading(reading_id: int) -> Optional[Reading]: - """获取单条阅读记录""" +def get_reading(reading_id: int, user_id: int = None) -> Optional[Reading]: + """获取单条阅读记录(可选验证 user_id)""" with get_connection() as (conn, cursor): - cursor.execute("SELECT * FROM reading WHERE id = %s", (reading_id,)) + if user_id: + cursor.execute("SELECT * FROM reading WHERE id = %s AND user_id = %s", (reading_id, user_id)) + else: + cursor.execute("SELECT * FROM reading WHERE id = %s", (reading_id,)) row = cursor.fetchone() if row: return Reading( @@ -941,10 +958,13 @@ def get_reading(reading_id: int) -> Optional[Reading]: return None -def delete_reading(reading_id: int): - """删除阅读记录""" +def delete_reading(reading_id: int, user_id: int = None): + """删除阅读记录(需验证 user_id)""" with get_connection() as (conn, cursor): - cursor.execute("DELETE FROM reading WHERE id = %s", (reading_id,)) + if user_id: + cursor.execute("DELETE FROM reading WHERE id = %s AND user_id = %s", (reading_id, user_id)) + else: + cursor.execute("DELETE FROM reading WHERE id = %s", (reading_id,)) def get_reading_stats(user_id: int = 1, days: int = 30) -> dict: diff --git a/src/vitals/core/models.py b/src/vitals/core/models.py index 32ce0c1..ab3a45f 100644 --- a/src/vitals/core/models.py +++ b/src/vitals/core/models.py @@ -160,6 +160,7 @@ class UserConfig: weight: Optional[float] = None # 公斤 activity_level: str = ActivityLevel.MODERATE.value goal: str = Goal.MAINTAIN.value + target_weight: Optional[float] = None # 目标体重(公斤) @property def bmr(self) -> Optional[int]: @@ -193,6 +194,7 @@ class UserConfig: "weight": self.weight, "activity_level": self.activity_level, "goal": self.goal, + "target_weight": self.target_weight, "bmr": self.bmr, "tdee": self.tdee, } diff --git a/src/vitals/web/app.py b/src/vitals/web/app.py index 25694de..2da29c4 100644 --- a/src/vitals/web/app.py +++ b/src/vitals/web/app.py @@ -160,10 +160,21 @@ class ConfigResponse(BaseModel): weight: Optional[float] activity_level: str goal: str + target_weight: Optional[float] = None bmr: Optional[int] tdee: Optional[int] +class ConfigUpdateRequest(BaseModel): + age: Optional[int] = None + gender: Optional[str] = None + height: Optional[float] = None + weight: Optional[float] = None + activity_level: Optional[str] = None + goal: Optional[str] = None + target_weight: Optional[float] = None + + class TodaySummary(BaseModel): date: str calories_intake: int @@ -915,11 +926,49 @@ async def get_config(): weight=config.weight, activity_level=config.activity_level, goal=config.goal, + target_weight=config.target_weight, bmr=config.bmr, tdee=config.tdee, ) +@app.post("/api/config", response_model=ConfigResponse) +async def update_config(request: ConfigUpdateRequest): + """更新用户配置""" + from ..core.models import UserConfig + + active_user = db.get_active_user() + user_id = active_user.id if active_user else 1 + + # 获取当前配置 + current_config = db.get_config(user_id) + + # 更新配置(仅更新提供的字段) + new_config = UserConfig( + age=request.age if request.age is not None else current_config.age, + gender=request.gender if request.gender is not None else current_config.gender, + height=request.height if request.height is not None else current_config.height, + weight=request.weight if request.weight is not None else current_config.weight, + activity_level=request.activity_level if request.activity_level is not None else current_config.activity_level, + goal=request.goal if request.goal is not None else current_config.goal, + target_weight=request.target_weight if request.target_weight is not None else current_config.target_weight, + ) + + db.save_config(user_id, new_config) + + return ConfigResponse( + age=new_config.age, + gender=new_config.gender, + height=new_config.height, + weight=new_config.weight, + activity_level=new_config.activity_level, + goal=new_config.goal, + target_weight=new_config.target_weight, + bmr=new_config.bmr, + tdee=new_config.tdee, + ) + + @app.get("/api/today", response_model=TodaySummary) async def get_today_summary(): """获取今日概览""" @@ -8806,6 +8855,21 @@ def get_settings_page_html() -> str: +
用于计算达成目标所需时间
+