mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-12 20:19:51 +08:00
Merge upstream/main into feat/356-usage-record-columns
This commit is contained in:
@@ -30,10 +30,10 @@ mod refunds;
|
||||
use self::flow::handle_wallet_flow;
|
||||
pub(in crate::handlers::public::support) use self::reads::build_wallet_balance_payload_for_user;
|
||||
use self::reads::{
|
||||
build_wallet_daily_usage_payload, build_wallet_payload, build_wallet_zero_today_entry,
|
||||
handle_wallet_balance, handle_wallet_today_cost, handle_wallet_transactions,
|
||||
parse_wallet_limit, parse_wallet_offset, wallet_fixed_offset, wallet_today_billing_date_string,
|
||||
wallet_transaction_payload_from_record,
|
||||
build_wallet_daily_usage_payload, build_wallet_live_today_usage_payload_for_user,
|
||||
build_wallet_payload, build_wallet_zero_today_entry, handle_wallet_balance,
|
||||
handle_wallet_today_cost, handle_wallet_transactions, parse_wallet_limit, parse_wallet_offset,
|
||||
wallet_fixed_offset, wallet_transaction_payload_from_record,
|
||||
};
|
||||
pub(crate) use self::recharge::sanitize_wallet_gateway_response;
|
||||
use self::recharge::{
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use super::{
|
||||
build_auth_error_response, build_auth_json_response, build_wallet_daily_usage_payload,
|
||||
build_wallet_payload, build_wallet_zero_today_entry, http, parse_wallet_limit,
|
||||
parse_wallet_offset, resolve_authenticated_local_user, unix_secs_to_rfc3339,
|
||||
wallet_fixed_offset, wallet_today_billing_date_string, wallet_transaction_payload_from_record,
|
||||
AppState, Body, GatewayPublicRequestContext, Response, WALLET_LEGACY_TIMEZONE,
|
||||
build_wallet_live_today_usage_payload_for_user, build_wallet_payload,
|
||||
build_wallet_zero_today_entry, http, parse_wallet_limit, parse_wallet_offset,
|
||||
resolve_authenticated_local_user, unix_secs_to_rfc3339, wallet_fixed_offset,
|
||||
wallet_transaction_payload_from_record, AppState, Body, GatewayPublicRequestContext, Response,
|
||||
WALLET_LEGACY_TIMEZONE,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
@@ -98,32 +99,43 @@ pub(super) async fn handle_wallet_flow(
|
||||
return build_auth_json_response(http::StatusCode::OK, payload, None);
|
||||
};
|
||||
|
||||
let mut today_entry = build_wallet_zero_today_entry();
|
||||
if let Ok(Some(today_usage)) = state
|
||||
.find_wallet_today_usage(&wallet.id, WALLET_LEGACY_TIMEZONE)
|
||||
.await
|
||||
let mut today_entry =
|
||||
match build_wallet_live_today_usage_payload_for_user(state, &auth.user.id).await {
|
||||
Ok(Some(today_usage)) => today_usage,
|
||||
_ => build_wallet_zero_today_entry(),
|
||||
};
|
||||
if today_entry
|
||||
.get("total_requests")
|
||||
.and_then(serde_json::Value::as_u64)
|
||||
.unwrap_or_default()
|
||||
== 0
|
||||
{
|
||||
today_entry = build_wallet_daily_usage_payload(
|
||||
today_usage.id,
|
||||
today_usage.billing_date,
|
||||
today_usage.billing_timezone,
|
||||
today_usage.total_cost_usd,
|
||||
today_usage.total_requests,
|
||||
today_usage.input_tokens,
|
||||
today_usage.output_tokens,
|
||||
today_usage.cache_creation_tokens,
|
||||
today_usage.cache_read_tokens,
|
||||
today_usage
|
||||
.first_finalized_at_unix_secs
|
||||
.and_then(unix_secs_to_rfc3339),
|
||||
today_usage
|
||||
.last_finalized_at_unix_secs
|
||||
.and_then(unix_secs_to_rfc3339),
|
||||
today_usage
|
||||
.aggregated_at_unix_secs
|
||||
.and_then(unix_secs_to_rfc3339),
|
||||
true,
|
||||
);
|
||||
if let Ok(Some(today_usage)) = state
|
||||
.find_wallet_today_usage(&wallet.id, WALLET_LEGACY_TIMEZONE)
|
||||
.await
|
||||
{
|
||||
today_entry = build_wallet_daily_usage_payload(
|
||||
today_usage.id,
|
||||
today_usage.billing_date,
|
||||
today_usage.billing_timezone,
|
||||
today_usage.total_cost_usd,
|
||||
today_usage.total_requests,
|
||||
today_usage.input_tokens,
|
||||
today_usage.output_tokens,
|
||||
today_usage.cache_creation_tokens,
|
||||
today_usage.cache_read_tokens,
|
||||
today_usage
|
||||
.first_finalized_at_unix_secs
|
||||
.and_then(unix_secs_to_rfc3339),
|
||||
today_usage
|
||||
.last_finalized_at_unix_secs
|
||||
.and_then(unix_secs_to_rfc3339),
|
||||
today_usage
|
||||
.aggregated_at_unix_secs
|
||||
.and_then(unix_secs_to_rfc3339),
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let fetch_size = offset.saturating_add(limit).min(5200);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use super::{
|
||||
build_auth_error_response, build_auth_json_response, build_auth_wallet_summary_payload, http,
|
||||
query_param_value, resolve_authenticated_local_user, unix_secs_to_rfc3339, AppState, Body,
|
||||
GatewayError, GatewayPublicRequestContext, Response, WALLET_LEGACY_TIMEZONE,
|
||||
GatewayPublicRequestContext, Response, WALLET_LEGACY_TIMEZONE,
|
||||
};
|
||||
use crate::handlers::shared::round_to;
|
||||
use aether_data_contracts::repository::usage::UsageSettledCostSummaryQuery;
|
||||
use chrono::Utc;
|
||||
use chrono::{TimeZone, Utc};
|
||||
use serde_json::json;
|
||||
|
||||
const WALLET_TODAY_COST_UNAVAILABLE_DETAIL: &str = "钱包今日费用数据暂不可用";
|
||||
@@ -143,6 +143,26 @@ pub(super) fn wallet_today_billing_date_string() -> String {
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn wallet_today_usage_window() -> Result<(String, String, u64, u64), String> {
|
||||
let offset = wallet_fixed_offset();
|
||||
let today = Utc::now().with_timezone(&offset).date_naive();
|
||||
let Some(local_start_naive) = today.and_hms_opt(0, 0, 0) else {
|
||||
return Err("wallet today start is invalid".to_string());
|
||||
};
|
||||
let Some(local_start) = offset.from_local_datetime(&local_start_naive).single() else {
|
||||
return Err("wallet today local start is ambiguous".to_string());
|
||||
};
|
||||
let local_end = local_start + chrono::Duration::days(1);
|
||||
let start_unix_secs = local_start.timestamp().max(0) as u64;
|
||||
let end_unix_secs = local_end.timestamp().max(0) as u64;
|
||||
Ok((
|
||||
today.to_string(),
|
||||
WALLET_LEGACY_TIMEZONE.to_string(),
|
||||
start_unix_secs,
|
||||
end_unix_secs,
|
||||
))
|
||||
}
|
||||
|
||||
pub(super) fn build_wallet_daily_usage_payload(
|
||||
id: Option<String>,
|
||||
date: String,
|
||||
@@ -193,6 +213,43 @@ pub(super) fn build_wallet_zero_today_entry() -> serde_json::Value {
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) async fn build_wallet_live_today_usage_payload_for_user(
|
||||
state: &AppState,
|
||||
user_id: &str,
|
||||
) -> Result<Option<serde_json::Value>, String> {
|
||||
if !state.has_usage_data_reader() {
|
||||
return Ok(None);
|
||||
}
|
||||
let (date, timezone, start_unix_secs, end_unix_secs) = wallet_today_usage_window()?;
|
||||
let summary = state
|
||||
.summarize_usage_settled_cost(&UsageSettledCostSummaryQuery {
|
||||
created_from_unix_secs: start_unix_secs,
|
||||
created_until_unix_secs: end_unix_secs,
|
||||
user_id: Some(user_id.to_string()),
|
||||
})
|
||||
.await
|
||||
.map_err(|err| format!("wallet today cost lookup failed: {err:?}"))?;
|
||||
Ok(Some(build_wallet_daily_usage_payload(
|
||||
None,
|
||||
date,
|
||||
timezone,
|
||||
summary.total_cost_usd,
|
||||
summary.total_requests,
|
||||
summary.input_tokens,
|
||||
summary.output_tokens,
|
||||
summary.cache_creation_tokens,
|
||||
summary.cache_read_tokens,
|
||||
summary
|
||||
.first_finalized_at_unix_secs
|
||||
.and_then(unix_secs_to_rfc3339),
|
||||
summary
|
||||
.last_finalized_at_unix_secs
|
||||
.and_then(unix_secs_to_rfc3339),
|
||||
Some(Utc::now().to_rfc3339()),
|
||||
true,
|
||||
)))
|
||||
}
|
||||
|
||||
pub(super) fn wallet_transaction_payload_from_record(
|
||||
record: &aether_data::repository::wallet::StoredAdminWalletTransaction,
|
||||
) -> serde_json::Value {
|
||||
@@ -253,65 +310,17 @@ pub(super) async fn handle_wallet_today_cost(
|
||||
Ok(value) => value,
|
||||
Err(response) => return response,
|
||||
};
|
||||
let today = Utc::now().date_naive();
|
||||
let Some(start_of_day) = today.and_hms_opt(0, 0, 0) else {
|
||||
return build_auth_error_response(
|
||||
http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"wallet today start is invalid",
|
||||
match build_wallet_live_today_usage_payload_for_user(state, &auth.user.id).await {
|
||||
Ok(Some(payload)) => build_auth_json_response(http::StatusCode::OK, payload, None),
|
||||
Ok(None) => build_auth_error_response(
|
||||
http::StatusCode::SERVICE_UNAVAILABLE,
|
||||
WALLET_TODAY_COST_UNAVAILABLE_DETAIL,
|
||||
false,
|
||||
);
|
||||
};
|
||||
let start_unix_secs = u64::try_from(
|
||||
chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(start_of_day, chrono::Utc)
|
||||
.timestamp(),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let end_unix_secs = start_unix_secs.saturating_add(24 * 3600);
|
||||
|
||||
let summary = match state
|
||||
.summarize_usage_settled_cost(&UsageSettledCostSummaryQuery {
|
||||
created_from_unix_secs: start_unix_secs,
|
||||
created_until_unix_secs: end_unix_secs,
|
||||
user_id: Some(auth.user.id.clone()),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
return build_auth_error_response(
|
||||
http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("wallet today cost lookup failed: {err:?}"),
|
||||
false,
|
||||
)
|
||||
),
|
||||
Err(detail) => {
|
||||
build_auth_error_response(http::StatusCode::INTERNAL_SERVER_ERROR, detail, false)
|
||||
}
|
||||
};
|
||||
|
||||
let first_finalized_at = summary
|
||||
.first_finalized_at_unix_secs
|
||||
.and_then(unix_secs_to_rfc3339);
|
||||
let last_finalized_at = summary
|
||||
.last_finalized_at_unix_secs
|
||||
.and_then(unix_secs_to_rfc3339);
|
||||
|
||||
build_auth_json_response(
|
||||
http::StatusCode::OK,
|
||||
json!({
|
||||
"id": serde_json::Value::Null,
|
||||
"date": today.to_string(),
|
||||
"timezone": "UTC",
|
||||
"total_cost": round_to(summary.total_cost_usd, 6),
|
||||
"total_requests": summary.total_requests,
|
||||
"input_tokens": summary.input_tokens,
|
||||
"output_tokens": summary.output_tokens,
|
||||
"cache_creation_tokens": summary.cache_creation_tokens,
|
||||
"cache_read_tokens": summary.cache_read_tokens,
|
||||
"first_finalized_at": first_finalized_at,
|
||||
"last_finalized_at": last_finalized_at,
|
||||
"aggregated_at": Utc::now().to_rfc3339(),
|
||||
"is_today": true,
|
||||
}),
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn handle_wallet_transactions(
|
||||
|
||||
Reference in New Issue
Block a user