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, } 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) -> Response { ( http::StatusCode::BAD_REQUEST, Json(json!({ "detail": detail.into() })), ) .into_response() } fn build_admin_referrals_unavailable_response() -> Response { ( http::StatusCode::SERVICE_UNAVAILABLE, Json(json!({ "detail": "Admin referral data unavailable" })), ) .into_response() } fn parse_limit(query: Option<&str>) -> Result { match query_param_value(query, "limit") { Some(value) => value .parse::() .map(|value| value.clamp(1, 200)) .map_err(|_| "limit 必须是正整数".to_string()), None => Ok(50), } } fn parse_offset(query: Option<&str>) -> Result { match query_param_value(query, "offset") { Some(value) => value .parse::() .map_err(|_| "offset 必须是非负整数".to_string()), None => Ok(0), } } fn parse_optional_bool(query: Option<&str>, key: &str) -> Result, 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 { 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 { 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, String> { let Some(body) = body.filter(|body| !body.is_empty()) else { return Ok(None); }; let payload = serde_json::from_slice::(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, 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, 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, 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, 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()), } }