fix: 返回键失效的根因 + 同步页重做 + 连接池会永久阻塞线程

返回键
- 根因是 Framework7 的页面过渡由动画事件驱动:push 时把 allowPageChange
  置 false,等动画报告结束再恢复。这个报告不来,路由就永久卡住,之后每次
  导航都被静默丢弃,back() 还会把上一页重建一份而不是弹出。
  实测对照:navigate({animate:false}) 前后状态完全正确,带动画则必卡。
  因此关掉页面过渡动画——导航同步完成,处处正确。动效改由内容承担
  (卡片入场、hero 揭示、顶部进度条),这个取舍里正确性优先。
- Screen 的返回改为显式 handler,先清掉残留过渡状态再 back(),
  不依赖路由自己的闸门。注意只清视图上的 router-transition 类:
  页面自身的 page-previous 是 F7 判断「回到哪一页」的依据,
  一并清掉会导致重建出一个重复的页面(中途踩过这个坑)。

同步页
- .btn 系列样式原本只定义在 pages/Pages.css,而那个文件只被一个没有路由的
  遗留页面引用,所以真实页面上按钮全都退化成 Framework7 的默认样式——
  就是你看到的三条链接。样式移进每个界面都会加载的 Screen.css。
- 主次分明:一个填充主按钮 + 两个带副标题的次按钮;补上「同步会取哪些数据」
  说明,页面不再是一大片空白。
- 进度条显示当前阶段(每日数据 2026-08-01 / 运动详情 12/174 / 身体成分…),
  原来只有「0 / 730 天」,几分钟里完全看不出在做什么。

后端
- MariaDB 连接池:_mariadb_release 用的是阻塞 put(),而队列 maxsize=10,
  _mariadb_acquire 在池空时又会新建连接。并发超过 10 之后,归还的线程会
  永久停在 put() 上,请求就此挂死。改为 put_nowait,多出来的连接直接关闭。
- 进程重启会带走同步线程却留下 status=syncing 的行,界面上是一个永远不动
  的进度条,还拒绝开始新同步。启动时清理。

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
ericwyuan
2026-08-24 08:41:48 +08:00
parent 67a7c4b87e
commit 6ad87115ab
11 changed files with 250 additions and 786 deletions

View File

@@ -68,9 +68,23 @@ def get_sync_status(user_id):
"progressCurrent": row.get("progress_current"),
"progressTotal": row.get("progress_total"),
"startedAt": row.get("started_at"),
"stage": row.get("stage"),
}
def reset_stale_syncs():
"""Clear a "syncing" status left behind by a process that went away.
The status lives in the database but the work lives in a thread. A restart
(or a crash) takes the thread and leaves the row, so the UI shows a
progress bar that will never move and refuses to start a new sync.
"""
execute(
"UPDATE sync_status SET status = 'idle', stage = NULL "
"WHERE status = 'syncing'"
)
class MFARequired(RuntimeError):
"""Raised when a password login needs a code this process cannot obtain."""
@@ -761,6 +775,7 @@ def sync_data(user_id, creds, days=None, client=None):
_set_sync_status(
user_id, "syncing", now,
records_synced=0, progress_current=0, progress_total=days,
stage="连接 Garmin",
)
try:
@@ -809,10 +824,13 @@ def sync_data(user_id, creds, days=None, client=None):
_set_sync_status(
user_id, "syncing", now,
records_synced=days_synced, progress_current=i + 1,
progress_total=days,
progress_total=days, stage=f"每日数据 {date_str}",
)
activities_synced = 0
_set_sync_status(user_id, "syncing", now, records_synced=days_synced,
progress_current=days, progress_total=days,
stage="运动记录")
try:
activities_synced = _sync_activities(
client, user_id, start_date, today.isoformat()
@@ -824,13 +842,23 @@ def sync_data(user_id, creds, days=None, client=None):
# opening one later is a local read.
details_synced = 0
try:
details_synced = sync_activity_details(client, user_id)
details_synced = sync_activity_details(
client, user_id,
on_progress=lambda d, n: _set_sync_status(
user_id, "syncing", now, records_synced=days_synced,
progress_current=days, progress_total=days,
stage=f"运动详情 {d}/{n}"),
)
except Exception as e:
day_errors.append(f"activity_details: {describe(e)}")
# Everything else Garmin holds: body composition, blood pressure, race
# predictions, challenges and devices. Account-wide, so once per sync.
extra_counts = {}
stage_names = {
"bodyComposition": "身体成分", "bloodPressure": "血压",
"racePredictions": "成绩预测", "challenges": "挑战赛", "devices": "设备",
}
for name, call in (
("bodyComposition",
lambda: extras.sync_body_composition(client, user_id, start_date,
@@ -843,6 +871,9 @@ def sync_data(user_id, creds, days=None, client=None):
("challenges", lambda: extras.sync_challenges(client, user_id)),
("devices", lambda: extras.sync_devices(client, user_id)),
):
_set_sync_status(user_id, "syncing", now, records_synced=days_synced,
progress_current=days, progress_total=days,
stage=stage_names.get(name, name))
try:
extra_counts[name] = call()
except Exception as e: # noqa: BLE001 - one section must not fail the sync
@@ -871,7 +902,7 @@ def sync_data(user_id, creds, days=None, client=None):
_set_sync_status(
user_id, "idle", now, records_synced=days_synced,
progress_current=days, progress_total=days,
progress_current=days, progress_total=days, stage=None,
last_error="; ".join(day_errors[:3]) if day_errors else None,
)
message = (