Skip to content

Apifox 标签同步刷新与全量存档修复记录

更新时间:2026-07-14;使用模型:Codex GPT-5;用户:Jsmond2016


问题摘要

用户基于 Apifox tag 同步接口后,可能继续在当前面板中手工添加接口,或通过外部插件插入 Quick Mock 接口。此时再次执行“刷新接口”或“确定同步”,原逻辑会删除这些非 Apifox 同步接口。

存档也存在相同的数据边界问题:原逻辑只保存当前 tag 命中的接口,导致恢复存档后,其他 tag、自定义及外部接口丢失。

修复后的原则是:

  • Apifox tag 只决定需要从 Apifox 拉取和更新的接口范围。
  • 刷新只替换 Apifox 同步产生的接口,不修改用户手工添加或外部插入的数据。
  • 存档是当前面板的完整快照,tag 只用于标识迭代及关联迭代文档。

用户操作路径

刷新覆盖

  1. 选择一个或多个 tag,同步 Apifox 接口。
  2. 在已同步模块中手工添加接口,或通过外部插件插入接口模块。
  3. 点击“刷新 Apifox 接口”,确认更新。
  4. 刷新完成后,手工接口或外部接口从面板中消失。

存档缺失

  1. 当前面板同时存在 tag 接口、自定义接口和外部接口。
  2. 选择一个迭代 tag 并执行存档。
  3. 存档预览及保存结果只包含该 tag 命中的接口。
  4. 恢复存档后,未命中 tag 的接口无法恢复。

根因分析

刷新按模块整体替换

旧逻辑使用模块的 apiDocUrl 判断模块是否来自 Apifox,并在刷新时先删除所有相同 URL 的模块,再追加最新解析结果。

该判断粒度过粗:

  • 用户可能直接在 Apifox 模块中添加自定义接口。
  • 外部批量 Quick Mock 模块也会继承当前 Apifox URL。
  • 模块共享同一 URL,不代表模块内每个接口都由 Apifox 同步维护。

因此,按 apiDocUrl 整模块替换会误删非 Apifox 数据。

存档错误复用 tag 筛选结果

旧存档逻辑遍历模块,只保留 api.tags 包含所选 tag 的接口,并仅保存这些接口引用的快速联调配置。这使“迭代标识”和“存档内容范围”被错误绑定。

修复方案

接口来源边界

ModuleConfig 增加以下可选元数据:

字段说明
source标识模块来源为 apifoxexternal
apifoxApiIds记录当前模块中由 Apifox 同步流程维护的接口 ID

新同步的模块会记录完整的 Apifox 接口 ID 清单。用户后续添加的接口不会进入该清单,因此即使它与 Apifox 接口使用相同 tag,也不会在刷新时被覆盖。

外部 Quick Mock 模块会明确标记为 external,不参与 Apifox 差异对比和替换。

刷新与确认同步

同步逻辑提取为独立纯函数,统一处理刷新和弹窗确认同步:

  1. 只提取 apifoxApiIds 中的接口参与新旧差异对比。
  2. 更新同名模块时,用新 Apifox 接口替换旧同步接口,并追加原模块中的自定义接口。
  3. Apifox 分组被删除但模块仍有自定义接口时,保留该模块并解除其 Apifox 来源关联。
  4. 外部模块及其他自定义模块保持原样。
  5. “合并”策略只追加不存在的 Apifox 接口,不改变已有自定义或外部数据。

历史数据兼容

历史配置没有 sourceapifoxApiIds。兼容逻辑仅在缺少 ID 清单时,根据以下条件识别旧 Apifox 接口:

  • 模块 apiDocUrl 与当前 Apifox 配置一致。
  • 接口存在同步时写入的 tag。
  • 模块不是 quick.mock.external 外部模块。

历史配置完成一次实际更新后,会写入新的来源和接口 ID 元数据。新版本不再依赖 tag 判断自定义接口来源。

全量存档

存档继续使用所选 tag 读取迭代文档,但保存内容改为当前面板完整快照:

  • 全部模块及模块内全部接口。
  • Apifox 来源和接口 ID 元数据。
  • 接口 tag、自定义响应等嵌套配置的独立副本。
  • 全部快速联调配置。
  • 当前 Apifox 配置和对应迭代文档信息。

归档弹窗说明同步调整,明确存档会保存当前面板全部数据。

验收标准

  • [x] 刷新 Apifox 后,同模块内手工添加的接口仍然存在。
  • [x] 自定义接口即使使用相同 tag,也不会被刷新覆盖。
  • [x] 外部 Quick Mock 模块不会参与 Apifox 替换。
  • [x] Apifox 已删除分组但仍含自定义接口时,模块和自定义接口继续保留。
  • [x] 合并策略只追加新 Apifox 接口。
  • [x] 历史同步配置能够继续刷新,并排除旧版外部模块。
  • [x] 存档包含当前面板全部模块、接口和快速联调配置。
  • [x] 存档内容使用独立数据副本,不直接引用当前 Store 数组。

回归测试

场景预期结果
历史 Apifox 模块混有自定义接口只选取带同步 tag 的旧接口参与更新
新版 Apifox 模块内自定义接口使用相同 tag依据 apifoxApiIds 保留自定义接口
外部模块使用相同 apiDocUrl外部模块完整保留
Apifox 分组删除但模块仍有自定义接口保留自定义接口,并解除模块 Apifox 关联
使用合并策略同步新增接口新接口追加,已有自定义和外部数据不变
存档面板含多个 tag、自定义及外部接口存档包含全部模块和接口
存档面板含未被接口引用的快速联调配置快速联调配置仍完整保存

验证结果

  • 用户按原始操作路径验证通过。
  • Vitest:5 个测试文件、19 条用例全部通过。
  • 定向 ESLint:通过。
  • Chrome 生产构建:通过。
  • 全量 TypeScript 检查仍有 2 个既有错误,分别位于 ArchiveListModal.tsxSyncApifoxModal.tsx,与本次修改无关。

任务状态

任务状态关联实现
区分 Apifox、手工和外部接口来源已完成types/index.tsapifoxUtils.tsbackground/index.ts
刷新及确认同步仅替换 Apifox 接口已完成apifoxSyncUtils.tsSyncApifoxModalButton.tsx
存档改为当前面板全量快照已完成archiveUtil.tsArchiveModal.tsx
增加同步边界和全量存档回归测试已完成apifoxSyncUtils.test.tsarchiveUtil.test.ts

基于 MIT 协议开源