package postgres import ( "context" "errors" "fmt" "time" "photography-backend/internal/model/entity" "photography-backend/internal/repository/interfaces" "go.uber.org/zap" "gorm.io/gorm" ) // photoRepositoryImpl 照片仓储实现 type photoRepositoryImpl struct { db *gorm.DB logger *zap.Logger } // NewPhotoRepository 创建照片仓储实现 func NewPhotoRepository(db *gorm.DB, logger *zap.Logger) interfaces.PhotoRepository { return &photoRepositoryImpl{ db: db, logger: logger, } } // Create 创建照片 func (r *photoRepositoryImpl) Create(ctx context.Context, photo *entity.Photo) error { return r.db.WithContext(ctx).Create(photo).Error } // GetByID 根据ID获取照片 func (r *photoRepositoryImpl) GetByID(ctx context.Context, id uint) (*entity.Photo, error) { var photo entity.Photo err := r.db.WithContext(ctx). Preload("User"). Preload("Categories"). Preload("Tags"). First(&photo, id).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("photo not found") } return nil, err } return &photo, nil } // GetByFilename 根据文件名获取照片 func (r *photoRepositoryImpl) GetByFilename(ctx context.Context, filename string) (*entity.Photo, error) { var photo entity.Photo err := r.db.WithContext(ctx).Where("filename = ?", filename).First(&photo).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("photo not found") } return nil, err } return &photo, nil } // Update 更新照片 func (r *photoRepositoryImpl) Update(ctx context.Context, photo *entity.Photo) error { return r.db.WithContext(ctx).Save(photo).Error } // Delete 删除照片 func (r *photoRepositoryImpl) Delete(ctx context.Context, id uint) error { return r.db.WithContext(ctx).Delete(&entity.Photo{}, id).Error } // List 获取照片列表 func (r *photoRepositoryImpl) List(ctx context.Context, params *entity.PhotoListParams) ([]*entity.Photo, int64, error) { var photos []*entity.Photo var total int64 query := r.db.WithContext(ctx).Model(&entity.Photo{}) // 应用过滤条件 if params.UserID != nil { query = query.Where("user_id = ?", *params.UserID) } if params.Status != nil { query = query.Where("status = ?", *params.Status) } if params.CategoryID != nil { query = query.Joins("JOIN photo_categories ON photos.id = photo_categories.photo_id"). Where("photo_categories.category_id = ?", *params.CategoryID) } if params.TagID != nil { query = query.Joins("JOIN photo_tags ON photos.id = photo_tags.photo_id"). Where("photo_tags.tag_id = ?", *params.TagID) } if params.DateFrom != nil { query = query.Where("taken_at >= ?", *params.DateFrom) } if params.DateTo != nil { query = query.Where("taken_at <= ?", *params.DateTo) } if params.Search != "" { query = query.Where("title ILIKE ? OR description ILIKE ?", "%"+params.Search+"%", "%"+params.Search+"%") } // 获取总数 if err := query.Count(&total).Error; err != nil { return nil, 0, err } // 应用排序 orderBy := "created_at DESC" if params.Sort != "" { order := "ASC" if params.Order == "desc" { order = "DESC" } orderBy = fmt.Sprintf("%s %s", params.Sort, order) } query = query.Order(orderBy) // 应用分页 if params.Page > 0 && params.Limit > 0 { offset := (params.Page - 1) * params.Limit query = query.Offset(offset).Limit(params.Limit) } // 预加载关联数据 query = query.Preload("User").Preload("Categories").Preload("Tags") // 查询数据 if err := query.Find(&photos).Error; err != nil { return nil, 0, err } return photos, total, nil } // ListByUserID 根据用户ID获取照片列表 func (r *photoRepositoryImpl) ListByUserID(ctx context.Context, userID uint, params *entity.PhotoListParams) ([]*entity.Photo, int64, error) { if params == nil { params = &entity.PhotoListParams{} } params.UserID = &userID return r.List(ctx, params) } // ListByStatus 根据状态获取照片列表 func (r *photoRepositoryImpl) ListByStatus(ctx context.Context, status entity.PhotoStatus, params *entity.PhotoListParams) ([]*entity.Photo, int64, error) { if params == nil { params = &entity.PhotoListParams{} } params.Status = &status return r.List(ctx, params) } // ListByCategory 根据分类获取照片列表 func (r *photoRepositoryImpl) ListByCategory(ctx context.Context, categoryID uint, params *entity.PhotoListParams) ([]*entity.Photo, int64, error) { if params == nil { params = &entity.PhotoListParams{} } params.CategoryID = &categoryID return r.List(ctx, params) } // Search 搜索照片 func (r *photoRepositoryImpl) Search(ctx context.Context, query string, params *entity.PhotoListParams) ([]*entity.Photo, int64, error) { if params == nil { params = &entity.PhotoListParams{} } params.Search = query return r.List(ctx, params) } // Count 统计照片总数 func (r *photoRepositoryImpl) Count(ctx context.Context) (int64, error) { var count int64 err := r.db.WithContext(ctx).Model(&entity.Photo{}).Count(&count).Error return count, err } // CountByUser 统计用户照片数 func (r *photoRepositoryImpl) CountByUser(ctx context.Context, userID uint) (int64, error) { var count int64 err := r.db.WithContext(ctx).Model(&entity.Photo{}). Where("user_id = ?", userID).Count(&count).Error return count, err } // CountByStatus 统计指定状态照片数 func (r *photoRepositoryImpl) CountByStatus(ctx context.Context, status entity.PhotoStatus) (int64, error) { var count int64 err := r.db.WithContext(ctx).Model(&entity.Photo{}). Where("status = ?", status).Count(&count).Error return count, err } // CountByCategory 统计分类照片数 func (r *photoRepositoryImpl) CountByCategory(ctx context.Context, categoryID uint) (int64, error) { var count int64 err := r.db.WithContext(ctx). Table("photo_categories"). Where("category_id = ?", categoryID). Count(&count).Error return count, err } // CountByStatus 统计指定状态照片数 func (r *photoRepositoryImpl) CountByStatus(ctx context.Context, status string) (int64, error) { var count int64 err := r.db.WithContext(ctx).Model(&entity.Photo{}). Where("status = ?", status).Count(&count).Error return count, err } // BatchUpdate 批量更新 func (r *photoRepositoryImpl) BatchUpdate(ctx context.Context, ids []uint, updates map[string]interface{}) error { if len(ids) == 0 || len(updates) == 0 { return nil } return r.db.WithContext(ctx).Model(&entity.Photo{}). Where("id IN ?", ids). Updates(updates).Error } // BatchUpdateCategories 批量更新分类 func (r *photoRepositoryImpl) BatchUpdateCategories(ctx context.Context, photoIDs []uint, categoryIDs []uint) error { if len(photoIDs) == 0 { return nil } return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { // 删除现有关联 if err := tx.Exec("DELETE FROM photo_categories WHERE photo_id IN ?", photoIDs).Error; err != nil { return err } // 添加新关联 for _, photoID := range photoIDs { for _, categoryID := range categoryIDs { if err := tx.Exec("INSERT INTO photo_categories (photo_id, category_id) VALUES (?, ?)", photoID, categoryID).Error; err != nil { return err } } } return nil }) } // BatchUpdateTags 批量更新标签 func (r *photoRepositoryImpl) BatchUpdateTags(ctx context.Context, photoIDs []uint, tagIDs []uint) error { if len(photoIDs) == 0 { return nil } return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { // 删除现有关联 if err := tx.Exec("DELETE FROM photo_tags WHERE photo_id IN ?", photoIDs).Error; err != nil { return err } // 添加新关联 for _, photoID := range photoIDs { for _, tagID := range tagIDs { if err := tx.Exec("INSERT INTO photo_tags (photo_id, tag_id) VALUES (?, ?)", photoID, tagID).Error; err != nil { return err } } } return nil }) } // BatchDelete 批量删除 func (r *photoRepositoryImpl) BatchDelete(ctx context.Context, ids []uint) error { if len(ids) == 0 { return nil } return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { // 删除关联关系 if err := tx.Exec("DELETE FROM photo_categories WHERE photo_id IN ?", ids).Error; err != nil { return err } if err := tx.Exec("DELETE FROM photo_tags WHERE photo_id IN ?", ids).Error; err != nil { return err } // 删除照片记录 return tx.Delete(&entity.Photo{}, ids).Error }) } // GetStats 获取照片统计信息 func (r *photoRepositoryImpl) GetStats(ctx context.Context) (*entity.PhotoStats, error) { var stats entity.PhotoStats // 总照片数 if total, err := r.Count(ctx); err != nil { return nil, err } else { stats.Total = total } // 按状态统计 for _, status := range []entity.PhotoStatus{ entity.PhotoStatusActive, entity.PhotoStatusDraft, entity.PhotoStatusArchived, } { if count, err := r.CountByStatus(ctx, status); err != nil { return nil, err } else { switch status { case entity.PhotoStatusActive: stats.Published = count case entity.PhotoStatusDraft: stats.Draft = count case entity.PhotoStatusArchived: stats.Archived = count } } } // 本月新增照片数 now := time.Now() startOfMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()) endOfMonth := startOfMonth.AddDate(0, 1, 0).Add(-time.Nanosecond) var monthlyCount int64 if err := r.db.WithContext(ctx).Model(&entity.Photo{}). Where("created_at >= ? AND created_at <= ?", startOfMonth, endOfMonth). Count(&monthlyCount).Error; err != nil { return nil, err } stats.ThisMonth = monthlyCount // 用户照片分布(Top 10) var userPhotoStats []struct { UserID uint `json:"user_id"` Username string `json:"username"` PhotoCount int64 `json:"photo_count"` } if err := r.db.WithContext(ctx). Table("photos"). Select("photos.user_id, users.username, COUNT(photos.id) as photo_count"). Joins("LEFT JOIN users ON photos.user_id = users.id"). Group("photos.user_id, users.username"). Order("photo_count DESC"). Limit(10). Find(&userPhotoStats).Error; err != nil { return nil, err } stats.UserPhotoCounts = make(map[string]int64) for _, stat := range userPhotoStats { stats.UserPhotoCounts[stat.Username] = stat.PhotoCount } return &stats, nil }