Merge pull request #615 from AAEE86/main

feat: 健康监控仪表盘与关联下钻优化,完善使用记录展示
This commit is contained in:
fawney19
2026-06-21 12:48:13 +08:00
committed by GitHub
41 changed files with 4445 additions and 1602 deletions
@@ -297,6 +297,12 @@ fn usage_matches_list_query(item: &StoredRequestUsageAudit, query: &UsageAuditLi
return false;
}
}
if item
.status_code
.is_some_and(|status_code| query.exclude_status_codes.contains(&status_code))
{
return false;
}
if let Some(is_stream) = query.is_stream {
if item.is_stream != is_stream {
return false;
@@ -372,6 +378,12 @@ fn usage_matches_keyword_search_query(
return false;
}
}
if item
.status_code
.is_some_and(|status_code| query.exclude_status_codes.contains(&status_code))
{
return false;
}
if let Some(is_stream) = query.is_stream {
if item.is_stream != is_stream {
return false;
@@ -589,6 +601,22 @@ fn usage_matches_breakdown_summary_query(
return false;
}
}
if let Some(model) = query.model.as_deref() {
if item.model != model {
return false;
}
}
if let Some(api_format) = query.api_format.as_deref() {
if item.api_format.as_deref() != Some(api_format) {
return false;
}
}
if item
.status_code
.is_some_and(|status_code| query.exclude_status_codes.contains(&status_code))
{
return false;
}
match query.group_by {
UsageBreakdownGroupBy::Model | UsageBreakdownGroupBy::Provider => true,
UsageBreakdownGroupBy::ApiFormat => item.api_format.is_some(),
@@ -86,6 +86,24 @@ fn dashboard_utc_to_unix_secs(value: DateTime<Utc>) -> u64 {
value.timestamp().max(0) as u64
}
fn push_postgres_usage_excluded_status_codes(
builder: &mut QueryBuilder<'_, Postgres>,
has_where: &mut bool,
status_codes: &[u16],
) {
if status_codes.is_empty() {
return;
}
builder.push(if *has_where { " AND " } else { " WHERE " });
*has_where = true;
builder.push("(\"usage\".status_code IS NULL OR \"usage\".status_code NOT IN (");
let mut separated = builder.separated(", ");
for status_code in status_codes {
separated.push_bind(i32::from(*status_code));
}
separated.push_unseparated("))");
}
fn dashboard_utc_midnight(value: DateTime<Utc>) -> DateTime<Utc> {
DateTime::<Utc>::from_naive_utc_and_offset(
value
@@ -2631,6 +2649,11 @@ ORDER BY request_count DESC, "usage".provider_name ASC
separated.push_unseparated(")");
}
}
push_postgres_usage_excluded_status_codes(
&mut builder,
&mut has_where,
&query.exclude_status_codes,
);
if let Some(is_stream) = query.is_stream {
builder.push(if has_where { " AND " } else { " WHERE " });
has_where = true;
@@ -2738,6 +2761,11 @@ OR (\"usage\".error_message IS NOT NULL AND BTRIM(\"usage\".error_message) <> ''
separated.push_unseparated(")");
}
}
push_postgres_usage_excluded_status_codes(
&mut builder,
&mut has_where,
&query.exclude_status_codes,
);
if let Some(is_stream) = query.is_stream {
builder.push(if has_where { " AND " } else { " WHERE " });
has_where = true;
@@ -2926,6 +2954,11 @@ OR (\"usage\".error_message IS NOT NULL AND BTRIM(\"usage\".error_message) <> ''
separated.push_unseparated(")");
}
}
push_postgres_usage_excluded_status_codes(
&mut builder,
&mut has_where,
&query.exclude_status_codes,
);
if let Some(is_stream) = query.is_stream {
builder.push(if has_where { " AND " } else { " WHERE " });
has_where = true;
@@ -3022,6 +3055,11 @@ OR (\"usage\".error_message IS NOT NULL AND BTRIM(\"usage\".error_message) <> ''
separated.push_unseparated(")");
}
}
push_postgres_usage_excluded_status_codes(
&mut builder,
&mut has_where,
&query.exclude_status_codes,
);
if let Some(is_stream) = query.is_stream {
builder.push(if has_where { " AND " } else { " WHERE " });
has_where = true;
@@ -4897,6 +4935,23 @@ WITH filtered_usage AS (
.push("\"usage\".provider_name = ")
.push_bind(provider_name.to_string());
}
if let Some(model) = query.model.as_deref() {
builder.push(if has_where { " AND " } else { " WHERE " });
builder
.push("\"usage\".model = ")
.push_bind(model.to_string());
}
if let Some(api_format) = query.api_format.as_deref() {
builder.push(if has_where { " AND " } else { " WHERE " });
builder
.push("\"usage\".api_format = ")
.push_bind(api_format.to_string());
}
push_postgres_usage_excluded_status_codes(
&mut builder,
&mut has_where,
&query.exclude_status_codes,
);
builder.push(filtered_extra_where);
builder.push(
r#"
@@ -4989,9 +5044,18 @@ ORDER BY request_count DESC, group_key ASC
&self,
query: &UsageBreakdownSummaryQuery,
) -> Result<Vec<StoredUsageBreakdownSummaryRow>, DataLayerError> {
if !query.exclude_status_codes.is_empty() {
return self.summarize_usage_breakdown_raw(query).await;
}
if query.provider_name.is_some() {
return self.summarize_usage_breakdown_raw(query).await;
}
if query.model.is_some() {
return self.summarize_usage_breakdown_raw(query).await;
}
if query.api_format.is_some() {
return self.summarize_usage_breakdown_raw(query).await;
}
let Some(user_id) = query.user_id.as_deref() else {
return self.summarize_usage_breakdown_raw(query).await;
};
@@ -5014,6 +5078,9 @@ ORDER BY request_count DESC, group_key ASC
created_until_unix_secs: dashboard_utc_to_unix_secs(raw_end),
user_id: Some(user_id.to_string()),
provider_name: None,
model: None,
api_format: None,
exclude_status_codes: query.exclude_status_codes.clone(),
group_by: query.group_by,
})
.await?;
@@ -5037,6 +5104,9 @@ ORDER BY request_count DESC, group_key ASC
created_until_unix_secs: dashboard_utc_to_unix_secs(raw_end),
user_id: Some(user_id.to_string()),
provider_name: None,
model: None,
api_format: None,
exclude_status_codes: query.exclude_status_codes.clone(),
group_by: query.group_by,
})
.await?;
@@ -481,6 +481,7 @@ AND LOWER(TRIM(COALESCE(provider_name, ''))) NOT IN ('unknown', 'unknow'))",
separated.push_unseparated(")");
}
}
push_sqlite_usage_excluded_status_codes(builder, has_where, &query.exclude_status_codes);
if let Some(is_stream) = query.is_stream {
push_sqlite_usage_where(builder, has_where);
builder
@@ -497,6 +498,23 @@ OR (error_message IS NOT NULL AND TRIM(error_message) <> ''))",
}
}
fn push_sqlite_usage_excluded_status_codes(
builder: &mut QueryBuilder<'_, Sqlite>,
has_where: &mut bool,
status_codes: &[u16],
) {
if status_codes.is_empty() {
return;
}
push_sqlite_usage_where(builder, has_where);
builder.push("(status_code IS NULL OR status_code NOT IN (");
let mut separated = builder.separated(", ");
for status_code in status_codes {
separated.push_bind(i64::from(*status_code));
}
separated.push_unseparated("))");
}
fn push_sqlite_usage_keyword_filters(
builder: &mut QueryBuilder<'_, Sqlite>,
query: &UsageAuditKeywordSearchQuery,
@@ -514,6 +532,7 @@ fn push_sqlite_usage_keyword_filters(
client_family: query.client_family.clone(),
exclude_unknown_model_or_provider: query.exclude_unknown_model_or_provider,
statuses: query.statuses.clone(),
exclude_status_codes: query.exclude_status_codes.clone(),
is_stream: query.is_stream,
error_only: query.error_only,
limit: None,
@@ -2488,6 +2507,23 @@ FROM "usage"
"provider_name",
query.provider_name.as_deref(),
);
push_sqlite_usage_optional_text_filter(
&mut builder,
&mut has_where,
"model",
query.model.as_deref(),
);
push_sqlite_usage_optional_text_filter(
&mut builder,
&mut has_where,
"api_format",
query.api_format.as_deref(),
);
push_sqlite_usage_excluded_status_codes(
&mut builder,
&mut has_where,
&query.exclude_status_codes,
);
if matches!(query.group_by, UsageBreakdownGroupBy::ApiFormat) {
push_sqlite_usage_where(&mut builder, &mut has_where);
builder.push("api_format IS NOT NULL");