feat(gateway/data): 新增 usage 关键词搜索、缓存命中摘要、结算成本摘要及 dashboard 聚合查询能力

- 新增 UsageAuditKeywordSearchQuery,支持多关键词、用户名、API Key 交叉过滤
- 新增 UsageCacheHitSummaryQuery/StoredUsageCacheHitSummary,统计请求缓存命中率
- 新增 UsageSettledCostSummaryQuery/StoredUsageSettledCostSummary,汇总结算成本
- 新增 UsageDashboardSummaryQuery、UsageBreakdownSummaryQuery 等 dashboard 聚合类型
- SQL 层实现对应查询方法,含 list_by_ids、list_usage_audits_by_keyword_search、count_usage_audits_by_keyword_search
- 将上述能力通过 GatewayDataState / AdminAppState 暴露给 handler 层
- user_me_usage 及 dashboard_filters 切换为新查询接口,移除旧的内存过滤逻辑
- 同步更新 memory 层及测试
This commit is contained in:
fawney19
2026-04-17 01:00:38 +08:00
parent 6e5af5ef70
commit 1e0bc61526
48 changed files with 5241 additions and 1162 deletions
@@ -4,7 +4,6 @@ use super::route_filters::{
parse_admin_monitoring_limit, parse_admin_monitoring_offset,
parse_admin_monitoring_username_filter,
};
use super::usage_helpers::admin_monitoring_usage_is_error;
use crate::constants::INTERNAL_GATEWAY_PATH_PREFIXES;
use crate::handlers::admin::request::{AdminAppState, AdminRequestContext};
use crate::query::monitoring as monitoring_query;
@@ -16,7 +15,9 @@ use aether_admin::observability::monitoring::{
build_admin_monitoring_system_status_payload_response,
build_admin_monitoring_user_behavior_payload_response,
};
use aether_data_contracts::repository::usage::UsageAuditListQuery;
use aether_data_contracts::repository::usage::{
UsageAuditSummaryQuery, UsageMonitoringErrorCountQuery,
};
use axum::{body::Body, response::Response};
pub(super) async fn build_admin_monitoring_audit_logs_response(
@@ -196,30 +197,27 @@ pub(super) async fn build_admin_monitoring_system_status_response(
.saturating_add(standalone_api_key_summary.active);
let today_usage = state
.list_usage_audits(&UsageAuditListQuery {
created_from_unix_secs: Some(today_start.timestamp().max(0) as u64),
..Default::default()
.summarize_usage_audits(&UsageAuditSummaryQuery {
created_from_unix_secs: today_start.timestamp().max(0) as u64,
created_until_unix_secs: now_unix_secs.saturating_add(1),
user_id: None,
provider_name: None,
model: None,
})
.await?;
let today_requests = today_usage.len();
let today_tokens = today_usage
.iter()
.map(|item| item.total_tokens)
.sum::<u64>();
let today_cost = today_usage
.iter()
.map(|item| item.total_cost_usd)
.sum::<f64>();
let today_requests = usize::try_from(today_usage.total_requests).unwrap_or(usize::MAX);
let today_tokens = today_usage.recorded_total_tokens;
let today_cost = today_usage.total_cost_usd;
let recent_errors = state
.list_usage_audits(&UsageAuditListQuery {
created_from_unix_secs: Some(recent_error_from.timestamp().max(0) as u64),
..Default::default()
})
.await?
.into_iter()
.filter(admin_monitoring_usage_is_error)
.count();
let recent_errors = usize::try_from(
state
.count_monitoring_usage_errors(&UsageMonitoringErrorCountQuery {
created_from_unix_secs: recent_error_from.timestamp().max(0) as u64,
created_until_unix_secs: now_unix_secs.saturating_add(1),
})
.await?,
)
.unwrap_or(usize::MAX);
let tunnel = state.tunnel.stats();
Ok(build_admin_monitoring_system_status_payload_response(