refactor: 大规模模块拆分与重组,新增 aether-admin crate

- 新建独立 aether-admin crate 承载 admin 相关共享契约与纯辅助函数
- 拆分 ai_pipeline 下 kiro/private_envelope/conversion/planner 等大文件为子模块目录
- 重组 admin handlers 各业务域(billing/oauth/provider/system/users 等)为目录结构,移除 shared.rs/builders.rs 等反模式
- 移除 ai_pipeline runtime adapters 旧实现(claude/openai/gemini/kiro/vertex/antigravity 等),改由 provider transport 统一承载
- 移除 control_facade/execution_facade/auth_snapshot_facade 等冗余 facade 层
- 拆分 query/billing 与 query/monitoring 模块、state/runtime/payments 与 security 模块
- 扩展架构测试覆盖 admin_billing/admin_model/admin_users 等新模块
- 删除 docs/architecture/refactor-execution-plan.md 已完成的执行计划文档
This commit is contained in:
fawney19
2026-04-09 00:10:38 +08:00
parent 4fb9882b54
commit 4fc95adfb9
663 changed files with 48471 additions and 40232 deletions
@@ -1,10 +1,11 @@
use crate::handlers::admin::provider::shared::payloads::{
OAUTH_ACCOUNT_BLOCK_PREFIX, OAUTH_REFRESH_FAILED_PREFIX,
};
use crate::{AppState, GatewayError};
use crate::handlers::admin::request::{AdminAppState, AdminGatewayProviderTransportSnapshot};
use crate::GatewayError;
use aether_admin::provider::quota as admin_provider_quota_pure;
use aether_contracts::{ExecutionPlan, ExecutionResult};
use aether_data_contracts::repository::provider_catalog::StoredProviderCatalogKey;
use std::collections::BTreeSet;
use std::time::{SystemTime, UNIX_EPOCH};
use tracing::warn;
@@ -14,59 +15,27 @@ pub(super) enum ProviderQuotaExecutionOutcome {
}
pub(super) fn provider_auto_remove_banned_keys(config: Option<&serde_json::Value>) -> bool {
config
.and_then(|value| value.get("pool_advanced"))
.and_then(serde_json::Value::as_object)
.and_then(|object| object.get("auto_remove_banned_keys"))
.and_then(serde_json::Value::as_bool)
.unwrap_or(false)
admin_provider_quota_pure::provider_auto_remove_banned_keys(config)
}
pub(super) fn should_auto_remove_structured_reason(reason: Option<&str>) -> bool {
reason
.map(str::trim)
.is_some_and(|value| value.starts_with(OAUTH_ACCOUNT_BLOCK_PREFIX))
admin_provider_quota_pure::should_auto_remove_structured_reason(reason)
}
pub(crate) fn normalize_string_id_list(values: Option<Vec<String>>) -> Option<Vec<String>> {
let mut out = Vec::new();
let mut seen = BTreeSet::new();
for value in values.into_iter().flatten() {
let trimmed = value.trim();
if trimmed.is_empty() || !seen.insert(trimmed.to_string()) {
continue;
}
out.push(trimmed.to_string());
}
(!out.is_empty()).then_some(out)
admin_provider_quota_pure::normalize_string_id_list(values)
}
pub(super) fn coerce_json_u64(value: &serde_json::Value) -> Option<u64> {
match value {
serde_json::Value::Number(number) => number.as_u64(),
serde_json::Value::String(text) => text.trim().parse::<u64>().ok(),
_ => None,
}
admin_provider_quota_pure::coerce_json_u64(value)
}
pub(super) fn coerce_json_f64(value: &serde_json::Value) -> Option<f64> {
match value {
serde_json::Value::Number(number) => number.as_f64(),
serde_json::Value::String(text) => text.trim().parse::<f64>().ok(),
_ => None,
}
admin_provider_quota_pure::coerce_json_f64(value)
}
pub(super) fn coerce_json_bool(value: &serde_json::Value) -> Option<bool> {
match value {
serde_json::Value::Bool(value) => Some(*value),
serde_json::Value::String(text) => match text.trim().to_ascii_lowercase().as_str() {
"true" | "1" => Some(true),
"false" | "0" => Some(false),
_ => None,
},
_ => None,
}
admin_provider_quota_pure::coerce_json_bool(value)
}
fn merge_upstream_metadata(
@@ -86,65 +55,21 @@ fn merge_upstream_metadata(
}
pub(super) fn extract_execution_error_message(result: &ExecutionResult) -> Option<String> {
if let Some(body_json) = result
.body
.as_ref()
.and_then(|body| body.json_body.as_ref())
.and_then(serde_json::Value::as_object)
{
if let Some(error) = body_json
.get("error")
.and_then(serde_json::Value::as_object)
{
if let Some(message) = error.get("message").and_then(serde_json::Value::as_str) {
let trimmed = message.trim();
if !trimmed.is_empty() {
return Some(trimmed.to_string());
}
}
}
if let Some(message) = body_json.get("message").and_then(serde_json::Value::as_str) {
let trimmed = message.trim();
if !trimmed.is_empty() {
return Some(trimmed.to_string());
}
}
}
result
.error
.as_ref()
.map(|error| error.message.trim().to_string())
.filter(|value| !value.is_empty())
admin_provider_quota_pure::extract_execution_error_message(result)
}
pub(super) fn quota_refresh_success_invalid_state(
key: &StoredProviderCatalogKey,
) -> (Option<u64>, Option<String>) {
let current_reason = key
.oauth_invalid_reason
.as_deref()
.map(str::trim)
.unwrap_or_default();
if current_reason.starts_with(OAUTH_REFRESH_FAILED_PREFIX) {
return (
key.oauth_invalid_at_unix_secs,
(!current_reason.is_empty()).then_some(current_reason.to_string()),
);
}
(None, None)
admin_provider_quota_pure::quota_refresh_success_invalid_state(key)
}
pub(super) fn coerce_json_string(value: Option<&serde_json::Value>) -> Option<String> {
value
.and_then(serde_json::Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned)
admin_provider_quota_pure::coerce_json_string(value)
}
pub(crate) async fn persist_provider_quota_refresh_state(
state: &AppState,
state: &AdminAppState<'_>,
key_id: &str,
metadata_update: Option<&serde_json::Value>,
oauth_invalid_at_unix_secs: Option<u64>,
@@ -182,12 +107,12 @@ pub(crate) async fn persist_provider_quota_refresh_state(
}
pub(super) async fn execute_provider_quota_plan(
state: &AppState,
transport: &crate::provider_transport::GatewayProviderTransportSnapshot,
state: &AdminAppState<'_>,
transport: &AdminGatewayProviderTransportSnapshot,
plan: ExecutionPlan,
quota_kind: &str,
) -> Result<ProviderQuotaExecutionOutcome, GatewayError> {
match crate::execution_runtime::execute_execution_runtime_sync_plan(state, None, &plan).await {
match state.execute_execution_runtime_sync_plan(None, &plan).await {
Ok(result) => Ok(ProviderQuotaExecutionOutcome::Response(result)),
Err(err) => {
let error = match err {