feat(ai): 添加 Provider 管理 API 端点
GET /ai/providers — 列出已注册提供商 GET /ai/providers/health — 各提供商健康状态 GET /ai/quota/summary — 租户配额使用摘要
This commit is contained in:
@@ -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>(
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user