This repository has been archived on 2026-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
new-api-key-bench/docs/bench交互设计文档.md
T
skycurtain c5f7d93d67 feat(api): 实装三个公开接口——groups/perf-metrics/rankings(全站视角数据)
- 新增 /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 四绿
2026-09-10 00:47:30 +08:00

202 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 | — |