mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 01:47:47 +08:00
279 lines
9.2 KiB
Rust
279 lines
9.2 KiB
Rust
use crate::data::state::{ReferralRelationshipListQuery, ReferralRewardListQuery};
|
|
use crate::handlers::admin::request::{AdminRouteRequest, AdminRouteResult};
|
|
use crate::handlers::admin::shared::{attach_admin_audit_response, query_param_value};
|
|
use crate::GatewayError;
|
|
use axum::{
|
|
body::Body,
|
|
http,
|
|
response::{IntoResponse, Response},
|
|
Json,
|
|
};
|
|
use serde::Deserialize;
|
|
use serde_json::json;
|
|
|
|
#[derive(Debug, Default, Deserialize)]
|
|
struct ReferralAdminMutationRequest {
|
|
note: Option<String>,
|
|
}
|
|
|
|
pub(crate) async fn maybe_build_local_admin_referrals_response(
|
|
request: AdminRouteRequest<'_>,
|
|
) -> AdminRouteResult {
|
|
let request_context = request.request_context();
|
|
let Some(decision) = request_context.decision() else {
|
|
return Ok(None);
|
|
};
|
|
if decision.route_family.as_deref() != Some("referrals_manage") {
|
|
return Ok(None);
|
|
}
|
|
let response = match decision.route_kind.as_deref() {
|
|
Some("list_referrals") => {
|
|
build_admin_referrals_list_response(&request.state(), &request_context).await?
|
|
}
|
|
Some("list_referral_rewards") => {
|
|
build_admin_referral_rewards_list_response(&request.state(), &request_context).await?
|
|
}
|
|
Some("retry_referral_reward") => {
|
|
build_admin_referral_reward_retry_response(
|
|
&request.state(),
|
|
&request_context,
|
|
request.request_body(),
|
|
)
|
|
.await?
|
|
}
|
|
Some("void_referral_reward") => {
|
|
build_admin_referral_reward_void_response(
|
|
&request.state(),
|
|
&request_context,
|
|
request.request_body(),
|
|
)
|
|
.await?
|
|
}
|
|
_ => build_admin_referrals_unavailable_response(),
|
|
};
|
|
Ok(Some(response))
|
|
}
|
|
|
|
fn admin_referrals_bad_request(detail: impl Into<String>) -> Response<Body> {
|
|
(
|
|
http::StatusCode::BAD_REQUEST,
|
|
Json(json!({ "detail": detail.into() })),
|
|
)
|
|
.into_response()
|
|
}
|
|
|
|
fn build_admin_referrals_unavailable_response() -> Response<Body> {
|
|
(
|
|
http::StatusCode::SERVICE_UNAVAILABLE,
|
|
Json(json!({ "detail": "Admin referral data unavailable" })),
|
|
)
|
|
.into_response()
|
|
}
|
|
|
|
fn parse_limit(query: Option<&str>) -> Result<usize, String> {
|
|
match query_param_value(query, "limit") {
|
|
Some(value) => value
|
|
.parse::<usize>()
|
|
.map(|value| value.clamp(1, 200))
|
|
.map_err(|_| "limit 必须是正整数".to_string()),
|
|
None => Ok(50),
|
|
}
|
|
}
|
|
|
|
fn parse_offset(query: Option<&str>) -> Result<usize, String> {
|
|
match query_param_value(query, "offset") {
|
|
Some(value) => value
|
|
.parse::<usize>()
|
|
.map_err(|_| "offset 必须是非负整数".to_string()),
|
|
None => Ok(0),
|
|
}
|
|
}
|
|
|
|
fn parse_optional_bool(query: Option<&str>, key: &str) -> Result<Option<bool>, String> {
|
|
let Some(value) = query_param_value(query, key) else {
|
|
return Ok(None);
|
|
};
|
|
match value.trim().to_ascii_lowercase().as_str() {
|
|
"true" | "1" | "yes" => Ok(Some(true)),
|
|
"false" | "0" | "no" => Ok(Some(false)),
|
|
_ => Err(format!("{key} 必须是布尔值")),
|
|
}
|
|
}
|
|
|
|
fn operator_id(
|
|
request_context: &crate::handlers::admin::request::AdminRequestContext<'_>,
|
|
) -> Option<String> {
|
|
request_context
|
|
.decision()
|
|
.and_then(|decision| decision.admin_principal.as_ref())
|
|
.map(|principal| principal.user_id.clone())
|
|
}
|
|
|
|
fn reward_id_from_path(path: &str, suffix: &str) -> Option<String> {
|
|
let trimmed = path.trim_end_matches('/');
|
|
let rest = trimmed.strip_prefix("/api/admin/referral-rewards/")?;
|
|
let id = rest.strip_suffix(suffix)?.trim_end_matches('/');
|
|
(!id.is_empty()).then_some(id.to_string())
|
|
}
|
|
|
|
fn parse_mutation_note(body: Option<&axum::body::Bytes>) -> Result<Option<String>, String> {
|
|
let Some(body) = body.filter(|body| !body.is_empty()) else {
|
|
return Ok(None);
|
|
};
|
|
let payload = serde_json::from_slice::<ReferralAdminMutationRequest>(body)
|
|
.map_err(|_| "请求数据验证失败".to_string())?;
|
|
Ok(payload
|
|
.note
|
|
.map(|value| value.trim().to_string())
|
|
.filter(|value| !value.is_empty()))
|
|
}
|
|
|
|
async fn build_admin_referrals_list_response(
|
|
state: &crate::handlers::admin::request::AdminAppState<'_>,
|
|
request_context: &crate::handlers::admin::request::AdminRequestContext<'_>,
|
|
) -> Result<Response<Body>, GatewayError> {
|
|
let query = request_context.query_string();
|
|
let limit = match parse_limit(query) {
|
|
Ok(value) => value,
|
|
Err(detail) => return Ok(admin_referrals_bad_request(detail)),
|
|
};
|
|
let offset = match parse_offset(query) {
|
|
Ok(value) => value,
|
|
Err(detail) => return Ok(admin_referrals_bad_request(detail)),
|
|
};
|
|
let first_paid = match parse_optional_bool(query, "first_paid") {
|
|
Ok(value) => value,
|
|
Err(detail) => return Ok(admin_referrals_bad_request(detail)),
|
|
};
|
|
let Some((items, total, stats)) = state
|
|
.app()
|
|
.list_admin_referral_relationships(ReferralRelationshipListQuery {
|
|
inviter: query_param_value(query, "inviter"),
|
|
invitee: query_param_value(query, "invitee"),
|
|
invite_code: query_param_value(query, "invite_code"),
|
|
first_paid,
|
|
limit,
|
|
offset,
|
|
})
|
|
.await?
|
|
else {
|
|
return Ok(build_admin_referrals_unavailable_response());
|
|
};
|
|
Ok(Json(json!({
|
|
"items": items,
|
|
"total": total,
|
|
"limit": limit,
|
|
"offset": offset,
|
|
"stats": stats,
|
|
}))
|
|
.into_response())
|
|
}
|
|
|
|
async fn build_admin_referral_rewards_list_response(
|
|
state: &crate::handlers::admin::request::AdminAppState<'_>,
|
|
request_context: &crate::handlers::admin::request::AdminRequestContext<'_>,
|
|
) -> Result<Response<Body>, GatewayError> {
|
|
let query = request_context.query_string();
|
|
let limit = match parse_limit(query) {
|
|
Ok(value) => value,
|
|
Err(detail) => return Ok(admin_referrals_bad_request(detail)),
|
|
};
|
|
let offset = match parse_offset(query) {
|
|
Ok(value) => value,
|
|
Err(detail) => return Ok(admin_referrals_bad_request(detail)),
|
|
};
|
|
let Some((items, total, stats)) = state
|
|
.app()
|
|
.list_admin_referral_rewards(ReferralRewardListQuery {
|
|
order_id: query_param_value(query, "order_id"),
|
|
reward_type: query_param_value(query, "reward_type"),
|
|
status: query_param_value(query, "status"),
|
|
limit,
|
|
offset,
|
|
})
|
|
.await?
|
|
else {
|
|
return Ok(build_admin_referrals_unavailable_response());
|
|
};
|
|
Ok(Json(json!({
|
|
"items": items,
|
|
"total": total,
|
|
"limit": limit,
|
|
"offset": offset,
|
|
"stats": stats,
|
|
}))
|
|
.into_response())
|
|
}
|
|
|
|
async fn build_admin_referral_reward_retry_response(
|
|
state: &crate::handlers::admin::request::AdminAppState<'_>,
|
|
request_context: &crate::handlers::admin::request::AdminRequestContext<'_>,
|
|
request_body: Option<&axum::body::Bytes>,
|
|
) -> Result<Response<Body>, GatewayError> {
|
|
let Some(reward_id) = reward_id_from_path(request_context.path(), "/retry") else {
|
|
return Ok(admin_referrals_bad_request("返利记录不存在"));
|
|
};
|
|
let note = match parse_mutation_note(request_body) {
|
|
Ok(value) => value,
|
|
Err(detail) => return Ok(admin_referrals_bad_request(detail)),
|
|
};
|
|
match state
|
|
.app()
|
|
.retry_referral_reward(
|
|
&reward_id,
|
|
operator_id(request_context).as_deref(),
|
|
note.as_deref(),
|
|
)
|
|
.await?
|
|
{
|
|
Some(reward) => Ok(attach_admin_audit_response(
|
|
Json(json!({ "reward": reward })).into_response(),
|
|
"admin_referral_reward_retry",
|
|
"retry_referral_reward",
|
|
"referral_reward",
|
|
&reward_id,
|
|
)),
|
|
None => Ok((
|
|
http::StatusCode::NOT_FOUND,
|
|
Json(json!({ "detail": "Referral reward not found" })),
|
|
)
|
|
.into_response()),
|
|
}
|
|
}
|
|
|
|
async fn build_admin_referral_reward_void_response(
|
|
state: &crate::handlers::admin::request::AdminAppState<'_>,
|
|
request_context: &crate::handlers::admin::request::AdminRequestContext<'_>,
|
|
request_body: Option<&axum::body::Bytes>,
|
|
) -> Result<Response<Body>, GatewayError> {
|
|
let Some(reward_id) = reward_id_from_path(request_context.path(), "/void") else {
|
|
return Ok(admin_referrals_bad_request("返利记录不存在"));
|
|
};
|
|
let note = match parse_mutation_note(request_body) {
|
|
Ok(value) => value,
|
|
Err(detail) => return Ok(admin_referrals_bad_request(detail)),
|
|
};
|
|
match state
|
|
.app()
|
|
.void_referral_reward(
|
|
&reward_id,
|
|
operator_id(request_context).as_deref(),
|
|
note.as_deref(),
|
|
)
|
|
.await?
|
|
{
|
|
Some(reward) => Ok(attach_admin_audit_response(
|
|
Json(json!({ "reward": reward })).into_response(),
|
|
"admin_referral_reward_void",
|
|
"void_referral_reward",
|
|
"referral_reward",
|
|
&reward_id,
|
|
)),
|
|
None => Ok((
|
|
http::StatusCode::NOT_FOUND,
|
|
Json(json!({ "detail": "Referral reward not found" })),
|
|
)
|
|
.into_response()),
|
|
}
|
|
}
|