Files

316 lines
10 KiB
Markdown
Raw Permalink 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.
# 问题反馈 API 接口定义
## 1. 文档状态
本文档定义 CozSweet 问题反馈提交接口,供后端实现和前后端联调使用。
前端已经按本文协议完成以下功能:
- 反馈分类和文字提交;
- 最多 3 张截图;
- 图片压缩和格式限制;
- Login Token 与 Guest Token 认证;
- 提交成功后展示 `feedbackId`
后端需要实现:
```http
POST /api/feedback
```
环境地址:
| 环境 | API Base URL |
| --- | --- |
| test 测试环境 | `https://testapi.banlv-ai.com` |
| pro 预发环境 | `https://proapi.banlv-ai.com` |
| production 生产环境 | `https://api.banlv-ai.com` |
## 2. 身份认证
接口需要 Bearer Token,支持以下两种身份:
1. 正式用户 Login Token
2. 游客 Guest Token。
```http
Authorization: Bearer <TOKEN>
```
后端必须从 Token 中解析用户或游客身份,不接受前端传递 `userId``guestId` 或其他身份字段。
Token 缺失、无效或过期时返回 HTTP `401`
## 3. 请求定义
### 3.1 请求地址
```http
POST <API_BASE_URL>/api/feedback
```
### 3.2 Content-Type
请求使用:
```http
Content-Type: multipart/form-data; boundary=<AUTO_GENERATED_BOUNDARY>
```
调用方不能手工固定 `boundary`。浏览器会根据 FormData 自动生成完整 Content-Type。
### 3.3 Multipart 字段
| 字段 | 类型 | 是否必填 | 说明 |
| --- | --- | --- | --- |
| `category` | string | 是 | 反馈分类,只允许标准枚举值。 |
| `content` | string | 是 | 用户反馈正文,去除首尾空白后长度为 10–2000。 |
| `context` | string | 是 | JSON 字符串,结构见下文。 |
| `images` | file[] | 否 | 重复字段,最多 3 张图片。没有图片时不发送该字段。 |
标准 `category`
| 值 | 含义 |
| --- | --- |
| `problem` | 功能异常或使用问题 |
| `suggestion` | 产品建议 |
| `payment` | 充值、订阅或支付问题 |
| `other` | 其他反馈 |
不支持分类别名。非法分类返回 HTTP `400`
### 3.4 Context JSON
`context` 在 multipart 中是一个 JSON 字符串:
```json
{
"appVersion": "0.1.0",
"platform": "android Android 15",
"browser": "facebook IAB / Chrome 135.0",
"viewport": "392x760@2.75"
}
```
| 字段 | 类型 | 最大长度 | 说明 |
| --- | --- | ---: | --- |
| `appVersion` | string | 64 | 前端应用版本。 |
| `platform` | string | 128 | 平台、系统名称和版本。 |
| `browser` | string | 256 | 浏览器或应用内浏览器信息。 |
| `viewport` | string | 64 | CSS viewport 宽高和设备像素比。 |
后端只需要保存上述 4 个字段。不要要求前端增加 User-Agent、设备型号、IP、聊天内容、联系方式或 Token。
### 3.5 图片规则
| 规则 | 要求 |
| --- | --- |
| 数量 | 03 张 |
| 单文件大小 | 不超过 2 MiB,即 `2 * 1024 * 1024` bytes |
| 接受格式 | JPEG、PNG、WebP |
| 接受 MIME | `image/jpeg``image/png``image/webp` |
| 推荐请求总大小上限 | 8 MiB |
前端允许用户选择不超过 5 MiB 的源文件,但提交前会压缩到最长边不超过 1600px、目标大小不超过 2 MiB。后端仍需独立执行数量、大小、扩展名、MIME 和文件魔数校验,不能信任浏览器提供的文件名或 Content-Type。
建议后端移除 EXIF 等元数据,并将图片保存到私有存储桶。反馈截图不得使用永久公开 URL。
## 4. 请求示例
### 4.1 仅文字反馈
```bash
curl -X POST 'https://api.banlv-ai.com/api/feedback' \
-H 'Authorization: Bearer <TOKEN>' \
-F 'category=problem' \
-F 'content=The send button did not respond after I entered a message.' \
-F 'context={"appVersion":"0.1.0","platform":"android Android 15","browser":"Chrome 135.0","viewport":"392x760@2.75"}'
```
### 4.2 带多张截图
```bash
curl -X POST 'https://api.banlv-ai.com/api/feedback' \
-H 'Authorization: Bearer <TOKEN>' \
-F 'category=payment' \
-F 'content=The payment completed but my balance did not update.' \
-F 'context={"appVersion":"0.1.0","platform":"android Android 15","browser":"facebook IAB / Chrome 135.0","viewport":"392x760@2.75"}' \
-F 'images=@payment-screen-1.webp;type=image/webp' \
-F 'images=@payment-screen-2.jpg;type=image/jpeg'
```
## 5. 成功响应
成功时返回 HTTP `200`
```json
{
"success": true,
"message": "Feedback submitted",
"data": {
"feedbackId": "c5db8e32-60f0-4f47-81bb-c22254c0562f"
}
}
```
前端公开响应字段只有:
| 字段 | 类型 | 是否必填 | 说明 |
| --- | --- | --- | --- |
| `data.feedbackId` | string | 是 | 非空反馈编号,推荐使用 UUID。 |
前端不需要后端返回用户信息、反馈正文、图片地址、状态、创建时间或诊断上下文。
## 6. 失败响应
失败响应使用统一 envelope
```json
{
"success": false,
"message": "Feedback content must contain at least 10 characters",
"error": "INVALID_FEEDBACK_CONTENT"
}
```
建议状态码:
| HTTP 状态 | error | 场景 |
| ---: | --- | --- |
| `400` | `INVALID_FEEDBACK_CATEGORY` | 分类不在标准枚举中。 |
| `400` | `INVALID_FEEDBACK_CONTENT` | 正文为空或长度不在 10–2000。 |
| `400` | `INVALID_FEEDBACK_CONTEXT` | Context 不是合法 JSON 或字段不合法。 |
| `400` | `TOO_MANY_FEEDBACK_IMAGES` | 图片超过 3 张。 |
| `401` | `UNAUTHORIZED` | Token 缺失、无效或过期。 |
| `413` | `FEEDBACK_PAYLOAD_TOO_LARGE` | 单文件或请求总大小超过限制。 |
| `415` | `UNSUPPORTED_FEEDBACK_IMAGE` | 图片格式、MIME 或文件魔数不支持。 |
| `429` | `FEEDBACK_RATE_LIMITED` | 用户或游客提交过于频繁。 |
| `500` | `FEEDBACK_SUBMIT_FAILED` | 持久化或文件存储失败。 |
`message` 应当是可展示给用户的简短英文文本。不得在响应中返回堆栈、存储路径、数据库错误或内部实现信息。
## 7. 后端处理流程
推荐处理顺序:
1. 验证 Bearer Token,解析正式用户或游客身份。
2. 读取 multipart 字段并验证分类、正文和 Context。
3. 在读取完整文件前检查图片数量和请求大小。
4. 校验每张图片的真实格式、文件魔数、尺寸和大小。
5. 使用服务端生成的随机对象名写入私有存储。
6. 创建反馈记录并关联图片对象。
7. 返回非空 `feedbackId`
8. 任一步失败时清理本次已经上传但未关联的图片。
每个成功请求创建一条反馈记录。当前前端没有发送 Idempotency-Key,因此后端不能依赖客户端幂等键去重。
## 8. 推荐数据模型
反馈主记录最少需要:
| 字段 | 说明 |
| --- | --- |
| `id` | UUID,作为 `feedbackId` 返回。 |
| `user_id` | 正式用户 ID,可为空。 |
| `guest_id` | Guest 身份 ID,可为空。 |
| `category` | 标准反馈分类。 |
| `content` | 反馈正文。 |
| `context` | JSON/JSONB,只保存定义的 4 个字段。 |
| `status` | 内部处理状态,例如 `open``resolved`。不返回前端。 |
| `created_at` | 服务端创建时间。 |
图片记录最少需要反馈 ID、私有对象 key、MIME、字节大小、宽高和创建时间。不要把用户原始文件名作为存储对象名。
正式用户必须只写入 `user_id`Guest 必须只写入 `guest_id`。身份信息由 Token 派生。
## 9. 安全与日志
- 建议按用户或 Guest 身份限流,例如每小时不超过 10 次。
- 普通应用日志不得记录反馈正文、Token、图片内容、原始文件名或完整 Context。
- 可以记录 `feedbackId`、分类、图片数量、处理时长和错误码。
- 图片下载和后台查看必须经过授权,不允许匿名公开访问。
- 数据保留、删除和后台访问需要遵循现有隐私政策。
## 10. OpenAPI 参考
```yaml
paths:
/api/feedback:
post:
summary: Submit user feedback
security:
- bearerAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- category
- content
- context
properties:
category:
type: string
enum: [problem, suggestion, payment, other]
content:
type: string
minLength: 10
maxLength: 2000
context:
type: string
description: JSON-encoded FeedbackContext
images:
type: array
maxItems: 3
items:
type: string
format: binary
responses:
"200":
description: Feedback submitted
content:
application/json:
schema:
type: object
required: [success, data]
properties:
success:
type: boolean
const: true
message:
type: string
data:
type: object
required: [feedbackId]
properties:
feedbackId:
type: string
minLength: 1
"400":
description: Invalid feedback payload
"401":
description: Missing or invalid token
"413":
description: Payload too large
"415":
description: Unsupported image
"429":
description: Too many requests
"500":
description: Feedback persistence failed
```
## 11. 联调验收
后端完成后至少验证:
1. Login Token 和 Guest Token 都能成功提交。
2. 无图片、1 张图片和 3 张图片均能创建反馈。
3. 第 4 张图片、超大图片、伪造 MIME 和非法 Context 被拒绝。
4. 成功响应始终包含非空 `data.feedbackId`
5. 存储失败时不留下孤立图片对象或半成品反馈记录。
6. 日志中不出现反馈正文、Token、图片内容和原始文件名。
7. 同一身份超过限流阈值后返回 HTTP `429`