refactor(sync): 手动同步与自动同步彻底分开

「历史范围」原本放在设置页,却只对同步页的一个按钮起作用;而同步页最
显眼的主按钮「同步最新数据」写死 2 天,根本不看这个设置。选了「全部
历史」再点主按钮,表现就是应用无视你 —— 这正是反复出现的「只同步下来
两天」。

现在两条链路各管各的:

* 自动同步:只在设置页配置(开关 + 频率),窗口固定 SYNC_DAYS,不再
  读 history_days。措辞也改成「拉取最近几天」,不再暗示会补历史。
* 手动同步:范围就在同步页当场选,紧挨着用它的按钮,并标出每个范围的
  实际代价(自上次同步 / 7 天 / … / 全部历史约 730 天、20-40 分钟)。
  两个按钮合成一个「开始同步」,写死 2 天的那个删掉。

history_days 保留为「上次手动选的范围」,只有同步页读它;默认值改成
-1(自上次同步),对日常使用是正确的起点。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ericwyuan
2026-09-01 09:20:52 +08:00
parent 4d55fb4776
commit 526ece7d14
13 changed files with 156 additions and 116 deletions

View File

@@ -128,10 +128,11 @@ def sync_all_accounts(days=None, respect_schedule=False):
that have auto-sync off or that were synced recently enough. A direct call
(a manual "sync everything") leaves it False and syncs unconditionally.
When `days` is not provided, each account's saved `history_days` from
`user_settings` is used (the user's 历史范围 picker), falling back to
`SYNC_DAYS`. The scheduled loop always passes `days` explicitly — 历史范围
is the window for the manual 全量同步, not for a half-hourly tick.
The window is `SYNC_DAYS` unless the caller overrides it. Auto-sync is
deliberately blind to the user's manual sync range: reading it here is what
let a half-hourly tick re-pull 730 days and hold the account in a 429 loop,
and it also made one setting mean two different things in two places.
Anything the user chooses by hand goes through `/garmin/sync` instead.
"""
rows = query_all("SELECT user_id FROM garmin_tokens")
results = []
@@ -148,22 +149,7 @@ def sync_all_accounts(days=None, respect_schedule=False):
results.append({"user": uid, "status": "skipped",
"reason": "not due"})
continue
# Resolve the sync window: prefer the user's saved history_days,
# then the caller override, then the global default.
if days is None:
# query_one returns a dict, so `s[0]` raised KeyError — caught
# by the per-account handler below, which meant every single
# scheduled tick failed for every account and nothing was ever
# synced automatically.
s = query_one(
"SELECT history_days FROM user_settings WHERE user_id = ?", (uid,)
)
user_days = s.get("history_days") if s else None
if user_days == 0:
user_days = 730 # 全部历史 → 最大范围
d = user_days if user_days is not None else SYNC_DAYS
else:
d = days
d = SYNC_DAYS if days is None else days
# Never poke Garmin while it is rate-limiting us — that is exactly
# what keeps the limit alive. Respect the persisted cooldown and sit
# this tick out.
@@ -187,12 +173,7 @@ def _loop():
try:
if claim(interval=TICK_SECONDS):
try:
# Explicitly the recent window, never the user's 历史范围:
# that setting describes the manual 全量同步. Letting a
# half-hourly tick re-pull "全部历史" meant 730 days x ~7
# Garmin calls every 30 minutes, which is precisely what
# kept the account in a 429 loop.
sync_all_accounts(days=SYNC_DAYS, respect_schedule=True)
sync_all_accounts(respect_schedule=True)
finally:
release()
except Exception as e: # noqa: BLE001 - the loop must outlive any single failure