feat(saas): add billing infrastructure — tables, types, service, handlers
B1.1 Billing database: - 5 tables: billing_plans, billing_subscriptions, billing_invoices, billing_payments, billing_usage_quotas - Seed data: Free(¥0)/Pro(¥49)/Team(¥199) plans - JSONB limits for flexible plan configuration Billing module (crates/zclaw-saas/src/billing/): - types.rs: BillingPlan, Subscription, Invoice, Payment, UsageQuota - service.rs: plan CRUD, subscription lookup, usage tracking, quota check - handlers.rs: REST API (plans list/detail, subscription, usage) - mod.rs: routes registered at /api/v1/billing/* Cargo.toml: added chrono feature to sqlx for DateTime<Utc> support
This commit is contained in:
55
crates/zclaw-saas/src/billing/handlers.rs
Normal file
55
crates/zclaw-saas/src/billing/handlers.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
//! 计费 HTTP 处理器
|
||||
|
||||
use axum::{
|
||||
extract::{Extension, Path, State},
|
||||
Json,
|
||||
};
|
||||
|
||||
use crate::auth::types::AuthContext;
|
||||
use crate::error::SaasResult;
|
||||
use crate::state::AppState;
|
||||
use super::service;
|
||||
use super::types::*;
|
||||
|
||||
/// GET /api/v1/billing/plans — 列出所有活跃计划
|
||||
pub async fn list_plans(
|
||||
State(state): State<AppState>,
|
||||
) -> SaasResult<Json<Vec<BillingPlan>>> {
|
||||
let plans = service::list_plans(&state.db).await?;
|
||||
Ok(Json(plans))
|
||||
}
|
||||
|
||||
/// GET /api/v1/billing/plans/:id — 获取单个计划详情
|
||||
pub async fn get_plan(
|
||||
State(state): State<AppState>,
|
||||
Path(plan_id): Path<String>,
|
||||
) -> SaasResult<Json<BillingPlan>> {
|
||||
let plan = service::get_plan(&state.db, &plan_id).await?
|
||||
.ok_or_else(|| crate::error::SaasError::NotFound("计划不存在".into()))?;
|
||||
Ok(Json(plan))
|
||||
}
|
||||
|
||||
/// GET /api/v1/billing/subscription — 获取当前订阅
|
||||
pub async fn get_subscription(
|
||||
State(state): State<AppState>,
|
||||
Extension(ctx): Extension<AuthContext>,
|
||||
) -> SaasResult<Json<serde_json::Value>> {
|
||||
let plan = service::get_account_plan(&state.db, &ctx.account_id).await?;
|
||||
let sub = service::get_active_subscription(&state.db, &ctx.account_id).await?;
|
||||
let usage = service::get_or_create_usage(&state.db, &ctx.account_id).await?;
|
||||
|
||||
Ok(Json(serde_json::json!({
|
||||
"plan": plan,
|
||||
"subscription": sub,
|
||||
"usage": usage,
|
||||
})))
|
||||
}
|
||||
|
||||
/// GET /api/v1/billing/usage — 获取当月用量
|
||||
pub async fn get_usage(
|
||||
State(state): State<AppState>,
|
||||
Extension(ctx): Extension<AuthContext>,
|
||||
) -> SaasResult<Json<UsageQuota>> {
|
||||
let usage = service::get_or_create_usage(&state.db, &ctx.account_id).await?;
|
||||
Ok(Json(usage))
|
||||
}
|
||||
Reference in New Issue
Block a user