Files
Genarrative/docs/audits/engineering/FRONTEND_LOGIC_BACKEND_MIGRATION_AUDIT_2026-04-21.md
2026-04-21 09:44:17 +08:00

12 KiB
Raw Blame History

前端应迁后端逻辑审计2026-04-21

更新时间:2026-04-21

0. 审计目标

这份文档只回答一个问题:

当前前端代码里哪些逻辑已经明显越过“前端只做表现Express 后端负责逻辑、数据与存储”的边界,应该继续迁到后端。

本轮不改业务代码,只做:

  1. 基于当前仓库状态给出高置信度候选点
  2. 标明代码证据
  3. 给出迁移优先级
  4. 说明迁移后前端应该保留什么、移走什么

1. 结论先行

结合当前代码与已有边界文档,前端里仍有 6 类逻辑应该继续后移:

  1. 运行时快照前置写入与本地镜像解释
  2. 鉴权 token 的浏览器本地真相
  3. 平台浏览历史的本地真相与迁移状态
  4. NPC 待接委托“换单”仍由前端直接触发正式生成
  5. quest/runtime item 的双环境混合编排
  6. 浏览器侧大型 AI orchestration 与 prompt/repair/fallback 主链

一句话判断:

当前前端已经不是最早那种“大量主算”的状态,但仍然保留了运行时镜像、生成编排和部分正式真相。后端边界还需要再收一轮,前端才算真正退回表现层。


2. 审计依据

2.1 文档依据

  1. docs/experience/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md
  2. docs/planning/EXPRESS_BACKEND_REFACTOR_PLAN_2026-04-08.md
  3. docs/technical/RUNTIME_STORY_BACKEND_BOUNDARY_MIGRATION_2026-04-19.md
  4. docs/audits/engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.md
  5. docs/audits/engineering/CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md

2.2 当前代码依据

  1. src/hooks/story/runtimeStoryCoordinator.ts
  2. src/services/apiClient.ts
  3. src/services/platformBrowseHistory.ts
  4. src/components/game-shell/PreGameSelectionFlow.tsx
  5. src/hooks/story/npcEncounterActions.ts
  6. src/services/questDirector.ts
  7. src/services/runtimeItemAiDirector.ts
  8. src/services/ai.ts

3. 当前高置信度应后移逻辑

3.1 运行时快照前置写入仍在前端

代码证据

src/hooks/story/runtimeStoryCoordinator.ts 当前仍存在以下链路:

  1. syncRuntimeSnapshot(...)
  2. syncRuntimeSnapshot(...) 内部直接调用 putSaveSnapshot(...)
  3. loadServerRuntimeOptionCatalog(...) 在请求 getRuntimeStoryState(...) 之前先写本地快照
  4. resolveServerRuntimeChoice(...) 在请求 resolveRuntimeStoryAction(...) 之前先写本地快照

对应位置:

  1. src/hooks/story/runtimeStoryCoordinator.ts:21
  2. src/hooks/story/runtimeStoryCoordinator.ts:25
  3. src/hooks/story/runtimeStoryCoordinator.ts:36
  4. src/hooks/story/runtimeStoryCoordinator.ts:99

当前问题

这意味着运行时正式动作发起前,前端仍会先落一份自己的快照真相,再去请求后端。

这条链的问题不是“有没有缓存”,而是:

  1. 前端仍在承担正式提交前的状态镜像
  2. 快照解释权没有完全收回到后端
  3. 运行时主链仍处于“本地镜像 + 服务端会话”并存状态

迁移建议

后端继续承接:

  1. 运行时快照写入
  2. 快照版本解释
  3. 动作提交前的状态一致性校验

前端只保留:

  1. 当前展示用的 view model
  2. 可选的只读恢复缓存
  3. 纯表现态的 loading / transition / animation state

优先级

P0


3.2 鉴权 token 仍由前端 localStorage 持有真相

代码证据

src/services/apiClient.ts 当前仍直接访问 window.localStorage 保存 access token

  1. getStoredAccessToken()
  2. setStoredAccessToken(...)
  3. clearStoredAccessToken(...)
  4. withAuthorizationHeaders(...) 直接从本地 token 组装请求头

对应位置:

  1. src/services/apiClient.ts:333
  2. src/services/apiClient.ts:341
  3. src/services/apiClient.ts:362
  4. src/services/apiClient.ts:382

当前问题

第三批清理已经收掉了“自动登录用户名/密码”本地真相,但 access token 仍然由浏览器长期持有。

这在当前项目边界下仍有两个问题:

  1. 正式鉴权真相仍没有完全收回后端 session 边界
  2. 前端 SDK 仍然负担 token 生命周期的关键部分

迁移建议

后端继续承接:

  1. session / refresh / cookie 真相
  2. 鉴权状态续期
  3. token 更新与失效策略

前端只保留:

  1. 当前是否已登录的展示态
  2. 统一的请求封装
  3. 401 后的 UI 响应

优先级

P0


3.3 平台浏览历史仍是“前端本地历史 + 后端回填”的双真相

代码证据

src/services/platformBrowseHistory.ts 当前仍维护一整套本地历史真相:

  1. readPlatformBrowseHistory(...)
  2. writePlatformBrowseHistory(...)
  3. hasPendingPlatformBrowseHistoryMigration(...)
  4. markPlatformBrowseHistoryMigrated(...)

对应位置:

  1. src/services/platformBrowseHistory.ts:77
  2. src/services/platformBrowseHistory.ts:103
  3. src/services/platformBrowseHistory.ts:151
  4. src/services/platformBrowseHistory.ts:164

src/components/game-shell/PreGameSelectionFlow.tsx 当前仍显式做:

  1. writePlatformBrowseHistory(...)
  2. 再调用 upsertProfileBrowseHistory(...)
  3. 同步成功后 markPlatformBrowseHistoryMigrated(...)
  4. 启动阶段读取 readPlatformBrowseHistory(...)
  5. 根据 hasPendingPlatformBrowseHistoryMigration(...) 决定是否补同步

对应位置:

  1. src/components/game-shell/PreGameSelectionFlow.tsx:383
  2. src/components/game-shell/PreGameSelectionFlow.tsx:392
  3. src/components/game-shell/PreGameSelectionFlow.tsx:394
  4. src/components/game-shell/PreGameSelectionFlow.tsx:433
  5. src/components/game-shell/PreGameSelectionFlow.tsx:466

当前问题

这条链已经不是单纯缓存,而是:

  1. 本地历史存储
  2. 本地同步标记
  3. 后端历史持久化

三套状态同时存在。

迁移建议

后端继续承接:

  1. 浏览历史唯一持久化真相
  2. 历史去重、排序、截断
  3. 迁移完成标记

前端只保留:

  1. 展示缓存
  2. 弱网下的临时 optimistic UI
  3. 刷新后重新拉取远端结果

优先级

P1


3.4 NPC 待接委托“换单”仍由前端直接发起正式生成

代码证据

src/hooks/story/npcEncounterActions.ts 当前仍保留:

  1. replacePendingNpcQuestOffer = async () => { ... }
  2. 内部直接调用 generateQuestForNpcEncounter(...)

对应位置:

  1. src/hooks/story/npcEncounterActions.ts:1561
  2. src/hooks/story/npcEncounterActions.ts:1595

当前问题

聊天后是否挂出待接委托已经后移,但“换一份委托”这条分支仍然是:

  1. 前端组装上下文
  2. 前端决定调用生成
  3. 前端直接把结果写回当前 story UI

这仍属于正式运行时任务编排没有收干净。

迁移建议

后端继续承接:

  1. NPC 待接委托换单决策
  2. 是否允许换单
  3. 换单后的任务草案生成
  4. 对应聊天态快照回填

前端只保留:

  1. 点击“换一份委托”
  2. loading / error 展示
  3. 消费后端返回的新 pending quest offer

优先级

P0


3.5 questDirector 仍是前端 SDK 与生成编排混合体

代码证据

src/services/questDirector.ts 当前同时承担:

  1. generateQuestForNpcEncounter(...)
  2. 浏览器路径 requestJson('/api/runtime/quests/generate')
  3. 非浏览器路径 requestChatMessageContent(...)
  4. 本地 compileQuestIntentToQuest(...) fallback

对应位置:

  1. src/services/questDirector.ts:213
  2. src/services/questDirector.ts:242
  3. src/services/questDirector.ts:267
  4. src/services/questDirector.ts:256
  5. src/services/questDirector.ts:281
  6. src/services/questDirector.ts:293

当前问题

这类文件虽然浏览器正式路径已经优先走后端,但职责仍混在一起:

  1. 前端 SDK
  2. Quest prompt 编排
  3. Quest intent 解析
  4. deterministic fallback compile

这会导致边界长期模糊,也让前端仍像“半个服务端”。

迁移建议

后端继续承接:

  1. quest intent 生成
  2. prompt 组装
  3. JSON 解析
  4. fallback compile

前端只保留:

  1. requestGenerateQuest(...) 这类轻量 SDK
  2. 请求参数组装
  3. 结果消费

优先级

P1


3.6 runtimeItemAiDirector 仍是前端 SDK 与意图生成混合体

代码证据

src/services/runtimeItemAiDirector.ts 当前同时承担:

  1. generateRuntimeItemAiIntents(...)
  2. 浏览器路径 requestJson('/api/runtime/items/runtime-intent')
  3. 非浏览器路径 requestChatMessageContent(...)
  4. 本地 buildRuntimeItemAiIntent(...) fallback

对应位置:

  1. src/services/runtimeItemAiDirector.ts:84
  2. src/services/runtimeItemAiDirector.ts:94
  3. src/services/runtimeItemAiDirector.ts:118

当前问题

它和 questDirector 是同类问题:

  1. 正式浏览器路径已经走后端
  2. 但前端文件仍然承担完整生成逻辑认知
  3. 文件职责仍然是双环境混合

迁移建议

后端继续承接:

  1. runtime item intent prompt
  2. 模型调用
  3. 结果解析与 fallback

前端只保留:

  1. 轻量请求 SDK
  2. 结果到 UI 的映射

优先级

P1


3.7 src/services/ai.ts 仍是浏览器侧正式 AI orchestration 热点

代码证据

当前 src/services/ai.ts 仍直接承担以下正式链路:

  1. requestChatMessageContent(...)
  2. requestPlainTextCompletionFromClient(...)
  3. streamPlainTextCompletionFromClient(...)
  4. generateCustomWorldProfile(...)
  5. generateInitialStory(...)
  6. generateNextStep(...)
  7. streamNpcChatDialogue(...)
  8. streamNpcRecruitDialogue(...)

对应位置:

  1. src/services/ai.ts:1732
  2. src/services/ai.ts:1868
  3. src/services/ai.ts:2038
  4. src/services/ai.ts:2339
  5. src/services/ai.ts:2447
  6. src/services/ai.ts:2487
  7. src/services/ai.ts:2529
  8. src/services/ai.ts:2570

并且文件内仍保留:

  1. JSON repair
  2. prompt 组装
  3. response normalize
  4. fallback/offline 响应
  5. 角色聊天建议与摘要生成

当前问题

这说明浏览器端并不只是“请求一个后端接口”,而是还在承担:

  1. prompt source
  2. 生成策略
  3. 错误修复
  4. fallback 编排
  5. 多类业务场景的正式 AI 出口

这与“前端只做表现”存在明确冲突。

迁移建议

后端继续承接:

  1. story / npc / recruit / custom-world 的 prompt 编排
  2. JSON repair
  3. fallback 策略
  4. streaming orchestration
  5. 模型调用与日志

前端只保留:

  1. 轻量 AI SDK
  2. SSE 文本流展示
  3. UI fallback 呈现

优先级

P0


4. 可以暂时保留在前端的部分

下面这些内容即使和上述模块同文件出现,也不属于必须后移的对象:

  1. 面板开关、loading、error、streaming 文本展示
  2. 动画时间线、过场状态、临时 UI 回显
  3. 表单草稿、筛选词、排序选项
  4. 只影响表现、不影响正式真相的 view model 拼接

迁移时要注意:

不是把所有前端代码都往后端搬,而是把“正式状态解释、规则裁决、生成编排、持久化真相”搬走。


5. 推荐迁移顺序

5.1 第一阶段

先收最危险的正式真相:

  1. runtimeStoryCoordinator.ts
  2. apiClient.ts
  3. npcEncounterActions.ts 里的 quest replace 分支

原因:

  1. 这三处最直接影响运行时真相和动作主链
  2. 不先收这些,前端仍然不是纯表现层

5.2 第二阶段

再拆双环境混合服务:

  1. questDirector.ts
  2. runtimeItemAiDirector.ts
  3. platformBrowseHistory.ts

原因:

  1. 这几处已经有后端承接基础
  2. 迁移成本相对可控

5.3 第三阶段

最后继续压缩浏览器 AI orchestration

  1. src/services/ai.ts
  2. 相关 prompt builder / repair helper / offline fallback

原因:

  1. 这部分体量大
  2. 链路多
  3. 更适合在前两阶段把 contract 稳住后集中拆

6. 建议产出物

如果后续按这份文档继续落地,建议每一批都至少同步产出:

  1. 一份落地文档,说明迁移了哪条链
  2. 一组 contract/route 变更说明
  3. 一组前端 SDK 收缩说明
  4. 一组防回退测试

7. 一句话结论

当前前端最需要继续后移的,不是零散小工具,而是:

运行时快照前置写入、鉴权 token、本地浏览历史真相、NPC 委托换单、quest/runtime item 双环境混合编排,以及 src/services/ai.ts 里仍然留在浏览器的正式 AI orchestration。