# 行程扭蛋

面向旅行途中动态切换的计划池工具：出发前准备正式、备用、雨天和跨城方案，旅行中按天气、闭馆、预约和体力状态切换。

## Metadata

- HTML: https://glenzli.com/projects/plan-gacha/
- Markdown: https://glenzli.com/projects/plan-gacha.md
- Collection: Projects
- Language: zh-CN
- Published: 2026-07-04
- Status: active
- Tags: travel, planner, weather, checklist, local-first

## Content

行程扭蛋是一个面向旅行途中的动态计划切换工具。它适合那种已经准备了 `n + m` 个候选行程，但每天还要根据天气、闭馆、预约、体力和临时变化重新决定去哪儿的场景。

它不是把旅行排成一条固定时间线，而是先建立一个计划池：正式行程、备用方案、雨天方案、跨城方案都放在一起。真正到旅行中，再按当天条件选择更合适的安排。当前版本是纯前端应用，数据默认保存在浏览器 `localStorage`，不依赖后端服务；部署宿主也可以选择注入远端存储适配器。

## 项目入口

  ![行程扭蛋项目 banner](/images/projects/plan-gacha-banner.png)

可以直接打开在线版本：[/apps/plan-gacha/](/apps/plan-gacha/?lang=zh)。

## 适合解决什么

    **出发前建立计划池**
    正式行程、备用方案、雨天方案、跨城方案可以先放在一起，不需要一开始就锁成单条时间线。

    **旅行中按天切换**
    看到明天天气、预约状态或体力变化后，可以快速换成更合适的安排。

    **避免计划崩掉**
    切换前提示日期限制、天气不合适、必去项未安排、预约订票未完成等问题。

    **给 AI 一个可执行格式**
    让 AI 生成、补充或重排行程，最后把结构化 JSON 粘贴回应用。

## 核心能力

      **多旅行计划管理**
      新建、删除、归档、恢复、导入、下载 JSON，也可以从完整示例快速体验。

    已归档旅行可以在编辑界面查看，但不会继续参与顶部计划切换，适合保留历史行程。

      **每日行程视图**
      移动端提供日期导航，桌面端提供左侧日期列表和中间详情。

    旅行中主要围绕当天操作：更新天气、查看候选、选择计划，再处理可能受到影响的后续日期。

      **行程生命周期与结束归档**
      优先打开进行中、即将出发或刚结束的旅行，并在行程结束后主动提醒归档。

    归档时可以选择记录币种、分类开销和简短感受；历史回看会展示总开销、分类明细和旅行总结。当前阶段没有相关旅行时，应用会进入明确的空状态，不会自动打开久远的旧计划。

      **候选计划池**
      每个计划可以包含多个地点、多个时间点、跨城市安排、提醒、tips 和预约订票信息。

    计划可以声明可去日期、不可用日期、天气黑名单、推荐天气、必去标记和已安排日期。

      **行程结果与历史边界**
      可以记录整天或单个地点本次未去，也可以把地点标记为以后不再安排。

    原计划仍保留在导出与只读归档中；已经过去并实际执行的计划不会在后续日期重复出现，回补历史记录时则可以从所选日期继续调整。

      **每日行程评价**
      已经执行的日期可以记录满意度、节奏、体力标签和简短备注。

    评价会绑定日期和当时采用的计划。之后重新安排同一天或更换计划，也不会覆盖原有反馈，适合在旅行结束后回看实际体验。

      **天气联动与约束判断**
      按计划中的城市、区县或坐标查询 Open-Meteo 天气，并基于计划规则判断是否适合。

    选择某个计划前，应用会评估它是否会影响后续安排，并把需要调整的天数留给用户重新决定。

      **预约订票与旅行清单**
      预约/门票状态由用户手动更新，旅行清单作为独立的长期清单维护。

    清单默认空白，可以手动编辑、填入示例或导入 JSON，支持勾选完成、本次不需要和一键重置本次状态。

## 使用方式

1. 新建旅行，设置开始日期和天数。
2. 从 `AI 生成计划池`、导入 JSON 或完整示例开始建立候选计划。
3. 出发前把必去、备用、雨天、跨城、预约订票、提醒和 tips 都放进计划池。
4. 旅行中打开当天页面，按天气、日期限制和已安排状态选择方案。
5. 如果选择会移动或清空已有安排，应用会先展示影响预览，再由用户确认。
6. 行程结束后归档旅行，并按需补充机票、住宿、交通、餐饮等开销与简短总结。

候选计划会分成三组：

- `可直接选择`：当前日期可用，且还没有安排在别天。
- `已安排，可移动`：已经排在其他日期，可以主动移动过来。
- `不可选`：日期不适合、当天不可用或闭馆等硬限制。

天气不合适不再直接阻止选择。它会用风险颜色提示，给用户保留临场判断空间；日期限制和闭馆仍然是硬限制。

## AI 工作流

应用不直接接入通用 AI API。核心原因是效果：旅行计划通常需要 AI 产品侧的一整套工具支持，例如地图、搜索和位置理解；如果只接入裸模型 API，效果反而容易变差。作为计划工具，行程扭蛋也没有必要内建一套复杂的 AI 周边体系。

当前 AI 相关功能采用「复制提示词 -> 外部 AI 生成 -> 粘贴 JSON」的方式：

- `AI 生成计划池`：适合从空计划开始生成完整计划，或给已有行程补充候选方案。
- `AI 编辑单项`：适合新增或修改一个具体计划。
- `AI 重排`：适合旅行途中根据剩余天数、天气和已去项目重新安排。

提示词会尽量只提供 AI 真正需要的信息：初始或增量规划会提供计划池 JSON 结构、已有计划 ID 和用户补充需求；单项编辑只提供当前计划或已有计划 ID；重排行程会提供固定日期、可调整日期、剩余计划、天气和预警，让 AI 输出可导入结果。

已经记录的每日评价会在 AI 重排和编辑时作为节奏、体力、交通与天气偏好的软参考，但不会自动变成地点黑名单或其他硬约束。

## 天气数据

天气查询使用免费的 [Open-Meteo](https://open-meteo.com/) 接口。应用会按地点和日期做本地缓存，避免短时间内重复请求同一个城市或区县的天气。

为了提升成功率，计划里的地点建议包含城市、区县或坐标；展示时可以只显示具体地点，但天气查询会优先使用更稳定的查询位置。

天气判断会结合天气类型、降水概率和计划的室内/室外属性：

- `31-50%` 降水概率：室外计划通常标为天气一般。
- `51%+` 降水概率：主打室外的计划标为天气不合适。
- `70%+` 降水概率：即使天气代码不明确，也会补进雨天风险判断。
- 大雨、雷雨：室外计划直接视为高风险；室内计划通常不受影响。

天气查询会优先使用 `location.weather_location`、城市、区县或坐标；如果只有景点名，查询可能失败或不稳定。

## 数据与导入导出

- 数据默认保存在浏览器 `localStorage`，没有内置账号系统或云服务；部署宿主可以选择提供下文所述的远端同步适配器。
- `127.0.0.1:5173` 和 `localhost:5173` 是不同浏览器 origin，数据不会互通。
- 旅行计划导出会直接下载 JSON，包含日期、住宿、计划池、日程、地点反馈、实际游玩结果、每日评价和归档总结；不包含旅行清单与天气缓存。导入支持粘贴 JSON 或选择本地 JSON 文件。
- 天气结果只作为本地缓存保存在浏览器里，不写入旅行导出 JSON 或远端同步文件。
- 旅行清单是全局独立数据，可以单独下载 JSON，之后可通过粘贴或选择文件导入，并选择替换或合并。
- 合并清单时，同分类同文本视为同一项；同文本不同分类会迁移可确定的状态，并提示冲突。
- 同文本多次出现或状态不一致时，不会自动覆盖当前清单，会提示用户整理后再导入。
- 当前同步文件 schema 为 `appSchemaVersion: "1.0"`；兼容字段新增、UI 调整和同步策略变化不升级 schema，只有破坏性数据结构变化才升级。

## 可选远端同步

行程扭蛋本身不绑定具体远端存储服务。部署环境可以注入一个最小 `driveStorage` 适配器，把旅行、日程、复盘状态和清单组成的完整工作区快照同步到远端 JSON 文件；天气缓存不会上传。

默认构建只在 URL 带 `?sync=1`，或本地已经记录过同步文件时显示同步入口；同时宿主必须实际提供 `driveStorage` API，否则入口不会展示。当前站点版本会通过外层页面提供这层能力，应用本体仍然保持纯前端。

首次同步会按文件名和应用标记查找 `plan-gacha.state.json`。本地为空时可以直接拉取远端；本地已有内容时会要求确认；找不到文件才会创建。发生冲突时可以选择远端覆盖、本地覆盖或自动合并；schema 不兼容、远端 JSON 无效，或者同一数据被两端同时修改时会明确报错。
