Files
makelore/个人头像云端维护需求文档.md
inman 22add3f01f
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled
完善客户端模块与工作区能力
2026-08-13 19:51:02 +08:00

198 lines
9.3 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.

# 个人头像云端维护
> Makelore 个人资料模块 · 服务端接口与联调说明
| 项目 | 内容 |
| --- | --- |
| 文档状态 | 服务端接口已完成,待客户端联调 |
| 目标读者 | 用户服务、文件存储、API 开发人员 |
| 客户端约束 | 客户端统一将头像裁剪为正方形,界面以圆形蒙版展示 |
## 联调前置
- 部署服务端时,先执行 Alembic 迁移:`20260813_user_avatar_cloud_0040`
- 新客户端只使用以下接口:`POST /api/user/avatar``DELETE /api/user/avatar``GET /api/user/agent-profile`
- 旧接口 `/api/auth/me/avatar` 仅保留兼容,新客户端不要继续使用。
## 1. 需求概述
Makelore 个人资料弹窗需要支持用户维护个人头像,并将头像按账号保存到云端。用户可在个人资料中上传、替换或移除头像;保存后,头像应在不同设备间同步。
**核心结论:**头像通过独立接口维护。服务端提供上传、删除接口,并在个人资料查询响应中返回只读字段 `avatar_url``avatar_url` 不得放入 Agent Profile 的 PUT 请求体。
## 2. 产品范围与行为
- 头像展示位置:侧边栏底部账号按钮、展开后的账号菜单、个人资料弹窗预览。聊天消息布局本期不增加用户头像。
- 上传格式PNG、JPEG、WebP。客户端会在上传前自动居中裁剪为正方形并压缩到最长边 512px。
- 展示方式:客户端使用圆形蒙版展示头像;服务端不需要保存圆形遮罩图,保存正方形图片即可。
- 保存时机:用户选择图片或点击移除后只更新弹窗草稿;点击“保存个人资料”时才调用上传或删除接口。
- 默认头像:没有头像或头像加载失败时,客户端回退显示用户姓名首字母。
- 账号隔离:头像必须绑定当前登录用户,不允许通过请求参数指定其他用户。
- URL 生命周期OSS 环境下 `avatar_url` 为 24 小时有效的签名 URL客户端启动或刷新资料时重新获取。
- URL 使用方式:客户端直接使用服务端返回的 `avatar_url`,不得持久化为永久地址,不得自行拼接、截断、改写或刷新签名参数。
## 3. 接口总览
| 方法 | 上游路径 | 用途 | 成功结果 |
| --- | --- | --- | --- |
| `GET` | `/api/user/agent-profile` | 查询个人资料 | 返回 `avatar_url` 字段 |
| `POST` | `/api/user/avatar` | 上传或替换头像 | 返回新的 `avatar_url` |
| `DELETE` | `/api/user/avatar` | 删除当前头像 | 返回 `avatar_url: null` |
> **路径说明:**上表是服务端上游接口路径。客户端通过 Electron Main 的 Host API 代理访问,服务端无需感知客户端的本地代理路径。
## 4. 认证与通用约定
- 所有接口使用现有登录态认证:`Authorization: Bearer <access_token>`
- 用户身份从 access token 解析,不接受 `user_id``username` 等客户端字段作为头像归属依据。
- 成功响应直接返回 JSON 对象;头像 URL 字段统一命名为只读响应字段 `avatar_url`
- `avatar_url` 只能出现在个人资料查询、上传成功和删除成功响应中,不能出现在 Agent Profile 的 PUT 请求体中。
- OSS 环境下 `avatar_url` 是 24 小时签名 URL客户端应直接将其用于头像展示不得持久化为永久地址也不得自行拼接或修改 URL。
- 接口失败时沿用项目现有错误响应结构,并返回稳定错误码。
## 5. 接口详细要求
### 5.1 扩展个人资料查询
现有接口保持不变,在返回的个人资料对象中提供只读字段 `avatar_url`。客户端启动和刷新资料时都应重新调用该接口,获取新的签名 URL。
```http
GET /api/user/agent-profile
Authorization: Bearer <access_token>
```
成功响应示例:
```json
{
"display_name": "小明",
"age": 18,
"gender": "male",
"avatar_url": "https://oss.example.com/avatars/user-1-v3.webp?X-Amz-Expires=86400&X-Amz-Signature=<signature>",
"share_age_with_agents": true,
"share_gender_with_agents": true,
"analysis_enabled": true,
"completed": true,
"version": 3,
"updated_at": "2026-08-13T00:00:00Z"
}
```
无头像时必须返回 `null`,而不是省略字段:
```json
{
"avatar_url": null
}
```
### 5.2 上传或替换头像
```http
POST /api/user/avatar
Authorization: Bearer <access_token>
Content-Type: multipart/form-data
```
请求表单字段:
| 字段名 | 类型 | 必填 | 要求 |
| --- | --- | --- | --- |
| `file` | `File` | 是 | 头像图片;支持 `image/png``image/jpeg``image/webp` |
成功响应:
```json
{
"avatar_url": "https://oss.example.com/avatars/user-1-v4.webp?X-Amz-Expires=86400&X-Amz-Signature=<signature>"
}
```
服务端会对上传内容做最终校验并转换为 WebP。上传接口同时承担新头像上传和旧头像替换替换成功后旧文件可以异步清理但不能影响新 URL 在有效期内的可用性。
### 5.3 删除头像
```http
DELETE /api/user/avatar
Authorization: Bearer <access_token>
```
成功响应:
```json
{
"avatar_url": null
}
```
删除后,个人资料查询接口也必须返回 `avatar_url: null`
## 6. 文件校验与存储要求
| 项目 | 要求 |
| --- | --- |
| 允许格式 | 客户端可上传 PNG、JPEG、WebP服务端校验后统一转成 WebP。 |
| 格式安全 | 不得上传伪装格式、SVG 或动态图片;服务端应校验 MIME、文件头和实际解码结果。 |
| 大小限制 | 按服务端配置执行;超过限制时返回 HTTP 413 和 `avatar_file_too_large`。 |
| 图片内容 | 拒绝损坏图片、空文件、无法解码文件和伪装格式文件。 |
| 归属隔离 | 文件对象、数据库记录和访问 URL 均必须绑定当前用户。 |
| URL 处理 | OSS 环境返回 24 小时签名 URL客户端不得持久化为永久地址不得自行拼接或修改 URL。 |
| 隐私 | 头像属于用户资料,不应出现在公开作品、作品列表或未授权用户的接口响应中。 |
> **客户端处理边界:**客户端目前会把图片处理为最长边 512px 的正方形,并可上传 PNG、JPEG 或 WebP不要上传伪装格式、SVG 或动态图片。服务端仍需做最终校验并转换为 WebP。
## 7. 错误响应
| 场景 | HTTP 状态 | `code` | 说明 |
| --- | ---: | --- | --- |
| 格式不支持 | 415 | `avatar_unsupported_type` | 仅允许 PNG/JPEG/WebP |
| 文件过大 | 413 | `avatar_file_too_large` | 超过服务端大小限制 |
| 图片损坏或无法解码 | 400 | `avatar_invalid_file` | 文件不是有效图片 |
| 上传失败 | 503 | `avatar_storage_unavailable` | 对象存储暂时不可用 |
错误响应示例:
```json
{
"detail": {
"code": "avatar_file_too_large",
"message": "头像文件超过服务端大小限制"
}
}
```
## 8. 客户端调用时序
1. 客户端启动或刷新资料时调用 `GET /api/user/agent-profile`,获取当前资料和新的 `avatar_url`;不要复用已过期的签名 URL。
2. 用户在个人资料弹窗中选择图片,客户端只生成本地预览,不请求服务端。
3. 用户点击保存;如果是新头像,客户端先调用 `POST /api/user/avatar`;如果用户选择移除,则先调用 `DELETE /api/user/avatar`
4. 头像上传或删除成功后,再调用现有 Agent Profile PUT 接口保存姓名、年龄、性别等资料。PUT 请求体不得包含 `avatar_url`
5. 如果后续资料 PUT 失败,重试时只重试资料 PUT不要重复上传或删除已经成功的头像操作。
6. 任一步失败,弹窗保持打开并展示错误;用户可以修正后重新保存。
> **联调注意:**头像接口与个人资料 PUT 是两个独立请求。`avatar_url` 是只读响应字段,服务端不需要在个人资料 PUT 中处理该字段,也不需要客户端为头像 URL 增加版本号或自行处理签名参数。
## 9. 验收标准
- 登录用户可以通过 `POST /api/user/avatar` 上传 PNG、JPEG 或 WebP并获得可直接展示的 `avatar_url`
- 再次上传新图片可以替换旧头像;`GET /api/user/agent-profile` 返回最新 `avatar_url`
- `DELETE /api/user/avatar` 成功后,`GET /api/user/agent-profile` 返回 `avatar_url: null`
- 不同用户之间无法读取、覆盖或删除彼此的头像。
- 超过大小限制、格式不支持、图片损坏时返回明确且稳定的错误 `code`
- 客户端启动或刷新资料时会重新获取 `avatar_url`OSS 签名 URL 过期后不会继续复用旧 URL。
- 客户端不把 `avatar_url` 放入 Agent Profile PUT 请求体,也不会自行拼接或修改 URL。
- 服务端最终将有效头像转换为 WebP伪装格式、SVG、动态图片和损坏文件会被拒绝。
- 服务端异常不会返回包含内部存储路径、访问凭据或对象存储密钥的响应。
## 10. 联调检查清单
- 部署前已执行 Alembic 迁移 `20260813_user_avatar_cloud_0040`
- `GET /api/user/agent-profile` 在有头像时返回 24 小时签名 `avatar_url`,无头像时返回 `null`
- `POST /api/user/avatar` 成功返回 `{ "avatar_url": "..." }`
- `DELETE /api/user/avatar` 成功返回 `{ "avatar_url": null }`
- Agent Profile PUT 请求体不接受、不处理 `avatar_url`
- 错误码和 HTTP 状态码与本文档一致415、413、400、503。
- 旧接口 `/api/auth/me/avatar` 继续兼容,但新客户端不调用。
客户端已按上述接口契约完成接入;服务端接口上线后即可进行联调。