fix(admin): preserve quota limits on partial group updates

This commit is contained in:
wucm667
2026-08-30 16:36:13 +08:00
parent b5827cfd54
commit 9f1effd71c
4 changed files with 81 additions and 7 deletions
@@ -81,8 +81,8 @@ func (f optionalLimitField) ToServiceInput() *float64 {
if f.value != nil {
return f.value
}
zero := 0.0
return &zero
unlimited := -1.0
return &unlimited
}
// NewGroupHandler creates a new admin group handler
@@ -0,0 +1,33 @@
package admin
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
func TestUpdateGroupRequestLimitFieldsTriState(t *testing.T) {
t.Run("omitted means unchanged", func(t *testing.T) {
var req UpdateGroupRequest
require.NoError(t, json.Unmarshal([]byte(`{}`), &req))
require.Nil(t, req.DailyLimitUSD.ToServiceInput())
require.Nil(t, req.WeeklyLimitUSD.ToServiceInput())
require.Nil(t, req.MonthlyLimitUSD.ToServiceInput())
})
t.Run("null means unlimited", func(t *testing.T) {
var req UpdateGroupRequest
require.NoError(t, json.Unmarshal([]byte(`{"daily_limit_usd":null}`), &req))
limit := req.DailyLimitUSD.ToServiceInput()
require.NotNil(t, limit)
require.Negative(t, *limit)
})
t.Run("number is preserved", func(t *testing.T) {
var req UpdateGroupRequest
require.NoError(t, json.Unmarshal([]byte(`{"weekly_limit_usd":0,"monthly_limit_usd":42.5}`), &req))
require.Equal(t, 0.0, *req.WeeklyLimitUSD.ToServiceInput())
require.Equal(t, 42.5, *req.MonthlyLimitUSD.ToServiceInput())
})
}
+10 -5
View File
@@ -680,11 +680,16 @@ func (s *adminServiceImpl) UpdateGroup(ctx context.Context, id int64, input *Upd
if input.SubscriptionType != "" {
group.SubscriptionType = input.SubscriptionType
}
// 限额字段:nil/负数 表示"无限制",0 表示"不允许用量",正数表示具体限额
// 前端始终发送这三个字段,无需 nil 守卫
group.DailyLimitUSD = normalizeLimit(input.DailyLimitUSD)
group.WeeklyLimitUSD = normalizeLimit(input.WeeklyLimitUSD)
group.MonthlyLimitUSD = normalizeLimit(input.MonthlyLimitUSD)
// 限额字段:nil 表示不修改,负数表示"无限制",0 表示"不允许用量",正数表示具体限额。
if input.DailyLimitUSD != nil {
group.DailyLimitUSD = normalizeLimit(input.DailyLimitUSD)
}
if input.WeeklyLimitUSD != nil {
group.WeeklyLimitUSD = normalizeLimit(input.WeeklyLimitUSD)
}
if input.MonthlyLimitUSD != nil {
group.MonthlyLimitUSD = normalizeLimit(input.MonthlyLimitUSD)
}
// 图片生成计费配置:负数表示清除(使用默认价格)
if input.AllowImageGeneration != nil {
group.AllowImageGeneration = *input.AllowImageGeneration
@@ -603,6 +603,42 @@ func TestAdminService_UpdateGroup_PreservesImageGenerationControlsWhenOmitted(t
require.InDelta(t, 0.5, repo.updated.ImageRateMultiplier, 1e-12)
}
func TestAdminService_UpdateGroup_LimitFieldsPartialUpdate(t *testing.T) {
daily, weekly, monthly := 10.0, 20.0, 30.0
existingGroup := &Group{
ID: 1,
Name: "existing-group",
Platform: PlatformOpenAI,
Status: StatusActive,
DailyLimitUSD: &daily,
WeeklyLimitUSD: &weekly,
MonthlyLimitUSD: &monthly,
}
repo := &groupRepoStubForAdmin{getByID: existingGroup}
svc := &adminServiceImpl{groupRepo: repo}
t.Run("non-quota update preserves all limits", func(t *testing.T) {
description := "updated"
group, err := svc.UpdateGroup(context.Background(), existingGroup.ID, &UpdateGroupInput{Description: &description})
require.NoError(t, err)
require.Equal(t, 10.0, *group.DailyLimitUSD)
require.Equal(t, 20.0, *group.WeeklyLimitUSD)
require.Equal(t, 30.0, *group.MonthlyLimitUSD)
})
t.Run("explicit changes and unlimited clear only touched limits", func(t *testing.T) {
newDaily, unlimited := 15.0, -1.0
group, err := svc.UpdateGroup(context.Background(), existingGroup.ID, &UpdateGroupInput{
DailyLimitUSD: &newDaily,
WeeklyLimitUSD: &unlimited,
})
require.NoError(t, err)
require.Equal(t, 15.0, *group.DailyLimitUSD)
require.Nil(t, group.WeeklyLimitUSD)
require.Equal(t, 30.0, *group.MonthlyLimitUSD)
})
}
func TestAdminService_UpdateGroup_DisablesBatchImageWhenImageGenerationDisabled(t *testing.T) {
existingGroup := &Group{
ID: 1,