mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
feat(gateway/data): 新增 usage 关键词搜索、缓存命中摘要、结算成本摘要及 dashboard 聚合查询能力
- 新增 UsageAuditKeywordSearchQuery,支持多关键词、用户名、API Key 交叉过滤 - 新增 UsageCacheHitSummaryQuery/StoredUsageCacheHitSummary,统计请求缓存命中率 - 新增 UsageSettledCostSummaryQuery/StoredUsageSettledCostSummary,汇总结算成本 - 新增 UsageDashboardSummaryQuery、UsageBreakdownSummaryQuery 等 dashboard 聚合类型 - SQL 层实现对应查询方法,含 list_by_ids、list_usage_audits_by_keyword_search、count_usage_audits_by_keyword_search - 将上述能力通过 GatewayDataState / AdminAppState 暴露给 handler 层 - user_me_usage 及 dashboard_filters 切换为新查询接口,移除旧的内存过滤逻辑 - 同步更新 memory 层及测试
This commit is contained in:
@@ -95,6 +95,29 @@ impl UserReadRepository for InMemoryUserReadRepository {
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn list_users_by_username_search(
|
||||
&self,
|
||||
username_search: &str,
|
||||
) -> Result<Vec<StoredUserSummary>, DataLayerError> {
|
||||
let username_search = username_search.trim().to_ascii_lowercase();
|
||||
if username_search.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
Ok(self
|
||||
.by_id
|
||||
.read()
|
||||
.expect("user repository lock")
|
||||
.values()
|
||||
.filter(|user| {
|
||||
user.username
|
||||
.to_ascii_lowercase()
|
||||
.contains(&username_search)
|
||||
})
|
||||
.cloned()
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn list_non_admin_export_users(
|
||||
&self,
|
||||
) -> Result<Vec<StoredUserExportRow>, DataLayerError> {
|
||||
|
||||
@@ -21,6 +21,20 @@ WHERE id = ANY($1::text[])
|
||||
ORDER BY id ASC
|
||||
"#;
|
||||
|
||||
const LIST_USERS_BY_USERNAME_SEARCH_SQL: &str = r#"
|
||||
SELECT
|
||||
id,
|
||||
username,
|
||||
email,
|
||||
role::text AS role,
|
||||
is_active,
|
||||
is_deleted
|
||||
FROM users
|
||||
WHERE is_deleted IS FALSE
|
||||
AND LOWER(username) LIKE $1
|
||||
ORDER BY id ASC
|
||||
"#;
|
||||
|
||||
const LIST_NON_ADMIN_EXPORT_USERS_SQL: &str = r#"
|
||||
SELECT
|
||||
id,
|
||||
@@ -199,6 +213,24 @@ impl SqlxUserReadRepository {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_users_by_username_search(
|
||||
&self,
|
||||
username_search: &str,
|
||||
) -> Result<Vec<StoredUserSummary>, DataLayerError> {
|
||||
let username_search = username_search.trim();
|
||||
if username_search.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
collect_query_rows(
|
||||
sqlx::query(LIST_USERS_BY_USERNAME_SEARCH_SQL)
|
||||
.bind(format!("%{}%", username_search.to_ascii_lowercase()))
|
||||
.fetch(&self.pool),
|
||||
map_user_row,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_non_admin_export_users(
|
||||
&self,
|
||||
) -> Result<Vec<StoredUserExportRow>, DataLayerError> {
|
||||
@@ -383,6 +415,13 @@ impl UserReadRepository for SqlxUserReadRepository {
|
||||
self.list_users_by_ids(user_ids).await
|
||||
}
|
||||
|
||||
async fn list_users_by_username_search(
|
||||
&self,
|
||||
username_search: &str,
|
||||
) -> Result<Vec<StoredUserSummary>, DataLayerError> {
|
||||
self.list_users_by_username_search(username_search).await
|
||||
}
|
||||
|
||||
async fn list_non_admin_export_users(
|
||||
&self,
|
||||
) -> Result<Vec<StoredUserExportRow>, DataLayerError> {
|
||||
|
||||
@@ -393,6 +393,11 @@ pub trait UserReadRepository: Send + Sync {
|
||||
user_ids: &[String],
|
||||
) -> Result<Vec<StoredUserSummary>, crate::DataLayerError>;
|
||||
|
||||
async fn list_users_by_username_search(
|
||||
&self,
|
||||
username_search: &str,
|
||||
) -> Result<Vec<StoredUserSummary>, crate::DataLayerError>;
|
||||
|
||||
async fn list_export_users(&self) -> Result<Vec<StoredUserExportRow>, crate::DataLayerError>;
|
||||
|
||||
async fn list_export_users_page(
|
||||
|
||||
Reference in New Issue
Block a user