diff --git a/docs/zibll-gap-matrix.md b/docs/zibll-gap-matrix.md
index c38a1d47..9f042571 100644
--- a/docs/zibll-gap-matrix.md
+++ b/docs/zibll-gap-matrix.md
@@ -99,8 +99,8 @@ Note: zibll「商品管理」`zibpay_product_page` is **paid article sales**, no
| 2b Shipping/after-sale/coupons/income/rebate | done |
| 3 users.php submenus | done |
| 4 SPA strip | done (ops routes redirect to native) |
-| 5+ behavior parity | done for ops shell — shipping/pending/express; after-sale; order note/actions; coupons create/edit/batch + product_scope; charge-cards; product commerce meta; plate allow_view/add_limit/follow/role + moderator roles + icon/sort; topic visibility; pay-menus modular split (coupons/charge-cards/shipping/after-sale) |
-| 5 remaining | zibll pay-hide/points plate options out of scope; optional further shell trim (orders/income still in pay-menus ~1k lines) |
+| 5+ behavior parity | done — native ops shell modularized (orders/shipping/after-sale/coupons/charge-cards/income/rebate/withdraw) + plate moderator roles/advanced limits/icon/sort + coupon product_scope create/edit/batch |
+| 5 remaining | zibll pay-hide/points plate options out of scope |
## Dual-UI rule
diff --git a/functions.php b/functions.php
index 45e0d6d5..e12f4b2f 100644
--- a/functions.php
+++ b/functions.php
@@ -368,6 +368,18 @@ if (is_admin() && class_exists('AKSRT_Admin_Pay_Shipping')) {
if (is_admin() && class_exists('AKSRT_Admin_Pay_After_Sale')) {
AKSRT_Admin_Pay_After_Sale::get_instance()->init();
}
+if (is_admin() && class_exists('AKSRT_Admin_Pay_Orders')) {
+ AKSRT_Admin_Pay_Orders::get_instance()->init();
+}
+if (is_admin() && class_exists('AKSRT_Admin_Pay_Withdrawals')) {
+ AKSRT_Admin_Pay_Withdrawals::get_instance()->init();
+}
+if (is_admin() && class_exists('AKSRT_Admin_Pay_Income')) {
+ AKSRT_Admin_Pay_Income::get_instance()->init();
+}
+if (is_admin() && class_exists('AKSRT_Admin_Pay_Rebate')) {
+ AKSRT_Admin_Pay_Rebate::get_instance()->init();
+}
// WordPress 原生用户菜单:消息/邀请码/认证/禁封(zibll users.php 子菜单对齐)
if (is_admin() && class_exists('AKSRT_Admin_Users_Menus')) {
diff --git a/inc/bootstrap.php b/inc/bootstrap.php
index 8ffddeec..675d3751 100644
--- a/inc/bootstrap.php
+++ b/inc/bootstrap.php
@@ -177,6 +177,10 @@ require_once AKSRT_DIR . '/inc/classes/admin/native/class-aksrt-admin-pay-coupon
require_once AKSRT_DIR . '/inc/classes/admin/native/class-aksrt-admin-pay-charge-cards.php';
require_once AKSRT_DIR . '/inc/classes/admin/native/class-aksrt-admin-pay-shipping.php';
require_once AKSRT_DIR . '/inc/classes/admin/native/class-aksrt-admin-pay-after-sale.php';
+require_once AKSRT_DIR . '/inc/classes/admin/native/class-aksrt-admin-pay-orders.php';
+require_once AKSRT_DIR . '/inc/classes/admin/native/class-aksrt-admin-pay-withdrawals.php';
+require_once AKSRT_DIR . '/inc/classes/admin/native/class-aksrt-admin-pay-income.php';
+require_once AKSRT_DIR . '/inc/classes/admin/native/class-aksrt-admin-pay-rebate.php';
require_once AKSRT_DIR . '/inc/classes/admin/native/class-aksrt-admin-users-menus.php';
require_once AKSRT_DIR . '/inc/classes/class-aksrt-admin.php';
diff --git a/inc/classes/admin/native/class-aksrt-admin-pay-income.php b/inc/classes/admin/native/class-aksrt-admin-pay-income.php
new file mode 100644
index 00000000..bc7bbfed
--- /dev/null
+++ b/inc/classes/admin/native/class-aksrt-admin-pay-income.php
@@ -0,0 +1,176 @@
+ensure_cap();
+ $this->render_header(__('创作分成', 'aksrt'));
+
+ if (!class_exists('AKSRT_Revenue')) {
+ echo '
' . esc_html__('分成模块未初始化。', 'aksrt') . '
';
+ $this->render_footer();
+ return;
+ }
+
+ $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
+ $page_num = max(1, (int) ($_GET['paged'] ?? 1));
+ $per_page = 20;
+ $svc = AKSRT_Revenue::get_instance();
+ $stats = $svc->get_stats();
+
+ echo '' . esc_html(sprintf(
+ /* translators: 1: total 2: pending 3: settled */
+ __('合计 %1$s · 待结算 %2$s · 已结算 %3$s', 'aksrt'),
+ number_format((float) ($stats['total'] ?? 0), 2),
+ number_format((float) ($stats['pending'] ?? 0), 2),
+ number_format((float) ($stats['settled'] ?? 0), 2)
+ )) . '
';
+
+ echo '';
+
+ $result = $svc->get_list([
+ 'status' => $status,
+ 'limit' => $per_page,
+ 'offset' => ($page_num - 1) * $per_page,
+ ]);
+ $items = $result['items'] ?? [];
+ $total = (int) ($result['total'] ?? 0);
+ $pages = max(1, (int) ($result['max_pages'] ?? 1));
+
+ echo '';
+ echo '| ID | ' . esc_html__('用户', 'aksrt') . ' | ' . esc_html__('订单号', 'aksrt') . ' | ' . esc_html__('金额', 'aksrt') . ' | ' . esc_html__('状态', 'aksrt') . ' | ' . esc_html__('时间', 'aksrt') . ' | ' . esc_html__('操作', 'aksrt') . ' | ';
+ echo '
';
+ if (!$items) {
+ echo '| ' . esc_html__('暂无分成记录', 'aksrt') . ' |
';
+ }
+ foreach ($items as $row) {
+ $id = (int) ($row['id'] ?? 0);
+ $user_id = (int) ($row['user_id'] ?? 0);
+ $user = $user_id > 0 ? get_userdata($user_id) : null;
+ $st = (string) ($row['status'] ?? '');
+ echo '';
+ echo '| ' . esc_html((string) $id) . ' | ';
+ echo '' . ($user ? esc_html($user->display_name) : esc_html((string) $user_id)) . ' | ';
+ echo '' . esc_html((string) ($row['order_number'] ?? '')) . ' | ';
+ echo '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' | ';
+ echo '' . esc_html($st) . ' | ';
+ echo '' . esc_html((string) ($row['created_at'] ?? '')) . ' | ';
+ echo '';
+ if ($st === 'pending') {
+ echo '';
+ } else {
+ echo '—';
+ }
+ echo ' |
';
+ }
+ echo '
';
+ $this->render_pagination($pages, $page_num);
+ $this->render_footer();
+ }
+
+ public function handle_settle_income(): void {
+ if (!current_user_can(self::CAPABILITY)) {
+ wp_die(esc_html__('权限不足', 'aksrt'));
+ }
+ $id = isset($_POST['revenue_id']) ? (int) $_POST['revenue_id'] : 0;
+ check_admin_referer('aksrt_pay_settle_income_' . $id);
+ $ok = $id > 0 && class_exists('AKSRT_Revenue') && AKSRT_Revenue::get_instance()->settle($id);
+ wp_safe_redirect($this->page_url('aksrt_income', ['aksrt_pay_msg' => $ok ? 'income_settled' : 'income_failed']));
+ exit;
+ }
+
+
+
+ private function ensure_cap(): void {
+ if (!current_user_can(self::CAPABILITY)) {
+ wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
+ }
+ }
+
+ private function render_header(string $title): void {
+ echo '';
+ echo '
' . esc_html($title) . '
';
+ $this->render_flash();
+ }
+
+ private function render_footer(): void {
+ echo '';
+ }
+
+ private function render_flash(): void {
+ if (empty($_GET['aksrt_pay_msg'])) {
+ return;
+ }
+ $code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
+ $map = [
+ 'income_settled' => __('分成已结算。', 'aksrt'),
+ 'income_failed' => __('分成结算失败。', 'aksrt'),
+ ];
+ if (!isset($map[$code])) {
+ return;
+ }
+ $class = str_contains($code, 'failed') ? 'notice-error' : 'notice-success';
+ echo '' . esc_html($map[$code]) . '
';
+ }
+
+ private function page_url(string $slug, array $args = []): string {
+ return add_query_arg(array_merge(['page' => $slug], $args), admin_url('admin.php'));
+ }
+
+ private function render_pagination(int $pages, int $page_num): void {
+ if ($pages <= 1) {
+ return;
+ }
+ echo '';
+ echo paginate_links([
+ 'base' => add_query_arg('paged', '%#%'),
+ 'format' => '',
+ 'prev_text' => '«',
+ 'next_text' => '»',
+ 'total' => $pages,
+ 'current' => $page_num,
+ ]);
+ echo '
';
+ }
+}
diff --git a/inc/classes/admin/native/class-aksrt-admin-pay-menus.php b/inc/classes/admin/native/class-aksrt-admin-pay-menus.php
index d5a21310..f9a076d7 100644
--- a/inc/classes/admin/native/class-aksrt-admin-pay-menus.php
+++ b/inc/classes/admin/native/class-aksrt-admin-pay-menus.php
@@ -1,8 +1,8 @@
[
'slug' => 'aksrt_orders',
'title' => __('订单', 'aksrt'),
- 'callback' => [$this, 'render_orders_page'],
+ 'callback' => static function (): void {
+ if (class_exists('AKSRT_Admin_Pay_Orders')) {
+ AKSRT_Admin_Pay_Orders::get_instance()->render_orders_page();
+ return;
+ }
+ wp_die(esc_html__('订单模块未初始化。', 'aksrt'));
+ },
],
'shipping' => [
'slug' => 'aksrt_shipping',
@@ -101,17 +101,35 @@ class AKSRT_Admin_Pay_Menus {
'income' => [
'slug' => 'aksrt_income',
'title' => __('创作分成', 'aksrt'),
- 'callback' => [$this, 'render_income_page'],
+ 'callback' => static function (): void {
+ if (class_exists('AKSRT_Admin_Pay_Income')) {
+ AKSRT_Admin_Pay_Income::get_instance()->render_income_page();
+ return;
+ }
+ wp_die(esc_html__('分成模块未初始化。', 'aksrt'));
+ },
],
'rebate' => [
'slug' => 'aksrt_rebate',
'title' => __('推广返佣', 'aksrt'),
- 'callback' => [$this, 'render_rebate_page'],
+ 'callback' => static function (): void {
+ if (class_exists('AKSRT_Admin_Pay_Rebate')) {
+ AKSRT_Admin_Pay_Rebate::get_instance()->render_rebate_page();
+ return;
+ }
+ wp_die(esc_html__('返佣模块未初始化。', 'aksrt'));
+ },
],
'withdraw' => [
'slug' => 'aksrt_withdraw',
'title' => __('提现审核', 'aksrt'),
- 'callback' => [$this, 'render_withdrawals_page'],
+ 'callback' => static function (): void {
+ if (class_exists('AKSRT_Admin_Pay_Withdrawals')) {
+ AKSRT_Admin_Pay_Withdrawals::get_instance()->render_withdrawals_page();
+ return;
+ }
+ wp_die(esc_html__('提现模块未初始化。', 'aksrt'));
+ },
],
];
}
@@ -131,11 +149,9 @@ class AKSRT_Admin_Pay_Menus {
36
);
- foreach ($this->pages() as $key => $page) {
- $parent = self::MENU_SLUG;
- // First item reuses top-level slug (WP pattern).
+ foreach ($this->pages() as $page) {
add_submenu_page(
- $parent,
+ self::MENU_SLUG,
$page['title'],
$page['title'],
self::CAPABILITY,
@@ -186,10 +202,6 @@ class AKSRT_Admin_Pay_Menus {
}
}
- /* ------------------------------------------------------------------
- * Page shells
- * ----------------------------------------------------------------*/
-
private function ensure_cap(): void {
if (!current_user_can(self::CAPABILITY)) {
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
@@ -199,45 +211,12 @@ class AKSRT_Admin_Pay_Menus {
private function render_header(string $title): void {
echo '';
echo '
' . esc_html($title) . '
';
- $this->render_flash();
}
private function render_footer(): void {
echo '';
}
- private function render_flash(): void {
- if (empty($_GET['aksrt_pay_msg'])) {
- return;
- }
- $code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
- $map = [
- 'order_updated' => __('订单状态已更新。', 'aksrt'),
- 'order_failed' => __('订单状态更新失败。', 'aksrt'),
- 'order_note_updated' => __('订单备注已保存。', 'aksrt'),
- 'order_note_failed' => __('订单备注保存失败。', 'aksrt'),
- 'withdraw_updated' => __('提现状态已更新。', 'aksrt'),
- 'withdraw_failed' => __('提现状态更新失败。', 'aksrt'),
- // Order detail still redirects shipping/after-sale actions here.
- 'shipping_updated' => __('物流状态已更新。', 'aksrt'),
- 'shipping_failed' => __('物流状态更新失败。', 'aksrt'),
- 'shipping_created' => __('发货记录已创建。', 'aksrt'),
- 'shipping_create_failed' => __('发货记录创建失败。', 'aksrt'),
- 'after_sale_created' => __('售后工单已创建。', 'aksrt'),
- 'after_sale_create_failed' => __('售后工单创建失败。', 'aksrt'),
- 'income_settled' => __('分成已结算。', 'aksrt'),
- 'income_failed' => __('分成结算失败。', 'aksrt'),
- 'rebate_settled' => __('返佣已结算。', 'aksrt'),
- 'rebate_cancelled' => __('返佣已取消。', 'aksrt'),
- 'rebate_failed' => __('返佣操作失败。', 'aksrt'),
- ];
- if (!isset($map[$code])) {
- return;
- }
- $class = str_contains($code, 'failed') ? 'notice-error' : 'notice-success';
- echo '' . esc_html($map[$code]) . '
';
- }
-
private function page_url(string $slug, array $args = []): string {
return add_query_arg(array_merge(['page' => $slug], $args), admin_url('admin.php'));
}
@@ -293,770 +272,4 @@ class AKSRT_Admin_Pay_Menus {
echo '' . esc_html($value) . '
';
echo '';
}
-
- /* ------------------------------------------------------------------
- * Orders
- * ----------------------------------------------------------------*/
-
- public function render_orders_page(): void {
- $this->ensure_cap();
- $this->render_header(__('订单', 'aksrt'));
-
- if (!class_exists('AKSRT_Order_Repository')) {
- echo '' . esc_html__('订单服务不可用。', 'aksrt') . '
';
- $this->render_footer();
- return;
- }
-
- $detail_order = isset($_GET['order']) ? sanitize_text_field((string) wp_unslash($_GET['order'])) : '';
- if ($detail_order !== '') {
- $this->render_order_detail_page($detail_order);
- $this->render_footer();
- return;
- }
-
- $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
- $search = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
- $page_num = max(1, (int) ($_GET['paged'] ?? 1));
- $per_page = 20;
-
- $repo = AKSRT_Order_Repository::get_instance();
- $args = [
- 'limit' => $per_page,
- 'offset' => ($page_num - 1) * $per_page,
- ];
- if ($status !== '' && $status !== 'all') {
- $args['status'] = $status;
- }
-
- $items = $repo->get_orders_admin($args, $search);
- $total = $repo->get_orders_count($args, $search);
- $pages = max(1, (int) ceil($total / $per_page));
- $labels = class_exists('AKSRT_Domain_Model')
- ? AKSRT_Domain_Model::order_status_labels()
- : [];
-
- echo '';
-
- echo '';
- echo '';
- echo '| ' . esc_html__('订单号', 'aksrt') . ' | ';
- echo '' . esc_html__('用户', 'aksrt') . ' | ';
- echo '' . esc_html__('类型', 'aksrt') . ' | ';
- echo '' . esc_html__('金额', 'aksrt') . ' | ';
- echo '' . esc_html__('状态', 'aksrt') . ' | ';
- echo '' . esc_html__('时间', 'aksrt') . ' | ';
- echo '' . esc_html__('操作', 'aksrt') . ' | ';
- echo '
';
-
- if (!$items) {
- echo '| ' . esc_html__('暂无订单', 'aksrt') . ' |
';
- }
-
- foreach ($items as $row) {
- $order_number = (string) ($row['order_number'] ?? '');
- $user_id = (int) ($row['user_id'] ?? 0);
- $user = $user_id > 0 ? get_userdata($user_id) : null;
- $status_key = (string) ($row['status'] ?? '');
- $status_label = $labels[$status_key] ?? $status_key;
-
- echo '';
- echo '' . esc_html($order_number) . ' | ';
- echo '' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . ' | ';
- echo '' . esc_html((string) ($row['product_type'] ?? '')) . ' | ';
- echo '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' ' . esc_html((string) ($row['currency'] ?? '')) . ' | ';
- echo '' . esc_html($status_label) . ' | ';
- echo '' . esc_html((string) ($row['created_at'] ?? '')) . ' | ';
- echo '';
- echo '' . esc_html__('详情', 'aksrt') . '';
- $this->render_order_quick_status_buttons($order_number, $status_key);
- echo '';
- echo ' | ';
- echo '
';
- }
-
- echo '
';
-
- if ($pages > 1) {
- echo '';
- echo paginate_links([
- 'base' => add_query_arg('paged', '%#%'),
- 'format' => '',
- 'prev_text' => '«',
- 'next_text' => '»',
- 'total' => $pages,
- 'current' => $page_num,
- ]);
- echo '
';
- }
-
- $this->render_footer();
- }
-
- private function render_order_detail_page(string $order_number): void {
- $repo = AKSRT_Order_Repository::get_instance();
- $order = $repo->get($order_number);
- if (!$order) {
- echo '' . esc_html__('订单不存在。', 'aksrt') . '
';
- echo '' . esc_html__('返回订单列表', 'aksrt') . '
';
- return;
- }
-
- $labels = class_exists('AKSRT_Domain_Model')
- ? AKSRT_Domain_Model::order_status_labels()
- : [];
- $context = $repo->get_admin_order_context($order_number);
- $user_id = (int) ($order['user_id'] ?? 0);
- $user = $user_id > 0 ? get_userdata($user_id) : null;
- $status = (string) ($order['status'] ?? '');
- $meta = is_array($order['meta_payload'] ?? null) ? $order['meta_payload'] : [];
- $customer_note = (string) ($context['customer_note'] ?? $order['note_text'] ?? '');
- $admin_note = is_array($meta) ? (string) ($meta['admin_note'] ?? '') : '';
- if ($admin_note === '' && $customer_note === '' && !empty($order['note']) && !is_array(json_decode((string) $order['note'], true))) {
- $admin_note = (string) $order['note'];
- }
-
- $shipping_items = class_exists('AKSRT_Shipping')
- ? AKSRT_Shipping::get_instance()->get_by_order($order_number)
- : [];
- $after_sale = class_exists('AKSRT_After_Sale')
- ? (AKSRT_After_Sale::get_instance()->get_list(['order_number' => $order_number, 'limit' => 20])['items'] ?? [])
- : [];
-
- echo '← ' . esc_html__('返回订单列表', 'aksrt') . '
';
-
- echo '';
- $rows = [
- __('订单号', 'aksrt') => $order_number,
- __('用户', 'aksrt') => $user ? $user->display_name . ' (#' . $user_id . ')' : (string) $user_id,
- __('商品类型', 'aksrt') => (string) ($order['product_type'] ?? ''),
- __('商品 ID', 'aksrt') => (string) ($order['product_id'] ?? ''),
- __('金额', 'aksrt') => number_format((float) ($order['amount'] ?? 0), 2) . ' ' . (string) ($order['currency'] ?? ''),
- __('状态', 'aksrt') => $labels[$status] ?? $status,
- __('支付方式', 'aksrt') => (string) ($order['payment_method'] ?? ''),
- __('交易号', 'aksrt') => (string) ($order['payment_transaction_id'] ?? ''),
- __('创建时间', 'aksrt') => (string) ($order['created_at'] ?? ''),
- __('支付时间', 'aksrt') => (string) ($order['paid_at'] ?? ''),
- __('买家备注', 'aksrt') => $customer_note,
- ];
- foreach ($rows as $label => $value) {
- echo '| ' . esc_html($label) . ' | ' . esc_html((string) $value) . ' |
';
- }
- if (!empty($context)) {
- echo '| ' . esc_html__('附加上下文', 'aksrt') . ' | ' . esc_html(wp_json_encode($context, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)) . ' |
';
- }
- echo '
';
-
- echo '' . esc_html__('运营备注', 'aksrt') . '
';
- echo '';
-
- echo '' . esc_html__('更新状态', 'aksrt') . '
';
- echo '';
- $this->render_order_quick_status_buttons($order_number, $status, true);
- echo '
';
- echo '';
-
- echo '' . esc_html__('物流记录', 'aksrt') . '
';
- echo '';
- if (!$shipping_items) {
- echo '' . esc_html__('暂无物流记录。', 'aksrt') . '
';
- } else {
- $status_options = $this->shipping_status_options(false);
- echo '| ID | ' . esc_html__('承运商', 'aksrt') . ' | ' . esc_html__('运单号', 'aksrt') . ' | ' . esc_html__('状态', 'aksrt') . ' | ' . esc_html__('时间', 'aksrt') . ' | ' . esc_html__('操作', 'aksrt') . ' |
';
- foreach ($shipping_items as $item) {
- $sid = (int) ($item['id'] ?? 0);
- $st = (string) ($item['status'] ?? '');
- echo '';
- echo '| ' . esc_html((string) $sid) . ' | ';
- echo '' . esc_html((string) ($item['carrier'] ?? '')) . ' | ';
- echo '' . esc_html((string) ($item['tracking_no'] ?? '')) . ' | ';
- echo '' . esc_html($status_options[$st] ?? $st) . ' | ';
- echo '' . esc_html((string) ($item['created_at'] ?? '')) . ' | ';
- echo ' | ';
- echo '
';
- }
- echo '
';
- }
-
- echo '' . esc_html__('售后工单', 'aksrt') . '
';
- echo '';
- if (!$after_sale) {
- echo '' . esc_html__('暂无售后工单。', 'aksrt') . '
';
- } else {
- echo '| ID | ' . esc_html__('类型', 'aksrt') . ' | ' . esc_html__('状态', 'aksrt') . ' | ' . esc_html__('时间', 'aksrt') . ' | ' . esc_html__('操作', 'aksrt') . ' |
';
- foreach ($after_sale as $item) {
- $tid = (int) ($item['id'] ?? 0);
- echo '';
- echo '| ' . esc_html((string) $tid) . ' | ';
- echo '' . esc_html((string) ($item['type_label'] ?? $item['type'] ?? '')) . ' | ';
- echo '' . esc_html((string) ($item['status_label'] ?? $item['status'] ?? '')) . ' | ';
- echo '' . esc_html((string) ($item['created_at'] ?? '')) . ' | ';
- echo '' . esc_html__('在售后页处理', 'aksrt') . ' | ';
- echo '
';
- }
- echo '
';
- }
- }
-
- public function handle_update_order_status(): void {
- if (!current_user_can(self::CAPABILITY)) {
- wp_die(esc_html__('权限不足', 'aksrt'));
- }
-
- $order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
- $new_status = isset($_POST['new_status']) ? sanitize_key((string) wp_unslash($_POST['new_status'])) : '';
- check_admin_referer('aksrt_pay_update_order_status_' . $order_number);
-
- $ok = false;
- if ($order_number !== '' && $new_status !== '' && class_exists('AKSRT_Order_Repository')) {
- $ok = AKSRT_Order_Repository::get_instance()->update_status($order_number, $new_status, ['force' => true]);
- }
-
- $args = ['aksrt_pay_msg' => $ok ? 'order_updated' : 'order_failed'];
- $redirect_order = isset($_POST['redirect_order']) ? sanitize_text_field((string) wp_unslash($_POST['redirect_order'])) : '';
- if ($redirect_order !== '') {
- $args['order'] = $redirect_order;
- }
-
- wp_safe_redirect($this->page_url('aksrt_orders', $args));
- exit;
- }
-
- public function handle_update_order_note(): void {
- if (!current_user_can(self::CAPABILITY)) {
- wp_die(esc_html__('权限不足', 'aksrt'));
- }
-
- $order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
- $admin_note = isset($_POST['admin_note']) ? sanitize_textarea_field((string) wp_unslash($_POST['admin_note'])) : '';
- check_admin_referer('aksrt_pay_update_order_note_' . $order_number);
-
- $ok = false;
- if ($order_number !== '' && class_exists('AKSRT_Order_Repository')) {
- $ok = AKSRT_Order_Repository::get_instance()->update_admin_note($order_number, $admin_note);
- }
-
- wp_safe_redirect($this->page_url('aksrt_orders', [
- 'order' => $order_number,
- 'aksrt_pay_msg' => $ok ? 'order_note_updated' : 'order_note_failed',
- ]));
- exit;
- }
-
- public function render_withdrawals_page(): void {
- $this->ensure_cap();
- $this->render_header(__('提现审核', 'aksrt'));
-
- if (!class_exists('AKSRT_Withdrawal')) {
- echo '' . esc_html__('提现模块未初始化。', 'aksrt') . '
';
- $this->render_footer();
- return;
- }
-
- $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
- $page_num = max(1, (int) ($_GET['paged'] ?? 1));
- $per_page = 20;
- $svc = AKSRT_Withdrawal::get_instance();
- $stats = $svc->get_stats();
-
- echo '' . esc_html(sprintf(
- /* translators: 1: total 2: pending 3: completed 4: paid */
- __('共 %1$d 条 · 待处理 %2$d · 已完成 %3$d · 已打款 %4$s', 'aksrt'),
- (int) ($stats['total'] ?? 0),
- (int) ($stats['pending'] ?? 0),
- (int) ($stats['completed'] ?? 0),
- number_format((float) ($stats['total_paid'] ?? 0), 2)
- )) . '
';
-
- echo '';
-
- $items = $svc->get_all([
- 'status' => $status,
- 'limit' => $per_page,
- 'offset' => ($page_num - 1) * $per_page,
- ]);
- $total = $svc->get_count(['status' => $status]);
- $pages = max(1, (int) ceil($total / $per_page));
-
- echo '';
- echo '';
- echo '| ID | ';
- echo '' . esc_html__('用户', 'aksrt') . ' | ';
- echo '' . esc_html__('金额', 'aksrt') . ' | ';
- echo '' . esc_html__('方式', 'aksrt') . ' | ';
- echo '' . esc_html__('账户', 'aksrt') . ' | ';
- echo '' . esc_html__('状态', 'aksrt') . ' | ';
- echo '' . esc_html__('申请时间', 'aksrt') . ' | ';
- echo '' . esc_html__('操作', 'aksrt') . ' | ';
- echo '
';
-
- if (!$items) {
- echo '| ' . esc_html__('暂无提现记录', 'aksrt') . ' |
';
- }
-
- foreach ($items as $row) {
- $id = (int) ($row['id'] ?? 0);
- $user_id = (int) ($row['user_id'] ?? 0);
- $user = $user_id > 0 ? get_userdata($user_id) : null;
- $st = (string) ($row['status'] ?? '');
-
- echo '';
- echo '| ' . esc_html((string) $id) . ' | ';
- echo '' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . ' | ';
- echo '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' | ';
- echo '' . esc_html((string) ($row['method'] ?? '')) . ' | ';
- echo '' . esc_html((string) ($row['account_info'] ?? '')) . ' | ';
- echo '' . esc_html($status_options[$st] ?? $st) . ' | ';
- echo '' . esc_html((string) ($row['created_at'] ?? '')) . ' | ';
- echo '';
- echo '';
- echo ' | ';
- echo '
';
- }
-
- echo '
';
-
- if ($pages > 1) {
- echo '';
- echo paginate_links([
- 'base' => add_query_arg('paged', '%#%'),
- 'format' => '',
- 'prev_text' => '«',
- 'next_text' => '»',
- 'total' => $pages,
- 'current' => $page_num,
- ]);
- echo '
';
- }
-
- $this->render_footer();
- }
-
- public function handle_update_withdrawal(): void {
- if (!current_user_can(self::CAPABILITY)) {
- wp_die(esc_html__('权限不足', 'aksrt'));
- }
-
- $id = isset($_POST['withdrawal_id']) ? (int) $_POST['withdrawal_id'] : 0;
- $new_status = isset($_POST['new_status']) ? sanitize_key((string) wp_unslash($_POST['new_status'])) : '';
- $note = isset($_POST['note']) ? sanitize_textarea_field((string) wp_unslash($_POST['note'])) : '';
- check_admin_referer('aksrt_pay_update_withdrawal_' . $id);
-
- $ok = false;
- if ($id > 0 && $new_status !== '' && class_exists('AKSRT_Withdrawal')) {
- $ok = AKSRT_Withdrawal::get_instance()->update_status($id, $new_status, $note);
- }
-
- wp_safe_redirect($this->page_url('aksrt_withdraw', [
- 'aksrt_pay_msg' => $ok ? 'withdraw_updated' : 'withdraw_failed',
- ]));
- exit;
- }
-
- /* ------------------------------------------------------------------
- * Income / rebate
- * ----------------------------------------------------------------*/
-
- public function render_income_page(): void {
- $this->ensure_cap();
- $this->render_header(__('创作分成', 'aksrt'));
-
- if (!class_exists('AKSRT_Revenue')) {
- echo '' . esc_html__('分成模块未初始化。', 'aksrt') . '
';
- $this->render_footer();
- return;
- }
-
- $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
- $page_num = max(1, (int) ($_GET['paged'] ?? 1));
- $per_page = 20;
- $svc = AKSRT_Revenue::get_instance();
- $stats = $svc->get_stats();
-
- echo '' . esc_html(sprintf(
- /* translators: 1: total 2: pending 3: settled */
- __('合计 %1$s · 待结算 %2$s · 已结算 %3$s', 'aksrt'),
- number_format((float) ($stats['total'] ?? 0), 2),
- number_format((float) ($stats['pending'] ?? 0), 2),
- number_format((float) ($stats['settled'] ?? 0), 2)
- )) . '
';
-
- echo '';
-
- $result = $svc->get_list([
- 'status' => $status,
- 'limit' => $per_page,
- 'offset' => ($page_num - 1) * $per_page,
- ]);
- $items = $result['items'] ?? [];
- $total = (int) ($result['total'] ?? 0);
- $pages = max(1, (int) ($result['max_pages'] ?? 1));
-
- echo '';
- echo '| ID | ' . esc_html__('用户', 'aksrt') . ' | ' . esc_html__('订单号', 'aksrt') . ' | ' . esc_html__('金额', 'aksrt') . ' | ' . esc_html__('状态', 'aksrt') . ' | ' . esc_html__('时间', 'aksrt') . ' | ' . esc_html__('操作', 'aksrt') . ' | ';
- echo '
';
- if (!$items) {
- echo '| ' . esc_html__('暂无分成记录', 'aksrt') . ' |
';
- }
- foreach ($items as $row) {
- $id = (int) ($row['id'] ?? 0);
- $user_id = (int) ($row['user_id'] ?? 0);
- $user = $user_id > 0 ? get_userdata($user_id) : null;
- $st = (string) ($row['status'] ?? '');
- echo '';
- echo '| ' . esc_html((string) $id) . ' | ';
- echo '' . ($user ? esc_html($user->display_name) : esc_html((string) $user_id)) . ' | ';
- echo '' . esc_html((string) ($row['order_number'] ?? '')) . ' | ';
- echo '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' | ';
- echo '' . esc_html($st) . ' | ';
- echo '' . esc_html((string) ($row['created_at'] ?? '')) . ' | ';
- echo '';
- if ($st === 'pending') {
- echo '';
- } else {
- echo '—';
- }
- echo ' |
';
- }
- echo '
';
- $this->render_pagination($pages, $page_num);
- $this->render_footer();
- }
-
- public function handle_settle_income(): void {
- if (!current_user_can(self::CAPABILITY)) {
- wp_die(esc_html__('权限不足', 'aksrt'));
- }
- $id = isset($_POST['revenue_id']) ? (int) $_POST['revenue_id'] : 0;
- check_admin_referer('aksrt_pay_settle_income_' . $id);
- $ok = $id > 0 && class_exists('AKSRT_Revenue') && AKSRT_Revenue::get_instance()->settle($id);
- wp_safe_redirect($this->page_url('aksrt_income', ['aksrt_pay_msg' => $ok ? 'income_settled' : 'income_failed']));
- exit;
- }
-
- public function render_rebate_page(): void {
- $this->ensure_cap();
- $this->render_header(__('推广返佣', 'aksrt'));
-
- if (!class_exists('AKSRT_Rebate')) {
- echo '' . esc_html__('返佣模块未初始化。', 'aksrt') . '
';
- $this->render_footer();
- return;
- }
-
- $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
- $page_num = max(1, (int) ($_GET['paged'] ?? 1));
- $per_page = 20;
- $svc = AKSRT_Rebate::get_instance();
- $stats = $svc->get_stats();
-
- echo '' . esc_html(sprintf(
- /* translators: 1: total 2: pending 3: settled amount */
- __('记录 %1$d · 待结算 %2$d · 已结算金额 %3$s', 'aksrt'),
- (int) ($stats['total'] ?? 0),
- (int) ($stats['pending'] ?? 0),
- number_format((float) ($stats['settled_amount'] ?? 0), 2)
- )) . '
';
-
- echo '';
-
- $result = $svc->get_list([
- 'status' => $status,
- 'limit' => $per_page,
- 'offset' => ($page_num - 1) * $per_page,
- ]);
- $items = $result['items'] ?? [];
- $total = (int) ($result['total'] ?? 0);
- $pages = max(1, (int) ceil(max(1, $total) / $per_page));
-
- echo '';
- echo '| ID | ' . esc_html__('用户', 'aksrt') . ' | ' . esc_html__('订单号', 'aksrt') . ' | ' . esc_html__('金额', 'aksrt') . ' | ' . esc_html__('状态', 'aksrt') . ' | ' . esc_html__('时间', 'aksrt') . ' | ' . esc_html__('操作', 'aksrt') . ' | ';
- echo '
';
- if (!$items) {
- echo '| ' . esc_html__('暂无返佣记录', 'aksrt') . ' |
';
- }
- foreach ($items as $row) {
- $id = (int) ($row['id'] ?? 0);
- $user_id = (int) ($row['user_id'] ?? 0);
- $user = $user_id > 0 ? get_userdata($user_id) : null;
- $st = (string) ($row['status'] ?? '');
- echo '';
- echo '| ' . esc_html((string) $id) . ' | ';
- echo '' . ($user ? esc_html($user->display_name) : esc_html((string) $user_id)) . ' | ';
- echo '' . esc_html((string) ($row['order_number'] ?? '')) . ' | ';
- echo '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' | ';
- echo '' . esc_html($st) . ' | ';
- echo '' . esc_html((string) ($row['created_at'] ?? '')) . ' | ';
- echo '';
- if ($st === 'pending') {
- echo '';
- echo '';
- } else {
- echo '—';
- }
- echo ' |
';
- }
- echo '
';
- $this->render_pagination($pages, $page_num);
- $this->render_footer();
- }
-
- public function handle_settle_rebate(): void {
- if (!current_user_can(self::CAPABILITY)) {
- wp_die(esc_html__('权限不足', 'aksrt'));
- }
- $id = isset($_POST['rebate_id']) ? (int) $_POST['rebate_id'] : 0;
- check_admin_referer('aksrt_pay_settle_rebate_' . $id);
- $ok = $id > 0 && class_exists('AKSRT_Rebate') && AKSRT_Rebate::get_instance()->settle($id);
- wp_safe_redirect($this->page_url('aksrt_rebate', ['aksrt_pay_msg' => $ok ? 'rebate_settled' : 'rebate_failed']));
- exit;
- }
-
- public function handle_cancel_rebate(): void {
- if (!current_user_can(self::CAPABILITY)) {
- wp_die(esc_html__('权限不足', 'aksrt'));
- }
- $id = isset($_POST['rebate_id']) ? (int) $_POST['rebate_id'] : 0;
- check_admin_referer('aksrt_pay_cancel_rebate_' . $id);
- $ok = $id > 0 && class_exists('AKSRT_Rebate') && AKSRT_Rebate::get_instance()->cancel($id);
- wp_safe_redirect($this->page_url('aksrt_rebate', ['aksrt_pay_msg' => $ok ? 'rebate_cancelled' : 'rebate_failed']));
- exit;
- }
-
- /**
- * @return array
- */
-
- private function shipping_status_options(bool $include_all = false): array {
- if (class_exists('AKSRT_Admin_Pay_Shipping')) {
- return AKSRT_Admin_Pay_Shipping::get_instance()->shipping_status_options_public($include_all);
- }
- return $include_all ? ['' => __('全部状态', 'aksrt')] : [];
- }
-
- private function after_sale_type_options(bool $include_all = false): array {
- if (class_exists('AKSRT_Admin_Pay_After_Sale')) {
- return AKSRT_Admin_Pay_After_Sale::get_instance()->after_sale_type_options_public($include_all);
- }
- return $include_all ? ['' => __('全部类型', 'aksrt')] : [
- 'refund' => __('仅退款', 'aksrt'),
- ];
- }
-
- private function render_carrier_field(string $selected = ''): void {
- if (class_exists('AKSRT_Admin_Pay_Shipping')) {
- AKSRT_Admin_Pay_Shipping::get_instance()->render_carrier_field_public($selected);
- return;
- }
- echo '';
- }
-
- private function render_order_quick_status_buttons(string $order_number, string $current_status, bool $redirect_detail = false): void {
- $actions = [
- 'completed' => __('完成', 'aksrt'),
- 'refunded' => __('退款', 'aksrt'),
- 'cancelled' => __('关闭', 'aksrt'),
- ];
- foreach ($actions as $status => $label) {
- if ($status === $current_status) {
- continue;
- }
- $confirm = '';
- if ($status === 'refunded') {
- $confirm = __('确认将该订单标记为已退款?', 'aksrt');
- } elseif ($status === 'cancelled') {
- $confirm = __('确认关闭该订单?', 'aksrt');
- } elseif ($status === 'completed') {
- $confirm = __('确认完成该订单?', 'aksrt');
- }
- echo '';
- }
- }
-
- private function render_pagination(int $pages, int $page_num): void {
- if ($pages <= 1) {
- return;
- }
- echo '';
- echo paginate_links([
- 'base' => add_query_arg('paged', '%#%'),
- 'format' => '',
- 'prev_text' => '«',
- 'next_text' => '»',
- 'total' => $pages,
- 'current' => $page_num,
- ]);
- echo '
';
- }
}
diff --git a/inc/classes/admin/native/class-aksrt-admin-pay-orders.php b/inc/classes/admin/native/class-aksrt-admin-pay-orders.php
new file mode 100644
index 00000000..6311e75b
--- /dev/null
+++ b/inc/classes/admin/native/class-aksrt-admin-pay-orders.php
@@ -0,0 +1,488 @@
+ensure_cap();
+ $this->render_header(__('订单', 'aksrt'));
+
+ if (!class_exists('AKSRT_Order_Repository')) {
+ echo '' . esc_html__('订单服务不可用。', 'aksrt') . '
';
+ $this->render_footer();
+ return;
+ }
+
+ $detail_order = isset($_GET['order']) ? sanitize_text_field((string) wp_unslash($_GET['order'])) : '';
+ if ($detail_order !== '') {
+ $this->render_order_detail_page($detail_order);
+ $this->render_footer();
+ return;
+ }
+
+ $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
+ $search = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
+ $page_num = max(1, (int) ($_GET['paged'] ?? 1));
+ $per_page = 20;
+
+ $repo = AKSRT_Order_Repository::get_instance();
+ $args = [
+ 'limit' => $per_page,
+ 'offset' => ($page_num - 1) * $per_page,
+ ];
+ if ($status !== '' && $status !== 'all') {
+ $args['status'] = $status;
+ }
+
+ $items = $repo->get_orders_admin($args, $search);
+ $total = $repo->get_orders_count($args, $search);
+ $pages = max(1, (int) ceil($total / $per_page));
+ $labels = class_exists('AKSRT_Domain_Model')
+ ? AKSRT_Domain_Model::order_status_labels()
+ : [];
+
+ echo '';
+
+ echo '';
+ echo '';
+ echo '| ' . esc_html__('订单号', 'aksrt') . ' | ';
+ echo '' . esc_html__('用户', 'aksrt') . ' | ';
+ echo '' . esc_html__('类型', 'aksrt') . ' | ';
+ echo '' . esc_html__('金额', 'aksrt') . ' | ';
+ echo '' . esc_html__('状态', 'aksrt') . ' | ';
+ echo '' . esc_html__('时间', 'aksrt') . ' | ';
+ echo '' . esc_html__('操作', 'aksrt') . ' | ';
+ echo '
';
+
+ if (!$items) {
+ echo '| ' . esc_html__('暂无订单', 'aksrt') . ' |
';
+ }
+
+ foreach ($items as $row) {
+ $order_number = (string) ($row['order_number'] ?? '');
+ $user_id = (int) ($row['user_id'] ?? 0);
+ $user = $user_id > 0 ? get_userdata($user_id) : null;
+ $status_key = (string) ($row['status'] ?? '');
+ $status_label = $labels[$status_key] ?? $status_key;
+
+ echo '';
+ echo '' . esc_html($order_number) . ' | ';
+ echo '' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . ' | ';
+ echo '' . esc_html((string) ($row['product_type'] ?? '')) . ' | ';
+ echo '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' ' . esc_html((string) ($row['currency'] ?? '')) . ' | ';
+ echo '' . esc_html($status_label) . ' | ';
+ echo '' . esc_html((string) ($row['created_at'] ?? '')) . ' | ';
+ echo '';
+ echo '' . esc_html__('详情', 'aksrt') . '';
+ $this->render_order_quick_status_buttons($order_number, $status_key);
+ echo '';
+ echo ' | ';
+ echo '
';
+ }
+
+ echo '
';
+
+ if ($pages > 1) {
+ echo '';
+ echo paginate_links([
+ 'base' => add_query_arg('paged', '%#%'),
+ 'format' => '',
+ 'prev_text' => '«',
+ 'next_text' => '»',
+ 'total' => $pages,
+ 'current' => $page_num,
+ ]);
+ echo '
';
+ }
+
+ $this->render_footer();
+ }
+
+ private function render_order_detail_page(string $order_number): void {
+ $repo = AKSRT_Order_Repository::get_instance();
+ $order = $repo->get($order_number);
+ if (!$order) {
+ echo '' . esc_html__('订单不存在。', 'aksrt') . '
';
+ echo '' . esc_html__('返回订单列表', 'aksrt') . '
';
+ return;
+ }
+
+ $labels = class_exists('AKSRT_Domain_Model')
+ ? AKSRT_Domain_Model::order_status_labels()
+ : [];
+ $context = $repo->get_admin_order_context($order_number);
+ $user_id = (int) ($order['user_id'] ?? 0);
+ $user = $user_id > 0 ? get_userdata($user_id) : null;
+ $status = (string) ($order['status'] ?? '');
+ $meta = is_array($order['meta_payload'] ?? null) ? $order['meta_payload'] : [];
+ $customer_note = (string) ($context['customer_note'] ?? $order['note_text'] ?? '');
+ $admin_note = is_array($meta) ? (string) ($meta['admin_note'] ?? '') : '';
+ if ($admin_note === '' && $customer_note === '' && !empty($order['note']) && !is_array(json_decode((string) $order['note'], true))) {
+ $admin_note = (string) $order['note'];
+ }
+
+ $shipping_items = class_exists('AKSRT_Shipping')
+ ? AKSRT_Shipping::get_instance()->get_by_order($order_number)
+ : [];
+ $after_sale = class_exists('AKSRT_After_Sale')
+ ? (AKSRT_After_Sale::get_instance()->get_list(['order_number' => $order_number, 'limit' => 20])['items'] ?? [])
+ : [];
+
+ echo '← ' . esc_html__('返回订单列表', 'aksrt') . '
';
+
+ echo '';
+ $rows = [
+ __('订单号', 'aksrt') => $order_number,
+ __('用户', 'aksrt') => $user ? $user->display_name . ' (#' . $user_id . ')' : (string) $user_id,
+ __('商品类型', 'aksrt') => (string) ($order['product_type'] ?? ''),
+ __('商品 ID', 'aksrt') => (string) ($order['product_id'] ?? ''),
+ __('金额', 'aksrt') => number_format((float) ($order['amount'] ?? 0), 2) . ' ' . (string) ($order['currency'] ?? ''),
+ __('状态', 'aksrt') => $labels[$status] ?? $status,
+ __('支付方式', 'aksrt') => (string) ($order['payment_method'] ?? ''),
+ __('交易号', 'aksrt') => (string) ($order['payment_transaction_id'] ?? ''),
+ __('创建时间', 'aksrt') => (string) ($order['created_at'] ?? ''),
+ __('支付时间', 'aksrt') => (string) ($order['paid_at'] ?? ''),
+ __('买家备注', 'aksrt') => $customer_note,
+ ];
+ foreach ($rows as $label => $value) {
+ echo '| ' . esc_html($label) . ' | ' . esc_html((string) $value) . ' |
';
+ }
+ if (!empty($context)) {
+ echo '| ' . esc_html__('附加上下文', 'aksrt') . ' | ' . esc_html(wp_json_encode($context, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)) . ' |
';
+ }
+ echo '
';
+
+ echo '' . esc_html__('运营备注', 'aksrt') . '
';
+ echo '';
+
+ echo '' . esc_html__('更新状态', 'aksrt') . '
';
+ echo '';
+ $this->render_order_quick_status_buttons($order_number, $status, true);
+ echo '
';
+ echo '';
+
+ echo '' . esc_html__('物流记录', 'aksrt') . '
';
+ echo '';
+ if (!$shipping_items) {
+ echo '' . esc_html__('暂无物流记录。', 'aksrt') . '
';
+ } else {
+ $status_options = $this->shipping_status_options(false);
+ echo '| ID | ' . esc_html__('承运商', 'aksrt') . ' | ' . esc_html__('运单号', 'aksrt') . ' | ' . esc_html__('状态', 'aksrt') . ' | ' . esc_html__('时间', 'aksrt') . ' | ' . esc_html__('操作', 'aksrt') . ' |
';
+ foreach ($shipping_items as $item) {
+ $sid = (int) ($item['id'] ?? 0);
+ $st = (string) ($item['status'] ?? '');
+ echo '';
+ echo '| ' . esc_html((string) $sid) . ' | ';
+ echo '' . esc_html((string) ($item['carrier'] ?? '')) . ' | ';
+ echo '' . esc_html((string) ($item['tracking_no'] ?? '')) . ' | ';
+ echo '' . esc_html($status_options[$st] ?? $st) . ' | ';
+ echo '' . esc_html((string) ($item['created_at'] ?? '')) . ' | ';
+ echo ' | ';
+ echo '
';
+ }
+ echo '
';
+ }
+
+ echo '' . esc_html__('售后工单', 'aksrt') . '
';
+ echo '';
+ if (!$after_sale) {
+ echo '' . esc_html__('暂无售后工单。', 'aksrt') . '
';
+ } else {
+ echo '| ID | ' . esc_html__('类型', 'aksrt') . ' | ' . esc_html__('状态', 'aksrt') . ' | ' . esc_html__('时间', 'aksrt') . ' | ' . esc_html__('操作', 'aksrt') . ' |
';
+ foreach ($after_sale as $item) {
+ $tid = (int) ($item['id'] ?? 0);
+ echo '';
+ echo '| ' . esc_html((string) $tid) . ' | ';
+ echo '' . esc_html((string) ($item['type_label'] ?? $item['type'] ?? '')) . ' | ';
+ echo '' . esc_html((string) ($item['status_label'] ?? $item['status'] ?? '')) . ' | ';
+ echo '' . esc_html((string) ($item['created_at'] ?? '')) . ' | ';
+ echo '' . esc_html__('在售后页处理', 'aksrt') . ' | ';
+ echo '
';
+ }
+ echo '
';
+ }
+ }
+
+ public function handle_update_order_status(): void {
+ if (!current_user_can(self::CAPABILITY)) {
+ wp_die(esc_html__('权限不足', 'aksrt'));
+ }
+
+ $order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
+ $new_status = isset($_POST['new_status']) ? sanitize_key((string) wp_unslash($_POST['new_status'])) : '';
+ check_admin_referer('aksrt_pay_update_order_status_' . $order_number);
+
+ $ok = false;
+ if ($order_number !== '' && $new_status !== '' && class_exists('AKSRT_Order_Repository')) {
+ $ok = AKSRT_Order_Repository::get_instance()->update_status($order_number, $new_status, ['force' => true]);
+ }
+
+ $args = ['aksrt_pay_msg' => $ok ? 'order_updated' : 'order_failed'];
+ $redirect_order = isset($_POST['redirect_order']) ? sanitize_text_field((string) wp_unslash($_POST['redirect_order'])) : '';
+ if ($redirect_order !== '') {
+ $args['order'] = $redirect_order;
+ }
+
+ wp_safe_redirect($this->page_url('aksrt_orders', $args));
+ exit;
+ }
+
+ public function handle_update_order_note(): void {
+ if (!current_user_can(self::CAPABILITY)) {
+ wp_die(esc_html__('权限不足', 'aksrt'));
+ }
+
+ $order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
+ $admin_note = isset($_POST['admin_note']) ? sanitize_textarea_field((string) wp_unslash($_POST['admin_note'])) : '';
+ check_admin_referer('aksrt_pay_update_order_note_' . $order_number);
+
+ $ok = false;
+ if ($order_number !== '' && class_exists('AKSRT_Order_Repository')) {
+ $ok = AKSRT_Order_Repository::get_instance()->update_admin_note($order_number, $admin_note);
+ }
+
+ wp_safe_redirect($this->page_url('aksrt_orders', [
+ 'order' => $order_number,
+ 'aksrt_pay_msg' => $ok ? 'order_note_updated' : 'order_note_failed',
+ ]));
+ exit;
+ }
+
+
+
+ private function shipping_status_options(bool $include_all = false): array {
+ if (class_exists('AKSRT_Admin_Pay_Shipping')) {
+ return AKSRT_Admin_Pay_Shipping::get_instance()->shipping_status_options_public($include_all);
+ }
+ return $include_all ? ['' => __('全部状态', 'aksrt')] : [];
+ }
+
+ private function after_sale_type_options(bool $include_all = false): array {
+ if (class_exists('AKSRT_Admin_Pay_After_Sale')) {
+ return AKSRT_Admin_Pay_After_Sale::get_instance()->after_sale_type_options_public($include_all);
+ }
+ return $include_all ? ['' => __('全部类型', 'aksrt')] : [
+ 'refund' => __('仅退款', 'aksrt'),
+ ];
+ }
+
+ private function render_carrier_field(string $selected = ''): void {
+ if (class_exists('AKSRT_Admin_Pay_Shipping')) {
+ AKSRT_Admin_Pay_Shipping::get_instance()->render_carrier_field_public($selected);
+ return;
+ }
+ echo '';
+ }
+
+ private function render_order_quick_status_buttons(string $order_number, string $current_status, bool $redirect_detail = false): void {
+ $actions = [
+ 'completed' => __('完成', 'aksrt'),
+ 'refunded' => __('退款', 'aksrt'),
+ 'cancelled' => __('关闭', 'aksrt'),
+ ];
+ foreach ($actions as $status => $label) {
+ if ($status === $current_status) {
+ continue;
+ }
+ $confirm = '';
+ if ($status === 'refunded') {
+ $confirm = __('确认将该订单标记为已退款?', 'aksrt');
+ } elseif ($status === 'cancelled') {
+ $confirm = __('确认关闭该订单?', 'aksrt');
+ } elseif ($status === 'completed') {
+ $confirm = __('确认完成该订单?', 'aksrt');
+ }
+ echo '';
+ }
+ }
+
+
+
+ private function ensure_cap(): void {
+ if (!current_user_can(self::CAPABILITY)) {
+ wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
+ }
+ }
+
+ private function render_header(string $title): void {
+ echo '';
+ echo '
' . esc_html($title) . '
';
+ $this->render_flash();
+ }
+
+ private function render_footer(): void {
+ echo '';
+ }
+
+ private function render_flash(): void {
+ if (empty($_GET['aksrt_pay_msg'])) {
+ return;
+ }
+ $code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
+ $map = [
+ 'order_updated' => __('订单状态已更新。', 'aksrt'),
+ 'order_failed' => __('订单状态更新失败。', 'aksrt'),
+ 'order_note_updated' => __('订单备注已保存。', 'aksrt'),
+ 'order_note_failed' => __('订单备注保存失败。', 'aksrt'),
+ 'shipping_updated' => __('物流状态已更新。', 'aksrt'),
+ 'shipping_failed' => __('物流状态更新失败。', 'aksrt'),
+ 'shipping_created' => __('发货记录已创建。', 'aksrt'),
+ 'shipping_create_failed' => __('发货记录创建失败。', 'aksrt'),
+ 'after_sale_created' => __('售后工单已创建。', 'aksrt'),
+ 'after_sale_create_failed' => __('售后工单创建失败。', 'aksrt'),
+ ];
+ if (!isset($map[$code])) {
+ return;
+ }
+ $class = str_contains($code, 'failed') ? 'notice-error' : 'notice-success';
+ echo '' . esc_html($map[$code]) . '
';
+ }
+
+ private function page_url(string $slug, array $args = []): string {
+ return add_query_arg(array_merge(['page' => $slug], $args), admin_url('admin.php'));
+ }
+
+ private function render_pagination(int $pages, int $page_num): void {
+ if ($pages <= 1) {
+ return;
+ }
+ echo '';
+ echo paginate_links([
+ 'base' => add_query_arg('paged', '%#%'),
+ 'format' => '',
+ 'prev_text' => '«',
+ 'next_text' => '»',
+ 'total' => $pages,
+ 'current' => $page_num,
+ ]);
+ echo '
';
+ }
+}
diff --git a/inc/classes/admin/native/class-aksrt-admin-pay-rebate.php b/inc/classes/admin/native/class-aksrt-admin-pay-rebate.php
new file mode 100644
index 00000000..d200e0d7
--- /dev/null
+++ b/inc/classes/admin/native/class-aksrt-admin-pay-rebate.php
@@ -0,0 +1,195 @@
+ensure_cap();
+ $this->render_header(__('推广返佣', 'aksrt'));
+
+ if (!class_exists('AKSRT_Rebate')) {
+ echo '' . esc_html__('返佣模块未初始化。', 'aksrt') . '
';
+ $this->render_footer();
+ return;
+ }
+
+ $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
+ $page_num = max(1, (int) ($_GET['paged'] ?? 1));
+ $per_page = 20;
+ $svc = AKSRT_Rebate::get_instance();
+ $stats = $svc->get_stats();
+
+ echo '' . esc_html(sprintf(
+ /* translators: 1: total 2: pending 3: settled amount */
+ __('记录 %1$d · 待结算 %2$d · 已结算金额 %3$s', 'aksrt'),
+ (int) ($stats['total'] ?? 0),
+ (int) ($stats['pending'] ?? 0),
+ number_format((float) ($stats['settled_amount'] ?? 0), 2)
+ )) . '
';
+
+ echo '';
+
+ $result = $svc->get_list([
+ 'status' => $status,
+ 'limit' => $per_page,
+ 'offset' => ($page_num - 1) * $per_page,
+ ]);
+ $items = $result['items'] ?? [];
+ $total = (int) ($result['total'] ?? 0);
+ $pages = max(1, (int) ceil(max(1, $total) / $per_page));
+
+ echo '';
+ echo '| ID | ' . esc_html__('用户', 'aksrt') . ' | ' . esc_html__('订单号', 'aksrt') . ' | ' . esc_html__('金额', 'aksrt') . ' | ' . esc_html__('状态', 'aksrt') . ' | ' . esc_html__('时间', 'aksrt') . ' | ' . esc_html__('操作', 'aksrt') . ' | ';
+ echo '
';
+ if (!$items) {
+ echo '| ' . esc_html__('暂无返佣记录', 'aksrt') . ' |
';
+ }
+ foreach ($items as $row) {
+ $id = (int) ($row['id'] ?? 0);
+ $user_id = (int) ($row['user_id'] ?? 0);
+ $user = $user_id > 0 ? get_userdata($user_id) : null;
+ $st = (string) ($row['status'] ?? '');
+ echo '';
+ echo '| ' . esc_html((string) $id) . ' | ';
+ echo '' . ($user ? esc_html($user->display_name) : esc_html((string) $user_id)) . ' | ';
+ echo '' . esc_html((string) ($row['order_number'] ?? '')) . ' | ';
+ echo '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' | ';
+ echo '' . esc_html($st) . ' | ';
+ echo '' . esc_html((string) ($row['created_at'] ?? '')) . ' | ';
+ echo '';
+ if ($st === 'pending') {
+ echo '';
+ echo '';
+ } else {
+ echo '—';
+ }
+ echo ' |
';
+ }
+ echo '
';
+ $this->render_pagination($pages, $page_num);
+ $this->render_footer();
+ }
+
+ public function handle_settle_rebate(): void {
+ if (!current_user_can(self::CAPABILITY)) {
+ wp_die(esc_html__('权限不足', 'aksrt'));
+ }
+ $id = isset($_POST['rebate_id']) ? (int) $_POST['rebate_id'] : 0;
+ check_admin_referer('aksrt_pay_settle_rebate_' . $id);
+ $ok = $id > 0 && class_exists('AKSRT_Rebate') && AKSRT_Rebate::get_instance()->settle($id);
+ wp_safe_redirect($this->page_url('aksrt_rebate', ['aksrt_pay_msg' => $ok ? 'rebate_settled' : 'rebate_failed']));
+ exit;
+ }
+
+ public function handle_cancel_rebate(): void {
+ if (!current_user_can(self::CAPABILITY)) {
+ wp_die(esc_html__('权限不足', 'aksrt'));
+ }
+ $id = isset($_POST['rebate_id']) ? (int) $_POST['rebate_id'] : 0;
+ check_admin_referer('aksrt_pay_cancel_rebate_' . $id);
+ $ok = $id > 0 && class_exists('AKSRT_Rebate') && AKSRT_Rebate::get_instance()->cancel($id);
+ wp_safe_redirect($this->page_url('aksrt_rebate', ['aksrt_pay_msg' => $ok ? 'rebate_cancelled' : 'rebate_failed']));
+ exit;
+ }
+
+
+
+ private function ensure_cap(): void {
+ if (!current_user_can(self::CAPABILITY)) {
+ wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
+ }
+ }
+
+ private function render_header(string $title): void {
+ echo '';
+ echo '
' . esc_html($title) . '
';
+ $this->render_flash();
+ }
+
+ private function render_footer(): void {
+ echo '';
+ }
+
+ private function render_flash(): void {
+ if (empty($_GET['aksrt_pay_msg'])) {
+ return;
+ }
+ $code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
+ $map = [
+ 'rebate_settled' => __('返佣已结算。', 'aksrt'),
+ 'rebate_cancelled' => __('返佣已取消。', 'aksrt'),
+ 'rebate_failed' => __('返佣操作失败。', 'aksrt'),
+ ];
+ if (!isset($map[$code])) {
+ return;
+ }
+ $class = str_contains($code, 'failed') ? 'notice-error' : 'notice-success';
+ echo '' . esc_html($map[$code]) . '
';
+ }
+
+ private function page_url(string $slug, array $args = []): string {
+ return add_query_arg(array_merge(['page' => $slug], $args), admin_url('admin.php'));
+ }
+
+ private function render_pagination(int $pages, int $page_num): void {
+ if ($pages <= 1) {
+ return;
+ }
+ echo '';
+ echo paginate_links([
+ 'base' => add_query_arg('paged', '%#%'),
+ 'format' => '',
+ 'prev_text' => '«',
+ 'next_text' => '»',
+ 'total' => $pages,
+ 'current' => $page_num,
+ ]);
+ echo '
';
+ }
+}
diff --git a/inc/classes/admin/native/class-aksrt-admin-pay-withdrawals.php b/inc/classes/admin/native/class-aksrt-admin-pay-withdrawals.php
new file mode 100644
index 00000000..f80e8a42
--- /dev/null
+++ b/inc/classes/admin/native/class-aksrt-admin-pay-withdrawals.php
@@ -0,0 +1,240 @@
+ensure_cap();
+ $this->render_header(__('提现审核', 'aksrt'));
+
+ if (!class_exists('AKSRT_Withdrawal')) {
+ echo '' . esc_html__('提现模块未初始化。', 'aksrt') . '
';
+ $this->render_footer();
+ return;
+ }
+
+ $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
+ $page_num = max(1, (int) ($_GET['paged'] ?? 1));
+ $per_page = 20;
+ $svc = AKSRT_Withdrawal::get_instance();
+ $stats = $svc->get_stats();
+
+ echo '' . esc_html(sprintf(
+ /* translators: 1: total 2: pending 3: completed 4: paid */
+ __('共 %1$d 条 · 待处理 %2$d · 已完成 %3$d · 已打款 %4$s', 'aksrt'),
+ (int) ($stats['total'] ?? 0),
+ (int) ($stats['pending'] ?? 0),
+ (int) ($stats['completed'] ?? 0),
+ number_format((float) ($stats['total_paid'] ?? 0), 2)
+ )) . '
';
+
+ echo '';
+
+ $items = $svc->get_all([
+ 'status' => $status,
+ 'limit' => $per_page,
+ 'offset' => ($page_num - 1) * $per_page,
+ ]);
+ $total = $svc->get_count(['status' => $status]);
+ $pages = max(1, (int) ceil($total / $per_page));
+
+ echo '';
+ echo '';
+ echo '| ID | ';
+ echo '' . esc_html__('用户', 'aksrt') . ' | ';
+ echo '' . esc_html__('金额', 'aksrt') . ' | ';
+ echo '' . esc_html__('方式', 'aksrt') . ' | ';
+ echo '' . esc_html__('账户', 'aksrt') . ' | ';
+ echo '' . esc_html__('状态', 'aksrt') . ' | ';
+ echo '' . esc_html__('申请时间', 'aksrt') . ' | ';
+ echo '' . esc_html__('操作', 'aksrt') . ' | ';
+ echo '
';
+
+ if (!$items) {
+ echo '| ' . esc_html__('暂无提现记录', 'aksrt') . ' |
';
+ }
+
+ foreach ($items as $row) {
+ $id = (int) ($row['id'] ?? 0);
+ $user_id = (int) ($row['user_id'] ?? 0);
+ $user = $user_id > 0 ? get_userdata($user_id) : null;
+ $st = (string) ($row['status'] ?? '');
+
+ echo '';
+ echo '| ' . esc_html((string) $id) . ' | ';
+ echo '' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . ' | ';
+ echo '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' | ';
+ echo '' . esc_html((string) ($row['method'] ?? '')) . ' | ';
+ echo '' . esc_html((string) ($row['account_info'] ?? '')) . ' | ';
+ echo '' . esc_html($status_options[$st] ?? $st) . ' | ';
+ echo '' . esc_html((string) ($row['created_at'] ?? '')) . ' | ';
+ echo '';
+ echo '';
+ echo ' | ';
+ echo '
';
+ }
+
+ echo '
';
+
+ if ($pages > 1) {
+ echo '';
+ echo paginate_links([
+ 'base' => add_query_arg('paged', '%#%'),
+ 'format' => '',
+ 'prev_text' => '«',
+ 'next_text' => '»',
+ 'total' => $pages,
+ 'current' => $page_num,
+ ]);
+ echo '
';
+ }
+
+ $this->render_footer();
+ }
+
+ public function handle_update_withdrawal(): void {
+ if (!current_user_can(self::CAPABILITY)) {
+ wp_die(esc_html__('权限不足', 'aksrt'));
+ }
+
+ $id = isset($_POST['withdrawal_id']) ? (int) $_POST['withdrawal_id'] : 0;
+ $new_status = isset($_POST['new_status']) ? sanitize_key((string) wp_unslash($_POST['new_status'])) : '';
+ $note = isset($_POST['note']) ? sanitize_textarea_field((string) wp_unslash($_POST['note'])) : '';
+ check_admin_referer('aksrt_pay_update_withdrawal_' . $id);
+
+ $ok = false;
+ if ($id > 0 && $new_status !== '' && class_exists('AKSRT_Withdrawal')) {
+ $ok = AKSRT_Withdrawal::get_instance()->update_status($id, $new_status, $note);
+ }
+
+ wp_safe_redirect($this->page_url('aksrt_withdraw', [
+ 'aksrt_pay_msg' => $ok ? 'withdraw_updated' : 'withdraw_failed',
+ ]));
+ exit;
+ }
+
+ /* ------------------------------------------------------------------
+ * Income / rebate
+ * ----------------------------------------------------------------*/
+
+
+
+ private function ensure_cap(): void {
+ if (!current_user_can(self::CAPABILITY)) {
+ wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
+ }
+ }
+
+ private function render_header(string $title): void {
+ echo '';
+ echo '
' . esc_html($title) . '
';
+ $this->render_flash();
+ }
+
+ private function render_footer(): void {
+ echo '';
+ }
+
+ private function render_flash(): void {
+ if (empty($_GET['aksrt_pay_msg'])) {
+ return;
+ }
+ $code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
+ $map = [
+ 'withdraw_updated' => __('提现状态已更新。', 'aksrt'),
+ 'withdraw_failed' => __('提现状态更新失败。', 'aksrt'),
+ ];
+ if (!isset($map[$code])) {
+ return;
+ }
+ $class = str_contains($code, 'failed') ? 'notice-error' : 'notice-success';
+ echo '' . esc_html($map[$code]) . '
';
+ }
+
+ private function page_url(string $slug, array $args = []): string {
+ return add_query_arg(array_merge(['page' => $slug], $args), admin_url('admin.php'));
+ }
+
+ private function render_pagination(int $pages, int $page_num): void {
+ if ($pages <= 1) {
+ return;
+ }
+ echo '';
+ echo paginate_links([
+ 'base' => add_query_arg('paged', '%#%'),
+ 'format' => '',
+ 'prev_text' => '«',
+ 'next_text' => '»',
+ 'total' => $pages,
+ 'current' => $page_num,
+ ]);
+ echo '
';
+ }
+}