Files
cmr-mini/doc/gameplay/多赛道Variant前后端最小契约.md

5.7 KiB
Raw Permalink Blame History

多赛道 Variant 前后端最小契约

文档版本v0.1 最后更新2026-04-02 18:33:00

本文档用于定义“多 KML / 多赛道 variant”第一阶段联调所需的最小前后端契约。

目标:

  • 先定最小可联调字段,不一开始追求大而全
  • 保证准备页、launch、地图、恢复、结果页能围绕同一 variantId 工作
  • 避免前端从页面交互反推后端字段

说明:

  • 本文档只定义最小契约建议
  • 不等同于最终后台配置模型
  • 不等同于最终数据库模型
  • 本文档优先服务前后端第一阶段联调

1. 最小联调目标

第一阶段只解决下面 4 件事:

  1. 一个活动可声明多个 variant
  2. 准备页能知道当前活动是否允许手选 / 随机 / 后端指定
  3. launch 能明确返回本局最终绑定的 variantId
  4. result / ongoing / recovery 能持续追溯同一个 variantId

一句话目标:

variantId 成为贯穿一局的稳定事实。


2. 契约原则

2.1 session 绑定优先

  • 前端可参与选择
  • 最终绑定以后端 session 为准

2.2 launch 是最终真相

  • 前端准备页即便做了手选或随机请求
  • 地图页真正消费的仍应是 launch 返回的最终绑定结果

2.3 恢复不重新分配

  • 恢复链只恢复既有 variantId
  • 不重新随机
  • 不重新提示选择

2.4 结果必须可追溯

  • 结果页
  • ongoing session
  • 历史成绩

都建议能反查:

  • variantId
  • 可选 variantName

3. 活动级最小字段建议

建议活动可玩信息中增加一个最小赛道编排块,例如在 play 返回里体现:

3.1 assignmentMode

含义:

  • 当前活动的赛道分配模式

建议最小取值:

  • manual
  • random
  • server-assigned

3.2 courseVariants

含义:

  • 当前活动可用赛道版本列表

建议最小字段:

  • id
  • name
  • description
  • routeCode
  • selectable

备注:

  • 第一阶段不一定要在 play 里返回所有复杂资源
  • 只要足够准备页展示选择即可

推荐最小形态示意:

{
  "play": {
    "assignmentMode": "manual",
    "courseVariants": [
      {
        "id": "variant_a",
        "name": "A 线",
        "description": "适合首次体验",
        "routeCode": "A",
        "selectable": true
      },
      {
        "id": "variant_b",
        "name": "B 线",
        "description": "稍长路线",
        "routeCode": "B",
        "selectable": true
      }
    ]
  }
}

4. launch 最小字段建议

launch 必须承担“最终绑定本局赛道”的责任。

建议在现有 launch 返回中增加一个明确的 variant 绑定块。

4.1 建议字段

  • launch.variant.id
  • launch.variant.name
  • launch.variant.routeCode
  • launch.variant.assignmentMode

如需保守,也可挂到 business 下,但建议语义上单独成块,避免和 release/session 混淆。

4.2 前端输入建议

如果是手选模式,前端建议向 launch 传:

  • variantId

如果是随机模式,前端可以:

  • 不传,由后端分配
  • 或显式传一个 assign=random 请求意图

4.3 输出约束

无论前端是否传入 variantIdlaunch 返回都必须给出最终绑定结果。

因为地图页只应消费:

  • 最终 variantId
  • 对应 manifest / config

不应再依赖准备页上的临时选择状态。

推荐最小形态示意:

{
  "launch": {
    "variant": {
      "id": "variant_b",
      "name": "B 线",
      "routeCode": "B",
      "assignmentMode": "manual"
    },
    "resolvedRelease": {
      "releaseId": "rel_xxx",
      "manifestUrl": "https://..."
    },
    "business": {
      "sessionId": "ses_xxx",
      "sessionToken": "..."
    }
  }
}

5. session / result 最小字段建议

5.1 session 摘要

建议在以下位置都可见:

  • ongoingSession
  • recentSession
  • session detail

最小补充:

  • variantId
  • variantName
  • routeCode

5.2 result

建议 GET /sessions/{sessionPublicID}/result 至少返回:

  • result.session.variantId
  • result.session.variantName
  • result.session.routeCode

这样前端单局结果页和历史结果页都能统一展示。


6. 前端第一阶段落点

前端第一阶段建议只做下面几件事:

6.1 准备页

  • 读取 assignmentMode
  • 读取 courseVariants[]
  • manual 下展示可选赛道列表
  • random 下展示“随机分配”
  • server-assigned 下只展示结果

6.2 launch 适配层

  • launch.variant.* 写入 GameLaunchEnvelope
  • variantId 一起进入地图页和恢复快照

6.3 结果与历史页

  • 显示本局 variantName / routeCode

6.4 故障恢复

  • 快照中补 variantId
  • 恢复时继续使用既有 variantId

7. 第一阶段后端落点

后端第一阶段建议只做下面几件事:

7.1 play

  • 返回 assignmentMode
  • 返回 courseVariants[]

7.2 launch

  • 接收可选 variantId
  • 返回最终绑定后的 variant 信息

7.3 session / result

  • 在 session 摘要和结果里带出 variantId

这样就足够完成第一阶段联调。


8. 和现有体系的关系

这份最小契约不替代现有六层检查,后续一旦开始实现,仍建议按六层检查推进:

  1. 文档
  2. 配置源
  3. 解析层
  4. 编译层
  5. 消费层
  6. 发布与联调层

特别是:

  • 配置层后续如果引入 courseVariants
  • 解析层如果开始读取多 variant 结构
  • 编译层如果开始按 variantId 产出 runtime profile

这三层都不能跳。


9. 一句话结论

多赛道第一阶段联调只需要先定住 assignmentModecourseVariants[]launch.variant.*session/result.variant* 这四组最小字段,让 variantId 成为贯穿一局的稳定事实。