init with react+axum+spacetimedb
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-26 18:06:23 +08:00
commit cbc27bad4a
20199 changed files with 883714 additions and 0 deletions

View 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` 的联动收回到显式子域流程里