feat(billing): activate real-time quota enforcement pipeline
- Wire relay handler to increment_usage() for JSON responses (tokens + relay_requests)
- Wire relay handler to increment_dimension("relay_requests") for SSE streams
- Add increment_dimension() function for hand_executions/pipeline_runs dimensions
- Schedule AggregateUsageWorker hourly for reconciliation (run_on_start=true)
- Mount mock payment routes in dev mode (ZCLAW_SAAS_DEV=true)
Previously the quota middleware always allowed requests because usage
counters were never incremented. Now relay requests update billing_usage_quotas
in real-time, with the aggregator providing hourly reconciliation.
This commit is contained in:
@@ -146,7 +146,10 @@ pub async fn get_or_create_usage(pool: &PgPool, account_id: &str) -> SaasResult<
|
||||
Ok(usage)
|
||||
}
|
||||
|
||||
/// 增加用量计数
|
||||
/// 增加用量计数(Relay 请求:tokens + relay_requests +1)
|
||||
///
|
||||
/// 在 relay handler 响应成功后直接调用,实现实时配额更新。
|
||||
/// 聚合器 `AggregateUsageWorker` 每小时做一次对账修正。
|
||||
pub async fn increment_usage(
|
||||
pool: &PgPool,
|
||||
account_id: &str,
|
||||
@@ -170,6 +173,39 @@ pub async fn increment_usage(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 增加单一维度用量计数(hand_executions / pipeline_runs / relay_requests)
|
||||
///
|
||||
/// 使用静态 SQL 分支(白名单),避免动态列名注入风险。
|
||||
pub async fn increment_dimension(
|
||||
pool: &PgPool,
|
||||
account_id: &str,
|
||||
dimension: &str,
|
||||
) -> SaasResult<()> {
|
||||
let usage = get_or_create_usage(pool, account_id).await?;
|
||||
|
||||
match dimension {
|
||||
"relay_requests" => {
|
||||
sqlx::query(
|
||||
"UPDATE billing_usage_quotas SET relay_requests = relay_requests + 1, updated_at = NOW() WHERE id = $1"
|
||||
).bind(&usage.id).execute(pool).await?;
|
||||
}
|
||||
"hand_executions" => {
|
||||
sqlx::query(
|
||||
"UPDATE billing_usage_quotas SET hand_executions = hand_executions + 1, updated_at = NOW() WHERE id = $1"
|
||||
).bind(&usage.id).execute(pool).await?;
|
||||
}
|
||||
"pipeline_runs" => {
|
||||
sqlx::query(
|
||||
"UPDATE billing_usage_quotas SET pipeline_runs = pipeline_runs + 1, updated_at = NOW() WHERE id = $1"
|
||||
).bind(&usage.id).execute(pool).await?;
|
||||
}
|
||||
_ => return Err(crate::error::SaasError::InvalidInput(
|
||||
format!("Unknown usage dimension: {}", dimension)
|
||||
)),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 检查用量配额
|
||||
pub async fn check_quota(
|
||||
pool: &PgPool,
|
||||
|
||||
Reference in New Issue
Block a user