feat(server/membership): add ai_points plan policy fields
Introduces ai_points_monthly and ai_point_base_chars on the membership plan policy, exposes them through TenantPlanAccess and MembershipInfo, and seeds Pro with a 1500-point monthly allowance across the local, server, and deploy configs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -145,6 +145,8 @@ type MembershipPlanConfig struct {
|
||||
Name string `mapstructure:"name"`
|
||||
ArticleGeneration int `mapstructure:"article_generation"`
|
||||
ArticleQuotaCycle string `mapstructure:"article_quota_cycle"`
|
||||
AIPointsMonthly int `mapstructure:"ai_points_monthly"`
|
||||
AIPointBaseChars int `mapstructure:"ai_point_base_chars"`
|
||||
ImageStorageBytes int64 `mapstructure:"image_storage_bytes"`
|
||||
CompanyLimit int `mapstructure:"company_limit"`
|
||||
SubscriptionDuration time.Duration `mapstructure:"subscription_duration"`
|
||||
@@ -154,6 +156,8 @@ type MembershipPlanConfig struct {
|
||||
type PlanQuotaPolicy struct {
|
||||
ArticleGeneration int `json:"article_generation"`
|
||||
ArticleQuotaCycle string `json:"article_quota_cycle"`
|
||||
AIPointsMonthly int `json:"ai_points_monthly"`
|
||||
AIPointBaseChars int `json:"ai_point_base_chars"`
|
||||
ImageStorageBytes int64 `json:"image_storage_bytes"`
|
||||
BrandLimit int `json:"brand_limit"`
|
||||
SubscriptionDurationSecs int64 `json:"subscription_duration_seconds,omitempty"`
|
||||
@@ -174,6 +178,8 @@ func (p MembershipPlanConfig) QuotaPolicy() PlanQuotaPolicy {
|
||||
policy := PlanQuotaPolicy{
|
||||
ArticleGeneration: maxInt(p.ArticleGeneration, 0),
|
||||
ArticleQuotaCycle: normalizeArticleQuotaCycle(p.ArticleQuotaCycle, p.Code),
|
||||
AIPointsMonthly: maxInt(p.AIPointsMonthly, 0),
|
||||
AIPointBaseChars: normalizeAIPointBaseChars(p.AIPointBaseChars),
|
||||
ImageStorageBytes: maxInt64(p.ImageStorageBytes, 0),
|
||||
BrandLimit: maxInt(p.CompanyLimit, 0),
|
||||
ContactAdminOnExpiry: p.ContactAdminOnExpiry,
|
||||
@@ -695,6 +701,10 @@ func normalizeMembershipConfig(cfg *MembershipConfig) {
|
||||
if plan.ArticleGeneration < 0 {
|
||||
plan.ArticleGeneration = 0
|
||||
}
|
||||
if plan.AIPointsMonthly < 0 {
|
||||
plan.AIPointsMonthly = 0
|
||||
}
|
||||
plan.AIPointBaseChars = normalizeAIPointBaseChars(plan.AIPointBaseChars)
|
||||
if plan.ImageStorageBytes < 0 {
|
||||
plan.ImageStorageBytes = 0
|
||||
}
|
||||
@@ -725,6 +735,7 @@ func defaultMembershipPlans() []MembershipPlanConfig {
|
||||
Name: "Free",
|
||||
ArticleGeneration: 3,
|
||||
ArticleQuotaCycle: ArticleQuotaCycleLifetime,
|
||||
AIPointBaseChars: 1000,
|
||||
ImageStorageBytes: 10 * 1024 * 1024,
|
||||
CompanyLimit: 1,
|
||||
SubscriptionDuration: 72 * time.Hour,
|
||||
@@ -735,6 +746,7 @@ func defaultMembershipPlans() []MembershipPlanConfig {
|
||||
Name: "Plus",
|
||||
ArticleGeneration: 200,
|
||||
ArticleQuotaCycle: ArticleQuotaCycleMonthly,
|
||||
AIPointBaseChars: 1000,
|
||||
ImageStorageBytes: 500 * 1024 * 1024,
|
||||
CompanyLimit: 1,
|
||||
SubscriptionDuration: 720 * time.Hour,
|
||||
@@ -744,6 +756,8 @@ func defaultMembershipPlans() []MembershipPlanConfig {
|
||||
Name: "Pro",
|
||||
ArticleGeneration: 400,
|
||||
ArticleQuotaCycle: ArticleQuotaCycleMonthly,
|
||||
AIPointsMonthly: 1500,
|
||||
AIPointBaseChars: 1000,
|
||||
ImageStorageBytes: 1024 * 1024 * 1024,
|
||||
CompanyLimit: 2,
|
||||
SubscriptionDuration: 720 * time.Hour,
|
||||
@@ -751,6 +765,13 @@ func defaultMembershipPlans() []MembershipPlanConfig {
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeAIPointBaseChars(value int) int {
|
||||
if value <= 0 {
|
||||
return 1000
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func normalizeArticleQuotaCycle(value, planCode string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||
case ArticleQuotaCycleLifetime:
|
||||
|
||||
@@ -237,6 +237,12 @@ membership: {}
|
||||
if pro.ArticleGeneration != 400 {
|
||||
t.Fatalf("expected pro article generation 400, got %d", pro.ArticleGeneration)
|
||||
}
|
||||
if pro.AIPointsMonthly != 1500 {
|
||||
t.Fatalf("expected pro ai points 1500, got %d", pro.AIPointsMonthly)
|
||||
}
|
||||
if pro.AIPointBaseChars != 1000 {
|
||||
t.Fatalf("expected pro ai point base chars 1000, got %d", pro.AIPointBaseChars)
|
||||
}
|
||||
if pro.CompanyLimit != 2 {
|
||||
t.Fatalf("expected pro company limit 2, got %d", pro.CompanyLimit)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user