You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

308 lines
10 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""API路由 - 对应Go的api/router.go"""
from fastapi import APIRouter, Depends, HTTPException, Header, Query
from typing import Optional
from sqlalchemy.orm import Session
from app.repositories import get_db
from app.services import StockService, FuturesService, AdminService
from app.models import (
Response, ErrorResponse, HealthResponse,
KLineQueryRequest, SymbolListRequest, BatchKLineRequest,
TradingDatesRequest, FuturesContractsRequest,
SourceSwitchRequest, BackfillRequest
)
from app.core.config import get_config
router = APIRouter()
# 获取配置
config = get_config()
# 认证依赖
def verify_api_key(x_api_key: Optional[str] = Header(None)):
"""验证API Key根据配置决定是否启用"""
if not config.server.auth_enabled:
# 认证已禁用,跳过验证
return x_api_key or "anonymous"
if not x_api_key:
raise HTTPException(status_code=401, detail="Missing API Key")
# TODO: 验证API Key有效性
return x_api_key
# ============================================
# 股票接口
# ============================================
@router.get("/stock/klines/{symbol}", response_model=Response)
def query_stock_klines(
symbol: str,
start: str = Query(..., description="开始日期 YYYYMMDD", min_length=8, max_length=8),
end: str = Query(..., description="结束日期 YYYYMMDD", min_length=8, max_length=8),
freq: str = Query(default="1d", description="周期"),
adjust: str = Query(default="", description="复权类型"),
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""查询股票K线"""
try:
service = StockService(db)
req = KLineQueryRequest(
symbol=symbol,
start=start,
end=end,
freq=freq,
adjust=adjust
)
data = service.query_klines(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/stock/symbols", response_model=Response)
def list_stock_symbols(
exchange: Optional[str] = Query(None, description="交易所筛选"),
keyword: Optional[str] = Query(None, description="关键词搜索"),
page: int = Query(default=1, ge=1, description="页码"),
size: int = Query(default=20, ge=1, le=100, description="每页数量"),
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""查询股票标的列表"""
try:
service = StockService(db)
req = SymbolListRequest(
exchange=exchange,
keyword=keyword,
page=page,
size=size
)
data = service.list_symbols(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/stock/klines/batch", response_model=Response)
def batch_query_stock_klines(
req: BatchKLineRequest,
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""批量查询股票K线"""
try:
service = StockService(db)
data = service.batch_query_klines(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/stock/trading-dates", response_model=Response)
def get_stock_trading_dates(
start: str = Query(..., description="开始日期 YYYYMMDD", min_length=8, max_length=8),
end: str = Query(..., description="结束日期 YYYYMMDD", min_length=8, max_length=8),
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""获取股票交易日历"""
try:
service = StockService(db)
req = TradingDatesRequest(start=start, end=end)
data = service.get_trading_dates(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# ============================================
# 期货接口
# ============================================
@router.get("/futures/klines/{symbol}", response_model=Response)
def query_futures_klines(
symbol: str,
start: str = Query(..., description="开始日期 YYYYMMDD", min_length=8, max_length=8),
end: str = Query(..., description="结束日期 YYYYMMDD", min_length=8, max_length=8),
freq: str = Query(default="1d", description="周期"),
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""查询期货K线"""
try:
service = FuturesService(db)
req = KLineQueryRequest(
symbol=symbol,
start=start,
end=end,
freq=freq
)
data = service.query_klines(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/futures/symbols", response_model=Response)
def list_futures_symbols(
exchange: Optional[str] = Query(None, description="交易所筛选"),
underlying: Optional[str] = Query(None, description="品种筛选"),
keyword: Optional[str] = Query(None, description="关键词搜索"),
page: int = Query(default=1, ge=1, description="页码"),
size: int = Query(default=20, ge=1, le=100, description="每页数量"),
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""查询期货标的列表"""
try:
service = FuturesService(db)
req = SymbolListRequest(
exchange=exchange,
underlying=underlying,
keyword=keyword,
page=page,
size=size
)
data = service.list_symbols(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/futures/klines/batch", response_model=Response)
def batch_query_futures_klines(
req: BatchKLineRequest,
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""批量查询期货K线"""
try:
service = FuturesService(db)
data = service.batch_query_klines(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/futures/continuous/{underlying}", response_model=Response)
def query_continuous_klines(
underlying: str,
start: str = Query(..., description="开始日期 YYYYMMDD", min_length=8, max_length=8),
end: str = Query(..., description="结束日期 YYYYMMDD", min_length=8, max_length=8),
freq: str = Query(default="1d", description="周期"),
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""查询主力连续合约K线预留"""
# TODO: 实现主力连续合约查询
from app.models import KLineData
data = KLineData(
symbol=f"{underlying}.MAIN",
name=f"{underlying}主力连续",
freq=freq,
count=0,
items=[]
)
return Response(code=0, message="success", data=data)
@router.get("/futures/trading-dates", response_model=Response)
def get_futures_trading_dates(
start: str = Query(..., description="开始日期 YYYYMMDD", min_length=8, max_length=8),
end: str = Query(..., description="结束日期 YYYYMMDD", min_length=8, max_length=8),
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""获取期货交易日历"""
try:
service = FuturesService(db)
req = TradingDatesRequest(start=start, end=end)
data = service.get_trading_dates(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/futures/contracts", response_model=Response)
def get_futures_contracts(
underlying: str = Query(..., description="品种代码"),
exchange: Optional[str] = Query(None, description="交易所筛选"),
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""获取品种合约列表"""
try:
service = FuturesService(db)
req = FuturesContractsRequest(underlying=underlying, exchange=exchange)
data = service.get_contracts_by_underlying(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# ============================================
# 管理接口
# ============================================
@router.get("/admin/source/status", response_model=Response)
def get_data_source_status(
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""获取数据源状态"""
try:
service = AdminService(db)
data = service.get_data_source_status()
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/admin/source/switch", response_model=Response)
def switch_data_source(
req: SourceSwitchRequest,
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""切换数据源"""
try:
service = AdminService(db)
service.switch_data_source(req)
return Response(code=0, message="数据源切换成功")
except Exception as e:
raise HTTPException(status_code=422, detail=str(e))
@router.post("/admin/backfill", response_model=Response)
def backfill_data(
req: BackfillRequest,
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
"""历史数据补录"""
try:
service = AdminService(db)
task_id = service.backfill_data(req)
return Response(
code=0,
message="补录任务已启动",
data={"task_id": task_id}
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/admin/health", response_model=HealthResponse)
def health_check(
db: Session = Depends(get_db)
):
"""健康检查(无需认证)"""
try:
service = AdminService(db)
return service.health_check()
except Exception as e:
raise HTTPException(status_code=503, detail=str(e))