feat(sync-history): 新增同步结果查询(每次自动/手动/立即同步的记录)

后端:
- db.py SCHEMA 新增 sync_history 表(不可变,每次同步尝试一行) + 索引
- garmin.py: _log_sync_history() 在 sync_data 全部出口记录; trigger 区分
  auto(调度器)/manual(同步页开始同步)/quick(设置页立即同步); start_sync
  限流拦截分支同样留档; 写入失败只告警不影响同步
- scheduler.py 自动同步传 trigger="auto"; routes 新增 GET /garmin/sync-history
  按时间倒序返回(默认 50 条,上限 200)

前端:
- api.ts 增加 SyncHistoryItem 类型 + getSyncHistory()
- 新页面 /sync-history/ 同步记录: 卡片列表, 状态(成功/失败/被限流)chip 配色,
  时间本地化(今天/昨天/X月X日), 触发类型标签, 范围与耗时
- 同步页与设置页同步区块均加入口链接
This commit is contained in:
ericwyuan
2026-09-02 20:38:48 +08:00
parent 26ff853f0b
commit 5e6fc01f76
13 changed files with 476 additions and 20 deletions

View File

@@ -88,7 +88,7 @@ class TestSyncAllAccounts:
seen = []
monkeypatch.setattr(
garmin_svc, "sync_data",
lambda uid, creds, days=None, client=None: seen.append(uid)
lambda uid, creds, days=None, client=None, trigger="manual": seen.append(uid)
or {"status": "success", "recordsSynced": days},
)
@@ -105,7 +105,7 @@ class TestSyncAllAccounts:
garmin_svc.save_token(user["id"], "t1")
garmin_svc.save_token(other["id"], "t2")
def flaky(uid, creds, days=None, client=None):
def flaky(uid, creds, days=None, client=None, trigger="manual"):
if uid == user["id"]:
raise RuntimeError("token expired")
return {"status": "success", "recordsSynced": 2}
@@ -160,7 +160,7 @@ class TestEndpoints:
garmin_svc.save_token(user["id"], "blob")
monkeypatch.setattr(
garmin_svc, "sync_data",
lambda uid, creds, days=None, client=None: {
lambda uid, creds, days=None, client=None, trigger="manual": {
"status": "success", "recordsSynced": days, "message": "ok",
},
)
@@ -173,7 +173,7 @@ class TestEndpoints:
garmin_svc.save_token(user["id"], "blob")
monkeypatch.setattr(
garmin_svc, "sync_data",
lambda uid, creds, days=None, client=None: {
lambda uid, creds, days=None, client=None, trigger="manual": {
"status": "success", "recordsSynced": days,
},
)
@@ -200,8 +200,9 @@ class TestSyncWindowResolution:
garmin_svc.save_token(user["id"], "t1")
seen = {}
def record(uid, creds, days=None, client=None):
def record(uid, creds, days=None, client=None, trigger="manual"):
seen["days"] = days
seen["trigger"] = trigger
return {"status": "success", "recordsSynced": 0}
monkeypatch.setattr(garmin_svc, "sync_data", record)