356 lines
13 KiB
Markdown
356 lines
13 KiB
Markdown
# PolyWeather API 文档(v1.8.1)
|
||
|
||
最后更新:`2026-05-28`
|
||
|
||
本文档描述当前对外可用 API 口径(`web/app.py` + `web/routes.py` + `frontend/app/api/*`)。
|
||
|
||
## 1. 基础信息
|
||
|
||
- 后端直连:`http://127.0.0.1:8000`
|
||
- 前端 BFF:`https://polyweather-pro.vercel.app/api/*`
|
||
- 返回格式:`application/json`
|
||
|
||
## 2. 请求链路
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
FE["Browser / Dashboard"] --> BFF["Next.js Route Handlers (/api/*)"]
|
||
BFF --> API["FastAPI (/web/app.py + /web/routes.py)"]
|
||
API --> WX["Weather Collector"]
|
||
API --> ANA["DEB + Hourly Consensus + Probability + Market Scan"]
|
||
API --> SSE["Realtime SSE (/api/events)"]
|
||
SSE --> EVENT["Redis Stream / SQLite Event Log"]
|
||
API --> PAY["Payment Intent + Event + Confirm Loops"]
|
||
API --> OBS["healthz / system status / metrics"]
|
||
```
|
||
|
||
## 3. 天气分析接口
|
||
|
||
| 接口 | 方法 | 用途 |
|
||
| :-- | :-- | :-- |
|
||
| `/api/cities` | GET | 监控城市列表 |
|
||
| `/api/city/{name}` | GET | 城市主分析 |
|
||
| `/api/city/{name}/summary` | GET | 轻量摘要 |
|
||
| `/api/city/{name}/detail` | GET | 聚合详情(含 market_scan) |
|
||
| `/api/history/{name}` | GET | 历史对账 |
|
||
| `/api/events` | GET | SSE 实时观测事件流 |
|
||
| `/api/internal/collector-patch` | POST | 采集器内部写入实时观测 patch |
|
||
|
||
### `GET /api/events`
|
||
|
||
浏览器实时图表入口,使用 `text/event-stream`。
|
||
|
||
参数:
|
||
|
||
- `cities=shanghai,hong kong`:可选,逗号分隔城市列表;为空表示订阅全部城市。
|
||
- `since_revision=<int>`:可选,断线重连后从指定 revision 之后 replay。
|
||
- `replay_limit=<int>`:可选,默认 `500`,后端会做上限保护。
|
||
|
||
事件:
|
||
|
||
- `connected`:连接建立,包含当前 `latest_revision`。
|
||
- `city_observation_patch.v1`:标准实时观测 patch,包含城市、序列、温度、观测 UTC 时间、城市时区、revision。
|
||
- `resync_required`:replay 窗口不足或事件存储不可用,前端应回到 HTTP snapshot 重建画面。
|
||
- `heartbeat`:保活。
|
||
|
||
生产环境推荐 `POLYWEATHER_EVENT_STORE=redis`,以 Redis Stream 保存短窗口事件并支持多 worker fanout;本地或单进程可使用 SQLite event log。
|
||
|
||
### `GET /api/city/{name}/detail`
|
||
|
||
可选参数:
|
||
|
||
- `force_refresh=true|false`
|
||
- `market_slug=<slug>`
|
||
- `target_date=YYYY-MM-DD`
|
||
|
||
重点字段:
|
||
|
||
- `market_scan.available`
|
||
- `market_scan.signal_label`
|
||
- `market_scan.edge_percent`
|
||
- `market_scan.anchor_model / anchor_high / anchor_settlement`
|
||
- `market_scan.yes_buy / no_buy`
|
||
- `market_scan.primary_market.tradable`
|
||
- `probabilities.engine / calibration_mode / calibration_version`
|
||
- `probabilities.raw_mu / raw_sigma / calibrated_mu / calibrated_sigma`
|
||
- `probabilities.shadow_distribution`
|
||
- `deb.hourly_consensus / deb.hourly_path.base_source`
|
||
- `intraday_meteorology.headline / confidence`
|
||
- `intraday_meteorology.base_case_bucket / upside_bucket / downside_bucket`
|
||
- `intraday_meteorology.next_observation_time`
|
||
- `intraday_meteorology.invalidation_rules / confirmation_rules / signal_contributions`
|
||
- `peak.first_h / peak.last_h / peak.status`
|
||
- `vertical_profile_signal.heating_setup / suppression_risk / trigger_risk / mixing_strength`
|
||
- `taf.signal.peak_window / suppression_level / disruption_level / markers`
|
||
|
||
### 城市决策卡市场层口径
|
||
|
||
- 前端会请求完整 `market_scan` / `all_buckets`,而不是只取 lite 结果。
|
||
- 温度桶匹配按今日预计最高温中枢映射,并区分 exact、range、or higher、or lower,避免把 30°C 附近的天气中枢误配到不合理尾部桶。
|
||
- `模型-市场差 = 模型概率 - 市场隐含概率`。正值表示天气概率高于市场报价;负值表示市场已经更充分计价。
|
||
- 温度桶标签会统一规范化 `C/F/°C/°F`,避免前端重复显示单位。
|
||
|
||
### `detail` 新增结构信号说明
|
||
|
||
`/api/city/{name}/detail` 现在会返回一组更偏交易场景的结构字段:
|
||
|
||
#### 1. `intraday_meteorology`
|
||
|
||
今日日内分析的专业气象判断层。该字段只做派生,不改变路由和缓存策略。
|
||
|
||
重点字段:
|
||
|
||
- `headline`:今日主判断,例如“峰值仍有上修空间 / 峰值受云雨压制”
|
||
- `confidence`:`low | medium | high`
|
||
- `base_case_bucket`:基准温度档位
|
||
- `upside_bucket`:上修路径档位
|
||
- `downside_bucket`:下修路径档位
|
||
- `next_observation_time`:下一次应重点看的本地时间
|
||
- `invalidation_rules`:2-4 条失效条件
|
||
- `confirmation_rules`:1-3 条确认条件
|
||
- `signal_contributions`:气象因子列表,含 `label`、`direction`、`strength`、`summary`
|
||
|
||
前端如果该字段暂缺,会降级使用现有 `paceView`、`boundaryRiskView`、`upperAirCue`、`probabilitySummary` 等字段。
|
||
|
||
#### 2. `probabilities`
|
||
|
||
概率层基于 legacy 高斯分桶,以 DEB 融合预测 `mu` 和 ensemble spread `sigma` 生成 1°C 粒度概率分布。前端图表将 legacy 高斯展示为水平概率温度带和 `mu` 参考线,不把概率分布渲染成时间序列曲线。
|
||
|
||
概率字段:
|
||
|
||
- `engine`:固定为 `legacy`
|
||
- `mu`:DEB 融合预测中心值
|
||
- `distribution`:当天合约桶概率分布
|
||
- `distribution_all`:包含外围桶的完整分布
|
||
|
||
#### 2.1 `deb.hourly_consensus`
|
||
|
||
DEB hourly consensus 是当前图表与峰值窗口的优先小时路径。
|
||
|
||
重点字段:
|
||
|
||
- `version`:当前为 `deb_hourly_consensus.v1`
|
||
- `base_source`:`multi_model_hourly_deb_weights`
|
||
- `times` / `temps`:城市当地日的小时路径
|
||
- `model_weights`:DEB 权重折叠后的模型权重
|
||
|
||
说明:
|
||
|
||
- 该路径是预测曲线,不是实测来源。
|
||
- 图表默认展示全天;“高温”视图仅根据该路径推导 peak-centric 窗口。
|
||
|
||
#### 3. `detail_depth`
|
||
|
||
`detail_depth` 用于区分轻量 detail 与完整 detail。前端如果发现:
|
||
|
||
- `detail_depth != "full"`
|
||
- 或 `forecast.daily` 只有当天一张卡
|
||
- 或模型层只剩单模型
|
||
|
||
会触发强刷完整 detail,并在 UI 上显示同步状态 / 占位卡,避免用户把中间态误判成完整分析。
|
||
|
||
#### 4. `peak`
|
||
|
||
- `first_h`:预计峰值窗口起始小时
|
||
- `last_h`:预计峰值窗口结束小时
|
||
- `status`:`before_peak | near_peak | after_peak`
|
||
|
||
这组字段用于让日内结构信号围绕真实峰值窗口分析,而不是固定只看下午。
|
||
|
||
#### 5. `vertical_profile_signal`
|
||
|
||
重点字段:
|
||
|
||
- `source`
|
||
- `window`
|
||
- `cape_max`
|
||
- `cin_min`
|
||
- `lifted_index_min`
|
||
- `boundary_layer_height_max`
|
||
- `shear_10m_180m_max`
|
||
- `suppression_risk`
|
||
- `trigger_risk`
|
||
- `mixing_strength`
|
||
- `shear_risk`
|
||
- `heating_setup`
|
||
- `heating_score`
|
||
- `summary_zh`
|
||
- `summary_en`
|
||
|
||
这组字段对应前端“高空结构信号 / Upper-Air Structure”卡片。
|
||
|
||
#### 6. `taf.signal`
|
||
|
||
仅对**非香港机场城市**启用。当前已支持解析:
|
||
|
||
- `FM`
|
||
- `TEMPO`
|
||
- `BECMG`
|
||
- `PROB30`
|
||
- `PROB40`
|
||
|
||
重点字段:
|
||
|
||
- `peak_window`
|
||
- `segments`
|
||
- `markers`
|
||
- `suppression_level`
|
||
- `disruption_level`
|
||
- `wind_shift`
|
||
- `summary_zh`
|
||
- `summary_en`
|
||
|
||
`markers` 会被前端温度走势图拿来做 `TAF 时段 / TAF Timing` 标记。
|
||
|
||
#### 7. 结算锚点口径
|
||
|
||
- 多数机场市场以 `METAR` / 机场主站实况为结算锚点。
|
||
- `Wunderground` 是历史页面或参考入口,不应在产品文案里被描述成“站”。
|
||
- `MGM / NMC / JMA / AMOS / HKO / CWA` 等官方站网属于增强层或明确官方站点层;只有合约规则明确指定时,才作为最终结算站点。
|
||
|
||
## 4. 鉴权与账户接口
|
||
|
||
| 接口 | 方法 | 用途 |
|
||
| :-- | :-- | :-- |
|
||
| `/api/auth/me` | GET | 当前登录态、积分、订阅状态 |
|
||
|
||
`/api/auth/me` 关键字段:
|
||
|
||
- `authenticated`
|
||
- `user_id`, `email`
|
||
- `points`, `weekly_points`, `weekly_rank`
|
||
- `subscription_active`, `subscription_plan_code`, `subscription_expires_at`
|
||
|
||
## 5. 支付接口
|
||
|
||
| 接口 | 方法 | 用途 |
|
||
| :-- | :-- | :-- |
|
||
| `/api/payments/config` | GET | 支付配置、代币列表、套餐、积分抵扣规则 |
|
||
| `/api/payments/runtime` | GET | 支付运行态、RPC 状态、event loop 状态、最近审计事件 |
|
||
| `/api/payments/wallets` | GET | 当前用户已绑定钱包 |
|
||
| `/api/payments/wallets/challenge` | POST | 获取绑定签名 challenge |
|
||
| `/api/payments/wallets/verify` | POST | 提交签名并绑定钱包 |
|
||
| `/api/payments/intents` | POST | 创建支付意图(intent) |
|
||
| `/api/payments/intents/{intent_id}` | GET | 查询 intent 最新状态 |
|
||
| `/api/payments/intents/{intent_id}/submit` | POST | 提交交易哈希 |
|
||
| `/api/payments/intents/{intent_id}/confirm` | POST | 手动触发确认 |
|
||
| `/api/payments/reconcile-latest` | POST | 对当前登录用户最近一笔 intent 做恢复性确认 |
|
||
|
||
### 支付状态建议
|
||
|
||
`POST /api/payments/intents` 支持的关键字段:
|
||
|
||
- `plan_code`:套餐,例如 `pro_monthly`
|
||
- `payment_mode`:`wallet` 或 `direct`
|
||
- `chain_id`:可选;多链支付时前端传用户选择的链,例如 Polygon `137` 或 Ethereum `1`
|
||
- `token_address`:可选;指定该链上的 USDC / USDC.e 合约地址
|
||
|
||
前端流程建议:
|
||
|
||
1. `POST /intents`
|
||
2. 钱包发链上交易
|
||
3. `POST /submit`
|
||
4. `POST /confirm`
|
||
5. 若 pending,轮询 `GET /intents/{id}` 直到 `confirmed`
|
||
|
||
## 6. 运维与观测接口
|
||
|
||
| 接口 | 方法 | 用途 |
|
||
| :-- | :-- | :-- |
|
||
| `/healthz` | GET | 基础健康检查 |
|
||
| `/api/system/status` | GET | 系统状态、功能开关、rollout 状态、轻量指标摘要 |
|
||
| `/metrics` | GET | Prometheus 风格指标导出 |
|
||
|
||
`/api/system/status` 当前会包含:
|
||
|
||
- `features.state_storage_mode`
|
||
- `probability.decision`
|
||
- `probability.ready_for_primary`
|
||
- `metrics`
|
||
|
||
`/metrics` 当前会导出:
|
||
|
||
- `polyweather_http_requests_total`
|
||
- `polyweather_http_request_duration_ms_*`
|
||
- `polyweather_source_requests_total`
|
||
- `polyweather_source_request_duration_ms_*`
|
||
|
||
## 7. Ops 管理接口
|
||
|
||
这些接口主要给 `/ops` 管理后台使用,默认要求:
|
||
|
||
- 已登录
|
||
- 当前邮箱位于 `POLYWEATHER_OPS_ADMIN_EMAILS`
|
||
|
||
| 接口 | 方法 | 用途 |
|
||
| :-- | :-- | :-- |
|
||
| `/api/ops/users` | GET | 按 Telegram ID / 用户名 / 邮箱查询用户 |
|
||
| `/api/ops/leaderboard/weekly` | GET | 本周积分榜 |
|
||
| `/api/ops/memberships` | GET | 当前有效会员(已按用户去重,保留最晚到期) |
|
||
| `/api/ops/users/grant-points` | POST | 手动补分 |
|
||
| `/api/ops/payments/incidents` | GET | 支付异常单(仅 `payment_intent_failed`) |
|
||
| `/api/ops/payments/incidents/{event_id}/resolve` | POST | 标记支付异常单已处理 |
|
||
|
||
`/api/ops/payments/incidents` 当前支持:
|
||
|
||
- `reason=<receiver_mismatch|sender_mismatch|event_mismatch|tx_reverted>`
|
||
- 默认不返回已标记处理的记录
|
||
- 重点用于排查“已付款未开通”“打到旧收款地址”等事故
|
||
## 8. 缓存策略(当前)
|
||
|
||
- `cities` / `summary` / `history`:BFF 支持 `ETag + 304`
|
||
- `summary?force_refresh=true`:`Cache-Control: no-store`
|
||
- 详情接口与支付接口:`no-store`
|
||
- `METAR` / `TAF` / settlement current 由后端各自维护短 TTL 缓存
|
||
- 实时事件层:`POLYWEATHER_EVENT_STORE=redis` 时使用 Redis Stream;未启用 Redis 时使用 SQLite `observation_patch_events` 作为 replay fallback
|
||
- 前端终端图表:HTTP detail 仍是完整 snapshot,SSE patch 只做增量观测追加;长连接断开或后台恢复时前端会按 `since_revision` replay 或强刷 detail
|
||
- 前端打开今日日内分析时,如果 full detail 或 market scan 正在同步,会先显示刷新锁,不展示可交互的旧内容
|
||
- 城市决策卡 AI 解读前端缓存键为 `city + local_date + locale + METAR signature`;signature 优先使用原始 METAR,缺失时回退到报文时间、观测时间和温度
|
||
- 城市决策卡 AI 解读使用两层前端缓存:页面内存缓存保存 loading / 流式进度 / 最终 payload,`localStorage` 保存最终成功 payload,默认 TTL 1 小时
|
||
- 后端城市 AI 缓存不使用 `local_time` 作为 key,避免同一观测因当前时钟变化反复失效
|
||
- 城市市场扫描完整桶缓存按 `city + local_date + full` 存储,默认 TTL 10 分钟
|
||
|
||
## 9. 调试示例
|
||
|
||
### 查询未来日期 market_scan
|
||
|
||
```bash
|
||
curl -s "http://127.0.0.1:8000/api/city/ankara/detail?force_refresh=true&target_date=2026-03-12"
|
||
```
|
||
|
||
### 校验支付配置
|
||
|
||
```bash
|
||
curl -s http://127.0.0.1:8000/api/payments/config | python3 -m json.tool
|
||
```
|
||
|
||
### 查看支付运行态
|
||
|
||
```bash
|
||
curl -s http://127.0.0.1:8000/api/payments/runtime | python3 -m json.tool
|
||
```
|
||
|
||
### 查看支付异常单
|
||
|
||
```bash
|
||
curl -s "http://127.0.0.1:8000/api/ops/payments/incidents?reason=receiver_mismatch" | python3 -m json.tool
|
||
```
|
||
|
||
### 查看系统状态
|
||
|
||
```bash
|
||
curl -s http://127.0.0.1:8000/api/system/status | python3 -m json.tool
|
||
```
|
||
|
||
### 观察支付自动补单
|
||
|
||
```bash
|
||
docker compose logs -f polyweather | egrep "payment event loop started|payment confirm loop started|payment auto-confirmed"
|
||
```
|
||
|
||
## 10. AGPL 与公开口径说明
|
||
|
||
本仓库代码自 `2026-03-30` 起采用 `AGPL-3.0-only`。对外公开文档仅覆盖通用 API 契约;生产商业策略参数、私有运营阈值与托管服务能力不在公开文档披露。
|
||
|
||
详见:[AGPL-3.0 与商用边界](OPEN_CORE_POLICY.md)
|