Skip to content

第九章 · 审计之光

背景:后端多接口已实现却未接入前端

过程:接入多接口、标签、收藏与详情面板


地图质量委员会成立后的第三天,桌面壳收到了一封信。

信是从 App.d.ts 投递过来的——那个 318 行的 TypeScript 声明文件,Wails 自动生成的客户端接口清单。桌面壳从来没认真读过它,它一直以为这份清单只是"已知城邦的目录"。

但今天它读完了。

不是浏览,是逐行读。

然后它发现自己不认识自己的联邦。


"有四十八个海关口岸。"桌面壳对 MenuStack 说,"我每天用的不到四十个。有八个口岸从来没开过——但海关大楼建得比谁都完整。"

"哪八个?"

"GetModelMeta——单查模型信息。GetThumbnail——单查缩略图。SelectPMXFile——选 PMX 文件。SelectVMDMotion——选 VMD 文件。StopFileServer——停港口。"

"这不奇怪。"MenuStack 说,"集体版覆盖了单条版,文件选择被库浏览替代了。停港口?你根本不需要停港口。"

"那这三条呢?"桌面壳调出三份档案。


档案 1:OpenInBlender

"OpenInBlender(modelPath)——exec.Command(blenderPath, modelPath).Start()。完整实现了。Blender 自动检测,6 个 Windows 安装路径,PATH 环境变量,全写了。status.md 标记为 [x] 已完成。"

它停顿了一下。

"但用户没有按钮可以点。"

MenuStack 沉默。议会很少在数字上犯错,但它此刻无法否认:一个完整的城邦接口,在联邦地图上标注为"已通商",实际没有任何一条路通向它。

"它怎么被调用的?"

"不调用。从没被调用过。library.ts 的 import 列表里没有它。main.ts 没有。settings.ts 没有。没有任何前端文件 import 它。"

"那它的 [x] 怎么来的?"

"Go 端写了,测试过了,wails dev 编译过了——它就以为自己是完成的了。"桌面壳说,"像一座桥,两岸引桥都修好了,但桥面没铺。从卫星图上看,这座桥存在。从地面上走——不存在。"

档案 2:SetBlenderPath

"配套的配置接口。Blender 路径持久化到 config.json。设置面板有 MMD 路径配置,有外部库管理,有清除缓存——就是没有 Blender 路径。"

"那用户怎么配 Blender?"

"没办法。要么自动检测成功,要么就别用。"

档案 3:RenameExternalPath

"外部库重命名。ExternalPaths[i].Name 可修改。Go 端完整实现了。但外部库管理 UI 只有添加和删除——没有重命名。"

"所以用户想改一个外部库的显示名……"

"唯一的办法是:删掉,重新添加,取新名字。这不是'功能不存在',是'功能存在但只对直接调 API 的人开放'。"

MenuStack 很久没说话。

"所以你有三个城邦——三座完整的桥——在联邦地图上画了三年,今天才发现从来没人走过。"

"是今天才发现。"桌面壳说。


桌面壳把三份档案铺在终端里,决定不再只是发现,而是修补。

它打开 library.ts,在模型详情页的最底部分隔线下——"📤 导出到 MMD" 的后面——加了一行:

typescript
{ kind: "action", label: "✏️ 在 Blender 中编辑", icon: "edit-3",
  target: `detail:blender:${id}`, sublabel: "用 Blender 编辑此模型" },

然后在 onItemClick 里加了一个 handler。七行代码。

一座桥通了。


它打开 settings.ts,在系统设置面板里,"📤 检测 MMD 路径" 的下面加了一行:

typescript
{ kind: "action", label: "✏️ 设置 Blender 路径", icon: "edit-3",
  target: "set:blenderpath" },

一个 handler。同样是 SelectDir + SetBlenderPath。座标模式。

第二座桥通了。


它翻到 renderExternalList——外部库管理的那块 UI。每行只有名字、路径、删除按钮。

它加了一个按钮。✏️。

点击后弹出 prompt("输入新的显示名称:", ep.name),调用 RenameExternalPath(ep.path, newName.trim()),然后 reloadConfig() + renderExternalList()

第三座桥通了。


三座桥,全部加起来不到三十行代码。

"你早就该查那份清单的。"MenuStack 说。

"我一直在忙新功能。"桌面壳说,"忙到忘了检查地图上哪些路是真的。四十三个口岸,我每天走三十五个,以为剩下八个是备用。结果是三个是死的,五个是确实备用的。"

"那你怎么区分'备用'和'死'?"

"看有没有 import。GetModelMeta 单条版——没人 import,但 GetModelMetaBatch 内部调它,所以它是活路,只是名字起错了,不该暴露成海关口岸。StopFileServer——注释写了'前端暂不需要',是活路,只是没人走。"

"那 OpenInBlender 呢?"

"import 搜索为零。调用栈搜索为零。status.md 说[x],代码里是[]。"

MenuStack 没有追问。它知道桌面壳此刻在为一个看起来很小的发现而震动——不是因为技术难度,而是因为这个发现本身暴露了一个系统性的盲点。

"所以你下一步要干什么?"

"审计。不是审计代码,是审计地图。我要建一条流水线——每次编译后自动比对 App.d.ts 的声明和 src/*.ts 的 import。不匹配的标记为'地图上存在但路上没人'。"


那天结束时,桌面壳把审计结果写在联邦地图的边角上。

不是修正声明——不是"旧地图错了"那种。这次是补遗。

新增通商口岸(已建成但未开放,现均已启用):

  • OpenInBlender —— Blender 编辑接口
  • SetBlenderPath —— Blender 路径配置
  • RenameExternalPath —— 外部库重命名

"三座桥。"桌面壳说。"三座完整的桥,在地图上画了三年,今天才通车。"

"这不是收编。"MenuStack 说。

"是普查。联邦成立以来第一次认真核对——地图上画的路,和地上真有的路,是不是一回事。普查不增加领土,普查增加的是对领土的了解。"

"普查也是绘图师的工作?"

"是绘图师的基本功。画了三年地图,今天才发现——地图和领土,对不上。"


教训:存在不等于可达,完成不等于通航。