From e2ffde99d57d58d99e754f8fc926e89b7085a039 Mon Sep 17 00:00:00 2001 From: intfoo Date: Wed, 15 Jul 2026 16:34:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(minute,monitoring):=20=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E6=95=B0=E6=8D=AE=E6=BA=90=E7=94=A8=E6=88=B7=E5=8D=95?= =?UTF-8?q?=E8=82=A1=E8=A1=A5=E6=8B=89/=E7=9B=91=E6=8E=A7=E9=A1=B5?= =?UTF-8?q?=E6=89=93=E9=80=9A=20(#121)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 承接 #126: 能力探测已补 KLINE_MINUTE_BATCH, 但两处取数/UI 仍按 TickFlow 档位拦截, 配了自定义分钟/实时源的 None 档用户依旧被挡。 - kline_sync.fetch_minute_single: 与 sync_minute_batch 一致, 先查 preferences.get_minute_data_provider(), 非 tickflow 且自定义源有 minute dataset 时走 custom provider, 避免分时图首次打开(本地无数据) 补拉返回空。 - Monitoring.tsx: None 档但配了自定义实时源时, 后端 is_realtime_allowed(realtime_mode=full_market) 仍返回 True。 以 quoteStatus.realtime_allowed 作为最终判据, 不再用 isNoneTier 一刀切拦截实时监控页。 Co-authored-by: shy3130 --- backend/app/services/kline_sync.py | 13 ++++++++++++- frontend/src/pages/settings/Monitoring.tsx | 5 ++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/backend/app/services/kline_sync.py b/backend/app/services/kline_sync.py index 530e088..e2ba0db 100644 --- a/backend/app/services/kline_sync.py +++ b/backend/app/services/kline_sync.py @@ -738,10 +738,21 @@ def fetch_intraday_monitor_batch( def fetch_minute_single(symbol: str, trade_date: date) -> pl.DataFrame: - """从 TickFlow 实时拉取单股单日分钟 K(不写入本地)。""" + """实时拉取单股单日分钟 K(不写入本地)。优先自定义分钟源, 回退 TickFlow。""" from datetime import datetime start_time = datetime(trade_date.year, trade_date.month, trade_date.day, 9, 25, 0) end_time = datetime(trade_date.year, trade_date.month, trade_date.day, 15, 5, 0) + + # 自定义数据源分流: 与 sync_minute_batch 一致, 配了自定义分钟源时走 custom provider, + # 避免无 TickFlow Pro+ 权限的用户分时图首次打开(本地无数据)时补拉失败返回空。 + provider_name = preferences.get_minute_data_provider() + if provider_name != "tickflow": + from app.data_providers import custom as custom_sources + if custom_sources.provider_has_dataset(provider_name, "minute"): + provider = custom_sources.get_provider(provider_name) + return provider.get_minute([symbol], start_time=start_time, end_time=end_time) + # 未配置 minute dataset → 回退 TickFlow + tf = get_client() try: raw = tf.klines.batch( diff --git a/frontend/src/pages/settings/Monitoring.tsx b/frontend/src/pages/settings/Monitoring.tsx index 6fc59ba..9b085f6 100644 --- a/frontend/src/pages/settings/Monitoring.tsx +++ b/frontend/src/pages/settings/Monitoring.tsx @@ -49,6 +49,9 @@ export function SettingsMonitoringPanel({ highlight }: { highlight?: string } = const toggleQuote = useToggleRealtimeQuotes() const tier = tierRank(caps?.label ?? '') const isNoneTier = tier < 0 + // None 档但配了自定义实时源时, 后端 is_realtime_allowed 仍返回 True (realtime_mode=full_market) + // 此时不应拦截实时监控页 — 用 quoteStatus.realtime_allowed 作为最终判据 + const realtimeAllowed = quoteStatus?.realtime_allowed ?? !isNoneTier const isFreeTier = tier === 0 const realtimeEnabled = prefs?.realtime_quotes_enabled ?? false // 分时图实时刷新间隔 (秒), 与后端 [3,60] clamp 对齐; 默认 6 @@ -278,7 +281,7 @@ export function SettingsMonitoringPanel({ highlight }: { highlight?: string } = } }, [highlight]) - if (isNoneTier) { + if (isNoneTier && !realtimeAllowed) { return (