fix(仓储): 修复 ApiKey 更新并发语义

ApiKey 更新时显式设置 updated_at 并回填,避免二次查询竞态
补充软删除范围注释以统一审计语义
This commit is contained in:
yangjianbo
2025-12-29 19:59:36 +08:00
parent 74db0c15ae
commit 042d82359c
2 changed files with 22 additions and 17 deletions

View File

@@ -91,32 +91,35 @@ func (r *apiKeyRepository) GetByKey(ctx context.Context, key string) (*service.A
} }
func (r *apiKeyRepository) Update(ctx context.Context, key *service.ApiKey) error { func (r *apiKeyRepository) Update(ctx context.Context, key *service.ApiKey) error {
exists, err := r.activeQuery().Where(apikey.IDEQ(key.ID)).Exist(ctx) // 使用原子操作:将软删除检查与更新合并到同一语句,避免竞态条件。
if err != nil { // 之前的实现先检查 Exist 再 UpdateOneID若在两步之间发生软删除
return err // 则会更新已删除的记录。
} // 这里选择 Update().Where(),确保只有未软删除记录能被更新。
if !exists { // 同时显式设置 updated_at避免二次查询带来的并发可见性问题。
return service.ErrApiKeyNotFound now := time.Now()
} builder := r.client.ApiKey.Update().
Where(apikey.IDEQ(key.ID), apikey.DeletedAtIsNil()).
builder := r.client.ApiKey.UpdateOneID(key.ID).
SetName(key.Name). SetName(key.Name).
SetStatus(key.Status) SetStatus(key.Status).
SetUpdatedAt(now)
if key.GroupID != nil { if key.GroupID != nil {
builder.SetGroupID(*key.GroupID) builder.SetGroupID(*key.GroupID)
} else { } else {
builder.ClearGroupID() builder.ClearGroupID()
} }
updated, err := builder.Save(ctx) affected, err := builder.Save(ctx)
if err == nil { if err != nil {
key.UpdatedAt = updated.UpdatedAt return err
return nil
} }
if dbent.IsNotFound(err) { if affected == 0 {
// 更新影响行数为 0说明记录不存在或已被软删除。
return service.ErrApiKeyNotFound return service.ErrApiKeyNotFound
} }
return err
// 使用同一时间戳回填,避免并发删除导致二次查询失败。
key.UpdatedAt = now
return nil
} }
func (r *apiKeyRepository) Delete(ctx context.Context, id int64) error { func (r *apiKeyRepository) Delete(ctx context.Context, id int64) error {

View File

@@ -289,8 +289,10 @@ func (r *groupRepository) DeleteCascade(ctx context.Context, id int64) ([]int64,
} }
// 2. Clear group_id for api keys bound to this group. // 2. Clear group_id for api keys bound to this group.
// 仅更新未软删除的记录,避免修改已删除数据,保证审计与历史回溯一致性。
// 与 ApiKeyRepository 的软删除语义保持一致,减少跨模块行为差异。
if _, err := txClient.ApiKey.Update(). if _, err := txClient.ApiKey.Update().
Where(apikey.GroupIDEQ(id)). Where(apikey.GroupIDEQ(id), apikey.DeletedAtIsNil()).
ClearGroupID(). ClearGroupID().
Save(ctx); err != nil { Save(ctx); err != nil {
return nil, err return nil, err