# 前端 API 接口格式统一性审查报告 > 审查时间:2026-06-09 > 范围:`frontend/` 下所有源码(`unpackage/dist` 等编译产物已排除) > 核心封装器:`frontend/utils/api.js` 的 `request(options)` --- ## 0. TL;DR 整体架构是统一的:所有 JSON 接口走同一 `request()` 入口,token 注入、401 跳转、响应解析、Mock 短路都集中在 `utils/api.js`。 **主要问题不在封装本身,而在一批"绕过封装"的散点**: - **OSS 上传/下载**没有沉淀到 `api.js`,导致 5 个页面 + 3 个 utils 各自实现一遍 - **20+ 处业务侧**重复判断 `code === 200`(封装器已保证) - **2 处历史遗留** URL 仍走旧版 `/api/user/*`(无 `/v1`) - **3 个模块** 自己造了 `request` 风格的 `uni.request` 轮子 优先级 P0:把 OSS 直传抽到 `utils/api.js`,可一次性消掉 30+ 处重复代码。 --- ## 1. 已统一的良好部分 ✅ ### 1.1 入口封装 — 95% 的 JSON 接口走 `request()` - 唯一封装器:`frontend/utils/api.js` 的 `request(options)`(L53–L134) - `frontend/utils/api.js` 内 60+ 个 API + `frontend/utils/task-api.js` 的 11 个 API 全部走 `request()` - 全项目 **46 个文件** 直接 import `@/utils/api` 或 `@/utils/task-api`,**无相对路径污染** ### 1.2 Token 注入 - 自动从 `uni.getStorageSync('access_token')` 读取 - Header 格式统一为 `Authorization: Bearer ` - 白名单(不注入 token):`/api/v1/auth/login|register|send-code|verify-code` ### 1.3 业务响应结构 后端约定:`{ code, message, data }`,封装器在 `request()` 内统一处理: | 输入 | 行为 | |---|---| | `HTTP 200/202` + `code === 200` | resolve 整体响应 | | `HTTP 200/202` + `code === 401/400/403` | 清 token + 跳登录 + reject | | `HTTP 200/202` + 其他 `code` | reject(`Error(message)`) | | `HTTP 200/202` + 无 `code` 字段 | resolve(`res.data`) ← 兜底 | | `HTTP 401` | 清 token + 跳登录 + reject | | 其他 HTTP 状态码 | reject(`Error(message || '请求失败 (statusCode)')`) | ### 1.4 环境与 Mock 开关 - `VITE_API_BASE_URL` / `VITE_USE_MOCK_API` 集中读取 - `IS_MOCK_API` 暴露给业务做短路 - `getWebSocketBaseUrl` / `getSegmentApiBaseUrl` / `getLaserApiBaseUrl` 三个 baseURL 工厂统一 ### 1.5 Dashboard 模块有专门封装 `dashboardApi` 对象 + `dashboardRequest()` 工厂(`utils/api.js` L999–L1007),统一了 mock 短路与 baseURL 前缀。 ### 1.6 WebSocket 入口统一 `utils/socket/SocketManager.js` 通过 `getWebSocketBaseUrl()` 工厂函数拿到 baseURL(L48–L49),token 拼在 query 上(`?token=Bearer_`),鉴权方式与 HTTP 通道保持一致。 --- ## 2. 不一致点(按严重度排序) ### 🔴 严重:页面层直连 `uni.request` / `uni.uploadFile` / `fetch` 下列文件**绕过了 `request()` 封装**,需要各自维护 token 注入、超时、错误处理、401 跳转。表中行号定位到调用点。 | 文件 | 行号 | 用途 | 风险点 | |---|---|---|---| | `pages/discover/discover.vue` | L101, L134 | OSS PostObject | 重复实现 fetch + uploadFile 分支 | | `pages/discover/generation-result.vue` | L316, L418, L495, L653 | OSS + 上传 | 4 处独立直传,逻辑高度相似 | | `pages/castlove/index.vue` | L261, L409 | 图片下载/上传 | 自行处理 token | | `pages/castlove/create.vue` | L430, L527, L540, L569, L614 | 全链路直传 | 5 处,逻辑重复 | | `pages/castlove/lenticular/lenticular-create.vue` | L331, L427, L440, L468, L512 | 全链路直传 | 5 处 | | `pages/castlove/lenticular/lenticular-result.vue` | L217, L319, L396, L554 | 全链路直传 | 4 处 | | `pages/square/components/WaterfallGrid.vue` | L712, L735, L777, L832 | 业务请求 | 4 处 `res.code === 200` | | `pages/square/components/HotCategoryBlock.vue` | L193 | 业务请求 | `code === 200` 重复判断 | | `pages/profile/profile.vue` | L1187 | 头像上传 | 自处理 statusCode | | `pages/profile/setNickname.vue` | L183 | 头像上传 | 自处理 statusCode,**无 token 注入** | | `pages/starbook/items.vue` | L136 | 业务请求 | `response.code === 200` | ### 🟡 中等:业务侧重复判断 `code === 200` `request()` 已经对 `code === 200` resolve 整体,业务侧**不应**再判断。下列位置违反封装契约: ``` utils/progress-manager.js:180 utils/activity-config.js:46, 69, 87, 105, 126 utils/craftMintSubmit.js:481, 510, 627, 690 pages/square/components/WaterfallGrid.vue:712, 735, 777, 832 pages/square/components/HotCategoryBlock.vue:193 pages/starbook/items.vue:136 pages/discover/discover.vue:195, 223 pages/discover/generation-result.vue:284 composables/useLaserMint.js:42 ``` ### 🟡 中等:部分模块自己造 `request` 风格轮子 | 文件 | 位置 | 说明 | |---|---|---| | `composables/useLaserDifyGenerate.js` | L19–L47 | 独立 `laserRequest()`,自行注入 token、超时、状态码判断,文件内注释自承 "返回格式与 api.js 的 request() 一致" | | `utils/laser-card/segmentationCloud.js` | L21–L39 | `uniRequest()` 重复实现,仅做状态码判断 | | `utils/laser-card/aliyunPortraitUni.js` | L273 | `uni.request` 走 OSS PostObject,因 OSS 协议需要单独处理 `x-oss-date` 等特殊头 | | `utils/craftMintSubmit.js` | L21, L57, L70, L399, L407, L423, L451 | 5 处 `uni.uploadFile`/`fetch` 混用,H5/App 分支自管 | > 这些位置**有合理理由**绕过 `request()`(OSS 直传需自定义头、需 `multipart/form-data`),但应**抽到 `utils/api.js`** 而不是散布在业务模块。 ### 🟡 中等:URL 风格不一致 虽然都带 `/api/v1` 前缀,但**功能模块 URL 风格混乱**: - 资源 URL:`/api/v1/mygalleries`(无 `/me` 也没有 `/users`)vs `/api/v1/galleries/:uid` vs `/api/v1/galleries/random`(`utils/api.js` L431–L452) - 历史遗留:`updateUserInfoApi` L215 仍走 `/api/user/update`(无 `/v1`),`deleteAccountApi` L276 走 `/api/user/delete-account` - 单复数、是否用 `/me` 表示当前用户不统一: - `/api/v1/auth/me` - `/api/v1/me/nickname` - `/api/v1/me/avatar` - `/api/v1/me/liked-assets` - `/api/v1/me/exhibited-assets` - `/api/v1/users/:uid/liked-assets` ### 🟢 轻微:拼装 URL 的方式不统一 - 多数用模板字符串拼在 `url` 内(`/api/v1/social/users?page=${page}&page_size=${pageSize}`,`utils/api.js` L226) - 也有用 `data` 字段传 query 的(GET 请求,如 `task-api.js` L15 `getDailyTasks` 把 `star_id` 放 `data` 字段,uni.request 在 GET 下会拼到 query) - 统一规范缺失,新人很容易混用 ### 🟢 轻微:`request` 默认 timeout 与 `uni.uploadFile` 超时不一致 - `request()` 默认 60000ms(L78) - `segmentPortraitApi` 用 120000ms(L749) - `downloadToLocal` 用 120000ms(`composables/useLaserSegment.js` L29) - 没有集中常量 --- ## 3. 统计摘要 | 指标 | 数值 | |---|---| | 直接 import `@/utils/api` 的文件 | 46 | | 仍走 `uni.request` 直连的文件 | 7 | | 仍走 `uni.uploadFile` 直连的文件 | 8 | | 仍走 `fetch()` 直连的文件 | 5 | | 业务侧 `code === 200` 二次判断处 | 20+ | | URL 路径里残留 `/api/user/*` 旧接口 | 2 | | 重复实现的 `request` 风格封装 | 3(`laserRequest`、`uniRequest`、`laserRequest`) | --- ## 4. 修复建议(按 ROI 排序) ### P0 — 把"绕过封装"的 OSS 直传抽到 `api.js` 新增一组 OSS 专用工具到 `utils/api.js`: ```js // 上传 OSS (H5: fetch + FormData / App: uni.uploadFile) export function uploadToOss({ host, dir, filePath, policy, signature, ... }) { ... } // 下载 OSS (H5: fetch + blob URL / App: uni.downloadFile) export function downloadFromOss(signedUrl) { ... } ``` **收益**:消掉 5 个页面、3 个 utils 中重复的 30+ 处直传代码;统一 token 注入;统一超时;统一错误处理。 ### P1 — 修复 `/api/user/*` 旧路径 - `updateUserInfoApi`(L215):`/api/user/update` → 对应的 v1 路径 - `deleteAccountApi`(L276):`/api/user/delete-account` → `/api/v1/me/account` 之类 ### P1 — 业务侧禁用 `code === 200` 二次判断 封装器已经处理,业务层应只 try/catch。可在 `request()` 里加注释: ```js // 业务侧请勿重复判断 code === 200,封装器已保证 resolve 时 code === 200 ``` ### P2 — URL 风格规范化 建议对齐: - 当前用户 → `/me`(已用):`/api/v1/me/liked-assets`、`/api/v1/me/exhibited-assets` 等等 - 指定用户 → `/users/:uid`:`/api/v1/users/:uid/liked-assets` - 收藏/资源 → 单数主语,操作作子路径:`/api/v1/galleries/...` 而不是 `/api/v1/mygalleries` ### P2 — 提取 `api.js` 默认 timeout 常量 ```js const DEFAULT_TIMEOUT = 60000 const UPLOAD_TIMEOUT = 120000 export const API_TIMEOUT = { default: DEFAULT_TIMEOUT, upload: UPLOAD_TIMEOUT, download: UPLOAD_TIMEOUT, } ``` ### P3 — 拼装 query 的工具函数 ```js export function buildQuery(params) { return '?' + new URLSearchParams(params).toString() } ``` 让所有 `getXxxApi` 走同一方式,而非分散在 `url`/`data` 两处。 --- ## 5. 后续 Action Items | 优先级 | 任务 | 涉及文件数 | 预估工时 | |---|---|---|---| | P0 | 把 OSS 上传/下载抽到 `utils/api.js` | 8 | 4h | | P1 | 修复 `/api/user/*` 旧路径 | 2 | 0.5h | | P1 | 业务侧清除 20+ 处 `code === 200` 二次判断 | 10+ | 1h | | P2 | URL 风格规范化 + `/me` 对齐 | 20+ | 2h | | P2 | 提取 `API_TIMEOUT` 常量 | 3 | 0.5h | | P3 | 引入 `buildQuery` 工具函数 | 30+ | 1h |