This commit is contained in:
xujiang
2025-07-10 18:09:11 +08:00
parent 5cbdc5af73
commit 604b9e59ba
95 changed files with 23709 additions and 19 deletions

View File

@ -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
}