# M7 联调、回归、部署与切流执行方案 日期:`2026-04-22` 归档说明:截至 `2026-04-26`,Rust 迁移已完成,旧 `server-node/` 已删除,M7 阶段性预检包装入口已移除。后续长期检查统一使用 `server-rs/scripts/check.ps1`、`server-rs/scripts/smoke.ps1`、`server-rs/scripts/oss-smoke.ps1` 与 `npm run api-server`。 ## 1. 文档目标 这份文档把 `M7:联调、回归、部署与切流任务清单` 从高层勾选项细化为可直接执行的工程方案。 M7 的目标不是新增玩法功能,而是在 `M0 ~ M6` 已迁移的 Rust 后端基础上完成切流前收口: 1. 固定本地、灰度、切流前的检查命令。 2. 固定 `Axum + SpacetimeDB + OSS` 的部署与回滚口径。 3. 固定观测字段、慢请求、上游失败日志与资产任务日志。 4. 固定旧 `server-node` 删除后的 Rust 主线回归与部署验证方式。 5. 等价拆分 `server-rs/crates/spacetime-module/src/lib.rs`,避免 SpacetimeDB 主工程继续退化为单大文件。 ## 2. 执行约束 1. 不改变现有 HTTP contract、SSE contract、SpacetimeDB 表名、reducer 名、procedure 名和对象键前缀。 2. 不把 LLM、OSS、短信、微信等外部副作用移入 SpacetimeDB reducer。 3. `spacetime-module` 拆分只做物理结构收口,不做 schema 重命名、字段删除、字段重排或 reducer/procedure 改名。 4. `server-node/` 已进入物理删除流程,M7 不再把旧 Node 后端作为运行时回退锚点。 5. 前端默认指向 Rust `api-server`;如需临时覆盖目标,只允许使用 `RUST_SERVER_TARGET` 或 `GENARRATIVE_RUNTIME_SERVER_TARGET` 指向 Rust 兼容服务。 ## 3. 测试体系 M7 固定四层测试入口: 1. Rust crate 级别:`cargo check/test` 覆盖 `api-server`、`spacetime-module`、`shared-contracts` 与模块 crate。 2. Axum handler 级别:继续复用 `api-server` 内已有 `build_router + tower::ServiceExt` 测试,重点覆盖 `healthz/auth/runtime/assets/custom-world/story` 的兼容响应。 3. SpacetimeDB 模块级别:`cargo check -p spacetime-module` 作为 schema/reducer/procedure 的最低门禁;需要真实数据库行为时使用 `spacetime publish --server local --yes` 后再跑 smoke。 4. 端到端主流程:`server-rs/scripts/smoke.ps1` 与 `server-rs/scripts/oss-smoke.ps1` 分别覆盖基础 HTTP contract 与真实 OSS 链路。 推荐本地顺序: ```powershell .\server-rs\scripts\check.ps1 .\server-rs\scripts\smoke.ps1 ``` ## 4. 部署准备 Axum 部署方式: 1. `cargo build -p api-server --release` 生成发布二进制。 2. 进程环境显式配置 `GENARRATIVE_API_HOST`、`GENARRATIVE_API_PORT`、`GENARRATIVE_API_LOG`。 3. 反向代理继续保留 `Host`、`X-Forwarded-For`、`X-Forwarded-Proto`、`X-Request-Id`。 4. SSE 路由必须禁用代理缓冲。 SpacetimeDB 发布方式: 1. 本地开发先执行 `server-rs/scripts/spacetime-dev.ps1` 启动 standalone。 2. 发布模块使用 `spacetime publish genarrative-dev --server local --yes --module-path server-rs/crates/spacetime-module`。 3. 若需要重置开发库,必须显式加 `--clear-database --yes`,不得默认清库。 4. 生成绑定时使用仓库根目录 `spacetime.json` 中的 `typescript` 与 `rust` 输出目录。 OSS / CDN / 域名方案: 1. 正式对象真相仍为 `bucket + object_key`。 2. bucket 默认私有读写,浏览器不直接匿名读取。 3. `/generated-*` 旧路径由 Axum 同源代理或 CDN 边缘回源到 Rust API。 4. CDN 只缓存可公开缓存的派生读结果,不把私有签名 URL 写入业务表。 环境变量最小清单: 1. `GENARRATIVE_API_HOST`、`GENARRATIVE_API_PORT`、`GENARRATIVE_API_LOG` 2. `GENARRATIVE_JWT_ISSUER`、`GENARRATIVE_JWT_SECRET` 3. `GENARRATIVE_SPACETIME_SERVER_URL`、`GENARRATIVE_SPACETIME_DATABASE`、`GENARRATIVE_SPACETIME_TOKEN` 4. `ALIYUN_OSS_BUCKET`、`ALIYUN_OSS_ENDPOINT`、`ALIYUN_OSS_ACCESS_KEY_ID`、`ALIYUN_OSS_ACCESS_KEY_SECRET` 5. `GENARRATIVE_LLM_PROVIDER`、`GENARRATIVE_LLM_BASE_URL`、`GENARRATIVE_LLM_API_KEY` 6. `DASHSCOPE_BASE_URL`、`DASHSCOPE_API_KEY` 7. `SMS_AUTH_ENABLED` 与短信供应商变量 8. `WECHAT_AUTH_ENABLED` 与微信 OAuth 变量 9. `RUST_SERVER_TARGET`、`GENARRATIVE_RUNTIME_SERVER_TARGET` ## 5. 灰度与切流 灰度环境固定为三段: 1. `preflight`:只跑 Rust 预检、smoke 与人工主链验证,不接正式用户流量。 2. `limited-rust`:小范围账号或灰度域名访问 Rust `api-server`,差异必须登记到 M7 验收记录。 3. `rust-primary`:反向代理或 Vite dev proxy 指向 Rust `api-server`,旧 Node 后端不作为运行时入口保留。 前端切换方式: 1. 默认使用 `RUST_SERVER_TARGET` 或 `GENARRATIVE_API_TARGET` 指向 Rust `api-server`。 2. 本地或灰度可覆盖 `GENARRATIVE_RUNTIME_SERVER_TARGET`,但目标仍必须是 Rust 兼容服务。 3. 紧急回退优先回滚到上一个 Rust 发布包或反向代理配置,不恢复 `server-node/` 工程目录。 ## 6. API 回归 第一批删除后不再保留 Node/Rust 对比脚本,M7 回归改为 Rust 主线 contract 验证: 1. `server-rs/scripts/check.ps1` 覆盖 Rust 工作区格式、clippy、构建与测试门禁。 2. `server-rs/scripts/smoke.ps1` 覆盖 `/healthz`、envelope 与 request id 基础 contract。 3. `server-rs/scripts/oss-smoke.ps1` 覆盖真实 OSS 链路。 4. 新增只读 contract 时优先补进 Rust 侧 smoke 或 handler 测试,不恢复 Node 对比脚本。 带登录、写入、OSS 或 SSE 的主流程仍由专门 smoke、handler 测试和人工验证清单负责。 ## 7. 观测能力 M7 观测字段固定为: 1. HTTP 访问日志:`method`、`uri`、`status`、`latency_ms`、`slow_request`、`request_id` 2. 错误日志:`request_id`、`status`、`error_code` 3. 上游失败:`provider`、`operation`、`request_id`、`status/code`、`message` 4. 关键 reducer:操作名、主实体 ID、结果状态 5. 资产任务:`task_id`、`character_id/entity_id`、`asset_kind`、`status` 慢请求阈值默认 `1000ms`,可通过 `GENARRATIVE_SLOW_REQUEST_THRESHOLD_MS` 覆盖。 ## 8. 数据迁移与回滚 当前 M7 不做一次性历史数据导入 SpacetimeDB 的危险迁移,采用按主链确认的渐进策略: 1. 已迁移主链以 SpacetimeDB 为真相源。 2. 未迁移或灰度失败主链必须继续迁入 Rust 主线后再开放,不回退到旧 Node 工程。 3. 资产二进制以 OSS 为真相,不回滚到本地 `public/generated-*` 写盘。 4. 若 SpacetimeDB schema 需要清库重发,只允许在开发库或明确灰度库执行 `--clear-database`。 5. 生产回滚优先切反向代理目标,不优先改代码。 ## 9. 验收定义 M7 完成时必须满足: 1. M7 文档、脚本、任务清单均同步。 2. `api-server` 和 `spacetime-module` 至少通过 `cargo check`。 3. 基础 smoke 脚本可执行,并覆盖 `healthz + envelope + request id`。 4. Rust 主线预检和 smoke 脚本可执行。 5. Vite dev proxy 默认指向 Rust `api-server`,仅保留 Rust 目标覆盖开关。 6. `spacetime-module` 已从单 `lib.rs` 拆为按 `runtime / gameplay / custom_world / asset_metadata / ai` 组织的文件结构。