12 KiB
前端应迁后端逻辑审计(2026-04-21)
更新时间:2026-04-21
0. 审计目标
这份文档只回答一个问题:
当前前端代码里,哪些逻辑已经明显越过“前端只做表现,Express 后端负责逻辑、数据与存储”的边界,应该继续迁到后端。
本轮不改业务代码,只做:
- 基于当前仓库状态给出高置信度候选点
- 标明代码证据
- 给出迁移优先级
- 说明迁移后前端应该保留什么、移走什么
1. 结论先行
结合当前代码与已有边界文档,前端里仍有 6 类逻辑应该继续后移:
- 运行时快照前置写入与本地镜像解释
- 鉴权 token 的浏览器本地真相
- 平台浏览历史的本地真相与迁移状态
- NPC 待接委托“换单”仍由前端直接触发正式生成
- quest/runtime item 的双环境混合编排
- 浏览器侧大型 AI orchestration 与 prompt/repair/fallback 主链
一句话判断:
当前前端已经不是最早那种“大量主算”的状态,但仍然保留了运行时镜像、生成编排和部分正式真相。后端边界还需要再收一轮,前端才算真正退回表现层。
2. 审计依据
2.1 文档依据
docs/experience/PROJECT_WORK_EXPERIENCE_PLAYBOOK.mddocs/planning/EXPRESS_BACKEND_REFACTOR_PLAN_2026-04-08.mddocs/technical/RUNTIME_STORY_BACKEND_BOUNDARY_MIGRATION_2026-04-19.mddocs/audits/engineering/ENGINEERING_CLEANUP_AND_BACKEND_BOUNDARY_AUDIT_2026-04-20.mddocs/audits/engineering/CURRENT_ENGINEERING_OPTIMIZATION_OPPORTUNITIES_2026-04-20.md
2.2 当前代码依据
src/hooks/story/runtimeStoryCoordinator.tssrc/services/apiClient.tssrc/services/platformBrowseHistory.tssrc/components/game-shell/PreGameSelectionFlow.tsxsrc/hooks/story/npcEncounterActions.tssrc/services/questDirector.tssrc/services/runtimeItemAiDirector.tssrc/services/ai.ts
3. 当前高置信度应后移逻辑
3.1 运行时快照前置写入仍在前端
代码证据
src/hooks/story/runtimeStoryCoordinator.ts 当前仍存在以下链路:
syncRuntimeSnapshot(...)syncRuntimeSnapshot(...)内部直接调用putSaveSnapshot(...)loadServerRuntimeOptionCatalog(...)在请求getRuntimeStoryState(...)之前先写本地快照resolveServerRuntimeChoice(...)在请求resolveRuntimeStoryAction(...)之前先写本地快照
对应位置:
src/hooks/story/runtimeStoryCoordinator.ts:21src/hooks/story/runtimeStoryCoordinator.ts:25src/hooks/story/runtimeStoryCoordinator.ts:36src/hooks/story/runtimeStoryCoordinator.ts:99
当前问题
这意味着运行时正式动作发起前,前端仍会先落一份自己的快照真相,再去请求后端。
这条链的问题不是“有没有缓存”,而是:
- 前端仍在承担正式提交前的状态镜像
- 快照解释权没有完全收回到后端
- 运行时主链仍处于“本地镜像 + 服务端会话”并存状态
迁移建议
后端继续承接:
- 运行时快照写入
- 快照版本解释
- 动作提交前的状态一致性校验
前端只保留:
- 当前展示用的 view model
- 可选的只读恢复缓存
- 纯表现态的 loading / transition / animation state
优先级
P0
3.2 鉴权 token 仍由前端 localStorage 持有真相
代码证据
src/services/apiClient.ts 当前仍直接访问 window.localStorage 保存 access token:
getStoredAccessToken()setStoredAccessToken(...)clearStoredAccessToken(...)withAuthorizationHeaders(...)直接从本地 token 组装请求头
对应位置:
src/services/apiClient.ts:333src/services/apiClient.ts:341src/services/apiClient.ts:362src/services/apiClient.ts:382
当前问题
第三批清理已经收掉了“自动登录用户名/密码”本地真相,但 access token 仍然由浏览器长期持有。
这在当前项目边界下仍有两个问题:
- 正式鉴权真相仍没有完全收回后端 session 边界
- 前端 SDK 仍然负担 token 生命周期的关键部分
迁移建议
后端继续承接:
- session / refresh / cookie 真相
- 鉴权状态续期
- token 更新与失效策略
前端只保留:
- 当前是否已登录的展示态
- 统一的请求封装
- 401 后的 UI 响应
优先级
P0
3.3 平台浏览历史仍是“前端本地历史 + 后端回填”的双真相
代码证据
src/services/platformBrowseHistory.ts 当前仍维护一整套本地历史真相:
readPlatformBrowseHistory(...)writePlatformBrowseHistory(...)hasPendingPlatformBrowseHistoryMigration(...)markPlatformBrowseHistoryMigrated(...)
对应位置:
src/services/platformBrowseHistory.ts:77src/services/platformBrowseHistory.ts:103src/services/platformBrowseHistory.ts:151src/services/platformBrowseHistory.ts:164
src/components/game-shell/PreGameSelectionFlow.tsx 当前仍显式做:
- 先
writePlatformBrowseHistory(...) - 再调用
upsertProfileBrowseHistory(...) - 同步成功后
markPlatformBrowseHistoryMigrated(...) - 启动阶段读取
readPlatformBrowseHistory(...) - 根据
hasPendingPlatformBrowseHistoryMigration(...)决定是否补同步
对应位置:
src/components/game-shell/PreGameSelectionFlow.tsx:383src/components/game-shell/PreGameSelectionFlow.tsx:392src/components/game-shell/PreGameSelectionFlow.tsx:394src/components/game-shell/PreGameSelectionFlow.tsx:433src/components/game-shell/PreGameSelectionFlow.tsx:466
当前问题
这条链已经不是单纯缓存,而是:
- 本地历史存储
- 本地同步标记
- 后端历史持久化
三套状态同时存在。
迁移建议
后端继续承接:
- 浏览历史唯一持久化真相
- 历史去重、排序、截断
- 迁移完成标记
前端只保留:
- 展示缓存
- 弱网下的临时 optimistic UI
- 刷新后重新拉取远端结果
优先级
P1
3.4 NPC 待接委托“换单”仍由前端直接发起正式生成
代码证据
src/hooks/story/npcEncounterActions.ts 当前仍保留:
replacePendingNpcQuestOffer = async () => { ... }- 内部直接调用
generateQuestForNpcEncounter(...)
对应位置:
src/hooks/story/npcEncounterActions.ts:1561src/hooks/story/npcEncounterActions.ts:1595
当前问题
聊天后是否挂出待接委托已经后移,但“换一份委托”这条分支仍然是:
- 前端组装上下文
- 前端决定调用生成
- 前端直接把结果写回当前 story UI
这仍属于正式运行时任务编排没有收干净。
迁移建议
后端继续承接:
- NPC 待接委托换单决策
- 是否允许换单
- 换单后的任务草案生成
- 对应聊天态快照回填
前端只保留:
- 点击“换一份委托”
- loading / error 展示
- 消费后端返回的新 pending quest offer
优先级
P0
3.5 questDirector 仍是前端 SDK 与生成编排混合体
代码证据
src/services/questDirector.ts 当前同时承担:
generateQuestForNpcEncounter(...)- 浏览器路径
requestJson('/api/runtime/quests/generate') - 非浏览器路径
requestChatMessageContent(...) - 本地
compileQuestIntentToQuest(...)fallback
对应位置:
src/services/questDirector.ts:213src/services/questDirector.ts:242src/services/questDirector.ts:267src/services/questDirector.ts:256src/services/questDirector.ts:281src/services/questDirector.ts:293
当前问题
这类文件虽然浏览器正式路径已经优先走后端,但职责仍混在一起:
- 前端 SDK
- Quest prompt 编排
- Quest intent 解析
- deterministic fallback compile
这会导致边界长期模糊,也让前端仍像“半个服务端”。
迁移建议
后端继续承接:
- quest intent 生成
- prompt 组装
- JSON 解析
- fallback compile
前端只保留:
requestGenerateQuest(...)这类轻量 SDK- 请求参数组装
- 结果消费
优先级
P1
3.6 runtimeItemAiDirector 仍是前端 SDK 与意图生成混合体
代码证据
src/services/runtimeItemAiDirector.ts 当前同时承担:
generateRuntimeItemAiIntents(...)- 浏览器路径
requestJson('/api/runtime/items/runtime-intent') - 非浏览器路径
requestChatMessageContent(...) - 本地
buildRuntimeItemAiIntent(...)fallback
对应位置:
src/services/runtimeItemAiDirector.ts:84src/services/runtimeItemAiDirector.ts:94src/services/runtimeItemAiDirector.ts:118
当前问题
它和 questDirector 是同类问题:
- 正式浏览器路径已经走后端
- 但前端文件仍然承担完整生成逻辑认知
- 文件职责仍然是双环境混合
迁移建议
后端继续承接:
- runtime item intent prompt
- 模型调用
- 结果解析与 fallback
前端只保留:
- 轻量请求 SDK
- 结果到 UI 的映射
优先级
P1
3.7 src/services/ai.ts 仍是浏览器侧正式 AI orchestration 热点
代码证据
当前 src/services/ai.ts 仍直接承担以下正式链路:
requestChatMessageContent(...)requestPlainTextCompletionFromClient(...)streamPlainTextCompletionFromClient(...)generateCustomWorldProfile(...)generateInitialStory(...)generateNextStep(...)streamNpcChatDialogue(...)streamNpcRecruitDialogue(...)
对应位置:
src/services/ai.ts:1732src/services/ai.ts:1868src/services/ai.ts:2038src/services/ai.ts:2339src/services/ai.ts:2447src/services/ai.ts:2487src/services/ai.ts:2529src/services/ai.ts:2570
并且文件内仍保留:
- JSON repair
- prompt 组装
- response normalize
- fallback/offline 响应
- 角色聊天建议与摘要生成
当前问题
这说明浏览器端并不只是“请求一个后端接口”,而是还在承担:
- prompt source
- 生成策略
- 错误修复
- fallback 编排
- 多类业务场景的正式 AI 出口
这与“前端只做表现”存在明确冲突。
迁移建议
后端继续承接:
- story / npc / recruit / custom-world 的 prompt 编排
- JSON repair
- fallback 策略
- streaming orchestration
- 模型调用与日志
前端只保留:
- 轻量 AI SDK
- SSE 文本流展示
- UI fallback 呈现
优先级
P0
4. 可以暂时保留在前端的部分
下面这些内容即使和上述模块同文件出现,也不属于必须后移的对象:
- 面板开关、loading、error、streaming 文本展示
- 动画时间线、过场状态、临时 UI 回显
- 表单草稿、筛选词、排序选项
- 只影响表现、不影响正式真相的 view model 拼接
迁移时要注意:
不是把所有前端代码都往后端搬,而是把“正式状态解释、规则裁决、生成编排、持久化真相”搬走。
5. 推荐迁移顺序
5.1 第一阶段
先收最危险的正式真相:
runtimeStoryCoordinator.tsapiClient.tsnpcEncounterActions.ts里的 quest replace 分支
原因:
- 这三处最直接影响运行时真相和动作主链
- 不先收这些,前端仍然不是纯表现层
5.2 第二阶段
再拆双环境混合服务:
questDirector.tsruntimeItemAiDirector.tsplatformBrowseHistory.ts
原因:
- 这几处已经有后端承接基础
- 迁移成本相对可控
5.3 第三阶段
最后继续压缩浏览器 AI orchestration:
src/services/ai.ts- 相关 prompt builder / repair helper / offline fallback
原因:
- 这部分体量大
- 链路多
- 更适合在前两阶段把 contract 稳住后集中拆
6. 建议产出物
如果后续按这份文档继续落地,建议每一批都至少同步产出:
- 一份落地文档,说明迁移了哪条链
- 一组 contract/route 变更说明
- 一组前端 SDK 收缩说明
- 一组防回退测试
7. 一句话结论
当前前端最需要继续后移的,不是零散小工具,而是:
运行时快照前置写入、鉴权 token、本地浏览历史真相、NPC 委托换单、quest/runtime item 双环境混合编排,以及 src/services/ai.ts 里仍然留在浏览器的正式 AI orchestration。