feat: 架构修复 - P0/P1 问题全面修复

P0 修复:
- runtimeStore: 添加缺失的 lastDayHistory 字段
- Gateway/RuntimeService: 状态同步改为内存优先,消除 glob 竞态
- App.jsx: 从 3075 行重构到 ~500 行,提取 8 个独立文件

P1 修复:
- CORS: 4 个服务改为从环境变量读取允许 origins
- MarketStore: 改为模块级单例模式
- Domain 层: 删除 trading thin wrapper,保留 news 真实逻辑
- 测试: 补齐 77 个 gateway/runtime 测试

新增文件:
- backend/tests/test_gateway.py (43 tests)
- frontend/src/hooks/useWebSocketHandler.js
- frontend/src/hooks/useStockRequestCallbacks.js
- frontend/src/hooks/useAgentCallbacks.js
- frontend/src/hooks/useRuntimeCallbacks.js
- frontend/src/hooks/useWatchlistCallbacks.js
- frontend/src/components/TickerBar.jsx
- frontend/src/components/HeaderRight.jsx
- frontend/src/components/ChartTabs.jsx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-23 18:45:57 +08:00
parent 80256a4079
commit 3926a6bd07
21 changed files with 4280 additions and 2790 deletions

View File

@@ -8,7 +8,16 @@ from typing import Any
from fastapi import FastAPI, Query
from fastapi.middleware.cors import CORSMiddleware
from backend.domains import trading as trading_domain
from backend.config.env_config import get_cors_origins
from backend.services.market import MarketService
from backend.tools.data_tools import (
get_company_news,
get_financial_metrics,
get_insider_trades,
get_market_cap,
get_prices,
search_line_items,
)
from shared.schema import (
CompanyNewsResponse,
FinancialMetricsResponse,
@@ -28,7 +37,7 @@ def create_app() -> FastAPI:
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_origins=get_cors_origins(),
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
@@ -45,12 +54,8 @@ def create_app() -> FastAPI:
start_date: str = Query(...),
end_date: str = Query(...),
) -> PriceResponse:
payload = trading_domain.get_prices_payload(
ticker=ticker,
start_date=start_date,
end_date=end_date,
)
return PriceResponse(ticker=payload["ticker"], prices=payload["prices"])
prices = get_prices(ticker=ticker, start_date=start_date, end_date=end_date)
return PriceResponse(ticker=ticker, prices=prices)
@app.get("/api/financials", response_model=FinancialMetricsResponse)
async def api_get_financials(
@@ -59,13 +64,13 @@ def create_app() -> FastAPI:
period: str = Query("ttm"),
limit: int = Query(10, ge=1, le=100),
) -> FinancialMetricsResponse:
payload = trading_domain.get_financials_payload(
metrics = get_financial_metrics(
ticker=ticker,
end_date=end_date,
period=period,
limit=limit,
)
return FinancialMetricsResponse(financial_metrics=payload["financial_metrics"])
return FinancialMetricsResponse(financial_metrics=metrics)
@app.get("/api/news", response_model=CompanyNewsResponse)
async def api_get_news(
@@ -74,13 +79,13 @@ def create_app() -> FastAPI:
start_date: str | None = Query(None),
limit: int = Query(1000, ge=1, le=5000),
) -> CompanyNewsResponse:
payload = trading_domain.get_news_payload(
news = get_company_news(
ticker=ticker,
end_date=end_date,
start_date=start_date,
limit=limit,
)
return CompanyNewsResponse(news=payload["news"])
return CompanyNewsResponse(news=news)
@app.get("/api/insider-trades", response_model=InsiderTradeResponse)
async def api_get_insider_trades(
@@ -89,18 +94,19 @@ def create_app() -> FastAPI:
start_date: str | None = Query(None),
limit: int = Query(1000, ge=1, le=5000),
) -> InsiderTradeResponse:
payload = trading_domain.get_insider_trades_payload(
trades = get_insider_trades(
ticker=ticker,
end_date=end_date,
start_date=start_date,
limit=limit,
)
return InsiderTradeResponse(insider_trades=payload["insider_trades"])
return InsiderTradeResponse(insider_trades=trades)
@app.get("/api/market/status")
async def api_get_market_status() -> dict[str, Any]:
"""Return current market status using the existing market service logic."""
return trading_domain.get_market_status_payload()
service = MarketService(tickers=[])
return service.get_market_status()
@app.get("/api/market-cap")
async def api_get_market_cap(
@@ -108,10 +114,12 @@ def create_app() -> FastAPI:
end_date: str = Query(...),
) -> dict[str, Any]:
"""Return market cap for one ticker/date."""
return trading_domain.get_market_cap_payload(
ticker=ticker,
end_date=end_date,
)
market_cap = get_market_cap(ticker=ticker, end_date=end_date)
return {
"ticker": ticker,
"end_date": end_date,
"market_cap": market_cap,
}
@app.get("/api/line-items", response_model=LineItemResponse)
async def api_get_line_items(
@@ -121,14 +129,14 @@ def create_app() -> FastAPI:
period: str = Query("ttm"),
limit: int = Query(10, ge=1, le=100),
) -> LineItemResponse:
payload = trading_domain.get_line_items_payload(
items = search_line_items(
ticker=ticker,
line_items=line_items,
end_date=end_date,
period=period,
limit=limit,
)
return LineItemResponse(search_results=payload["search_results"])
return LineItemResponse(search_results=items)
return app