feat(ai): 添加 Provider 管理 API 端点
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

GET /ai/providers — 列出已注册提供商
GET /ai/providers/health — 各提供商健康状态
GET /ai/quota/summary — 租户配额使用摘要
This commit is contained in:
iven
2026-05-05 15:19:49 +08:00
parent a16e86bf04
commit 50b9e8d683
2 changed files with 67 additions and 0 deletions

View File

@@ -487,6 +487,61 @@ where
Ok(Json(ApiResponse::ok(result)))
}
// === Provider 管理 ===
pub async fn provider_health<S>(
State(state): State<AiState>,
Extension(ctx): Extension<TenantContext>,
) -> Result<Json<ApiResponse<serde_json::Value>>, erp_core::error::AppError>
where
AiState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "ai.analysis.list")?;
let statuses = state.provider_registry.health_check_all().await;
let result: serde_json::Value = statuses.iter().map(|entry| {
let (name, health) = entry.pair();
serde_json::json!({
"provider": name,
"healthy": health.is_healthy(),
"status": match health {
crate::provider::registry::ProviderHealth::Healthy { last_check } =>
serde_json::json!({"status": "healthy", "last_check": last_check.to_rfc3339()}),
crate::provider::registry::ProviderHealth::Degraded { last_check, error } =>
serde_json::json!({"status": "degraded", "last_check": last_check.to_rfc3339(), "error": error}),
crate::provider::registry::ProviderHealth::Unavailable { since, error } =>
serde_json::json!({"status": "unavailable", "since": since.to_rfc3339(), "error": error}),
},
})
}).collect();
Ok(Json(ApiResponse::ok(result)))
}
pub async fn provider_names<S>(
State(state): State<AiState>,
Extension(ctx): Extension<TenantContext>,
) -> Result<Json<ApiResponse<Vec<String>>>, erp_core::error::AppError>
where
AiState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "ai.analysis.list")?;
Ok(Json(ApiResponse::ok(state.provider_registry.provider_names())))
}
pub async fn quota_summary<S>(
State(state): State<AiState>,
Extension(ctx): Extension<TenantContext>,
) -> Result<Json<ApiResponse<crate::service::quota::QuotaSummary>>, erp_core::error::AppError>
where
AiState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "ai.usage.list")?;
let summary = state.quota.get_usage_summary(ctx.tenant_id).await?;
Ok(Json(ApiResponse::ok(summary)))
}
// === 透析风险评估KDIGO 规则) ===
pub async fn assess_dialysis_risk<S>(

View File

@@ -246,5 +246,17 @@ impl AiModule {
"/ai/dialysis/risk-assessment",
axum::routing::post(crate::handler::assess_dialysis_risk),
)
.route(
"/ai/providers/health",
axum::routing::get(crate::handler::provider_health),
)
.route(
"/ai/providers",
axum::routing::get(crate::handler::provider_names),
)
.route(
"/ai/quota/summary",
axum::routing::get(crate::handler::quota_summary),
)
}
}