5.7 KiB
5.7 KiB
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 已具备:
battle_state真相表create_battle_statereducerresolve_combat_actionreducerfight / spar两种模式下的纯规则推进
但当前仍缺一层明确能力:
- Axum 还不能同步拿到 battle 快照
spacetime-client还没有 battle procedure 调用封装api-server还没有独立的战斗 facade
因此本轮只补下面三层:
spacetime-modulebattle procedurespacetime-clientbattle procedure 调用与返回值映射api-server最小战斗 HTTP facade
2. 当前明确不做的事
本轮刻意不做:
- 不兼容旧
POST /api/runtime/story/actions/resolve - 不兼容旧
GET /api/runtime/story/state/:sessionId - 不把
inventory_use提前接回战斗主链 - 不把
quest / progression / npc / story_event自动联动写回 - 不把 battle 直接拼进
RuntimeStoryActionResponse
原因很直接:
- 这些属于更高层的 runtime story 编排问题
- 当前 battle 子域应该先把“独立可调用、同步可返回”这一层固定下来
- 先补 procedure + facade,后续
resolve_story_action才有稳定下游可调入口
3. spacetime-module 的新增口径
3.1 reducer 继续保留
已有 reducer 继续保留:
create_battle_stateresolve_combat_action
职责不变:
- reducer 仍然只负责 battle 真相写入
- reducer 不直接向调用方返回业务快照
3.2 新增 procedure
本轮新增两个 procedure:
create_battle_state_and_returnresolve_combat_action_and_return
职责冻结如下:
- procedure 只包一层
try_with_tx - procedure 内部复用 reducer 共享的写入 helper
- procedure 负责把最终
battle_state或resolve result同步返回给 Axum
3.3 返回类型
本轮冻结两种返回 DTO:
BattleStateProcedureResultResolveCombatActionProcedureResult
字段口径统一为:
oksnapshot或resulterror_message
这样能与现有 story / treasure / npc procedure 返回风格保持一致。
4. spacetime-client 的新增口径
spacetime-client 本轮新增两条最小调用链:
create_battle_stateresolve_combat_action
调用策略继续沿用当前已验证模式:
- 先建立
DbConnection - 等待
on_connect - 再调用对应 procedure
- 统一经
oneshot + timeout收口结果
当前不做:
- battle 订阅
- battle cache 读模型
- battle 长连接复用策略
5. api-server 的新增 facade 口径
5.1 路由
本轮新增两条最小路由:
POST /api/story/battlesPOST /api/story/battles/resolve
这两条路由的定位不是旧 runtime 兼容层,而是:
- 面向新 Rust 后端内部联调
- 面向后续
resolve_story_action编排层调用
5.2 POST /api/story/battles
请求体只提交 battle 建立所需的业务字段:
storySessionIdruntimeSessionIdtargetNpcIdtargetNamebattleModeplayerHpplayerMaxHpplayerManaplayerMaxManatargetHptargetMaxHp
由 Axum 自动补齐:
battleStateIdactorUserIdcreatedAtMicros
响应返回:
battleState
5.3 POST /api/story/battles/resolve
请求体只提交单次动作推进所需字段:
battleStateIdfunctionIdactionTextbaseDamagemanaCosthealmanaRestorecounterMultiplierBasisPoints
由 Axum 自动补齐:
updatedAtMicros
响应返回:
battleStatecombat
其中 combat 至少包含:
damageDealtdamageTakenoutcome
6. 认证与字段真相边界
6.1 actorUserId
actorUserId 不接受前端自填。
必须由:
AuthenticatedAccessTokenclaims.user_id
直接生成。
6.2 时间字段
createdAtMicros 与 updatedAtMicros 不接受外部写入。
必须由 Axum 在请求时生成,原因如下:
- 避免客户端伪造 battle 创建时间
- 保持 Rust 后端各 facade 的时间字段风格一致
- 让后续 battle / story / npc 联调时便于统一日志与排障
7. 错误映射口径
当前 battle facade 的错误映射冻结如下:
- battle mode 非法、请求 JSON 非法、字段校验失败:
400 SpacetimeClientError::Runtime(_):400- 其他
SpacetimeClientError:502
返回 details.provider 统一写:
- battle 输入准备错误:
story-battle - SpacetimeDB 上游错误:
spacetimedb
8. 本轮验收
满足以下条件,视为本轮 facade 基线完成:
module-combat已新增 procedure 返回 DTOspacetime-module已新增create_battle_state_and_returnspacetime-module已新增resolve_combat_action_and_returnspacetime-client已可同步创建战斗并推进单次动作api-server已新增两条最小 battle facade 路由cargo check -p module-combat -p spacetime-client -p api-server -p spacetime-module通过
9. 下一步建议
本轮完成后,后续最稳的顺序是:
- 把 battle facade 接入
resolve_story_action - 设计 battle 结束后的
story_event追加口径 - 再把
quest / progression / inventory的联动收回到显式子域流程里