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