新增全局流水号
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class PayFlows {
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_flows';
|
||||
private static ?self $instance = null;
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
public static function get_instance(): self {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function render_page(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
}
|
||||
$keyword = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
|
||||
$event_type = isset($_GET['event_type']) ? sanitize_key((string) wp_unslash($_GET['event_type'])) : '';
|
||||
$asset = isset($_GET['asset']) ? sanitize_key((string) wp_unslash($_GET['asset'])) : '';
|
||||
$direction = isset($_GET['direction']) ? sanitize_key((string) wp_unslash($_GET['direction'])) : '';
|
||||
$user_id = isset($_GET['user_id']) ? max(0, (int) $_GET['user_id']) : 0;
|
||||
$date_from = isset($_GET['date_from']) ? $this->sanitize_date((string) wp_unslash($_GET['date_from'])) : '';
|
||||
$date_to = isset($_GET['date_to']) ? $this->sanitize_date((string) wp_unslash($_GET['date_to'])) : '';
|
||||
$page = max(1, (int) ($_GET['paged'] ?? 1));
|
||||
$query = \AKSRT\Finance\FinancialFlowQuery::get_instance();
|
||||
$result = $query->search([
|
||||
'keyword' => $keyword,
|
||||
'event_type' => $event_type,
|
||||
'asset' => $asset,
|
||||
'direction' => $direction,
|
||||
'user_id' => $user_id,
|
||||
'date_from' => $this->date_bound_utc($date_from, false),
|
||||
'date_to' => $this->date_bound_utc($date_to, true),
|
||||
'page' => $page,
|
||||
'per_page' => 20,
|
||||
]);
|
||||
$pages = max(1, (int) ceil($result['total'] / $result['per_page']));
|
||||
|
||||
echo '<div class="wrap"><h1>' . esc_html__('流水明细', 'aksrt') . '</h1>';
|
||||
echo '<p class="description">' . esc_html__('仅展示已经确认发生的现金、余额和积分变动;流水不可编辑。', 'aksrt') . '</p>';
|
||||
echo '<form method="get" style="margin:12px 0;display:flex;gap:6px;align-items:center;flex-wrap:wrap;">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '">';
|
||||
echo '<input type="search" name="s" value="' . esc_attr($keyword) . '" placeholder="' . esc_attr__('流水号 / 订单号 / 提现单号 / 渠道交易号', 'aksrt') . '" style="min-width:280px;">';
|
||||
echo '<input type="number" min="1" name="user_id" value="' . esc_attr($user_id > 0 ? (string) $user_id : '') . '" placeholder="' . esc_attr__('用户 ID', 'aksrt') . '" class="small-text">';
|
||||
echo '<select name="event_type"><option value="">' . esc_html__('全部事件', 'aksrt') . '</option>';
|
||||
foreach ($query->event_types() as $key => $label) {
|
||||
echo '<option value="' . esc_attr($key) . '"' . selected($event_type, $key, false) . '>' . esc_html($this->event_label($label)) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
$this->render_select('asset', $asset, __('全部资产', 'aksrt'), ['cash' => __('现金', 'aksrt'), 'balance' => __('余额', 'aksrt'), 'points' => __('积分', 'aksrt')]);
|
||||
$this->render_select('direction', $direction, __('全部方向', 'aksrt'), ['in' => __('收入', 'aksrt'), 'out' => __('支出', 'aksrt')]);
|
||||
echo '<label>' . esc_html__('从', 'aksrt') . ' <input type="date" name="date_from" value="' . esc_attr($date_from) . '"></label>';
|
||||
echo '<label>' . esc_html__('至', 'aksrt') . ' <input type="date" name="date_to" value="' . esc_attr($date_to) . '"></label>';
|
||||
submit_button(__('筛选', 'aksrt'), 'secondary', '', false);
|
||||
if ($keyword !== '' || $event_type !== '' || $asset !== '' || $direction !== '' || $user_id > 0 || $date_from !== '' || $date_to !== '') {
|
||||
echo '<a class="button" href="' . esc_url($this->page_url()) . '">' . esc_html__('清除条件', 'aksrt') . '</a>';
|
||||
}
|
||||
echo '</form>';
|
||||
echo '<p>' . esc_html(sprintf(__('共 %d 条流水', 'aksrt'), (int) $result['total'])) . '</p>';
|
||||
echo '<table class="wp-list-table widefat fixed striped"><thead><tr>';
|
||||
echo '<th style="width:180px">' . esc_html__('流水号 / 时间', 'aksrt') . '</th>';
|
||||
echo '<th style="width:130px">' . esc_html__('事件', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('用户与分录', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('关联业务', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('渠道交易号', 'aksrt') . '</th>';
|
||||
echo '</tr></thead><tbody>';
|
||||
if ($result['items'] === []) {
|
||||
echo '<tr><td colspan="5">' . esc_html__('暂无匹配流水', 'aksrt') . '</td></tr>';
|
||||
}
|
||||
foreach ($result['items'] as $flow) {
|
||||
echo '<tr>';
|
||||
echo '<td><code>' . esc_html((string) $flow['flow_number']) . '</code><br><span class="description">' . esc_html($this->local_time((string) $flow['created_at'])) . '</span></td>';
|
||||
echo '<td><strong>' . esc_html($this->event_label((string) $flow['event_type'])) . '</strong><br><code>' . esc_html((string) $flow['event_type']) . '</code></td>';
|
||||
echo '<td>' . $this->entries_html((array) $flow['entries']) . '</td>';
|
||||
echo '<td>' . $this->reference_html($flow) . '</td>';
|
||||
echo '<td>' . ((string) ($flow['provider_transaction_id'] ?? '') !== '' ? '<code>' . esc_html((string) $flow['provider_transaction_id']) . '</code>' : '—') . '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
echo '</tbody></table>';
|
||||
if ($pages > 1) {
|
||||
echo '<div class="tablenav"><div class="tablenav-pages">' . wp_kses_post((string) paginate_links([
|
||||
'base' => add_query_arg('paged', '%#%'),
|
||||
'format' => '',
|
||||
'prev_text' => '«',
|
||||
'next_text' => '»',
|
||||
'total' => $pages,
|
||||
'current' => $page,
|
||||
])) . '</div></div>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/** @param array<string,string> $options */
|
||||
private function render_select(string $name, string $value, string $empty_label, array $options): void {
|
||||
echo '<select name="' . esc_attr($name) . '"><option value="">' . esc_html($empty_label) . '</option>';
|
||||
foreach ($options as $key => $label) {
|
||||
echo '<option value="' . esc_attr($key) . '"' . selected($value, $key, false) . '>' . esc_html($label) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
}
|
||||
|
||||
/** @param array<int,array<string,mixed>> $entries */
|
||||
private function entries_html(array $entries): string {
|
||||
if ($entries === []) {
|
||||
return '—';
|
||||
}
|
||||
$lines = [];
|
||||
foreach ($entries as $entry) {
|
||||
$user_id = (int) ($entry['user_id'] ?? 0);
|
||||
$user = $user_id > 0 ? get_userdata($user_id) : null;
|
||||
$user_label = $user ? $user->display_name . ' (#' . $user_id . ')' : '#' . $user_id;
|
||||
$asset = (string) ($entry['asset'] ?? '');
|
||||
$delta = (int) ($entry['delta_minor'] ?? 0);
|
||||
$amount = $asset === 'points'
|
||||
? (string) abs($delta)
|
||||
: \AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, abs($delta))->decimal();
|
||||
$asset_label = ['cash' => __('现金', 'aksrt'), 'balance' => __('余额', 'aksrt'), 'points' => __('积分', 'aksrt')][$asset] ?? $asset;
|
||||
$lines[] = esc_html($user_label) . ' · ' . esc_html($asset_label) . ' <strong>' . ($delta > 0 ? '+' : '-') . esc_html($amount) . '</strong>';
|
||||
}
|
||||
return implode('<br>', $lines);
|
||||
}
|
||||
|
||||
/** @param array<string,mixed> $flow */
|
||||
private function reference_html(array $flow): string {
|
||||
$lines = [];
|
||||
if (!empty($flow['order_number'])) {
|
||||
$url = add_query_arg(['page' => PayOrders::PAGE_SLUG, 'order' => (string) $flow['order_number']], admin_url('admin.php'));
|
||||
$lines[] = esc_html__('订单', 'aksrt') . ' <a href="' . esc_url($url) . '"><code>' . esc_html((string) $flow['order_number']) . '</code></a>';
|
||||
}
|
||||
if (!empty($flow['withdrawal_number'])) {
|
||||
$lines[] = esc_html__('提现', 'aksrt') . ' <code>' . esc_html((string) $flow['withdrawal_number']) . '</code>';
|
||||
}
|
||||
if ($lines === [] && !empty($flow['ref_key'])) {
|
||||
$lines[] = esc_html((string) ($flow['ref_type'] ?? '')) . ' · ' . esc_html((string) $flow['ref_key']);
|
||||
}
|
||||
return $lines !== [] ? implode('<br>', $lines) : '—';
|
||||
}
|
||||
|
||||
private function event_label(string $event_type): string {
|
||||
$labels = [
|
||||
'order_payment' => __('订单支付', 'aksrt'),
|
||||
'order_refund' => __('订单退款', 'aksrt'),
|
||||
'withdraw_hold' => __('提现冻结', 'aksrt'),
|
||||
'withdraw_refund' => __('提现退回', 'aksrt'),
|
||||
'withdraw_payout' => __('提现打款', 'aksrt'),
|
||||
'creator_income' => __('创作收益', 'aksrt'),
|
||||
'admin_adjustment' => __('后台调账', 'aksrt'),
|
||||
'points_exchange' => __('积分兑换', 'aksrt'),
|
||||
'transfer' => __('用户转账', 'aksrt'),
|
||||
];
|
||||
return $labels[$event_type] ?? $event_type;
|
||||
}
|
||||
|
||||
private function sanitize_date(string $date): string {
|
||||
return preg_match('/^\d{4}-\d{2}-\d{2}$/', $date) === 1 ? $date : '';
|
||||
}
|
||||
|
||||
private function date_bound_utc(string $date, bool $exclusive_end): string {
|
||||
if ($date === '') {
|
||||
return '';
|
||||
}
|
||||
$local = new \DateTimeImmutable($date . ' 00:00:00', wp_timezone());
|
||||
if ($exclusive_end) {
|
||||
$local = $local->modify('+1 day');
|
||||
}
|
||||
return $local->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
private function local_time(string $utc): string {
|
||||
if ($utc === '') {
|
||||
return '';
|
||||
}
|
||||
try {
|
||||
return (new \DateTimeImmutable($utc, new \DateTimeZone('UTC')))->setTimezone(wp_timezone())->format('Y-m-d H:i:s');
|
||||
} catch (\Exception $e) {
|
||||
return $utc;
|
||||
}
|
||||
}
|
||||
|
||||
private function page_url(): string {
|
||||
return add_query_arg(['page' => self::PAGE_SLUG], admin_url('admin.php'));
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,13 @@ class PayMenus {
|
||||
wp_die(esc_html__('订单模块未初始化。', 'aksrt'));
|
||||
},
|
||||
],
|
||||
'flows' => [
|
||||
'slug' => PayFlows::PAGE_SLUG,
|
||||
'title' => __('流水明细', 'aksrt'),
|
||||
'callback' => static function (): void {
|
||||
PayFlows::get_instance()->render_page();
|
||||
},
|
||||
],
|
||||
'products' => [
|
||||
'slug' => 'aksrt_pay_products',
|
||||
'title' => __('付费文章', 'aksrt'),
|
||||
|
||||
@@ -79,6 +79,10 @@ class PayOrders {
|
||||
}
|
||||
|
||||
$items = $repo->get_orders_admin($args, $search);
|
||||
$flow_numbers = \AKSRT\Finance\FinancialFlowQuery::get_instance()->order_payment_number_map(array_map(
|
||||
static fn(array $row): string => (string) ($row['order_number'] ?? ''),
|
||||
$items
|
||||
));
|
||||
$total = $repo->get_orders_count($args, $search);
|
||||
$pages = max(1, (int) ceil($total / $per_page));
|
||||
$labels = class_exists('\AKSRT\Core\DomainModel')
|
||||
@@ -102,6 +106,7 @@ class PayOrders {
|
||||
'user_id' => $user_id > 0 ? $user_id : null,
|
||||
'payment_method' => $pay_method !== '' ? $pay_method : null,
|
||||
'status' => ($status !== '' && $status !== 'all') ? $status : null,
|
||||
's' => $search !== '' ? $search : null,
|
||||
]));
|
||||
$cls = ($product_type === $tkey) ? 'current' : '';
|
||||
$tab_bits[] = '<li><a class="' . esc_attr($cls) . '" href="' . esc_url($url) . '">' . esc_html($tlabel) . '</a></li>';
|
||||
@@ -126,6 +131,7 @@ class PayOrders {
|
||||
'user_id' => $user_id > 0 ? $user_id : null,
|
||||
'payment_method' => $mkey !== '' ? $mkey : null,
|
||||
'status' => ($status !== '' && $status !== 'all') ? $status : null,
|
||||
's' => $search !== '' ? $search : null,
|
||||
]));
|
||||
$cls = ($pay_method === $mkey) ? 'current' : '';
|
||||
$m_bits[] = '<li><a class="' . esc_attr($cls) . '" href="' . esc_url($url) . '">' . esc_html($mlabel) . '</a></li>';
|
||||
@@ -156,7 +162,7 @@ class PayOrders {
|
||||
}
|
||||
echo '</select> ';
|
||||
echo '<input type="number" min="0" name="user_id" value="' . esc_attr($user_id > 0 ? (string) $user_id : '') . '" placeholder="' . esc_attr__('用户 ID', 'aksrt') . '" class="small-text" /> ';
|
||||
echo '<input type="search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('订单号 / 类型 / 用户', 'aksrt') . '" /> ';
|
||||
echo '<input type="search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('订单号 / 流水号 / 类型 / 用户', 'aksrt') . '" /> ';
|
||||
submit_button(__('筛选', 'aksrt'), 'secondary', '', false);
|
||||
if ($product_id > 0 || $product_type !== '' || $user_id > 0 || $pay_method !== '') {
|
||||
$filter_bits = [];
|
||||
@@ -189,12 +195,12 @@ class PayOrders {
|
||||
|
||||
echo '<table class="wp-list-table widefat fixed striped">';
|
||||
echo '<thead><tr>';
|
||||
echo '<th>' . esc_html__('订单号', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('订单号 / 支付流水号', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('用户', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('类型', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('金额', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('商品信息', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('金额 / 支付方式', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('状态', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('时间', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('创建 / 支付时间', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('操作', 'aksrt') . '</th>';
|
||||
echo '</tr></thead><tbody>';
|
||||
|
||||
@@ -208,14 +214,22 @@ class PayOrders {
|
||||
$user = $user_id > 0 ? get_userdata($user_id) : null;
|
||||
$status_key = (string) ($row['status'] ?? '');
|
||||
$status_label = $labels[$status_key] ?? $status_key;
|
||||
$flow_number = (string) ($flow_numbers[$order_number] ?? '');
|
||||
$product_id = (int) ($row['product_id'] ?? 0);
|
||||
$product_type = sanitize_key((string) ($row['product_type'] ?? ''));
|
||||
$meta = is_array($row['meta_payload'] ?? null) ? $row['meta_payload'] : [];
|
||||
$product_title = (string) ($meta['product_title'] ?? $meta['title'] ?? $meta['package_title'] ?? '');
|
||||
if ($product_title === '' && $product_id > 0 && in_array($product_type, ['resource', 'shop_product'], true)) {
|
||||
$product_title = (string) get_the_title($product_id);
|
||||
}
|
||||
|
||||
echo '<tr>';
|
||||
echo '<td><code>' . esc_html($order_number) . '</code></td>';
|
||||
echo '<td><code>' . esc_html($order_number) . '</code><br><span class="description">' . esc_html__('流水:', 'aksrt') . ($flow_number !== '' ? '<code>' . esc_html($flow_number) . '</code>' : '—') . '</span></td>';
|
||||
echo '<td>' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . '</td>';
|
||||
echo '<td>' . esc_html((string) ($row['product_type'] ?? '')) . '</td>';
|
||||
echo '<td>' . esc_html($this->cash_decimal((string) ($row['amount'] ?? '0'))) . ' ' . esc_html((string) ($row['currency'] ?? '')) . '</td>';
|
||||
echo '<td><strong>' . esc_html($product_title !== '' ? $product_title : $product_type) . '</strong><br><span class="description">' . esc_html($product_type) . ($product_id > 0 ? ' #' . esc_html((string) $product_id) : '') . '</span></td>';
|
||||
echo '<td><strong>' . esc_html($this->order_amount($row)) . '</strong><br><span class="description">' . esc_html((string) ($row['payment_method'] ?? '')) . '</span></td>';
|
||||
echo '<td>' . esc_html($status_label) . '</td>';
|
||||
echo '<td>' . esc_html((string) ($row['created_at'] ?? '')) . '</td>';
|
||||
echo '<td>' . esc_html((string) ($row['created_at'] ?? '')) . '<br><span class="description">' . esc_html((string) ($row['paid_at'] ?? '') ?: '—') . '</span></td>';
|
||||
echo '<td style="display:flex;gap:6px;flex-wrap:wrap;align-items:center;">';
|
||||
echo '<a class="button button-small" href="' . esc_url($this->page_url('aksrt_orders', ['order' => $order_number])) . '">' . esc_html__('详情', 'aksrt') . '</a>';
|
||||
$this->render_order_quick_status_buttons($order_number, $status_key);
|
||||
@@ -262,6 +276,7 @@ class PayOrders {
|
||||
? \AKSRT\Core\DomainModel::order_status_labels()
|
||||
: [];
|
||||
$context = $repo->get_admin_order_context($order_number);
|
||||
$flow_number = \AKSRT\Finance\FinancialFlowQuery::get_instance()->order_payment_number($order_number);
|
||||
$user_id = (int) ($order['user_id'] ?? 0);
|
||||
$user = $user_id > 0 ? get_userdata($user_id) : null;
|
||||
$status = (string) ($order['status'] ?? '');
|
||||
@@ -277,13 +292,14 @@ class PayOrders {
|
||||
echo '<table class="widefat striped" style="max-width:960px;"><tbody>';
|
||||
$rows = [
|
||||
__('订单号', 'aksrt') => $order_number,
|
||||
__('支付流水号', 'aksrt') => $flow_number !== '' ? $flow_number : '—',
|
||||
__('用户', 'aksrt') => $user ? $user->display_name . ' (#' . $user_id . ')' : (string) $user_id,
|
||||
__('商品类型', 'aksrt') => (string) ($order['product_type'] ?? ''),
|
||||
__('商品 ID', 'aksrt') => (string) ($order['product_id'] ?? ''),
|
||||
__('金额', 'aksrt') => $this->cash_decimal((string) ($order['amount'] ?? '0')) . ' ' . (string) ($order['currency'] ?? ''),
|
||||
__('金额', 'aksrt') => $this->order_amount($order),
|
||||
__('状态', 'aksrt') => $labels[$status] ?? $status,
|
||||
__('支付方式', 'aksrt') => (string) ($order['payment_method'] ?? ''),
|
||||
__('交易号', 'aksrt') => (string) ($order['payment_transaction_id'] ?? ''),
|
||||
__('渠道交易号', 'aksrt') => (string) ($order['payment_transaction_id'] ?? ''),
|
||||
__('创建时间', 'aksrt') => (string) ($order['created_at'] ?? ''),
|
||||
__('支付时间', 'aksrt') => (string) ($order['paid_at'] ?? ''),
|
||||
__('买家备注', 'aksrt') => $customer_note,
|
||||
@@ -440,6 +456,16 @@ class PayOrders {
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array<string,mixed> $order */
|
||||
private function order_amount(array $order): string {
|
||||
$currency = sanitize_key((string) ($order['currency'] ?? ''));
|
||||
$method = sanitize_key((string) ($order['payment_method'] ?? ''));
|
||||
if ($currency === 'points' || $method === 'points') {
|
||||
return max(0, (int) ($order['amount'] ?? 0)) . ' ' . __('积分', 'aksrt');
|
||||
}
|
||||
return $this->cash_decimal((string) ($order['amount'] ?? '0')) . ' ' . strtoupper((string) ($order['currency'] ?? 'CNY'));
|
||||
}
|
||||
|
||||
private function render_header(string $title): void {
|
||||
echo '<div class="wrap">';
|
||||
echo '<h1>' . esc_html($title) . '</h1>';
|
||||
|
||||
@@ -39,6 +39,10 @@ class PayWithdrawals {
|
||||
}
|
||||
|
||||
$status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
|
||||
$keyword = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
|
||||
$user_filter = isset($_GET['user_id']) ? max(0, (int) $_GET['user_id']) : 0;
|
||||
$date_from = isset($_GET['date_from']) ? $this->sanitize_date((string) wp_unslash($_GET['date_from'])) : '';
|
||||
$date_to = isset($_GET['date_to']) ? $this->sanitize_date((string) wp_unslash($_GET['date_to'])) : '';
|
||||
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
|
||||
$per_page = 20;
|
||||
$svc = \AKSRT\Payment\Withdrawal::get_instance();
|
||||
@@ -79,31 +83,47 @@ class PayWithdrawals {
|
||||
);
|
||||
}
|
||||
echo '</select> ';
|
||||
echo '<input type="search" name="s" value="' . esc_attr($keyword) . '" placeholder="' . esc_attr__('提现单号 / 用户 ID', 'aksrt') . '" /> ';
|
||||
echo '<input type="number" min="1" name="user_id" value="' . esc_attr($user_filter > 0 ? (string) $user_filter : '') . '" placeholder="' . esc_attr__('用户 ID', 'aksrt') . '" class="small-text" /> ';
|
||||
echo '<label>' . esc_html__('从', 'aksrt') . ' <input type="date" name="date_from" value="' . esc_attr($date_from) . '"></label> ';
|
||||
echo '<label>' . esc_html__('至', 'aksrt') . ' <input type="date" name="date_to" value="' . esc_attr($date_to) . '"></label> ';
|
||||
submit_button(__('筛选', 'aksrt'), 'secondary', '', false);
|
||||
if ($status !== '' || $keyword !== '' || $user_filter > 0 || $date_from !== '' || $date_to !== '') {
|
||||
echo ' <a class="button" href="' . esc_url($this->page_url(self::PAGE_SLUG)) . '">' . esc_html__('清除条件', 'aksrt') . '</a>';
|
||||
}
|
||||
echo '</form>';
|
||||
|
||||
$items = $svc->get_all([
|
||||
'status' => $status,
|
||||
'keyword' => $keyword,
|
||||
'user_id' => $user_filter,
|
||||
'date_from' => $date_from,
|
||||
'date_to' => $date_to,
|
||||
'limit' => $per_page,
|
||||
'offset' => ($page_num - 1) * $per_page,
|
||||
]);
|
||||
$total = $svc->get_count(['status' => $status]);
|
||||
$total = $svc->get_count([
|
||||
'status' => $status,
|
||||
'keyword' => $keyword,
|
||||
'user_id' => $user_filter,
|
||||
'date_from' => $date_from,
|
||||
'date_to' => $date_to,
|
||||
]);
|
||||
$pages = max(1, (int) ceil($total / $per_page));
|
||||
|
||||
echo '<table class="wp-list-table widefat fixed striped">';
|
||||
echo '<thead><tr>';
|
||||
echo '<th>ID</th>';
|
||||
echo '<th>' . esc_html__('提现单号', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('用户', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('金额', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('方式', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('账户', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('金额明细', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('渠道 / 账户', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('状态', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('申请时间', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('申请 / 处理时间', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('操作', 'aksrt') . '</th>';
|
||||
echo '</tr></thead><tbody>';
|
||||
|
||||
if (!$items) {
|
||||
echo '<tr><td colspan="8">' . esc_html__('暂无提现记录', 'aksrt') . '</td></tr>';
|
||||
echo '<tr><td colspan="7">' . esc_html__('暂无提现记录', 'aksrt') . '</td></tr>';
|
||||
}
|
||||
|
||||
foreach ($items as $row) {
|
||||
@@ -115,25 +135,21 @@ class PayWithdrawals {
|
||||
$net = (string) ($row['amount'] ?? '0.00');
|
||||
$fee = (int) ($row['fee_rate'] ?? 0);
|
||||
$acct = (string) ($row['display_account'] ?? $row['account_info'] ?? '');
|
||||
$amount_txt = $net;
|
||||
if ($gross !== $net) {
|
||||
$amount_txt = sprintf(
|
||||
/* translators: 1: gross 2: net 3: fee percent */
|
||||
__('申请 %1$s / 到账 %2$s(手续费 %3$d%%)', 'aksrt'),
|
||||
$gross,
|
||||
$net,
|
||||
$fee
|
||||
);
|
||||
}
|
||||
$amount_txt = sprintf(
|
||||
/* translators: 1: gross 2: net 3: fee percent */
|
||||
__('申请 %1$s / 到账 %2$s(手续费 %3$d%%)', 'aksrt'),
|
||||
$gross,
|
||||
$net,
|
||||
$fee
|
||||
);
|
||||
|
||||
echo '<tr>';
|
||||
echo '<td>' . esc_html((string) $id) . '</td>';
|
||||
echo '<td><code>' . esc_html((string) ($row['withdrawal_number'] ?? '')) . '</code></td>';
|
||||
echo '<td>' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . '</td>';
|
||||
echo '<td>' . esc_html($amount_txt) . '</td>';
|
||||
echo '<td>' . esc_html((string) ($row['method'] ?? '')) . '</td>';
|
||||
echo '<td>' . esc_html($acct) . '</td>';
|
||||
echo '<td><strong>' . esc_html((string) ($row['method'] ?? '')) . '</strong><br><span class="description">' . esc_html($acct) . '</span></td>';
|
||||
echo '<td>' . esc_html($status_options[$st] ?? $st) . '</td>';
|
||||
echo '<td>' . esc_html((string) ($row['created_at'] ?? '')) . '</td>';
|
||||
echo '<td>' . esc_html((string) ($row['created_at'] ?? '')) . '<br><span class="description">' . esc_html((string) ($row['processed_at'] ?? '') ?: '—') . '</span></td>';
|
||||
echo '<td>';
|
||||
$next_statuses = $st === 'pending' ? ['processing', 'rejected'] : ($st === 'processing' ? ['completed', 'rejected'] : []);
|
||||
if ($next_statuses === []) {
|
||||
@@ -258,6 +274,10 @@ class PayWithdrawals {
|
||||
return add_query_arg(array_merge(['page' => $slug], $args), admin_url('admin.php'));
|
||||
}
|
||||
|
||||
private function sanitize_date(string $date): string {
|
||||
return preg_match('/^\d{4}-\d{2}-\d{2}$/', $date) === 1 ? $date : '';
|
||||
}
|
||||
|
||||
private function render_pagination(int $pages, int $page_num): void {
|
||||
if ($pages <= 1) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user