mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
fix: 修复账号批量操作快捷多选不生效和今日 Token 不准确 (#328)
* fix(pool): 修复账号批量操作快捷多选不生效 - 解析号池账号列表接口的 quick_selectors 查询参数 - 列表查询复用现有快捷筛选匹配逻辑 - 支持冷却列表与普通列表的快捷筛选后分页 - 更新架构测试中的 pool admin helper 边界断言 * fix(dashboard): 今日 Token 改为按分项汇总口径展示 - /api/dashboard/stats 的 today.tokens 改为 输入+输出+写缓存+读缓存 - “今日 Token”卡片 value 与 subValue 口径保持一致 - 同步更新 dashboard 相关测试断言
This commit is contained in:
@@ -35,8 +35,8 @@ pub(crate) use self::batch_shared::{
|
||||
};
|
||||
pub(crate) use self::support::{
|
||||
admin_pool_provider_id_from_path, parse_admin_pool_page, parse_admin_pool_page_size,
|
||||
parse_admin_pool_search, parse_admin_pool_status_filter, AdminPoolResolveSelectionRequest,
|
||||
ADMIN_POOL_BANNED_KEY_CLEANUP_EMPTY_MESSAGE,
|
||||
parse_admin_pool_quick_selectors, parse_admin_pool_search, parse_admin_pool_status_filter,
|
||||
AdminPoolResolveSelectionRequest, ADMIN_POOL_BANNED_KEY_CLEANUP_EMPTY_MESSAGE,
|
||||
ADMIN_POOL_PROVIDER_CATALOG_READER_UNAVAILABLE_DETAIL,
|
||||
ADMIN_POOL_PROVIDER_CATALOG_WRITER_UNAVAILABLE_DETAIL,
|
||||
};
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use super::{
|
||||
admin_pool_provider_id_from_path, admin_provider_pool_config, build_admin_pool_error_response,
|
||||
parse_admin_pool_page, parse_admin_pool_page_size, parse_admin_pool_search,
|
||||
parse_admin_pool_status_filter, pool_payloads, pool_selection,
|
||||
parse_admin_pool_page, parse_admin_pool_page_size, parse_admin_pool_quick_selectors,
|
||||
parse_admin_pool_search, parse_admin_pool_status_filter, pool_payloads, pool_selection,
|
||||
read_admin_provider_pool_cooldown_key_ids, read_admin_provider_pool_runtime_state,
|
||||
AdminProviderPoolRuntimeState, ProviderCatalogKeyListOrder, ProviderCatalogKeyListQuery,
|
||||
ADMIN_POOL_PROVIDER_CATALOG_READER_UNAVAILABLE_DETAIL,
|
||||
};
|
||||
use crate::handlers::admin::request::{AdminAppState, AdminRequestContext};
|
||||
use crate::GatewayError;
|
||||
use aether_admin::provider::pool as admin_provider_pool_pure;
|
||||
use axum::{
|
||||
body::Body,
|
||||
http,
|
||||
@@ -53,6 +54,9 @@ pub(super) async fn build_admin_pool_list_keys_response(
|
||||
}
|
||||
};
|
||||
let search = parse_admin_pool_search(query).map(|value| value.to_ascii_lowercase());
|
||||
let quick_selectors = admin_provider_pool_pure::admin_pool_sanitize_quick_selectors(
|
||||
parse_admin_pool_quick_selectors(query),
|
||||
);
|
||||
let status = match parse_admin_pool_status_filter(query) {
|
||||
Ok(value) => value,
|
||||
Err(detail) => {
|
||||
@@ -93,10 +97,63 @@ pub(super) async fn build_admin_pool_list_keys_response(
|
||||
};
|
||||
if let Some(keyword) = search.as_ref() {
|
||||
keys.retain(|key| {
|
||||
key.name.to_ascii_lowercase().contains(keyword)
|
||||
|| key.id.to_ascii_lowercase().contains(keyword)
|
||||
pool_selection::admin_pool_matches_search(
|
||||
state,
|
||||
key,
|
||||
&provider.provider_type,
|
||||
Some(keyword),
|
||||
)
|
||||
});
|
||||
}
|
||||
if !quick_selectors.is_empty() {
|
||||
keys.retain(|key| {
|
||||
quick_selectors.iter().all(|selector| {
|
||||
pool_selection::admin_pool_matches_quick_selector(
|
||||
state,
|
||||
key,
|
||||
&provider.provider_type,
|
||||
selector,
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
pool_selection::admin_pool_sort_keys(&mut keys);
|
||||
let total = keys.len();
|
||||
let keys = keys
|
||||
.into_iter()
|
||||
.skip(page_offset)
|
||||
.take(page_size)
|
||||
.collect::<Vec<_>>();
|
||||
(keys, total)
|
||||
} else if !quick_selectors.is_empty() {
|
||||
let mut keys = state
|
||||
.list_provider_catalog_keys_by_provider_ids(std::slice::from_ref(&provider.id))
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|key| match status.as_str() {
|
||||
"active" => key.is_active,
|
||||
"inactive" => !key.is_active,
|
||||
_ => true,
|
||||
})
|
||||
.filter(|key| {
|
||||
pool_selection::admin_pool_matches_search(
|
||||
state,
|
||||
key,
|
||||
&provider.provider_type,
|
||||
search.as_deref(),
|
||||
)
|
||||
})
|
||||
.filter(|key| {
|
||||
quick_selectors.iter().all(|selector| {
|
||||
pool_selection::admin_pool_matches_quick_selector(
|
||||
state,
|
||||
key,
|
||||
&provider.provider_type,
|
||||
selector,
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
pool_selection::admin_pool_sort_keys(&mut keys);
|
||||
let total = keys.len();
|
||||
let keys = keys
|
||||
|
||||
@@ -60,6 +60,18 @@ pub(crate) fn parse_admin_pool_search(query: Option<&str>) -> Option<String> {
|
||||
.filter(|value| !value.is_empty())
|
||||
}
|
||||
|
||||
pub(crate) fn parse_admin_pool_quick_selectors(query: Option<&str>) -> Vec<String> {
|
||||
query_param_value(query, "quick_selectors")
|
||||
.map(|value| {
|
||||
value
|
||||
.split(',')
|
||||
.map(|item| item.trim().to_string())
|
||||
.filter(|item| !item.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) fn parse_admin_pool_status_filter(query: Option<&str>) -> Result<String, String> {
|
||||
let value = query_param_value(query, "status")
|
||||
.unwrap_or_else(|| "all".to_string())
|
||||
|
||||
@@ -213,6 +213,14 @@ fn dashboard_format_today_token_subvalue(totals: &DashboardUsageTotals) -> Strin
|
||||
)
|
||||
}
|
||||
|
||||
fn dashboard_today_token_value_from_subvalue(totals: &DashboardUsageTotals) -> u64 {
|
||||
totals
|
||||
.effective_input_tokens
|
||||
.saturating_add(totals.output_tokens)
|
||||
.saturating_add(totals.cache_creation_tokens)
|
||||
.saturating_add(totals.cache_read_tokens)
|
||||
}
|
||||
|
||||
fn dashboard_parse_tz_offset_minutes(query: Option<&str>) -> Result<i32, String> {
|
||||
query_param_value(query, "tz_offset_minutes")
|
||||
.map(|value| {
|
||||
@@ -1591,9 +1599,10 @@ pub(super) async fn handle_dashboard_stats_get(
|
||||
"cache_creation": period_totals.cache_creation_tokens,
|
||||
"cache_read": period_totals.cache_read_tokens,
|
||||
});
|
||||
let today_token_value = dashboard_today_token_value_from_subvalue(&today_totals);
|
||||
let today_payload = json!({
|
||||
"requests": today_totals.requests,
|
||||
"tokens": today_totals.total_tokens,
|
||||
"tokens": today_token_value,
|
||||
"cost": dashboard_round_f64(today_totals.total_cost_usd, 4),
|
||||
"actual_cost": dashboard_round_f64(today_totals.actual_total_cost_usd, 4),
|
||||
"cache_creation_tokens": today_totals.cache_creation_tokens,
|
||||
@@ -1634,7 +1643,7 @@ pub(super) async fn handle_dashboard_stats_get(
|
||||
},
|
||||
{
|
||||
"name": "今日 Token",
|
||||
"value": dashboard_format_token_compact(today_totals.total_tokens),
|
||||
"value": dashboard_format_token_compact(today_token_value),
|
||||
"subValue": dashboard_format_today_token_subvalue(&today_totals),
|
||||
"icon": "Zap",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user