From 116612ffec820ed95e50d36afc732227244e3925 Mon Sep 17 00:00:00 2001 From: YONGYE Date: Tue, 13 Jan 2026 18:34:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E7=9A=AE=E8=82=A4?= =?UTF-8?q?=E6=94=B6=E8=97=8F=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/repository/interfaces.go | 7 +- internal/repository/repository_sqlite_test.go | 28 ++++---- internal/repository/texture_repository.go | 70 +++++++++++-------- internal/service/mocks_test.go | 34 +++------ internal/service/texture_service.go | 23 +----- internal/service/texture_service_test.go | 5 +- pkg/database/cache.go | 5 ++ 7 files changed, 74 insertions(+), 98 deletions(-) diff --git a/internal/repository/interfaces.go b/internal/repository/interfaces.go index d2f586c..b565655 100644 --- a/internal/repository/interfaces.go +++ b/internal/repository/interfaces.go @@ -56,17 +56,12 @@ type TextureRepository interface { Delete(ctx context.Context, id int64) error BatchDelete(ctx context.Context, ids []int64) (int64, error) // 批量删除 IncrementDownloadCount(ctx context.Context, id int64) error - IncrementFavoriteCount(ctx context.Context, id int64) error - DecrementFavoriteCount(ctx context.Context, id int64) error CreateDownloadLog(ctx context.Context, log *model.TextureDownloadLog) error - IsFavorited(ctx context.Context, userID, textureID int64) (bool, error) - AddFavorite(ctx context.Context, userID, textureID int64) error - RemoveFavorite(ctx context.Context, userID, textureID int64) error + ToggleFavorite(ctx context.Context, userID, textureID int64) (bool, error) GetUserFavorites(ctx context.Context, userID int64, page, pageSize int) ([]*model.Texture, int64, error) CountByUploaderID(ctx context.Context, uploaderID int64) (int64, error) } - // YggdrasilRepository Yggdrasil仓储接口 type YggdrasilRepository interface { GetPasswordByID(ctx context.Context, id int64) (string, error) diff --git a/internal/repository/repository_sqlite_test.go b/internal/repository/repository_sqlite_test.go index 4a1fff8..eb75046 100644 --- a/internal/repository/repository_sqlite_test.go +++ b/internal/repository/repository_sqlite_test.go @@ -98,7 +98,6 @@ func TestProfileRepository_Basic(t *testing.T) { t.Fatalf("CountByUserID mismatch: %d err=%v", count, err) } - if err := profileRepo.UpdateLastUsedAt(ctx, "p-uuid"); err != nil { t.Fatalf("UpdateLastUsedAt err: %v", err) } @@ -150,22 +149,20 @@ func TestTextureRepository_Basic(t *testing.T) { t.Fatalf("FindByHashAndUploaderID mismatch") } - _ = textureRepo.IncrementFavoriteCount(ctx, tex.ID) - _ = textureRepo.DecrementFavoriteCount(ctx, tex.ID) + _, _ = textureRepo.ToggleFavorite(ctx, u.ID, tex.ID) + favList, _, _ := textureRepo.GetUserFavorites(ctx, u.ID, 1, 10) + if len(favList) == 0 { + t.Fatalf("GetUserFavorites expected at least 1 favorite") + } + _, _ = textureRepo.ToggleFavorite(ctx, u.ID, tex.ID) + favList, _, _ = textureRepo.GetUserFavorites(ctx, u.ID, 1, 10) + if len(favList) != 0 { + t.Fatalf("GetUserFavorites expected 0 favorites after toggle off") + } + _ = textureRepo.IncrementDownloadCount(ctx, tex.ID) _ = textureRepo.CreateDownloadLog(ctx, &model.TextureDownloadLog{TextureID: tex.ID, UserID: &u.ID, IPAddress: "127.0.0.1"}) - // 收藏 - _ = textureRepo.AddFavorite(ctx, u.ID, tex.ID) - if fav, err := textureRepo.IsFavorited(ctx, u.ID, tex.ID); err == nil { - if !fav { - t.Fatalf("IsFavorited expected true") - } - } else { - t.Skipf("IsFavorited not supported by sqlite: %v", err) - } - _ = textureRepo.RemoveFavorite(ctx, u.ID, tex.ID) - // 批量更新与删除 if affected, err := textureRepo.BatchUpdate(ctx, []int64{tex.ID}, map[string]interface{}{"name": "tex-new"}); err != nil || affected != 1 { t.Fatalf("BatchUpdate mismatch, affected=%d err=%v", affected, err) @@ -187,7 +184,7 @@ func TestTextureRepository_Basic(t *testing.T) { if list, total, err := textureRepo.Search(ctx, "search", model.TextureTypeCape, true, 1, 10); err != nil || total == 0 || len(list) == 0 { t.Fatalf("Search mismatch, total=%d len=%d err=%v", total, len(list), err) } - _ = textureRepo.AddFavorite(ctx, u.ID, tex.ID+1) + _, _ = textureRepo.ToggleFavorite(ctx, u.ID, tex.ID+1) if favList, total, err := textureRepo.GetUserFavorites(ctx, u.ID, 1, 10); err != nil || total == 0 || len(favList) == 0 { t.Fatalf("GetUserFavorites mismatch, total=%d len=%d err=%v", total, len(favList), err) } @@ -206,7 +203,6 @@ func TestTextureRepository_Basic(t *testing.T) { _ = textureRepo.Delete(ctx, tex.ID) } - func TestClientRepository_Basic(t *testing.T) { db := testutil.NewTestDB(t) repo := NewClientRepository(db) diff --git a/internal/repository/texture_repository.go b/internal/repository/texture_repository.go index 6382d31..f442490 100644 --- a/internal/repository/texture_repository.go +++ b/internal/repository/texture_repository.go @@ -138,42 +138,52 @@ func (r *textureRepository) IncrementDownloadCount(ctx context.Context, id int64 UpdateColumn("download_count", gorm.Expr("download_count + ?", 1)).Error } -func (r *textureRepository) IncrementFavoriteCount(ctx context.Context, id int64) error { - return r.db.WithContext(ctx).Model(&model.Texture{}).Where("id = ?", id). - UpdateColumn("favorite_count", gorm.Expr("favorite_count + ?", 1)).Error -} - -func (r *textureRepository) DecrementFavoriteCount(ctx context.Context, id int64) error { - return r.db.WithContext(ctx).Model(&model.Texture{}).Where("id = ?", id). - UpdateColumn("favorite_count", gorm.Expr("favorite_count - ?", 1)).Error -} - func (r *textureRepository) CreateDownloadLog(ctx context.Context, log *model.TextureDownloadLog) error { return r.db.WithContext(ctx).Create(log).Error } -func (r *textureRepository) IsFavorited(ctx context.Context, userID, textureID int64) (bool, error) { - var count int64 - // 使用 Select("1") 优化,只查询是否存在,不需要查询所有字段 - err := r.db.WithContext(ctx).Model(&model.UserTextureFavorite{}). - Select("1"). - Where("user_id = ? AND texture_id = ?", userID, textureID). - Limit(1). - Count(&count).Error - return count > 0, err -} +func (r *textureRepository) ToggleFavorite(ctx context.Context, userID, textureID int64) (bool, error) { + var isAdded bool + err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + var count int64 + err := tx.Model(&model.UserTextureFavorite{}). + Where("user_id = ? AND texture_id = ?", userID, textureID). + Count(&count).Error + if err != nil { + return err + } -func (r *textureRepository) AddFavorite(ctx context.Context, userID, textureID int64) error { - favorite := &model.UserTextureFavorite{ - UserID: userID, - TextureID: textureID, - } - return r.db.WithContext(ctx).Create(favorite).Error -} + if count > 0 { + result := tx.Where("user_id = ? AND texture_id = ?", userID, textureID). + Delete(&model.UserTextureFavorite{}) + if result.Error != nil { + return result.Error + } + if result.RowsAffected > 0 { + if err := tx.Model(&model.Texture{}).Where("id = ?", textureID). + UpdateColumn("favorite_count", gorm.Expr("GREATEST(favorite_count - 1, 0)")).Error; err != nil { + return err + } + } + isAdded = false + return nil + } -func (r *textureRepository) RemoveFavorite(ctx context.Context, userID, textureID int64) error { - return r.db.WithContext(ctx).Where("user_id = ? AND texture_id = ?", userID, textureID). - Delete(&model.UserTextureFavorite{}).Error + favorite := &model.UserTextureFavorite{ + UserID: userID, + TextureID: textureID, + } + if err := tx.Create(favorite).Error; err != nil { + return err + } + if err := tx.Model(&model.Texture{}).Where("id = ?", textureID). + UpdateColumn("favorite_count", gorm.Expr("favorite_count + 1")).Error; err != nil { + return err + } + isAdded = true + return nil + }) + return isAdded, err } func (r *textureRepository) GetUserFavorites(ctx context.Context, userID int64, page, pageSize int) ([]*model.Texture, int64, error) { diff --git a/internal/service/mocks_test.go b/internal/service/mocks_test.go index 434e3de..255dd83 100644 --- a/internal/service/mocks_test.go +++ b/internal/service/mocks_test.go @@ -391,37 +391,24 @@ func (m *MockTextureRepository) IncrementFavoriteCount(ctx context.Context, id i return nil } -func (m *MockTextureRepository) DecrementFavoriteCount(ctx context.Context, id int64) error { - if texture, ok := m.textures[id]; ok && texture.FavoriteCount > 0 { - texture.FavoriteCount-- - } - return nil -} - func (m *MockTextureRepository) CreateDownloadLog(ctx context.Context, log *model.TextureDownloadLog) error { return nil } -func (m *MockTextureRepository) IsFavorited(ctx context.Context, userID, textureID int64) (bool, error) { - if userFavs, ok := m.favorites[userID]; ok { - return userFavs[textureID], nil - } - return false, nil -} - -func (m *MockTextureRepository) AddFavorite(ctx context.Context, userID, textureID int64) error { +func (m *MockTextureRepository) ToggleFavorite(ctx context.Context, userID, textureID int64) (bool, error) { if m.favorites[userID] == nil { m.favorites[userID] = make(map[int64]bool) } - m.favorites[userID][textureID] = true - return nil -} - -func (m *MockTextureRepository) RemoveFavorite(ctx context.Context, userID, textureID int64) error { - if userFavs, ok := m.favorites[userID]; ok { - delete(userFavs, textureID) + isFavorited := m.favorites[userID][textureID] + m.favorites[userID][textureID] = !isFavorited + if texture, ok := m.textures[textureID]; ok { + if !isFavorited { + texture.FavoriteCount++ + } else if texture.FavoriteCount > 0 { + texture.FavoriteCount-- + } } - return nil + return !isFavorited, nil } func (m *MockTextureRepository) GetUserFavorites(ctx context.Context, userID int64, page, pageSize int) ([]*model.Texture, int64, error) { @@ -474,7 +461,6 @@ func (m *MockTextureRepository) BatchDelete(ctx context.Context, ids []int64) (i return deleted, nil } - // ============================================================================ // Service Mocks // ============================================================================ diff --git a/internal/service/texture_service.go b/internal/service/texture_service.go index f73102d..d9a477e 100644 --- a/internal/service/texture_service.go +++ b/internal/service/texture_service.go @@ -219,7 +219,6 @@ func (s *textureService) Delete(ctx context.Context, textureID, uploaderID int64 } func (s *textureService) ToggleFavorite(ctx context.Context, userID, textureID int64) (bool, error) { - // 确保材质存在 texture, err := s.textureRepo.FindByID(ctx, textureID) if err != nil { return false, err @@ -228,30 +227,14 @@ func (s *textureService) ToggleFavorite(ctx context.Context, userID, textureID i return false, ErrTextureNotFound } - isFavorited, err := s.textureRepo.IsFavorited(ctx, userID, textureID) + isAdded, err := s.textureRepo.ToggleFavorite(ctx, userID, textureID) if err != nil { return false, err } - if isFavorited { - // 已收藏 -> 取消收藏 - if err := s.textureRepo.RemoveFavorite(ctx, userID, textureID); err != nil { - return false, err - } - if err := s.textureRepo.DecrementFavoriteCount(ctx, textureID); err != nil { - return false, err - } - return false, nil - } + s.cacheInv.BatchInvalidate(ctx, s.cacheKeys.UserFavoritesPattern(userID)) - // 未收藏 -> 添加收藏 - if err := s.textureRepo.AddFavorite(ctx, userID, textureID); err != nil { - return false, err - } - if err := s.textureRepo.IncrementFavoriteCount(ctx, textureID); err != nil { - return false, err - } - return true, nil + return isAdded, nil } func (s *textureService) GetUserFavorites(ctx context.Context, userID int64, page, pageSize int) ([]*model.Texture, int64, error) { diff --git a/internal/service/texture_service_test.go b/internal/service/texture_service_test.go index b231f12..ae91f50 100644 --- a/internal/service/texture_service_test.go +++ b/internal/service/texture_service_test.go @@ -3,6 +3,7 @@ package service import ( "carrotskin/internal/model" "context" + "strings" "testing" "go.uber.org/zap" @@ -564,7 +565,7 @@ func TestTextureServiceImpl_Create(t *testing.T) { ctx := context.Background() // UploadTexture需要文件数据,这里创建一个简单的测试数据 - fileData := []byte("fake png data for testing") + fileData := []byte(strings.Repeat("x", 512)) texture, err := textureService.UploadTexture( ctx, tt.uploaderID, @@ -760,7 +761,7 @@ func TestTextureServiceImpl_FavoritesAndLimit(t *testing.T) { UploaderID: 1, Name: "T", }) - _ = textureRepo.AddFavorite(context.Background(), 1, i) + _, _ = textureRepo.ToggleFavorite(context.Background(), 1, i) } cacheManager := NewMockCacheManager() diff --git a/pkg/database/cache.go b/pkg/database/cache.go index ffe6fe7..6e96958 100644 --- a/pkg/database/cache.go +++ b/pkg/database/cache.go @@ -369,6 +369,11 @@ func (b *CacheKeyBuilder) ProfilePattern(userID int64) string { return fmt.Sprintf("%sprofile:*:%d*", b.prefix, userID) } +// UserFavoritesPattern 用户收藏相关的所有缓存键模式 +func (b *CacheKeyBuilder) UserFavoritesPattern(userID int64) string { + return fmt.Sprintf("%sfavorites:*:%d*", b.prefix, userID) +} + // Exists 检查缓存键是否存在 func (cm *CacheManager) Exists(ctx context.Context, key string) (bool, error) { if !cm.config.Enabled || cm.redis == nil {