fix: complete gemini frontend and usage follow-up

This commit is contained in:
boos
2026-05-19 10:37:29 +08:00
parent 31e95c2634
commit 11144d0697
7 changed files with 121 additions and 49 deletions
+53 -20
View File
@@ -152,7 +152,12 @@ pub async fn list_users(
.fetch_one(&*state.db)
.await?;
let items: Vec<UserResponse> = users.into_iter().map(|u| UserResponse::from(u)).collect();
let mut items = Vec::with_capacity(users.len());
for user in users {
let mut item = UserResponse::from(user);
attach_usage_summary(&state, &mut item).await?;
items.push(item);
}
Ok(Json(json!({
"data": items,
"users": items,
@@ -182,7 +187,9 @@ pub async fn get_user(
.fetch_optional(&*state.db)
.await?
.ok_or_else(|| AppError::NotFound(format!("User {} not found", id)))?;
Ok(Json(json!({ "data": UserResponse::from(user) })))
let mut item = UserResponse::from(user);
attach_usage_summary(&state, &mut item).await?;
Ok(Json(json!({ "data": item })))
}
/// POST /api/admin/users
@@ -228,13 +235,15 @@ pub async fn create_user(
let key = format!("sk-user-{}", uuid::Uuid::new_v4());
let key_name = format!("{} 的默认密钥", payload.username.trim());
sqlx::query("INSERT INTO downstream_api_keys (name, key, user_id, enabled) VALUES (?, ?, ?, ?)")
.bind(&key_name)
.bind(&key)
.bind(user_id)
.bind(true)
.execute(&mut *tx)
.await?;
sqlx::query(
"INSERT INTO downstream_api_keys (name, key, user_id, enabled) VALUES (?, ?, ?, ?)",
)
.bind(&key_name)
.bind(&key)
.bind(user_id)
.bind(true)
.execute(&mut *tx)
.await?;
tx.commit().await?;
@@ -264,11 +273,11 @@ pub async fn get_user_usage(
r#"
SELECT
COUNT(l.id) as total_requests,
SUM(l.cost) as total_cost
COALESCE(SUM(l.cost), 0.0) as total_cost
FROM usage_logs l
JOIN downstream_api_keys k ON l.downstream_key_id = k.id
WHERE k.user_id = ?
"#
"#,
)
.bind(id)
.fetch_one(&*state.db)
@@ -277,11 +286,11 @@ pub async fn get_user_usage(
// Last 7 days cost
let last_7_days_cost: f64 = sqlx::query_scalar(
r#"
SELECT SUM(cost)
SELECT COALESCE(SUM(l.cost), 0.0)
FROM usage_logs l
JOIN downstream_api_keys k ON l.downstream_key_id = k.id
WHERE k.user_id = ? AND l.created_at >= datetime('now', '-7 days')
"#
"#,
)
.bind(id)
.fetch_one(&*state.db)
@@ -299,7 +308,7 @@ pub async fn get_user_usage(
WHERE k.user_id = ? AND l.created_at >= datetime('now', '-30 days')
GROUP BY day
ORDER BY day ASC
"#
"#,
)
.bind(id)
.fetch_all(&*state.db)
@@ -314,7 +323,7 @@ pub async fn get_user_usage(
WHERE k.user_id = ?
ORDER BY l.id DESC
LIMIT 50
"#
"#,
)
.bind(id)
.fetch_all(&*state.db)
@@ -325,10 +334,10 @@ pub async fn get_user_usage(
json!({
"id": row.get::<i64, _>("id"),
"model": row.get::<Option<String>, _>("model"),
"prompt_tokens": row.get::<i64, _>("prompt_tokens"),
"completion_tokens": row.get::<i64, _>("completion_tokens"),
"total_tokens": row.get::<i64, _>("total_tokens"),
"cost": row.get::<f64, _>("cost"),
"prompt_tokens": row.get::<Option<i64>, _>("prompt_tokens").unwrap_or(0),
"completion_tokens": row.get::<Option<i64>, _>("completion_tokens").unwrap_or(0),
"total_tokens": row.get::<Option<i64>, _>("total_tokens").unwrap_or(0),
"cost": row.get::<Option<f64>, _>("cost").unwrap_or(0.0),
"created_at": row.get::<Option<String>, _>("created_at"),
})
})
@@ -345,7 +354,6 @@ pub async fn get_user_usage(
})))
}
/// PUT /api/admin/users/{id}
pub async fn update_user(
State(state): State<AppState>,
@@ -468,3 +476,28 @@ async fn ensure_email_available(
}
Ok(())
}
async fn attach_usage_summary(state: &AppState, item: &mut UserResponse) -> AppResult<()> {
let (total_requests, total_cost, last_7_days_cost): (i64, f64, f64) = sqlx::query_as(
r#"
SELECT
COUNT(l.id) AS total_requests,
COALESCE(SUM(l.cost), 0.0) AS total_cost,
COALESCE(SUM(CASE
WHEN l.created_at >= datetime('now', '-7 days') THEN l.cost
ELSE 0.0
END), 0.0) AS last_7_days_cost
FROM usage_logs l
JOIN downstream_api_keys k ON l.downstream_key_id = k.id
WHERE k.user_id = ?
"#,
)
.bind(item.id)
.fetch_one(&*state.db)
.await?;
item.total_requests = Some(total_requests);
item.total_cost = Some(total_cost);
item.last_7_days_cost = Some(last_7_days_cost);
Ok(())
}