- 新表 ingest_jobs(迁移 0019): id UUID/task/params JSONB/
status(pending|running|success|failed,CheckConstraint)/
result JSONB(统计摘要)/error/created_at/started_at/finished_at
- POST /ingest/bzzoiro: 启动后台前创建 pending job,响应返回 job_id;
仍 require_admin。后台 _run_bzzoiro 流转 running→success/failed,
result 按子任务(events/standings/stats)记录摘要(errors 截断 10 条)
- _update_job 尽力而为: 状态更新失败只记日志,绝不拖垮采集主流程;
与 IngestFailure 死信独立(行级 vs 任务级,可同时存在)
- 新增 admin 端点(挂 /api/v1/admin 路由,路由级 require_admin):
GET /admin/ingest/jobs/{job_id} 与 GET /admin/ingest/jobs?limit&status
- 前端采集页: 提交后凭 job_id 3 秒轮询,终态展示结果摘要/失败原因;
无 job_id 时回退旧的 30 秒盲等 + 系统日志提示
- 测试 11 项: 建 job+job_id 契约、非法 task 422、成功/失败/all 流转、
update 失败不拖垮采集、部分失败仍 success、admin 端点 200/404/列表、
结构守护(job 路由在 admin 路由且带 require_admin)
- 禁止项确认: 未动分批 UoW、BzzoiroSource、死信与 Bronze 写入;
docs(01/03/05/07/README)同步 13 张表与端点说明
254 lines
8.4 KiB
Markdown
254 lines
8.4 KiB
Markdown
# 03 · API 参考
|
||
|
||
Base URL: `http://localhost:8000` · 交互式文档: `/docs`(Swagger)与 `/redoc`
|
||
|
||
所有数据端点返回 JSON。错误统一为 `{"detail": "<message>"}` + 对应 HTTP 状态码。
|
||
|
||
## 鉴权模型
|
||
|
||
| 级别 | 端点 | 说明 |
|
||
|---|---|---|
|
||
| **公开只读** | `GET /leagues`、`GET /matches`、`GET /matches/{id}`、`GET /matches/{id}/context`、`GET /standings`、`GET /health*` | 无需任何凭据;公开站直接调用 |
|
||
| **公开 + 限流** | `POST /predict` | 内存滑动窗口限流(10 次/分钟/IP) |
|
||
| **需管理员** | `GET /predictions*`、`POST /ingest/bzzoiro`、`/eval/*`、`POST /backtest`、`/admin/**` | Cookie 会话(`POST /auth/login` 颁发)或 `X-API-Key` 头 |
|
||
|
||
管理端点在生产环境未配置鉴权时 fail-closed(503),不会静默放行。
|
||
|
||
---
|
||
|
||
## 数据查询(公开只读)
|
||
|
||
### `GET /api/v1/leagues`
|
||
|
||
列出已入库联赛(P1-3: 公开站联赛筛选动态加载来源)。
|
||
|
||
```json
|
||
[{"id": 1, "code": "E0", "name": "Premier League", "country": "England"}]
|
||
```
|
||
|
||
仅返回 `id/code/name/country` 四个展示字段,不含任何配置或密钥信息。
|
||
|
||
### `GET /api/v1/matches`
|
||
|
||
比赛列表,游标分页。
|
||
|
||
| 参数 | 说明 |
|
||
|---|---|
|
||
| `league` | 联赛代码,如 `E0` / `SP1` / `D1` / `I1` / `F1` |
|
||
| `status` | `scheduled` / `finished`(不传 = 全部) |
|
||
| `date` | `YYYY-MM-DD`,当天比赛 |
|
||
| `cursor` | 上一页返回的 `next_cursor` |
|
||
| `limit` | 1–100,默认 50 |
|
||
|
||
响应(倒序,含双方中文名与 xG):
|
||
|
||
```json
|
||
{
|
||
"items": [
|
||
{"id": 42, "league_code": "E0", "season": "2026-2027",
|
||
"home_team": "Arsenal", "away_team": "Manchester United",
|
||
"home_team_zh": null, "away_team_zh": null,
|
||
"match_date": "2026-09-15T19:00:00Z", "match_status": "scheduled",
|
||
"home_goals": null, "away_goals": null,
|
||
"match_stage": "第 5 轮", "home_xg": null, "away_xg": null}
|
||
],
|
||
"next_cursor": "2026-09-15T19:00:00+00:00|41",
|
||
"has_more": true
|
||
}
|
||
```
|
||
|
||
### `GET /api/v1/matches/{id}`
|
||
|
||
单场比赛详情,字段同上,另含 `stats`(统计)与 `recent_predictions`(最近 5 条预测摘要)。
|
||
|
||
### `GET /api/v1/matches/{id}/context`
|
||
|
||
比赛上下文(公开只读,P1-2: 公开站详情页「近况/交锋」数据来源;不触发 LLM):
|
||
|
||
- `home_recent`: 主队最近 5 场已完赛
|
||
- `away_recent`: 客队最近 5 场已完赛
|
||
- `h2h`: 双方最近 5 次交手
|
||
|
||
```json
|
||
{
|
||
"home_recent": [
|
||
{"match_date": "2026-09-12T14:00:00+00:00", "home_team": "阿森纳",
|
||
"away_team": "切尔西", "home_goals": 2, "away_goals": 1}
|
||
],
|
||
"away_recent": [],
|
||
"h2h": []
|
||
}
|
||
```
|
||
|
||
数据不足时对应列表为空(前端展示空态)。比赛不存在返回 404。
|
||
|
||
### `GET /api/v1/standings`
|
||
|
||
联赛积分榜(公开只读)。参数:`league`(联赛代码,空 = 全部)、`season`(空 = 各联赛最新赛季)。
|
||
|
||
```json
|
||
{"leagues": [
|
||
{"league_code": "E0", "league_name": "Premier League", "season": "2026-2027",
|
||
"retrieved_at": "2026-09-20T08:00:00+00:00",
|
||
"rows": [{"position": 1, "team": "阿森纳", "team_en": "Arsenal",
|
||
"played": 5, "won": 4, "drawn": 1, "lost": 0,
|
||
"goals_for": 11, "goals_against": 3, "goal_diff": 8,
|
||
"points": 13, "xg_for": 9.8, "xg_against": 3.9,
|
||
"form": "WWWDW", "zone": "UEFA Champions League"}]}
|
||
]}
|
||
```
|
||
|
||
---
|
||
|
||
## 预测
|
||
|
||
### `POST /api/v1/predict`
|
||
|
||
对一场比赛做 LLM 预测。**核心端点。**
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"match_id": 42,
|
||
"mode": "multi",
|
||
"model": "gpt-4o",
|
||
"prompt_version": "v1"
|
||
}
|
||
```
|
||
|
||
| 字段 | 默认 | 说明 |
|
||
|---|---|---|
|
||
| `match_id` | 必填 | 比赛 ID |
|
||
| `mode` | `multi` | `multi` = 5 专家 + 终裁;`single` = 单次调用 |
|
||
| `model` | 配置值 | 覆盖本次模型(single 模式下生效) |
|
||
| `prompt_version` | `v1` | prompt 版本(multi 模式即 agent prompt 版本) |
|
||
|
||
响应(multi 模式):
|
||
|
||
```json
|
||
{
|
||
"prediction_id": 7,
|
||
"provider": "openai",
|
||
"model": "gpt-4o",
|
||
"prompt_version": "multi_v1",
|
||
"mode": "multi",
|
||
"pred_home_goals": 2.1,
|
||
"pred_away_goals": 1.0,
|
||
"pred_1x2": "1",
|
||
"subjective_confidence": 0.68,
|
||
"reasoning": "综合 xg 报告的进球期望 2.1-1.0 与 form 报告的三连胜势头……",
|
||
"agent_outputs": [
|
||
{"agent": "h2h", "status": "ok", "data_sufficiency": "medium",
|
||
"analysis": "近 5 次交锋主队 3 胜……", "home_edge": 0.4,
|
||
"subjective_confidence": 0.7, "key_evidence": ["近5次交锋主队3胜", "主场交锋3连胜"],
|
||
"exp_home_goals": null, "exp_away_goals": null, "probable_score": null,
|
||
"model": "gpt-4o-mini", "latency_ms": 2100,
|
||
"prompt_tokens": 380, "completion_tokens": 120},
|
||
{"agent": "standings", "status": "no_data", "data_sufficiency": "none",
|
||
"analysis": "该维度无数据,跳过分析。", "home_edge": null, "subjective_confidence": null,
|
||
"key_evidence": [], "exp_home_goals": null, "exp_away_goals": null,
|
||
"probable_score": null, "model": "", "latency_ms": null,
|
||
"prompt_tokens": null, "completion_tokens": null}
|
||
],
|
||
"agent_weights": {"form": 0.9, "stats": 0.8, "home_away": 0.7, "standings": 0.8, "h2h": 0.8},
|
||
"context": "[5 份报告的 JSON 串]",
|
||
"latency_ms": 9800
|
||
}
|
||
```
|
||
|
||
`agent.status` 取值:`ok` / `no_data`(维度无数据,已跳过 LLM)/ `error`(调用失败,fail-open 不阻断)/ `parse_error`。
|
||
|
||
错误:404 比赛不存在;502 LLM 调用失败(终裁失败时整体失败,专家失败不会)。
|
||
|
||
### `GET /api/v1/predictions?match_id=&limit=`(需管理员)
|
||
|
||
预测历史(倒序),含 `settled` 与实际比分回填状态。
|
||
|
||
### `GET /api/v1/predictions/{id}`(需管理员)
|
||
|
||
单条预测详情(含完整 `agent_outputs`)。
|
||
|
||
---
|
||
|
||
## 数据采集
|
||
|
||
### `POST /api/v1/ingest/bzzoiro`(需管理员)
|
||
|
||
从 bzzoiro(唯一数据源)采集数据并入库(幂等,重复跑安全)。任务在后台异步执行,请求立即返回。
|
||
|
||
```json
|
||
{"task": "all", "leagues": ["E0", "SP1"], "date_from": "2025-08-01", "date_to": "2026-09-08", "status": "finished"}
|
||
```
|
||
|
||
| 字段 | 默认 | 说明 |
|
||
|---|---|---|
|
||
| `task` | `events` | 采集任务:`events`(比赛)/ `standings`(积分榜)/ `stats`(统计回填,含 xG)/ `all` |
|
||
| `leagues` | 全部已知联赛 | 联赛代码列表,如 `["E0", "SP1"]` |
|
||
| `date_from` / `date_to` | 空 | 日期范围(`YYYY-MM-DD`) |
|
||
| `status` | 空(两者都采) | `finished`(已完赛)/ `scheduled`(未来赛程) |
|
||
| `season` | 当前赛季 | standings 赛季,如 `"2026-2027"` |
|
||
| `limit` | 100 | stats 回填单次最大比赛数(1–500) |
|
||
|
||
- 响应:`{"ok": true, "job_id": "<uuid>", "message": "……"}`,`job_id` 用于查询任务状态
|
||
- `task=stats` 只补空字段、不创建比赛(xG/射门/控球等统计回填)
|
||
|
||
### `GET /api/v1/admin/ingest/jobs/{job_id}`(需管理员)
|
||
|
||
查询一次采集任务的状态(`ingest_jobs` 表,任务级可观测性):
|
||
|
||
```json
|
||
{"id": "…", "task": "standings", "params": {"leagues": ["E0"]},
|
||
"status": "success", "result": {"total_upserted": 20, "errors": []},
|
||
"error": null, "created_at": "…", "started_at": "…", "finished_at": "…"}
|
||
```
|
||
|
||
- `status` 取值:`pending`(已创建未开始)/ `running` / `success` / `failed`
|
||
- 管理端采集页提交后凭 `job_id` 轮询本端点直至终态
|
||
- 与 `ingest_failures` 死信独立:死信记录单条管线抓取失败(行级),job 记录整次任务结果
|
||
|
||
### `GET /api/v1/admin/ingest/jobs?limit=20&status=`(需管理员)
|
||
|
||
列出最近采集任务(最新在前),可按 `status` 过滤,`limit` 1–100。
|
||
|
||
> 历史版本曾有独立的 understat(xG)与 injuries(伤停)采集端点,
|
||
> 已随数据源收敛为 bzzoiro 唯一来源而移除。
|
||
|
||
---
|
||
|
||
## 评估
|
||
|
||
### `POST /api/v1/eval/settle`(需管理员)
|
||
|
||
赛后回填实际比分:
|
||
|
||
```json
|
||
{"prediction_id": 7, "home_goals": 2, "away_goals": 1}
|
||
```
|
||
|
||
### `GET /api/v1/eval/summary`(需管理员)
|
||
|
||
按 `provider × model` 聚合已结算预测:
|
||
|
||
```json
|
||
{"summary": [
|
||
{"provider": "openai", "model": "gpt-4o", "total": 12,
|
||
"accuracy_1x2": 58.3, "avg_score_rmse": 1.21, "avg_subjective_confidence": 0.65}
|
||
]}
|
||
```
|
||
|
||
> 提示:multi 模式存的 `model` 是终裁模型、`prompt_version` 是 `multi_v1`,
|
||
> 因此 summary 里天然可对比 multi vs single、以及不同 prompt 版本的效果。
|
||
|
||
---
|
||
|
||
## 基础
|
||
|
||
| 端点 | 权限 | 说明 |
|
||
|---|---|---|
|
||
| `GET /health` | 公开 | 存活检查 |
|
||
| `GET /docs`、`GET /redoc` | 公开 | Swagger UI / ReDoc |
|
||
| `POST /auth/login`、`POST /auth/logout`、`GET /auth/me` | 公开 | 管理员 Cookie 会话登录/登出/当前用户 |
|
||
| `POST /auth/change-password` | 需管理员 | 修改管理员密码 |
|
||
| `POST /backtest` | 需管理员 | 历史回测(对已完赛比赛批量预测并评估) |
|