mirror of
https://ghfast.top/https://github.com/aeroxw/tick-stock-panel.git
synced 2026-09-12 14:24:15 +08:00
fix(frontend): 分时轮询停表 — 历史日/收盘定版后不再无效轮询
自选个股详情弹窗打开即按 6s 轮询分时接口 (StockPreviewDialog), 但两类 场景下数据不可变, 轮询纯属浪费: 周末/翻看历史日期 (后端回退出的历史日 本地分钟K不变), 以及当日收盘定版后。 新增共享停表工厂 minuteRefetchInterval (kline.ts 单一权威): - source=none / 响应日期 < 北京今天 → 停 - 当日: 收盘根 (≥15:00 的K) 已出现 且 已过 15:30 → 停。 完整性只认收盘根不按根数 —— 实测全天 241 根 (含 09:30 竞价根与 15:00 收盘根), 按根数阈值会在第 240 根 (14:59) 到位时误停, 错过 收盘根; 15:00 后盘后固定价成交可持续到 15:30, 其间量/额仍可能变, 与后端 quote 定版窗口终点 (15:30) 同口径 - 其余 (当日盘中) 照常轮询, 实时性不受影响 StockMultiDayIntradayChart 的最新日查询此前无条件轮询 (无 source=none 停表), 一并接入。判定扫全行取时间标签, 不依赖后端排序契约。 验证: pnpm build 通过。
This commit is contained in:
@@ -3,7 +3,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import { api, type MinuteKlineRow } from '@/lib/api'
|
||||
import { QK } from '@/lib/queryKeys'
|
||||
import { klineMinuteQueryOptions } from '@/lib/kline'
|
||||
import { klineMinuteQueryOptions, minuteRefetchInterval } from '@/lib/kline'
|
||||
import { EChartsIntraday } from '@/components/EChartsIntraday'
|
||||
|
||||
interface Props {
|
||||
@@ -40,7 +40,7 @@ export function StockIntradayChart({
|
||||
// 避免读到分钟增量落盘的上一轮本地分区; 历史日期后端自行忽略 live。
|
||||
...klineMinuteQueryOptions(symbol, date ?? undefined, refetchIntervalMs != null),
|
||||
enabled: !!symbol && !!date,
|
||||
refetchInterval: query => query.state.data?.source === 'none' ? false : refetchIntervalMs,
|
||||
refetchInterval: minuteRefetchInterval(refetchIntervalMs),
|
||||
})
|
||||
|
||||
const fetchMinute = useMutation({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef } from 'react'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { Download, Loader2, RefreshCw } from 'lucide-react'
|
||||
import { api, type MinuteKlineSession } from '@/lib/api'
|
||||
import { klineMinuteQueryOptions, klineMinuteRangeQueryOptions } from '@/lib/kline'
|
||||
import { klineMinuteQueryOptions, klineMinuteRangeQueryOptions, minuteRefetchInterval } from '@/lib/kline'
|
||||
import { toast } from '@/components/Toast'
|
||||
import { EChartsMultiDayIntraday } from '@/components/EChartsMultiDayIntraday'
|
||||
|
||||
@@ -36,7 +36,7 @@ export function StockMultiDayIntradayChart({
|
||||
// live: 当日盘中直接实时拉取, 不被分钟增量落盘的本地分区(≥60s一轮)拖慢
|
||||
...klineMinuteQueryOptions(symbol, undefined, true),
|
||||
enabled: !!symbol,
|
||||
refetchInterval: refetchIntervalMs,
|
||||
refetchInterval: minuteRefetchInterval(refetchIntervalMs),
|
||||
})
|
||||
|
||||
const sessions = useMemo(() => {
|
||||
|
||||
@@ -70,6 +70,59 @@ export function klineMinuteQueryOptions(symbol: string, date?: string, live?: bo
|
||||
}
|
||||
}
|
||||
|
||||
/** 收盘分钟标签: 连续竞价全天最后一根分钟K的时间。 */
|
||||
const CLOSE_MINUTE = '15:00'
|
||||
|
||||
/** 全天真正定版时刻: 15:00 收盘后仍可按收盘价成交 (盘后固定价) 至 15:30,
|
||||
* 其间成交量/额仍可能变化 —— 与后端 quote_service 定版重试窗口终点
|
||||
* (15:30) 同口径; 盘后管道默认 15:35 在此之后启动。 */
|
||||
const MARKET_ALL_OVER = '15:30'
|
||||
|
||||
/** 北京时间今日 YYYY-MM-DD (与后端 cn_today 同口径; 不用 UTC toISOString, 避免傍晚错日)。 */
|
||||
function cnToday(): string {
|
||||
return new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai' }).format(new Date())
|
||||
}
|
||||
|
||||
/** 北京时间此刻 'HH:MM'。 */
|
||||
function cnNowHHMM(): string {
|
||||
return new Intl.DateTimeFormat('en-GB', { timeZone: 'Asia/Shanghai', hour: '2-digit', minute: '2-digit', hour12: false }).format(new Date())
|
||||
}
|
||||
|
||||
/** 行 datetime ('YYYY-MM-DD HH:MM:SS' 北京墙钟契约) → 'HH:MM'; 非常规格式返回 ''。 */
|
||||
function rowMinute(r: { datetime?: string } | undefined): string {
|
||||
const s = String(r?.datetime ?? '')
|
||||
return s.length >= 16 ? s.slice(11, 16) : ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 分时轮询停表 — 数据不可变时停止, 消除无效请求 (周末/盘后/翻看历史日期)。
|
||||
*
|
||||
* - 未传间隔 → 不轮询 (调用方默认关闭)
|
||||
* - 无数据 (source=none) → 停
|
||||
* - 响应日期 < 北京今天 → 停: 历史日的本地分钟K不可变 (周末"最新"会被
|
||||
* 后端回退到上一交易日, 同样命中此规则)
|
||||
* - 当日: 收盘根 (≥15:00 的K) 已出现 **且** 已过 15:30 → 停。
|
||||
* 完整性**只认收盘根, 不按根数** —— 实测本系统全天 241 根 (首根 09:30
|
||||
* 竞价根 + 15:00 收盘根), 按根数阈值会在第 240 根 (14:59) 到位时误停,
|
||||
* 恰好错过 15:00 收盘根; 收盘根判据还天然兼容盘中缺根与部分交易日。
|
||||
* 而 15:00 收盘根出现后**不能立即停**: 盘后按收盘价成交可持续到 15:30,
|
||||
* 其间量/额仍可能更新 (当前数据源分钟K止于 15:00, 此窗口为防御性保留,
|
||||
* 与后端"15:30 才允许定版/重建"的边界一致)。
|
||||
* - 其余 (当日 15:30 前) → 按间隔继续
|
||||
*/
|
||||
export function minuteRefetchInterval(ms?: number) {
|
||||
return (query: any): number | false => {
|
||||
if (ms == null) return false
|
||||
const d = query.state.data
|
||||
if (!d) return ms
|
||||
if (d.source === 'none') return false
|
||||
if (typeof d.date === 'string' && d.date < cnToday()) return false
|
||||
const closeBarArrived = (d.rows ?? []).some((r: { datetime?: string }) => rowMinute(r) >= CLOSE_MINUTE)
|
||||
if (closeBarArrived && cnNowHHMM() >= MARKET_ALL_OVER) return false
|
||||
return ms
|
||||
}
|
||||
}
|
||||
|
||||
/** 多日分时查询配置 — 分时 tab 的 StockMultiDayIntradayChart 与 邻近预取 共用。
|
||||
* 内嵌「仅同 symbol 占位」守卫 (key 结构 ['kline-minute-range', symbol, days], index 1 为 symbol),
|
||||
* 与 klineDailyQueryOptions 同源, 调用点不再各自手写。 */
|
||||
|
||||
Reference in New Issue
Block a user