本文件用于帮助快速理解仓库结构与运行方式,便于开发、重构、测试与排障。
RoadbookMaker 是一个基于网页的地图标记与行程规划工具:
- 前端:
static/下的纯原生 JavaScript + Leaflet,直接在浏览器运行,无构建步骤。核心文件:static/index.html:入口 HTML。static/style.css:样式定义(含 CSS 变量实现的明亮/暗色模式)。static/script.js:RoadbookApp主入口与类骨架,保留全局配置、constructor、init()、bindEvents()、分享导入/移动端/调试等主流程。static/app_*.js:按职责拆分的前端模块,通过RoadbookApp.prototype扩展主类。当前包含app_utils.js、app_theme.js、app_history.js、app_search.js、app_ai.js、app_date_filter.js、app_date_notes.js、app_sidebar.js、app_detail_panels.js、app_tooltips.js、app_map.js、app_io.js。static/help_tour.js:新手引导(罩子高亮 + tooltip + 自动演示点/线/日程编辑)。static/online_mode.js:在线模式 API 交互。static/ai_assistant.js:AI 助手功能,实现自然语言驱动的地图操作。static/debug.js:调试模块,提供应用状态快照、日志捕获与环境信息查看(见第 10 节)。static/html_export.js:负责生成包含完整数据与交互逻辑的独立 HTML/TXT 导出文件。
- 后端:
backend/下的 Go + Gin API 服务(入口backend/cmd/roadbook-api/main.go),提供:- 在线模式(登录/JWT、计划 CRUD、分享读取)
- 地图搜索聚合与代理(
/api/cnmap/search、/api/tianmap/search) - AI 助手代理(
/api/v1/ai/*),负责与大语言模型交互 trafficpos等辅助 API(/api/trafficpos)
- 部署:Nginx 提供静态资源,并反向代理
/api/到后端(见nginx.prod.conf:25);Docker 镜像把前端 + 后端打包在一起(见Dockerfile、Dockerfile.local、docker-entrypoint.sh)。
在线模式数据持久化是“文件系统仓库”:计划以 data/<id>.json 形式写入磁盘(见 backend/internal/plan/repository.go:16)。
- 纯静态资源:直接打开
static/index.html或由 Nginx 提供。 - 前端对后端基址的判定:
localhost/127.0.0.1/file://时使用http://127.0.0.1:5436(见static/script.js:5)- 否则使用当前域名(见
static/script.js:8)
- 生成后端配置(交互式):
./scripts/generate_config.sh(写入backend/configs/config.json,见scripts/generate_config.sh:23) - 管理 Agent 软链接:
./scripts/link_agents.sh(用于同步.agent配置到.coco/.gemini等目录,见scripts/link_agents.sh) - 编译后端:
./backend/scripts/build.sh(产物:backend/roadbook-api,见backend/scripts/build.sh:34) - 启动后端:在
backend/目录执行./roadbook-api(README 与backend/cmd/roadbook-api/main.go:41)
- 从项目根目录构建本地镜像:
./build.sh(会先在backend/里go build再docker build -f./Dockerfile.local,见build.sh:16、build.sh:21) - 镜像运行示例:
docker run -d -p 5215:80 roadbook(见build.sh:26)
容器启动逻辑:docker-entrypoint.sh 会启动 Nginx,并在 /app 下启动后端二进制(见 docker-entrypoint.sh:4、docker-entrypoint.sh:7)。
- 模块:
module github.com/chenxuan520/roadmap/backend,go 1.18(见backend/go.mod:1、backend/go.mod:3)。 - 框架与库:Gin(HTTP)、
golang-jwt/jwt/v4(JWT)、google/uuid(计划ID)、golang.org/x/time/rate(限流)(见backend/go.mod:6-backend/go.mod:9)。 - 路由组织:在
backend/internal/server/server.go内组装 Gin Engine;/api/v1下是认证/计划接口,/api下保留搜索与其它公共接口(见backend/internal/server/server.go:65、backend/internal/server/server.go:97)。
- 无框架:主入口是
static/script.js,核心业务已拆到static/app_*.js;static/online_mode.js负责在线模式/云端保存/登录,static/ai_assistant.js负责 AI 助手。 - 前端模块约定:
script.js必须先于app_*.js加载,由script.js定义RoadbookApp,再由各模块扩展RoadbookApp.prototype。 - 若修改
index.html脚本顺序,必须保证window.app = new RoadbookApp()执行前,所有app_*.js已经完成注册。 - 新手引导:
static/help_tour.js(罩子高亮 + 步骤引导;入口在帮助面板❓内) - 调试逻辑独立于
static/debug.js,通过DebugModule类实现,仅在需要时被调用。 - 在线 token 存储:
localStorage的 key 为online_token(见static/online_mode.js:6)。 - 在线模式快捷键:在在线模式且已登录时,
Ctrl/Cmd+S触发saveToCloud()云端保存(见static/online_mode.js:41)。
- 命名约定:不要使用下划线
_开头命名“私有”函数、变量或绑定的事件处理器(推荐使用boundHandler形式),保持简洁。 - 注释约定:修改代码时,必须保留原有的关键注释(特别是数据结构定义、业务逻辑说明等),除非该逻辑已被完全移除。
- 语法检查:每次修改代码后,必须自行检查语法,确保代码可编译/可运行。
- Go 后端:需要检查 unused imports 等编译错误。
- 前端 JavaScript:必须使用
node -c <filename>.js命令检查语法。
- 变量作用域检查:在重构代码(特别是移动代码块)时,必须严格检查变量的作用域(Block Scope)。
- 错误案例:将定义在
try块内部的const变量(如assistantMsg)引用的代码移动到try...finally块外部,导致 ReferenceError。 - 避免方法:移动代码前,先确认所引用的变量在目标位置是否可见。如果需要在块外使用,请将变量声明提升到块外(使用
let)。
- 错误案例:将定义在
本仓库未提供统一格式化/校验工具配置文件(例如 ESLint/Prettier/Go fmt hook),修改时建议保持周边代码的现有缩进与命名风格。
- 计划仓库测试:
backend/internal/plan/repository_test.go(覆盖 Save/FindAll/Delete 等;测试会把dataDir指向临时目录,见backend/internal/plan/repository_test.go:14-backend/internal/plan/repository_test.go:34)。 - 限流器惰性清理测试:
backend/internal/middleware/middleware_test.go(覆盖 TTL/清理间隔/阈值等,见backend/internal/middleware/middleware_test.go:22)。
运行方式(在 backend/ 下):go test ./...。
backend/scripts/backend_test.sh 会:
- 生成临时
backend/configs/config.json(包含jwtSecret与用户 salted SHA256 hash) - 编译并启动后端,然后用
curl/jq走一组 API(含登录、计划 CRUD、分享)
该脚本依赖:openssl、sha256sum、jq、curl(见 backend/scripts/backend_test.sh:47、backend/scripts/backend_test.sh:102、backend/scripts/backend_test.sh:115)。
前端 E2E 测试位于 test/,通过一个轻量静态服务器加载 static/index.html 做 UI 交互验证(测试用例会 mock 搜索等外部请求,避免依赖后端)。
- 本地运行(不下载浏览器内核,复用系统 Chrome):在仓库根目录执行
cd test && npm run setup:local && npm run test:local - CI 运行(runner 上安装 Chromium 后 headless 跑):
cd test && npm run setup:ci && npm run test:ci(见/.github/workflows/ci_frontend.yml:1)
更多说明见 test/README.md。
- 后端必须配置
jwtSecret且用户列表不能为空(见backend/internal/config/config.go:42-backend/internal/config/config.go:50)。 scripts/generate_config.sh会用openssl rand生成jwtSecret,并对密码做 salted SHA256(见scripts/generate_config.sh:71、scripts/generate_config.sh:80)。
- JWT 认证通过
Authorization: Bearer <token>传递(见backend/internal/middleware/middleware.go:90-backend/internal/middleware/middleware.go:112)。所有/api/v1下(除登录和分享外)的接口,包括计划管理和 AI 助手,都需要认证。 /api/v1/login有 IP 限流中间件(见backend/internal/server/server.go:69与backend/internal/middleware/middleware.go:69)。- 计划文件仓库包含路径遍历防御:对
id做filepath.Base(id) == id校验(见backend/internal/plan/repository.go:54、backend/internal/plan/repository.go:76、backend/internal/plan/repository.go:148)。
- 分享接口无需认证:
GET /api/v1/share/plans/:id(见backend/internal/server/server.go:71-backend/internal/server/server.go:75)。 - Nginx 仅将
/api/反代到后端(见nginx.prod.conf:25),前端静态文件直接由 Nginx 读取。
- 离线数据保存在浏览器
localStorage(例如路书数据与在线 token)。共享电脑使用时需注意清理。
- 默认读取路径:运行目录下的
configs/config.json(见backend/internal/config/config.go:26)。 - 生成位置:项目根目录脚本写入
backend/configs/config.json(见scripts/generate_config.sh:23、scripts/generate_config.sh:114)。 - 字段:
port、allowed_origins、allow_null_origin_for_dev、jwtSecret、users、search、ai(见backend/internal/config/config.go:15-backend/internal/config/config.go:23)。 - AI 配置 (
ai字段):enabled: 是否启用 AI 助手。base_url: AI 服务提供商的 Base URL(兼容 OpenAI 格式)。key: API Key。model: 模型名称。
- 搜索配置 (
search字段):providers: 目前支持gaode(高德地图),需配置key。
Gin 内置了一个“允许来源列表 + 可选 null origin”逻辑:
allowed_origins命中时回写Access-Control-Allow-Origin等头- 若
allow_null_origin_for_dev=true且Origin: null,允许用于file://本地开发测试
见 backend/internal/server/server.go:19-backend/internal/server/server.go:41。
- 后端计划文件目录是相对路径
data/(见backend/internal/plan/repository.go:17)。 - Docker 镜像会创建
/app/data并将后端工作目录设置为/app(见Dockerfile:35、docker-entrypoint.sh:7),保证相对路径落在容器内可写目录。
cloudflare/ 目录包含一个完整的 Cloudflare Worker 实现,可作为 Go 后端(backend/)的 Serverless 替代方案。
- 功能范围:覆盖 Go 后端核心能力(JWT 认证、计划 CRUD、操作式地图编辑 API、搜索代理、KV 数据存储),并支持 Cloudflare Workers AI。
- 部署与使用:详情见
cloudflare/README.md。 - 切换方式:前端通过双击标题配置 BaseURL,可切换到 Worker 后端。
- 对齐要求(重要):
cloudflare/worker.js必须与backend/保持严格的功能对齐。- 任何对 Go 后端 API 的修改(路径、请求参数、返回 JSON 结构、字段命名、错误码),都必须同步修改
cloudflare/worker.js。 - 本地 Agent 或外部自动化编辑地图时,优先使用
POST /api/v1/plans/:id/map/actions,不要让 Agent 用 GET 后手工改content再 PUT 的方式重写整包数据。 - KV 存储的 JSON 结构尽量与 Go 后端文件存储兼容(CamelCase 字段名),方便潜在迁移。
TrafficPos经纬度顺序(机场:lat,lon;火车站:lon,lat)已对齐;修改时必须保持一致。
- Git 写操作:除非用户明确要求,否则严禁自动执行
git add/git commit/git push等。 - 推送后流水线监控(强制):一旦按用户明确要求执行
git push,必须立即监控该 push 触发的所有 GitHub Actions。使用gh run list/gh run view --log-failed定位失败 job,修复并重新推送,直到该 commit 或修复 commit 对应的相关流水线全部成功。不得在未确认流水线状态的情况下报告“完成”。 - Plan Mode 限制:严禁使用
ExitPlanMode工具;按用户指令直接执行。 - 重置/回滚限制(重要):任何
reset/checkout/restore/ “还原文件”等操作,只允许回滚 我本次会话里明确修改过的文件;涉及到非我修改的文件,除非用户明确点名要求,否则一律禁止重置。
- 给 AI 的失败信息必须包含可定位上下文,避免只返回“为空/失败”等不可操作描述。
- 至少包含:
action名称 + 关键参数(例如date)+ 关键内容截断预览(例如notePreview)+(如适用)失败分支原因(如“日期不在行程中/ID 无效/参数缺失”)。 - 示例:
更新日期备注失败: 参数缺失(date="2025-01-01", note=undefined)/Action Failed: update_date_note - date not in itinerary. date=2025-01-01, notePreview=...
- 除非用户明确要求,否则禁止修改
backend/configs/config.json(该文件常被用作本地测试配置,误改会影响开发者环境)。
- 任何前端行为变更(按钮/弹窗/交互流程/快捷键/删除-撤销语义/DOM id 或 class/文案影响选择器等)必须同步:
- 新增或更新 Playwright E2E 用例(
test/tests/*.spec.mjs),并确保选择器稳定(优先使用 id)。 - 若变更影响调试模式、导入导出、在线模式等关键路径,必须补充对应覆盖用例。
- 新增或更新 Playwright E2E 用例(
- 默认只运行新增/修改的那几个用例文件,不要一次性跑全量(除非用户明确要求)。
- 推荐命令:
cd test && npm run test:local -- tests/<your-new>.spec.mjs - 修改前端 JS 后必须做语法检查:
for f in static/script.js static/app_*.js static/html_export.js; do node -c "$f"; done
- 代码组织:
static/script.js现在主要承担主入口与类骨架职责。新增前端逻辑时,优先放入最相关的现有static/app_*.js模块;只有在职责边界明确且现有模块都不合适时,才新增新的模块文件。 - 入口约束:
static/script.js顶部仍承载apiBaseUrl与版本号初始化,且 GitHub Pages 工作流会向该文件首行注入window.API_BASE_URL;不要把这段入口逻辑移走。 - 加载约束: 若新增新的
static/app_*.js文件,必须同步更新static/index.html、static/sw.js与.github/workflows/ci_frontend.yml。
项目内置调试模式,由 static/debug.js 实现,方便查看应用内部状态并导出现场信息。
- 激活方式:URL 添加查询参数
?debug=true(例如:http://localhost:5215/?debug=true)。 - 入口:地图右下角控件区(帮助按钮
❓旁边)出现🐞按钮。 - 核心功能 (
DebugModule):- 环境嗅探:收集 UA、屏幕尺寸、时区、网络状态等。
- 状态快照:完整抓取
app.markers、app.connections、localStorage等数据。 - 日志捕获:Hook
console与window.onerror,记录最近 200 条日志与 50 条错误(仅在 debug 模式下生效)。 - 存储分析:统计
localStorage各 key 占用大小,帮助排查配额问题。
- 交互:调试窗口支持关键词过滤、JSON 导出下载、一键复制全部信息。
RoadbookMaker 实现了 Progressive Web App (PWA) 支持,提供类原生应用体验与离线访问能力。
static/manifest.json:Web App Manifest,定义应用名称、图标、主题色及显示模式(Standalone)。static/sw.js:Service Worker 脚本。导航请求走Network First,静态资源走Stale-While-Revalidate。- 预缓存列表:
index.html,style.css,script.js及所有核心 JS 模块。 - 运行时缓存:同源静态资源与
unpkgCDN 资源按需写入运行时缓存,不再因为外部 CDN 预缓存失败而让安装整体失败。 - 更新机制:缓存名由资源清单 + 手动 schema 版本共同生成,
activate时仅清理旧的roadbook-*缓存。
- 预缓存列表:
- 安装提示:在
static/script.js中监听beforeinstallprompt事件,拦截默认提示,为自定义安装引导预留接口(当前实现为拦截后保存事件,等待后续扩展 UI 触发)。 - 更新检测:每次页面加载时会自动注册/更新 Service Worker。