register_default_panels(); add_action('rest_api_init', [$this, 'register_rest_routes']); } /** * Register user-center fragment routes. */ public function register_rest_routes(): void { register_rest_route('aksrt/v1', '/user-center/panel/(?P[a-z0-9_-]+)', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [$this, 'rest_panel'], 'permission_callback' => static function (): bool { return is_user_logged_in(); }, 'args' => [ 'tab' => [ 'required' => true, 'sanitize_callback' => 'sanitize_key', 'validate_callback' => static function ($value): bool { return preg_match('/^[a-z0-9_-]+$/', (string) $value) === 1; }, ], ], ]); } /** * Render a user-center panel or a revenue reward fragment. */ public function rest_panel(\WP_REST_Request $request) { $tab = sanitize_key((string) $request->get_param('tab')); $tabs = $this->get_dashboard_tabs(); if (!isset($tabs[$tab])) { return new \WP_Error('aksrt_user_center_panel', __('用户中心面板不存在', 'aksrt'), ['status' => 404]); } $user = wp_get_current_user(); $context = function_exists('aksrt_get_user_center_render_context') ? aksrt_get_user_center_render_context($user) : []; if (!is_array($context) || !$context) { return new \WP_Error('aksrt_user_center_context', __('用户中心数据加载失败', 'aksrt'), ['status' => 500]); } $income_request = []; if ($tab === 'creator') { $currency = sanitize_key((string) $request->get_param('income_currency')); $status = sanitize_key((string) $request->get_param('income_status')); $income_request = [ 'currency' => in_array($currency, ['', 'CNY', 'points'], true) ? $currency : '', 'status' => in_array($status, ['', 'pending', 'settled', 'cancelled', 'reversed', 'pending_recovery'], true) ? $status : '', 'page' => max(1, min(100, (int) $request->get_param('income_page'))), ]; $context['creator_income_request'] = $income_request; } $revenue_request = []; if ($tab === 'revenue') { $reward_tab = sanitize_key((string) $request->get_param('reward_tab')); if (!in_array($reward_tab, ['detail', 'orders', 'users'], true)) { $reward_tab = 'detail'; } $revenue_request = [ 'reward_tab' => $reward_tab, 'commission_page' => max(1, min(100, (int) $request->get_param('commission_page'))), 'invite_page' => max(1, min(100, (int) $request->get_param('invite_page'))), ]; $context['revenue_request'] = $revenue_request; } $html = function_exists('aksrt_capture_user_center_panel') ? aksrt_capture_user_center_panel($tab, $tabs[$tab], $context) : ''; if ($html === '') { return new \WP_Error('aksrt_user_center_panel_empty', __('用户中心面板暂不可用', 'aksrt'), ['status' => 500]); } return rest_ensure_response([ 'success' => true, 'data' => [ 'tab' => $tab, 'html' => $html, 'reward_tab' => $revenue_request['reward_tab'] ?? '', 'creator_income' => $income_request, 'commission_page' => $revenue_request['commission_page'] ?? 1, 'invite_page' => $revenue_request['invite_page'] ?? 1, ], ]); } /** * 注册默认面板 */ private function register_default_panels(): void { $settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_user_center_settings() : [ 'tabs' => ['profile', 'posts', 'comments', 'favorites'], 'avatar_upload' => true, 'cover_upload' => true, 'social_links' => true, ]; $definitions = [ 'profile' => [ 'label' => __('个人信息', 'aksrt'), 'icon' => 'user', 'priority' => 10, 'type' => 'panel', ], 'level' => [ 'label' => __('我的等级', 'aksrt'), 'icon' => 'chart-line', 'priority' => 15, 'type' => 'panel', ], 'posts' => [ 'label' => __('我的文章', 'aksrt'), 'icon' => 'file-alt', 'priority' => 20, 'type' => 'panel', ], 'creator' => [ 'label' => __('创作中心', 'aksrt'), 'icon' => 'feather-alt', 'priority' => 18, 'type' => 'panel', ], 'comments' => [ 'label' => __('我的评论', 'aksrt'), 'icon' => 'comment', 'priority' => 30, 'type' => 'panel', ], 'favorites' => [ 'label' => __('我的收藏', 'aksrt'), 'icon' => 'star', 'priority' => 40, 'type' => 'panel', ], 'vip' => [ 'label' => __('我的会员', 'aksrt'), 'icon' => 'crown', 'priority' => 45, 'type' => 'panel', ], 'orders' => [ 'label' => __('我的订单', 'aksrt'), 'icon' => 'receipt', 'priority' => 48, 'type' => 'panel', ], 'credits' => [ 'label' => class_exists('\AKSRT\Settings\Settings') ? sprintf(__('我的%s', 'aksrt'), \AKSRT\Settings\Settings::get_currency_name()) : __('我的积分', 'aksrt'), 'icon' => 'wallet', 'priority' => 50, 'type' => 'panel', ], 'revenue' => [ 'label' => __('收益中心', 'aksrt'), 'icon' => 'chart-line', 'priority' => 55, 'type' => 'panel', ], 'notif' => [ 'label' => __('消息通知', 'aksrt'), 'icon' => 'bell', 'priority' => 60, 'type' => 'link', ], 'auth' => [ 'label' => __('官方认证', 'aksrt'), 'icon' => 'id-badge', 'priority' => 65, 'type' => 'panel', ], 'settings' => [ 'label' => __('账号设置', 'aksrt'), 'icon' => 'cog', 'priority' => 70, 'type' => 'panel', ], ]; $this->panels = []; $configured = array_values(array_filter(array_map('sanitize_key', (array) ($settings['tabs'] ?? [])))); if ($configured === []) { $configured = ['profile', 'posts', 'comments', 'favorites']; } $level_on = class_exists('\AKSRT\User\Level') && !empty(\AKSRT\User\Level::settings()['enabled']); $credits_on = class_exists('\AKSRT\Settings\Settings') && \AKSRT\Settings\Settings::credits_enabled(); $balance_on = class_exists('\AKSRT\Credits\Balance') && \AKSRT\Credits\Balance::enabled(); $revenue_tab_on = class_exists('\AKSRT\Settings\Settings') && !empty(\AKSRT\Settings\Settings::get_payment_affiliate_settings()['enabled']) && \AKSRT\Settings\Settings::invite_registration_enabled(); $creator_tab_on = class_exists('\AKSRT\Settings\Settings') && !empty(\AKSRT\Settings\Settings::get_payment_revenue_settings()['enabled']); $vip_on = class_exists('\AKSRT\Settings\Settings') && !empty(\AKSRT\Settings\Settings::get_payment_vip_settings()['enabled']); // Follow configured order; always auto-include enabled surfaces. $order = $configured; // Creator center is a dedicated workspace, separate from the posts archive. if ($creator_tab_on && !in_array('creator', $order, true)) { $insert_at = array_search('profile', $order, true); array_splice($order, $insert_at === false ? 0 : (int) $insert_at + 1, 0, ['creator']); } if (!$creator_tab_on) { $order = array_values(array_filter($order, static fn(string $key): bool => $key !== 'creator')); } // Growth level is a dedicated user-center surface, separate from spendable points. if ($level_on && !in_array('level', $order, true)) { $insert_at = array_search('profile', $order, true); array_splice($order, $insert_at === false ? 0 : (int) $insert_at + 1, 0, ['level']); } if (!$level_on) { $order = array_values(array_filter($order, static fn(string $key): bool => $key !== 'level')); } if ($vip_on && !in_array('vip', $order, true)) { $insert_at = count($order); foreach (['credits', 'favorites', 'posts'] as $anchor) { $idx = array_search($anchor, $order, true); if ($idx !== false) { $insert_at = (int) $idx + 1; break; } } array_splice($order, $insert_at, 0, ['vip']); } // Orders panel: always available when payment stack exists (UserOrders or Order). $orders_on = class_exists('\AKSRT\Payment\UserOrders') || class_exists('\AKSRT\Payment\Order'); if ($orders_on && !in_array('orders', $order, true)) { $insert_at = count($order); foreach (['vip', 'credits', 'favorites'] as $anchor) { $idx = array_search($anchor, $order, true); if ($idx !== false) { $insert_at = (int) $idx + ($anchor === 'vip' ? 1 : 0); if ($anchor !== 'vip') { $insert_at = (int) $idx + 1; } break; } } array_splice($order, $insert_at, 0, ['orders']); } if (($credits_on || $balance_on) && !in_array('credits', $order, true)) { $order[] = 'credits'; } if ($revenue_tab_on && !in_array('revenue', $order, true)) { $insert_at = count($order); foreach (['credits', 'favorites', 'posts'] as $anchor) { $idx = array_search($anchor, $order, true); if ($idx !== false) { $insert_at = (int) $idx + 1; break; } } array_splice($order, $insert_at, 0, ['revenue']); } $auth_on = class_exists('\AKSRT\User\AuthApply') && !empty(\AKSRT\User\AuthApply::settings()['enabled']); if ($auth_on && !in_array('auth', $order, true)) { $order[] = 'auth'; } if (!in_array('settings', $order, true)) { $order[] = 'settings'; } foreach ($order as $key) { if (!isset($definitions[$key])) { continue; } if ($key === 'level' && !$level_on) { continue; } if ($key === 'credits' && !$credits_on && !$balance_on) { continue; } if ($key === 'revenue' && !$revenue_tab_on) { continue; } if ($key === 'vip' && !$vip_on) { continue; } if ($key === 'orders' && !$orders_on) { continue; } if ($key === 'auth' && !$auth_on) { continue; } $panel = $definitions[$key]; if ($key === 'credits' && $balance_on) { $panel['label'] = __('我的资产', 'aksrt'); $panel['icon'] = 'wallet'; } $this->panels[$key] = $panel; } // Notif is a link entry (messages page), not a dashboard panel body. $this->panels['notif'] = $definitions['notif']; // Keep stable order by priority. uasort($this->panels, static function (array $a, array $b): int { return ((int) ($a['priority'] ?? 100)) <=> ((int) ($b['priority'] ?? 100)); }); /** * 允许插件/子主题注册额外面板 * * @param array $panels 面板注册表 */ $this->panels = apply_filters('aksrt_user_center_panels', $this->panels); } /** * 获取面板注册表 */ public function get_panels(): array { return $this->panels; } /** * 获取可切换的本地面板(不含外链入口)。 */ public function get_dashboard_tabs(): array { return array_filter($this->panels, static function (array $panel): bool { return ($panel['type'] ?? 'panel') === 'panel'; }); } /** * 判断当前页面是否为本人中心 */ public static function is_page(): bool { return function_exists('aksrt_is_user_center_page') && aksrt_is_user_center_page(); } }