fare-alert
Provides destination city photos via Wikipedia's REST API for dashboard city cards, with caching and an offline fallback to built-in travel poster silhouettes when the Wikimedia domain is unreachable.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@fare-alertset a low-fare alert for Hangzhou to Chongqing under ¥500 one-way"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
FareAlert · 低价出行提醒组件
监控未来 N 天 机票最低价(含机建燃油,即最终支付口径),低于你的心理价位就推送到 iPhone;同时对比 12306 列车全席位票价(二等座/卧铺/普速,含学生票估算),帮你选出出行最优方案。自带 Web 仪表盘,单文件依赖极简,适合部署在私人设备上长期运行。
个人比价参考工具:数据来自公开接口的低频查询,不破解任何签名/验证码,购票始终跳转官方/平台页面人工完成。
📖 新手建议先看 使用指南.md —— 组件介绍 / 从零搭建 / 推送配置 / 日常使用,一条龙图文教程。
功能特性
✈️ 机票低价监控:去哪儿价格日历(免登录公开接口),一次拉取未来数月每日最低价+航班号,总价=裸价+机建+燃油(可配置)
🔔 低于心理价位即推送:Bark(iPhone 通知)+ ServerChan(微信)双通道,带去重/冷却/降价再提醒,不轰炸
🚄 列车对比(全席位):12306 票价接口(免登录),接口查到什么席位就展示什么(二等座/一等座/商务座/硬卧/软卧/动卧/硬座…),含学生票估算,识别夕发朝至车次
🧳 托运行李标注:按航司标注是否含免费托运,廉航/特价舱自动加 ⚠️ 提示
🧠 联想输入:城市/车站全部从数据源字典联想选择(中文/全拼/首字母,如 hzd→杭州东),不依赖手输精确文字,不会因偏差走模糊搜索
📊 Web 仪表盘:60 天价格热力日历、价格趋势图、"出行最优"卡片对比(机票/动车/学生动车/卧铺)、配置全可视化编辑
⚙️ 一切皆可配:出发/到达城市、窗口天数、心理价位、税费、数据源开关、查询频率——页面上直接改
📦 多线路:支持同时监控任意多条线路(如 杭州→重庆、成都→杭州 …)
🔁 往返模式:按"去程+返程合计总价"判定阈值,主趋势图显示往返合计、另附返程独立趋势,日历热力按合计着色
🌍 国际航线:免 Key 即可用——自动接入去哪儿国际特价日历(稀疏但真实的含税促销价);配置免费 Amadeus Key 后升级为全日期覆盖 + 真实起降时刻(数据源页内置 3 步配置向导 + 一键测试连接)
🕐 航班时刻与时长(v0.19 跨日班期补全):详情卡起飞→落地时间轴;杭州出发/到达线路自动经机场官网公开班期板沉淀「航班号+星期几」计划时刻库(零密钥、6h 缓存),目标星期未沉淀时自动借用同号航班其他班期时刻(「跨日班期·参考」徽标),仅有起飞时落地按大圆估算(~ 前缀);配置 Amadeus 后优先实时刻
📱 PWA(v0.20):PNG 图标 + Service Worker 离线壳(静态资源缓存优先、接口网络优先+离线兜底),iPhone Safari「添加到主屏幕」后获得独立图标与最近一屏数据的离线阅读;数据源页新增「时刻库沉淀进度」七天可视化,时刻缺口原因一目了然
🩹 缺价日自动补全:去哪儿日历缓存没价的日期(返回空价)自动用 Amadeus 含税最低价补洞,日历/趋势/最优卡打「Amadeus补」徽标,补全结果缓存 24 小时保护免费额度;未配密钥时优雅跳过
📐 无价日插值估算(v0.11):两侧 14 天内都有真实价的空洞日按距离线性插值(琥珀色斜纹+≈前缀,取整到 10 元);插值/参考价一律不触发提醒、不参与最低价/均价/TOP5 统计,趋势图估算点为琥珀空心圆
🕸 爬虫监控: 每次抓取逐步实时可视化——数据源/动作/耗时/条数/缓存命中/错误,保留最近 20 次运行历史; 手动查询时自动 2 秒轮询刷新
🫀 数据源健康自愈(v0.12): 每轮抓取自动聚合各源健康分(滑动窗口),单轮≥2步失败或连续3轮失败即降级并展开诊断(候选原因+修复动作),恢复后自动解除;爬虫监控页顶部常驻绿/琥珀/红徽标,MCP snapshot_get 同步透出降级源供 Agent 自助排障
🧭 预算找目的地(v0.13): "¥X 以内从 A 出发能去哪"——逐候选查价格日历,按含税总价升序返回可去列表(日期/航司/直达购票链接/实时·缓存新鲜度徽标);实发请求有硬预算(默认8,上限15,缓存命中不耗预算),结果缓存 6 小时;Web UI 新增「预算找目的地」页,MCP 新增 reverse_search 工具
📈 洞察周报(v0.14): 每轮查询自动归档每日线路指标(最低含税价/均价/低价天数,保留180天),周报汇总本周最低·均价·环比上周·最佳出发日,每个数字可从历史重算;Web UI 新增「洞察周报」页(周度走势图+摘要卡),推送默认关闭(push.weekly_enabled 开启后每 7 天自动推一次)
🔗 一键直达购票: KPI 卡、对比卡、车次表全部可点——机票直达去哪儿下单页, 车次直达 12306 预填查询页(自动带入日期/车站)
🔒 本地优先:数据只落在本机 data/ 目录,推送 key 只存在本机 config.json(不入库)
Related MCP server: Airline Reservation MCP Server
界面预览
Web 仪表盘(v0.8 设计系统:Skyscanner 式浅色+城市图鉴+低价柱状图,移动端自适应并带浮动低价提醒横幅,iPhone 可"添加到主屏幕"当 App 用):
v0.29 起新增 /v2 前后端分离版(web/ 下的 Vite+Preact 工程,构建产物由后端同源托管,使用者无需 Node;开发者改界面见使用指南第十一节)。
仪表盘 | 配置编辑 |
KPI 卡片 / 价格热力日历 / 趋势图 / 出行最优判定 | 线路 CRUD / 数据源开关 / 推送设置 |
快速开始
Windows
安装 Python 3.10+(勾选 Add to PATH)
安装依赖:
pip install -r requirements.txt双击
start_windows.bat,浏览器自动打开 http://127.0.0.1:8765在"提醒推送"页填入 Bark Key(iPhone App Store 下载 Bark,复制 Key),点"发送测试推送"验证
想要后台自动监控:以管理员运行
deploy/register_task.ps1(计划任务每 45 分钟静默查询一次,满足条件即推送)想要开机自启(面板+抓取循环):运行
tools/install_autostart.ps1(写 HKCU Run 键,免管理员,默认装 webui+worker 双条目;-Components webui仅面板;-Uninstall卸载;已在运行时自动跳过)。worker 循环持续采集班期板,星期覆盖 7 天长满后起飞时刻缺口自动收敛
macOS / Linux
pip3 install -r requirements.txt
bash start_mac.sh
# 或后台常驻: python3 main.py --loopDocker(推荐用于 NAS / 云主机 / 长期无头部署)
v0.69 起提供容器化部署:镜像内置生产级 WSGI 服务器(waitress),数据与配置全部挂载在宿主机,容器随删随建:
# 首次:准备 config.json(可先 cp config.example.json config.json 改好再起)
docker compose up -d --build
# 查看面板
open http://127.0.0.1:8765
# 升级:git pull 后重建
docker compose up -d --build容器内 Web 面板与 Windows 本机同模型:内置监督线程自动拉起/热换 main.py --loop 采集进程,无需额外配置 worker 容器。data/ 持久化班期库/快照/历史,config.json 改动重启容器即生效。
iPhone 上怎么用?
iPhone 不跑 Python 后端,它的角色是接收推送 + 查看面板:
推送:装 Bark → 复制 Key 填入配置,低于价位秒收通知,通知可直接跳购票页
面板:把 Windows/Mac 上 config.json 的
webui.host改为0.0.0.0(并在防火墙放行 Python),手机 Safari 访问http://电脑局域网IP:8765,分享→添加到主屏幕,即得到一个全功能 App 图标
配置说明
首次运行自动生成 config.json(个人配置,已 gitignore);模板见 config.example.json,所有项均可在 Web UI 修改:
配置 | 说明 | 默认 |
routes[].from_city / to_city | 出发/到达城市(必须中文城市名) | 杭州 / 重庆 |
routes[].trip_type | 行程类型:oneway 单程 / roundtrip 往返(按合计价判定) | oneway |
routes[].intl + from_iata / to_iata | 国际航线开关 + IATA 三字码(intl 时必填,如 HGH/NRT) | false / 空 |
routes[].window_days | 查询窗口天数 | 60 |
routes[].threshold_total | 心理价位(总价口径,含机建燃油) | 500 |
routes[].train_compare.station_pairs | 动车对比车站对,如 杭州东 | 重庆北 |
tax.airport_fee + fuel_surcharge | 机建+燃油(当前合计 120) | 50+70 |
push.bark_key / serverchan_sendkey | 推送通道(留空禁用) | 空 |
schedule.interval_minutes | 轮询间隔(分钟) | 45 |
sources.enabled | 数据源开关(可用/规划中) | 全开 |
sources.amadeus | 国际数据源(env=test/prod + client_id + client_secret,在 Web UI 数据源页填写) | test / 空 |
数据源与合规
源 | 状态 | 说明 |
去哪儿·价格日历 | ✅ 可用 | 免登录免签名的公开接口,一次返回 365 天每日最低价(挂牌价,不含 OTA 券后;与券后实付通常只差小几十,可接受) |
12306·票价查询 | ✅ 可用 | 免登录低频查询,结果缓存 24h;全席位(含卧铺/普速 K/T/Z);学生票按 动车二等75折/硬座5折/硬卧=卧铺-硬座半价 估算(以下单页为准) |
去哪儿·国际特价日历 | ✅ 可用(免 Key) | 国际线路的兜底数据源:返回稀疏但真实的含税促销价(每月约 3-5 个日期),配 Amadeus Key 后自动合并、同日取更低 |
Amadeus·低价日历(国际+补全) | ✅ 可用(需免费 Key) | developers.amadeus.com(注意复数)注册 Self-Service,用 /v1/shopping/flight-dates/cheapest 查含税最低价;国际线路全量使用,国内线路缺价日自动补洞(缓存24h) |
Amadeus·航班时刻表 | ✅ 可用(需免费 Key) | /v1/schedules 按月查询真实起降时刻,匹配航班号自动填充详情卡;24h 缓存 |
机场班期·参考时刻 | ✅ 可用(免 Key) | 杭州机场官网公开班期板(与浏览器同源只读接口),按 航班号+星期 沉淀计划起降时刻库;目标星期未沉淀时跨日借用同号航班班期(v0.19),详情卡打「计划时刻·机场班期/跨日班期·参考」徽标,Amadeus 配置后自动让位实时刻 |
Wikipedia·城市图片 | ⚠️ 视网络而定 | /api/city-photo 经 REST summary 取城市实拍图(缓存24h,失败负缓存);大陆网络下 Wikimedia 域名常不可达,自动回退到内置旅行海报剪影(离线可用) |
临近日参考价(内置) | ✅ 可用(免 Key) | 源日历"查价"空洞日自动镜像最近有价日的价格作展示参考(7 天内紧参考/45 天内远参考,标注距离天数);仅用于补全日历显示,不触发提醒、不参与最优价判定,点击直达当日实查 |
插值估算价(内置) | ✅ 可用(免 Key) | v0.11:空洞日两侧 14 天内均有真实价时按距离线性插值;仅展示用,不触发提醒、不参与统计,详情卡标注参考航班号 |
携程 H5 / 航司官网 | 🚧 规划中 | 备选交叉验证源 |
原则:不破解签名、不绕过验证码、低频查询(分钟级)、只读公开数据、购票永远人工。请将本工具用于个人比价,勿改造为商业爬虫。
项目结构
fare-alert/
├── main.py # 后端核心:查询→判定→推送 流水线
├── webui.py # Web 仪表盘(Flask)
├── core/
│ ├── config.py # 配置加载/合并
│ ├── flights.py # 去哪儿价格日历
│ ├── trains.py # 12306 票价+车站字典
│ ├── alerts.py # 阈值判定/去重/消息组装
│ ├── notify.py # Bark/ServerChan 推送+历史
│ ├── crawl.py # 抓取遥测(爬虫监控数据)
│ └── report.py # 静态 HTML 报告(旧版)
├── webui/ # 前端(原生 JS,无构建步骤)
├── deploy/ # Windows 计划任务注册/卸载
├── tools/ # 接口侦查脚本(开发用)
└── data/ # 快照/状态/日志/提醒历史(运行时生成)开发路线图
v0.1 实现:双源查询、含税总价、阈值推送、托运标注、计划任务
v0.2 可视化:Web 仪表盘、热力日历、趋势图、出行最优判定、可视化配置
v0.3 全席位+联想输入:12306 全席位(卧铺/普速)、城市/车站拼音联想选择、快捷线路模板、多线路编辑器、学生卧铺估算
v0.4 爬虫监控+直达购票: 抓取步骤实时可视化(耗时/条数/缓存/错误)、KPI/对比卡/车次表一键直达购票页、车次时刻+历时展示、航班时刻字段预留
v0.5 高级趋势图: 平滑曲线、渐变面积/描边、悬浮十字线提示、周末底纹、阈值线与低价点高亮
v0.6 浅色重设计+往返+国际: shadcn 风格浅色主题、趋势图浅色化并新增返程趋势、往返合计判定、Amadeus 国际线接入
v0.6.1 缺价补全: 去哪儿日历空洞自动用 Amadeus 含税价回填、来源徽标、24h 缓存、内置城市→IATA 映射
v0.7 国际免Key+航班时刻+UI精修: 去哪儿国际特价日历免 Key 兜底、Amadeus 时刻表增强(真实起降+时长)、时长大圆估算、详情卡起降时间轴、Amadeus 配置向导+测试连接、v0.7 设计系统(渐变强调色/热度日历/时刻卡片)
v0.8 缺价补全+Skyscanner式UI+移动提醒: 临近日参考价补全(7天紧/45天远二级半径+距离标注+≈标记,日历 60/60 无空洞)、v0.8 设计系统(Skyscanner 配色/城市图鉴 Hero/低价柱状图视图/日历柱状一键切换)、移动端浮动低价提醒横幅+震动+系统通知授权按钮、城市旅行海报剪影(6 种 SVG 场景离线渲染,Wikipedia 实拍图网络可达时自动叠加)、Amadeus 官方域名修正(developer→developers)、中转行程按航段匹配真实时刻、无时刻状态"时刻待接入"徽标
v0.9 扁平趋势图+API指引: Google Flights 式扁平趋势(去渐变/辉光/动画,均价点线,最低价标注)、Amadeus 注册直达与推送指引
v0.10 TOP5+目的地情报: 低价日 TOP5 表、目的地天气(Open-Meteo)/汇率情报卡、Amadeus 隐私说明
v0.11 插值补全: 无价日期两侧真实价线性插值(琥珀标识,不参与统计)、趋势图估算空心点
eng 工程化: AGENTS.md 人机守则、G0-G6 验收门禁(tools/acceptance.py)、核心单测、CI
M1 Agent接入: 自研零依赖 MCP Server(stdio)五工具 fare_search/train_search/watch_add/watch_del/snapshot_get、"一句话建监控"意图解析(12组单测+8项协议自测+独立E2E验收)
M2 源健康自愈: 健康分/双阈值降级/诊断报告/UI徽标/MCP透出(单测 7/7)
M3 反向搜索(首版): 预算过滤含税口径/请求硬预算+限速/6h结果缓存/UI页签+直达链接/MCP工具(单测 5/5,多城市并行与节假日联动留 M3.1)
v0.18 零密钥航班时刻: 萧山机场公开班期板→周班期时刻库(核心/sched_board),Amadeus 未配置时兜底填充起降时刻并打徽标,含共享航班号索引/到达板双时刻优先/15项单测;周报自动推送增加无渠道守卫(不再空耗 7 天计时器)。复核修复:时刻库合并改「双时刻整替/残缺行只填不删」防丢时刻、每板每日抓取硬上限落地(≤2次/日)、库文件原子写+坏库先备份、推送渠道非 200 显式标 ERR、周报全失败 6h 退避防 45 分钟风暴、非杭州线路跳过班期抓取
v0.19 跨日班期时刻补全: 班期板无日期参数(官网页面无日期选择,?date= 与路径段探测均无效,按日期回填否决)→改跨日借用:日历价已证明航班号当日执飞,board_lookup_x 先精确星期再跨星期借用同号时刻(优先双时刻条目),「跨日班期·参考」徽标;仅有起飞时新增落地估算 arr_est(大圆+滑行,~ 前缀+虚线样式,不写入 arr_time 防混源);时刻仍纯展示不影响提醒。新增 9 项单测(42+8 全绿),dump_dom 改同步等待版防半写
v0.20 真 PWA + 时刻库透明度: 新增 tools/gen_icons.py(PIL 渐变底+纸飞机,可复现)产出 512/192/180/32 与 maskable 图标,manifest 补 PNG(iOS apple-touch-icon 不支持 SVG 的空白主屏图标修复);sw.js 离线壳(/static 缓存优先、?v= 版本 busted;/api GET 网络优先+缓存兜底);/api/sched-stats + 数据源页七天沉淀进度 widget(回答「为什么这天没时刻」);周报推送无渠道时自动跳转推送页并高亮输入框。新增 2 项单测(44+8 全绿)
M4-M5: 洞察周报/插件生态(见 docs/迭代路线图.md)
v0.21 时刻覆盖仪表 + 自启 + selftest 隔离: snapshot 每线路写入 time_coverage(起飞/落地时刻 精确/借用/估算/缺失 四档统计,剔除邻近日参考价),数据源页新增「本轮时刻覆盖体检」分组条形图,时刻质量从黑盒变仪表;mcp_server 支持 FAREALERT_HOME 环境变量,selftest 改临时目录副本运行(修复并发验收假 FAIL,真实 config.json 零触碰+校验);tools/install_autostart.ps1 一条命令装开机自启(HKCU Run 键,免管理员,8765 已监听自动跳过,-Uninstall 卸载,-Mode task 备选计划任务);周报页无推送渠道时「立即推送」按钮直接禁用并提示,不再点了才报错。新增 5 项单测(49+8 全绿)
v0.22 经停友好时刻借用 + 按字段来源 + 推送修复: board_lookup_x 城市匹配改四层分级(双城>仅出发侧=经停>仅到达侧>拒绝),经停航班(杭州→克拉玛依经停郑州类)不再被整体误杀,跨 dow 借用只认双城/经停两档;FlightDeal 拆出 dep_src/arr_src 独立字段,「起飞精确+落地借用」混合态可表达,前端徽章按段显示;测试推送重复告警去重+test-push 标记;autostart wrapper 读 config webui.port 不再写死 8765。新增 4 项单测(53+8 全绿)
v0.23 趋势图重画 + 全站去装饰渐变: 趋势主线从 AI 感靛紫统一为品牌蓝并加 7% 面积填充,新增顶部图例行(低于价位/高于价位/含税总价/30日均价/插值估算),最低价标签改绿色胶囊,点位色收敛为绿/灰两档,悬停点统一品牌蓝;全站清理 20 处装饰性渐变(品牌名渐变字/按钮/日历格/角标/城市海报底色改纯色),仅保留功能性纹理与遮罩。视觉对齐 Skyscanner 式克制风格。
v0.24 沉淀进度 ETA + 周报推送三步引导: 时刻库沉淀进度 widget 增加「预计全覆盖日期」推算(按当前星期推算剩余 dow 的入库日期,覆盖 N/7 天·预计 M/D 全覆盖,每个 dow 悬停可见预计入库日);周报页推送引导从单条警示升级为按缺口动态生成的分步清单(无渠道/未启用两维检测,/api/weekly-report 新增 channel_ready 字段,一键跳转提醒推送页),修复用户 config 缺 weekly_enabled 键时周报静默不推且无引导的问题;CSS 引入 4 级间距变量(--sp-1~4),容器 1100→1160px、卡片/网格间距统一收口,版面呼吸感更接近开源旅行产品。
v0.24.1 API v1 版本化别名: 全部 /api/* 路由自动镜像为 /api/v1/*(同 endpoint 同方法,旧路径一等公民不受影响),为未来独立前端/原生 App 提供可钉住的稳定契约——前后端物理分离的「留缝」落地;新增 tests/test_api_v1.py 4 项契约测试(路由镜像完备性/v0-v1 响应等价/POST 方法保持/新字段),并修复测试文件直跑时项目根不在 sys.path 的问题。tools/probe_ota_times.py 留档本日时刻源勘探结论(去哪儿日历接口无起降时刻字段,思路关闭)。
v0.25 同航线真实时长先验: 落地时刻估算从「大圆圈几何模型」升级为「数据驱动」——萧山到达板每天 1600+ 行双时刻(前站起飞 preschtime + 落地 jhsj)沉淀出 83 城真实飞行时长中位数,直飞航线的 arr_est 与时长文本改用同航线实测值(重庆 140min/成都 155min/郑州 100min/曼谷 240min,几何模型普遍高估 10-30 分钟);国际前站城市名归一化(「曼谷素万那普机场」→「曼谷」)+ 前缀合并查询取最繁忙机场;经停航线保持几何+中转模型(先验只覆盖单段,不能映射多段)。tools/probe_dest_boards2.py 留档目的地机场勘探负结论(郑州 DNS 失败/重庆官网域名停放/成都 403,大陆目的地机场板数据源关闭),返程方向(落地杭州)时刻可由萧山到达板精确命中,开启往返线路即自然生效。
v0.26 无号日历价参考班次 + Docker 部署修复: Amadeus 日历价无航班号的日期(曼谷 48/50)按城市+星期从萧山板库补「当日参考班次」chips(exact/cross-dow 标记, 只展示不触发提醒); Docker start.sh 补 --loop 修复调度假死, 端口统一 8765; 顶栏新增「提醒未配置」健康徽标直达推送页。全套 43+9 单测全绿。
v0.26.1 参考班次补挂修复: nearby-ref 日期在 enrich 之后才补齐导致挂载点失效, 新增幂等 _attach_alt_times 在 fill 后补挂(只挂去程), 曼谷 48/50 日期获得起飞时刻参考; 同轮修复测试归位/徽标实时刷新/start.sh 优雅关停。全套 43+11 单测全绿。
v0.27 覆盖率合并口径 + 部署可观测: KPI 覆盖大数字改精确+参考合并口径(副文案拆分); /api/health 一站式健康端点(快照新鲜度/板库沉淀/推送就绪, 供外部 uptime 监控轮询); 顶栏更新时间带绿/黄/红新鲜度状态色。
v0.32 落地时刻精确化 + v2 默认入口: 出发板 nextschtime 提取为精确落地时刻(经停存 via 元数据)+ 离线回填 backfill_from_cache + 快照再富化工具 reenrich_times.py, 双时刻条目 3253→6075(重庆真实落地 22/39, 此前全 0); / 根路径 302 至 /v2/, 经典版转 /classic 维护模式; Docker HEALTHCHECK。85 单测全绿。(v0.28–v0.31 设计令牌/web 前端工程/三波 tab 迁移详见 docs/迭代路线图.md)
v0.33 中转/经停透明化 + CI 前端门禁: 联程票首段到达+中转城市、经停票经停站+到达时刻从板库透传至日详情时间线(「中转 武汉 · 08:55 到」), 四线 81 天获得说明, 估算落地不再是无解释的孤数; ci.yml 新增 frontend job(npm ci+build+git diff --exit-code, dist 与提交强制同步)。87 单测全绿。
v0.34 部署工业化: worker 自启 + 心跳可观测: 「时刻显示不全」的机制根因=抓取循环无人拉起、班期 dow 覆盖停在手动运行那两天。autostart_worker.ps1 单实例守卫 + install_autostart.ps1 升级双组件(-Components webui,worker); main.py 每轮写心跳、/api/health 暴露 worker 存活年龄; 无时刻文案按换季/联程段细化原因。92 单测全绿。
v0.35 调度心跳面板化 + 板库 API 勘探归档: v2 数据源页新增「调度心跳」卡——/api/health 的 worker 状态(运行中脉冲/心跳过期/未启动三态 + 最近轮次成败与分钟年龄)从命令行知识变成面板可视化, 未启动时直接给出 autostart_worker.ps1 启动指引; tools/probe_hbh_date.py 归档板库 API 勘探负结论(无 date 参数, keywords 仅航班号, 只服务当天 → dow 覆盖唯一路径=worker 每日积累, v0.34 自启方向实证正确)。92 单测 + ui_check(新增 v0.35 断言) 全绿。
v0.36 UI 质感专项: 排版放大 + 趋势图重做: 「不够高级」的根因之一=字号系统偏小(正文 13px/KPI 数字 22px)。v2 专属排版令牌层(--fs-body2/title2/hero2/kpi2, 正文 14/标题 16/Hero 28/KPI 30) + 趋势图重做(Catmull-Rom 平滑曲线、去掉 60 个噪声点只留信息点、最低价绿色胶囊标签、周末淡色底带、心理价位文案标签、悬浮卡片阴影升级) + tab 导航改分段控制器 + Hero 去 emoji(无照片显示城市首字、纸飞机换 SVG)。92 单测 + ui_check(新增 v0.36 断言) 全绿。
v0.37 agent 自主验收闭环: verify_release MCP 工具: 多 agent 协作的最后一块拼图——子 agent 改完代码可自助跑验收链, 不再依赖主 agent 手工转述。新增 MCP 工具 verify_release(steps=unittest/ui_check/build/health, 默认 unittest+ui_check+health), 返回结构化 {all_ok, steps:[{step,ok,seconds,tail}]}; health 步骤读 config 探活 /api/health 并给 worker 三态语义(alive/stale/none)。mcp_selftest 扩到 10 项(含真实 tools/call verify_release), 新增 5 个全 mock 单测, 全套 97 全绿。
v0.38 部署硬化: worker 每日自愈机制: 「起飞时间显示不全」的机制根因=自启只在登录时触发, 桌面数周不重登则 worker 死后无人拉活, dow 覆盖冻结(实测 2/7)。双层自愈: ① webui 进程内守护线程(每日 07:00 窗口探测 main.py --loop, 缺失则拉起; 纯 Python 跨平台, 不依赖任务计划权限——本机 Register-ScheduledTask 实测拒绝非提权会话); ② install_autostart.ps1 尽力注册每日计划任务(允许的宿主生效, 拒绝则告警不失败)。/api/health 暴露 revive 双层状态(supervisor+task), v2 数据源页心跳卡新增自愈状态行。106 单测 + ui_check(v0.38 断言) 全绿。
v0.41 时刻回写闭环 + catch-up 自愈 + 迁移备份: ① 每日巡检后离线回写快照起降时刻(core/reenrich, 零网络请求, 无变化跳写+滚动备份, 面板当日反映班期库增长), 数据源页巡检行显示回写覆盖; ② 迟到开机 catch-up——错过 07:00 窗口的桌面在 worker 心跳 >6h 且当日未补拉时仍探测+拉起一次(修复周末关机后班期 dow 覆盖冻结 2/7 的根因, 实测重启当日 2/7→3/7); ③ tools/backup.py + restore.py 一键备份/跨设备还原(config+快照+班期库+提醒状态+缓存+周报历史, 白名单精确匹配+realpath 纵深防御防 zip 穿越, 单坏成员不中断整包还原)。133 单测 + ui_check(v0.41 断言) + acceptance 全绿; Zeno 评审 PASS-with-notes, P1(zip-slip 前缀旁路)当轮修复。(v0.39–v0.40 推送可诊断化/巡检定时化详见 docs/迭代路线图.md)
v0.42 公务舱低价监控 + 共享航班时刻兜底: ① cabin_watch 配置块(默认目的地杭州可改, 多出发地白名单, 独立阈值+冷却), core/cabin_monitor.py 环形历史(data/cabin_history.json, 同日同舱最新价覆盖) + 阈值命中走既有推送通道; ② 公务舱报价走 Amadeus flight-offers(travelClass=BUSINESS, 窗口内均匀采样≤8天, 无 key 安全跳过), 去哪儿低价日历实测仅经济舱地板价、列表页需签名不可用; ③ 共享航班号(SC2114/SC2135/G5虚拟号)班期库零收录导致的时刻空白, 现挂同日同航线已知班次作参考(alt_times 语义不变不造假), reenrich 变更检测含 alt_times(修复 coverage 不变时跳写的盲区), 实测 12/12 天补上参考班次; ④ /api/cabin + v2 公务舱监控卡(历史最低/样本数/阈值状态/最近提醒)。单测 133→145。
v0.43 公务舱精确时刻 + 监控面板编辑: ① fetch_cabin_offers 解析 flight-offers itineraries.segments 的 departure.at/arrival.at, 公务舱行带上当日精确起降时刻+航班号(多段联程拼 MU5458/CZ3383 并标中转机场), time_src=amadeus 为最强源; ② 修复 v0.42 真 bug——经济舱日历无缺口时 "if not gaps: return" 早退导致公务舱采集被整体跳过, 采集块前移到补全早退之前, 且 gap 覆盖集只统计经济舱行(公务舱行不再干扰经济舱补全判定); ③ _enrich_flight_times 加 offer-exact 守卫(多段/单段的 schedules 匹配不再覆盖 flight-offers 精确时刻); ④ /api/config 校验 cabin_watch(enabled/cabins/default_to_city/threshold_total/cooldown_hours/watch_from_cities, 出发城市列表去空去重), v2 CabinCard 面板可视化编辑(启用开关/目的地/阈值/冷却/出发城市列表, 保存走既有 config POST 通道)。单测 145→153。
v0.44 经济舱 offer 精确兜底 + Amadeus 用量透明: ① 时刻覆盖推广到经济舱——cheapest-dates 也补不到的缺口日期, 用 flight-offers(ECONOMY, max=3) 逐日兜底, 一并拿到精确起降时刻+航班号+中转信息, 与公务舱共用 _best_offer/_cabin_offer_times 解析; ② 兜底结果按日期缓存 24h(含负缓存: API 无报价的日期当天不再重试), 45 分钟轮询循环下配额安全; ③ Amadeus 调用按日计数(14 天滚动), /api/amadeus-usage + 数据源页"今日调用 N 次"让配额消耗可见; ④ 公务舱监控表加近 10 样本 sparkline 走势列(对齐主趋势图 accent 风格)。单测 153→158。
v0.45 监控城市点选 + 覆盖率趋势: ① 公务舱监控编辑卡输入升级——目的地与监控出发城市不再手打顿号分隔文本, 目的地复用 AcField 城市库联想下拉, 出发城市改为 chips 点选输入(城市库过滤+回车自由添加+Backspace 删末尾+点 × 移除, 留空=全部出发地), 与路线页同一份城市数据源, 杜绝手输偏差; ② 时刻覆盖率从瞬时值升级为历史趋势——每日 KPI 归档新增 cov{dx,db,dm} 起飞时刻计数, core.history.coverage_trend 纯函数聚合成日线序列, /api/coverage-trend + 数据源页覆盖体检卡顶部趋势小图(扁平填充无渐变, 首尾占比/跨日参考/缺失占比标注, 单点日显示"基线已记录"), 让"起飞时刻全覆盖"目标的推进进度可见; ③ 存量 history.json 无 cov 的旧记录安全跳过不画 0%。单测 158→164。
v0.46 班期板按行内真实日期入档: 机场板接口单次返回跨两天(今天+明天)的航班行, 旧逻辑把整板按抓取日星期入档——次日航班错进错误星期档, 既产生错误“精确命中”又浪费免费次日数据。① 每行改从自身 jhsj 日期(leave=起飞日/arrive=到达日)解析真实 weekday 入档, 解析失败回退抓取日, 一次抓取可沉淀两个星期档; ② 存量库格式迁移(fmt=2 标记): 旧格式错档无法就地修复, 发现即清空并用 7 天板缓存离线重放重建(零网络), 幂等; ③ 本地实测: 星期覆盖 3/7→5/7(工作日档全齐), 剩余两档随本周轮询自然补齐, board_lookup 精确命中率上升 → 起飞时刻精确占比上涨的直接根因修复。单测 164→166。
v0.47 公务舱多目的地监控 + 采集节奏可视: ① cabin_watch 目的地从单值升级为 to_cities 列表(默认杭州, 随时增删改), 旧配置 default_to_city 自动派生列表并保持同步, route_qualifies 按列表匹配任意目的地; ② /api/cabin 新增 refresh(每轮间隔+上轮/下轮时间, 来自 worker 心跳)与 qualifying_routes(当前正在采集的路线), 卡片上直接可见“每 45 分钟一轮 · 上轮 xx:xx · 下轮 ≈xx:xx”与采集路线标签, 无匹配路线时提示去路线页添加; ③ 编辑面板目的地改 CityPicker 多选标签。单测 166→172。
v0.48 起飞时刻全覆盖(参考班次提升): 240 张有价票中 116 张不显示起飞时间, 诊断发现其中 103 张(nearby-ref/国际日历行)的 alt_times 里已存真实班次时刻却从未提升到 dep_time, 另 13 张(斜杠中转串如 SC2114/SC2135 + 插值行)连参考班次都没挂。① core/sched_board.promote_alt_time 纯函数: 从 city_dep_times 列表选最优参考班次(同星期精确优先, 再按最早起飞), 提升 dep_src="alt-ref"; ② _attach_alt_times 对"有 alt 无 dep_time"的行直接提升(reenrich 离线回放同样生效), _enrich 斜杠段两段都查不到时回退挂同航线当日参考班次并提升; ③ 双 UI 新增“参考班次”琥珀徽标(标注非本航班号, 仅供参考), KPI 覆盖口径把 alt-ref 归入"参考"桶不冒充精确, time_coverage 归入 borrow 桶。实测离线回放: 缺起飞 116→0, 真实票覆盖 90/98→98/98(100%), 全程零新增网络请求。单测 172→174。
v0.49 返程起飞时刻对称覆盖: 去程 alt-ref 提升只服务 outbound, 往返模式切回程(return_deals)依旧 --:--。① sched_board.city_dep_times 泛化为 _ref_deps 共享内核, 新增 city_return_dep_times 直接读到达板 preschtime(=外地→杭州起飞时刻), 零新增请求; ② _attach_alt_times 增加 from_city 返程模式, _enrich_flight_times 增加 direction="ret" 使城市敏感查询(板库/时刻/斜杠回退/无号行)全部改走返程口径(leg_from/leg_to/leg_fi/leg_ti 反转); ③ reenrich 离线回放覆盖 raw_ret 块, TIME_FIELDS 写回+变更检测同步生效。快照当前 4 路线全 oneway(return_deals 为空)属前瞻性覆盖——切往返后返程同样具备时刻提升能力。单测 174→176, acceptance 13/13。
v0.50 公务舱监控自动反采(镜像腿): "出发地历史最低公务舱价+目的地默认杭州可改+定时刷新"在 v0.42~v0.47 已成型, 但存量路线全是"杭州→外地"方向, to_cities=[杭州] 时一条都不匹配——此前必须手动添加反向路线才会真正采集。① cabin_leg 纯函数: 路线 to_city 被监控→直采本腿, from_city 被监控→自动派生镜像腿(外地→杭州), watch_from_cities 按监控腿自身出发城市过滤; ② 采集块 direction=out 独占(顺带修复往返模式下 ret 调用会采错方向的历史问题), 镜像腿换 IATA 对调后 fetch_cabin_offers(购票链接自动指向正确方向), 镜像行经 cabin_out 独立通道回传——绝不混入经济舱 deals(不污染 cheapest/告警口径); ③ 历史记录键 "-rev" 与经济舱路线 id 不冲突, 提醒文案用监控腿城市("公务舱低价 重庆到杭州"); ④ /api/cabin qualifying_routes 改报真实监控腿+mirror 标记, CabinCard 加"镜像"徽标; ⑤ 实测开启后存量 4 路线自动派生 重庆/郑州/曼谷/成都→杭州 四条镜像采集腿。单测 176→179, acceptance 13/13。
v0.51 部署对齐: worker 代码版本热替换: 修复"页面反复看不到起飞时间"的真根因——旧 worker 进程(PID 15300, 早于 v0.48 启动)一直活着, autostart 只在无 loop 时拉起、revive 只救死 loop, 升级永远传不到活进程, 每轮用旧逻辑覆盖快照(293 有价票 110 缺起飞)。① core/version.py 单一版本源, worker 心跳盖章 code_ver, /api/health 暴露 code_synced; ② revive 守护每轮先比心跳版本(文件比对零成本), 落后即热替换 kill+relaunch(30 分钟冷却+心跳时间戳守卫防误杀长首轮), supervisor 快照记录 last_stale_restart; ③ 数据源页心跳卡"Worker 代码落后"琥珀警告+已热替换 vX→vY 提示; ④ tools/restart_worker.ps1/restart_all.ps1 一键强杀重拉(部署后必跑); ⑤ 实测 restart_all 后 code_synced=true, 缺起飞 110→0。单测 179→188, acceptance 13/13。
v0.52 公务舱历史新低提醒: "出发地历史最低公务舱价"此前只对照固定阈值——创了历史新低但仍在阈值上方时不会提醒。① record_low 记账时打 record 标(首样本不算, 防引导期全量误报), 携带前低 record_prev; ② record_alert_candidate 纯函数: 新低必须严格低于"已提醒过的最低价"才再提醒(alerted_low 持久化于 state.json, 同价重观测永不重复提醒); ③ main 提醒优先级: 历史新低 > 阈值命中, 文案"公务舱历史新低 重庆到杭州 · 10-02 ¥1650 (前低 ¥1800)", 若同时低于阈值追加标注; ④ cabin_watch.alert_record_low 开关(默认开, /api/config 校验+回显), CabinCard 编辑面板复选框, 表格历史最低列"新低"徽标(最近一轮创新低时), 最近提醒行显示提醒类型; ⑤ 实测 /api/cabin 回显 alert_record_low=true, 4 条镜像腿(重庆/郑州/曼谷/成都→杭州)在采。单测 188→192, acceptance 13/13。
v0.53 手机局域网可达闭环: iPhone 上看板+提醒此前卡在两处隐性知识——webui 默认绑 127.0.0.1(改 config+防火墙+重启三步手工), 手机上也不知道该输哪个地址。① /api/lan-info: 返回绑定 host/port、本机局域网 IP(UDP connect 探路由, 零发包)、lan_open 与拼好的手机 URL; ② PushView 手机访问卡: chip 展示"局域网已开放/仅本机可访问"+可直接抄的 URL, 未开放时提示一键脚本; ③ tools/enable_lan.ps1 一键开放(改绑定 0.0.0.0+防火墙规则+重启 webui, 打印手机地址), -Revert 一键收回; ④ PWA 已有(添加到主屏幕即 App 图标), Bark 推送不依赖页面在线。单测 192→195(lan-info 契约/开放拼 URL/仅本机隐藏 URL), ui_check v0.53×2, acceptance 13/13。
v0.54 班期库浏览器: 时刻库(3711 班号)是 alt-ref 参考起飞时刻的引擎, 但此前完全不可查——"为什么这天是参考班次""周三到底飞什么"无从回答。① /api/board: 按城市对(子串匹配)/航班号(大小写不敏感)检索班期库, 代表条目取跨 dow 众数起飞时刻(个别一天的时刻漂移不污染常态), 返回航司/机型/起降/班期 dow 列表, 按起飞时间排序, limit≤200; ② SourcesView 时刻库沉淀卡内嵌 BoardExplorer: 出发/到达城市输入+班期查询, 结果表格 7 个 dow 迷你徽标直观展示班期(周一~周日哪些飞), 命中数与空态引导; ③ .dow-mini 复用设计令牌(零硬编码色过 STRUCT 门禁)。单测 195→200(契约/城市过滤/排序/航班号检索/dow 排序/limit), ui_check v0.54×2, acceptance 13/13。
v0.55 较昨日降价追踪+骤降提醒: 阈值提醒的盲区——600→400 的大幅阴跌只要没破线就一直静默。① core/history.day_drops: 对比最近两个归档日的每路线窗口最低价, sharp 需同时过双闸(相对≥drop_pct% 且 绝对≥drop_abs 元), 旧测试小票面误报/低基数百分比误报都拦住; ② main.run_once 归档后即比价, 骤降即推送"骤降提醒 杭州到重庆: 窗口最低 ¥480 较昨日 -20.0% (¥-120)", state["_drop"] 按 route+date 去重防重跑重复提醒; ③ /api/drops 端点+配置 alert.drop_pct(默认15)/drop_abs(默认50, /api/config 校验); ④ 仪表盘最低价 KPI 加"较昨日 ▼¥120 (-20%)"涨跌徽标(降=绿/涨=琥珀), 数据一眼可感。单测 200→205(双闸/涨不算/新路线跳过/API 契约), ui_check v0.55×2, acceptance 13/13。
v0.56 骤降提醒配置面板: v0.55 的双闸参数此前只能手改 config.json, 推送页零入口。① PushView 新增"骤降提醒"卡: 降幅百分比/降幅金额两个数字输入(缺省回显 15%/¥50), alert 节点合并保存不覆盖 cooldown/top_n 等既有键; ② 实时预演——下方逐路线列出环比(前值→现值/Δ/百分比/升降配色), isSharp 用未保存的输入值客户端重算双闸, 每行 chip 即时显示"会提醒/未达双闸", 调参即刻看到效果而非等下轮; ③ 空态引导"归档两个查询日后显示逐日环比"。单测 205→209(默认填充/合法值直通/越界钳制/字符串数字容错), ui_check v0.56×1, acceptance 13/13。
v0.57 周报「本周值得关注」: 周报页此前只有逐路线环比卡, 缺跨路线速览层。① core/weekly.week_highlights 纯函数——从同一归档重算本周最大降幅(周窗口最低价 vs 上周)、双闸骤降(扫本周相邻归档日, 与 /api/drops 同口径 15%/¥50)、当前破线路线(最新快照 days_below), 输出结构化板块+可断言文本, 平稳周返回"本周价格平稳"; ② build_weekly 返回体新增 highlights, /api/weekly-report 自动透传(推送文案保守不动, 只做 API+UI); ③ WeeklyView 在总览卡与路线卡之间渲染「⭐ 本周值得关注」: 最大降幅行(前低→本周低/Δ/百分比)、破线行(窗口内 N 天≤阈值·最低价)、骤降行(日期+前值→现值)。单测 209→213(降幅/骤降/破线三态+空历史安全), ui_check v0.57×1, acceptance 13/13。
v0.58 周报推送正文接入速览 + 推送预览: v0.57 的「本周值得关注」只在网页端可见, 手机收到的周报正文仍是逐路线流水账。① core/weekly.push_text 纯函数——正文头部(📊 行下)插入 highlights 速览行, 平稳周也带安抚性结论, 无 highlights 原样透传, build_weekly 返回体新增 push_text 字段; ② worker 定时周报(main.py)与手动推送(webui /api/weekly-push)两个入口统一改走 push_text, 线上格式一致; ③ WeeklyView 新增「预览推送正文」按钮——展开即见手机实际收到的完整正文(头行+速览行+逐路线), 所见即所得再点推送。单测 213→218(头部插入/平稳周/无 highlights 透传/无头部前置/build_weekly 暴露), ui_check v0.58×1, acceptance 13/13。
v0.59 公务舱监控运维闭环: 监控已建成(v0.42 采集环形库/v0.47 多目的地/v0.50 镜像腿/v0.52 历史新低提醒), 但运维链路有三个断点——手动刷新接口 /api/run 无人调用、刷新间隔只展示不可改、Amadeus 密钥未配置时采集空转无提示。① CabinCard 新增「立即刷新」按钮, 打通 /api/run(trigger=manual)→刷新完成回显, 不再等 45 分钟下一轮; ② 编辑表单开放「刷新间隔(分钟)」字段, 随监控配置一并保存(写入 schedule.interval_minutes, 后端钳制 ≥5 分钟); ③ /api/cabin 新增 amadeus_ready 就绪标志, 卡片在启用+未配置密钥时显示琥珀色「公务舱数据源未配置」徽标, 空转不再静默。单测 218 全绿(既有 shape 测试增补 amadeus_ready 契约断言), ui_check v0.59×1。
v0.60 提醒历史分类治理: alerts.json 200 条记录全是 title='t' 手动测试噪音, 真实提醒(阈值/公务舱/骤降/周报/巡检)被淹没且无法区分。① core/notify.classify_alert 纯函数——按标题模式分类 test/weekly/cabin-record/cabin/drop/patrol/threshold/other, ≤2 字符标题直接判 test(无任何生产调用点会产出这么短的标题, 't'/'b' 垃圾一并归入); ② push_all 新增 kind 参数, 全部 8 个调用点(main.py×6/webui.py×2)显式传类, _record_alert 存档 kind(缺省回退 classify); ③ /api/alerts GET 给无 kind 的存量行回填分类+返回全量 counts; 新增 DELETE /api/alerts 只清 test 类行(其他类型原样保留); ④ PushView 提醒历史卡重做——7 个过滤 chips(全部/低价破线/公务舱[含新低]/骤降/周报/巡检/测试)带实时计数, 每行类型徽标, 「清理测试」按钮一键去噪(confirm 确认)。单测 218→222(classify 模式/存档 kind 双路径/API 回填+counts/DELETE 只清测试), ui_check v0.60×1。
v0.61 多路线总览看板: 路线一多, 跨线对比只能逐个点 route-tab 人肉翻。① 新组件 web/src/components/OverviewCard.jsx——聚合 snapshot 全部路线, 按 cheapest_total/threshold_total 比值升序(最接近/已破线的排最前, 无报价沉底), 行内含 城市对(单程→/往返⇄)/窗口最低价+日期/徽标(已破线 N 天[绿]·差 ¥X(+Y%)[灰]·暂无报价)/环比箭头(对接 /api/drops, ▼降▲升)/源异常 chip(flight_source_status 以 error 开头时提示); ② app.jsx 在 route-tabs 上方挂载, 点击行 setRouteId+清空 selDate 直达该路线仪表盘; rows<2 自动隐藏, 单路线零打扰; ③ app.css 新增 .ov-list/.ov-row(:hover/:current)/.ov-arrow 全走设计令牌零硬编码。ui_check v0.61×1(源码/构建产物/DOM 三层断言)。
v0.62 公务舱历史低价榜: 采集环形库早就在存每轮观察值, 但面板只报一个光秃秃的最低数——什么时候的票? 现在离记录还差多少? 都要人肉翻。① core/cabin_monitor.history_board 纯函数——逐监控腿聚合: 历史最低+所属乘机日期(low_date)/最新观察(latest, 按 ts 排序同日重写语义)/距新低差值(gap)/样本数, 无有效价的腿跳过, 按最低价升序(最优出发地领跑); ② /api/cabin 返回体新增 board 字段, 前端不再自行推导; ③ CabinCard 表格升级为「🏆 历史低价榜」——历史最低列带日期副标(如 10-05)+最近一轮新低徽标, 新增「最新」列(现价+距新低 +¥X), 走势 spark/样本/阈值状态保留, 旧后端快照走本地回退推导; ④ .cw-date 副标样式全令牌。单测 222→225(board 三态: 最低带日期+最新+gap 排序跳空腿/空历史安全), ui_check v0.62×1。
v0.63 总览看板时刻+走势: 起降时刻此前已覆盖明细卡/日历/TOP5/结论卡, 总览看板是最后一个只报价不报时刻的界面。① OverviewCard 每行新增 ov-times 副标——最优价航班的 起飞-落地 时刻(如 19:45-22:25), title 悬浮完整起降信息, 数据缺失时退化为仅起飞或整行隐藏; ② 新增 OvSpark 迷你走势线——60 天窗口全部有价日按日期序连成折线, 最低价点高亮圆点, 复用 cabin 卡 spark-line/spark-min 类两处视觉对齐, <2 个价点自动隐藏; ③ .ov-spark/.ov-times 样式全令牌(tabular-nums 对齐时刻数字)。ui_check v0.63×1(源码/dist/DOM 三层断言 ov-times)。
v0.64 全局最优推荐: 总览看板按行对比仍要用户自己扫一遍挑出到底该买哪班。① OverviewCard 顶部新增「🏆 全局最优」hero 行——rows 本就按 cheapest/threshold 比值升序, 队首即跨全部监控路线的性价比最高班, 行内含 城市对/最低价+日期/起降时刻(复用 ov-times)/距出发天数(客户端本地日历差)/破线或差距徽标; ② 点击 hero 直达该路线并选中该日期(onPick 扩展为 (id, date), app.jsx setSelDate(date) 直接打开当日详情), 普通行行为不变; ③ .ov-best/.ov-best-tag 绿系令牌(green-d/green-bg/card2)与破线语义同色。ui_check v0.64×1(源码/dist/DOM 三层断言 全局最优)。
v0.65 周报推送全局最优行: v0.64 的「全局最优」hero 只在网页总览可见, 手机端收到的周报正文仍是逐路线流水账, 唯一答案没有到达最终消费端。① core/weekly.global_best(snapshot) 纯函数——跨路线取 窗口最低价/心理价位 比值最小一班(最接近或已破线者优先, 同比值再比低价, 坏阈值/非数值价安全跳过), 返回城市对/日期/总价/起降时刻/阈值与差额; global_best_line(gb) 单行文案「🏆 全局最优 杭州→重庆 09-20 ¥380 (19:45-22:25) 比心理价低¥120」(超线时改报距心理价差额); ② attach_global_best(report, snapshot)——推送正文 📊 头行之后插入该行(已存在不重复插), 同时写 report['global_best'] 供 API 透出; ③ worker 定时周报(main.py)与 /api/weekly-report、/api/weekly-push 手动推送两个入口统一附加, /api/snapshot 顺带透出 global_best——服务端一处计算, 网页/手机/接口三端一致, 与 hero 行同规则(比值升序队首), 永不互相矛盾。单测 225→230(比值择优含时刻/坏数据返回 None/单行格式破线+超线/插入表头后且不重复/无 best 或无头行安全), ui_check v0.65×1(纯后端断言: 函数+双入口接线), 版本 0.65 四处盖章。
v0.66 公务舱出发城市独立巡检: 用户追加「出发地历史最低公务舱价提醒+目的地默认杭州可改+定时刷新」——v0.42~v0.62 已建齐环形库/多目的地/镜像腿/历史新低提醒/历史低价榜, 但出发城市仍只能从存量路线镜像推导, 想监控一条没有路线的出发地必须手动添加反向经济舱路线; 且公务舱数据只在整轮路线扫描时顺带刷新。① core/cabin_monitor.patrol_legs(cw, routes) 纯函数——watch_from_cities×to_cities 全叉积开独立监控腿, 自动剔除已由路线直采/镜像覆盖的城市对与同城对, 结果排序稳定; ② main.py 采集+告警内核抽取为 _cabin_absorb(路线腿与巡检腿共用同一历史键/冷却/推送语义), 新增 cabin_patrol_once——无 Amadeus 密钥/无巡检腿安全跳过并记录原因, 巡检腿历史键 patrol--; ③ 定时双轨: run_once 每轮顺带巡检(手动刷新也覆盖), --loop 按 cabin_watch.refresh_minutes(默认30, 钳5-720)在两轮扫描之间独立补跑(睡眠切片≤60s, 无额外线程, state 写入保持单线程); ④ /api/cabin 新增 patrol 块(节奏/腿清单/上轮时间与状态), CabinCard 显示「独立巡检 每 N 分钟」+巡检腿 chips(区别于镜像徽标), 编辑表单开放独立巡检间隔。单测 230→237(叉积去重排序/路线覆盖剔除/禁用与空输入/同城对/refresh 默认与钳制/API patrol 契约), ui_check v0.66×1(核心函数+main 接线+webui 键+源码/dist 独立巡检断言), 版本 0.66 四处盖章。
v0.67 灰色日期精点补全: 用户判断「查不到飞行数据源的日期并非没票, 直接按日期精点查询能查到」——侦察实锤两个根因: ① offer 精点补全被 gap_dates[:6] 截断, 洞>6 时排位靠后的日期永远轮不到点查(饿死); ② qunar-intl 促销日历常只回 1-2 条尾部真实价, 头部日期距最近锚点>45 天超出参考价半径, 整段头空白。修复: ① _cached_fill_offers 改为轮转预算制——每轮只点查缓存缺失/过期的洞(≤6 个/轮), 负缓存即轮转游标, 后续洞自动轮入, 全窗口洞最终都会被逐日 flight-offers 精点(需 Amadeus 密钥), 新增 stats 出参(holes/probed/deferred); ② _fill_reference_deals 半径 45→75, 单一尾部锚点即可参考覆盖整个 60 天窗口(仍仅展示、不触发提醒/统计); ③ 数据源页 offer fill 步骤显示「缺N天·本轮点查M·待轮转K」。单测新增轮转跨批覆盖+参考价头部可达(30 文件全绿), ui_check v0.67 断言, 版本 0.67 四处盖章。
v0.68 参考班次落地时间补全: 用户痛点「航班只显示起飞时间、落地一直 --:--」——侦察实锤 dep_time 已 100% 覆盖, 真缺口是 arr_time(国际线 60/60 全缺, 国内 24~47/60 缺); 班期库 flight_sched_db.json(3711 班)的 dow 条目本就同时含 dep+arr, 但 _ref_deps 只透传 dep 把 arr 丢了。修复: ① sched_board._ref_deps 条目新增 arr 透传; ② promote_alt_time 校验并携带 arr(垃圾值置空); ③ main._attach_alt_times 拆 need_dep/need_arr——缺落地的行从提升的参考班次直接补 arr_time(arr_src=alt-ref, 不覆盖已有值), _flight_dict 序列化 alt_times 携带 arr; ④ DayDetail 参考班次 chip 显示「航班号 起飞→落地」。全程零请求零密钥, Amadeus 密钥接入后仍会升级为精查时刻。单测新增 arr 搭车+落地参考回填(15+55 全绿), ui_check v0.68 断言, 版本 0.68 四处盖章。
v0.69 当日班期表 + 生产级部署: 用户反馈「还是看不到航班班次的具体起飞时间」+「部署太轻量」。数据侧复核: 快照 240 行 miss_dep=0/miss_arr=0, DOM 零占位符——痛点实质是「只看得到最低价那一班」而非数据缺失。① 新增 /api/day-schedule?from&to&date 零密钥接口: 按城市对+星期列出班期库全部班次(no/dep→arr/exact 徽标, 出发杭州走离港板, 其余城市走到达板反查); ② DayDetail 详情卡内嵌「当日班期表 · N班」chips 条, 无报价日期也展示, 一次点击看全该日每个班次的起飞→落地; ③ webui 主进程从 Flask dev server 升级为 waitress 生产级 WSGI(8 线程, ImportError 优雅回退保持零依赖可启动); ④ Dockerfile+docker-compose.yml: python:3.13-slim, 数据/配置宿主机挂载, HEALTHCHECK 走 /api/health, 容器内监督线程同 Windows 模型自动拉起 worker。单测新增 day-schedule 契约 5 例(缺参 400/坏日期 400/去程含 arr/返程走到达板/未知城市对空而 ok), ui_check v0.69 断言, 版本 0.69 四处盖章。
v0.70 班期直达 + 省% + CI: ① 当日班期表每个班次 chip 升级为深链——dayListUrl 复用任意已有 deal url 模板替换 goDate(国际城市拼写以真实 deal 为准), 无 deal 时按城市名构造, 点击任一班次直达去哪儿当日航班列表; ② 全局最优卡破线 chip 从「已破线 ¥X」升级为「已破线 ¥X · 省 Y%」; ③ 新增 .github/workflows/ci.yml: ubuntu+windows 双平台跑全量单测与 compileall, web-build 任务 npm ci+build 验证前端可构建, docker-build 任务构建镜像验证 Dockerfile; README 顶部挂 CI 徽章并修正 CI 范围文案(单测+构建级, 完整 acceptance 仍本地)。
v0.71 班期航司机型 + ghcr 镜像发布: ① sched_board._ref_deps 透传 airline/craft(板库 v0.26 起每行就存 chinese_hs/jxzs, 组装参考列表时被丢弃, 同 v0.68 arr 修法), /api/day-schedule rows 与 alt_times 序列化同步带出; ② DayDetail 班期表/参考班次 chip 追加机型弱化标注 .ft-alt-craft, 悬浮 title 显示「航司 机型」; ③ CI 新增 docker-publish 任务: push master 自动构建并发布 ghcr.io/ashleymao523/fare-alert:latest 与 :sha 双标签——任意设备只需 Docker + docker-compose.yml 即 pull 迁移, 无需本地构建工具链。新增单测 test_airline_craft_ride_along(透传 + 旧库无字段回退空串), ui_check v0.71 断言, 版本 0.71 四处盖章。
v0.72 当日班期 24 小时时间线: 回应早期反馈「没有 24 小时计时的时间线」——DayDetail 班期区升级双层: 上层 .tl-rail 时间线(0-24h 横轴, 3h 细刻度/6h 主刻度, 每班起飞时刻百分比落位打点, 当日班期实心/跨日参考空心, 上下双车道避让, hover 放大), 打点即深链直达去哪儿当日列表, 悬浮显示班次/起降/航司/机型; 下层保留完整 chips 列表, 分布与明细互补。纯前端复用 /api/day-schedule, 零新增请求; CSS 全设计令牌零硬编码色值。ui_check v0.72 断言, 版本 0.72 四处盖章。
v0.73 当日班期全量 + 经停标注: 数据侦察实锤——杭州-成都单航线去重后 25-27 班/天, 而 /api/day-schedule 沿用 limit=12 只出一半班次; 板库 3716 条 dow 记录中 1008 条带经停信息(via/via_arr), 管道源头同样丢弃。① sched_board._ref_deps 透传 via(经停)字段, limit=None 时返回全量去重列表(默认 limit=4 行为不变); ② /api/day-schedule 改 limit=None 全量取, 响应新增 total/has_more(>30), rows 截前 30 条防超大响应; ③ main._flight_dict 与 core/reenrich 的 alt_times 序列化同步透传 via(三处同步, 幂等检测不误报); ④ DayDetail 班期标签改用 sched.total 计数, 时间线打点/班期 chip title 追加「经停X」, 无落地但带 via 的 chip 正文弱化标注经停, has_more 时追加「+另有N班」直达深链。单测新增 via 透传+全量 limit 与 day-schedule total/via 契约, ui_check v0.73 断言, 版本 0.73 四处盖章。
v0.74 班次历时 + tag 发布链路: ① core/sched_board.flight_duration 纯函数——真实起降时刻直接换算「2h45m」历时(落地早于起飞按跨零点 +1 天), 缺任一时刻/经停行(无最终落地)返回空串绝不编造; /api/day-schedule rows 新增 dur 字段; ② DayDetail 班期 chip 追加历时弱化标注, 时间线打点/芯片 title 加「历时X」, 与动车侧历时列对齐; ③ CI 加 tag 触发: 推 v* tag 自动发 GitHub Release(generate_release_notes) + docker 镜像追加 :vX.Y 版本标签(latest/sha 保留); ④ 新增 tools/upgrade.ps1 一键升级(git pull → npm build → restart_all)。复探归档: 机场板 API 的 GET/POST 日期参数与路径段全被忽略(v0.35 负结论互证), dow 覆盖唯一路径仍是每日运行自然沉淀。单测新增历时矩阵与 day-schedule dur 契约, ui_check v0.74 断言, 版本 0.74 四处盖章。
v0.75 起降区间时间线 + 精确历时回填 + 每日自动备份: ① 时间线从「起飞单点」升级为「起飞→落地」起降区间条(.tl-span): 有落地时刻的班次渲染区间条(红眼航班画到 24:00 且 title 标「次日到达」, 最短 0.6% 保证可点), 无落地(经停行/缺数据)保留原打点; ② main._attach_alt_times 新增精确历时回填——起降时刻都真实且 dur 为空或带「(估)」时, 用 sched_board.flight_duration_hm 的精确值替换估算(估算仅落地缺时按大圆生成); ③ 新增 core/auto_backup: 每日首轮调度前自动 zip 备份 config+data(标记 data/auto_backup.json 防重, 保留最近 7 份, 异常只记状态不抛出), run_once 接线, /api/health 暴露 backup{count,last,last_ts}; 长期部署设备升级/误删不再丢班期库。单测新增 test_auto_backup(created/skip/保留7份)与 flight_duration_hm 元组断言, ui_check v0.75 断言, 版本 0.75 四处盖章。
v0.76 PWA 补全 + 部署自检 doctor + 精点补查通道: ① v2 界面补全 PWA(manifest.webmanifest + apple-touch-icon, iPhone「添加到主屏」即得图标+全屏运行); ② 新增 tools/doctor.py 部署自检——本机模式体检 webui/快照/worker/代码同步/班期库/备份/推送/间隔红线/dist 构建产物/PWA 共 10 项, --url 模式支持局域网远程设备(仅 API 检查), PASS/WARN/FAIL 三级, exit code 可接 uptime 包装; ③ 灰色「查价日」问题实证收案: qunar 低价日历这些日期服务端即无缓存价(快照与线上一致, 非无票非 bug), 按日精点 API(touchInner/touchInter) 被 Bella 签名+浏览器指纹风控挡死(直调/无头/内嵌真实浏览器三途径全 1999, 证据存档 tools/point_probe.py); ④ 双轨补全方案落地——track A: Amadeus 按日 offers(代码就绪, 密钥未配), track B: 新增 core/point_fill 精点缓存模块(真实浏览器抓到价 POST /api/point-fill, TTL 48h, 每轮爬取重放覆盖 interp/nearby-ref 参考价行, 真实行永不覆盖), GET /api/point-gaps 列出待补日期, POST 后快照热补立即生效。单测新增 test_point_fill(7 用例: 落盘/TTL/真实行保护/热补/缺口窗口)与 test_doctor(24 断言三级矩阵), ui_check v0.76 断言, 版本 0.76 四处盖章。
v0.77 全功能容器 + 借班透明化 + 部署指南: ① Docker 镜像升级为全功能单容器——新入口 run_all.py 同容器监督 webui + worker 循环(子进程崩溃 3s 自动重生, 日志合并进 docker logs), FA_ROLE=all|webui|worker 可选角色; 旧镜像只跑 webui 无爬虫, 容器部署下班期库永不沉淀、时刻永远靠借——这正是「页面总是没有起飞时间」的部署侧根因; ② 跨 dow 借用时刻透明化——board_lookup_x 借用分支返回拷贝并标记 borrow_dow(源星期几), FlightDeal/序列化/前端全链路透传, 徽标从模糊的「跨日参考」细化为「借周三参考」, title 说明沉淀满 7 天后自动升级「班期精查」; ③ doctor 新增 check_deploy 部署形态检测(容器 FA_ROLE 不含 worker 时 WARN 提示班期库不会沉淀); ④ 新增 docs/部署指南.md——Windows 本机常驻/Docker 全功能(NAS/旧机/树莓派)/局域网+PWA+iPhone Bark 三形态、数据迁移、班期沉淀机制(每天存 2 个 dow, 常驻 7 天=7/7 全覆盖)一页讲清。单测新增 test_run_all(9 断言角色矩阵)与 test_sched_board borrow_dow 2 用例(借用标记+库不被污染+exact 无标记), test_doctor 补 deploy 断言, ui_check v0.77 断言, 版本 0.77 四处盖章。
v0.78 精点补查 UI + 时刻沉淀透明卡: ① 运维页新增「精点补查 · 缺价日期回填」卡——GET /api/point-gaps 按路线列出聚合日历未出价的日期(已回填绿 chip), 点击 chip 展开回填表单并附去哪儿单日精查直达链接(与日详情同款 deep link), 填「最终付款价(含税)+航班号+起降时刻(选填)」POST /api/point-fill 后快照热更新, 卡内明示口径(自动扣机建+燃油/缓存 48h/真实源优先); ② 新增「时刻沉淀 · 按路线透明度」卡——各路线起飞时刻精确/借用双色占比条(snapshot time_coverage), 七星期班期覆盖格 + 缺口自动补齐预测(板库每轮沉淀当日+次日, 周三/周日何时长满直接给日期)。验证: ui_check v0.78 断言(精点补查/nextRunForDow/两 api helper 源码+dist), 版本 0.78 四处盖章。
v0.79 精点回填书签 + 开机自启体检: ① 运维页「精点补查」卡新增书签脚本区块——GET /api/bookmarklet 生成绑定当前面板地址的 bookmarklet(手机从局域网 URL 生成即指向台式机), 添加到收藏栏后在去哪儿精查页点击, 从渲染 DOM 挖最低价(¥ 2-5 位 + 50-99999 过滤, 尽力抓航班号/起降时刻), no-cors text/plain POST 回填, 抓不到价弹窗手输, 页角 toast 反馈结果; ② /api/point-fill 支持 from_city/to_city 城市对自动解析 route_id(bookmarklet 只知城市名), 歧义/未知返回 400; ③ doctor 新增 check_autostart——Windows HKCU Run + 计划任务双路探测 webui/worker 自启是否齐装, 未装/不完整 WARN 并给安装命令, 重启存活从「口头相信」变成可验证。验证: 新增 test_bookmarklet(生成器形状/origin 注入/城市对解析 200+400/端点契约), test_doctor +autostart 断言, ui_check v0.79 断言, 版本 0.79 四处盖章。
v0.80 反向缓存榜 + 借班转正预告 + compose 健康检查: ① 「预算找目的地」页新增 GET /api/reverse-latest——纯读 6 小时 reverse 缓存(零网络请求), 按 含税总价 升序回最近扫描命中线路(城市/日期/航班号/航司/裸价/直达链接/新鲜度), 前端首屏即渲染「最近扫描 · 命中 N 条线路」卡片榜, 该 tab 从空表单变成默认有内容; ② time_coverage 新增 promote_on/promote_dow——对每条借班行按 borrow_dow 推「该星期几的下一个日历日」, 取最早者, 时刻覆盖 KPI 直接显示「MM/DD 起借班转精查」, 把「为什么是参考时刻」变成带截止日的透明承诺; ③ docker-compose 补 healthcheck 声明(与镜像内置双保险), unhealthy 容器触发 unless-stopped 重启。验证: test_time_coverage +3(下个周日/最早 dow/无借班), 新增 test_reverse_latest(排序/税口径/字段/空缓存), ui_check v0.80 断言, 版本 0.80 四处盖章。
v0.81 时刻覆盖条 + 逐日矩阵 API + 每日巡检: ① 仪表盘日历卡片新增 TimeStrip——60 格逐日时刻质量热力条(绿=板库真实/蓝=跨周借班/金=邻近参考/灰=无价格), 悬停显示该日航班号+起降时刻, 借班格附转精查日期, 图例区实时汇总四类计数并预告「周X MM/DD 板库轮询补齐」; ② 新增 GET /api/time-coverage——把快照逐 deal 展开成逐日 kind 矩阵, 同时从班期库 dow 直方图推导 heal 自愈预告(缺失星期几的下一个日历日), 前端一次拉取全线路复用; ③ 新增 .github/workflows/daily-check.yml——每天 09:30 定时跑全套测试+前端构建(部署分量: 仓库不只在 push 时过 CI, 而是每天自证一次, 依赖漂移/API 变化一天内暴露)。验证: 新增 test_time_coverage_api(形状/计数/heal/借班不虚诺), /v2 DOM 断言 tstrip 渲染, 版本 0.81 四处盖章。
v0.82 精点直达一键化 + 自启落装(MSIX 注册表遮蔽攻关): ① 运维页「精点补查」缺价 chip 从「点选中再找链接」升级为 anchor 直达——一点即开去哪儿单日精查页(新标签)并同步选中回填表单, 未回填 chip 一律带 ↗, 已回填 chip 保持绿态提示有效期与让位规则; ② 回填面板每 20 秒自动重拉 /api/point-gaps——书签是 no-cors fire-and-forget 无法回调面板, 轮询让「点书签 → 面板变绿」无需手动刷新; ③ 本机自启真正落装并查清一例疑难: 安装脚本写的 HKCU Run 项在真实注册表里(reg.exe 可证), 但本机日常 python 是 Microsoft Store MSIX 版——MSIX 注册表虚拟化把 HKCU Run 的读遮蔽/写拒绝, doctor 因此假阴性。新增 tools/install_autostart.py: 在用户 Startup 文件夹(shell:startup)落一个 FareAlertStartup.cmd(文件系统不受 MSIX 遮蔽, 双包装器幂等自守不重复拉起), doctor.check_autostart 增加 Startup 文件夹探测并在检出 MSIX 时注明「Run 项以此为准」, 本机 verdict 回到 0 FAIL(worker 与 webui 同版 0.82, 班期库照常沉淀, 9-16/9-20 自愈节点不依赖手动拉起)。验证: ui_check 新增 v0.82 断言(anchor 直达/20s 轮询/启动文件夹探测/MSIX 注记), 版本 0.82 四处盖章。
常见问题
机票起降时刻从哪来? 去哪儿低价日历只返回每日最低价+航班号(列表页需签名,按合规原则不破解)。v0.18 起杭州相关线路自动用机场官网公开班期板按「航班号+星期几」沉淀计划时刻(零密钥);v0.19 起目标星期未沉淀时自动借用同号航班其他班期时刻(跨日班期·参考),仅有起飞时落地按大圆估算(~ 前缀);配置 Amadeus 后优先实时刻。车次时刻/历时来自 12306, 原生即有。
为什么按"总价"而不是裸价判定? 你下单付的就是裸价+机建+燃油,以最终支付口径对比心理价位才有意义。
和 OTA 券后价有偏差? 日历接口是挂牌价,不含平台券;偏差通常几十元内,收到提醒后点进下单页看券后实付即可。
学生票准吗? 12306 查询接口不区分学生价,这里按 动车二等座75折/硬座5折/硬卧=卧铺-硬座半价 估算(略偏低),优惠区间/资格以下单页为准。
推送没收到? 先在 Web UI 点"发送测试推送";Bark 确认 Key 正确、通知权限开启;Windows 计划任务看"日志"页排查。
开发与验收
每轮改动(无论人还是 AI)都必须走同一条验收流水线,全绿才算完成:
python tools/acceptance.py门禁覆盖 G0 静态检查 / G1 单测 / G2 API 契约 / G3 DOM 断言 / G4-G6 红线(提醒、隐私、估算价)。
人机协作守则见 AGENTS.md,逐条门禁定义见 docs/验收规范.md,里程碑与 DoD 见 docs/迭代路线图.md,API 契约见 docs/API.md。 本仓库已接入 GitHub Actions CI(push/PR 自动跑双平台单测 + 前端构建 + Docker 镜像构建);完整 acceptance 门禁(含运行中面板的 API/DOM 契约)仍在本地执行,本地全绿 + CI 全绿才可合入。
v0.83 精查回填养板库 + 公务舱监控去 Amadeus 依赖: ① point-fill 回填行新增舱位标签与城市对, 带航班号+时刻的行同时落入 sched_deposit 队列, worker 每轮 update_sched_db 吸收进持久班期库(按行自身星期分桶, 板库已有行不被覆盖)——班期接口只返回"当日+次日", 周日这类空洞板库自身永远抓不到, 现在用户在精查页点开任意周日航班回填即可即点即补, 起飞时刻显示的最大空洞类从根上可治; ② 公务舱监控原先绑死 Amadeus flight-offers(无密钥=每 30 分钟空转 skip), 现在 /api/bookmarklet 支持 ?cabin=business 生成舱位书签, 舱位筛选页抓到的行带 cabin 标签进 point 缓存, cabin_patrol_once 无密钥时走 absorb_point_cabin 通道——同一套环形历史+历史新低告警无 key 存活, skip 文案改为可执行的 standby 指引; ③ doctor 板库检查对缺失星期具名(如"缺周日")并给出双自愈路径(该星期几运行日自动补 / 精查书签回填立即补)。新增 6 项单测(put_rows 舱位字段、deposit 队列过滤追加、absorb_deposit 补 dow/不覆盖板库行/幂等、absorb_point_cabin 分组过滤 TTL)。
v0.84 借班一致性投票 + reenrich 标签修复 + api_push 常备通道: ① 跨 dow 借班此前"任取一个 dow 的时刻"无一致性校验, 不同星期执行同一航班时刻有偏差时会静默借错——board_lookup_x 借用分支升级众数投票: ≥2 个 dow 同 dep 时刻 → 借该一致时刻并带 borrow_votes 票数(证据越足越可信); 多 dow 分歧无众数 → borrow_unstable=True, DayDetail 徽标三态化(分歧警示?/票数一致 N 票原文案/单候选不标分歧); 单候选视为证据薄而非分歧, 诚实区分"不确定"与"证据少"两种状态; ② v0.77 遗留 bug 修复——reenrich 重查时刻时不回写 borrow_dow/borrow_votes/borrow_unstable 三键, 精确命中后旧借用标签残留造成"来源失真", 现在 TIME_FIELDS 三键齐清, 重查后标签与真实来源一致; ③ 新增 tools/api_push.py 正式推送工具(参数化 TAG, OLD=origin/master 自动推导, worktree clean 门禁, credential fill 取 token, 本地 annotated tag 解析重建)——github.com:443 不通而 api.github.com 可达的环境从此一条命令完成 commit+tag 推送, v0.83 推送三坑(循环步进/短 sha/TAG 未展开)全部内置修复。验证: TestBorrowConsensus 4 用例(众数+votes+库纯净/分歧 unstable/单候选不 unstable/exact 无投票键), 全套 271 单测绿。
v0.85 推送带上起飞时刻 + 可信度标记: 手机端最需要时刻的三个触点全部补齐——① 阈值提醒标题带可信起降窗口(exact 或 ≥2 票一致才上标题, 单 dow 借用/分歧时刻只留在正文带标记, 不在锁屏冒充事实); ② 提醒正文逐航班行插入 "07:45-10:20(3票)" 式窗口+可信度——exact 无标记 / 跨 dow 一致 N票 / 单 dow 借用或 alt 参考"参考" / dow 分歧"⚠", 无时刻不加噪; ③ 周报全局最优行同步同一套标记 "(19:45-22:25·3票)"; 实现上 core/alerts 新增 dep_arr_text/conf_mark 双态助手(FlightDeal 属性与快照 dict 同一实现), 周报与阈值提醒共享; global_best 提取器补齐 borrow 三键。新增 tests/test_alert_msg.py 10 用例(窗口三态/五种来源标记/dict 形态/标题门控/正文标记/周报三态), 全套 281 单测绿。
v0.86 迁移包完整性升级 + Web 一键导出导入: 「部署分量」与跨设备迁移收口——① v0.41 的备份清单落后于数据演进: history.json(周报/骤降历史)、cabin_history.json(公务舱环形库)、point_fill_cache.json(精点回填缓存)、sched_deposit.json(养板队列)统统不在包里, 迁到新设备会静默丢四大耐久状态, 全部补回 ITEMS; ② 迁移包带 bundle_manifest.json(format/code_ver/逐成员 sha256), restore 先验签再落盘——传输损坏或被改动的包直接拒收(--force 可强行, 自担风险), v0.41 无封条的旧包仍兼容; ③ 导入前自动落 pre-restore-safety-*.zip 安全备份, 坏导入本身一条命令可回滚; ④ 新增 GET /api/bundle/export(浏览器直接下载迁移包, 手机/Mac 无需 SSH)与 POST /api/bundle/import(sha256 校验→安全备份→恢复, 篡改包 409), 运维页新增「📦 备份与迁移」卡片。新增 9 项单测(清单回归/封条覆盖全部成员/篡改拒收/旧包兼容/manifest 不落盘/安全备份/导出流 PK 头/探针往返/非 zip 拒收), 全套 290 单测绿。
v0.87 灰点自动补价: Booking 无 key 交叉源(国内+国际): 用户点破的关键观察——灰色日期"直接精点其实查得到价"。实证定位: 去哪儿日历网关对 365 天每天都返回行, 但 ~320 行 price 为空(服务端尚未生成缓存底价), 抓取端 if not p: continue 把它们丢成灰点; 逐日列表接口仍被 Bella 签名门禁(v0.76 结论未变)。解法是换攻击面: flights.booking.com/api/flights/LOWEST_PRICE 公开无 key, 3-5 秒一次返回真实可购最低价(含最便宜航司与航班数), 国内国际灰点日期通吃。① 新增 core/booking_fill.py——近日期优先的预算轮询(每轮最多 6 个, 同 v0.67 amadeus 模式), 正缓存 48h/负缓存 24h(兼作轮换游标), 间隔 ≥4s, EUR→CNY 汇率可配(booking_fill.fx_eur_cny, 默认 7.8); ② main.py 国内路径三个 amadeus 短路口(未配密钥/无 IATA/最终返回)与国际路径(promo 之后此前完全没有 offer 级补全)全部接入 _booking_cross_fill——无 Amadeus 密钥的开源部署从此也有自动补价; ③ 口径诚实: booking-ref 加入 NON_REAL_SOURCES(flights/history 双份+前端 cheapestFlight/isRefDeal 同步)——真实报价但属国际渠道参考价, 通常高于国内 OTA, 只展示不触发阈值提醒/不进 KPI/不进历史最低/不作为插值锚; 精点回填(point-fill)可覆盖 booking-ref 行, 去哪儿日历生成缓存价后真实行自然接管; ④ UI: DayDetail「Booking参考」徽标(悬停说明口径), 数据源页新增可开关的 Booking 源(default-on 语义修复: enabled 键缺失不再显示为关闭), 爬取流水 booking-fill 步骤打点。端到端实测: 杭州-重庆 2026-10-14 补到 ¥1238(厦航 MF, 208 班), 偶发失败日期进负缓存次日自动重试。新增 4 项单测(响应解析/预算轮询+缓存回放/仅补缺失日期/NON_REAL 语义+精点让位), 全套 294 单测绿。
v0.88 Booking 完整行程: 灰点直出精确时刻 + 偶发失败负缓存分级: 用户连续点破两个问题——①"精点能查到数据的日期在聚合页是灰的"②"航班起降时刻缺失"。实证定位: ① v0.87 的负缓存不区分"服务端明确无报价(200 且无 minPrice)"和"偶发调用失败(限流/超时/5xx)", 一次 unlucky 调用把日期锁 24h, 而 live 复测证明这些日期大多有价(杭州-重庆 2026-10-13 实测 163.2 EUR 可查); ② LOWEST_PRICE 响应本就携带 flightOffers[15] 完整报价——segments 精确起降 ISO 时刻、marketingCarrier+flightNumber 航班号、totalTime 历时、多段=中转/多leg=经停、planeType 机型、travellerCheckedLuggage 真实托运额度, v0.87 只读了 aggregation.minPrice 把行程全扔了。修复: ① fetch_lowest 对 200 无报价返回 no_data 语义, 负缓存分两级——err(网络类)90 分钟重试、nodata(服务端确认)12 小时, 旧无 kind 负缓存自动按 err 迁移重探, "精点可查"的日期数分钟内自愈; ② 新增 offer_itinerary 解析——灰点 booking-ref 行直出航班号/精确起降/历时/中转/机型/真实托运(dep_src=booking), FlightDeal 增 baggage_note 字段透出到 UI; ③ 新增 attach_times: 有真实价但缺精确起飞的日期, 用同日 Booking 行程钉参考时刻(dep_src=booking-x, 只升级缺失/借班行, amadeus/airport-board/alt-ref 权威保留, 价格与航班号身份不动); ④ main._booking_cross_fill 补完 time_gaps 探测+attach 接线(顺带修复上轮遗留的 docstring 断裂语法错误), _flight_dict 透出真实托运; ⑤ UI: DayDetail 新增「Booking精确」「Booking同日参考」时刻徽标(悬停说明口径), Kpis 时刻覆盖 KPI 排除 booking-ref 行与 booking-x 参考时刻(口径与 alt-ref 对齐)。单测 4→10(fetch no_data/error 语义/负缓存 TTL 分级+旧缓存迁移/err 写回/offer 字段解析/extra_dates 只写缓存后 attach/attach 覆盖策略), 全套 300 单测绿。
v0.89 当日实测班次时刻表 + systemd 裸机部署: 用户核心痛点「页面总是无法显示航班班次的具体起飞时间」的第三层根治——v0.88 只取 Booking 最便宜一班, 但响应本带 ~15 个完整报价(不同航班各带精确时刻)。① core/booking_fill.offer_list: 全班次解析(航班号去重、按起飞排序、含历时/机型/经停), 缓存条目新增 offers 键; ② 灰点行(booking-ref)的 alt_times 直接挂当日实测班次列表(exact+src=booking), 详情卡新增「当日实测班次·N班」区——该日真实可购航班的精确起飞→落地, 非星期推断; ③ 真实价行被钉 booking-x 参考时刻的同时也挂上实测班次列表(alt_times 原为空时), _attach_alt_times 不覆盖已有值所以口径稳定; ④ webui /api/day-schedule 叠加 Booking 实测: 同号航班的当日实测时刻优先于板库星期推断, 板库没有的航班补充进列表, 24小时时间线的 tip 标注「Booking当日实测」; ⑤ 部署升级: 新增 deploy/systemd/(fare-alert.service + install.sh 一键装, 崩溃15s自动拉起)与 deploy/README.md(Docker/NAS/树莓派/Windows 三形态部署指南)。单测 10→12(offer_list 解析去重排序/offers 优先挂载+attach 班次列表断言)。
v0.90 遗留缓存实测班次升级轮转: v0.89 的实测班次覆盖停在 48/240 不再增长——根因是 v0.89 之前写入的 159 个旧正缓存日期只带单班次时刻(dep 有、offers 无), fill_gaps 视为“完整”永久跳过。① 跳过条件收紧为 dep+offers 双全, 无 offers 的旧正缓存重新进入探测轮转(仍受 max_per_cycle 预算节流), 逐轮升级为当日全班次时刻表; ② 升级探测失败保留原参考价(deferred 下轮重试), 绝不把已有正缓存覆盖成负缓存; ③ attach_times 用缓存实测 offers 覆盖无来源标记的板库参考 alts(同日实测 > 星期推断), 已带 booking src 的不重复覆盖。单测 12→15(旧正缓存重探升级/升级失败保价/无源 alts 覆盖)。
v0.91 实测班次覆盖监控可见化: v0.90 的升级轮转在后台静默跑, 用户只能等覆盖慢慢变多。本版把进度变成指标: ① core/booking_fill.coverage_stats 统计新鲜正缓存/已带实测班次/待升级天数/百分比(负缓存与过期条目剔除, 异常桶不崩); ② /api/health 新增 timetable 块, 部署盒子/外部 uptime 监控可直接拉取; ③ 前端“数据源”页调度心跳卡新增覆盖进度条(百分比 + 剩余天数提示, 覆盖完成显示✔)。单测 15→16(混合缓存计数)。
v0.92 实测班次直达购票+低价班标记: 详情卡「当日实测班次」区的每班从纯文本升级为可点链接——点击任意实测班次直达去哪儿当日航班列表购票(原来只有 24h 时间线的点可点); 当日 OTA 最低价航班(deal.flight_no)若在实测列表中, 该班 chip 加绿色「低价」角标高亮——价格、精确起降时刻、购票链接三位一体(匹配不上则不硬造标记); 链接 chip 无下划线、hover 提亮边框, 与既有设计令牌风格一致。
v0.93 灰点反饥饿根治: 用户点破「聚合日历查不到的日期, 精点直查其实有价」——实证定位为三重叠加死锁: ①v0.87 僵尸 worker(升级后进程未换, 16:06 才被 restart 接替)给郑州/成都写了只有 cny 无 dep/offers 的 4 键缓存条目; ②v0.88 起回放门槛收紧为 dep+offers 双全, 4 键条目永远不回放, 快照重建后退回灰点; ③v0.90 升级轮转本该救它们, 但 targets 把灰点日期与时刻升级日期混合排序, 每轮 6 个探测预算被近端 time-gaps 吃光, 远端灰点永久饥饿(live 实证: 单独喂杭州-重庆 6 个灰点日期 6/6 立即探测成功)。修法两刀: fix A 灰点日期在探测队列绝对优先于时刻升级(组内仍近日期优先); fix B 僵尸 4 键条目(连 dep 键都没有)在灰点日期直接回放参考价——真实 GDS 参考价远比 interp 合成行可信, 时刻由升级轮转后补; 探测失败也回放旧价兜底并刷新 ts 获得 48h 冷却(防每轮重试风暴)。带 dep 无 offers 的 v0.89 条目保留 v0.90 升级语义不受影响。线上验证: 杭州-重庆 11-05
11-12 六个灰点全部转为 booking-ref 行(¥1738-2520, 带精确时刻+6 班当日时刻表), 郑州/曼谷 10-28 同步自愈; 郑州 11-0305/成都 11-01~06 的僵尸条目与曼谷 11 月未探测日期由新预算顺序按轮自动补齐。单测 18 用例全绿(新增僵尸回放/预算优先级 2 例, 失败兜底断言升级), 版本 0.93 五处盖章。v0.94 逐班参考价: 详情卡「当日实测班次」此前只显示每班的时刻/机型, 价格只有一个日期地板价——用户无法直接对比 3U2581 与 GJ8827 哪班便宜。live 探测实证 LOWEST_PRICE 的 flightOffers 里每个 offer 都带 priceBreakdown.total(units+nanos, EUR 含税总价, 与 aggregation.minPrice 同构), 且 offers 本身按价格升序。链路: offer_list 解析 price_eur(非 EUR/缺字段兼容为 0) → 缓存 offers 自动带上 → _deal_from/attach_times 的 alt_times 按 fx(booking_fill.fx_eur_cny, 默认 7.8)折算出 price(CNY) → /api/snapshot alt_times 透出 → DayDetail.jsx 实测班次 chip 显示「¥参考价」+ title 提示。旧缓存 offers 无 price_eur 不显示价格(不报错), 升级轮转 48h 内自然带上; 参考价为 GDS 国际渠道 EUR 折算, 通常高于国内 OTA 实付, 仅展示不进提醒/KPI。单测 19 用例全绿(price 解析/无价兼容/fx 折算/attach 透传 4 处断言新增), 版本 0.94 五处盖章。
v0.95 逐班价全量回填 + 部署自愈加固: 两处收尾。① 回填工具化——v0.94 的逐班价只有新探测的日期才有, 存量 240 条缓存要等 48h TTL 过期后按 6/轮慢慢轮转(约 2 天才能全部带价); 新增 core.backfill_prices +
tools/backfill_offer_prices.pyCLI: 跳过已带价/负缓存条目(负缓存 TTL 语义不动), 失败探测绝不覆盖正条目, 每 10 条做一次 entry 级磁盘合并保存(reload 磁盘再覆盖 touched 条目, 与并发 worker 轮次互不丢写), >=4s 限速, 支持 --max/--interval/--routes。② 部署三层自愈——运行 tools/install_autostart.py 安装登录自启(Startup 文件夹 .cmd 防 MSIX 注册表面纱 + HKCU Run 双保险), 尽力注册每日 07:30 的 FareAlertWorkerRevive 计划任务(硬内核主机可能拒绝, 拒绝时靠 webui 内置 07:00 supervisor 线程兜底), /api/health 的 revive.task 可观测安装状态。单测 20 用例全绿(新增回填合并/跳过/预算 3 例), 版本 0.95 五处盖章。v0.96 当日最低参考班徽章: 详情卡「当日实测班次」按起飞时刻排序展示, 用户要比价得逐班扫一遍价格——现在后端
_alt_times_with_best_ref在 booking 来源且 price>0 的班次里选出参考价最低者标best_ref, 前端该班 chip 加琥珀色「最低参考」角标; 时刻排序保持不动(比价看徽章, 赶时间看顺序两不误), 无价/非 booking 班次永不中标, 并列取首个。实测: 杭州-重庆 09-16 的 3U2579(¥1296) 压过 HU7421(¥2101) 中标。回填验收: 4 路由 x 60 天 = 240/240 全部带价, 印证「灰点日期精点必有价」。v0.97 周报亮点带起降时刻: 归档层(core/history._route_metrics)此前只存价格统计, 最优班的 dep/arr/no/dur 在归档边界被丢掉——周报亮点与推送从归档生成, 自然显示不了「几点飞」。现在归档透传时刻, week_highlights 的 sharp_drops/below_threshold 行携带 dep_time/arr_time/flight_no, 前端骤降/破线行渲染时刻 chip, 推送亮点文本「06:50起飞」; 明日首个新归档日后生效(今日已归档的 4 条 meta 无时刻, 属预期)。
v0.98 运行时看门狗(第 4 层自愈): 前三层(07:00 supervisor/Startup/.cmd/07:30 计划任务)都只负责「启动」, 运行中死亡要等 6h 追赶或次日窗口。① core/revive 心跳 >=90min(2 个采集周期)立即探活+复活, 每小时限 1 次防风暴, 进程在而心跳老只记 note 不 kill; ② tools/watchdog.py 外部守护 webui 本体(每 15min 计划任务 FareAlertWatchdog, 连续 2 次探活失败才重启, 30min 冷却), 状态落 data/watchdog_state.json 并入 /api/health; ③ 修复 register_revive_task.ps1 路径反斜杠丢失(toolsautostart_worker.ps1 -> tools/autostart_worker.ps1, 原任务明早首跑必失败), 两任务已重注册验证 Ready。
v0.99 ECB 每日汇率: 灰点日期的精点补价(booking.com 单日报价)此前用写死 7.8 换算 EUR, 是参考价最大误差源。① core/fx.py 每日 ECB 参考汇率链: frankfurter.app(ECB 免 key 镜像) -> ECB 官方 SDW CSV -> 隔夜缓存(stale 标记) -> 固定汇率兜底, 12h 刷新, 全链路不抛异常; ② booking 缓存条目新增 eur 原值, 回放时按当前汇率重算 cny(旧条目兼容直通), fill_gaps/backfill_prices 统一走 _resolve_fx; ③ config 新增 booking_fill.fx_mode(ecb/fixed, 默认 fixed 零网络), /api/health 新增 fx 观测块。test_fx 6 例。
v1.00 推送当日参考班+看门狗绝对路径修复: ① 阈值推送正文新增「当日班次参考」行——best_ref_alt 镜像 UI 端 v0.96 best_ref 语义(alt_times 中 src==booking 且 price>0 的最低价班次), OTA 最低价行缺可信起飞时刻时, 推送仍点名具体班次+精确起降窗+参考价(「当日班次参考: 3U2579 06:50-09:25 参考¥1296」), 无窗/无价自然跳过; ② 实弹修复: FareAlertWatchdog 计划任务 action 裸 python.exe 在任务会话解析到 WindowsApps 存根别名, 每 15min 触发全部 0x80070002 静默失败(注册以来从未真正跑过, 昨晚 state 全靠手动运行)——register_watchdog_task.ps1 改为注册时解析 sys.executable 绝对路径+Test-Path 校验, 重注册后 schtasks /run 实测 exit 0、watchdog_state.json 刷新、/api/health 回到 healthy。test_alert_msg +3 例。
v1.08 班期平衡 2.0(balance 2.0): v1.07 首轮实测(周日晚 0→32 班, 周六 909→931)证明管线正确但回填太慢——每(星期×方向)只探 1 个日期、offer 只取前 8 条、12h 节流。core/dow_balance.py 升级: 严重饥饿(低于峰值 20%)的星期每轮探测窗口内 3 个未来日期(候选池 6 个按 done 去重滚动续探), fetch_lowest 新增 offer_limit 参数让平衡探针拉全日约 30 条航班行(快照条目不受影响仍是前 8), 节流在存在严重饥饿星期时放宽为 4h, 恢复后回到 12h; 弱而不严重(20%~35%)的星期保持每日 1 个日期的温和节奏。absorb 只计真正新增的航班+星期对, 既有观察条目永不覆盖。tests/test_v108.py 10 断言(严重分级/多日期探测/offer_limit 传递/4h vs 12h 双节流/去重续探)全绿。
v1.09 公务舱无密钥采集(keyless business-cabin patrol): 公务舱模块自 v0.66 起骨架完整(巡检腿/历史环/历史新低/告警冷却/书签回填吸收), 但 cabin_history.json 一直是空 routes——无 Amadeus 密钥时巡检只 standby, 采集从未真实发生; 去哪儿日历网关对舱位参数无响应(实测 8 种变体全失效), H5 列表页签名门禁未变。换攻击面: flights.booking.com LOWEST_PRICE 网关接受 cabinClass=BUSINESS, 纯 HTTP 无 key 可用(实测 CKG→HGH 公务 €741 vs 经济 €159, HGH→BKK €346, 含税总价+精确时刻+15 条 offer)。① core/booking_fill.fetch_lowest 加 cabin_class 参数(默认 ECONOMY 不变); ② core/cabin_monitor 新增 probe_dates(窗口内日期按最后观测时间排序: 从未探测优先→最陈旧优先, 每腿每轮 K 个, 零额外状态重启自动续)与 booking_cabin_rows(EUR×ECB 汇率−税费=裸价, total_price() 精确还原含税应付价); ③ main.cabin_patrol_once 重构: 书签回填吸收从无密钥分支提升为每轮必跑, Booking BUSINESS 无密钥探测每轮必跑(间隔沿用 call_interval≥4s), Amadeus 通道有密钥时叠加; ④ source=booking-cabin 不入 NON_REAL_SOURCES(真实可购报价, 直接进历史环与历史新低告警), /api/cabin 增加 booking_cabin_ready。tests/test_v109.py 9 断言: 参数透传+minPrice 解析、探测优先级(从未探测→陈旧优先→k 截断)、裸价+税费回环=含税应付、空值守卫、默认配置、booking-cabin 真源语义。
v1.10 Agent 任务中心 + 公务舱精准化(agent task center & per-flight cabin timetable): ① 观测优先的任务中心——core/agent_tasks.build_ledger 把 7 类后台任务(全量扫描/公务舱巡检/班期平衡/每日备份/汇率刷新/健康巡检/洞察周报)从各自独立的状态文件投影成统一账本 {id, name, icon, status(ok/late/idle, 1.5x 节奏判 late, 时钟偏移算 ok), last_run, next_due, detail, manual_trigger}; webui 新增 GET /api/tasks 与 POST /api/tasks/cabin-patrol/run(手动点火公务舱巡检, 推送静默), 前端新增 🤖 任务中心标签页(卡片式账本+刷新+手动触发), 调度仍由现有 worker loop 负责——后续逐任务搬迁调度零 UI 改动。② 公务舱采集精准化(用户点名「尽快优先补齐, 要求精准」)——v1.09 每日期只记 aggregation.minPrice 一条匿名最低价, LOWEST_PRICE 响应里
15 条逐班报价(各自的税后 EUR 总价+精确起降时刻+航班号)全被丢弃; 现在 booking_cabin_rows 把整组 offers 逐班转 FlightDeal 入史(minPrice 降级为无 offers 兜底), record_low 去重键升级为 日期×舱位×航班号(多班共存/同班替换, fno-less 旧调用共享 "" 槽完全兼容), HISTORY_CAP 120→300, probe_dates_per_round 默认 6→12, offer_limit 3→8, history_board 榜单带 low_fno。实测 PEK→HGH 2026-10-20: n_offers=152, 8 班公务舱逐班入史 ¥5534.611349.2(HU7103 17:00 中转次日达 vs HU7577 06:55 直飞, 价差近半——v1.09 这类信息全丢)。tests/test_v110.py 9 断言 + tests/test_cabin_precision.py 7 断言全绿。v1.11 公务舱标签页(cabin tab: from zero): 排查实锤——公务舱后端自 v0.42 存在、v1.09 无密钥采集打通、v1.10 逐班精准入史, 但前端 9 个标签页(dash/crawl/agents/routes/reverse/weekly/sources/push/logs)里没有 cabin, index.html/app.js 里 "cabin/公务舱" 零命中——所有逐班数据用户在页面上完全看不到, 「部署太轻量」的直接实锤。本版从零建 👔 公务舱标签页: ① record_low 新增 dep/arr 持久化(v1.10 只存 fno, 时刻表无起降可言; 旧行随同班替换语义下一轮巡检自动补齐); ② cabin_monitor.history_timetable 纯函数——每腿取最便宜 N=8 条 fno 观测(价格升序/日期 tie-break), 腿按各自最低价排序, fno-less 旧观测仅入榜单不入时刻表; ③ /api/cabin 新增 timetable 投影, 服务端用 city_iata 拼 Booking 公务舱搜索深链(type=ONEWAY&cabinClass=BUSINESS); ④ 前端 cabin-status 状态条(最近巡检结果+「立即巡检」手动触发, 探测中转圈 2-3 分钟后自动刷新)+历史最低徽章(low+low_fno+low_date)+逐班时刻表(日期/航班号/起降时刻/含税总价, 整行可点直达 Booking), 样式全走设计令牌。tests/test_v111.py 4 断言: dep/arr 入环+legacy 兼容、投影排序/fno-less 剔除、per_leg 截断+空守卫、webui 三件套接线契约。
v1.12 时刻质量热力图+公务舱走势图(time heat + cabin sparklines): 排查实锤——v0.81 就建好的 /api/time-coverage 逐日质量矩阵前端从未调用(app.js 里 "time-coverage" 零命中), 时刻来源构成只有 7 条汇总条形图, 用户看不到具体哪天是跨日借用、哪天能自动转正; 240 deals 快照实况: exact 196(booking 144+机场板 52)+borrow 42(booking-x 跨日借用)+alt 2, 零缺失行, 主诉剩余=42 行借用+2 行参考的可监督性。本版: ① 时刻覆盖体检卡内新增 timeHeat 容器(顺手删除误加的 tab-timeheat 空 section), loadTimeHeat/renderTimeHeat 每条监控线路一行——腿名+四色计数徽章(精确/借用/参考/无参考中文)+逐日格子条(exact 绿/borrow 蓝/alt 琥珀/noref 灰, title=日期 班次 起降时刻, 借用格带 promote_on 自愈转正日), 随 renderTimeCoverage 既有三处调用点自动刷新; ② cabin_monitor.history_timetable 每组新增 spark 投影——该腿观测按日期取 min 价格、日期升序、截最新 30 点(fno-less 旧观测有真实价格, 计入); ③ 前端 cabinSpark: SVG polyline 120x28, min-max 归一化, 最低点绿点标记+「近N日 ¥lo–¥hi」标签, 插在公务舱腿卡头部下方; ④ 样式全设计令牌(th-cell 四色族/徽章 pill/sparkline stroke 走 accent)。tests/test_v112.py 5 断言: spark 按日 min+升序+fno-less 计入、30 截断保最新、空历史守卫、webui 三件套接线契约(timeHeat 容器/loadTimeHeat+cabinSpark/css)、误加 section 移除。
v1.13 v2 主入口补齐任务中心+公务舱逐班表(v2: agents + cabin timetable): 排查实锤——v1.10 任务中心与 v1.11 公务舱逐班时刻表只落在经典版 /classic, 用户默认打开的 v2 主页(Preact) TABS 里没有 agents/cabin, dump 实测 hash 回落仪表盘(三个 tab bytes 相同 57853), 「部署太轻量」的延续。本版: ① web/src/components/AgentsView.jsx 从零建——fetchTasks 账本投影, AgentCard 状态徽章(ok 绿/late 琥珀+浅底/idle 灰)+detail+上次(距今格式化)/节奏/下次 三段 meta, manual_trigger 手动点火(scan-all 走 /api/run 其余走各自 trigger, busy 转圈完成后自动刷新账本); ② web/src/components/CabinTimetable.jsx 从零建——消费 /api/cabin 的 v1.11 timetable 投影: 每腿一卡(腿名+SparkPts 走势), 逐班四列表(日期/航班号/起–降/含税总价), 整行点击 window.open Booking 公务舱深链; SparkPts 用 v1.12 后端 spark 投影(按日 min/升序/30 点/fno-less 计入)替代旧本地 slice(-10)——数据口径与经典版完全一致; ③ CabinCard 非编辑模式尾部接线 , app.jsx TABS 加 agents(第5位)——v2 现在 9 个功能页; ④ 样式全走 tokens(app.css 追加 v1.13 块: agents-grid 自适应网格/agent-card 状态配色/cab-tt 腿卡/hover 行高亮), npm run build 产出新 bundle(index-Djm1FoKa.js 132.93kB)。tests/test_v113.py 3 断言: agents 三件套接线(app.jsx TABS+import+render/组件+api.js 导出)、cabin 三件套(CabinTimetable 消费 timetable+spark+深链/CabinCard 接线)、构建产物新鲜度(dist 引用+css 含 .agents-grid)。
v1.14 公务舱时刻缺口优先回查(time-gap-first probe + coverage chip): 根因实锤——probe_dates 只按最后观测时间排序, 某日若历史上只拿到过匿名 minPrice 兜底行(无航班号无时刻), 它按 ts 显得『新鲜』永远排不到探测队列前——时刻格永远空着。本版: ① core/cabin_monitor.py probe_dates 排序升级为 (pri, ts, date): pri=0 从未探测 / pri=1 最新观测批无 dep+arr(time_first=True 时插队) / pri=2 常规最旧优先, time_first=False 保留纯 v1.09 口径; _newest_timed(route) 按日取最新批 ts+是否有任一行带起降时刻(同批共享 ts); ② time_gap_dates() 纯函数圈定窗口内缺口日, main.py cabin_patrol_once 每腿 gaps 交集计数 n_refill→info.time_refill, last_status 追加『· 时刻回查 N 日』(任务中心卡片直接透出); ③ history_timetable 每腿新增 timed/total(展示行的满时刻计数), v2 CabinTimetable 每腿头部渲染『时刻 x/y』覆盖芯片(满覆盖绿 cab-tt-cov.full, 悬停说明回查策略), app.css 追加 v1.14 芯片样式全走 tokens; ④ 推送通道排查确认 _cabin_absorb 已接 push_all(kind=cabin-record/cabin)+冷却, 无需重复接线。补充修正: 缺口判定含两类——a) 最新观测批无时刻(匿名兜底行), b) 当日最便宜 fno 行无时刻(旧书签行按价格永远压住新带时刻观测); 预算切分: 全新 60 天窗口的几十个从未探测日会占满整个 k 预算饿死缺口修复, 现每轮最多一半名额(k//2, 至少 1)优先给缺口日, 其余继续轮转。 history_timetable 按 (日期,航班号) 去重, 带时刻观测升级同航班无时刻孪生行(否则回查永远不可见)。 tests/test_v114.py 9 断言: 无时刻日插队(never→gap→stale)/time_first=False 旧口径保留/同批带时刻行判定/缺口日窗口过滤/timetable timed+total/组件芯片接线/巡检计数接线。
v1.14.1 cabin_refill 一次性缺口补查运维工具(one-shot gap refill): v1.14 的 k//2 预算切分每 30 分钟一轮只修几个缺口日, 全补完要等数小时; 本版 tools/cabin_refill.py 把『缺口日精点能查到』(用户原话)变成一条命令立等可查——按巡检同一条数据通路(Booking LOWEST_PRICE BUSINESS → booking_cabin_rows → _cabin_absorb, 推送关闭+空 state dict)一次性补完全部时刻缺口日, 只写 cabin_history.json(原子写, 不碰 state.json, 与在跑 worker 并发安全); --dry 只列缺口清单不发请求, --max N 控制每腿上限(默认 20); 输出严格区分 transient(非 200/限流, 稍后重试)与 server-confirmed no offers(服务器确认无票)两种空结果, 与 v0.88.1 语义对齐——实测验证: 工具跑通 17 缺口日全部 transient(裸调证实网关 429 IP 级限流, 换 UA 无效), 输出不误读为无票。ui_check v1.14.1 断言工具存在+双语义标记。
v1.15 Booking 网关 429 熔断+会话指纹轮换(throttle circuit breaker + fingerprint rotation): 实锤——连续两轮巡检整轮零报价+17 缺口日补查全空, 裸调抓包证实网关 429『Rate limit exceeded』(IP 级, 无 Retry-After, 响应还下发新 cookie, 限流键=IP+cookie+UA 组合); 旧代码非 200 一律 None, 调用方把限流当普通瞬时错误继续打满整轮探测, 拉长封锁窗口。本版: ① core/booking_fill.py fetch_lowest 429 显式返回 {"throttled": True}(区别于 no_data/None 三态语义), UA 指纹池 current_fingerprint(config 钉死 UA 作池首, idx=0 字节级保留旧行为)/bump_fingerprint(模块态轮换, 无 IO)/warm_session(GET 搜索页暖 cookie, 429 响应也发 cookie); ② 公务舱巡检循环(main.py cabin_patrol_once)遇 throttled: 每次 bump+重建会话+重暖, 两击熔断当腿剩余探测, info.throttle 透出, last_status 追加『网关限流熔断xN』——任务中心卡片直说限流而非无票; ③ 经济舱 fill_gaps 同样两击熔断(限流不写负缓存不污染轮转游标, stats.throttled 透出); ④ tools/cabin_refill.py 遇 429 熔断当腿并提示 rerun later。 tests/test_v115.py 8 断言: 429→throttled/503→None 保留/throttled 不产生报价行/idx0=config 钉死 UA 兼容/bump 轮换/warm 命中搜索页/fill_gaps 两击停+零负缓存污染/巡检+工具接线。
v1.15.1 429 限流自适应退避(adaptive patrol backoff): v1.15 熔断止住当轮空转, 但 worker 仍按固定 30 分钟间隔下一轮继续撞墙。本版给退避加上自适应: ① core/cabin_monitor.py patrol_gap(base_seconds, prev_streak, throttle_hits) 纯函数——本轮再限流则 streak+1, 下一轮间隔 2×/4×/6× 封顶(30→60→120→180 分钟), 干净一轮立即复位 base; ② main.py 巡检开头读上轮 throttle_streak, 结尾写 info.throttle_streak/interval_effective_minutes, last_status 追加『限流退避下轮+N分』, worker 循环 next_patrol 改用 effective 间隔(异常回退 patrol_gap); ③ 任务中心卡片 cadence 跟随 interval_effective_minutes, 退避中不再误报 late; ④ /api/cabin 投影透出 throttle_streak/interval_effective_minutes。 tests/test_v1151.py 7 断言: 干净轮复位/首熔断 2×/二连 4×/三连 6× 封顶/卡片退避中节奏/干净轮回 base/端到端接线。
v1.23 公务舱时刻按需精查(on-demand route fill): 后台巡逻每轮只补 4 班且 429 自适应退避最长 180 分钟, 用户盯着的腿页面缺口却等不起——把 v1.22 的携程 CDP 精查能力从后台配额里解放出来做成按需: ① core/cdp_board 新增 fill_route(route_targets) 单航线定向靶点——该腿 timeless 观测按 fno 去重/最早日期优先, 红线与后台完全一致(6h 行缓存/24h 负缓存/共享账本每日 24 次硬上限/连续 2 败熔断), changed_db 原子写回; ② webui 新增 POST /api/tasks/cdp-fill/run(body {from_city,to_city,max_fnos?,force?}), exact>0 时回写 cabin_history, 返回逐班 rows(fno×date×起降×how 溯源); ③ 公务舱标签页每条腿卡片新增『补齐时刻』按钮——防重入/状态行轮播/成功后整面板自动刷新, 页面缺口 3-5 秒变精确起降时刻; ④ 与巡逻共享同一账本互补不抢占, 超限时诚实报 capped 而非越权; ⑤ 上库通道实弹——出口网络对 github.com:443 TCP 重置(api.github.com 正常), git push 连挂 4 次后复活 v0.84 的 tools/api_push.py git-data API 通道完成推送, API ref 更新实测会触发 push 工作流(CI 自动三连绿)。实弹 3 查 1 沉淀(CZ8885@10-08 大兴 16:00→虹桥 18:20 入库)。tests/test_v123.py 8 断言(城市过滤/最早优先/已定时跳过/全负缓存短路/熔断/限流截断/正常回写落盘)全绿。
v1.30 公务舱精度统一(leg- merge + src classes): 诊断实锤——同一真实航段在历史里存在两条路由(booking 估算 patrol- 与去哪儿实抓 point-), 北京-杭州 4487.8(估算) 与 1200(实抓) 长期并存, 阈值判定对着估算价跑造成漏报; 开源侦察(GitHub API 检索 qunar-flight-reminder 等同类项目)结论: 无维护中的签名破解方案, CDP 真浏览器采集保持为唯一精准源。本版: ① 精度分级 SRC_CLASS{qunar:2,point-cabin:2,amadeus:1,booking:0}, record_low 同(日期×舱位×航班)精准优先去重, 估算价永不覆盖精准行, 返回在位行时剥离陈旧 record 标记(防幽灵重推); ② 自动迁移 point-/patrol-* → leg-* 合一, 按前缀盖 src 章, precise-wins 去重, 幂等, load_history 检测即原子持久化(实测 6 旧路由→3 条 leg-*, 北京-杭州低价 4487.8→1200 修正为去哪儿实抓值); ③ 精度缺口定向——仅有估算观测且无精准行的日期标记为缺口, probe_dates 优先补抓; CDP 覆盖提速: 节奏每 4 轮→每 2 轮, cdp_dates_per_round 2→4, cdp_capture_daily_cap 12→24; ④ main._cabin_absorb 统一 hid=leg-出发-目的 并按通道传 source(qunar/booking/amadeus), 历史/告警/推送 route_id 全走同一规范键, 双路由根除; ⑤ 前端时刻表价格格「精准/估算」徽章(按行 src)+腿卡「精准 x/y」chip, 样式全走设计令牌; ⑥ trains.py v1.30 多路线缓存收编——v4 缓存按路线分离, 双路线共存互不覆盖, from_cache 命中零重拉, force 旁路。tests/test_v130.py 28 断言+test_v127/test_cabin_monitor 同步更新全绿, acceptance 13/13。
v1.27 公务舱真浏览器精准采集(CDP cabin capture): Booking 无密钥网关连吃 429(streak 34+)期间公务舱缺口日无人补价——把 v1.22 验证过的真浏览器 CDP 管线对准去哪儿触屏版列表页, 实弹链路 筛选→舱位→公务/头等舱→确定 全部打通(2026-09-17 recon: 列表 207 卡, 过滤后 102 卡, MU5231 同航班 370→1075 经济/公务价差实锤, MU5138 07:00-09:10 ¥1290 入 point_fill 缓存带 cabin 标签)。① core/cdp_cabin.py: capture_batch 单浏览器批量靶点(启动成本摊薄), 四步点击+轮询式挖掘(单次 evaluate 会撞渲染竞态), 航班号+起降+含税总价三要素走 put_rows 原生 cabin/from/to 字段, 零新管线下轮巡逻自动吸收; 新 profile 首次播种 warm cookie(edge_prof_recon_warm), 登录墙显式检测; ② patrol_fill 钩子: 节流触发(n_thr>0)+每 4 轮保底+每日 12 日硬上限(cdp_cabin_ledger.json), main.py 巡检循环接入 /api/cabin 可观测; ③ tools/cdp_cabin_fill.py 运维 CLI(--dry/--smoke/--max), 实弹冒烟 19 秒落盘 1 行; ④ 精度说明: 触屏版报价=去哪儿页面实付价, 与 OTA 券后小偏差既有容忍策略不变。tests/test_v127.py 全绿。
v1.24 公务舱每日最低价趋势面板(daily-lowest trend): 公务舱标签页每条腿新增宽幅走势卡——SVG 渐变面积线(v0.28 令牌配色)+网格刻度+心理价位阈值虚线(cabin_watch.threshold_total 落在量程内才画)+最低/最高极值点+悬停十字线带日期价格读数, 数据复用 v1.12 spark 序列(每观测日最低含税总价, 近30观测日), 纯前端零新接口; history_timetable 行 tsrc 缺省时按时补 patrol 溯源(有时刻的行不再裸奔); tests/test_v124.py 5 断言(前端面/CSS 令牌/缓存版本/tsrc 缺省/版本号)全绿, 全量 474 OK。
v1.25 公务舱分线阈值(per-leg route_thresholds): 统一阈值 ¥1000 的产品死角——三条巡逻腿历史最低 1277/4488/1468 全在其上, 阈值提醒对它们永远不可能触发。① core/cabin_monitor.leg_threshold(cw,from,to) 纯函数: route_thresholds["出发>目的"] 精确命名该腿则压过全局 threshold_total, 键中文/IATA 双兼容(经 core.intl 归一, 北京>上海 ≡ BJS>SHA, 上海→SHA), 垃圾键值(非数字/非正/无>分隔)一律忽略回落; evaluate_alert 逐腿用各自生效线, 命中项带 threshold 字段。② main.py 推送: 新低路径与阈值命中路径的『已低于阈值/阈值 ¥t』全部改走 leg_threshold, 告别全局盲拍。③ webui: _validate_config 清洗 route_thresholds(正数+含>键保留); /api/cabin 给 timetable 每腿与 board 每行盖 threshold 戳。④ 前端双端: v1 走势图阈值线优先用该腿生效线+历史最低徽章旁『阈值 ¥x · 已达』chip; v2 CabinCard 编辑卡逐腿输入框(留空=用统一值), 概要行分线一览, 逐腿徽标判定改 r.threshold; vite 产物重打包。tests/test_v125.py 7 断言(精确/IATA/回落/垃圾/逐腿命中/校验清洗/版本钉)全绿, 全量 481 OK; test_v124 版本钉改版本无关(缓存破版本机制断言), 告别每轮升版误报。
v1.22 公务舱时刻携程板补全(ctrip CDP board): 上海板 timeDays=-1|0|1 物理够不到的远期缺口日与无机场板腿(重庆→上海整腿 20 号全缺)需要『按日期精查』数据源——大兴官网瑞数盾/首都机场无板/重庆板防护重定向/飞常准授权墙逐一实证排除后, 携程航班动态详情页 URL 直达任意未来日期(实测 HO1254@2026-10-25 大兴T2 21:25→浦东 23:40、3U8971@2026-10-16 江北T3 07:15→浦东T2 09:40, 数据源飞友)。本版: ① core/cdp_board.py 真实 Edge/Chrome CDP 拉起最小化有头窗口(无头被鲸护拦截), 单页加载提取 flight-detail-header 与 flight-detail-body-left 文本得航司/航班号/计划起降/城市/航站楼, 双航站楼布局按行归类; ② 红线与上海板一致: 6h 行缓存+24h 负缓存(仅成功渲染但无数据时落)+共享账本每日 24 次硬上限(失败也记账)+连续 2 失败熔断; ③ earliest-first 靶点, own-dow 沉淀(airport-board 权威/ctrip-detail 同层刷新); ④ patrol 挂 cdp_fill, /api/cabin 透出, 面板新增『携程板时刻』统计行。实弹 2 查 2 沉淀(CZ8879 12:00→14:10、HU7605 15:35→17:45)。tests/test_v122.py 13 断言全绿。
v1.21 公务舱时刻价值优先调度(sh board value-first slots): 上海板 timeDays 只认 -1|0|1, dow_targets 却按最早观测日排序让『天天飞缺 7 星期』的航班主任永占槽位, 缺口恰在今天/明天的航班(HO1258/1260 类)永远排队。本版: ① 缺口与今明窗口无交集的号直接跳过(查了也沉淀不了任何新行); ② 可完成(全部缺口都在窗口内)的号优先, 缺行少者优先, 腿覆盖向 8/8 收敛; ③ sh_fill 只查有用的偏移(目标缺星期∈该偏移日期的星期), 单轮预算减半; ④ stats.dow_new(从未实现)移除, 新增 filled(实际沉淀行数), 面板统计行『航班 N』改『时刻行 N』。tests/test_v121.py 8 断言全绿。
v1.20 公务舱时刻预算守护(sh board cap absolutism + neg cache): v1.19 实弹暴露越权——force=True 把每日硬上限也一起绕过, 账本烧到 32/24; CZ3440/CZ3480 这类板上查无此班的号继续每天占计划槽位。本版: ① fetch_flight 的每日 24 次上限改为绝对执行, force 只跳过 6h 行缓存(职责回归『刷新旧行』而非『超越预算』); ② success 空 flightList 落 24h 负缓存(fetch_flight 新增 neg-cache 返回, 零网络零预算), neg_hit 只读探针, dow 目标过滤掉双偏移全负的号——计划槽位让给真航班(MF8561/CZ8899 等), 起飞时刻覆盖持续提升; ③ /api/cabin 经 state 整体透传 sh_fill.neg, cabin 面板状态条新增上海板统计行(查询/精确回写/航班/负缓存/额度满/WAF 熔断)。tests/test_v120.py 7 断言全绿(上限绝对性/force 刷新语义/负缓存 TTL/槽位腾退/前端渲染)。
v1.19 公务舱时刻预算精准化(sh board budget precision): v1.18 上线后实弹复盘——CZ3440/CZ3480 在上海板两个方向×两个日期偏移全部 0 行(疑似共享号/非两场起降), 但每天仍烧 4 次查询额度; 北京→上海腿卡 2/8, 因 CZ8803 的 10 月缺口日期坐在 6h 缓存命中区里, 而 CZ8879(12:00 起飞班)先烧掉了预算——远期日期插队, 最近能买的票反而等不到时刻。本版: ① dow_targets 按最早观测日期升序排序(远期日期不再饿死近程缺口); ② sh_fill/fetch_flight 增加 force 透传, tools/sh_cabin_fill.py 加 --force 一次性绕过 6h 行缓存强制重查(仍受每日 24 次上限保护); ③ 跨零点到达标记——apply_exact_times 检测 arr<=dep 写 cross_day, cabin timetable 行投影透传, UI 行时刻带『+1』徽标(22:25–22:10 一眼看出次日落地, 非负时长); ④ v2 构建产物同步重打包。tests/test_v119.py 5 断言全绿。
v1.18 公务舱时刻接入上海机场官方板(sh board exact cabin times): 用户点名公务舱模块「尽快补齐, 数据源要求精准」——萧山机场板天然看不到上海两场起降的航班, Booking 网关又连日 429 封锁, 北京→上海腿时刻表长期 2/8 缺时刻。本版接入上海机场(集团)官网 Avinex 航班板(与浏览器同源, 零密钥零签名): ① core/sh_board.py fetch_flight——POST OldFlightHandler.aspx 按航班号+方向+日期偏移(-1|0|1)直查官方计划起降/城市/航司/候机楼, 6h 缓存+全局每日 24 次硬上限(与萧山板共用记账, 失败也烧额度); ② exact_targets 圈定今/明窗口缺时刻上海腿观测, apply_exact_times 按 日期×航班号×城市对 契约原地回写(tsrc=shanghai-board, 已有真实时刻永不覆盖); ③ merge_sh_rows 按自身星期沉淀进 flight_sched_db(萧山 airport-board 行保持权威只填空, 同 tier 最新覆盖), dow 补齐后查询自熄灭, 远期日期由 v1.16 借用契约消费; ④ cabin_patrol_once 每轮挂钩 sh_fill, /api/cabin 透出统计, UI 行带『上海板』徽标; ⑤ tools/sh_cabin_fill.py 一次性补查运维工具。实测 HO1254 相邻日期到达 23:35 vs 23:30——按日精确>按星期借用。tests/test_v118.py 16 断言全绿。
v1.17 公务舱推送带起降时刻(cabin push dep-arr times): 公务舱低价提醒此前只报『日期+价格』, 用户要求精准——推送必须直接看到航班号与起飞/落地时刻, 免去回面板二次确认。本版: ① core/cabin_monitor.py evaluate_alert 命中项透传 fno/dep/arr(历史记录早就存了, 只是没带给通知层); ② 新增 push_time_suffix(row, sched, from, to) 纯函数——时刻优先取观测自身, 缺时刻的行按 v1.16 三方精准契约(航班号+起飞星期+城市对)从排班库现场借用, 借不到返回空串保持干净文案, 共享号取首段, 'HHMM' 自动补冒号; ③ main.py _cabin_absorb 两条推送路径(历史新低/阈值)统一追加『· 航班号 HH:MM–HH:MM』后缀, 排班库仅在真正触发推送时懒加载(零推送零开销)。 tests/test_v117.py 7 用例: 自身时刻带航班号/缺时刻三方借用/城市不匹配拒绝/共享号首段/无可信时刻留空/阈值命中透传字段/端到端接线。
v1.16 公务舱时刻借用(cabin sched time borrow): 公务舱时刻表大量行长期『时刻待采』——timed 补全走 Booking 网关(连日 429 封锁), 而经济舱侧的零密钥排班库(机场板实测, 3768 班, 每天更新)早就存着同一批航班的星期制时刻。本版把库借过去: ① core/cabin_monitor.py borrow_sched_time(row, sched, from, to) 纯函数——fno+起飞星期+城市三方全匹配才填(不匹配不借, 精准优先), 共享航班号 'CZ3502/CZ2326' 取首段, 已有真实时刻的行永不覆盖, 命中行标 tsrc=sched-borrow; ② history_timetable/history_board 增加可选 sched 参数, top-N 行与历史最低行原地升级 dep/arr, leg 透出 borrowed 计数与 timed 复算; ③ webui /api/cabin 加载 flight_sched_db 传入两处投影; ④ UI: 借用行『借用』徽标(hover 说明来源与购票页为准), 卡片头部『时刻 x/y · 借用z』进度章。 tests/test_v116.py 10 断言: 三方匹配填时刻/城市不匹配拒绝/星期缺口拒绝/共享号首段/真实时刻不覆盖/时刻表借用+计数/无库保持原样/board 最低行升级/webui 接线/UI 徽标。
v1.07 周末班期自动平衡(dow balance): 机器周末关机导致板库永远缺周六/周日沉积(实测 dow0-4 各 3000+ 班, dow5=909, dow6=0), 周末出发的航班时刻长期停留在弱参考钉——而用户手动精点查询其实都能查到(精点端点一直活着)。core/dow_balance.py 每轮扫描统计七天覆盖, 发现饥饿星期(低于峰值 35% 或绝对地板 60)就对未来 +2..+25 天内最近的该星期日期, 走既有 fetch_lowest 报价管道拉一次当日全部 offers, 映射进 sched_deposit 队列并当场吸收——每行按自己的星期归档, 既有观察条目永远优先(absorb 只填空), 12h 轮节流 + 同 dow+日期+方向 7 天去重 + 异常全捕获绝不影响主扫描。周末出发的起飞时刻由此自动升级为 起飞·班期精查, 无需周末开机、无需新数据源。tests/test_v107.py 18 断言(检测/窗口日期/映射/吸收/不覆盖/节流/失败安全)全绿。
v1.06 多段联程首段时刻升级 + 航班号复用防护: 中转行程(如 CZ3502/CZ2326 杭州经广州转重庆)的起飞时刻此前沿用 Booking 同日最低价借用钉——可能是另一条行程的时刻, 实测偏差可达 5 小时以上(CZ3502 真实 18:35, 借用钉 13:10)。现在首段航班号+星期在出发机场板库精确命中时, apply_board_upgrade_first_leg 只升级起飞时刻为该班机真实时刻(起飞·班期精查 徽标), 落地与中转信息保持诚实标注(落地侧徽标独立)。同时修复航班号复用陷阱: SC2118 周三实为华夏航空 乌鲁木齐→杭州、其余天为山东航空 杭州→厦门, board_lookup_x 精确命中前先校验出发机场侧, 不同机场的同号班机不再误当同一班——快照里「中转 杭州」这类荒谬数据清零(实测 33 条 booking-x 多段行: 21 条升级首段精查、10 条降级跨日参考、2 条参考班次, 复用污染 0 残留)。tests/test_v106.py 14 断言 + 既有 test_sched_board 语义更新(经停宽容仅限到达侧)全绿。
v1.05 精点回填 v2: 书签抓全列表 + 日详情就地回填: 用户确认「灰色日期直接精点能查到数据」——qunar 日历服务端无缓存价(非售罄), 单日列表 API 被 Bella 签名+指纹风控拦(自动化三路皆堵: 无头/嵌入浏览器/CDP, 当日复测防御 JS 仍在), 真人浏览器是唯一通路, 于是把通路翻倍提效: ① bookmarklet v2 卡片级抓取——价格节点向上爬祖先找「航班号+HH:MM」最深容器, 每班航班号+起降时刻+价格全量入 rows(单日多班一次 POST, 上限12班, 抓不到降级 v1 最低价再降级手输); ② put_rows 同日多行取最低价入缓存槽(原语义后写覆盖, 高价会顶掉低价), 全量行照常喂班期沉积队列(一次精点教会全线当日时刻); ③ DayDetail 参考价日期(booking-ref/interp/nearby-ref)底部就地回填表单(付款价必填+航班号/起降选填, postPointFill 复用), 不用再跑去「数据抓取」页找日期; ④ CrawlView 书签文案升级 v2。新增 tests/test_v105.py 13 断言(最低价胜出/裸价口径/booking-ref让位/真价不动/已回填日期退出gap列表/书签ORIGIN绑定+卡片标记+node--check语法/前端接线)。
v1.04 真价行时刻升级: 精确板期压过 Booking 借用钉: 用户持续推进「页面要显示航班班次具体起飞时间」——实测发现 43 个 qunar 真价航班号里 42 个在 3758 班的机场班期库里有班期(GS6582/HU7421/3U2581 等 dow 0-4 全满), 但 booking_attach(v0.88) 先把同日 Booking 最便宜行程钉成 booking-x, 之后板库 attach 因「dep+arr 都已有值」被跳过, 真价行 94 天里 66 天一直卡在借用时刻。修复: ① core/sched_board.apply_board_upgrade 纯函数——仅当行是 booking-x 且板库精确命中(dow 命中, 机场维度唯一=同一物理航班)时, 用本航班号时刻覆写并清空 borrow 溯源, 价格与航班身份永不动; ② _enrich_flight_times 单段路径增加 bk_borrowed 升级分支, 跨星期板期(exact=False)永不覆写 booking-x(同日他航班仍优于错星期); ③ booking_fill EXACT_SOURCES 含 airport-board 合同上锁, 升级后下一轮 Booking 钉子自动让位, 无乒乓; ④ crawl step 文案带 (bk-upgrade xN)。预期一轮后真价行 dep_exact 0→约 66, 剩余 28 天=周五(库 909 班)/周日(0 班, 09-20 自愈)板库缺口。新增 tests/test_v104.py 5 断言(仅 booking-x 可升级/升级后三源改 airport-board+清 borrow/无 dep 不升级/合成库 exact vs 跨 dow 策略链/无乒乓合同)。
v1.03 时刻口径修正 + CDP 负结论归档: 用户确认「灰价日并非售罄, 精点有价」后复盘时刻统计, 发现快照 240 行中 144 行带 Booking 同日真实行程时刻(dep_src=booking: 重庆 23/郑州 43/曼谷 58/成都 20), 但 time_coverage 只认 amadeus/airport-board 为精确、又把 NON_REAL(booking-ref) 行整体排除出分母, 制造「精确时刻 0 天」假象; /api/time-coverage 同病——booking/booking-x/point-fill 全落 noref 灰格, 日历热力条 144 天显示「无时刻」。修复: ① EXACT_TIME_SOURCES 纳入 booking/point-fill, 纯函数 time_kind(dep_src) 统一分类(exact/borrow/alt/noref, booking-x 算借用), 前后端共用; ② time_coverage 新增 ref_total/ref_dep_exact/ref_dep_borrow/ref_dep_missing 参考行计数, 口径透明; ③ 日历图例改「当日真实时刻/参考借用时刻/无时刻·待精查」, 数据源页时刻沉淀卡新增参考行计数行; ④ CDP 自动精点负结论归档 point_fill.py——Chrome 136+ 默认 profile 忽略 --remote-debugging-port(接管真实 Chrome 不可能), 独立 profile+iPhone 模拟过登录墙但列表页软拦「没有查询到符合条件的航班」, Trip.com HTTP 000; ⑤ Booking 按日轮转健康实证(最近 run 探 0·缓 0·补价 23·补时刻覆盖全部灰价日), 真价自动补齐唯一待办仍是 Amadeus 免费密钥。新增 tests/test_v103.py(time_kind 五映射 + ref_* 计数三态)。
v1.02 自动精点引导 + 班期缺口周报: 用户确认「缺数据源日期直接精点能查到」后复盘发现两条补齐通道都没真正运转——Amadeus 轮转精点(v0.67)因密钥未配每轮空转 skip, 书签回填缓存 0 行从未用过, 于是 143 个灰点日只能等 Booking 参考价。本版把「配免费密钥即全自动补齐」做成显式引导: ① /api/point-gaps 新增 auto_fill 块(amadeus_ready/per_round=6/interval_min/eta_rounds/register_url), 精点面板横幅两种状态——未配密钥时直达 developer.amadeus.com/register 免费注册(个人邮箱即可, 之前卡住的是 enterprise 销售页)并给「配好后每轮自动精点 6 日」预期, 已配时显示激活态与补齐轮数预估(缺口最多路线 ceil(N/6) 轮); ② 数据源页 Amadeus 区新增三步注册指引 + 「测试密钥」一键校验按钮(调 /api/amadeus-test); ③ 周报正文末尾新增班期库沉淀行——sched_stats 统计 7 个 weekday 覆盖, 缺口日给出「常驻运行下 M/D 自动补齐」预测(板库每日沉淀当日+次日 dow), 本机实测「6/7 缺周日 → 09/20 自愈」; ④ build_weekly 加 data_dir 参数, webui 两个周报入口与 worker 周报推送全链路透传。新增 tests/test_v102.py 4 断言(auto_fill 契约 keyless/armed + dow 行三态: 无库诚实 0/7、全满沉默、周日缺给自愈日期)。
v1.01 灰点作战清单修复(booking-ref 误判): /api/point-gaps 的 gap_dates 只认 interp/nearby-ref 为缺价日——v0.87 引入 booking-ref 后, 交叉补价盖住的日期被误判为「已有真价」, 四条线路 143 个灰点日(杭州-重庆 23/郑州 41/曼谷 59/成都 20)在精点补查面板里显示「无缺价日」, 而这些日期 OTA 单日精查实际有价(服务端聚合日历无缓存价≠售罄)。修复: gap_dates 判定统一走 NON_REAL_SOURCES(interp/nearby-ref/booking-ref), 主爬虫 Booking 轮转不受影响(它用独立 covered 集合选日期, 零副作用)。实弹佐证: 内置嵌入式浏览器访问去哪儿单日列表也被软拦(返回「没有查询到符合条件的航班」), 印证 v0.76 侦察「无 key 自动精点不可行」——精点回填必须真人真浏览器(Chrome/手机书签), 修复后面板每个灰点日恢复「点日期直达精查→点书签自动回填」闭环。test_booking_fill 语义断言同步修正(booking-ref 日=靶子)。
免责声明
本项目仅聚合公开接口数据做个人出行比价提醒,不保证价格实时准确,不构成购票建议;购票请以航司/12306/平台下单页为准。请遵守各数据源服务条款,合理控制查询频率。
License
MIT © FareAlert contributors
This server cannot be deployed
Maintenance
Related MCP Connectors
Search award flights and cash fares, optimize points, and predict fares inside ChatGPT and Claude.
Google Flights search data: fares, routes, stops, and price insights via a hosted MCP server.
Flight search, airfare analytics, flexible destinations
Real-time Google Flights fares for agents. Three things people do with this server. Scan for deals: one call takes a date range and a list of destination airports, expands every combination server side, and returns each fare with Google's own low, typical or high verdict. Put live search in your app: flat JSON with a bookable link on every result, and round trips priced as paired legs. Run a 24/7 AI travel agent: add the server, sign in with Google, and schedule it. No ads, no sponsored content. You bring your own RapidAPI key, so every search is billed to your plan and never to anyone else's. Add https://flights.flightpowers.com/mcp , click Sign in, sign in with Google, and paste your RapidAPI key once on the page that opens. Scripts and clients without a sign-in button send the key as x-rapidapi-key on the same URL.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables travel search workflows including airport lookup, route comparison, travel timing guidance, and external booking links with commission-eligible links.561 npm1MIT
- FlicenseNot gradedqualityDmaintenanceProvides tools for searching flights, creating and canceling bookings, and retrieving user reservations through natural language.-
- FlicenseNot gradedqualityBmaintenanceEnables tracking flights and grouping them into trips, with tools to create, list, and manage trips and flight bookings.-
- FlicenseNot gradedqualityDmaintenanceEnables real-time flight and train ticket queries, dynamic trip tracking, and knowledge-based Q&A about rail and aviation policies.-