264 lines
12 KiB
Go
264 lines
12 KiB
Go
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,
|
|
}
|
|
} |