From e2133cd76f69aca74f807fab208044e292e7898c Mon Sep 17 00:00:00 2001 From: truewhile <62226914+truewhile@users.noreply.github.com> Date: Sat, 17 Oct 2026 23:24:48 +0800 Subject: [PATCH] =?UTF-8?q?app=E5=85=BC=E5=AE=B9=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/favorites_sync.go | 18 +++++++++++++++--- internal/service/favorites_sync_test.go | 13 +++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/internal/service/favorites_sync.go b/internal/service/favorites_sync.go index cc72d5d..4b6e446 100644 --- a/internal/service/favorites_sync.go +++ b/internal/service/favorites_sync.go @@ -35,16 +35,28 @@ func IsUserFavorite(ctx context.Context, repo *repository.Container, userID, med func setLocalFavorite(ctx context.Context, repo *repository.Container, userID, mediaID string, favorite bool) error { if favorite { var existing model.Favorite - err := repo.DB.WithContext(ctx). + // 收藏行的删除是软删除;重新收藏时必须连已软删除的行一起查出来, + // 否则会再次插入同一 (user_id, media_id),撞上唯一索引后稳定返回 + // 500,用户表现为“取消收藏后无法再收藏”。 + err := repo.DB.WithContext(ctx).Unscoped(). Where("user_id = ? AND media_id = ?", userID, mediaID). First(&existing).Error - if errors.Is(err, gorm.ErrRecordNotFound) { + switch { + case errors.Is(err, gorm.ErrRecordNotFound): return repo.DB.WithContext(ctx).Create(&model.Favorite{ UserID: userID, MediaID: mediaID, }).Error + case err != nil: + return err + case existing.DeletedAt.Valid: + return repo.DB.WithContext(ctx).Unscoped(). + Model(&model.Favorite{}). + Where("id = ?", existing.ID). + Update("deleted_at", nil).Error + default: + return nil } - return err } return repo.DB.WithContext(ctx). Where("user_id = ? AND media_id = ?", userID, mediaID). diff --git a/internal/service/favorites_sync_test.go b/internal/service/favorites_sync_test.go index 0c7e85c..64366c1 100644 --- a/internal/service/favorites_sync_test.go +++ b/internal/service/favorites_sync_test.go @@ -44,6 +44,19 @@ func TestSyncUserFavoriteWritesLocalForRemoteID(t *testing.T) { if favorite { t.Fatal("expected remote favourite to be removed locally") } + + // 软删除后重新收藏必须恢复原行:唯一索引 (user_id, media_id) 不允许 + // 再插入一条,旧的实现会在这里返回约束错误。 + if err := SyncUserFavorite(context.Background(), repos, nil, userID, remoteMediaID, true); err != nil { + t.Fatalf("SyncUserFavorite re-favorite: %v", err) + } + favorite, err = IsUserFavorite(context.Background(), repos, userID, remoteMediaID) + if err != nil { + t.Fatalf("IsUserFavorite after re-favorite: %v", err) + } + if !favorite { + t.Fatal("expected re-favourite to restore the soft-deleted row") + } } func TestFavoriteItemsIncludesRemoteFavourites(t *testing.T) {