diff --git a/backend/internal/repository/api_key_repo.go b/backend/internal/repository/api_key_repo.go index 859eefd5..667193a6 100644 --- a/backend/internal/repository/api_key_repo.go +++ b/backend/internal/repository/api_key_repo.go @@ -3,6 +3,7 @@ package repository import ( "context" "database/sql" + "fmt" "time" dbent "github.com/Wei-Shaw/sub2api/ent" @@ -257,9 +258,12 @@ func (r *apiKeyRepository) Update(ctx context.Context, key *service.APIKey) erro } func (r *apiKeyRepository) Delete(ctx context.Context, id int64) error { + // 存在唯一键约束 生成tombstone key 用来释放原key,长度远小于 128,满足 schema 限制 + tombstoneKey := fmt.Sprintf("__deleted__%d__%d", id, time.Now().UnixNano()) // 显式软删除:避免依赖 Hook 行为,确保 deleted_at 一定被设置。 affected, err := r.client.APIKey.Update(). Where(apikey.IDEQ(id), apikey.DeletedAtIsNil()). + SetKey(tombstoneKey). SetDeletedAt(time.Now()). Save(ctx) if err != nil { diff --git a/backend/internal/repository/api_key_repo_integration_test.go b/backend/internal/repository/api_key_repo_integration_test.go index a8989ff2..7d5c1826 100644 --- a/backend/internal/repository/api_key_repo_integration_test.go +++ b/backend/internal/repository/api_key_repo_integration_test.go @@ -151,6 +151,31 @@ func (s *APIKeyRepoSuite) TestDelete() { s.Require().Error(err, "expected error after delete") } +func (s *APIKeyRepoSuite) TestCreate_AfterSoftDelete_AllowsSameKey() { + user := s.mustCreateUser("recreate-after-soft-delete@test.com") + const reusedKey = "sk-reuse-after-soft-delete" + + first := &service.APIKey{ + UserID: user.ID, + Key: reusedKey, + Name: "First Key", + Status: service.StatusActive, + } + s.Require().NoError(s.repo.Create(s.ctx, first), "create first key") + + s.Require().NoError(s.repo.Delete(s.ctx, first.ID), "soft delete first key") + + second := &service.APIKey{ + UserID: user.ID, + Key: reusedKey, + Name: "Second Key", + Status: service.StatusActive, + } + s.Require().NoError(s.repo.Create(s.ctx, second), "create second key with same key") + s.Require().NotZero(second.ID) + s.Require().NotEqual(first.ID, second.ID, "recreated key should be a new row") +} + // --- ListByUserID / CountByUserID --- func (s *APIKeyRepoSuite) TestListByUserID() {