refactor(runtime): 优化管理端摘要查询与维护聚合链路

- 为 provider catalog key 和 video task 列表增加 summary/page 查询与排序能力,减少列表场景读取重字段
- 将多处 SQL 结果读取改为流式收集,降低 `fetch_all` 的内存占用
- 把日/小时统计、钱包日用量等维护任务改为数据库侧 `CTE + upsert` 聚合
- 修复视频任务轮询更新时从本地 snapshot 回填稀疏字段,避免 `prompt` 和请求体信息丢失
This commit is contained in:
AAEE86
2026-04-12 20:45:47 +08:00
committed by fawney19
parent 6aa16ec792
commit b2d85d70ca
55 changed files with 1954 additions and 1382 deletions

View File

@@ -4,6 +4,7 @@ use crate::{
AdminBillingRuleWriteInput, GatewayError, LocalMutationOutcome,
};
use aether_data::postgres::PostgresPool;
use futures_util::TryStreamExt;
use sqlx::Row;
fn internal(err: impl ToString) -> GatewayError {
@@ -136,7 +137,7 @@ WHERE ($1::TEXT IS NULL OR task_type = $1)
)?;
let offset = u64::from(page.saturating_sub(1) * page_size);
let rows = sqlx::query(
let mut rows = sqlx::query(
r#"
SELECT
id,
@@ -162,16 +163,13 @@ LIMIT $4
.bind(is_enabled)
.bind(i64::try_from(offset).map_err(|err| GatewayError::Internal(err.to_string()))?)
.bind(i64::from(page_size))
.fetch_all(pool)
.await
.map_err(internal)?;
.fetch(pool);
let mut items = Vec::new();
while let Some(row) = rows.try_next().await.map_err(internal)? {
items.push(admin_billing_rule_from_row(&row)?);
}
Ok((
rows.iter()
.map(admin_billing_rule_from_row)
.collect::<Result<Vec<_>, _>>()?,
total,
))
Ok((items, total))
}
pub(crate) async fn find_admin_billing_rule(
@@ -374,7 +372,7 @@ WHERE ($1::TEXT IS NULL OR api_format = $1)
)?;
let offset = u64::from(page.saturating_sub(1) * page_size);
let rows = sqlx::query(
let mut rows = sqlx::query(
r#"
SELECT
id,
@@ -406,16 +404,13 @@ LIMIT $6
.bind(is_enabled)
.bind(i64::try_from(offset).map_err(|err| GatewayError::Internal(err.to_string()))?)
.bind(i64::from(page_size))
.fetch_all(pool)
.await
.map_err(internal)?;
.fetch(pool);
let mut items = Vec::new();
while let Some(row) = rows.try_next().await.map_err(internal)? {
items.push(admin_billing_collector_from_row(&row)?);
}
Ok((
rows.iter()
.map(admin_billing_collector_from_row)
.collect::<Result<Vec<_>, _>>()?,
total,
))
Ok((items, total))
}
pub(crate) async fn find_admin_billing_collector(

View File

@@ -2,6 +2,7 @@ use std::collections::BTreeMap;
use aether_data::postgres::PostgresPool;
use chrono::{DateTime, Utc};
use futures_util::TryStreamExt;
use serde_json::{json, Value};
use sqlx::Row;
@@ -36,7 +37,7 @@ WHERE a.created_at >= $1
.await
.map_err(|err| GatewayError::Internal(format!("admin audit logs count failed: {err}")))?;
let rows = sqlx::query(
let mut rows = sqlx::query(
r#"
SELECT
a.id,
@@ -64,21 +65,24 @@ LIMIT $4 OFFSET $5
.bind(event_type)
.bind(i64::try_from(limit).unwrap_or(i64::MAX))
.bind(i64::try_from(offset).unwrap_or(i64::MAX))
.fetch_all(pool)
.await
.map_err(|err| GatewayError::Internal(format!("admin audit logs read failed: {err}")))?;
.fetch(pool);
let mut items = Vec::new();
while let Some(row) = rows
.try_next()
.await
.map_err(|err| GatewayError::Internal(format!("admin audit logs read failed: {err}")))?
{
items.push(admin_audit_log_row_to_json(row));
}
Ok((
rows.into_iter().map(admin_audit_log_row_to_json).collect(),
usize::try_from(total.max(0)).unwrap_or(usize::MAX),
))
Ok((items, usize::try_from(total.max(0)).unwrap_or(usize::MAX)))
}
pub(crate) async fn list_admin_suspicious_activities(
pool: &PostgresPool,
cutoff_time: DateTime<Utc>,
) -> Result<Vec<Value>, GatewayError> {
let rows = sqlx::query(
let mut rows = sqlx::query(
r#"
SELECT
id,
@@ -102,13 +106,15 @@ LIMIT 100
"login_failed",
"request_rate_limited",
])
.fetch_all(pool)
.await
.map_err(|err| {
.fetch(pool);
let mut items = Vec::new();
while let Some(row) = rows.try_next().await.map_err(|err| {
GatewayError::Internal(format!("admin suspicious activities read failed: {err}"))
})?;
})? {
items.push(admin_suspicious_row_to_json(row));
}
Ok(rows.into_iter().map(admin_suspicious_row_to_json).collect())
Ok(items)
}
pub(crate) async fn read_admin_user_behavior_event_counts(
@@ -116,7 +122,7 @@ pub(crate) async fn read_admin_user_behavior_event_counts(
user_id: &str,
cutoff_time: DateTime<Utc>,
) -> Result<BTreeMap<String, u64>, GatewayError> {
let rows = sqlx::query(
let mut rows = sqlx::query(
r#"
SELECT event_type, COUNT(*)::bigint AS count
FROM audit_logs
@@ -127,22 +133,25 @@ GROUP BY event_type
)
.bind(user_id)
.bind(cutoff_time)
.fetch_all(pool)
.await
.map_err(|err| GatewayError::Internal(format!("admin user behavior read failed: {err}")))?;
.fetch(pool);
let mut counts = BTreeMap::new();
while let Some(row) = rows
.try_next()
.await
.map_err(|err| GatewayError::Internal(format!("admin user behavior read failed: {err}")))?
{
let Ok(event_type) = row.try_get::<String, _>("event_type") else {
continue;
};
let count = row
.try_get::<i64, _>("count")
.ok()
.and_then(|value| u64::try_from(value.max(0)).ok())
.unwrap_or(0);
counts.insert(event_type, count);
}
Ok(rows
.into_iter()
.filter_map(|row| {
let event_type = row.try_get::<String, _>("event_type").ok()?;
let count = row
.try_get::<i64, _>("count")
.ok()
.and_then(|value| u64::try_from(value.max(0)).ok())
.unwrap_or(0);
Some((event_type, count))
})
.collect())
Ok(counts)
}
pub(crate) async fn list_user_audit_logs(
@@ -176,7 +185,7 @@ WHERE user_id = $1
}
};
let rows = match sqlx::query(
let mut rows = sqlx::query(
r#"
SELECT id, event_type, description, ip_address, status_code, created_at
FROM audit_logs
@@ -192,21 +201,17 @@ LIMIT $4 OFFSET $5
.bind(event_type)
.bind(i64::try_from(limit).unwrap_or(i64::MAX))
.bind(i64::try_from(offset).unwrap_or(i64::MAX))
.fetch_all(pool)
.await
.fetch(pool);
let mut items = Vec::new();
while let Some(row) = rows
.try_next()
.await
.map_err(|err| GatewayError::Internal(format!("user audit logs read failed: {err}")))?
{
Ok(value) => value,
Err(err) => {
return Err(GatewayError::Internal(format!(
"user audit logs read failed: {err}"
)))
}
};
items.push(user_audit_log_row_to_json(row));
}
Ok((
rows.into_iter().map(user_audit_log_row_to_json).collect(),
total,
))
Ok((items, total))
}
fn admin_audit_log_row_to_json(row: sqlx::postgres::PgRow) -> Value {