主要变更: - 采用 go-zero 框架替代 Gin,提升开发效率 - 重构项目结构,API 文件模块化组织 - 将 model 移至 api/internal/model 目录 - 移除 common 包,改为标准 pkg 目录结构 - 实现统一的仓储模式,支持配置驱动数据库切换 - 简化测试策略,专注 API 集成测试 - 更新 CLAUDE.md 文档,提供详细的开发指导 技术栈更新: - 框架: Gin → go-zero v1.6.0+ - 代码生成: 引入 goctl 工具 - 架构模式: 四层架构 → go-zero 三层架构 (Handler→Logic→Model) - 项目布局: 遵循 Go 社区标准和 go-zero 最佳实践
196 lines
7.9 KiB
Go
196 lines
7.9 KiB
Go
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
|
|
} |