docs: refresh v1.8.1 release docs
This commit is contained in:
@@ -19,6 +19,13 @@ UVICORN_WORKERS=1
|
||||
UID=1000
|
||||
GID=1000
|
||||
POLYWEATHER_STATE_STORAGE_MODE=sqlite
|
||||
# Realtime chart event store. Production should use Redis Stream; local/single-process
|
||||
# development can set POLYWEATHER_EVENT_STORE=sqlite.
|
||||
POLYWEATHER_EVENT_STORE=redis
|
||||
POLYWEATHER_REDIS_URL=redis://polyweather_redis:6379/0
|
||||
POLYWEATHER_REDIS_STREAM_KEY=stream:city_observation
|
||||
POLYWEATHER_REDIS_STREAM_MAXLEN=50000
|
||||
POLYWEATHER_REDIS_REQUIRED=true
|
||||
# Backend CORS allowlist. Add your Vercel production/preview domains when
|
||||
# NEXT_PUBLIC_POLYWEATHER_API_BASE_URL points browsers directly at this backend.
|
||||
WEB_CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:3000,https://polyweather.top,https://www.polyweather.top,https://api.polyweather.top
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# Changelog
|
||||
|
||||
## 1.8.1 - 2026-05-28
|
||||
|
||||
### 文档与发布
|
||||
- README / README_ZH 改用 `frontend/public/static/web.png` 与 `frontend/public/static/tel.png` 作为产品截图,并移除旧 `docs/images` README 截图引用。
|
||||
- 同步版本源到 `1.8.1`,刷新 API、Supabase、技术债、PolygonScan 验证等文档标题版本。
|
||||
- 更新前端、实时事件、数据源、模型栈与服务文档,补齐 Redis Stream + SSE Patch、DEB hourly consensus、城市当地时间图表、legacy 高斯图表叠加、跑道/CoWIN 曲线和中英文 Telegram 推送口径。
|
||||
|
||||
### 当前线上口径确认
|
||||
- 生产实时层为“HTTP snapshot + SSE patch + replayable event store”;前端只消费 `/api/events`,不直接连接 Redis。
|
||||
- `deb_hourly_consensus.v1` 是峰值窗口与 DEB 曲线展示的优先小时路径;DEB 不作为实测来源。
|
||||
- AMSC/AMOS 跑道曲线和香港 CoWIN 6087 参考站曲线按城市当地时间展示,结算跑道高亮,辅助跑道弱化。
|
||||
|
||||
|
||||
## 1.8.0 - 2026-05-27
|
||||
|
||||
### 新增与重构
|
||||
|
||||
@@ -9,19 +9,19 @@ Public docs center: `/docs/intro` on the main site (bilingual product documentat
|
||||
|
||||
## Product Screenshots
|
||||
|
||||
### Global Dashboard
|
||||
### Realtime Terminal
|
||||
|
||||

|
||||

|
||||
|
||||
### City Analysis (Ankara)
|
||||
### Telegram Runway Alerts
|
||||
|
||||

|
||||

|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#yangyuan-zhen/PolyWeather&Date)
|
||||
|
||||
## Product Status (2026-05-23)
|
||||
## Product Status (2026-05-28)
|
||||
|
||||
- Subscription live: `Pro Monthly 10 USDC`.
|
||||
- Points system live: earn via group chat, welcome bonus (+20), first-message-of-day bonus (+2), weekly participation rewards.
|
||||
@@ -31,12 +31,21 @@ Public docs center: `/docs/intro` on the main site (bilingual product documentat
|
||||
- Auto-reconciliation live: event listener + periodic confirm loop.
|
||||
- Ops dashboard live: `/ops` for memberships, leaderboard, manual point grants, and payment incident triage.
|
||||
- Lightweight observability live: `/healthz`, `/api/system/status`, `/metrics`.
|
||||
- Realtime terminal live: visible city charts subscribe through `/api/events?cities=...&since_revision=...`, receive `city_observation_patch.v1` SSE patches, and replay short gaps from Redis Stream in production or SQLite fallback in local/single-node mode.
|
||||
- Chart refresh is observation-driven: live patches merge into the current chart without a loading overlay; only visible charts run a 60s no-patch fallback, and returning from a background browser tab triggers a foreground catch-up refresh.
|
||||
- Temperature charts default to All Day, keep an optional Peak window derived from the DEB hourly path, and render all timestamps in the selected city's local time.
|
||||
- The chart core has been split into focused logic/canvas/state modules; Recharts now receives explicit measured dimensions to avoid 0x0 rendering and disappearing curves.
|
||||
- DEB hourly consensus (`deb_hourly_consensus.v1`) is now the preferred hourly forecast path for peak-window detection and chart overlays; DEB remains a forecast curve, never an observation source.
|
||||
- Legacy Gaussian probability is rendered as horizontal probability bands and a `mu` reference line on the chart, rather than as a fake time-series curve.
|
||||
- Settlement runway curves are visible by default for AMSC/AMOS cities; the configured settlement runway is highlighted and auxiliary runways are shown as secondary context.
|
||||
- Hong Kong uses CoWIN station `6087` (Po Leung Kuk Choi Kai Yau School) as the 1-minute reference-station curve, with HKO 10-minute observations kept as the official meteorological layer.
|
||||
- Telegram airport/runway pushes are bilingual by default and use settlement-endpoint runway temperatures for slope/current/summary copy.
|
||||
- Runtime state, cache, and core offline training/backfill flows now use SQLite as the primary path; legacy JSON/JSONL files remain only for migration, export, and explicit fallback input.
|
||||
- EMOS/CRPS calibration is wired and trainable, but production should stay on `legacy` or `emos_shadow`; `emos_primary` is only for candidates that pass local offline evaluation and manual rollout.
|
||||
- Intraday analysis is now positioned as a professional meteorology read: headline, confidence, base/upside/downside paths, next observation point, evidence chain, failure modes, and confirmation rules.
|
||||
- Intraday modal now blocks stale cached detail during refresh, so users do not briefly trade off old city/date data before full detail arrives.
|
||||
- City decision cards now include the AI airport read: METAR, DEB, model cluster, and the AI expected-high center are resolved before mapping the result to temperature buckets.
|
||||
- AI airport reads now use in-page memory cache, browser `localStorage`, and backend short-TTL cache; returning from another dashboard tab restores existing stream text or final results before any new request is needed.
|
||||
- Terminal city cards now combine settlement observations, DEB hourly consensus, model cluster context, calibrated probability, and market-bucket mapping without blocking the chart on AI text generation.
|
||||
- Terminal data uses page memory cache, browser `localStorage`, backend short-TTL cache, SSE patch replay, and foreground refresh so returning from another tab restores the latest visible chart state quickly.
|
||||
- Market bucket matching now uses the full `all_buckets` surface and strict exact / range / or-higher / or-lower direction checks, reducing bad matches to unreasonable tail buckets.
|
||||
- The card label “model-market difference” means `model probability - market-implied probability`; positive values indicate weather probability above market pricing, while negative values indicate the YES is already priced more fully.
|
||||
- Calibrated model probability is now the primary probability panel. It shows the active production probability engine (legacy Gaussian or EMOS), while model consensus remains a secondary reference.
|
||||
@@ -62,8 +71,9 @@ See: [AGPL-3.0 & Commercial Boundary](docs/OPEN_CORE_POLICY.md)
|
||||
|
||||
- Aggregates observations and forecasts for 51 monitored cities.
|
||||
- Uses DEB (Dynamic Error Balancing) to blend multi-model highs.
|
||||
- Builds a DEB-weighted hourly consensus path for peak-window logic and chart display.
|
||||
- Generates settlement-oriented calibrated probability buckets (`mu` + bucket distribution) via legacy Gaussian or EMOS/CRPS calibration.
|
||||
- Adds city decision cards that combine AI airport reads, expected-high centers, full market-bucket mapping, and model-market difference in one view.
|
||||
- Adds city decision cards that combine live observations, expected-high centers, full market-bucket mapping, and model-market difference in one view.
|
||||
- Reuses one analysis core across web dashboard and Telegram bot.
|
||||
- Adds payment audit trails, replay tooling, and incident visibility in ops.
|
||||
- Adds peak-window-oriented intraday analysis with meteorology headline, path buckets, evidence chain, invalidation rules, and confirmation rules.
|
||||
@@ -88,7 +98,10 @@ flowchart LR
|
||||
WX --> AMOS["AMOS runway sensors (Korea)"]
|
||||
WX --> HKO["HKO / CWA / NOAA / Official settlement sources"]
|
||||
|
||||
API --> ANA["DEB + Trend + Probability + Market Scan"]
|
||||
API --> ANA["DEB + Hourly Consensus + Probability + Market Scan"]
|
||||
API --> SSE["SSE /api/events"]
|
||||
WX --> SSE
|
||||
SSE --> EVENT["Redis Stream / SQLite Event Log"]
|
||||
ANA --> PAY["Payment State (Intent + Event + Confirm Loop)"]
|
||||
ANA --> STATE["SQLite runtime state"]
|
||||
```
|
||||
@@ -125,6 +138,8 @@ npm run dev
|
||||
- `TAF` is used as an airport-side confirmation layer, not as the main temperature model.
|
||||
- Calibrated probability uses legacy Gaussian (default) or EMOS/CRPS when evaluated; model vote counts remain an explanatory consensus line, not the final probability.
|
||||
- Browser extension remains a lightweight monitoring + basic-bias product, while the site holds the full analysis experience.
|
||||
- Realtime terminal charts use SSE patches plus replayable event storage; full HTTP detail remains the authoritative snapshot.
|
||||
- Chart observations are shown in the city's local time, not the browser timezone.
|
||||
|
||||
## Runtime Data (Recommended on VPS)
|
||||
|
||||
@@ -134,8 +149,14 @@ Use external runtime storage to avoid SQLite/git conflicts:
|
||||
POLYWEATHER_RUNTIME_DATA_DIR=/var/lib/polyweather
|
||||
POLYWEATHER_DB_PATH=/var/lib/polyweather/polyweather.db
|
||||
POLYWEATHER_STATE_STORAGE_MODE=sqlite
|
||||
POLYWEATHER_EVENT_STORE=redis
|
||||
POLYWEATHER_REDIS_URL=redis://polyweather_redis:6379/0
|
||||
POLYWEATHER_REDIS_STREAM_MAXLEN=50000
|
||||
POLYWEATHER_REDIS_REQUIRED=true
|
||||
```
|
||||
|
||||
For local development or a strict single-process fallback, keep `POLYWEATHER_EVENT_STORE=sqlite`.
|
||||
|
||||
## EMOS Local Training
|
||||
|
||||
Do not run full EMOS retraining on a small VPS. The VPS should collect data and load approved calibration files; training should run on a local/dev machine using a copied production SQLite database:
|
||||
@@ -214,5 +235,5 @@ curl http://127.0.0.1:8000/api/payments/runtime
|
||||
|
||||
## Version
|
||||
|
||||
- Version: `v1.8.0`
|
||||
- Last Updated: `2026-05-23`
|
||||
- Version: `v1.8.1`
|
||||
- Last Updated: `2026-05-28`
|
||||
|
||||
+32
-13
@@ -6,15 +6,15 @@
|
||||
|
||||
## 产品截图
|
||||
|
||||
### 全球看板
|
||||
### 实时终端
|
||||
|
||||

|
||||

|
||||
|
||||
### 城市分析(Ankara)
|
||||
### Telegram 跑道推送
|
||||
|
||||

|
||||

|
||||
|
||||
## 当前产品状态(2026-05-23)
|
||||
## 当前产品状态(2026-05-28)
|
||||
|
||||
- 已上线订阅制:`Pro 月付 10 USDC`。
|
||||
- 已上线积分体系:群内发言赚分 + 首次发言欢迎奖励 (+20) + 每日首条消息奖励 (+2) + 每周全员参与奖。
|
||||
@@ -26,6 +26,15 @@
|
||||
- 已上线轻量运营后台:`/ops`(会员、周榜、补分、支付异常单)。
|
||||
- 已上线轻量可观测性:`/healthz`、`/api/system/status`、`/metrics`。
|
||||
- 已补最小外部监控栈:Prometheus + Alertmanager + Grafana + Telegram 告警 relay。
|
||||
- 实时终端已切换到可重放事件流:可见城市图表通过 `/api/events?cities=...&since_revision=...` 订阅 `city_observation_patch.v1`,生产环境使用 Redis Stream 做短窗口 replay,本地/单进程可回退 SQLite event log。
|
||||
- 图表刷新由实测事件驱动:SSE patch 直接合并到当前曲线,不弹 loading 遮罩;只有可见图表启用 60 秒无 patch 兜底,浏览器后台返回前台时会主动补齐最新 detail。
|
||||
- 城市图表默认展示“全天”,可选“高温”窗口由 DEB hourly path 推导;所有图表横轴都按城市当地时间展示,不按用户浏览器时区。
|
||||
- 核心图表组件已拆分为逻辑、状态与 canvas 渲染模块;Recharts 使用 `ResizeObserver` 后的明确宽高,规避 0x0 渲染和长时间挂页后曲线消失。
|
||||
- DEB hourly consensus(`deb_hourly_consensus.v1`)已作为峰值窗口和图表 DEB 曲线的优先小时路径;DEB 仍然是预测曲线,不作为实测来源。
|
||||
- legacy 高斯概率在图表上展示为概率温度带和 `mu` 参考线,不再伪装成一条时间序列曲线。
|
||||
- AMSC/AMOS 城市的结算跑道曲线默认展示并高亮,辅助跑道作为弱化曲线保留;釜山单跑道只展示 `SR/SL` 结算跑道,不再重复显示 AMOS 聚合线。
|
||||
- 香港默认展示 CoWIN `6087`(保良局陈守仁小学)1 分钟参考站曲线,HKO 10 分钟实测保留为官方气象层。
|
||||
- Telegram 机场/跑道推送默认中英文双语,并统一使用结算端点跑道温度计算当前值、15 分钟趋势和文案。
|
||||
- 运行态状态、缓存与核心离线训练/回填链路已完成 SQLite 主路径收口;legacy JSON/JSONL 仅保留给迁移、导出与显式回退输入。
|
||||
- EMOS/CRPS 校准链路已接通,但生产主概率保持 `legacy` 或 `emos_shadow`;`emos_primary` 只在本地离线评估通过并手动灰度后启用。
|
||||
- 官方增强站网已统一接入:
|
||||
@@ -37,15 +46,15 @@
|
||||
- `CWA`(台湾)
|
||||
- 东京现已接入羽田 `JMA AMeDAS` 10 分钟温度作为官方增强层。
|
||||
- 已支持 Dashboard 定向预热 worker / cron 路径,运行态在 `/api/system/status` 与 `/ops` 可见。
|
||||
- `/ops` 现已展示缓存桶数量、summary cache hit/miss 与 prewarm heartbeat。
|
||||
- `/ops` 现已展示缓存桶数量、summary cache hit/miss 与运行态 heartbeat。
|
||||
- 今日日内分析已改为“专业气象判断台”:顶部先给气象主判断、置信度、基准/上修/下修路径、下一观测点,再展示证据链、失效条件、确认条件和模型层。
|
||||
- 日内分析弹窗在 full detail / market detail 同步完成前会锁住旧内容并显示刷新状态,避免用户短暂看到上一轮缓存数据后误判。
|
||||
- 城市决策卡已接入 AI 机场报文解读:先用 METAR、DEB、多模型集群和 AI 最高温中枢判断天气路径,再映射到温度桶。
|
||||
- AI 机场报文解读现在同时使用页面内存缓存、浏览器 `localStorage` 和后端短 TTL 缓存;从其他选项卡切回决策卡时会优先恢复已有流式内容或最终结果。
|
||||
- 终端城市卡已改为结构化实况 + DEB hourly consensus + 多模型集群 + 校准概率 + 市场温度桶,不再让图表等待 AI 文案生成。
|
||||
- 终端数据同时使用页面内存缓存、浏览器 `localStorage`、后端短 TTL 缓存、SSE patch replay 和前台恢复刷新;从其他选项卡切回时会优先恢复最新可见图表状态。
|
||||
- 市场温度桶匹配已改为完整 `all_buckets` 映射,按 exact / range / or higher / or lower 方向严格匹配,避免把天气中枢错配到不合理尾部桶。
|
||||
- 决策卡中的“模型-市场差”口径为 `模型概率 - 市场隐含概率`,正值表示天气概率高于市场报价,负值表示市场已经更充分计价。
|
||||
- 概率区已改为”校准模型概率”;默认展示生产概率引擎输出(legacy 高斯或 EMOS),模型共识作为辅助参考。
|
||||
- 今日日内结构解读已支持可选 Groq 改写层,失败时自动回退规则文案。
|
||||
- 今日日内结构解读以规则与结构化信号为主,AI 文案只作为可降级辅助层,不替代实测、DEB、TAF 或结算逻辑。
|
||||
- 前端设计系统全面重构:统一 CSS token 体系、消除 !important 滥用(134→49)、合并断点(18→10)、数百处硬编码颜色迁移至 CSS 变量、添加 ARIA 无障碍属性和键盘导航。完整审查记录见 `docs/frontend-ui-design-review.md`。
|
||||
|
||||
## 许可证与商用边界(重要)
|
||||
@@ -62,8 +71,9 @@
|
||||
|
||||
- 聚合 51 个监控城市的实测与预报数据。
|
||||
- DEB(Dynamic Error Balancing)融合多模型最高温。
|
||||
- 构建 DEB 加权小时共识曲线,用于峰值窗口判断和图表默认 DEB 展示。
|
||||
- 输出结算导向校准概率分布(`mu` + 温度桶),通过 legacy 高斯或 EMOS/CRPS 校准引擎。
|
||||
- 地图城市决策卡把 AI 机场报文解读、最高温中枢、完整市场温度桶和模型-市场差放在同一张卡中展示。
|
||||
- 地图城市决策卡把结构化实况、最高温中枢、完整市场温度桶和模型-市场差放在同一张卡中展示。
|
||||
- Web 仪表盘与 Telegram Bot 复用同一分析内核。
|
||||
- 支付链路具备事件重放、SQLite 审计事件与 RPC 容灾能力。
|
||||
- 官方增强层与跑道级传感器支持按国家 provider 统一接入(含韩国 AMOS 首尔/釜山跑道实测),不替代机场主站、METAR 或明确官方结算站。
|
||||
@@ -85,7 +95,10 @@ flowchart LR
|
||||
WX --> OM["Open-Meteo"]
|
||||
WX --> HKO["HKO / CWA / NOAA 等官方结算源"]
|
||||
|
||||
API --> ANA["DEB + 趋势 + 概率 + 市场扫描"]
|
||||
API --> ANA["DEB + 小时共识 + 概率 + 市场扫描"]
|
||||
API --> SSE["SSE /api/events"]
|
||||
WX --> SSE
|
||||
SSE --> EVENT["Redis Stream / SQLite Event Log"]
|
||||
ANA --> PAY["支付状态(Intent + Event + Confirm Loop)"]
|
||||
ANA --> STATE["SQLite runtime state<br/>legacy files only for migration/export fallback"]
|
||||
```
|
||||
@@ -121,8 +134,14 @@ npm run dev
|
||||
POLYWEATHER_RUNTIME_DATA_DIR=/var/lib/polyweather
|
||||
POLYWEATHER_DB_PATH=/var/lib/polyweather/polyweather.db
|
||||
POLYWEATHER_STATE_STORAGE_MODE=sqlite
|
||||
POLYWEATHER_EVENT_STORE=redis
|
||||
POLYWEATHER_REDIS_URL=redis://polyweather_redis:6379/0
|
||||
POLYWEATHER_REDIS_STREAM_MAXLEN=50000
|
||||
POLYWEATHER_REDIS_REQUIRED=true
|
||||
```
|
||||
|
||||
本地开发或严格单进程兜底可使用 `POLYWEATHER_EVENT_STORE=sqlite`。
|
||||
|
||||
## EMOS 本地训练流程
|
||||
|
||||
低配 VPS 只负责采集、服务和加载已通过评估的参数,不建议在 VPS 上跑 EMOS 全量训练。训练前先从 VPS 拉 SQLite 副本到本地:
|
||||
@@ -230,5 +249,5 @@ POLYWEATHER_OPS_ADMIN_EMAILS=yhrsc30@gmail.com
|
||||
|
||||
## 当前版本
|
||||
|
||||
- 版本:`v1.8.0`
|
||||
- 文档最后更新:`2026-05-23`
|
||||
- 版本:`v1.8.1`
|
||||
- 文档最后更新:`2026-05-28`
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
# 机场高频实时数据源
|
||||
|
||||
最后更新:`2026-05-28`
|
||||
|
||||
## 已接入城市
|
||||
|
||||
| 城市 | 机场 | ICAO/站点 | 数据源 | 频率 | 类型 | 费用 |
|
||||
|------|------|-----------|--------|------|------|------|
|
||||
| 首尔 | 仁川国际 | RKSI | AMOS (`global.amo.go.kr`) | 1 分钟 | 跑道对温度(2对) | 免费 |
|
||||
| 釜山 | 金海国际 | RKPK | AMOS (`global.amo.go.kr`) | 1 分钟 | 跑道对温度(1对) | 免费 |
|
||||
| 香港 | CoWIN 6087 | 6087 | CoWIN (`cowin.hku.hk`) | 1 分钟 | 参考站温度(保良局陈守仁小学) | 免费 |
|
||||
| 香港 | HKO | HKO | HKO 官方 CSV (`data.weather.gov.hk`) | 10 分钟 | 官方气象站温度 | 免费 |
|
||||
| 台北 | 松山/中央气象署 | 466920 | CWA 开放数据 | 10 分钟 | 官方站点温度 | 免费 |
|
||||
| 北京 | 首都机场 | ZBAA | AMSC AWOS | 1 分钟 | 跑道端点气温 | 免费 |
|
||||
| 上海 | 浦东机场 | ZSPD | AMSC AWOS | 1 分钟 | 跑道端点气温 | 免费 |
|
||||
| 广州 | 白云机场 | ZGGG | AMSC AWOS | 1 分钟 | 跑道端点气温 | 免费 |
|
||||
| 成都 | 双流机场 | ZUUU | AMSC AWOS | 1 分钟 | 跑道端点气温 | 免费 |
|
||||
| 重庆 | 江北机场 | ZUCK | AMSC AWOS | 1 分钟 | 跑道端点气温 | 免费 |
|
||||
| 武汉 | 天河机场 | ZHHH | AMSC AWOS | 1 分钟 | 跑道端点气温 | 免费 |
|
||||
| 青岛 | 胶东机场 | ZSQD | AMSC AWOS | 1 分钟 | 跑道端点气温 | 免费 |
|
||||
| 东京 | 羽田 | RJTT | JMA AMeDAS (`jma.go.jp`) | 10 分钟 | 机场站点实时温度 | 免费 |
|
||||
| 安卡拉 | Esenboğa | 17128 | MGM (`servis.mgm.gov.tr`) | 5-15 分钟 | 机场站点实时温度 | 免费 |
|
||||
| 伊斯坦布尔 | 伊斯坦布尔机场 | 17058 | MGM (`servis.mgm.gov.tr`) | 5-15 分钟 | 机场站点实时温度 | 免费 |
|
||||
@@ -36,6 +48,14 @@
|
||||
> 后端通过 `weather_sources.py` 拉取并注入 `airport_primary`,前端市场监控通过
|
||||
> `resolveMonitorTemperature` 优先读取 `airport_primary.temp` 获得小数精度温度。
|
||||
|
||||
> **CoWIN 6087**: 香港图表默认参考站为 HKU CoWIN `6087`(保良局陈守仁小学)。
|
||||
> 该源提供约 1 分钟温度序列,作为 PM 最高温市场的高频参考曲线;HKO 10 分钟数据
|
||||
> 仍作为官方气象层保留。后端通过 `cowin_sources.py` 拉取并写入 `cowin_obs`。
|
||||
|
||||
> **AMSC AWOS**: 中国内地跑道城市读取 AMSC `getWindPlate` 中的 `TDZ_TEMP` /
|
||||
> `MID_TEMP` / `END_TEMP`。这些字段是跑道观测位置气温,不是道面温度。
|
||||
> 结算跑道展示使用配置的结算端点;辅助跑道只作为背景曲线。
|
||||
|
||||
## 推送机制
|
||||
|
||||
- 每城按原生频率独立推送,不捆绑
|
||||
@@ -44,14 +64,16 @@
|
||||
- 仅当当前温度距 DEB 预测最高 ≤3°C 时推送
|
||||
- 确认过峰值后自动停止
|
||||
|
||||
## 前端实时同步与 SSE Patch 机制
|
||||
## 前端实时同步与 SSE Patch / Redis Stream 机制
|
||||
|
||||
为了向用户提供秒级实况响应并降低服务器负载,系统已从定时轮询架构全面迁移至 **Server-Sent Events (SSE) 增量更新(SSE Patch)** 架构。
|
||||
为了向用户提供接近行情盘的实况响应并降低服务器负载,系统使用 **HTTP snapshot + Server-Sent Events (SSE) Patch + 可重放事件日志** 架构。生产环境推荐 Redis Stream;本地或单进程可回退 SQLite event log。
|
||||
|
||||
### 1. 数据推送链路 (Data Pipeline)
|
||||
1. **Collector 采集端触发**:在 `weather_sources.py` 中,当高频实况源(如 AMOS, CoWIN, MADIS 等)采集到温度更新或观测时间变更时,会调用 `_emit_temperature_patch_if_changed` 过滤重复值,并异步向 `/api/internal/collector-patch` 发送 POST 报文。
|
||||
2. **FastAPI SSE 广播**:FastAPI 后端的 `sse_router.py` 接收到 Patch 后,将其推入 `sse_manager` 进行全局广播,事件被包装为 `city_patch` 增量包,包含自增的全局 `revision` 和最新的 `changes`。
|
||||
3. **BFF 代理流**:浏览器前端通过 BFF (Next.js rewrites) 建立与 `/api/events` 的持久连接,从而无需定时轮询。
|
||||
2. **标准化事件**:`realtime_patch_schema.py` 将旧 `city_patch` 或新 payload 统一成 `city_observation_patch.v1`。
|
||||
3. **事件存储**:生产环境写入 Redis Stream(`stream:city_observation`)并生成全局递增 `revision`;SQLite `observation_patch_events` 保留为本地/兜底 replay。
|
||||
4. **FastAPI SSE 广播**:FastAPI 后端的 `sse_router.py` 根据城市订阅集合向匹配连接推送 patch;断线重连时按 `since_revision` replay。
|
||||
5. **BFF 代理流**:浏览器前端通过 BFF 建立与 `/api/events` 的持久连接,从而无需固定整图轮询。
|
||||
|
||||
### 2. 前端消费与刷新规则 (Frontend Freshness Rules)
|
||||
- **扫描列表免轮询更新**:`use-scan-terminal-query.ts` 通过 `useSsePatchVersion` 钩子订阅全局 SSE 版本。当有任何城市产生更新时,列表将触发按需重绘,之前固定的 5 分钟 `setInterval` 定时轮询已被彻底禁用。
|
||||
@@ -62,6 +84,8 @@
|
||||
1. 调用轻量级的 `/api/city/{city}/summary` 快速拉取最新实况温度。
|
||||
2. 调用 `fetchHourlyForecastForCity(city, { ignoreCache: true })` 强刷完整的城市详情数据,确保数据一致性。
|
||||
- **按需加载与 Stagger 优化**:在加载城市详情时,前端会优先加载 Active 状态的图表,而处于 Background/非活动状态的图表则通过 staggered timer (按槽位索引延迟 300ms~1500ms) 异步获取,以分流请求峰值。
|
||||
- **前台恢复补齐**:浏览器标签页长时间在后台时,回来后会主动强刷可见图表 full detail,避免 SSE 被浏览器挂起后曲线落后。
|
||||
- **当地时间**:patch 中保留 `city_timezone` / `observed_at_utc`,前端按城市当地时间绘制横轴。
|
||||
|
||||
## 消息模板
|
||||
|
||||
@@ -83,6 +107,11 @@ Seoul / Incheon 16:03
|
||||
| `TELEGRAM_AIRPORT_PUSH_INTERVAL_SEC` | 循环轮询间隔 | `60` |
|
||||
| `TELEGRAM_AIRPORT_PUSH_LANGUAGE` | 机场推送语言覆盖,可选 `both`/`en`/`zh` | `both` |
|
||||
| `KNMI_API_KEY` | KNMI API 密钥(阿姆斯特丹必填) | — |
|
||||
| `POLYWEATHER_EVENT_STORE` | 实时事件存储,可选 `redis`/`sqlite` | `sqlite` |
|
||||
| `POLYWEATHER_REDIS_URL` | Redis Stream 连接地址 | `redis://127.0.0.1:6379/0` |
|
||||
| `POLYWEATHER_REDIS_STREAM_KEY` | Redis Stream key | `stream:city_observation` |
|
||||
| `POLYWEATHER_REDIS_STREAM_MAXLEN` | Redis Stream 保留长度 | `50000` |
|
||||
| `POLYWEATHER_REDIS_REQUIRED` | Redis 不可用时是否启动失败 | `true` |
|
||||
|
||||
## 未接入城市
|
||||
|
||||
|
||||
+46
-4
@@ -1,6 +1,6 @@
|
||||
# PolyWeather API 文档(v1.8.0)
|
||||
# PolyWeather API 文档(v1.8.1)
|
||||
|
||||
最后更新:`2026-04-27`
|
||||
最后更新:`2026-05-28`
|
||||
|
||||
本文档描述当前对外可用 API 口径(`web/app.py` + `web/routes.py` + `frontend/app/api/*`)。
|
||||
|
||||
@@ -17,7 +17,9 @@ 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 + Trend + Probability + Market Scan"]
|
||||
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"]
|
||||
```
|
||||
@@ -31,6 +33,27 @@ flowchart LR
|
||||
| `/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`
|
||||
|
||||
@@ -51,6 +74,7 @@ flowchart LR
|
||||
- `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`
|
||||
@@ -90,7 +114,7 @@ flowchart LR
|
||||
|
||||
#### 2. `probabilities`
|
||||
|
||||
概率层基于 legacy 高斯分桶,以 DEB 融合预测 μ 和 ensemble spread σ 生成 1°C 粒度概率分布。
|
||||
概率层基于 legacy 高斯分桶,以 DEB 融合预测 `mu` 和 ensemble spread `sigma` 生成 1°C 粒度概率分布。前端图表将 legacy 高斯展示为水平概率温度带和 `mu` 参考线,不把概率分布渲染成时间序列曲线。
|
||||
|
||||
概率字段:
|
||||
|
||||
@@ -99,6 +123,22 @@ flowchart LR
|
||||
- `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。前端如果发现:
|
||||
@@ -255,6 +295,8 @@ flowchart LR
|
||||
- `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 小时
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 城市实时数据源总览
|
||||
|
||||
> 最后更新: 2026-05-26 | 51 城市
|
||||
> 最后更新: 2026-05-28 | 51 城市
|
||||
|
||||
## 数据源分级
|
||||
|
||||
@@ -111,11 +111,30 @@ ankara, istanbul, helsinki, amsterdam, paris
|
||||
- **Tier 4 城市**(仅 METAR):修正效果有限,不建议依赖
|
||||
|
||||
|
||||
## 实时事件与图表刷新逻辑
|
||||
|
||||
当前终端图表不是固定整图轮询,而是:
|
||||
|
||||
1. 首屏 / 切换城市时拉取 `/api/city/{city}/detail` 作为完整 snapshot。
|
||||
2. 可见图表连接 `/api/events?cities=...&since_revision=...&replay_limit=500`。
|
||||
3. 采集器产出 `city_observation_patch.v1` 后写入 Redis Stream(生产)或 SQLite event log(本地/兜底),再通过 SSE 推给浏览器。
|
||||
4. 前端把 patch 追加到已有实测序列,不显示 loading 遮罩;只有可见图表 2 分钟无 patch 时才启动 60 秒兜底刷新。
|
||||
5. 浏览器从后台切回前台时,前端会立即补一次 full detail,防止长时间挂页后图表落后。
|
||||
|
||||
频率取决于源头:
|
||||
|
||||
- AMSC / AMOS / CoWIN / MSS:源头约 1 分钟,图表按 1 分钟粒度追加。
|
||||
- MADIS:源头约 5 分钟。
|
||||
- HKO / CWA / JMA / FMI / KNMI:源头约 10 分钟。
|
||||
- METAR-only 城市:按 METAR 可用频率和缓存 TTL,不伪装成 1 分钟实测。
|
||||
|
||||
所有图表横轴和 tooltip 时间均按城市当地时间展示,不按用户浏览器时区。
|
||||
|
||||
## 关于网站终端图表的数据曲线展示逻辑
|
||||
|
||||
### 1. 实测数据(默认全开,突出核心)
|
||||
|
||||
- **跑道全量展示**:北京、上海、广州、成都、重庆、武汉、首尔等城市的跑道实测数据,默认全量开启,无需手动勾选。
|
||||
- **跑道全量展示**:北京、上海、广州、成都、重庆、武汉、青岛、首尔、釜山等城市的跑道实测数据,默认全量开启,无需手动勾选。
|
||||
- **结算跑道高亮**:系统内置了各大机场的官方结算跑道映射。命中的跑道将被**重点强调**(加粗的青色实线 #009688,线宽 2.8),并标记为“[跑道号] 结算跑道”。具体的跑道映射如下:
|
||||
- 北京:19/01
|
||||
- 上海:17L/35R
|
||||
@@ -125,15 +144,24 @@ ankara, istanbul, helsinki, amsterdam, paris
|
||||
- 武汉:04/22
|
||||
- 青岛:16/34
|
||||
- 首尔:15R/33L
|
||||
- 釜山:SR/SL
|
||||
- **辅助跑道弱化**:同一机场下的其他非结算跑道,也会同时展示,但采用较细的虚线(线宽 1.2)以作陪衬区分。
|
||||
- **其他实测展示**:所有城市的 METAR 报文曲线、官方气象站实测(如 Hong Kong / Lau Fau Shan 的香港天文台曲线)均默认展示。
|
||||
- **单跑道机场去重**:釜山只有 `SR/SL` 跑道曲线时,不再额外展示 AMOS 聚合线,避免两条线语义重复。
|
||||
- **香港参考曲线**:Hong Kong 默认展示 CoWIN `6087`(保良局陈守仁小学)1 分钟参考站曲线;HKO 10 分钟实测作为官方气象层保留。
|
||||
- **其他实测展示**:所有城市的 METAR 报文曲线、官方气象站实测(如 Shenzhen / Lau Fau Shan 的 HKO 自动站、Taipei 的 CWA)均默认展示。
|
||||
|
||||
### 2. 核心预测数据(默认展示)
|
||||
|
||||
- **DEB 模型融合**:作为平台核心的高精度智能融合预测曲线,默认始终展示给用户。
|
||||
- **DEB 模型融合**:作为平台核心的智能融合预测曲线,默认始终展示给用户。DEB 是预测,不参与“实测接近峰值”的视觉预警计算。
|
||||
- **DEB hourly consensus**:图表优先使用 `deb_hourly_consensus.v1` 的小时路径展示 DEB 曲线和推导“高温”窗口;如果缺失才回退旧的 hourly + DEB offset 路径。
|
||||
|
||||
### 3. 多模型原始数据(默认隐藏,按需自选)
|
||||
|
||||
- **保持整洁**:为了防止图表线缆过于杂乱,各大原始模型(ECMWF, GFS, ICON, GEM 等)的数据曲线在初次加载时**默认隐藏**。
|
||||
- **特例**:仅针对巴黎(Paris),由于其 AROME HD 是高精度的 15 分钟级临近预报,极具参考价值,因此默认开启。
|
||||
- **自由交互**:用户可通过图表底部的图例交互按钮,随时自由勾选、叠加或隐藏任意所需的数据曲线。
|
||||
|
||||
### 4. 高斯概率图层
|
||||
|
||||
- legacy 高斯概率不会作为时间序列曲线展示。
|
||||
- 图表上只渲染概率温度带和 `mu` 参考线,帮助用户判断当前实测距概率中心和高概率区域的关系。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 商业化说明(Production)
|
||||
|
||||
最后更新:`2026-04-18`
|
||||
最后更新:`2026-05-28`
|
||||
|
||||
## 1. 定位
|
||||
|
||||
@@ -8,8 +8,8 @@ PolyWeather 是面向温度结算场景的气象决策层,不是通用天气
|
||||
|
||||
核心价值:
|
||||
|
||||
- 观测优先(METAR / 机场主站 / 明确官方站点;MGM、NMC、JMA、AMOS 等作为增强层)
|
||||
- 结算导向(DEB + 校准概率桶)
|
||||
- 观测优先(METAR / 机场主站 / 明确官方站点;MGM、JMA、AMOS、AMSC、CoWIN、CWA 等作为增强层)
|
||||
- 结算导向(DEB hourly consensus + 校准概率桶)
|
||||
- 气象判断优先(证据链、失效条件、下一观测点)
|
||||
- 市场映射(行情对照 + 错价雷达),但不把交易建议放在第一层产品承诺
|
||||
|
||||
@@ -24,6 +24,8 @@ PolyWeather 是面向温度结算场景的气象决策层,不是通用天气
|
||||
| 支付自动确认 | 已上线 | Event Loop + Confirm Loop |
|
||||
| 钱包绑定 | 已上线 | 浏览器钱包 + WalletConnect |
|
||||
| 私有频道推送 | 已上线 | 可拆分业务频道 |
|
||||
| 实时终端 | 已上线 | SSE Patch + Redis Stream replay |
|
||||
| Telegram 双语推送 | 已上线 | 中英文一起推送,结算跑道端点口径 |
|
||||
|
||||
## 3. 权限模型(当前)
|
||||
|
||||
@@ -33,6 +35,7 @@ PolyWeather 是面向温度结算场景的气象决策层,不是通用天气
|
||||
- 今日日内深度分析(含高温时段)
|
||||
- 专业气象结论条、证据链、失效条件、确认条件
|
||||
- 概率分布层(基于 DEB 融合 + 高斯分桶)
|
||||
- 实时终端多城市图表(跑道/官方站点/DEB/概率带)
|
||||
- 历史对账 + 未来日期分析
|
||||
- 全平台智能气象推送
|
||||
|
||||
@@ -62,6 +65,7 @@ PolyWeather 是面向温度结算场景的气象决策层,不是通用天气
|
||||
5. 安全边界:敏感配置不进仓库。
|
||||
6. 数据口径:机场市场按 METAR / 机场主站解释;Wunderground 只可描述为历史页面或参考入口,不描述成“站”。
|
||||
7. 加载口径:日内分析和右侧详情在 full detail 未补齐前必须显示同步状态,不能把旧缓存伪装成完整付费内容。
|
||||
8. 实时口径:终端图表应优先保留已有曲线并无痛合并 patch,不用 loading 遮罩覆盖已可用数据。
|
||||
|
||||
## 7. 后续路线
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 配置与密钥管理(中文)
|
||||
|
||||
最后更新:`2026-05-28`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
PolyWeather 的环境变量很多,但不是所有变量都属于同一层级。
|
||||
@@ -79,6 +81,7 @@ PolyWeather 的环境变量很多,但不是所有变量都属于同一层级
|
||||
- `POLYWEATHER_RUNTIME_DATA_DIR`
|
||||
- `POLYWEATHER_DB_PATH`
|
||||
- `POLYWEATHER_STATE_STORAGE_MODE`
|
||||
- `POLYWEATHER_EVENT_STORE`
|
||||
|
||||
前端:
|
||||
|
||||
@@ -104,6 +107,8 @@ PolyWeather 的环境变量很多,但不是所有变量都属于同一层级
|
||||
- `POLYWEATHER_AUTH_REQUIRE_SUBSCRIPTION`
|
||||
- `POLYWEATHER_OPS_ADMIN_EMAILS`
|
||||
- `POLYWEATHER_STATE_STORAGE_MODE`
|
||||
- `POLYWEATHER_EVENT_STORE`
|
||||
- `POLYWEATHER_REDIS_REQUIRED`
|
||||
- `POLYWEATHER_PAYMENT_ENABLED`
|
||||
- `POLYGON_WALLET_WATCH_ENABLED`
|
||||
- `TELEGRAM_ALERT_PUSH_ENABLED`
|
||||
@@ -127,6 +132,9 @@ PolyWeather 的环境变量很多,但不是所有变量都属于同一层级
|
||||
- `TELEGRAM_MARKET_FOCUS_DIGEST_TOP_N`
|
||||
- `POLYWEATHER_PAYMENT_RPC_URLS`
|
||||
- `TAF_CACHE_TTL_SEC`
|
||||
- `POLYWEATHER_REDIS_URL`
|
||||
- `POLYWEATHER_REDIS_STREAM_KEY`
|
||||
- `POLYWEATHER_REDIS_STREAM_MAXLEN`
|
||||
|
||||
策略:
|
||||
|
||||
@@ -215,6 +223,10 @@ TELEGRAM_CHAT_ID=...
|
||||
POLYWEATHER_RUNTIME_DATA_DIR=/var/lib/polyweather
|
||||
POLYWEATHER_DB_PATH=/var/lib/polyweather/polyweather.db
|
||||
POLYWEATHER_STATE_STORAGE_MODE=sqlite
|
||||
POLYWEATHER_EVENT_STORE=redis
|
||||
POLYWEATHER_REDIS_URL=redis://polyweather_redis:6379/0
|
||||
POLYWEATHER_REDIS_STREAM_MAXLEN=50000
|
||||
POLYWEATHER_REDIS_REQUIRED=true
|
||||
UID=1000
|
||||
GID=1000
|
||||
POLYWEATHER_AUTH_ENABLED=true
|
||||
@@ -245,6 +257,8 @@ POLYWEATHER_BACKEND_URL=http://polyweather_web:8000
|
||||
- `POLYWEATHER_RUNTIME_DATA_DIR` 建议放在仓库外,例如 `/var/lib/polyweather`。
|
||||
- `docker-compose.yml` 会把这个目录同时挂载到容器内的 `/var/lib/polyweather` 和 `/app/data`,兼容现有缓存与 SQLite 路径。
|
||||
- `POLYWEATHER_STATE_STORAGE_MODE` 当前线上推荐直接使用 `sqlite`。
|
||||
- `POLYWEATHER_EVENT_STORE=redis` 表示实时观测 patch 使用 Redis Stream 做短窗口 replay 和多 worker fanout;本地或单进程可改为 `sqlite`。
|
||||
- `POLYWEATHER_REDIS_REQUIRED=true` 表示 Redis 不可用时后端启动失败,避免生产环境广播不可 replay 的实时事件;开发环境可设为 `false` 允许回退 SQLite。
|
||||
- `POLYWEATHER_PAYMENT_RPC_URLS` 支持逗号分隔多个 RPC;如果暂时只用单 RPC,也可以继续只配 `POLYWEATHER_PAYMENT_RPC_URL`。
|
||||
- 机器人市场监控包含 `关键提醒` 与 `关注清单`:关键提醒逐城判断并受冷却控制,关注清单每轮先扫描完整城市列表,再按全局 Top N 推送;同一轮已经触发关键提醒的城市不会重复出现在关注清单里。
|
||||
- `TELEGRAM_MARKET_FOCUS_DIGEST_INTERVAL_SEC` 表示主动推送间隔,默认 `1800` 秒(30 分钟)。
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 前端部署配置(Vercel)
|
||||
|
||||
最后更新:`2026-05-28`
|
||||
|
||||
本文只覆盖 `frontend` 目录对应的 Next.js 前端部署。
|
||||
|
||||
## 一、部署目标
|
||||
@@ -16,6 +18,12 @@
|
||||
2. Next `/api/*` -> `POLYWEATHER_API_BASE_URL`
|
||||
3. FastAPI 后端 -> 分析 / 支付 / 鉴权服务
|
||||
|
||||
实时图表同样走 Next Route Handler / rewrite:
|
||||
|
||||
1. 浏览器 `EventSource` -> `/api/events?cities=...&since_revision=...`
|
||||
2. Next 转发到 FastAPI `/api/events`
|
||||
3. FastAPI 从 Redis Stream / SQLite event log replay 后进入 live SSE
|
||||
|
||||
## 二、Vercel 项目设置
|
||||
|
||||
在 Vercel 导入 GitHub 仓库后,使用下面的设置:
|
||||
@@ -193,6 +201,7 @@ Vercel 部署前至少确认:
|
||||
4. 如果启用鉴权,Supabase redirect URL 已包含前端域名
|
||||
5. `GET /api/payments/config` 返回的是当前最新地址,而不是旧收款合约
|
||||
6. 如果启用了 `/ops`,确认 `POLYWEATHER_OPS_ADMIN_EMAILS` 已在 Vercel 与后端同时配置
|
||||
7. 确认 `/api/events` 没有被 CDN 缓存或压缩成普通 JSON;它必须保持 `text/event-stream`
|
||||
|
||||
## 九、常见问题
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
本文档记录 PolyWeather 当前开放模型接入、区域覆盖差异,以及 DEB 在新增模型后的计权规则。
|
||||
|
||||
最后更新:`2026-05-27`
|
||||
最后更新:`2026-05-28`
|
||||
|
||||
## 1. 接入方式
|
||||
|
||||
@@ -172,7 +172,33 @@ raw current_forecasts
|
||||
|
||||
当 `weights_info` 出现 `家族去重`,表示当前输入模型数量多于 DEB 实际入模数量,系统已先折叠同家族模型。
|
||||
|
||||
### 5.1 版本化预测与偏差校正
|
||||
### 5.1 DEB hourly consensus
|
||||
|
||||
当前图表和峰值窗口优先使用 `deb_hourly_consensus.v1`。
|
||||
|
||||
入口:
|
||||
|
||||
- `src/analysis/deb_hourly_consensus.py`
|
||||
- `web/analysis_service.py`
|
||||
- `src/analysis/trend_engine.py`
|
||||
|
||||
处理逻辑:
|
||||
|
||||
```text
|
||||
multi_model hourly curves
|
||||
-> 按 DEB 可用模型和家族去重口径选择候选
|
||||
-> 使用 DEB 权重生成逐小时 consensus
|
||||
-> 输出 deb.hourly_consensus
|
||||
-> 图表 DEB Forecast 与 peak-window 逻辑优先读取该路径
|
||||
```
|
||||
|
||||
关键口径:
|
||||
|
||||
- `deb_hourly_consensus.v1` 是预测曲线,不是实测曲线。
|
||||
- 它只用于日内形状、峰值窗口和 DEB Forecast 展示;视觉预警和“接近峰值”状态必须优先读取实测/跑道/官方站点当前值与今日实测高点。
|
||||
- 如果多模型小时路径缺失,系统才回退旧的 `hourly_plus_deb_offset`。
|
||||
|
||||
### 5.2 版本化预测与偏差校正
|
||||
|
||||
DEB 原始融合逻辑不推倒重写,`calculate_dynamic_weights(...)` 仍作为 raw baseline。线上生产入口使用 `calculate_deb_prediction(...)` 包装 raw baseline,并在有足够历史样本时追加城市级 recent signed-bias correction。
|
||||
|
||||
@@ -213,6 +239,7 @@ python scripts/backtest_deb_versions.py \
|
||||
- `multi_model`
|
||||
- `multi_model_daily`
|
||||
- `source_forecasts.open_meteo_multi_model.model_metadata`
|
||||
- `deb.hourly_consensus`
|
||||
|
||||
显示分组:
|
||||
|
||||
@@ -233,6 +260,13 @@ python scripts/backtest_deb_versions.py \
|
||||
|
||||
区域模型不覆盖时不显示空模型。
|
||||
|
||||
终端图表默认:
|
||||
|
||||
- 展示“全天”视图,按城市当地日 00:00-23:59 和真实观测时间绘制。
|
||||
- 可选“高温”视图,窗口由 `deb_hourly_consensus.v1` 的峰值区域推导。
|
||||
- DEB Forecast 使用橙色预测曲线;实测/跑道/官方站点曲线独立展示,不把 DEB 当成实测。
|
||||
- legacy 高斯概率显示为水平温度带和 `mu` 参考线,不参与时间序列曲线。
|
||||
|
||||
### 6.1 “来源 Open-Meteo”是什么意思
|
||||
|
||||
前端中的 `来源 - Open-Meteo` 表示本次多模型数据通过 Open-Meteo model API 归一化接入。
|
||||
|
||||
+17
-1
@@ -1,6 +1,6 @@
|
||||
# 外部监控与告警说明
|
||||
|
||||
最后更新:`2026-04-18`
|
||||
最后更新:`2026-05-28`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
- Relay 把告警推到运营频道
|
||||
- Grafana 展示趋势面板
|
||||
- 巡检脚本补健康检查
|
||||
- 关注 `/api/events` 长连接与 realtime event store 是否正常 replay
|
||||
|
||||
## 2. 组件
|
||||
|
||||
@@ -89,6 +90,14 @@ POLYWEATHER_MONITORING_ALERT_CHAT_IDS=
|
||||
- Source Requests by Outcome
|
||||
- Source Error Rate (15m)
|
||||
|
||||
实时事件层建议额外观察:
|
||||
|
||||
- Redis Stream latest revision
|
||||
- Redis 连接状态
|
||||
- SQLite fallback 是否被启用
|
||||
- SSE active connection count
|
||||
- `resync_required` 出现频率
|
||||
|
||||
## 7. 巡检脚本
|
||||
|
||||
手动巡检:
|
||||
@@ -102,6 +111,7 @@ python scripts/check_ops_health.py --base-url http://127.0.0.1:8000
|
||||
- `/healthz`
|
||||
- `/api/system/status`
|
||||
- `/metrics`
|
||||
- `/api/events`(手动验证时查看 `connected` / `heartbeat` / replay 事件)
|
||||
|
||||
任何一项失败都会非零退出,适合挂到 crontab 或 systemd timer。
|
||||
|
||||
@@ -130,6 +140,11 @@ python scripts/check_ops_health.py --base-url http://127.0.0.1:8000
|
||||
- 多日预报是否只返回当天单卡
|
||||
- 日内分析 full detail / market scan 是否仍在同步
|
||||
- 右侧详情面板是否正在用同步占位卡提示用户
|
||||
- 实时事件状态:
|
||||
- event store 类型(Redis / SQLite)
|
||||
- latest revision
|
||||
- Redis 是否连通
|
||||
- 是否处于 `degraded_from=redis` fallback
|
||||
|
||||
这意味着:
|
||||
|
||||
@@ -180,3 +195,4 @@ npm run build
|
||||
- 更细粒度支付指标
|
||||
- 按城市/来源拆分的业务 SLA
|
||||
- 按城市拆分的前端补齐耗时与 stale-detail 告警
|
||||
- Redis Stream 长度、内存与 replay gap 告警
|
||||
|
||||
+35
-25
@@ -1,53 +1,63 @@
|
||||
# 外部服务依赖总览
|
||||
|
||||
最后更新:`2026-05-23`
|
||||
最后更新:`2026-05-28`
|
||||
|
||||
项目调用了 20 个外部服务,按状态分为三类。
|
||||
项目调用外部天气、鉴权、支付和实时事件服务。原则是:核心链路必须有明确健康检查;可选数据源不可拖垮已可用城市;实时事件层可从 Redis Stream 降级到 SQLite event log。
|
||||
|
||||
## 核心(必须有,挂了服务不可用)
|
||||
|
||||
| 服务 | 用途 | 状态 |
|
||||
| ---------------------- | --------------------- | ---- |
|
||||
| Open-Meteo | 52 城天气预报 | ✅ |
|
||||
| --- | --- | --- |
|
||||
| Open-Meteo | 51 城天气预报、多模型小时曲线、DEB hourly consensus 输入 | ✅ |
|
||||
| AviationWeather (NOAA) | METAR / TAF 航空观测 | ✅ |
|
||||
| MADIS (NOAA) | 美国 5 分钟高频观测 | ✅ |
|
||||
| Supabase | 用户认证 + 订阅 | ✅ |
|
||||
| Telegram Bot API | Bot 消息 + 群成员检查 | ✅ |
|
||||
| KNMI | Amsterdam 10 分钟观测 | ✅ |## 国家气象源(特定城市必须)
|
||||
| MADIS (NOAA) | 美国机场 5 分钟高频观测 | ✅ |
|
||||
| Supabase | 用户认证、订阅状态、会员恢复 | ✅ |
|
||||
| Telegram Bot API | Bot 消息、群成员检查、双语跑道推送 | ✅ |
|
||||
| Redis | `city_observation_patch.v1` Stream、SSE replay、多 worker fanout | ✅ |
|
||||
| SQLite | 运行态数据库、支付审计、实时事件 fallback | ✅ |
|
||||
|
||||
## 国家气象源(特定城市必须)
|
||||
|
||||
| 服务 | 城市 | 状态 |
|
||||
| -------------------- | --------------------- | ----------- |
|
||||
| JMA (日本) | Tokyo | ✅ |
|
||||
| KMA + AMOS (韩国) | Seoul, Busan | ✅ |
|
||||
| AMSC AWOS (中国) | 北京/上海/广州等 6 城 | ✅ |
|
||||
| --- | --- | --- |
|
||||
| JMA AMeDAS | Tokyo | ✅ |
|
||||
| AMOS (韩国) | Seoul, Busan 跑道传感器 | ✅ |
|
||||
| AMSC AWOS (中国) | 北京、上海、广州、成都、重庆、武汉、青岛跑道端点气温 | ✅ |
|
||||
| MGM (土耳其) | Ankara, Istanbul | ✅ |
|
||||
| FMI (芬兰) | Helsinki | ✅ |
|
||||
| HKO (香港) | Hong Kong | ✅ |
|
||||
| KNMI (荷兰) | Amsterdam | ✅(需 key) |
|
||||
| CoWIN 6087 (香港) | Hong Kong 1 分钟参考站 | ✅ |
|
||||
| HKO (香港) | Hong Kong / Shenzhen / Lau Fau Shan 10 分钟官方气象层 | ✅ |
|
||||
| CWA (台湾) | Taipei | ✅ |
|
||||
| NMC (中国) | 国内城市 fallback | ✅ |
|
||||
| Singapore MSS | Singapore | ✅ |
|
||||
| IMS Lod (以色列) | Tel Aviv | ✅ |
|
||||
| AEROWEB / AROME HD | Paris | ✅ |
|
||||
| NMC (中国) | 国内城市 fallback | ✅ |
|
||||
| IMGW (波兰) | Warsaw | ⚠️ 未配 key |
|
||||
| Russia pogodaiklimat | Moscow | ❌ 已移除 |
|
||||
|
||||
## 可选 / 已禁用
|
||||
|
||||
| 服务 | 用途 | 状态 |
|
||||
| -------------- | ------------- | ----------- |
|
||||
| --- | --- | --- |
|
||||
| OpenWeatherMap | 天气 fallback | ⚠️ 未配 key |
|
||||
| VisualCrossing | 历史天气 | ⚠️ 未配 key |
|
||||
| Meteoblue | 天气预报 | ❌ 已移除 |
|
||||
| SynopticData | 美国站点观测 | ⚠️ 未配 key |
|
||||
| Meteoblue | 天气预报 | ❌ 已移除 |
|
||||
| Russia pogodaiklimat | Moscow 历史源 | ❌ 已移除 |
|
||||
| Groq | AI commentary | ❌ 已移除 |
|
||||
|
||||
## AI / 其他
|
||||
## AI / 支付 / 前端
|
||||
|
||||
| 服务 | 用途 | 状态 |
|
||||
| ----------------- | ---------------- | ----------- |
|
||||
| --- | --- | --- |
|
||||
| MiMo (xiaomimimo) | 城市分析 AI 评论 | ✅ 当前使用 |
|
||||
| DeepSeek | AI fallback | - 备用 |
|
||||
| Groq | AI commentary | ❌ 已移除 |
|
||||
| Polygon RPC | 链上支付 | ✅ |
|
||||
| WalletConnect | 前端钱包连接 | ⚠️ 未配 key |
|
||||
| DeepSeek | AI fallback | 备用 |
|
||||
| Polygon RPC | 链上支付、自动确认 | ✅ |
|
||||
| WalletConnect | 前端钱包连接 | ⚠️ 未配 key 时钱包入口降级 |
|
||||
|
||||
## 合计
|
||||
## 运维口径
|
||||
|
||||
15 个在用,3 个可选/未配置,3 个已移除。
|
||||
- 生产实时事件推荐:`POLYWEATHER_EVENT_STORE=redis` + `POLYWEATHER_REDIS_URL=redis://polyweather_redis:6379/0`。
|
||||
- 本地或单进程兜底:`POLYWEATHER_EVENT_STORE=sqlite`。
|
||||
- Redis 只负责短窗口 replay 与多 worker fanout,不是长期天气历史库。
|
||||
- DEB hourly consensus 依赖 Open-Meteo 多模型小时曲线;若上游限流,图表应保留已有 snapshot 和实测 patch,不把缺失模型误报为实测缺失。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Supabase + 登录 + 支付接入说明(v1.8.0)
|
||||
# Supabase + 登录 + 支付接入说明(v1.8.1)
|
||||
|
||||
最后更新:`2026-03-14`
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 技术债与工程待办(v1.8.0)
|
||||
# 技术债与工程待办(v1.8.1)
|
||||
|
||||
最后更新:`2026-05-10`
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 审查日期:2026-06 | 视角:系统架构师 | 范围:完整数据采集→分析→API→前端状态
|
||||
>
|
||||
> **修复状态:8/8 已完成**
|
||||
> **修复状态:9/9 已完成;最后校准:2026-05-28**
|
||||
|
||||
## 一、数据架构总览
|
||||
|
||||
@@ -13,9 +13,8 @@
|
||||
Open-Meteo (预报+多模型) ─┐
|
||||
METAR/TAF (航空气象) ─┤
|
||||
NWS (美国) / MGM (土耳其) ─┤
|
||||
JMA/AMOS/NMC/HKO/CWA ─┤
|
||||
Wunderground / NOAA ─┤
|
||||
Polymarket Gamma/CLOB ─┤
|
||||
JMA/AMOS/AMSC/HKO/CWA ─┤
|
||||
CoWIN / MADIS / NOAA ─┤
|
||||
├─ WeatherDataCollector ├─ dashboard-client.ts
|
||||
│ (内存缓存 + SQLite磁盘缓存) │ (ETag浏览器缓存 + SWR)
|
||||
│ │
|
||||
@@ -27,8 +26,11 @@ Polymarket Gamma/CLOB ─┤
|
||||
│ ├─ ThreadPoolExecutor(4) │ (120s TTL)
|
||||
│ └─ AI 增强层 (DeepSeek) │
|
||||
│ │
|
||||
└─ FastAPI routes └─ API代理 (Next.js rewrites)
|
||||
(36个端点 + ETag 304)
|
||||
├─ FastAPI routes └─ API代理 (Next.js rewrites)
|
||||
│ (HTTP snapshot + ETag 304)
|
||||
│
|
||||
└─ SSE /api/events
|
||||
(Redis Stream / SQLite replay)
|
||||
```
|
||||
|
||||
## 二、数据采集层
|
||||
@@ -43,10 +45,8 @@ Polymarket Gamma/CLOB ─┤
|
||||
| NWS | 国家预报 | 美国 | 按请求 |
|
||||
| MGM | 国家官方 | 土耳其 | 300s |
|
||||
| ECMWF/GFS/ICON/GEM/JMA | 多模型 NWP | 全球 | 300s |
|
||||
| HKO/CWA/NOAA/AMOS/NMC | 结算观测 | 特定国家 | 60s (AMOS) / 300s |
|
||||
| Wunderground | 个人气象站 | 全球备用 | 按请求 |
|
||||
| Polymarket Gamma | 市场发现 | 所有温度市场 | 60s |
|
||||
| Polymarket CLOB | 订单簿 | 匹配市场 | 30s |
|
||||
| HKO/CWA/NOAA/AMOS/AMSC/CoWIN | 结算/参考观测 | 特定国家 | 60s-600s |
|
||||
| Redis Stream | 实时事件 replay / 多 worker fanout | 后端内部 | event-driven |
|
||||
|
||||
### 待改进
|
||||
|
||||
@@ -78,7 +78,7 @@ Polymarket Gamma/CLOB ─┤
|
||||
|
||||
**已修复:TTL 匹配** — `SCAN_TERMINAL_PAYLOAD_TTL_SEC` 30s → 120s,匹配 ThreadPoolExecutor(4)×60 城的实际重算耗时。
|
||||
|
||||
**已实现:SSE 增量推送(SSE Patch)与按需刷新** — 引入 FastAPI SSE 广播通道 (`/api/events`)。数据采集端更新时自动向 `/api/internal/collector-patch` 推送最新温度;前端扫描终端订阅该流,不再执行固定的 5 分钟定时轮询,而是根据 Patch 变化即时更新列表;当前选中图表基于 `useLatestPatch` 实现 1 分钟级温度的增量合并与实时曲线绘制。
|
||||
**已实现:SSE 增量推送(SSE Patch)与可重放事件日志** — 引入 FastAPI SSE 广播通道 (`/api/events`)。数据采集端更新时自动向 `/api/internal/collector-patch` 推送最新温度;后端标准化为 `city_observation_patch.v1`,写入 Redis Stream(生产)或 SQLite event log(兜底)后再广播。前端扫描终端订阅该流,不再执行固定的 5 分钟定时轮询,而是根据 Patch 变化即时更新列表;当前选中图表基于 `useLatestPatch` 实现 1 分钟级温度的增量合并与实时曲线绘制。
|
||||
|
||||
| # | 问题 | 优先级 |
|
||||
|---|------|------|
|
||||
@@ -94,7 +94,7 @@ Polymarket Gamma/CLOB ─┤
|
||||
|
||||
**已修复:扫描数据复用** — `preloadCityFromRow()` 从扫描终端行预填充城市详情缓存,选城市后详情面板立即显示。
|
||||
|
||||
**已实现:SSE 订阅与 2 分钟无 Patch 兜底机制** — 引入 `useLatestPatch` 与 `useSsePatchVersion` 管理实况数据的准实时合并。若长连接中断或 2 分钟内未收到任何增量 Patch,可见图表自动触发 60s 降级轮询(从 `/api/city/{city}/summary` 获取最新实况,并以 ignoreCache 强刷 full detail)。
|
||||
**已实现:SSE 订阅、replay 与 2 分钟无 Patch 兜底机制** — 引入 `useLatestPatch` 与 `useSsePatchVersion` 管理实况数据的准实时合并。若长连接中断,前端使用 `since_revision` replay 缺失事件;若 2 分钟内未收到任何增量 Patch,可见图表自动触发 60s 降级轮询(从 `/api/city/{city}/summary` 获取最新实况,并以 ignoreCache 强刷 full detail)。浏览器后台恢复时会主动刷新可见图表 full detail。
|
||||
|
||||
## 六、待办
|
||||
|
||||
|
||||
@@ -4,22 +4,22 @@
|
||||
|
||||
PolyWeather(仓库:`yangyuan-zhen/PolyWeather`)定位为**面向温度类结算预测市场(如 Polymarket 的温度结算合约)**的”生产级气象情报系统”,核心在于把多源天气观测/预报转化为**结算导向的概率桶(μ + bucket distribution)**;同时提供 Web 仪表盘与 Telegram Bot 两套交互入口,并包含 Polygon 链上 USDC/USDC.e 支付、自动补单与订阅/积分体系。项目 README 现明确仓库代码采用 `AGPL-3.0-only`,同时将品牌、商标、生产私有数据与运营阈值保留在代码许可证之外。
|
||||
|
||||
> **2026-05-23 更新(v1.7.0)**:Polymarket 价格拉取与 UI 层(MarketDecisionLine)已删除,`market_scan` 当前返回空;LGBM 已完全移除,概率引擎仅保留 legacy 高斯 + EMOS/CRPS;Groq、Meteoblue、NMC、pogodaiklimat 数据源和 prewarm 预热系统已移除。
|
||||
> **2026-05-28 更新(v1.8.1)**:终端图表已切换为 HTTP snapshot + SSE Patch + Redis Stream/SQLite replay 架构;DEB hourly consensus (`deb_hourly_consensus.v1`) 成为峰值窗口和 DEB 曲线展示的优先小时路径;README 产品截图迁移到 `frontend/public/static`。
|
||||
## 项目概览
|
||||
|
||||
PolyWeather 的目标与范围在 README/README_ZH 中定义得较清楚:为温度结算市场提供气象情报(多源采集→融合→概率→对照市场报价),并提供“官方看板(Vercel 前端)+ VPS 后端 + Telegram Bot”。
|
||||
项目主功能可归纳为五层:
|
||||
**天气层(数据源/采集)**:聚合 51 个城市的实测与预报;支持 AviationWeather METAR(机场观测)、韩国 AMOS 跑道级观测(首尔/釜山)、土耳其 MGM 站网、Open-Meteo(含多模型与集合预报)、美国 NWS(仅美国城市)、以及部分城市使用明确官方站点或历史页面入口(香港 HKO、台湾/深圳相关历史页面等)等。机场类市场仍以 METAR / 机场主站为结算锚点,Wunderground 不描述为物理观测站。
|
||||
**天气层(数据源/采集)**:聚合 51 个城市的实测与预报;支持 AviationWeather METAR/TAF、韩国 AMOS 跑道级观测(首尔/釜山)、中国 AMSC AWOS 跑道端点气温、香港 CoWIN 6087 / HKO、土耳其 MGM、Open-Meteo 多模型与集合预报、美国 MADIS HFMETAR 等。机场类市场仍以 METAR / 机场主站或明确官方结算源为锚点,Wunderground 不描述为物理观测站。
|
||||
**分析层(DEB/趋势/概率/结算口径)**:
|
||||
DEB(Dynamic Error Balancing)基于过去 N 天模型误差(MAE)倒数加权,输出融合预报;运行态仍维护近 14 天 `daily_records` 缓存做当前对账,但长期监督真值与训练特征已经迁到 SQLite 永久表中,并支持基于 WU(Weather Underground 口径)四舍五入的结算命中评估。
|
||||
趋势/概率引擎在 `trend_engine.py` 中实现:综合“集合预报区间→σ/μ→高温窗口→死盘判定→温度桶概率分布→边界提示”等,用于 bot 展示与 web 结构化数据输出。
|
||||
**城市决策层(Scan Terminal / 结构化实况层)**:地图点击城市后加入城市决策卡,前端拉取 full detail、多模型区间、最新 METAR,并通过 `/api/city/{name}/detail` 生成城市级 结构化解读。该解读由 `structured_signal`、`structured_signal`、`reasoning`、`structured_signal`、`risks` 与原始 METAR 证据组成;最高温中枢优先使用 DEB,再回退到 DEB、多模型中心、日内 pace 或当前实测。
|
||||
**城市决策层(Scan Terminal / 结构化实况层)**:地图点击城市后加入城市决策卡,前端拉取 full detail、多模型区间、最新 METAR/官方站点/跑道观测,并通过 `/api/city/{name}/detail` 生成城市级结构化数据。终端图表使用 HTTP snapshot + SSE patch + Redis/SQLite replay,最高温中枢优先使用 DEB hourly consensus,再结合多模型中心、日内 pace 和当前实测。
|
||||
**市场层(Polymarket 行情对照)**:*[v1.7.0 已移除]* 原先从 Gamma API 发现市场、从 CLOB 读取价格/盘口并计算”模型-市场差”,已于 2026-05-23 随 Polymarket 价格拉取层一并删除。当前 `market_scan` 返回空。
|
||||
**商业化与支付**:订阅(`Pro Monthly 10 USDC`)、积分抵扣、Polygon 链上收款合约(USDC/USDC.e),并提供“事件监听 + 周期确认”的自动补单机制。
|
||||
**支持的数据集/数据源**:项目不是传统“训练数据集+模型训练”的机器学习仓库;其“数据集”本质是外部实时/预报 API 与站点观测数据。对外部数据的使用需要遵守来源方的访问与速率限制,例如 AviationWeather Data API 明确限制请求频率(含每分钟请求上限/建议降低频率与使用缓存文件)。
|
||||
**许可证**:仓库根目录 `LICENSE` 当前为 `AGPL-3.0-only`。同时 README 与策略文档明确:品牌、商标、生产私有数据与运营策略不随代码许可证一并授权。
|
||||
(插图:项目 README 中包含产品截图,可用于快速理解信息架构与 UI 形态)
|
||||

|
||||
(插图:项目 README 中包含产品截图,可用于快速理解实时终端与 Telegram 推送形态)
|
||||

|
||||
|
||||
## 架构与代码库分析
|
||||
|
||||
@@ -45,7 +45,7 @@ DEB(Dynamic Error Balancing)基于过去 N 天模型误差(MAE)倒数加
|
||||
flowchart TB
|
||||
subgraph Clients
|
||||
WEB[Next.js Frontend<br/>Vercel]
|
||||
CITYCARD[Scan Terminal City Cards<br/>AI airport read + bucket mapping]
|
||||
CITYCARD[Scan Terminal City Cards<br/>structured observations + probability mapping]
|
||||
TG[Telegram Bot<br/>TeleBot + Handlers]
|
||||
end
|
||||
|
||||
@@ -127,7 +127,7 @@ Scan Terminal 的城市决策卡现在承担“从天气分析到市场动作解
|
||||
|
||||
**Python 依赖**:`requirements.txt` 包含 `requests`、`loguru`、`pyTelegramBotAPI`、`python-dotenv`、`numpy`、`web3`、`fastapi`、`uvicorn` 等,符合“采集+bot+api+链上交互”的需求。
|
||||
**容器环境**:`Dockerfile` 基于 `python:3.11-slim`,默认启动 bot;`docker-compose.yml` 通过不同 command 分别启动 bot 与 web(`python bot_listener.py` / `python web/app.py`),并挂载运行态数据目录。
|
||||
**前端依赖**:前端 README 描述 Next.js、Leaflet、Chart.js、Supabase Auth、WalletConnect 等;`frontend/package.json` 是前端依赖来源。
|
||||
**前端依赖**:前端 README 描述 Next.js、Leaflet、Recharts、Supabase Auth、WalletConnect 等;`frontend/package.json` 是前端依赖来源。
|
||||
### 数据预处理、模型与“训练/推理”管线
|
||||
|
||||
本项目的“模型”主要是统计融合与规则/启发式引擎,而非深度网络训练:
|
||||
@@ -181,7 +181,7 @@ Web/Telegram 请求 → FastAPI 调用采集器抓取/复用缓存 → 分析引
|
||||
**对标结论**:PolyWeather 与这类“全球神经天气模型”不在同一层级:PolyWeather 是“面向结算市场的产品化情报系统”,其价值核心是**将预测转成可交易/可结算的决策信息**。短中期内更高 ROI 的方向不是“自训大模型”,而是把现有“采集+后处理+市场映射”的链路做成**可复现、可观测、可评测、可扩展**的工程平台;在许可合规前提下,再评估引入外部模型推理作为额外信号源。
|
||||
## 优先级改进建议
|
||||
|
||||
下表按截至 `2026-05-23` 的真实状态重排优先级。已完成项不再继续列为”待做”,只保留当前仍需推进的事项。
|
||||
下表按截至 `2026-05-28` 的真实状态重排优先级。已完成项不再继续列为”待做”,只保留当前仍需推进的事项。
|
||||
| 优先级 | 改进项 | 预估工作量 | 主要收益 | 主要风险 | 可执行步骤(建议顺序) |
|
||||
| ------ | --------------------------------------------------------------------------------------------------------------------------------- | -------------------: | ------------------------------------------------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 中 | **把最小外部监控继续补深**:从“可告警”提升到“可运营” | 3–7 天 | 不再只知道服务坏没坏,还能看资源趋势、来源 SLA 和支付波动 | 指标过多会带来维护噪音 | 1) 增加节点 CPU/内存/磁盘 → 2) 增加 SQLite/支付体积与事件趋势 → 3) 把 HTTP/来源指标细分到城市/来源维度 → 4) 增加日报或异常摘要 |
|
||||
@@ -270,5 +270,3 @@ PolyWeather 的评测应围绕“结算场景”而非传统数值天气预报
|
||||
- Pangu-Weather(权重 BY-NC-SA,禁商用):https://github.com/198808xc/Pangu-Weather
|
||||
- Polymarket 官方 Python CLOB SDK(MIT):https://github.com/Polymarket/py-clob-client
|
||||
- aiopolymarket(async、类型安全):https://github.com/the-odds-company/aiopolymarket
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 261 KiB |
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
# PolyWeatherCheckout PolygonScan 验证(v1.8.0)
|
||||
# PolyWeatherCheckout PolygonScan 验证(v1.8.1)
|
||||
|
||||
最后更新:`2026-03-20`
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# PolyWeather 前端产品审查报告
|
||||
|
||||
> 审查日期:2026-06 | 视角:产品经理 | 范围:`frontend/` 全部页面、组件、用户流程
|
||||
> 审查日期:2026-06 | 视角:产品经理 | 范围:`frontend/` 全部页面、组件、用户流程 | 最后校准:2026-05-28
|
||||
|
||||
## 一、产品概览
|
||||
|
||||
@@ -18,7 +18,7 @@ PolyWeather 是一个面向天气衍生品交易者的气象情报平台。核
|
||||
|
||||
| 路由 | 功能 | 是否必需登录 |
|
||||
|------|------|-------------|
|
||||
| `/` | 主看板 — AI 天气决策台 | 否 |
|
||||
| `/` | 主看板 — 实时天气决策台 | 否 |
|
||||
| `/account` | 账户中心 — 身份/订阅/钱包/积分/Bot 绑定 | 是 |
|
||||
| `/auth/login` | 登录页(Google OAuth + 邮箱密码) | 否 |
|
||||
| `/docs/[...slug]` | 产品文档中心(8 篇双语文档) | 否 |
|
||||
@@ -37,7 +37,7 @@ PolyWeather 是一个面向天气衍生品交易者的气象情报平台。核
|
||||
│ 点击城市 → 自动添加到决策卡工作区 + 切换到卡片视图 │
|
||||
│ 免费用户和 Pro 用户均可使用 │
|
||||
├─ 决策卡(分析) ──────────────────────────────┤
|
||||
│ 钉选的城市卡片:AI 机场解读 + 模型集群 + 市场层 + 图表 │
|
||||
│ 钉选的城市卡片:结构化实况 + 模型集群 + 市场层 + 实时图表 │
|
||||
│ 需要 Pro 订阅 │
|
||||
└──────────────────────────────────────────────┘
|
||||
└── 右侧栏:城市简报面板(始终可见,免费可用)
|
||||
@@ -46,7 +46,7 @@ PolyWeather 是一个面向天气衍生品交易者的气象情报平台。核
|
||||
### 关键用户路径
|
||||
|
||||
1. **新用户落地** → 看到 3 步引导 → 地图 + 城市列表 → 点击城市 → 看到城市简报 → 想深入分析 → 遇到 Pro 付费墙(含功能说明)
|
||||
2. **Pro 用户工作流** → 地图选城市 → 自动钉选到决策卡 → 展开卡片 → 阅读 AI 报文解读 → 查看市场层 → 判断交易方向
|
||||
2. **Pro 用户工作流** → 地图选城市 → 自动钉选到决策卡 → 展开卡片 → 查看实测/DEB/概率/市场层 → 判断交易方向
|
||||
|
||||
## 三、做得好的地方
|
||||
|
||||
@@ -67,4 +67,3 @@ PolyWeather 是一个面向天气衍生品交易者的气象情报平台。核
|
||||
| 1 | **Docs 无搜索** — 8 篇文档没有搜索功能,用户必须逐篇浏览 | 🟢 待做 |
|
||||
| 2 | **Ops 面板无审计日志** — 管理员补发积分等操作没有审计记录 | 🟢 需后端 |
|
||||
| 3 | **注册后邮件验证引导** — 未验证邮箱的用户反复登录失败 | 🟡 需后端 |
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
**Goal:** Add Redis Stream as the production realtime event store and live fanout source while preserving the existing SSE patch protocol.
|
||||
|
||||
**Implementation status (2026-05-28):** completed in `v1.8.1`. Production uses Redis Stream when `POLYWEATHER_EVENT_STORE=redis`; SQLite remains the local/fallback event log. Browser API and frontend contract stayed on `/api/events` + `city_observation_patch.v1`.
|
||||
|
||||
**Architecture:** Keep `city_observation_patch.v1` and numeric `revision` as the browser contract. Add a Redis-backed event store beside the existing SQLite store, select it through an event-store factory, and let Redis-backed deployments fan out live events through a Redis subscriber loop instead of direct in-process ingest broadcast.
|
||||
|
||||
**Tech Stack:** FastAPI, redis-py, Redis Stream, SQLite fallback, pytest, existing frontend EventSource hook.
|
||||
@@ -16,21 +18,21 @@
|
||||
- Create: `tests/test_redis_realtime_event_store.py`
|
||||
- Create: `web/redis_realtime_event_store.py`
|
||||
|
||||
- [ ] **Step 1: Write failing tests**
|
||||
- [x] **Step 1: Write failing tests**
|
||||
|
||||
Cover append, replay by city, replay gap, and fallback-independent event shape.
|
||||
|
||||
- [ ] **Step 2: Verify tests fail**
|
||||
- [x] **Step 2: Verify tests fail**
|
||||
|
||||
Run: `python -m pytest tests/test_redis_realtime_event_store.py -q`
|
||||
|
||||
Expected: fails because `web.redis_realtime_event_store` does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement Redis store**
|
||||
- [x] **Step 3: Implement Redis store**
|
||||
|
||||
Add `RedisRealtimeEventStore` with `append_event`, `latest_revision`, `replay_events`, `replay_requires_resync`, and idempotent `start_live_subscription`.
|
||||
|
||||
- [ ] **Step 4: Verify tests pass**
|
||||
- [x] **Step 4: Verify tests pass**
|
||||
|
||||
Run: `python -m pytest tests/test_redis_realtime_event_store.py -q`
|
||||
|
||||
@@ -41,19 +43,19 @@ Run: `python -m pytest tests/test_redis_realtime_event_store.py -q`
|
||||
- Modify: `web/routers/sse_router.py`
|
||||
- Test: `tests/test_sse_replay.py`
|
||||
|
||||
- [ ] **Step 1: Write failing tests**
|
||||
- [x] **Step 1: Write failing tests**
|
||||
|
||||
Cover `POLYWEATHER_EVENT_STORE=redis` selecting Redis, SQLite fallback when Redis is not configured, and Redis ingest not doing direct local broadcast.
|
||||
|
||||
- [ ] **Step 2: Verify tests fail**
|
||||
- [x] **Step 2: Verify tests fail**
|
||||
|
||||
Run: `python -m pytest tests/test_sse_replay.py tests/test_realtime_event_store_factory.py -q`
|
||||
|
||||
- [ ] **Step 3: Implement factory and router integration**
|
||||
- [x] **Step 3: Implement factory and router integration**
|
||||
|
||||
Use Redis store when configured; otherwise keep existing SQLite store. Ingest broadcasts directly only for stores that do not provide external live fanout.
|
||||
|
||||
- [ ] **Step 4: Verify tests pass**
|
||||
- [x] **Step 4: Verify tests pass**
|
||||
|
||||
Run: `python -m pytest tests/test_sse_replay.py tests/test_realtime_event_store_factory.py -q`
|
||||
|
||||
@@ -63,11 +65,11 @@ Run: `python -m pytest tests/test_sse_replay.py tests/test_realtime_event_store_
|
||||
- Modify: `requirements.txt`
|
||||
- Modify: `docs/superpowers/specs/2026-05-27-redis-stream-realtime-event-architecture-design.md`
|
||||
|
||||
- [ ] **Step 1: Add redis-py dependency**
|
||||
- [x] **Step 1: Add redis-py dependency**
|
||||
|
||||
Add `redis>=5.0.0` to `requirements.txt`.
|
||||
|
||||
- [ ] **Step 2: Add final config notes**
|
||||
- [x] **Step 2: Add final config notes**
|
||||
|
||||
Ensure the design doc names the runtime env vars used by code.
|
||||
|
||||
@@ -76,7 +78,7 @@ Ensure the design doc names the runtime env vars used by code.
|
||||
**Files:**
|
||||
- No production edits unless tests reveal a gap.
|
||||
|
||||
- [ ] **Step 1: Run backend realtime tests**
|
||||
- [x] **Step 1: Run backend realtime tests**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -84,7 +86,7 @@ Run:
|
||||
python -m pytest tests/test_realtime_patch_schema.py tests/test_realtime_event_store.py tests/test_redis_realtime_event_store.py tests/test_realtime_event_store_factory.py tests/test_sse_replay.py -q
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run broader frontend/backend checks**
|
||||
- [x] **Step 2: Run broader frontend/backend checks**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -95,7 +97,7 @@ npm run typecheck
|
||||
npm run build
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Inspect diff**
|
||||
- [x] **Step 3: Inspect diff**
|
||||
|
||||
Run:
|
||||
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@
|
||||
> 日期: 2026-05-27
|
||||
> 范围: PolyWeather 网站图表实时观测事件层
|
||||
> 目标服务器: 2 vCPU / 8 GB RAM / 50 GB 系统盘
|
||||
> 实施状态: 2026-05-28 已落地为 `v1.8.1`
|
||||
|
||||
## 背景
|
||||
|
||||
@@ -29,7 +30,7 @@
|
||||
- 前端协议保持 `city_observation_patch.v1`,不让浏览器感知 Redis。
|
||||
- 继续使用 numeric `revision`,兼容现有前端 `since_revision` 逻辑。
|
||||
- 支持按城市订阅、断线 replay、多 worker fanout。
|
||||
- 事件只保留短窗口,默认 24 小时;生产不建议超过 24 小时。
|
||||
- 事件只保留短窗口;当前默认按 Redis Stream `MAXLEN` 控制,建议以约 24 小时 replay 为目标,不做 72 小时保留。
|
||||
|
||||
## 非目标
|
||||
|
||||
|
||||
+9
-2
@@ -9,7 +9,7 @@ PolyWeather Pro 的生产前端工程。
|
||||
|
||||
- Next.js App Router
|
||||
- React + Tailwind
|
||||
- Leaflet + Chart.js
|
||||
- Leaflet + Recharts
|
||||
- Supabase Auth
|
||||
- WalletConnect + 浏览器 EVM 钱包
|
||||
|
||||
@@ -23,6 +23,11 @@ PolyWeather Pro 的生产前端工程。
|
||||
|
||||
- 主站 Dashboard 支持地图、城市详情、今日日内分析和账户中心
|
||||
- `/docs` 已提供公开双语产品文档中心,解释日内分析、校准概率、模型栈、TAF 和结算来源
|
||||
- 终端图表默认展示全天,可切换高温窗口;所有横轴与 tooltip 时间按城市当地时间渲染
|
||||
- 可见终端图表通过 SSE patch 无痛增量刷新,后台切回前台时主动补齐最新 detail,不用 loading 遮罩覆盖已有曲线
|
||||
- AMSC/AMOS 跑道城市默认展示结算跑道曲线并高亮,辅助跑道弱化展示;单跑道机场不重复展示聚合线
|
||||
- 香港默认展示 CoWIN 6087 参考站 1 分钟曲线,并保留 HKO 10 分钟官方气象层
|
||||
- legacy 高斯概率在图表上展示为概率温度带和 `mu` 参考线,不作为时间序列曲线
|
||||
- 今日日内分析支持:
|
||||
- `锚点状态`
|
||||
- `当前节奏`
|
||||
@@ -185,6 +190,8 @@ Ops:
|
||||
- 支付相关路由:`no-store`
|
||||
- 当 detail 缓存只返回单模型或单日 forecast 时,前端会自动强刷完整 detail,并在补齐前显示同步提示 / 占位卡
|
||||
- 今日日内分析打开时如果正在切换城市、日期或 detail 深度,弹窗会阻断旧内容点击并显示刷新锁
|
||||
- 终端图表订阅 `/api/events?cities=...&since_revision=...&replay_limit=500`,接收 `city_observation_patch.v1`;无 patch 超过 2 分钟时,可见图表才触发 60 秒兜底刷新
|
||||
- 前端只消费 HTTP snapshot + SSE patch,不直接感知 Redis;Redis Stream / SQLite event log 都由后端统一封装
|
||||
|
||||
## Vercel 节流建议
|
||||
|
||||
@@ -199,4 +206,4 @@ Ops:
|
||||
|
||||
详见根目录策略文档:`docs/OPEN_CORE_POLICY.md`
|
||||
|
||||
最后更新:`2026-05-23`
|
||||
最后更新:`2026-05-28`
|
||||
|
||||
@@ -3,18 +3,14 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
Activity,
|
||||
ArrowRight,
|
||||
BarChart3,
|
||||
Check,
|
||||
CheckCircle2,
|
||||
CloudSun,
|
||||
Gauge,
|
||||
LineChart,
|
||||
LockKeyhole,
|
||||
Radar,
|
||||
ShieldCheck,
|
||||
TrendingUp,
|
||||
} from "lucide-react";
|
||||
import { I18nProvider, useI18n } from "@/hooks/useI18n";
|
||||
import {
|
||||
@@ -22,22 +18,6 @@ import {
|
||||
hasSupabasePublicEnv,
|
||||
} from "@/lib/supabase/client";
|
||||
|
||||
const RAW_MARKET_ROWS_EN = [
|
||||
["New York", "91.8°F", "+2.4", "High Anomaly", "Approve Temp"],
|
||||
["Austin", "103.1°F", "+1.1", "Normal", "Observe"],
|
||||
["Seoul", "83.4°F", "-0.7", "Low Anomaly", "Veto Anomaly"],
|
||||
["Tokyo", "88.2°F", "+1.8", "High Anomaly", "Approve Temp"],
|
||||
["London", "72.6°F", "-0.2", "Normal", "Observe"],
|
||||
];
|
||||
|
||||
const RAW_MARKET_ROWS_ZH = [
|
||||
["纽约", "91.8°F", "+2.4", "高偏差", "符合高温"],
|
||||
["奥斯汀", "103.1°F", "+1.1", "正常", "持续观察"],
|
||||
["首尔", "83.4°F", "-0.7", "低偏差", "否决偏差"],
|
||||
["东京", "88.2°F", "+1.8", "高偏差", "符合高温"],
|
||||
["伦敦", "72.6°F", "-0.2", "正常", "持续观察"],
|
||||
];
|
||||
|
||||
const COVERAGE_EN = [
|
||||
"Live airport observations",
|
||||
"DEB blend forecast",
|
||||
@@ -94,7 +74,6 @@ function InstitutionalLandingScreen() {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const marketRows = isEn ? RAW_MARKET_ROWS_EN : RAW_MARKET_ROWS_ZH;
|
||||
const coverage = isEn ? COVERAGE_EN : COVERAGE_ZH;
|
||||
const platformCards = isEn
|
||||
? [
|
||||
@@ -132,10 +111,6 @@ function InstitutionalLandingScreen() {
|
||||
},
|
||||
];
|
||||
|
||||
const modelLabels = isEn
|
||||
? ["DEB Blend", "Live METAR", "Model Consensus"]
|
||||
: ["DEB 融合预测", "METAR 机场实测", "模型多方共识"];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#f4f7fb] text-slate-950">
|
||||
<header className="sticky top-0 z-30 border-b border-slate-200 bg-white/95 backdrop-blur">
|
||||
@@ -263,88 +238,28 @@ function InstitutionalLandingScreen() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl border border-slate-300 bg-white shadow-[0_24px_80px_rgba(15,23,42,0.16)]">
|
||||
<div className="overflow-hidden rounded-2xl border border-slate-300 bg-white shadow-[0_24px_80px_rgba(15,23,42,0.16)] animate-fade-up [animation-delay:650ms] opacity-0">
|
||||
<div className="flex items-center justify-between border-b border-slate-200 px-4 py-3">
|
||||
<div className="flex items-center gap-2 text-sm font-bold">
|
||||
<BarChart3 size={16} className="text-blue-700" />
|
||||
{isEn ? "Weather Intelligence Console" : "气象决策分析台"}
|
||||
<LineChart size={16} className="text-blue-700" />
|
||||
{isEn ? "Realtime Terminal" : "实时终端"}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs font-semibold text-slate-500">
|
||||
<span className="h-2 w-2 rounded-full bg-emerald-500 animate-pulse" />
|
||||
{isEn ? "Live" : "实时数据"}
|
||||
{isEn ? "Real product screenshot" : "真实产品截图"}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-3 p-3 lg:grid-cols-[1fr_0.85fr]">
|
||||
<div className="rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow">
|
||||
<div className="flex items-center justify-between border-b border-slate-200 bg-slate-50/80 px-4 py-2.5">
|
||||
<strong className="text-sm text-slate-800">{isEn ? "Temperature Metrics" : "温度决策指标"}</strong>
|
||||
<span className="text-xs font-semibold text-slate-500 uppercase tracking-wider">
|
||||
{isEn ? "Observed / Deviation / Decision" : "实测 / 偏差 / 决策"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="divide-y divide-slate-100">
|
||||
{marketRows.map((row) => (
|
||||
<div
|
||||
key={row[0]}
|
||||
className="grid grid-cols-[1.1fr_0.8fr_0.6fr_0.7fr_0.9fr] items-center gap-2 px-3 py-3 text-sm"
|
||||
>
|
||||
<span className="font-semibold">{row[0]}</span>
|
||||
<span className="font-mono text-slate-700">{row[1]}</span>
|
||||
<span
|
||||
className={
|
||||
row[2].startsWith("+")
|
||||
? "font-mono font-bold text-emerald-700"
|
||||
: "font-mono font-bold text-red-600"
|
||||
}
|
||||
>
|
||||
{row[2]}
|
||||
</span>
|
||||
<span className="text-xs font-bold text-slate-500">
|
||||
{row[3]}
|
||||
</span>
|
||||
<span className="rounded bg-blue-50 px-2 py-1 text-xs font-bold text-blue-700">
|
||||
{row[4]}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div className="rounded-xl border border-slate-200 p-4">
|
||||
<div className="mb-3 flex items-center justify-between">
|
||||
<strong className="text-sm">{isEn ? "Model Stack" : "模型堆栈"}</strong>
|
||||
<LineChart size={16} className="text-blue-700" />
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{modelLabels.map((label, index) => (
|
||||
<div key={label}>
|
||||
<div className="mb-1 flex justify-between text-xs font-semibold text-slate-500">
|
||||
<span>{label}</span>
|
||||
<span>{[82, 67, 74][index]}%</span>
|
||||
</div>
|
||||
<div className="h-2 rounded-full bg-slate-100">
|
||||
<div
|
||||
className="h-2 rounded-full bg-blue-600"
|
||||
style={{ width: `${[82, 67, 74][index]}%` }}
|
||||
<div className="bg-slate-50 p-2 sm:p-3">
|
||||
<div className="overflow-hidden rounded-xl border border-slate-200 bg-white">
|
||||
<img
|
||||
src="/static/web.png"
|
||||
alt={isEn ? "PolyWeather realtime terminal screenshot" : "PolyWeather 实时终端截图"}
|
||||
className="block h-auto w-full object-contain"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-xl border border-emerald-200 bg-emerald-50 p-4">
|
||||
<div className="mb-2 flex items-center gap-2 text-sm font-bold text-emerald-800">
|
||||
<TrendingUp size={16} className="animate-pulse" />
|
||||
{isEn ? "Current Anomaly / Action" : "当前异常与决策"}
|
||||
</div>
|
||||
<p className="text-sm leading-relaxed text-emerald-900/90 font-medium">
|
||||
{isEn
|
||||
? "New York high-temperature target shows a positive observation deviation with confirmed airport evidence."
|
||||
: "纽约高温阈值监测出现显著的正向观测偏差,机场天气实况已验证确认。"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -420,8 +335,8 @@ function InstitutionalLandingScreen() {
|
||||
</h3>
|
||||
<p className="mt-3 text-sm text-slate-500 leading-relaxed">
|
||||
{isEn
|
||||
? "Full access to the institutional weather intelligence workspace. Live METAR, DEB forecasts, probability distribution, AI decision cards, and real-time alerts."
|
||||
: "完整访问机构级天气决策分析台。实时 METAR、DEB 预报、概率分布、AI 决策卡片、实时通知。"}
|
||||
? "Full access to the institutional weather intelligence workspace. Live METAR, DEB forecasts, probability distribution, realtime terminal charts, and alerts."
|
||||
: "完整访问机构级天气决策分析台。实时 METAR、DEB 预报、概率分布、实时终端图表、实时通知。"}
|
||||
</p>
|
||||
<div className="mt-6 flex items-baseline">
|
||||
<span className="text-5xl font-black tracking-tight text-slate-900">
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "polyweather-frontend",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "polyweather-frontend",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.1",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "^1.1.2",
|
||||
"@supabase/ssr": "^0.5.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "polyweather-frontend",
|
||||
"version": "1.8.0",
|
||||
"version": "1.8.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node scripts/sync-next-server-chunks.mjs --clean-root && next dev",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 352 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 237 KiB |
Reference in New Issue
Block a user