6.2 KiB
6.2 KiB
M4 module-combat battle state 查询设计(2026-04-22)
更新时间:2026-04-22
补充状态:2026-04-22
当前 battle query 纵切片已经完成到“真实可编译、可生成 binding、可被 Axum 调用”的状态:
spacetime-module中的get_battle_stateprocedure 已稳定存在。spacetime-client/src/module_bindings已重新执行spacetime generate,当前已真实包含:battle_state_query_input_typeget_battle_state_procedurebattle_state.reward_items对应字段
spacetime-client/src/lib.rs里原本返回“binding 尚未生成”的占位get_battle_state(...)已替换为真实 procedure 调用。cargo check -p spacetime-client与cargo check -p api-server已再次通过。
当前仍未完成的只有长时回归验证:
cargo test -p api-server --bin api-server story_battles --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml在当前机器上编译耗时较长,尚未在单次时窗内拿到最终断言结果。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 链路已经具备:
module-combat已冻结battle_state领域类型与纯结算规则。spacetime-module已有create_battle_state_and_return、resolve_combat_action_and_return。spacetime-client与api-server已能创建战斗并推进单次动作。
但现在仍缺一个最基本的恢复能力:
- battle 建立后,Axum 还不能按
battle_state_id重新读取真相态。 - 页面刷新、重连或后续 story 编排都缺一个稳定的单战斗查询入口。
- 后续若要把 battle 收口进
resolve_story_action,也需要先有独立 battle query 可复用。
因此本轮先补最小 battle state 查询切片,不提前跳到更重的 runtime story 兼容。
2. 当前冻结范围
本轮只包含以下能力:
- 新增公开接口:
GET /api/story/battles/:battleStateId - 认证方式:Bearer JWT
- 数据来源:
SpacetimeDB procedure get_battle_state - 返回体只包含:
battleState
本轮明确不做:
- 不兼容旧
GET /api/runtime/story/state/:sessionId - 不补 battle 列表查询
- 不做
battle_state订阅与 cache 读模型 - 不在查询链路里拼装
story_event / npc / quest / inventory - 不把 battle query 直接拼回旧
RuntimeStoryActionResponse
3. 接口 contract
3.1 请求
- 方法:
GET - 路径:
/api/story/battles/:battleStateId - 认证:必须携带 Bearer JWT
- 路径参数:
battleStateId:目标战斗状态 ID
3.2 成功响应
成功响应延续当前 api-server 统一 envelope,data 字段结构为:
{
"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 已有策略:
SpacetimeClientError::Runtime(_)映射为400- 其他
SpacetimeClientError映射为502 - 错误
details.provider固定为spacetimedb
4. 分层职责
4.1 module-combat
职责:
- 冻结
BattleStateQueryInput - 负责 query input builder 与 validator
- 继续复用
BattleStateProcedureResult作为最小查询返回壳
不负责:
- HTTP 路径解析
- JWT 鉴权
- battle view model 编译
4.2 spacetime-module
职责:
- 读取
battle_state - 校验
battle_state_id - 返回单个
BattleStateSnapshot
4.3 spacetime-client
职责:
- 构造
BattleStateQueryInput - 调用
get_battle_state - 把 generated binding 结果映射为
BattleStateRecord
当前实现补充:
reward_items已按 generated binding 映射回BattleStateRecord.reward_items,不再用空集合占位。- battle query 当前不再依赖 façade stub 或手写假返回。
4.4 api-server
职责:
- 暴露
GET /api/story/battles/:battleStateId - 做 Bearer JWT 鉴权
- 透传
battleStateId - 把
BattleStateRecord映射到 battle JSON payload
5. 验收口径
本轮验收只要求以下几点:
api-server路由树已真实挂出该接口- 未登录访问返回
401 - 在
SpacetimeDB未发布或未连通时返回502 cargo test -p api-server story_battles可通过cargo check -p module-combat -p spacetime-module -p spacetime-client -p api-server可通过npm run check:encoding已执行,确保新增中文文档没有编码损坏
当前验证状态:
- 第 5 条已达成。
- 第 4、6 条仍在继续追,不应提前宣称通过。
6. 后续边界
这条最小 battle query 落地后,后续再继续拆下一层:
- 评估 battle 查询是否需要补 actor ownership 校验
- 设计 battle 结束事件如何接入
story_event - 再把 battle query 与
story state / resolve_story_action / currentStory汇总到更高层编排
在这些 contract 未冻结前,不应把当前接口误称为“旧 runtime story state 已迁移完成”。