mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
fix(usage): 完善 cache-affinity 时间线的用户名回退与模型空值兜底
- 当 auth 用户查询失败时回退到历史 username,避免时间线丢失用户信息 - SQL 投影对 usage.model 使用 COALESCE 兜底空值 - IntervalTimelineCard 在刷新间隔为 0 时跳过定时刷新 - Usage 页面关闭时间线卡片的自动轮询
This commit is contained in:
@@ -86,11 +86,20 @@ pub(super) async fn build_admin_usage_cache_affinity_interval_timeline_response(
|
||||
|
||||
if include_user_info && user_id.is_none() {
|
||||
let user_ids: Vec<_> = grouped.keys().cloned().collect();
|
||||
let user_map = load_usage_cache_affinity_usernames(state, &user_ids).await?;
|
||||
for (user_id, username) in user_map {
|
||||
usernames_by_user_id.insert(user_id, username);
|
||||
let mut should_use_legacy_usernames = !state.has_auth_user_data_reader();
|
||||
match load_usage_cache_affinity_usernames(state, &user_ids).await {
|
||||
Ok(user_map) => {
|
||||
usernames_by_user_id.extend(user_map);
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
error = ?err,
|
||||
"admin usage cache affinity interval timeline user lookup failed"
|
||||
);
|
||||
should_use_legacy_usernames = true;
|
||||
}
|
||||
}
|
||||
if !state.has_auth_user_data_reader() {
|
||||
if should_use_legacy_usernames {
|
||||
for (user_id, username) in legacy_usernames_by_user_id {
|
||||
usernames_by_user_id.entry(user_id).or_insert(username);
|
||||
}
|
||||
|
||||
@@ -2605,6 +2605,90 @@ async fn gateway_handles_admin_usage_cache_affinity_interval_timeline_without_le
|
||||
upstream_handle.abort();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn gateway_handles_admin_usage_cache_affinity_interval_timeline_with_legacy_username_fallback_when_auth_user_lookup_fails(
|
||||
) {
|
||||
let (_upstream_url, upstream_hits, upstream_handle) =
|
||||
start_usage_upstream("/api/admin/usage/cache-affinity/interval-timeline").await;
|
||||
let mut usage_one = sample_usage_row(
|
||||
"usage-auth-lookup-fail-1",
|
||||
"req-auth-lookup-fail-1",
|
||||
Some("user-1"),
|
||||
Some("key-1"),
|
||||
Some("primary"),
|
||||
"OpenAI",
|
||||
"gpt-5",
|
||||
"completed",
|
||||
10,
|
||||
2,
|
||||
0.01,
|
||||
0.012,
|
||||
recent_unix_secs(55),
|
||||
);
|
||||
usage_one.username = Some("stale-alice".to_string());
|
||||
let mut usage_two = sample_usage_row(
|
||||
"usage-auth-lookup-fail-2",
|
||||
"req-auth-lookup-fail-2",
|
||||
Some("user-1"),
|
||||
Some("key-1"),
|
||||
Some("primary"),
|
||||
"OpenAI",
|
||||
"gpt-5",
|
||||
"completed",
|
||||
12,
|
||||
3,
|
||||
0.01,
|
||||
0.012,
|
||||
recent_unix_secs(50),
|
||||
);
|
||||
usage_two.username = Some("stale-alice".to_string());
|
||||
|
||||
let usage_repository = Arc::new(InMemoryUsageReadRepository::seed(vec![
|
||||
usage_one, usage_two,
|
||||
]));
|
||||
let gateway = build_router_with_state(
|
||||
AppState::new()
|
||||
.expect("gateway should build")
|
||||
.with_data_state_for_tests(GatewayDataState::with_usage_reader_for_tests(
|
||||
usage_repository,
|
||||
))
|
||||
.with_auth_users_for_tests([StoredUserAuthRecord {
|
||||
id: "user-1".to_string(),
|
||||
email: None,
|
||||
email_verified: false,
|
||||
username: " ".to_string(),
|
||||
password_hash: None,
|
||||
role: "user".to_string(),
|
||||
auth_source: "local".to_string(),
|
||||
allowed_providers: None,
|
||||
allowed_api_formats: None,
|
||||
allowed_models: None,
|
||||
is_active: true,
|
||||
is_deleted: false,
|
||||
created_at: None,
|
||||
last_login_at: None,
|
||||
}]),
|
||||
);
|
||||
let (gateway_url, gateway_handle) = start_server(gateway).await;
|
||||
|
||||
let response = admin_request(reqwest::Client::new().get(format!(
|
||||
"{gateway_url}/api/admin/usage/cache-affinity/interval-timeline?hours=24&limit=100&include_user_info=true"
|
||||
)))
|
||||
.send()
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let payload: serde_json::Value = response.json().await.expect("json body should parse");
|
||||
assert_eq!(payload["total_points"], 1);
|
||||
assert_eq!(payload["points"][0]["user_id"], "user-1");
|
||||
assert_eq!(payload["users"]["user-1"], "stale-alice");
|
||||
assert_eq!(*upstream_hits.lock().expect("mutex should lock"), 0);
|
||||
|
||||
gateway_handle.abort();
|
||||
upstream_handle.abort();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn gateway_handles_admin_usage_cache_affinity_ttl_analysis_locally_with_trusted_admin_principal(
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user