diff --git a/assets/admin/src/schema/sections/payment/vip.js b/assets/admin/src/schema/sections/payment/vip.js index e9029e87..8958a2ca 100644 --- a/assets/admin/src/schema/sections/payment/vip.js +++ b/assets/admin/src/schema/sections/payment/vip.js @@ -84,7 +84,14 @@ export const vipSchema = { fields: [ { key: 'vip_upgrade', label: '允许升级', type: 'switch', default: true }, { key: 'vip_upgrade_desc', label: '升级说明', type: 'textarea', default: '', dependsOn: { key: 'vip_upgrade', value: true } }, - { key: 'vip_upgrade_product', label: '升级产品', type: 'input', default: '', dependsOn: { key: 'vip_upgrade', value: true } }, + { + key: 'vip_upgrade_product', + label: '升级产品', + type: 'textarea', + default: '', + dependsOn: { key: 'vip_upgrade', value: true }, + help: '每行一条。永久VIP1升级:permanent|价格|标题;按剩余天:unit|每日单价|标题;限时跳转永久:jump|价格|标题;兼容旧格式:价格|天数|标题', + }, ], }, ], diff --git a/inc/classes/admin/schema/class-aksrt-admin-schema-payment.php b/inc/classes/admin/schema/class-aksrt-admin-schema-payment.php index a053f18a..5d370d5b 100644 --- a/inc/classes/admin/schema/class-aksrt-admin-schema-payment.php +++ b/inc/classes/admin/schema/class-aksrt-admin-schema-payment.php @@ -143,7 +143,7 @@ class AKSRT_Admin_Schema_Payment { ['key' => 'upgrade', 'title' => '升级设置', 'fields' => [ ['key' => 'vip_upgrade', 'label' => '允许升级', 'type' => 'switch', 'default' => true], ['key' => 'vip_upgrade_desc', 'label' => '升级说明', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true]], - ['key' => 'vip_upgrade_product', 'label' => '升级产品', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true], 'help' => '固定价:价格|天数|标题;按剩余天数:unit|每日单价|标题'], + ['key' => 'vip_upgrade_product', 'label' => '升级产品', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true], 'help' => '每行一条。永久VIP1:permanent|价格|标题;剩余天:unit|每日单价|标题;跳转永久:jump|价格|标题;兼容:价格|天数|标题'], ]], ], ], diff --git a/inc/classes/class-aksrt-vip-service.php b/inc/classes/class-aksrt-vip-service.php index cabeb8d7..c1d40a3c 100644 --- a/inc/classes/class-aksrt-vip-service.php +++ b/inc/classes/class-aksrt-vip-service.php @@ -317,8 +317,10 @@ class AKSRT_Vip_Service { $action = 'upgrade'; } - // Dedicated upgrade product (index 901 fixed, 902 unit remaining days). - if ($index === 901 || $index === 902) { + // Dedicated upgrade product: + // 901 permanent/fixed, 902 unit remaining days, 903 jump permanent. + $upgrade_mode = ''; + if (in_array($index, [901, 902, 903], true)) { $upgrade_items = $this->build_upgrade_items($user_id, $expires, $is_permanent, (string) ($upgrade['product'] ?? '')); $match = null; foreach ($upgrade_items as $ui) { @@ -339,6 +341,7 @@ class AKSRT_Vip_Service { $days = (int) $match['days']; $base_price = (float) ($match['base_price'] ?? $amount); $discount_tag = (string) ($match['discount_tag'] ?? __('升级专享', 'aksrt')); + $upgrade_mode = sanitize_key((string) ($match['upgrade_mode'] ?? '')); $action = 'upgrade'; } else { $packages = $this->parse_packages((string) ($vip['packages_raw'] ?? '')); @@ -377,6 +380,7 @@ class AKSRT_Vip_Service { 'package_title' => (string) ($pkg['title'] ?? ''), 'package_id' => $package_id, 'action' => $action, + 'upgrade_mode' => $upgrade_mode, 'base_price' => $base_price, 'discount_tag' => $discount_tag, ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); @@ -512,6 +516,7 @@ class AKSRT_Vip_Service { $level = (int) ($meta['level'] ?? 0); $days = (int) ($meta['days'] ?? 0); + $upgrade_mode = sanitize_key((string) ($meta['upgrade_mode'] ?? '')); if ($level <= 0) { $decoded = $this->decode_package_id((int) ($paid_order['product_id'] ?? 0)); if ($decoded) { @@ -525,13 +530,21 @@ class AKSRT_Vip_Service { $level = 2; } - $this->activate_vip($user_id, $level, $days); + // unit upgrade: raise level only, keep current expiry (zibll). + if ($upgrade_mode === 'unit') { + $this->activate_vip($user_id, $level, 0, true); + } else { + $this->activate_vip($user_id, $level, $days, false); + } } /** * Apply membership meta. + * + * @param bool $keep_expiry When true (unit upgrade), only raise level and do not change expiry. + * When days<=0 and keep_expiry=false, set permanent (expires=0). */ - public function activate_vip(int $user_id, int $level, int $days = 0): void { + public function activate_vip(int $user_id, int $level, int $days = 0, bool $keep_expiry = false): void { if ($user_id <= 0) { return; } @@ -540,6 +553,11 @@ class AKSRT_Vip_Service { $new_level = max($current_level, $level); update_user_meta($user_id, 'aksrt_vip_level', $new_level); + // Unit upgrade (zibll): keep vip_exp_date, only raise level. + if ($keep_expiry) { + return; + } + if ($days <= 0) { // Permanent. update_user_meta($user_id, 'aksrt_vip_expires', 0); @@ -723,11 +741,19 @@ class AKSRT_Vip_Service { } /** - * Build upgrade package items (fixed and/or remaining-days unit). + * Build upgrade package items (zibll vip_upgrade_product parity). * - * upgrade.product formats: - * - fixed: price|days|title - * - unit: unit|daily_price|title (price = remaining_days * daily_price, days = remaining) + * upgrade.product multi-line formats: + * - permanent VIP1: permanent|price|title[|show_price] + * - fixed (alias): price|days|title + * - timed unit: unit|daily_price|title + * - timed jump permanent: jump|price|title[|show_price] + * + * Rules: + * - permanent user: only permanent/fixed (days=0 preferred) + * - timed user: unit (keep expiry) + optional jump (to permanent) + * + * package index: 901 permanent/fixed, 902 unit, 903 jump * * @return array> */ @@ -738,7 +764,10 @@ class AKSRT_Vip_Service { } $items = []; $lines = preg_split('/\r\n|\r|\n/', $raw) ?: []; - $fixed_i = 0; + $has_permanent = false; + $has_unit = false; + $has_jump = false; + foreach ($lines as $line) { $line = trim((string) $line); if ($line === '') { @@ -747,8 +776,9 @@ class AKSRT_Vip_Service { $parts = array_map('trim', explode('|', $line)); $mode = strtolower((string) ($parts[0] ?? '')); + // unit|daily|title — timed VIP1 only. if ($mode === 'unit') { - if ($is_permanent || $expires <= time()) { + if ($is_permanent || $expires <= time() || $has_unit) { continue; } $daily = isset($parts[1]) ? (float) $parts[1] : 0.0; @@ -779,16 +809,89 @@ class AKSRT_Vip_Service { 'is_upgrade_product' => true, 'upgrade_mode' => 'unit', ]; + $has_unit = true; continue; } - $parsed = $this->parse_packages($line); - if (empty($parsed[0])) { + // jump|price|title[|show_price] — timed VIP1 only → permanent VIP2. + if ($mode === 'jump') { + if ($is_permanent || $has_jump) { + continue; + } + $price = isset($parts[1]) ? (float) $parts[1] : 0.0; + if ($price <= 0) { + continue; + } + $title = sanitize_text_field((string) ($parts[2] ?? '')); + if ($title === '') { + $title = __('升级为永久会员', 'aksrt'); + } + $show = isset($parts[3]) ? (float) $parts[3] : 0.0; + $items[] = [ + 'package_id' => $this->encode_package_id(2, 903), + 'level' => 2, + 'index' => 903, + 'action' => 'upgrade', + 'price' => round($price, 2), + 'base_price' => round($price, 2), + 'show_price' => max(0.0, $show), + 'discount_tag' => __('永久升级', 'aksrt'), + 'days' => 0, + 'title' => $title, + 'price_label' => $this->format_cash_label($price), + 'show_price_label' => $show > 0 ? $this->format_cash_label($show) : '', + 'days_label' => __('永久', 'aksrt'), + 'cta_label' => __('立即升级', 'aksrt'), + 'is_upgrade_product' => true, + 'upgrade_mode' => 'jump', + ]; + $has_jump = true; continue; } - $fixed_i++; - // Only one fixed upgrade product id 901. - if ($fixed_i > 1) { + + // permanent|price|title[|show_price] + if ($mode === 'permanent') { + if ($has_permanent) { + continue; + } + $price = isset($parts[1]) ? (float) $parts[1] : 0.0; + if ($price <= 0) { + continue; + } + $title = sanitize_text_field((string) ($parts[2] ?? '')); + if ($title === '') { + $title = __('永久会员升级', 'aksrt'); + } + $show = isset($parts[3]) ? (float) $parts[3] : 0.0; + $items[] = [ + 'package_id' => $this->encode_package_id(2, 901), + 'level' => 2, + 'index' => 901, + 'action' => 'upgrade', + 'price' => round($price, 2), + 'base_price' => round($price, 2), + 'show_price' => max(0.0, $show), + 'discount_tag' => __('升级专享', 'aksrt'), + 'days' => 0, + 'title' => $title, + 'price_label' => $this->format_cash_label($price), + 'show_price_label' => $show > 0 ? $this->format_cash_label($show) : '', + 'days_label' => __('永久', 'aksrt'), + 'cta_label' => __('立即升级', 'aksrt'), + 'is_upgrade_product' => true, + 'upgrade_mode' => 'permanent', + ]; + $has_permanent = true; + continue; + } + + // fixed: price|days|title + $parsed = $this->parse_packages($line); + if (empty($parsed[0]) || $has_permanent) { + continue; + } + // For permanent VIP1, only accept days=0 fixed lines as permanent upgrade. + if ($is_permanent && (int) $parsed[0]['days'] > 0) { continue; } $pkg = $parsed[0]; @@ -810,9 +913,19 @@ class AKSRT_Vip_Service { : sprintf(__('%d 天', 'aksrt'), (int) $pkg['days']), 'cta_label' => __('立即升级', 'aksrt'), 'is_upgrade_product' => true, - 'upgrade_mode' => 'fixed', + 'upgrade_mode' => ((int) $pkg['days'] <= 0) ? 'permanent' : 'fixed', ]; + $has_permanent = true; } + + // Permanent VIP1 should not see unit/jump. + if ($is_permanent) { + $items = array_values(array_filter($items, static function ($it) { + $m = (string) ($it['upgrade_mode'] ?? ''); + return in_array($m, ['permanent', 'fixed'], true); + })); + } + return $items; }