From 42acb1a630f33d55af1392b6e7dd9054e9d48345 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Jul 2026 09:56:30 +0800 Subject: [PATCH] refactor(admin): finish pay-menus modular split under 300-line shell --- docs/zibll-gap-matrix.md | 4 +- functions.php | 12 + inc/bootstrap.php | 4 + .../native/class-aksrt-admin-pay-income.php | 176 ++++ .../native/class-aksrt-admin-pay-menus.php | 853 +----------------- .../native/class-aksrt-admin-pay-orders.php | 488 ++++++++++ .../native/class-aksrt-admin-pay-rebate.php | 195 ++++ .../class-aksrt-admin-pay-withdrawals.php | 240 +++++ 8 files changed, 1150 insertions(+), 822 deletions(-) create mode 100644 inc/classes/admin/native/class-aksrt-admin-pay-income.php create mode 100644 inc/classes/admin/native/class-aksrt-admin-pay-orders.php create mode 100644 inc/classes/admin/native/class-aksrt-admin-pay-rebate.php create mode 100644 inc/classes/admin/native/class-aksrt-admin-pay-withdrawals.php 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 '
'; + echo ''; + echo ' '; + submit_button(__('筛选', 'aksrt'), 'secondary', '', false); + 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 ''; + echo ''; + if (!$items) { + echo ''; + } + 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 ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo '
ID' . esc_html__('用户', 'aksrt') . '' . esc_html__('订单号', 'aksrt') . '' . esc_html__('金额', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html__('暂无分成记录', 'aksrt') . '
' . esc_html((string) $id) . '' . ($user ? esc_html($user->display_name) : esc_html((string) $user_id)) . '' . esc_html((string) ($row['order_number'] ?? '')) . '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . '' . esc_html($st) . '' . esc_html((string) ($row['created_at'] ?? '')) . ''; + if ($st === 'pending') { + echo '
'; + echo ''; + echo ''; + wp_nonce_field('aksrt_pay_settle_income_' . $id); + submit_button(__('结算', 'aksrt'), 'small', '', false); + echo '
'; + } else { + 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 ' '; - submit_button(__('筛选', 'aksrt'), 'secondary', '', false); - echo '
'; - - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - - if (!$items) { - echo ''; - } - - 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 ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - - echo '
' . esc_html__('订单号', 'aksrt') . '' . esc_html__('用户', 'aksrt') . '' . esc_html__('类型', 'aksrt') . '' . esc_html__('金额', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html__('暂无订单', 'aksrt') . '
' . esc_html($order_number) . '' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . '' . esc_html((string) ($row['product_type'] ?? '')) . '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' ' . esc_html((string) ($row['currency'] ?? '')) . '' . esc_html($status_label) . '' . esc_html((string) ($row['created_at'] ?? '')) . ''; - echo '' . esc_html__('详情', 'aksrt') . ''; - $this->render_order_quick_status_buttons($order_number, $status_key); - echo '
'; - echo ''; - echo ''; - wp_nonce_field('aksrt_pay_update_order_status_' . $order_number); - echo ''; - submit_button(__('更新', 'aksrt'), 'small', '', false); - 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 ''; - } - if (!empty($context)) { - echo ''; - } - echo '
' . esc_html($label) . '' . esc_html((string) $value) . '
' . esc_html__('附加上下文', 'aksrt') . '
' . esc_html(wp_json_encode($context, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)) . '
'; - - echo '

' . esc_html__('运营备注', 'aksrt') . '

'; - echo '
'; - echo ''; - echo ''; - wp_nonce_field('aksrt_pay_update_order_note_' . $order_number); - echo '

'; - submit_button(__('保存备注', 'aksrt'), 'secondary', '', false); - echo '
'; - - echo '

' . esc_html__('更新状态', 'aksrt') . '

'; - echo '
'; - $this->render_order_quick_status_buttons($order_number, $status, true); - echo '
'; - echo '
'; - echo ''; - echo ''; - echo ''; - wp_nonce_field('aksrt_pay_update_order_status_' . $order_number); - echo ' '; - submit_button(__('保存状态', 'aksrt'), 'primary', '', false); - echo '
'; - - echo '

' . esc_html__('物流记录', 'aksrt') . '

'; - echo '
'; - echo ''; - echo ''; - echo ''; - wp_nonce_field('aksrt_pay_create_shipping_' . $order_number); - echo '

'; - echo '

'; - echo '

'; - submit_button(__('创建发货', 'aksrt'), 'secondary', '', false); - echo ' ' . esc_html__('物流管理', 'aksrt') . ''; - echo '
'; - if (!$shipping_items) { - echo '

' . esc_html__('暂无物流记录。', 'aksrt') . '

'; - } else { - $status_options = $this->shipping_status_options(false); - echo ''; - foreach ($shipping_items as $item) { - $sid = (int) ($item['id'] ?? 0); - $st = (string) ($item['status'] ?? ''); - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - echo '
ID' . esc_html__('承运商', 'aksrt') . '' . esc_html__('运单号', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html((string) $sid) . '' . esc_html((string) ($item['carrier'] ?? '')) . '' . esc_html((string) ($item['tracking_no'] ?? '')) . '' . esc_html($status_options[$st] ?? $st) . '' . esc_html((string) ($item['created_at'] ?? '')) . '
'; - echo ''; - echo ''; - echo ''; - wp_nonce_field('aksrt_pay_update_shipping_' . $sid); - echo ''; - echo ''; - submit_button(__('更新', 'aksrt'), 'small', '', false); - echo '
'; - } - - echo '

' . esc_html__('售后工单', 'aksrt') . '

'; - echo '
'; - echo ''; - echo ''; - echo ''; - echo ''; - wp_nonce_field('aksrt_pay_create_after_sale_' . $order_number); - echo '

'; - echo '

'; - echo '

'; - echo '

'; - submit_button(__('创建售后', 'aksrt'), 'secondary', '', false); - echo ' ' . esc_html__('售后管理', 'aksrt') . ''; - echo '
'; - if (!$after_sale) { - echo '

' . esc_html__('暂无售后工单。', 'aksrt') . '

'; - } else { - echo ''; - foreach ($after_sale as $item) { - $tid = (int) ($item['id'] ?? 0); - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - echo '
ID' . esc_html__('类型', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html((string) $tid) . '' . esc_html((string) ($item['type_label'] ?? $item['type'] ?? '')) . '' . esc_html((string) ($item['status_label'] ?? $item['status'] ?? '')) . '' . esc_html((string) ($item['created_at'] ?? '')) . '' . esc_html__('在售后页处理', 'aksrt') . '
'; - } - } - - 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 '
'; - echo ''; - echo ' '; - submit_button(__('筛选', 'aksrt'), 'secondary', '', false); - 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 ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - - if (!$items) { - echo ''; - } - - 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 ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - - echo '
ID' . esc_html__('用户', 'aksrt') . '' . esc_html__('金额', 'aksrt') . '' . esc_html__('方式', 'aksrt') . '' . esc_html__('账户', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('申请时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html__('暂无提现记录', 'aksrt') . '
' . esc_html((string) $id) . '' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . '' . esc_html((string) ($row['method'] ?? '')) . '' . esc_html((string) ($row['account_info'] ?? '')) . '' . esc_html($status_options[$st] ?? $st) . '' . esc_html((string) ($row['created_at'] ?? '')) . ''; - echo '
'; - echo ''; - echo ''; - wp_nonce_field('aksrt_pay_update_withdrawal_' . $id); - echo ''; - echo ''; - submit_button(__('更新', 'aksrt'), 'small', '', false); - 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 '
'; - echo ''; - echo ' '; - submit_button(__('筛选', 'aksrt'), 'secondary', '', false); - 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 ''; - echo ''; - if (!$items) { - echo ''; - } - 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 ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - echo '
ID' . esc_html__('用户', 'aksrt') . '' . esc_html__('订单号', 'aksrt') . '' . esc_html__('金额', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html__('暂无分成记录', 'aksrt') . '
' . esc_html((string) $id) . '' . ($user ? esc_html($user->display_name) : esc_html((string) $user_id)) . '' . esc_html((string) ($row['order_number'] ?? '')) . '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . '' . esc_html($st) . '' . esc_html((string) ($row['created_at'] ?? '')) . ''; - if ($st === 'pending') { - echo '
'; - echo ''; - echo ''; - wp_nonce_field('aksrt_pay_settle_income_' . $id); - submit_button(__('结算', 'aksrt'), 'small', '', false); - echo '
'; - } else { - 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 '
'; - echo ''; - echo ' '; - submit_button(__('筛选', 'aksrt'), 'secondary', '', false); - 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 ''; - echo ''; - if (!$items) { - echo ''; - } - 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 ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - echo '
ID' . esc_html__('用户', 'aksrt') . '' . esc_html__('订单号', 'aksrt') . '' . esc_html__('金额', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html__('暂无返佣记录', 'aksrt') . '
' . esc_html((string) $id) . '' . ($user ? esc_html($user->display_name) : esc_html((string) $user_id)) . '' . esc_html((string) ($row['order_number'] ?? '')) . '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . '' . esc_html($st) . '' . esc_html((string) ($row['created_at'] ?? '')) . ''; - if ($st === 'pending') { - echo '
'; - echo ''; - echo ''; - wp_nonce_field('aksrt_pay_settle_rebate_' . $id); - submit_button(__('结算', 'aksrt'), 'small', '', false); - echo '
'; - echo '
'; - echo ''; - echo ''; - wp_nonce_field('aksrt_pay_cancel_rebate_' . $id); - submit_button(__('取消', 'aksrt'), 'small', '', false); - echo '
'; - } else { - 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 '
'; - echo ''; - echo ''; - echo ''; - if ($redirect_detail) { - echo ''; - } - wp_nonce_field('aksrt_pay_update_order_status_' . $order_number); - printf( - '', - $confirm !== '' ? ' onclick="return confirm(\'' . esc_js($confirm) . '\');"' : '', - esc_html($label) - ); - 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 ' '; + submit_button(__('筛选', 'aksrt'), 'secondary', '', false); + echo '
'; + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + if (!$items) { + echo ''; + } + + 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 ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo '
' . esc_html__('订单号', 'aksrt') . '' . esc_html__('用户', 'aksrt') . '' . esc_html__('类型', 'aksrt') . '' . esc_html__('金额', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html__('暂无订单', 'aksrt') . '
' . esc_html($order_number) . '' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . '' . esc_html((string) ($row['product_type'] ?? '')) . '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' ' . esc_html((string) ($row['currency'] ?? '')) . '' . esc_html($status_label) . '' . esc_html((string) ($row['created_at'] ?? '')) . ''; + echo '' . esc_html__('详情', 'aksrt') . ''; + $this->render_order_quick_status_buttons($order_number, $status_key); + echo '
'; + echo ''; + echo ''; + wp_nonce_field('aksrt_pay_update_order_status_' . $order_number); + echo ''; + submit_button(__('更新', 'aksrt'), 'small', '', false); + 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 ''; + } + if (!empty($context)) { + echo ''; + } + echo '
' . esc_html($label) . '' . esc_html((string) $value) . '
' . esc_html__('附加上下文', 'aksrt') . '
' . esc_html(wp_json_encode($context, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)) . '
'; + + echo '

' . esc_html__('运营备注', 'aksrt') . '

'; + echo '
'; + echo ''; + echo ''; + wp_nonce_field('aksrt_pay_update_order_note_' . $order_number); + echo '

'; + submit_button(__('保存备注', 'aksrt'), 'secondary', '', false); + echo '
'; + + echo '

' . esc_html__('更新状态', 'aksrt') . '

'; + echo '
'; + $this->render_order_quick_status_buttons($order_number, $status, true); + echo '
'; + echo '
'; + echo ''; + echo ''; + echo ''; + wp_nonce_field('aksrt_pay_update_order_status_' . $order_number); + echo ' '; + submit_button(__('保存状态', 'aksrt'), 'primary', '', false); + echo '
'; + + echo '

' . esc_html__('物流记录', 'aksrt') . '

'; + echo '
'; + echo ''; + echo ''; + echo ''; + wp_nonce_field('aksrt_pay_create_shipping_' . $order_number); + echo '

'; + echo '

'; + echo '

'; + submit_button(__('创建发货', 'aksrt'), 'secondary', '', false); + echo ' ' . esc_html__('物流管理', 'aksrt') . ''; + echo '
'; + if (!$shipping_items) { + echo '

' . esc_html__('暂无物流记录。', 'aksrt') . '

'; + } else { + $status_options = $this->shipping_status_options(false); + echo ''; + foreach ($shipping_items as $item) { + $sid = (int) ($item['id'] ?? 0); + $st = (string) ($item['status'] ?? ''); + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo '
ID' . esc_html__('承运商', 'aksrt') . '' . esc_html__('运单号', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html((string) $sid) . '' . esc_html((string) ($item['carrier'] ?? '')) . '' . esc_html((string) ($item['tracking_no'] ?? '')) . '' . esc_html($status_options[$st] ?? $st) . '' . esc_html((string) ($item['created_at'] ?? '')) . '
'; + echo ''; + echo ''; + echo ''; + wp_nonce_field('aksrt_pay_update_shipping_' . $sid); + echo ''; + echo ''; + submit_button(__('更新', 'aksrt'), 'small', '', false); + echo '
'; + } + + echo '

' . esc_html__('售后工单', 'aksrt') . '

'; + echo '
'; + echo ''; + echo ''; + echo ''; + echo ''; + wp_nonce_field('aksrt_pay_create_after_sale_' . $order_number); + echo '

'; + echo '

'; + echo '

'; + echo '

'; + submit_button(__('创建售后', 'aksrt'), 'secondary', '', false); + echo ' ' . esc_html__('售后管理', 'aksrt') . ''; + echo '
'; + if (!$after_sale) { + echo '

' . esc_html__('暂无售后工单。', 'aksrt') . '

'; + } else { + echo ''; + foreach ($after_sale as $item) { + $tid = (int) ($item['id'] ?? 0); + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo '
ID' . esc_html__('类型', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html((string) $tid) . '' . esc_html((string) ($item['type_label'] ?? $item['type'] ?? '')) . '' . esc_html((string) ($item['status_label'] ?? $item['status'] ?? '')) . '' . esc_html((string) ($item['created_at'] ?? '')) . '' . esc_html__('在售后页处理', 'aksrt') . '
'; + } + } + + 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 '
'; + echo ''; + echo ''; + echo ''; + if ($redirect_detail) { + echo ''; + } + wp_nonce_field('aksrt_pay_update_order_status_' . $order_number); + printf( + '', + $confirm !== '' ? ' onclick="return confirm(\'' . esc_js($confirm) . '\');"' : '', + esc_html($label) + ); + 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 '
'; + echo ''; + echo ' '; + submit_button(__('筛选', 'aksrt'), 'secondary', '', false); + 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 ''; + echo ''; + if (!$items) { + echo ''; + } + 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 ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo '
ID' . esc_html__('用户', 'aksrt') . '' . esc_html__('订单号', 'aksrt') . '' . esc_html__('金额', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html__('暂无返佣记录', 'aksrt') . '
' . esc_html((string) $id) . '' . ($user ? esc_html($user->display_name) : esc_html((string) $user_id)) . '' . esc_html((string) ($row['order_number'] ?? '')) . '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . '' . esc_html($st) . '' . esc_html((string) ($row['created_at'] ?? '')) . ''; + if ($st === 'pending') { + echo '
'; + echo ''; + echo ''; + wp_nonce_field('aksrt_pay_settle_rebate_' . $id); + submit_button(__('结算', 'aksrt'), 'small', '', false); + echo '
'; + echo '
'; + echo ''; + echo ''; + wp_nonce_field('aksrt_pay_cancel_rebate_' . $id); + submit_button(__('取消', 'aksrt'), 'small', '', false); + echo '
'; + } else { + 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 '
'; + echo ''; + echo ' '; + submit_button(__('筛选', 'aksrt'), 'secondary', '', false); + 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 ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + if (!$items) { + echo ''; + } + + 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 ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo '
ID' . esc_html__('用户', 'aksrt') . '' . esc_html__('金额', 'aksrt') . '' . esc_html__('方式', 'aksrt') . '' . esc_html__('账户', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('申请时间', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html__('暂无提现记录', 'aksrt') . '
' . esc_html((string) $id) . '' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . '' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . '' . esc_html((string) ($row['method'] ?? '')) . '' . esc_html((string) ($row['account_info'] ?? '')) . '' . esc_html($status_options[$st] ?? $st) . '' . esc_html((string) ($row['created_at'] ?? '')) . ''; + echo '
'; + echo ''; + echo ''; + wp_nonce_field('aksrt_pay_update_withdrawal_' . $id); + echo ''; + echo ''; + submit_button(__('更新', 'aksrt'), 'small', '', false); + 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 '
'; + } +}