Files
Genarrative/docs/audits/engineering/ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md
高物 ddcb5d5c8c
Some checks failed
CI / verify (push) Has been cancelled
Rework story engine flow and reorganize project docs
2026-04-06 23:19:00 +08:00

13 KiB
Raw Blame History

工程优化审查报告2026-03-29

说明

  • 扫描范围:src/scripts/docs/package.jsonvite.config.tstsconfig.json
  • 已执行校验:npm run lintnpm run buildnpm run check:content
  • 本报告只从工程角度讨论结构、边界、质量门禁、可维护性与可扩展性
  • 按仓库说明,暂不讨论中文乱码本身

当前结论

项目当前可构建、可运行、内容校验可通过,说明基础功能链路是通的;但从工程视角看,已经出现明显的“单点过重、边界混杂、质量门禁偏弱、编辑器与运行时耦合”问题。继续叠需求会越来越依赖人工记忆和局部经验,回归风险会持续上升。

当前最值得优先处理的不是单个 UI 细节,而是以下四个工程主题:

  1. 运行时主链路的职责拆分还不够,核心 hook / 组件已经过载
  2. 缺少真正的工程质量门禁,lint 目前本质上只是 tsc
  3. 编辑器、运行时、类后端能力都混在同一个 Vite 配置里
  4. 持久化、AI 调用、编辑器保存等基础设施仍然是“分散手写”

运行状态快照

  • npm run lint 通过
  • npm run build 通过
  • npm run check:content 通过
  • 应用代码下未发现测试文件:src/scripts/docs/ 内没有 *.test.* / *.spec.*
  • 构建产物已出现较大 chunk
  • dist/assets/App-*.js407 KB
  • dist/assets/itemCatalog-*.js414 KB
  • dist/assets/PresetEditor-*.js109 KB

代码体征

下列文件已经明显进入“超大模块”区间:

文件 行数 观察
src/hooks/useStoryGeneration.ts 3304 同时管理剧情、NPC 交互、交易、送礼、招募、任务、角色聊天、道具/锻造接入
src/components/PresetEditor.tsx 2244 多编辑器入口聚合在一个巨型组件中
src/hooks/useCombatFlow.ts 1791 同时承担战斗推演、动画时序、逃跑演出、状态落地
src/components/GameShell.tsx 1592 入口 UI、选角、世界选择、自定义世界、场景切换、浮层控制全部集中
src/types.ts 663 运行时、AI、编辑器、自定义世界、背包、任务类型集中在一个总文件

补充信号:

  • src/components/GameShell.tsx 内有 16 个 useState、10 个 useEffect、13 个 useMemo
  • src/hooks/useStoryGeneration.ts 虽然只有少量 React state但内部累计 40+ 个函数,已经是“巨型流程控制器”
  • src/hooks/useCombatFlow.ts 内有大量时间常量、动画常量、sleep + setGameState 过程式循环,测试成本很高

优先级问题

P0运行时主链路职责过度集中

证据:

  • src/hooks/useStoryGeneration.ts:868-930 进入 hook 后立即开始定义交易、送礼、招募、角色聊天等子流程
  • src/hooks/useStoryGeneration.ts:3191-3303 返回对象同时暴露剧情、任务、NPC UI、角色聊天 UI、背包/锻造 UI
  • src/components/GameShell.tsx:293-360 组件 props 很多,内部 state 也很多,承担“壳层 + 流程 + 浮层 + 自定义世界生成 + 场景切换”
  • src/hooks/useCombatFlow.ts:559-1787 将战斗计算和战斗演出揉在同一层里

影响:

  • 任何一个新需求都容易同时碰到剧情、UI、战斗、背包、NPC 关系四五条链路
  • 代码 review 很难聚焦,改动一处时往往需要脑内跟完整条大流程
  • 单元测试难写,因为逻辑不是纯函数,而是大量闭包 + 过程式状态推进
  • 长期会形成“只有熟悉历史上下文的人才能安全修改”的隐性门槛

建议:

  • useStoryGeneration 拆为“剧情推进”“NPC 交互”“角色聊天”“任务结算”“模态框控制”几个子域
  • useCombatFlow 拆成“纯战斗结算引擎”和“战斗播放适配层”
  • GameShell 回到壳层职责,只负责路由态、页面态、模态挂载与 props 编排
  • 以“领域职责”拆分,而不是按“文件太长了随便切一刀”拆分

P0缺少真正的工程质量门禁

证据:

  • package.json:11lint 实际只有 tsc --noEmit
  • package.json 中没有 testformatlint:fix 等基础脚本
  • 根目录未发现 .eslintrc*.prettierrc*.editorconfig
  • 代码目录下没有测试文件

影响:

  • 当前项目的“能过 lint”只代表类型没炸不代表风格一致、依赖正确、Hooks 规则正确、死代码已清理
  • 大型 hook / 大型组件的重构几乎没有自动回归保护
  • 运行时行为、编辑器行为、AI fallback 行为主要依赖人工回归

建议:

  • 补齐 ESLint、Prettier、EditorConfig至少覆盖 React Hooks、import、unused code、复杂度基线
  • 引入 Vitest先覆盖纯数据层与纯规则层
  • useCombatFlowstateFunctionsnpcInteractionsquestFlow 增加单元测试
  • 为“开局 -> 选世界 -> 选角色 -> 进入剧情 -> 战斗 -> 存档恢复”补最小 E2E smoke
  • CI 中至少串联:类型检查 + 单测 + build + 内容校验

P1编辑器、运行时、类后端能力全部耦合在 Vite 配置里

证据:

  • vite.config.ts:151-203 在 Vite 插件里实现了 LLM 代理
  • vite.config.ts:206-269 在 Vite 插件里实现了通用 JSON 文件读写 API
  • vite.config.ts:253 直接写回 src/data/*.json
  • vite.config.ts:265-266vite.config.ts:400-401preview 阶段也挂了这些接口
  • vite.config.ts:425-434 启动时默认把这些“编辑器后端能力”全部注册进去

影响:

  • 本地编辑器能力与运行时能力没有清晰边界
  • preview 环境仍可写源码文件,发布边界不清晰
  • 未来如果要做独立部署、多人协作、远程编辑、权限控制,会非常难迁移
  • Vite 配置同时扮演构建配置、代理层、文件服务层、编辑器后端,职责失衡

建议:

  • 将编辑器读写 API 从 vite.config.ts 抽到独立的本地工具服务或独立脚本
  • 至少区分 dev-only write apipreview/prod read-only api
  • 对编辑器保存接口建立统一客户端 SDK避免组件直接散落 fetch('/api/...')
  • LLM 代理也建议独立成 server/scripts/dev-server/,不要继续长在构建配置里

P1持久化策略分散且直接序列化大状态对象

证据:

  • src/hooks/useGamePersistence.ts:152-167 会在状态变化时自动把完整快照写入 localStorage
  • src/hooks/useGamePersistence.ts:157-163 快照包含 gameState + bottomTab + currentStory
  • src/hooks/useGamePersistence.ts:68-116 恢复逻辑已经开始承担大量 schema 纠偏职责
  • src/data/customWorldLibrary.ts:1-282 自定义世界库单独维护一套 localStorage 读写与 normalize
  • src/hooks/useGameSettings.ts 也单独维护一套本地设置持久化

影响:

  • 状态结构一旦继续膨胀,快照写入频率和反序列化成本都会增加
  • schema 迁移会越来越依赖手工 normalize 补丁
  • 不同持久化入口各写一套 parser / normalizer风格和鲁棒性难统一
  • 当前保存的是“运行中大对象”,而不是“稳定领域快照”,长期会放大兼容成本

建议:

  • 建立统一的 persistence 层,集中管理 key、version、migration、节流、序列化策略
  • GameState 做“可持久化切片”和“运行时临时切片”分层
  • 自动保存增加节流/去抖,避免每次状态波动都全量落盘
  • 如果继续扩展角色聊天、自定义世界、编辑器草稿,建议评估 IndexedDB 替代 localStorage

P1运行时与编辑器仍在同一个前端入口体系中包体继续膨胀

证据:

  • src/main.tsx:21-34 通过 window.location.pathname 手写分发页面
  • src/main.tsx:60 只有“游戏”和“PresetEditor”两个大入口
  • PresetEditorItemCatalogEditorStateFunctionEditor 都属于重型模块
  • 构建产物已经出现 App407 KBitemCatalog414 KB 的 chunk

影响:

  • 游戏端与编辑器端的演进节奏被绑定在一个 SPA 入口上
  • 编辑器相关数据和静态资源容易继续抬高构建体积
  • 未来增加更多编辑器页、更多世界模板、更多资源目录后,冷启动成本会更明显

建议:

  • 将编辑器拆成独立入口,至少做成独立 route module而不是单个 PresetEditor
  • 继续下钻按 tab 做懒加载,尤其是 items/functions/npcs
  • 将静态大数据、资源目录索引、编辑器专用预览逻辑做更细的 chunk 拆分
  • 如果项目后续会长期保留编辑器,建议直接分成 game app / editor app 两个 entry

P2编辑器基础设施重复实现较多

证据:

  • src/components/PresetEditor.tsx:111-181 自己实现 cloneValuesaveJsonObject
  • src/components/StateFunctionEditor.tsx:113-130 再次实现 cloneValueSectionCard
  • src/components/ItemCatalogEditor.tsx:94 再次实现保存请求
  • src/hooks/useInventoryFlow.ts:8src/hooks/useEquipmentFlow.ts:10src/hooks/useForgeFlow.ts:12src/hooks/useTreasureFlow.ts:10 重复声明 CommitGeneratedState

影响:

  • 修改保存行为、错误处理、深拷贝策略时需要多处同步
  • 编辑器 UI 风格与交互行为容易逐步漂移
  • 公共契约没有收拢到共享层,维护成本会逐步抬高

建议:

  • editor/shared/ 层,集中放保存 SDK、表单字段、卡片容器、克隆工具、错误处理
  • 抽通用的 CommitGeneratedState 类型定义
  • 将编辑器请求和覆盖保存逻辑统一走一个 client

P2类型系统已经出现“总文件过载”

证据:

  • src/types.ts 共 663 行
  • src/types.ts:1-260 同时包含世界、动画、技能、对话、自定义世界、物品等类型
  • src/types.ts:536-663 又继续承接剧情、聊天、任务、GameState、AI 响应

影响:

  • 任一领域类型变化都会增加总文件冲突概率
  • 新人理解类型边界成本高
  • 编辑器类型、运行时类型、AI 传输类型被放在一起,不利于演化

建议:

  • 按领域拆分:types/combat.tstypes/story.tstypes/item.tstypes/customWorld.tstypes/persistence.ts
  • GameState 相关类型与 editor override 类型分开
  • AI request/response contract 单独收口,避免继续堆进总类型文件

P2AI 客户端层过厚,且重复了多套请求与解析逻辑

证据:

  • src/services/ai.ts 共 1153 行
  • src/services/ai.ts:540-605608-678745-790 分别手写了 JSON completion、纯文本 completion、流式 completion
  • src/services/ai.ts:680-697 手写了多段 JSON 解析兜底
  • src/services/ai.ts:76-78591-594662-666 主要依赖 console.* 打日志

影响:

  • LLM 行为扩展时容易继续复制请求模板、错误处理、超时逻辑
  • 错误分类不够稳定,观测主要停留在 console 层
  • prompt、transport、fallback、parse 被放在一起,后续测试和替换模型都不够轻

建议:

  • llmClient,统一 transport、timeout、stream、error taxonomy
  • llmParsers,将 JSON parse / plain text parse / suggestion parse 独立
  • 为关键 prompt 输出建立 fixture 测试,至少覆盖 fallback 与异常响应
  • 如果后续要接多个模型,尽早把 provider 层和 prompt 层解耦

P2手写路由与死代码开始累积

证据:

  • src/main.tsx:21-34 采用手写 pathname.startsWith(...)
  • src/components/GameShell.tsx:1511 存在 false && showTeamModal

影响:

  • 路由能力不具备可扩展性,也不利于后续加 404、重定向、权限判断、嵌套路由
  • 死代码继续堆积后,会误导维护者对真实入口和真实 UI 状态的判断

建议:

  • 引入正式路由层,哪怕只做轻量路由也比手写分发更清晰
  • 清理已经废弃的 UI 分支和不可达逻辑
  • 对“临时下线的功能”改为 feature flag 或明确注释,不要用 false &&

建议落地顺序

第一阶段:先补工程底座

  • 增加 ESLint / Prettier / EditorConfig
  • 增加 test 脚本与 Vitest
  • 把 CI 最小闭环搭起来类型检查、单测、build、内容校验

第二阶段:先拆边界,再拆大文件

  • 先把 Vite 中的编辑器写文件接口、LLM 代理抽走
  • 再把 GameShelluseStoryGenerationuseCombatFlow 按职责拆域
  • 拆分时优先保持外部接口稳定,避免一次性全仓大改

第三阶段:收敛基础设施

  • 统一 persistence 层
  • 统一 editor shared 层
  • 统一 AI client 层
  • 拆分 types.ts

第四阶段:降低发布成本

  • 将 editor 与 game 做更明确的入口拆分
  • 优化 chunk 边界
  • 评估是否把编辑器做成独立 app

一句话结论

这个仓库当前最需要优化的不是“再补几个功能”,而是把已经验证有效的玩法与工具链,从“靠大文件和经验串起来”升级为“靠清晰边界、统一基础设施和自动化门禁支撑起来”。只要这一步不做,后续每次加内容、加编辑器能力、加 AI 流程,工程成本都会持续上升。