完善活动运营域与联调标准化

This commit is contained in:
2026-04-03 13:11:41 +08:00
parent 0e28f70bad
commit 129ea935db
56 changed files with 11004 additions and 196 deletions

836
t2b.md Normal file
View File

@@ -0,0 +1,836 @@
# T2B 协作清单
> 文档版本v1.10
> 最后更新2026-04-03 13:08:15
说明:
- 本文件由总控维护,写给后端线程
- 目标是把“后台生产闭环”第一阶段需要落地的东西讲清楚
- 只写当前阶段实施说明,不写长讨论稿
- 正式架构文档以 [后台生产闭环架构草案](D:/dev/cmr-mini/doc/backend/后台生产闭环架构草案.md) 为准
---
## 0. 当前阶段状态与下一步
backend 当前已完成:
- 生产骨架对象落库与 `/dev/workbench` 最小联调台
- `MapRuntimeBinding -> EventRelease -> launch.runtime` 主链接通
- `EventPresentation / ContentBundle / EventRelease` 第一阶段接通
- `Event` 默认 active 三元组固化:
- `currentPresentationId`
- `currentContentBundleId`
- `currentRuntimeBindingId`
- `publish` 默认继承当前 active 三元组
- `Bootstrap Demo``一键补齐 Runtime 并发布` 已可从空白状态跑完整测试链
- workbench 日志已补齐:
- 分步日志
- 真实错误
- stack
- 最后一次 curl
- 预期判定
当前主线不再是继续补对象,而是进入:
**联调标准化阶段**
本阶段 backend 的核心任务只有 3 件事:
1. 固化“一键测试”链路,确保从空白环境可重复跑通
2. 固化详细日志口径,失败时明确定位在哪一步
3. 固化稳定测试数据,并逐步支持更接近生产的真实输入
当前不建议 backend 继续发散去做:
- 更多新对象
- 更多 workbench 管理按钮
- 更复杂后台 UI
- 过早扩大奖励、社交、审核流
---
## 1. 本次目标
本次不是让 backend 一次性做完整后台,而是先搭出**最小生产骨架**,让下面这条链能真正闭环:
```text
地图输入
-> 瓦片版本
-> KML 导入
-> 赛道 variant
-> 活动绑定
-> release
-> launch
-> 客户端消费
```
当前重点是:
- 先把对象模型定下来
- 先让地图、KML、活动三条输入链有正式落点
- 先让客户端只认发布产物
补充确认:
- 本次接受 backend 采用**增量演进**方式推进
- 不要求一次性推翻当前已稳定联调的:
- `Event`
- `EventRelease`
- `Session`
主链
当前补充确认:
- 生产骨架第一阶段与活动运营域第二阶段第四刀已经完成
- backend 当前下一步应切到“联调标准化”,而不是继续新增对象层级
---
## 2. 本次范围
### 2.1 本次必须做
- 定义并落库以下核心对象:
- `Place`
- `MapAsset`
- `TileRelease`
- `CourseSet`
- `CourseVariant`
- `CourseSource`
- `Event`
- `EventPresentation`
- `ContentBundle`
- `MapRuntimeBinding`
- `EventRelease`
- 先让 KML 不再只是文件,而能转成 `CourseVariant`
- 先让活动不再只是页面概念,而能正式绑定:
- 展示定义
- 内容包
- 运行绑定
- 发布版本
- 第一阶段优先落以下对象:
- `Place`
- `MapAsset`
- `TileRelease`
- `CourseSource`
- `CourseSet`
- `CourseVariant`
- `MapRuntimeBinding`
- 第一阶段允许暂缓完整落库:
- `EventPresentation`
- `ContentBundle`
但对象语义必须先在架构上定清楚
### 2.2 本次先不做
- 奖励系统
- 社交系统
- 复杂审核流
- 完整后台 UI
- 大而全的素材编排器
- 高级权限体系
---
## 3. 后端对象最小理解
### 3.1 地图运行域
#### `Place`
- 地点
- 上层业务对象
- 一个地点下可有多张地图
#### `MapAsset`
- 某个地点下的一张地图资源
- 一张地图可有多个瓦片版本
#### `TileRelease`
- 某张地图的具体瓦片发布版本
#### `CourseSet`
- 一组赛道集合
- 例如:校园顺序赛、校园积分赛
#### `CourseVariant`
- 一个具体可运行赛道方案
- 顺序赛 8 点 / 12 点
- 积分赛 A / B / C 方案
- 客户端最终只应认这个对象
#### `CourseSource`
- 原始输入源
- KML 只是来源,不是最终业务对象
### 3.2 活动运营域
#### `Event`
- 活动业务对象
- 默认体验活动和定制活动都属于它
#### `EventPresentation`
- 活动卡片、详情页、H5 schema
#### `ContentBundle`
- 图片、音频、动画、文创、结果页资源等内容包
#### `MapRuntimeBinding`
- 活动运行时绑定哪张地图、哪条赛道、哪套瓦片、哪套配置
#### `EventRelease`
- 客户端真正消费的活动发布版本
---
## 4. 最小关系建议
建议 backend 先按这个关系理解:
- `Place 1 -> N MapAsset`
- `MapAsset 1 -> N TileRelease`
- `MapAsset 1 -> N CourseSet`
- `CourseSet 1 -> N CourseVariant`
- `CourseVariant N -> 1 CourseSource`
- `Event 1 -> N EventPresentation`
- `Event 1 -> N ContentBundle`
- `Event 1 -> N MapRuntimeBinding`
- `Event 1 -> N EventRelease`
其中:
- `MapRuntimeBinding` 负责引用:
- `placeId`
- `mapId`
- `tileReleaseId`
- `courseSetId`
- `courseVariantId`
---
## 5. 后端第一阶段建议实施顺序
### 第一步:按增量方式落库最小对象
建议先把表和基础模型定下来。
第一阶段优先级建议:
1. `places`
2. `map_assets`
3. `tile_releases`
4. `course_sources`
5. `course_sets`
6. `course_variants`
7. `map_runtime_bindings`
第二阶段再补:
8. `event_presentations`
9. `content_bundles`
说明:
- 当前稳定的 `events / event_releases / sessions` 主链保留
- 本次是在现有骨架上增量补生产对象,不做一次性替换式重构
### 第二步:先打通 KML 导入链
目标:
- 上传 KML
- 保存 `CourseSource`
- 解析控制点与起终点
- 生成一个 `CourseVariant`
- 归入某个 `CourseSet`
### 第三步:先打通活动绑定链
目标:
- 一个 `Event` 可绑定:
- `EventPresentation`
- `ContentBundle`
- `MapRuntimeBinding`
### 第四步:先打通发布链
目标:
- 生成 `EventRelease`
- `launch` 先继续返回当前稳定字段:
- `resolvedRelease`
- `business`
- `variant`
- 第二阶段再补完整运行对象字段:
- `placeId`
- `mapId`
- `tileReleaseId`
- `courseVariantId`
- `eventReleaseId`
### 第五步:把第一阶段生产骨架接口接入 `/dev/workbench`
目标:
- 不让第一阶段对象只停留在 API 目录里
- 在 workbench 里形成最小可操作联调面板
- 用于验证对象关系和生产闭环,不用于替代正式后台
本步建议只做:
#### A. 地点与地图
- `Place` 列表
- 新建 `Place`
-`Place` 下新建 `MapAsset`
-`MapAsset` 下新建 `TileRelease`
- 查看详情
#### B. 赛道与 KML
- `CourseSource` 列表
- 新建 `CourseSource`
- 新建 `CourseSet`
-`CourseSet` 下新建 `CourseVariant`
- 查看详情
#### C. 运行绑定
- `MapRuntimeBinding` 列表
- 新建 `MapRuntimeBinding`
- 选择:
- `place`
- `map`
- `tile release`
- `course variant`
- 查看详情
本步明确不做:
- 完整后台 UI
- Event 全量编辑
- `EventPresentation` 可视化搭建
- `ContentBundle` 大资源管理台
- Build / Release 全流程可视化
- 删除、批量操作、审核流
一句话:
**workbench 当前只做“第一阶段生产骨架联调台”,不做“正式后台管理系统”。**
### 第六步:进入“最小接线”阶段
目标:
-`MapRuntimeBinding` 和当前 `EventRelease` 接起来
- 让运行对象开始逐步进入 `launch`
- 保持当前前端稳定链不被打断
本步建议优先做:
#### A. `EventRelease` 接 `MapRuntimeBinding`
-`EventRelease` 上补 `runtimeBindingId`
- 查询 `EventRelease` 时可带出最小 `runtime binding` 摘要
#### B. `launch` 新增 `runtime` 摘要块
- 保留当前稳定字段:
- `resolvedRelease`
- `business`
- `variant`
- 新增一个兼容性的 `runtime` 块,建议最少返回:
- `runtimeBindingId`
- `placeId`
- `mapId`
- `tileReleaseId`
- `courseSetId`
- `courseVariantId`
- 如字段成本不高,可附带:
- `placeName`
- `mapName`
- `routeCode`
#### C. `workbench` 最小接线验证
-`/dev/workbench` 上增加:
- `EventRelease` 选择或查看
- 绑定 `runtimeBinding`
- 查看 release 当前已接入的运行对象摘要
本步明确要求:
- 不修改旧字段语义
- 不移除旧字段
- 不让前端现有 `launch` 链断掉
- 先做到“后端可挂接、可透出、可验证”
---
## 6. 当前接口落地方向
当前阶段建议 backend 后续接口逐步收敛到:
### 6.1 生产侧接口
- 创建地点
- 创建地图
- 创建瓦片版本记录
- 上传 KML
- 生成赛道 variant
- 创建活动
- 保存活动展示定义
- 保存内容包引用
- 保存运行绑定
- 创建活动 release
### 6.2 客户端消费接口
- 活动列表
- 活动详情
- `launch`
- session start
- session finish
- result
- history
关键要求:
- 客户端只消费 release 产物
- 不再消费原始 KML
- 不再消费地图原始资产
- `launch` 采用两阶段兼容,不要求第一阶段打断当前前端稳定链
### 6.3 workbench 联调台
backend 下一步建议把以下接口先接到 `/dev/workbench`
- `Place`
- `MapAsset`
- `TileRelease`
- `CourseSource`
- `CourseSet`
- `CourseVariant`
- `MapRuntimeBinding`
接入目标:
- list
- create
- detail
- binding
不建议当前阶段接入:
- edit
- delete
- batch
- 审核流
### 6.4 最小接线阶段接口方向
backend 下一步建议新增或补齐以下能力:
- `EventRelease` 挂接 `runtimeBindingId`
- 查询 `EventRelease` 时返回最小运行绑定摘要
- `launch` 返回新增 `runtime` 摘要块
当前目标不是让前端强依赖新字段,而是先让:
- release 和 runtime binding 接上
- `launch` 能把运行对象透出来
- 前后端可以开始验证运行对象链是活的
### 6.5 第四刀:发布闭环阶段
在第三刀已经完成:
- `MapRuntimeBinding -> EventRelease`
- `launch.runtime` 兼容透出
之后,下一步建议进入真正的**发布闭环阶段**。
目标:
- 不再要求“先 publish再手工 bind runtime”
- 改为 publish 时就能直接产出带 `runtimeBindingId` 的完整 `EventRelease`
- 保持当前旧接口和旧字段完全兼容
本步建议优先做:
#### A. publish/build 接口支持 `runtimeBindingId`
- 在当前发布链中允许显式传入 `runtimeBindingId`
- 如果传入,则发布完成后直接把 release 绑好 runtime
- 如果不传入,则继续保持当前兼容行为
#### B. workbench publish 面板接入 runtime 选择
-`/dev/workbench` 的发布操作区增加 `Runtime Binding` 选择
- 支持一条完整联调链:
- 选 release source / build
-`runtimeBindingId`
- publish
- 直接 launch 验证
#### C. release 查询继续返回 runtime 摘要
- `Get Release`
- `launch`
都继续透出当前最小 `runtime` 摘要,供前端和总控验证。
本步关键要求:
- 只加能力,不改旧语义
- 新流程优先,但旧流程继续可用
- 发布结果尽量原子,避免漏掉 runtime 挂接
---
## 7. 当前需要 backend 重点注意的边界
1. KML 只是输入源,不是最终业务对象
2. 活动不是素材仓库,活动只引用 `ContentBundle`
3. 地图上层必须有 `Place`,不要让 `MapAsset` 直接当最上层
4. 客户端最终必须只认 `EventRelease`
5. launch 返回必须落到具体:
- `placeId`
- `mapId`
- `tileReleaseId`
- `courseVariantId`
- `eventReleaseId`
---
## 8. 当前待 backend 回写确认
请 backend 线程后续重点回写以下确认:
1. 第一阶段表结构是否接受这套对象拆分
2. `Place / MapAsset / TileRelease / CourseSource / CourseSet / CourseVariant / MapRuntimeBinding` 是否按当前顺序推进
3. KML 导入链是否准备按 `CourseSource -> CourseVariant`
4. 活动是否接受拆成:
- `Event`
- `EventPresentation`
- `ContentBundle`
- `MapRuntimeBinding`
- `EventRelease`
5. `launch` 两阶段兼容方案是否按当前确认推进
6. workbench 是否按“第一阶段生产骨架联调台”接入,且只做 list / create / detail / binding
本轮新增执行项:
7. 是否按“第三刀最小接线”推进:
- `MapRuntimeBinding -> EventRelease`
- `launch.runtime` 摘要透出
- 继续保持旧字段兼容
8. 是否按“第四刀发布闭环”推进:
- publish 直接支持 `runtimeBindingId`
- workbench publish 面板增加 runtime 选择
- 继续保留“先 publish再 bind runtime”的兼容路径
9. 是否进入“活动运营域第二阶段”:
- `EventPresentation` 最小落库
- `ContentBundle` 最小落库
- `EventRelease` 明确绑定 `presentation / bundle / runtime`
10. 是否进入“活动运营域第二阶段第二刀”:
- `event detail` 透出最小 `presentation / bundle` 摘要
- `release detail` 透出最小 `presentation / bundle / runtime` 摘要
- `launch` 增加兼容性的 `presentation / contentBundle` 摘要块
- publish 在未显式传入时允许按 event 当前默认配置自动补齐 `presentation / bundle`
---
## 9. 一句话结论
本次给 backend 的实施要求很简单:
**先别继续围绕散装页面和散装配置推进,先把地图运行域和活动运营域的最小骨架搭起来。**
当前下一步重点已经进一步明确为:
**活动运营域第二阶段第三刀第一版已完成backend 下一步切到“展示定义统一导入与默认绑定”阶段。**
### 6.6 第五刀:前端正式接线阶段
当前 backend 已完成第四刀第一版:
- publish 直接支持 `runtimeBindingId`
- workbench publish 区支持直接填写 `Runtime Binding ID`
- 发布成功返回 `runtime`
- 旧的“先 publish再 bind runtime”路径继续兼容
因此下一步建议正式进入**前端接线阶段**。
目标:
- 前端开始正式消费 `launch.runtime`
- 活动准备页、地图页、结果页、历史页开始逐步展示运行对象摘要
- 继续保持旧字段兼容,不要求一轮切掉老逻辑
前端第一阶段建议优先做:
#### A. `launch.runtime` 消费
- 读取并缓存:
- `runtimeBindingId`
- `placeId`
- `mapId`
- `tileReleaseId`
- `courseSetId`
- `courseVariantId`
- 如后端已返回名称摘要,也同步接入:
- `placeName`
- `mapName`
- `routeCode`
#### B. 准备页与地图页最小展示
- 在准备页展示当前地点 / 地图 / 赛道摘要
- 在地图页调试或摘要区透出当前 runtime 对象
#### C. 结果与历史摘要逐步接入
- 先不强改所有列表
- 优先让单局结果页和历史详情页能看见:
- `place`
- `map`
- `variant`
- `routeCode`
当前阶段原则:
- 前端正式上场,但只接新增摘要,不推翻现有稳定页面主链
- `resolvedRelease / business / variant` 旧字段仍继续保留和可用
- 如果后端某些名称摘要尚未补齐,前端先按 ID + 已有字段兜底
### 6.7 第六刀之后的下一步:活动运营域第二阶段第二刀
当前 backend 已完成:
- `0009_event_ops_phase2.sql`
- `EventPresentation` 最小落库
- `ContentBundle` 最小落库
- `EventRelease` 已可绑定:
- `presentationId`
- `bundleId`
- `runtimeBindingId`
- publish 已支持显式挂接:
- `presentationId`
- `contentBundleId`
- `runtimeBindingId`
因此下一步建议进入“活动运营域第二阶段第二刀”。目标是:
-`EventPresentation / ContentBundle` 不只存在于后台和 publish 输入里
- 而是正式进入可查询、可验证、可消费的发布摘要
建议顺序:
#### A. `event detail` 透出当前展示与内容包摘要
建议最少返回:
- `currentPresentation`
- `presentationId`
- `templateKey`
- `version`
- `currentContentBundle`
- `bundleId`
- `bundleType`
- `version`
#### B. `Get Release` 透出完整最小摘要
建议 `release detail` 同时包含:
- `presentation`
- `contentBundle`
- `runtime`
前两者先以摘要形式返回,不先做复杂 schema 下发。
#### C. `launch` 增加兼容性的活动运营摘要块
在保持旧字段与当前 `runtime` 不变的前提下,新增:
- `presentation`
- `contentBundle`
建议最少包括:
- `presentationId`
- `templateKey`
- `bundleId`
- `bundleType`
#### D. publish 增加默认补齐逻辑
如果 publish 未显式传入:
- `presentationId`
- `contentBundleId`
允许按 event 当前默认配置自动补齐。
本步明确不建议做:
- 前端立即全面消费 presentation / bundle
- 复杂活动页面 schema 下发
- 内容包全量资源编排
- 正式后台大 UI
### 6.8 活动运营域第二阶段第三刀:发布摘要闭环与内容包导入入口
当前已确认:
- frontend 已完成“活动运营域摘要第一刀”
- 当前前端进入联调回归与小范围修复阶段
- backend 下一步不应继续围绕前端页面做字段补丁,而应继续把活动运营域本身做完整
本刀建议拆成两步,但按一个阶段推进。
#### A. 先把 release 摘要闭环
目标:
-`EventRelease` 真正成为活动运营域统一发布产物
- 保证以下几处返回的摘要语义一致:
- `event detail`
- `event play`
- `launch`
- `release detail`
建议 `release detail` 最少透出:
- `presentation`
- `presentationId`
- `templateKey`
- `version`
- `contentBundle`
- `bundleId`
- `bundleType`
- `version`
- `runtime`
- `runtimeBindingId`
- `placeId`
- `mapId`
- `tileReleaseId`
- `courseVariantId`
同时建议 `/dev/workbench` 的 release 查看区,能直接验证这三类摘要。
#### B. 再打开 `ContentBundle` 统一导入入口
目标:
- 不再只手工创建 `ContentBundle`
- 让后续静态资源、音频、动画、文创等内容,先通过统一导入入口进入 bundle
当前阶段建议只做“入口”和“元信息”,不做完整资源平台。
建议最小能力:
- 新增 `ContentBundle Import` 最小接口
- 接收:
- `bundleType`
- `sourceType`
- `manifestUrl` 或等价资源清单入口
- `version`
- `title`
- 创建后生成:
- `bundleId`
- `bundleType`
- `version`
- `assetManifest`
- `status`
当前明确不做:
- 复杂资源上传工作流
- 大文件管理台
- 资源审核流
- 全量 H5 schema 组装
关键原则:
- `ContentBundle` 先做统一导入入口,不先做复杂资源管理系统
- frontend 当前不消费资源明细,只继续认摘要
- 先把“发布对象完整”和“内容资源有正式入口”两件事做起来
### 6.9 活动运营域第二阶段第四刀:展示定义统一导入与默认绑定
当前已确认:
- backend 已完成:
- `event detail / play / launch / release detail` 活动运营摘要闭环
- `ContentBundle` 统一导入入口第一版
- frontend 当前不再扩新页面链,继续联调回归
所以下一步 backend 不建议继续围绕玩家侧摘要补字段,而应继续把活动运营域生产链做完整。
#### A. 打开 `EventPresentation` 统一导入入口
目标:
- 后续外部活动卡片/H5 搭建系统,不再只靠手工创建 presentation
- 而是通过统一入口把展示定义正式导入 backend
建议最小能力:
- 新增 `EventPresentation Import` 最小接口
- 接收:
- `templateKey`
- `sourceType`
- `schemaUrl` 或等价 schema 入口
- `version`
- `title`
- 创建后生成:
- `presentationId`
- `templateKey`
- `version`
- `schema`
- `status`
#### B. 固化 `Event` 当前默认 active 绑定
目标:
-`Event` 当前默认使用的:
- `presentation`
- `contentBundle`
- `runtimeBinding`
三者关系稳定下来
建议至少明确:
- `currentPresentationId`
- `currentContentBundleId`
- `currentRuntimeBindingId`
以及 publish 在未显式传入时,默认如何继承这三者。
#### C. `/dev/workbench` 增加最小验证
建议补:
- `Import Presentation`
- 查看 event 当前 active
- presentation
- bundle
- runtime
- 在 publish 区验证默认继承是否正确
当前明确不做:
- 复杂展示编辑器
- 全量 H5 schema 编排平台
- 大型资源后台
关键原则:
- `EventPresentation``ContentBundle` 都要有统一导入入口
- `Event` 继续做业务壳和默认绑定,不吞大资源
- 玩家前端继续只认发布摘要,不认后台草稿对象