Files
cozsweet-frontend-nextjs/docs/backend/FRONTEND_FEEDBACK_API.md
T

10 KiB
Raw Blame History

问题反馈 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,支持以下两种身份:

  1. 正式用户 Login Token
  2. 游客 Guest Token。
Authorization: Bearer <TOKEN>

后端必须从 Token 中解析用户或游客身份,不接受前端传递 userIdguestId 或其他身份字段。

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 图片规则

规则 要求
数量 03 张
单文件大小 不超过 2 MiB,即 2 * 1024 * 1024 bytes
接受格式 JPEG、PNG、WebP
接受 MIME image/jpegimage/pngimage/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 正文为空或长度不在 102000。
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 内部处理状态,例如 openresolved。不返回前端。
created_at 服务端创建时间。

图片记录最少需要反馈 ID、私有对象 key、MIME、字节大小、宽高和创建时间。不要把用户原始文件名作为存储对象名。

正式用户必须只写入 user_idGuest 必须只写入 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. 联调验收

后端完成后至少验证:

  1. Login Token 和 Guest Token 都能成功提交。
  2. 无图片、1 张图片和 3 张图片均能创建反馈。
  3. 第 4 张图片、超大图片、伪造 MIME 和非法 Context 被拒绝。
  4. 成功响应始终包含非空 data.feedbackId
  5. 存储失败时不留下孤立图片对象或半成品反馈记录。
  6. 日志中不出现反馈正文、Token、图片内容和原始文件名。
  7. 同一身份超过限流阈值后返回 HTTP 429