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:
@@ -71,6 +71,8 @@ type MembershipInfo struct {
|
||||
EndAt *string `json:"end_at"`
|
||||
ArticleGenerationLimit int `json:"article_generation_limit"`
|
||||
ArticleQuotaCycle string `json:"article_quota_cycle"`
|
||||
AIPointsMonthly int `json:"ai_points_monthly"`
|
||||
AIPointBaseChars int `json:"ai_point_base_chars"`
|
||||
CompanyLimit int `json:"company_limit"`
|
||||
ImageStorageBytes int64 `json:"image_storage_bytes"`
|
||||
ContactAdminOnExpiry bool `json:"contact_admin_on_expiry"`
|
||||
@@ -271,6 +273,8 @@ func (s *AuthService) loadMembershipInfo(ctx context.Context, tenantID int64) (*
|
||||
EndAt: formatTimeValuePtr(access.EndAt),
|
||||
ArticleGenerationLimit: access.ArticleGenerationLimit(),
|
||||
ArticleQuotaCycle: access.ArticleQuotaCycle(),
|
||||
AIPointsMonthly: access.AIPointsMonthlyLimit(),
|
||||
AIPointBaseChars: access.AIPointBaseChars(),
|
||||
CompanyLimit: access.BrandLimit(),
|
||||
ImageStorageBytes: access.ImageStorageBytes(),
|
||||
ContactAdminOnExpiry: access.Policy.ContactAdminOnExpiry,
|
||||
|
||||
@@ -98,6 +98,38 @@ func (a *TenantPlanAccess) ArticleQuotaCycle() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *TenantPlanAccess) AIPointsMonthlyLimit() int {
|
||||
if a == nil {
|
||||
return 0
|
||||
}
|
||||
return maxInt(a.Policy.AIPointsMonthly, 0)
|
||||
}
|
||||
|
||||
func (a *TenantPlanAccess) AIPointBaseChars() int {
|
||||
if a == nil || a.Policy.AIPointBaseChars <= 0 {
|
||||
return 1000
|
||||
}
|
||||
return a.Policy.AIPointBaseChars
|
||||
}
|
||||
|
||||
func (a *TenantPlanAccess) AIPointsWindow(now time.Time) (time.Time, time.Time, *time.Time) {
|
||||
if a == nil {
|
||||
return time.Time{}, time.Time{}, nil
|
||||
}
|
||||
|
||||
windowStart := a.StartAt.UTC()
|
||||
windowEnd := a.EndAt.UTC()
|
||||
cycleStart, cycleEnd := monthlyCycleBounds(windowStart, now.UTC())
|
||||
if cycleStart.Before(windowStart) {
|
||||
cycleStart = windowStart
|
||||
}
|
||||
if !windowEnd.IsZero() && cycleEnd.After(windowEnd) {
|
||||
cycleEnd = windowEnd
|
||||
}
|
||||
resetAt := cycleEnd
|
||||
return cycleStart, cycleEnd, &resetAt
|
||||
}
|
||||
|
||||
func (a *TenantPlanAccess) ArticleQuotaWindow(now time.Time) (time.Time, time.Time, *time.Time) {
|
||||
if a == nil {
|
||||
return time.Time{}, time.Time{}, nil
|
||||
|
||||
Reference in New Issue
Block a user