Files
Situation-Awareness-Platfor…/实习讲解/经历3-API接口前后端集成详解.md
cfy666 c1e9a4be83 chore: sync local changes and add documentation
- Update yarn.lock
- Add project implementation docs in docs/
- Add personal internship experience notes in 实习讲解/
2026-06-29 19:47:30 +08:00

17 KiB
Raw Permalink Blame History

API 接口前后端集成 — 面试版

简历原话:"协作设计可信态势感知平台API接口完成50+数据接口的前后端集成"

这篇文档帮你理解 API 集成层到底做了什么、怎么做的,以及面试时怎么讲。


一、先搞清楚API 集成层是什么?

API 集成层是前端和后端之间的"翻译官"和"交通管制员"。它不是简单的 axios.get('/xxx'),而是一整套基础设施:

页面组件Vue 组件)
      │  调用 API 函数
      ▼
API 函数层29 个文件641 个函数)
  → 定义接口 URL、method、参数格式
      │  调用 request()
      ▼
请求基础设施request.ts273 行)
  → Axios 实例、请求拦截器、响应拦截器
  → AES 加密/解密、Token 注入、Token 过期自动刷新
  → 错误统一处理、缓存降级
      │  HTTP 请求
      ▼
后端服务器

一句话概括:我参与设计和集成了平台的 API 通信层,包括定义了 50+ 个态势感知相关的数据接口并搭建了完整的请求基础设施加密、Token 刷新、错误处理、缓存降级),覆盖全平台 641 个接口。


二、API 整体规模

数据 数值
API 函数总数 641 个
API 文件数 29 个
金银湖态势感知相关 106 个situation.ts 50 + index.ts 45 + home.ts 5 + auth.ts 3 + scanCenter.ts 3
最大的文件 repo/index.ts161 个函数)

按模块分布

模块 文件 函数数 负责什么
态势感知看板 jyh/situation.ts 50 威胁地图、CVE 趋势、组件排行、漏洞分布等
金银湖核心 jyh/index.ts 45 AI 对话、版本详情、SBOM、许可证、修复建议
仓库管理 repo/index.ts 161 仓库 CRUD、分支、标签、Wiki、文件、Webhook
组织管理 org/index.ts 55 组织 CRUD、成员、首页、CLA、开发者门户
用户管理 user/index.ts 55+ 登录注册、OAuth、个人资料、SSH 密钥、Token
合并请求 merge/index.ts 62 MR 创建/审查、差异对比、代码评审、门禁
讨论系统 discussion/index.ts 50 讨论 CRUD、分类、评论、投票、排行榜
其他 20+ 个文件 issue、commit、branch 等 ~120 Issue、提交、分支、标签、发布、通知等

三、请求基础设施(面试核心 — request.ts

src/utils/request.ts273 行)是整个平台 API 通信的中枢,每一行都在解决实际问题。

3.1 整体架构

组件调用 API 函数
      │
      ▼
proxyService(params)  ← 创建一个 Axios 实例
      │
      ├── 请求拦截器 #1注入 Header
      │   ├── DP_TOKEN / DP_REFRESH_TOKENToken 头)
      │   ├── Authorization: Bearer xxx权限头
      │   ├── page-title / page-repo-id / page-ref页面监控头
      │   ├── gitcode-utm-source来源追踪头
      │   └── URL 前缀重写setPassportPrefix /uc
      │
      ├── 请求拦截器 #2缓存降级检查
      │   └── degradeInterceptor 决定是否读缓存
      │
      ├── ===== 发出 HTTP 请求 =====
      │
      ├── 响应拦截器 #1成功处理
      │   ├── code===500 → 检查登录状态
      │   └── AES 解密响应数据($Decrypt
      │
      ├── 响应拦截器 #1失败处理
      │   ├── 401 → Token 自动刷新refreshing 锁 + 请求队列)
      │   ├── 其他 4xx/5xx → dealWarning防抖 200ms
      │   └── 网络超时 → 静默处理
      │
      └── 响应拦截器 #2缓存降级存储
          └── 成功时存缓存,失败时读缓存返回

3.2 AES 加密解密

// request.ts — 所有响应数据经过 AES-128-CBC 解密
import CryptoJS from 'crypto-js';

const sKey = CryptoJS.enc.Utf8.parse('mXzfCSPBKmEA8aLq');
const iv   = CryptoJS.enc.Utf8.parse('tbeJJLC6dZQXXtWr');

// 解密:响应拦截器中自动调用
const $Decrypt = (text) => {
  let src = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Base64.parse(text));
  let bytes = CryptoJS.AES.decrypt(src, sKey, {
    iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7
  });
  return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
};

// 响应拦截器中response.data.data = $Decrypt(response.data.data)

为什么用加密? 态势感知平台涉及敏感的安全数据AES-128-CBC 加密保证了传输过程中即使被截获也无法直接读取。

3.3 Token 自动刷新 + 请求队列

这是整个基础设施中设计最巧妙的部分——当 Token 过期时,多个并发请求只触发一次刷新

// request.ts — Token 刷新逻辑(简化版)
let refreshing = false;    // 刷新锁
const queue = [];          // 请求等待队列

// 响应拦截器的错误处理分支
async (error) => {
  if (response?.status === 401 && Token && 不是刷新接口) {
    if (refreshing) {
      // 情况 A已经有刷新在进行 → 当前请求排队等待
      return new Promise((resolve) => {
        queue.unshift({ config, resolve });
      });
    }

    // 情况 B第一个遇到 401 的请求 → 执行刷新
    refreshing = true;
    const res = await refreshToken();  // 调刷新接口
    refreshing = false;

    if (res.status === 200) {
      // 刷新成功 → 更新 Token → 重放所有排队的请求
      localStorage.setItem('op_access_token', newToken);
      queue.forEach(({ config, resolve }) => {
        config.headers.Authorization = `Bearer ${newToken}`;
        resolve(axios(config));        // 用新 Token 重发
      });
      queue.splice(0);                  // 清空队列
      return axios(config);             // 重发当前请求
    }
  }
}

场景:用户打开一个有 5 个图表的页面5 个请求同时发出,全部返回 401。第一个请求触发 refreshToken,其余 4 个进入队列等待。刷新成功后5 个请求用新 Token 全部重发。不会出现 5 个请求各自刷新一次 Token 的情况。

3.4 错误统一处理status.ts

// status.ts — HTTP 状态码 → 用户提示 + 事件通知
export function showMessage(res) {
  switch (res.status) {
    case 400: return res.data.error_message;     // 参数错误
    case 401: emitEvent('logout', ...); break;   // 未授权 → 触发登出
    case 403: emitEvent('forbiddenRefresh');     // 无权限 → 刷新页面
    case 404: break;                              // 不存在 → 不提示
    case 408: return '请求超时';
    case 500: return '服务器错误';
    case 502: return '网络错误';
    case 504: return '网络超时';
    default:  return res.data.error_message || '连接出错';
  }
}

// 用防抖包裹200ms防止短时间内多次弹错误提示
export function dealWarning() {
  return debounce((res) => {
    const msg = showMessage(res);
    if (msg) Message.error(msg);
  }, 200);
}

面试话术

"请求基础设施有四个关键设计。第一AES-128-CBC 加密传输敏感数据在传输过程中全程加密。第二Token 自动刷新 + 请求队列——用 refreshing 锁和 Promise 队列机制,保证多个并发的 401 请求只触发一次 Token 刷新,其余排队等待,刷新成功后统一重发。第三,错误统一处理——所有 HTTP 错误状态码映射为用户友好的提示信息,并用 200ms 防抖避免短时间弹多次错误。第四API 缓存降级——请求成功时缓存到 IndexedDB失败时自动读缓存兜底。"


四、API 函数层 — 50+ 态势感知接口怎么组织的?

4.1 统一的 API 函数格式

每个 API 函数遵循统一的格式:

// src/api/jyh/situation.ts — 示例:态势感知看板 API 函数
import request from '@/utils/request';

// 格式export function 函数名(参数): 返回类型
//            ↓ 语义化命名       ↓ TypeScript 泛型

// 全球威胁地图数据
export function mapDataPage1(params?: any): Promise<any> {
  return request({
    url: `/api/v1/page1/map/mapData`,   // ← 接口路径
    method: 'get',                        // ← 请求方法
    params                                // ← GET 方式用 params
  });
}

// AI 对话POST 方式)
export function fetchChatUseSql(data): Promise<any> {
  return request({
    url: '/ai/v1/chat/use_sql',          // ← AI 服务接口
    method: 'POST',                        // ← POST 请求
    data                                  // ← POST 方式用 data 传 JSON body
  });
}

// 文件上传FormData 方式)
export function uploadImage(data): Promise<any> {
  const formData = new FormData();
  formData.append('file', data.file);
  return request({
    url: '/api/v1/upload/image',
    method: 'POST',
    data: formData,
    headers: { 'Content-Type': 'multipart/form-data' }
  });
}

4.2 态势感知相关的 50+ 接口

态势感知模块的接口集中在 src/api/jyh/situation.ts50 个函数),按三个页面组织:

页面 接口前缀 端点数 干什么
第1页全球风险态势 /api/v1/page1/* 13 个 威胁地图、CVE 趋势、组件排行、漏洞分布、行业饼图
第2页开源生态与贡献 /api/v1/page2/* ~18 个 企业榜单、基础设施覆盖率、高校俱乐部、社区健康、语言竞争力、开发者、热力图
第3页武汉市开源生态 /api/v1/page3/* ~19 个 HarmonyOS 统计、项目分布、开发者、企业、高校、社区、政策、镜像站点

加上 jyh/index.ts 中的 45 个通用接口AI 对话、版本详情、SBOM、许可证、预警订阅、修复建议、质量任务等金银湖模块共有 106 个 API


五、两个错误处理工具函数

5.1 reqCatch — 安全包装 API 调用

// src/utils/catch.ts
// 把 try/catch 包装成一个通用函数,避免每个组件都写 try/catch
export async function reqCatch(req, params) {
  try {
    const data = await req(params);
    return { data, error: null };   // 成功 → 返回数据
  } catch (e) {
    return { data: null, error: e }; // 失败 → 返回错误对象,不会 throw
  }
}

// 用法:
const res = await reqCatch(getOrg, { orgId: 'xxx' });
if (!res.error) {
  // 成功处理
} else if (res.error.error_code === 404) {
  // 404 处理
}

5.2 reqCatchV2 — 升级版,自动发事件

export async function reqCatchV2(req) {
  try {
    const data = await req();
    return { data, error: null };
  } catch (e) {
    emitEvent('responseError', e);   // ← 自动通过事件总线通知全局
    return { data: null, error: e };
  }
}

为什么有两版? reqCatch 是早期版本,静默捕获错误让调用方自己处理。reqCatchV2 增加了自动事件通知——在路由守卫里用 V2错误会被 eventBus 监听到,触发统一的 403/404 页面跳转。


六、API 缓存降级系统degradeInterceptor.ts

这是一个 360 行的自研系统,核心思想是:API 成功时缓存结果,失败时读缓存兜底

请求发出
    │
    ├── 请求拦截器
    │   ├── 是否匹配缓存策略URL 正则匹配)
    │   ├── 是否处于降级状态?(上次请求失败,且在熔断时间内)
    │   │   → 是:直接读 IndexedDB 缓存返回(不发请求)
    │   │   → 否:正常发请求(带重试增强)
    │
    ├── 请求成功 → 响应拦截器
    │   └── 缓存结果到 IndexedDB + 更新状态为 success
    │
    └── 请求失败 → 响应拦截器
        ├── 记录失败状态 + 时间戳
        └── 读 IndexedDB 缓存返回(用户看到旧数据而不是错误)

关键配置storage-apis.ts

const storageApis = [
  { url: '/api/v1/issues',        timeout: 5000, retry: 2 },
  { url: '/api/v1/merge_requests', timeout: 5000, retry: 2 },
  // ...
];

面试话术

"API 缓存降级系统的思路是'请求成功时缓存,失败时读缓存兜底'。在请求拦截器里判断是否命中缓存策略——如果上次请求失败且在熔断时间内,就直接读 IndexedDB 返回缓存数据,不发网络请求。响应成功时自动更新缓存,失败时记录状态并返回历史缓存。还有缓存条数上限控制和过期清理机制。"


七、面试问答准备

Q1"完成50+数据接口的前后端集成"具体做了什么?

我参与了两方面的工作。第一是接口定义——和 backend 协作设计并集成了 50+ 个态势感知相关的数据接口,按三个页面组织(全球态势、开源生态、武汉生态),定义了接口路径、请求方法、参数格式和返回结构。第二是搭建了完整的请求基础设施——包括 Axios 实例封装、AES-128-CBC 加密传输、Token 自动刷新 + 请求队列、HTTP 状态码统一处理、API 缓存降级系统等。全平台共集成了 641 个 API 函数,分布在 29 个文件中。

Q2Token 过期自动刷新是怎么实现的?

用了一个 refreshing 锁 + Promise 请求队列的机制。当请求返回 401 时,先检查 refreshing 是否为 true——如果是说明已经有请求在刷新 Token 了,当前请求就包装成一个 Promise 放进等待队列;如果不是,就执行刷新并设 refreshing = true。刷新成功后更新 Token然后遍历队列用新 Token 重发所有等待的请求。这样无论同时有多少个请求返回 401都只会发一次刷新请求。

Q3为什么需要 AES 加密?

这个平台涉及安全态势数据,有一定的敏感性。后端返回的数据在传输层用 AES-128-CBC 加密,前端在响应拦截器中自动解密。加密密钥和 IV 是硬编码在前端代码中的生产环境会用更安全的方式管理。加密流程对业务代码完全透明——API 函数只是调用 request(),加密解密都在拦截器里自动完成。

Q4reqCatch 和 reqCatchV2 有什么区别?

都是 try/catch 的通用封装,避免每个页面都写 try/catch。区别在于reqCatch 静默捕获错误,返回 { data, error } 结构,让调用方自己判断。reqCatchV2 在捕获错误后还会通过事件总线 emitEvent('responseError', e) 通知全局——这主要用于路由守卫里,发生 403/404 时通过事件触发全局的页面跳转。新代码基本都用 V2。

Q5API 缓存降级是怎么运作的?

这是一套自研的请求降级系统。基于 IndexedDB 做持久化缓存。请求成功时自动缓存响应数据;请求失败时判断是否在熔断时间内——如果是,直接读缓存返回,用户看到的是旧数据而不是错误页面。还有最大缓存条数限制和过期清理机制。通过 storage-apis.ts 配置哪些接口走缓存策略,可以设置超时、重试次数、熔断时间等参数。

Q6HTTP 错误怎么统一处理的?

在响应拦截器里统一处理。根据 HTTP 状态码映射不同的行为400 返回参数错误提示、401 触发 Token 刷新或登出、403 触发页面刷新、404 静默不提示、5xx 显示服务端错误提示。错误提示用 dealWarning 函数包裹了 200ms 防抖,避免短时间内多个请求同时失败时弹出多条错误消息。还有 customError: true 参数,允许特定请求跳过默认错误处理,由调用方自行处理。


八、关键数字(面试时用)

数据 数值
全平台 API 函数总数 641 个
API 文件数 29 个
态势感知金银湖API 数 106 个
态势感知看板接口situation.ts 50 个
请求基础设施代码量 request.ts 273 行 + degradeInterceptor.ts 360 行 + catch.ts 46 行 + status.ts 76 行
Token 刷新机制 refreshing 锁 + Promise 队列,确保多并发 401 只刷新一次
加密方式 AES-128-CBC密钥 mXzfCSPBKmEA8aLqIV tbeJJLC6dZQXXtWr
请求超时 30 秒
错误提示防抖 200ms

九、涉及的源码文件(需要看的时候查)

做什么 文件在哪
请求核心基础设施 src/utils/request.ts273 行Axios、拦截器、加密、Token 刷新)
API 缓存降级系统 src/utils/degradeInterceptor.ts360 行IndexedDB 缓存、熔断、重试)
错误捕获包装函数 src/utils/catch.tsreqCatch + reqCatchV2
HTTP 状态码处理 src/utils/status.tsdealWarning + showMessage
缓存配置 src/api/storage-apis.ts(哪些接口走缓存策略)
缓存存储层 src/utils/apiStorage.tsIndexedDB 封装)
态势感知看板 API50 端点) src/api/jyh/situation.ts
金银湖核心 API45 端点) src/api/jyh/index.ts
金银湖首页 API5 端点) src/api/jyh/home.ts
金银湖认证 API3 端点) src/api/jyh/auth.ts
仓库管理 API161 端点) src/api/repo/index.ts
组织管理 API55 端点) src/api/org/index.ts
用户管理 API55+ 端点) src/api/user/index.ts
合并请求 API62 端点) src/api/merge/index.ts
讨论系统 API50 端点) src/api/discussion/index.ts