# T2B 协作清单 > 文档版本:v1.15 > 最后更新:2026-04-03 19:26:23 说明: - 本文件由总控维护,写给后端线程 - 目标是把“后台生产闭环”第一阶段需要落地的东西讲清楚 - 只写当前阶段实施说明,不写长讨论稿 - 正式架构文档以 [后台生产闭环架构草案](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 并发布` 已可从空白状态跑完整测试链 - `一键标准回归` 与 `回归结果汇总` 已接入标准联调入口 - `前端调试日志` 与 `当前 Launch 实际配置摘要` 已接入 workbench - 三类标准 demo 入口已显式挂出: - `evt_demo_001` - `evt_demo_score_o_001` - `evt_demo_variant_manual_001` - workbench 日志已补齐: - 分步日志 - 真实错误 - stack - 最后一次 curl - 预期判定 当前主线不再是继续补对象,而是进入: **联调标准化阶段** 本阶段 backend 的核心任务只有 3 件事: 1. 固化“一键测试”链路,确保从空白环境可重复跑通 2. 固化详细日志口径,失败时明确定位在哪一步 3. 固化稳定测试数据,并逐步支持更接近生产的真实输入 当前认为“真实输入替换第二刀”已经完成,backend 当前已完成: **活动卡片列表最小产品化配合第一刀** 优先顺序建议: 1. 维持现有一键回归链稳定 2. 为活动列表页补最小卡片摘要接口或字段 3. 保持活动详情页与列表页使用同一套活动摘要口径 说明: - 真实 `content manifest` - 真实 `presentation schema` - 真实 `活动文案样例` 这三类输入已接入,当前不再作为本轮重点。 原则: - 仍走同一条一键回归链 - 不重新设计联调流程 - 只是把 demo 输入逐步换成更接近生产的真实输入 当前 backend 不建议切去做: - 新的玩家侧页面入口 - 更多管理对象 - 更复杂后台 UI ### 0.5 当前分工 backend 当前这一轮已完成: 1. 活动卡片列表最小字段集对应的后端摘要 2. 卡片摘要接口/返回字段收口 3. 默认体验活动与普通活动的状态标记 当前进入: **活动卡片列表最小产品化第一刀联调回归与小范围修复配合阶段** 要求: - 继续挂在现有标准 demo 与一键回归链上 - 不新开流程 - 不新开对象层级 - 优先响应前端在列表页第一刀联调中暴露的字段、默认值和语义问题 当前进一步明确 backend 的执行口径如下: ### 0.1 一键测试链路 请继续以这条链作为唯一标准联调入口维护: ```text Bootstrap Demo -> 一键补齐 Runtime 并发布 -> launch / play / result / history 回归 ``` 要求: - 从空白环境直接可跑 - 不依赖手工预铺 6~8 个对象 - 同一条链可反复执行 - 失败时能明确知道卡在哪一跳 ### 0.2 详细日志口径 workbench 和相关 backend 调试输出,当前应至少统一包含: - 当前步骤名 - 核心输入参数 - 真实错误信息 - stack - 最后一次 curl - 预期判定 不要只输出“失败了”,要能回答: - 是哪一步失败 - 为什么失败 - 用什么请求复现 ### 0.3 稳定测试数据 当前 demo 数据不要继续散落手工维护,统一以 backend 准备的一键测试数据为准。 后续逐步支持以下更接近生产的真实输入: - 地图资源 URL - KML / 赛道文件 - 内容 manifest - presentation schema - 活动文案样例 ### 0.4 当前不建议做 联调标准化阶段不要继续发散去做: - 新对象扩张 - 新管理面板 - 更复杂 workbench UI - 复杂后台运营功能 - 与当前联调闭环无关的页面能力 当前不建议 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` 继续做业务壳和默认绑定,不吞大资源 - 玩家前端继续只认发布摘要,不认后台草稿对象