Merge branch 'master' into gptimage2-param-limit-in-backend

This commit is contained in:
2026-07-14 17:59:31 +08:00
39 changed files with 2668 additions and 745 deletions
@@ -16,6 +16,7 @@ use module_runtime::{
build_runtime_profile_recharge_order_admin_list_input,
build_runtime_profile_recharge_refund_hold_prepare_input,
build_runtime_profile_recharge_refund_hold_preview_input,
build_runtime_profile_recharge_refund_manual_review_resolve_input,
build_runtime_profile_recharge_refund_settlement_plan,
build_runtime_profile_wallet_manual_restriction_upsert_input,
};
@@ -27,7 +28,8 @@ use sha2::{Digest, Sha256};
use shared_contracts::admin::{
AdminProfileWalletPayload, AdminRechargeOrderEntryPayload, AdminRechargeOrderListQuery,
AdminRechargeOrderListResponse, AdminRechargeRefundActionResponse,
AdminRechargeRefundExecuteRequest, AdminRechargeRefundHoldPayload, AdminRechargeRefundPayload,
AdminRechargeRefundExecuteRequest, AdminRechargeRefundHoldPayload,
AdminRechargeRefundManualReviewResolveRequest, AdminRechargeRefundPayload,
AdminRechargeRefundPreviewRequest, AdminRechargeRefundPreviewResponse,
AdminRechargeRefundRegisterRequest, AdminUserDetailQuery, AdminUserDetailResponse,
AdminUserSummaryPayload, AdminWalletManualRestrictionPayload, AdminWalletRestrictionRequest,
@@ -448,6 +450,49 @@ pub async fn admin_register_recharge_refund(
))
}
pub async fn admin_resolve_recharge_refund_manual_review(
State(state): State<AppState>,
Extension(request_context): Extension<RequestContext>,
Extension(admin): Extension<AuthenticatedAdmin>,
Json(payload): Json<AdminRechargeRefundManualReviewResolveRequest>,
) -> Result<Json<Value>, Response> {
let input = build_runtime_profile_recharge_refund_manual_review_resolve_input(
payload.out_refund_no,
admin.session().subject.clone(),
payload.reason,
)
.map_err(|message| {
error_response(
&request_context,
AppError::from_status(StatusCode::BAD_REQUEST).with_message(message),
)
})?;
let (refund, _, _, resolution_code) = state
.spacetime_client()
.resolve_profile_recharge_refund_manual_review(input)
.await
.map_err(|error| {
error_response(
&request_context,
AppError::from_status(StatusCode::CONFLICT)
.with_message("人工确认退款归属失败")
.with_details(json!({"message": error.to_string()})),
)
})?;
let entry = load_order(&state, &request_context, &refund.order_id).await?;
let user = load_user_summary(&state, &entry.order.user_id);
Ok(json_success_body(
Some(&request_context),
AdminRechargeRefundActionResponse {
out_refund_no: refund.out_refund_no,
provider_status: refund.provider_status.as_str().to_string(),
result_code: resolution_code,
provider_status_unknown: false,
order: map_order_entry(entry, user),
},
))
}
pub async fn admin_update_wallet_restriction(
State(state): State<AppState>,
Extension(request_context): Extension<RequestContext>,
@@ -872,6 +917,9 @@ fn map_refund(refund: RuntimeProfileRechargeRefundSnapshot) -> AdminRechargeRefu
unrecovered_points: refund.unrecovered_points,
recovery_status: refund.recovery_status.as_str().to_string(),
last_error_code: refund.last_error_code,
manual_review_resolved_by_admin_user_id: refund.manual_review_resolved_by_admin_user_id,
manual_review_resolution_reason: refund.manual_review_resolution_reason,
manual_review_resolved_at_micros: refund.manual_review_resolved_at_micros,
}
}
@@ -21,7 +21,7 @@ use crate::{
admin_recharge::{
admin_execute_recharge_refund, admin_get_user_detail, admin_list_recharge_orders,
admin_preview_recharge_refund, admin_register_recharge_refund,
admin_update_wallet_restriction,
admin_resolve_recharge_refund_manual_review, admin_update_wallet_restriction,
},
runtime_profile::{
admin_disable_profile_redeem_code, admin_disable_profile_task_config,
@@ -275,6 +275,12 @@ pub fn router(state: AppState) -> Router<AppState> {
require_admin_auth,
)),
)
.route(
"/admin/api/profile/recharge-refunds/manual-review/resolve",
post(admin_resolve_recharge_refund_manual_review).route_layer(
middleware::from_fn_with_state(state.clone(), require_admin_auth),
),
)
.route(
"/admin/api/profile/users/detail",
get(admin_get_user_detail).route_layer(middleware::from_fn_with_state(
@@ -54,7 +54,11 @@ pub(crate) struct PersistedWechatRefundObservation {
pub fn spawn_profile_recharge_refund_reconciliation_worker(state: AppState) {
if !state.wechat_pay_refund_reconciliation_enabled() {
debug!("wechat pay refund reconciliation worker is disabled");
if state.wechat_pay_client().supports_refund_reconciliation() {
warn!("real wechat pay is active but refund reconciliation worker is disabled");
} else {
debug!("wechat pay refund reconciliation worker is disabled");
}
return;
}
if !state.wechat_pay_client().supports_refund_reconciliation() {
@@ -62,6 +66,8 @@ pub fn spawn_profile_recharge_refund_reconciliation_worker(state: AppState) {
return;
}
info!("wechat pay refund reconciliation worker is enabled");
tokio::spawn(async move {
let mut trade_bill_scan_slot = 0;
let mut hold_not_found_counts = BTreeMap::<String, u8>::new();
@@ -940,6 +946,9 @@ mod tests {
recovery_status: RuntimeProfileRechargeRefundRecoveryStatus::NotApplicable,
last_recovery_ledger_id: None,
last_error_code: None,
manual_review_resolved_by_admin_user_id: None,
manual_review_resolution_reason: None,
manual_review_resolved_at_micros: None,
}
}