后端重写提交

This commit is contained in:
2026-04-22 12:34:49 +08:00
parent cf8da3f50f
commit 997a8daada
438 changed files with 53355 additions and 865 deletions

View File

@@ -0,0 +1,202 @@
# 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-client``cargo 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_return``resolve_combat_action_and_return`
3. `spacetime-client``api-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` 统一 envelope`data` 字段结构为:
```json
{
"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 已迁移完成”。