# -*- coding: utf-8 -*- """ =================================== 股票数据接口 =================================== 职责: 1. 提供 GET /api/v1/stocks/{code}/quote 实时行情接口 2. 提供 GET /api/v1/stocks/{code}/history 历史行情接口 """ import logging from fastapi import APIRouter, HTTPException, Query from api.v1.schemas.stocks import ( StockQuote, StockHistoryResponse, KLineData, ) from api.v1.schemas.common import ErrorResponse from src.services.stock_service import StockService logger = logging.getLogger(__name__) router = APIRouter() @router.get( "/{stock_code}/quote", response_model=StockQuote, responses={ 200: {"description": "行情数据"}, 404: {"description": "股票不存在", "model": ErrorResponse}, 500: {"description": "服务器错误", "model": ErrorResponse}, }, summary="获取股票实时行情", description="获取指定股票的最新行情数据" ) def get_stock_quote(stock_code: str) -> StockQuote: """ 获取股票实时行情 获取指定股票的最新行情数据 Args: stock_code: 股票代码(如 600519、00700、AAPL) Returns: StockQuote: 实时行情数据 Raises: HTTPException: 404 - 股票不存在 """ try: service = StockService() # 使用 def 而非 async def,FastAPI 自动在线程池中执行 result = service.get_realtime_quote(stock_code) if result is None: raise HTTPException( status_code=404, detail={ "error": "not_found", "message": f"未找到股票 {stock_code} 的行情数据" } ) return StockQuote( stock_code=result.get("stock_code", stock_code), stock_name=result.get("stock_name"), current_price=result.get("current_price", 0.0), change=result.get("change"), change_percent=result.get("change_percent"), open=result.get("open"), high=result.get("high"), low=result.get("low"), prev_close=result.get("prev_close"), volume=result.get("volume"), amount=result.get("amount"), update_time=result.get("update_time") ) except HTTPException: raise except Exception as e: logger.error(f"获取实时行情失败: {e}", exc_info=True) raise HTTPException( status_code=500, detail={ "error": "internal_error", "message": f"获取实时行情失败: {str(e)}" } ) @router.get( "/{stock_code}/history", response_model=StockHistoryResponse, responses={ 200: {"description": "历史行情数据"}, 422: {"description": "不支持的周期参数", "model": ErrorResponse}, 500: {"description": "服务器错误", "model": ErrorResponse}, }, summary="获取股票历史行情", description="获取指定股票的历史 K 线数据" ) def get_stock_history( stock_code: str, period: str = Query("daily", description="K 线周期", pattern="^(daily|weekly|monthly)$"), days: int = Query(30, ge=1, le=365, description="获取天数") ) -> StockHistoryResponse: """ 获取股票历史行情 获取指定股票的历史 K 线数据 Args: stock_code: 股票代码 period: K 线周期 (daily/weekly/monthly) days: 获取天数 Returns: StockHistoryResponse: 历史行情数据 """ try: service = StockService() # 使用 def 而非 async def,FastAPI 自动在线程池中执行 result = service.get_history_data( stock_code=stock_code, period=period, days=days ) # 转换为响应模型 data = [ KLineData( date=item.get("date"), open=item.get("open"), high=item.get("high"), low=item.get("low"), close=item.get("close"), volume=item.get("volume"), amount=item.get("amount"), change_percent=item.get("change_percent") ) for item in result.get("data", []) ] return StockHistoryResponse( stock_code=stock_code, stock_name=result.get("stock_name"), period=period, data=data ) except ValueError as e: # period 参数不支持的错误(如 weekly/monthly) raise HTTPException( status_code=422, detail={ "error": "unsupported_period", "message": str(e) } ) except Exception as e: logger.error(f"获取历史行情失败: {e}", exc_info=True) raise HTTPException( status_code=500, detail={ "error": "internal_error", "message": f"获取历史行情失败: {str(e)}" } )