Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

阶段 5-A:计费与用量设计

auth 多租户 IDaaS 商业化第一块——按租户用量计费、收钱、配额执行。 目标:能知道每个租户用了多少、能按套餐收费、超额拦截。 本文档为可追溯设计依据,定稿于 2026-07-10。整体路线见 docs/TENANT_PHASE1.md


0. 现有可复用基础

  • tenants 表已有 max_users(配额字段雏形)。
  • audit_logs 已记录大部分事件(登录/验证/CRUD),可作用量数据源。
  • enforce_user_quota / enforce_tenant_usable(tenant.rs)是配额校验的现成模式,可泛化。
  • RBAC + 中间件 + RequestMeta.tenant_id 已就绪,埋点能拿到 tenant 上下文。

1. 用量采集

原则:埋点不阻塞主链路(fire-and-forget 到内存 channel,后台批写)。

采集指标

metric含义采集点
mau月活用户(按 user 去重)login / OAuth grant 成功
api_calls受保护 API 调用数auth_middleware(每次带 token 请求)
tokens_issuedtoken 签发数login / refresh / OAuth grant
oauth_loginsOAuth 授权次数/oauth/token 各 grant

存储:Redis 实时计数 + 定时落库

  • Redis:HINCRBY usage:{tenant_id}:{yyyymmdd} {metric} 1(hash,每天一行)。
  • MAU 去重:SADD usage:mau:{tenant_id}:{yyyymm} {user_id}(set,月末 SCARD)。
  • 定时任务(每小时):Redis → usage_daily 表(持久化 + 清 Redis 旧 key)。

usage_daily 表

CREATE TABLE usage_daily (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    tenant_id VARCHAR(50) NOT NULL,
    date CHAR(8) NOT NULL,                  -- yyyymmdd
    api_calls INTEGER DEFAULT 0,
    tokens_issued INTEGER DEFAULT 0,
    oauth_logins INTEGER DEFAULT 0,
    mau INTEGER DEFAULT 0,                  -- 仅当天去重用户数(月聚合另算)
    created_at DATETIME DEFAULT (datetime('now','+8 hours')),
    UNIQUE(tenant_id, date)
);
CREATE INDEX idx_usage_tenant_date ON usage_daily(tenant_id, date);

2. 套餐与订阅

CREATE TABLE plans (
    id VARCHAR(50) PRIMARY KEY,             -- free / pro / enterprise
    name VARCHAR(100) NOT NULL,
    price_monthly INTEGER NOT NULL,         -- 单位:分
    max_users INTEGER NOT NULL,             -- 0 = 不限
    max_api_calls_monthly INTEGER NOT NULL,
    max_oauth_clients INTEGER NOT NULL,
    features TEXT DEFAULT '[]',             -- JSON: ["sso","branding","audit_export",...]
    is_active BOOLEAN DEFAULT TRUE,
    sort_order INTEGER DEFAULT 0
);

CREATE TABLE subscriptions (
    tenant_id VARCHAR(50) PRIMARY KEY REFERENCES tenants(id),
    plan_id VARCHAR(50) NOT NULL REFERENCES plans(id),
    status VARCHAR(20) NOT NULL,            -- trialing / active / past_due / canceled
    current_period_end DATETIME,            -- 当前周期结束(到期续费/降级)
    stripe_customer_id VARCHAR(100),        -- 支付网关客户 ID
    stripe_subscription_id VARCHAR(100),
    created_at DATETIME DEFAULT (datetime('now','+8 hours')),
    updated_at DATETIME DEFAULT (datetime('now','+8 hours'))
);

plans 预置(db.rs seed):free(0 元,限 10 用户/1k 调用)、pro(99 元/月,1k 用户/100k 调用)、enterprise(999 元/月,不限 + SSO + 品牌)。


3. 配额执行(泛化 enforce_user_quota)

tenant.rsenforce_user_quota 泛化:

#![allow(unused)]
fn main() {
async fn enforce_quota(db: &Database, tenant_id: &str, metric: QuotaMetric) -> Result<()>
// QuotaMetric: Users / OauthClients / ApiCalls(monthly) / Storage
}
  • create_user → enforce_quota(Users)(已有,重构)。
  • create_oauth_client → enforce_quota(OauthClients)。
  • API 调用(auth_middleware)→ 月度 api_calls 计数(Redis),超额返 429(限流,不拒服务)或降级提示。

4. 计费账单 + 支付

推荐:Stripe Billing(国际,最省事)

  • 创建 Stripe Customer(租户首次订阅)→ Subscription(按 plan)→ Invoice(周期账单)。
  • Webhook invoice.paid / invoice.payment_failed 同步 subscriptions.status(active/past_due)。
  • 前端用 Stripe Checkout / Customer Portal(自助管理订阅/换卡),不用自建支付表单。
  • 国内客户:Stripe 支持支付宝(需 Stripe 账户开通),或单独对接支付宝微信(自建,工作量大)。

欠费处理

  • past_due(支付失败):宽限期(3-7 天)→ 仍失败降级(功能只读/隐藏 SSO)→ 不删数据。
  • canceled:数据保留 N 天(可恢复)→ 到期删除(合规)。

自建账单(备选,国内为主)

  • invoices 表(金额/周期/状态)+ 支付宝微信支付接口 + 手动对账。工作量大,不推荐除非纯国内。

5. API

方法路径权限说明
GET/api/tenant/usage租户管理员本租户用量(当月 MAU/调用/token + 趋势)
GET/api/tenant/subscription租户管理员当前套餐/周期/账单历史
POST/api/tenant/subscribe租户管理员选套餐 → 返 Stripe Checkout session URL
GET/api/admin/tenants/:id/usage平台 admin指定租户用量
GET/api/admin/billing/overview平台 admin全平台收入/用量总览
POST/api/webhooks/stripe公开(签名校验)Stripe 事件回调(订阅状态同步)

/api/tenant/* 是租户管理员域(持 system:* 本租户权限 + 不持 admin:manage),跟 /api/admin/*(平台 admin)区分。


6. 前端

  • 租户管理页加「用量」tab:MAU/调用/token 图表(echarts 或简易柱状)。
  • 租户管理员(需建 role_type=custom 的租户 admin 角色):
    • 「订阅与账单」页:当前套餐 + 用量进度条 + 账单历史 + 升级按钮(跳 Stripe Portal)。
  • 平台 admin「计费总览」页:各租户收入/用量/到期提醒。

7. 计价模型(待你定,影响 plans 配置)

模型适合复杂度
固定套餐(free/pro/enterprise 月费)用户量可控、功能分层低(plans 表即可)
按 MAU(每活跃用户月费)To SaaS 开发者、用量波动大中(MAU 精确去重 + 按量结算)
按调用(每万次 API)API 优先、机器调用多中(调用计数 + 按量)
混合(套餐 + 超额按量)兼顾稳定 + 弹性中高

建议起步:固定套餐(最简,3 档),跑通后再加超额按量(pro 套餐含 X 调用,超出按量)。


8. 成本(1 人估)

子项工作量
用量采集(埋点 + Redis + usage_daily + 定时任务)~1 周
plans/subscriptions 表 + 配额泛化~0.5 周
Stripe Billing 集成(Customer/Subscription/Webhook)~1.5 周
API(usage/subscription/webhook)~0.5 周
前端(用量图表 + 订阅页 + 总览)~1 周
合计~4-4.5 周

难点:Stripe Billing 状态机(trialing/active/past_due/canceled + webhook 幂等);用量采集性能(异步队列不阻塞主链路)。


9. 增量落地(每步可独立上线)

  1. 用量采集 + usage_daily(先看清谁用了多少,不收费)—— 1 周。先做这步,决策有数据支撑。
  2. plans/subscriptions + 配额泛化(能限超额,免费 + 限额)—— 0.5 周。
  3. Stripe Billing(真能收钱)—— 1.5 周。
  4. 前端用量/账单页(自助体验)—— 1 周。

10. 不在本阶段做(边界)

  • ❌ SSO 企业版(阶段 5-B 另设计:Inbound OIDC / SAML)。
  • ❌ 合规认证(SOC 2 / ISO 27001,流程非代码,数月 + 第三方)。
  • ❌ 计价模型动态配置(起步用 plans 表硬编码三档即可)。
  • ❌ 退款/税务/多币种(Stripe 默认能力,不自建)。