mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
feat(stats): 新增聚合读路径与回填机制并重构 dashboard/usage 读取链路
- 新增 stats_user_summary 及 user_daily_provider/api_format/cost_savings 等聚合表 - 扩展 stats_daily/hourly 有效 token 与响应时间等字段,maintenance runtime 同步写入 - 新增 backfill 模块与 --apply-backfills 命令补齐历史聚合数据 - 重写 dashboard_filters、usage_heatmap、user_rollups 查询改走聚合表 - 同步更新 baseline_v2.sql 与 migration 集,README/dev.sh 补充回填用法
This commit is contained in:
@@ -65,6 +65,7 @@ impl InMemoryAuthApiKeySnapshotRepository {
|
||||
.map(|value| value as i64),
|
||||
false,
|
||||
0,
|
||||
0,
|
||||
0.0,
|
||||
snapshot.api_key_is_standalone,
|
||||
)
|
||||
@@ -487,6 +488,7 @@ impl AuthApiKeyWriteRepository for InMemoryAuthApiKeySnapshotRepository {
|
||||
record.expires_at_unix_secs.map(|value| value as i64),
|
||||
record.auto_delete_on_expiry,
|
||||
record.total_requests as i64,
|
||||
record.total_tokens as i64,
|
||||
record.total_cost_usd,
|
||||
false,
|
||||
)?;
|
||||
@@ -607,6 +609,7 @@ impl AuthApiKeyWriteRepository for InMemoryAuthApiKeySnapshotRepository {
|
||||
record.expires_at_unix_secs.map(|value| value as i64),
|
||||
record.auto_delete_on_expiry,
|
||||
record.total_requests as i64,
|
||||
record.total_tokens as i64,
|
||||
record.total_cost_usd,
|
||||
true,
|
||||
)?;
|
||||
@@ -1018,6 +1021,7 @@ mod tests {
|
||||
Some(200),
|
||||
false,
|
||||
14,
|
||||
1_400,
|
||||
1.5,
|
||||
false,
|
||||
)
|
||||
@@ -1038,6 +1042,7 @@ mod tests {
|
||||
None,
|
||||
true,
|
||||
2,
|
||||
25,
|
||||
0.25,
|
||||
true,
|
||||
)
|
||||
|
||||
@@ -150,6 +150,7 @@ SELECT
|
||||
CAST(EXTRACT(EPOCH FROM api_keys.expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
api_keys.auto_delete_on_expiry,
|
||||
api_keys.total_requests,
|
||||
COALESCE(api_keys.total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(api_keys.total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
api_keys.is_standalone
|
||||
FROM api_keys
|
||||
@@ -175,6 +176,7 @@ SELECT
|
||||
CAST(EXTRACT(EPOCH FROM api_keys.expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
api_keys.auto_delete_on_expiry,
|
||||
api_keys.total_requests,
|
||||
COALESCE(api_keys.total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(api_keys.total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
api_keys.is_standalone
|
||||
FROM api_keys
|
||||
@@ -199,6 +201,7 @@ SELECT
|
||||
CAST(EXTRACT(EPOCH FROM api_keys.expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
api_keys.auto_delete_on_expiry,
|
||||
api_keys.total_requests,
|
||||
COALESCE(api_keys.total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(api_keys.total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
api_keys.is_standalone
|
||||
FROM api_keys
|
||||
@@ -223,6 +226,7 @@ SELECT
|
||||
CAST(EXTRACT(EPOCH FROM api_keys.expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
api_keys.auto_delete_on_expiry,
|
||||
api_keys.total_requests,
|
||||
COALESCE(api_keys.total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(api_keys.total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
api_keys.is_standalone
|
||||
FROM api_keys
|
||||
@@ -247,6 +251,7 @@ SELECT
|
||||
CAST(EXTRACT(EPOCH FROM api_keys.expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
api_keys.auto_delete_on_expiry,
|
||||
api_keys.total_requests,
|
||||
COALESCE(api_keys.total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(api_keys.total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
api_keys.is_standalone
|
||||
FROM api_keys
|
||||
@@ -315,6 +320,7 @@ SELECT
|
||||
CAST(EXTRACT(EPOCH FROM api_keys.expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
api_keys.auto_delete_on_expiry,
|
||||
api_keys.total_requests,
|
||||
COALESCE(api_keys.total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(api_keys.total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
api_keys.is_standalone
|
||||
FROM api_keys
|
||||
@@ -348,6 +354,7 @@ INSERT INTO api_keys (
|
||||
is_standalone,
|
||||
auto_delete_on_expiry,
|
||||
total_requests,
|
||||
total_tokens,
|
||||
total_cost_usd,
|
||||
created_at,
|
||||
updated_at
|
||||
@@ -371,6 +378,7 @@ VALUES (
|
||||
$14,
|
||||
$15,
|
||||
$16,
|
||||
$17,
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
@@ -390,6 +398,7 @@ RETURNING
|
||||
CAST(EXTRACT(EPOCH FROM expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
auto_delete_on_expiry,
|
||||
total_requests,
|
||||
COALESCE(total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
is_standalone
|
||||
"#;
|
||||
@@ -413,6 +422,7 @@ INSERT INTO api_keys (
|
||||
is_standalone,
|
||||
auto_delete_on_expiry,
|
||||
total_requests,
|
||||
total_tokens,
|
||||
total_cost_usd,
|
||||
created_at,
|
||||
updated_at
|
||||
@@ -436,6 +446,7 @@ VALUES (
|
||||
$14,
|
||||
$15,
|
||||
$16,
|
||||
$17,
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
@@ -455,6 +466,7 @@ RETURNING
|
||||
CAST(EXTRACT(EPOCH FROM expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
auto_delete_on_expiry,
|
||||
total_requests,
|
||||
COALESCE(total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
is_standalone
|
||||
"#;
|
||||
@@ -485,6 +497,7 @@ RETURNING
|
||||
CAST(EXTRACT(EPOCH FROM expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
auto_delete_on_expiry,
|
||||
total_requests,
|
||||
COALESCE(total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
is_standalone
|
||||
"#;
|
||||
@@ -519,6 +532,7 @@ RETURNING
|
||||
CAST(EXTRACT(EPOCH FROM expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
auto_delete_on_expiry,
|
||||
total_requests,
|
||||
COALESCE(total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
is_standalone
|
||||
"#;
|
||||
@@ -547,6 +561,7 @@ RETURNING
|
||||
CAST(EXTRACT(EPOCH FROM expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
auto_delete_on_expiry,
|
||||
total_requests,
|
||||
COALESCE(total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
is_standalone
|
||||
"#;
|
||||
@@ -574,6 +589,7 @@ RETURNING
|
||||
CAST(EXTRACT(EPOCH FROM expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
auto_delete_on_expiry,
|
||||
total_requests,
|
||||
COALESCE(total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
is_standalone
|
||||
"#;
|
||||
@@ -612,6 +628,7 @@ RETURNING
|
||||
CAST(EXTRACT(EPOCH FROM expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
auto_delete_on_expiry,
|
||||
total_requests,
|
||||
COALESCE(total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
is_standalone
|
||||
"#;
|
||||
@@ -640,6 +657,7 @@ RETURNING
|
||||
CAST(EXTRACT(EPOCH FROM expires_at) AS BIGINT) AS expires_at_unix_secs,
|
||||
auto_delete_on_expiry,
|
||||
total_requests,
|
||||
COALESCE(total_tokens, 0)::BIGINT AS total_tokens,
|
||||
COALESCE(CAST(total_cost_usd AS DOUBLE PRECISION), 0) AS total_cost_usd,
|
||||
is_standalone
|
||||
"#;
|
||||
@@ -1049,6 +1067,7 @@ impl AuthApiKeyWriteRepository for SqlxAuthApiKeySnapshotReadRepository {
|
||||
.bind(expires_at)
|
||||
.bind(record.auto_delete_on_expiry)
|
||||
.bind(record.total_requests as i64)
|
||||
.bind(record.total_tokens as i64)
|
||||
.bind(record.total_cost_usd)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -1099,6 +1118,7 @@ impl AuthApiKeyWriteRepository for SqlxAuthApiKeySnapshotReadRepository {
|
||||
.bind(expires_at)
|
||||
.bind(record.auto_delete_on_expiry)
|
||||
.bind(record.total_requests as i64)
|
||||
.bind(record.total_tokens as i64)
|
||||
.bind(record.total_cost_usd)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -1373,6 +1393,7 @@ fn map_auth_api_key_export_row(
|
||||
row_get(row, "expires_at_unix_secs")?,
|
||||
row_get(row, "auto_delete_on_expiry")?,
|
||||
row_get::<i32>(row, "total_requests")?.into(),
|
||||
row_get::<i64>(row, "total_tokens")?,
|
||||
row_get(row, "total_cost_usd")?,
|
||||
row_get(row, "is_standalone")?,
|
||||
)
|
||||
|
||||
@@ -266,6 +266,7 @@ pub struct StoredAuthApiKeyExportRecord {
|
||||
pub expires_at_unix_secs: Option<u64>,
|
||||
pub auto_delete_on_expiry: bool,
|
||||
pub total_requests: u64,
|
||||
pub total_tokens: u64,
|
||||
pub total_cost_usd: f64,
|
||||
pub is_standalone: bool,
|
||||
}
|
||||
@@ -288,6 +289,7 @@ impl StoredAuthApiKeyExportRecord {
|
||||
expires_at_unix_secs: Option<i64>,
|
||||
auto_delete_on_expiry: bool,
|
||||
total_requests: i64,
|
||||
total_tokens: i64,
|
||||
total_cost_usd: f64,
|
||||
is_standalone: bool,
|
||||
) -> Result<Self, crate::DataLayerError> {
|
||||
@@ -333,6 +335,7 @@ impl StoredAuthApiKeyExportRecord {
|
||||
.transpose()?,
|
||||
auto_delete_on_expiry,
|
||||
total_requests: parse_u64_i64(total_requests, "api_keys.total_requests")?,
|
||||
total_tokens: parse_u64_i64(total_tokens, "api_keys.total_tokens")?,
|
||||
total_cost_usd,
|
||||
is_standalone,
|
||||
})
|
||||
@@ -369,6 +372,7 @@ pub struct CreateUserApiKeyRecord {
|
||||
pub expires_at_unix_secs: Option<u64>,
|
||||
pub auto_delete_on_expiry: bool,
|
||||
pub total_requests: u64,
|
||||
pub total_tokens: u64,
|
||||
pub total_cost_usd: f64,
|
||||
}
|
||||
|
||||
@@ -398,6 +402,7 @@ pub struct CreateStandaloneApiKeyRecord {
|
||||
pub expires_at_unix_secs: Option<u64>,
|
||||
pub auto_delete_on_expiry: bool,
|
||||
pub total_requests: u64,
|
||||
pub total_tokens: u64,
|
||||
pub total_cost_usd: f64,
|
||||
}
|
||||
|
||||
@@ -833,6 +838,7 @@ mod tests {
|
||||
None,
|
||||
false,
|
||||
-1,
|
||||
0,
|
||||
0.0,
|
||||
false,
|
||||
)
|
||||
@@ -857,6 +863,7 @@ mod tests {
|
||||
Some(200),
|
||||
false,
|
||||
12,
|
||||
480,
|
||||
1.25,
|
||||
false,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user