ericwyuan
|
6de7562cd8
|
[阶段5.1] 开机自启 + 接入真实 garminconnect,修复同步的三处 API 误用
fix(garmin): 同步代码调用的 garminconnect API 全是错的
装上库核对签名后发现(garminconnect 0.2.8),原代码从未被真正
执行过,三处调用都不成立:
1. get_activities(date) —— 该方法签名是 (start, limit),收的是
分页下标和条数,不是日期。传日期等于在问"第 2026-08-23 条运动",
而且少传一个参数必然 TypeError。
改为 get_activities_by_date(start, end),整个窗口一次取回,
顺带把原来"每天一次调用"减为一次。
2. 睡眠数据不在 get_user_summary 里,是独立的 get_sleep_data(cdate);
原代码从 summary 里读 sleep 字段,结果会把每一晚都记成无睡眠数据。
HRV 同理,走 get_hrv_data(cdate)。
3. 字段名不符:实际是 totalSteps / totalKilocalories /
averageStressLevel,原代码用的是 steps / calories.total /
stress.average。
其他改进:
- 运动记录改用 Garmin 自己的 activityId 作主键,重复同步同一窗口
不再产生重复行(原来每次同步都会把同一条运动再插一遍)
- 某天无数据时返回全 None,不再写入空行让读端点再过滤掉
- 原来 bare except 吞掉每天的异常,全部失败也报 success;
现在全窗口失败会如实返回 error 并记录原因
- 同步天数、是否走 Garmin 中国区改为环境变量可配
tests/test_garmin_sync.py (23 通过):
- 用 StubClient 模拟真实 0.2.8 的接口形状,无需库、凭证或网络
- 回归用例覆盖上述三处误用:活动必须按日期区间一次取回、
睡眠与 HRV 必须走各自端点
- 重复同步不产生重复的天和重复的运动记录
- 单天失败跳过、全窗口失败报错、登录失败如实上报
部署:
- NAS 安装 garminconnect(pydantic-core 有 cp38 x86_64 轮子,
无需 gcc)
- 新增 deploy/S99garmin.sh 开机自启脚本,与 NAS 上既有的
S99frpc.sh 同一套惯例;以 root 启动但降权到 ericwyuan 运行,
因为服务不需要特权而 .env 里有数据库和 API 凭证
- PID 文件放应用目录而非 /var/run(非特权用户写不了)
NAS 真机全量测试: 240 passed
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
|
2026-08-23 19:05:19 +08:00 |
|
ericwyuan
|
c83340742c
|
[阶段4.1] AI 健康建议 - 多模型可切换 + 大上下文 + 失败兜底
services/ai.py:
- 模型目录(catalog)按短 id 索引,业务代码不感知厂商
gemini-flash (Google, 1M 上下文)
llama-70b / qwen-72b / deepseek-r1 (NVIDIA NIM, 128k)
仅注册纯文本模型,不含视觉模型
- 两个 provider: GeminiProvider、OpenAICompatProvider
(后者兼容 NVIDIA NIM / Ollama / vLLM)
- 大上下文: 每日指标序列化为 CSV 而非 JSON,同样的数据 token 数约为
1/4,一整年历史仍远小于最小的 128k 窗口;按 AI_DAY_BUDGET 截断
- 兜底链: 首选模型超时/报错/返回无法解析的文本时自动降级到下一个,
meta.fallbackFrom 记录降级路径
- 响应解析容忍 markdown 代码块包裹和 JSON 前的多余句子
services/analysis.py:
- get_ai_recommendations(): 所有模型都失败时回落到规则引擎,
端点始终 200,meta.source 区分 ai / rules
routes/analysis.py:
- GET /api/analysis/models 列出模型及各自是否已配置密钥
- GET /api/analysis/ai-recommendations?model=&days=
tests/test_ai.py (59 通过, 全程 mock 不联网):
- prompt: 大预算截断保留最新的天、缺失指标不写成 "None"、
一年数据估算 token 数上界
- 解析: 代码块包裹/前置句子/单对象/非法 priority/空建议 等 7 种畸形输入
- provider: 超时、HTTP 4xx/5xx、响应结构异常均转为 AIError;
未配置密钥时不发出任何请求
- 兜底: gemini 超时后 llama 接管、首个成功则不再调用第二个
- 端点: /models 不泄漏 API key;无密钥时仍返回 200 + 规则建议
密钥一律从环境变量读取,.env.example 只留空占位符。
全量: 161 passed, 1 skipped
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
|
2026-08-23 12:38:19 +08:00 |
|
ericwyuan
|
3b2d0697f0
|
[阶段1.1-1.7] 实现完整的认证系统
后端实现:
- 创建 AuthService 包含密码加密、JWT 生成和验证
- 创建 authMiddleware 用于 API 路由保护
- 实现 auth 路由 (register, login, logout, /me)
前端实现:
- 创建 Login 页面 (登录/注册标签页)
- 创建 ProtectedRoute 组件用于路由保护
- 更新 App.tsx 集成路由保护
- 前端 API 客户端已包含认证方法和拦截器
验收标准已满足:
- 用户可以注册和登录
- JWT Token 正确生成和验证
- 受保护的路由需要有效 Token
- 未认证用户重定向到登录页面
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
|
2026-08-23 12:25:33 +08:00 |
|