diff --git a/docs/zibll-gap-matrix.md b/docs/zibll-gap-matrix.md index 9f042571..48496f4e 100644 --- a/docs/zibll-gap-matrix.md +++ b/docs/zibll-gap-matrix.md @@ -48,7 +48,7 @@ Focus: admin **entry & page shape**. Runtime field parity is Phase 5+. | `zibpay_order_page` | Orders (legacy) | SPA | same native list | | | `zibpay_page#/shipping` | Shipping | SPA | `aksrt_shipping` | `AKSRT_Shipping` | | `zibpay_page#/after-sale` | After-sale | SPA | `aksrt_after_sale` | `AKSRT_After_Sale` | -| `zibpay_product_page` | Paid-post sales report | partial | optional later `aksrt_pay_products` | content pay | +| `zibpay_product_page` | Paid-post sales report | done | `aksrt_pay_products` | content pay (`AKSRT_Admin_Pay_Products` + order aggregation) | | `zibpay_income_page` | Creator income | `PaymentRevenue` | `aksrt_income` | `AKSRT_Revenue` | | `zibpay_rebate_page` | Rebate | `PaymentRebate` | `aksrt_rebate` | `AKSRT_Rebate` | | `zibpay_withdraw` | Withdraw | `ShopWithdrawals` | `aksrt_withdraw` | `AKSRT_Withdrawal` | @@ -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 — 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 | +| 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 + paid-post sales report (`aksrt_pay_products`) + topic pay/points visibility admin/runtime alignment | +| 5 remaining | plate follow-pay (zibll paid follow) still optional; topic-level pay/points/hide-part done | ## Dual-UI rule diff --git a/functions.php b/functions.php index e12f4b2f..4c793d43 100644 --- a/functions.php +++ b/functions.php @@ -371,6 +371,9 @@ if (is_admin() && class_exists('AKSRT_Admin_Pay_After_Sale')) { if (is_admin() && class_exists('AKSRT_Admin_Pay_Orders')) { AKSRT_Admin_Pay_Orders::get_instance()->init(); } +if (is_admin() && class_exists('AKSRT_Admin_Pay_Products')) { + AKSRT_Admin_Pay_Products::get_instance()->init(); +} if (is_admin() && class_exists('AKSRT_Admin_Pay_Withdrawals')) { AKSRT_Admin_Pay_Withdrawals::get_instance()->init(); } diff --git a/inc/bootstrap.php b/inc/bootstrap.php index 675d3751..db167446 100644 --- a/inc/bootstrap.php +++ b/inc/bootstrap.php @@ -178,6 +178,7 @@ require_once AKSRT_DIR . '/inc/classes/admin/native/class-aksrt-admin-pay-charge 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-products.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'; 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 f9a076d7..f3189845 100644 --- a/inc/classes/admin/native/class-aksrt-admin-pay-menus.php +++ b/inc/classes/admin/native/class-aksrt-admin-pay-menus.php @@ -54,6 +54,17 @@ class AKSRT_Admin_Pay_Menus { wp_die(esc_html__('订单模块未初始化。', 'aksrt')); }, ], + 'products' => [ + 'slug' => 'aksrt_pay_products', + 'title' => __('付费文章', 'aksrt'), + 'callback' => static function (): void { + if (class_exists('AKSRT_Admin_Pay_Products')) { + AKSRT_Admin_Pay_Products::get_instance()->render_products_page(); + return; + } + wp_die(esc_html__('付费文章模块未初始化。', 'aksrt')); + }, + ], 'shipping' => [ 'slug' => 'aksrt_shipping', 'title' => __('发货及物流', 'aksrt'), diff --git a/inc/classes/admin/native/class-aksrt-admin-pay-orders.php b/inc/classes/admin/native/class-aksrt-admin-pay-orders.php index 6311e75b..615fff0b 100644 --- a/inc/classes/admin/native/class-aksrt-admin-pay-orders.php +++ b/inc/classes/admin/native/class-aksrt-admin-pay-orders.php @@ -48,10 +48,12 @@ class AKSRT_Admin_Pay_Orders { 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; + $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : ''; + $search = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : ''; + $product_type = isset($_GET['product_type']) ? sanitize_key((string) wp_unslash($_GET['product_type'])) : ''; + $product_id = isset($_GET['product_id']) ? max(0, (int) $_GET['product_id']) : 0; + $page_num = max(1, (int) ($_GET['paged'] ?? 1)); + $per_page = 20; $repo = AKSRT_Order_Repository::get_instance(); $args = [ @@ -61,6 +63,12 @@ class AKSRT_Admin_Pay_Orders { if ($status !== '' && $status !== 'all') { $args['status'] = $status; } + if ($product_type !== '') { + $args['product_type'] = $product_type; + } + if ($product_id > 0) { + $args['product_id'] = $product_id; + } $items = $repo->get_orders_admin($args, $search); $total = $repo->get_orders_count($args, $search); @@ -71,6 +79,12 @@ class AKSRT_Admin_Pay_Orders { echo '
'; echo '| ' . esc_html__('商品', 'aksrt') . ' | '; + echo '' . esc_html__('ID', 'aksrt') . ' | '; + echo '' . esc_html__('付费类型', 'aksrt') . ' | '; + echo '' . esc_html__('模式', 'aksrt') . ' | '; + echo '' . esc_html__('标价', 'aksrt') . ' | '; + echo '' . esc_html__('销售均价', 'aksrt') . ' | '; + echo '' . esc_html__('订单销量', 'aksrt') . ' | '; + echo '' . esc_html__('Meta 销量', 'aksrt') . ' | '; + echo '' . esc_html__('销售总额', 'aksrt') . ' | '; + echo '' . esc_html__('操作', 'aksrt') . ' | '; + echo '
|---|---|---|---|---|---|---|---|---|---|
| ' . esc_html__('暂无付费文章。', 'aksrt') . ' | |||||||||
| '; + if ($edit_url) { + echo '' . esc_html($title) . ''; + } else { + echo '' . esc_html($title) . ''; + } + echo ' | '; + echo '' . esc_html((string) $post_id) . ' | '; + echo '' . esc_html((string) $pt_label) . ' | '; + echo '' . esc_html($modo_label) . ' | '; + echo '' . esc_html($price_text) . ' | '; + echo '' . esc_html(number_format($avg, 2)) . ' | '; + echo '' . esc_html((string) $db_sales) . ' | '; + echo '' . esc_html((string) max(0, $meta_sales)) . ' | '; + echo '' . esc_html(number_format($revenue, 2)) . ' | '; + echo ''; + if ($edit_url) { + echo '' . esc_html__('编辑', 'aksrt') . ''; + } + echo '' . esc_html__('订单', 'aksrt') . ''; + $permalink = get_permalink($post_id); + if ($permalink) { + echo '' . esc_html__('查看', 'aksrt') . ''; + } + echo ' | '; + echo '
' . esc_html__('本页统计内容付费文章(product_type=resource),不含商城实体商品。订单销量来自已支付/已发货/已完成订单;Meta 销量为缓存计数。', 'aksrt') . '
'; + } + + private function ensure_cap(): void { + if (!current_user_can(self::CAPABILITY)) { + wp_die(esc_html__('您没有权限访问此页面。', 'aksrt')); + } + } + + private function render_header(string $title): void { + echo '@@ -104,26 +151,42 @@ class AKSRT_Topic_Meta_Box {
-
- -