From 2f8e7ead17ed714b8cea50cca896867ea1cde32a Mon Sep 17 00:00:00 2001 From: wonder Date: Mon, 25 May 2026 15:25:40 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E5=8A=A0=E8=BD=BD=E4=B8=83=E7=89=9B=E4=BA=91?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复 buildURL 中 url.PathEscape 对路径分隔符 / 的错误编码(/→%2F), 改为逐段编码后拼接 2. AssetResponse 新增 key 字段,返回七牛云对象存储 Key 3. 前端改用 /api/v1/assets/download 接口加载图片,通过后端 302 重定向 访问 CDN,避免浏览器直接访问 CDN 可能出现的跨域或编码问题 4. 更新 api.md 文档,补充 key 字段说明和 download 接口公开属性 --- backend/internal/handler/generate.go | 2 ++ backend/internal/service/storage.go | 6 +++++- docs/api.md | 12 +++++++++--- frontend/src/api/generate.ts | 3 ++- frontend/src/api/types.ts | 2 ++ 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/backend/internal/handler/generate.go b/backend/internal/handler/generate.go index 0512976..7ec2df0 100755 --- a/backend/internal/handler/generate.go +++ b/backend/internal/handler/generate.go @@ -36,6 +36,7 @@ type GenerateResponse struct { // AssetResponse 单个素材响应。 type AssetResponse struct { + Key string `json:"key"` URL string `json:"url"` Format string `json:"format"` } @@ -156,6 +157,7 @@ func runPipelineBg(projectID, taskID string, req GenerateRequest) { return } assets[i] = AssetResponse{ + Key: key, URL: cdnURL, Format: a.Format, } diff --git a/backend/internal/service/storage.go b/backend/internal/service/storage.go index da6e3a8..8eac783 100644 --- a/backend/internal/service/storage.go +++ b/backend/internal/service/storage.go @@ -94,5 +94,9 @@ func (s *StorageService) Delete(ctx context.Context, key string) error { // buildURL 根据配置构建完整的 CDN URL。 func (s *StorageService) buildURL(key string) string { host := strings.TrimRight(s.cfg.CDNHost, "/") - return fmt.Sprintf("%s/%s", host, url.PathEscape(key)) + segments := strings.Split(key, "/") + for i, seg := range segments { + segments[i] = url.PathEscape(seg) + } + return fmt.Sprintf("%s/%s", host, strings.Join(segments, "/")) } diff --git a/docs/api.md b/docs/api.md index 9d9f91f..36bf89d 100755 --- a/docs/api.md +++ b/docs/api.md @@ -483,8 +483,8 @@ POST /api/v1/prompt/optimize "data": { "assets": [ { - "id": "asset_001", - "url": "https://cdn.example.com/users/user_a1B2c3D4/projects/proj_abc123/tasks/task_xyz789/output/spritesheet.png", + "key": "generation/proj_abc123/task_xyz789/0.png", + "url": "https://cdn.example.com/generation/proj_abc123/task_xyz789/0.png", "format": "png", "width": 256, "height": 64, @@ -500,9 +500,15 @@ POST /api/v1/prompt/optimize } ``` +| 字段 | 类型 | 说明 | +|------|------|------| +| `key` | string | 七牛云对象存储 Key | +| `url` | string | CDN 直接访问 URL | +| `format` | string | 图片格式(png 等) | + ### GET /api/v1/assets/download -需认证。返回 302 重定向到七牛云 CDN URL。 +公开接口(无需认证)。返回 302 重定向到七牛云 CDN URL。前端可通过此接口加载素材图片,作为 CDN 直接访问的替代方案。 | 参数 | 类型 | 说明 | |------|------|------| diff --git a/frontend/src/api/generate.ts b/frontend/src/api/generate.ts index a03b2c6..bd568c4 100755 --- a/frontend/src/api/generate.ts +++ b/frontend/src/api/generate.ts @@ -21,7 +21,8 @@ export async function getAssets(taskId: string): Promise { const resp = await get(`/api/v1/tasks/${taskId}/assets`) return resp.assets.map((a, i) => ({ id: `asset-${i}`, - url: a.url, + key: a.key, + url: `/api/v1/assets/download?key=${encodeURIComponent(a.key)}`, format: a.format, width: resp.metadata.frameWidth, height: resp.metadata.frameHeight, diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts index 3fbdefd..4bd6529 100755 --- a/frontend/src/api/types.ts +++ b/frontend/src/api/types.ts @@ -79,6 +79,7 @@ export interface Task { // 素材 export interface Asset { id: string + key: string url: string format: string width: number @@ -114,6 +115,7 @@ export interface GenerateResponse { // 素材列表响应 — 对应 GET /api/v1/tasks/:taskId/assets export interface AssetsResponse { assets: { + key: string url: string format: string }[]