fix
This commit is contained in:
@ -0,0 +1,375 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user