package dto import ( "mime/multipart" "time" "photography-backend/internal/model/entity" ) // CreatePhotoRequest 创建照片请求 type CreatePhotoRequest struct { Title string `json:"title" binding:"required,min=1,max=200" validate:"required,min=1,max=200"` Description string `json:"description" binding:"omitempty,max=1000" validate:"omitempty,max=1000"` Filename string `json:"filename" binding:"required" validate:"required"` OriginalURL string `json:"original_url" binding:"required,url" validate:"required,url"` FileSize int64 `json:"file_size" binding:"omitempty,min=0" validate:"omitempty,min=0"` MimeType string `json:"mime_type" binding:"omitempty" validate:"omitempty"` Width int `json:"width" binding:"omitempty,min=0" validate:"omitempty,min=0"` Height int `json:"height" binding:"omitempty,min=0" validate:"omitempty,min=0"` UserID uint `json:"user_id" binding:"required,min=1" validate:"required,min=1"` AlbumID *uint `json:"album_id" binding:"omitempty,min=1" validate:"omitempty,min=1"` CategoryID *uint `json:"category_id" binding:"omitempty,min=1" validate:"omitempty,min=1"` TagIDs []uint `json:"tag_ids" binding:"omitempty" validate:"omitempty"` IsPublic bool `json:"is_public" binding:"omitempty"` IsFeatured bool `json:"is_featured" binding:"omitempty"` } // UpdatePhotoRequest 更新照片请求 type UpdatePhotoRequest struct { Title *string `json:"title" binding:"omitempty,min=1,max=200" validate:"omitempty,min=1,max=200"` Description *string `json:"description" binding:"omitempty,max=1000" validate:"omitempty,max=1000"` AlbumID *uint `json:"album_id" binding:"omitempty,min=0" validate:"omitempty,min=0"` CategoryID *uint `json:"category_id" binding:"omitempty,min=0" validate:"omitempty,min=0"` TagIDs []uint `json:"tag_ids" binding:"omitempty" validate:"omitempty"` IsPublic *bool `json:"is_public" binding:"omitempty"` IsFeatured *bool `json:"is_featured" binding:"omitempty"` LocationName *string `json:"location_name" binding:"omitempty,max=200" validate:"omitempty,max=200"` Latitude *float64 `json:"latitude" binding:"omitempty,min=-90,max=90" validate:"omitempty,min=-90,max=90"` Longitude *float64 `json:"longitude" binding:"omitempty,min=-180,max=180" validate:"omitempty,min=-180,max=180"` } // UploadPhotoRequest 上传照片请求 type UploadPhotoRequest struct { File *multipart.FileHeader `form:"photo" binding:"required" validate:"required"` Title string `form:"title" binding:"omitempty,max=200" validate:"omitempty,max=200"` Description string `form:"description" binding:"omitempty,max=1000" validate:"omitempty,max=1000"` AlbumID *uint `form:"album_id" binding:"omitempty,min=1" validate:"omitempty,min=1"` CategoryID *uint `form:"category_id" binding:"omitempty,min=1" validate:"omitempty,min=1"` TagNames []string `form:"tag_names" binding:"omitempty" validate:"omitempty"` IsPublic bool `form:"is_public" binding:"omitempty"` IsFeatured bool `form:"is_featured" binding:"omitempty"` } // PhotoResponse 照片响应 type PhotoResponse struct { ID uint `json:"id"` Title string `json:"title"` Description string `json:"description"` Filename string `json:"filename"` OriginalURL string `json:"original_url"` ThumbnailURL string `json:"thumbnail_url"` MediumURL string `json:"medium_url"` LargeURL string `json:"large_url"` FileSize int64 `json:"file_size"` MimeType string `json:"mime_type"` Width int `json:"width"` Height int `json:"height"` AspectRatio float64 `json:"aspect_ratio"` // EXIF 信息 CameraMake string `json:"camera_make"` CameraModel string `json:"camera_model"` LensModel string `json:"lens_model"` FocalLength *float64 `json:"focal_length"` Aperture *float64 `json:"aperture"` ShutterSpeed string `json:"shutter_speed"` ISO *int `json:"iso"` TakenAt *time.Time `json:"taken_at"` // 地理位置 LocationName string `json:"location_name"` Latitude *float64 `json:"latitude"` Longitude *float64 `json:"longitude"` // 关联信息 UserID uint `json:"user_id"` AlbumID *uint `json:"album_id"` CategoryID *uint `json:"category_id"` User *UserResponse `json:"user,omitempty"` Album *AlbumResponse `json:"album,omitempty"` Category *CategoryResponse `json:"category,omitempty"` Tags []TagResponse `json:"tags,omitempty"` // 状态和统计 IsPublic bool `json:"is_public"` IsFeatured bool `json:"is_featured"` ViewCount int `json:"view_count"` LikeCount int `json:"like_count"` DownloadCount int `json:"download_count"` SortOrder int `json:"sort_order"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // PhotoListItem 照片列表项(简化版) type PhotoListItem struct { ID uint `json:"id"` Title string `json:"title"` ThumbnailURL string `json:"thumbnail_url"` Width int `json:"width"` Height int `json:"height"` AspectRatio float64 `json:"aspect_ratio"` IsPublic bool `json:"is_public"` IsFeatured bool `json:"is_featured"` ViewCount int `json:"view_count"` LikeCount int `json:"like_count"` CreatedAt time.Time `json:"created_at"` } // ListPhotosOptions 照片列表查询选项 type ListPhotosOptions struct { Page int `json:"page" form:"page" binding:"omitempty,min=1" validate:"omitempty,min=1"` Limit int `json:"limit" form:"limit" binding:"omitempty,min=1,max=100" validate:"omitempty,min=1,max=100"` Sort string `json:"sort" form:"sort" binding:"omitempty,oneof=id title created_at updated_at taken_at view_count like_count" validate:"omitempty,oneof=id title created_at updated_at taken_at view_count like_count"` Order string `json:"order" form:"order" binding:"omitempty,oneof=asc desc" validate:"omitempty,oneof=asc desc"` UserID *uint `json:"user_id" form:"user_id" binding:"omitempty,min=1" validate:"omitempty,min=1"` AlbumID *uint `json:"album_id" form:"album_id" binding:"omitempty,min=1" validate:"omitempty,min=1"` CategoryID *uint `json:"category_id" form:"category_id" binding:"omitempty,min=1" validate:"omitempty,min=1"` TagIDs []uint `json:"tag_ids" form:"tag_ids" binding:"omitempty" validate:"omitempty"` IsPublic *bool `json:"is_public" form:"is_public" binding:"omitempty"` IsFeatured *bool `json:"is_featured" form:"is_featured" binding:"omitempty"` Search string `json:"search" form:"search" binding:"omitempty,max=100" validate:"omitempty,max=100"` Year *int `json:"year" form:"year" binding:"omitempty,min=1900,max=2100" validate:"omitempty,min=1900,max=2100"` Month *int `json:"month" form:"month" binding:"omitempty,min=1,max=12" validate:"omitempty,min=1,max=12"` } // SearchPhotosOptions 照片搜索选项 type SearchPhotosOptions struct { Query string `json:"query" form:"query" binding:"required,min=1" validate:"required,min=1"` Page int `json:"page" form:"page" binding:"omitempty,min=1" validate:"omitempty,min=1"` Limit int `json:"limit" form:"limit" binding:"omitempty,min=1,max=100" validate:"omitempty,min=1,max=100"` Sort string `json:"sort" form:"sort" binding:"omitempty,oneof=relevance created_at view_count like_count" validate:"omitempty,oneof=relevance created_at view_count like_count"` Order string `json:"order" form:"order" binding:"omitempty,oneof=asc desc" validate:"omitempty,oneof=asc desc"` CategoryID *uint `json:"category_id" form:"category_id" binding:"omitempty,min=1" validate:"omitempty,min=1"` TagIDs []uint `json:"tag_ids" form:"tag_ids" binding:"omitempty" validate:"omitempty"` UserID *uint `json:"user_id" form:"user_id" binding:"omitempty,min=1" validate:"omitempty,min=1"` IsPublic *bool `json:"is_public" form:"is_public" binding:"omitempty"` } // PhotoListResponse 照片列表响应 type PhotoListResponse struct { Photos []PhotoListItem `json:"photos"` Total int64 `json:"total"` Page int `json:"page"` Limit int `json:"limit"` } // ProcessPhotoOptions 照片处理选项 type ProcessPhotoOptions struct { GenerateThumbnails bool `json:"generate_thumbnails"` ThumbnailSizes []string `json:"thumbnail_sizes"` ExtractEXIF bool `json:"extract_exif"` GenerateHash bool `json:"generate_hash"` OptimizeSize bool `json:"optimize_size"` WatermarkEnabled bool `json:"watermark_enabled"` } // PhotoStatsResponse 照片统计响应 type PhotoStatsResponse struct { Total int64 `json:"total"` Published int64 `json:"published"` Private int64 `json:"private"` Featured int64 `json:"featured"` TotalViews int64 `json:"total_views"` TotalLikes int64 `json:"total_likes"` TotalDownloads int64 `json:"total_downloads"` FileSize int64 `json:"file_size"` CategoryCounts map[string]int64 `json:"category_counts"` TagCounts map[string]int64 `json:"tag_counts"` Recent []PhotoListItem `json:"recent"` Popular []PhotoListItem `json:"popular"` } // ConvertToPhotoResponse 将照片实体转换为响应DTO func ConvertToPhotoResponse(photo *entity.Photo) *PhotoResponse { if photo == nil { return nil } response := &PhotoResponse{ ID: photo.ID, Title: photo.Title, Description: photo.Description, Filename: photo.Filename, OriginalURL: photo.OriginalURL, ThumbnailURL: photo.ThumbnailURL, MediumURL: photo.MediumURL, LargeURL: photo.LargeURL, FileSize: photo.FileSize, MimeType: photo.MimeType, Width: photo.Width, Height: photo.Height, AspectRatio: photo.GetAspectRatio(), // EXIF CameraMake: photo.CameraMake, CameraModel: photo.CameraModel, LensModel: photo.LensModel, FocalLength: photo.FocalLength, Aperture: photo.Aperture, ShutterSpeed: photo.ShutterSpeed, ISO: photo.ISO, TakenAt: photo.TakenAt, // 地理位置 LocationName: photo.LocationName, Latitude: photo.Latitude, Longitude: photo.Longitude, // 关联 UserID: photo.UserID, AlbumID: photo.AlbumID, CategoryID: photo.CategoryID, // 状态 IsPublic: photo.IsPublic, IsFeatured: photo.IsFeatured, ViewCount: photo.ViewCount, LikeCount: photo.LikeCount, DownloadCount: photo.DownloadCount, SortOrder: photo.SortOrder, CreatedAt: photo.CreatedAt, UpdatedAt: photo.UpdatedAt, } // 转换关联对象 if photo.User.ID != 0 { response.User = ConvertToUserResponse(&photo.User) } if photo.Category != nil { response.Category = ConvertToCategoryResponse(photo.Category) } return response } // ConvertToPhotoListItem 将照片实体转换为列表项DTO func ConvertToPhotoListItem(photo *entity.Photo) PhotoListItem { return PhotoListItem{ ID: photo.ID, Title: photo.Title, ThumbnailURL: photo.ThumbnailURL, Width: photo.Width, Height: photo.Height, AspectRatio: photo.GetAspectRatio(), IsPublic: photo.IsPublic, IsFeatured: photo.IsFeatured, ViewCount: photo.ViewCount, LikeCount: photo.LikeCount, CreatedAt: photo.CreatedAt, } }