release: v1.4.0
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 1.4.0 - 2026-03-14
|
||||||
|
|
||||||
|
- 统一收费阶段产品口径,发布 PolyWeather Pro `v1.4.0`
|
||||||
|
- 前端交付覆盖账户、支付、权限展示与缓存策略
|
||||||
|
- 支付链路支持 intent -> submit -> confirm 与自动补单
|
||||||
|
- 文档统一切换到单一版本源管理
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# 前端交付与重构报告(v1.4)
|
# 前端交付与重构报告(v1.4.0)
|
||||||
|
|
||||||
最后更新:`2026-03-14`
|
最后更新:`2026-03-14`
|
||||||
|
|
||||||
|
|||||||
@@ -131,8 +131,10 @@ docker compose logs -f polyweather | egrep "polymarket wallet activity watcher s
|
|||||||
- Tech debt (ZH): [docs/TECH_DEBT_ZH.md](docs/TECH_DEBT_ZH.md)
|
- Tech debt (ZH): [docs/TECH_DEBT_ZH.md](docs/TECH_DEBT_ZH.md)
|
||||||
- Payment verification: [docs/payments/POLYGONSCAN_VERIFY.md](docs/payments/POLYGONSCAN_VERIFY.md)
|
- Payment verification: [docs/payments/POLYGONSCAN_VERIFY.md](docs/payments/POLYGONSCAN_VERIFY.md)
|
||||||
- Frontend report: [FRONTEND_REDESIGN_REPORT.md](FRONTEND_REDESIGN_REPORT.md)
|
- Frontend report: [FRONTEND_REDESIGN_REPORT.md](FRONTEND_REDESIGN_REPORT.md)
|
||||||
|
- Release process: [RELEASE.md](RELEASE.md)
|
||||||
|
- Changelog: [CHANGELOG.md](CHANGELOG.md)
|
||||||
|
|
||||||
## Version
|
## Version
|
||||||
|
|
||||||
- Version: `v1.4`
|
- Version: `v1.4.0`
|
||||||
- Last Updated: `2026-03-14`
|
- Last Updated: `2026-03-14`
|
||||||
|
|||||||
+3
-1
@@ -131,8 +131,10 @@ docker compose logs -f polyweather | egrep "polymarket wallet activity watcher s
|
|||||||
- 技术债(主文档):[docs/TECH_DEBT.md](docs/TECH_DEBT.md)
|
- 技术债(主文档):[docs/TECH_DEBT.md](docs/TECH_DEBT.md)
|
||||||
- 支付合约验证:[docs/payments/POLYGONSCAN_VERIFY.md](docs/payments/POLYGONSCAN_VERIFY.md)
|
- 支付合约验证:[docs/payments/POLYGONSCAN_VERIFY.md](docs/payments/POLYGONSCAN_VERIFY.md)
|
||||||
- 前端报告:[FRONTEND_REDESIGN_REPORT.md](FRONTEND_REDESIGN_REPORT.md)
|
- 前端报告:[FRONTEND_REDESIGN_REPORT.md](FRONTEND_REDESIGN_REPORT.md)
|
||||||
|
- 发布流程:[RELEASE.md](RELEASE.md)
|
||||||
|
- 变更记录:[CHANGELOG.md](CHANGELOG.md)
|
||||||
|
|
||||||
## 当前版本
|
## 当前版本
|
||||||
|
|
||||||
- 版本:`v1.4`
|
- 版本:`v1.4.0`
|
||||||
- 最后更新:`2026-03-14`
|
- 最后更新:`2026-03-14`
|
||||||
|
|||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
# 版本发布流程
|
||||||
|
|
||||||
|
本项目采用语义化版本号:`MAJOR.MINOR.PATCH`
|
||||||
|
|
||||||
|
当前单一版本源为根目录 [VERSION](E:/web/PolyWeather/VERSION) 文件,所有对外文档与前端版本号都从这里同步。
|
||||||
|
|
||||||
|
## 版本规则
|
||||||
|
|
||||||
|
- `PATCH`:修复缺陷、文档修正、兼容性不变的小改动
|
||||||
|
- `MINOR`:新增能力、接口扩展、向后兼容的功能迭代
|
||||||
|
- `MAJOR`:不兼容变更、核心架构升级、公开接口重大调整
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
- `1.4.0 -> 1.4.1`:告警逻辑修正、缓存修正、文档修正
|
||||||
|
- `1.4.0 -> 1.5.0`:新增支付能力、新增页面、新增 API
|
||||||
|
- `1.4.0 -> 2.0.0`:接口重构或数据结构不兼容
|
||||||
|
|
||||||
|
## 日常升版步骤
|
||||||
|
|
||||||
|
### 1. 升版本号
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/bump_version.py patch
|
||||||
|
```
|
||||||
|
|
||||||
|
可选参数:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/bump_version.py minor
|
||||||
|
python scripts/bump_version.py major
|
||||||
|
python scripts/bump_version.py 1.5.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 检查同步结果
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/sync_version.py
|
||||||
|
git diff
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 补充 Changelog
|
||||||
|
|
||||||
|
在 [CHANGELOG.md](E:/web/PolyWeather/CHANGELOG.md) 对应版本下补齐:
|
||||||
|
|
||||||
|
- 新增能力
|
||||||
|
- 修复项
|
||||||
|
- 兼容性说明
|
||||||
|
|
||||||
|
### 4. 验证
|
||||||
|
|
||||||
|
建议至少执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
如涉及后端核心逻辑,补充执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 提交与打标签
|
||||||
|
|
||||||
|
工作区干净后再打标签:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add .
|
||||||
|
git commit -m "release: v1.4.1"
|
||||||
|
git tag v1.4.1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 推送
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push
|
||||||
|
git push origin v1.4.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## 当前约束
|
||||||
|
|
||||||
|
- 不直接手改多份文档版本号
|
||||||
|
- 不在工作区脏状态下打 release tag
|
||||||
|
- `README.md`、前端 `package.json`、文档标题版本都通过脚本同步
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# PolyWeather API 文档(v1.4)
|
# PolyWeather API 文档(v1.4.0)
|
||||||
|
|
||||||
最后更新:`2026-03-14`
|
最后更新:`2026-03-14`
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Supabase + 登录 + 支付接入说明(v1.4)
|
# Supabase + 登录 + 支付接入说明(v1.4.0)
|
||||||
|
|
||||||
最后更新:`2026-03-14`
|
最后更新:`2026-03-14`
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# 技术债与工程待办(v1.4)
|
# 技术债与工程待办(v1.4.0)
|
||||||
|
|
||||||
最后更新:`2026-03-14`
|
最后更新:`2026-03-14`
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 技术债与工程待办(v1.4)
|
# 技术债与工程待办(v1.4.0)
|
||||||
|
|
||||||
最后更新:`2026-03-14`
|
最后更新:`2026-03-14`
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
## 执行摘要
|
## 执行摘要
|
||||||
|
|
||||||
PolyWeather(仓库:`yangyuan-zhen/PolyWeather`)定位为**面向温度类结算预测市场(如 Polymarket 的温度结算合约)**的“生产级气象情报系统”,核心在于把多源天气观测/预报转化为**结算导向的概率桶(μ + bucket distribution)**,并进一步映射到市场报价完成**错价扫描**;同时提供 Web 仪表盘与 Telegram Bot 两套交互入口,并包含 Polygon 链上 USDC/USDC.e 支付、自动补单与订阅/积分体系。项目 README 明确其“Open-Core”边界:仓库公开天气聚合、基础分析、看板、Bot、标准支付流程;生产私有部分包含商业风控、阈值与运营工具等。
|
PolyWeather(仓库:`yangyuan-zhen/PolyWeather`)定位为**面向温度类结算预测市场(如 Polymarket 的温度结算合约)**的“生产级气象情报系统”,核心在于把多源天气观测/预报转化为**结算导向的概率桶(μ + bucket distribution)**,并进一步映射到市场报价完成**错价扫描**;同时提供 Web 仪表盘与 Telegram Bot 两套交互入口,并包含 Polygon 链上 USDC/USDC.e 支付、自动补单与订阅/积分体系。项目 README 明确其“Open-Core”边界:仓库公开天气聚合、基础分析、看板、Bot、标准支付流程;生产私有部分包含商业风控、阈值与运营工具等。
|
||||||
从工程实现看,当前版本(README 标注 `v1.4`,最后更新 `2026-03-14`)已经落地关键业务闭环:多源天气采集(Open-Meteo、AviationWeather METAR、土耳其 MGM、香港 HKO、台湾 CWA、美国 NWS 等)、DEB 动态融合、趋势/概率引擎、Polymarket 只读行情层、前端缓存策略(ETag/304、force_refresh no-store)、以及支付事件监听与确认循环。
|
从工程实现看,当前版本(README 标注 `v1.4.0`,最后更新 `2026-03-14`)已经落地关键业务闭环:多源天气采集(Open-Meteo、AviationWeather METAR、土耳其 MGM、香港 HKO、台湾 CWA、美国 NWS 等)、DEB 动态融合、趋势/概率引擎、Polymarket 只读行情层、前端缓存策略(ETag/304、force_refresh no-store)、以及支付事件监听与确认循环。
|
||||||
但它也暴露出典型的“从快速迭代走向稳态生产”的结构性问题:核心模块(如 `src/data_collection/weather_sources.py`、`web/app.py`)体量巨大、职责耦合;配置/依赖/可复现性仍偏脚本化;测试覆盖存在但 CI/CD 缺失;多处自定义缓存与状态文件并发一致性风险;以及对第三方 API(Open-Meteo、AviationWeather、Supabase、Polymarket)在**配额、变更、SLA、合规**方面需要更系统的治理。
|
但它也暴露出典型的“从快速迭代走向稳态生产”的结构性问题:核心模块(如 `src/data_collection/weather_sources.py`、`web/app.py`)体量巨大、职责耦合;配置/依赖/可复现性仍偏脚本化;测试覆盖存在但 CI/CD 缺失;多处自定义缓存与状态文件并发一致性风险;以及对第三方 API(Open-Meteo、AviationWeather、Supabase、Polymarket)在**配额、变更、SLA、合规**方面需要更系统的治理。
|
||||||
本报告给出的改进方向按收益/风险/工作量分级,优先建议聚焦三条主线:
|
本报告给出的改进方向按收益/风险/工作量分级,优先建议聚焦三条主线:
|
||||||
第一,**模块化与工程化**:将采集/分析/市场层/支付/鉴权拆成清晰边界,补齐 CI、测试、类型与规范化配置;第二,**可观测性与可靠性**:统一缓存与状态管理、增加限流与退避策略、建立指标与告警;第三,**模型与评测体系**:围绕“结算命中率 + 偏差(MAE/RMSE)+ 概率校准(Brier/CRPS)+ 错价信号有效性(Edge/PnL 模拟)”建立基准与回归测试,并可在合规前提下评估引入更先进的气象后处理(如 EMOS)或外部 AI 预报(GraphCast/FourCastNet/Pangu-Weather 的商业许可限制需特别注意)。
|
第一,**模块化与工程化**:将采集/分析/市场层/支付/鉴权拆成清晰边界,补齐 CI、测试、类型与规范化配置;第二,**可观测性与可靠性**:统一缓存与状态管理、增加限流与退避策略、建立指标与告警;第三,**模型与评测体系**:围绕“结算命中率 + 偏差(MAE/RMSE)+ 概率校准(Brier/CRPS)+ 错价信号有效性(Edge/PnL 模拟)”建立基准与回归测试,并可在合规前提下评估引入更先进的气象后处理(如 EMOS)或外部 AI 预报(GraphCast/FourCastNet/Pangu-Weather 的商业许可限制需特别注意)。
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# PolyWeatherCheckout PolygonScan 验证(v1.4)
|
# PolyWeatherCheckout PolygonScan 验证(v1.4.0)
|
||||||
|
|
||||||
最后更新:`2026-03-14`
|
最后更新:`2026-03-14`
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "polyweather-frontend",
|
"name": "polyweather-frontend",
|
||||||
"version": "0.1.0",
|
"version": "1.4.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
VERSION_FILE = ROOT / "VERSION"
|
||||||
|
CHANGELOG_FILE = ROOT / "CHANGELOG.md"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_version(raw: str) -> tuple[int, int, int]:
|
||||||
|
match = re.fullmatch(r"(\d+)\.(\d+)\.(\d+)", raw.strip())
|
||||||
|
if not match:
|
||||||
|
raise ValueError(f"Invalid version: {raw!r}")
|
||||||
|
return tuple(int(part) for part in match.groups())
|
||||||
|
|
||||||
|
|
||||||
|
def format_version(parts: tuple[int, int, int]) -> str:
|
||||||
|
return ".".join(str(part) for part in parts)
|
||||||
|
|
||||||
|
|
||||||
|
def bump(parts: tuple[int, int, int], level: str) -> tuple[int, int, int]:
|
||||||
|
major, minor, patch = parts
|
||||||
|
if level == "patch":
|
||||||
|
return major, minor, patch + 1
|
||||||
|
if level == "minor":
|
||||||
|
return major, minor + 1, 0
|
||||||
|
if level == "major":
|
||||||
|
return major + 1, 0, 0
|
||||||
|
raise ValueError(f"Unsupported bump level: {level}")
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_changelog_entry(version: str) -> None:
|
||||||
|
text = CHANGELOG_FILE.read_text(encoding="utf-8")
|
||||||
|
if re.search(rf"^## {re.escape(version)} - ", text, flags=re.MULTILINE):
|
||||||
|
return
|
||||||
|
heading = f"## {version} - TBD"
|
||||||
|
lines = text.splitlines()
|
||||||
|
if not lines:
|
||||||
|
updated = f"# Changelog\n\n{heading}\n\n- TBD\n"
|
||||||
|
else:
|
||||||
|
updated = "\n".join([lines[0], "", heading, "", "- TBD", "", *lines[1:]]) + "\n"
|
||||||
|
CHANGELOG_FILE.write_text(updated, encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
raise SystemExit("Usage: python scripts/bump_version.py [patch|minor|major|X.Y.Z]")
|
||||||
|
|
||||||
|
current = parse_version(VERSION_FILE.read_text(encoding="utf-8"))
|
||||||
|
arg = sys.argv[1].strip()
|
||||||
|
|
||||||
|
if re.fullmatch(r"\d+\.\d+\.\d+", arg):
|
||||||
|
new_version = arg
|
||||||
|
else:
|
||||||
|
new_version = format_version(bump(current, arg))
|
||||||
|
|
||||||
|
VERSION_FILE.write_text(new_version + "\n", encoding="utf-8")
|
||||||
|
ensure_changelog_entry(new_version)
|
||||||
|
print(f"Bumped version: {format_version(current)} -> {new_version}")
|
||||||
|
print("Next step: python scripts/sync_version.py")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
VERSION_FILE = ROOT / "VERSION"
|
||||||
|
VERSION = VERSION_FILE.read_text(encoding="utf-8").strip()
|
||||||
|
DISPLAY_VERSION = f"v{VERSION}"
|
||||||
|
|
||||||
|
|
||||||
|
TEXT_REPLACEMENTS = {
|
||||||
|
ROOT / "README.md": [
|
||||||
|
(r"- Version: `v[\d.]+`", f"- Version: `{DISPLAY_VERSION}`"),
|
||||||
|
],
|
||||||
|
ROOT / "README_ZH.md": [
|
||||||
|
(r"- 版本:`v[\d.]+`", f"- 版本:`{DISPLAY_VERSION}`"),
|
||||||
|
],
|
||||||
|
ROOT / "FRONTEND_REDESIGN_REPORT.md": [
|
||||||
|
(r"前端交付与重构报告(v[\d.]+)", f"前端交付与重构报告({DISPLAY_VERSION})"),
|
||||||
|
],
|
||||||
|
ROOT / "docs" / "API_ZH.md": [
|
||||||
|
(r"PolyWeather API 文档(v[\d.]+)", f"PolyWeather API 文档({DISPLAY_VERSION})"),
|
||||||
|
],
|
||||||
|
ROOT / "docs" / "SUPABASE_SETUP_ZH.md": [
|
||||||
|
(r"Supabase \+ 登录 \+ 支付接入说明(v[\d.]+)", f"Supabase + 登录 + 支付接入说明({DISPLAY_VERSION})"),
|
||||||
|
],
|
||||||
|
ROOT / "docs" / "TECH_DEBT.md": [
|
||||||
|
(r"技术债与工程待办(v[\d.]+)", f"技术债与工程待办({DISPLAY_VERSION})"),
|
||||||
|
],
|
||||||
|
ROOT / "docs" / "TECH_DEBT_ZH.md": [
|
||||||
|
(r"技术债与工程待办(v[\d.]+)", f"技术债与工程待办({DISPLAY_VERSION})"),
|
||||||
|
],
|
||||||
|
ROOT / "docs" / "payments" / "POLYGONSCAN_VERIFY.md": [
|
||||||
|
(r"PolyWeatherCheckout PolygonScan 验证(v[\d.]+)", f"PolyWeatherCheckout PolygonScan 验证({DISPLAY_VERSION})"),
|
||||||
|
],
|
||||||
|
ROOT / "docs" / "deep-research-report.md": [
|
||||||
|
(r"README 标注 `v[\d.]+`", f"README 标注 `{DISPLAY_VERSION}`"),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def sync_text_file(path: Path, replacements: list[tuple[str, str]]) -> None:
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
updated = text
|
||||||
|
for pattern, replacement in replacements:
|
||||||
|
updated = re.sub(pattern, replacement, updated)
|
||||||
|
if updated != text:
|
||||||
|
path.write_text(updated, encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def sync_frontend_package() -> None:
|
||||||
|
path = ROOT / "frontend" / "package.json"
|
||||||
|
data = json.loads(path.read_text(encoding="utf-8"))
|
||||||
|
if data.get("version") != VERSION:
|
||||||
|
data["version"] = VERSION
|
||||||
|
path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
for path, replacements in TEXT_REPLACEMENTS.items():
|
||||||
|
sync_text_file(path, replacements)
|
||||||
|
sync_frontend_package()
|
||||||
|
print(f"Synchronized version to {VERSION}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user