From 211c2c2c4ed82e68211db69cc59fe9f296599cfd Mon Sep 17 00:00:00 2001 From: "liweiliang0905@gmail.com" Date: Tue, 27 Jan 2026 16:22:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(api):=20=E6=B7=BB=E5=8A=A0=E7=9D=A1?= =?UTF-8?q?=E7=9C=A0=E8=AF=84=E4=BC=B0=E7=AB=AF=E7=82=B9=20/api/sleep/asse?= =?UTF-8?q?ssment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.5 --- src/vitals/web/app.py | 141 ++++++++++++++++++++++++++++++++++++++++++ tests/test_web.py | 11 ++++ 2 files changed, 152 insertions(+) diff --git a/src/vitals/web/app.py b/src/vitals/web/app.py index 7631a14..3d5c79d 100644 --- a/src/vitals/web/app.py +++ b/src/vitals/web/app.py @@ -1377,6 +1377,147 @@ async def get_nutrition_recommendations(): } +@app.get("/api/sleep/assessment") +async def get_sleep_assessment(): + """获取睡眠评估数据""" + active_user = db.get_active_user() + if not active_user: + raise HTTPException(status_code=400, detail="没有激活的用户") + + today = date.today() + week_start = today - timedelta(days=6) + month_start = today - timedelta(days=29) + + # 获取近 7 天和近 30 天睡眠数据 + week_records = db.get_sleep_records(start_date=week_start, end_date=today, user_id=active_user.id) + month_records = db.get_sleep_records(start_date=month_start, end_date=today, user_id=active_user.id) + + # 计算平均睡眠时长 + week_durations = [r.duration for r in week_records] + month_durations = [r.duration for r in month_records] + + avg_week = sum(week_durations) / len(week_durations) if week_durations else 0 + avg_month = sum(month_durations) / len(month_durations) if month_durations else 0 + + # 判断睡眠状态 + def get_sleep_status(avg_hours): + if avg_hours < 5: + return { + "level": "severe", + "label": "严重不足", + "color": "#EF4444", + "icon": "alert-circle" + } + elif avg_hours < 6: + return { + "level": "insufficient", + "label": "睡眠不足", + "color": "#F59E0B", + "icon": "alert-triangle" + } + elif avg_hours <= 9: + return { + "level": "ideal", + "label": "睡眠充足", + "color": "#10B981", + "icon": "check-circle" + } + else: + return { + "level": "excessive", + "label": "睡眠过多", + "color": "#3B82F6", + "icon": "info" + } + + status = get_sleep_status(avg_week) + + # 统计理想天数 + ideal_days = sum(1 for d in week_durations if 7 <= d <= 9) + insufficient_days = sum(1 for d in week_durations if d < 7) + + month_ideal_days = sum(1 for d in month_durations if 7 <= d <= 9) + month_insufficient_days = sum(1 for d in month_durations if d < 7) + + # 健康影响信息 + warning_impacts = { + "cognitive": { + "title": "认知能力", + "effects": ["注意力下降 40%", "记忆力减退", "决策能力受损"] + }, + "physical": { + "title": "身体健康", + "effects": ["免疫力下降", "肥胖风险增加 33%", "心血管疾病风险上升"] + }, + "emotional": { + "title": "情绪状态", + "effects": ["焦虑抑郁风险增加", "情绪波动大", "压力耐受力降低"] + }, + "exercise": { + "title": "运动表现", + "effects": ["反应速度下降", "肌肉恢复减慢", "受伤风险增加"] + } + } + + benefit_impacts = { + "cognitive": { + "title": "认知提升", + "effects": ["记忆巩固增强", "专注力提高", "创造力活跃"] + }, + "physical": { + "title": "身体修复", + "effects": ["免疫系统强化", "肌肉组织修复", "激素分泌平衡"] + }, + "emotional": { + "title": "情绪稳定", + "effects": ["情绪调节能力增强", "压力抵抗力提升", "心态积极"] + }, + "exercise": { + "title": "运动增益", + "effects": ["运动表现提升 15%", "恢复速度加快", "耐力增强"] + } + } + + health_impacts = warning_impacts if status["level"] in ["severe", "insufficient"] else benefit_impacts + + # 建议 + if status["level"] == "severe": + suggestion = "建议立即调整作息,每天提前 1 小时入睡" + elif status["level"] == "insufficient": + suggestion = "尝试提前 30 分钟入睡,逐步调整作息" + elif status["level"] == "ideal": + suggestion = "继续保持,规律作息是健康的基石!" + else: + suggestion = "睡眠时间过长也可能影响健康,建议控制在 7-9 小时" + + # 距理想的差距 + ideal_target = 7.5 + gap_hours = round(ideal_target - avg_week, 1) if avg_week < 7 else 0 + + return { + "avg_duration": round(avg_week, 1), + "avg_duration_month": round(avg_month, 1), + "status": status, + "gap_hours": gap_hours, + "ideal_days_count": ideal_days, + "insufficient_days_count": insufficient_days, + "total_days": len(week_durations), + "month_stats": { + "ideal_days": month_ideal_days, + "insufficient_days": month_insufficient_days, + "total_days": len(month_durations) + }, + "health_impacts": health_impacts, + "suggestion": suggestion, + "thresholds": { + "severe": 5, + "insufficient": 6, + "ideal_min": 7, + "ideal_max": 9 + } + } + + @app.get("/api/meals", response_model=list[MealResponse]) async def get_meals( days: int = Query(default=30, ge=1, le=365, description="查询天数"), diff --git a/tests/test_web.py b/tests/test_web.py index bdefc69..c65f459 100644 --- a/tests/test_web.py +++ b/tests/test_web.py @@ -328,3 +328,14 @@ def test_nutrition_recommendations_endpoint(client): assert "today_intake" in data assert "gaps" in data assert "suggestions" in data + + +def test_sleep_assessment_endpoint(client): + """测试睡眠评估 API""" + response = client.get("/api/sleep/assessment") + assert response.status_code == 200 + data = response.json() + assert "avg_duration" in data + assert "status" in data + assert "health_impacts" in data + assert "ideal_days_count" in data \ No newline at end of file