This commit is contained in:
251
docs/technical/M4_MODULE_COMBAT_AXUM_FACADE_DESIGN_2026-04-21.md
Normal file
251
docs/technical/M4_MODULE_COMBAT_AXUM_FACADE_DESIGN_2026-04-21.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# M4 module-combat Axum facade 设计(2026-04-21)
|
||||
|
||||
更新时间:`2026-04-21`
|
||||
|
||||
## 0. 文档目标
|
||||
|
||||
本文件只冻结一件事:
|
||||
|
||||
**把已经完成 reducer 化的 `module-combat` 再向上接一层最小同步返回链,让 `api-server` 可以显式创建战斗、推进单次战斗动作,并立即拿到 battle 快照结果。**
|
||||
|
||||
这份文档不是完整 `runtime story actions/resolve` 兼容方案,也不替代后续的 `resolve_story_action` 编排设计。
|
||||
|
||||
---
|
||||
|
||||
## 1. 本轮要解决的问题
|
||||
|
||||
当前 `module-combat` 已具备:
|
||||
|
||||
1. `battle_state` 真相表
|
||||
2. `create_battle_state` reducer
|
||||
3. `resolve_combat_action` reducer
|
||||
4. `fight / spar` 两种模式下的纯规则推进
|
||||
|
||||
但当前仍缺一层明确能力:
|
||||
|
||||
1. Axum 还不能同步拿到 battle 快照
|
||||
2. `spacetime-client` 还没有 battle procedure 调用封装
|
||||
3. `api-server` 还没有独立的战斗 facade
|
||||
|
||||
因此本轮只补下面三层:
|
||||
|
||||
1. `spacetime-module` battle procedure
|
||||
2. `spacetime-client` battle procedure 调用与返回值映射
|
||||
3. `api-server` 最小战斗 HTTP facade
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前明确不做的事
|
||||
|
||||
本轮刻意不做:
|
||||
|
||||
1. 不兼容旧 `POST /api/runtime/story/actions/resolve`
|
||||
2. 不兼容旧 `GET /api/runtime/story/state/:sessionId`
|
||||
3. 不把 `inventory_use` 提前接回战斗主链
|
||||
4. 不把 `quest / progression / npc / story_event` 自动联动写回
|
||||
5. 不把 battle 直接拼进 `RuntimeStoryActionResponse`
|
||||
|
||||
原因很直接:
|
||||
|
||||
1. 这些属于更高层的 runtime story 编排问题
|
||||
2. 当前 battle 子域应该先把“独立可调用、同步可返回”这一层固定下来
|
||||
3. 先补 procedure + facade,后续 `resolve_story_action` 才有稳定下游可调入口
|
||||
|
||||
---
|
||||
|
||||
## 3. `spacetime-module` 的新增口径
|
||||
|
||||
### 3.1 reducer 继续保留
|
||||
|
||||
已有 reducer 继续保留:
|
||||
|
||||
1. `create_battle_state`
|
||||
2. `resolve_combat_action`
|
||||
|
||||
职责不变:
|
||||
|
||||
1. reducer 仍然只负责 battle 真相写入
|
||||
2. reducer 不直接向调用方返回业务快照
|
||||
|
||||
### 3.2 新增 procedure
|
||||
|
||||
本轮新增两个 procedure:
|
||||
|
||||
1. `create_battle_state_and_return`
|
||||
2. `resolve_combat_action_and_return`
|
||||
|
||||
职责冻结如下:
|
||||
|
||||
1. procedure 只包一层 `try_with_tx`
|
||||
2. procedure 内部复用 reducer 共享的写入 helper
|
||||
3. procedure 负责把最终 `battle_state` 或 `resolve result` 同步返回给 Axum
|
||||
|
||||
### 3.3 返回类型
|
||||
|
||||
本轮冻结两种返回 DTO:
|
||||
|
||||
1. `BattleStateProcedureResult`
|
||||
2. `ResolveCombatActionProcedureResult`
|
||||
|
||||
字段口径统一为:
|
||||
|
||||
1. `ok`
|
||||
2. `snapshot` 或 `result`
|
||||
3. `error_message`
|
||||
|
||||
这样能与现有 `story / treasure / npc` procedure 返回风格保持一致。
|
||||
|
||||
---
|
||||
|
||||
## 4. `spacetime-client` 的新增口径
|
||||
|
||||
`spacetime-client` 本轮新增两条最小调用链:
|
||||
|
||||
1. `create_battle_state`
|
||||
2. `resolve_combat_action`
|
||||
|
||||
调用策略继续沿用当前已验证模式:
|
||||
|
||||
1. 先建立 `DbConnection`
|
||||
2. 等待 `on_connect`
|
||||
3. 再调用对应 procedure
|
||||
4. 统一经 `oneshot + timeout` 收口结果
|
||||
|
||||
当前不做:
|
||||
|
||||
1. battle 订阅
|
||||
2. battle cache 读模型
|
||||
3. battle 长连接复用策略
|
||||
|
||||
---
|
||||
|
||||
## 5. `api-server` 的新增 facade 口径
|
||||
|
||||
### 5.1 路由
|
||||
|
||||
本轮新增两条最小路由:
|
||||
|
||||
1. `POST /api/story/battles`
|
||||
2. `POST /api/story/battles/resolve`
|
||||
|
||||
这两条路由的定位不是旧 runtime 兼容层,而是:
|
||||
|
||||
1. 面向新 Rust 后端内部联调
|
||||
2. 面向后续 `resolve_story_action` 编排层调用
|
||||
|
||||
### 5.2 `POST /api/story/battles`
|
||||
|
||||
请求体只提交 battle 建立所需的业务字段:
|
||||
|
||||
1. `storySessionId`
|
||||
2. `runtimeSessionId`
|
||||
3. `targetNpcId`
|
||||
4. `targetName`
|
||||
5. `battleMode`
|
||||
6. `playerHp`
|
||||
7. `playerMaxHp`
|
||||
8. `playerMana`
|
||||
9. `playerMaxMana`
|
||||
10. `targetHp`
|
||||
11. `targetMaxHp`
|
||||
|
||||
由 Axum 自动补齐:
|
||||
|
||||
1. `battleStateId`
|
||||
2. `actorUserId`
|
||||
3. `createdAtMicros`
|
||||
|
||||
响应返回:
|
||||
|
||||
1. `battleState`
|
||||
|
||||
### 5.3 `POST /api/story/battles/resolve`
|
||||
|
||||
请求体只提交单次动作推进所需字段:
|
||||
|
||||
1. `battleStateId`
|
||||
2. `functionId`
|
||||
3. `actionText`
|
||||
4. `baseDamage`
|
||||
5. `manaCost`
|
||||
6. `heal`
|
||||
7. `manaRestore`
|
||||
8. `counterMultiplierBasisPoints`
|
||||
|
||||
由 Axum 自动补齐:
|
||||
|
||||
1. `updatedAtMicros`
|
||||
|
||||
响应返回:
|
||||
|
||||
1. `battleState`
|
||||
2. `combat`
|
||||
|
||||
其中 `combat` 至少包含:
|
||||
|
||||
1. `damageDealt`
|
||||
2. `damageTaken`
|
||||
3. `outcome`
|
||||
|
||||
---
|
||||
|
||||
## 6. 认证与字段真相边界
|
||||
|
||||
### 6.1 `actorUserId`
|
||||
|
||||
`actorUserId` 不接受前端自填。
|
||||
|
||||
必须由:
|
||||
|
||||
1. `AuthenticatedAccessToken`
|
||||
2. `claims.user_id`
|
||||
|
||||
直接生成。
|
||||
|
||||
### 6.2 时间字段
|
||||
|
||||
`createdAtMicros` 与 `updatedAtMicros` 不接受外部写入。
|
||||
|
||||
必须由 Axum 在请求时生成,原因如下:
|
||||
|
||||
1. 避免客户端伪造 battle 创建时间
|
||||
2. 保持 Rust 后端各 facade 的时间字段风格一致
|
||||
3. 让后续 battle / story / npc 联调时便于统一日志与排障
|
||||
|
||||
---
|
||||
|
||||
## 7. 错误映射口径
|
||||
|
||||
当前 battle facade 的错误映射冻结如下:
|
||||
|
||||
1. battle mode 非法、请求 JSON 非法、字段校验失败:`400`
|
||||
2. `SpacetimeClientError::Runtime(_)`:`400`
|
||||
3. 其他 `SpacetimeClientError`:`502`
|
||||
|
||||
返回 `details.provider` 统一写:
|
||||
|
||||
1. battle 输入准备错误:`story-battle`
|
||||
2. SpacetimeDB 上游错误:`spacetimedb`
|
||||
|
||||
---
|
||||
|
||||
## 8. 本轮验收
|
||||
|
||||
满足以下条件,视为本轮 facade 基线完成:
|
||||
|
||||
1. `module-combat` 已新增 procedure 返回 DTO
|
||||
2. `spacetime-module` 已新增 `create_battle_state_and_return`
|
||||
3. `spacetime-module` 已新增 `resolve_combat_action_and_return`
|
||||
4. `spacetime-client` 已可同步创建战斗并推进单次动作
|
||||
5. `api-server` 已新增两条最小 battle facade 路由
|
||||
6. `cargo check -p module-combat -p spacetime-client -p api-server -p spacetime-module` 通过
|
||||
|
||||
---
|
||||
|
||||
## 9. 下一步建议
|
||||
|
||||
本轮完成后,后续最稳的顺序是:
|
||||
|
||||
1. 把 battle facade 接入 `resolve_story_action`
|
||||
2. 设计 battle 结束后的 `story_event` 追加口径
|
||||
3. 再把 `quest / progression / inventory` 的联动收回到显式子域流程里
|
||||
Reference in New Issue
Block a user