From fdb31832e5167251c82aa6fca541f29f6f3831fb Mon Sep 17 00:00:00 2001 From: ddCat <2695092045@qq.com> Date: Sat, 15 Aug 2026 17:00:30 +0800 Subject: [PATCH] =?UTF-8?q?fix(api):=20=E7=B4=A0=E6=9D=90=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=20-=20=E5=A2=9E=E5=8A=A0=20ID=20=E9=95=BF=E5=BA=A6?= =?UTF-8?q?=E8=BE=B9=E7=95=8C=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/service/user_data.go | 10 ++++- backend/internal/service/user_data_test.go | 46 ++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/backend/internal/service/user_data.go b/backend/internal/service/user_data.go index 17b22facd..3b886be48 100644 --- a/backend/internal/service/user_data.go +++ b/backend/internal/service/user_data.go @@ -5,6 +5,7 @@ import ( "errors" "strings" "time" + "unicode/utf8" "infinite-canvas/backend/internal/model" @@ -295,6 +296,13 @@ func assetFromJSON(userID string, raw json.RawMessage) (model.Asset, error) { if id == "" { id = newID() } + if utf8.RuneCountInString(id) > model.AssetIDMaxLength { + return model.Asset{}, BadAuthRequest("素材 ID 不能超过 80 个字符") + } + primaryVersionID := strings.TrimSpace(payload.PrimaryVersionID) + if utf8.RuneCountInString(primaryVersionID) > 36 { + return model.Asset{}, BadAuthRequest("素材主版本 ID 不能超过 36 个字符") + } category := model.AssetCategory(strings.TrimSpace(payload.Category)) if category == "" { category = model.AssetCategoryOther @@ -309,7 +317,7 @@ func assetFromJSON(userID string, raw json.RawMessage) (model.Asset, error) { Kind: strings.TrimSpace(payload.Kind), Category: category, Status: status, - PrimaryVersionID: strings.TrimSpace(payload.PrimaryVersionID), + PrimaryVersionID: primaryVersionID, Title: strings.TrimSpace(payload.Title), PayloadJSON: string(raw), CreatedAt: createdAt, diff --git a/backend/internal/service/user_data_test.go b/backend/internal/service/user_data_test.go index 95cd1d6ac..76445545c 100644 --- a/backend/internal/service/user_data_test.go +++ b/backend/internal/service/user_data_test.go @@ -2,9 +2,55 @@ package service import ( "encoding/json" + "strings" "testing" + + "infinite-canvas/backend/internal/model" ) +func TestAssetFromJSONAcceptsDeterministicGenerationID(t *testing.T) { + id := "generation_" + strings.Repeat("a", 64) + raw, err := json.Marshal(map[string]any{"id": id, "kind": "image", "title": "生成图片"}) + if err != nil { + t.Fatalf("marshal asset: %v", err) + } + + asset, err := assetFromJSON("user-1", raw) + if err != nil { + t.Fatalf("assetFromJSON: %v", err) + } + if asset.ID != id { + t.Fatalf("asset ID = %q, want %q", asset.ID, id) + } + if len([]rune(asset.ID)) > model.AssetIDMaxLength { + t.Fatalf("generation asset ID length = %d, limit %d", len([]rune(asset.ID)), model.AssetIDMaxLength) + } +} + +func TestAssetFromJSONRejectsIDOverLimit(t *testing.T) { + raw, err := json.Marshal(map[string]any{"id": strings.Repeat("a", model.AssetIDMaxLength+1), "kind": "image"}) + if err != nil { + t.Fatalf("marshal asset: %v", err) + } + + _, err = assetFromJSON("user-1", raw) + if err == nil || !strings.Contains(err.Error(), "素材 ID 不能超过 80 个字符") { + t.Fatalf("assetFromJSON error = %v", err) + } +} + +func TestAssetFromJSONRejectsPrimaryVersionIDOverLimit(t *testing.T) { + raw, err := json.Marshal(map[string]any{"id": "asset-1", "primaryVersionId": strings.Repeat("v", 37)}) + if err != nil { + t.Fatalf("marshal asset: %v", err) + } + + _, err = assetFromJSON("user-1", raw) + if err == nil || !strings.Contains(err.Error(), "素材主版本 ID 不能超过 36 个字符") { + t.Fatalf("assetFromJSON error = %v", err) + } +} + func TestValidateSyncedPayloadAllowsDataURLMentionInErrorMessage(t *testing.T) { raw, err := json.Marshal(map[string]interface{}{ "nodes": []interface{}{