From f5884d16085ae684d854a1a9bc4a6c1c908a583e Mon Sep 17 00:00:00 2001 From: liuxiongfeng Date: Tue, 3 Feb 2026 12:06:05 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20jsonb=5Fset=20=E5=B5=8C=E5=A5=97?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=97=A0=E6=B3=95=E5=88=9B=E5=BB=BA=E5=A4=9A?= =?UTF-8?q?=E5=B1=82=20key=20=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PostgreSQL jsonb_set 在 create_if_missing=true 时无法一次性创建多层嵌套路径。 例如设置 {antigravity_quota_scopes,gemini_image} 时,如果 antigravity_quota_scopes 不存在, jsonb_set 不会自动创建外层 key,导致更新静默失败(affected=1 但数据未变)。 修复方案:嵌套两次 jsonb_set,先确保外层 key 存在,再设置内层值。 同时在设置限流时更新 last_used_at,使刚触发 429 的账号调度优先级降低。 Cherry-picked from slovx2/sub2api: 4b57e80e --- backend/internal/repository/account_repo.go | 29 ++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/backend/internal/repository/account_repo.go b/backend/internal/repository/account_repo.go index c11c079b..e4e837e2 100644 --- a/backend/internal/repository/account_repo.go +++ b/backend/internal/repository/account_repo.go @@ -809,12 +809,21 @@ func (r *accountRepository) SetAntigravityQuotaScopeLimit(ctx context.Context, i return err } - path := "{antigravity_quota_scopes," + string(scope) + "}" + scopeKey := string(scope) client := clientFromContext(ctx, r.client) result, err := client.ExecContext( ctx, - "UPDATE accounts SET extra = jsonb_set(COALESCE(extra, '{}'::jsonb), $1::text[], $2::jsonb, true), updated_at = NOW() WHERE id = $3 AND deleted_at IS NULL", - path, + `UPDATE accounts SET + extra = jsonb_set( + jsonb_set(COALESCE(extra, '{}'::jsonb), '{antigravity_quota_scopes}'::text[], COALESCE(extra->'antigravity_quota_scopes', '{}'::jsonb), true), + ARRAY['antigravity_quota_scopes', $1]::text[], + $2::jsonb, + true + ), + updated_at = NOW(), + last_used_at = NOW() + WHERE id = $3 AND deleted_at IS NULL`, + scopeKey, raw, id, ) @@ -829,6 +838,7 @@ func (r *accountRepository) SetAntigravityQuotaScopeLimit(ctx context.Context, i if affected == 0 { return service.ErrAccountNotFound } + if err := enqueueSchedulerOutbox(ctx, r.sql, service.SchedulerOutboxEventAccountChanged, &id, nil, nil); err != nil { log.Printf("[SchedulerOutbox] enqueue quota scope failed: account=%d err=%v", id, err) } @@ -849,12 +859,19 @@ func (r *accountRepository) SetModelRateLimit(ctx context.Context, id int64, sco return err } - path := "{model_rate_limits," + scope + "}" client := clientFromContext(ctx, r.client) result, err := client.ExecContext( ctx, - "UPDATE accounts SET extra = jsonb_set(COALESCE(extra, '{}'::jsonb), $1::text[], $2::jsonb, true), updated_at = NOW() WHERE id = $3 AND deleted_at IS NULL", - path, + `UPDATE accounts SET + extra = jsonb_set( + jsonb_set(COALESCE(extra, '{}'::jsonb), '{model_rate_limits}'::text[], COALESCE(extra->'model_rate_limits', '{}'::jsonb), true), + ARRAY['model_rate_limits', $1]::text[], + $2::jsonb, + true + ), + updated_at = NOW() + WHERE id = $3 AND deleted_at IS NULL`, + scope, raw, id, )