Add backend foundation and config-driven workbench
This commit is contained in:
54
backend/docs/README.md
Normal file
54
backend/docs/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Backend Docs
|
||||
|
||||
这套文档服务两个目的:
|
||||
|
||||
1. 让后面开发时能快速查到当前后端边界
|
||||
2. 把“配置驱动游戏”的核心约束写清楚,避免业务层和游戏层重新耦合
|
||||
|
||||
## 建议阅读顺序
|
||||
|
||||
1. [系统架构](D:/dev/cmr-mini/backend/docs/系统架构.md)
|
||||
2. [核心流程](D:/dev/cmr-mini/backend/docs/核心流程.md)
|
||||
3. [API 清单](D:/dev/cmr-mini/backend/docs/接口清单.md)
|
||||
4. [数据模型](D:/dev/cmr-mini/backend/docs/数据模型.md)
|
||||
5. [配置管理方案](D:/dev/cmr-mini/backend/docs/配置管理方案.md)
|
||||
6. [前后端联调清单](D:/dev/cmr-mini/backend/docs/前后端联调清单.md)
|
||||
7. [TodoList](D:/dev/cmr-mini/backend/docs/todolist.md)
|
||||
8. [开发说明](D:/dev/cmr-mini/backend/docs/开发说明.md)
|
||||
|
||||
## 当前系统范围
|
||||
|
||||
当前 backend 已覆盖:
|
||||
|
||||
- 多租户入口识别
|
||||
- APP 短信登录
|
||||
- 微信小程序登录
|
||||
- 手机号绑定与账号合并
|
||||
- 首页卡片与入口聚合
|
||||
- Event 详情与 play 上下文
|
||||
- 以 `event_release` 为核心的 launch
|
||||
- session 生命周期
|
||||
- session 结果沉淀
|
||||
- 开发 workbench
|
||||
|
||||
下一阶段建议重点:
|
||||
|
||||
- 可伸缩配置管理
|
||||
- source/build/release 分层
|
||||
- 配置构建器
|
||||
- 发布资产清单
|
||||
|
||||
## 当前最重要的设计约束
|
||||
|
||||
- 用户是平台级,不是俱乐部级
|
||||
- 渠道是入口,不是用户体系
|
||||
- `event` 是业务对象,不是运行配置本体
|
||||
- `event_release` 才是进入游戏时真正绑定的配置发布对象
|
||||
- `game_session` 必须固化当时实际使用的 release
|
||||
|
||||
## 代码入口
|
||||
|
||||
- 程序入口:[main.go](D:/dev/cmr-mini/backend/cmd/api/main.go)
|
||||
- 应用装配:[app.go](D:/dev/cmr-mini/backend/internal/app/app.go)
|
||||
- 路由注册:[router.go](D:/dev/cmr-mini/backend/internal/httpapi/router.go)
|
||||
- migration:[migrations](D:/dev/cmr-mini/backend/migrations)
|
||||
255
backend/docs/todolist.md
Normal file
255
backend/docs/todolist.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# Backend TodoList
|
||||
|
||||
## 1. 目标
|
||||
|
||||
这份 TodoList 只列当前需要 backend 配合联调和近期应推进的事项。
|
||||
|
||||
原则:
|
||||
|
||||
- 不重复写已经稳定可用的能力
|
||||
- 优先写会影响前后端联调闭环的点
|
||||
- 边界不清的事项单独标记“需确认”
|
||||
|
||||
## 2. 当前联调现状
|
||||
|
||||
当前已经可联调的主链:
|
||||
|
||||
- 微信小程序登录
|
||||
- `GET /events/{eventPublicID}/play`
|
||||
- `POST /events/{eventPublicID}/launch`
|
||||
- `POST /sessions/{sessionPublicID}/start`
|
||||
- `POST /sessions/{sessionPublicID}/finish`
|
||||
- `GET /sessions/{sessionPublicID}/result`
|
||||
|
||||
小程序侧已经具备:
|
||||
|
||||
- backend 地址和 token 持久化
|
||||
- `launch -> GameLaunchEnvelope` 适配
|
||||
- 进入地图后自动上报 `session start`
|
||||
- 对局结束后自动上报 `session finish`
|
||||
|
||||
所以 backend 现在最重要的不是再扩散接口,而是把当前契约和语义收稳。
|
||||
|
||||
## 3. P0 必做
|
||||
|
||||
## 3.1 固定 session 状态语义
|
||||
|
||||
需要 backend 明确并固定:
|
||||
|
||||
- `finished`
|
||||
- `failed`
|
||||
- `cancelled`
|
||||
|
||||
建议当前口径:
|
||||
|
||||
- 正常打终点完成:`finished`
|
||||
- 超时结束:`failed`
|
||||
- 主动退出 / 放弃恢复:`cancelled`
|
||||
|
||||
说明:
|
||||
|
||||
- 小程序现在已经按这个方向接
|
||||
- 如果 backend 想改这 3 个状态语义,需要先讨论,不要单边改
|
||||
|
||||
## 3.2 明确“放弃恢复”的后端处理
|
||||
|
||||
这是当前最值得后端配合确认的一点。
|
||||
|
||||
当前小程序本地恢复逻辑已经是:
|
||||
|
||||
- 进入程序检测到未正常结束对局
|
||||
- 弹确认框
|
||||
- 玩家可“继续恢复”或“放弃”
|
||||
|
||||
现在本地“放弃”只会清除本地恢复快照。
|
||||
|
||||
backend 需要确认的目标语义是:
|
||||
|
||||
> 玩家点击“放弃恢复”后,这一局是否应同时在业务后端标记为 `cancelled`。
|
||||
|
||||
我建议 backend 采用:
|
||||
|
||||
- **是,应标记为 `cancelled`**
|
||||
|
||||
原因:
|
||||
|
||||
- 否则 `ongoingSession` 会继续存在
|
||||
- `/events/{id}/play` 和 `/me/entry-home` 可能一直把它当成可继续的局
|
||||
- 会和小程序本地“已放弃”产生语义分叉
|
||||
|
||||
建议 backend 配合确认:
|
||||
|
||||
1. `POST /sessions/{id}/finish` 使用 `status=cancelled` 是否就是官方放弃语义
|
||||
2. 如果客户端持有旧 `sessionToken`,恢复放弃时是否允许直接调用 `finish(cancelled)`
|
||||
3. `cancelled` 后,`event play` 和 `entry-home` 中不再返回为 `ongoingSession`
|
||||
|
||||
备注:
|
||||
|
||||
- 如果 backend 认可这套语义,小程序侧下一步就可以把“点击放弃恢复”改成同步调用 `finish(cancelled)`。
|
||||
|
||||
## 3.3 保证 start / finish 幂等与重复调用安全
|
||||
|
||||
联调和真实环境里,以下情况很常见:
|
||||
|
||||
- 网络重试
|
||||
- 页面重进
|
||||
- 故障恢复后二次补报
|
||||
- 用户重复点击
|
||||
|
||||
backend 需要确认:
|
||||
|
||||
- `start` 重复调用的幂等语义
|
||||
- `finish` 重复调用的幂等语义
|
||||
|
||||
建议:
|
||||
|
||||
- `start`:如果已 `running`,返回当前 session,视为成功
|
||||
- `finish`:如果已进入终态,返回当前 session/result,视为成功
|
||||
|
||||
目的:
|
||||
|
||||
- 不把客户端补偿逻辑变成一堆冲突分支
|
||||
|
||||
## 3.4 固定 `launch` 返回契约,不随意漂移
|
||||
|
||||
当前客户端已经按下面这些字段接入:
|
||||
|
||||
- `launch.resolvedRelease.releaseId`
|
||||
- `launch.resolvedRelease.manifestUrl`
|
||||
- `launch.resolvedRelease.manifestChecksumSha256`
|
||||
- `launch.config.configUrl`
|
||||
- `launch.config.configLabel`
|
||||
- `launch.config.releaseId`
|
||||
- `launch.config.routeCode`
|
||||
- `launch.business.sessionId`
|
||||
- `launch.business.sessionToken`
|
||||
- `launch.business.sessionTokenExpiresAt`
|
||||
|
||||
backend 现在需要做的是:
|
||||
|
||||
- 先保持这些字段名稳定
|
||||
- 如果要调整命名或层级,先沟通
|
||||
|
||||
## 4. P1 应尽快做
|
||||
|
||||
## 4.1 增加用户身体资料读取接口
|
||||
|
||||
小程序侧已经有:
|
||||
|
||||
- telemetry profile 合并入口
|
||||
- 心率/卡路里计算逻辑
|
||||
|
||||
backend 下一步建议提供:
|
||||
|
||||
- 当前用户 body profile 查询接口
|
||||
|
||||
建议返回至少包含:
|
||||
|
||||
- `birthDate` 或 `heartRateAge`
|
||||
- `weightKg`
|
||||
- `restingHeartRateBpm`
|
||||
- `maxHeartRateBpm`(可选)
|
||||
|
||||
这样后面心率页和消耗估算就能真实接业务数据。
|
||||
|
||||
## 4.2 给 `session result` 补一点稳定摘要字段校验
|
||||
|
||||
客户端现在会上报:
|
||||
|
||||
- `finalDurationSec`
|
||||
- `finalScore`
|
||||
- `completedControls`
|
||||
- `totalControls`
|
||||
- `distanceMeters`
|
||||
- `averageSpeedKmh`
|
||||
|
||||
backend 建议补两件事:
|
||||
|
||||
- 合理性校验
|
||||
- 空值容忍
|
||||
|
||||
不要因为某个可选字段缺失就整局 finish 失败。
|
||||
|
||||
## 4.3 dev workbench 增加一组“恢复 / 取消恢复”场景按钮
|
||||
|
||||
当前 workbench 已经很好用了。
|
||||
|
||||
建议后续再补:
|
||||
|
||||
- 标记 session 为 `cancelled`
|
||||
- 查询 ongoing session
|
||||
- 快速查看某个用户最新 session 状态
|
||||
|
||||
这会很适合配合小程序故障恢复联调。
|
||||
|
||||
## 5. P2 下一阶段
|
||||
|
||||
## 5.1 配置后台 source / build / release 真正开始做
|
||||
|
||||
当前已经有:
|
||||
|
||||
- 表结构
|
||||
- 架构文档
|
||||
|
||||
还缺:
|
||||
|
||||
- source CRUD
|
||||
- build 触发
|
||||
- manifest 产物生成
|
||||
- release 发布
|
||||
- asset index 查询
|
||||
|
||||
这个建议在当前主链联稳之后再推进。
|
||||
|
||||
## 5.2 page / cards / competition 等业务对象继续长出来
|
||||
|
||||
这部分不是当前联调阻塞项,但后面会成为业务壳的重要组成。
|
||||
|
||||
## 6. 需要先讨论再动的边界
|
||||
|
||||
这些事项 backend 不建议自己先拍板:
|
||||
|
||||
### 6.1 `failed` 是否专指超时
|
||||
|
||||
当前建议是:
|
||||
|
||||
- 超时 -> `failed`
|
||||
- 主动退出 / 放弃恢复 -> `cancelled`
|
||||
|
||||
如果 backend 有别的语义方案,需要先统一。
|
||||
|
||||
### 6.2 放弃恢复是否一定写后端
|
||||
|
||||
我个人建议写后端,并落成 `cancelled`。
|
||||
|
||||
但如果 backend 团队认为:
|
||||
|
||||
- 放弃恢复只影响本地
|
||||
- 业务上仍允许以后继续从服务端 ongoing session 恢复
|
||||
|
||||
那就必须明确告知客户端,不然两边会冲突。
|
||||
|
||||
### 6.3 result 页是以后继续本地展示,还是跳业务结果页
|
||||
|
||||
当前客户端是本地结果页。
|
||||
|
||||
backend 后面如果要接业务结果页,最好提前定:
|
||||
|
||||
- finish 成功后是否仍停留地图内结果页
|
||||
- 还是跳业务壳结果页
|
||||
|
||||
## 7. 我建议的最近动作
|
||||
|
||||
backend 现在最值得先做的,不是扩接口,而是先确认下面 3 条:
|
||||
|
||||
1. `finished / failed / cancelled` 三态语义
|
||||
2. 放弃恢复是否写 `cancelled`
|
||||
3. `start / finish` 是否按幂等处理
|
||||
|
||||
这 3 条一旦确定,前后端联调会顺很多。
|
||||
|
||||
## 8. 一句话结论
|
||||
|
||||
当前 backend 最重要的任务不是“再加更多接口”,而是:
|
||||
|
||||
> 先把 session 运行态语义和故障恢复放弃语义定稳,再继续扩后台配置系统。
|
||||
369
backend/docs/前后端联调清单.md
Normal file
369
backend/docs/前后端联调清单.md
Normal file
@@ -0,0 +1,369 @@
|
||||
# 前后端联调清单
|
||||
|
||||
## 1. 目的
|
||||
|
||||
这份清单只回答三件事:
|
||||
|
||||
1. 小程序当前已经具备哪些接后端的前置能力
|
||||
2. backend 当前已经提供了哪些可联调接口
|
||||
3. 哪些链路已经能接,哪些链路还缺适配
|
||||
|
||||
本文不讨论未来大而全后台方案,只服务当前联调落地。
|
||||
|
||||
## 2. 当前结论
|
||||
|
||||
当前状态可以概括成一句话:
|
||||
|
||||
> backend 业务主链已经可联调;小程序地图运行内核也已经成型;两边之间还缺一层业务接入和会话上报适配。
|
||||
|
||||
也就是说:
|
||||
|
||||
- 登录、活动详情、launch、session、result 这一条后端链已经可用
|
||||
- 小程序地图页已经支持携带 `configUrl / releaseId / sessionId / sessionToken`
|
||||
- 但小程序当前仍主要走本地 demo / 直连 OSS manifest
|
||||
- 真正的“后端 launch -> 地图页 -> session start/finish/result”还没有正式接上
|
||||
|
||||
## 3. 小程序当前已具备的联调基础
|
||||
|
||||
## 3.1 启动信封已经成型
|
||||
|
||||
地图页不是只吃一个 `configUrl`,而是吃一份启动信封:
|
||||
|
||||
- [gameLaunch.ts](D:/dev/cmr-mini/miniprogram/utils/gameLaunch.ts)
|
||||
|
||||
当前结构:
|
||||
|
||||
- `config.configUrl`
|
||||
- `config.configLabel`
|
||||
- `config.configChecksumSha256`
|
||||
- `config.releaseId`
|
||||
- `config.routeCode`
|
||||
- `business.source`
|
||||
- `business.competitionId`
|
||||
- `business.eventId`
|
||||
- `business.launchRequestId`
|
||||
- `business.participantId`
|
||||
- `business.sessionId`
|
||||
- `business.sessionToken`
|
||||
- `business.sessionTokenExpiresAt`
|
||||
- `business.realtimeEndpoint`
|
||||
- `business.realtimeToken`
|
||||
|
||||
这意味着:
|
||||
|
||||
- backend `launch` 返回的数据结构已经能自然装进小程序地图启动链
|
||||
- 地图页并不需要重构启动模型,只需要把业务页接到 `GameLaunchEnvelope`
|
||||
|
||||
## 3.2 地图页已经支持远端 manifest 启动
|
||||
|
||||
- [map.ts](D:/dev/cmr-mini/miniprogram/pages/map/map.ts)
|
||||
|
||||
当前地图页会:
|
||||
|
||||
1. 解析 `GameLaunchEnvelope`
|
||||
2. 调 `loadRemoteMapConfig(configUrl)`
|
||||
3. 编译 runtime profile
|
||||
4. 启动 `MapEngine`
|
||||
|
||||
所以只要后端能给出:
|
||||
|
||||
- `manifestUrl`
|
||||
- `releaseId`
|
||||
- `configChecksumSha256`
|
||||
|
||||
地图页就可以直接跑。
|
||||
|
||||
## 3.3 会话态字段已经进入地图页
|
||||
|
||||
地图页当前已经能接收并持有:
|
||||
|
||||
- `sessionId`
|
||||
- `sessionToken`
|
||||
- `sessionTokenExpiresAt`
|
||||
|
||||
这说明后面接:
|
||||
|
||||
- `POST /sessions/{id}/start`
|
||||
- `POST /sessions/{id}/finish`
|
||||
|
||||
不需要再改地图启动协议。
|
||||
|
||||
## 3.4 故障恢复也已经具备会话上下文承载
|
||||
|
||||
故障恢复快照当前会保留:
|
||||
|
||||
- `launchEnvelope`
|
||||
- 运行态快照
|
||||
|
||||
这意味着一旦接入后端 session 后,恢复链也可以继续沿用同一份 `launchEnvelope`。
|
||||
|
||||
## 4. backend 当前已具备的联调基础
|
||||
|
||||
## 4.1 路由主链已落地
|
||||
|
||||
- [router.go](D:/dev/cmr-mini/backend/internal/httpapi/router.go)
|
||||
|
||||
当前已实现:
|
||||
|
||||
- `POST /auth/login/wechat-mini`
|
||||
- `GET /me/entry-home`
|
||||
- `GET /events/{eventPublicID}/play`
|
||||
- `POST /events/{eventPublicID}/launch`
|
||||
- `POST /sessions/{sessionPublicID}/start`
|
||||
- `POST /sessions/{sessionPublicID}/finish`
|
||||
- `GET /sessions/{sessionPublicID}/result`
|
||||
- `GET /me/results`
|
||||
|
||||
## 4.2 launch 返回结构已贴近客户端
|
||||
|
||||
- [核心流程.md](D:/dev/cmr-mini/backend/docs/核心流程.md)
|
||||
- [接口清单.md](D:/dev/cmr-mini/backend/docs/接口清单.md)
|
||||
|
||||
当前 `launch` 返回重点:
|
||||
|
||||
- `launch.resolvedRelease.releaseId`
|
||||
- `launch.resolvedRelease.manifestUrl`
|
||||
- `launch.resolvedRelease.manifestChecksumSha256`
|
||||
- `launch.business.sessionId`
|
||||
- `launch.business.sessionToken`
|
||||
|
||||
这和小程序 `GameLaunchEnvelope` 基本是同一语义。
|
||||
|
||||
## 4.3 session 运行态和结果态已分离
|
||||
|
||||
- [session_service.go](D:/dev/cmr-mini/backend/internal/service/session_service.go)
|
||||
|
||||
当前已经区分:
|
||||
|
||||
- 业务登录态:`access_token`
|
||||
- 局内运行态:`sessionToken`
|
||||
|
||||
这对地图页是对的,因为地图页真正需要的是:
|
||||
|
||||
- 进入前有业务 token
|
||||
- 进入后局内动作用 sessionToken
|
||||
|
||||
## 4.4 开发 workbench 已可用于联调
|
||||
|
||||
- [dev_handler.go](D:/dev/cmr-mini/backend/internal/httpapi/handlers/dev_handler.go)
|
||||
|
||||
当前 workbench 已能串:
|
||||
|
||||
- bootstrap
|
||||
- auth
|
||||
- entry/home
|
||||
- event play / launch
|
||||
- session start / finish / detail
|
||||
- result 查询
|
||||
|
||||
这对前后端联调非常有价值,说明后端已经不是“只看文档”阶段。
|
||||
|
||||
## 5. 当前已经能接的链路
|
||||
|
||||
## 5.1 P0:登录与业务页前置链
|
||||
|
||||
可接:
|
||||
|
||||
1. 小程序 `wx.login`
|
||||
2. `POST /auth/login/wechat-mini`
|
||||
3. 拿到 `accessToken`
|
||||
4. 调 `GET /me/entry-home`
|
||||
5. 调 `GET /events/{eventPublicID}/play`
|
||||
|
||||
当前缺口:
|
||||
|
||||
- 小程序还没有正式业务页 API 适配层
|
||||
- 还没有统一 token 持久化与请求封装
|
||||
|
||||
## 5.2 P0:launch 进入地图
|
||||
|
||||
可接:
|
||||
|
||||
1. 前置业务页拿到 event play
|
||||
2. 调 `POST /events/{eventPublicID}/launch`
|
||||
3. 把返回结果映射成 `GameLaunchEnvelope`
|
||||
4. `navigateTo('/pages/map/map?...')`
|
||||
|
||||
当前缺口:
|
||||
|
||||
- 还没有一层 `backend launch -> GameLaunchEnvelope` 的适配函数
|
||||
- 当前 `gameLaunch.ts` 仍偏 demo/static config 驱动
|
||||
|
||||
## 5.3 P0:finish 回传结果
|
||||
|
||||
可接:
|
||||
|
||||
1. 地图页结束一局
|
||||
2. 提取结果摘要
|
||||
3. 用 `sessionId + sessionToken` 调 `POST /sessions/{id}/finish`
|
||||
4. 业务页或结果页再查 `GET /sessions/{id}/result`
|
||||
|
||||
当前缺口:
|
||||
|
||||
- 小程序本地结果页已经有摘要,但还没有正式调用 backend finish
|
||||
- finish payload 和本地 `resultSummary` 之间还需要一层映射
|
||||
|
||||
## 6. 当前还不能说已经接通的链路
|
||||
|
||||
## 6.1 配置后台 source/build/release
|
||||
|
||||
backend 当前已经有:
|
||||
|
||||
- 表结构
|
||||
- 文档模型
|
||||
|
||||
但还没有真正开放:
|
||||
|
||||
- `config source`
|
||||
- `build`
|
||||
- `release assets`
|
||||
- `preview launch`
|
||||
|
||||
也就是说:
|
||||
|
||||
**配置后台链还不能联调,只能联业务主链。**
|
||||
|
||||
## 6.2 body profile / 遥测个体化
|
||||
|
||||
小程序已经有:
|
||||
|
||||
- 身体数据入口
|
||||
- 遥测 runtime profile
|
||||
|
||||
backend 文档里也规划了:
|
||||
|
||||
- 用户身体资料
|
||||
|
||||
但当前接口清单里还没有明确的 body profile 读接口落到小程序链上,所以这条还不能算当前联调主线。
|
||||
|
||||
## 7. 当前最大的接口适配缺口
|
||||
|
||||
我认为目前最大缺口只有 4 个:
|
||||
|
||||
### 7.1 业务 API 客户端缺失
|
||||
|
||||
小程序当前缺:
|
||||
|
||||
- 统一 `request` 封装
|
||||
- token 持久化
|
||||
- access token 刷新
|
||||
- backend DTO -> 小程序 view model 适配
|
||||
|
||||
### 7.2 launch 适配层缺失
|
||||
|
||||
需要一层明确的转换:
|
||||
|
||||
`LaunchResponse -> GameLaunchEnvelope`
|
||||
|
||||
这里最适合单独做成一个小模块,而不是散落在页面里。
|
||||
|
||||
### 7.3 session finish 映射缺失
|
||||
|
||||
地图页当前本地已经有:
|
||||
|
||||
- 用时
|
||||
- 分数
|
||||
- 完成点数
|
||||
- 里程
|
||||
- 速度
|
||||
- 最大心率
|
||||
|
||||
但还没有一个稳定函数把它映射为 backend finish payload。
|
||||
|
||||
### 7.4 业务结果页与地图结果页还未打通
|
||||
|
||||
现在地图页已经有自己的结果页。
|
||||
|
||||
后面要决定:
|
||||
|
||||
- 地图页结果页先本地展示,再异步回传
|
||||
- 还是 finish 成功后跳业务结果页
|
||||
|
||||
这件事需要前后端统一策略。
|
||||
|
||||
## 8. 推荐联调顺序
|
||||
|
||||
建议按下面顺序推进,不要跳步:
|
||||
|
||||
### 第一步:接微信小程序登录
|
||||
|
||||
目标:
|
||||
|
||||
- 小程序拿到 `accessToken`
|
||||
- 能请求鉴权接口
|
||||
|
||||
### 第二步:接 event play
|
||||
|
||||
目标:
|
||||
|
||||
- 小程序业务页能拿到:
|
||||
- `event`
|
||||
- `resolvedRelease`
|
||||
- `play.canLaunch`
|
||||
- `play.ongoingSession`
|
||||
|
||||
### 第三步:接 launch -> map
|
||||
|
||||
目标:
|
||||
|
||||
- 从后端 launch 返回直接进入地图
|
||||
- 不再靠 demo preset 手工切配置
|
||||
|
||||
### 第四步:接 start / finish / result
|
||||
|
||||
目标:
|
||||
|
||||
- 开赛后能回传 start
|
||||
- 结束后能回传 finish
|
||||
- 结果页能查 backend result
|
||||
|
||||
### 第五步:再考虑 ongoing session 恢复
|
||||
|
||||
目标:
|
||||
|
||||
- backend ongoing session
|
||||
- 本地故障恢复
|
||||
|
||||
两条链统一口径
|
||||
|
||||
## 9. 当前已落地的小程序联调适配
|
||||
|
||||
小程序侧当前已经补了第一批适配层:
|
||||
|
||||
- [backendAuth.ts](D:/dev/cmr-mini/miniprogram/utils/backendAuth.ts)
|
||||
- [backendApi.ts](D:/dev/cmr-mini/miniprogram/utils/backendApi.ts)
|
||||
- [backendLaunchAdapter.ts](D:/dev/cmr-mini/miniprogram/utils/backendLaunchAdapter.ts)
|
||||
- [index.ts](D:/dev/cmr-mini/miniprogram/pages/index/index.ts)
|
||||
|
||||
当前已具备:
|
||||
|
||||
- 后端 base URL 本地持久化
|
||||
- access / refresh token 本地持久化
|
||||
- 微信小程序登录请求封装
|
||||
- `event play` 请求封装
|
||||
- `launch -> GameLaunchEnvelope` 适配
|
||||
- 从首页直接 `launch` 进入地图
|
||||
- 地图页 `session start / finish` 上报接入
|
||||
|
||||
因此当前主链已从“可分析”进入“可实测”。
|
||||
|
||||
## 10. 我建议的最近行动项
|
||||
|
||||
如果开始联调,我建议先做这 3 件事:
|
||||
|
||||
1. 新增小程序 `backendApi` 请求层
|
||||
先只包 auth / event play / launch / session finish
|
||||
|
||||
2. 新增 `launchAdapter`
|
||||
把 backend launch 响应稳定转成 `GameLaunchEnvelope`
|
||||
|
||||
3. 新增 `finishAdapter`
|
||||
把地图页结果摘要稳定转成 backend finish payload
|
||||
|
||||
这三件做完,前后端主链就能真正接起来。
|
||||
|
||||
## 11. 一句话结论
|
||||
|
||||
当前最真实的进度判断是:
|
||||
|
||||
> backend 业务后端主链已经进入可联调阶段;小程序地图运行内核也已经具备承接能力;下一步最值钱的是补小程序业务 API 层和 launch/finish 两个适配器。
|
||||
182
backend/docs/开发说明.md
Normal file
182
backend/docs/开发说明.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# 开发说明
|
||||
|
||||
## 1. 环境变量
|
||||
|
||||
参考 [`.env.example`](D:/dev/cmr-mini/backend/.env.example)。
|
||||
|
||||
当前最关键的变量:
|
||||
|
||||
- `APP_ENV`
|
||||
- `HTTP_ADDR`
|
||||
- `DATABASE_URL`
|
||||
- `JWT_ACCESS_SECRET`
|
||||
- `AUTH_SMS_PROVIDER`
|
||||
- `AUTH_DEV_SMS_CODE`
|
||||
- `WECHAT_MINI_APP_ID`
|
||||
- `WECHAT_MINI_APP_SECRET`
|
||||
- `WECHAT_MINI_DEV_PREFIX`
|
||||
- `LOCAL_EVENT_DIR`
|
||||
- `ASSET_BASE_URL`
|
||||
|
||||
## 2. 本地启动
|
||||
|
||||
```powershell
|
||||
cd D:\dev\cmr-mini\backend
|
||||
go run .\cmd\api
|
||||
```
|
||||
|
||||
如果你想固定跑开发工作台常用端口 `18090`,直接执行:
|
||||
|
||||
```powershell
|
||||
cd D:\dev\cmr-mini\backend
|
||||
.\scripts\start-dev.ps1
|
||||
```
|
||||
|
||||
默认会设置:
|
||||
|
||||
- `APP_ENV=development`
|
||||
- `HTTP_ADDR=:18090`
|
||||
- `DATABASE_URL=postgres://postgres:asdf*123@192.168.100.77:5432/cmr20260401?sslmode=disable`
|
||||
- `AUTH_SMS_PROVIDER=console`
|
||||
- `WECHAT_MINI_DEV_PREFIX=dev-`
|
||||
|
||||
启动后可直接打开:
|
||||
|
||||
- [http://127.0.0.1:18090/dev/workbench](http://127.0.0.1:18090/dev/workbench)
|
||||
|
||||
## 3. 当前开发约定
|
||||
|
||||
### 3.1 开发阶段先不用 Redis
|
||||
|
||||
当前第一版全部依赖:
|
||||
|
||||
- PostgreSQL
|
||||
- JWT
|
||||
- refresh token 持久化
|
||||
|
||||
Redis 后面只在需要性能优化、限流或短期票据缓存时再接。
|
||||
|
||||
### 3.2 开发环境短信
|
||||
|
||||
当前默认可走 `console` provider。
|
||||
|
||||
用途:
|
||||
|
||||
- 本地联调无需接真实短信供应商
|
||||
|
||||
### 3.3 微信小程序开发态
|
||||
|
||||
当前支持 `dev-` 前缀 code。
|
||||
|
||||
适合:
|
||||
|
||||
- 后端联调
|
||||
- workbench 快速验证
|
||||
|
||||
### 3.4 本地配置目录
|
||||
|
||||
当前支持从根目录 [event](D:/dev/cmr-mini/event) 导入本地配置文件。
|
||||
|
||||
相关环境变量:
|
||||
|
||||
- `LOCAL_EVENT_DIR`
|
||||
- `ASSET_BASE_URL`
|
||||
|
||||
作用:
|
||||
|
||||
- `LOCAL_EVENT_DIR` 决定本地 source config 从哪里读
|
||||
- `ASSET_BASE_URL` 决定 preview build 时如何把相对资源路径归一化成可运行 URL
|
||||
|
||||
## 4. Migration
|
||||
|
||||
当前 migration 文件在 [migrations](D:/dev/cmr-mini/backend/migrations)。
|
||||
|
||||
执行原则:
|
||||
|
||||
1. 按编号顺序执行
|
||||
2. schema 变更只通过新增 migration 完成
|
||||
3. 不直接改线上已执行 migration
|
||||
|
||||
## 5. 开发工作台
|
||||
|
||||
### `POST /dev/bootstrap-demo`
|
||||
|
||||
它会保证 demo 数据存在:
|
||||
|
||||
- `tenant_demo`
|
||||
- `mini-demo`
|
||||
- `evt_demo_001`
|
||||
- `rel_demo_001`
|
||||
- `card_demo_001`
|
||||
|
||||
### `GET /dev/workbench`
|
||||
|
||||
这是当前最重要的联调工具。
|
||||
|
||||
可以直接测试:
|
||||
|
||||
- 登录
|
||||
- 入口解析
|
||||
- 首页聚合
|
||||
- event play
|
||||
- 配置导入、preview build、publish build
|
||||
- launch
|
||||
- session start / finish
|
||||
- result
|
||||
- profile
|
||||
|
||||
并且支持:
|
||||
|
||||
- quick flow
|
||||
- scenario 保存/导入/导出
|
||||
- curl 导出
|
||||
- request history
|
||||
|
||||
## 6. 当前推荐联调顺序
|
||||
|
||||
### 场景一:小程序快速进入
|
||||
|
||||
1. `bootstrap-demo`
|
||||
2. `login/wechat-mini`
|
||||
3. `me/entry-home`
|
||||
4. `events/{id}/play`
|
||||
5. `events/{id}/launch`
|
||||
6. `sessions/{id}/start`
|
||||
7. `sessions/{id}/finish`
|
||||
8. `sessions/{id}/result`
|
||||
|
||||
### 场景二:APP 主身份
|
||||
|
||||
1. `auth/sms/send`
|
||||
2. `auth/login/sms`
|
||||
3. `me/entry-home`
|
||||
4. `launch`
|
||||
5. `session`
|
||||
6. `result`
|
||||
|
||||
### 场景三:微信轻账号绑定手机号
|
||||
|
||||
1. `login/wechat-mini`
|
||||
2. `auth/sms/send` with `scene=bind_mobile`
|
||||
3. `auth/bind/mobile`
|
||||
4. `me/profile`
|
||||
|
||||
### 场景四:配置发布到可启动 release
|
||||
|
||||
1. `bootstrap-demo`
|
||||
2. `dev/events/{eventPublicID}/config-sources/import-local`
|
||||
3. `dev/config-builds/preview`
|
||||
4. `dev/config-builds/publish`
|
||||
5. `events/{id}`
|
||||
6. `events/{id}/launch`
|
||||
|
||||
## 7. 当前后续开发建议
|
||||
|
||||
文档整理完之后,后面建议按这个顺序继续:
|
||||
|
||||
1. 抽出更通用的 `play context -> launch` 模型
|
||||
2. 补赛事与报名层
|
||||
3. 补页面配置和白标首页
|
||||
4. 再考虑实时网关票据
|
||||
|
||||
不要跳回去把玩法规则塞进 backend。
|
||||
425
backend/docs/接口清单.md
Normal file
425
backend/docs/接口清单.md
Normal file
@@ -0,0 +1,425 @@
|
||||
# API 清单
|
||||
|
||||
本文档只记录当前 backend 已实现接口,不写未来规划接口。
|
||||
|
||||
## 1. Health
|
||||
|
||||
### `GET /healthz`
|
||||
|
||||
用途:
|
||||
|
||||
- 健康检查
|
||||
|
||||
## 2. Auth
|
||||
|
||||
### `POST /auth/sms/send`
|
||||
|
||||
用途:
|
||||
|
||||
- 发登录验证码
|
||||
- 发绑定手机号验证码
|
||||
|
||||
核心参数:
|
||||
|
||||
- `countryCode`
|
||||
- `mobile`
|
||||
- `clientType`
|
||||
- `deviceKey`
|
||||
- `scene`
|
||||
|
||||
### `POST /auth/login/sms`
|
||||
|
||||
用途:
|
||||
|
||||
- APP 手机号验证码登录
|
||||
|
||||
返回重点:
|
||||
|
||||
- `user`
|
||||
- `tokens.accessToken`
|
||||
- `tokens.refreshToken`
|
||||
|
||||
### `POST /auth/login/wechat-mini`
|
||||
|
||||
用途:
|
||||
|
||||
- 微信小程序登录
|
||||
|
||||
开发态:
|
||||
|
||||
- 支持 `dev-` 前缀 code
|
||||
|
||||
### `POST /auth/bind/mobile`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 已登录用户绑定手机号
|
||||
- 必要时执行账号合并
|
||||
|
||||
### `POST /auth/refresh`
|
||||
|
||||
用途:
|
||||
|
||||
- 刷新 access token
|
||||
|
||||
### `POST /auth/logout`
|
||||
|
||||
用途:
|
||||
|
||||
- 撤销 refresh token
|
||||
|
||||
## 3. Entry / Home
|
||||
|
||||
### `GET /entry/resolve`
|
||||
|
||||
用途:
|
||||
|
||||
- 解析当前入口归属哪个 tenant / channel
|
||||
|
||||
查询参数:
|
||||
|
||||
- `channelCode`
|
||||
- `channelType`
|
||||
- `platformAppId`
|
||||
- `tenantCode`
|
||||
|
||||
### `GET /home`
|
||||
|
||||
用途:
|
||||
|
||||
- 返回入口首页卡片
|
||||
|
||||
### `GET /cards`
|
||||
|
||||
用途:
|
||||
|
||||
- 只返回卡片列表
|
||||
|
||||
### `GET /me/entry-home`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 首页聚合接口
|
||||
|
||||
返回重点:
|
||||
|
||||
- `user`
|
||||
- `tenant`
|
||||
- `channel`
|
||||
- `cards`
|
||||
- `ongoingSession`
|
||||
- `recentSession`
|
||||
|
||||
## 4. Event
|
||||
|
||||
### `GET /events/{eventPublicID}`
|
||||
|
||||
用途:
|
||||
|
||||
- Event 详情
|
||||
|
||||
返回重点:
|
||||
|
||||
- `event`
|
||||
- `release`
|
||||
- `resolvedRelease`
|
||||
|
||||
### `GET /events/{eventPublicID}/play`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 活动详情页 / 开始前准备页聚合
|
||||
|
||||
返回重点:
|
||||
|
||||
- `event`
|
||||
- `release`
|
||||
- `resolvedRelease`
|
||||
- `play.canLaunch`
|
||||
- `play.primaryAction`
|
||||
- `play.launchSource`
|
||||
- `play.ongoingSession`
|
||||
- `play.recentSession`
|
||||
|
||||
### `POST /events/{eventPublicID}/launch`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 基于当前 event 的可启动 release 创建一局 session
|
||||
|
||||
请求体重点:
|
||||
|
||||
- `releaseId`
|
||||
- `clientType`
|
||||
- `deviceKey`
|
||||
|
||||
返回重点:
|
||||
|
||||
- `launch.source`
|
||||
- `launch.resolvedRelease`
|
||||
- `launch.config`
|
||||
- `launch.business.sessionId`
|
||||
- `launch.business.sessionToken`
|
||||
|
||||
### `GET /events/{eventPublicID}/config-sources`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 查看某个 event 的 source config 列表
|
||||
|
||||
### `GET /config-sources/{sourceID}`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 查看单条 source config 明细
|
||||
|
||||
### `GET /config-builds/{buildID}`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 查看单次 build 明细
|
||||
|
||||
## 5. Session
|
||||
|
||||
### `GET /sessions/{sessionPublicID}`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 查询一局详情
|
||||
|
||||
返回重点:
|
||||
|
||||
- `session`
|
||||
- `event`
|
||||
- `resolvedRelease`
|
||||
|
||||
### `POST /sessions/{sessionPublicID}/start`
|
||||
|
||||
鉴权:
|
||||
|
||||
- `sessionToken`
|
||||
|
||||
用途:
|
||||
|
||||
- 将 session 从 `launched` 推进到 `running`
|
||||
|
||||
### `POST /sessions/{sessionPublicID}/finish`
|
||||
|
||||
鉴权:
|
||||
|
||||
- `sessionToken`
|
||||
|
||||
用途:
|
||||
|
||||
- 结束一局
|
||||
- 同时沉淀结果摘要
|
||||
|
||||
请求体重点:
|
||||
|
||||
- `sessionToken`
|
||||
- `status`
|
||||
- `summary.finalDurationSec`
|
||||
- `summary.finalScore`
|
||||
- `summary.completedControls`
|
||||
- `summary.totalControls`
|
||||
- `summary.distanceMeters`
|
||||
- `summary.averageSpeedKmh`
|
||||
- `summary.maxHeartRateBpm`
|
||||
|
||||
### `GET /me/sessions`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 查询用户最近 session
|
||||
|
||||
## 6. Result
|
||||
|
||||
### `GET /sessions/{sessionPublicID}/result`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 查询单局结果页数据
|
||||
|
||||
返回重点:
|
||||
|
||||
- `session`
|
||||
- `result`
|
||||
|
||||
`session` 中会带:
|
||||
|
||||
- `releaseId`
|
||||
- `configLabel`
|
||||
|
||||
### `GET /me/results`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 查询用户最近结果列表
|
||||
|
||||
## 7. Profile
|
||||
|
||||
### `GET /me`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- 当前用户基础信息
|
||||
|
||||
### `GET /me/profile`
|
||||
|
||||
鉴权:
|
||||
|
||||
- Bearer token
|
||||
|
||||
用途:
|
||||
|
||||
- “我的页”聚合接口
|
||||
|
||||
返回重点:
|
||||
|
||||
- `user`
|
||||
- `bindings`
|
||||
- `recentSessions`
|
||||
|
||||
## 8. Dev
|
||||
|
||||
### `POST /dev/bootstrap-demo`
|
||||
|
||||
环境:
|
||||
|
||||
- 仅 non-production
|
||||
|
||||
用途:
|
||||
|
||||
- 自动准备 demo tenant / channel / event / release / card
|
||||
|
||||
### `GET /dev/workbench`
|
||||
|
||||
环境:
|
||||
|
||||
- 仅 non-production
|
||||
|
||||
用途:
|
||||
|
||||
- 后端自带 API 测试面板
|
||||
|
||||
当前支持:
|
||||
|
||||
- bootstrap
|
||||
- auth
|
||||
- entry/home
|
||||
- event/play/launch
|
||||
- session start/finish/detail
|
||||
- result 查询
|
||||
- profile 查询
|
||||
- quick flows
|
||||
- scenarios
|
||||
- request history
|
||||
- curl 导出
|
||||
|
||||
### `GET /dev/config/local-files`
|
||||
|
||||
环境:
|
||||
|
||||
- 仅 non-production
|
||||
|
||||
用途:
|
||||
|
||||
- 列出本地配置目录中的 JSON 文件
|
||||
|
||||
### `POST /dev/events/{eventPublicID}/config-sources/import-local`
|
||||
|
||||
环境:
|
||||
|
||||
- 仅 non-production
|
||||
|
||||
用途:
|
||||
|
||||
- 从本地配置目录导入 source config
|
||||
|
||||
请求体重点:
|
||||
|
||||
- `fileName`
|
||||
- `notes`
|
||||
|
||||
### `POST /dev/config-builds/preview`
|
||||
|
||||
环境:
|
||||
|
||||
- 仅 non-production
|
||||
|
||||
用途:
|
||||
|
||||
- 基于 source config 生成 preview build
|
||||
|
||||
请求体重点:
|
||||
|
||||
- `sourceId`
|
||||
|
||||
### `POST /dev/config-builds/publish`
|
||||
|
||||
环境:
|
||||
|
||||
- 仅 non-production
|
||||
|
||||
用途:
|
||||
|
||||
- 将成功的 preview build 发布成正式 release
|
||||
- 自动切换 `event.current_release_id`
|
||||
|
||||
请求体重点:
|
||||
|
||||
- `buildId`
|
||||
|
||||
返回重点:
|
||||
|
||||
- `release.releaseId`
|
||||
- `release.manifestUrl`
|
||||
- `release.configLabel`
|
||||
171
backend/docs/数据模型.md
Normal file
171
backend/docs/数据模型.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# 数据模型
|
||||
|
||||
当前 migration 共 5 版。
|
||||
|
||||
## 1. 迁移清单
|
||||
|
||||
- [0001_init.sql](D:/dev/cmr-mini/backend/migrations/0001_init.sql)
|
||||
- [0002_launch.sql](D:/dev/cmr-mini/backend/migrations/0002_launch.sql)
|
||||
- [0003_home.sql](D:/dev/cmr-mini/backend/migrations/0003_home.sql)
|
||||
- [0004_results.sql](D:/dev/cmr-mini/backend/migrations/0004_results.sql)
|
||||
- [0005_config_pipeline.sql](D:/dev/cmr-mini/backend/migrations/0005_config_pipeline.sql)
|
||||
|
||||
## 2. 表分组
|
||||
|
||||
### 2.1 多租户与入口
|
||||
|
||||
- `tenants`
|
||||
- `entry_channels`
|
||||
|
||||
职责:
|
||||
|
||||
- 识别品牌壳
|
||||
- 识别渠道入口
|
||||
- 承接后续俱乐部 / 政府公众号 / H5 / 二维码入口
|
||||
|
||||
### 2.2 用户与登录
|
||||
|
||||
- `users`
|
||||
- `login_identities`
|
||||
- `auth_sms_codes`
|
||||
- `auth_refresh_tokens`
|
||||
|
||||
职责:
|
||||
|
||||
- 平台级用户
|
||||
- 多身份登录
|
||||
- 验证码记录
|
||||
- refresh token 持久化
|
||||
|
||||
当前身份示例:
|
||||
|
||||
- `mobile`
|
||||
- `wechat_mini_openid`
|
||||
- `wechat_unionid`
|
||||
|
||||
### 2.3 业务对象与配置发布
|
||||
|
||||
- `events`
|
||||
- `event_releases`
|
||||
|
||||
职责分工:
|
||||
|
||||
- `events` 管业务对象身份和展示
|
||||
- `event_releases` 管发布后的运行配置入口
|
||||
|
||||
关键字段:
|
||||
|
||||
- `events.current_release_id`
|
||||
- `event_releases.release_public_id`
|
||||
- `event_releases.config_label`
|
||||
- `event_releases.manifest_url`
|
||||
- `event_releases.manifest_checksum_sha256`
|
||||
- `event_releases.route_code`
|
||||
|
||||
### 2.4 首页与入口卡片
|
||||
|
||||
- `cards`
|
||||
|
||||
职责:
|
||||
|
||||
- 支撑首页卡片
|
||||
- 运营入口聚合
|
||||
- tenant/channel 维度展示控制
|
||||
|
||||
### 2.5 运行态
|
||||
|
||||
- `game_sessions`
|
||||
- `session_results`
|
||||
|
||||
职责:
|
||||
|
||||
- 固化一局游戏
|
||||
- 固化该局绑定的 release
|
||||
- 固化局后结果摘要
|
||||
|
||||
### 2.6 配置构建与发布资产
|
||||
|
||||
- `event_config_sources`
|
||||
- `event_config_builds`
|
||||
- `event_release_assets`
|
||||
|
||||
职责:
|
||||
|
||||
- 保存编辑态 source config
|
||||
- 保存构建后的 manifest 和 asset index
|
||||
- 保存正式 release 关联的资产清单
|
||||
|
||||
## 3. 当前最关键的关系
|
||||
|
||||
### `tenant -> entry_channel`
|
||||
|
||||
一个 tenant 下可有多个渠道入口。
|
||||
|
||||
### `user -> login_identity`
|
||||
|
||||
一个平台用户可绑定多个登录身份。
|
||||
|
||||
### `event -> event_release`
|
||||
|
||||
一个 event 可有多个 release。
|
||||
|
||||
客户端真正进入游戏时,最终会消费其中一份 release 的 manifest。
|
||||
|
||||
### `event_release -> game_session`
|
||||
|
||||
一局 session 必须绑定一份明确的 release。
|
||||
|
||||
这是当前系统最关键的配置驱动约束。
|
||||
|
||||
### `game_session -> session_result`
|
||||
|
||||
一局结束后可有一条结果摘要。
|
||||
|
||||
### `event_config_source -> event_config_build -> event_release`
|
||||
|
||||
这是后续配置生命周期主链:
|
||||
|
||||
- source 是编辑态
|
||||
- build 是构建态
|
||||
- release 是发布态
|
||||
|
||||
## 4. 当前已落库但仍应注意的边界
|
||||
|
||||
### 4.1 不要把玩法细节塞回事件主表
|
||||
|
||||
当前数据库只记录:
|
||||
|
||||
- 发布关系
|
||||
- manifest 入口
|
||||
- 结果摘要
|
||||
|
||||
玩法解释器仍应留在游戏客户端。
|
||||
|
||||
### 4.2 不要让历史局跟随当前 release 漂移
|
||||
|
||||
即使 event 后面发布新版本:
|
||||
|
||||
- 旧 session 仍然指向旧 `event_release_id`
|
||||
- 旧 result 仍然对应旧 release
|
||||
|
||||
### 4.3 不要把登录态和运行态混在一起
|
||||
|
||||
当前已有两种 token:
|
||||
|
||||
- `access_token`
|
||||
- `sessionToken`
|
||||
|
||||
后面如果加实时网关,也应继续区分。
|
||||
|
||||
## 5. 当前缺口
|
||||
|
||||
当前 schema 还没有这些模块:
|
||||
|
||||
- `competitions`
|
||||
- `registrations`
|
||||
- `page_configs`
|
||||
- `clubs`
|
||||
- `client_devices`
|
||||
- 实时票据 / 网关票据
|
||||
|
||||
这些后面要按真正业务需要补 migration,不要先拍脑袋建大而全表。
|
||||
204
backend/docs/核心流程.md
Normal file
204
backend/docs/核心流程.md
Normal file
@@ -0,0 +1,204 @@
|
||||
# 核心流程
|
||||
|
||||
## 1. 总流程
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Entry Resolve"] --> B["Auth"]
|
||||
B --> C["Home / Cards"]
|
||||
C --> D["Event Play"]
|
||||
D --> E["Resolve Release"]
|
||||
E --> F["Launch Session"]
|
||||
F --> G["Client Load Manifest"]
|
||||
G --> H["Session Start / Finish"]
|
||||
H --> I["Result / History"]
|
||||
```
|
||||
|
||||
## 2. 入口解析
|
||||
|
||||
入口层先解决:
|
||||
|
||||
- 用户从哪个渠道进来
|
||||
- 当前归属哪个 `tenant`
|
||||
- 当前品牌壳和首页卡片应该加载什么
|
||||
|
||||
当前对应接口:
|
||||
|
||||
- `GET /entry/resolve`
|
||||
- `GET /home`
|
||||
- `GET /cards`
|
||||
- `GET /me/entry-home`
|
||||
|
||||
## 3. 登录流程
|
||||
|
||||
### 3.1 APP
|
||||
|
||||
APP 当前主链是手机号验证码:
|
||||
|
||||
1. `POST /auth/sms/send`
|
||||
2. `POST /auth/login/sms`
|
||||
3. 返回 `access_token + refresh_token`
|
||||
|
||||
### 3.2 微信小程序
|
||||
|
||||
微信小程序当前主链是:
|
||||
|
||||
1. 客户端 `wx.login`
|
||||
2. `POST /auth/login/wechat-mini`
|
||||
3. 后端换取 `openid`
|
||||
4. 返回 `access_token + refresh_token`
|
||||
|
||||
开发环境也支持 `dev-` 前缀 code。
|
||||
|
||||
### 3.3 绑定与合并
|
||||
|
||||
当小程序用户后续绑定手机号时:
|
||||
|
||||
1. 先发 `bind_mobile` 场景验证码
|
||||
2. `POST /auth/bind/mobile`
|
||||
3. 如果手机号已属于别的用户,则合并到手机号主账号
|
||||
|
||||
当前策略是:
|
||||
|
||||
- 手机号账号优先
|
||||
- 微信轻账号并入手机号账号
|
||||
|
||||
## 4. 首页流程
|
||||
|
||||
首页不是固定首页,而是“入口上下文首页”。
|
||||
|
||||
当前聚合接口:
|
||||
|
||||
- `GET /me/entry-home`
|
||||
|
||||
它会返回:
|
||||
|
||||
- 当前用户
|
||||
- 当前 tenant
|
||||
- 当前 channel
|
||||
- 当前 cards
|
||||
- 继续中的 session
|
||||
- 最近一局 session
|
||||
|
||||
## 5. Event Play 流程
|
||||
|
||||
活动详情页或开始前准备页不应该只拿 `event`。
|
||||
|
||||
它还必须拿到:
|
||||
|
||||
- 当前是否可启动
|
||||
- 当前会落到哪份 `release`
|
||||
- 是否有 ongoing session
|
||||
- 当前推荐动作是什么
|
||||
|
||||
当前聚合接口:
|
||||
|
||||
- `GET /events/{eventPublicID}/play`
|
||||
|
||||
它会返回:
|
||||
|
||||
- `event`
|
||||
- `release`
|
||||
- `resolvedRelease`
|
||||
- `play.canLaunch`
|
||||
- `play.primaryAction`
|
||||
- `play.launchSource`
|
||||
- `play.ongoingSession`
|
||||
- `play.recentSession`
|
||||
|
||||
## 6. Launch 流程
|
||||
|
||||
### 6.1 当前原则
|
||||
|
||||
启动一局游戏时,不是“启动一个 event”。
|
||||
|
||||
而是:
|
||||
|
||||
> 基于 event 当前可启动的 release,创建一条固化 release 的 session。
|
||||
|
||||
### 6.2 当前接口
|
||||
|
||||
- `POST /events/{eventPublicID}/launch`
|
||||
|
||||
当前请求体支持:
|
||||
|
||||
- `releaseId`
|
||||
- `clientType`
|
||||
- `deviceKey`
|
||||
|
||||
当前返回会带:
|
||||
|
||||
- `launch.source`
|
||||
- `launch.resolvedRelease`
|
||||
- `launch.config`
|
||||
- `launch.business.sessionId`
|
||||
- `launch.business.sessionToken`
|
||||
|
||||
### 6.3 客户端应如何使用
|
||||
|
||||
客户端进入游戏前,应以返回中的这几项为准:
|
||||
|
||||
- `launch.resolvedRelease.releaseId`
|
||||
- `launch.resolvedRelease.manifestUrl`
|
||||
- `launch.resolvedRelease.manifestChecksumSha256`
|
||||
|
||||
而不是再拿 `event` 自己去猜。
|
||||
|
||||
## 7. Session 流程
|
||||
|
||||
### 7.1 当前接口
|
||||
|
||||
- `GET /sessions/{sessionPublicID}`
|
||||
- `POST /sessions/{sessionPublicID}/start`
|
||||
- `POST /sessions/{sessionPublicID}/finish`
|
||||
- `GET /me/sessions`
|
||||
|
||||
### 7.2 鉴权模型
|
||||
|
||||
查询接口:
|
||||
|
||||
- 用 `access_token`
|
||||
|
||||
局内动作接口:
|
||||
|
||||
- 用 `sessionToken`
|
||||
|
||||
这保证了业务登录态和一局游戏运行态是分开的。
|
||||
|
||||
## 8. 结果流程
|
||||
|
||||
### 8.1 当前接口
|
||||
|
||||
- `GET /sessions/{sessionPublicID}/result`
|
||||
- `GET /me/results`
|
||||
|
||||
### 8.2 当前 finish payload
|
||||
|
||||
`finish` 当前支持上传结果摘要:
|
||||
|
||||
- `finalDurationSec`
|
||||
- `finalScore`
|
||||
- `completedControls`
|
||||
- `totalControls`
|
||||
- `distanceMeters`
|
||||
- `averageSpeedKmh`
|
||||
- `maxHeartRateBpm`
|
||||
|
||||
### 8.3 结果页约束
|
||||
|
||||
结果页应该基于 session 结果查看,不应该回头去查当前 event 当前 release。
|
||||
|
||||
因为:
|
||||
|
||||
- 一个 event 未来可能发布新版本
|
||||
- 历史结果必须追溯到当时真实跑过的那份 release
|
||||
|
||||
## 9. 当前最应该坚持的流程约束
|
||||
|
||||
业务主线应始终保持为:
|
||||
|
||||
`entry -> auth -> event play -> resolve release -> launch -> session -> result`
|
||||
|
||||
不要退回成:
|
||||
|
||||
`event -> launch -> game`
|
||||
200
backend/docs/系统架构.md
Normal file
200
backend/docs/系统架构.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# 系统架构
|
||||
|
||||
## 1. 目标
|
||||
|
||||
当前 backend 不是一个“给地图页喂数据的简单服务”,而是一个业务壳后端。
|
||||
|
||||
它负责:
|
||||
|
||||
- 用户与登录
|
||||
- 多租户与多入口
|
||||
- 首页与业务入口聚合
|
||||
- Event 业务对象
|
||||
- 配置发布解析
|
||||
- 启动一局游戏
|
||||
- session 生命周期
|
||||
- 结果沉淀
|
||||
|
||||
它不负责:
|
||||
|
||||
- 解释游戏玩法细节
|
||||
- 运行时解析复杂地图规则
|
||||
- 直接下发数据库编辑态对象给客户端
|
||||
|
||||
## 2. 分层
|
||||
|
||||
### 2.1 平台层
|
||||
|
||||
平台层统一处理:
|
||||
|
||||
- `tenant`
|
||||
- `entry_channel`
|
||||
- `user`
|
||||
- `login_identity`
|
||||
- `auth_refresh_token`
|
||||
|
||||
这层是整个平台共用能力。
|
||||
|
||||
### 2.2 业务层
|
||||
|
||||
业务层统一处理:
|
||||
|
||||
- `card`
|
||||
- `event`
|
||||
- `event_play`
|
||||
- `entry_home`
|
||||
- `profile`
|
||||
|
||||
它面向页面和运营入口,但不直接承载游戏规则。
|
||||
|
||||
### 2.3 配置发布层
|
||||
|
||||
配置发布层统一处理:
|
||||
|
||||
- `event_release`
|
||||
- `manifest_url`
|
||||
- `manifest_checksum_sha256`
|
||||
- `route_code`
|
||||
|
||||
这层是“客户端真正进入游戏时要消费的运行配置入口”。
|
||||
|
||||
### 2.4 运行层
|
||||
|
||||
运行层统一处理:
|
||||
|
||||
- `game_session`
|
||||
- `session_token`
|
||||
- `session_results`
|
||||
|
||||
这层不关心编辑态,只关心“一局游戏”。
|
||||
|
||||
## 3. 最重要的对象关系
|
||||
|
||||
### 3.1 `event`
|
||||
|
||||
`event` 是业务对象。
|
||||
|
||||
它负责:
|
||||
|
||||
- 活动身份
|
||||
- 展示名称
|
||||
- 业务状态
|
||||
- 当前指向的发布版本
|
||||
|
||||
它不是客户端实际运行的配置文件本体。
|
||||
|
||||
### 3.2 `event_release`
|
||||
|
||||
`event_release` 是配置发布对象。
|
||||
|
||||
它负责:
|
||||
|
||||
- 这次发布的 `manifest_url`
|
||||
- 配置标签 `config_label`
|
||||
- 可选校验值
|
||||
- 可选 `route_code`
|
||||
|
||||
进入游戏时,客户端真正需要的是这里。
|
||||
|
||||
### 3.3 `game_session`
|
||||
|
||||
`game_session` 是运行对象。
|
||||
|
||||
它必须固化:
|
||||
|
||||
- 当前用户
|
||||
- 当前 event
|
||||
- 当前实际使用的 `event_release`
|
||||
- 当前 `session_token`
|
||||
|
||||
这样后续哪怕 event 切到新 release,旧 session 也不会漂移。
|
||||
|
||||
## 4. 配置驱动原则
|
||||
|
||||
这套系统必须坚持下面这条原则:
|
||||
|
||||
> 业务层先解析出一份可启动的 release,客户端再基于这份 release 的 manifest 进入游戏。
|
||||
|
||||
不能走成:
|
||||
|
||||
> 客户端拿到 event 后自己再去推断该加载哪份配置
|
||||
|
||||
所以当前接口都在往这个方向收口:
|
||||
|
||||
- `GET /events/{id}/play` 会返回 `resolvedRelease`
|
||||
- `POST /events/{id}/launch` 会返回 `resolvedRelease`
|
||||
- `GET /sessions/{id}` 会返回 `resolvedRelease`
|
||||
- `GET /sessions/{id}/result` 能追溯到当时的 release
|
||||
|
||||
## 5. 代码分层
|
||||
|
||||
### 5.1 入口层
|
||||
|
||||
- [main.go](D:/dev/cmr-mini/backend/cmd/api/main.go)
|
||||
- [app.go](D:/dev/cmr-mini/backend/internal/app/app.go)
|
||||
- [config.go](D:/dev/cmr-mini/backend/internal/app/config.go)
|
||||
|
||||
### 5.2 HTTP 层
|
||||
|
||||
- [router.go](D:/dev/cmr-mini/backend/internal/httpapi/router.go)
|
||||
- [handlers](D:/dev/cmr-mini/backend/internal/httpapi/handlers)
|
||||
- [middleware](D:/dev/cmr-mini/backend/internal/httpapi/middleware)
|
||||
|
||||
### 5.3 用例层
|
||||
|
||||
- [service](D:/dev/cmr-mini/backend/internal/service)
|
||||
|
||||
当前主要服务:
|
||||
|
||||
- `AuthService`
|
||||
- `EntryService`
|
||||
- `HomeService`
|
||||
- `EntryHomeService`
|
||||
- `EventService`
|
||||
- `EventPlayService`
|
||||
- `SessionService`
|
||||
- `ResultService`
|
||||
- `ProfileService`
|
||||
- `DevService`
|
||||
|
||||
### 5.4 数据层
|
||||
|
||||
- [store/postgres](D:/dev/cmr-mini/backend/internal/store/postgres)
|
||||
|
||||
特点:
|
||||
|
||||
- 手写 SQL
|
||||
- `pgx` 连接池
|
||||
- 不依赖 ORM
|
||||
|
||||
### 5.5 平台适配层
|
||||
|
||||
- [jwtx](D:/dev/cmr-mini/backend/internal/platform/jwtx)
|
||||
- [security](D:/dev/cmr-mini/backend/internal/platform/security)
|
||||
- [wechatmini](D:/dev/cmr-mini/backend/internal/platform/wechatmini)
|
||||
|
||||
## 6. 当前边界
|
||||
|
||||
### 6.1 backend 管什么
|
||||
|
||||
- 业务身份
|
||||
- 配置发布解析
|
||||
- 启动编排
|
||||
- 一局的生命周期和结果
|
||||
|
||||
### 6.2 游戏客户端管什么
|
||||
|
||||
- 下载 `manifest_url`
|
||||
- 解析运行配置
|
||||
- 驱动地图和玩法
|
||||
- 产生过程数据和结束摘要
|
||||
|
||||
### 6.3 后续网关该怎么接
|
||||
|
||||
后面如果接实时网关,建议仍然走:
|
||||
|
||||
- backend 负责登录与 launch
|
||||
- launch 或 session 负责产出短期实时票据
|
||||
- 网关只认 backend 签发的运行态票据
|
||||
|
||||
不要把微信身份或业务 token 直接暴露给实时网关。
|
||||
412
backend/docs/配置管理方案.md
Normal file
412
backend/docs/配置管理方案.md
Normal file
@@ -0,0 +1,412 @@
|
||||
# 配置管理方案
|
||||
|
||||
## 1. 目标
|
||||
|
||||
后续 backend 不应该只“管理一个 event JSON 文件”,而应该管理一整套可伸缩的配置生命周期。
|
||||
|
||||
这套生命周期至少要覆盖:
|
||||
|
||||
1. 编辑态源配置
|
||||
2. 构建态中间产物
|
||||
3. 对外发布版本
|
||||
4. 启动时绑定的 release
|
||||
5. 运行完成后的 session 追溯
|
||||
|
||||
核心目标不是支持当前字段,而是支持以后继续加字段时,主架构不需要推翻。
|
||||
|
||||
## 2. 当前现状
|
||||
|
||||
当前根目录下的 [event](D:/dev/cmr-mini/event) 已经保存了最小启动配置样例:
|
||||
|
||||
- [classic-sequential.json](D:/dev/cmr-mini/event/classic-sequential.json)
|
||||
- [score-o.json](D:/dev/cmr-mini/event/score-o.json)
|
||||
|
||||
从这两个样例看,当前“最小启动配置”已经有了很好的雏形:
|
||||
|
||||
- `app`
|
||||
- `map`
|
||||
- `playfield`
|
||||
- `game.mode`
|
||||
|
||||
这类文件很适合作为运行时 manifest 的基础形态。
|
||||
|
||||
但如果后续继续往里面堆:
|
||||
|
||||
- 赛事规则
|
||||
- 计分规则
|
||||
- 内容页
|
||||
- 安全策略
|
||||
- 品牌配置
|
||||
- 多媒体资源
|
||||
- telemetry 开关
|
||||
- 实验字段
|
||||
|
||||
就不能再只靠单个最终 JSON 手工维护了。
|
||||
|
||||
## 3. 核心原则
|
||||
|
||||
### 3.1 稳定的是层,不是字段
|
||||
|
||||
后端要稳定的是这些层:
|
||||
|
||||
- `source config`
|
||||
- `build`
|
||||
- `release`
|
||||
- `launch`
|
||||
- `session`
|
||||
|
||||
而不是把所有具体配置字段都设计成强结构数据库列。
|
||||
|
||||
### 3.2 编辑态和运行态必须分离
|
||||
|
||||
编辑态:
|
||||
|
||||
- 配置项可以很多
|
||||
- 允许草稿
|
||||
- 允许试验字段
|
||||
- 允许中间状态
|
||||
|
||||
运行态:
|
||||
|
||||
- 必须稳定
|
||||
- 必须可校验
|
||||
- 必须有版本
|
||||
- 必须能被客户端直接消费
|
||||
|
||||
### 3.3 客户端只消费发布产物
|
||||
|
||||
客户端进入游戏时,不应直接读取编辑态对象。
|
||||
|
||||
客户端应该只消费:
|
||||
|
||||
- `manifest_url`
|
||||
- `manifest_checksum_sha256`
|
||||
- 与 manifest 配套的发布资源
|
||||
|
||||
### 3.4 session 必须固化 release
|
||||
|
||||
只要一局启动了:
|
||||
|
||||
- 必须固化 `event_release_id`
|
||||
- 后续 event 切新发布,不影响老 session
|
||||
- 结果页和历史页都必须能回看当时那份配置
|
||||
|
||||
## 4. 三层配置模型
|
||||
|
||||
## 4.1 第一层:源配置
|
||||
|
||||
这是编辑态配置。
|
||||
|
||||
建议特点:
|
||||
|
||||
- 允许字段增长
|
||||
- 允许草稿
|
||||
- 允许频繁修改
|
||||
- 主要存 `jsonb`
|
||||
|
||||
它对应“最大启动配置”或“完整编辑配置集合”。
|
||||
|
||||
### 可能包含的块
|
||||
|
||||
- `app`
|
||||
- `branding`
|
||||
- `map`
|
||||
- `playfield`
|
||||
- `game`
|
||||
- `rules`
|
||||
- `scoring`
|
||||
- `timeControl`
|
||||
- `content`
|
||||
- `assets`
|
||||
- `safety`
|
||||
- `telemetry`
|
||||
- `featureFlags`
|
||||
|
||||
## 4.2 第二层:构建产物
|
||||
|
||||
这是后端根据源配置构建出来的中间结果。
|
||||
|
||||
建议职责:
|
||||
|
||||
- schema 校验
|
||||
- 引用资源补全
|
||||
- 相对路径转绝对路径
|
||||
- 生成最终 manifest
|
||||
- 生成资产清单
|
||||
- 记录构建日志
|
||||
|
||||
这一层是后续做“预览构建”“草稿预览”“发布前检查”的关键。
|
||||
|
||||
## 4.3 第三层:发布版本
|
||||
|
||||
这是正式对外运行时版本。
|
||||
|
||||
建议职责:
|
||||
|
||||
- 绑定 build 结果
|
||||
- 绑定 manifest URL
|
||||
- 绑定 checksum
|
||||
- 绑定资源清单
|
||||
- 进入 launch 链路
|
||||
|
||||
当前已有的 `event_releases` 就是这层的起点,但后面还需要更完整的 build / assets 支撑。
|
||||
|
||||
## 5. 最小启动配置和最大配置怎么定义
|
||||
|
||||
建议不要把“最小配置 / 最大配置”当成数据库对象名,而要作为两种形态理解。
|
||||
|
||||
### 5.1 最小启动配置
|
||||
|
||||
就是客户端能开局所必需的最小 manifest。
|
||||
|
||||
建议包含:
|
||||
|
||||
- `schemaVersion`
|
||||
- `releaseId`
|
||||
- `app`
|
||||
- `map`
|
||||
- `playfield`
|
||||
- `game`
|
||||
- 必要资源引用
|
||||
|
||||
特点:
|
||||
|
||||
- 结构稳定
|
||||
- 字段尽量少
|
||||
- 客户端可直接消费
|
||||
|
||||
### 5.2 最大配置
|
||||
|
||||
就是完整编辑态 source config。
|
||||
|
||||
特点:
|
||||
|
||||
- 字段可以很多
|
||||
- 块可以不断扩展
|
||||
- 不要求直接给客户端消费
|
||||
- 构建后才会变成运行时 manifest
|
||||
|
||||
## 6. 当前 event 目录该扮演什么角色
|
||||
|
||||
当前根目录 [event](D:/dev/cmr-mini/event) 建议继续保留,但角色要明确:
|
||||
|
||||
它应该是:
|
||||
|
||||
- 本地源配置样例目录
|
||||
- 构建输入参考目录
|
||||
- 调试和原型验证输入
|
||||
|
||||
它不应该直接承担:
|
||||
|
||||
- 线上唯一配置源
|
||||
- 发布版本存储
|
||||
- 客户端直接运行入口
|
||||
|
||||
线上真正的运行入口应当是:
|
||||
|
||||
- 数据库里的 release 元数据
|
||||
- 对象存储/CDN 里的 manifest 和资源
|
||||
|
||||
## 7. 数据模型建议
|
||||
|
||||
在当前 [数据模型.md](D:/dev/cmr-mini/backend/docs/数据模型.md) 基础上,建议新增 3 张核心表。
|
||||
|
||||
这 3 张表的第一版 migration 已经落在:
|
||||
|
||||
- [0005_config_pipeline.sql](D:/dev/cmr-mini/backend/migrations/0005_config_pipeline.sql)
|
||||
|
||||
## 7.1 `event_config_sources`
|
||||
|
||||
用途:
|
||||
|
||||
- 存编辑态源配置版本
|
||||
|
||||
建议字段:
|
||||
|
||||
- `id`
|
||||
- `event_id`
|
||||
- `source_version_no`
|
||||
- `source_kind`
|
||||
- `schema_id`
|
||||
- `schema_version`
|
||||
- `status`
|
||||
- `source_jsonb`
|
||||
- `notes`
|
||||
- `created_by_user_id`
|
||||
- `created_at`
|
||||
|
||||
说明:
|
||||
|
||||
- `source_jsonb` 存完整编辑态配置
|
||||
- `schema_id + schema_version` 用来做校验
|
||||
|
||||
## 7.2 `event_config_builds`
|
||||
|
||||
用途:
|
||||
|
||||
- 存一次构建的结果
|
||||
|
||||
建议字段:
|
||||
|
||||
- `id`
|
||||
- `event_id`
|
||||
- `source_id`
|
||||
- `build_no`
|
||||
- `build_status`
|
||||
- `build_log`
|
||||
- `manifest_jsonb`
|
||||
- `asset_index_jsonb`
|
||||
- `created_by_user_id`
|
||||
- `created_at`
|
||||
|
||||
说明:
|
||||
|
||||
- `manifest_jsonb` 是构建后得到的运行 manifest
|
||||
- `asset_index_jsonb` 是构建时收集到的资源清单
|
||||
|
||||
## 7.3 `event_release_assets`
|
||||
|
||||
用途:
|
||||
|
||||
- 存 release 的资源清单
|
||||
|
||||
建议字段:
|
||||
|
||||
- `id`
|
||||
- `event_release_id`
|
||||
- `asset_type`
|
||||
- `asset_key`
|
||||
- `asset_path`
|
||||
- `asset_url`
|
||||
- `checksum`
|
||||
- `size_bytes`
|
||||
- `meta_jsonb`
|
||||
|
||||
说明:
|
||||
|
||||
- 这张表非常适合后面做资源核对、回滚、调试和发布检查
|
||||
|
||||
## 8. 强结构和弱结构怎么分
|
||||
|
||||
## 8.1 强结构字段
|
||||
|
||||
这些字段后端应强约束:
|
||||
|
||||
- `event_id`
|
||||
- `release_id`
|
||||
- `manifest_url`
|
||||
- `manifest_checksum_sha256`
|
||||
- `status`
|
||||
- `published_at`
|
||||
- `session_public_id`
|
||||
- `event_release_id`
|
||||
|
||||
这些是运行链路基础,不适合做成松散字段。
|
||||
|
||||
## 8.2 弱结构字段
|
||||
|
||||
这些字段建议主要放 `jsonb`:
|
||||
|
||||
- 玩法规则
|
||||
- 计分策略
|
||||
- 文案内容
|
||||
- H5 内容块
|
||||
- 品牌视觉配置
|
||||
- 资源扩展配置
|
||||
- feature flags
|
||||
- 实验字段
|
||||
|
||||
这样后面新增字段时,主链路不会被迫重构。
|
||||
|
||||
## 9. 后端后续能力建议
|
||||
|
||||
## 9.1 源配置管理
|
||||
|
||||
建议支持:
|
||||
|
||||
- 保存草稿 source
|
||||
- 查看 source 历史版本
|
||||
- source diff
|
||||
- 从文件导入 source
|
||||
|
||||
## 9.2 构建能力
|
||||
|
||||
建议支持:
|
||||
|
||||
- 校验 source schema
|
||||
- 校验资源引用存在
|
||||
- 生成 manifest
|
||||
- 生成 asset index
|
||||
- 输出 build log
|
||||
|
||||
## 9.3 发布能力
|
||||
|
||||
建议支持:
|
||||
|
||||
- 从某个 build 发布 release
|
||||
- 生成 `manifest_url`
|
||||
- 上传 release 资产
|
||||
- 标记当前生效 release
|
||||
- 回滚旧 release
|
||||
|
||||
## 9.4 调试能力
|
||||
|
||||
建议支持:
|
||||
|
||||
- 预览构建结果
|
||||
- 查看某个 release 资产清单
|
||||
- 查看某个 session 实际绑定的 release 和 manifest
|
||||
|
||||
## 10. 推荐 API 路线
|
||||
|
||||
建议后面按这个顺序补接口:
|
||||
|
||||
### 第一批:source
|
||||
|
||||
- `POST /events/{id}/config-sources`
|
||||
- `GET /events/{id}/config-sources`
|
||||
- `GET /config-sources/{id}`
|
||||
|
||||
### 第二批:build
|
||||
|
||||
- `POST /config-sources/{id}/build`
|
||||
- `GET /builds/{id}`
|
||||
- `GET /builds/{id}/manifest`
|
||||
|
||||
### 第三批:release
|
||||
|
||||
- `POST /builds/{id}/release`
|
||||
- `GET /releases/{id}`
|
||||
- `GET /releases/{id}/assets`
|
||||
|
||||
### 第四批:preview
|
||||
|
||||
- `GET /events/{id}/preview-play`
|
||||
- `POST /builds/{id}/preview-launch`
|
||||
|
||||
## 11. 推荐开发顺序
|
||||
|
||||
当前最值得先做的不是配置后台 UI,而是配置构建器。
|
||||
|
||||
建议顺序:
|
||||
|
||||
1. 先定义 source config 和 manifest 的字段边界
|
||||
2. 先建 `event_config_sources`
|
||||
3. 先做 schema 校验器
|
||||
4. 先做 build 产物生成
|
||||
5. 再建 `event_config_builds`
|
||||
6. 再做正式 release 发布
|
||||
7. 最后才做后台编辑器
|
||||
|
||||
原因很简单:
|
||||
|
||||
- 没有 build/release 核心能力,后台只是个大表单
|
||||
- 先把构建链打通,后面各种管理壳层才有基础
|
||||
|
||||
## 12. 一句话结论
|
||||
|
||||
后续 backend 不该做成“管理一个越来越大的 event JSON 文件”,而应该做成:
|
||||
|
||||
> 源配置管理 + 构建产物管理 + release 发布管理 + session 绑定 release
|
||||
|
||||
这样以后无论你配置项怎么继续长,主架构都还能撑住。
|
||||
Reference in New Issue
Block a user