fix: 题材热点缓存改为开盘前失效,避免开盘后显示上个交易日数据
非交易时段(凌晨/早盘前/周末)访问题材接口时,旧逻辑把上个交易日 数据缓存 18 小时,导致开盘后缓存未过期仍读到旧数据。 - 非交易时段缓存 TTL 由固定 18h 改为"截止到下一次开盘时刻": 早盘前→9:30 失效、午休→13:00 失效、收盘后/周末→下一交易日 9:30 失效 - 题材列表交易时段强制不读缓存,双保险实时拉取 - 热点穿透图/题材股票缓存同样收敛到开盘前失效 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+35
-11
@@ -55,9 +55,31 @@ def _is_trading_time() -> bool:
|
||||
or _TRADING_AFTERNOON[0] <= t <= _TRADING_AFTERNOON[1])
|
||||
|
||||
|
||||
def _dynamic_ttl() -> int:
|
||||
"""盘中返回 2 分钟缓存 TTL,非交易时段 18 小时(覆盖到下一交易日)"""
|
||||
return 0 if _is_trading_time() else 18
|
||||
def _next_open_delta_seconds() -> int:
|
||||
"""非交易时段写入的缓存距下次开盘的秒数。
|
||||
|
||||
缓存只允许存活到下一次开盘(早盘 9:30 / 午休后 13:00)前一刻,
|
||||
保证交易日开盘后缓存必然过期并实时拉取,不会读到上个交易日写入的旧数据。
|
||||
"""
|
||||
now = datetime.now(_CST)
|
||||
# 午休 11:30-13:00 → 截止今天 13:00
|
||||
if _TRADING_MORNING[1] < now.time() < _TRADING_AFTERNOON[0]:
|
||||
open_dt = now.replace(hour=13, minute=0, second=0, microsecond=0)
|
||||
return max(0, int((open_dt - now).total_seconds()))
|
||||
# 其余非交易时段(早盘前 / 收盘后 / 周末 / 节假日)→ 下一个工作日 9:30
|
||||
for days in range(0, 8):
|
||||
d = (now + timedelta(days=days)).date()
|
||||
if d.weekday() >= 5: # 跳过周末
|
||||
continue
|
||||
open_dt = datetime(d.year, d.month, d.day, 9, 30, tzinfo=_CST)
|
||||
if open_dt > now:
|
||||
return max(0, int((open_dt - now).total_seconds()))
|
||||
return 0
|
||||
|
||||
|
||||
def _list_ttl_seconds() -> int:
|
||||
"""题材列表缓存秒数:交易时段 0(不缓存、实时拉取);非交易时段缓存到下次开盘前失效"""
|
||||
return 0 if _is_trading_time() else _next_open_delta_seconds()
|
||||
|
||||
|
||||
# ---- 请求封装 ----
|
||||
@@ -126,9 +148,11 @@ async def fetch_theme_list(sort_field: int = 1, asc: bool = False) -> list[dict]
|
||||
asc: True=升序, False=降序
|
||||
"""
|
||||
cache_key = f"theme_list:{sort_field}:{asc}"
|
||||
cached = get_cache(cache_key)
|
||||
if cached is not None:
|
||||
return json.loads(cached)
|
||||
# 交易时段强制实时:跳过缓存读取,避免命中非交易时段写入的上个交易日旧数据
|
||||
if not _is_trading_time():
|
||||
cached = get_cache(cache_key)
|
||||
if cached is not None:
|
||||
return json.loads(cached)
|
||||
|
||||
sort = 1 if asc else -1
|
||||
# hotRank 数值越小越热,"热度降序(最热在前)" 需反转为接口升序
|
||||
@@ -156,9 +180,9 @@ async def fetch_theme_list(sort_field: int = 1, asc: bool = False) -> list[dict]
|
||||
page += 1
|
||||
|
||||
if items:
|
||||
ttl = _dynamic_ttl()
|
||||
if ttl > 0:
|
||||
set_cache(cache_key, json.dumps(items, ensure_ascii=False), ttl_hours=ttl)
|
||||
ttl_s = _list_ttl_seconds()
|
||||
if ttl_s > 0:
|
||||
set_cache(cache_key, json.dumps(items, ensure_ascii=False), ttl_seconds=ttl_s)
|
||||
return items
|
||||
|
||||
|
||||
@@ -234,8 +258,8 @@ _GRAPH_CACHE_SECONDS = 60
|
||||
|
||||
|
||||
def _graph_ttl_seconds() -> int:
|
||||
"""图聚合结果与题材股票子层的缓存秒数:盘中 60 秒,非盘中 18 小时"""
|
||||
return _GRAPH_CACHE_SECONDS if _is_trading_time() else 18 * 3600
|
||||
"""图聚合结果与题材股票子层的缓存秒数:盘中 60 秒;非盘中缓存到下次开盘前失效"""
|
||||
return _GRAPH_CACHE_SECONDS if _is_trading_time() else _next_open_delta_seconds()
|
||||
|
||||
|
||||
# 后台重建锁:cache_key -> asyncio.Lock,幂等去重,防止并发重复聚合
|
||||
|
||||
Reference in New Issue
Block a user