"""板块数据路由:行业板块、概念板块 注意:行业/概念必须走不同 path(/industry、/concept),不能仅靠 query 参数区分。 上游反代/CDN 可能按 path 缓存并忽略 query,导致两个 tab 返回相同数据。 """ from fastapi import APIRouter, Query, HTTPException from fastapi.responses import JSONResponse from services import eastmoney, mootdx router = APIRouter() # 禁止任何中间层缓存,确保每次请求都打到后端 _NO_CACHE_HEADERS = {"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0"} _SORT_KEYS = {"mainNetInflow", "changePercent"} def _sort_sectors(data: list[dict], sort: str, order: str) -> list[dict]: """后端排序:mainNetInflow=资金流入,changePercent=涨幅%""" reverse = order != "asc" if sort == "changePercent": # changePercent 为 None 的排到最后(与方向无关) return sorted( data, key=lambda x: (x.get("changePercent") is None, -(x.get("changePercent") or 0)), reverse=reverse, ) return sorted(data, key=lambda x: -(x.get("mainNetInflow") or 0), reverse=reverse) async def _sector_response(sector_type: str, sort: str, order: str) -> JSONResponse: data = await eastmoney.fetch_sector_list(sector_type) source = None if not data: # 降级:通达信 mootdx(不含实时资金流数据) md_data = await mootdx.fetch_sector_list(sector_type) if md_data: data = md_data source = "mootdx" if data: data = _sort_sectors(data, sort, order) payload: dict = {"data": data, "count": len(data), "type": sector_type} if source: payload["source"] = source return JSONResponse(payload, headers=_NO_CACHE_HEADERS) @router.get("/industry", summary="行业板块列表") async def sector_industry( sort: str = Query("mainNetInflow", description="排序字段:mainNetInflow=资金流入, changePercent=涨幅%"), order: str = Query("desc", description="排序方向:asc=升序, desc=降序"), ): if sort not in _SORT_KEYS: sort = "mainNetInflow" if order not in ("asc", "desc"): order = "desc" return await _sector_response("industry", sort, order) @router.get("/concept", summary="概念板块列表") async def sector_concept( sort: str = Query("mainNetInflow", description="排序字段:mainNetInflow=资金流入, changePercent=涨幅%"), order: str = Query("desc", description="排序方向:asc=升序, desc=降序"), ): if sort not in _SORT_KEYS: sort = "mainNetInflow" if order not in ("asc", "desc"): order = "desc" return await _sector_response("concept", sort, order)