81
server-rs/crates/api-server/src/asset_billing.rs
Normal file
81
server-rs/crates/api-server/src/asset_billing.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use axum::http::StatusCode;
|
||||
use serde_json::json;
|
||||
use spacetime_client::SpacetimeClientError;
|
||||
|
||||
use crate::{http_error::AppError, state::AppState};
|
||||
|
||||
pub(crate) const ASSET_OPERATION_POINTS_COST: u64 = 1;
|
||||
|
||||
/// 资产操作统一预扣叙世币;扣费流水 ID 由业务资源 ID 参与构造,保证重试幂等。
|
||||
pub(crate) async fn consume_asset_operation_points(
|
||||
state: &AppState,
|
||||
owner_user_id: &str,
|
||||
asset_kind: &str,
|
||||
asset_id: &str,
|
||||
) -> Result<(), AppError> {
|
||||
let ledger_id = format!(
|
||||
"asset_generation_consume:{}:{}:{}",
|
||||
owner_user_id, asset_kind, asset_id
|
||||
);
|
||||
state
|
||||
.spacetime_client()
|
||||
.consume_profile_wallet_points(
|
||||
owner_user_id.to_string(),
|
||||
ASSET_OPERATION_POINTS_COST,
|
||||
ledger_id,
|
||||
current_utc_micros(),
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(map_asset_operation_wallet_error)
|
||||
}
|
||||
|
||||
/// 外部生成或发布 mutation 失败后补偿退款;退款失败只记日志,避免覆盖原始业务错误。
|
||||
pub(crate) async fn refund_asset_operation_points(
|
||||
state: &AppState,
|
||||
owner_user_id: &str,
|
||||
asset_kind: &str,
|
||||
asset_id: &str,
|
||||
) {
|
||||
let ledger_id = format!(
|
||||
"asset_generation_refund:{}:{}:{}",
|
||||
owner_user_id, asset_kind, asset_id
|
||||
);
|
||||
if let Err(error) = state
|
||||
.spacetime_client()
|
||||
.refund_profile_wallet_points(
|
||||
owner_user_id.to_string(),
|
||||
ASSET_OPERATION_POINTS_COST,
|
||||
ledger_id,
|
||||
current_utc_micros(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!(
|
||||
owner_user_id,
|
||||
asset_kind,
|
||||
asset_id,
|
||||
error = %error,
|
||||
"资产操作失败后的叙世币退款失败"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn map_asset_operation_wallet_error(error: SpacetimeClientError) -> AppError {
|
||||
let status = match &error {
|
||||
SpacetimeClientError::Runtime(_) => StatusCode::BAD_REQUEST,
|
||||
SpacetimeClientError::Procedure(message) if message.contains("叙世币余额不足") => {
|
||||
StatusCode::CONFLICT
|
||||
}
|
||||
_ => StatusCode::BAD_GATEWAY,
|
||||
};
|
||||
|
||||
AppError::from_status(status).with_details(json!({
|
||||
"provider": "profile-wallet",
|
||||
"message": error.to_string(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn current_utc_micros() -> i64 {
|
||||
time::OffsetDateTime::now_utc().unix_timestamp_nanos() as i64 / 1_000
|
||||
}
|
||||
Reference in New Issue
Block a user