feat: publish sanitized custom.97 source
This commit is contained in:
1022 files changed
+69903
-19207
No files matched your search
+32
-26
@@ -3,16 +3,19 @@ package service
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/i18n"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
"github.com/QuantumNous/new-api/relaykit/dto"
|
||||
"github.com/QuantumNous/new-api/relaykit/types"
|
||||
"github.com/QuantumNous/new-api/setting/operation_setting"
|
||||
|
||||
"github.com/bytedance/gopkg/util/gopool"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -27,13 +30,14 @@ import (
|
||||
type BillingSession struct {
|
||||
relayInfo *relaycommon.RelayInfo
|
||||
funding FundingSource
|
||||
preConsumedQuota int // 实际预扣额度(信任用户可能为 0)
|
||||
tokenConsumed int // 令牌额度实际扣减量
|
||||
extraReserved int // 发送前补充预扣的额度(订阅退款时需要单独回滚)
|
||||
trusted bool // 是否命中信任额度旁路
|
||||
fundingSettled bool // funding.Settle 已成功,资金来源已提交
|
||||
settled bool // Settle 全部完成(资金 + 令牌)
|
||||
refunded bool // Refund 已调用
|
||||
preConsumedQuota int // 实际预扣额度(信任用户可能为 0)
|
||||
tokenConsumed int // 令牌额度实际扣减量
|
||||
extraReserved int // 发送前补充预扣的额度(订阅退款时需要单独回滚)
|
||||
trusted bool // 是否命中信任额度旁路
|
||||
fundingSettled bool // funding.Settle 已成功,资金来源已提交
|
||||
settled bool // Settle 全部完成(资金 + 令牌)
|
||||
refunded bool // Refund 已调用
|
||||
lang string // 请求声明的语言,为空表示未声明
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
@@ -68,7 +72,7 @@ func (s *BillingSession) Settle(actualQuota int) error {
|
||||
}
|
||||
if tokenErr != nil {
|
||||
// 资金来源已提交,令牌调整失败只能记录日志;标记 settled 防止 Refund 误退资金
|
||||
common.SysLog(fmt.Sprintf("error adjusting token quota after funding settled (userId=%d, tokenId=%d, delta=%d): %s",
|
||||
common.SysLog(common.LogText("error adjusting token quota after funding settled (userId=%d, tokenId=%d, delta=%d): %s",
|
||||
s.relayInfo.UserId, s.relayInfo.TokenId, delta, tokenErr.Error()))
|
||||
}
|
||||
}
|
||||
@@ -90,7 +94,7 @@ func (s *BillingSession) Refund(c *gin.Context) {
|
||||
s.refunded = true
|
||||
s.mu.Unlock()
|
||||
|
||||
logger.LogInfo(c, fmt.Sprintf("用户 %d 请求失败, 返还预扣费(token_quota=%s, funding=%s)",
|
||||
logger.LogInfo(c, common.LogText("user %d request failed, refunding the pre-consumed quota (token_quota=%s, funding=%s)",
|
||||
s.relayInfo.UserId,
|
||||
logger.FormatQuota(s.tokenConsumed),
|
||||
s.funding.Source(),
|
||||
@@ -108,17 +112,17 @@ func (s *BillingSession) Refund(c *gin.Context) {
|
||||
gopool.Go(func() {
|
||||
// 1) 退还资金来源
|
||||
if err := funding.Refund(); err != nil {
|
||||
common.SysLog("error refunding billing source: " + err.Error())
|
||||
common.SysLog(common.LogText("error refunding billing source: %s", err.Error()))
|
||||
}
|
||||
if extraReserved > 0 && funding.Source() == BillingSourceSubscription && subscriptionId > 0 {
|
||||
if err := model.PostConsumeUserSubscriptionDelta(subscriptionId, -int64(extraReserved)); err != nil {
|
||||
common.SysLog("error refunding subscription extra reserved quota: " + err.Error())
|
||||
common.SysLog(common.LogText("error refunding subscription extra reserved quota: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
// 2) 退还令牌额度
|
||||
if tokenConsumed > 0 && !isPlayground {
|
||||
if err := model.IncreaseTokenQuota(tokenId, tokenKey, tokenConsumed); err != nil {
|
||||
common.SysLog("error refunding token quota: " + err.Error())
|
||||
common.SysLog(common.LogText("error refunding token quota: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -200,9 +204,9 @@ func (s *BillingSession) preConsume(c *gin.Context, quota int) *types.NewAPIErro
|
||||
if s.shouldTrust(c) {
|
||||
s.trusted = true
|
||||
effectiveQuota = 0
|
||||
logger.LogInfo(c, fmt.Sprintf("用户 %d 额度充足, 信任且不需要预扣费 (funding=%s)", s.relayInfo.UserId, s.funding.Source()))
|
||||
logger.LogInfo(c, common.LogText("user %d has enough quota, trusted without pre-consume (funding=%s)", s.relayInfo.UserId, s.funding.Source()))
|
||||
} else if effectiveQuota > 0 {
|
||||
logger.LogInfo(c, fmt.Sprintf("用户 %d 需要预扣费 %s (funding=%s)", s.relayInfo.UserId, logger.FormatQuota(effectiveQuota), s.funding.Source()))
|
||||
logger.LogInfo(c, common.LogText("user %d needs pre-consume %s (funding=%s)", s.relayInfo.UserId, logger.FormatQuota(effectiveQuota), s.funding.Source()))
|
||||
}
|
||||
|
||||
// ---- 1) 预扣令牌额度 ----
|
||||
@@ -218,7 +222,7 @@ func (s *BillingSession) preConsume(c *gin.Context, quota int) *types.NewAPIErro
|
||||
// 预扣费失败,回滚令牌额度
|
||||
if s.tokenConsumed > 0 && !s.relayInfo.IsPlayground {
|
||||
if rollbackErr := model.IncreaseTokenQuota(s.relayInfo.TokenId, s.relayInfo.TokenKey, s.tokenConsumed); rollbackErr != nil {
|
||||
common.SysLog(fmt.Sprintf("error rolling back token quota (userId=%d, tokenId=%d, amount=%d, fundingErr=%s): %s",
|
||||
common.SysLog(common.LogText("error rolling back token quota (userId=%d, tokenId=%d, amount=%d, fundingErr=%s): %s",
|
||||
s.relayInfo.UserId, s.relayInfo.TokenId, s.tokenConsumed, err.Error(), rollbackErr.Error()))
|
||||
}
|
||||
s.tokenConsumed = 0
|
||||
@@ -230,13 +234,13 @@ func (s *BillingSession) preConsume(c *gin.Context, quota int) *types.NewAPIErro
|
||||
userQuota = 0
|
||||
}
|
||||
return types.NewErrorWithStatusCode(
|
||||
fmt.Errorf("用户额度不足, 剩余额度: %s", logger.FormatQuota(userQuota)),
|
||||
errors.New(i18n.T(c, i18n.MsgQuotaUserInsufficient, map[string]any{"Remaining": logger.FormatQuota(userQuota)})),
|
||||
types.ErrorCodeInsufficientUserQuota, http.StatusForbidden,
|
||||
types.ErrOptionWithSkipRetry(), types.ErrOptionWithNoRecordErrorLog())
|
||||
}
|
||||
errMsg := err.Error()
|
||||
if strings.Contains(errMsg, "no active subscription") || strings.Contains(errMsg, "subscription quota insufficient") {
|
||||
return types.NewErrorWithStatusCode(fmt.Errorf("订阅额度不足或未配置订阅: %s", errMsg), types.ErrorCodeInsufficientUserQuota, http.StatusForbidden, types.ErrOptionWithSkipRetry(), types.ErrOptionWithNoRecordErrorLog())
|
||||
return types.NewErrorWithStatusCode(errors.New(i18n.T(c, i18n.MsgQuotaSubscriptionInsufficient, map[string]any{"Error": errMsg})), types.ErrorCodeInsufficientUserQuota, http.StatusForbidden, types.ErrOptionWithSkipRetry(), types.ErrOptionWithNoRecordErrorLog())
|
||||
}
|
||||
return types.NewError(err, types.ErrorCodeUpdateDataError, types.ErrOptionWithSkipRetry())
|
||||
}
|
||||
@@ -275,7 +279,7 @@ func (s *BillingSession) reserveFunding(delta int, requireAvailableQuota bool) e
|
||||
case *SubscriptionFunding:
|
||||
if err := model.PostConsumeUserSubscriptionDelta(funding.subscriptionId, int64(delta)); err != nil {
|
||||
return types.NewErrorWithStatusCode(
|
||||
fmt.Errorf("订阅额度不足或未配置订阅: %s", err.Error()),
|
||||
errors.New(i18n.Translate(s.lang, i18n.MsgQuotaSubscriptionInsufficient, map[string]any{"Error": err.Error()})),
|
||||
types.ErrorCodeInsufficientUserQuota,
|
||||
http.StatusForbidden,
|
||||
types.ErrOptionWithSkipRetry(),
|
||||
@@ -292,13 +296,13 @@ func (s *BillingSession) rollbackFundingReserve(delta int) {
|
||||
switch funding := s.funding.(type) {
|
||||
case *WalletFunding:
|
||||
if err := model.IncreaseUserQuota(funding.userId, delta, false); err != nil {
|
||||
common.SysLog("error rolling back wallet funding reserve: " + err.Error())
|
||||
common.SysLog(common.LogText("error rolling back wallet funding reserve: %s", err.Error()))
|
||||
} else {
|
||||
funding.consumed -= delta
|
||||
}
|
||||
case *SubscriptionFunding:
|
||||
if err := model.PostConsumeUserSubscriptionDelta(funding.subscriptionId, -int64(delta)); err != nil {
|
||||
common.SysLog("error rolling back subscription funding reserve: " + err.Error())
|
||||
common.SysLog(common.LogText("error rolling back subscription funding reserve: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,8 +324,8 @@ func (s *BillingSession) shouldTrust(c *gin.Context) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
trustQuota := common.GetTrustQuota()
|
||||
if trustQuota <= 0 {
|
||||
trustQuota := operation_setting.GetQuotaSetting().TrustQuotaUSD * common.QuotaPerUnit
|
||||
if trustQuota <= 0 || math.IsNaN(trustQuota) || math.IsInf(trustQuota, 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -329,7 +333,7 @@ func (s *BillingSession) shouldTrust(c *gin.Context) bool {
|
||||
tokenTrusted := s.relayInfo.TokenUnlimited
|
||||
if !tokenTrusted {
|
||||
tokenQuota := c.GetInt("token_quota")
|
||||
tokenTrusted = tokenQuota > trustQuota
|
||||
tokenTrusted = float64(tokenQuota) > trustQuota
|
||||
}
|
||||
if !tokenTrusted {
|
||||
return false
|
||||
@@ -337,7 +341,7 @@ func (s *BillingSession) shouldTrust(c *gin.Context) bool {
|
||||
|
||||
switch s.funding.Source() {
|
||||
case BillingSourceWallet:
|
||||
return s.relayInfo.UserQuota > trustQuota
|
||||
return float64(s.relayInfo.UserQuota) > trustQuota
|
||||
case BillingSourceSubscription:
|
||||
// 订阅不能启用信任旁路。原因:
|
||||
// 1. PreConsumeUserSubscription 要求 amount>0 来创建预扣记录并锁定订阅
|
||||
@@ -389,13 +393,13 @@ func NewBillingSession(c *gin.Context, relayInfo *relaycommon.RelayInfo, preCons
|
||||
}
|
||||
if userQuota <= 0 {
|
||||
return nil, types.NewErrorWithStatusCode(
|
||||
fmt.Errorf("用户额度不足, 剩余额度: %s", logger.FormatQuota(userQuota)),
|
||||
errors.New(i18n.T(c, i18n.MsgQuotaUserInsufficient, map[string]any{"Remaining": logger.FormatQuota(userQuota)})),
|
||||
types.ErrorCodeInsufficientUserQuota, http.StatusForbidden,
|
||||
types.ErrOptionWithSkipRetry(), types.ErrOptionWithNoRecordErrorLog())
|
||||
}
|
||||
if userQuota-preConsumedQuota < 0 {
|
||||
return nil, types.NewErrorWithStatusCode(
|
||||
fmt.Errorf("预扣费额度失败, 用户剩余额度: %s, 需要预扣费额度: %s", logger.FormatQuota(userQuota), logger.FormatQuota(preConsumedQuota)),
|
||||
errors.New(i18n.T(c, i18n.MsgQuotaPreConsumeInsufficient, map[string]any{"Remaining": logger.FormatQuota(userQuota), "Required": logger.FormatQuota(preConsumedQuota)})),
|
||||
types.ErrorCodeInsufficientUserQuota, http.StatusForbidden,
|
||||
types.ErrOptionWithSkipRetry(), types.ErrOptionWithNoRecordErrorLog())
|
||||
}
|
||||
@@ -404,6 +408,7 @@ func NewBillingSession(c *gin.Context, relayInfo *relaycommon.RelayInfo, preCons
|
||||
session := &BillingSession{
|
||||
relayInfo: relayInfo,
|
||||
funding: &WalletFunding{userId: relayInfo.UserId},
|
||||
lang: i18n.StatedLang(c),
|
||||
}
|
||||
if apiErr := session.preConsume(c, preConsumedQuota); apiErr != nil {
|
||||
return nil, apiErr
|
||||
@@ -418,6 +423,7 @@ func NewBillingSession(c *gin.Context, relayInfo *relaycommon.RelayInfo, preCons
|
||||
}
|
||||
session := &BillingSession{
|
||||
relayInfo: relayInfo,
|
||||
lang: i18n.StatedLang(c),
|
||||
funding: &SubscriptionFunding{
|
||||
requestId: relayInfo.RequestId,
|
||||
userId: relayInfo.UserId,
|
||||
|
||||
Reference in new issue
Block a user