## Video-Flow API 使用指南(方案一:保持现状,统一规范与示例) > 目标:在不改动现有代码结构与签名的前提下,提供清晰的一致性用法、错误处理约定与多场景接入示例。 ### 目录结构与职责 - `constants.ts`:基础配置(`BASE_URL` 从 `NEXT_PUBLIC_BASE_URL` 注入) - `request.ts`:Axios 实例与拦截器、通用 `get/post/put/del`、`stream`(SSE风格下载进度)、`downloadStream`、`streamJsonPost` - `errorHandle.ts`:错误码映射与统一提示、特殊码处理(如 401 跳转登录、402 不弹提示) - `common.ts`:通用类型与与上传相关的工具(获取七牛 Token、上传) - `resources.ts`:示例资源接口封装(基于 `post`) - 其他业务模块(如 `video_flow.ts`、`export-adapter.ts` 等)各自封装具体接口/逻辑 ### 统一调用规范 1. 使用 `request.ts` 提供的 `get/post/put/del` 包装函数发起请求,返回后端响应体(已通过响应拦截器做业务码检查)。 2. 业务成功码:`code === 0` 或 `code === 202`(长任务/排队等需要前端自行处理状态)。若非成功码,拦截器会调用 `errorHandle` 并 `Promise.reject`。 3. 认证:前端从 `localStorage.token` 注入 `Authorization: Bearer `,请确保登录流程写入 `token`。 4. 基础地址:通过环境变量 `NEXT_PUBLIC_BASE_URL` 注入,构建前需设置。 ### 错误处理约定 - `errorHandle(code, message?)`:统一弹出错误(402 除外),并处理特殊码: - `401`:清除 `localStorage.token` 并跳转 `/login` - `402`:由 `request.ts` 响应拦截器在收到后端 `402` 且带 `detail` 时触发通知(积分不足),并拒绝请求 - 网络/解析类错误:`handleRequestError` 会降级为通用错误提示并抛出异常 ### 常用请求示例 ```ts import { get, post, put, del } from '@/api/request'; type User = { id: number; name: string }; type ApiResponse = { code: number; successful: boolean; message: string; data: T }; // GET const user = await get>('/user/detail?id=1'); // POST const created = await post>('/user/create', { name: 'Evan' }); // PUT await put>('/user/update', { id: 1, name: 'Kent' }); // DELETE await del>('/user/remove?id=1'); ``` ### 流式与下载(SSE/文件) 1) SSE/渐进消息(基于 Axios 下载进度事件解析 `data: {}` 行) ```ts import { stream } from '@/api/request'; await stream<{ type: string; message?: string; progress?: number }>({ url: '/api/video-flow/export/ai-clips', method: 'POST', data: { /* your body */ }, onMessage: (evt) => { if (evt.type === 'progress') { // 更新进度条 } }, onError: (err) => { /* 兜底错误处理 */ }, onComplete: () => { /* 完成回调 */ } }); ``` 2) 文本行分隔的 JSON 流(`fetch` + `ReadableStream`,每行一个 JSON) ```ts import { streamJsonPost } from '@/api/request'; await streamJsonPost('/sse/json', { /* body */ }, (json) => { // 每次解析到一行 JSON }); ``` 3) 文件下载(Blob) ```ts import { downloadStream } from '@/api/request'; await downloadStream('/download/file', 'result.mp4'); ``` ### 多场景接入指南 #### 浏览器前端(React/Next.js CSR) - 直接使用 `get/post/put/del`;确保登录后将 `token` 写入 `localStorage` - 环境变量:在 `.env.local` 配置 `NEXT_PUBLIC_BASE_URL` - 错误提示:由 `errorHandle` 统一处理;402 会展示积分不足通知 #### Next.js Route Handler(服务端 API) - 如需在 Route Handler 内调用后端接口:建议使用服务端 `fetch` 并自行附加服务端凭证;避免直接依赖 `localStorage` - 若需要转发到现有后端并复用通知/事件流格式,可参考 `api/export-adapter.ts` 的 SSE 写法 #### Next.js Server Components/SSR - 服务端不具备 `localStorage`,如需鉴权请改为从 Cookie/Headers 传递 token,并在转发时设置 `Authorization` - 服务器端可直接使用 `fetch(BASE_URL + path, { headers })` #### Node/Serverless(Vercel/Cloudflare) - 同 SSR 原则:不使用 `localStorage`;从密钥存储中读取后以请求头传递 - SSE 转发时,按 `text/event-stream` 写入 `ReadableStream` 并传递 `data: \n\n` ### 最佳实践 - 统一通过 `request.ts` 发起请求,不绕过拦截器;对长任务/排队返回 `202` 时在页面层处理轮询或 SSE - 上传流程:使用 `common.ts/getUploadToken` 获取 token 与 domain,再使用 `uploadToQiniu` 上传,监听进度 - 将后端的业务错误码保持为服务端规范,在前端只做展示与关键码分支(401/402) ### 常见问题(FAQ) - Q: 为什么拿不到 `localStorage` 中的 token? - A: 服务端渲染或 Route Handler 中无 `localStorage`,需通过 Cookie/Headers 传递。 - Q: 返回 `code !== 0/202` 时如何自定义处理? - A: 可在业务层 `try/catch` 捕获 `post/get` 的拒绝 Promise,并根据 `error.message` 或后端 `data.message` 做分支。 - Q: SSE 收不到事件? - A: 确认后端使用 `text/event-stream`,每条消息以 `data: \n\n` 输出;前端在 `onDownloadProgress` 中按行解析。 ---