@@ -60,35 +60,86 @@ func CreateFolderHandler(c *gin.Context) {
6060
6161// FolderResponse 文件夹响应(包含图片数量)
6262type FolderResponse struct {
63- ID uint `json:"id"`
64- Name string `json:"name"`
65- Type string `json:"type"`
66- Year int `json:"year"`
67- Month int `json:"month"`
68- CreatedAt string `json:"created_at"`
69- UpdatedAt string `json:"updated_at"`
70- ImageCount int64 `json:"image_count"`
71- CoverImage string `json:"cover_image,omitempty"`
63+ ID uint `json:"id"`
64+ Name string `json:"name"`
65+ Type string `json:"type"`
66+ Year int `json:"year"`
67+ Month int `json:"month"`
68+ CreatedAt string `json:"created_at"`
69+ UpdatedAt string `json:"updated_at"`
70+ ImageCount int64 `json:"image_count"`
71+ CoverImage string `json:"cover_image,omitempty"`
72+ CoverImageSource * ImageSource `json:"cover_image_source,omitempty"`
7273}
7374
74- func toPublicImagePath (raw string ) string {
75+ type ImageSource struct {
76+ Kind string `json:"kind"`
77+ Value string `json:"value"`
78+ }
79+
80+ const (
81+ IMAGE_SOURCE_KIND_HTTP_URL = "http_url"
82+ IMAGE_SOURCE_KIND_STORAGE_RELATIVE = "storage_relative"
83+ )
84+
85+ func normalizeStoragePath (raw string ) string {
7586 trimmed := strings .TrimSpace (raw )
7687 if trimmed == "" {
7788 return ""
7889 }
7990
80- lower := strings .ToLower (trimmed )
81- if strings .HasPrefix (lower , "http://" ) || strings .HasPrefix (lower , "https://" ) {
82- return trimmed
83- }
84-
8591 normalized := strings .ReplaceAll (trimmed , "\\ " , "/" )
8692 if idx := strings .Index (normalized , "/storage/" ); idx >= 0 {
8793 return normalized [idx :]
8894 }
8995 if idx := strings .Index (normalized , "storage/" ); idx >= 0 {
9096 return "/" + normalized [idx :]
9197 }
98+ return ""
99+ }
100+
101+ func buildImageSource (raw string ) * ImageSource {
102+ trimmed := strings .TrimSpace (raw )
103+ if trimmed == "" {
104+ return nil
105+ }
106+
107+ lower := strings .ToLower (trimmed )
108+ if strings .HasPrefix (lower , "http://" ) || strings .HasPrefix (lower , "https://" ) {
109+ return & ImageSource {Kind : IMAGE_SOURCE_KIND_HTTP_URL , Value : trimmed }
110+ }
111+
112+ if storagePath := normalizeStoragePath (trimmed ); storagePath != "" {
113+ return & ImageSource {Kind : IMAGE_SOURCE_KIND_STORAGE_RELATIVE , Value : storagePath }
114+ }
115+
116+ // 安全兜底:不向前端暴露无法识别的本地路径(尤其绝对路径)
117+ return nil
118+ }
119+
120+ func pickFirstImageSource (candidates ... string ) * ImageSource {
121+ for _ , candidate := range candidates {
122+ if source := buildImageSource (candidate ); source != nil {
123+ return source
124+ }
125+ }
126+ return nil
127+ }
128+
129+ func toPublicImagePath (raw string ) string {
130+ trimmed := strings .TrimSpace (raw )
131+ if trimmed == "" {
132+ return ""
133+ }
134+
135+ lower := strings .ToLower (trimmed )
136+ if strings .HasPrefix (lower , "http://" ) || strings .HasPrefix (lower , "https://" ) {
137+ return trimmed
138+ }
139+
140+ if storagePath := normalizeStoragePath (trimmed ); storagePath != "" {
141+ return storagePath
142+ }
92143
93144 return ""
94145}
@@ -158,20 +209,16 @@ func GetFoldersHandler(c *gin.Context) {
158209 log .Printf ("[API] 查询文件夹封面候选失败: %v\n " , err )
159210 }
160211
161- pickCover := func (c folderCoverCandidate ) string {
162- for _ , v := range []string {
163- toPublicImagePath (c .ThumbnailPath ),
164- toPublicImagePath (c .LocalPath ),
165- strings .TrimSpace (c .ThumbnailURL ),
166- strings .TrimSpace (c .ImageURL ),
167- } {
168- if strings .TrimSpace (v ) != "" {
169- return v
170- }
171- }
172- return ""
212+ pickCoverSource := func (c folderCoverCandidate ) * ImageSource {
213+ return pickFirstImageSource (
214+ c .ThumbnailPath ,
215+ c .LocalPath ,
216+ c .ThumbnailURL ,
217+ c .ImageURL ,
218+ )
173219 }
174220 coverMap := make (map [uint ]string , len (folders ))
221+ coverSourceMap := make (map [uint ]* ImageSource , len (folders ))
175222 for _ , candidate := range coverCandidates {
176223 if candidate .FolderID == "" {
177224 continue
@@ -184,26 +231,28 @@ func GetFoldersHandler(c *gin.Context) {
184231 if _ , exists := coverMap [fid ]; exists {
185232 continue
186233 }
187- cover := pickCover (candidate )
188- if cover == "" {
234+ coverSource := pickCoverSource (candidate )
235+ if coverSource == nil {
189236 continue
190237 }
191- coverMap [fid ] = cover
238+ coverMap [fid ] = coverSource .Value
239+ coverSourceMap [fid ] = coverSource
192240 }
193241
194242 // 构建响应,包含图片数量
195243 responses := make ([]FolderResponse , len (folders ))
196244 for i , folder := range folders {
197245 responses [i ] = FolderResponse {
198- ID : folder .ID ,
199- Name : folder .Name ,
200- Type : folder .Type ,
201- Year : folder .Year ,
202- Month : folder .Month ,
203- CreatedAt : folder .CreatedAt .Format ("2006-01-02 15:04:05" ),
204- UpdatedAt : folder .UpdatedAt .Format ("2006-01-02 15:04:05" ),
205- ImageCount : countMap [folder .ID ],
206- CoverImage : coverMap [folder .ID ],
246+ ID : folder .ID ,
247+ Name : folder .Name ,
248+ Type : folder .Type ,
249+ Year : folder .Year ,
250+ Month : folder .Month ,
251+ CreatedAt : folder .CreatedAt .Format ("2006-01-02 15:04:05" ),
252+ UpdatedAt : folder .UpdatedAt .Format ("2006-01-02 15:04:05" ),
253+ ImageCount : countMap [folder .ID ],
254+ CoverImage : coverMap [folder .ID ],
255+ CoverImageSource : coverSourceMap [folder .ID ],
207256 }
208257 }
209258
@@ -212,22 +261,24 @@ func GetFoldersHandler(c *gin.Context) {
212261}
213262
214263type FolderImageTaskResponse struct {
215- TaskID string `json:"task_id"`
216- Prompt string `json:"prompt"`
217- ModelID string `json:"model_id,omitempty"`
218- ProviderName string `json:"provider_name,omitempty"`
219- LocalPath string `json:"local_path,omitempty"`
220- ThumbnailPath string `json:"thumbnail_path,omitempty"`
221- ImageURL string `json:"image_url,omitempty"`
222- ThumbnailURL string `json:"thumbnail_url,omitempty"`
223- Width int `json:"width,omitempty"`
224- Height int `json:"height,omitempty"`
225- CreatedAt string `json:"created_at"`
226- UpdatedAt string `json:"updated_at,omitempty"`
227- Status string `json:"status"`
228- TotalCount int `json:"total_count,omitempty"`
229- ErrorMessage string `json:"error_message,omitempty"`
230- ConfigSnap string `json:"config_snapshot,omitempty"`
264+ TaskID string `json:"task_id"`
265+ Prompt string `json:"prompt"`
266+ ModelID string `json:"model_id,omitempty"`
267+ ProviderName string `json:"provider_name,omitempty"`
268+ LocalPath string `json:"local_path,omitempty"`
269+ ThumbnailPath string `json:"thumbnail_path,omitempty"`
270+ ImageURL string `json:"image_url,omitempty"`
271+ ThumbnailURL string `json:"thumbnail_url,omitempty"`
272+ ImageSource * ImageSource `json:"image_source,omitempty"`
273+ ThumbnailSource * ImageSource `json:"thumbnail_source,omitempty"`
274+ Width int `json:"width,omitempty"`
275+ Height int `json:"height,omitempty"`
276+ CreatedAt string `json:"created_at"`
277+ UpdatedAt string `json:"updated_at,omitempty"`
278+ Status string `json:"status"`
279+ TotalCount int `json:"total_count,omitempty"`
280+ ErrorMessage string `json:"error_message,omitempty"`
281+ ConfigSnap string `json:"config_snapshot,omitempty"`
231282}
232283
233284// GetFolderImagesHandler 获取指定文件夹下的图片列表(分页)
@@ -304,21 +355,23 @@ func GetFolderImagesHandler(c *gin.Context) {
304355 responses := make ([]FolderImageTaskResponse , len (tasks ))
305356 for i , task := range tasks {
306357 responses [i ] = FolderImageTaskResponse {
307- TaskID : task .TaskID ,
308- Prompt : task .Prompt ,
309- ModelID : task .ModelID ,
310- ProviderName : task .ProviderName ,
311- LocalPath : toPublicImagePath (task .LocalPath ),
312- ThumbnailPath : toPublicImagePath (task .ThumbnailPath ),
313- ImageURL : strings .TrimSpace (task .ImageURL ),
314- ThumbnailURL : strings .TrimSpace (task .ThumbnailURL ),
315- Width : task .Width ,
316- Height : task .Height ,
317- CreatedAt : task .CreatedAt .Format (time .RFC3339 ),
318- Status : task .Status ,
319- TotalCount : task .TotalCount ,
320- ErrorMessage : task .ErrorMessage ,
321- ConfigSnap : task .ConfigSnapshot ,
358+ TaskID : task .TaskID ,
359+ Prompt : task .Prompt ,
360+ ModelID : task .ModelID ,
361+ ProviderName : task .ProviderName ,
362+ LocalPath : toPublicImagePath (task .LocalPath ),
363+ ThumbnailPath : toPublicImagePath (task .ThumbnailPath ),
364+ ImageURL : strings .TrimSpace (task .ImageURL ),
365+ ThumbnailURL : strings .TrimSpace (task .ThumbnailURL ),
366+ ImageSource : pickFirstImageSource (task .LocalPath , task .ImageURL , task .ThumbnailPath , task .ThumbnailURL ),
367+ ThumbnailSource : pickFirstImageSource (task .ThumbnailPath , task .LocalPath , task .ThumbnailURL , task .ImageURL ),
368+ Width : task .Width ,
369+ Height : task .Height ,
370+ CreatedAt : task .CreatedAt .Format (time .RFC3339 ),
371+ Status : task .Status ,
372+ TotalCount : task .TotalCount ,
373+ ErrorMessage : task .ErrorMessage ,
374+ ConfigSnap : task .ConfigSnapshot ,
322375 }
323376 }
324377
0 commit comments