7.2 KiB
7.2 KiB
M4 Runtime Inventory State Query 设计(2026-04-22)
更新时间:2026-04-22
0. 文档目标
本文件只冻结当前 M4 的一个最小新增切片:
新增 GET /api/runtime/sessions/:runtimeSessionId/inventory,让 Axum 能从 SpacetimeDB 同步读取当前玩家在指定 runtime_session 下的 inventory_slot 真相态,不提前承诺旧 GameState.playerInventory / playerEquipment 全量兼容。
本轮目标不是把旧前端背包 view model 一次性全迁到 Rust,也不是把 inventory_use / craft / dismantle / reforge 一次性补齐。
1. 为什么先做这个切片
当前 inventory 主链已经具备:
module-inventory已冻结inventory_slot、apply_inventory_mutation的首版领域 contract。spacetime-module已有inventory_slot真相表与apply_inventory_mutationreducer。treasure / quest / battle奖励物品已经能同步写入inventory_slot。
真正缺的部分是:
- 背包真相态还没有稳定查询入口。
api-server还不能按runtime_session_id + 当前用户同步返回当前背包与装备状态。- 后续如果要把背包面板、装备面板或 runtime story projection 收口到
SpacetimeDB,需要先有一个最小 inventory query 切片可复用。
因此本轮先补“只读查询”能力,不提前跳到更重的旧前端状态兼容。
2. 当前冻结范围
本轮只包含以下能力:
- 新增公开接口:
GET /api/runtime/sessions/:runtimeSessionId/inventory - 认证方式:Bearer JWT
- 查询 scope:
runtime_session_id + 当前登录用户 - 数据来源:
SpacetimeDB procedure get_runtime_inventory_state - 返回体只包含:
runtimeSessionIdactorUserIdbackpackItemsequipmentItems
本轮明确不做:
- 不兼容旧
GameState.playerInventory - 不兼容旧
GameState.playerEquipment - 不补按
story_session_id或全局用户维度的 inventory 查询 - 不做 inventory public subscription / view
- 不在查询链路里拼装 quest / npc / battle / story_event
- 不提前做
inventory_use / craft / dismantle / reforge
3. 为什么按 runtime_session_id + 当前用户 查询
当前 inventory_slot 的主作用域字段是:
runtime_session_idactor_user_idstory_session_id
本轮选择 runtime_session_id + actor_user_id 作为最小 query scope,原因如下:
inventory_slot当前是运行态背包真相,不是单个 story session 的临时投影。- 同一
runtime_session下的宝箱、任务、战斗奖励都已经按这个 scope 汇入同一张表。 - 旧前端背包面板语义本质上也是“当前运行态玩家的背包”,不是“某个 story session 的局部背包”。
- 若后续确实需要更细的
story_session投影,应通过上层 façade 或专门 view 提供,而不是先把真相查询 scope 做窄。
4. 接口 contract
4.1 请求
- 方法:
GET - 路径:
/api/runtime/sessions/:runtimeSessionId/inventory - 认证:必须携带 Bearer JWT
- 路径参数:
runtimeSessionId:目标运行时会话 ID
4.2 成功响应
成功响应延续当前 api-server 统一 envelope,data 字段结构为:
{
"runtimeSessionId": "runtime_xxx",
"actorUserId": "user_xxx",
"backpackItems": [
{
"slotId": "invslot_xxx",
"containerKind": "backpack",
"slotKey": "invslot_xxx",
"itemId": "consumable_heal_potion",
"category": "消耗品",
"name": "疗伤药",
"description": "用于恢复少量气血。",
"quantity": 3,
"rarity": "common",
"tags": ["healing"],
"stackable": true,
"stackKey": "heal_potion",
"equipmentSlotId": null,
"sourceKind": "treasure_reward",
"sourceReferenceId": "treasure_xxx",
"createdAt": "2026-04-22T00:00:00.000000Z",
"updatedAt": "2026-04-22T00:01:00.000000Z"
}
],
"equipmentItems": [
{
"slotId": "invslot_weapon_xxx",
"containerKind": "equipment",
"slotKey": "weapon",
"itemId": "weapon_trial_blade",
"category": "武器",
"name": "试作短剑",
"description": "一柄适合入门者的短剑。",
"quantity": 1,
"rarity": "rare",
"tags": ["weapon"],
"stackable": false,
"stackKey": "weapon_trial_blade",
"equipmentSlotId": "weapon",
"sourceKind": "quest_reward",
"sourceReferenceId": "quest_xxx",
"createdAt": "2026-04-22T00:00:00.000000Z",
"updatedAt": "2026-04-22T00:05:00.000000Z"
}
]
}
4.3 错误响应
当前延续 runtime/profile/story 查询已有策略:
SpacetimeClientError::Runtime(_)映射为400- 其他
SpacetimeClientError映射为502 - 错误
details.provider:- 参数构建或本地语义错误:
runtime-inventory - 下游
SpacetimeDB失败:spacetimedb
- 参数构建或本地语义错误:
5. 分层职责
5.1 module-inventory
职责:
- 冻结
RuntimeInventoryStateQueryInput - 冻结
RuntimeInventoryStateSnapshot - 冻结
RuntimeInventorySlotRecord - 负责 builder、validator 与 record 映射
不负责:
- HTTP 路径解析
- JWT 鉴权
- 旧前端背包 view model 编译
5.2 spacetime-module
职责:
- 读取指定
runtime_session_id + actor_user_id下的inventory_slot - 按
container_kind拆成backpackItems / equipmentItems - 复用
module-inventory的 query input / snapshot 结构 - 通过
get_runtime_inventory_stateprocedure 返回当前真相态
5.3 spacetime-client
职责:
- 构造
RuntimeInventoryStateQueryInput - 调用
get_runtime_inventory_state - 把返回结果映射为稳定 Rust record
5.4 api-server
职责:
- 暴露
GET /api/runtime/sessions/:runtimeSessionId/inventory - 做 Bearer JWT 鉴权
- 从 token 中注入
actorUserId - 把 inventory record 映射到 JSON payload
6. 排序规则
为了保证前端和后续 façade 读到稳定顺序,本轮冻结以下排序口径:
backpackItems- 先按
slot_key - 再按
slot_id
- 先按
equipmentItems- 先按装备位固定顺序:
weapon -> armor -> relic - 再按
slot_id
- 先按装备位固定顺序:
当前不在 query 层额外做“按稀有度”“按分类”“按来源”的排序投影。
7. 验收口径
本轮验收只要求以下几点:
module-inventory已补 inventory query contract 与最小测试spacetime-module已新增get_runtime_inventory_stateprocedurespacetime-client已能同步读取 inventory 真相态api-server已真实挂出GET /api/runtime/sessions/:runtimeSessionId/inventorycargo check -p module-inventory -p spacetime-module -p spacetime-client -p api-server可通过npm run check:encoding已执行,确保新增中文文档与接口文件没有编码损坏
8. 后续边界
这条最小 inventory query 落地后,后续再继续拆下一层:
- 评估是否需要补
story session局部 inventory projection - 评估是否需要把 inventory query 接成旧背包面板 view model
- 再继续补
inventory_use / craft / dismantle / reforge - 最后再考虑 inventory subscription / public view
在这些 contract 未冻结前,不应把当前接口误称为“旧背包系统已完整迁移完成”。