mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-04-19 01:27:28 +00:00
🚀 refactor: Simplify subscription quota to total amount model
Remove per-model subscription items and switch to a single total quota per plan and user subscription. Update billing, reset, and logging flows to operate on total quota, and refactor admin/user UI to configure and display total quota consistently.
This commit is contained in:
@@ -26,17 +26,9 @@ func PreConsumeBilling(c *gin.Context, preConsumedQuota int, relayInfo *relaycom
|
||||
|
||||
pref := common.NormalizeBillingPreference(relayInfo.UserSetting.BillingPreference)
|
||||
trySubscription := func() *types.NewAPIError {
|
||||
quotaTypes := model.GetModelQuotaTypes(relayInfo.OriginModelName)
|
||||
quotaType := 0
|
||||
if len(quotaTypes) > 0 {
|
||||
quotaType = quotaTypes[0]
|
||||
}
|
||||
|
||||
// For subscription item: per-request consumes 1, per-quota consumes preConsumedQuota quota units.
|
||||
// For total quota: consume preConsumedQuota quota units.
|
||||
subConsume := int64(preConsumedQuota)
|
||||
if quotaType == 1 {
|
||||
subConsume = 1
|
||||
}
|
||||
if subConsume <= 0 {
|
||||
subConsume = 1
|
||||
}
|
||||
@@ -58,8 +50,7 @@ func PreConsumeBilling(c *gin.Context, preConsumedQuota int, relayInfo *relaycom
|
||||
}
|
||||
|
||||
relayInfo.BillingSource = BillingSourceSubscription
|
||||
relayInfo.SubscriptionItemId = res.ItemId
|
||||
relayInfo.SubscriptionQuotaType = quotaType
|
||||
relayInfo.SubscriptionId = res.UserSubscriptionId
|
||||
relayInfo.SubscriptionPreConsumed = res.PreConsumed
|
||||
relayInfo.SubscriptionPostDelta = 0
|
||||
relayInfo.SubscriptionAmountTotal = res.AmountTotal
|
||||
@@ -76,8 +67,7 @@ func PreConsumeBilling(c *gin.Context, preConsumedQuota int, relayInfo *relaycom
|
||||
|
||||
tryWallet := func() *types.NewAPIError {
|
||||
relayInfo.BillingSource = BillingSourceWallet
|
||||
relayInfo.SubscriptionItemId = 0
|
||||
relayInfo.SubscriptionQuotaType = 0
|
||||
relayInfo.SubscriptionId = 0
|
||||
relayInfo.SubscriptionPreConsumed = 0
|
||||
return PreConsumeQuota(c, preConsumedQuota, relayInfo)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
@@ -90,15 +89,14 @@ func appendBillingInfo(relayInfo *relaycommon.RelayInfo, other map[string]interf
|
||||
other["billing_preference"] = relayInfo.UserSetting.BillingPreference
|
||||
}
|
||||
if relayInfo.BillingSource == "subscription" {
|
||||
if relayInfo.SubscriptionItemId != 0 {
|
||||
other["subscription_item_id"] = relayInfo.SubscriptionItemId
|
||||
if relayInfo.SubscriptionId != 0 {
|
||||
other["subscription_id"] = relayInfo.SubscriptionId
|
||||
}
|
||||
other["subscription_quota_type"] = relayInfo.SubscriptionQuotaType
|
||||
if relayInfo.SubscriptionPreConsumed > 0 {
|
||||
other["subscription_pre_consumed"] = relayInfo.SubscriptionPreConsumed
|
||||
}
|
||||
// post_delta: settlement delta applied after actual usage is known (can be negative for refund)
|
||||
if relayInfo.SubscriptionQuotaType == 0 && relayInfo.SubscriptionPostDelta != 0 {
|
||||
if relayInfo.SubscriptionPostDelta != 0 {
|
||||
other["subscription_post_delta"] = relayInfo.SubscriptionPostDelta
|
||||
}
|
||||
if relayInfo.SubscriptionPlanId != 0 {
|
||||
@@ -108,12 +106,8 @@ func appendBillingInfo(relayInfo *relaycommon.RelayInfo, other map[string]interf
|
||||
other["subscription_plan_title"] = relayInfo.SubscriptionPlanTitle
|
||||
}
|
||||
// Compute "this request" subscription consumed + remaining
|
||||
consumed := relayInfo.SubscriptionPreConsumed
|
||||
usedFinal := relayInfo.SubscriptionAmountUsedAfterPreConsume
|
||||
if relayInfo.SubscriptionQuotaType == 0 {
|
||||
consumed = relayInfo.SubscriptionPreConsumed + relayInfo.SubscriptionPostDelta
|
||||
usedFinal = relayInfo.SubscriptionAmountUsedAfterPreConsume + relayInfo.SubscriptionPostDelta
|
||||
}
|
||||
consumed := relayInfo.SubscriptionPreConsumed + relayInfo.SubscriptionPostDelta
|
||||
usedFinal := relayInfo.SubscriptionAmountUsedAfterPreConsume + relayInfo.SubscriptionPostDelta
|
||||
if consumed < 0 {
|
||||
consumed = 0
|
||||
}
|
||||
@@ -132,13 +126,6 @@ func appendBillingInfo(relayInfo *relaycommon.RelayInfo, other map[string]interf
|
||||
if consumed > 0 {
|
||||
other["subscription_consumed"] = consumed
|
||||
}
|
||||
// Fallback: if plan info missing (older requests), best-effort fetch by item id.
|
||||
if relayInfo.SubscriptionPlanId == 0 && relayInfo.SubscriptionItemId != 0 {
|
||||
if info, err := model.GetSubscriptionPlanInfoBySubscriptionItemId(relayInfo.SubscriptionItemId); err == nil && info != nil {
|
||||
other["subscription_plan_id"] = info.PlanId
|
||||
other["subscription_plan_title"] = info.PlanTitle
|
||||
}
|
||||
}
|
||||
// Wallet quota is not deducted when billed from subscription.
|
||||
other["wallet_quota_deducted"] = 0
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
func ReturnPreConsumedQuota(c *gin.Context, relayInfo *relaycommon.RelayInfo) {
|
||||
// Always refund subscription pre-consumed (can be non-zero even when FinalPreConsumedQuota is 0)
|
||||
needRefundSub := relayInfo.BillingSource == BillingSourceSubscription && relayInfo.SubscriptionItemId != 0 && relayInfo.SubscriptionPreConsumed > 0
|
||||
needRefundSub := relayInfo.BillingSource == BillingSourceSubscription && relayInfo.SubscriptionId != 0 && relayInfo.SubscriptionPreConsumed > 0
|
||||
needRefundToken := relayInfo.FinalPreConsumedQuota != 0
|
||||
if !needRefundSub && !needRefundToken {
|
||||
return
|
||||
|
||||
@@ -505,16 +505,15 @@ func PostConsumeQuota(relayInfo *relaycommon.RelayInfo, quota int, preConsumedQu
|
||||
|
||||
// 1) Consume from wallet quota OR subscription item
|
||||
if relayInfo != nil && relayInfo.BillingSource == BillingSourceSubscription {
|
||||
// For subscription: quotaType=0 uses quota units delta; quotaType=1 uses fixed 0 delta (pre-consumed 1 on request begin)
|
||||
if relayInfo.SubscriptionQuotaType == 0 {
|
||||
if relayInfo.SubscriptionItemId == 0 {
|
||||
return errors.New("subscription item id is missing")
|
||||
}
|
||||
if err := model.PostConsumeUserSubscriptionDelta(relayInfo.SubscriptionItemId, int64(quota)); err != nil {
|
||||
if relayInfo.SubscriptionId == 0 {
|
||||
return errors.New("subscription id is missing")
|
||||
}
|
||||
delta := int64(quota) - relayInfo.SubscriptionPreConsumed
|
||||
if delta != 0 {
|
||||
if err := model.PostConsumeUserSubscriptionDelta(relayInfo.SubscriptionId, delta); err != nil {
|
||||
return err
|
||||
}
|
||||
// Track delta for logging/UI (net consumed = preConsumed + postDelta)
|
||||
relayInfo.SubscriptionPostDelta += int64(quota)
|
||||
relayInfo.SubscriptionPostDelta += delta
|
||||
}
|
||||
} else {
|
||||
// Wallet
|
||||
|
||||
@@ -53,7 +53,7 @@ func runSubscriptionQuotaResetOnce() {
|
||||
ctx := context.Background()
|
||||
totalReset := 0
|
||||
for {
|
||||
n, err := model.ResetDueSubscriptionItems(subscriptionResetBatchSize)
|
||||
n, err := model.ResetDueSubscriptions(subscriptionResetBatchSize)
|
||||
if err != nil {
|
||||
logger.LogWarn(ctx, fmt.Sprintf("subscription quota reset task failed: %v", err))
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user