# M4 RPG Runtime Inventory SpacetimeDB 基座记录(2026-04-21) 更新时间:`2026-04-21` ## 0. 文档目标 本文件只记录一件事: **把 `module-inventory` 从“只有 README 占位”推进到“已有首版背包领域契约、SpacetimeDB `inventory_slot` 真相表与 `apply_inventory_mutation` reducer”的真实落地结果。** 本轮目标不是一次性迁完 Node 版所有背包玩法,而是先把后续 `story / quest / runtime-item / npc` 都能稳定复用的最小背包真相源立起来。 --- ## 1. 本轮落地范围 本轮只落实下面 4 件事: 1. 新增 `server-rs/crates/module-inventory/` 真实 crate,而不是继续停留在 README 占位。 2. 在 `module-inventory` 中冻结 `inventory_slot`、`apply_inventory_mutation` 的首版领域类型、输入输出和字段校验 helper。 3. 在 `server-rs/crates/spacetime-module/` 中新增 `inventory_slot` 表。 4. 在 `spacetime-module` 中新增 `apply_inventory_mutation` reducer,形成最小可编译背包主链。 --- ## 2. 当前冻结的数据口径 ### 2.1 `inventory_slot` 当前首版字段冻结为: 1. `slot_id` 2. `runtime_session_id` 3. `story_session_id` 4. `actor_user_id` 5. `container_kind` 6. `slot_key` 7. `item_id` 8. `category` 9. `name` 10. `description` 11. `quantity` 12. `rarity` 13. `tags` 14. `stackable` 15. `stack_key` 16. `equipment_slot_id` 17. `source_kind` 18. `source_reference_id` 19. `created_at` 20. `updated_at` 当前策略: 1. `inventory_slot` 采用“单槽位即单真相行”的口径,不再把背包塞回 runtime snapshot 大 JSON。 2. `Backpack / Equipment` 统一进同一张表,通过 `container_kind + slot_key` 区分容器和装备位。 3. 首版堆叠不再依赖 Node 版的隐式 heuristic,统一冻结为 `stackable + stack_key` 显式口径。 ### 2.2 `apply_inventory_mutation` 当前首版只支持 4 类 mutation: 1. `GrantItem` 2. `ConsumeItem` 3. `EquipItem` 4. `UnequipItem` 当前策略: 1. `GrantItem` 负责发放新物品,并在 `Backpack` 内按 `stack_key` 合并可堆叠物品。 2. `ConsumeItem` 负责安全扣减堆叠数量,数量归零时删除槽位。 3. `EquipItem` 负责把背包中的可装备物品移动到目标装备位,并自动把原装备挪回背包。 4. `UnequipItem` 负责把装备位物品退回背包。 --- ## 3. 当前刻意未做 本轮明确没有扩到以下范围: 1. 还没有落 `UseItem / Craft / Dismantle / Reforge` 这类更高阶背包动作。 2. `quest_turn_in` 奖励物品链当前已进入聚合 reducer 接线,但 `npc_trade`、`npc_gift` 仍未落专属 reducer。 3. 当前已经补上最小同步查询切片 `GET /api/runtime/sessions/:runtimeSessionId/inventory`, 但还没有落背包 public view,也没有让前端直读 `inventory_slot`。 4. 还没有把 Node 版 `inventoryMutationService.ts` 整体迁到 Rust,只先冻结首版真相表和最小规则。 也就是说,本轮只是把 `module-inventory` 的基座立起来,不宣称已经完成完整背包玩法迁移。 --- ## 4. 关键规则冻结 ### 4.1 非堆叠物品固定单槽位单数量 当前规则: 1. `stackable = false` 的物品必须固定 `quantity = 1`。 2. 可装备物品固定 `equipment_slot_id != None` 且必须 `stackable = false`。 3. 后续如果要支持“同名但独立词缀装备”,继续沿用“一件装备一条 `inventory_slot`”。 ### 4.2 装备切换不引入新真相副本 当前规则: 1. 装备和卸下都只是在同一条 `inventory_slot` 上切换 `container_kind + slot_key`。 2. 遇到同装备位冲突时,原装备直接回到 `Backpack`,不额外创建临时副本。 3. 这样后续做 Axum façade 或前端 view 时,可以稳定用 `slot_id` 追踪同一件物品。 ### 4.3 背包真相优先,展示读模型后置 当前规则: 1. `module-inventory` 只负责状态真相与 mutation 规则。 2. 若后续需要“前端背包列表”“装备面板读模型”,优先通过 `view` 或 Axum façade 暴露。 3. 不新增第二份背包真相副本,也不回退到多个 service 各自改 JSON。 --- ## 5. 本轮新增的真实工程落点 ### 5.1 新增 crate 1. `server-rs/crates/module-inventory/Cargo.toml` 2. `server-rs/crates/module-inventory/src/lib.rs` ### 5.2 workspace 与主工程聚合 1. `server-rs/Cargo.toml` - 已把 `crates/module-inventory` 纳入 workspace members 2. `server-rs/crates/spacetime-module/Cargo.toml` - 已接入 `module-inventory` 依赖 3. `server-rs/crates/spacetime-module/src/lib.rs` - 已接入 `module-inventory` 类型 - 已新增 `inventory_slot` - 已新增 `apply_inventory_mutation` --- ## 6. 验证目标 本轮应至少验证: 1. `module-inventory` crate 可以独立 `cargo check / cargo test` 2. `spacetime-module` 能成功编译并接入新表与 reducer 3. 不会把现有中文内容写坏,编码检查继续通过 --- ## 7. 下一步建议 按当前节奏,后续应继续按下面顺序推进: 1. 在 `module-inventory` 中继续补 `UseItem / Craft / Dismantle / Reforge` 对应的纯规则契约。 2. 继续把 `npc_trade / npc_gift / runtime-item` 发物链改成显式调用 `apply_inventory_mutation`,并补齐 quest / treasure 之外的奖励入口。 3. 在最小同步查询切片稳定后,再评估是否继续补 `inventory view`、旧前端背包读模型兼容或 public subscription。 4. 最后再把 Node 版 `inventoryMutationService.ts` 的玩法细节逐步迁走。