fix(billing): reject saturated pre-consume quota

This commit is contained in:
CaIon
2026-07-11 13:14:21 +08:00
parent dad57a6bb8
commit 621927f710
5 changed files with 124 additions and 5 deletions
+18
View File
@@ -2,6 +2,7 @@ package service
import (
"fmt"
"net/http"
"github.com/QuantumNous/new-api/logger"
relaycommon "github.com/QuantumNous/new-api/relay/common"
@@ -17,6 +18,23 @@ const (
// PreConsumeBilling 根据用户计费偏好创建 BillingSession 并执行预扣费。
// 会话存储在 relayInfo.Billing 上,供后续 Settle / Refund 使用。
func PreConsumeBilling(c *gin.Context, preConsumedQuota int, relayInfo *relaycommon.RelayInfo) *types.NewAPIError {
if relayInfo != nil && relayInfo.QuotaClamp != nil {
clamp := relayInfo.QuotaClamp
return types.NewErrorWithStatusCode(
fmt.Errorf("pre-consume quota is out of range: operation=%s kind=%s value=%g", clamp.Op, clamp.Kind, clamp.Original),
types.ErrorCodeModelPriceError,
http.StatusBadRequest,
types.ErrOptionWithSkipRetry(),
)
}
if preConsumedQuota < 0 {
return types.NewErrorWithStatusCode(
fmt.Errorf("pre-consume quota cannot be negative: %d", preConsumedQuota),
types.ErrorCodeModelPriceError,
http.StatusBadRequest,
types.ErrOptionWithSkipRetry(),
)
}
session, apiErr := NewBillingSession(c, relayInfo, preConsumedQuota)
if apiErr != nil {
return apiErr
+35
View File
@@ -1,10 +1,12 @@
package service
import (
"net/http"
"testing"
"github.com/QuantumNous/new-api/common"
relaycommon "github.com/QuantumNous/new-api/relay/common"
"github.com/QuantumNous/new-api/types"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
@@ -72,3 +74,36 @@ func TestAttachQuotaSaturationNoClampNoMarker(t *testing.T) {
_, hasAdmin := other["admin_info"]
require.False(t, hasAdmin, "no admin_info should be added when there is no clamp")
}
func TestPreConsumeBillingRejectsSaturatedQuotaBeforeDeduction(t *testing.T) {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(nil)
info := &relaycommon.RelayInfo{
QuotaClamp: &common.QuotaClamp{
Op: "QuotaFromFloat",
Kind: common.QuotaClampOverflow,
Original: 1e30,
Clamped: common.MaxQuota,
},
}
apiErr := PreConsumeBilling(c, common.MaxQuota, info)
require.NotNil(t, apiErr)
require.Equal(t, types.ErrorCodeModelPriceError, apiErr.GetErrorCode())
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode)
require.Nil(t, info.Billing)
}
func TestPreConsumeBillingRejectsNegativeQuotaBeforeDeduction(t *testing.T) {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(nil)
info := &relaycommon.RelayInfo{}
apiErr := PreConsumeBilling(c, -1, info)
require.NotNil(t, apiErr)
require.Equal(t, types.ErrorCodeModelPriceError, apiErr.GetErrorCode())
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode)
require.Nil(t, info.Billing)
}