# 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