fix
This commit is contained in:
196
backend-old/internal/model/dto/album_dto.go
Normal file
196
backend-old/internal/model/dto/album_dto.go
Normal file
@ -0,0 +1,196 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"photography-backend/internal/model/entity"
|
||||
)
|
||||
|
||||
// CreateAlbumRequest 创建相册请求
|
||||
type CreateAlbumRequest 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"`
|
||||
Slug string `json:"slug" binding:"omitempty,min=1,max=255" validate:"omitempty,min=1,max=255"`
|
||||
CategoryID *uint `json:"category_id" binding:"omitempty,min=1" validate:"omitempty,min=1"`
|
||||
IsPublic bool `json:"is_public" binding:"omitempty"`
|
||||
IsFeatured bool `json:"is_featured" binding:"omitempty"`
|
||||
Password string `json:"password" binding:"omitempty,min=6" validate:"omitempty,min=6"`
|
||||
}
|
||||
|
||||
// UpdateAlbumRequest 更新相册请求
|
||||
type UpdateAlbumRequest 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"`
|
||||
Slug *string `json:"slug" binding:"omitempty,min=1,max=255" validate:"omitempty,min=1,max=255"`
|
||||
CoverPhotoID *uint `json:"cover_photo_id" binding:"omitempty,min=0" validate:"omitempty,min=0"`
|
||||
CategoryID *uint `json:"category_id" binding:"omitempty,min=0" validate:"omitempty,min=0"`
|
||||
IsPublic *bool `json:"is_public" binding:"omitempty"`
|
||||
IsFeatured *bool `json:"is_featured" binding:"omitempty"`
|
||||
Password *string `json:"password" binding:"omitempty,min=0" validate:"omitempty,min=0"` // 空字符串表示移除密码
|
||||
SortOrder *int `json:"sort_order" binding:"omitempty,min=0" validate:"omitempty,min=0"`
|
||||
}
|
||||
|
||||
// AlbumResponse 相册响应
|
||||
type AlbumResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Slug string `json:"slug"`
|
||||
CoverPhotoID *uint `json:"cover_photo_id"`
|
||||
UserID uint `json:"user_id"`
|
||||
CategoryID *uint `json:"category_id"`
|
||||
IsPublic bool `json:"is_public"`
|
||||
IsFeatured bool `json:"is_featured"`
|
||||
HasPassword bool `json:"has_password"`
|
||||
ViewCount int `json:"view_count"`
|
||||
LikeCount int `json:"like_count"`
|
||||
PhotoCount int `json:"photo_count"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
User *UserResponse `json:"user,omitempty"`
|
||||
Category *CategoryResponse `json:"category,omitempty"`
|
||||
CoverPhoto *PhotoListItem `json:"cover_photo,omitempty"`
|
||||
RecentPhotos []PhotoListItem `json:"recent_photos,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// AlbumListItem 相册列表项(简化版)
|
||||
type AlbumListItem struct {
|
||||
ID uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Slug string `json:"slug"`
|
||||
IsPublic bool `json:"is_public"`
|
||||
IsFeatured bool `json:"is_featured"`
|
||||
HasPassword bool `json:"has_password"`
|
||||
ViewCount int `json:"view_count"`
|
||||
LikeCount int `json:"like_count"`
|
||||
PhotoCount int `json:"photo_count"`
|
||||
CoverPhoto *PhotoListItem `json:"cover_photo,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ListAlbumsOptions 相册列表查询选项
|
||||
type ListAlbumsOptions 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 view_count like_count photo_count" validate:"omitempty,oneof=id title created_at updated_at view_count like_count photo_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"`
|
||||
CategoryID *uint `json:"category_id" form:"category_id" binding:"omitempty,min=1" validate:"omitempty,min=1"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// AlbumListResponse 相册列表响应
|
||||
type AlbumListResponse struct {
|
||||
Albums []AlbumListItem `json:"albums"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
// AddPhotosToAlbumRequest 向相册添加照片请求
|
||||
type AddPhotosToAlbumRequest struct {
|
||||
PhotoIDs []uint `json:"photo_ids" binding:"required,min=1" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
// RemovePhotosFromAlbumRequest 从相册移除照片请求
|
||||
type RemovePhotosFromAlbumRequest struct {
|
||||
PhotoIDs []uint `json:"photo_ids" binding:"required,min=1" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
// AlbumPasswordRequest 相册密码验证请求
|
||||
type AlbumPasswordRequest struct {
|
||||
Password string `json:"password" binding:"required" validate:"required"`
|
||||
}
|
||||
|
||||
// AlbumStatsResponse 相册统计响应
|
||||
type AlbumStatsResponse struct {
|
||||
Total int64 `json:"total"`
|
||||
Published int64 `json:"published"`
|
||||
Private int64 `json:"private"`
|
||||
Featured int64 `json:"featured"`
|
||||
WithPassword int64 `json:"with_password"`
|
||||
TotalViews int64 `json:"total_views"`
|
||||
TotalLikes int64 `json:"total_likes"`
|
||||
TotalPhotos int64 `json:"total_photos"`
|
||||
CategoryCounts map[string]int64 `json:"category_counts"`
|
||||
Recent []AlbumListItem `json:"recent"`
|
||||
Popular []AlbumListItem `json:"popular"`
|
||||
}
|
||||
|
||||
// ConvertToAlbumResponse 将相册实体转换为响应DTO
|
||||
func ConvertToAlbumResponse(album *entity.Album) *AlbumResponse {
|
||||
if album == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
response := &AlbumResponse{
|
||||
ID: album.ID,
|
||||
Title: album.Title,
|
||||
Description: album.Description,
|
||||
Slug: album.Slug,
|
||||
CoverPhotoID: album.CoverPhotoID,
|
||||
UserID: album.UserID,
|
||||
CategoryID: album.CategoryID,
|
||||
IsPublic: album.IsPublic,
|
||||
IsFeatured: album.IsFeatured,
|
||||
HasPassword: album.HasPassword(),
|
||||
ViewCount: album.ViewCount,
|
||||
LikeCount: album.LikeCount,
|
||||
PhotoCount: album.PhotoCount,
|
||||
SortOrder: album.SortOrder,
|
||||
CreatedAt: album.CreatedAt,
|
||||
UpdatedAt: album.UpdatedAt,
|
||||
}
|
||||
|
||||
// 转换关联对象
|
||||
if album.User.ID != 0 {
|
||||
response.User = ConvertToUserResponse(&album.User)
|
||||
}
|
||||
if album.Category != nil {
|
||||
response.Category = ConvertToCategoryResponse(album.Category)
|
||||
}
|
||||
if album.CoverPhoto != nil {
|
||||
coverPhoto := ConvertToPhotoListItem(album.CoverPhoto)
|
||||
response.CoverPhoto = &coverPhoto
|
||||
}
|
||||
|
||||
// 转换最近照片
|
||||
if len(album.Photos) > 0 {
|
||||
recentPhotos := make([]PhotoListItem, 0, len(album.Photos))
|
||||
for _, photo := range album.Photos {
|
||||
recentPhotos = append(recentPhotos, ConvertToPhotoListItem(&photo))
|
||||
}
|
||||
response.RecentPhotos = recentPhotos
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// ConvertToAlbumListItem 将相册实体转换为列表项DTO
|
||||
func ConvertToAlbumListItem(album *entity.Album) AlbumListItem {
|
||||
item := AlbumListItem{
|
||||
ID: album.ID,
|
||||
Title: album.Title,
|
||||
Slug: album.Slug,
|
||||
IsPublic: album.IsPublic,
|
||||
IsFeatured: album.IsFeatured,
|
||||
HasPassword: album.HasPassword(),
|
||||
ViewCount: album.ViewCount,
|
||||
LikeCount: album.LikeCount,
|
||||
PhotoCount: album.PhotoCount,
|
||||
CreatedAt: album.CreatedAt,
|
||||
UpdatedAt: album.UpdatedAt,
|
||||
}
|
||||
|
||||
// 转换封面照片
|
||||
if album.CoverPhoto != nil {
|
||||
coverPhoto := ConvertToPhotoListItem(album.CoverPhoto)
|
||||
item.CoverPhoto = &coverPhoto
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
107
backend-old/internal/model/dto/auth_dto.go
Normal file
107
backend-old/internal/model/dto/auth_dto.go
Normal file
@ -0,0 +1,107 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"photography-backend/internal/model/entity"
|
||||
)
|
||||
|
||||
// LoginRequest 登录请求
|
||||
type LoginRequest struct {
|
||||
Email string `json:"email" binding:"required,email" validate:"required,email"`
|
||||
Password string `json:"password" binding:"required" validate:"required"`
|
||||
}
|
||||
|
||||
// RegisterRequest 注册请求
|
||||
type RegisterRequest struct {
|
||||
Username string `json:"username" binding:"required,min=3,max=50" validate:"required,min=3,max=50"`
|
||||
Email string `json:"email" binding:"required,email" validate:"required,email"`
|
||||
Password string `json:"password" binding:"required,min=6" validate:"required,min=6"`
|
||||
Name string `json:"name" binding:"max=100" validate:"max=100"`
|
||||
}
|
||||
|
||||
// RefreshTokenRequest 刷新令牌请求
|
||||
type RefreshTokenRequest struct {
|
||||
RefreshToken string `json:"refresh_token" binding:"required" validate:"required"`
|
||||
}
|
||||
|
||||
// TokenResponse 令牌响应
|
||||
type TokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int64 `json:"expires_in"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
|
||||
// LoginResponse 登录响应
|
||||
type LoginResponse struct {
|
||||
Token TokenResponse `json:"token"`
|
||||
User UserResponse `json:"user"`
|
||||
}
|
||||
|
||||
// RegisterResponse 注册响应
|
||||
type RegisterResponse struct {
|
||||
Token TokenResponse `json:"token"`
|
||||
User UserResponse `json:"user"`
|
||||
}
|
||||
|
||||
// TokenClaims JWT 令牌声明
|
||||
type TokenClaims struct {
|
||||
UserID uint `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Role entity.UserRole `json:"role"`
|
||||
TokenID string `json:"token_id"`
|
||||
IssuedAt time.Time `json:"issued_at"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
|
||||
// ResetPasswordRequest 重置密码请求
|
||||
type ResetPasswordRequest struct {
|
||||
Email string `json:"email" binding:"required,email" validate:"required,email"`
|
||||
}
|
||||
|
||||
// ConfirmResetPasswordRequest 确认重置密码请求
|
||||
type ConfirmResetPasswordRequest struct {
|
||||
Token string `json:"token" binding:"required" validate:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required,min=6" validate:"required,min=6"`
|
||||
}
|
||||
|
||||
// VerifyEmailRequest 验证邮箱请求
|
||||
type VerifyEmailRequest struct {
|
||||
Token string `json:"token" binding:"required" validate:"required"`
|
||||
}
|
||||
|
||||
// LogoutRequest 登出请求
|
||||
type LogoutRequest struct {
|
||||
Token string `json:"token" binding:"required" validate:"required"`
|
||||
}
|
||||
|
||||
// AuthStatsResponse 认证统计响应
|
||||
type AuthStatsResponse struct {
|
||||
TotalLogins int64 `json:"total_logins"`
|
||||
ActiveSessions int64 `json:"active_sessions"`
|
||||
FailedAttempts int64 `json:"failed_attempts"`
|
||||
RecentLogins []LoginInfo `json:"recent_logins"`
|
||||
LoginsByHour map[string]int64 `json:"logins_by_hour"`
|
||||
LoginsByDevice map[string]int64 `json:"logins_by_device"`
|
||||
}
|
||||
|
||||
// LoginInfo 登录信息
|
||||
type LoginInfo struct {
|
||||
UserID uint `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
LoginTime time.Time `json:"login_time"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
UserAgent string `json:"user_agent"`
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
// ValidateTokenResponse 验证令牌响应
|
||||
type ValidateTokenResponse struct {
|
||||
Valid bool `json:"valid"`
|
||||
Claims *TokenClaims `json:"claims,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
143
backend-old/internal/model/dto/category_dto.go
Normal file
143
backend-old/internal/model/dto/category_dto.go
Normal file
@ -0,0 +1,143 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"photography-backend/internal/model/entity"
|
||||
)
|
||||
|
||||
// CreateCategoryRequest 创建分类请求
|
||||
type CreateCategoryRequest struct {
|
||||
Name string `json:"name" binding:"required,min=1,max=100" validate:"required,min=1,max=100"`
|
||||
Description string `json:"description" binding:"omitempty,max=1000" validate:"omitempty,max=1000"`
|
||||
Slug string `json:"slug" binding:"omitempty,min=1,max=100" validate:"omitempty,min=1,max=100"`
|
||||
ParentID *uint `json:"parent_id" binding:"omitempty,min=1" validate:"omitempty,min=1"`
|
||||
Color string `json:"color" binding:"omitempty,len=7" validate:"omitempty,len=7"`
|
||||
CoverImage string `json:"cover_image" binding:"omitempty,url" validate:"omitempty,url"`
|
||||
SortOrder int `json:"sort_order" binding:"omitempty,min=0" validate:"omitempty,min=0"`
|
||||
}
|
||||
|
||||
// UpdateCategoryRequest 更新分类请求
|
||||
type UpdateCategoryRequest struct {
|
||||
Name *string `json:"name" binding:"omitempty,min=1,max=100" validate:"omitempty,min=1,max=100"`
|
||||
Description *string `json:"description" binding:"omitempty,max=1000" validate:"omitempty,max=1000"`
|
||||
Slug *string `json:"slug" binding:"omitempty,min=1,max=100" validate:"omitempty,min=1,max=100"`
|
||||
ParentID *uint `json:"parent_id" binding:"omitempty,min=0" validate:"omitempty,min=0"`
|
||||
Color *string `json:"color" binding:"omitempty,len=7" validate:"omitempty,len=7"`
|
||||
CoverImage *string `json:"cover_image" binding:"omitempty,url" validate:"omitempty,url"`
|
||||
SortOrder *int `json:"sort_order" binding:"omitempty,min=0" validate:"omitempty,min=0"`
|
||||
IsActive *bool `json:"is_active" binding:"omitempty"`
|
||||
}
|
||||
|
||||
// CategoryResponse 分类响应
|
||||
type CategoryResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Slug string `json:"slug"`
|
||||
ParentID *uint `json:"parent_id"`
|
||||
Color string `json:"color"`
|
||||
CoverImage string `json:"cover_image"`
|
||||
Sort int `json:"sort"`
|
||||
IsActive bool `json:"is_active"`
|
||||
PhotoCount int64 `json:"photo_count"`
|
||||
AlbumCount int64 `json:"album_count"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// CategoryTreeResponse 分类树响应
|
||||
type CategoryTreeResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Slug string `json:"slug"`
|
||||
ParentID *uint `json:"parent_id"`
|
||||
Color string `json:"color"`
|
||||
CoverImage string `json:"cover_image"`
|
||||
Sort int `json:"sort"`
|
||||
IsActive bool `json:"is_active"`
|
||||
PhotoCount int64 `json:"photo_count"`
|
||||
AlbumCount int64 `json:"album_count"`
|
||||
Children []CategoryTreeResponse `json:"children"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ListCategoriesOptions 分类列表查询选项
|
||||
type ListCategoriesOptions 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 name sort created_at updated_at" validate:"omitempty,oneof=id name sort created_at updated_at"`
|
||||
Order string `json:"order" form:"order" binding:"omitempty,oneof=asc desc" validate:"omitempty,oneof=asc desc"`
|
||||
ParentID *uint `json:"parent_id" form:"parent_id" binding:"omitempty,min=0" validate:"omitempty,min=0"`
|
||||
IsActive *bool `json:"is_active" form:"is_active" binding:"omitempty"`
|
||||
Search string `json:"search" form:"search" binding:"omitempty,max=100" validate:"omitempty,max=100"`
|
||||
WithCount bool `json:"with_count" form:"with_count" binding:"omitempty"`
|
||||
}
|
||||
|
||||
// CategoryListResponse 分类列表响应
|
||||
type CategoryListResponse struct {
|
||||
Categories []CategoryResponse `json:"categories"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
// ReorderCategoriesRequest 重新排序分类请求
|
||||
type ReorderCategoriesRequest struct {
|
||||
ParentID *uint `json:"parent_id" binding:"omitempty,min=0" validate:"omitempty,min=0"`
|
||||
CategoryIDs []uint `json:"category_ids" binding:"required,min=1" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
// CategoryStatsResponse 分类统计响应
|
||||
type CategoryStatsResponse struct {
|
||||
Total int64 `json:"total"`
|
||||
Active int64 `json:"active"`
|
||||
TopLevel int64 `json:"top_level"`
|
||||
PhotoCounts map[string]int64 `json:"photo_counts"`
|
||||
Popular []CategoryResponse `json:"popular"`
|
||||
}
|
||||
|
||||
// ConvertToCategoryResponse 将分类实体转换为响应DTO
|
||||
func ConvertToCategoryResponse(category *entity.Category) *CategoryResponse {
|
||||
if category == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &CategoryResponse{
|
||||
ID: category.ID,
|
||||
Name: category.Name,
|
||||
Description: category.Description,
|
||||
ParentID: category.ParentID,
|
||||
Color: category.Color,
|
||||
CoverImage: category.CoverImage,
|
||||
Sort: category.Sort,
|
||||
IsActive: category.IsActive,
|
||||
PhotoCount: category.PhotoCount,
|
||||
CreatedAt: category.CreatedAt,
|
||||
UpdatedAt: category.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertToCategoryTreeResponse 将分类树转换为响应DTO
|
||||
func ConvertToCategoryTreeResponse(tree []entity.CategoryTree) []CategoryTreeResponse {
|
||||
result := make([]CategoryTreeResponse, len(tree))
|
||||
for i, category := range tree {
|
||||
result[i] = CategoryTreeResponse{
|
||||
ID: category.ID,
|
||||
Name: category.Name,
|
||||
Description: category.Description,
|
||||
ParentID: category.ParentID,
|
||||
Color: category.Color,
|
||||
CoverImage: category.CoverImage,
|
||||
Sort: category.Sort,
|
||||
IsActive: category.IsActive,
|
||||
PhotoCount: category.PhotoCount,
|
||||
Children: ConvertToCategoryTreeResponse(category.Children),
|
||||
CreatedAt: category.CreatedAt,
|
||||
UpdatedAt: category.UpdatedAt,
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
264
backend-old/internal/model/dto/photo_dto.go
Normal file
264
backend-old/internal/model/dto/photo_dto.go
Normal file
@ -0,0 +1,264 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
135
backend-old/internal/model/dto/tag_dto.go
Normal file
135
backend-old/internal/model/dto/tag_dto.go
Normal file
@ -0,0 +1,135 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"photography-backend/internal/model/entity"
|
||||
)
|
||||
|
||||
// CreateTagRequest 创建标签请求
|
||||
type CreateTagRequest struct {
|
||||
Name string `json:"name" binding:"required,min=1,max=50" validate:"required,min=1,max=50"`
|
||||
Color string `json:"color" binding:"omitempty,len=7" validate:"omitempty,len=7"`
|
||||
}
|
||||
|
||||
// UpdateTagRequest 更新标签请求
|
||||
type UpdateTagRequest struct {
|
||||
Name *string `json:"name" binding:"omitempty,min=1,max=50" validate:"omitempty,min=1,max=50"`
|
||||
Color *string `json:"color" binding:"omitempty,len=7" validate:"omitempty,len=7"`
|
||||
IsActive *bool `json:"is_active" binding:"omitempty"`
|
||||
}
|
||||
|
||||
// TagResponse 标签响应
|
||||
type TagResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Color string `json:"color"`
|
||||
UseCount int `json:"use_count"`
|
||||
IsActive bool `json:"is_active"`
|
||||
IsPopular bool `json:"is_popular"`
|
||||
PhotoCount int64 `json:"photo_count"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// TagListItem 标签列表项(简化版)
|
||||
type TagListItem struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Color string `json:"color"`
|
||||
UseCount int `json:"use_count"`
|
||||
IsActive bool `json:"is_active"`
|
||||
IsPopular bool `json:"is_popular"`
|
||||
}
|
||||
|
||||
// ListTagsOptions 标签列表查询选项
|
||||
type ListTagsOptions 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 name use_count created_at updated_at" validate:"omitempty,oneof=id name use_count created_at updated_at"`
|
||||
Order string `json:"order" form:"order" binding:"omitempty,oneof=asc desc" validate:"omitempty,oneof=asc desc"`
|
||||
IsActive *bool `json:"is_active" form:"is_active" binding:"omitempty"`
|
||||
Search string `json:"search" form:"search" binding:"omitempty,max=100" validate:"omitempty,max=100"`
|
||||
Popular bool `json:"popular" form:"popular" binding:"omitempty"`
|
||||
}
|
||||
|
||||
// TagListResponse 标签列表响应
|
||||
type TagListResponse struct {
|
||||
Tags []TagResponse `json:"tags"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
// TagCloudResponse 标签云响应
|
||||
type TagCloudResponse struct {
|
||||
Tags []TagCloudItem `json:"tags"`
|
||||
}
|
||||
|
||||
// TagCloudItem 标签云项
|
||||
type TagCloudItem struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Color string `json:"color"`
|
||||
UseCount int `json:"use_count"`
|
||||
Weight int `json:"weight"` // 1-10 的权重,用于控制标签大小
|
||||
}
|
||||
|
||||
// TagStatsResponse 标签统计响应
|
||||
type TagStatsResponse struct {
|
||||
Total int64 `json:"total"`
|
||||
Active int64 `json:"active"`
|
||||
Popular []TagResponse `json:"popular"`
|
||||
PhotoCounts map[string]int64 `json:"photo_counts"`
|
||||
Recent []TagResponse `json:"recent"`
|
||||
}
|
||||
|
||||
// ConvertToTagResponse 将标签实体转换为响应DTO
|
||||
func ConvertToTagResponse(tag *entity.Tag) *TagResponse {
|
||||
if tag == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &TagResponse{
|
||||
ID: tag.ID,
|
||||
Name: tag.Name,
|
||||
Color: tag.Color,
|
||||
UseCount: tag.UseCount,
|
||||
IsActive: tag.IsActive,
|
||||
IsPopular: tag.IsPopular(),
|
||||
CreatedAt: tag.CreatedAt,
|
||||
UpdatedAt: tag.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertToTagListItem 将标签实体转换为列表项DTO
|
||||
func ConvertToTagListItem(tag *entity.Tag) TagListItem {
|
||||
return TagListItem{
|
||||
ID: tag.ID,
|
||||
Name: tag.Name,
|
||||
Color: tag.Color,
|
||||
UseCount: tag.UseCount,
|
||||
IsActive: tag.IsActive,
|
||||
IsPopular: tag.IsPopular(),
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertToTagCloudItem 将标签实体转换为标签云项
|
||||
func ConvertToTagCloudItem(tag *entity.Tag, maxUseCount int) TagCloudItem {
|
||||
// 计算权重(1-10)
|
||||
weight := 1
|
||||
if maxUseCount > 0 {
|
||||
weight = int(float64(tag.UseCount)/float64(maxUseCount)*9) + 1
|
||||
if weight > 10 {
|
||||
weight = 10
|
||||
}
|
||||
}
|
||||
|
||||
return TagCloudItem{
|
||||
ID: tag.ID,
|
||||
Name: tag.Name,
|
||||
Color: tag.Color,
|
||||
UseCount: tag.UseCount,
|
||||
Weight: weight,
|
||||
}
|
||||
}
|
||||
148
backend-old/internal/model/dto/user_dto.go
Normal file
148
backend-old/internal/model/dto/user_dto.go
Normal file
@ -0,0 +1,148 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"photography-backend/internal/model/entity"
|
||||
)
|
||||
|
||||
// CreateUserRequest 创建用户请求
|
||||
type CreateUserRequest struct {
|
||||
Username string `json:"username" binding:"required,min=3,max=50" validate:"required,min=3,max=50"`
|
||||
Email string `json:"email" binding:"required,email" validate:"required,email"`
|
||||
Password string `json:"password" binding:"required,min=6" validate:"required,min=6"`
|
||||
Name string `json:"name" binding:"max=100" validate:"max=100"`
|
||||
Role entity.UserRole `json:"role" binding:"omitempty,oneof=user admin photographer" validate:"omitempty,oneof=user admin photographer"`
|
||||
}
|
||||
|
||||
// UpdateUserRequest 更新用户请求
|
||||
type UpdateUserRequest struct {
|
||||
Username *string `json:"username" binding:"omitempty,min=3,max=50" validate:"omitempty,min=3,max=50"`
|
||||
Email *string `json:"email" binding:"omitempty,email" validate:"omitempty,email"`
|
||||
Name *string `json:"name" binding:"omitempty,max=100" validate:"omitempty,max=100"`
|
||||
Avatar *string `json:"avatar" binding:"omitempty,url" validate:"omitempty,url"`
|
||||
Bio *string `json:"bio" binding:"omitempty,max=1000" validate:"omitempty,max=1000"`
|
||||
Website *string `json:"website" binding:"omitempty,url" validate:"omitempty,url"`
|
||||
Location *string `json:"location" binding:"omitempty,max=100" validate:"omitempty,max=100"`
|
||||
IsActive *bool `json:"is_active" binding:"omitempty"`
|
||||
IsPublic *bool `json:"is_public" binding:"omitempty"`
|
||||
}
|
||||
|
||||
// ChangePasswordRequest 修改密码请求
|
||||
type ChangePasswordRequest struct {
|
||||
OldPassword string `json:"old_password" binding:"required" validate:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required,min=6" validate:"required,min=6"`
|
||||
}
|
||||
|
||||
// UserResponse 用户响应
|
||||
type UserResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Bio string `json:"bio"`
|
||||
Website string `json:"website"`
|
||||
Location string `json:"location"`
|
||||
Role entity.UserRole `json:"role"`
|
||||
IsActive bool `json:"is_active"`
|
||||
IsPublic bool `json:"is_public"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
LastLogin *time.Time `json:"last_login"`
|
||||
LoginCount int `json:"login_count"`
|
||||
PhotoCount int64 `json:"photo_count"`
|
||||
AlbumCount int64 `json:"album_count"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// UserProfileResponse 用户档案响应(公开信息)
|
||||
type UserProfileResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Bio string `json:"bio"`
|
||||
Website string `json:"website"`
|
||||
Location string `json:"location"`
|
||||
Role entity.UserRole `json:"role"`
|
||||
PhotoCount int64 `json:"photo_count"`
|
||||
AlbumCount int64 `json:"album_count"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// ListUsersOptions 用户列表查询选项
|
||||
type ListUsersOptions 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 username email created_at updated_at" validate:"omitempty,oneof=id username email created_at updated_at"`
|
||||
Order string `json:"order" form:"order" binding:"omitempty,oneof=asc desc" validate:"omitempty,oneof=asc desc"`
|
||||
Role entity.UserRole `json:"role" form:"role" binding:"omitempty,oneof=user admin photographer" validate:"omitempty,oneof=user admin photographer"`
|
||||
IsActive *bool `json:"is_active" form:"is_active" binding:"omitempty"`
|
||||
IsPublic *bool `json:"is_public" form:"is_public" binding:"omitempty"`
|
||||
Search string `json:"search" form:"search" binding:"omitempty,max=100" validate:"omitempty,max=100"`
|
||||
}
|
||||
|
||||
// UserListResponse 用户列表响应
|
||||
type UserListResponse struct {
|
||||
Users []UserResponse `json:"users"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
// UserStatsResponse 用户统计响应
|
||||
type UserStatsResponse struct {
|
||||
Total int64 `json:"total"`
|
||||
Active int64 `json:"active"`
|
||||
Inactive int64 `json:"inactive"`
|
||||
Verified int64 `json:"verified"`
|
||||
Unverified int64 `json:"unverified"`
|
||||
RoleCounts map[entity.UserRole]int64 `json:"role_counts"`
|
||||
RecentLogins []UserResponse `json:"recent_logins"`
|
||||
}
|
||||
|
||||
// ConvertToUserResponse 将用户实体转换为响应DTO
|
||||
func ConvertToUserResponse(user *entity.User) *UserResponse {
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &UserResponse{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Email: user.Email,
|
||||
Name: user.Name,
|
||||
Avatar: user.Avatar,
|
||||
Bio: user.Bio,
|
||||
Website: user.Website,
|
||||
Location: user.Location,
|
||||
Role: user.Role,
|
||||
IsActive: user.IsActive,
|
||||
IsPublic: user.IsPublic,
|
||||
EmailVerified: user.EmailVerified,
|
||||
LastLogin: user.LastLogin,
|
||||
LoginCount: user.LoginCount,
|
||||
CreatedAt: user.CreatedAt,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertToUserProfile 将用户实体转换为公开档案DTO
|
||||
func ConvertToUserProfile(user *entity.User) *UserProfileResponse {
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &UserProfileResponse{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Avatar: user.Avatar,
|
||||
Bio: user.Bio,
|
||||
Website: user.Website,
|
||||
Location: user.Location,
|
||||
Role: user.Role,
|
||||
CreatedAt: user.CreatedAt,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user