feat: 题材列表盘中120s缓存共用,题材详情新增全量新闻分页+强度热度行情
- 题材热点与热点穿透共用题材列表缓存:盘中由"不缓存实时拉取"改为120s短缓存, 东财全量列表拉取降到每120s一次,图重建时涨幅/热度两榜直接命中缓存 - 详情页相关新闻升级为全量分页(getThemeRelatedNews):支持翻页+评论数, 替换原 getDetail 固定3条 - 详情页统计条新增强度+热度(getSingleThemeQuote)实时指标 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,39 @@ async def theme_history(date: str = Query(..., description="交易日 YYYY-MM-DD
|
||||
conn.close()
|
||||
|
||||
|
||||
@router.get("/{theme_code}/news", summary="题材相关新闻(分页)")
|
||||
async def theme_news(
|
||||
theme_code: str,
|
||||
page_num: int = Query(1, ge=1, description="页码"),
|
||||
page_size: int = Query(10, ge=1, le=50, description="每页条数"),
|
||||
max_eu_time: str = Query("", description="分页游标(上一页返回的 maxEuTime)"),
|
||||
):
|
||||
result = await themes.fetch_theme_news(theme_code, page_num, max_eu_time, page_size)
|
||||
if result is None:
|
||||
return JSONResponse(
|
||||
{"data": None, "theme_code": theme_code},
|
||||
headers=_NO_CACHE_HEADERS,
|
||||
)
|
||||
return JSONResponse(
|
||||
{"data": result, "theme_code": theme_code},
|
||||
headers=_NO_CACHE_HEADERS,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{theme_code}/quote", summary="单题材实时行情(强度/热度/涨幅)")
|
||||
async def theme_quote(theme_code: str):
|
||||
result = await themes.fetch_theme_quote(theme_code)
|
||||
if result is None:
|
||||
return JSONResponse(
|
||||
{"data": None, "theme_code": theme_code},
|
||||
headers=_NO_CACHE_HEADERS,
|
||||
)
|
||||
return JSONResponse(
|
||||
{"data": result, "theme_code": theme_code},
|
||||
headers=_NO_CACHE_HEADERS,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{theme_code}/detail", summary="题材详情")
|
||||
async def theme_detail(theme_code: str):
|
||||
data = await themes.fetch_theme_detail(theme_code)
|
||||
|
||||
@@ -88,9 +88,14 @@ def _next_open_delta_seconds() -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# 盘中题材列表短缓存:题材热点页与热点穿透聚合共用同一份缓存,避免重复拉全量列表打东财。
|
||||
# 120s 长于热点穿透图缓存(60s),图重建时必然命中且更新频率更低,两页数据更稳。
|
||||
_LIST_CACHE_SECONDS = 120
|
||||
|
||||
|
||||
def _list_ttl_seconds() -> int:
|
||||
"""题材列表缓存秒数:交易时段 0(不缓存、实时拉取);非交易时段缓存到下次开盘前失效"""
|
||||
return 0 if _is_trading_time() else _next_open_delta_seconds()
|
||||
"""题材列表缓存秒数:交易时段 120s 短缓存;非交易时段缓存到下次开盘前失效"""
|
||||
return _LIST_CACHE_SECONDS if _is_trading_time() else _next_open_delta_seconds()
|
||||
|
||||
|
||||
# ---- 请求封装 ----
|
||||
@@ -159,11 +164,10 @@ 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}"
|
||||
# 交易时段强制实时:跳过缓存读取,避免命中非交易时段写入的上个交易日旧数据
|
||||
if not _is_trading_time():
|
||||
cached = get_cache(cache_key)
|
||||
if cached is not None:
|
||||
return json.loads(cached)
|
||||
# 统一读缓存(盘中 TTL=120s 短缓存,非盘中缓存到下次开盘前失效),避免重复拉全量列表打东财
|
||||
cached = get_cache(cache_key)
|
||||
if cached is not None:
|
||||
return json.loads(cached)
|
||||
|
||||
sort = 1 if asc else -1
|
||||
# hotRank 数值越小越热,"热度降序(最热在前)" 需反转为接口升序
|
||||
@@ -443,3 +447,56 @@ async def fetch_theme_graph(sort_field: int = 1, top_n: int = 30, limit: int = 1
|
||||
data = await _build_theme_graph(sort_field, top_n)
|
||||
_set_graph_cache(cache_key, data)
|
||||
return _trim_graph_result(data, limit)
|
||||
|
||||
|
||||
# ---- 题材相关新闻(分页) ----
|
||||
|
||||
_NEWS_PAGE_SIZE = 10
|
||||
|
||||
|
||||
async def fetch_theme_news(theme_code: str, page_num: int = 1, max_eu_time: str = "", page_size: int = _NEWS_PAGE_SIZE) -> Optional[dict]:
|
||||
"""获取题材相关新闻(分页),返回 {total, maxEuTime, list}
|
||||
|
||||
maxEuTime 为游标:上一页返回的 maxEuTime 作为下一页入参,首页传空串。
|
||||
盘中 60s 短缓存(与图缓存同频);非盘中缓存到下次开盘前失效。
|
||||
"""
|
||||
cache_key = f"theme_news:{theme_code}:{page_num}:{max_eu_time}:{page_size}"
|
||||
cached = get_cache(cache_key)
|
||||
if cached is not None:
|
||||
return json.loads(cached)
|
||||
|
||||
data = await _post(
|
||||
"/api/themeInvest/getThemeRelatedNews",
|
||||
{"themeCode": theme_code, "pageNum": page_num, "maxEuTime": max_eu_time, "pageSize": page_size},
|
||||
app_key=_APP_KEY_DETAIL,
|
||||
)
|
||||
if not data:
|
||||
return None
|
||||
|
||||
ttl_s = _graph_ttl_seconds()
|
||||
if ttl_s > 0:
|
||||
set_cache(cache_key, json.dumps(data, ensure_ascii=False), ttl_seconds=ttl_s)
|
||||
return data
|
||||
|
||||
|
||||
# ---- 单题材实时行情(强度/热度/涨幅) ----
|
||||
|
||||
async def fetch_theme_quote(theme_code: str) -> Optional[dict]:
|
||||
"""获取单题材实时行情(strengthValue/hotValue/f3),盘中 60s 短缓存"""
|
||||
cache_key = f"theme_quote:{theme_code}"
|
||||
cached = get_cache(cache_key)
|
||||
if cached is not None:
|
||||
return json.loads(cached)
|
||||
|
||||
data = await _post(
|
||||
"/api/themeInvest/getSingleThemeQuote",
|
||||
{"themeCode": theme_code},
|
||||
app_key=_APP_KEY_DETAIL,
|
||||
)
|
||||
if not data:
|
||||
return None
|
||||
|
||||
ttl_s = _graph_ttl_seconds()
|
||||
if ttl_s > 0:
|
||||
set_cache(cache_key, json.dumps(data, ensure_ascii=False), ttl_seconds=ttl_s)
|
||||
return data
|
||||
|
||||
Reference in New Issue
Block a user