Archived
- 新增 /api/user/groups:用户分组与倍率(未启用分组时为空对象),与 /api/pricing group_ratio 互补 - 新增 /api/perf-metrics/summary:全站模型服务质量(延迟/成功率/TPS/小时级序列),hours 参数;对拼车工具补「模型健康度」视角 - 新增 /api/rankings:全站模型用量排行(名次/份额/环比增长),period 参数;车长选主推模型的数据依据 - 三者均 authMode none,公开接口组置于清单最前;/api/status 的「唯一」表述改为公开接口组之一 - spec 断言 6→9(路径顺序、鉴权分布 none×4+strict×3+lenient×2);交互设计文档 §1 清单重排补全站视角定位说明;README 计数更新 - 验证:test 89/89 + type-check + lint + build 四绿
202 lines
13 KiB
Markdown
202 lines
13 KiB
Markdown
# bench 交互设计与施工方案
|
||
|
||
> new-api-key-bench:NewAPI sk-key 接口的「文档 + 调试」二合一前端工具。
|
||
>
|
||
> 本文档是施工蓝图:所有交互与技术决策已定案,按此施工,不再讨论结构性问题。
|
||
|
||
---
|
||
|
||
## 1. 项目定位
|
||
|
||
替代 Postman 的专用调试台(bench):左侧选接口 → 右侧 tab 内看文档、填 key、发请求、看原始响应。对接 9 个接口,服务 karpool 拼车项目的接口调试与换算验证。
|
||
|
||
**接口清单**(数据源:`docs/NewAPI-sk-key接口文档.md`,按此顺序展示,不分组):
|
||
|
||
| 序 | 路径 | 鉴权 | 响应特征 |
|
||
|---|------|------|---------|
|
||
| 0 | `GET /api/user/groups` | 无(完全公开) | 用户分组与倍率列表;未启用分组时为空对象 |
|
||
| 1 | `GET /api/perf-metrics/summary` | 公开门控(默认无需凭证) | 全站模型服务质量:延迟/成功率/TPS/小时级序列;`hours` 参数 |
|
||
| 2 | `GET /api/rankings` | 公开门控(默认无需凭证) | 全站模型用量排行:名次/份额/环比增长;`period` 参数 |
|
||
| 3 | `GET /api/status` | 无(完全公开) | 站点配置与计费参数:`quota_per_unit`(换算比率权威来源)、`quota_display_type`、汇率等 |
|
||
| 4 | `GET /v1/dashboard/billing/subscription` | TokenAuth(严格) | 小 JSON,美元额度(无限额时 1 亿占位) |
|
||
| 5 | `GET /v1/dashboard/billing/usage` | TokenAuth(严格) | 小 JSON,累计美分 |
|
||
| 6 | `GET /v1/models` | TokenAuth(严格) | 中等 JSON,模型数组 |
|
||
| 7 | `GET /api/usage/token` | TokenAuthReadOnly(宽松) | 小 JSON,累计 quota |
|
||
| 8 | `GET /api/log/token` | TokenAuthReadOnly(宽松) | **大响应**:最近 1000 条日志,pretty-print 2~4MB |
|
||
|
||
> 0~3 完全公开或默认公开(面板隐藏 sk-key 输入行);7、8 宽松鉴权(key 过期/耗尽仍可查);4~6 严格鉴权(key 无效直接被拒)。tab 内标出该差异,避免误判「接口坏了」。
|
||
> 7 与 5 构成换算交叉验证对(total_used ÷ 500000 ≈ total_usage ÷ 100),Tabs 并行对账是核心工作流。3 是换算比率的权威来源(quota_per_unit 可被管理员修改,500000 只是默认值)。1、2 是「全站视角」数据——单个 sk-key 日志看不到的服务质量与用量排行,服务模型健康度判断。
|
||
|
||
---
|
||
|
||
## 2. 布局
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────────────────────┐
|
||
│ new-api-key-bench (暗色默认) │
|
||
├──────────────────────────┬─────────────────────────────────────────────────┤
|
||
│ 接口列表 │ ┌──────────────┬──────────────┬───────────┐ │
|
||
│ │ │/v1/dashboar… │/api/log/token│/api/usage…│ × │
|
||
│ /v1/dashboard/billing/ │ ├──────────────┴──────────────┴───────────┴──┤ │
|
||
│ subscription │ │ GET /v1/dashboard/billing/subscription │ │
|
||
│ /v1/dashboard/billing/ │ ├─────────────────────────────────────────────┤ │
|
||
│ usage │ │ sk-key ┌─────────────────────────┐ │ │
|
||
│ /v1/models │ │ │ sk-xxxxxxxxxxxx 👁 │ [发送请求]│ │
|
||
│ /api/usage/token │ │ └─────────────────────────┘ │ │
|
||
│ /api/log/token │ ├─────────────────────────────────────────────┤ │
|
||
│ │ │ 200 OK · 156ms · 234 KB [复制] [⤓] │ │
|
||
│ │ │ ┌─────────────────────────────────────────┐ │ │
|
||
│ │ │ │ 1 { │ │ │
|
||
│ │ │ │ 2 "object": "billing_subscription", │ │ │
|
||
│ │ │ │ 3 "hard_limit_usd": 100000000, │ │ │
|
||
│ │ │ │ (虚拟滚动 JSON · DOM 恒 ~40 行) │ │ │
|
||
│ │ │ └─────────────────────────────────────────┘ │ │
|
||
└──────────────────────────┴─────────────────────────────────────────────────┘
|
||
~300px sider(纯路径列表, 自适应(tab 内自带 sk-key 框)
|
||
无分组无编号,按清单顺序)
|
||
```
|
||
|
||
- **左 sider**(~300px):接口列表,**不分组、不编号**,按 §1 清单顺序平铺 9 个路径;选中态高亮
|
||
- **右主区**:`t-tabs` 顶部,激活 tab 内容 = 请求行(method + 路径 + 鉴权徽标)→ 调试行(sk-key 输入 + 发送按钮)→ 响应区(元信息 + 虚拟滚动 JSON)
|
||
- 暗色默认(TDesign dark theme + CSS 变量)
|
||
|
||
---
|
||
|
||
## 3. 交互细则
|
||
|
||
### 3.1 Tabs
|
||
|
||
- 点击 sider 接口:该接口**无 tab 则新开,有则激活**(Postman 语义)
|
||
- 关闭 tab:丢弃该 tab 状态(响应缓存随之释放);若关的是激活 tab,激活相邻 tab;全部关光 → 空态引导页(提示从左侧选择接口)
|
||
- tab 标签直接显示接口路径(如 `/v1/models`、`/api/log/token`),与 sider 列表一致;宽度不够时省略号截断
|
||
- tab 上限即 5(接口总数),无需溢出策略
|
||
|
||
### 3.2 sk-key(每 tab 独立)
|
||
|
||
- 每个 tab 自带 key 输入框(`t-input` type=password + 显隐切换),互不影响,可并行调试多把 key
|
||
- **不做持久化**:key 仅存 Pinia 内存——切 tab 不丢,F5 刷新随整页状态一并重置(tabs、key、响应缓存同生共死,语义干净);不写 localStorage / sessionStorage,sk-key 不落盘
|
||
|
||
### 3.3 请求
|
||
|
||
- 纯手动:点「发送」才发请求;TanStack Query `staleTime: Infinity`,切走再切回 tab **不自动重发**
|
||
- queryKey:`[endpointId, skKey]`——同 tab 换 key 视为新查询;不同 tab(不同 key)缓存天然隔离
|
||
- 发送前校验:key 为空 → 输入框标红提示,不发请求
|
||
- 请求中按钮 loading;响应元信息记录:状态码、耗时 ms、响应体字节数
|
||
|
||
### 3.4 响应区
|
||
|
||
- **虚拟滚动 JSON 文本视图**(对齐 DevTools Network Response):
|
||
- `JSON.stringify(data, null, 2)` → 按 `\n` 切行 → 虚拟滚动窗口渲染
|
||
- 行号列 + 等宽字体;**语法高亮仅对可视区行**做正则着色(行级懒高亮)
|
||
- DOM 恒定 ~40 行,1000 条日志滚动帧率与 10 条无异
|
||
- JSON 解析失败(非 JSON 响应)→ 原样文本展示,不高亮
|
||
- 空态(未发送):占位提示「填写 sk-key 后发送请求」
|
||
- 错误态:网络错误(fetch reject)与 HTTP 非 2xx 分开展示;非 2xx 时仍展示响应体(调试 401/403 场景是刚需)
|
||
- 操作:复制(格式化 JSON)、下载 `.json` 文件
|
||
|
||
---
|
||
|
||
## 4. 数据与状态设计
|
||
|
||
### 4.1 端点元数据(静态)
|
||
|
||
`src/api/endpoints.ts`——接口定义(按清单顺序)、鉴权方式、注意事项文案、示例响应。纯数据无逻辑,不含分组概念。
|
||
|
||
### 4.2 Pinia stores
|
||
|
||
- `useBenchStore`:`openTabs: EndpointId[]`(开启顺序)、`activeTab: EndpointId | null`;tab 的 key 由 queryKey 直接承载,无需单独 store
|
||
- sk-key 不做独立 store 与持久化:作为 EndpointPanel 内的组件局部状态(`ref`),生命周期与 tab 内容一致,随刷新重置
|
||
|
||
### 4.3 请求层
|
||
|
||
- `src/api/client.ts`:薄 fetch wrapper——拼 Authorization 头、计时、返回 `{ status, ms, bytes, text, json }`;无拦截器无重试(调试工具要看到真实失败)
|
||
- `src/api/queries.ts`:TanStack Query 接入,`useQuery({ queryKey: [epId, skKey], enabled: 手动触发 })`
|
||
|
||
### 4.4 组件结构
|
||
|
||
```
|
||
src/
|
||
├── api/
|
||
│ ├── endpoints.ts # 5 接口静态元数据
|
||
│ ├── client.ts # fetch wrapper(计时/字节数/解析)
|
||
│ └── queries.ts # TanStack Query 查询定义
|
||
├── stores/
|
||
│ └── bench.ts # tabs 状态
|
||
├── components/
|
||
│ ├── side-nav.vue # 左侧接口菜单(平铺路径列表)
|
||
│ ├── request-tabs.vue # t-tabs 容器(开/关/激活)
|
||
│ ├── endpoint-panel.vue # 单接口完整面板(请求行+调试+响应)
|
||
│ ├── doc-card.vue # 请求行/鉴权徽标/注意事项(tab 内文档信息)
|
||
│ └── json-text-view.vue # 虚拟滚动 JSON 文本视图(核心性能组件)
|
||
├── lib/
|
||
│ └── jsonView.ts # 行窗口计算、行级高亮(纯函数,vitest 覆盖)
|
||
├── App.vue # 双栏布局骨架
|
||
└── main.ts # 装配 pinia / vue-query / tdesign / router
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 工程配置
|
||
|
||
### 5.1 代理(vite.config.ts)
|
||
|
||
```ts
|
||
const GATEWAY = process.env.GATEWAY ?? 'https://ai-gateway.furtherverse.com'
|
||
|
||
server: {
|
||
proxy: {
|
||
'/api': { target: GATEWAY, changeOrigin: true },
|
||
'/v1': { target: GATEWAY, changeOrigin: true },
|
||
},
|
||
}
|
||
```
|
||
|
||
只配 `/api`、`/v1` 两条(5 接口仅命中此前缀);`GATEWAY` 环境变量可覆盖。`changeOrigin: true` 改写 Host 头,网关虚拟主机路由才正确。
|
||
|
||
### 5.2 质量
|
||
|
||
- **命名规范**:文件名一律 kebab-case 且**语义化完整,宁长勿缩写**(如 `json-text-view.vue` 而非 `jtv.vue`,`endpoint-panel.vue` 而非 `ep.vue`;变量/函数同理,`endpointList` 优于 `epList`);模板中引用组件用大驼峰(`<SideNav />`、`<JsonTextView />`)
|
||
- prettier:`semi: true`、单引号、100 宽(已生效并提交)
|
||
- oxlint + eslint 双引擎(脚手架自带链路)
|
||
- vitest:纯函数层测试(`lib/jsonView.ts` 的窗口/高亮计算、`client.ts` 的 URL 与头拼装);`.spec.ts` 与源码同目录
|
||
- `build` 保持 `type-check && build-only`(脚手架默认)
|
||
|
||
### 5.3 依赖(已安装)
|
||
|
||
`tdesign-vue-next@1.20.7`、`@tanstack/vue-query@5.102.8`、`@vueuse/core@14.4.0`、`vitest@5.0.0` + 脚手架自带(vue-router / pinia / oxlint / eslint / prettier)
|
||
|
||
> **VueUse 使用边界**(避免与既有职责重叠):`json-text-view.vue` 用 `useElementSize`(容器尺寸监听)、`useThrottleFn`(滚动节流);响应区复制按钮用 `useClipboard`。loading/error 态归 TanStack Query,tab 状态归 Pinia,输入显隐归 TDesign——这些场景**不用** VueUse。
|
||
|
||
> vue-router:空 routes 保留(单页应用,路由层暂不启用,留作后续扩展位)
|
||
|
||
---
|
||
|
||
## 6. 施工清单(按序)
|
||
|
||
1. vite.config 补代理 + vitest include;`src/` 清模板残留(counter store、HelloWorld 引用)
|
||
2. `api/endpoints.ts` 静态数据 + `lib/jsonView.ts` 纯函数(含单测)
|
||
3. `stores/bench.ts`(tabs 状态)+ `api/client.ts` + `api/queries.ts`(sk-key 为 EndpointPanel 局部状态,无持久化)
|
||
4. App 骨架 + SideNav + RequestTabs + EndpointPanel + DocCard
|
||
5. JsonTextView 虚拟滚动(大响应性能验收:`/api/log/token` 1000 条日志不卡顿)
|
||
6. 暗色主题落地 + 空态/错误态打磨 + 复制/下载
|
||
7. 全量验证:lint + type-check + build + test,真机调通 5 接口
|
||
|
||
---
|
||
|
||
## 7. 决策记录(已闭环,不再开放)
|
||
|
||
| 决策点 | 定案 | 备注 |
|
||
|--------|------|------|
|
||
| 组件库 | TDesign | Table/虚拟滚动成熟、腾讯周更维护、暗色 CSS 变量 |
|
||
| 请求层 | TanStack Query + fetch | 不引 axios,避免双层抽象 |
|
||
| JSON 展示 | 虚拟滚动文本视图 | 不解析业务结构(Postman/DevTools 先例,解析是正式项目的职责) |
|
||
| 布局 | 左 sider + 右 tabs | Postman 形态;sider 平铺 5 路径,不分组不编号,tab 标签即路径 |
|
||
| tab 内文档 | 鉴权徽标 + 注意事项 | 说明内容都在 tab 页里,sider 只管路径 |
|
||
| key 输入 | 每 tab 独立 | queryKey 天然隔离 |
|
||
| key 持久化 | 不做 | 仅 Pinia 内存(tab 组件局部 ref),刷新重置,不落盘 |
|
||
| 请求语义 | 手动 + staleTime Infinity | 调试工具的响应不该自己变 |
|
||
| 排除清单 | 不做 | 知识留在 docs/ 接口文档 |
|
||
| 主题 | 暗色默认 | — |
|
||
| 命名规范 | 文件 kebab-case 且语义化(宁长勿缩写)/ 模板引用大驼峰 | 见 §5.2 |
|
||
| 目录名 | new-api-key-bench | — |
|