feat(admin): redesign withdrawal review page and drop dead withdrawal code
- Split PayWithdrawals into shell + PayWithdrawalsList flat sibling trait - Add process/policy callout, open/paid/fee stats, status views with counts - Filter by source and payout method; reset keeps the status view - Show net payout with gross/fee breakdown, copyable payee, note column - Replace inline select form with per-row actions and a confirm dialog; reject requires a reason; double-submit guard; retry reuses the idempotency key; redirect back preserving filters and paging - Withdrawal: add get_status_counts() and method filter; get_stats() now returns open_gross / paid_net / paid_fee - Remove unused REST routes (/payment/withdrawals, /transition), admin_withdrawals(), update_status(), has_open_withdrawal(), count_open(), resolve_gross_amount() Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1d6bc8f7cc
commit
d98f8d7975
9 files changed
+862
-414
No files matched your search
@@ -18,96 +18,5 @@ class PaymentController extends ControllerBase {
|
||||
|
||||
public function register_routes(): void {
|
||||
$this->register_section_routes('payment', 'payment');
|
||||
|
||||
register_rest_route(self::API_NAMESPACE, '/payment/withdrawals', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'get_withdrawals'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
'args' => [
|
||||
'page' => ['type' => 'integer', 'minimum' => 1, 'default' => 1],
|
||||
'per_page' => ['type' => 'integer', 'minimum' => 1, 'maximum' => 50, 'default' => 20],
|
||||
'status' => ['type' => 'string', 'enum' => ['', 'pending', 'processing', 'completed', 'rejected']],
|
||||
],
|
||||
]);
|
||||
|
||||
// Withdrawal status update
|
||||
register_rest_route(self::API_NAMESPACE, '/payment/withdrawals/(?P<id>\d+)', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'update_withdrawal_status'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
'args' => [
|
||||
'id' => ['type' => 'integer', 'minimum' => 1, 'required' => true],
|
||||
'status' => ['type' => 'string', 'required' => true, 'enum' => ['processing', 'completed', 'rejected']],
|
||||
'note' => ['type' => 'string', 'default' => '', 'maxLength' => 1000, 'sanitize_callback' => 'sanitize_textarea_field'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_withdrawals(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Payment\Withdrawal')) {
|
||||
return $this->success(['data' => ['items' => [], 'total' => 0, 'page' => 1, 'total_pages' => 1, 'stats' => []]]);
|
||||
}
|
||||
|
||||
$status = sanitize_key((string) $request->get_param('status'));
|
||||
$page = max(1, (int) $request->get_param('page'));
|
||||
$per_page = max(1, min(50, (int) $request->get_param('per_page'))) ?: 20;
|
||||
|
||||
$items = \AKSRT\Payment\Withdrawal::get_instance()->get_all([
|
||||
'status' => $status,
|
||||
'limit' => $per_page,
|
||||
'offset' => ($page - 1) * $per_page,
|
||||
]);
|
||||
|
||||
$total = \AKSRT\Payment\Withdrawal::get_instance()->get_count(['status' => $status]);
|
||||
$stats = \AKSRT\Payment\Withdrawal::get_instance()->get_stats();
|
||||
|
||||
return $this->success([
|
||||
'data' => [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'total_pages' => max(1, (int) ceil($total / $per_page)),
|
||||
'stats' => $stats,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /payment/withdrawals/{id} — 更新提现状态
|
||||
*/
|
||||
public function update_withdrawal_status(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Payment\Withdrawal')) {
|
||||
return $this->error('提现模块未初始化', 500);
|
||||
}
|
||||
|
||||
$id = (int) $request->get_param('id');
|
||||
$params = $this->get_json_params($request);
|
||||
$new_status = sanitize_key((string) ($params['status'] ?? ''));
|
||||
$note = sanitize_textarea_field((string) ($params['note'] ?? ''));
|
||||
|
||||
$valid_statuses = ['processing', 'completed', 'rejected'];
|
||||
if (!in_array($new_status, $valid_statuses, true)) {
|
||||
return $this->error('无效的提现状态');
|
||||
}
|
||||
|
||||
if ($id <= 0) {
|
||||
return $this->error('无效的提现 ID');
|
||||
}
|
||||
|
||||
$operation_key = \AKSRT\Ledger\OperationKey::from_request($request, 'admin-withdraw-transition', get_current_user_id());
|
||||
if (is_wp_error($operation_key)) {
|
||||
return $this->error($operation_key->get_error_message(), (int) ($operation_key->get_error_data()['status'] ?? 400));
|
||||
}
|
||||
$result = \AKSRT\Payment\Withdrawal::get_instance()->process_status_change($id, $new_status, $note, $operation_key);
|
||||
|
||||
if (is_wp_error($result)) {
|
||||
$data = $result->get_error_data();
|
||||
return $this->error($result->get_error_message(), is_array($data) ? (int) ($data['status'] ?? 400) : 400);
|
||||
}
|
||||
if (is_array($result)) {
|
||||
return $this->success(array_merge(['message' => __('提现状态已更新', 'aksrt')], $result));
|
||||
}
|
||||
return $this->error(__('更新失败', 'aksrt'));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user