更新BK缓存策略
This commit is contained in:
@@ -318,14 +318,14 @@ def _get_sector_session() -> AsyncSession:
|
||||
global _sector_session
|
||||
if _sector_session is None:
|
||||
_sector_session = AsyncSession(
|
||||
impersonate="chrome120",
|
||||
impersonate="chrome131",
|
||||
headers={
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
||||
"Referer": "https://data.eastmoney.com/bkzj/hy.html",
|
||||
"Accept": "*/*",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||
},
|
||||
timeout=10,
|
||||
timeout=5,
|
||||
)
|
||||
return _sector_session
|
||||
|
||||
@@ -370,7 +370,7 @@ async def fetch_sector_list(sector_type: str) -> list[dict]:
|
||||
set_cache(cache_key, json.dumps(push2_data, ensure_ascii=False), ttl_hours=ttl)
|
||||
return push2_data
|
||||
|
||||
# push2 失败,用 akshare
|
||||
# push2 失败,用 akshare(不写磁盘缓存)
|
||||
akshare_data = await akshare_task
|
||||
if akshare_data:
|
||||
_sector_cache[sector_type] = (akshare_data, now)
|
||||
@@ -385,14 +385,14 @@ async def _fetch_push2(sector_type: str) -> list[dict]:
|
||||
|
||||
session = _get_sector_session()
|
||||
ut = await get_em_ut()
|
||||
url = (
|
||||
f"https://push2.eastmoney.com/api/qt/clist/get"
|
||||
f"?fs={fs}&fields={SECTOR_FIELDS}"
|
||||
f"&fid=f62&po=1&pz=500&pn=1&np=1&fltt=2"
|
||||
f"&invt=2&ut={ut}"
|
||||
)
|
||||
|
||||
for attempt in range(2):
|
||||
url = (
|
||||
f"https://push2.eastmoney.com/api/qt/clist/get"
|
||||
f"?fs={fs}&fields={SECTOR_FIELDS}"
|
||||
f"&fid=f62&po=1&pz=500&pn=1&np=1&fltt=2"
|
||||
f"&invt=2&ut={ut}"
|
||||
)
|
||||
try:
|
||||
resp = await session.get(url)
|
||||
if resp.status_code != 200:
|
||||
@@ -432,6 +432,11 @@ async def _fetch_push2(sector_type: str) -> list[dict]:
|
||||
if "disconnect" in err.lower() or "refused" in err.lower() or attempt == 1:
|
||||
await get_em_ut(force_refresh=True)
|
||||
ut = _em_ut
|
||||
# 先尝试更新现有会话的 headers
|
||||
try:
|
||||
session.headers.update({"Referer": "https://data.eastmoney.com/bkzj/hy.html"})
|
||||
except Exception:
|
||||
pass
|
||||
# 重建会话(TLS 指纹可能会被缓存)
|
||||
global _sector_session
|
||||
_sector_session = None
|
||||
@@ -445,10 +450,12 @@ import akshare as ak
|
||||
|
||||
|
||||
async def _fetch_akshare(sector_type: str) -> list[dict]:
|
||||
"""akshare 降级方案(同花顺数据源)"""
|
||||
"""akshare 降级方案(东方财富数据源)"""
|
||||
loop = asyncio.get_event_loop()
|
||||
code_map_key = f"board_codes:{sector_type}"
|
||||
|
||||
def _get():
|
||||
def _build_code_map():
|
||||
"""获取板块代码映射(HTTP 较慢,结果单独缓存 24h)"""
|
||||
code_map = {}
|
||||
try:
|
||||
if sector_type == "industry":
|
||||
@@ -469,16 +476,34 @@ async def _fetch_akshare(sector_type: str) -> list[dict]:
|
||||
code_map[str(r.get("name", ""))] = str(r.get("code", ""))
|
||||
except Exception:
|
||||
pass
|
||||
return code_map
|
||||
|
||||
def _get_fund_flow():
|
||||
if sector_type == "industry":
|
||||
df = ak.stock_fund_flow_industry()
|
||||
return ak.stock_fund_flow_industry()
|
||||
else:
|
||||
df = ak.stock_fund_flow_concept()
|
||||
return ak.stock_fund_flow_concept()
|
||||
|
||||
return code_map, df
|
||||
# 1. 尝试从缓存读取 code_map
|
||||
code_map = {}
|
||||
cached_map = get_cache(code_map_key)
|
||||
if cached_map is not None:
|
||||
code_map = json.loads(cached_map)
|
||||
|
||||
try:
|
||||
code_map, df = await loop.run_in_executor(None, _get)
|
||||
if code_map:
|
||||
# 已有缓存,只需获取资金流
|
||||
df = await loop.run_in_executor(None, _get_fund_flow)
|
||||
else:
|
||||
# 首次:code_map + 资金流并发获取
|
||||
map_data, df = await asyncio.gather(
|
||||
loop.run_in_executor(None, _build_code_map),
|
||||
loop.run_in_executor(None, _get_fund_flow),
|
||||
)
|
||||
if map_data:
|
||||
code_map = map_data
|
||||
set_cache(code_map_key, json.dumps(code_map, ensure_ascii=False), ttl_hours=24)
|
||||
|
||||
if df is None or df.empty:
|
||||
return []
|
||||
df = df.sort_values("净额", ascending=False)
|
||||
|
||||
@@ -458,12 +458,12 @@ export interface SectorResponse {
|
||||
* 获取东方财富板块列表(按主力净流入排序)
|
||||
* @param type industry=行业板块, concept=概念板块
|
||||
*/
|
||||
export async function fetchSectors(type: SectorType): Promise<SectorItem[]> {
|
||||
export async function fetchSectors(type: SectorType, signal?: AbortSignal): Promise<SectorItem[]> {
|
||||
const baseUrl = getApiBaseUrl();
|
||||
const url = `${baseUrl}/api/sectors?type=${type}`;
|
||||
|
||||
try {
|
||||
const resp = await fetch(url, { method: "GET" });
|
||||
const resp = await fetch(url, { method: "GET", signal });
|
||||
if (!resp.ok) return [];
|
||||
const result: SectorResponse = await resp.json();
|
||||
return result.data || [];
|
||||
|
||||
+12
-4
@@ -1,5 +1,5 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { useState, useEffect, useMemo, useRef } from "react";
|
||||
import { fetchSectors, type SectorItem, type SectorType } from "@/lib/stock-api";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { ArrowLeft, TrendingUp, TrendingDown, RefreshCw } from "lucide-react";
|
||||
@@ -21,17 +21,25 @@ function SectorsPage() {
|
||||
const [data, setData] = useState<SectorItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [sort, setSort] = useState<SortMode>("mainNetInflow");
|
||||
const abortRef = useRef<AbortController | null>(null);
|
||||
|
||||
const loadData = (t: SectorType) => {
|
||||
// 取消上次未完成的请求
|
||||
abortRef.current?.abort();
|
||||
const controller = new AbortController();
|
||||
abortRef.current = controller;
|
||||
setLoading(true);
|
||||
fetchSectors(t).then((items) => {
|
||||
setData(items);
|
||||
setLoading(false);
|
||||
fetchSectors(t, controller.signal).then((items) => {
|
||||
if (!controller.signal.aborted) {
|
||||
setData(items);
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadData(tab);
|
||||
return () => abortRef.current?.abort();
|
||||
}, [tab]);
|
||||
|
||||
const sorted = useMemo(() => {
|
||||
|
||||
Reference in New Issue
Block a user