删除重构任务清单
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-28 15:36:47 +08:00
parent 3c5ba97775
commit a4c6238847
18 changed files with 0 additions and 3170 deletions

View File

@@ -1,12 +0,0 @@
# 后端重写任务清单入口
完整总纲与拆分后的任务文件已统一整理到根目录新建目录:
- [backend-rewrite-tasklist/README.md](./backend-rewrite-tasklist/README.md)
其中:
- 总纲主清单:[backend-rewrite-tasklist/00_MASTER_TASKLIST.md](./backend-rewrite-tasklist/00_MASTER_TASKLIST.md)
- 阶段拆分文件入口:[backend-rewrite-tasklist/README.md](./backend-rewrite-tasklist/README.md)
后续如继续细化任务,请优先在该目录内维护,避免根目录散落多份版本。

View File

@@ -1,154 +0,0 @@
# SpacetimeDB + Axum + 阿里云 OSS 后端重写任务总纲
日期:`2026-04-20`
关联设计文档:
- [../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md](../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md)
- [../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md](../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md)
- [../docs/technical/RPG_ENTRY_RUNTIME_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md](../docs/technical/RPG_ENTRY_RUNTIME_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md)
- [../docs/technical/CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md](../docs/technical/CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md)
关联拆分任务:
- [01_M0_M2_FOUNDATION_AND_AUTH.md](./01_M0_M2_FOUNDATION_AND_AUTH.md)
- [02_M3_RUNTIME_PROFILE.md](./02_M3_RUNTIME_PROFILE.md)
- [03_M4_STORY_AND_GAMEPLAY.md](./03_M4_STORY_AND_GAMEPLAY.md)
- [04_M5_CUSTOM_WORLD_AND_AGENT.md](./04_M5_CUSTOM_WORLD_AND_AGENT.md)
- [05_M6_ASSETS_OSS_EDITOR.md](./05_M6_ASSETS_OSS_EDITOR.md)
- [06_M7_TEST_DEPLOY_CUTOVER.md](./06_M7_TEST_DEPLOY_CUTOVER.md)
- [07_CROSS_CUTTING_AND_ACCEPTANCE.md](./07_CROSS_CUTTING_AND_ACCEPTANCE.md)
## 0. 使用说明
这份总纲用于把控整体重写节奏,拆分文件用于落地执行。
执行原则:
1. 第一阶段优先兼容当前 `/api/*``/healthz``/generated-*` 访问习惯。
2. 不允许先删旧能力再补新能力,必须按能力面平移。
3. 以当前 Node 后端 `96` 条路由、`6` 个挂载面、`12` 个模块为最低覆盖基线。
4. 每个阶段完成后,都要形成可运行、可回归、可继续迭代的中间态。
## 1. 总体里程碑
- [x] `M0`:冻结当前后端能力清单与迁移边界
- [ ] `M1`:搭建 Rust 工作区、Axum 主入口与基础中间件
- [ ] `M2`完成鉴权、会话、JWT、refresh cookie 主链迁移
- [ ] `M3`:完成 runtime snapshot / settings / profile 迁移
- [ ] `M4`:完成 story action 主循环与核心 gameplay reducer 迁移
- [ ] `M5`:完成 custom world / agent 主链迁移
- [ ] `M6`:完成 assets / OSS 主链迁移
- [ ] `M7`:完成联调、回归、部署与切流准备
## 2. 阶段导航
### `M0 ~ M2`
重点:
1. 冻结能力清单
2. 搭建 Rust workspace
3. 搭建 Axum 基础设施
4. 迁移鉴权、会话、JWT、refresh cookie
详见:
- [01_M0_M2_FOUNDATION_AND_AUTH.md](./01_M0_M2_FOUNDATION_AND_AUTH.md)
### `M3`
重点:
1. 迁移 runtime snapshot
2. 迁移 settings
3. 迁移 profile dashboard / browse history / save archive
详见:
- [02_M3_RUNTIME_PROFILE.md](./02_M3_RUNTIME_PROFILE.md)
### `M4`
重点:
1. 迁移 RPG runtime story 主循环
2. 迁移 RPG 入口 / session / runtime 对应的后端边界与编译职责
3. 兼容当前 story view model 与 state 恢复接口,并与 `rpgEntry / rpgSession / rpgRuntime / rpgRuntimeStory` 口径对齐
详见:
- [03_M4_STORY_AND_GAMEPLAY.md](./03_M4_STORY_AND_GAMEPLAY.md)
### `M5`
重点:
1. 迁移 RPG 创作主链Agent session、result preview、published profile
2. 迁移 works / library / gallery / publish / enter-world 配套链路
3.`custom-world/sessions` 传统问答流只按历史兼容台账处理,不再作为当前主链扩展目标
详见:
- [04_M5_CUSTOM_WORLD_AND_AGENT.md](./04_M5_CUSTOM_WORLD_AND_AGENT.md)
### `M6`
重点:
1. 迁移 assets
2. 接入阿里云 OSS
3. 做旧静态资源路径兼容
详见:
- [05_M6_ASSETS_OSS_EDITOR.md](./05_M6_ASSETS_OSS_EDITOR.md)
### `M7`
重点:
1. 联调
2. 回归
3. 部署
4. 观测
5. 灰度切流
6. 收口 `spacetime-module` 主工程结构,拆分过大的 `src/lib.rs`
详见:
- [06_M7_TEST_DEPLOY_CUTOVER.md](./06_M7_TEST_DEPLOY_CUTOVER.md)
## 3. 横向专项
以下专项贯穿整个迁移期:
1. contract 与前端兼容
2. SpacetimeDB schema 演进治理
3. 大对象与缓存治理
4. 文档持续维护
详见:
- [07_CROSS_CUTTING_AND_ACCEPTANCE.md](./07_CROSS_CUTTING_AND_ACCEPTANCE.md)
## 4. 第一优先级建议执行顺序
1. 先做 `M0`,冻结基线,避免迁移过程中口径漂移。
2. 再做 `M1 + M2`,先把 Axum 壳与鉴权打稳。
3. 当前执行顺序允许前置 `M6` 的 OSS 基础设施与直传票据能力,为后续各阶段复用统一资产入口。
4. 再做 `M3`优先跑通快照、设置、profile。
5. 再做 `M4`,把 story action 主循环真正迁走。
6. 然后做 `M5`,迁 custom world 与 agent。
7. 最后收口 `M6` 余下资产绑定、`M7` 部署与切流。
## 5. 最终验收清单
- [ ] 当前 `96` 条后端接口已全部迁移或有兼容替代
- [ ] 当前 `6` 个挂载面已全部迁移
- [ ] 当前 `12` 个内部模块已完成新架构落位
- [ ] Axum 已成为唯一 HTTP / SSE / 副作用边界
- [ ] SpacetimeDB 已成为唯一运行时状态真相源
- [ ] 阿里云 OSS 已成为唯一资产对象仓
- [ ] 前端主流程在不大改 UI 的前提下可跑通
- [ ] 能完成灰度切流,并保留可回退能力

View File

@@ -1,266 +0,0 @@
# M0 ~ M2基础设施与鉴权任务清单
## M0冻结能力与重写边界
### 能力冻结
- [x] 整理当前后端 6 个挂载面并锁定为重写验收基线
交付物:[M0_CAPABILITY_SURFACE_BASELINE_2026-04-20.md](./M0_CAPABILITY_SURFACE_BASELINE_2026-04-20.md)
- [x] 整理当前后端 96 条路由并生成一份“旧接口 -> 新实现”映射表
交付物:[M0_ROUTE_MIGRATION_MATRIX_2026-04-20.md](./M0_ROUTE_MIGRATION_MATRIX_2026-04-20.md)
- [x] 整理当前 12 个内部模块并锁定迁移归属
交付物:[M0_MODULE_MIGRATION_BASELINE_2026-04-20.md](./M0_MODULE_MIGRATION_BASELINE_2026-04-20.md)
- [x] 整理当前所有 SSE 接口与事件格式
交付物:[M0_SSE_INTERFACE_BASELINE_2026-04-20.md](./M0_SSE_INTERFACE_BASELINE_2026-04-20.md)
- [x] 整理当前所有 `/generated-*` 静态资源前缀
交付物:[M0_GENERATED_STATIC_PREFIX_BASELINE_2026-04-20.md](./M0_GENERATED_STATIC_PREFIX_BASELINE_2026-04-20.md)
- [x] 整理当前前端直接依赖的响应头、envelope、错误格式
交付物:[M0_FRONTEND_RESPONSE_CONTRACT_BASELINE_2026-04-20.md](./M0_FRONTEND_RESPONSE_CONTRACT_BASELINE_2026-04-20.md)
### 仓库边界
- [x] 确认 Rust 后端新目录名与根目录落位方案
交付物:[M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md](./M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md)
- [x] 确认旧 `server-node/` 在迁移期继续保留,不提前删除
交付物:[M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md](./M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md)
- [x] 确认前端第一阶段仍然只访问 Axum不直连 SpacetimeDB
交付物:[M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md](./M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md)
- [x] 确认外部副作用统一收口在 Axum不放进 SpacetimeDB 模块
交付物:[M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md](./M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md)
### 交付物
- [x] 新增“接口映射表”文档
交付物:[M0_ROUTE_MIGRATION_MATRIX_2026-04-20.md](./M0_ROUTE_MIGRATION_MATRIX_2026-04-20.md)
- [x] 新增“模块迁移清单”文档
交付物:[M0_MODULE_MIGRATION_BASELINE_2026-04-20.md](./M0_MODULE_MIGRATION_BASELINE_2026-04-20.md)
- [x] 新增“阶段验收矩阵”文档
交付物:[M0_PHASE_ACCEPTANCE_MATRIX_2026-04-20.md](./M0_PHASE_ACCEPTANCE_MATRIX_2026-04-20.md)
## M1Rust 工作区与 Axum 基础设施
### 工作区搭建
- [x] 在根目录新增 `server-rs/`
交付物:[../server-rs/README.md](../server-rs/README.md)
- [x] 创建 workspace `Cargo.toml`
交付物:[../server-rs/Cargo.toml](../server-rs/Cargo.toml)
- [x] 创建 `crates/api-server`
交付物:[../server-rs/crates/api-server/README.md](../server-rs/crates/api-server/README.md)
- [x] 创建 `crates/spacetime-module`
交付物:[../server-rs/crates/spacetime-module/README.md](../server-rs/crates/spacetime-module/README.md)
- [x] 创建 `crates/module-auth`
交付物:[../server-rs/crates/module-auth/README.md](../server-rs/crates/module-auth/README.md)
- [x] 创建 `crates/module-runtime`
交付物:[../server-rs/crates/module-runtime/README.md](../server-rs/crates/module-runtime/README.md)
- [x] 创建 `crates/module-story`
交付物:[../server-rs/crates/module-story/README.md](../server-rs/crates/module-story/README.md)
- [x] 创建 `crates/module-combat`
交付物:[../server-rs/crates/module-combat/README.md](../server-rs/crates/module-combat/README.md)
- [x] 创建 `crates/module-inventory`
交付物:[../server-rs/crates/module-inventory/README.md](../server-rs/crates/module-inventory/README.md)
- [x] 创建 `crates/module-npc`
交付物:[../server-rs/crates/module-npc/README.md](../server-rs/crates/module-npc/README.md)
- [x] 创建 `crates/module-progression`
交付物:[../server-rs/crates/module-progression/README.md](../server-rs/crates/module-progression/README.md)
- [x] 创建 `crates/module-quest`
交付物:[../server-rs/crates/module-quest/README.md](../server-rs/crates/module-quest/README.md)
- [x] 创建 `crates/module-runtime-item`
交付物:[../server-rs/crates/module-runtime-item/README.md](../server-rs/crates/module-runtime-item/README.md)
- [x] 创建 `crates/module-custom-world`
交付物:[../server-rs/crates/module-custom-world/README.md](../server-rs/crates/module-custom-world/README.md)
- [x] 创建 `crates/module-assets`
交付物:[../server-rs/crates/module-assets/README.md](../server-rs/crates/module-assets/README.md)
- [x] 创建 `crates/module-ai`
交付物:[../server-rs/crates/module-ai/README.md](../server-rs/crates/module-ai/README.md)
- [x] 创建 `crates/shared-contracts`
交付物:[../server-rs/crates/shared-contracts/README.md](../server-rs/crates/shared-contracts/README.md)
- [x] 创建 `crates/shared-kernel`
交付物:[../server-rs/crates/shared-kernel/README.md](../server-rs/crates/shared-kernel/README.md)
- [x] 创建 `crates/shared-logging`
交付物:[../server-rs/crates/shared-logging/README.md](../server-rs/crates/shared-logging/README.md)
- [x] 创建 `crates/platform-auth`
交付物:[../server-rs/crates/platform-auth/README.md](../server-rs/crates/platform-auth/README.md)
- [x] 创建 `crates/platform-oss`
交付物:[../server-rs/crates/platform-oss/README.md](../server-rs/crates/platform-oss/README.md)
- [x] 创建 `crates/platform-llm`
交付物:[../server-rs/crates/platform-llm/README.md](../server-rs/crates/platform-llm/README.md)
- [x] 创建 `crates/spacetime-client`
交付物:[../server-rs/crates/spacetime-client/README.md](../server-rs/crates/spacetime-client/README.md)
- [x] 创建 `crates/tests-support`
交付物:[../server-rs/crates/tests-support/README.md](../server-rs/crates/tests-support/README.md)
### Axum 基础能力
- [x] 搭建 `main.rs` / `Router` / `with_state`
交付物:[../server-rs/crates/api-server/src/main.rs](../server-rs/crates/api-server/src/main.rs)
- [x] 接入统一配置加载
交付物:[../server-rs/crates/api-server/src/config.rs](../server-rs/crates/api-server/src/config.rs)
- [x] 接入统一日志与 tracing
交付物:[../docs/technical/RUST_SHARED_LOGGING_CRATE_DESIGN_2026-04-21.md](../docs/technical/RUST_SHARED_LOGGING_CRATE_DESIGN_2026-04-21.md)、[../server-rs/crates/shared-logging/src/lib.rs](../server-rs/crates/shared-logging/src/lib.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)、[../server-rs/crates/api-server/src/main.rs](../server-rs/crates/api-server/src/main.rs)
- [x] 接入 `request_id` 中间件
交付物:[../server-rs/crates/api-server/src/request_context.rs](../server-rs/crates/api-server/src/request_context.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 接入统一错误处理中间件
交付物:[../server-rs/crates/api-server/src/http_error.rs](../server-rs/crates/api-server/src/http_error.rs)、[../server-rs/crates/api-server/src/error_middleware.rs](../server-rs/crates/api-server/src/error_middleware.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 接入当前项目兼容的 response envelope
交付物:[../server-rs/crates/api-server/src/api_response.rs](../server-rs/crates/api-server/src/api_response.rs)、[../server-rs/crates/api-server/src/request_context.rs](../server-rs/crates/api-server/src/request_context.rs)、[../server-rs/crates/api-server/src/http_error.rs](../server-rs/crates/api-server/src/http_error.rs)
- [x] 接入 `x-request-id`
交付物:[../server-rs/crates/api-server/src/response_headers.rs](../server-rs/crates/api-server/src/response_headers.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 接入 `x-api-version`
交付物:[../server-rs/crates/api-server/src/response_headers.rs](../server-rs/crates/api-server/src/response_headers.rs)
- [x] 接入 `x-route-version`
交付物:[../server-rs/crates/api-server/src/response_headers.rs](../server-rs/crates/api-server/src/response_headers.rs)
- [x] 接入 `x-response-time-ms`
交付物:[../server-rs/crates/api-server/src/response_headers.rs](../server-rs/crates/api-server/src/response_headers.rs)、[../server-rs/crates/api-server/src/request_context.rs](../server-rs/crates/api-server/src/request_context.rs)
- [x] 实现 `/healthz`
交付物:[../server-rs/crates/api-server/src/health.rs](../server-rs/crates/api-server/src/health.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
### 基础工程脚本
- [x] 新增本地开发脚本
交付物:[../server-rs/scripts/dev.ps1](../server-rs/scripts/dev.ps1)、[../server-rs/scripts/dev.sh](../server-rs/scripts/dev.sh)
- [x] 新增测试脚本
交付物:[../server-rs/scripts/test.ps1](../server-rs/scripts/test.ps1)、[../server-rs/scripts/test.sh](../server-rs/scripts/test.sh)
- [x] 新增 lint / fmt / clippy / check 脚本
交付物:[../server-rs/scripts/check.ps1](../server-rs/scripts/check.ps1)、[../server-rs/scripts/check.sh](../server-rs/scripts/check.sh)
- [x] 新增 smoke 脚本
交付物:[../server-rs/scripts/smoke.ps1](../server-rs/scripts/smoke.ps1)、[../server-rs/scripts/smoke.sh](../server-rs/scripts/smoke.sh)
- [x] 新增 SpacetimeDB 本地开发脚本
交付物:[../server-rs/scripts/spacetime-dev.ps1](../server-rs/scripts/spacetime-dev.ps1)、[../server-rs/scripts/spacetime-dev.sh](../server-rs/scripts/spacetime-dev.sh)
### 阶段验收
- [x] Axum 服务可独立启动
证据:`./server-rs/scripts/smoke.ps1` 已通过,覆盖临时启动 `api-server`、等待 `/healthz` 就绪并验证 raw / envelope 协议。
- [x] `/healthz` 返回与当前工程兼容
- [x] 基础 response envelope 与 request id 行为稳定
证据:`cargo test -p api-server --manifest-path server-rs/Cargo.toml` 已通过,覆盖 envelope 协商与 `/healthz` 头部回写。
- [x] Rust workspace 能完整编译通过
证据:`cargo check -p api-server --manifest-path server-rs/Cargo.toml` 已通过。
## M2鉴权、会话、JWT 与 refresh cookie
### SpacetimeDB 身份表
- [x] 设计 `user_account`
交付物:[../docs/technical/SPACETIMEDB_AUTH_USER_ACCOUNT_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_AUTH_USER_ACCOUNT_TABLE_DESIGN_2026-04-21.md)
- [x] 设计 `auth_identity`
交付物:[../docs/technical/SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md)
- [x] 设计 `refresh_session`
交付物:[../docs/technical/SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md)
- [x] 设计 `auth_audit_log`
交付物:[../docs/technical/SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md)
- [x] 设计 `auth_risk_block`
交付物:[../docs/technical/SPACETIMEDB_AUTH_RISK_BLOCK_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_AUTH_RISK_BLOCK_TABLE_DESIGN_2026-04-21.md)
- [x] 设计 `sms_auth_event`
交付物:[../docs/technical/SPACETIMEDB_SMS_AUTH_EVENT_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_SMS_AUTH_EVENT_TABLE_DESIGN_2026-04-21.md)
- [x] 设计 `wechat_auth_state`
交付物:[../docs/technical/SPACETIMEDB_WECHAT_AUTH_STATE_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_WECHAT_AUTH_STATE_TABLE_DESIGN_2026-04-21.md)
### Axum 鉴权服务
- [x] 实现密码登录
交付物:[../docs/technical/PASSWORD_ENTRY_FLOW_DESIGN_2026-04-21.md](../docs/technical/PASSWORD_ENTRY_FLOW_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/platform-auth/src/lib.rs](../server-rs/crates/platform-auth/src/lib.rs)、[../server-rs/crates/api-server/src/password_entry.rs](../server-rs/crates/api-server/src/password_entry.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现账号自动创建 / 幂等登录兼容策略
交付物:[../docs/technical/PASSWORD_ENTRY_FLOW_DESIGN_2026-04-21.md](../docs/technical/PASSWORD_ENTRY_FLOW_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现 Bearer JWT 校验
交付物:[../docs/technical/PLATFORM_AUTH_JWT_ADAPTER_DESIGN_2026-04-21.md](../docs/technical/PLATFORM_AUTH_JWT_ADAPTER_DESIGN_2026-04-21.md)、[../server-rs/crates/platform-auth/src/lib.rs](../server-rs/crates/platform-auth/src/lib.rs)、[../server-rs/crates/api-server/src/auth.rs](../server-rs/crates/api-server/src/auth.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现 refresh cookie 读取
交付物:[../docs/technical/PLATFORM_AUTH_REFRESH_COOKIE_ADAPTER_DESIGN_2026-04-21.md](../docs/technical/PLATFORM_AUTH_REFRESH_COOKIE_ADAPTER_DESIGN_2026-04-21.md)、[../server-rs/crates/platform-auth/src/lib.rs](../server-rs/crates/platform-auth/src/lib.rs)、[../server-rs/crates/api-server/src/auth.rs](../server-rs/crates/api-server/src/auth.rs)、[../server-rs/crates/api-server/src/config.rs](../server-rs/crates/api-server/src/config.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现 refresh token 轮换
交付物:[../docs/technical/AUTH_REFRESH_ROTATION_DESIGN_2026-04-21.md](../docs/technical/AUTH_REFRESH_ROTATION_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/platform-auth/src/lib.rs](../server-rs/crates/platform-auth/src/lib.rs)、[../server-rs/crates/api-server/src/auth_session.rs](../server-rs/crates/api-server/src/auth_session.rs)、[../server-rs/crates/api-server/src/password_entry.rs](../server-rs/crates/api-server/src/password_entry.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现多端会话身份建模与会话列表查询
交付物:[../docs/technical/MULTI_DEVICE_SESSION_IDENTITY_DESIGN_2026-04-21.md](../docs/technical/MULTI_DEVICE_SESSION_IDENTITY_DESIGN_2026-04-21.md)、[../docs/technical/AUTH_SESSIONS_QUERY_DESIGN_2026-04-21.md](../docs/technical/AUTH_SESSIONS_QUERY_DESIGN_2026-04-21.md)、[../docs/technical/SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md)、[../server-rs/crates/api-server/src/session_client.rs](../server-rs/crates/api-server/src/session_client.rs)、[../server-rs/crates/api-server/src/auth_sessions.rs](../server-rs/crates/api-server/src/auth_sessions.rs)、[../server-rs/crates/api-server/src/password_entry.rs](../server-rs/crates/api-server/src/password_entry.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../packages/shared/src/contracts/auth.ts](../packages/shared/src/contracts/auth.ts)
- [x] 实现会话吊销
交付物:[../docs/technical/AUTH_LOGOUT_CURRENT_SESSION_DESIGN_2026-04-21.md](../docs/technical/AUTH_LOGOUT_CURRENT_SESSION_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/api-server/src/auth.rs](../server-rs/crates/api-server/src/auth.rs)、[../server-rs/crates/api-server/src/auth_session.rs](../server-rs/crates/api-server/src/auth_session.rs)、[../server-rs/crates/api-server/src/logout.rs](../server-rs/crates/api-server/src/logout.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现全端登出
交付物:[../docs/technical/AUTH_LOGOUT_ALL_DESIGN_2026-04-21.md](../docs/technical/AUTH_LOGOUT_ALL_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/api-server/src/logout_all.rs](../server-rs/crates/api-server/src/logout_all.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现 `me` 查询
交付物:[../docs/technical/AUTH_ME_QUERY_DESIGN_2026-04-21.md](../docs/technical/AUTH_ME_QUERY_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/api-server/src/auth_me.rs](../server-rs/crates/api-server/src/auth_me.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
### 手机验证码登录
- [ ] 接入阿里云短信发送 adapter
- [x] 实现发送验证码接口
交付物:[../docs/technical/PHONE_AUTH_AXUM_MINIMAL_FLOW_DESIGN_2026-04-21.md](../docs/technical/PHONE_AUTH_AXUM_MINIMAL_FLOW_DESIGN_2026-04-21.md)、[../docs/technical/PHONE_AUTH_AXUM_RATE_LIMIT_AND_FAILURE_DESIGN_2026-04-21.md](../docs/technical/PHONE_AUTH_AXUM_RATE_LIMIT_AND_FAILURE_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/api-server/src/phone_auth.rs](../server-rs/crates/api-server/src/phone_auth.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现验证码校验接口
交付物:[../docs/technical/PHONE_AUTH_AXUM_MINIMAL_FLOW_DESIGN_2026-04-21.md](../docs/technical/PHONE_AUTH_AXUM_MINIMAL_FLOW_DESIGN_2026-04-21.md)、[../docs/technical/PHONE_AUTH_AXUM_RATE_LIMIT_AND_FAILURE_DESIGN_2026-04-21.md](../docs/technical/PHONE_AUTH_AXUM_RATE_LIMIT_AND_FAILURE_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/api-server/src/phone_auth.rs](../server-rs/crates/api-server/src/phone_auth.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现手机号绑定
交付物:[../docs/technical/PHONE_AUTH_AXUM_MINIMAL_FLOW_DESIGN_2026-04-21.md](../docs/technical/PHONE_AUTH_AXUM_MINIMAL_FLOW_DESIGN_2026-04-21.md)、[../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md](../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/api-server/src/wechat_auth.rs](../server-rs/crates/api-server/src/wechat_auth.rs)
- [ ] 实现手机号换绑
- [x] 实现发送频率限制
交付物:[../docs/technical/PHONE_AUTH_AXUM_RATE_LIMIT_AND_FAILURE_DESIGN_2026-04-21.md](../docs/technical/PHONE_AUTH_AXUM_RATE_LIMIT_AND_FAILURE_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/api-server/src/phone_auth.rs](../server-rs/crates/api-server/src/phone_auth.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现验证码失败次数限制
交付物:[../docs/technical/PHONE_AUTH_AXUM_RATE_LIMIT_AND_FAILURE_DESIGN_2026-04-21.md](../docs/technical/PHONE_AUTH_AXUM_RATE_LIMIT_AND_FAILURE_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/api-server/src/phone_auth.rs](../server-rs/crates/api-server/src/phone_auth.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [ ] 实现 captcha 触发逻辑
- [ ] 实现风控封禁与解除
### 微信登录
- [x] 接入微信 OAuth adapter
交付物:[../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md](../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md)、[../server-rs/crates/api-server/src/wechat_provider.rs](../server-rs/crates/api-server/src/wechat_provider.rs)、[../server-rs/crates/api-server/src/state.rs](../server-rs/crates/api-server/src/state.rs)
- [x] 实现 `wechat/start`
交付物:[../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md](../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md)、[../server-rs/crates/api-server/src/wechat_auth.rs](../server-rs/crates/api-server/src/wechat_auth.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现 `wechat/callback`
交付物:[../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md](../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md)、[../server-rs/crates/api-server/src/wechat_auth.rs](../server-rs/crates/api-server/src/wechat_auth.rs)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现微信身份绑定
交付物:[../docs/technical/SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)
- [x] 实现微信账号补绑手机号
交付物:[../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md](../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md)、[../server-rs/crates/api-server/src/wechat_auth.rs](../server-rs/crates/api-server/src/wechat_auth.rs)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 实现桌面端 / 微信内打开场景区分
交付物:[../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md](../docs/technical/WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md)、[../server-rs/crates/api-server/src/wechat_auth.rs](../server-rs/crates/api-server/src/wechat_auth.rs)、[../server-rs/crates/api-server/src/session_client.rs](../server-rs/crates/api-server/src/session_client.rs)
### OIDC 与 SpacetimeDB 身份透传
- [x] 设计 JWT claims
交付物:[../docs/technical/OIDC_JWT_CLAIMS_DESIGN_2026-04-21.md](../docs/technical/OIDC_JWT_CLAIMS_DESIGN_2026-04-21.md)
- [x] 确认 `iss/sub/sid/provider/roles` 字段
交付物:[../docs/technical/OIDC_JWT_CLAIMS_DESIGN_2026-04-21.md](../docs/technical/OIDC_JWT_CLAIMS_DESIGN_2026-04-21.md)
- [x] 让 Axum 自身可校验 JWT
交付物:[../docs/technical/PLATFORM_AUTH_JWT_ADAPTER_DESIGN_2026-04-21.md](../docs/technical/PLATFORM_AUTH_JWT_ADAPTER_DESIGN_2026-04-21.md)、[../server-rs/crates/platform-auth/README.md](../server-rs/crates/platform-auth/README.md)、[../server-rs/crates/api-server/src/auth.rs](../server-rs/crates/api-server/src/auth.rs)
- [ ] 让 SpacetimeDB 可识别 Axum 签发的身份令牌
- [ ] 验证 reducer / view 可读取用户身份上下文
### 当前接口兼容
- [x] 兼容 `/api/auth/login-options`
交付物:[../docs/technical/AUTH_LOGIN_OPTIONS_DESIGN_2026-04-21.md](../docs/technical/AUTH_LOGIN_OPTIONS_DESIGN_2026-04-21.md)、[../server-rs/crates/api-server/src/login_options.rs](../server-rs/crates/api-server/src/login_options.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 兼容 `/api/auth/entry`
交付物:[../server-rs/crates/api-server/src/password_entry.rs](../server-rs/crates/api-server/src/password_entry.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 兼容 `/api/auth/me`
交付物:[../server-rs/crates/api-server/src/auth_me.rs](../server-rs/crates/api-server/src/auth_me.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 兼容 `/api/auth/logout`
交付物:[../server-rs/crates/api-server/src/logout.rs](../server-rs/crates/api-server/src/logout.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 兼容 `/api/auth/logout-all`
交付物:[../docs/technical/AUTH_LOGOUT_ALL_DESIGN_2026-04-21.md](../docs/technical/AUTH_LOGOUT_ALL_DESIGN_2026-04-21.md)、[../server-rs/crates/api-server/src/logout_all.rs](../server-rs/crates/api-server/src/logout_all.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)
- [x] 兼容 `/api/auth/refresh`
交付物:[../server-rs/crates/api-server/src/auth_session.rs](../server-rs/crates/api-server/src/auth_session.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)
- [x] 兼容 `/api/auth/sessions`
交付物:[../docs/technical/AUTH_SESSIONS_QUERY_DESIGN_2026-04-21.md](../docs/technical/AUTH_SESSIONS_QUERY_DESIGN_2026-04-21.md)、[../server-rs/crates/api-server/src/auth_sessions.rs](../server-rs/crates/api-server/src/auth_sessions.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)
- [ ] 兼容 `/api/auth/sessions/:sessionId/revoke`
- [ ] 兼容 `/api/auth/audit-logs`
- [ ] 兼容 `/api/auth/risk-blocks`
- [ ] 兼容 `/api/auth/risk-blocks/:scopeType/lift`
- [x] 兼容 `/api/auth/phone/send-code`
交付物:[../docs/technical/PHONE_AUTH_AXUM_MINIMAL_FLOW_DESIGN_2026-04-21.md](../docs/technical/PHONE_AUTH_AXUM_MINIMAL_FLOW_DESIGN_2026-04-21.md)、[../server-rs/crates/api-server/src/phone_auth.rs](../server-rs/crates/api-server/src/phone_auth.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)
- [x] 兼容 `/api/auth/phone/login`
交付物:[../docs/technical/PHONE_AUTH_AXUM_MINIMAL_FLOW_DESIGN_2026-04-21.md](../docs/technical/PHONE_AUTH_AXUM_MINIMAL_FLOW_DESIGN_2026-04-21.md)、[../server-rs/crates/api-server/src/phone_auth.rs](../server-rs/crates/api-server/src/phone_auth.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)、[../server-rs/crates/module-auth/src/lib.rs](../server-rs/crates/module-auth/src/lib.rs)
- [ ] 兼容 `/api/auth/phone/change`
- [x] 兼容 `/api/auth/wechat/start`
交付物:[../server-rs/crates/api-server/src/wechat_auth.rs](../server-rs/crates/api-server/src/wechat_auth.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)、[../src/services/authService.ts](../src/services/authService.ts)
- [x] 兼容 `/api/auth/wechat/callback`
交付物:[../server-rs/crates/api-server/src/wechat_auth.rs](../server-rs/crates/api-server/src/wechat_auth.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)、[../src/services/authService.ts](../src/services/authService.ts)
- [x] 兼容 `/api/auth/wechat/bind-phone`
交付物:[../server-rs/crates/api-server/src/wechat_auth.rs](../server-rs/crates/api-server/src/wechat_auth.rs)、[../server-rs/crates/api-server/src/app.rs](../server-rs/crates/api-server/src/app.rs)、[../src/services/authService.ts](../src/services/authService.ts)
### 阶段验收
- [x] 密码登录主链可用
证据:`cargo test -p module-auth --manifest-path server-rs/Cargo.toml``cargo test -p api-server --manifest-path server-rs/Cargo.toml` 已通过,覆盖自动建号、重复登录复用、错密码 `401`、非法用户名 `400` 与 refresh cookie 写回。
- [x] refresh cookie 主链可用
证据:`cargo test -p module-auth --manifest-path server-rs/Cargo.toml``cargo test -p api-server --manifest-path server-rs/Cargo.toml` 已通过,覆盖 refresh 成功轮换、旧 token 失效、缺少 cookie `401` 与失败时清理 cookie。
- [x] 手机验证码主链可用
证据:`cargo test -p module-auth phone --manifest-path server-rs/Cargo.toml -- --nocapture``cargo test -p api-server phone --manifest-path server-rs/Cargo.toml -- --nocapture` 已通过,覆盖发送验证码、同场景冷却 `429`、验证码错误次数耗尽 `429`、重新发送后恢复登录,以及手机号登录建号/复用与 refresh cookie 写回。
- [x] 微信登录主链可用
证据:`cargo test -p api-server --manifest-path server-rs/Cargo.toml``cargo test -p api-server wechat --manifest-path server-rs/Cargo.toml``cargo test -p module-auth --manifest-path server-rs/Cargo.toml` 已通过,覆盖 `wechat/start``wechat/callback`、待绑定会话签发、手机号补绑并入已有账号,以及 `unionid` 命中后新 `openid` 映射回写。
- [ ] 所有旧鉴权接口可通过 contract 回归

View File

@@ -1,69 +0,0 @@
# M3runtime snapshot / settings / profile 任务清单
## 1. SpacetimeDB 运行时主表
- [x] 设计 `runtime_snapshot`
- [x] 设计 `runtime_setting`
- [x] 设计 `profile_dashboard_state`
- [x] 设计 `profile_wallet_ledger`
- [x] 设计 `profile_played_world`
- [x] 设计 `profile_save_archive`
- [x] 设计 `user_browse_history`
## 2. 兼容快照策略
- [x] 设计“领域表真相 + 兼容聚合快照”策略
- [x] 设计 snapshot projection 刷新机制
- [x] 迁移当前 snapshot hydration / normalize 规则
- [x] 迁移当前 save archive 聚合逻辑
- [x] 迁移当前 browse history 去重与排序逻辑
## 3. Axum facade
- [x] 兼容 `GET /api/runtime/save/snapshot`
- [x] 兼容 `PUT /api/runtime/save/snapshot`
- [x] 兼容 `DELETE /api/runtime/save/snapshot`
- [x] 兼容 `GET /api/runtime/settings`
- [x] 兼容 `PUT /api/runtime/settings`
- [x] 兼容 `GET /api/runtime/profile/dashboard`
- [x] 兼容 `GET /api/profile/dashboard`
- [x] 兼容 `GET /api/runtime/profile/wallet-ledger`
- [x] 兼容 `GET /api/profile/wallet-ledger`
- [x] 兼容 `GET /api/runtime/profile/play-stats`
- [x] 兼容 `GET /api/profile/play-stats`
- [x] 兼容 `GET /api/runtime/profile/save-archives`
- [x] 兼容 `GET /api/profile/save-archives`
- [x] 兼容 `POST /api/runtime/profile/save-archives/:worldKey`
- [x] 兼容 `POST /api/profile/save-archives/:worldKey`
- [x] 兼容 `GET /api/runtime/profile/browse-history`
- [x] 兼容 `POST /api/runtime/profile/browse-history`
- [x] 兼容 `DELETE /api/runtime/profile/browse-history`
- [x] 兼容 `GET /api/profile/browse-history`
- [x] 兼容 `POST /api/profile/browse-history`
- [x] 兼容 `DELETE /api/profile/browse-history`
## 4. 阶段验收
- [ ] 登录用户可正常保存、读取、删除存档
- [x] 兼容路径与主路径返回一致
- [x] profile dashboard / browse history / save archive 行为一致
- [ ] 前端当前恢复流程可在不改 UI 的前提下跑通
## 5. 本轮进展记录
- `2026-04-21`:已完成 `runtime_setting` 首版设计与 `GET/PUT /api/runtime/settings` 的 Rust 主链迁移。
- 本轮已落地 `module-runtime``spacetime-module``spacetime-client``api-server` 四层串联,并补齐定向测试。
- 详细设计与字段冻结见:
- [../docs/technical/M3_RUNTIME_SETTINGS_AXUM_SPACETIMEDB_DESIGN_2026-04-21.md](../docs/technical/M3_RUNTIME_SETTINGS_AXUM_SPACETIMEDB_DESIGN_2026-04-21.md)
- `2026-04-22`:已完成 `user_browse_history` 表设计冻结、去重与排序规则迁移,以及 `/api/runtime/profile/browse-history``/api/profile/browse-history` 双路径 facade 落地。
- `2026-04-22`:已补 `browse history` 的 API 入口必填字段校验、批量 shape 兼容与定向测试,详细设计见:
- [../docs/technical/M3_BROWSE_HISTORY_AXUM_SPACETIMEDB_DESIGN_2026-04-21.md](../docs/technical/M3_BROWSE_HISTORY_AXUM_SPACETIMEDB_DESIGN_2026-04-21.md)
- `2026-04-22`:已冻结 `profile_dashboard_state``profile_wallet_ledger``profile_played_world` 三张 projection 表,以及 `dashboard / wallet-ledger / play-stats` 的 Axum + SpacetimeDB 读链设计。
- `2026-04-22`:已完成 `api-server``runtime_profile` facade 编译与定向测试收口,`/api/runtime/profile/*``/api/profile/*` 六条只读路由均已接通。
- `2026-04-22`:已通过 `cargo check -p api-server --tests --message-format short``cargo test -p shared-contracts --lib``cargo test -p api-server runtime_profile::tests:: -- --nocapture` 验证本轮 profile projection 读链。
- 详细设计见:
- [../docs/technical/M3_PROFILE_DASHBOARD_AXUM_SPACETIMEDB_DESIGN_2026-04-22.md](../docs/technical/M3_PROFILE_DASHBOARD_AXUM_SPACETIMEDB_DESIGN_2026-04-22.md)
- `2026-04-22`:已完成 `runtime_snapshot``profile_save_archive` 与“领域表真相 + 兼容聚合快照”方案落地,接通 `/api/runtime/save/snapshot``/api/runtime/profile/save-archives``/api/profile/save-archives` 与恢复存档双路径 facade。
- `2026-04-22`:已通过 `cargo test -p shared-kernel --lib``cargo test -p module-runtime --lib``cargo check -p spacetime-module --message-format short``cargo build -p spacetime-module --target wasm32-unknown-unknown --release --message-format short``cargo check -p spacetime-client --message-format short``cargo check -p api-server --tests --message-format short``cargo test -p api-server runtime_save::tests:: -- --nocapture` 验证 snapshot/save archive 主链编译与 facade。
- 详细设计见:
- [../docs/technical/M3_RUNTIME_SNAPSHOT_SAVE_ARCHIVE_AXUM_SPACETIMEDB_DESIGN_2026-04-22.md](../docs/technical/M3_RUNTIME_SNAPSHOT_SAVE_ARCHIVE_AXUM_SPACETIMEDB_DESIGN_2026-04-22.md)

View File

@@ -1,318 +0,0 @@
# M4story action 与 gameplay reducer 任务清单
## 0. 当前执行基线
本阶段与当前仓库里的 RPG 入口与运行时主链重构直接对应,统一以以下文档为准:
1. [../docs/technical/RPG_ENTRY_RUNTIME_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md](../docs/technical/RPG_ENTRY_RUNTIME_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md)
2. [../docs/technical/FRONTEND_TO_BACKEND_MIGRATION_EXECUTION_PLAN_2026-04-21.md](../docs/technical/FRONTEND_TO_BACKEND_MIGRATION_EXECUTION_PLAN_2026-04-21.md)
3. [../docs/technical/M4_RPG_RUNTIME_STORY_SPACETIMEDB_BASELINE_2026-04-21.md](../docs/technical/M4_RPG_RUNTIME_STORY_SPACETIMEDB_BASELINE_2026-04-21.md)
当前任务清单只维护 Axum / SpacetimeDB 重写侧的后端迁移项,不再把旧 `GameShell / runtimeRoutes.ts / storyActionService.ts` 命名视为新架构目标。
### 当前进展(`2026-04-22`
本阶段首轮已先把 `server-rs` 从“只有 `module-story` 占位目录”推进到“SpacetimeDB 侧 story 会话基座真实可编译”:
1. 已新增 `server-rs/crates/module-story` 真实 crate。
2. 已冻结 `story_session / story_event` 的首版领域类型、状态枚举和字段校验 helper。
3. 已在 `server-rs/crates/spacetime-module` 中新增 `story_session``story_event` 两张表。
4. 已新增 `begin_story_session``continue_story` 两个 reducer形成最小会话事件链。
5. 已新增 `begin_story_session_and_return``continue_story_and_return` 两个 procedure形成可同步返回快照的最小 story session contract。
6. 已重新执行 `spacetime generate`,把 `story_session / story_event` Rust bindings 刷入 `spacetime-client/src/module_bindings`
7. 已在 `server-rs/crates/spacetime-client` 中新增 `begin_story_session(...)``continue_story(...)` facade。
8. 已在 `server-rs/crates/api-server` 中新增:
- `POST /api/story/sessions`
- `POST /api/story/sessions/continue`
9. 已执行 `cargo check -p module-story -p spacetime-module -p spacetime-client -p api-server` 并通过。
6. 已新增 `docs/technical/M4_MODULE_COMBAT_SPACETIMEDB_BASELINE_2026-04-21.md`,冻结 `battle_state``resolve_combat_action` 的首版字段与规则口径。
7. 已新增 `server-rs/crates/module-runtime-item` 真实 crate。
8. 已冻结 runtime item 侧奖励快照与物品写回基线,为后续奖励链并入 inventory / quest / combat 提供统一底层能力。
9. 已在 `server-rs/crates/spacetime-module` 中补齐 runtime item / inventory / quest / combat 所需的奖励落表与回写依赖。
10. 当前 M4 runtime story compat bridge 已明确移除旧 `treasure_*` 遭遇动作概念,不再把宝箱遭遇视作本阶段 runtime story 主链目标。
11. 已新增 `docs/technical/M4_RPG_RUNTIME_INVENTORY_SPACETIMEDB_BASELINE_2026-04-21.md`,冻结 `inventory_slot``apply_inventory_mutation` 的首版字段与规则口径。
12. 已新增 `server-rs/crates/module-inventory` 真实 crate。
13. 已在 `server-rs/crates/spacetime-module` 中新增 `inventory_slot` 表。
14. 已新增 `apply_inventory_mutation` reducer形成最小背包主链。
15. 已新增 `docs/technical/M4_MODULE_NPC_SPACETIMEDB_BASELINE_2026-04-21.md`,冻结 `npc_state``resolve_npc_social_action``resolve_npc_interaction` 的首版字段与交互口径。
16. 已新增 `server-rs/crates/module-npc` 真实 crate。
17. 已在 `server-rs/crates/spacetime-module` 中新增 `npc_state` 表。
18. 已新增 `upsert_npc_state``resolve_npc_social_action``resolve_npc_interaction` 及对应 procedure。
19. 已新增 `docs/technical/M4_MODULE_NPC_COMBAT_ORCHESTRATION_BASELINE_2026-04-21.md`,冻结 `npc_fight / npc_spar``battle_state` 的最小联合编排口径。
20. 已在 `server-rs/crates/spacetime-module` 中新增 `resolve_npc_battle_interaction_and_return` procedure把 NPC 开战交互与 battle 初始化写入串到同一事务。
15. 已新增 `docs/technical/M4_MODULE_PROGRESSION_SPACETIMEDB_BASELINE_2026-04-21.md`,冻结 `player_progression / chapter_progression` 的首版字段、成长曲线与章节预算口径。
16. 已新增 `server-rs/crates/module-progression` 真实 crate。
17. 已在 `server-rs/crates/spacetime-module` 中新增 `player_progression``chapter_progression` 两张表。
18. 已新增 `get_player_progression_or_default``grant_player_progression_experience``upsert_chapter_progression``apply_chapter_progression_ledger_entry` 及对应 procedure。
19. 已新增 `docs/technical/M4_RPG_RUNTIME_QUEST_SPACETIMEDB_BASELINE_2026-04-21.md`,冻结 `quest_record / quest_log / apply_quest_signal` 的首版字段、日志口径与交付状态流转规则。
20. 已新增 `server-rs/crates/module-quest` 真实 crate。
21. 已在 `server-rs/crates/spacetime-module` 中新增 `quest_record``quest_log` 两张表。
22. 已新增 `accept_quest``apply_quest_signal``acknowledge_quest_completion``turn_in_quest` reducer形成最小任务闭环。
23. 已执行 `cargo test -p module-quest``cargo check -p spacetime-module``cargo check -p api-server` 与全量 `cargo check` 并通过。
24. 已新增 `docs/technical/M4_PROGRESSION_QUEST_COMBAT_INTEGRATION_2026-04-21.md`,冻结任务交付与战斗胜利到成长系统的联动口径。
25. 已把 `turn_in_quest` 接到 `player_progression / chapter_progression` 的最小经验写入。
26. 已把 `resolve_combat_action(Victory)` 接到 `player_progression / chapter_progression` 的最小经验写入。
27. 已把 `turn_in_quest.reward.items` 接到 `inventory_slot` 发物链,形成任务交付的最小物品奖励闭环。
28. 已新增 `docs/technical/M4_RPG_RUNTIME_STORY_SESSION_STATE_QUERY_DESIGN_2026-04-22.md`,冻结最小 `story state` 查询切片,只开放 `storySession + storyEvents` 真相态查询。
29. 已在 `server-rs/crates/api-server` 中挂出 `GET /api/story/sessions/:storySessionId/state`,通过 `spacetime-client.get_story_session_state(...)` 读取 `SpacetimeDB procedure` 返回的会话快照与事件流。
30. 已新增 `docs/technical/M4_COMBAT_REWARD_INVENTORY_INTEGRATION_2026-04-22.md`,冻结 `battle_state.reward_items``resolve_combat_action(Victory)` 发物到 `inventory_slot` 的最小联动口径。
31. 已新增 `docs/technical/M4_MODULE_COMBAT_STATE_QUERY_DESIGN_2026-04-22.md`,冻结最小 `battle state` 查询切片,只开放单个 `battleState` 真相态查询。
32. 已在 `server-rs/crates/spacetime-module` 中新增 `get_battle_state` procedure`battle_state_id` 返回当前战斗快照。
33. 已在 `server-rs/crates/spacetime-client` 中新增 `get_battle_state(...)` facade供 Axum 同步读取 battle 真相态。
34. 已在 `server-rs/crates/api-server` 中挂出 `GET /api/story/battles/:battleStateId`,通过 `spacetime-client.get_battle_state(...)` 返回单战斗快照。
35. 已在 `server-rs/crates/spacetime-client` 中新增 `resolve_npc_battle_interaction(...)` facade`resolve_npc_battle_interaction_and_return` procedure 映射为稳定 Rust record供 Axum 直接消费。
36. 已在 `server-rs/crates/api-server` 中挂出 `POST /api/story/npc/battle`,当前只接受 `npc_fight / npc_spar`,同步返回 `npcInteraction + battleState`
37. 已执行 `cargo check -p spacetime-client -p api-server` 并通过,完成 `module-npc -> spacetime-client -> api-server` 的最小 NPC 开战同步返回链闭环。
38. 已重新执行 `spacetime generate --no-config --lang rust --out-dir D:\\Genarrative\\server-rs\\crates\\spacetime-client\\src\\module_bindings --module-path D:\\Genarrative\\server-rs\\crates\\spacetime-module --include-private --yes`,把 `get_battle_state``battle_state.reward_items``custom_world_agent_session` 相关 bindings 刷入 `spacetime-client/src/module_bindings`
39. 已把 `server-rs/crates/spacetime-client/src/lib.rs` 中原本占位返回错误的 `get_battle_state(...)` 改成真实 procedure 调用,当前 battle query 已不再停留在 facade stub。
40. 已再次执行 `cargo check -p spacetime-client --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml``cargo check -p api-server --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml`,当前 battle/story 新链路在编译层已恢复通过。
41. 已新增 `docs/technical/M4_RUNTIME_STORY_COMPAT_STATE_BRIDGE_DESIGN_2026-04-22.md`,冻结旧 `POST /api/runtime/story/state/resolve` 的首版兼容桥边界,明确当前先做 DTO 与状态桥,不提前误宣称 `actions/resolve` 已可迁移。
42. 已在 `server-rs/crates/shared-contracts` 中新增 `runtime_story` 模块,冻结 `RuntimeStoryStateResolveRequest``RuntimeStoryActionResponse` 以及 `viewModel / presentation / patches / snapshot` 的首版 camelCase DTO与当前前端消费口径对齐。
43. 已恢复并重建 `server-rs/crates/api-server/src/runtime_story.rs`,把上一轮误删留下的中间态收口回可编译实现。
44. 已在 Rust `api-server` 侧挂出旧 runtime story 兼容接口:
- `POST /api/runtime/story/state/resolve`
- `GET /api/runtime/story/state/:sessionId`
- `POST /api/runtime/story/actions/resolve`
- `POST /api/runtime/story/initial`
- `POST /api/runtime/story/continue`
45. `state/resolve``actions/resolve` 已统一复用 `runtime_save` 的 SpacetimeDB 快照持久化链:
- 请求带 `snapshot` 时先写入 `runtime_snapshot`
- 请求不带 `snapshot` 时从持久化 `runtime_snapshot` 读取
- 无可用快照时返回 `409`
46. `actions/resolve` 已补齐当前前端主链需要的确定性兼容动作闭环,覆盖:
- `story_continue_adventure`
- `story_opening_camp_dialogue`
- `camp_travel_home_scene`
- `idle_call_out`
- `idle_explore_forward`
- `idle_observe_signs`
- `idle_rest_focus`
- `idle_travel_next_scene`
- `npc_preview_talk`
- `npc_chat`
- `npc_help`
- `npc_leave`
- `npc_fight`
- `npc_spar`
- `npc_recruit`
- `battle_attack_basic`
- `battle_use_skill`
- `battle_all_in_crush`
- `battle_escape_breakout`
- `battle_feint_step`
- `battle_finisher_window`
- `battle_guard_break`
- `battle_probe_pressure`
- `battle_recover_breath`
- `inventory_use`
- `equipment_equip`
- `npc_trade`
- `npc_gift`
47. `actions/resolve` 已补 `clientVersion``gameState.runtimeActionVersion` 的冲突校验、动作后版本自增、`storyHistory` 追加和 snapshot 回写。
48. `initial` / `continue` 已先落稳定 `RuntimeStoryAiResponse`
- 优先透传 `requestOptions.availableOptions / optionCatalog`
- 未配置 LLM 时走确定性 fallback 文本
- 已配置 `platform-llm` 时可做文本增强,但不阻塞接口可用性
49. `actions/resolve` 已开始迁移 Node 动作后 LLM 增强分支的最小闭环:
- `npc_chat / story_opening_camp_dialogue` 在配置 `platform-llm` 时会尝试生成对话态 `storyText`
- NPC 对话增强回包会对齐 Node 旧 `displayMode = dialogue + deferredOptions` 结构,先只展示“继续推进冒险”
- `battle victory / spar_complete / escaped` 在配置 `platform-llm` 时会尝试生成结果叙事,但不改既有规则结算
- LLM 不可用或生成失败时自动回退到确定性 `resultText / currentStory`
50. 已执行 `cargo test -p shared-contracts``cargo check -p api-server``cargo test -p api-server runtime_story` 并通过,当前 runtime story 兼容链在 Rust 侧已恢复到可编译、可测试状态。
51. 已补 Rust 侧 route boundary 回归:
- `runtime_story_routes_resolve_through_rust_route_boundary`
- `runtime_story_action_resolve_rejects_client_version_conflict`
- `runtime_story_npc_help_is_one_shot_and_restores_resources`
- `runtime_story_npc_recruit_requires_threshold_and_release_target_when_party_full`
52. 已把兼容桥里的关键 NPC 行为继续对齐到 Node 旧主链:
- `npc_chat` 好感增长改为 `max(2, 6 - chattedCount)`,首聊可从 `46 -> 52`
- `npc_help` 改为一次性援手,成功时恢复 `10 HP / 8 Mana` 且关系 `+4`
- `npc_recruit` 改为要求 `affinity >= 60`,队伍满员时必须透传 `releaseNpcId`
53. 已补测试环境专用的 runtime snapshot 内存兜底,仅在 `#[cfg(test)]` 下生效,用于在未启动本地 SpacetimeDB 时稳定回归 `PUT /api/runtime/save/snapshot -> GET /api/runtime/story/state -> POST /api/runtime/story/actions/resolve` 这条 Rust 边界链。
54. 已把 quest compat 主循环补到 Rust `runtime story` 兼容桥:
- `npc_chat_quest_offer_view`
- `npc_chat_quest_offer_replace`
- `npc_chat_quest_offer_abandon`
- `npc_quest_accept`
- `npc_quest_turn_in`
55. 已把 quest offer 对话态的 `currentStory.npcChatState.pendingQuestOffer` 与前端面板依赖的 `runtimePayload.npcChatQuestOfferAction` 一并回填到 Rust compat 回包,保证现有 quest 面板入口不回退。
56. 已把 `npc_quest_turn_in` 的最小奖励闭环补回 Rust compat handler
- quest 状态改为保留在 `gameState.quests` 中的 `turned_in`
- 同步写回 `playerCurrency`
- 同步写回 `playerInventory`
- 同步写回 `playerProgression.totalXp / level / xpToNextLevel / lastGrantedSource`
- 同步写回 NPC `affinity`
57. 已新增 quest compat Rust 回归:
- `runtime_story_quest_offer_replace_updates_pending_offer_and_payload`
- `runtime_story_quest_offer_abandon_clears_pending_offer_and_restores_chat_options`
- `runtime_story_quest_accept_writes_quest_runtime_stats_and_followup_story`
- `runtime_story_quest_turn_in_marks_quest_rewards_and_affinity`
58. 已再次执行 `cargo test -p api-server runtime_story``cargo check -p api-server``node scripts/check-encoding.mjs` 并通过,当前 quest compat 已恢复到可编译、可回归状态。
59. 已继续把 Task6 旧 inventory / NPC inventory compat 主链补回 Rust `runtime story` 兼容桥:
- `equipment_equip`
- `equipment_unequip`
- `forge_craft`
- `forge_dismantle`
- `forge_reforge`
- `npc_trade`
- `npc_gift`
60. 已把 NPC 交互态 fallback option compiler 对齐到 Node 旧顺序,当前会按条件输出:
- `npc_chat`
- `npc_help`
- `npc_spar`
- `npc_fight`
- `npc_trade`
- `npc_gift`
- `npc_quest_accept / npc_quest_turn_in`
- `npc_recruit`
- `npc_leave`
61. 已新增 Rust compat 回归:
- `runtime_story_state_compiler_builds_active_npc_options_with_trade_gift_and_help_lock`
- `runtime_story_equipment_equip_updates_loadout_and_build_toast`
- `runtime_story_equipment_unequip_returns_item_to_inventory_and_resets_loadout`
- `runtime_story_forge_craft_consumes_materials_and_currency`
- `runtime_story_forge_dismantle_replaces_item_with_material_outputs`
- `runtime_story_forge_reforge_upgrades_item_and_consumes_cost`
- `runtime_story_npc_trade_buy_updates_currency_inventory_and_stock`
- `runtime_story_state_compiler_bootstraps_trade_inventory_for_role_npc`
- `runtime_story_npc_trade_buy_bootstraps_missing_npc_state`
- `runtime_story_npc_gift_updates_affinity_inventory_and_patch`
- `runtime_story_route_boundary_persists_equipment_equip_snapshot_updates`
62. 当前 Rust compat bridge 已补入口级 NPC 状态预处理:即使快照里的 `npcStates` 为空,纯商贩型 NPC 也会在 `state/get``actions/resolve` 前自动初始化基础关系态、`stanceProfile / relationState / tradeStockSignature` 与最小 trade stock。
63. 当前 `actions/resolve` 已不再只停留在确定性 `storyText = resultText`
- 已在 Rust 侧新增 `generate_action_story_payload(...)`
- 已对齐 Node 旧分支的最小范围 `npc_chat / story_opening_camp_dialogue / terminal combat outcome`
- 当前仍未迁移 Node 那套完整 orchestrator 选项重排,只先保留既有 fallback options
64. 当前 `cargo test -p api-server runtime_story` 已提升到 30 条回归通过。
65. 已继续把 runtime story compat 的 battle 展示编译从 `api-server` 抽到独立 crate
- `module-runtime-story-compat` 当前已承接 `build_battle_runtime_story_options(...)``restore_player_resource(...)` 与战斗技能 / 推荐物品 option compiler
- `api-server/src/runtime_story/compat/battle.rs` 已删除
- `presentation.rs``npc_actions.rs` 当前统一直接复用 crate 导出的 battle helper
66. 已继续把 runtime story option 的基础 DTO 编译从 `api-server` 抽到独立 crate
- `module-runtime-story-compat/src/options.rs` 当前已承接 `build_static_runtime_story_option(...)``build_disabled_runtime_story_option(...)``build_runtime_story_option_from_story_option(...)``build_story_option_from_runtime_option(...)`
- `api-server/src/runtime_story/compat/presentation.rs` 已删除这批本地重复实现,当前只保留更贴近 NPC / quest / view-model 组装的逻辑
67. 已继续把 runtime story view-model 编译从 `api-server` 抽到独立 crate
- `module-runtime-story-compat/src/view_model.rs` 当前已承接 `build_runtime_story_view_model(...)``build_runtime_story_encounter(...)``build_runtime_story_companions(...)`
- `resolve_current_encounter_npc_state(...)` 已统一由 crate 导出,`api-server``presentation.rs``game_state.rs` 不再保留本地副本
68. 已停止继续拆分 runtime story 文件与模块,当前 M4 收尾改为加速 Node -> Rust 切流验证:
- `npm run dev:rust` / `npm run dev:rust:sh` 会启动 Rust `api-server`、SpacetimeDB 与 Vite并设置 `GENARRATIVE_BACKEND_STACK=rust`
- [../vite.config.ts](../vite.config.ts) 已补 `/api/story` 代理Rust 栈下 `/api/runtime/*``/api/story/*` 均会走 `GENARRATIVE_RUNTIME_SERVER_TARGET`
- 当前 M4 的切流目标以“旧 runtime story 兼容接口 + 新 story/battle 查询切片可由 Rust 承接”为准,不再把继续拆 crate 作为本阶段阻塞项
当前验证边界补充:
1. `story_sessions` / `story_battles` 的二进制测试目标在当前机器上编译耗时仍然较长,还没有把更大范围的 story/battle 回归全部收拢到单次时窗内。
2. `node scripts/check-encoding.mjs` 已再次执行并通过,当前本轮涉及的中文文件编码未被写坏。
3. 当前可以确认的是:
- `module -> generated bindings -> spacetime-client -> api-server` 的编译链已打通
- Rust `runtime story` compat route boundary 与关键 NPC 主循环规则已有回归覆盖
- Rust `actions/resolve` 已开始承接 Node 动作后 LLM 文本增强,但完整 orchestrator / 真相链仍未完成
当前这轮不再继续扩 `runtime_story` 模块拆分。`resolve_story_action` / `sync_runtime_snapshot_projection` 作为真相态深化项转入后续收口或 M7 前置风险清单M4 当前按“旧 `/api/runtime/story/*` 兼容接口在 Rust 侧闭环 + `/api/story/*` 新切片代理可切到 Rust + 关键 gameplay 回归通过”收尾。
## 1. SpacetimeDB gameplay 表
- [x] 设计 `story_session`
- [x] 设计 `story_event`
- [x] 设计 `npc_state`
- [x] 设计 `quest_record`
- [x] 设计 `inventory_slot`
- [x] 设计 runtime item 奖励快照基线
- [x] 设计 `battle_state`
- [x] 设计 `player_progression`
- [x] 设计 `chapter_progression`
## 2. 核心 reducer
- [ ] 设计 `resolve_story_action`(转入真相态深化,不阻塞 M4 兼容切流收尾)
- [x] 设计 `continue_story`
- [x] 设计 `begin_story_session`
- [ ] 设计 `sync_runtime_snapshot_projection`(转入真相态深化,不阻塞 M4 兼容切流收尾)
- [x] 设计 `apply_quest_signal`
- [x] 设计 `apply_inventory_mutation`
- [x] 设计 `resolve_npc_interaction`
- [x] 设计 runtime item 奖励回写基线
- [x] 设计 `resolve_combat_action`
- [x] 设计 `update_progression_state`
## 3. 当前主链模块落位
- [ ] 迁移 `rpg-entry` 配套后端入口能力
- [ ] 迁移 `rpg-profile` 资料域
- [x] 迁移 `rpg-runtime-story`
- [x] 迁移 `combat`
- [ ] 迁移 `inventory`
- [ ] 迁移 `npc`
- [x] 迁移 `progression`
- [x] 迁移 `quest`
- [x] 迁移 `runtime-item`
- [x] 迁移 runtime snapshot 归一化、view model compiler 与状态同步规则
## 4. 兼容接口
- [x] 兼容 `POST /api/runtime/story/actions/resolve`
- [x] 兼容 `GET /api/runtime/story/state/:sessionId`
- [x] 兼容 `POST /api/runtime/story/state/resolve`
- [x] 兼容 `POST /api/runtime/story/initial`
- [x] 兼容 `POST /api/runtime/story/continue`
补充说明:
1. 当前已落地的是两类 Rust facade
- 新真相态接口:
- `POST /api/story/sessions`
- `POST /api/story/sessions/continue`
- `GET /api/story/sessions/:storySessionId/state`
- `GET /api/story/battles/:battleStateId`
- `POST /api/story/npc/battle`
- 旧 runtime story 兼容接口:
- `POST /api/runtime/story/state/resolve`
- `GET /api/runtime/story/state/:sessionId`
- `POST /api/runtime/story/actions/resolve`
- `POST /api/runtime/story/initial`
- `POST /api/runtime/story/continue`
2. 其中新真相态接口仍是 `story session / battle / NPC 开战` 的底层切片;旧 `runtime/story/*` 则是复用 `runtime_snapshot` 的兼容桥,不等价于最终真相态实现。
3. 当前 `runtime/story/*` 已能返回旧前端需要的 `RuntimeStoryActionResponse / AIResponse` 形状,但内部动作仍以确定性兼容编排为主,不代表 `resolve_story_action` 真相 reducer 已完成。
4. 当前新增的 `battle state` 查询仍只返回单个 `battleState` 真相切片,不等价于 runtime story 全量视图。
5. 后续 `M4` 仍需把兼容桥逐步替换成真正的 story action / snapshot projection 真相链。
## 5. ViewModel 兼容
- [x] 兼容当前 `RuntimeStoryActionResponse`
- [x] 兼容当前 `RuntimeStoryOptionView`
- [x] 兼容当前 `interaction` 元数据
- [x] 兼容当前 battle / toast / patch 响应结构
- [x] 兼容当前 `currentStory` 回填逻辑
## 6. 阶段验收
- [x] 当前前端 story 选项点击后可走新后端闭环
- [x] NPC / quest / combat 主循环行为不回退
- [x] `story state` 恢复链可用
- [x] 后端边界与当前 `rpgEntry -> rpgSession -> rpgRuntime -> rpgRuntimeStory -> rpgProfile` 口径一致
- [x] 旧 Node 版 story route 回归用例完成平移
阶段验收补充说明:
1. `当前前端 story 选项点击后可走新后端闭环` 当前按 Rust `api-server` 的真实边界回归判定已满足:
- `PUT /api/runtime/save/snapshot`
- `GET /api/runtime/story/state/runtime-main`
- `POST /api/runtime/story/actions/resolve`
但这不等于“生产默认流量已经切到 Rust”。
2. `story state 恢复链可用` 当前指:
- 请求带 `snapshot` 时可先写后读
- 请求不带 `snapshot` 时可从已持久化 `runtime_snapshot` 恢复
3. `旧 Node 版 story route 回归用例完成平移` 当前指:
- 已平移 Node 的 `rpg runtime story routes resolve through the new route boundary`
- 已补 `clientVersion` 冲突回归
- 已把 `npc_chat``46 -> 52` Node 旧语义对齐进 Rust compat handler
4. `NPC / quest / combat 主循环行为不回退` 当前按 Rust compat 回归口径已可勾选:
- 当前 runtime story compat bridge 已明确移除 `treasure_*` 遭遇动作,不再把 treasure 视作本阶段 runtime story 主循环的一部分。
- `npc_chat / npc_help / npc_recruit / npc_chat_quest_offer_* / npc_quest_accept / npc_quest_turn_in / npc_fight / npc_spar / battle_* / inventory_use / equipment_equip / equipment_unequip / forge_craft / forge_dismantle / forge_reforge / npc_trade / npc_gift` 已有确定性兼容闭环。
- 当前已补 battle option compiler、`battle_use_skill``inventory_use``equipment_equip / equipment_unequip``forge_*``npc_trade``npc_gift` 与胜利后的 `hostileNpcsDefeated` / `playerProgression.lastGrantedSource = hostile_npc` 写回。
- 当前已补 NPC 交互态入口预处理:纯商贩型 NPC 即使没有预填 `npcStates.*.inventory`,也会在 compat bridge 内自动恢复可交易库存与基础关系态,不再依赖 Node 侧预热。
- 更大范围 Node 回归与真相态 reducer 替换不再作为 M4 阻塞项,转入 M7 切流前回归矩阵。
5. `后端边界与当前 rpgEntry -> ...` 当前按 Rust 代理与路由覆盖可勾选:
- 前端真实调用链已对齐 `/api/runtime/story/*`
- Rust 栈已覆盖 `/api/runtime/*``/api/story/*` 代理目标
- `npm run dev:rust` 是本地 Rust 切流入口M7 再做远端灰度与回退验证

View File

@@ -1,117 +0,0 @@
# M5custom world / gallery / agent 任务清单
## 0. 当前执行基线
本阶段与当前仓库里的创作链重构直接对应,统一以以下文档为准:
1. [../docs/technical/CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md](../docs/technical/CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md)
2. [../docs/technical/CURRENT_AGENT_CREATION_FLOW_STAGE4_CLEANUP_CHECK_2026-04-21.md](../docs/technical/CURRENT_AGENT_CREATION_FLOW_STAGE4_CLEANUP_CHECK_2026-04-21.md)
当前逻辑层命名和职责边界应优先使用 `rpgCreation / rpgAgent / rpgWorld` 口径;本任务清单继续保留 `custom world` 文件名,只是为了和后端重写阶段文档编号保持一致。
本轮首批可编码表设计见:
3. [../docs/technical/SPACETIMEDB_CUSTOM_WORLD_AGENT_STAGE1_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_CUSTOM_WORLD_AGENT_STAGE1_TABLE_DESIGN_2026-04-21.md)
4. [../docs/technical/SPACETIMEDB_CUSTOM_WORLD_PUBLISH_WORLD_STAGE4_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_CUSTOM_WORLD_PUBLISH_WORLD_STAGE4_DESIGN_2026-04-21.md)
5. [../docs/technical/SPACETIMEDB_CUSTOM_WORLD_AXUM_FACADE_STAGE5_DESIGN_2026-04-22.md](../docs/technical/SPACETIMEDB_CUSTOM_WORLD_AXUM_FACADE_STAGE5_DESIGN_2026-04-22.md)
6. [../docs/technical/SPACETIMEDB_CUSTOM_WORLD_AGENT_SESSION_STAGE6_DESIGN_2026-04-22.md](../docs/technical/SPACETIMEDB_CUSTOM_WORLD_AGENT_SESSION_STAGE6_DESIGN_2026-04-22.md)
7. [../docs/technical/SPACETIMEDB_CUSTOM_WORLD_WORKS_AND_AGENT_EXTENSION_STAGE9_DESIGN_2026-04-22.md](../docs/technical/SPACETIMEDB_CUSTOM_WORLD_WORKS_AND_AGENT_EXTENSION_STAGE9_DESIGN_2026-04-22.md)
## 1. SpacetimeDB custom world 表
- [x] 设计 `custom_world_profile`
- [x] 设计 `custom_world_session`
- [x] 设计 `custom_world_agent_session`
- [x] 设计 `custom_world_agent_message`
- [x] 设计 `custom_world_agent_operation`
- [x] 设计 `custom_world_draft_card`
- [x] 设计 `custom_world_asset_link`(已在 Stage 1 文档中明确冻结为 `M6 assets / OSS` 继续落地,不阻塞 `M5` 验收)
- [x] 设计 `custom_world_gallery_entry`
## 2. 当前 RPG 创作主链
- [x] 迁移 result preview compilerStage 9 按冻结口径落最小 preview compiler不再搬 Node 全量 compiler
- [x] 迁移 published profile compileStage 3 已落地)
- [x] 迁移 works 聚合读模型Stage 9 Rust procedure + Axum facade 已接通)
- [x] 迁移 library 存储与删除Stage 2 设计已冻结,待继续接 Axum 兼容)
- [x] 迁移 publish / unpublishStage 2 设计已冻结,待继续接 Agent publish gate
- [x] 迁移 publish_world 串联主链Stage 4 设计已冻结,待继续接 Axum action / publish gate
- [x] 迁移 publish gate / enter-world gatesession snapshot / works / action 共用 gate 已接通)
- [x] 迁移 gallery 列表与详情Stage 2 设计已冻结,待继续接 Axum 兼容)
## 3. RPG 创作 Agent 主链
- [x] 迁移 session createStage 6 首批 Agent session skeleton
- [x] 迁移 session snapshotStage 6 首批 Agent session skeleton
- [x] 迁移 message submitStage 7 deterministic message / operation 最小闭环)
- [x] 迁移 message streamStage 8 SSE facade 已落地)
- [x] 迁移 operation queryStage 7 deterministic message / operation 最小闭环)
- [x] 迁移 card detailStage 9 Rust procedure + Axum facade 已接通)
- [x] 迁移 card update统一走 `/actions``update_draft_card`
- [x] 迁移 action registry / supportedActionssession 真相态 `supportedActions` 已接通)
- [x] 迁移 draft foundation统一走 `/actions``draft_foundation`
- [x] 迁移 result preview 生成session 最小 `resultPreview` 已接通)
- [x] 迁移 entity generationAxum 兼容 `/api/custom-world/entity``/api/runtime/custom-world/entity` 已接通)
- [x] 迁移 role / scene asset sync最小 action 占位闭环与兼容图片入口已接通)
- [x] 迁移 checkpoint / blocker / quality findings 主链session / works / preview / publish gate 已接通)
## 4. Axum 编排层
- [x] 接入 LLM 编排entity / scene-npc 兼容入口优先接 LLM + fallback
- [x] 接入世界草稿编译(`draft_foundation / update_draft_card / sync_result_profile` 已形成最小草稿编译闭环)
- [x] 接入服务端 result preview 编译(最小 preview contract 已接入 session 快照)
- [x] 接入角色 / 地点 / 场景 NPC 生成(最小兼容入口已接通)
- [x] 接入封面图生成(最小兼容入口已接通)
- [x] 接入场景图生成(最小兼容入口已接通)
- [x] 接入 OSS 对象写入与绑定(`M5` 兼容图片入口已闭环为本地可消费资产;正式 `asset_object / asset_entity_binding / OSS` 主链顺延 `M6`
- [x] 接入 SSE 事件分发Stage 8 SSE facade 已接通)
## 5. 当前正式接口与历史兼容台账
### 5.1 当前正式接口
- [x] 兼容 `/api/runtime/custom-world-library`Stage 5 首批 Axum facade
- [x] 兼容 `/api/runtime/custom-world-library/:profileId`owner-only detail 查询已补齐)
- [x] 兼容 `/api/runtime/custom-world-library/:profileId/publish`Stage 5 首批 Axum facade
- [x] 兼容 `/api/runtime/custom-world-library/:profileId/unpublish`Stage 5 首批 Axum facade
- [x] 兼容 `/api/runtime/custom-world-gallery`Stage 5 首批 Axum facade
- [x] 兼容 `/api/runtime/custom-world-gallery/:ownerUserId/:profileId`Stage 5 首批 Axum facade
- [x] 兼容 `/api/runtime/custom-world/works`
- [x] 兼容 `/api/runtime/custom-world/agent/sessions`Stage 6 首批 Axum facade
- [x] 兼容 `/api/runtime/custom-world/agent/sessions/:sessionId`Stage 6 首批 Axum facade
- [x] 兼容 `DELETE /api/runtime/custom-world/agent/sessions/:sessionId`(草稿物理清理;若作品卡误以已发布来源 session 删除,则回落到关联 profile 软删除并返回 works
- [x] 兼容 `/api/runtime/custom-world/agent/sessions/:sessionId/messages`Stage 7 deterministic message submit
- [x] 兼容 `/api/runtime/custom-world/agent/sessions/:sessionId/messages/stream`Stage 8 SSE facade
- [x] 兼容 `/api/runtime/custom-world/agent/sessions/:sessionId/actions`Stage 9 全量 action procedure 已接通)
- [x] 兼容 `/api/runtime/custom-world/agent/sessions/:sessionId/operations/:operationId`Stage 7 deterministic operation query
- [x] 兼容 `/api/runtime/custom-world/agent/sessions/:sessionId/cards/:cardId`
- [x] 兼容 `/api/custom-world/entity`
- [x] 兼容 `/api/runtime/custom-world/entity`
- [x] 兼容 `/api/custom-world/scene-npc`
- [x] 兼容 `/api/runtime/custom-world/scene-npc`
- [x] 兼容 `/api/custom-world/scene-image`
- [x] 兼容 `/api/custom-world/cover-image`
- [x] 兼容 `/api/custom-world/cover-upload`
### 5.2 历史兼容台账(非当前主链)
- [x] 评估 `/api/runtime/custom-world/sessions` 是否仍需保留历史兼容映射(确认无需保留,旧链已物理删除)
- [x] 评估 `/api/runtime/custom-world/sessions/:sessionId` 是否仍需保留历史兼容映射(确认无需保留,旧链已物理删除)
- [x] 评估 `/api/runtime/custom-world/sessions/:sessionId/answers` 是否仍需保留历史兼容映射(确认无需保留,旧链已物理删除)
- [x] 评估 `/api/runtime/custom-world/sessions/:sessionId/generate/stream` 是否仍需保留历史兼容映射(确认无需保留,旧链已物理删除)
## 6. 阶段验收
- [x] RPG 创作主链可用:`agent session -> result preview -> published profile`
- [x] works / library / gallery / publish / enter-world 主链可用
- [x] RPG 创作 Agent 主链可用
- [x] agent 会话、消息、卡片、操作不再依赖单大 JSON 会话体
- [x]`custom-world/sessions` 问答流不再作为当前主链扩展目标
## 7. 本轮执行结果
- [x] Stage 9 文档、任务清单、Rust module、spacetime-client、api-server 已对齐
- [x] `cargo check -p spacetime-client`
- [x] `cargo check -p api-server`
- [x] `CARGO_TARGET_DIR=D:\\Genarrative\\server-rs\\target-codex-m5-check cargo check -p api-server`
- [x] `node scripts/check-encoding.mjs ...` 编码检查通过

View File

@@ -1,153 +0,0 @@
# M6assets / 阿里云 OSS 任务清单
说明:
1. `editor` 已于 `2026-04-21` 被确认为遗留无用模块,不再纳入本轮 Rust 后端重写范围。
2. 本文件保留原文件名仅用于延续既有任务编号与链接,不再继续安排 editor 迁移项。
## 1. OSS 基础设施
- [x] 创建 OSS bucket 方案
- [x] 设计对象键前缀
- [x] 设计 `object_key -> cdn_url` 解析策略
- [x] 设计 public / private 对象访问策略
- [x] 设计签名 URL 输出策略
- [x] 设计 `x-oss-meta-*` 元数据规范
- [x] 设计内容 hash / 版本字段规范Stage 1 明确为 `asset_object.content_hash: Option<String>` + `version = 1`,后续强 hash 单独阶段再扩)
## 2. 上传与对象确认
- [x] 实现浏览器 `PostObject` 直传签名接口
- [x] 实现 STS 临时授权接口
- [x] 实现服务端上传 helper
- [x] 实现上传完成后的对象确认接口
- [x] 实现对象绑定业务实体 reducer
补充说明:
1.`2026-04-21` 起,当前重写节奏允许在 `M3/M4/M5` 之前先前置落地 `M6` 的 OSS 基础设施。
2. 当前已在 `server-rs/crates/platform-oss``server-rs/crates/api-server` 落下最小可用链路:
- `PostObject` 直传签名能力
- `/api/assets/direct-upload-tickets`
- `/api/assets/objects/confirm`
- 兼容旧 `/generated-*` 前缀的对象键规划
- `.env/.env.local` 的 OSS 环境变量加载
- 服务端 `HEAD Object` 校验
- `asset_object` 确认真实 SpacetimeDB 持久化
- `/api/assets/objects/bind`
- `asset_entity_binding` 业务实体槽位绑定
- `/api/assets/sts-upload-credentials` 禁用式 contract
- 服务端 `PutObject` 上传 helper
3. 当前 bucket 已明确为私有读写;后续正式存储口径改为 `bucket + object_key` 双列,不再把匿名公开 URL 当成真相。
4. 当前 STS 接口按“服务器上传、Web 只下载”的需求固定为 `403` 禁用式 contract不向浏览器下发 OSS 写权限。
5. `2026-04-21` 已通过 live test 验证:真实 OSS 上传后,`/api/assets/objects/confirm` 能把 `xushi-dev + object_key` 写入本地 `genarrative-dev.asset_object`,并可继续通过 `/api/assets/objects/bind` 绑定到业务实体槽位。
## 3. 资产任务系统
- [x] 设计 `asset_job`Stage 1 明确不新增重复表AI 资产任务先复用 `AiTaskService / ai_task` 口径)
- [x] 设计 `asset_object`
- [x] 设计 `asset_manifest`Stage 1 使用 OSS JSON manifest + `asset_object` 表达集合对象,不新增结构化表)
- [x] 设计 `character_visual_asset`Stage 1 使用 `asset_entity_binding: character / primary_visual`,强业务表延后)
- [x] 设计 `character_animation_asset`Stage 1 使用 `asset_entity_binding: character / animation_set` 绑定总 manifest强业务表延后
- [x] 设计 `scene_image_asset`Stage 1 使用 `asset_entity_binding: custom_world_landmark / scene_image`,强业务表延后)
- [x] 设计 `sprite_sheet_asset`Qwen 独立工具已清理Stage 1 仅保留历史 `/generated-qwen-sprites/*` 读取兼容)
补充说明:
1. `asset_object` 当前已冻结核心存储口径为:
- `bucket`
- `object_key`
2. 详细设计见:
- [../docs/technical/SPACETIMEDB_ASSET_OBJECT_STORAGE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_ASSET_OBJECT_STORAGE_DESIGN_2026-04-21.md)
- [../docs/technical/SPACETIMEDB_ASSET_OBJECT_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_ASSET_OBJECT_TABLE_DESIGN_2026-04-21.md)
- [../docs/technical/ASSET_OBJECT_CONFIRM_FLOW_DESIGN_2026-04-21.md](../docs/technical/ASSET_OBJECT_CONFIRM_FLOW_DESIGN_2026-04-21.md)
- [../docs/technical/M6_OSS_SERVER_UPLOAD_AND_STS_POLICY_2026-04-21.md](../docs/technical/M6_OSS_SERVER_UPLOAD_AND_STS_POLICY_2026-04-21.md)
3. 当前已在 `server-rs/crates/spacetime-module` 落下 `asset_object` 首版表骨架,并完成 `api-server -> SpacetimeDB` 的最小对象确认闭环。
4. 元数据、版本、manifest 与强业务资产表边界见:
- [../docs/technical/M6_ASSET_METADATA_HASH_VERSION_AND_SPECIALIZED_TABLE_BOUNDARY_2026-04-22.md](../docs/technical/M6_ASSET_METADATA_HASH_VERSION_AND_SPECIALIZED_TABLE_BOUNDARY_2026-04-22.md)
## 4. 资产生成链路
- [x] 迁移角色主形象生成Stage 1 已接通 Rust `generate / jobs / publish` 最小 OSS 主链,当前仍为 SVG 占位生成,不代表真实 DashScope 图片模型已迁完)
- [x] 迁移角色动作生成Stage 1 已接通 Rust `generate / jobs / publish` 最小 OSS 主链,当前 `image-sequence` 为 SVG 占位帧,视频类策略优先复用参考视频或仓库占位预览,不代表真实视频模型已迁完)
- [x] 迁移动作模板查询Stage 1 已接通 Rust 内置模板列表兼容接口)
- [x] 迁移视频导入Stage 1 已接通 Data URL 视频导入到 OSS 草稿区,不再写本地 `public/`
- [x] 迁移工作流缓存Stage 1 已接通 Rust `GET/POST character-workflow-cache` 到 OSS JSON 草稿对象,不再写本地 `public/`
- [x] 迁移场景图生成(已完成 Stage 2custom world `scene-image` 走真实 DashScope 图片生成,并继续写入 `OSS + asset_object + asset_entity_binding`
- [x] 迁移封面图上传(已完成 Stage 2custom world `cover-image / cover-upload` 已补齐真实 DashScope 生成与 `cropRect + 16:9 + WebP 压缩`
- [x] 首批收口 custom world `scene-image / cover-image / cover-upload` 到正式 `OSS + asset_object + asset_entity_binding` 主链(保持旧 `/generated-*` 返回 contract不再写仓库 `public/`
补充说明:
1. custom world 兼容图片入口现已完成 Stage 1 + Stage 2正式资产真相链、真实 DashScope 图片生成,以及封面上传裁剪压缩都已迁完。
2. 详细边界见:
- [../docs/technical/M6_CUSTOM_WORLD_ASSET_OSS_INTEGRATION_STAGE1_2026-04-22.md](../docs/technical/M6_CUSTOM_WORLD_ASSET_OSS_INTEGRATION_STAGE1_2026-04-22.md)
3. 角色动作模板与视频导入第一批已新增独立设计文档,当前只迁移:
- `GET /api/assets/character-animation/templates`
- `POST /api/assets/character-animation/import-video`
- [../docs/technical/M6_CHARACTER_ANIMATION_IMPORT_AND_TEMPLATE_STAGE1_2026-04-22.md](../docs/technical/M6_CHARACTER_ANIMATION_IMPORT_AND_TEMPLATE_STAGE1_2026-04-22.md)
4. 角色资产工作流缓存第一批已新增独立设计文档,当前把旧本地 `workflow-cache.json` 改为 OSS JSON 草稿对象:
- `GET /api/assets/character-workflow-cache/:characterId`
- `POST /api/assets/character-workflow-cache`
- [../docs/technical/M6_CHARACTER_WORKFLOW_CACHE_OSS_STAGE1_2026-04-22.md](../docs/technical/M6_CHARACTER_WORKFLOW_CACHE_OSS_STAGE1_2026-04-22.md)
5. `2026-04-22` 复核确认:旧独立 `qwen-sprite-tool + qwenSpriteRoutes.ts` 已在 `2026-04-21` 清理,不再作为本轮现役迁移主链;当前仍保留的 `Qwen` 相关内容仅包括:
- 角色资产 prompt 层对 `packages/shared/src/prompts/qwenSprite.ts` 的复用
- 历史资源前缀 `/generated-qwen-sprites/*` 的读取兼容
6. custom world 图片链 Stage 2 已完成:
- `scene-image / cover-image` 已替换为真实 DashScope 图片生成
- `cover-upload` 已补回 Node 旧链路中的 `cropRect + 16:9 + WebP 压缩`
- 详细口径与验证结果见 [../docs/technical/M6_CUSTOM_WORLD_ASSET_OSS_INTEGRATION_STAGE2_2026-04-22.md](../docs/technical/M6_CUSTOM_WORLD_ASSET_OSS_INTEGRATION_STAGE2_2026-04-22.md)
## 5. 路径兼容
- [x] 兼容 `/generated-character-drafts/*`
- [x] 兼容 `/generated-characters/*`
- [x] 兼容 `/generated-animations/*`
- [x] 兼容 `/generated-custom-world-scenes/*`
- [x] 兼容 `/generated-custom-world-covers/*`
- [x] 兼容 `/generated-qwen-sprites/*`
补充说明:
1. 第一批路径兼容由 Rust `api-server` 同源代理到私有 OSS 短期读签名,不回退本地 `public/`,详细边界见:
- [../docs/technical/M6_LEGACY_GENERATED_PATH_OSS_READ_COMPAT_2026-04-22.md](../docs/technical/M6_LEGACY_GENERATED_PATH_OSS_READ_COMPAT_2026-04-22.md)
2. 当前 Stage 1 先全量代理对象内容,不实现视频 Range 分片;若后续真实视频体积变大,再按播放器需求补 Range。
## 6. 兼容接口
- [x] 兼容 `/api/assets/character-visual/generate`
- [x] 兼容 `/api/assets/character-visual/jobs/:taskId`
- [x] 兼容 `/api/assets/character-visual/publish`
- [x] 兼容 `/api/assets/character-animation/generate`
- [x] 兼容 `/api/assets/character-animation/jobs/:taskId`
- [x] 兼容 `/api/assets/character-animation/publish`
- [x] 兼容 `/api/assets/character-animation/import-video`
- [x] 兼容 `/api/assets/character-animation/templates`
- [x] 兼容 `/api/assets/character-workflow-cache`
- [x] 兼容 `/api/assets/character-workflow-cache/:characterId`
## 7. 阶段验收
- [x] OSS 直传对象可被服务端确认并写入 `asset_object`
- [x] 所有新生成资产都写入 OSSStage 1 覆盖当前现役角色主形象、角色动作、workflow cache、视频导入、custom world 场景图/封面图;历史清理掉的 Qwen 独立工具不再计入现役主链)
- [x] 前端仍能通过旧路径习惯访问资源Stage 1 通过 Rust 同源代理私有 OSS 对象,开发期 Vite 代理已覆盖现役 generated 前缀)
- [x] 资产任务状态可查询(角色主形象与角色动作已通过 `jobs/:taskId` 复用 `AiTaskService`;同步上传/确认链路以接口返回结果为状态)
- [x] 已确认对象可绑定到业务实体槽位
补充说明:
1. custom world 的 `scene-image / cover-image / cover-upload` 已在本轮切到正式 OSS 对象与绑定主链。
2. 角色主形象第一批已新增独立设计文档与 Rust 最小闭环:
- [../docs/technical/M6_CHARACTER_VISUAL_ASSET_OSS_INTEGRATION_STAGE1_2026-04-22.md](../docs/technical/M6_CHARACTER_VISUAL_ASSET_OSS_INTEGRATION_STAGE1_2026-04-22.md)
3. 当前角色主形象 `generate` 先用 Rust SVG 占位生成打通 `task + OSS drafts + publish + asset_object + asset_entity_binding` 主链,后续再替换成真实图片模型。
4. 角色动作模板与视频导入第一批已接入 Rust
- `templates` 返回旧内置模板 contract。
- `import-video` 当前只接受 `data:video/*;base64,...`,并写入 OSS `generated-character-drafts/*` 草稿区。
5. 角色资产工作流缓存第一批已接入 Rust
- 保存时写入 OSS `generated-character-drafts/{character}/workflow-cache/workflow-cache.json`
- 读取时未命中返回 `cache: null`,保持旧前端 contract。
6. 角色动作第一批已接入 Rust
- `generate` 直接写入 OSS `generated-character-drafts/*`
- `jobs/:taskId``AiTaskService` 派生旧任务状态 contract。
- `publish` 会把动作帧与总 manifest 写入 OSS `generated-animations/*`,并确认 `asset_object + asset_entity_binding`
7. custom world 场景图、封面图、封面上传已在 `M6_CUSTOM_WORLD_ASSET_OSS_INTEGRATION_STAGE1_2026-04-22.md` + `M6_CUSTOM_WORLD_ASSET_OSS_INTEGRATION_STAGE2_2026-04-22.md` 范围内完成正式 `OSS + asset_object + asset_entity_binding` 主链、真实 DashScope 图片生成和封面上传裁剪压缩。
8. `content_hash/version``asset_job``asset_manifest` 与强业务资产表当前已冻结 Stage 1 边界,不再作为 M6 第一批工程阻塞项后续若要做内容去重、manifest 查询、审核/回滚或 sprite sheet 强结构化,再进入独立阶段。

View File

@@ -1,66 +0,0 @@
# M7联调、回归、部署与切流任务清单
## 1. 测试体系
- [x] 为 Axum handler 补接口测试(现阶段以既有 `api-server` handler 测试编译门禁 + `server-rs/scripts/check.ps1` 固化;新增接口测试继续按主链补齐)
- [x] 为 SpacetimeDB reducer 补规则测试(现阶段以 `cargo check -p spacetime-module` 作为 schema/reducer/procedure 最小门禁;真实数据库规则回归继续由本地 publish smoke 承接)
- [x] 为 view / projection 补数据一致性测试(现阶段以 `shared-contracts` contract 回归与 SpacetimeDB schema check 固化投影字段门禁)
- [x] 为 auth 主链补集成测试(现有 `shared-contracts``api-server` 鉴权 handler 测试已纳入 Rust 主线检查入口)
- [x] 为 runtime snapshot 主链补集成测试(现有 runtime contract 回归已纳入 Rust 主线检查入口)
- [x] 为 story action 主链补集成测试(现有 runtime story contract / handler 测试编译已纳入 Rust 主线检查入口)
- [x] 为 custom world / agent 主链补集成测试(现阶段纳入 `api-server` 编译与 Rust 主线检查;真实 LLM/OSS 环境联调继续由 smoke 承接)
- [x] 为 assets / OSS 主链补集成测试(现有 M6 OSS smoke 与 contract 测试保留Rust 主线检查固化基础门禁)
- [x] 为兼容 contract 补回归测试(`cargo test -p shared-contracts` 已纳入 Rust 主线检查)
## 2. 部署准备
- [x] 设计 Axum 部署方式
- [x] 设计 SpacetimeDB 发布方式
- [x] 设计 OSS bucket / CDN / 域名方案
- [x] 设计环境变量清单
- [x] 设计灰度环境
- [x] 设计数据迁移脚本
- [x] 设计回滚策略
- [x] 准备本地 Rust 一键联调脚本(`npm run dev:rust` 同时启动前端、Rust `api-server` 与本地 SpacetimeDB
- [x] 准备 Ubuntu 发布包构建脚本(`npm run build:rust:ubuntu` 生成 `build/<timestamp>/`,包含 `web/``api-server``spacetime_module.wasm``start.sh``stop.sh`,并默认 scp 上传到目标服务器)
## 3. 观测能力
- [x] 接入 tracing / request id / structured logs
- [x] 接入慢请求追踪
- [x] 接入上游 LLM / OSS / 短信 / 微信失败日志(沿用既有 provider error envelope 与 tracingM7 固化字段口径)
- [x] 接入关键 reducer 执行日志(现阶段固定 reducer 操作日志字段口径,真实 publish 日志回看继续由 SpacetimeDB smoke 承接)
- [x] 接入资产任务状态日志(沿用 `AiTaskService / ai_task` 状态链M7 固化 `task_id / status / asset_kind` 观测口径)
## 4. 切流准备
- [x] 准备旧 Node 与新 Rust 双跑窗口
- [x] 准备 API 对比脚本
- [x] 准备主流程 smoke 清单
- [x] 准备前端切换开关
- [x] 准备回退开关
## 5. 主工程结构收口
- [x] 拆分 `server-rs/crates/spacetime-module/src/lib.rs`,按业务模块与 SpacetimeDB 的 `table / reducer / procedure / view` 聚合结构整理为 `runtime``gameplay::{story/combat/inventory/npc/quest/runtime_item/progression}``custom_world``asset_metadata``ai` 等子模块,主工程 crate 根入口只保留模块声明、统一导出与最小发布入口
执行约束:
1. 这是切流前的工程结构收口,不是新功能扩张;拆分过程中不得改变既有 table schema、reducer / procedure 名称、对外 contract 与 publish 行为。
2. 拆分后的模块边界必须与 `M0` 已冻结的模块迁移归属一致,避免 `spacetime-module` 再回退成单大包。
3. 拆分完成后至少要保持 `cargo check`、SpacetimeDB 本地 build / publish 开发链路与主流程回归脚本可继续通过。
## 6. 阶段验收
- [x] 本地切流前预检通过M7 阶段性预检包装入口已归档,长期入口改为 `server-rs/scripts/check.ps1`
- [x] 主流程基础回归通过(`cargo check -p spacetime-module``cargo check -p api-server``cargo test -p shared-contracts``cargo test -p api-server --no-run`
- [ ] 全链路 smoke 通过
- [ ] 主流程真实环境回归通过
- [ ] 关键 SSE 接口联调通过
- [ ] 可在灰度环境完成切流
补充说明:
1. M7 已新增 [../docs/technical/M7_TEST_DEPLOY_CUTOVER_EXECUTION_PLAN_2026-04-22.md](../docs/technical/M7_TEST_DEPLOY_CUTOVER_EXECUTION_PLAN_2026-04-22.md),冻结本地预检、部署、灰度、双跑、回滚与结构收口口径。
2. 本轮新增 [../docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md](../docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md),并落地 `scripts/dev-rust-stack.ps1``scripts/dev-rust-stack.sh``scripts/deploy-rust-remote.sh`;其中发布脚本当前语义为生成 Ubuntu release 包。
3. 当前 M7 阶段性 preflight 入口已归档;真实全链路 smoke、关键 SSE 联调与灰度切流仍依赖 Rust/SpacetimeDB/OSS/LLM 的完整运行环境,不在无外部服务的本地预检中虚假勾选。

View File

@@ -1,62 +0,0 @@
# 横向专项、执行顺序与最终验收
## 1. 横向专项任务
### Contract 与前端兼容
- [x] 梳理当前 `packages/shared/src/contracts/*` 到 Rust DTO 的映射
- [x] 设计 Rust 侧 contract 生成或手写策略
- [x] 保持当前字段名、枚举值、响应结构稳定
- [x] 为 breaking change 建立显式变更流程
### SpacetimeDB schema 演进治理
- [x] 约定 stable reducer 命名规则
- [x] 约定 stable table 命名规则
- [x] 约定列追加式演进规则
- [x] 约定软删除而不是直接删表删列的场景
- [x] 约定事件表与投影表拆分规则
### 大对象与缓存治理
- [x] 明确哪些内容入 OSS
- [x] 明确哪些内容只存 SpacetimeDB 元数据
- [x] 明确哪些内容允许短期本地缓存
- [x] 明确 workflow cache 生命周期
### 文档维护
- [x] 每个阶段完成后同步更新设计文档
- [x] 每个阶段完成后补一份落地记录
- [x] 完成接口迁移后更新新的模块与 API 索引文档
- [ ] `M4` 结构变更同步对齐 `docs/technical/RPG_ENTRY_RUNTIME_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md`
- [x] `M5` 结构变更同步对齐 `docs/technical/CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md`
补充说明:
1. 横向治理规则已冻结在 [../docs/technical/BACKEND_REWRITE_CROSS_CUTTING_GOVERNANCE_2026-04-22.md](../docs/technical/BACKEND_REWRITE_CROSS_CUTTING_GOVERNANCE_2026-04-22.md)。
2. Rust 侧 96 条 Axum 路由索引已冻结在 [../docs/technical/RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md](../docs/technical/RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md)。
3. `M4` 当前仍存在 `runtime_story` 独立 crate 拆分工作区,结构文档对齐需等该拆分收口后再勾选。
## 2. 第一优先级建议执行顺序
1. 先做 `M0`,冻结基线,避免迁移过程中口径漂移。
2. 再做 `M1 + M2`,先把 Axum 壳与鉴权打稳。
3. 再做 `M3`优先跑通快照、设置、profile。
4. 进入 `M4``M5` 前,先用两份 `2026-04-21` 执行方案冻结当前仓库里的 RPG 运行时链与创作链结构口径。
5. 再做 `M4`,把 RPG runtime story 主循环真正迁走。
6. 然后做 `M5`,迁 RPG 创作主链、works/library/gallery 与 agent。
7. 最后做 `M6 + M7`,收口 assets、editor、部署与切流。
## 3. 最终验收清单
- [x] 当前 `96` 条后端接口已全部迁移或有兼容替代
- [ ] 当前 `6` 个挂载面已全部迁移
- [ ] 当前 `12` 个内部模块已完成新架构落位
- [ ] Axum 已成为唯一 HTTP / SSE / 副作用边界
- [ ] SpacetimeDB 已成为唯一运行时状态真相源
- [ ] 阿里云 OSS 已成为唯一资产对象仓
- [ ] `M4` 已与 `rpgEntry / rpgSession / rpgRuntime / rpgRuntimeStory / rpgProfile` 主链口径一致
- [x] `M5` 已与 `agent session -> result preview -> published profile` 主链口径一致
- [ ] 前端主流程在不大改 UI 的前提下可跑通
- [ ] 能完成灰度切流,并保留可回退能力

View File

@@ -1,183 +0,0 @@
# M0后端挂载面冻结基线
日期:`2026-04-20`
依据来源:
- [../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md](../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md)
- [../server-node/manifests/backend-capability-index.json](../server-node/manifests/backend-capability-index.json)
## 1. 文档目的
这份文档用于完成 `M0` 的第一条任务:
- 整理当前后端 `6` 个挂载面并锁定为重写验收基线
这里的“冻结”不是要求新后端永远维持原实现,而是要求:
1. 当前 Node 后端历史基线仍固定为这 `6` 个挂载面。
2. 本轮 Rust 后端的 active rewrite target 固定覆盖其中 `5` 个挂载面:`assets``auth``health``runtime-main``runtime-story-action`
3. `editor` 作为历史遗留挂载面继续保留对照记录,但自 `2026-04-21` 起不纳入 `server-rs` 本轮重写验收。
4. 允许内部实现从 `Express + PostgreSQL + 本地 public/generated-*` 重写为 `Axum + SpacetimeDB + 阿里云 OSS`,但不允许把挂载面职责打散到无法对照验收。
## 2. 冻结结论
当前 Node 后端的正式挂载面固定为以下 `6` 个:
| 挂载面 ID | 中文名称 | 当前路由数 | 当前入口 | 必须保留的顶层路径 |
| --- | --- | --- | --- | --- |
| `assets` | 资产生成工具面 | `14` | `server-node/src/app.ts -> /api/assets` | `/api/assets/*` |
| `auth` | 鉴权与会话面 | `17` | `server-node/src/app.ts -> /api/auth` | `/api/auth/*` |
| `editor` | 编辑器工具面 | `3` | `server-node/src/app.ts -> /api/editor` | `/api/editor/*` |
| `health` | 基础健康检查 | `1` | `server-node/src/app.ts -> /healthz` | `/healthz` |
| `runtime-main` | 运行时主能力面 | `59` | `server-node/src/app.ts -> /api` | `/api/runtime/*``/api/profile/*``/api/custom-world/*``/api/llm/*``/api/ws/*` |
| `runtime-story-action` | 运行时 Story Action 面 | `2` | `server-node/src/app.ts -> /api/runtime/story` | `/api/runtime/story/*` |
冻结总数:
1. 历史对外挂载面:`6`
2. 本轮 active rewrite target`5`
3. 已登记路由:`96`
4. 公开接口:`10`
5. JWT 接口:`69`
6. 开关控制接口:`17`
7. 流式接口:`6`
## 3. 各挂载面冻结要求
### 3.1 `assets`
当前定位:
1. 角色主形象生成
2. 角色动作生成
3. Qwen 精灵表生成与保存
4. 工作流缓存
5. 产物发布到 `public/generated-*`
重写后的冻结要求:
1. 仍保留独立的 `/api/assets/*` 命名空间。
2. 仍保留“生成任务、任务状态查询、发布/保存”三类操作语义。
3. 当前基于本地 `public/generated-*` 的产物落地,可改为 `OSS + 元数据表`,但前端一阶段必须继续通过原有路径习惯访问资源。
4. 当前 `ASSETS_API_ENABLED` 门禁能力必须保留。
### 3.2 `auth`
当前定位:
1. 本地账号登录
2. 手机验证码登录
3. 微信登录
4. refresh session
5. 会话吊销
6. 审计与风控
重写后的冻结要求:
1. 仍保留独立的 `/api/auth/*` 命名空间。
2. 仍保留当前 `JWT + refresh cookie` 双令牌模型。
3. 仍保留 `password / phone / wechat` 三类登录能力面。
4. 仍保留审计日志、风控封禁、会话列表与会话吊销能力。
### 3.3 `editor`
当前定位:
1. 编辑器 JSON 读取
2. 编辑器 JSON 回写
3. 图标目录枚举
重写后的冻结要求:
1. `server-node/src/app.ts -> /api/editor/*` 的历史存在事实继续保留在基线文档中。
2.`2026-04-21` 起,该挂载面不纳入 `server-rs` 本轮重写范围,不再作为 `M1 ~ M6` 主线交付目标。
3. 若未来仍需清理或替代 editor需要在遗留链路依赖核对完成后单独立项。
### 3.4 `health`
当前定位:
1. 提供后端进程健康探针
2. 为代理层与 smoke 提供最小可用确认
重写后的冻结要求:
1. 仍保留 `/healthz`
2. 仍返回简单、无鉴权、无数据库强耦合的健康状态。
3. 仍可作为 smoke 与部署探针的第一检查点。
### 3.5 `runtime-main`
当前定位:
1. 运行时存档、设置、个人档案
2. 聊天、剧情、任务、运行时物品意图
3. custom world library / gallery / sessions
4. custom world agent 会话、消息、操作
重写后的冻结要求:
1. 仍保留运行时主入口作为最大能力面,不把这些能力拆散到前端无法感知的新命名空间。
2. 仍兼容当前:
- `/api/runtime/*`
- `/api/profile/*`
- `/api/custom-world/*`
- `/api/llm/*`
- `/api/ws/*`
3. 除公开画廊与少量公开接口外,仍以登录态为默认访问前提。
4. 当前大量业务逻辑虽然会迁到 `SpacetimeDB reducer/view + Axum facade`,但对前端看起来仍应是一个统一运行时能力面。
### 3.6 `runtime-story-action`
当前定位:
1. story choice 动作解析
2. story session 状态恢复
重写后的冻结要求:
1. 仍保留 `/api/runtime/story/*` 作为独立挂载面。
2. 仍保持“前端动作输入 -> 后端统一结算 -> 返回新状态”的接口职责。
3. 当前 `storyActionService` 里跨 `quest / inventory / runtime-item / npc / progression / combat / runtime` 的协作,迁移后必须继续存在,只是实现位置改到 `SpacetimeDB + Axum`
## 4. 挂载面与新架构映射
| 当前挂载面 | 新架构主归属 | 说明 |
| --- | --- | --- |
| `assets` | `Axum + OSS + SpacetimeDB asset metadata` | 外部副作用在 Axum二进制在 OSS任务与引用状态在 SpacetimeDB。 |
| `auth` | `Axum auth-service + SpacetimeDB auth tables` | 登录副作用与 cookie/JWT 在 Axum身份与会话状态在 SpacetimeDB。 |
| `editor` | `遗留保留于 server-node` | 历史挂载面对照,当前不进入 Rust 重写主链。 |
| `health` | `Axum health route` | 维持最小化健康检查面。 |
| `runtime-main` | `Axum runtime facade + SpacetimeDB runtime/custom-world tables` | Axum 维持兼容 REST/SSESpacetimeDB 负责状态真相。 |
| `runtime-story-action` | `Axum story facade + SpacetimeDB gameplay reducers/views` | Story Action 入口继续独立存在,但结算内核迁到新状态层。 |
## 5. 本轮冻结后的硬约束
后续迁移中,不允许出现以下情况:
1. 把历史 `6` 个挂载面减少成更少但无法一一对照的“超级入口”。
2. 为了迎合本轮重写范围而把历史存在的 `/api/editor/*` 从基线文档中抹掉。
3. 把当前 `/api/auth/*``/api/assets/*``/api/runtime/story/*` 顶层命名空间直接改掉。
4. 在未完成路径兼容前,直接移除 `/healthz``/generated-*` 的既有访问习惯。
5. 在未完成契约回归前,把 `runtime-main``runtime-story-action` 的职责重新混成一个难以验收的大入口。
## 6. 本任务完成定义
当以下条件成立时,这条任务视为完成:
1. 当前历史 `6` 个挂载面已经有正式书面冻结清单。
2. 每个挂载面都有:
- 当前入口
- 当前路由数
- 顶层路径空间
- 重写后必须保留的职责边界
3. 本轮 active rewrite target 为 `5` 个,且 `editor` 的遗留/不迁移口径已经冻结。
4. 后续任务可以直接以这份文档作为验收引用,不再靠聊天记录记忆。
## 7. 后续直接依赖这份基线的任务
1. 整理当前后端 `96` 条路由并生成“旧接口 -> 新实现”映射表
2. 整理当前 `12` 个内部模块并锁定迁移归属
3. 设计 Axum 路由树
4. 设计 SpacetimeDB 表 / reducer / view 分层

View File

@@ -1,262 +0,0 @@
# M0前端直接依赖的响应头、Envelope 与错误格式冻结基线
日期:`2026-04-20`
依据来源:
- `server-node/src/http.ts`
- `server-node/src/middleware/responseEnvelope.ts`
- `server-node/src/middleware/errorHandler.ts`
- `server-node/src/middleware/requestId.ts`
- `packages/shared/src/http.ts`
- `src/services/apiClient.ts`
- `src/services/authService.ts`
- `src/services/aiService.ts`
- `src/editor/shared/jsonClient.ts`
- `src/services/apiClient.test.ts`
## 1. 文档目的
这份文档用于完成 `M0` 的第六条任务:
- 整理当前前端直接依赖的响应头、envelope、错误格式
这里的“直接依赖”指的是:如果 Axum 重写时把这些头或 body 结构改掉,当前前端 `src/services/*`、编辑器请求层和鉴权异常处理就会立刻出问题。
## 2. 冻结结论
当前前端直接依赖的响应契约,冻结为以下 4 层:
1. 请求侧默认会发送 `x-genarrative-response-envelope: v1`
2. 响应侧默认要回 `x-request-id``x-api-version``x-route-version`
3. 成功响应在请求方要求 envelope 时,必须返回标准 `ok/data/error/meta` 结构。
4. 错误响应既要兼容标准 envelope也要兼容旧式 `{ error, meta }` / `{ message, code }` 解析回退。
补充结论:
1. 当前正式前端代码里,没有生产用例主动关闭 envelope 请求头。
2. `x-response-time-ms` 当前不是前端代码的直接读取项,但属于现有兼容头集合,重写时仍应保留。
3. 鉴权链路额外直接依赖错误码 `CAPTCHA_REQUIRED``error.details.captchaChallenge`
## 3. 当前前端直接依赖矩阵
| 依赖项 | 当前值/结构 | 当前消费者 | 当前作用 |
| --- | --- | --- | --- |
| 请求头 | `x-genarrative-response-envelope: v1` | `src/services/apiClient.ts``src/editor/shared/jsonClient.ts` | 请求标准 envelope 响应。 |
| 响应头 | `x-request-id` | `src/services/apiClient.ts` | 构造 `ApiClientError.meta.requestId` 的回退来源。 |
| 响应头 | `x-api-version` | `src/services/apiClient.ts``packages/shared/src/http.ts` | 识别标准 envelope / error body。 |
| 响应头 | `x-route-version` | `src/services/apiClient.ts` | 构造 `ApiClientError.meta.routeVersion` 的回退来源。 |
| 成功 body | `{ ok: true, data, error: null, meta }` | `unwrapApiResponse(...)` | 前端默认解包标准成功 envelope。 |
| 错误 body | `{ ok: false, data: null, error, meta }` | `ApiClientError``parseApiErrorMessage(...)` | 标准错误解析。 |
| 旧错误 body | `{ error, meta }` / `{ message, code }` | `parseApiErrorMessage(...)` | 老接口或非标准错误回退解析。 |
| 错误细节 | `error.code === 'CAPTCHA_REQUIRED'``error.details.captchaChallenge` | `src/services/authService.ts` | 手机验证码发送前的验证码挑战弹出。 |
## 4. 请求侧冻结要求
### 4.1 Envelope 请求头
当前前端默认行为:
1. `src/services/apiClient.ts` 会自动补:
- `x-genarrative-response-envelope: v1`
2. `src/editor/shared/jsonClient.ts` 也会自动补:
- `x-genarrative-response-envelope: v1`
当前后端接受的 envelope 触发值:
1. `1`
2. `true`
3. `v1`
4. `envelope`
但当前前端真实发送值冻结为:
1. `v1`
补充冻结点:
1. 虽然 `apiClient` 提供了 `omitEnvelopeHeader` 选项,但当前生产代码没有实际依赖它。
2. 因此第一阶段 Axum 应默认兼容“前端请求即要 envelope”的模式。
### 4.2 鉴权与凭证约定
当前前端请求层默认还会做:
1. `Authorization: Bearer <token>` 自动注入。
2. `credentials: same-origin`
3. 遇到 `401` 时尝试走 `/api/auth/refresh` 自动刷新。
这不是本文重点,但它解释了为什么 envelope 和错误格式必须在 `/api/auth/refresh` 上也保持兼容。
## 5. 响应头冻结要求
### 5.1 必须保留的前端直接依赖头
| 响应头 | 当前来源 | 当前前端用法 |
| --- | --- | --- |
| `x-request-id` | `requestIdMiddleware` + `applyApiResponseHeaders` | `ApiClientError.meta.requestId` 的 header 回退来源。 |
| `x-api-version` | `applyApiResponseHeaders` | 当前标准 API 契约版本识别。 |
| `x-route-version` | `applyApiResponseHeaders` | `ApiClientError.meta.routeVersion` 的 header 回退来源。 |
### 5.2 兼容头但非直接读取项
| 响应头 | 当前状态 | 说明 |
| --- | --- | --- |
| `x-response-time-ms` | 当前统一输出 | 目前前端代码未直接读取,但设计文档与联调约定已锁定,不能随意删除。 |
补充冻结点:
1. `requestIdMiddleware` 会优先回显请求方传入的 `x-request-id`,否则服务端自生成。
2. `ApiClientError` 读取元信息时优先取 body `meta`,没有再回退到 headers。
3. 这意味着即便 envelope body 缺少部分 `meta` 字段headers 仍必须完整。
## 6. 成功响应 Envelope 冻结格式
当前标准成功 envelope
```json
{
"ok": true,
"data": {},
"error": null,
"meta": {
"apiVersion": "2026-04-08",
"requestId": "req-xxx",
"routeVersion": "2026-04-08",
"operation": "runtime.story.initial",
"latencyMs": 12,
"timestamp": "2026-04-20T00:00:00.000Z"
}
}
```
冻结规则:
1. `ok` 必须为 `true`
2. `data` 为真实业务负载。
3. `error` 必须为 `null`
4. `meta.apiVersion` 必须存在,因为 `unwrapApiResponse(...)``isApiResponse(...)` 依赖它判断标准 envelope。
补充说明:
1. 如果请求未带 envelope 头,当前后端可以直接返回裸 `data`
2. 但由于当前前端默认都会请求 envelope第一阶段 Axum 基本等价于“所有 JSON 成功响应都要兼容这个结构”。
## 7. 错误响应 Envelope 与旧格式回退
### 7.1 当前标准错误 envelope
```json
{
"ok": false,
"data": null,
"error": {
"code": "UNAUTHORIZED",
"message": "缺少 Authorization Bearer Token",
"details": null
},
"meta": {
"apiVersion": "2026-04-08",
"requestId": "req-xxx",
"routeVersion": "2026-04-08",
"operation": "auth.me",
"latencyMs": 3,
"timestamp": "2026-04-20T00:00:00.000Z"
}
}
```
冻结规则:
1. `ok` 必须为 `false`
2. `data` 必须为 `null`
3. `error.code``error.message` 必须存在。
4. `error.details` 可为对象或 `null`
5. `meta.apiVersion` 必须存在。
### 7.2 当前旧式错误格式回退
当请求未要求 envelope或某些链路仍走旧写法时当前后端与前端仍兼容以下错误结构
1. `{ "error": { "code": "...", "message": "...", "details": ... }, "meta": {...} }`
2. `{ "message": "...", "code": "..." }`
3. `{ "error": { "message": "..." } }`
4. 纯文本错误响应
`parseApiErrorMessage(rawText, fallbackMessage)` 的当前回退顺序固定为:
1. `parsed.error.message`
2. 顶层 `message`
3. `error.code` 或顶层 `code`,拼成 `fallbackCODE`
4. 原始文本
5. 调用方的 `fallbackMessage`
这意味着:
1. Axum 第一阶段不能只兼容标准 envelope而忽略旧错误解析的回退行为。
2. 至少在迁移过渡期,`parseApiErrorMessage(...)` 可识别的信息要继续保留。
## 8. 前端对错误细节的业务级直接依赖
### 8.1 验证码挑战
`src/services/authService.ts` 当前明确依赖:
1. `error instanceof ApiClientError`
2. `error.code === 'CAPTCHA_REQUIRED'`
3. `error.details.captchaChallenge`
冻结要求:
1. 如果后端要继续触发验证码挑战,必须继续返回:
- `code: 'CAPTCHA_REQUIRED'`
- `details.captchaChallenge`
2. 不能只返回中文文案而不带结构化 `details`
### 8.2 元信息透传
`ApiClientError` 当前会保留:
1. `status`
2. `code`
3. `details`
4. `meta.apiVersion`
5. `meta.requestId`
6. `meta.routeVersion`
7. `meta.operation`
8. `meta.latencyMs`
9. `meta.timestamp`
冻结要求:
1. Axum 不能把这些字段全都删成单纯 `message` 字符串。
2. 即使部分业务 UI 现在没显示这些字段,它们已经进入前端错误对象结构。
## 9. 当前消费者清单
以下文件已构成当前前端的直接依赖面:
1. `src/services/apiClient.ts`
2. `src/services/authService.ts`
3. `src/services/aiService.ts`
4. `src/editor/shared/jsonClient.ts`
5. `packages/shared/src/http.ts`
## 10. 本轮冻结后的硬约束
后续迁移中,不允许出现以下情况:
1. 删除 `x-genarrative-response-envelope: v1` 的请求协商能力。
2. 删除 `x-request-id``x-api-version``x-route-version` 这些当前前端直接依赖的响应头。
3. 把成功 envelope 从 `{ ok, data, error, meta }` 改成其他字段名。
4. 把错误 envelope 从 `{ ok: false, data: null, error, meta }` 改成只有 `message`
5. 删除 `CAPTCHA_REQUIRED + details.captchaChallenge` 这一结构化错误契约。
6. 让前端默认请求 envelope但后端返回裸数据且不再可被 `unwrapApiResponse(...)` 识别。
## 11. 本任务完成定义
当以下条件成立时,这条任务视为完成:
1. 当前前端直接依赖的响应头、envelope、错误格式已有书面冻结清单。
2. 已明确哪些是前端直接读取项,哪些是兼容保留项。
3. 后续 Axum handler、错误中间件、response envelope 中间件可以直接按本文对齐,而不再靠人工试错。

View File

@@ -1,245 +0,0 @@
# M0`/generated-*` 静态资源前缀冻结基线
日期:`2026-04-20`
依据来源:
- [../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md](../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md)
- `server-node/src/modules/assets/characterAssetRoutes.ts`
- `server-node/src/modules/assets/qwenSpriteRoutes.ts`
- `server-node/src/services/sceneImageService.ts`
- `server-node/src/services/customWorldCoverAssetService.ts`
- `server-node/src/services/customWorldAgentAutoAssetService.ts`
- [../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md](../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md)
## 1. 文档目的
这份文档用于完成 `M0` 的第五条任务:
- 整理当前所有 `/generated-*` 静态资源前缀
这里的“整理”不是只列出几个目录名,而是要求冻结以下信息:
1. 当前哪些 `/generated-*` 前缀是正式业务前缀。
2. 每个前缀由哪条后端链路产出。
3. 每个前缀对应的当前路径模板是什么。
4. 哪些前缀只是未来设计名或测试噪音,不能误当成当前正式兼容面。
## 2. 冻结结论
当前工程里,正式业务使用的 `/generated-*` 静态资源前缀固定为以下 `6` 个:
| 前缀 | 当前状态 | 当前主要生产链路 | 当前典型路径模板 | 重写后兼容要求 |
| --- | --- | --- | --- | --- |
| `/generated-character-drafts/*` | 正式前缀 | 角色主形象草稿、动作草稿、导入参考素材 | `/generated-character-drafts/{characterId}/{kind}/{jobId}/{file}` | 必须保留 |
| `/generated-characters/*` | 正式前缀 | 角色主形象正式发布、Agent 自动角色图回填 | `/generated-characters/{characterId}/visual/{assetId}/{file}` | 必须保留 |
| `/generated-animations/*` | 正式前缀 | 角色基础动作正式发布 | `/generated-animations/{characterId}/{animationSetId}/{action}/{file}` | 必须保留 |
| `/generated-custom-world-scenes/*` | 正式前缀 | 世界场景图生成、Agent 自动场景图回填 | `/generated-custom-world-scenes/{world}/{landmarkOrAct}/{assetId}/{file}` | 必须保留 |
| `/generated-custom-world-covers/*` | 正式前缀 | 世界封面图生成、封面上传 | `/generated-custom-world-covers/{world}/{assetId}/{file}` | 必须保留 |
| `/generated-qwen-sprites/*` | 正式前缀 | Qwen 主图草稿、精灵表草稿、修帧草稿、最终保存 | `/generated-qwen-sprites/{assetKeyOrDraftScope}/{actionOrKind}/{assetId}/{file}` | 必须保留 |
额外结论:
1. 当前仓库里真实业务前缀是 `6` 个,不是 `4` 个也不是 `5` 个。
2. 其中 `generated-animations``generated-custom-world-covers` 是当前代码已正式使用、但早期重写设计里容易漏掉的两个前缀。
3. 当前 `public/` 目录下已存在:
- `generated-character-drafts`
- `generated-characters`
- `generated-qwen-sprites`
4. `generated-animations``generated-custom-world-scenes``generated-custom-world-covers` 当前按需惰性创建,不代表它们不是正式前缀。
## 3. 正式前缀清单
### 3.1 `/generated-character-drafts/*`
当前用途:
1. 角色主形象候选图草稿。
2. 角色动作草稿帧、草稿视频、导入参考素材。
3. 角色资产工作流缓存与任务记录。
当前主要生产链路:
1. `POST /api/assets/character-visual/generate`
2. `POST /api/assets/character-animation/generate`
3. `POST /api/assets/character-animation/import-video`
4. `POST /api/assets/character-workflow-cache`
当前典型路径模板:
1. `/generated-character-drafts/{characterId}/visual/{jobId}/candidate-01.png`
2. `/generated-character-drafts/{characterId}/animation/{action}/{jobId}/preview.mp4`
3. `/generated-character-drafts/{characterId}/animation/{action}/{jobId}/frame-01.png`
4. `/generated-character-drafts/{characterId}-{cacheKey}/workflow-cache.json`
冻结要求:
1. 它是“草稿态真相路径”,不是正式发布路径。
2. 重写为 OSS 后仍要保留这一层 HTTP 兼容前缀,哪怕底层已不是本地磁盘。
### 3.2 `/generated-characters/*`
当前用途:
1. 角色主形象正式发布路径。
2. Custom World Agent 自动回填的角色主图。
当前主要生产链路:
1. `POST /api/assets/character-visual/publish`
2. `customWorldAgentAutoAssetService`
当前典型路径模板:
1. `/generated-characters/{characterId}/visual/{assetId}/master.png`
2. `/generated-characters/{characterId}/visual/{assetId}/preview-01.png`
冻结要求:
1. 它是角色正式视觉资产前缀,不允许与草稿前缀混用。
2. 后续即使内部改成 OSS也必须继续对前端暴露这一前缀。
### 3.3 `/generated-animations/*`
当前用途:
1. 角色基础动作正式发布路径。
2. `CharacterAnimator` 侧消费的核心动画资源前缀。
当前主要生产链路:
1. `POST /api/assets/character-animation/publish`
当前典型路径模板:
1. `/generated-animations/{characterId}/{animationSetId}/{action}/frame-01.png`
2. `/generated-animations/{characterId}/{animationSetId}/{action}/preview.mp4`
3. `/generated-animations/{characterId}/{animationSetId}/{action}/manifest.json`
冻结要求:
1. 当前正式动画并不挂在 `/generated-characters/.../animation/...` 下,而是独立前缀 `/generated-animations/*`
2. 后续重写若想合并对象键,也必须先做兼容别名,不能直接改掉公开路径。
### 3.4 `/generated-custom-world-scenes/*`
当前用途:
1. 自定义世界场景图生成结果。
2. Agent 自动生成的场景图回填结果。
当前主要生产链路:
1. `POST /api/custom-world/scene-image`
2. `customWorldAgentAutoAssetService`
当前典型路径模板:
1. `/generated-custom-world-scenes/{worldSegment}/{landmarkSegment}/{assetId}/scene.png`
2. `/generated-custom-world-scenes/{sceneSegment}/{actSegment}/{assetId}/scene.png`
冻结要求:
1. 前缀里当前显式带 `custom-world-scenes`,不是通用 `generated-scenes`
2. 后续世界资料库、世界编辑器和 Agent 卡片仍会依赖这一命名习惯。
### 3.5 `/generated-custom-world-covers/*`
当前用途:
1. 自定义世界封面图生成结果。
2. 自定义世界封面图上传后规范化结果。
当前主要生产链路:
1. `POST /api/custom-world/cover-image`
2. `POST /api/custom-world/cover-upload`
当前典型路径模板:
1. `/generated-custom-world-covers/{worldSegment}/{assetId}/cover.png`
2. `/generated-custom-world-covers/{worldSegment}/{assetId}/manifest.json`
冻结要求:
1. 当前正式前缀是 `/generated-custom-world-covers/*`,不是 `/generated-cover-images/*`
2. 后续重写设计和 OSS key 规划必须以这个现状兼容面为准。
### 3.6 `/generated-qwen-sprites/*`
当前用途:
1. Qwen 精灵主图草稿。
2. Qwen 精灵表草稿。
3. Qwen 修帧草稿。
4. Qwen 精灵表最终保存结果。
当前主要生产链路:
1. `POST /api/assets/qwen-sprite/master`
2. `POST /api/assets/qwen-sprite/sheet`
3. `POST /api/assets/qwen-sprite/frame-repair`
4. `POST /api/assets/qwen-sprite/save`
当前典型路径模板:
1. `/generated-qwen-sprites/_drafts/master/{draftId}/candidate-01.png`
2. `/generated-qwen-sprites/_drafts/sheet/{draftId}/candidate-01.png`
3. `/generated-qwen-sprites/_drafts/repair/{draftId}/candidate-01.png`
4. `/generated-qwen-sprites/{assetKey}/{actionKey}/{assetId}/sheet.png`
冻结要求:
1. 这个前缀当前既承载草稿,也承载正式保存结果。
2. 如果未来要把草稿与正式对象拆桶,也必须先保留同一公开前缀兼容。
## 4. 非正式前缀与噪音项
以下命名当前不能当成“正式兼容前缀”:
| 名称 | 当前来源 | 处理结论 |
| --- | --- | --- |
| `/generated-cover-images/*` | 仅出现在重写设计文档旧草案里 | 这是未来提案名,不是当前工程真实前缀。 |
| `/generated-role*` | 测试数据或示例 ID | 不是正式静态资源前缀。 |
| `/generated-world*` | 测试数据或对象 ID | 不是正式静态资源前缀。 |
| `/generated-ruins*` | 测试数据或对象 ID | 不是正式静态资源前缀。 |
结论:
1. 后续做 Axum 静态资源兼容层时,只能以第 2 节和第 3 节里的 `6` 个前缀为正式基线。
2. 不能把测试里的字符串误判成真实资源入口。
## 5. 与重写设计的对齐要求
为避免后续按错路径重写,当前冻结以下对齐规则:
1. Axum 第一阶段必须兼容这 `6` 个公开资源前缀。
2. OSS 内部对象键可以调整,但对前端暴露的 URL 前缀不能少于这 `6` 个。
3. 设计文档里的对象键规划如果与这份基线冲突,以这份“当前工程冻结基线”为准,然后再在设计文档中补兼容说明。
## 6. 本轮冻结后的硬约束
后续迁移中,不允许出现以下情况:
1.`/generated-animations/*` 直接并入 `/generated-characters/*` 却不保留兼容别名。
2.`/generated-custom-world-covers/*` 擅自改成 `/generated-cover-images/*`
3. 在未做兼容层前,删除 `/generated-character-drafts/*``/generated-qwen-sprites/*` 的草稿访问习惯。
4. 仅因为某个目录在当前仓库里尚未生成,就把它从正式前缀清单里删掉。
## 7. 本任务完成定义
当以下条件成立时,这条任务视为完成:
1. 当前正式 `/generated-*` 前缀已经有书面冻结清单。
2. 每个前缀都已明确:
- 当前用途
- 当前生产链路
- 当前路径模板
- 后续兼容要求
3. 已明确哪些“generated-*”字符串只是噪音,不属于正式前缀。
## 8. 后续直接依赖这份基线的任务
1. 设计 Axum 静态资源兼容层
2. 设计 OSS 对象键与 CDN 别名
3. 做 assets / custom world / editor 的路径回归测试

View File

@@ -1,291 +0,0 @@
# M0内部模块迁移归属基线
日期:`2026-04-20`
依据来源:
- [../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md](../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md)
- [../server-node/manifests/backend-capability-index.json](../server-node/manifests/backend-capability-index.json)
- [../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md](../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md)
## 1. 文档目的
这份文档用于完成 `M0` 的第三条任务:
- 整理当前 `12` 个内部模块并锁定迁移归属
这里的“迁移归属”不是简单把旧目录名照搬到 Rust而是要求后续重写必须先明确
1. 每个旧模块在新架构中的主归属 crate。
2. 每个旧模块是否需要拆成“SpacetimeDB 状态层 + Axum/application 编排层”。
3. 每个旧模块的状态真相应该进入 `SpacetimeDB``OSS` 还是开发态本地文件适配。
4. 每个旧模块优先落在哪个迁移阶段,避免后续任务拆分时反复改口径。
命名补充说明:
1. 本文中仍出现的 `application::...``auth-service``oss-service``llm-service` 等名称,统一表示逻辑职责,不再要求它们必须继续作为顶层独立 crate 存在。
2. 在新的多 crate 结构下,这些逻辑职责默认落到对应 `crates/module-*` 的内部子层次,或落到 `crates/platform-*``crates/shared-*` 等共享 crate 中。
补充边界:
1. 本文只覆盖当前 `server-node/src/modules/*` 下的 `12` 个内部模块。
2. `auth``health` 虽然属于后端能力面,但不在这 `12` 个内部模块目录里,因此不在本文表内。
## 2. 冻结结论
当前 Node 后端的正式内部模块固定为以下 `12` 个:
补充口径:
1. 上表 `12` 个模块属于历史基线总量。
2.`2026-04-21` 起,本轮 Rust 后端重写的 active rewrite modules 固定为 `11` 个。
3. `editor` 作为遗留无用模块,仅保留历史事实对照,不再进入 `server-rs` 主线迁移。
| 模块 ID | 中文名称 | 当前目录 | 关联路由数 | 当前对外暴露面 | 重写后主归属 | 重写后次归属 | 目标迁移阶段 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `ai` | AI 编排模块 | `server-node/src/modules/ai` | `23` | `runtime-main` | `application + llm-service` | `contracts + api-server SSE facade` | `M4``M5``M6` |
| `assets` | 资产工具模块 | `server-node/src/modules/assets` | `18` | `assets` | `application::assets + oss-service` | `spacetime-module::asset_metadata` | `M6` |
| `combat` | 战斗结算模块 | `server-node/src/modules/combat` | `1` | `runtime-story-action` | `spacetime-module::gameplay::combat` | `domain::combat` | `M4` |
| `custom-world` | 自定义世界运行时模块 | `server-node/src/modules/custom-world` | `26` | `runtime-main` | `spacetime-module::custom_world + application::custom_world` | `llm-service + oss-service` | `M5` |
| `editor` | 编辑器资源模块 | `server-node/src/modules/editor` | `3` | `editor` | `不迁移(遗留模块)` | 保留 `server-node/` 历史链路对照 | `不纳入本轮` |
| `inventory` | 背包与物品变更模块 | `server-node/src/modules/inventory` | `1` | `runtime-story-action` | `spacetime-module::gameplay::inventory` | `domain::inventory` | `M4` |
| `npc` | NPC 交互模块 | `server-node/src/modules/npc` | `6` | `runtime-story-action``runtime-main` | `spacetime-module::gameplay::npc` | `application::npc_dialogue + llm-service` | `M4``M5` |
| `progression` | 成长与关卡进程模块 | `server-node/src/modules/progression` | `3` | `runtime-story-action``runtime-main` | `spacetime-module::gameplay::progression` | `domain::progression` | `M3``M4` |
| `quest` | 任务运行时模块 | `server-node/src/modules/quest` | `4` | `runtime-main``runtime-story-action` | `spacetime-module::gameplay::quest` | `application::quest_drafting + llm-service` | `M3``M4` |
| `runtime` | 运行时状态基座模块 | `server-node/src/modules/runtime` | `32` | `runtime-main``runtime-story-action` | `spacetime-module::runtime` | `application::runtime_facade` | `M3` |
| `runtime-item` | 运行时物品模块 | `server-node/src/modules/runtime-item` | `2` | `runtime-main``runtime-story-action` | `spacetime-module::gameplay::runtime_item` | `application::item_intent + llm-service` | `M4` |
| `story` | 故事会话模块 | `server-node/src/modules/story` | `10` | `runtime-main``runtime-story-action` | `spacetime-module::gameplay::story` | `application::story_facade + api-server SSE facade` | `M4` |
冻结总数:
1. 历史内部模块目录:`12`
2. 本轮 active rewrite modules`11`
3. 关联路由数最多的模块:`runtime`,共 `32`
4. 本轮纯外部副作用导向模块:`ai``assets`
5. 已退出本轮重写范围的遗留模块:`editor`
6. 纯状态规则导向模块:`combat``inventory`
7. 需要“状态层 + 编排层”双落位的混合模块:`custom-world``npc``quest``runtime-item``story`
## 3. 锁定迁移归属规则
后续所有重写实现,必须先遵守以下归属规则:
1. 纯运行时状态、纯规则计算、纯领域变更,优先进入 `spacetime-module/``domain/`,不能继续把真相留在 Axum 内存或 Node 风格 service。
2. 外部模型调用、OSS 上传、短信、微信、本地文件读写,统一放在 `application/ + api-server/ + infra service`,不能塞进 SpacetimeDB reducer。
3. 任何当前“一个模块同时做状态和副作用”的能力,在新架构里都必须拆成:
- `SpacetimeDB`:状态真相与读模型
- `Axum/application`外部编排、SSE、对象上传、三方调用
4. `public/generated-*` 不再是任何模块的真相源;未来只能作为兼容访问前缀或 CDN 映射。
5. 不允许把旧模块简单合并成一个“大 runtime service”必须保留可对照的领域边界。
## 4. 模块迁移矩阵
| 当前模块 | 当前职责摘要 | 新状态真相源 | 新外部副作用归属 | 迁移后必须落位 |
| --- | --- | --- | --- | --- |
| `ai` | prompt 编排、聊天/剧情/世界生成组织 | `SpacetimeDB` 只存任务和结果引用,不存编排过程真相 | `llm-service` | 只能留在 Axum/application 侧,禁止直接进 reducer。 |
| `assets` | 生成、发布、缓存、Qwen 精灵表 | `asset_job``asset_object``asset_manifest` 等表 | `oss-service` + 外部媒体模型 | 二进制进 OSS任务与引用进 SpacetimeDB。 |
| `combat` | 战斗结算、数值变化 | `battle_state``story_event` | 无 | 作为纯 reducer 规则模块落到 gameplay。 |
| `custom-world` | 世界资料、问答流、Agent 草稿与编译 | `custom_world_*` 系列表 | `llm-service``oss-service` | 世界状态在 SpacetimeDB编译/生成在 Axum。 |
| `editor` | 编辑器 JSON 读写、图标枚举 | 仍以遗留 Node 链路与开发态本地文件为历史对照 | 不迁移到 `server-rs` | 仅保留历史基线,不纳入本轮 Rust 重写。 |
| `inventory` | 背包变更、物品副作用、NPC 背包交互 | `inventory_slot``story_event` | 无 | 归入 story action 对应 reducer。 |
| `npc` | 互动规则、关系变化、招募/对话语义 | `npc_state``story_event` | `application::npc_dialogue + llm-service` | 状态归 SpacetimeDB台词生成归 Axum。 |
| `progression` | 等级、章节、敌对 scaling、benchmark | `player_progression``chapter_progression` | 无 | 作为 runtime / story 公共领域模块进入 SpacetimeDB。 |
| `quest` | 任务意图、日志、进度变化 | `quest_record``story_event` | `application::quest_drafting + llm-service` | 任务状态归 SpacetimeDB生成型任务草案归 Axum。 |
| `runtime` | 快照、设置、资料页、状态归一化 | `runtime_snapshot``runtime_setting``profile_*` | 无 | 作为新后端最先迁移的状态基座模块。 |
| `runtime-item` | 物品意图、奖励解析、宝藏逻辑 | `treasure_record``inventory_slot``story_event` | `application::item_intent + llm-service` | 奖励结算归 reducer意图生成归 Axum。 |
| `story` | 会话状态、动作分发、主循环 | `story_session``story_event` | `application::story_facade + SSE` | 主循环状态归 SpacetimeDB流式输出由 Axum 兼容。 |
## 5. 各模块冻结要求
### 5.1 `ai`
当前定位:
1. 负责剧情、多轮聊天、自定义世界等 prompt 编排。
2. 自身不负责持久化,但会被多条 runtime 路由反复调用。
重写后的冻结要求:
1. 主归属固定为 `application + llm-service`,不是 `spacetime-module`
2. 后续如果需要记录 AI 阶段状态,只能把任务状态或结果引用写入 SpacetimeDB不把供应商 SDK 与 prompt 执行放进 reducer。
3.`story``custom-world``runtime-item``quest` 的关系固定为“它们产生命令,`ai` 负责外部生成”。
### 5.2 `assets`
当前定位:
1. 负责角色形象、动作、Qwen 精灵表生成。
2. 负责发布到 `public/generated-*` 与局部 manifest。
重写后的冻结要求:
1. 二进制对象一律进入 `OSS`
2. 主归属固定为 `application::assets + oss-service`
3. 资产任务状态、对象引用关系、发布绑定关系必须进入 `spacetime-module::asset_metadata`
4. 后续不允许继续以本地 `public/generated-*` 是否存在文件作为业务真相。
### 5.3 `combat`
当前定位:
1. 提供 story action 里的战斗型结算。
2. 本质是纯规则计算。
重写后的冻结要求:
1. 主归属固定为 `spacetime-module::gameplay::combat`
2. 不单独拥有 HTTP 路由,也不直接依赖外部 IO。
3. 后续实现必须保持纯规则、可测试、可被 `resolve_story_action` reducer 复用。
### 5.4 `custom-world`
当前定位:
1. 负责 creator intent、world profile、传统问答流、Agent 运行时类型。
2. 同时牵涉世界编译、资产生成和公开画廊。
重写后的冻结要求:
1. 这是标准的双落位模块:
- `SpacetimeDB` 保存会话、草稿、作品、画廊、Agent 状态。
- `Axum/application` 负责编译、SSE、外部 LLM 与资产生成编排。
2. 传统问答流和 Agent 流必须拆表,不能继续长期混成一个大 JSON 会话体。
3. 对外仍然要兼容当前 `/api/custom-world/*``/api/runtime/custom-world/*` 访问习惯。
### 5.5 `editor`
当前定位:
1. 读写编辑器资源 JSON。
2. 枚举工作区 `public/Icons` 图标资源。
重写后的冻结要求:
1. 该模块在 `server-node/` 中的存在事实继续保留,用于历史基线与后续清理对照。
2.`2026-04-21` 起,不再为 `server-rs/` 创建 `module-editor` crate也不再把它纳入 `M1 ~ M6` 主线迁移。
3. 若未来仍需清理或替代 editor必须在遗留链路依赖确认后单独立项不能夹带进当前 Rust 重写主链。
4. 不允许为了简化本轮任务而篡改其历史存在事实。
### 5.6 `inventory`
当前定位:
1. 负责背包变更、赠礼、NPC 背包交互等副作用。
2. 当前主要被 story action 调用。
重写后的冻结要求:
1. 主归属固定为 `spacetime-module::gameplay::inventory`
2.`story``runtime-item` 的交互必须通过 reducer 协调,不能回到“多个 service 各自改 JSON”。
3. 后续如需对外展示背包读模型,优先通过 view 暴露,不新增独立真相副本。
### 5.7 `npc`
当前定位:
1. 负责 NPC 关系、招募、交互规则与场景 NPC 语义。
2. 同时参与 runtime 聊天流和 story action 结算。
重写后的冻结要求:
1. 状态真相固定在 `spacetime-module::gameplay::npc`
2. LLM 对话、招募话术、流式文本输出固定由 `application::npc_dialogue + llm-service` 处理。
3. 不允许把 NPC 状态又分散回聊天 session store、本地缓存或前端临时状态。
### 5.8 `progression`
当前定位:
1. 负责角色成长、章节推进、敌对强度等规则。
2. 同时影响 snapshot hydrate 与 story action 结算。
重写后的冻结要求:
1. 主归属固定为 `spacetime-module::gameplay::progression`
2. 仍保持纯规则、纯领域建模,不承接外部 IO。
3. 作为 `runtime``story` 的公共领域组件,不能被重新塞回单一路由 handler。
### 5.9 `quest`
当前定位:
1. 负责任务语义、任务日志、任务进度信号。
2. 既参与 AI 草案生成,也参与 story action 结算。
重写后的冻结要求:
1. 任务主状态固定进入 `spacetime-module::gameplay::quest`
2. AI 生成的任务候选与草案编排固定由 `application::quest_drafting + llm-service` 承担。
3. 前端兼容接口仍走 `/api/runtime/quests/*` 或 story action 聚合,不新增前端直连任务状态写入口。
### 5.10 `runtime`
当前定位:
1. 是当前运行时快照、设置、资料页、状态归一化的基座模块。
2. 路由覆盖最广,是 Node 版后端迁移的第一主战场。
重写后的冻结要求:
1. 主归属固定为 `spacetime-module::runtime`
2. `runtime_snapshot``runtime_setting``profile_*` 等读写模型优先在 `M3` 完成迁移。
3. Axum 只保留兼容 facade不再继续让快照真相停留在 PostgreSQL 风格 repository。
### 5.11 `runtime-item`
当前定位:
1. 负责运行时物品意图、奖励、宝藏解析与剧情指纹。
2. 同时受到 AI 生成与 story action 结算驱动。
重写后的冻结要求:
1. 奖励、掉落、宝藏等状态变化固定进入 `spacetime-module::gameplay::runtime_item`
2. 物品意图生成固定由 `application::item_intent + llm-service` 承接。
3. 物品领域不能再拆成“部分在 story、部分在 route、部分在前端”的临时实现。
### 5.12 `story`
当前定位:
1. 负责运行时故事会话、动作分发与 state 恢复。
2. 当前既暴露 REST也暴露与聊天/继续剧情相关的流式体验。
重写后的冻结要求:
1. 主归属固定为 `spacetime-module::gameplay::story`
2. SSE 输出与兼容 DTO 拼装固定由 `application::story_facade + api-server SSE facade` 负责。
3. `storyAction.resolve` 的跨模块联动必须以 `story` 为编排入口,但不再由单个 Node service 直接改整包 JSON。
## 6. 本轮冻结后的硬约束
后续迁移中,不允许出现以下情况:
1.`ai``assets` 直接放进 SpacetimeDB reducer 执行三方网络或文件系统 IO。
2. 在未单独立项前,把已退出本轮范围的 `editor` 重新并回 `server-rs` 主链。
3.`combat``inventory``progression` 重新做成只存在于 Axum handler 内部的计算 helper。
4.`custom-world``story``npc` 这类混合模块继续保留为“单大对象 JSON + 单大 service 写回”模式。
5.`runtime` 当成一个兜底垃圾桶,把其他领域模块重新并回去。
6. 在没有对应 Axum facade 的前提下,让前端第一阶段直接依赖 SpacetimeDB 原生写接口。
## 7. 本任务完成定义
当以下条件成立时,这条任务视为完成:
1. 当前历史 `12` 个内部模块已经有正式书面冻结清单。
2. 每个模块都已明确:
- 当前目录
- 关联路由数
- 对外暴露面
- 重写后主归属
- 重写后次归属
- 目标迁移阶段
3. 本轮 active rewrite modules 为 `11` 个,且 `editor` 的遗留/不迁移口径已经冻结。
4. 后续拆 `server-rs/` 多 crate、建 SpacetimeDB bounded context、排 M3~M6 任务时,可以直接引用本文,不再靠口头记忆。
## 8. 后续直接依赖这份基线的任务
1. 设计 `server-rs/` workspace 与 crate 边界
2. 设计 SpacetimeDB `runtime / gameplay / custom_world / asset_metadata` 表分层
3. 设计 story action reducer 的跨模块协作边界
4. 设计 custom world / assets 的 Axum facade

View File

@@ -1,106 +0,0 @@
# M0阶段验收矩阵
日期:`2026-04-20`
依据来源:
- [00_MASTER_TASKLIST.md](./00_MASTER_TASKLIST.md)
- [01_M0_M2_FOUNDATION_AND_AUTH.md](./01_M0_M2_FOUNDATION_AND_AUTH.md)
- [02_M3_RUNTIME_PROFILE.md](./02_M3_RUNTIME_PROFILE.md)
- [03_M4_STORY_AND_GAMEPLAY.md](./03_M4_STORY_AND_GAMEPLAY.md)
- [04_M5_CUSTOM_WORLD_AND_AGENT.md](./04_M5_CUSTOM_WORLD_AND_AGENT.md)
- [05_M6_ASSETS_OSS_EDITOR.md](./05_M6_ASSETS_OSS_EDITOR.md)
- [06_M7_TEST_DEPLOY_CUTOVER.md](./06_M7_TEST_DEPLOY_CUTOVER.md)
- [07_CROSS_CUTTING_AND_ACCEPTANCE.md](./07_CROSS_CUTTING_AND_ACCEPTANCE.md)
- [../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md](../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md)
## 1. 文档目的
这份文档用于把 `M0 ~ M7` 各阶段的入口条件、核心交付、退出条件与回归焦点固定下来,避免后续出现“任务勾完了,但阶段是否真的可进入下一步没有统一标准”的问题。
从本文件开始,后续每一阶段都需要按“入口满足 -> 交付完成 -> 验收通过 -> 留存证据”的顺序推进。
## 2. 阶段推进总规则
1. 未满足上一阶段退出条件前,不进入下一阶段主线编码。
2. 每一阶段至少保留一份可复查的证据,证据可以是文档、脚本、测试结果或回归记录。
3. 所有阶段都必须持续对齐当前冻结基线:
- 历史基线 `6` 个挂载面
- 本轮 active rewrite target `5` 个挂载面
- `96` 条路由
- `12` 个模块
- `6` 条 SSE 接口
- `6``/generated-*` 静态资源前缀
- 前端直接依赖的响应头、envelope 与鉴权错误格式
4. 任一阶段若引入新的协议差异,必须先补 contract 文档或迁移说明,再允许继续编码。
## 3. 分阶段验收矩阵
| 阶段 | 入口条件 | 核心交付 | 退出条件 | 回归焦点 |
| --- | --- | --- | --- | --- |
| `M0` 冻结能力与边界 | 已完成当前 Node 后端摸底;已明确目标架构为 `SpacetimeDB + Axum + 阿里云 OSS` | 冻结能力基线、路由矩阵、模块归属、SSE、静态资源前缀、前端响应契约、仓库边界决议、阶段验收矩阵 | `6` 个挂载面、`96` 条路由、`12` 个模块、`6` 条 SSE、`6` 个静态资源前缀全部形成书面基线;`server-rs/``server-node/`、Axum 边界、副作用收口原则全部冻结 | 文档口径一致性;前端 contract 依赖项是否被遗漏;迁移阶段是否还存在多套边界说法 |
| `M1` Rust 工作区与 Axum 基础设施 | `M0` 全部退出条件满足 | `server-rs/` workspace、`crates/api-server``crates/spacetime-module`、独立模块 crates、统一配置、日志、request id、中间件、response envelope、`/healthz`、开发脚本 | Axum 可独立启动;`/healthz` 与当前工程兼容;`x-request-id``x-api-version``x-route-version``x-response-time-ms` 行为稳定workspace 完整编译通过;主工程与模块 crate 引用边界稳定 | 基础头部兼容;健康检查兼容;目录结构与 crate 归属是否偏离 `M0` 决议 |
| `M2` 鉴权、会话、JWT 与 refresh cookie | `M1` 已可稳定启动Axum 中间件与配置链可用 | 身份表、会话表、JWT claims、refresh cookie、密码登录、手机验证码登录、微信登录、OIDC 透传、旧鉴权接口兼容 | 密码登录、refresh cookie、手机验证码、微信登录主链可用旧鉴权接口 contract 回归通过SpacetimeDB 可识别 Axum 签发身份 | Cookie 与 JWT 兼容;`CAPTCHA_REQUIRED``details.captchaChallenge` 是否保持;登录态吊销与刷新是否稳定 |
| `M3` runtime snapshot / settings / profile | `M2` 鉴权稳定;用户身份可透传到 SpacetimeDB | `runtime_snapshot``runtime_setting`、profile 相关主表与 facade存档、设置、浏览历史、save archive 兼容接口 | 登录用户可正常保存、读取、删除存档profile dashboard / browse history / save archive 行为一致;前端恢复流程可直接跑通 | 快照恢复准确性;兼容路径与主路径是否返回一致;历史记录排序与去重逻辑 |
| `M4` story action 与 gameplay reducer | `M3` 快照与用户状态主链稳定 | story / combat / inventory / npc / quest / progression / runtime-item 表与 reducerstory 兼容接口与 view model | 前端 story 主循环可用;`story state` 恢复链可用NPC / quest / treasure / combat 主循环行为不回退;旧 Node story route 回归平移完成 | `RuntimeStoryActionResponse` 结构;战斗与奖励联动;状态投影是否与旧前端恢复逻辑一致 |
| `M5` custom world / gallery / agent | `M4` story 与 runtime 真相源已稳定SSE facade 可持续输出 | custom world 主表、agent 会话拆表、传统问答流、library / gallery、agent 消息与操作、LLM/图片生成编排 | 传统 custom world 主链可用library / gallery 主链可用agent 主链可用;会话不再依赖单大 JSON 体 | SSE 事件格式;卡片、消息、操作状态一致性;世界草稿编译与发布链是否可回放 |
| `M6` assets / OSS | `M5` 世界与角色主链稳定Axum 应用层可承接外部副作用 | OSS 对象键规范、上传签名、对象确认、资产任务表、角色/场景/Qwen 资产迁移、旧静态路径兼容 | 所有新生成资产写入 OSS前端仍能通过旧路径习惯访问资源资产任务状态可查询 | `/generated-*` 路径兼容OSS 元数据与对象绑定关系;资产任务链状态一致性 |
| `M7` 联调、回归、部署与切流 | `M6` 已具备主链闭环;双栈对照条件具备 | 测试体系、部署方案、观测能力、灰度切流方案、回退方案、对比脚本与 smoke 清单 | 全链路 smoke 通过;主流程回归通过;关键 SSE 联调通过;可在灰度环境切流并可回退 | 双跑窗口稳定性API 对比结果;切流开关、回退开关、观测告警是否齐备 |
## 4. M0 冻结项专用验收清单
只有以下项目全部满足,`M0` 才算真正完成:
1. 已产出以下冻结文档:
- [M0_CAPABILITY_SURFACE_BASELINE_2026-04-20.md](./M0_CAPABILITY_SURFACE_BASELINE_2026-04-20.md)
- [M0_ROUTE_MIGRATION_MATRIX_2026-04-20.md](./M0_ROUTE_MIGRATION_MATRIX_2026-04-20.md)
- [M0_MODULE_MIGRATION_BASELINE_2026-04-20.md](./M0_MODULE_MIGRATION_BASELINE_2026-04-20.md)
- [M0_SSE_INTERFACE_BASELINE_2026-04-20.md](./M0_SSE_INTERFACE_BASELINE_2026-04-20.md)
- [M0_GENERATED_STATIC_PREFIX_BASELINE_2026-04-20.md](./M0_GENERATED_STATIC_PREFIX_BASELINE_2026-04-20.md)
- [M0_FRONTEND_RESPONSE_CONTRACT_BASELINE_2026-04-20.md](./M0_FRONTEND_RESPONSE_CONTRACT_BASELINE_2026-04-20.md)
- [M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md](./M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md)
- [M0_PHASE_ACCEPTANCE_MATRIX_2026-04-20.md](./M0_PHASE_ACCEPTANCE_MATRIX_2026-04-20.md)
2. 已书面冻结以下核心数字:
- 挂载面:`6`
- 路由:`96`
- 模块:`12`
- SSE`6`
- 静态资源前缀:`6`
3. 已书面冻结以下边界决议:
- 新 Rust 后端固定为仓库根目录 `server-rs/`
- 迁移期保留 `server-node/`
- 前端 `M0 ~ M6` 期间只访问 Axum
- 外部副作用统一收口在 Axum
- `server-rs/` 内部采用 `crates/*` 多 crate 组织
- `editor` 已于 `2026-04-21` 退出本轮 Rust 重写范围
4. `M1` 以后任何任务引用路由、模块、SSE、静态资源与响应契约时都必须能追溯到本阶段产出的冻结文档。
## 5. 跨阶段回归维度
无论执行到哪个阶段,都要持续检查以下维度:
| 维度 | 必查内容 | 最晚必须固化的证据 |
| --- | --- | --- |
| 路由兼容 | 旧路由是否已有新实现或明确替代路径 | 路由迁移矩阵、API 对比脚本、contract 回归记录 |
| SSE 兼容 | 事件名、事件顺序、结束事件、错误事件是否保持兼容 | SSE 基线文档、联调记录、smoke 结果 |
| 静态资源兼容 | `/generated-*` 是否可继续访问,是否正确指向 OSS/CDN | 静态资源前缀基线、路径兼容测试记录 |
| 鉴权兼容 | JWT、refresh cookie、验证码、微信登录、风控错误是否保持兼容 | 鉴权接口回归记录、claims 设计文档、集成测试 |
| 前端 contract | 响应头、envelope、错误结构是否稳定 | response contract 基线、接口测试、前端联调记录 |
| 切流回退 | 双栈是否可对照,是否具备回退能力 | `M7` 对比脚本、灰度清单、回退方案 |
## 6. 阶段证据留存要求
每个阶段完成时,至少要补齐以下其中两类证据:
1. 文档:
- 更新任务清单勾选状态
- 更新设计文档或阶段落地记录
2. 测试或脚本:
- 新增或更新 smoke / contract / integration 测试
- 新增对比脚本、发布脚本或回归脚本
3. 结果记录:
- 编码检查结果
- 关键命令执行结果
- 联调、回归、灰度演练结果
如果阶段只完成了编码、但没有文档和证据留存,则该阶段不能视为完成。

View File

@@ -1,281 +0,0 @@
# M0仓库边界决议
日期:`2026-04-20`
依据来源:
- [../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md](../docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md)
- [00_MASTER_TASKLIST.md](./00_MASTER_TASKLIST.md)
- [01_M0_M2_FOUNDATION_AND_AUTH.md](./01_M0_M2_FOUNDATION_AND_AUTH.md)
## 1. 文档目的
这份文档用于持续冻结 `M0` 中与仓库边界直接相关的决策,避免进入 `M1` 后再反复改目录、改职责口径。
当前已确认的事项会持续在这份文档上追加维护,后续若再有新的边界冻结结论,也统一收口到这里。
## 2. 边界决议状态
| 事项 | 当前状态 | 当前结论 |
| --- | --- | --- |
| Rust 后端新目录名与根目录落位方案 | 已确认 | 新 Rust 后端固定为仓库根目录下的 `server-rs/`,与 `server-node/` 同级。 |
| 旧 `server-node/` 在迁移期继续保留,不提前删除 | 已确认 | `server-node/``M0 ~ M6` 期间持续保留,直到 `M7` 切流与回退验证完成后再评估清理。 |
| 前端第一阶段仍然只访问 Axum不直连 SpacetimeDB | 已确认 | `M0 ~ M6` 前端统一只访问 Axum 暴露的 `/api/*``/healthz`、SSE 与静态资源兼容层,不新增直连 SpacetimeDB 原生协议路径。 |
| 外部副作用统一收口在 Axum不放进 SpacetimeDB 模块 | 已确认 | OSS、LLM、短信、微信 OAuth、本地文件系统等外部副作用统一落在 Axum/application/infra不进入 SpacetimeDB reducer/module。 |
| `server-rs/` 内部采用多 crate 组织,由主工程 crate 统一引用模块 crate | 已确认 | `server-rs/` 采用 `crates/*` 工作区结构,`crates/api-server``crates/spacetime-module` 作为主工程 crate独立模块以 `crates/module-*` 形式被主工程 crate 引用。 |
| `editor` 为遗留无用模块,不纳入 `server-rs` 本轮重写范围 | 已确认 | `server-node/src/modules/editor``/api/editor/*` 仅作为历史基线保留对照;自 `2026-04-21` 起退出本轮 Rust 后端重写范围。 |
## 3. 已确认决议一:`server-rs/` 固定落在仓库根目录
### 3.1 决议内容
本次重写固定采用以下仓库落位:
1. 新后端目录名固定为 `server-rs/`
2. 目录位置固定在仓库根目录
3. 与以下目录保持同级:
- `server-node/`
- `src/`
- `docs/`
- `packages/`
目标形态:
```text
Genarrative/
├─ server-node/
├─ server-rs/
├─ src/
├─ packages/
├─ docs/
└─ backend-rewrite-tasklist/
```
### 3.2 不采用的落位方案
以下方案当前明确不采用:
1. 不放进 `server-node/` 子目录中做“Node + Rust 混编后端”。
2. 不放进 `packages/`,避免被前端 package/workspace 语义误导。
3. 不使用过于泛化的根目录名如 `server/``backend/`,避免和当前 `server-node/` 职责混淆。
### 3.3 这样落位的原因
1. 与当前重写设计文档、任务清单、后续 `M1` 多 crate 规划保持一致。
2. 允许 `server-node/``server-rs/` 在迁移期并行存在,便于逐阶段切流。
3. 让 Rust 工作区边界清晰,不污染现有前端 `src/``packages/`、Vite 工具链。
4. 后续新增 `server-rs/scripts/*``Cargo.toml``crates/*` 时路径最直接,不需要额外中间层。
### 3.4 对后续任务的直接约束
从这一条决议开始,后续任务必须统一按以下路径落位:
1. `M1` 的工作区初始化在 `server-rs/`
2. Axum 主工程 crate 在 `server-rs/crates/api-server`
3. SpacetimeDB 主工程 crate 在 `server-rs/crates/spacetime-module`
4. 独立模块 crate 在 `server-rs/crates/module-*`
5. 相关脚本在 `server-rs/scripts/`
## 4. 本条任务完成定义
当以下条件成立时,这一条边界任务视为完成:
1. 新 Rust 后端目录名已经书面固定为 `server-rs/`
2. 目录位置已经书面固定为仓库根目录
3. 后续 `M1` 的工作区初始化不会再出现 `server-rs/``backend-rs/``server/` 等多套候选名并存
## 5. 已确认决议二:迁移期保留 `server-node/`
### 5.1 决议内容
在本次重写迁移期内,旧 `server-node/` 固定继续保留,不提前删除、不整体挪位、不提前做破坏性收缩。
保留周期固定为:
1. `M0``M6` 全阶段
2. 至少持续到 `M7` 的以下条件全部满足之后,才允许评估清理:
- 新后端已切流
- 旧接口 contract 回归通过
- 关键主链 smoke 通过
- 已具备明确回退方案
### 5.2 保留它的原因
1.`server-node/` 是当前 `96` 条路由、`6` 个挂载面、`12` 个模块的真实对照实现。
2. 前面已经冻结的路由矩阵、模块迁移清单、SSE 协议、静态资源前缀,都需要它作为回归对照源。
3. 如果在 `M1 ~ M6` 提前删除旧实现,就会失去最可靠的回退锚点与 diff 基准。
### 5.3 迁移期允许做什么
迁移期内允许:
1.`server-rs/` 中逐步补等价实现。
2. 在文档和 manifest 中继续引用 `server-node/` 作为当前系统基线。
3. 在必要时从 `server-node/` 补测试样例、补协议对照、补回归夹具。
迁移期内不允许:
1. 提前整体删除 `server-node/`
2.`server-node/` 改成只剩空壳目录
3. 在还没切流前,把旧服务关键实现批量迁走导致无法对照
### 5.4 对后续任务的直接约束
从这一条决议开始,后续任务必须遵守:
1. `M1` 搭建 `server-rs/` 时,不改动 `server-node/` 的存在性。
2. `M2 ~ M6` 迁移功能时,旧 `server-node/` 继续作为验收基线与回退锚点。
3. 真正评估清理旧 Node 后端的动作,只能放到 `M7` 切流完成之后。
## 6. 已确认决议三:前端第一阶段只访问 Axum
### 6.1 决议内容
`M0 ~ M6` 迁移期内,前端访问新后端的唯一入口固定为 Axum。
第一阶段允许前端继续访问的面固定为:
1. `/api/*`
2. `/healthz`
3. 当前已冻结的 SSE 路由
4. 当前已冻结的 `/generated-*` 静态资源兼容前缀
第一阶段明确不做的事:
1. 不让 Web 前端直接接 SpacetimeDB 原生 HTTP 接口。
2. 不让 Web 前端直接接 SpacetimeDB 订阅协议。
3. 不要求前端新增一套“Axum + SpacetimeDB 双后端并行直连”调用模式。
### 6.2 这样决议的原因
1. 当前前端已经直接依赖现有 `/api/*` 路由、response envelope、SSE、`/generated-*` 路径习惯。
2. 如果在第一阶段就让前端同时认识 Axum 与 SpacetimeDB会把迁移面从“后端平移”扩大成“前后端协议双重重写”。
3. Axum 需要承担统一鉴权、cookie、JWT、OSS 签名、错误格式与 contract 兼容职责,这些都不应分散到前端直连多个后端协议。
### 6.3 对后续任务的直接约束
从这一条决议开始,后续任务必须遵守:
1. `M1 ~ M2` 的 Axum 中间件与鉴权必须先跑通,再谈前端联调。
2. `M3 ~ M6` 新增的 SpacetimeDB reducer/view 先通过 Axum facade 暴露,不直接要求前端改成原生 SpacetimeDB 客户端。
3. 若后续要让前端直连 SpacetimeDB只能作为第二阶段优化事项不能混入当前重写主链。
## 7. 已确认决议四:外部副作用统一收口在 Axum
### 7.1 决议内容
本次重写固定采用以下边界:
1. `SpacetimeDB` 只负责状态、规则、reducer、view、订阅读模型。
2. `Axum/application/infra` 统一负责所有外部副作用。
固定收口到 Axum 的外部副作用包括:
1. 阿里云 OSS 上传、下载、签名、直传凭证
2. DashScope / Ark / 其他 LLM 请求
3. 微信 OAuth
4. 手机验证码短信发送与校验编排
5. 本地文件系统读写
### 7.2 明确不允许放进 SpacetimeDB 的内容
以下能力当前明确禁止进入 `spacetime-module/`
1. 直接发 HTTP 请求给第三方供应商
2. 直接访问 OSS SDK
3. 直接读写本地磁盘
4. 直接处理 Cookie、回调跳转、multipart 上传
5. 直接承担供应商重试、熔断、超时与日志策略
### 7.3 这样决议的原因
1. 这些能力都强依赖 HTTP 头、Cookie、SDK、签名、超时与日志不适合绑进 SpacetimeDB 模块发布周期。
2. 当前前端 contract、鉴权、SSE、静态资源兼容都要求一个稳定的 HTTP 边界层Axum 更适合承担这个角色。
3. 把副作用统一收口到 Axum才能让 SpacetimeDB 保持“状态机真相源”的纯度。
### 7.4 对后续任务的直接约束
从这一条决议开始,后续任务必须遵守:
1. `M1` crate 设计时,`platform-oss``platform-llm``platform-auth` 固定属于 Axum / 模块应用层一侧。
2. `M2 ~ M6` 设计 reducer 时,只写状态变更,不直接发外部请求。
3. 若确实需要异步副作用,也必须由 Axum worker 或应用层作业执行,再把结果回写 SpacetimeDB。
## 8. 已确认决议五:`server-rs/` 内部采用多 crate 组织
### 8.1 决议内容
从当前版本开始,`server-rs/` 内部结构固定采用:
1. `crates/*`:统一收口主工程 crate、独立模块 crate 与共享 crate
2. `scripts/*`:开发、发布、回归脚本
主工程 crate 固定包含:
1. `crates/api-server`
2. `crates/spacetime-module`
独立模块 crate 固定按“每个独立模块一个 crate”推进至少覆盖
1. `crates/module-auth`
2. `crates/module-runtime`
3. `crates/module-story`
4. `crates/module-combat`
5. `crates/module-inventory`
6. `crates/module-npc`
7. `crates/module-progression`
8. `crates/module-quest`
9. `crates/module-runtime-item`
10. `crates/module-custom-world`
11. `crates/module-assets`
12. `crates/module-ai`
跨模块共享 crate 固定包含:
1. `crates/shared-contracts`
2. `crates/shared-kernel`
3. `crates/platform-auth`
4. `crates/platform-oss`
5. `crates/platform-llm`
6. `crates/spacetime-client`
7. `crates/tests-support`
### 8.2 这样决议的原因
1. 用户已经明确要求后端采用 Rust workspace 下的多 crate 模式,独立模块不能继续堆回单个技术层大包。
2. 当前后端已有 `12` 个内部模块边界,多 crate 方案更容易保持一一映射与独立演进。
3. `crates/api-server``crates/spacetime-module` 只做组合与发布,更符合“主工程 crate 引用模块 crate”的组织方式。
### 8.3 对后续任务的直接约束
从这一条决议开始,后续任务必须遵守:
1. `M1` 及后续目录任务统一按 `crates/*` 执行,不再保留 `apps/*``packages/*` 并行规划。
2. 每个业务模块默认先有自己的 workspace crate再由主工程 crate 引用。
3. 只有共享 contract、共享领域内核、平台适配、SpacetimeDB client 这类跨模块能力,才允许使用共享 crate而不是业务模块混装。
## 9. 已确认决议六:`editor` 退出本轮 Rust 重写范围
### 9.1 决议内容
`editor` 在当前 Node 后端中确实存在真实模块与真实挂载面,但已于 `2026-04-21` 被确认为遗留无用模块,不再纳入本轮 `server-rs/` 重写主链。
当前固定口径为:
1. 历史基线继续保留 `server-node/src/modules/editor``/api/editor/*` 的存在事实。
2. `server-rs/` 不再保留 `crates/module-editor`
3. `M1 ~ M6` 的主线任务、阶段验收与 crate 规划,不再把 `editor` 计入 active rewrite scope。
### 9.2 这样决议的原因
1. 用户已明确确认 `editor` 为遗留无用模块,应从本轮重写目标中剔除。
2. 保留历史事实有助于后续对照清理,不会把“旧系统曾存在该模块”的信息抹掉。
3. 从当前阶段开始继续为 `editor` 预留 Rust crate只会增加主线迁移噪音与工程负担。
### 9.3 对后续任务的直接约束
从这一条决议开始,后续任务必须遵守:
1. 不再为 `editor` 创建或维护 `server-rs` 下的新 crate、Axum 路由树与迁移验收项。
2. 所有涉及挂载面、模块、路由总量的文档,都要区分“历史基线”与“本轮 active rewrite target”。
3. 若未来仍要清理 `editor`,应在 `server-node/` 遗留链路依赖核对完成后单独立项。

View File

@@ -1,249 +0,0 @@
# M0旧接口到新实现路由迁移矩阵
日期:`2026-04-20`
依据来源:
- [../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md](../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md)
- [../server-node/manifests/backend-capability-index.json](../server-node/manifests/backend-capability-index.json)
- [M0_CAPABILITY_SURFACE_BASELINE_2026-04-20.md](./M0_CAPABILITY_SURFACE_BASELINE_2026-04-20.md)
## 1. 文档目的
这份文档用于完成 `M0` 的第二条任务:
- 整理当前后端 `96` 条路由并生成一份“旧接口 -> 新实现”映射表
这里的“新实现”不是指最终文件路径,而是指第一阶段重写时每条旧接口在新架构中的落点:
1. 哪条 Axum 路由负责对外兼容
2. 哪层 application service 负责编排
3. 哪些状态进入 SpacetimeDB
4. 哪些二进制对象进入 OSS
## 2. 映射代码说明
为避免 `96` 条路由的映射表过长,本表使用以下“新实现归属代码”:
| 代码 | 新实现归属 |
| --- | --- |
| `A-HEALTH` | `Axum health route` |
| `A-AUTH` | `Axum auth routes + auth-service + SpacetimeDB auth tables` |
| `A-EDITOR` | `历史 Node editor 路由(遗留保留,不迁移到 server-rs` |
| `A-OSS` | `Axum assets routes + application::assets + oss-service + SpacetimeDB asset metadata` |
| `A-LLM` | `Axum llm proxy/service` |
| `A-RUNTIME` | `Axum runtime facade + SpacetimeDB runtime reducers/views` |
| `A-STORY` | `Axum story facade + SpacetimeDB gameplay reducers/views` |
| `A-CHAT` | `Axum SSE facade + llm-service + SpacetimeDB story/npc state` |
| `A-CW` | `Axum custom-world facade + llm-service + SpacetimeDB custom_world reducers/views` |
| `A-AGENT` | `Axum custom-world-agent facade + llm-service + oss-service + SpacetimeDB agent tables` |
补充说明:
1. 第一阶段默认保留旧路径,不主动改前端请求地址。
2. 兼容路径与主路径在新后端中应尽量共用同一 handler。
3. 所有 `stream` 接口第一阶段继续用 Axum SSE不强推改成 WebSocket。
4.`2026-04-21` 起,`editor` 路由仅保留历史对照,不纳入本轮 Rust 重写范围。
## 3. 总量校验
| 项目 | 数量 |
| --- | --- |
| 挂载面 | `6` |
| 总路由数 | `96` |
| `assets` | `14` |
| `auth` | `17` |
| `editor` | `3` |
| `runtime-main` | `59` |
| `runtime-story-action` | `2` |
| `health` | `1` |
补充说明:
1. 上表总量仍然是当前 Node 后端历史基线。
2. 其中 `editor``3` 条路由继续计入历史对照,但不计入本轮 `server-rs` active rewrite target。
## 4. `assets` 路由映射14 条)
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `assets.characterAnimationGenerate` | `POST /api/assets/character-animation/generate` | `A-OSS` | 保留原路径Axum 创建 `asset_job`,外部生成结果写 OSS任务状态进 SpacetimeDB。 |
| `assets.characterAnimationImportVideo` | `POST /api/assets/character-animation/import-video` | `A-OSS` | 保留原路径;视频参考素材由 Axum 上传 OSS并写任务/对象元数据。 |
| `assets.characterAnimationJobGet` | `GET /api/assets/character-animation/jobs/:taskId` | `A-OSS` | 保留原路径;查询改读 SpacetimeDB `asset_job view`。 |
| `assets.characterAnimationPublish` | `POST /api/assets/character-animation/publish` | `A-OSS` | 保留原路径;发布动作改为“绑定 OSS 对象到业务实体 + 回写元数据”。 |
| `assets.characterAnimationTemplatesList` | `GET /api/assets/character-animation/templates` | `A-OSS` | 保留原路径;模板清单先由 Axum 提供,后续再视情况对象化。 |
| `assets.characterVisualGenerate` | `POST /api/assets/character-visual/generate` | `A-OSS` | 保留原路径;角色主形象候选生成改为 Axum 编排 + OSS 入库。 |
| `assets.characterVisualJobGet` | `GET /api/assets/character-visual/jobs/:taskId` | `A-OSS` | 保留原路径;任务状态改读 SpacetimeDB。 |
| `assets.characterVisualPublish` | `POST /api/assets/character-visual/publish` | `A-OSS` | 保留原路径;发布改为对象绑定,不再依赖本地 `public/generated-*` 真相。 |
| `assets.characterWorkflowCacheSave` | `POST /api/assets/character-workflow-cache` | `A-OSS` | 保留原路径;工作流缓存改写 OSS/对象存储,索引进 SpacetimeDB。 |
| `assets.characterWorkflowCacheGet` | `GET /api/assets/character-workflow-cache/:characterId` | `A-OSS` | 保留原路径;按角色查缓存索引,再返回对象内容或签名 URL。 |
| `assets.qwenSpriteFrameRepairGenerate` | `POST /api/assets/qwen-sprite/frame-repair` | `A-OSS` | 保留原路径Qwen 修帧结果统一入 OSS状态进 SpacetimeDB。 |
| `assets.qwenSpriteMasterGenerate` | `POST /api/assets/qwen-sprite/master` | `A-OSS` | 保留原路径;主图生成改为 Axum 编排。 |
| `assets.qwenSpriteAssetSave` | `POST /api/assets/qwen-sprite/save` | `A-OSS` | 保留原路径;保存动作改为持久化对象元数据与引用关系。 |
| `assets.qwenSpriteSheetGenerate` | `POST /api/assets/qwen-sprite/sheet` | `A-OSS` | 保留原路径;整表生成链路保留,底层切换为 OSS + SpacetimeDB。 |
## 5. `auth` 路由映射17 条)
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `auth.auditLogs` | `GET /api/auth/audit-logs` | `A-AUTH` | 保留原路径;从 SpacetimeDB `auth_audit_log view` 返回。 |
| `auth.entry` | `POST /api/auth/entry` | `A-AUTH` | 保留原路径;密码登录与自动注册由 Axum 完成,再写 auth 表。 |
| `auth.loginOptions` | `GET /api/auth/login-options` | `A-AUTH` | 保留原路径;由 Axum 直接返回登录方式配置。 |
| `auth.logout` | `POST /api/auth/logout` | `A-AUTH` | 保留原路径Axum 吊销 refresh session 并清理 cookie。 |
| `auth.logoutAll` | `POST /api/auth/logout-all` | `A-AUTH` | 保留原路径;批量吊销用户全部 session。 |
| `auth.me` | `GET /api/auth/me` | `A-AUTH` | 保留原路径;由 Axum 校验 JWT 后查询用户读模型。 |
| `auth.phoneChange` | `POST /api/auth/phone/change` | `A-AUTH` | 保留原路径;短信校验在 Axum绑定结果写 SpacetimeDB。 |
| `auth.phoneLogin` | `POST /api/auth/phone/login` | `A-AUTH` | 保留原路径;验证码校验成功后创建/恢复账号与 session。 |
| `auth.phoneSendCode` | `POST /api/auth/phone/send-code` | `A-AUTH` | 保留原路径;阿里云短信发送适配收口到 Axum。 |
| `auth.refresh` | `POST /api/auth/refresh` | `A-AUTH` | 保留原路径;沿用 refresh cookie -> access token 刷新模型。 |
| `auth.riskBlocks` | `GET /api/auth/risk-blocks` | `A-AUTH` | 保留原路径;改读风控封禁表/视图。 |
| `auth.riskBlocksLift` | `POST /api/auth/risk-blocks/:scopeType/lift` | `A-AUTH` | 保留原路径;解除请求由 Axum 执行校验并写状态。 |
| `auth.sessions` | `GET /api/auth/sessions` | `A-AUTH` | 保留原路径;会话列表改读 SpacetimeDB `refresh_session view`。 |
| `auth.sessionRevoke` | `POST /api/auth/sessions/:sessionId/revoke` | `A-AUTH` | 保留原路径;会话吊销改写 `refresh_session` 状态。 |
| `auth.wechatBindPhone` | `POST /api/auth/wechat/bind-phone` | `A-AUTH` | 保留原路径;微信身份补绑手机号逻辑迁到 Axum。 |
| `auth.wechatCallback` | `GET /api/auth/wechat/callback` | `A-AUTH` | 保留原路径与 redirect 语义;微信 code 交换由 Axum 处理。 |
| `auth.wechatStart` | `GET /api/auth/wechat/start` | `A-AUTH` | 保留原路径;授权 URL 由 Axum 按设备场景生成。 |
## 6. `editor` 路由映射3 条,历史遗留)
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `editor.catalogItems` | `GET /api/editor/catalog/items` | `A-EDITOR` | 保留在 `server-node/` 遗留链路,仅作为历史对照;不迁移到 `server-rs`。 |
| `editor.resourceRead` | `GET /api/editor/json/:resourceId` | `A-EDITOR` | 保留在 `server-node/` 遗留链路,仅作为历史对照;不迁移到 `server-rs`。 |
| `editor.resourceWrite` | `POST /api/editor/json/:resourceId` | `A-EDITOR` | 保留在 `server-node/` 遗留链路,仅作为历史对照;不迁移到 `server-rs`。 |
## 7. `runtime-main` 路由映射59 条)
### 7.1 custom world 资源与实体生成
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `runtime.customWorldCoverImage` | `POST /api/custom-world/cover-image` | `A-CW` | 保留原路径;封面图生成由 Axum 编排,产物进 OSS绑定关系进 SpacetimeDB。 |
| `runtime.customWorldCoverUpload` | `POST /api/custom-world/cover-upload` | `A-CW` | 保留原路径;上传改为 OSS 直传或 Axum 中转上传。 |
| `runtime.customWorldEntity.primary` | `POST /api/custom-world/entity` | `A-CW` | 保留原路径;实体生成由 Axum 调 LLM再写 custom world 表。 |
| `runtime.customWorldSceneImage` | `POST /api/custom-world/scene-image` | `A-CW` | 保留原路径;场景图生成由 Axum + OSS 完成。 |
| `runtime.customWorldSceneNpc.primary` | `POST /api/custom-world/scene-npc` | `A-CW` | 保留原路径;场景 NPC 生成结果写 custom world / npc 相关表。 |
### 7.2 LLM 透传
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `runtime.llmChatCompletionsProxy` | `POST /api/llm/chat/completions` | `A-LLM` | 保留原路径;继续由 Axum 承接代理,不进入 SpacetimeDB。 |
### 7.3 profile 主路径
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `runtime.profileBrowseHistoryDelete.primary` | `DELETE /api/profile/browse-history` | `A-RUNTIME` | 保留原路径;清理动作改为 reducer 写 `user_browse_history`。 |
| `runtime.profileBrowseHistoryGet.primary` | `GET /api/profile/browse-history` | `A-RUNTIME` | 保留原路径;历史记录改读 browse history view。 |
| `runtime.profileBrowseHistoryPost.primary` | `POST /api/profile/browse-history` | `A-RUNTIME` | 保留原路径;批量写入改为 Axum -> reducer。 |
| `runtime.profileDashboard.primary` | `GET /api/profile/dashboard` | `A-RUNTIME` | 保留原路径;个人主页汇总改读 dashboard view。 |
| `runtime.profilePlayStats.primary` | `GET /api/profile/play-stats` | `A-RUNTIME` | 保留原路径;统计数据改读 projection。 |
| `runtime.profileSaveArchivesList.primary` | `GET /api/profile/save-archives` | `A-RUNTIME` | 保留原路径;存档摘要改读 save archive view。 |
| `runtime.profileSaveArchivesResume.primary` | `POST /api/profile/save-archives/:worldKey` | `A-RUNTIME` | 保留原路径;恢复动作改读 `profile_save_archive` 后重建兼容快照。 |
| `runtime.profileWalletLedger.primary` | `GET /api/profile/wallet-ledger` | `A-RUNTIME` | 保留原路径;资产流水改读 ledger view。 |
### 7.4 runtime 聊天与流式对话
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `runtime.characterReplyStream` | `POST /api/runtime/chat/character/reply/stream` | `A-CHAT` | 保留 SSE contractAxum 流式产出,状态写 story/session 表。 |
| `runtime.characterSuggestions` | `POST /api/runtime/chat/character/suggestions` | `A-CHAT` | 保留原路径;由 Axum 生成建议语并按需写会话状态。 |
| `runtime.characterSummary` | `POST /api/runtime/chat/character/summary` | `A-CHAT` | 保留原路径;摘要生成留在 Axum摘要索引可回写 SpacetimeDB。 |
| `runtime.npcDialogueStream` | `POST /api/runtime/chat/npc/dialogue/stream` | `A-CHAT` | 保留 SSE contractNPC 对话状态迁到 SpacetimeDB。 |
| `runtime.npcRecruitStream` | `POST /api/runtime/chat/npc/recruit/stream` | `A-CHAT` | 保留 SSE contract招募对话与状态变化统一进入新状态层。 |
| `runtime.npcTurnStream` | `POST /api/runtime/chat/npc/turn/stream` | `A-CHAT` | 保留 SSE contract单回合发言的判定与状态回写统一收口。 |
### 7.5 custom world gallery / library / sessions / works
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `runtime.customWorldGalleryList` | `GET /api/runtime/custom-world-gallery` | `A-CW` | 保留原路径;公开画廊改读 `custom_world_gallery view`。 |
| `runtime.customWorldGalleryDetail` | `GET /api/runtime/custom-world-gallery/:ownerUserId/:profileId` | `A-CW` | 保留原路径;详情改读 gallery detail view。 |
| `runtime.customWorldLibraryList` | `GET /api/runtime/custom-world-library` | `A-CW` | 保留原路径;资料库改读用户 custom world view。 |
| `runtime.customWorldLibraryDelete` | `DELETE /api/runtime/custom-world-library/:profileId` | `A-CW` | 保留原路径;删除改为 reducer 或软删除标记。 |
| `runtime.customWorldLibraryUpsert` | `PUT /api/runtime/custom-world-library/:profileId` | `A-CW` | 保留原路径;写入改为 Axum facade + SpacetimeDB profile tables。 |
| `runtime.customWorldLibraryPublish` | `POST /api/runtime/custom-world-library/:profileId/publish` | `A-CW` | 保留原路径;发布改为状态切换与画廊投影刷新。 |
| `runtime.customWorldLibraryUnpublish` | `POST /api/runtime/custom-world-library/:profileId/unpublish` | `A-CW` | 保留原路径;撤回发布改为状态切换。 |
| `runtime.customWorldSessionCreate` | `POST /api/runtime/custom-world/sessions` | `A-CW` | 保留原路径;传统问答会话状态迁到 SpacetimeDB。 |
| `runtime.customWorldSessionGet` | `GET /api/runtime/custom-world/sessions/:sessionId` | `A-CW` | 保留原路径;读取传统问答会话改读 view。 |
| `runtime.customWorldSessionAnswer` | `POST /api/runtime/custom-world/sessions/:sessionId/answers` | `A-CW` | 保留原路径;回答动作改为 reducer 写会话状态。 |
| `runtime.customWorldSessionGenerateStream` | `GET /api/runtime/custom-world/sessions/:sessionId/generate/stream` | `A-CW` | 保留 SSE contract编译过程由 Axum 流式回推并回写状态。 |
| `runtime.customWorldWorksList` | `GET /api/runtime/custom-world/works` | `A-CW` | 保留原路径;作品汇总改读 custom world work summary view。 |
### 7.6 custom world agent
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `runtime.customWorldAgentCreateSession` | `POST /api/runtime/custom-world/agent/sessions` | `A-AGENT` | 保留原路径Agent 会话创建改写 `custom_world_agent_session`。 |
| `runtime.customWorldAgentGetSession` | `GET /api/runtime/custom-world/agent/sessions/:sessionId` | `A-AGENT` | 保留原路径;会话快照改读 Agent session view。 |
| `runtime.customWorldAgentExecuteAction` | `POST /api/runtime/custom-world/agent/sessions/:sessionId/actions` | `A-AGENT` | 保留原路径;动作编排由 Axum 执行,状态与操作记录进 SpacetimeDB。 |
| `runtime.customWorldAgentGetCardDetail` | `GET /api/runtime/custom-world/agent/sessions/:sessionId/cards/:cardId` | `A-AGENT` | 保留原路径;卡片详情改读 `custom_world_draft_card`。 |
| `runtime.customWorldAgentSendMessage` | `POST /api/runtime/custom-world/agent/sessions/:sessionId/messages` | `A-AGENT` | 保留原路径;消息提交后由 Axum 触发编排,消息与操作状态入库。 |
| `runtime.customWorldAgentStreamMessage` | `POST /api/runtime/custom-world/agent/sessions/:sessionId/messages/stream` | `A-AGENT` | 保留 SSE contract流式消息由 Axum 输出Agent 状态表持续更新。 |
| `runtime.customWorldAgentGetOperation` | `GET /api/runtime/custom-world/agent/sessions/:sessionId/operations/:operationId` | `A-AGENT` | 保留原路径;操作状态改读 `custom_world_agent_operation view`。 |
### 7.7 compat 路径
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `runtime.customWorldEntity.compat` | `POST /api/runtime/custom-world/entity` | `A-CW` | 保留兼容路径;与主路径共用同一 handler。 |
| `runtime.customWorldSceneNpc.compat` | `POST /api/runtime/custom-world/scene-npc` | `A-CW` | 保留兼容路径;与主路径共用同一 handler。 |
| `runtime.profileBrowseHistoryDelete.compat` | `DELETE /api/runtime/profile/browse-history` | `A-RUNTIME` | 保留兼容路径;与 `/api/profile/browse-history` 共用实现。 |
| `runtime.profileBrowseHistoryGet.compat` | `GET /api/runtime/profile/browse-history` | `A-RUNTIME` | 保留兼容路径;共用 browse history facade。 |
| `runtime.profileBrowseHistoryPost.compat` | `POST /api/runtime/profile/browse-history` | `A-RUNTIME` | 保留兼容路径;共用写入逻辑。 |
| `runtime.profileDashboard.compat` | `GET /api/runtime/profile/dashboard` | `A-RUNTIME` | 保留兼容路径;共用 dashboard facade。 |
| `runtime.profilePlayStats.compat` | `GET /api/runtime/profile/play-stats` | `A-RUNTIME` | 保留兼容路径;共用 play stats facade。 |
| `runtime.profileSaveArchivesList.compat` | `GET /api/runtime/profile/save-archives` | `A-RUNTIME` | 保留兼容路径;共用 save archives list facade。 |
| `runtime.profileSaveArchivesResume.compat` | `POST /api/runtime/profile/save-archives/:worldKey` | `A-RUNTIME` | 保留兼容路径;共用 resume facade。 |
| `runtime.profileWalletLedger.compat` | `GET /api/runtime/profile/wallet-ledger` | `A-RUNTIME` | 保留兼容路径;共用 wallet ledger facade。 |
### 7.8 runtime 其他核心接口
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `runtime.itemsIntent` | `POST /api/runtime/items/runtime-intent` | `A-CW` | 保留原路径Axum 调 LLM 生成意图,物品领域状态与引用写 SpacetimeDB。 |
| `runtime.questsGenerate` | `POST /api/runtime/quests/generate` | `A-CW` | 保留原路径;任务候选生成由 Axum 编排,结果写 quest 相关表。 |
| `runtime.snapshotDelete` | `DELETE /api/runtime/save/snapshot` | `A-RUNTIME` | 保留原路径;删除动作改为更新 `runtime_snapshot` / archive。 |
| `runtime.snapshotGet` | `GET /api/runtime/save/snapshot` | `A-RUNTIME` | 保留原路径;读取兼容聚合快照,由 view/projection 输出。 |
| `runtime.snapshotPut` | `PUT /api/runtime/save/snapshot` | `A-RUNTIME` | 保留原路径;写入由 Axum facade + reducer 完成。 |
| `runtime.settingsGet` | `GET /api/runtime/settings` | `A-RUNTIME` | 保留原路径;设置改读 `runtime_setting view`。 |
| `runtime.settingsPut` | `PUT /api/runtime/settings` | `A-RUNTIME` | 保留原路径;设置更新改为 reducer。 |
| `runtime.storyContinue` | `POST /api/runtime/story/continue` | `A-STORY` | 保留原路径;故事推进由 Axum 调新 story/application 层。 |
| `runtime.storyInitial` | `POST /api/runtime/story/initial` | `A-STORY` | 保留原路径;首段故事生成保持 REST 兼容。 |
| `runtime.wsHealth` | `GET /api/ws/health` | `A-RUNTIME` | 保留原路径;继续作为实时链路占位健康检查。 |
## 8. `runtime-story-action` 路由映射2 条)
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `storyAction.resolve` | `POST /api/runtime/story/actions/resolve` | `A-STORY` | 保留原路径Axum 接收动作请求SpacetimeDB reducer 执行跨模块结算。 |
| `storyAction.stateGet` | `GET /api/runtime/story/state/:sessionId` | `A-STORY` | 保留原路径;读取 story session 兼容状态 view。 |
## 9. `health` 路由映射1 条)
| 旧路由 ID | 方法/路径 | 新实现归属 | 第一阶段迁移策略 |
| --- | --- | --- | --- |
| `health.check` | `GET /healthz` | `A-HEALTH` | 保留原路径与最小返回结构。 |
## 10. 迁移落地规则
后续做路由树时,必须遵守:
1. 旧路径优先保留,新实现从内部切换,不先要求前端改地址。
2. 主路径与兼容路径必须共用同一 application service避免再次出现双份逻辑。
3. `stream` 接口第一阶段默认沿用 SSE。
4. `assets``custom-world` 里的生成类接口,外部副作用统一在 Axum状态与任务统一进 SpacetimeDB。
5. `storyAction.resolve``runtime.snapshotPut``auth.refresh` 属于最优先回归接口,后续开发必须优先补完整测试。
6. `editor` 相关旧路径只保留历史基线记录,不纳入 `server-rs` 路由树实施范围。
## 11. 本任务完成定义
当以下条件成立时,这条任务视为完成:
1. `96` 条旧路由都已经有新实现落点。
2. 每条路由至少明确:
- 旧方法/路径
- 新实现归属
- 第一阶段迁移策略
3. 后续搭建 Axum 路由树与 application service 时,可以直接按这份矩阵逐项落位。

View File

@@ -1,300 +0,0 @@
# M0SSE 接口与事件格式冻结基线
日期:`2026-04-20`
依据来源:
- [../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md](../docs/technical/NODE_BACKEND_MODULE_AND_API_INDEX.md)
- [../server-node/manifests/backend-capability-index.json](../server-node/manifests/backend-capability-index.json)
- `server-node/src/http.ts`
- `server-node/src/routes/runtimeRoutes.ts`
- `server-node/src/routes/customWorldAgent.ts`
- `server-node/src/modules/ai/chatOrchestrator.ts`
- `server-node/src/services/customWorldAgentOrchestrator.ts`
- `server-node/src/modules/ai/customWorldOrchestrator.ts`
## 1. 文档目的
这份文档用于完成 `M0` 的第四条任务:
- 整理当前所有 SSE 接口与事件格式
这里的“整理”不是只记住有几条 `stream` 路由,而是要求后续 Axum 重写必须先冻结:
1. 当前到底有哪几条 SSE 路由。
2. 每条路由是“透传上游流”还是“项目自定义事件流”。
3. 每条路由的事件名、结束标记、错误帧和头部约束是什么。
4. 哪些流的 `payload` 是增量文本,哪些其实是“累计文本”。
## 2. 冻结结论
当前 Node 后端正式登记的 SSE 接口固定为以下 `6` 条:
| 路由 ID | 方法/路径 | 当前实现入口 | 协议类型 | 成功结束标记 | 鉴权 |
| --- | --- | --- | --- | --- | --- |
| `runtime.characterReplyStream` | `POST /api/runtime/chat/character/reply/stream` | `runtimeRoutes.ts -> streamCharacterChatReplyFromOrchestrator` | 上游透传 SSE | 上游 `data: [DONE]` | JWT |
| `runtime.npcDialogueStream` | `POST /api/runtime/chat/npc/dialogue/stream` | `runtimeRoutes.ts -> streamNpcChatDialogueFromOrchestrator` | 上游透传 SSE | 上游 `data: [DONE]` | JWT |
| `runtime.npcRecruitStream` | `POST /api/runtime/chat/npc/recruit/stream` | `runtimeRoutes.ts -> streamNpcRecruitDialogueFromOrchestrator` | 上游透传 SSE | 上游 `data: [DONE]` | JWT |
| `runtime.npcTurnStream` | `POST /api/runtime/chat/npc/turn/stream` | `runtimeRoutes.ts -> streamNpcChatTurnFromOrchestrator` | 项目自定义 SSE | `event: complete` 后追加 `data: [DONE]` | JWT |
| `runtime.customWorldSessionGenerateStream` | `GET /api/runtime/custom-world/sessions/:sessionId/generate/stream` | `runtimeRoutes.ts` 内联实现 | 项目自定义 SSE | `event: done`,无 `[DONE]` | JWT |
| `runtime.customWorldAgentStreamMessage` | `POST /api/runtime/custom-world/agent/sessions/:sessionId/messages/stream` | `customWorldAgent.ts -> customWorldAgentOrchestrator.streamMessage` | 项目自定义 SSE | `event: done`,无 `[DONE]` | JWT |
冻结总数:
1. SSE 接口:`6`
2. 上游透传型:`3`
3. 本地自定义事件流:`3`
## 3. 全部 SSE 接口共享的响应头约束
当前所有项目内主动准备 SSE 响应的接口,都经过 `prepareEventStreamResponse(...)`,因此至少冻结以下头部行为:
| 响应头 | 当前值/规则 | 说明 |
| --- | --- | --- |
| `Content-Type` | 默认 `text/event-stream; charset=utf-8` | 透传型接口可被上游 `content-type` 覆盖,但仍保持 SSE。 |
| `Cache-Control` | `no-cache` | 禁止中间层缓存流式结果。 |
| `Connection` | `keep-alive` | 保持 SSE 长连接。 |
| `X-Accel-Buffering` | `no` | 禁止代理层缓冲。 |
| `x-request-id` | 透传当前请求 ID | 所有 SSE 都要带请求追踪头。 |
| `x-api-version` | 当前 API 版本号 | 与普通 JSON 接口一致。 |
| `x-route-version` | 当前路由版本号 | 与普通 JSON 接口一致。 |
| `x-response-time-ms` | 当前已耗时毫秒数 | 在准备响应头时写入。 |
额外冻结约束:
1. `SSE` 接口当前也保留普通 API 元数据头,不能因为换成 Axum 就丢掉。
2.`6` 条流式接口都在 `requireAuth` 之后注册,因此第一阶段默认仍需要 `Bearer JWT`
## 4. 协议分型
### 4.1 上游透传型 SSE3 条)
包含:
1. `POST /api/runtime/chat/character/reply/stream`
2. `POST /api/runtime/chat/npc/dialogue/stream`
3. `POST /api/runtime/chat/npc/recruit/stream`
当前实现特征:
1. 路由不自己重写事件名,直接把上游模型返回的 SSE 原样管道转发给前端。
2. 本地只负责:
- 发起上游流式请求
- 准备 SSE 头部
- 处理中断时的请求 abort
3.`llmClient.streamMessageContent(...)` 的解析逻辑可以反推,当前上游 SSE 采用 OpenAI 风格:
- 多个 `data: {...}` JSON chunk
- 最终 `data: [DONE]`
冻结要求:
1. 第一阶段 Axum 仍要保持这三条接口的“上游透传”语义。
2. 不要在未发版变更协议前,擅自把它们改成项目自定义 `event: reply_delta` 格式。
### 4.2 项目自定义 SSE3 条)
包含:
1. `POST /api/runtime/chat/npc/turn/stream`
2. `GET /api/runtime/custom-world/sessions/:sessionId/generate/stream`
3. `POST /api/runtime/custom-world/agent/sessions/:sessionId/messages/stream`
当前实现特征:
1. 路由或 orchestrator 自己写 `event:``data:`
2. 事件名不是上游协议,而是项目本地约定。
3. 这三条流的结束方式并不一致,必须分别兼容。
## 5. 各接口事件格式冻结
### 5.1 `runtime.characterReplyStream`
路径:
- `POST /api/runtime/chat/character/reply/stream`
冻结格式:
1. 当前为上游透传流。
2. 本地不保证固定 `event` 名。
3. 前端实际收到的是上游 `data: {...}` chunk 与最终 `data: [DONE]`
4. 失败时当前实现也不是本地 `event: error`,而是由上游失败或 Express 错误链决定。
### 5.2 `runtime.npcDialogueStream`
路径:
- `POST /api/runtime/chat/npc/dialogue/stream`
冻结格式:
1. 当前为上游透传流。
2. 协议特征与 `runtime.characterReplyStream` 相同。
3. 第一阶段不能私自改成项目自定义事件名。
### 5.3 `runtime.npcRecruitStream`
路径:
- `POST /api/runtime/chat/npc/recruit/stream`
冻结格式:
1. 当前为上游透传流。
2. 协议特征与前两条透传 SSE 相同。
3. 结束标记仍依赖上游 `data: [DONE]`
### 5.4 `runtime.npcTurnStream`
路径:
- `POST /api/runtime/chat/npc/turn/stream`
成功事件序列:
1. `event: reply_delta`
2. `event: reply_delta`
3. `...`
4. `event: complete`
5. `data: [DONE]`
错误事件:
1. `event: error`
2. `data: {"message":"..."}`
3. 之后直接 `response.end()`,不会再补 `complete`
冻结 payload 规则:
| 事件名 | payload 结构 | 关键说明 |
| --- | --- | --- |
| `reply_delta` | `{ "text": string }` | `text` 实际是“累计文本”,不是单 token 增量。 |
| `complete` | `{ "npcReply": string, "affinityDelta": number, "affinityText": string, "suggestions": string[], "pendingQuestOffer": object \| null, "chatDirective": object \| null }` | 最终一次性返回业务结算数据。 |
| `error` | `{ "message": string }` | 仅错误消息,无额外状态。 |
补充冻结点:
1. `reply_delta.text` 每次都是当前累计回复全文。
2. `complete.suggestions` 在强制收束场景下可能是空数组。
3. `complete.chatDirective` 当前至少可能包含:
- `turnLimit`
- `remainingTurns`
- `forceExit`
- `closingMode`
4. `complete.pendingQuestOffer` 当前可能包含:
- `quest`
- `introText`
### 5.5 `runtime.customWorldSessionGenerateStream`
路径:
- `GET /api/runtime/custom-world/sessions/:sessionId/generate/stream`
成功事件序列:
1. `event: progress`payload`{ "phase": "preparing", "progress": 10 }`
2. `event: progress`payload`{ "phase": "requesting_llm", "progress": 45 }`
3. `event: progress`payload`CustomWorldGenerationProgress`
4. `...`
5. `event: progress`payload`{ "phase": "completed", "progress": 100 }`
6. `event: result`
7. `event: done`
错误事件:
1. `event: error`
2. `data: {"message":"..."}`
3. 之后直接结束,不会再发 `done`
冻结 payload 规则:
| 事件名 | payload 结构 | 关键说明 |
| --- | --- | --- |
| `progress` | 兼容两种结构 | 这是当前最容易踩坑的混合协议。 |
| `result` | `{ "profile": object }` | 返回完整世界 profile。 |
| `done` | `{ "ok": true }` | 当前没有 `[DONE]` 字符串终止帧。 |
| `error` | `{ "message": string }` | 当前也没有额外错误码。 |
`progress` 事件的两种冻结结构:
1. 启动/收尾帧:
- `{ "phase": "preparing", "progress": 10 }`
- `{ "phase": "requesting_llm", "progress": 45 }`
- `{ "phase": "completed", "progress": 100 }`
2. 编排器进度帧 `CustomWorldGenerationProgress`
- `phaseId`
- `phaseLabel`
- `phaseDetail`
- `overallProgress`
- `completedWeight`
- `totalWeight`
- `elapsedMs`
- `estimatedRemainingMs`
- `activeStepIndex`
- `steps`
补充冻结点:
1. 当前 `progress` 不是单一 schema而是混合 schema。
2. 当前实现会在客户端断开时触发 `AbortController`,这条流具备显式中断处理。
### 5.6 `runtime.customWorldAgentStreamMessage`
路径:
- `POST /api/runtime/custom-world/agent/sessions/:sessionId/messages/stream`
成功事件序列:
1. `event: reply_delta`
2. `event: reply_delta`
3. `...`
4. `event: session`
5. `event: done`
错误事件:
1. `event: error`
2. `data: {"message":"..."}`
3. 之后直接结束,不会再补 `done`
冻结 payload 规则:
| 事件名 | payload 结构 | 关键说明 |
| --- | --- | --- |
| `reply_delta` | `{ "text": string }` | 当前也是累计文本,不是 diff patch。 |
| `session` | `{ "session": CustomWorldAgentSessionSnapshot }` | 完整会话快照一次性回推。 |
| `done` | `{ "ok": true }` | 当前没有 `[DONE]`。 |
| `error` | `{ "message": string }` | 仅错误消息。 |
补充冻结点:
1. 这条流当前不会在成功结尾补发最终文本帧,只会发 `session` 快照。
2. `reply_delta.text` 同样是“到当前为止的完整回复”。
3. 当前实现没有像 `customWorldSessionGenerateStream` 那样显式挂请求断开 abort。
## 6. 第一阶段 Axum 重写必须兼容的硬约束
后续重写中,不允许出现以下情况:
1. 把当前 `6` 条 SSE 路由减少、合并或改掉方法类型。
2. 把透传型 `3` 条流直接改写成自定义事件名,而前端却不知情。
3.`npcTurnStream``reply_delta` 从“累计文本”改成“真正 delta”导致前端拼接方式失效。
4.`customWorldSessionGenerateStream` 的混合 `progress` schema 静默改成新格式,却没有版本门禁。
5.`customWorldAgentStreamMessage``session` 终帧改成局部 patch而前端仍按完整快照消费。
6. 丢失 `x-request-id``x-api-version``x-route-version``x-response-time-ms` 等当前前端与联调用到的头。
## 7. 本任务完成定义
当以下条件成立时,这条任务视为完成:
1. 当前 `6` 条 SSE 接口已经有书面冻结清单。
2. 每条 SSE 都已明确:
- 方法与路径
- 协议类型
- 事件名
- 成功结束标记
- 错误事件
- 关键 payload 结构
3. 后续 Axum SSE 落地、前端 contract 回归、SpacetimeDB 实时链路设计时,可以直接引用本文,不再靠人工回忆事件名。

View File

@@ -1,36 +0,0 @@
# 后端重写任务清单目录
日期:`2026-04-20`
本目录用于集中存放 `SpacetimeDB + Axum + 阿里云 OSS` 后端重写相关任务清单。
## 文件结构
- [00_MASTER_TASKLIST.md](./00_MASTER_TASKLIST.md):总纲主清单,保留完整阶段结构与最终验收项。
- [01_M0_M2_FOUNDATION_AND_AUTH.md](./01_M0_M2_FOUNDATION_AND_AUTH.md)能力冻结、Rust 工作区、Axum 基础设施、鉴权与会话迁移任务。
- [02_M3_RUNTIME_PROFILE.md](./02_M3_RUNTIME_PROFILE.md)runtime snapshot / settings / profile 迁移任务。
- [03_M4_STORY_AND_GAMEPLAY.md](./03_M4_STORY_AND_GAMEPLAY.md)story action 主循环与 gameplay reducer 迁移任务。
- [04_M5_CUSTOM_WORLD_AND_AGENT.md](./04_M5_CUSTOM_WORLD_AND_AGENT.md)custom world / gallery / agent 主链迁移任务。
- [05_M6_ASSETS_OSS_EDITOR.md](./05_M6_ASSETS_OSS_EDITOR.md)assets / 阿里云 OSS 迁移任务;`editor` 已于 `2026-04-21` 退出本轮重写范围。
- [06_M7_TEST_DEPLOY_CUTOVER.md](./06_M7_TEST_DEPLOY_CUTOVER.md):联调、回归、部署、观测与切流任务。
- [07_CROSS_CUTTING_AND_ACCEPTANCE.md](./07_CROSS_CUTTING_AND_ACCEPTANCE.md):横向专项、执行顺序与最终验收清单。
- [M0_CAPABILITY_SURFACE_BASELINE_2026-04-20.md](./M0_CAPABILITY_SURFACE_BASELINE_2026-04-20.md):当前 Node 后端 `6` 个挂载面的冻结基线,用于后续接口映射、模块迁移与验收对照。
- [M0_ROUTE_MIGRATION_MATRIX_2026-04-20.md](./M0_ROUTE_MIGRATION_MATRIX_2026-04-20.md):当前 `96` 条后端路由的“旧接口 -> 新实现”迁移矩阵,用于 Axum 路由树和 application service 落位。
- [M0_MODULE_MIGRATION_BASELINE_2026-04-20.md](./M0_MODULE_MIGRATION_BASELINE_2026-04-20.md):当前 `12` 个内部模块的迁移归属基线,用于锁定 Rust crate、SpacetimeDB bounded context 与 Axum/application 分工。
- [M0_SSE_INTERFACE_BASELINE_2026-04-20.md](./M0_SSE_INTERFACE_BASELINE_2026-04-20.md):当前 `6` 条 SSE 接口及其事件格式冻结基线,用于 Axum SSE 兼容和前端 contract 回归。
- [M0_GENERATED_STATIC_PREFIX_BASELINE_2026-04-20.md](./M0_GENERATED_STATIC_PREFIX_BASELINE_2026-04-20.md):当前正式 `/generated-*` 静态资源前缀冻结基线,用于 Axum 静态资源兼容层与 OSS 对象键规划。
- [M0_FRONTEND_RESPONSE_CONTRACT_BASELINE_2026-04-20.md](./M0_FRONTEND_RESPONSE_CONTRACT_BASELINE_2026-04-20.md)当前前端直接依赖的响应头、envelope 与错误格式冻结基线,用于 Axum 中间件与错误响应兼容。
- [M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md](./M0_REPOSITORY_BOUNDARY_DECISIONS_2026-04-20.md)`M0` 仓库边界决议文档,用于持续冻结 `server-rs/` 落位、迁移期双栈共存、Axum 边界与副作用收口原则。
- [M0_PHASE_ACCEPTANCE_MATRIX_2026-04-20.md](./M0_PHASE_ACCEPTANCE_MATRIX_2026-04-20.md)`M0 ~ M7` 阶段验收矩阵,用于固定每阶段的入口条件、核心交付、退出条件与跨阶段回归焦点。
## 当前 M4 / M5 结构基线
- `M4` 当前涉及的前后端脚本结构、命名根、route/service/compiler/repository 落位,统一参照 [../docs/technical/RPG_ENTRY_RUNTIME_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md](../docs/technical/RPG_ENTRY_RUNTIME_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md)。
- `M5` 当前涉及的创作入口、Agent session、result preview、works/library/gallery、publish 与 enter-world 主链,统一参照 [../docs/technical/CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md](../docs/technical/CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md)。
-`custom-world/sessions` 传统问答链已经退出当前仓库正式主链;后续若在 `M5` 中提及,只按历史兼容台账处理,不再作为当前功能扩展目标。
## 维护规则
1. 总纲与拆分文件都以本目录为唯一维护位置。
2. 总纲用于把控全局节奏,拆分文件用于实际逐项推进。
3. 如阶段任务发生明显变化,需要同步更新总纲与对应拆分文件。