# M4 RPG Runtime Story SpacetimeDB 基座记录(2026-04-21) 更新时间:`2026-04-22` ## 0. 文档目标 本文件只记录一件事: **把 `M4` 从“只有任务清单和 crate 占位”推进到“SpacetimeDB 侧已有最小可编译 story 会话基座”的真实落地结果。** 本轮只落最小骨架,不扩到完整 runtime story action 迁移,不改前端交互界面设计。 --- ## 1. 本轮落地范围 本轮按 `M4` 与 `RPG_ENTRY_RUNTIME_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md` 的交叉口径,只落实下面 6 件事: 1. 新增 `server-rs/crates/module-story/` 真实 crate,而不是继续停留在 README 占位。 2. 在 `module-story` 中冻结 `story_session / story_event` 的首版领域类型、ID 前缀、状态枚举与字段校验 helper。 3. 在 `server-rs/crates/spacetime-module/` 中新增 `story_session`、`story_event` 两张表。 4. 在 `spacetime-module` 中新增 `begin_story_session`、`continue_story` 两个 reducer,形成最小可编译会话主链。 5. 在 `spacetime-module` 中新增 `begin_story_session_and_return`、`continue_story_and_return` 两个 procedure,让 Axum 可同步拿到结果快照。 6. 在 `spacetime-client` 与 `api-server` 中新增最小 story session facade,打通 `server-rs` 侧纵向调用链。 --- ## 2. 本轮新增的真实工程落点 ### 2.1 新增 crate 1. `server-rs/crates/module-story/Cargo.toml` 2. `server-rs/crates/module-story/src/lib.rs` ### 2.2 workspace 与主工程聚合 1. `server-rs/Cargo.toml` - 已把 `crates/module-story` 纳入 workspace members 2. `server-rs/crates/spacetime-module/Cargo.toml` - 已接入 `module-story` 依赖 3. `server-rs/crates/spacetime-module/src/lib.rs` - 已接入 `module-story` 类型 - 已新增 `story_session` - 已新增 `story_event` - 已新增 `begin_story_session` - 已新增 `continue_story` - 已新增 `begin_story_session_and_return` - 已新增 `continue_story_and_return` 4. `server-rs/crates/spacetime-client/src/lib.rs` - 已新增 `begin_story_session(...)` - 已新增 `continue_story(...)` 5. `server-rs/crates/api-server/src/story_sessions.rs` - 已新增 `POST /api/story/sessions` - 已新增 `POST /api/story/sessions/continue` --- ## 3. 当前冻结的数据口径 ### 3.1 `story_session` 当前首版字段冻结为: 1. `story_session_id` 2. `runtime_session_id` 3. `actor_user_id` 4. `world_profile_id` 5. `initial_prompt` 6. `opening_summary` 7. `latest_narrative_text` 8. `latest_choice_function_id` 9. `status` 10. `version` 11. `created_at` 12. `updated_at` 当前策略: 1. `story_session` 保持 private 真相表口径。 2. 当前只解决“故事会话存在、版本递增、最新叙事状态可追踪”。 3. 不在本轮提前塞入 quest、combat、npc、inventory 混合字段。 ### 3.2 `story_event` 当前首版字段冻结为: 1. `event_id` 2. `story_session_id` 3. `event_kind` 4. `narrative_text` 5. `choice_function_id` 6. `created_at` 当前策略: 1. 事件先只承接 `SessionStarted / StoryContinued` 两类最小事件。 2. 先证明事件追加模型能工作,再扩到 `resolve_story_action` 真实子域事件。 --- ## 4. 当前 reducer 口径 ### 4.1 `begin_story_session` 当前负责: 1. 校验 `StorySessionInput` 2. 拒绝重复 `story_session_id` 3. 写入 `story_session` 4. 追加一条 `SessionStarted` 事件 ### 4.2 `continue_story` 当前负责: 1. 校验 `StoryContinueInput` 2. 校验目标 `story_session` 必须存在 3. 以事件追加方式写入 `story_event` 4. 递增 `story_session.version` 5. 更新 `latest_narrative_text / latest_choice_function_id / updated_at` --- ## 5. 当前 procedure / facade 口径 ### 5.1 `begin_story_session_and_return` 当前负责: 1. 在单次 procedure 调用里执行 `begin_story_session_tx` 2. 直接返回 `storySession + storyEvent` 快照 3. 供 `spacetime-client` 与 `api-server` 直接同步消费 ### 5.2 `continue_story_and_return` 当前负责: 1. 在单次 procedure 调用里执行 `continue_story_tx` 2. 直接返回推进后的 `storySession + storyEvent` 快照 3. 避免 Axum 再额外读取 private table ### 5.3 `spacetime-client` story facade 当前已新增: 1. `begin_story_session(...)` 2. `continue_story(...)` 当前策略: 1. `spacetime-client` 负责把 `module-story` 输入 builder 映射到 generated bindings。 2. procedure 错误统一折叠为 `SpacetimeClientError`,供 Axum 映射为 `400 / 502`。 ### 5.4 `api-server` story session facade 当前已新增: 1. `POST /api/story/sessions` 2. `POST /api/story/sessions/continue` 当前 contract: 1. 两个接口都要求 Bearer JWT。 2. `actorUserId` 由 JWT claims 提供,不允许前端透传。 3. `storySessionId` / `eventId` 由 Rust 服务端使用 `module-story` 的 ID helper 生成。 4. 两个接口当前都返回: - `storySession` - `storyEvent` --- ## 6. 当前刻意未做 本轮明确没有扩到以下范围: 1. 还没有落 `resolve_story_action` 2. 还没有落 `sync_runtime_snapshot_projection` 3. 还没有接入 `npc_state / quest_record / battle_state / inventory_slot` 4. 还没有兼容旧 `POST /api/runtime/story/actions/resolve` 5. 还没有兼容旧 `GET /api/runtime/story/state/:sessionId` 6. 还没有兼容旧 `POST /api/runtime/story/state/resolve` 7. 还没有兼容旧 `POST /api/runtime/story/initial` 8. 还没有兼容旧 `POST /api/runtime/story/continue` 9. 还没有把 `server-node` 现有 `rpg-runtime-story` 主链切换到 `server-rs` 10. 还没有改任何前端交互界面设计 也就是说,本轮只是把 `M4` 的 SpacetimeDB 会话基座与最小 Axum facade 立起来,不宣称已经完成 runtime story 兼容迁移。 --- ## 7. 验证结果 本轮已执行: 1. `cargo check -p module-story --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml` 2. `cargo check -p spacetime-module --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml` 3. `cargo check -p spacetime-module --target wasm32-unknown-unknown --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml` 4. `spacetime generate --no-config --lang rust --out-dir D:\\Genarrative\\server-rs\\crates\\spacetime-client\\src\\module_bindings --module-path D:\\Genarrative\\server-rs\\crates\\spacetime-module --include-private --yes` 5. `cargo check -p spacetime-client --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml` 6. `cargo check -p api-server --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml` 7. `npm run check:encoding` 结果: 1. 全部通过。 --- ## 8. 下一步建议 按当前节奏,后续应继续按下面顺序推进: 1. 先冻结 `story state` 查询 contract,明确是新 `/api/story/sessions/:storySessionId/state` 还是兼容旧 `/api/runtime/story/state/*`。 2. 再把 `story_session` 与 `runtime_snapshot` 的 projection / sync 边界补清。 3. 再把 `resolve_story_action` 的输入/输出与 `RuntimeStoryActionRequest` 对齐成下一个 reducer / procedure 设计。 4. 再逐步把 `npc / quest / treasure / combat` 子域动作接成显式事件与独立 reducer。 5. 最后再处理旧 Node `runtime story` 兼容接口与前端实际切换。 --- ## 9. 后续增量状态(`2026-04-22`) 在本文件记录的首轮 story session 基座之上,当前仓库又继续补了两条与 `M4 story runtime` 直接相关的增量切片: 1. 已补 `GET /api/story/sessions/:storySessionId/state` - 当前只返回 `storySession + storyEvents` - 不兼容旧 `RuntimeStoryActionResponse` 2. 已补 `GET /api/story/battles/:battleStateId` - 当前只返回单个 `battleState` - 供 battle 刷新、重连和后续 story 编排复用 3. 已补 `POST /api/story/npc/battle` - 当前只承接 `npc_fight / npc_spar` - 同步返回 `npcInteraction + battleState` 同时,本轮还完成了以下工程收口: 1. 已重新执行 `spacetime generate --no-config --lang rust --out-dir D:\\Genarrative\\server-rs\\crates\\spacetime-client\\src\\module_bindings --module-path D:\\Genarrative\\server-rs\\crates\\spacetime-module --include-private --yes`。 2. 已把 `spacetime-client` 中 battle query 的占位实现替换为真实 procedure 调用。 3. 已再次执行 `cargo check -p spacetime-client --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml` 与 `cargo check -p api-server --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml` 并通过。 当前仍需继续追的验证项: 1. `story_sessions` / `story_battles` 相关二进制测试在当前机器上编译时间较长,还没有在单次时窗内拿到最终断言结果。 2. `npm run check:encoding` 已启动,但尚未在单次时窗内跑完。 因此,当前准确口径应为: 1. `M4 story session / story state / battle state / NPC 开战` 的最小后端编译链已经打通。 2. 旧 `runtime story` 兼容接口与旧 view model 兼容仍未完成。 3. 长时回归测试与编码检查仍在继续推进,不应提前宣称整阶段验收完成。