M4 runtime story Rust migration wrap-up
This commit is contained in:
@@ -297,3 +297,125 @@ server-rs/crates/api-server/src/
|
||||
这组 resolver 虽然仍是 action orchestration,但已经不依赖 HTTP / `AppState`,只依赖快照 `Value`、当前故事 `currentStory`、共享 DTO 与内部 helper,因此适合先作为 `api-server` 内部模块沉淀。
|
||||
|
||||
迁移后 [compat.rs](D:/Genarrative/server-rs/crates/api-server/src/runtime_story/compat.rs) 对这些动作只保留 functionId 分发、快照桥接与少量共享 glue code,不再承载 battle / equipment / forge / NPC / quest 的具体结算细节。
|
||||
|
||||
## 11. 独立 crate 抽取边界
|
||||
|
||||
完成第二阶段后,已经可以进入第三阶段,但独立 crate 仍按最小安全边界推进:
|
||||
|
||||
1. 新 crate 命名为 `module-runtime-story-compat`。
|
||||
2. `module-runtime-story-compat` 只承接“无 HTTP / 无 `AppState`”的 compat 核心:
|
||||
- runtime story action 分发与确定性结算
|
||||
- battle / equipment / forge / NPC / quest action resolver
|
||||
- `Value` 快照态读写 helper
|
||||
- `RuntimeStoryActionResponse` 的 view model / presentation 编译
|
||||
3. `api-server` 继续保留:
|
||||
- Axum route handler
|
||||
- `RequestContext / AuthenticatedAccessToken`
|
||||
- `runtime_snapshot` 持久化与读取
|
||||
- `clientVersion` 校验到 HTTP error 的映射
|
||||
- `platform-llm` 动作后文本增强
|
||||
4. 首批迁移不把 AI 文本增强放进新 crate,因为它依赖 `AppState` 和 `platform-llm`。
|
||||
5. 首批迁移不把 test route boundary 放进新 crate,route boundary 仍属于 `api-server`。
|
||||
|
||||
这一步完成后,`api-server` 的 `runtime_story/compat.rs` 应该只负责:
|
||||
|
||||
1. 从 HTTP 请求恢复 / 持久化 snapshot
|
||||
2. 调用 `module-runtime-story-compat` 产出确定性动作结果或状态响应
|
||||
3. 需要时调用本地 AI 增强
|
||||
4. 将最终响应包回 `Json<Value>`
|
||||
|
||||
这就是从“`api-server` 内部模块”到“独立 crate”的首个可验证切片。
|
||||
|
||||
截至当前工作区,第三阶段首批独立 crate 已落地:
|
||||
|
||||
1. 已新增 [module-runtime-story-compat](D:/Genarrative/server-rs/crates/module-runtime-story-compat)。
|
||||
2. 已接入 [server-rs/Cargo.toml](D:/Genarrative/server-rs/Cargo.toml) workspace。
|
||||
3. [api-server/Cargo.toml](D:/Genarrative/server-rs/crates/api-server/Cargo.toml) 已新增对 `module-runtime-story-compat` 的依赖。
|
||||
4. 首批迁入新 crate 的内容包括:
|
||||
- `StoryResolution`
|
||||
- `GeneratedStoryPayload`
|
||||
- `CurrentEncounterNpcQuestContext`
|
||||
- `PendingQuestOfferContext`
|
||||
- `RuntimeStoryActionResponseParts`
|
||||
- `CONTINUE_ADVENTURE_FUNCTION_ID`
|
||||
- `MAX_TASK5_COMPANIONS`
|
||||
- `simple_story_resolution`
|
||||
- `resolve_action_text`
|
||||
- `build_status_patch`
|
||||
- `current_world_type`
|
||||
5. 第三阶段继续推进后,当前已经从 `api-server` 抽到独立 crate 的纯逻辑还包括:
|
||||
- `core.rs`:JSON 快照读写、runtime stat、story history、progression、encounter 清理
|
||||
- `game_state.rs`:encounter / inventory / equipment 的基础 helper
|
||||
- `forge.rs`:锻造配方、重铸成本、材料消耗、拆解产物、重铸产物、货币文本
|
||||
- `forge_actions.rs`:`forge_craft / forge_dismantle / forge_reforge` 三条动作结算
|
||||
- `npc_support.rs`:赠礼好感收益、交易价格、数量文案、满员换队招募 helper
|
||||
- `battle.rs`:`battle_* / inventory_use` 的纯动作结算、patch 生成与胜负写回
|
||||
6. 当前 [api-server 的 compat.rs](D:/Genarrative/server-rs/crates/api-server/src/runtime_story/compat.rs) 已经不再内嵌上述纯逻辑,只保留:
|
||||
- Axum handler
|
||||
- snapshot 读写
|
||||
- `clientVersion` 校验
|
||||
- functionId 分发
|
||||
- HTTP error 映射
|
||||
- 动作后 AI 文本增强
|
||||
7. 当前 [api-server 的 forge.rs](D:/Genarrative/server-rs/crates/api-server/src/runtime_story/compat/forge.rs) 已收缩成极薄 bridge,只为 NPC trade bootstrap 复用新 crate 暴露的运行时物品构造 helper,锻造规则主体不再保留本地副本。
|
||||
8. 当前 [api-server 的 battle.rs](D:/Genarrative/server-rs/crates/api-server/src/runtime_story/compat/battle.rs) 也已从“结算 + 展示”收缩成“展示编译 + 少量本地 helper”:
|
||||
- battle 动作结算主链已经迁入 `module-runtime-story-compat`
|
||||
- `api-server` 本地仅继续保留 `build_battle_runtime_story_options(...)` 与 `restore_player_resource(...)` 这类仍被 presentation / NPC 辅助逻辑直接依赖的部分
|
||||
- 这为下一步继续把 battle option compiler 收进独立 crate 做好了边界准备
|
||||
|
||||
这意味着第三阶段已经不只是“创建了新 crate”,而是完成了第一批真正跨 crate 的 compat 纯逻辑迁移,并且保持 route boundary 与既有测试口径不变。
|
||||
|
||||
同日继续推进后,battle 这块已经完成从“先迁结算主链”到“连展示编译一起迁”的下一步:
|
||||
|
||||
1. [module-runtime-story-compat 的 battle.rs](D:/Genarrative/server-rs/crates/module-runtime-story-compat/src/battle.rs) 当前已同时承接:
|
||||
- `resolve_battle_action(...)`
|
||||
- `restore_player_resource(...)`
|
||||
- `build_battle_runtime_story_options(...)`
|
||||
- 技能冷却读取、推荐物品挑选、战斗技能 option compiler 等 battle 展示辅助
|
||||
2. [api-server 的 compat.rs](D:/Genarrative/server-rs/crates/api-server/src/runtime_story/compat.rs) 已直接从 `module-runtime-story-compat` 导入 battle 展示编译与资源恢复 helper。
|
||||
3. [api-server 本地的 compat/battle.rs](D:/Genarrative/server-rs/crates/api-server/src/runtime_story/compat/battle.rs) 已删除,不再保留 battle 规则的本地副本。
|
||||
4. 到这一步,`api-server` 在 runtime story compat 上对 battle 的职责已经只剩:
|
||||
- functionId 分发
|
||||
- route handler / snapshot bridge
|
||||
- AI 文本增强后的最终响应拼装
|
||||
|
||||
这说明第三阶段已经不只是在“拆 crate”,而是在真实压缩 `api-server` 的 compat 规则面。接下来更合理的推进方向将不再是 battle,而是继续评估 `presentation` 中还能进一步抽到独立 crate 的纯 view model / option compiler 边界。
|
||||
|
||||
同日继续推进后,`presentation` 中最通用的一层 option DTO 编译也已经开始抽离:
|
||||
|
||||
1. 已新增 [options.rs](D:/Genarrative/server-rs/crates/module-runtime-story-compat/src/options.rs),统一承接:
|
||||
- `build_static_runtime_story_option(...)`
|
||||
- `build_runtime_story_option_with_payload(...)`
|
||||
- `build_disabled_runtime_story_option(...)`
|
||||
- `build_runtime_story_option_from_story_option(...)`
|
||||
- `build_story_option_from_runtime_option(...)`
|
||||
- `infer_option_scope(...)`
|
||||
2. [module-runtime-story-compat 的 lib.rs](D:/Genarrative/server-rs/crates/module-runtime-story-compat/src/lib.rs) 已对外 re-export 这些 option helper,供 `api-server` 直接复用。
|
||||
3. [api-server 的 presentation.rs](D:/Genarrative/server-rs/crates/api-server/src/runtime_story/compat/presentation.rs) 已删除本地重复实现,只保留 NPC option 组合、view model 组装、quest currentStory 等尚未完全独立的部分。
|
||||
|
||||
这一步的意义不是单纯减少行数,而是先把 `RuntimeStoryOptionView` 的最小稳定编译面收敛到独立 crate。后续若继续外提 `view model` 与 `fallback option compiler`,将不需要再重复搬运这些 option 基础件。
|
||||
|
||||
同日继续推进后,`presentation` 中的纯 view-model builder 也已经抽到独立 crate:
|
||||
|
||||
1. 已新增 [view_model.rs](D:/Genarrative/server-rs/crates/module-runtime-story-compat/src/view_model.rs),统一承接:
|
||||
- `build_runtime_story_view_model(...)`
|
||||
- `build_runtime_story_companions(...)`
|
||||
- `build_runtime_story_encounter(...)`
|
||||
- `resolve_current_encounter_npc_state(...)`
|
||||
2. [api-server 的 presentation.rs](D:/Genarrative/server-rs/crates/api-server/src/runtime_story/compat/presentation.rs) 已删除本地 view-model 组装实现,继续只负责状态响应 orchestration、dialogue currentStory、fallback option compiler 与 quest 辅助。
|
||||
3. [api-server 的 game_state.rs](D:/Genarrative/server-rs/crates/api-server/src/runtime_story/compat/game_state.rs) 当前也直接复用 crate 导出的 `resolve_current_encounter_npc_state(...)`,避免 NPC 状态查询 helper 在 `api-server` 和 crate 之间出现两套实现。
|
||||
|
||||
至此,`module-runtime-story-compat` 已经覆盖了 runtime story 兼容层的以下纯逻辑面:
|
||||
|
||||
1. JSON 快照读写与基础状态 helper
|
||||
2. battle / forge / npc support 的纯规则结算
|
||||
3. battle option compiler
|
||||
4. runtime story option DTO 编译
|
||||
5. runtime story view-model 编译
|
||||
|
||||
`api-server` 当前的剩余重点已经更集中在:
|
||||
|
||||
1. HTTP / snapshot bridge
|
||||
2. functionId 分发
|
||||
3. AI 文本增强
|
||||
4. NPC / quest fallback option 与 currentStory 组合逻辑
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
## 文档列表
|
||||
|
||||
- [RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md](./RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md):冻结 Rust 本地一键联调脚本与 Ubuntu 发布包构建脚本的执行口径,覆盖 `npm run dev:rust`、`npm run build:rust:ubuntu`、Vite release、Linux `api-server`、SpacetimeDB wasm、启动停止脚本和安全清库开关。
|
||||
- [RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md](./RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md):记录当前 Rust `api-server` 已挂载的 96 条 Axum 路由,按 auth、assets、runtime、custom world、story、generated path 等挂载面归类,用于对照 Node 能力基线与切流 smoke 清单。
|
||||
- [BACKEND_REWRITE_CROSS_CUTTING_GOVERNANCE_2026-04-22.md](./BACKEND_REWRITE_CROSS_CUTTING_GOVERNANCE_2026-04-22.md):冻结后端重写收口阶段的横向治理规则,覆盖 TypeScript contract 到 Rust DTO 映射、SpacetimeDB schema 演进、大对象 / workflow cache 存储边界和文档维护门禁。
|
||||
- [PLATFORM_LLM_TEXT_GATEWAY_DESIGN_2026-04-21.md](./PLATFORM_LLM_TEXT_GATEWAY_DESIGN_2026-04-21.md):`platform-llm` 文本模型网关首版设计,冻结 OpenAI 兼容 `/chat/completions`、SSE 增量解析、错误模型与重试边界。
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
# Rust 本地联调与远端发布脚本方案
|
||||
|
||||
日期:`2026-04-22`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
本方案补齐 `server-rs` 在 M7 切流前需要的两类工程脚本:
|
||||
|
||||
1. 本地一键联调脚本:同时启动本地 SpacetimeDB、Rust `api-server` 与 Web 前端,并通过现有 Vite 代理开关把运行时 API 指向 Rust。
|
||||
2. Ubuntu 发布包构建脚本:在仓库根目录生成 `build/<当前时间>/` 发布目录,内含前端 release、Linux `api-server`、SpacetimeDB wasm、启动脚本与停止脚本。
|
||||
|
||||
脚本只做部署与联调编排,不改变 HTTP contract、SpacetimeDB schema 命名、对象存储键规划和前端默认 Node 开发入口。
|
||||
|
||||
## 2. 本地脚本
|
||||
|
||||
入口:
|
||||
|
||||
```powershell
|
||||
npm run dev:rust
|
||||
```
|
||||
|
||||
跨平台 Bash 入口:
|
||||
|
||||
```bash
|
||||
npm run dev:rust:sh
|
||||
```
|
||||
|
||||
Windows 下 `dev:rust:sh`、`deploy:rust:remote` 与 `build:rust:ubuntu` 会通过 `scripts/run-bash-script.mjs` 优先查找 Git Bash;如安装路径不标准,可用 `GENARRATIVE_BASH` 指定 `bash` 可执行文件。
|
||||
|
||||
默认端口:
|
||||
|
||||
1. Web 前端:`http://127.0.0.1:3000`
|
||||
2. Rust `api-server`:`http://127.0.0.1:8082`
|
||||
3. SpacetimeDB standalone:`http://127.0.0.1:3101`
|
||||
4. SpacetimeDB database:`genarrative-dev`
|
||||
|
||||
默认流程:
|
||||
|
||||
1. 检查 `cargo`、`node` 与 `spacetime` CLI。
|
||||
2. 启动 `spacetime --root-dir server-rs/.spacetimedb/local start --edition standalone --listen-addr 127.0.0.1:3101`。
|
||||
3. 等待 `spacetime server ping http://127.0.0.1:3101` 可用。
|
||||
4. 执行 `spacetime publish genarrative-dev --server http://127.0.0.1:3101 --module-path server-rs/crates/spacetime-module --yes`。
|
||||
5. 注入 `GENARRATIVE_API_*` 与 `GENARRATIVE_SPACETIME_*` 后启动 `cargo run -p api-server`。
|
||||
6. 注入 `GENARRATIVE_BACKEND_STACK=rust`、`RUST_SERVER_TARGET`、`GENARRATIVE_RUNTIME_SERVER_TARGET` 后启动 Vite。
|
||||
7. 任一子进程退出时,脚本回收其余子进程。
|
||||
|
||||
Vite 代理覆盖范围:
|
||||
|
||||
1. `/api/runtime/*` 会在 Rust 栈下代理到 Rust `api-server`,覆盖旧 runtime story 兼容接口。
|
||||
2. `/api/story/*` 会在 Rust 栈下代理到 Rust `api-server`,覆盖新 story session、battle 查询与 NPC battle 切片接口。
|
||||
3. 其他 `/api/auth`、`/api/assets`、`/api/custom-world`、`/api/llm` 等路径仍由同一个 `GENARRATIVE_RUNTIME_SERVER_TARGET` 控制,便于 M7 按服务能力逐项做对比 smoke。
|
||||
|
||||
安全边界:
|
||||
|
||||
1. 默认不执行 `--clear-database`。
|
||||
2. 只有显式传入 `-ClearDatabase` 或 `--clear-database` 才允许清库重发。
|
||||
3. 如需要复用已经启动的 SpacetimeDB,可传 `-SkipSpacetime` / `--skip-spacetime`。
|
||||
4. 如只想启动进程不发布模块,可传 `-SkipPublish` / `--skip-publish`。
|
||||
|
||||
常用示例:
|
||||
|
||||
```powershell
|
||||
.\scripts\dev-rust-stack.ps1 -ApiPort 8090 -SpacetimePort 3110 -Database genarrative-dev
|
||||
.\scripts\dev-rust-stack.ps1 -SkipSpacetime -SkipPublish
|
||||
.\scripts\dev-rust-stack.ps1 -ClearDatabase
|
||||
```
|
||||
|
||||
```bash
|
||||
./scripts/dev-rust-stack.sh --api-port 8090 --spacetime-port 3110 --database genarrative-dev
|
||||
./scripts/dev-rust-stack.sh --skip-spacetime --skip-publish
|
||||
./scripts/dev-rust-stack.sh --clear-database
|
||||
```
|
||||
|
||||
## 3. Ubuntu 发布包脚本
|
||||
|
||||
入口:
|
||||
|
||||
```bash
|
||||
npm run build:rust:ubuntu
|
||||
```
|
||||
|
||||
兼容入口:
|
||||
|
||||
```bash
|
||||
npm run deploy:rust:remote
|
||||
```
|
||||
|
||||
保留 `deploy:rust:remote` 是为了不打断既有命令习惯;当前语义已调整为“生成 Ubuntu 发布包”,不再通过 SSH 进入服务器执行部署。
|
||||
|
||||
默认流程:
|
||||
|
||||
1. 在仓库根目录创建 `build/`。
|
||||
2. 在 `build/` 下创建当前时间命名的目标目录,例如 `build/20260422-153000/`。
|
||||
3. 使用 Vite 构建前端 release 到目标目录的 `web/`。
|
||||
4. 执行 `cargo build -p api-server --release --target x86_64-unknown-linux-gnu --manifest-path server-rs/Cargo.toml`,并把 `api-server` 复制到目标目录。
|
||||
5. 执行 `cargo build -p spacetime-module --release --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml`,并把 `spacetime_module.wasm` 复制到目标目录。
|
||||
6. 在目标目录写入 `web-server.mjs`,用于托管 `web/` 并把 `/api/*`、`/generated-*`、`/healthz` 反代到本包内的 `api-server`。
|
||||
7. 在目标目录写入 `start.sh` 与 `stop.sh`。
|
||||
|
||||
发布包结构:
|
||||
|
||||
```text
|
||||
build/<timestamp>/
|
||||
├─ web/
|
||||
├─ api-server
|
||||
├─ spacetime_module.wasm
|
||||
├─ web-server.mjs
|
||||
├─ start.sh
|
||||
├─ stop.sh
|
||||
└─ README.md
|
||||
```
|
||||
|
||||
常用示例:
|
||||
|
||||
```bash
|
||||
npm run build:rust:ubuntu -- --name 20260422-153000
|
||||
npm run build:rust:ubuntu -- --database genarrative-dev --web-port 3000 --api-port 8082 --spacetime-port 3101
|
||||
```
|
||||
|
||||
目标服务器启动:
|
||||
|
||||
```bash
|
||||
cd build/<timestamp>
|
||||
./start.sh
|
||||
./stop.sh
|
||||
```
|
||||
|
||||
安全边界:
|
||||
|
||||
1. 构建脚本不读取、不传输、不打印生产密钥。
|
||||
2. 目标服务器 `.env`、`.env.local` 或进程环境仍由服务器本身维护。
|
||||
3. `start.sh` 默认不清空 SpacetimeDB;只有显式执行 `./start.sh --clear-database` 才允许清库重发。
|
||||
4. `start.sh` 使用 `spacetime publish --bin-path spacetime_module.wasm --yes` 发布当前包内 wasm。
|
||||
5. 当前脚本是单目录进程启动方案,不替代生产 systemd、Nginx、TLS、日志轮转与守护进程配置。
|
||||
|
||||
目标服务器最小要求:
|
||||
|
||||
1. Ubuntu x86_64。
|
||||
2. 已安装 `node`,用于运行发布包内的 `web-server.mjs`。
|
||||
3. 已安装 `spacetime` CLI,`start.sh` 会启动本地 SpacetimeDB 并发布 wasm。
|
||||
4. 业务密钥通过目标服务器环境变量或发布包同目录 `.env.local` 提供。
|
||||
|
||||
## 4. 与 M7 的关系
|
||||
|
||||
这套脚本补齐 M7 的部署执行入口,但不等价于完成灰度切流。M7 后续仍需要在真实 OSS、LLM、短信、微信、SpacetimeDB 数据库和反向代理环境下完成全链路 smoke、关键 SSE 联调和灰度切流验收。
|
||||
Reference in New Issue
Block a user