Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion model/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -952,8 +952,29 @@ func maybeResetUserSubscriptionWithPlanTx(tx *gorm.DB, sub *UserSubscription, pl
return tx.Save(sub).Error
}

func subscriptionMatchesModelGroup(sub *UserSubscription, modelName string, usingGroup string) bool {
if sub == nil {
return false
}
upgradeGroup := strings.TrimSpace(sub.UpgradeGroup)
if upgradeGroup == "" {
return true
}
usingGroup = strings.TrimSpace(usingGroup)
if usingGroup != "" && usingGroup != "auto" {
return usingGroup == upgradeGroup
}
modelGroups := GetModelEnableGroups(modelName)
for _, group := range modelGroups {
if strings.TrimSpace(group) == upgradeGroup {
return true
}
}
return false
}
Comment on lines +955 to +974
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use AllowedGroups for eligibility; UpgradeGroup is a different concern.

This matcher makes every restricted-but-non-upgrading plan universally match (UpgradeGroup == "" returns true), and it also turns existing upgrade-only plans into group-restricted ones, which breaks the backward-compatibility goal. A single UpgradeGroup value also cannot represent the issue's comma-separated multi-group allowlist, so the restriction needs to be persisted and matched via AllowedGroups on the UserSubscription snapshot instead.


// PreConsumeUserSubscription pre-consumes from any active subscription total quota.
func PreConsumeUserSubscription(requestId string, userId int, modelName string, quotaType int, amount int64) (*SubscriptionPreConsumeResult, error) {
func PreConsumeUserSubscription(requestId string, userId int, modelName string, usingGroup string, quotaType int, amount int64) (*SubscriptionPreConsumeResult, error) {
if userId <= 0 {
return nil, errors.New("invalid userId")
}
Expand Down Expand Up @@ -999,8 +1020,13 @@ func PreConsumeUserSubscription(requestId string, userId int, modelName string,
if len(subs) == 0 {
return errors.New("no active subscription")
}
hasMatchedSubscription := false
for _, candidate := range subs {
sub := candidate
if !subscriptionMatchesModelGroup(&sub, modelName, usingGroup) {
continue
}
hasMatchedSubscription = true
plan, err := getSubscriptionPlanByIdTx(tx, sub.PlanId)
if err != nil {
return err
Expand Down Expand Up @@ -1048,6 +1074,9 @@ func PreConsumeUserSubscription(requestId string, userId int, modelName string,
returnValue.AmountUsedAfter = sub.AmountUsed
return nil
}
if !hasMatchedSubscription {
return errors.New("no active subscription")
}
return fmt.Errorf("subscription quota insufficient, need=%d", amount)
})
if err != nil {
Expand Down
9 changes: 5 additions & 4 deletions service/billing_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,11 @@ func NewBillingSession(c *gin.Context, relayInfo *relaycommon.RelayInfo, preCons
session := &BillingSession{
relayInfo: relayInfo,
funding: &SubscriptionFunding{
requestId: relayInfo.RequestId,
userId: relayInfo.UserId,
modelName: relayInfo.OriginModelName,
amount: subConsume,
requestId: relayInfo.RequestId,
userId: relayInfo.UserId,
modelName: relayInfo.OriginModelName,
usingGroup: relayInfo.UsingGroup,
amount: subConsume,
},
}
// 必须传 subConsume 而非 preConsumedQuota,保证 SubscriptionFunding.amount、
Expand Down
3 changes: 2 additions & 1 deletion service/funding_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type SubscriptionFunding struct {
requestId string
userId int
modelName string
usingGroup string
amount int64 // 预扣的订阅额度(subConsume)
subscriptionId int
preConsumed int64
Expand All @@ -85,7 +86,7 @@ func (s *SubscriptionFunding) Source() string { return BillingSourceSubscription

func (s *SubscriptionFunding) PreConsume(_ int) error {
// amount 参数被忽略,使用内部 s.amount(已在构造时根据 preConsumedQuota 计算)
res, err := model.PreConsumeUserSubscription(s.requestId, s.userId, s.modelName, 0, s.amount)
res, err := model.PreConsumeUserSubscription(s.requestId, s.userId, s.modelName, s.usingGroup, 0, s.amount)
if err != nil {
return err
}
Expand Down