8.2 KiB
M3:runtime settings Axum + SpacetimeDB 落地设计
日期:2026-04-21
关联任务:
- ../../backend-rewrite-tasklist/02_M3_RUNTIME_PROFILE.md
- ../../backend-rewrite-tasklist/M0_PHASE_ACCEPTANCE_MATRIX_2026-04-20.md
关联现状:
- SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md
- CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md
server-node/src/routes/rpg-profile/rpgProfileRoutes.tsserver-node/src/repositories/runtimeRepository.ts
1. 文档目的
02_M3_RUNTIME_PROFILE.md 已经冻结了 M3 的任务范围,但还没有把首批可编码切片细化到表字段、procedure、Axum facade、兼容错误格式和测试策略。
本文件只解决 M3 第一批最小纵向切片:
GET /api/runtime/settingsPUT /api/runtime/settings
以及其在 Rust 重写中的完整落位:
module-runtime的字段约束与 DTOcrates/spacetime-module的runtime_setting表与 procedurecrates/spacetime-client的 procedure 调用封装crates/api-server的兼容 facade 与响应 contract
本文件不新增 checklist,不替代 ../../backend-rewrite-tasklist/02_M3_RUNTIME_PROFILE.md,只补足可以直接编码的技术口径。
2. 为什么先做 runtime settings
在 M3 范围内,runtime settings 是当前最适合先迁移的纵向切片:
- 读写模型最小,只依赖
user_id + music_volume + platform_theme。 - 旧 Node 逻辑没有跨表聚合、副作用和复杂 projection。
- 前端 contract 清晰,兼容路径只有一条,不涉及
/api/profile/*双路径。 - 可以先把
Axum -> JWT -> SpacetimeDB procedure -> 标准 envelope主链跑通,为后续browse history / snapshot / save archive / dashboard复用。
3. 旧实现冻结口径
当前 Node 侧 runtime settings 行为来自:
server-node/src/routes/rpg-profile/rpgProfileRoutes.tsserver-node/src/repositories/runtimeRepository.ts
冻结行为如下:
3.1 路由
GET /api/runtime/settingsPUT /api/runtime/settings
两条接口都要求 JWT。
3.2 请求体
PUT /api/runtime/settings 请求体:
{
"musicVolume": 0.42,
"platformTheme": "light"
}
校验规则:
musicVolume必须在0 ~ 1。platformTheme只接受light | dark。
3.3 默认值
默认值来自 packages/shared/src/contracts/runtime.ts:
DEFAULT_MUSIC_VOLUME = 0.42DEFAULT_PLATFORM_THEME = "light"
当用户从未写入过设置时,读取接口必须返回默认值,而不是 404 或 null。
3.4 归一化规则
旧 Node 写入时会做以下归一化:
musicVolume强制 clamp 到0 ~ 1platformTheme如果不是dark,统一回退到light
Rust 重写阶段仍保持同样语义,避免前端产生行为漂移。
4. Rust 落位决议
4.1 crate 分工
本切片固定按以下边界落位:
crates/module-runtime- 定义
RuntimeSettings领域 DTO、默认值、字段校验与归一化规则。
- 定义
crates/spacetime-module- 定义
runtime_setting表。 - 提供
upsert_runtime_setting_and_returnprocedure。
- 定义
crates/spacetime-client- 提供
get_runtime_settings、put_runtime_settings调用封装。
- 提供
crates/api-server- 提供
GET/PUT /api/runtime/settings。 - 保持当前 envelope / 错误格式 / 请求头兼容。
- 提供
4.2 身份边界
当前阶段前端仍只访问 Axum,不直连 SpacetimeDB。
因此:
- 用户身份仍由 Axum 侧 JWT middleware 校验。
- Axum 从已校验的 access token claims 中取
user_id。 user_id作为 procedure 入参写入runtime_setting。
注意:
- 这不是最终的 SpacetimeDB 原生身份透传形态。
- 在 M3 首批切片里,先以 Axum 作为唯一鉴权边界,保证与当前前端 contract 一致。
5. SpacetimeDB 表设计
5.1 表名
runtime_setting
5.2 字段
| 字段 | 类型 | 说明 |
|---|---|---|
user_id |
String |
主键,绑定平台用户 |
music_volume |
f32 |
音量,持久化归一化后的值 |
platform_theme |
RuntimePlatformTheme |
平台主题枚举 |
created_at |
Timestamp |
首次创建时间 |
updated_at |
Timestamp |
最近更新时间 |
5.3 设计决议
- 每个用户只保留一行设置,不做历史版本表。
user_id直接作为主键,避免再引入无业务价值的自增 ID。platform_theme固定为枚举,不把light/dark继续散落成字符串字面量。- 首批阶段不把设置拆成多行 KV 表,避免简单需求被过度抽象。
6. Procedure 设计
6.1 不单独暴露 reducer 给 Axum
本切片优先提供 procedure,而不是让 Axum 直接调 reducer + 再查询表。
原因:
- 当前
spacetime-client已经以 procedure 返回结果的模式承接资产链。 - 设置接口需要同步返回最终写入结果,procedure 可减少一次额外查询。
- 当前
runtime_setting不需要客户端订阅,private table + procedure 更直接。
6.2 Procedure 列表
get_runtime_setting_or_defaultupsert_runtime_setting_and_return
返回 DTO 固定为:
RuntimeSettingSnapshot {
user_id
music_volume
platform_theme
created_at_micros
updated_at_micros
}
如果用户还没有设置记录:
get_runtime_setting_or_default返回默认值快照。- 但不强制立即插入表,避免纯读取请求制造无意义写入。
7. Axum facade 设计
7.1 GET /api/runtime/settings
行为:
- 走
require_bearer_auth。 - 从
claims.user_id取用户 ID。 - 调
spacetime_client.get_runtime_settings(user_id)。 - 返回:
{
"musicVolume": 0.42,
"platformTheme": "light"
}
7.2 PUT /api/runtime/settings
行为:
- 走
require_bearer_auth。 - 使用 Axum
Json+serde解析请求。 - 在
module-runtime内做归一化。 - 调
spacetime_client.put_runtime_settings(user_id, payload)。 - 返回归一化后的最终值。
7.3 错误映射
- 请求体解析失败:
400 BAD_REQUEST - 字段校验失败:
400 BAD_REQUEST - SpacetimeDB 调用失败:
502 BAD_GATEWAY - JWT 缺失或失效:沿用现有
401 UNAUTHORIZED
错误 details.provider 固定为:
runtime-settings:本地字段归一化或 DTO 构建失败spacetimedb:procedure 调用失败
8. 首批测试策略
本切片测试分两层:
8.1 必跑测试
module-runtime- 默认值
- clamp 规则
- theme 归一化
api-server- 未登录返回
401 - 请求 envelope 打开时返回标准
ok/data/error/meta - JSON 结构与字段名兼容
- 未登录返回
8.2 可选联调测试
补一条 #[ignore] 的集成测试:
- 需要本地 SpacetimeDB 已启动
- 需要当前
spacetime-module已发布 - 验证
PUT -> GET能往返一致
原因:
- 当前仓库已有资产链的
#[ignore]集成测试模式。 - 在未稳定建立测试 harness 前,不强制把 SpacetimeDB 作为默认单测前置条件。
9. 后续扩展顺序
runtime settings 完成后,M3 后续能力按以下顺序推进:
user_browse_historyruntime_snapshotprofile_save_archiveprofile_dashboard_state + profile_wallet_ledger + profile_played_world
顺序原因:
browse_history仍是单表为主,只带去重与排序规则。snapshot和save_archive依赖兼容聚合策略,复杂度更高。dashboard / play-stats / wallet-ledger依赖 projection,更适合放在 snapshot 规则固定后收口。
10. 本文完成定义
当以下条件成立时,本设计文档视为完成:
runtime settings的字段、默认值、归一化规则、procedure 与 Axum facade 已书面冻结。- 后续编码无需再猜测:
- 表字段名
- 主键策略
- 默认值来源
- Axum 与 SpacetimeDB 的职责边界
- 可以直接据此开始
module-runtime、spacetime-module、spacetime-client、api-server编码。