@@ -729,7 +729,30 @@ func (h *Handler) UpdateAccountScheduler(c *gin.Context) {
729729 return
730730 }
731731 if h .store != nil {
732- h .store .ApplyAccountSchedulerOverrides (id , nullableInt64Pointer (scoreBiasOverride ), nullableInt64Pointer (baseConcurrencyOverride ))
732+ if scoreBiasOverride .Set || baseConcurrencyOverride .Set {
733+ current := h .store .FindByID (id )
734+ var score * int64
735+ var concurrency * int64
736+ if current != nil {
737+ current .Mu ().RLock ()
738+ if current .ScoreBiasOverride != nil {
739+ value := * current .ScoreBiasOverride
740+ score = & value
741+ }
742+ if current .BaseConcurrencyOverride != nil {
743+ value := * current .BaseConcurrencyOverride
744+ concurrency = & value
745+ }
746+ current .Mu ().RUnlock ()
747+ }
748+ if scoreBiasOverride .Set {
749+ score = nullableInt64Pointer (scoreBiasOverride .Value )
750+ }
751+ if baseConcurrencyOverride .Set {
752+ concurrency = nullableInt64Pointer (baseConcurrencyOverride .Value )
753+ }
754+ h .store .ApplyAccountSchedulerOverrides (id , score , concurrency )
755+ }
733756 if allowedAPIKeyIDs .Set {
734757 h .store .ApplyAccountAllowedAPIKeys (id , allowedAPIKeyIDs .Values )
735758 }
@@ -804,23 +827,26 @@ func parseOptionalStringSliceField(raw json.RawMessage, field string) (optionalS
804827 return optionalStringSlice {Set : true , Values : out }, nil
805828}
806829
807- func parseOptionalIntegerField (raw json.RawMessage , field string , minValue , maxValue int64 ) (sql.NullInt64 , error ) {
808- if len (raw ) == 0 || string (raw ) == "null" {
809- return sql.NullInt64 {}, nil
830+ func parseOptionalIntegerField (raw json.RawMessage , field string , minValue , maxValue int64 ) (database.OptionalNullInt64 , error ) {
831+ if len (raw ) == 0 {
832+ return database.OptionalNullInt64 {}, nil
833+ }
834+ if string (raw ) == "null" {
835+ return database.OptionalNullInt64 {Set : true }, nil
810836 }
811837
812838 var number json.Number
813839 if err := json .Unmarshal (raw , & number ); err != nil {
814- return sql. NullInt64 {}, fmt .Errorf ("%s 必须是整数或 null" , field )
840+ return database. OptionalNullInt64 {}, fmt .Errorf ("%s 必须是整数或 null" , field )
815841 }
816842 value , err := number .Int64 ()
817843 if err != nil {
818- return sql. NullInt64 {}, fmt .Errorf ("%s 必须是整数或 null" , field )
844+ return database. OptionalNullInt64 {}, fmt .Errorf ("%s 必须是整数或 null" , field )
819845 }
820846 if value < minValue || value > maxValue {
821- return sql. NullInt64 {}, fmt .Errorf ("%s 超出范围,必须在 %d..%d 之间" , field , minValue , maxValue )
847+ return database. OptionalNullInt64 {}, fmt .Errorf ("%s 超出范围,必须在 %d..%d 之间" , field , minValue , maxValue )
822848 }
823- return sql.NullInt64 {Int64 : value , Valid : true }, nil
849+ return database. OptionalNullInt64 { Set : true , Value : sql.NullInt64 {Int64 : value , Valid : true } }, nil
824850}
825851
826852func parseOptionalIntegerSliceField (raw json.RawMessage , field string ) (database.OptionalInt64Slice , error ) {
@@ -3117,12 +3143,13 @@ func (h *Handler) ListAPIKeys(c *gin.Context) {
31173143}
31183144
31193145type createKeyReq struct {
3120- Name string `json:"name"`
3121- Key string `json:"key"`
3122- QuotaLimit * float64 `json:"quota_limit"`
3123- Quota * float64 `json:"quota"`
3124- ExpiresAt string `json:"expires_at"`
3125- ExpiresInDays * int `json:"expires_in_days"`
3146+ Name string `json:"name"`
3147+ Key string `json:"key"`
3148+ QuotaLimit * float64 `json:"quota_limit"`
3149+ Quota * float64 `json:"quota"`
3150+ ExpiresAt string `json:"expires_at"`
3151+ ExpiresInDays * int `json:"expires_in_days"`
3152+ AllowedGroupIDs json.RawMessage `json:"allowed_group_ids"`
31263153}
31273154
31283155// generateKey 生成随机 API Key
@@ -3178,6 +3205,11 @@ func (h *Handler) CreateAPIKey(c *gin.Context) {
31783205 writeError (c , http .StatusBadRequest , err .Error ())
31793206 return
31803207 }
3208+ allowedGroupIDs , err := parseOptionalIntegerSliceField (req .AllowedGroupIDs , "allowed_group_ids" )
3209+ if err != nil {
3210+ writeError (c , http .StatusBadRequest , err .Error ())
3211+ return
3212+ }
31813213
31823214 key := req .Key
31833215 if key == "" {
@@ -3193,17 +3225,39 @@ func (h *Handler) CreateAPIKey(c *gin.Context) {
31933225
31943226 ctx , cancel := context .WithTimeout (c .Request .Context (), 5 * time .Second )
31953227 defer cancel ()
3228+ if allowedGroupIDs .Set {
3229+ missing , err := h .db .VerifyAccountGroupIDs (ctx , allowedGroupIDs .Values )
3230+ if err != nil {
3231+ writeInternalError (c , err )
3232+ return
3233+ }
3234+ if len (missing ) > 0 {
3235+ values := make ([]string , 0 , len (missing ))
3236+ for _ , value := range missing {
3237+ values = append (values , strconv .FormatInt (value , 10 ))
3238+ }
3239+ writeError (c , http .StatusBadRequest , "allowed_group_ids 包含不存在的分组 ID: " + strings .Join (values , ", " ))
3240+ return
3241+ }
3242+ }
31963243
31973244 id , err := h .db .InsertAPIKeyWithOptions (ctx , database.APIKeyInput {
3198- Name : req .Name ,
3199- Key : key ,
3200- QuotaLimit : quotaLimit ,
3201- ExpiresAt : expiresAt ,
3245+ Name : req .Name ,
3246+ Key : key ,
3247+ QuotaLimit : quotaLimit ,
3248+ ExpiresAt : expiresAt ,
3249+ AllowedGroupIDs : allowedGroupIDs .Values ,
32023250 })
32033251 if err != nil {
32043252 writeError (c , http .StatusInternalServerError , "创建失败: " + err .Error ())
32053253 return
32063254 }
3255+ if allowedGroupIDs .Set {
3256+ values := dedupeInt64 (allowedGroupIDs .Values )
3257+ if h .store != nil {
3258+ h .store .SetAPIKeyAllowedGroups (id , values )
3259+ }
3260+ }
32073261 h .invalidateAPIKeyRuntimeCaches (ctx , key )
32083262
32093263 // 记录安全审计日志
@@ -3215,12 +3269,13 @@ func (h *Handler) CreateAPIKey(c *gin.Context) {
32153269 expiresAtResponse = & formatted
32163270 }
32173271 c .JSON (http .StatusOK , createAPIKeyResponse {
3218- ID : id ,
3219- Key : key ,
3220- Name : req .Name ,
3221- QuotaLimit : quotaLimit ,
3222- QuotaUsed : 0 ,
3223- ExpiresAt : expiresAtResponse ,
3272+ ID : id ,
3273+ Key : key ,
3274+ Name : req .Name ,
3275+ QuotaLimit : quotaLimit ,
3276+ QuotaUsed : 0 ,
3277+ ExpiresAt : expiresAtResponse ,
3278+ AllowedGroupIDs : dedupeInt64 (allowedGroupIDs .Values ),
32243279 })
32253280}
32263281
0 commit comments