*/ public function get_modal_view_model(int $user_id, int $prefer_level = 1): array { $catalog = $this->get_catalog($user_id); $vip = $this->get_vip_settings_context(); $member = $this->get_vip_member_context($user_id); $levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : []; $prefer_level = max(1, min(2, $prefer_level)); $level_view = []; foreach ([1, 2] as $level) { if (empty($levels[$level]['enabled'])) { continue; } $equity = (string) ($levels[$level]['equity'] ?? ''); $equity = function_exists('aksrt_format_vip_equity_html') ? (string) aksrt_format_vip_equity_html($equity) : wp_kses_post($equity); $level_view[] = [ 'level' => $level, 'name' => wp_kses_post((string) ($levels[$level]['name_html'] ?? $levels[$level]['name'] ?? ('VIP' . $level))), 'name_text' => (string) ($levels[$level]['name'] ?? ('VIP' . $level)), 'desc' => wp_kses_post((string) ($levels[$level]['desc'] ?? '')), 'equity' => $equity, 'img_icon' => esc_url_raw((string) ($levels[$level]['img_icon'] ?? '')), ]; } $package_ids = []; $has_renew = false; $has_upgrade = false; $groups = is_array($catalog['level_packages'] ?? null) ? $catalog['level_packages'] : []; foreach ($groups as $group) { if (!is_array($group)) { continue; } $action = sanitize_key((string) ($group['action'] ?? 'pay')); $has_renew = $has_renew || $action === 'renew'; $has_upgrade = $has_upgrade || $action === 'upgrade'; foreach ((array) ($group['items'] ?? []) as $item) { if (is_array($item) && (int) ($item['package_id'] ?? 0) > 0) { $package_ids[] = (int) $item['package_id']; } } } $points_exchange = is_array($catalog['points_exchange'] ?? null) ? $catalog['points_exchange'] : []; $methods = is_array($catalog['payment_methods'] ?? null) ? $catalog['payment_methods'] : []; // 支付方式 logo(与充值/积分弹窗的 payment-method-radio 组件同源)。 $methods = array_map(function ($m) { $id = sanitize_key((string) ($m['id'] ?? '')); return [ 'id' => $id, 'name' => (string) ($m['name'] ?? $id), 'logo' => $this->payment_method_logo_url($id), ]; }, $methods); $card_pass = is_array($catalog['card_pass'] ?? null) ? array_merge(['enabled' => false, 'desc' => '', 'only_password' => false], $catalog['card_pass']) : ['enabled' => false, 'desc' => '', 'only_password' => false]; $no_payment_method = $methods === []; $action_reasons = [ 'purchase' => $no_payment_method ? 'no_payment_method' : ($package_ids === [] ? 'no_package' : ''), 'renew' => $no_payment_method ? 'no_payment_method' : ($has_renew ? '' : 'not_available'), 'upgrade' => $no_payment_method ? 'no_payment_method' : ($has_upgrade ? '' : 'not_available'), 'points_exchange' => !empty($points_exchange['enabled']) && !empty($points_exchange['packages']) ? '' : 'not_available', ]; $member_state = (string) ($member['state'] ?? 'unopened'); $member_version = substr(hash('sha256', (string) wp_json_encode([ 'level' => (int) ($member['level'] ?? 0), 'expires' => (int) ($member['expires'] ?? 0), 'state' => $member_state, ])), 0, 16); $catalog_version = substr(hash('sha256', (string) wp_json_encode([ 'packages' => $groups, 'points' => $points_exchange, 'methods' => $methods, 'levels' => $level_view, ])), 0, 16); $user = get_userdata($user_id); $avatar_url = function_exists('get_avatar_url') ? (string) get_avatar_url($user_id, ['size' => 42]) : ''; return [ 'enabled' => !empty($catalog['enabled']), 'prefer_level' => $prefer_level, 'catalog' => [ 'desc' => (string) ($catalog['desc'] ?? ''), 'more' => (string) ($vip['more'] ?? ''), 'currency_name' => (string) ($catalog['currency_name'] ?? ''), 'level_packages' => $groups, 'points_exchange' => $points_exchange, 'wallet_balance' => (string) ($catalog['wallet_balance'] ?? '0.00'), ], 'member' => [ 'id' => $user_id, 'name' => $user ? (string) $user->display_name : '', 'avatar_url' => $avatar_url, 'level' => (int) ($member['level'] ?? 0), 'expires' => (int) ($member['expires'] ?? 0), 'expires_label' => (string) ($member['expires_label'] ?? ''), 'state' => (string) ($member['state'] ?? 'unopened'), 'is_permanent' => !empty($member['is_permanent']), 'logged_in' => $user_id > 0, ], 'levels' => $level_view, 'payment_methods' => $methods, 'card_pass' => $card_pass, 'currency_mark' => (string) (\AKSRT\Settings\Settings::get_payment_gateway_settings()['currency']['mark'] ?? '¥'), 'allowed_actions' => [ 'purchase' => $package_ids !== [] && $methods !== [], 'renew' => $has_renew && $methods !== [], 'upgrade' => $has_upgrade && $methods !== [], 'points_exchange' => !empty($points_exchange['enabled']) && !empty($points_exchange['packages']), ], 'action_reasons' => $action_reasons, 'version' => [ 'schema' => 1, 'member' => $member_version, 'catalog' => $catalog_version, // Stable revision token; do not use request time and bust short cache. 'updated_at' => $member_version . ':' . $catalog_version, 'tags' => ['vip', 'orders'], ], ]; } /** * 积分兑换会员弹窗(参考主题 zibpay_get_vip_points_exchange_modal 1:1 移植)。 * * 服务端静态输出参考主题 refresh_modal 同构外壳: * .modal.flex.jc.fade > .modal-dialog.modal-mini.full-sm * > .modal-content > .modal-body * 与余额充值/购买积分弹窗同构(静止态由 vip-modal.css 显式隐藏,打开态由 * pay-vip.js 的自包含开关以内联 display:flex 驱动)。 * * 入口有两处(均带 data-aksrt-vip-points-open,pay-vip.js 文档级委托开合): * 会员购买弹窗内的「积分兑换会员」按钮,以及 /user/vip/ 档位购买卡下方的 * 入口横条(aksrt_get_vip_points_exchange_entry_html)。.pay-vip 触发点 * 全站可见,因此由 aksrt_render_vip_points_exchange_modal() 在 wp_footer * 全局输出一次。 * * @return string 空串表示积分兑换不可用(不输出弹窗壳)。 */ public function get_points_exchange_modal_html(int $user_id = 0): string { $user_id = max(0, $user_id); if ($user_id <= 0 || !class_exists('\AKSRT\Settings\Settings')) { return ''; } // 先做零成本门控,避免每次页面渲染都去查积分余额。 $vip = $this->get_vip_settings_context(); if (empty($vip['enabled']) || empty($vip['points_exchange']['enabled']) || !\AKSRT\Settings\Settings::credits_enabled()) { return ''; } $catalog = $this->get_catalog($user_id); $points_ex = is_array($catalog['points_exchange'] ?? null) ? $catalog['points_exchange'] : []; $packages = is_array($points_ex['packages'] ?? null) ? $points_ex['packages'] : []; if ($packages === []) { return ''; } $levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : []; $user_points = max(0, (int) ($catalog['user_points'] ?? 0)); $currency = (string) ($catalog['currency_name'] ?? __('积分', 'aksrt')); $points_center_url = function_exists('aksrt_get_user_center_tab_url') ? aksrt_get_user_center_tab_url('credits') : home_url('/'); // 会员卡片(参考主题 data-for/data-value 选中模型;data-value 为本主题的套餐 ID)。 $cards = ''; $min_points = 0; $first_package_id = 0; foreach ($packages as $index => $package) { if (!is_array($package)) { continue; } $package_id = max(0, (int) ($package['package_id'] ?? 0)); if ($package_id <= 0) { continue; } if ($first_package_id === 0) { $first_package_id = $package_id; } $level = max(1, min(2, (int) ($package['level'] ?? 1))); $points = max(0, (int) ($package['points'] ?? 0)); // 参考主题时长行:30天 / 永久;time 为 Permanent 时按永久渲染。 $is_permanent = !empty($package['is_permanent']); $pkg_time = max(0, (int) ($package['time'] ?? 0)); $pkg_unit = ((string) ($package['unit'] ?? 'day')) === 'month' ? 'month' : 'day'; $duration = ($is_permanent || $pkg_time <= 0) ? esc_html__('永久', 'aksrt') : (string) $pkg_time . '' . esc_html($pkg_unit === 'month' ? __('个月', 'aksrt') : __('天', 'aksrt')) . ''; $level_name = (string) ($levels[$level]['name_html'] ?? $levels[$level]['name'] ?? ('VIP' . $level)); $cards .= '
' . '' . $this->points_exchange_card_icon($levels[$level] ?? []) . wp_kses_post($level_name) . '' . '
' . '' . $duration . '' . '' . $points . '' . esc_html($currency) . '' . '
'; if ($min_points === 0 || $points < $min_points) { $min_points = $points; } } if ($cards === '') { return ''; } $desc_text = trim((string) ($vip['points_exchange']['desc'] ?? '')); $desc = $desc_text !== '' // 管理员编辑的富文本说明:保存端已 wp_kses_post,这里同级别输出 // (参考主题 zibpay_get_vip_points_exchange_modal 原样拼接)。 ? '
' . wp_kses_post($desc_text) . '
' : ''; // 最低价套餐仍超出用户积分 → 参考主题的 badg c-red btn-block 提示。 // (参考主题源码写作 `!$min_val > $user_points`,因运算符优先级恒假, // 此处按其原意取真实比较,保证提示分支可达。) if ($min_points > $user_points) { $form = '
' . esc_html__('积分不足,暂时无法兑换', 'aksrt') . '
'; } else { $form = '
' . '' . '' . '' . '
'; } $header = '
' . '' . '' . '' . esc_html__('积分兑换会员', 'aksrt') . '
'; $user_box = '
' . '' . '' . ' ' . esc_html__('我的积分', 'aksrt') . '' . '' . $user_points . '' . '
'; $lists = '
' . esc_html__('请选择需兑换的会员', 'aksrt') . '
' . '
' . $cards . '
'; return ''; } /** * 积分兑换卡片等级图标(参考主题 zibpay_get_vip_card_icon)。 * * @param array $level_settings 等级设置(含 name / img_icon)。 */ private function points_exchange_card_icon(array $level_settings): string { $name_text = (string) ($level_settings['name'] ?? ''); $icon_url = (string) ($level_settings['img_icon'] ?? ''); if ($icon_url === '') { return ''; } return ''; } /** * 支付方式 logo 映射(assets/img/pay-*-logo.svg,与充值/积分弹窗一致)。 */ private function payment_method_logo_url(string $id): string { $logos = [ 'balance' => 'pay-balance-logo.svg', 'alipay' => 'pay-alipay-logo.svg', 'epay_alipay' => 'pay-alipay-logo.svg', 'wechatpay' => 'pay-wechat-logo.svg', 'epay_wxpay' => 'pay-wechat-logo.svg', 'paypal' => 'pay-paypal-logo.svg', 'stripe' => 'pay-stripe-logo.svg', 'card_pass' => 'pay-card-pass-logo.svg', ]; $file = $logos[$id] ?? ''; return $file === '' ? '' : esc_url_raw(get_template_directory_uri() . '/assets/img/' . $file); } /** * Mini hover card for header (legacy aksrtpay_get_vip_card). */ public function render_vip_card(int $level, int $user_id = 0): string { $level = max(1, min(2, $level)); $vip = $this->get_vip_settings_context(); $levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : []; if (empty($levels[$level]['enabled'])) { return ''; } $name = wp_kses_post((string) ($levels[$level]['name_html'] ?? $levels[$level]['name'] ?? ('VIP' . $level))); $name_text = (string) ($levels[$level]['name'] ?? ('VIP' . $level)); $equity = (string) ($levels[$level]['equity'] ?? ''); $cls = $user_id > 0 ? ' pay-vip' : ' signin-loader'; $equity_html = $equity !== '' && function_exists('aksrt_format_vip_equity_html') ? aksrt_format_vip_equity_html($equity, 'mb10 relative') : ($equity !== '' ? '
' . wp_kses_post($equity) . '
' : ''); $icon_url = esc_url((string) ($levels[$level]['img_icon'] ?? '')); $icon = $icon_url !== '' ? '' : ''; $html = '
'; $html .= ''; $html .= '
' . $icon . '
'; $html .= '
'; $html .= '
' . $icon . '' . wp_kses_post($name) . '
'; $html .= $equity_html; $html .= '' . wp_kses_post(sprintf(__('开通%s', 'aksrt'), $name)) . ''; $html .= '
'; return $html; } }