mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-03-30 03:43:39 +00:00
Routes quota alerts through a subscription-specific check when billing from subscriptions, preventing wallet-based thresholds from triggering false warnings. Updates the notification settings description and localization keys to clarify that both wallet and subscription balances are monitored.
79 lines
2.6 KiB
Go
79 lines
2.6 KiB
Go
package service
|
||
|
||
import (
|
||
"fmt"
|
||
|
||
"github.com/QuantumNous/new-api/logger"
|
||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||
"github.com/QuantumNous/new-api/types"
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
const (
|
||
BillingSourceWallet = "wallet"
|
||
BillingSourceSubscription = "subscription"
|
||
)
|
||
|
||
// PreConsumeBilling 根据用户计费偏好创建 BillingSession 并执行预扣费。
|
||
// 会话存储在 relayInfo.Billing 上,供后续 Settle / Refund 使用。
|
||
func PreConsumeBilling(c *gin.Context, preConsumedQuota int, relayInfo *relaycommon.RelayInfo) *types.NewAPIError {
|
||
session, apiErr := NewBillingSession(c, relayInfo, preConsumedQuota)
|
||
if apiErr != nil {
|
||
return apiErr
|
||
}
|
||
relayInfo.Billing = session
|
||
return nil
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// SettleBilling — 后结算辅助函数
|
||
// ---------------------------------------------------------------------------
|
||
|
||
// SettleBilling 执行计费结算。如果 RelayInfo 上有 BillingSession 则通过 session 结算,
|
||
// 否则回退到旧的 PostConsumeQuota 路径(兼容按次计费等场景)。
|
||
func SettleBilling(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, actualQuota int) error {
|
||
if relayInfo.Billing != nil {
|
||
preConsumed := relayInfo.Billing.GetPreConsumedQuota()
|
||
delta := actualQuota - preConsumed
|
||
|
||
if delta > 0 {
|
||
logger.LogInfo(ctx, fmt.Sprintf("预扣费后补扣费:%s(实际消耗:%s,预扣费:%s)",
|
||
logger.FormatQuota(delta),
|
||
logger.FormatQuota(actualQuota),
|
||
logger.FormatQuota(preConsumed),
|
||
))
|
||
} else if delta < 0 {
|
||
logger.LogInfo(ctx, fmt.Sprintf("预扣费后返还扣费:%s(实际消耗:%s,预扣费:%s)",
|
||
logger.FormatQuota(-delta),
|
||
logger.FormatQuota(actualQuota),
|
||
logger.FormatQuota(preConsumed),
|
||
))
|
||
} else {
|
||
logger.LogInfo(ctx, fmt.Sprintf("预扣费与实际消耗一致,无需调整:%s(按次计费)",
|
||
logger.FormatQuota(actualQuota),
|
||
))
|
||
}
|
||
|
||
if err := relayInfo.Billing.Settle(actualQuota); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 发送额度通知(订阅计费使用订阅剩余额度)
|
||
if actualQuota != 0 {
|
||
if relayInfo.BillingSource == BillingSourceSubscription {
|
||
checkAndSendSubscriptionQuotaNotify(relayInfo)
|
||
} else {
|
||
checkAndSendQuotaNotify(relayInfo, actualQuota-preConsumed, preConsumed)
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 回退:无 BillingSession 时使用旧路径
|
||
quotaDelta := actualQuota - relayInfo.FinalPreConsumedQuota
|
||
if quotaDelta != 0 {
|
||
return PostConsumeQuota(relayInfo, quotaDelta, relayInfo.FinalPreConsumedQuota, true)
|
||
}
|
||
return nil
|
||
}
|