From fb7cf921a96405466781473d2434ef9b8543237a Mon Sep 17 00:00:00 2001 From: shy3130 <415333856@qq.com> Date: Tue, 7 Jul 2026 11:43:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=9C=8B=E6=9D=BF=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E6=94=B9=E4=B8=BA=E5=90=8E=E7=AB=AF=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E9=87=8D=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原「刷新」只重新请求前端数据(overview.refetch), 不动后端缓存, 跨天 残留时拿到的是旧数据。改为「重载」: - 新增 POST /api/data/refresh-cache 端点: clear_cache + refresh_cache 重建 enriched 内存缓存, 同时清 overview 聚合缓存 + screener TTL 缓存 - 看板按钮先调后端重建缓存, 再刷新看板数据 - 与设置页「清理并刷新」(清前端 react-query)互补, 这个重建后端 Polars 缓存 --- backend/app/api/data.py | 19 +++++++++++++++++++ frontend/src/lib/api.ts | 1 + frontend/src/pages/Dashboard.tsx | 10 +++++++--- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/backend/app/api/data.py b/backend/app/api/data.py index 08cee45..4aaaf39 100644 --- a/backend/app/api/data.py +++ b/backend/app/api/data.py @@ -862,3 +862,22 @@ def get_version(request: Request) -> dict: return {"version": v} return {"version": "v0.0.0"} + + +@router.post("/refresh-cache") +def refresh_cache(request: Request) -> dict: + """重建 Polars 内存缓存 (clear + refresh), 不清数据、不清 alerts。 + + 用于跨天后缓存残留、或维表更新后手动刷新场景。 + 与设置页「清理并刷新」的区别: 那个清前端 react-query, 这个重建后端 enriched 缓存。 + """ + repo = request.app.state.repo + repo.clear_cache() + repo.refresh_cache() + # 清除 Overview 总览聚合结果缓存 + Screener 历史 TTL 缓存 + from app.api.overview import invalidate_overview_cache + invalidate_overview_cache() + from app.services.screener import ScreenerService + ScreenerService.clear_history_cache() + logger.info("refresh-cache: Polars 缓存已重建") + return {"ok": True} diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 00a2565..c3a22e8 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -1317,6 +1317,7 @@ export const api = { dataStatus: () => request('/api/data/status'), dataClear: () => request<{ deleted_files: number }>('/api/data/clear', { method: 'POST' }), + refreshCache: () => request<{ ok: boolean }>('/api/data/refresh-cache', { method: 'POST' }), enrichedSchema: (table: string) => request(`/api/data/schema/${table}`), testEndpoint: (url: string, rounds?: number) => diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index feff432..be56704 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -573,10 +573,14 @@ export function Dashboard() { }).catch(() => { /* 查询失败不阻塞, 用户仍可手动点击获取 */ }) }, [hasNoData, fetchJobId]) - // 手动刷新: 显示旋转动画; SSE 自动刷新: 静默, 无体感 + // 手动刷新: 先重建后端 Polars 缓存(解决跨天残留), 再重新拉看板数据 const handleRefresh = () => { setManualFetching(true) - overview.refetch().finally(() => setManualFetching(false)) + api.refreshCache() + .then(() => qc.invalidateQueries({ queryKey: ['overview-market'] })) + .finally(() => { + overview.refetch().finally(() => setManualFetching(false)) + }) } if (overview.isLoading && !data) { @@ -671,7 +675,7 @@ export function Dashboard() { disabled={manualFetching} className="inline-flex items-center gap-1 rounded-btn border border-border bg-elevated px-2 py-1 text-[11px] text-secondary transition-colors hover:text-foreground disabled:opacity-50" > - 刷新 + 重载