# Backend
> 文档版本：v1.41
> 最后更新：2026-04-07 18:15:01


这套后端现在已经能支撑一条完整主链：

`entry -> auth -> home/cards -> event play -> launch -> session -> result`

当前已实现接口总数：

- `116`

开发环境补充说明：

- `/admin/ops-workbench` 与 `/ops/admin/*` 在 `APP_ENV != production` 时默认免登录可用。
- 即使浏览器里残留旧的玩家 token、过期 token 或非 ops token，运维链也会自动回退到 dev ops 上下文。
- 生产环境不变，仍然必须使用正式 ops token。
- 运维后台当前采用“左侧流程导航 + 中间单主视图 + 右侧状态/日志”结构，不再把地图、路线、活动、发布动作平铺在一个长页面里。
- `资源总览` 先展示关键统计与当前运行时信息；地图页只保留关联活动数量和摘要，活动明细统一放到 `活动管理 / 活动编排`。
- `地图 / 地点管理` 当前按“地图列表优先”组织：
  - 先看地图列表
  - 右上角只放 `添加地图 / 添加地点`
  - 地点编辑区改成省/市两级选择
  - 一张地图只属于一个地点，一个地点可挂多张地图

新增的游客模式公开接口：

- `GET /public/experience-maps`
- `GET /public/experience-maps/{mapAssetPublicID}`
- `GET /public/events/{eventPublicID}`
- `GET /public/events/{eventPublicID}/play`
- `POST /public/events/{eventPublicID}/launch`

这组接口用于支撑“未登录游客只体验默认活动”的最小链路。

新增的地图资源与默认体验活动接口：

- `GET /experience-maps`
- `GET /experience-maps/{mapAssetPublicID}`

这组接口用于支撑“地图列表下的默认活动”最小产品化需求。

新增的运维地图管理接口：

- `GET /ops/admin/region-options`
- `GET /admin/map-assets`
- `PUT /admin/map-assets/{mapAssetPublicID}`
- `GET /ops/admin/map-assets`
- `PUT /ops/admin/map-assets/{mapAssetPublicID}`
- `GET /ops/admin/course-sources`
- `GET /ops/admin/course-sources/{sourcePublicID}`
- `GET /ops/admin/course-sets/{courseSetPublicID}`
- `POST /ops/admin/events`
- `PUT /ops/admin/events/{eventPublicID}`

这组接口用于把运维后台收成统一主流程：

- 地图管理：列表 / 新增 / 编辑 / 详情 / 关联活动
- KML / 赛道管理：围绕当前地图查看赛道集和默认路线
- 活动管理：列表 / 新增 / 修改 / 详情 / 默认绑定 / 发布
- 运维后台 UI 当前也已按“左侧导航 + 单主视图 + 右侧常驻状态栏”收口，不再把所有功能平铺在一个长页面里
- 主区当前已改成宽屏自适应，不再固定窄宽度浪费屏幕空间

并且已经按“配置驱动游戏”收口：

- 业务对象是 `event`
- 运行配置对象是 `event_release`
- 真正进入游戏时客户端消费的是 `manifest_url`
- `session` 会固化当时实际绑定的 `release`

当前还要明确一条业务规则：

- 玩家进入游戏，必须基于“已发布 release”
- `event` 默认绑定、活动草稿配置、未发布 presentation / content bundle 都不能直接作为玩家正式进入依据
- 当前 `currentPresentation` / `currentContentBundle` 在玩家链路里表示的是：
  - 当前已发布 release 实际绑定的展示版本摘要
  - 当前已发布 release 实际绑定的内容包摘要
- 它们不是 event 草稿默认值摘要
- 当前 `play.canLaunch` 和 `launch` 也已按同一套规则收口：
  - 只有当当前发布 release 同时具备：
    - `manifest`
    - `runtime`
    - `presentation`
    - `content bundle`
    时，玩家才允许正式进入
- 游客模式当前只允许进入：
  - `is_default_experience = true` 的活动
  - 且必须基于当前已发布 release
- 游客模式当前不开放：
  - `/me/entry-home`
  - `/me/results`
  - 用户历史成绩与报名态
- 游客模式 `launch` 会复用正式 session 模型，但返回：
  - `launch.source = public-default-experience`
  - `launch.business.isGuest = true`

当前 workbench 里新增的“当前 Launch 实际配置摘要”仅用于调试：

- 它会由 backend 代读当前 launch 对应的 manifest
- 用来显示：
  - `configUrl`
  - `releaseId`
  - `manifestUrl`
  - `schemaVersion`
  - `playfield.kind`
  - `game.mode`
- 这块只服务联调排查，不参与正式客户端运行链路
- 正式客户端仍应直接消费 `launch` 返回的：
  - `launch.config.configUrl`
  - `launch.resolvedRelease.manifestUrl`

当前 workbench 里新增的“前端调试日志”也仅用于联调：

- frontend 可将页面侧调试日志 `POST` 到 `/dev/client-logs`
- backend 会临时保留最近 200 条日志，供 workbench 查看与清空
- 这块只用于联调排查，不替代正式生产日志体系

当前 demo 真实输入第一刀也已经接入：

- workbench 的玩法切换会自动填入 backend 内置的：
  - `game manifest`
  - `presentation schema`
  - `content manifest`
- 这些 demo 资源通过 backend 提供的 dev 路由读取：
  - `GET /dev/demo-assets/manifests/{demoKey}`
  - `GET /dev/demo-assets/presentations/{demoKey}`
  - `GET /dev/demo-assets/content-manifests/{demoKey}`

当前 workbench 的 `Bootstrap` 语义也已经拆开：

- `Bootstrap Demo（只准备数据）`
  - 只准备 demo 测试数据和基础已发布态，不额外重新发布当前玩法
- `Bootstrap + 发布当前玩法`
  - 先准备 demo，再对当前选中的玩法执行一遍“发布活动配置（自动补 Runtime）”
- 这两条路由只服务联调，不进入正式客户端发布链
- 当前三条标准 demo 在 `Bootstrap Demo（只准备数据）` 后都会直接具备：
  - 当前 release
  - runtime
  - presentation
  - content bundle
- 也就是说，frontend 当前从首页选顺序赛、积分赛、多赛道时，已经可以直接走“当前已发布 release”语义联调入口、详情和 `canLaunch`
- 当前联调样例文案也已从 `Demo ...` 收口为中文活动样例，便于前端和总控直接对口排查
- 当前 manual 多赛道 demo 也已切到 4 条正式 OSS KML：
  - `gotomars/kml/lxcb-001/2026-04-07/route01.kml`
  - `gotomars/kml/lxcb-001/2026-04-07/route02.kml`
  - `gotomars/kml/lxcb-001/2026-04-07/route03.kml`
  - `gotomars/kml/lxcb-001/2026-04-07/route04.kml`
- 资源目录约束同步明确：
  - 正式资源目录只认 `OSS / CDN`
  - 本地 `tmp/` 仅作为临时收件箱，不参与正式发布源
- 当前运维入口第一期已开始落地，先开放两条最小录入链：
  - `POST /admin/ops/tile-releases/import`
  - `POST /admin/ops/course-sets/import-kml-batch`
- 当前目标是先把：
  - 地图瓦片版本录入
  - KML 批量录入并组装为 `course set / variants`
  收成可重复执行的运维入口，而不是继续依赖手工脚本或改代码上传 OSS
- 当前运维入口第二期已起步，开始支持统一资源纳管：
  - `GET /admin/assets`
  - `POST /admin/assets/register-link`
  - `POST /admin/assets/upload`
  - `GET /admin/assets/{assetPublicID}`
- 当前边界：
  - 允许“上传文件”与“登记外链”两种录入模式
  - backend 负责 OSS 存储和资源对象登记
  - 运维后台当前开始使用独立鉴权链：
    - `/ops/auth/*`
    - `/ops/admin/*`
  - 设计目标：
    - 运维账号与前端玩家账号完全分离
    - 生产环境走手机号验证码注册/登录
    - 后续可扩多租户与角色分级
  - 已新增独立运维入口：
    - `GET /admin/ops-workbench`
  - 当前开始把“调试后台”和“运维后台”拆开：
    - `/dev/workbench` 继续只做联调、回归、日志与摘要
    - `/admin/ops-workbench` 只做资源录入、OSS 纳管、地图/KML 导入
  - 当前开发环境为了录资源和调发布方便，运维后台默认免登录放行：
    - 可直接打开 `/admin/ops-workbench`
    - 可直接调用 `/ops/admin/*`
    - 只有主动验证运维账号链路时，才需要使用 `/ops/auth/*`
  - `Import Tile Release / Import KML Batch` 当前已从调试工作台主操作区迁到：
    - `/admin/ops-workbench`
  - `/dev/workbench` 当前只保留运维后台入口说明与跳转，不再承载正式资源录入操作
  - `/admin/ops-workbench` 当前已收成 5 块结构：
    - 资源总览
    - 地图资源管理
    - 资源录入
    - 赛道集管理
    - 活动绑定
    - 发布中心
  - 当前地图资源管理第一刀已接进运维后台：
    - 读取地点列表
    - 新建地点
    - 读取地点详情
    - 新建地图资源
    - 读取地图详情
    - 查看当前瓦片版本与默认活动摘要

当前活动卡片列表最小产品化第一刀也已经进入 backend：

- `/cards`
- `/home`
- `/me/entry-home`

这三处当前已统一补齐最小活动卡片摘要字段：

- `summary`
- `status`
- `statusCode`
- `timeWindow`
- `ctaText`
- `isDefaultExperience`
- `eventType`
- `currentPresentation`
- `currentContentBundle`

当前口径：

- 卡片摘要与详情页继续共用同一套“当前发布 release 摘要”语义
- `currentPresentation / currentContentBundle` 仍表示：
  - 当前已发布 release 实际绑定的展示版本摘要
  - 当前已发布 release 实际绑定的内容包摘要
- `isDefaultExperience` 当前由卡片显式字段控制
- `timeWindow / ctaText` 当前先按后端派生规则提供，允许后续继续演进

当前“准备页地图预览 V1”也已开始接入，但仍保持只读增强项边界：

- 当前只把预览字段挂在：
  - `GET /events/{eventPublicID}`
  - `GET /events/{eventPublicID}/play`
- 当前新增字段为：
  - `preview.mode`
  - `preview.baseTiles.tileBaseUrl`
  - `preview.baseTiles.zoom`
  - `preview.baseTiles.tileSize`
  - `preview.viewport.width / height`
  - `preview.viewport.minLon / minLat / maxLon / maxLat`
  - `preview.variants[].controls`
  - `preview.variants[].legs`
  - `preview.selectedVariantId`
- 当前只服务准备页只读地图预览：
  - 不进入正式 launch 主链
  - 不单独造新的地图资源体系
- 非 demo / 非带预览元数据的活动允许返回空
- workbench 当前也已新增固定摘要卡：
  - `准备页地图预览状态`
  - 用于直接查看当前活动回出的 preview 关键字段

## 文档导航

- [文档索引](D:/dev/cmr-mini/backend/docs/README.md)
- [系统架构](D:/dev/cmr-mini/backend/docs/系统架构.md)
- [核心流程](D:/dev/cmr-mini/backend/docs/核心流程.md)
- [API 清单](D:/dev/cmr-mini/backend/docs/接口清单.md)
- [数据模型](D:/dev/cmr-mini/backend/docs/数据模型.md)
- [配置管理方案](D:/dev/cmr-mini/backend/docs/配置管理方案.md)
- [资源对象与目录方案](D:/dev/cmr-mini/backend/docs/资源对象与目录方案.md)
- [后台管理最小方案](D:/dev/cmr-mini/backend/docs/后台管理最小方案.md)
- [开发说明](D:/dev/cmr-mini/backend/docs/开发说明.md)

## 快速启动

1. 配置环境变量，参考 [`.env.example`](D:/dev/cmr-mini/backend/.env.example)
2. 按顺序执行 [migrations](D:/dev/cmr-mini/backend/migrations)
3. 启动服务

```powershell
cd D:\dev\cmr-mini\backend
.\start-backend.ps1
```

## 当前重点

- 统一登录：短信 + 微信小程序
- 多入口：`tenant + entry_channel`
- 首页聚合：`/home`、`/cards`、`/me/entry-home`
- 配置驱动启动：`/events/{id}/play`、`/events/{id}/launch`
- 局生命周期：`start / finish / detail`
- 局后结果：`/sessions/{id}/result`、`/me/results`
- 第一阶段生产骨架：`places / map-assets / tile-releases / course-sources / course-sets / course-variants / runtime-bindings`
- 第三刀最小接线：`runtimeBinding -> eventRelease -> launch.runtime`
- 第四刀发布闭环：`publish(runtimeBindingId) -> eventRelease -> launch.runtime`
- 活动运营域第二阶段：`event_presentations / content_bundles / event_release -> presentation,bundle,runtime`
- 活动运营域第二阶段第二刀：`event detail / event play / launch -> presentation,bundle 摘要`
- 活动运营域第二阶段第三刀：`release 摘要闭环 + content bundle import`
- 活动运营域第二阶段第四刀：`presentation import + event 默认 active 绑定 + publish 默认继承`
- 开发工作台：`/dev/workbench`
  - 用户主链调试
  - 资源对象与 Event 组装调试
  - Build / Publish / Rollback 调试
  - Release / RuntimeBinding 最小挂接验证
  - Event Presentation / Content Bundle 最小挂接验证
  - Content Bundle Import 最小导入验证
  - Presentation Import / Event 默认绑定 / Publish 默认继承验证
  - Runtime 自动补齐 + 默认绑定发布一键验证
  - Bootstrap Demo 自动回填最小生产骨架 ID
  - 一键测试环境：可从空白状态自动准备 demo event、source/build/release、presentation、content bundle、place、map asset、tile release、course source、course set、course variant、runtime binding，并输出逐步日志与预期判定
  - 一键标准回归：在标准发布链跑通后，继续自动验证 `play / launch / result / history`
  - 真实输入替换第一刀：`Bootstrap Demo` 已改用真实可访问的 KML 与地图资源 URL
  - manual 多赛道 demo：已切到真实 `c01.kml / c02.kml` 输入
  - 前端调试日志：
    - `POST /dev/client-logs`
    - `GET /dev/client-logs`
    - `DELETE /dev/client-logs`
  - 显式玩法入口：
    - 顺序赛：`evt_demo_001`
    - 积分赛：`evt_demo_score_o_001`
    - 多赛道：`evt_demo_variant_manual_001`


