# Echo Nexus · 云排行榜 mini-service 回响星核 / Echo Nexus 的 P2 云排行榜服务(v0.8.2 / Task 10-b)。 把信标本机排行榜(`localStorage`)升级为云端 Top100,玩家间可比。 ## 技术栈 - **运行时**:Bun(原生 TypeScript,无需编译) - **框架**:Hono(轻量 web 框架,bun 原生支持) - **存储**:内存数组,重启清空(够用,符合 P2 规格) - **端口**:固定 `3030`(Caddy 通过 `?XTransformPort=3030` 转发) ## 目录结构 ``` mini-services/leaderboard-service/ ├── package.json # 独立 bun 项目 ├── index.ts # Hono 入口 └── README.md ``` ## API 所有响应自动 CORS 开放,OPTIONS 预检由 Hono 自动处理。 | 方法 | 路径 | 入参 | 返回 | |---|---|---|---| | GET | `/` | — | `{ service, version, ok, uptime }` 健康检查 | | GET | `/api/leaderboard` | — | `{ entries: BeaconScoreEntry[], total }` Top100 | | POST | `/api/leaderboard` | body `{ entry: BeaconScoreEntry }` | `{ entries, total, rank }` rank=1..N 或 -1 | | GET | `/api/leaderboard/stats` | — | `{ totalSubmissions, uniquePlayers, topScore }` | ### `BeaconScoreEntry` 结构 ```ts { timestamp: number, // 提交时间戳 dateKey: string, // YYYY-MM-DD 或 YYYY-Www challenge: string, // decode | expedition | pulse | boss | insight difficulty: string, // routine | anomaly | singular progress: number, // 0-1,1 = 完成 score: number, // 最终得分 durationSec: number, // 完成时长(秒) isWeekly?: boolean, // 周挑战记录 isTimed?: boolean // 限时挑战记录(预留) } ``` ## 存储 & 防刷规则 - **容量**:最多 1000 条,按分数降序;超过自动丢弃尾部。 - **Top 榜**:API 返回前 100 条。 - **同分排序**:先看用时短,再看提交早。 - **防刷**:同 `dateKey + challenge` 10 秒内只接受 1 次提交,返回 `429`。 ## 启动 ```bash cd mini-services/leaderboard-service bun run dev # 等价于 bun --hot index.ts,文件变更自动重启 ``` ## 联调(前端请求规范) 前端必须用相对路径 + `?XTransformPort=3030`: ```ts // 拉取全球 Top100 const res = await fetch("/api/leaderboard?XTransformPort=3030"); const { entries, total } = await res.json(); // 提交一条记录 const res = await fetch("/api/leaderboard?XTransformPort=3030", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ entry }), }); const { entries, total, rank } = await res.json(); ``` **禁止** `fetch("http://localhost:3030/...")` —— 不通过 Caddy 网关,会被浏览器同源策略拦截。 ## 部署形态 独立 bun 项目,与主 Next.js 项目解耦: - 主项目端口 3000(Next.js dev) - 本服务端口 3030(Hono + Bun) - Caddy 网关端口 81(按 `?XTransformPort=3030` 转发到本服务) 服务重启会清空数据,符合 P2 阶段规格(不需要持久化)。