P2-1 no_data 门控依赖文案子串(脆弱): - context_builder 新增 SliceResult(text/has_data/n_records), 5 个切片函数改为显式声明 has_data - base._slice_has_data() 优先取结构化结果,str 返回仍走文案回退 (兼容既有测试 mock 与自定义切片) - build_context 的 has_stats/has_injuries 直接取切片声明 P2-2 agent_weights 无校验即落库: - validation 新增 AgentWeightsSchema / validate_agent_weights: 未知专家名丢弃、越界值钳制、总和非 1 时归一化 - orchestrator 落库前对 agent_weights 做校验 P2-3 1x2 与比分不一致被静默修正: - 仍以比分修正,但补 logger.warning 暴露 LLM 自相矛盾 P2-5/P2-6 prompt 缓存不可刷新 + 缓存键不含模板内容: - 新增 clear_prompt_cache() 供改模板后显式失效 - 缓存键纳入模板内容 hash,模板一改缓存自动失效 P2-7 ingest/backtest/settle 接口无鉴权: - 新增 require_admin_key 依赖(X-API-Key), ADMIN_API_KEY 未设置时放行并告警(不破坏本地开发) - 挂到 3 个 ingest 接口 + backtest + eval/settle P2-8 前端请求竞态 + 未使用游标分页: - Matches.tsx 用递增 seq 丢弃过期响应,避免旧筛选结果覆盖新筛选 - 接入后端已有的 cursor 分页 + 「加载更多」按钮 附带: .env.example 补齐 LLM_TIMEOUT / 分档模型 / ADMIN_API_KEY; tests 新增 10 个用例覆盖 P2-1/2/3。
61 lines
2.3 KiB
Python
61 lines
2.3 KiB
Python
"""采集路由。"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
|
|
from src.api.deps import require_admin_key
|
|
from src.api.schemas import IngestBzzoiroRequest, IngestResponse, IngestUnderstatRequest, IngestInjuriesRequest, IngestSimpleResponse
|
|
from src.data.sources import get_source
|
|
from src.data.injuries import ingest_injuries
|
|
from src.db.unit_of_work import get_uow
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api/v1", tags=["ingest"])
|
|
|
|
|
|
@router.post("/ingest/bzzoiro", response_model=IngestResponse, dependencies=[Depends(require_admin_key)])
|
|
async def ingest_bzzoiro_route(req: IngestBzzoiroRequest):
|
|
"""触发 bzzoiro 采集。"""
|
|
source = get_source("bzzoiro")
|
|
try:
|
|
async with get_uow() as session:
|
|
result = await source.ingest(
|
|
session,
|
|
leagues=req.leagues,
|
|
date_from=req.date_from,
|
|
date_to=req.date_to,
|
|
status=req.status,
|
|
)
|
|
return IngestResponse(**result)
|
|
except Exception as e:
|
|
logger.exception("bzzoiro ingest failed")
|
|
raise HTTPException(500, "数据采集失败,请查看服务器日志")
|
|
|
|
|
|
@router.post("/ingest/understat", response_model=IngestSimpleResponse, dependencies=[Depends(require_admin_key)])
|
|
async def ingest_understat_route(req: IngestUnderstatRequest):
|
|
"""触发 understat xG 回填。"""
|
|
source = get_source("understat")
|
|
try:
|
|
async with get_uow() as session:
|
|
result = await source.ingest(session, league=req.league, season=req.season)
|
|
return IngestSimpleResponse(**result)
|
|
except Exception as e:
|
|
logger.exception("understat ingest failed")
|
|
raise HTTPException(500, "xG 回填失败,请查看服务器日志")
|
|
|
|
|
|
@router.post("/ingest/injuries", response_model=IngestSimpleResponse, dependencies=[Depends(require_admin_key)])
|
|
async def ingest_injuries_route(req: IngestInjuriesRequest):
|
|
"""触发伤停采集。"""
|
|
try:
|
|
async with get_uow() as session:
|
|
result = await ingest_injuries(session, date=req.date)
|
|
return IngestSimpleResponse(**result)
|
|
except Exception as e:
|
|
logger.exception("injuries ingest failed")
|
|
raise HTTPException(500, "伤停采集失败,请查看服务器日志")
|