fix(monitor): 分时图刷新开关不生效 + Pro+ 默认开启 + 刷新条件统一

三处问题:

1. 后端 API 漏接线 (settings.py):
   - 入参模型 RealtimeMonitorConfigIn 缺 minute_intraday_refresh →
     PUT 时 Pydantic 静默丢弃, 开关拨了也不存
   - GET /preferences 响应没返回该字段 → 前端永远读 undefined→false,
     开关拨完弹回 OFF
   服务层 (preferences.py) 本就有 get/set, 纯 API 层漏接。

2. 默认值 (preferences.py):
   get_minute_intraday_refresh 原默认 False。改为三态: 未设置过的
   key 按 Pro+ 权限定默认 (有权限=开, 无=关); 用户主动设置过的
   (含 False) 以用户选择为准。用 'in data' 区分 未设置 vs 设为 False。

3. 刷新条件不一致 (Watchlist.tsx):
   Watchlist 用 || (开关或实时行情任一即刷新), Screener 用 && (两者都要)。
   与设置卡片描述「需 Pro+ 权限 + 实时行情运行」对齐, 统一成 &&。

后端 216 测试通过; 前端 tsc 通过。
This commit is contained in:
shy3130
2026-07-13 22:28:04 +08:00
parent 933d216c83
commit 590de5d694
3 changed files with 19 additions and 5 deletions
+2
View File
@@ -428,6 +428,7 @@ def get_preferences() -> dict:
"webhook_enabled_default": preferences.get_webhook_enabled_default(),
"webhook_default_channels": preferences.get_webhook_default_channels(),
"sidebar_index_symbols": preferences.get_sidebar_index_symbols(),
"minute_intraday_refresh": preferences.get_minute_intraday_refresh(),
"nav_order": preferences.get_nav_order(),
"nav_hidden": preferences.get_nav_hidden(),
"screener_auto_run": preferences.get_screener_auto_run(),
@@ -752,6 +753,7 @@ class RealtimeMonitorConfigIn(BaseModel):
strategy_monitor_ids: list[str] | None = None
sidebar_index_symbols: list[str] | None = None
screener_auto_run: bool | None = None
minute_intraday_refresh: bool | None = None
@router.put("/preferences/realtime-monitor")
+14 -2
View File
@@ -91,8 +91,20 @@ def get_minute_sync_enabled() -> bool:
def get_minute_intraday_refresh() -> bool:
"""自选列表分时图是否跟随实时行情刷新 (默认关闭, 开启后盘中按 SSE 频率刷新)。"""
return load().get("minute_intraday_refresh", False)
"""自选列表分时图是否跟随实时行情刷新
默认值随权限: 有实时行情权限 (Pro+) 的用户默认开启, 否则关闭。
用户主动设置过的 (key 存在) 以用户选择为准, 即使是 False 也尊重。
"""
data = load()
if "minute_intraday_refresh" in data:
return bool(data["minute_intraday_refresh"])
# 未设置过: 有权限默认开, 无权限默认关。
try:
from app.services.quote_service import QuoteService
return QuoteService.is_realtime_allowed()
except Exception:
return False
def get_minute_sync_days() -> int:
+3 -3
View File
@@ -625,8 +625,8 @@ export function Watchlist() {
const klineData = dailyKVisible ? (klineBatch.data?.data ?? {}) : {}
// 批量分时数据 (Pro+ 用户, 列可见时才拉)
// 刷新策略: 实时行情运行中自动按 15s 轮询 (不接 SSE 高频, 避免每秒拉 TickFlow 触限流);
// 用户也可在实时监控设置里单独开启 minute_intraday_refresh 强制刷新 (即使未开实时行情)
// 刷新策略: 仅当实时行情运行 且 用户在实时监控设置里开启 minute_intraday_refresh 时
// 按 15s 轮询 (不接 SSE 高频, 避免每秒拉 TickFlow 触限流); 与 Screener / 设置卡片描述一致。
const { data: prefsData } = usePreferences()
const intradayRefreshEnabled = prefsData?.minute_intraday_refresh ?? false
const minuteBatch = useQuery({
@@ -634,7 +634,7 @@ export function Watchlist() {
queryFn: () => api.klineMinuteBatch(symbols),
enabled: intradayVisible && symbols.length > 0,
staleTime: 10_000,
refetchInterval: (realtimeRunning || intradayRefreshEnabled) ? 15_000 : false,
refetchInterval: (intradayRefreshEnabled && realtimeRunning) ? 15_000 : false,
})
const minuteData = intradayVisible ? (minuteBatch.data?.data ?? {}) : {}