From 6109878c7e09222816e02d4458254b9497055abe Mon Sep 17 00:00:00 2001 From: gouki Date: Sun, 2 Aug 2026 16:32:36 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B9=B3=E5=8F=B0=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E8=AE=B0=E5=BD=95=EF=BC=88last=5Fsynced=5Fat?= =?UTF-8?q?=20+=20=E5=89=8D=E7=AB=AF=E5=B1=95=E7=A4=BA=E6=9C=80=E5=90=8E?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=97=B6=E9=97=B4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/database.py | 16 ++++++++++------ app/models/provider.py | 3 +++ app/schemas/provider.py | 1 + app/services/sync_service.py | 5 +++++ static/js/app.js | 7 ++++++- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/app/database.py b/app/database.py index 57d82e6..d01f52c 100644 --- a/app/database.py +++ b/app/database.py @@ -51,15 +51,19 @@ def init_db() -> None: def _migrate_assets_db() -> None: - """轻量迁移:为已有 assets 表补充新增列(SQLite create_all 不会修改已有表结构)""" + """轻量迁移:为已有表补充新增列(SQLite create_all 不会修改已有表结构)""" import sqlalchemy as sa with assets_engine.begin() as conn: - if not sa.inspect(conn).has_table("assets"): - return - cols = {c["name"] for c in sa.inspect(conn).get_columns("assets")} - if "external_id" not in cols: - conn.execute(sa.text("ALTER TABLE assets ADD COLUMN external_id VARCHAR")) + insp = sa.inspect(conn) + if insp.has_table("assets"): + cols = {c["name"] for c in insp.get_columns("assets")} + if "external_id" not in cols: + conn.execute(sa.text("ALTER TABLE assets ADD COLUMN external_id VARCHAR")) + if insp.has_table("providers"): + cols = {c["name"] for c in insp.get_columns("providers")} + if "last_synced_at" not in cols: + conn.execute(sa.text("ALTER TABLE providers ADD COLUMN last_synced_at DATETIME")) def get_session() -> Generator[Session, None, None]: diff --git a/app/models/provider.py b/app/models/provider.py index b660078..b932f69 100644 --- a/app/models/provider.py +++ b/app/models/provider.py @@ -41,4 +41,7 @@ class Provider(SQLModel, table=True): ) enabled: bool = Field(default=True, description="是否启用") remark: Optional[str] = Field(default=None, description="备注") + last_synced_at: Optional[datetime] = Field( + default=None, description="最近一次 SDK 同步时间" + ) created_at: datetime = Field(default_factory=datetime.utcnow, description="创建时间") diff --git a/app/schemas/provider.py b/app/schemas/provider.py index e543463..651e902 100644 --- a/app/schemas/provider.py +++ b/app/schemas/provider.py @@ -42,4 +42,5 @@ class ProviderUpdate(SQLModel): class ProviderRead(ProviderBase): id: int created_at: datetime + last_synced_at: Optional[datetime] = None has_api_config: bool = False diff --git a/app/services/sync_service.py b/app/services/sync_service.py index 3342595..4e17f33 100644 --- a/app/services/sync_service.py +++ b/app/services/sync_service.py @@ -6,6 +6,7 @@ """ import json +from datetime import datetime from typing import Optional from fastapi import HTTPException, status @@ -217,4 +218,8 @@ def sync_provider(session: Session, provider_id: int) -> dict: except Exception as e: # noqa: BLE001 result["account_error"] = str(e) + provider.last_synced_at = datetime.utcnow() + session.add(provider) + session.commit() + result["last_synced_at"] = provider.last_synced_at.isoformat() return result diff --git a/static/js/app.js b/static/js/app.js index 786749e..6c4aa05 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -315,6 +315,7 @@ const ProvidersView = { 未配置 API {{ p.enabled ? '启用' : '停用' }} +
最后同步:{{ fmtTime(p.last_synced_at) }}
@@ -335,6 +336,10 @@ const ProvidersView = { try { const r = await Api.get('/providers/adapters'); supported.value = r.supported_types || []; } catch (e) { /* ignore */ } }); function isSupported(t) { return !!t && supported.value.includes(t); } + function fmtTime(iso) { + if (!iso) return ''; + return new Date(iso).toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); + } async function test(p) { testing[p.id] = true; delete results[p.id]; try { @@ -358,7 +363,7 @@ const ProvidersView = { } catch (e) { results[p.id] = { ok: false, text: '✗ ' + e.message }; } finally { syncing[p.id] = false; } } - return { store, Fmt, testing, syncing, results, isSupported, test, sync, seed: seedProviders, create: openProviderCreate, edit: openProviderEdit, del: deleteProvider }; + return { store, Fmt, testing, syncing, results, isSupported, fmtTime, test, sync, seed: seedProviders, create: openProviderCreate, edit: openProviderEdit, del: deleteProvider }; }, };