10 KiB
问题反馈 API 接口定义
1. 文档状态
本文档定义 CozSweet 问题反馈提交接口,供后端实现和前后端联调使用。
前端已经按本文协议完成以下功能:
- 反馈分类和文字提交;
- 最多 3 张截图;
- 图片压缩和格式限制;
- Login Token 与 Guest Token 认证;
- 提交成功后展示
feedbackId。
后端需要实现:
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,支持以下两种身份:
- 正式用户 Login Token;
- 游客 Guest Token。
Authorization: Bearer <TOKEN>
后端必须从 Token 中解析用户或游客身份,不接受前端传递 userId、guestId 或其他身份字段。
Token 缺失、无效或过期时返回 HTTP 401。
3. 请求定义
3.1 请求地址
POST <API_BASE_URL>/api/feedback
3.2 Content-Type
请求使用:
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 字符串:
{
"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 图片规则
| 规则 | 要求 |
|---|---|
| 数量 | 0–3 张 |
| 单文件大小 | 不超过 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 仅文字反馈
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 带多张截图
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:
{
"success": true,
"message": "Feedback submitted",
"data": {
"feedbackId": "c5db8e32-60f0-4f47-81bb-c22254c0562f"
}
}
前端公开响应字段只有:
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
data.feedbackId |
string | 是 | 非空反馈编号,推荐使用 UUID。 |
前端不需要后端返回用户信息、反馈正文、图片地址、状态、创建时间或诊断上下文。
6. 失败响应
失败响应使用统一 envelope:
{
"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. 后端处理流程
推荐处理顺序:
- 验证 Bearer Token,解析正式用户或游客身份。
- 读取 multipart 字段并验证分类、正文和 Context。
- 在读取完整文件前检查图片数量和请求大小。
- 校验每张图片的真实格式、文件魔数、尺寸和大小。
- 使用服务端生成的随机对象名写入私有存储。
- 创建反馈记录并关联图片对象。
- 返回非空
feedbackId。 - 任一步失败时清理本次已经上传但未关联的图片。
每个成功请求创建一条反馈记录。当前前端没有发送 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 参考
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. 联调验收
后端完成后至少验证:
- Login Token 和 Guest Token 都能成功提交。
- 无图片、1 张图片和 3 张图片均能创建反馈。
- 第 4 张图片、超大图片、伪造 MIME 和非法 Context 被拒绝。
- 成功响应始终包含非空
data.feedbackId。 - 存储失败时不留下孤立图片对象或半成品反馈记录。
- 日志中不出现反馈正文、Token、图片内容和原始文件名。
- 同一身份超过限流阈值后返回 HTTP
429。