fix
This commit is contained in:
84
backend-old/internal/model/entity/album.go
Normal file
84
backend-old/internal/model/entity/album.go
Normal file
@ -0,0 +1,84 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Album 相册实体
|
||||
type Album struct {
|
||||
ID uint `json:"id" gorm:"primarykey"`
|
||||
Title string `json:"title" gorm:"not null;size:200"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Slug string `json:"slug" gorm:"uniqueIndex;size:255"`
|
||||
CoverPhotoID *uint `json:"cover_photo_id" gorm:"index"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
CategoryID *uint `json:"category_id" gorm:"index"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:true;index"`
|
||||
IsFeatured bool `json:"is_featured" gorm:"default:false;index"`
|
||||
Password string `json:"-" gorm:"size:255"` // 私密相册密码
|
||||
ViewCount int `json:"view_count" gorm:"default:0;index"`
|
||||
LikeCount int `json:"like_count" gorm:"default:0;index"`
|
||||
PhotoCount int `json:"photo_count" gorm:"default:0;index"`
|
||||
SortOrder int `json:"sort_order" gorm:"default:0;index"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
|
||||
|
||||
// 关联
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
Category *Category `json:"category,omitempty" gorm:"foreignKey:CategoryID"`
|
||||
CoverPhoto *Photo `json:"cover_photo,omitempty" gorm:"foreignKey:CoverPhotoID"`
|
||||
Photos []Photo `json:"photos,omitempty" gorm:"foreignKey:AlbumID"`
|
||||
}
|
||||
|
||||
// AlbumStats 相册统计信息
|
||||
type AlbumStats 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"` // 总点赞数
|
||||
TotalPhotos int64 `json:"total_photos"` // 总照片数
|
||||
CategoryCounts map[string]int64 `json:"category_counts"` // 各分类相册数量
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Album) TableName() string {
|
||||
return "albums"
|
||||
}
|
||||
|
||||
// HasPassword 检查是否设置了密码
|
||||
func (a *Album) HasPassword() bool {
|
||||
return a.Password != ""
|
||||
}
|
||||
|
||||
// IsEmpty 检查相册是否为空
|
||||
func (a *Album) IsEmpty() bool {
|
||||
return a.PhotoCount == 0
|
||||
}
|
||||
|
||||
// CanViewBy 检查指定用户是否可以查看相册
|
||||
func (a *Album) CanViewBy(user *User) bool {
|
||||
// 公开相册
|
||||
if a.IsPublic && !a.HasPassword() {
|
||||
return true
|
||||
}
|
||||
|
||||
// 相册所有者或管理员
|
||||
if user != nil && (user.ID == a.UserID || user.IsAdmin()) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// CanEditBy 检查指定用户是否可以编辑相册
|
||||
func (a *Album) CanEditBy(user *User) bool {
|
||||
if user == nil {
|
||||
return false
|
||||
}
|
||||
return user.ID == a.UserID || user.IsAdmin()
|
||||
}
|
||||
131
backend-old/internal/model/entity/category.go
Normal file
131
backend-old/internal/model/entity/category.go
Normal file
@ -0,0 +1,131 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Category 分类实体
|
||||
type Category struct {
|
||||
ID uint `json:"id" gorm:"primarykey"`
|
||||
Name string `json:"name" gorm:"not null;size:100"`
|
||||
Slug string `json:"slug" gorm:"uniqueIndex;not null;size:100"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
ParentID *uint `json:"parent_id" gorm:"index"`
|
||||
Color string `json:"color" gorm:"default:#3b82f6;size:7"`
|
||||
CoverImage string `json:"cover_image" gorm:"size:500"`
|
||||
Sort int `json:"sort" gorm:"default:0;index"`
|
||||
SortOrder int `json:"sort_order" gorm:"default:0;index"`
|
||||
IsActive bool `json:"is_active" gorm:"default:true;index"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
|
||||
|
||||
// 关联
|
||||
Parent *Category `json:"parent,omitempty" gorm:"foreignKey:ParentID"`
|
||||
Children []Category `json:"children,omitempty" gorm:"foreignKey:ParentID"`
|
||||
Photos []Photo `json:"photos,omitempty" gorm:"foreignKey:CategoryID"`
|
||||
Albums []Album `json:"albums,omitempty" gorm:"foreignKey:CategoryID"`
|
||||
PhotoCount int64 `json:"photo_count" gorm:"-"` // 照片数量,不存储在数据库中
|
||||
}
|
||||
|
||||
// CategoryTree 分类树结构(用于前端显示)
|
||||
type CategoryTree struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Description string `json:"description"`
|
||||
ParentID *uint `json:"parent_id"`
|
||||
Color string `json:"color"`
|
||||
CoverImage string `json:"cover_image"`
|
||||
Sort int `json:"sort"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
IsActive bool `json:"is_active"`
|
||||
PhotoCount int64 `json:"photo_count"`
|
||||
Children []CategoryTree `json:"children"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// CategoryStats 分类统计信息
|
||||
type CategoryStats struct {
|
||||
Total int64 `json:"total"` // 总分类数
|
||||
Active int64 `json:"active"` // 活跃分类数
|
||||
TopLevel int64 `json:"top_level"` // 顶级分类数
|
||||
TotalCategories int64 `json:"total_categories"` // 总分类数(别名)
|
||||
MaxLevel int64 `json:"max_level"` // 最大层级
|
||||
FeaturedCount int64 `json:"featured_count"` // 特色分类数
|
||||
PhotoCounts map[string]int64 `json:"photo_counts"` // 各分类照片数量
|
||||
}
|
||||
|
||||
// CategoryListParams 分类列表查询参数
|
||||
type CategoryListParams struct {
|
||||
Page int `json:"page" form:"page"`
|
||||
Limit int `json:"limit" form:"limit"`
|
||||
Search string `json:"search" form:"search"`
|
||||
ParentID *uint `json:"parent_id" form:"parent_id"`
|
||||
IsActive *bool `json:"is_active" form:"is_active"`
|
||||
IncludeStats bool `json:"include_stats" form:"include_stats"`
|
||||
SortBy string `json:"sort_by" form:"sort_by"`
|
||||
Order string `json:"order" form:"order"`
|
||||
}
|
||||
|
||||
// CreateCategoryRequest 创建分类请求
|
||||
type CreateCategoryRequest struct {
|
||||
Name string `json:"name" binding:"required,max=100"`
|
||||
Slug string `json:"slug" binding:"required,max=100"`
|
||||
Description string `json:"description" binding:"max=500"`
|
||||
ParentID *uint `json:"parent_id"`
|
||||
Color string `json:"color" binding:"max=7"`
|
||||
CoverImage string `json:"cover_image" binding:"max=500"`
|
||||
Sort int `json:"sort"`
|
||||
}
|
||||
|
||||
// UpdateCategoryRequest 更新分类请求
|
||||
type UpdateCategoryRequest struct {
|
||||
Name *string `json:"name" binding:"omitempty,max=100"`
|
||||
Slug *string `json:"slug" binding:"omitempty,max=100"`
|
||||
Description *string `json:"description" binding:"max=500"`
|
||||
ParentID *uint `json:"parent_id"`
|
||||
Color *string `json:"color" binding:"omitempty,max=7"`
|
||||
CoverImage *string `json:"cover_image" binding:"omitempty,max=500"`
|
||||
SortOrder *int `json:"sort_order"`
|
||||
IsActive *bool `json:"is_active"`
|
||||
}
|
||||
|
||||
// ReorderCategoriesRequest 重新排序分类请求
|
||||
type ReorderCategoriesRequest struct {
|
||||
ParentID *uint `json:"parent_id"`
|
||||
CategoryIDs []uint `json:"category_ids" binding:"required,min=1"`
|
||||
}
|
||||
|
||||
// GenerateSlugRequest 生成slug请求
|
||||
type GenerateSlugRequest struct {
|
||||
Name string `json:"name" binding:"required,max=100"`
|
||||
}
|
||||
|
||||
// GenerateSlugResponse 生成slug响应
|
||||
type GenerateSlugResponse struct {
|
||||
Slug string `json:"slug"`
|
||||
}
|
||||
|
||||
// SuccessResponse 成功响应
|
||||
type SuccessResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Category) TableName() string {
|
||||
return "categories"
|
||||
}
|
||||
|
||||
// IsTopLevel 检查是否为顶级分类
|
||||
func (c *Category) IsTopLevel() bool {
|
||||
return c.ParentID == nil
|
||||
}
|
||||
|
||||
// HasChildren 检查是否有子分类
|
||||
func (c *Category) HasChildren() bool {
|
||||
return len(c.Children) > 0
|
||||
}
|
||||
244
backend-old/internal/model/entity/photo.go
Normal file
244
backend-old/internal/model/entity/photo.go
Normal file
@ -0,0 +1,244 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Photo 照片实体
|
||||
type Photo struct {
|
||||
ID uint `json:"id" gorm:"primarykey"`
|
||||
Title string `json:"title" gorm:"not null;size:200"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Filename string `json:"filename" gorm:"not null;size:255"`
|
||||
OriginalFilename string `json:"original_filename" gorm:"not null;size:255"`
|
||||
UniqueFilename string `json:"unique_filename" gorm:"not null;size:255"`
|
||||
FilePath string `json:"file_path" gorm:"not null;size:500"`
|
||||
OriginalURL string `json:"original_url" gorm:"not null;size:500"`
|
||||
ThumbnailURL string `json:"thumbnail_url" gorm:"size:500"`
|
||||
MediumURL string `json:"medium_url" gorm:"size:500"`
|
||||
LargeURL string `json:"large_url" gorm:"size:500"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
MimeType string `json:"mime_type" gorm:"size:100"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Status PhotoStatus `json:"status" gorm:"default:active;size:20"`
|
||||
|
||||
// EXIF 信息
|
||||
Camera string `json:"camera" gorm:"size:100"`
|
||||
Lens string `json:"lens" gorm:"size:100"`
|
||||
CameraMake string `json:"camera_make" gorm:"size:100"`
|
||||
CameraModel string `json:"camera_model" gorm:"size:100"`
|
||||
LensModel string `json:"lens_model" gorm:"size:100"`
|
||||
FocalLength *float64 `json:"focal_length" gorm:"type:decimal(5,2)"`
|
||||
Aperture *float64 `json:"aperture" gorm:"type:decimal(3,1)"`
|
||||
ShutterSpeed string `json:"shutter_speed" gorm:"size:20"`
|
||||
ISO *int `json:"iso"`
|
||||
TakenAt *time.Time `json:"taken_at"`
|
||||
|
||||
// 地理位置信息
|
||||
LocationName string `json:"location_name" gorm:"size:200"`
|
||||
Latitude *float64 `json:"latitude" gorm:"type:decimal(10,8)"`
|
||||
Longitude *float64 `json:"longitude" gorm:"type:decimal(11,8)"`
|
||||
|
||||
// 关联
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
AlbumID *uint `json:"album_id" gorm:"index"`
|
||||
CategoryID *uint `json:"category_id" gorm:"index"`
|
||||
|
||||
// 状态和统计
|
||||
IsPublic bool `json:"is_public" gorm:"default:true;index"`
|
||||
IsFeatured bool `json:"is_featured" gorm:"default:false;index"`
|
||||
ViewCount int `json:"view_count" gorm:"default:0;index"`
|
||||
LikeCount int `json:"like_count" gorm:"default:0;index"`
|
||||
DownloadCount int `json:"download_count" gorm:"default:0"`
|
||||
SortOrder int `json:"sort_order" gorm:"default:0;index"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
|
||||
|
||||
// 关联对象
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
Album *Album `json:"album,omitempty" gorm:"foreignKey:AlbumID"`
|
||||
Category *Category `json:"category,omitempty" gorm:"foreignKey:CategoryID"`
|
||||
Tags []Tag `json:"tags,omitempty" gorm:"many2many:photo_tags;"`
|
||||
}
|
||||
|
||||
// PhotoStatus 照片状态枚举
|
||||
type PhotoStatus string
|
||||
|
||||
const (
|
||||
PhotoStatusActive PhotoStatus = "active"
|
||||
PhotoStatusInactive PhotoStatus = "inactive"
|
||||
PhotoStatusDeleted PhotoStatus = "deleted"
|
||||
PhotoStatusDraft PhotoStatus = "draft"
|
||||
PhotoStatusPrivate PhotoStatus = "private"
|
||||
)
|
||||
|
||||
// Status constants for compatibility
|
||||
const (
|
||||
StatusPublished PhotoStatus = "active"
|
||||
StatusDraft PhotoStatus = "draft"
|
||||
StatusArchived PhotoStatus = "inactive"
|
||||
)
|
||||
|
||||
// PhotoTag 照片标签关联表
|
||||
type PhotoTag struct {
|
||||
PhotoID uint `json:"photo_id" gorm:"primaryKey"`
|
||||
TagID uint `json:"tag_id" gorm:"primaryKey"`
|
||||
}
|
||||
|
||||
// PhotoStats 照片统计信息
|
||||
type PhotoStats 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"` // 总文件大小
|
||||
TotalSize int64 `json:"total_size"` // 总大小(别名)
|
||||
ThisMonth int64 `json:"this_month"` // 本月新增
|
||||
Today int64 `json:"today"` // 今日新增
|
||||
StatusStats map[string]int64 `json:"status_stats"` // 状态统计
|
||||
CategoryCounts map[string]int64 `json:"category_counts"` // 各分类照片数量
|
||||
TagCounts map[string]int64 `json:"tag_counts"` // 各标签照片数量
|
||||
}
|
||||
|
||||
// PhotoListParams 照片列表查询参数
|
||||
type PhotoListParams struct {
|
||||
Page int `json:"page" form:"page"`
|
||||
Limit int `json:"limit" form:"limit"`
|
||||
Sort string `json:"sort" form:"sort"`
|
||||
Order string `json:"order" form:"order"`
|
||||
Search string `json:"search" form:"search"`
|
||||
UserID *uint `json:"user_id" form:"user_id"`
|
||||
Status *PhotoStatus `json:"status" form:"status"`
|
||||
CategoryID *uint `json:"category_id" form:"category_id"`
|
||||
TagID *uint `json:"tag_id" form:"tag_id"`
|
||||
DateFrom *time.Time `json:"date_from" form:"date_from"`
|
||||
DateTo *time.Time `json:"date_to" form:"date_to"`
|
||||
}
|
||||
|
||||
// CreatePhotoRequest 创建照片请求
|
||||
type CreatePhotoRequest struct {
|
||||
Title string `json:"title" binding:"required,max=200"`
|
||||
Description string `json:"description" binding:"max=1000"`
|
||||
OriginalFilename string `json:"original_filename"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
Status string `json:"status" binding:"oneof=active inactive"`
|
||||
Camera string `json:"camera" binding:"max=100"`
|
||||
Lens string `json:"lens" binding:"max=100"`
|
||||
ISO *int `json:"iso"`
|
||||
Aperture *float64 `json:"aperture"`
|
||||
ShutterSpeed string `json:"shutter_speed" binding:"max=20"`
|
||||
FocalLength *float64 `json:"focal_length"`
|
||||
TakenAt *time.Time `json:"taken_at"`
|
||||
CategoryIDs []uint `json:"category_ids"`
|
||||
TagIDs []uint `json:"tag_ids"`
|
||||
}
|
||||
|
||||
// UpdatePhotoRequest 更新照片请求
|
||||
type UpdatePhotoRequest struct {
|
||||
Title *string `json:"title" binding:"omitempty,max=200"`
|
||||
Description *string `json:"description" binding:"max=1000"`
|
||||
Status *string `json:"status" binding:"omitempty,oneof=active inactive"`
|
||||
Camera *string `json:"camera" binding:"omitempty,max=100"`
|
||||
Lens *string `json:"lens" binding:"omitempty,max=100"`
|
||||
ISO *int `json:"iso"`
|
||||
Aperture *float64 `json:"aperture"`
|
||||
ShutterSpeed *string `json:"shutter_speed" binding:"omitempty,max=20"`
|
||||
FocalLength *float64 `json:"focal_length"`
|
||||
TakenAt *time.Time `json:"taken_at"`
|
||||
CategoryIDs *[]uint `json:"category_ids"`
|
||||
TagIDs *[]uint `json:"tag_ids"`
|
||||
}
|
||||
|
||||
// BatchUpdatePhotosRequest 批量更新照片请求
|
||||
type BatchUpdatePhotosRequest struct {
|
||||
Status *string `json:"status" binding:"omitempty,oneof=active inactive"`
|
||||
CategoryIDs *[]uint `json:"category_ids"`
|
||||
TagIDs *[]uint `json:"tag_ids"`
|
||||
}
|
||||
|
||||
// PhotoFormat 照片格式
|
||||
type PhotoFormat struct {
|
||||
ID uint `json:"id" gorm:"primarykey"`
|
||||
PhotoID uint `json:"photo_id" gorm:"not null;index"`
|
||||
Format string `json:"format" gorm:"not null;size:20"` // jpg, png, webp
|
||||
Quality int `json:"quality" gorm:"not null"` // 1-100
|
||||
Width int `json:"width" gorm:"not null"`
|
||||
Height int `json:"height" gorm:"not null"`
|
||||
FileSize int64 `json:"file_size" gorm:"not null"`
|
||||
URL string `json:"url" gorm:"not null;size:500"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
func (PhotoFormat) TableName() string {
|
||||
return "photo_formats"
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Photo) TableName() string {
|
||||
return "photos"
|
||||
}
|
||||
|
||||
// TableName 指定关联表名
|
||||
func (PhotoTag) TableName() string {
|
||||
return "photo_tags"
|
||||
}
|
||||
|
||||
// GetAspectRatio 获取宽高比
|
||||
func (p *Photo) GetAspectRatio() float64 {
|
||||
if p.Height == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(p.Width) / float64(p.Height)
|
||||
}
|
||||
|
||||
// IsLandscape 是否为横向
|
||||
func (p *Photo) IsLandscape() bool {
|
||||
return p.Width > p.Height
|
||||
}
|
||||
|
||||
// IsPortrait 是否为纵向
|
||||
func (p *Photo) IsPortrait() bool {
|
||||
return p.Width < p.Height
|
||||
}
|
||||
|
||||
// IsSquare 是否为正方形
|
||||
func (p *Photo) IsSquare() bool {
|
||||
return p.Width == p.Height
|
||||
}
|
||||
|
||||
// HasLocation 是否有地理位置信息
|
||||
func (p *Photo) HasLocation() bool {
|
||||
return p.Latitude != nil && p.Longitude != nil
|
||||
}
|
||||
|
||||
// HasEXIF 是否有EXIF信息
|
||||
func (p *Photo) HasEXIF() bool {
|
||||
return p.CameraMake != "" || p.CameraModel != "" || p.TakenAt != nil
|
||||
}
|
||||
|
||||
// GetDisplayURL 获取显示URL(根据尺寸)
|
||||
func (p *Photo) GetDisplayURL(size string) string {
|
||||
switch size {
|
||||
case "thumbnail":
|
||||
if p.ThumbnailURL != "" {
|
||||
return p.ThumbnailURL
|
||||
}
|
||||
case "medium":
|
||||
if p.MediumURL != "" {
|
||||
return p.MediumURL
|
||||
}
|
||||
case "large":
|
||||
if p.LargeURL != "" {
|
||||
return p.LargeURL
|
||||
}
|
||||
}
|
||||
return p.OriginalURL
|
||||
}
|
||||
99
backend-old/internal/model/entity/tag.go
Normal file
99
backend-old/internal/model/entity/tag.go
Normal file
@ -0,0 +1,99 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Tag 标签实体
|
||||
type Tag struct {
|
||||
ID uint `json:"id" gorm:"primarykey"`
|
||||
Name string `json:"name" gorm:"uniqueIndex;not null;size:50"`
|
||||
Slug string `json:"slug" gorm:"uniqueIndex;not null;size:50"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Color string `json:"color" gorm:"default:#6b7280;size:7"`
|
||||
UseCount int `json:"use_count" gorm:"default:0;index"`
|
||||
PhotoCount int64 `json:"photo_count" gorm:"-"` // 不存储在数据库中
|
||||
IsActive bool `json:"is_active" gorm:"default:true;index"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
|
||||
|
||||
// 关联
|
||||
Photos []Photo `json:"photos,omitempty" gorm:"many2many:photo_tags;"`
|
||||
}
|
||||
|
||||
// TagStats 标签统计信息
|
||||
type TagStats struct {
|
||||
Total int64 `json:"total"` // 总标签数
|
||||
Active int64 `json:"active"` // 活跃标签数
|
||||
Used int64 `json:"used"` // 已使用标签数
|
||||
Unused int64 `json:"unused"` // 未使用标签数
|
||||
AvgPhotosPerTag float64 `json:"avg_photos_per_tag"` // 平均每个标签的照片数
|
||||
Popular []Tag `json:"popular"` // 热门标签
|
||||
PhotoCounts map[string]int64 `json:"photo_counts"` // 各标签照片数量
|
||||
}
|
||||
|
||||
// TagListParams 标签列表查询参数
|
||||
type TagListParams struct {
|
||||
Page int `json:"page" form:"page"`
|
||||
Limit int `json:"limit" form:"limit"`
|
||||
Search string `json:"search" form:"search"`
|
||||
IsActive *bool `json:"is_active" form:"is_active"`
|
||||
SortBy string `json:"sort_by" form:"sort_by"`
|
||||
SortOrder string `json:"sort_order" form:"sort_order"`
|
||||
}
|
||||
|
||||
// CreateTagRequest 创建标签请求
|
||||
type CreateTagRequest struct {
|
||||
Name string `json:"name" binding:"required,max=50"`
|
||||
Slug string `json:"slug" binding:"required,max=50"`
|
||||
Description string `json:"description" binding:"max=500"`
|
||||
Color string `json:"color" binding:"max=7"`
|
||||
}
|
||||
|
||||
// UpdateTagRequest 更新标签请求
|
||||
type UpdateTagRequest struct {
|
||||
Name *string `json:"name" binding:"omitempty,max=50"`
|
||||
Slug *string `json:"slug" binding:"omitempty,max=50"`
|
||||
Description *string `json:"description" binding:"max=500"`
|
||||
Color *string `json:"color" binding:"omitempty,max=7"`
|
||||
IsActive *bool `json:"is_active"`
|
||||
}
|
||||
|
||||
// TagWithCount 带有照片数量的标签
|
||||
type TagWithCount struct {
|
||||
Tag
|
||||
PhotoCount int64 `json:"photo_count"`
|
||||
}
|
||||
|
||||
// TagCloudItem 标签云项目
|
||||
type TagCloudItem struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Color string `json:"color"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Tag) TableName() string {
|
||||
return "tags"
|
||||
}
|
||||
|
||||
// IsPopular 检查是否为热门标签(使用次数 >= 10)
|
||||
func (t *Tag) IsPopular() bool {
|
||||
return t.UseCount >= 10
|
||||
}
|
||||
|
||||
// IncrementUseCount 增加使用次数
|
||||
func (t *Tag) IncrementUseCount() {
|
||||
t.UseCount++
|
||||
}
|
||||
|
||||
// DecrementUseCount 减少使用次数
|
||||
func (t *Tag) DecrementUseCount() {
|
||||
if t.UseCount > 0 {
|
||||
t.UseCount--
|
||||
}
|
||||
}
|
||||
150
backend-old/internal/model/entity/user.go
Normal file
150
backend-old/internal/model/entity/user.go
Normal file
@ -0,0 +1,150 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UserRole 用户角色枚举
|
||||
type UserRole string
|
||||
|
||||
const (
|
||||
UserRoleUser UserRole = "user"
|
||||
UserRoleAdmin UserRole = "admin"
|
||||
UserRolePhotographer UserRole = "photographer"
|
||||
)
|
||||
|
||||
// User 用户实体
|
||||
type User struct {
|
||||
ID uint `json:"id" gorm:"primarykey"`
|
||||
Username string `json:"username" gorm:"uniqueIndex;not null;size:50"`
|
||||
Email string `json:"email" gorm:"uniqueIndex;not null;size:100"`
|
||||
Password string `json:"-" gorm:"not null;size:255"`
|
||||
Name string `json:"name" gorm:"size:100"`
|
||||
Avatar string `json:"avatar" gorm:"size:500"`
|
||||
Bio string `json:"bio" gorm:"type:text"`
|
||||
Website string `json:"website" gorm:"size:200"`
|
||||
Location string `json:"location" gorm:"size:100"`
|
||||
Role UserRole `json:"role" gorm:"default:user;size:20"`
|
||||
IsActive bool `json:"is_active" gorm:"default:true"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:true"`
|
||||
EmailVerified bool `json:"email_verified" gorm:"default:false"`
|
||||
LastLogin *time.Time `json:"last_login"`
|
||||
LoginCount int `json:"login_count" gorm:"default:0"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
|
||||
|
||||
// 关联
|
||||
Photos []Photo `json:"photos,omitempty" gorm:"foreignKey:UserID"`
|
||||
Albums []Album `json:"albums,omitempty" gorm:"foreignKey:UserID"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (User) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
// IsAdmin 检查是否为管理员
|
||||
func (u *User) IsAdmin() bool {
|
||||
return u.Role == UserRoleAdmin
|
||||
}
|
||||
|
||||
// IsPhotographer 检查是否为摄影师
|
||||
func (u *User) IsPhotographer() bool {
|
||||
return u.Role == UserRolePhotographer || u.Role == UserRoleAdmin
|
||||
}
|
||||
|
||||
// CanManagePhoto 检查是否可以管理指定照片
|
||||
func (u *User) CanManagePhoto(photo *Photo) bool {
|
||||
return u.ID == photo.UserID || u.IsAdmin()
|
||||
}
|
||||
|
||||
// CanManageAlbum 检查是否可以管理指定相册
|
||||
func (u *User) CanManageAlbum(album *Album) bool {
|
||||
return u.ID == album.UserID || u.IsAdmin()
|
||||
}
|
||||
|
||||
// UserStats 用户统计信息
|
||||
type UserStats struct {
|
||||
Total int64 `json:"total"` // 总用户数
|
||||
Active int64 `json:"active"` // 活跃用户数
|
||||
ThisMonth int64 `json:"this_month"` // 本月新增
|
||||
Today int64 `json:"today"` // 今日新增
|
||||
RoleStats map[string]int64 `json:"role_stats"` // 角色统计
|
||||
}
|
||||
|
||||
// CreateUserRequest 创建用户请求
|
||||
type CreateUserRequest struct {
|
||||
Username string `json:"username" binding:"required,min=3,max=50"`
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Password string `json:"password" binding:"required,min=6"`
|
||||
Name string `json:"name" binding:"max=100"`
|
||||
Role UserRole `json:"role" binding:"oneof=user admin photographer"`
|
||||
}
|
||||
|
||||
// UpdateUserRequest 更新用户请求
|
||||
type UpdateUserRequest struct {
|
||||
Username *string `json:"username" binding:"omitempty,min=3,max=50"`
|
||||
Email *string `json:"email" binding:"omitempty,email"`
|
||||
Name *string `json:"name" binding:"omitempty,max=100"`
|
||||
Avatar *string `json:"avatar" binding:"omitempty,max=500"`
|
||||
Bio *string `json:"bio" binding:"omitempty,max=1000"`
|
||||
Website *string `json:"website" binding:"omitempty,max=200"`
|
||||
Location *string `json:"location" binding:"omitempty,max=100"`
|
||||
Role *UserRole `json:"role" binding:"omitempty,oneof=user admin photographer"`
|
||||
IsActive *bool `json:"is_active"`
|
||||
}
|
||||
|
||||
// UpdateCurrentUserRequest 更新当前用户请求
|
||||
type UpdateCurrentUserRequest struct {
|
||||
Username *string `json:"username" binding:"omitempty,min=3,max=50"`
|
||||
Email *string `json:"email" binding:"omitempty,email"`
|
||||
Name *string `json:"name" binding:"omitempty,max=100"`
|
||||
Avatar *string `json:"avatar" binding:"omitempty,max=500"`
|
||||
Bio *string `json:"bio" binding:"omitempty,max=1000"`
|
||||
Website *string `json:"website" binding:"omitempty,max=200"`
|
||||
Location *string `json:"location" binding:"omitempty,max=100"`
|
||||
}
|
||||
|
||||
// ChangePasswordRequest 修改密码请求
|
||||
type ChangePasswordRequest struct {
|
||||
OldPassword string `json:"old_password" binding:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required,min=6"`
|
||||
}
|
||||
|
||||
// UserStatus 用户状态
|
||||
type UserStatus string
|
||||
|
||||
const (
|
||||
UserStatusActive UserStatus = "active"
|
||||
UserStatusInactive UserStatus = "inactive"
|
||||
UserStatusBanned UserStatus = "banned"
|
||||
UserStatusPending UserStatus = "pending"
|
||||
)
|
||||
|
||||
// UserListParams 用户列表查询参数
|
||||
type UserListParams struct {
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
Sort string `json:"sort"`
|
||||
Order string `json:"order"`
|
||||
Role *UserRole `json:"role"`
|
||||
Status *UserStatus `json:"status"`
|
||||
Search string `json:"search"`
|
||||
CreatedFrom *time.Time `json:"created_from"`
|
||||
CreatedTo *time.Time `json:"created_to"`
|
||||
LastLoginFrom *time.Time `json:"last_login_from"`
|
||||
LastLoginTo *time.Time `json:"last_login_to"`
|
||||
}
|
||||
|
||||
// UserGlobalStats 全局用户统计信息
|
||||
type UserGlobalStats struct {
|
||||
Total int64 `json:"total"`
|
||||
Active int64 `json:"active"`
|
||||
Admins int64 `json:"admins"`
|
||||
Editors int64 `json:"editors"`
|
||||
Users int64 `json:"users"`
|
||||
MonthlyRegistrations int64 `json:"monthly_registrations"`
|
||||
}
|
||||
Reference in New Issue
Block a user