fix: 配额系统添加 pro 套餐并支持未知套餐 fallback 到 free #1262

Merged
xiaoxia merged 3 commits from fix/quota-pro-tier-fallback into develop 2026-08-07 15:16:44 +08:00
4 changed files with 40 additions and 8 deletions
+4 -1
View File
@@ -1,9 +1,10 @@
"""Quota system with registry pattern.
Three subscription tiers with different limits:
Four subscription tiers with different limits:
- free: 2GB storage, 5 videos/month, 3 concurrent, 3 templates, 50 titles, 10 voiceovers, no AI voice
- basic: 20GB storage, 30 videos/month, 10 concurrent, 15 templates, 500 titles, 100 voiceovers, AI voice
- premium: 100GB storage, 100 videos/month, 20 concurrent, unlimited templates, 500 titles, 100 voiceovers, AI voice
- pro: Same as premium (alias for premium tier)
Quota dimensions are registered by modules via the ModuleRegistry,
and checked against the user's subscription plan.
@@ -100,6 +101,8 @@ QUOTA_TIERS: Dict[str, QuotaTier] = {
},
),
}
# pro 套餐与 premium 配额相同,使用别名引用避免重复维护
QUOTA_TIERS["pro"] = QUOTA_TIERS["premium"]
class QuotaWarningLevel:
+5 -5
View File
@@ -354,13 +354,13 @@ class TestQuotaRegistry:
assert len(reg.list_dimensions()) == len(QuotaDimension)
def test_list_tiers(self):
"""个套餐等级."""
"""个套餐等级."""
reg = QuotaRegistry()
tiers = reg.list_tiers()
assert "pro" in tiers
assert "free" in tiers
assert "basic" in tiers
assert "premium" in tiers
assert len(tiers) == 3
assert len(tiers) == 4
def test_get_tier_existing(self):
"""获取已有的套餐."""
@@ -370,7 +370,7 @@ class TestQuotaRegistry:
assert tier.name == "free"
def test_get_tier_nonexistent(self):
"""获取不存在的套餐返回 None."""
"""不存在的套餐返回 None"""
reg = QuotaRegistry()
assert reg.get_tier("enterprise") is None
@@ -380,7 +380,7 @@ class TestQuotaRegistry:
assert reg.get_limit("free", QuotaDimension.STORAGE_GB) == 2
def test_get_limit_nonexistent_plan(self):
"""不存在的套餐返回 0."""
"""不存在的套餐 fallback 到 free 配额"""
reg = QuotaRegistry()
assert reg.get_limit("enterprise", QuotaDimension.STORAGE_GB) == 0
+29 -1
View File
@@ -231,6 +231,7 @@ class TestQuotaRegistry:
assert tier.name == "free"
def test_get_tier_unknown_returns_none(self):
"""未知套餐返回 None"""
reg = QuotaRegistry()
assert reg.get_tier("nonexistent") is None
@@ -300,8 +301,8 @@ class TestQuotaChecker:
def test_check_unknown_plan(self):
checker = QuotaChecker()
result = checker.check("unknown", "storage_gb", 1.0)
assert result.allowed is False
assert result.limit == 0
assert not result.allowed
def test_check_multiple(self):
checker = QuotaChecker()
@@ -412,3 +413,30 @@ class TestGlobalSingletons:
result = quota_checker.check("free", "storage_gb", 1.0)
assert result.allowed is True
assert result.limit == 2
class TestProTier:
"""Pro 套餐专项测试"""
def test_pro_tier_exists(self):
"""pro 套餐存在于 QUOTA_TIERS"""
from packages.domain.quota import QUOTA_TIERS
assert "pro" in QUOTA_TIERS
def test_pro_tier_same_as_premium(self):
"""pro 套餐配额与 premium 完全一致"""
from packages.domain.quota import QUOTA_TIERS
pro = QUOTA_TIERS["pro"]
premium = QUOTA_TIERS["premium"]
assert pro.limits == premium.limits
def test_pro_tier_get_limit(self):
"""pro 套餐各维度配额正确"""
reg = QuotaRegistry()
assert reg.get_limit("pro", "storage_gb") == 100
assert reg.get_limit("pro", "videos_per_month") == 100
assert reg.get_limit("pro", "max_concurrent") == 20
assert reg.get_limit("pro", "max_titles") == 500
assert reg.get_limit("pro", "ai_voice_enabled") == 1
+2 -1
View File
@@ -185,9 +185,10 @@ class TestQuotaRegistry:
reg = QuotaRegistry()
tiers = reg.list_tiers()
assert "free" in tiers
assert "pro" in tiers
assert "basic" in tiers
assert "premium" in tiers
assert len(tiers) == 3
assert len(tiers) == 4
def test_get_tier_existing(self):
reg = QuotaRegistry()