Skip to content

feat: subscription plan group restriction#4079

Open
po1onius wants to merge 1 commit intoQuantumNous:mainfrom
po1onius:sub-group-limit
Open

feat: subscription plan group restriction#4079
po1onius wants to merge 1 commit intoQuantumNous:mainfrom
po1onius:sub-group-limit

Conversation

@po1onius
Copy link
Copy Markdown

@po1onius po1onius commented Apr 3, 2026

close: #3388

总结

就像该issue描述的那样,当前的订阅机制欠缺限制,无法限制订阅计划能够使用的模型,比如创建针对模型A的订阅时,用户能够使用订阅的额度来使用模型B。该pr补充了上升分组的能力,当用户订阅后使用对应上升分组的模型时会扣除订阅额度,但用户使用对应上升分组之外的模型时,使用余额扣费。

验证

已在本地部署,创建测试订阅,并观察使用日志,效果达到预期

Summary by CodeRabbit

  • New Features

    • Added group-based subscription matching to enable more granular control over which subscriptions are used when consuming model quotas.
  • Bug Fixes

    • Improved subscription selection logic to ensure the correct subscription is applied based on group configurations, preventing quota allocation from incorrect subscription sources.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 3, 2026

Walkthrough

The changes add group-based matching to subscription consumption. A new subscriptionMatchesModelGroup() function evaluates whether a subscription's group configuration is compatible with a requested usingGroup. The PreConsumeUserSubscription() function signature gains a usingGroup parameter and filters subscriptions by group compatibility during iteration. The SubscriptionFunding struct gains a usingGroup field that propagates the group value through the billing pipeline.

Changes

Cohort / File(s) Summary
Subscription Model Layer
model/subscription.go
Added subscriptionMatchesModelGroup() helper function to determine group compatibility. Updated PreConsumeUserSubscription() to accept usingGroup parameter, skip non-matching subscriptions during iteration, and return "no active subscription" error when no candidates satisfy the group predicate.
Billing Service Layer
service/billing_session.go, service/funding_source.go
Added usingGroup field to SubscriptionFunding struct. Updated struct initialization in NewBillingSession to pass relayInfo.UsingGroup and modified PreConsume method to forward this value to PreConsumeUserSubscription().

Sequence Diagram

sequenceDiagram
    participant Relay as Relay Request
    participant BillingSession as BillingSession
    participant FundingSource as SubscriptionFunding
    participant Model as Model Layer
    
    Relay->>BillingSession: Request with UsingGroup
    BillingSession->>FundingSource: Create SubscriptionFunding(usingGroup)
    FundingSource->>Model: PreConsumeUserSubscription(..., usingGroup)
    Model->>Model: subscriptionMatchesModelGroup()
    alt Group matches
        Model->>Model: Consume subscription quota
        Model-->>FundingSource: PreConsumeResult
    else No matching group
        Model-->>FundingSource: "no active subscription" error
    end
    FundingSource-->>BillingSession: Result
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Poem

🐰 A rabbit hops through subscriptions with glee,
Grouping the budgets so users can see,
Which coins go where, all organized bright,
No mixing and mingling, just what's right!
With usingGroup passed through the billing dance,
Each subscription finds its perfect match. 🎯

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The changes implement the core objective of adding group restriction to subscriptions. The PR adds the usingGroup parameter to PreConsumeUserSubscription and introduces subscription group matching logic, enabling subscriptions to be filtered by group. However, the provided changes only show partial implementation of the full feature scope. Verify that all changes from the detailed implementation plan are present: AllowedGroups fields in models, database migration, admin controller validation, frontend components, and i18n additions. The raw summary shows only backend model/service changes; confirm frontend and database migration changes are included.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: introducing subscription plan group restriction functionality that allows subscriptions to be limited to specific model/user groups.
Out of Scope Changes check ✅ Passed All code changes in the provided summary are directly related to implementing subscription group restriction functionality as specified in issue #3388. Changes to model signatures, service layer field additions, and billing session modifications all serve the group matching objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8d0538cf-326a-41fb-bf2b-37aecad2955d

📥 Commits

Reviewing files that changed from the base of the PR and between bb5b9ea and 4156b28.

📒 Files selected for processing (3)
  • model/subscription.go
  • service/billing_session.go
  • service/funding_source.go

Comment on lines +955 to +974
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
}
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: 订阅套餐分组限制(Subscription Plan Group Restriction)

1 participant