Files
Genarrative/docs/technical/M4_MODULE_COMBAT_STATE_QUERY_DESIGN_2026-04-22.md
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

6.2 KiB
Raw Blame History

M4 module-combat battle state 查询设计2026-04-22

更新时间:2026-04-22

补充状态:2026-04-22

当前 battle query 纵切片已经完成到“真实可编译、可生成 binding、可被 Axum 调用”的状态:

  1. spacetime-module 中的 get_battle_state procedure 已稳定存在。
  2. spacetime-client/src/module_bindings 已重新执行 spacetime generate,当前已真实包含:
    • battle_state_query_input_type
    • get_battle_state_procedure
    • battle_state.reward_items 对应字段
  3. spacetime-client/src/lib.rs 里原本返回“binding 尚未生成”的占位 get_battle_state(...) 已替换为真实 procedure 调用。
  4. cargo check -p spacetime-clientcargo check -p api-server 已再次通过。

当前仍未完成的只有长时回归验证:

  1. cargo test -p api-server --bin api-server story_battles --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml 在当前机器上编译耗时较长,尚未在单次时窗内拿到最终断言结果。
  2. npm run check:encoding 已启动但尚未在单次时窗内跑完。

0. 文档目标

本文件只冻结当前 M4 的一个最小新增切片:

新增 GET /api/story/battles/:battleStateId,让 Axum 能从 SpacetimeDB 同步读取单个 battle_state 当前快照,不提前承诺旧 runtime story state 兼容。

这轮目标不是实现旧 GET /api/runtime/story/state/:sessionId 的战斗子视图兼容,也不是把 battle + story_event + currentStory 一次性收口进 resolve_story_action


1. 为什么先补这个切片

当前 battle 链路已经具备:

  1. module-combat 已冻结 battle_state 领域类型与纯结算规则。
  2. spacetime-module 已有 create_battle_state_and_returnresolve_combat_action_and_return
  3. spacetime-clientapi-server 已能创建战斗并推进单次动作。

但现在仍缺一个最基本的恢复能力:

  1. battle 建立后Axum 还不能按 battle_state_id 重新读取真相态。
  2. 页面刷新、重连或后续 story 编排都缺一个稳定的单战斗查询入口。
  3. 后续若要把 battle 收口进 resolve_story_action,也需要先有独立 battle query 可复用。

因此本轮先补最小 battle state 查询切片,不提前跳到更重的 runtime story 兼容。


2. 当前冻结范围

本轮只包含以下能力:

  1. 新增公开接口:GET /api/story/battles/:battleStateId
  2. 认证方式Bearer JWT
  3. 数据来源:SpacetimeDB procedure get_battle_state
  4. 返回体只包含:
    • battleState

本轮明确不做:

  1. 不兼容旧 GET /api/runtime/story/state/:sessionId
  2. 不补 battle 列表查询
  3. 不做 battle_state 订阅与 cache 读模型
  4. 不在查询链路里拼装 story_event / npc / quest / inventory
  5. 不把 battle query 直接拼回旧 RuntimeStoryActionResponse

3. 接口 contract

3.1 请求

  • 方法:GET
  • 路径:/api/story/battles/:battleStateId
  • 认证:必须携带 Bearer JWT
  • 路径参数:
    • battleStateId:目标战斗状态 ID

3.2 成功响应

成功响应延续当前 api-server 统一 envelopedata 字段结构为:

{
  "battleState": {
    "battleStateId": "battle_xxx",
    "storySessionId": "storysess_xxx",
    "runtimeSessionId": "runtime_xxx",
    "actorUserId": "user_xxx",
    "chapterId": "chapter_xxx",
    "targetNpcId": "npc_xxx",
    "targetName": "黑爪狼",
    "battleMode": "fight",
    "status": "ongoing",
    "playerHp": 42,
    "playerMaxHp": 60,
    "playerMana": 12,
    "playerMaxMana": 20,
    "targetHp": 18,
    "targetMaxHp": 30,
    "experienceReward": 18,
    "rewardItems": [],
    "turnIndex": 1,
    "lastActionFunctionId": "battle_attack_basic",
    "lastActionText": "普通攻击",
    "lastResultText": "普通攻击命中了黑爪狼,本次攻击已经完成结算。",
    "lastDamageDealt": 12,
    "lastDamageTaken": 4,
    "lastOutcome": "ongoing",
    "version": 2,
    "createdAt": "2026-04-22T00:00:00.000000Z",
    "updatedAt": "2026-04-22T00:01:00.000000Z"
  }
}

3.3 错误响应

当前延续 battle facade 已有策略:

  1. SpacetimeClientError::Runtime(_) 映射为 400
  2. 其他 SpacetimeClientError 映射为 502
  3. 错误 details.provider 固定为 spacetimedb

4. 分层职责

4.1 module-combat

职责:

  1. 冻结 BattleStateQueryInput
  2. 负责 query input builder 与 validator
  3. 继续复用 BattleStateProcedureResult 作为最小查询返回壳

不负责:

  1. HTTP 路径解析
  2. JWT 鉴权
  3. battle view model 编译

4.2 spacetime-module

职责:

  1. 读取 battle_state
  2. 校验 battle_state_id
  3. 返回单个 BattleStateSnapshot

4.3 spacetime-client

职责:

  1. 构造 BattleStateQueryInput
  2. 调用 get_battle_state
  3. 把 generated binding 结果映射为 BattleStateRecord

当前实现补充:

  1. reward_items 已按 generated binding 映射回 BattleStateRecord.reward_items,不再用空集合占位。
  2. battle query 当前不再依赖 façade stub 或手写假返回。

4.4 api-server

职责:

  1. 暴露 GET /api/story/battles/:battleStateId
  2. 做 Bearer JWT 鉴权
  3. 透传 battleStateId
  4. BattleStateRecord 映射到 battle JSON payload

5. 验收口径

本轮验收只要求以下几点:

  1. api-server 路由树已真实挂出该接口
  2. 未登录访问返回 401
  3. SpacetimeDB 未发布或未连通时返回 502
  4. cargo test -p api-server story_battles 可通过
  5. cargo check -p module-combat -p spacetime-module -p spacetime-client -p api-server 可通过
  6. npm run check:encoding 已执行,确保新增中文文档没有编码损坏

当前验证状态:

  1. 第 5 条已达成。
  2. 第 4、6 条仍在继续追,不应提前宣称通过。

6. 后续边界

这条最小 battle query 落地后,后续再继续拆下一层:

  1. 评估 battle 查询是否需要补 actor ownership 校验
  2. 设计 battle 结束事件如何接入 story_event
  3. 再把 battle query 与 story state / resolve_story_action / currentStory 汇总到更高层编排

在这些 contract 未冻结前,不应把当前接口误称为“旧 runtime story state 已迁移完成”。