ensure_cap(); $this->page_url = admin_url('admin.php?page=' . self::PAGE_SLUG); if (!class_exists('\AKSRT\Payment\ChargeCard')) { echo '

' . esc_html__('卡密管理', 'aksrt') . '

'; echo '

' . esc_html__('卡密模块未初始化。', 'aksrt') . '

'; return; } $tab = isset($_GET['tab']) ? sanitize_key((string) wp_unslash($_GET['tab'])) : ''; $page_title = __('卡密管理', 'aksrt'); $head_but = '' . esc_html__('添加卡密', 'aksrt') . ''; // 页面级动作(参考主题在渲染前处理 add / delete)。 $action = isset($_REQUEST['action']) ? sanitize_key((string) wp_unslash($_REQUEST['action'])) : ''; if ($action === 'add') { $this->handle_add(); } elseif ($action === 'delete') { $this->handle_delete(); } switch ($tab) { case 'add': $page_title = __('添加卡密', 'aksrt'); $head_but = '' . esc_html__('返回列表', 'aksrt') . ''; break; case 'export': $page_title = __('导出卡密', 'aksrt'); $head_but = '' . esc_html__('返回列表', 'aksrt') . ''; break; } echo '
'; $this->print_assets(); echo '

' . esc_html($page_title) . '

'; echo $head_but; // phpcs:ignore WordPress.Security.EscapeOutput -- 内部构造,已转义。 if ($tab === 'add') { $this->render_add_form(); } elseif ($tab === 'export') { $this->render_export_form(); } else { $this->render_list(); } echo '
'; } /* ------------------------------------------------------------------ * 列表 * ------------------------------------------------------------------ */ private function render_list(): void { $svc = \AKSRT\Payment\ChargeCard::get_instance(); $s = isset($_REQUEST['s']) ? sanitize_text_field((string) wp_unslash($_REQUEST['s'])) : ''; $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : ''; $type = isset($_GET['type']) ? sanitize_key((string) wp_unslash($_GET['type'])) : ''; $other = isset($_GET['other']) ? sanitize_text_field((string) wp_unslash($_GET['other'])) : ''; $orderby = isset($_GET['orderby']) ? sanitize_key((string) wp_unslash($_GET['orderby'])) : 'modified_time'; $order = isset($_GET['desc']) ? (strtoupper((string) wp_unslash($_GET['desc'])) === 'ASC' ? 'ASC' : 'DESC') : 'DESC'; $paged = max(1, (int) ($_GET['paged'] ?? 1)); $per_page = max(1, min(100, (int) ($_REQUEST['ice_perpage'] ?? 30))); // 参考主题:status=0 表示未使用;本主题规范状态为 active。 if ($status === '0' || $status === 'active') { $status = 'active'; } elseif (!in_array($status, ['used', 'expired', 'disabled'], true)) { $status = ''; } if (!in_array($type, ['balance', 'vip', 'credits', 'custom'], true)) { $type = ''; } // 标题右侧按钮与过滤链接。 $head_but = ''; if ($s !== '') { $head_but .= '
' . sprintf(esc_html__('搜索 “%s” 的内容', 'aksrt'), esc_html($s)) . '
'; } else { $head_but .= '' . esc_html__('添加卡密', 'aksrt') . ''; $head_but .= '' . esc_html__('导出卡密', 'aksrt') . ''; } $sub_but = [ ['name' => __('全部', 'aksrt'), 'href' => $this->page_url], ]; if ($s === '') { $sub_but[] = ['name' => __('未使用', 'aksrt'), 'href' => add_query_arg('status', '0', $this->page_url)]; $sub_but[] = ['name' => __('已使用', 'aksrt'), 'href' => add_query_arg('status', 'used', $this->page_url)]; foreach ($this->type_labels() as $_k => $_v) { $sub_but[] = ['name' => $_v, 'href' => add_query_arg('type', $_k, $this->page_url)]; } } $but_html = ''; foreach ($sub_but as $but) { $but_html .= '
  • ' . esc_html($but['name']) . '
  • | '; } $but_html = substr($but_html, 0, -2); $search = '
    '; echo $head_but; // phpcs:ignore WordPress.Security.EscapeOutput -- 已转义。 echo '
    ' . $search . '
    '; $result = $svc->get_list([ 'status' => $status, 'type' => $type, 'other' => $other, 'search' => $s, 'orderby' => $orderby, 'order' => $order, 'limit' => $per_page, 'offset' => ($paged - 1) * $per_page, ]); $items = (array) ($result['items'] ?? []); $count_all = (int) ($result['total'] ?? 0); if (!$items) { echo '
    '; echo ''; echo ''; echo '
    ' . esc_html__('未找到对应内容,或暂无内容', 'aksrt') . '
    '; return; } // 表头(与参考主题同列、同排序链接)。 $theads = [ ['width' => '6%', 'orderby' => 'card', 'name' => __('卡号', 'aksrt')], ['width' => '10%', 'orderby' => 'password', 'name' => __('密码', 'aksrt')], ['width' => '5%', 'orderby' => 'type', 'name' => __('类型', 'aksrt')], ['width' => '5%', 'orderby' => 'create_time', 'name' => __('创建时间', 'aksrt')], ['width' => '5%', 'orderby' => 'modified_time', 'name' => __('更新时间', 'aksrt')], ['width' => '5%', 'orderby' => 'status', 'name' => __('状态', 'aksrt')], ['width' => '8%', 'orderby' => 'other', 'name' => __('标识', 'aksrt')], ]; $thead_th = ''; foreach ($theads as $thead) { $orderby_links = ''; if ($thead['orderby']) { $orderby_url = add_query_arg('orderby', $thead['orderby']); $orderby_links .= ''; $orderby_links .= ''; $orderby_links = '' . $orderby_links . ''; } $thead_th .= '' . esc_html($thead['name']) . $orderby_links . ''; } $tbody = ''; foreach ($items as $msg) { $meta = []; if (!empty($msg['meta'])) { $decoded = json_decode((string) $msg['meta'], true); $meta = is_array($decoded) ? $decoded : []; } $status_html = ''; if ($msg['type'] === 'custom') { $type_html = '' . esc_html__('自定义卡密', 'aksrt') . ''; } else { $status_html = '' . esc_html__('未使用', 'aksrt') . ''; if ($msg['status'] === 'used') { $status_html = '' . esc_html__('已使用', 'aksrt') . ''; if (!empty($msg['order_number'])) { $order_link_url = admin_url('admin.php?page=aksrt_orders&s=' . urlencode((string) $msg['order_number'])); $status_html .= ' [' . esc_html__('查看', 'aksrt') . ']'; } } elseif ($msg['status'] === 'expired') { $status_html = '' . esc_html__('已过期', 'aksrt') . ''; } elseif ($msg['status'] === 'disabled') { $status_html = '' . esc_html__('已禁用', 'aksrt') . ''; } } $other_a = ''; if (!empty($msg['note'])) { $other_a = '' . esc_html($msg['note']) . ''; } if ($msg['type'] === 'vip') { $type_html = '' . esc_html__('会员兑换', 'aksrt') . ''; $level = (int) ($meta['level'] ?? 1); $type_html .= '
    ' . esc_html($this->vip_level_name($level)) . ':' . esc_html($this->vip_time_text($meta)); } elseif ($msg['type'] === 'credits') { $type_html = '' . esc_html__('积分兑换', 'aksrt') . ''; $type_html .= '
    ' . esc_html__('积分:', 'aksrt') . esc_html((string) (int) ($meta['points'] ?? 0)); } elseif ($msg['type'] === 'balance') { $type_html = '' . esc_html__('余额充值', 'aksrt') . ''; $type_html .= '
    ' . esc_html__('金额:', 'aksrt') . esc_html($this->balance_amount_text($meta)); } else { $type_html = '' . esc_html__('自定义卡密', 'aksrt') . ''; } $card_name = (string) ($meta['custom_name']['card_name'] ?? ''); $password_name = (string) ($meta['custom_name']['password_name'] ?? ''); $tbody .= ''; $tbody .= ' '; $tbody .= "" . esc_html($card_name) . ' ' . esc_html((string) $msg['card']) . ''; $tbody .= "" . esc_html($password_name) . ' ' . esc_html((string) $msg['password']) . ''; $tbody .= "$type_html"; $tbody .= '' . esc_html((string) $msg['created_at']) . ''; $tbody .= '' . esc_html((string) ($msg['used_at'] ?: $msg['created_at'])) . ''; $tbody .= "$status_html"; $tbody .= "$other_a"; $tbody .= ''; } echo '
    '; echo '
    '; wp_nonce_field(self::NONCE); echo '
    '; echo '
    '; echo ''; echo '' . $thead_th . ''; // phpcs:ignore WordPress.Security.EscapeOutput -- 已转义。 echo '' . $tbody . ''; // phpcs:ignore WordPress.Security.EscapeOutput -- 已转义。 echo '
    '; echo '
    '; echo '
    '; echo '
    '; $this->render_pagenavi($count_all, $per_page); } /* ------------------------------------------------------------------ * 添加卡密 * ------------------------------------------------------------------ */ private function render_add_form(): void { $type_labels = $this->type_labels(); ?>

    field_row( __('卡密类型', 'aksrt'), $this->button_set('type', $type_labels, 'balance'), [] ); $this->field_row( __('添加方式', 'aksrt'), $this->button_set('add_type', [ 'auto' => __('系统自动生成', 'aksrt'), 'import' => __('导入卡密', 'aksrt'), ], 'auto'), [] ); // 自动生成 $this->field_row( __('生成数量', 'aksrt'), $this->spinner('auto_num', 20, 1, 1000, 10, __('张', 'aksrt')), [['key' => 'add_type', 'op' => '==', 'val' => 'auto']], __('需要生成多少张卡密(单次生成数量太多可能会对服务器性能造成影响)', 'aksrt') ); $this->field_row( __('标识', 'aksrt'), '', [['key' => 'add_type', 'op' => '==', 'val' => 'auto']], __('对生成的卡密做标记标识,方便后期查找管理', 'aksrt') . '
    ' . sprintf(esc_html__('注意:标识中不能有%1$s字符,系统已占用', 'aksrt'), 'shipped') ); $this->field_row( __('面额', 'aksrt'), '', [ ['key' => 'add_type', 'op' => '==', 'val' => 'auto'], ['key' => 'type', 'op' => 'any', 'val' => 'balance,credits'], ], __('单张卡密的面额', 'aksrt') ); $this->field_row( __('兑换会员', 'aksrt'), __('会员等级', 'aksrt') . ' ' . $this->button_set('vip_exchange[level]', [ '1' => $this->vip_level_name(1), '2' => $this->vip_level_name(2), ], '1'), [ ['key' => 'add_type', 'op' => '==', 'val' => 'auto'], ['key' => 'type', 'op' => '==', 'val' => 'vip'], ] ); $this->field_row( ' ', __('兑换会员时长', 'aksrt') . '
    ', [ ['key' => 'add_type', 'op' => '==', 'val' => 'auto'], ['key' => 'type', 'op' => '==', 'val' => 'vip'], ], __('填', 'aksrt') . 'Permanent' . __('为永久', 'aksrt') ); $this->field_row( ' ', __('时长单位', 'aksrt') . ' ' . $this->button_set('vip_exchange[unit]', [ 'day' => __('天', 'aksrt'), 'month' => __('个月', 'aksrt'), ], 'month'), [ ['key' => 'add_type', 'op' => '==', 'val' => 'auto'], ['key' => 'type', 'op' => '==', 'val' => 'vip'], ['key' => 'vip_exchange[time]', 'op' => '!=', 'val' => 'Permanent'], ] ); $this->field_row( __('自定义显示名称', 'aksrt'), __('卡号显示名称', 'aksrt') . '

    ' . __('卡密显示名称', 'aksrt') . '
    ', [ ['key' => 'add_type', 'op' => '==', 'val' => 'auto'], ['key' => 'type', 'op' => '==', 'val' => 'custom'], ] ); $this->field_row( __('高级选项', 'aksrt'), '', [['key' => 'add_type', 'op' => '==', 'val' => 'auto']] ); $this->field_row( __('自定义卡号位数', 'aksrt'), $this->spinner('auto_rand_number_limit', 20, 1, 50, 5, __('位数', 'aksrt')), [ ['key' => 'add_type', 'op' => '==', 'val' => 'auto'], ['key' => 'auto_top_s', 'op' => 'checked'], ] ); $this->field_row( __('自定义密码位数', 'aksrt'), $this->spinner('auto_rand_password_limit', 35, 1, 50, 5, __('位数', 'aksrt')), [ ['key' => 'add_type', 'op' => '==', 'val' => 'auto'], ['key' => 'auto_top_s', 'op' => 'checked'], ], __('自定义自动生成的长度(不能太短,太短可能会出现重复)如果启用了单密码模式,可以将卡号位数设置为0', 'aksrt') ); // 导入卡密(与参考主题一致的提示文案与示例)。 $this->submessage_row( [ ['key' => 'add_type', 'op' => '!=', 'val' => 'auto'], ['key' => 'type', 'op' => 'any', 'val' => 'balance,credits'], ], '

    ' . esc_html__('导入卡密', 'aksrt') . '

  • ' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '' . esc_html__('卡号 密码 面额 标识', 'aksrt') . '
  • ' . esc_html__('卡号、密码、面额、标识默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '
  • ' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '
  • ' . esc_html__('如果使用的是单密码模式,卡号部分随便填什么都行,但不能留空', 'aksrt') . '
  • ' . esc_html__('数据示例', 'aksrt') . '

    4849977843617 3w20V4Qe7dRhAIoPYum6KSA 50 ' . sprintf(esc_html__('%1$s余额', 'aksrt'), esc_html('50' . $this->currency_unit())) . '
    6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq 100 ' . sprintf(esc_html__('%1$s余额', 'aksrt'), esc_html('100' . $this->currency_unit())) . '
    ' ); $this->submessage_row( [ ['key' => 'add_type', 'op' => '!=', 'val' => 'auto'], ['key' => 'type', 'op' => '==', 'val' => 'vip'], ], '

    ' . esc_html__('导入会员兑换卡密', 'aksrt') . '

  • ' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '' . esc_html__('卡号 密码 会员参数 标识', 'aksrt') . '
  • ' . esc_html__('卡号、密码、面额、标识默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '
  • ' . esc_html__('会员参数格式为:', 'aksrt') . 'level=1&time=5&unit=day
  • ' . esc_html__('参数说明:level为等级,只能为1或2。time为时长,填Permanent或数字。unit为时长单位,只能填day(天)或者month(月)', 'aksrt') . '
  • ' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '
  • ' . esc_html__('如果使用的是单密码模式,卡号部分随便填什么都行,但不能留空', 'aksrt') . '
  • ' . esc_html__('数据示例', 'aksrt') . '

    4849977843617 3w20V4Qe7dRhAIoPYum6KSA level=1&time=5&unit=day ' . esc_html__('5天一级会员', 'aksrt') . '
    6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq level=2&time=Permanent ' . esc_html__('永久2级会员', 'aksrt') . '
    ' ); $this->field_row( __('单密码模式', 'aksrt'), '', [ ['key' => 'add_type', 'op' => '==', 'val' => 'import'], ['key' => 'type', 'op' => '==', 'val' => 'custom'], ] ); $this->submessage_row( [ ['key' => 'add_type', 'op' => '==', 'val' => 'import'], ['key' => 'type', 'op' => '==', 'val' => 'custom'], ['key' => 'only_pass_s', 'op' => 'unchecked'], ], '

    ' . esc_html__('自定义卡密导入', 'aksrt') . '

  • ' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '' . esc_html__('卡号 密码 标识 自定义参数', 'aksrt') . '
  • ' . esc_html__('默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '
  • ' . esc_html__('自定义参数格式为:', 'aksrt') . 'card_name=授权号&password_name=授权密码,' . esc_html__('card_name为卡号的显示名称,password_name为卡密的显示名称,可以留空', 'aksrt') . '
  • ' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '
  • ' . esc_html__('数据示例', 'aksrt') . '

    4849977843617 3w20V4Qe7dRhAIoPYum6KSA ' . esc_html__('平台卡密01', 'aksrt') . ' card_name=' . esc_html__('授权号', 'aksrt') . '&password_name=' . esc_html__('授权码', 'aksrt') . '
    6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq ' . esc_html__('平台卡密01', 'aksrt') . ' card_name=' . esc_html__('授权号', 'aksrt') . '&password_name=' . esc_html__('授权码', 'aksrt') . '
    111111@qq.com 11assssddsds ' . esc_html__('某某账号', 'aksrt') . ' card_name=' . esc_html__('账号', 'aksrt') . '&password_name=' . esc_html__('密码', 'aksrt') . '
    ' ); $this->submessage_row( [ ['key' => 'add_type', 'op' => '==', 'val' => 'import'], ['key' => 'type', 'op' => '==', 'val' => 'custom'], ['key' => 'only_pass_s', 'op' => 'checked'], ], '

    ' . esc_html__('自定义卡密导入(单密码模式)', 'aksrt') . '

  • ' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '' . esc_html__('密码 标识 自定义参数', 'aksrt') . '
  • ' . esc_html__('默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '
  • ' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '
  • ' . esc_html__('自定义参数格式为:', 'aksrt') . 'password_name=授权密码,' . esc_html__('password_name为卡密的显示名称,可以留空', 'aksrt') . '
  • ' . esc_html__('数据示例', 'aksrt') . '

    3w20V4Qe7dRhAIoPYum6KSA ' . esc_html__('平台卡密01', 'aksrt') . ' password_name=' . esc_html__('授权码', 'aksrt') . '
    SkdJZ5vWcpqPxrbs3pqPcpq ' . esc_html__('平台卡密01', 'aksrt') . ' password_name=' . esc_html__('授权码', 'aksrt') . '
    ' ); $this->field_row( __('卡密数据', 'aksrt'), '', [['key' => 'add_type', 'op' => '==', 'val' => 'import']] ); $this->field_row( __('自定义分隔符号', 'aksrt'), '', [['key' => 'add_type', 'op' => '==', 'val' => 'import']], __('卡号和密码之间分割符号(默认为空格分割)', 'aksrt') ); ?>
    field_row( __('卡密类型', 'aksrt'), $this->button_set('type', $this->type_labels(), 'balance'), [] ); $this->field_row( __('选择状态', 'aksrt'), $this->button_set('status', [ 'all' => __('全部', 'aksrt'), '0' => __('未使用', 'aksrt'), 'used' => __('已使用', 'aksrt'), ], 'all'), [['key' => 'type', 'op' => '!=', 'val' => 'custom']] ); $this->field_row( __('导出格式', 'aksrt'), $this->button_set('export_format', [ 'text' => __('文本文档', 'aksrt'), 'xls' => __('Excel表格', 'aksrt'), ], 'xls'), [] ); $this->field_row( ' ', __('分隔符号', 'aksrt') . '
    ', [['key' => 'export_format', 'op' => '==', 'val' => 'text']], __('卡号和密码之间分割符号(默认为空格分割)', 'aksrt') ); ?>
    get_export_rows($type, $status); if (!$rows) { wp_die(esc_html__('暂无可导出的内容', 'aksrt')); } $filename = $type . '_' . gmdate('d_m_Y'); // 组装行数据(与参考主题字段一致)。 $title = []; $data = []; foreach ($rows as $row) { $meta = []; if (!empty($row['meta'])) { $decoded = json_decode((string) $row['meta'], true); $meta = is_array($decoded) ? $decoded : []; } $status_text = $row['status'] === 'used' ? __('已使用', 'aksrt') : ($row['status'] === 'expired' ? __('已过期', 'aksrt') : ($row['status'] === 'disabled' ? __('已禁用', 'aksrt') : __('未使用', 'aksrt'))); switch ($type) { case 'credits': // 积分兑换 $title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('面额', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')]; $data[] = [(string) $row['card'], (string) $row['password'], (string) (int) ($meta['points'] ?? 0), (string) $row['note'], $status_text]; break; case 'vip': // 会员兑换 $title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('兑换会员', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')]; $data[] = [(string) $row['card'], (string) $row['password'], $this->vip_meta_query($meta), (string) $row['note'], $status_text]; break; case 'custom': // 自定义 $title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')]; $data[] = [(string) $row['card'], (string) $row['password'], (string) $row['note'], '']; break; default: // 余额充值 $title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('面额', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')]; $data[] = [(string) $row['card'], (string) $row['password'], $this->balance_amount_text($meta), (string) $row['note'], $status_text]; } } if ($export_format === 'text') { $division = isset($_REQUEST['text_division']) ? (string) wp_unslash($_REQUEST['text_division']) : ' '; header('Content-type:application/octet-stream'); header('Accept-Ranges:bytes'); header('Content-Disposition:attachment;filename=' . $filename . '.txt'); header('Pragma: no-cache'); header('Pragma: public'); header('Expires: 0'); $_data = []; foreach ($data as $val) { $_data[] = implode($division, $val); } echo implode("\n", $_data); // phpcs:ignore WordPress.Security.EscapeOutput } else { $this->export_excel($data, $title, $filename); } exit; } /** * 导出 xls(参考主题 zib_export_excel 同款行为,GB2312 编码保证 Excel 兼容)。 * * @param array> $data * @param array $title */ private function export_excel(array $data, array $title, string $filename): void { header('Content-type:application/octet-stream'); header('Accept-Ranges:bytes'); header('Content-type:application/vnd.ms-excel'); header('Content-Disposition:attachment;filename=' . $filename . '.xls'); header('Pragma: no-cache'); header('Expires: 0'); $convert = static function (string $value): string { return function_exists('mb_convert_encoding') ? (string) @mb_convert_encoding($value, 'GB2312', 'UTF-8') : $value; }; if (!empty($title)) { $title = array_map($convert, $title); echo implode("\t", $title) . "\n"; } if (!empty($data)) { $_data = []; foreach ($data as $val) { $val = array_map($convert, array_map('strval', $val)); $_data[] = implode("\t", $val); } echo implode("\n", $_data); } } /* ------------------------------------------------------------------ * 动作处理 * ------------------------------------------------------------------ */ private function handle_add(): void { check_admin_referer(self::NONCE); $add_type = isset($_REQUEST['add_type']) ? sanitize_key((string) wp_unslash($_REQUEST['add_type'])) : 'auto'; $pass_type = isset($_REQUEST['type']) ? sanitize_key((string) wp_unslash($_REQUEST['type'])) : ''; if (!in_array($pass_type, ['balance', 'vip', 'credits', 'custom'], true)) { $this->page_notice(__('错误!', 'aksrt'), __('参数传入错误', 'aksrt'), 'error'); return; } if ($add_type === 'import') { $this->handle_import($pass_type); return; } // 自动生成。 $auto_num = isset($_REQUEST['auto_num']) ? (int) $_REQUEST['auto_num'] : 0; if (!$auto_num) { $this->page_notice(__('错误!', 'aksrt'), __('请输入需要生成的数量', 'aksrt'), 'error'); return; } $pass_meta = []; switch ($pass_type) { case 'balance': case 'credits': $auto_price = isset($_REQUEST['auto_price']) ? floatval(round((float) $_REQUEST['auto_price'], 2)) : 0; if (!$auto_price) { $this->page_notice(__('错误!', 'aksrt'), __('请输入卡密的面额', 'aksrt'), 'error'); return; } $pass_meta = $pass_type === 'credits' ? ['points' => (int) $auto_price] : ['amount' => number_format($auto_price, 2, '.', '')]; break; case 'vip': $vip = isset($_REQUEST['vip_exchange']) && is_array($_REQUEST['vip_exchange']) ? wp_unslash($_REQUEST['vip_exchange']) : []; if (empty($vip['time'])) { $this->page_notice(__('错误!', 'aksrt'), __('请填写兑换会员参数', 'aksrt'), 'error'); return; } $pass_meta = $this->vip_meta($vip); break; } // 自定义显示名称(参考主题:所有类型均可附加)。 $custom_name = isset($_REQUEST['custom_name']) && is_array($_REQUEST['custom_name']) ? wp_unslash($_REQUEST['custom_name']) : []; if (!empty($custom_name['card_name'])) { $pass_meta['custom_name']['card_name'] = trim(sanitize_text_field((string) $custom_name['card_name'])); } if (!empty($custom_name['password_name'])) { $pass_meta['custom_name']['password_name'] = trim(sanitize_text_field((string) $custom_name['password_name'])); } $rand_number = 20; $rand_password = 35; if (!empty($_REQUEST['auto_top_s'])) { $rand_number = isset($_REQUEST['auto_rand_number_limit']) ? (int) $_REQUEST['auto_rand_number_limit'] : 20; $rand_password = isset($_REQUEST['auto_rand_password_limit']) ? (int) $_REQUEST['auto_rand_password_limit'] : 35; } $remarks = isset($_REQUEST['auto_remarks']) ? sanitize_text_field((string) wp_unslash($_REQUEST['auto_remarks'])) : ''; $created = \AKSRT\Payment\ChargeCard::get_instance()->generate_cards($pass_type, $auto_num, $pass_meta, $rand_number, $rand_password, $remarks); $this->page_notice(__('完成!', 'aksrt'), sprintf(__('已自动生成%s个卡密', 'aksrt'), (string) $created)); } private function handle_import(string $pass_type): void { $import_data = isset($_REQUEST['import_data']) ? (string) wp_unslash($_REQUEST['import_data']) : ''; if (!$import_data) { $this->page_notice(__('错误!', 'aksrt'), __('请粘贴您需要导入的数据', 'aksrt'), 'error'); return; } $import_data_array = explode("\r\n", $import_data); $import_division = isset($_REQUEST['import_division']) ? (string) wp_unslash($_REQUEST['import_division']) : ' '; if ($import_division === '') { $import_division = ' '; } $success_i = 0; $error_i = 0; $svc = \AKSRT\Payment\ChargeCard::get_instance(); foreach ($import_data_array as $line) { $v = explode($import_division, $line); $pass_meta = []; $_card = isset($v[0]) ? trim((string) $v[0]) : ''; $_pass = isset($v[1]) ? trim((string) $v[1]) : ''; $_other = isset($v[3]) ? trim((string) $v[3]) : ''; switch ($pass_type) { case 'balance': case 'credits': $val = isset($v[2]) && $v[2] !== '' ? (float) $v[2] : 0; if ($pass_type === 'credits') { $pass_meta = $val ? ['points' => (int) $val] : []; } else { $pass_meta = $val ? ['amount' => number_format($val, 2, '.', '')] : []; } if (!$pass_meta) { $error_i++; continue 2; } break; case 'vip': $vip_pass_data = isset($v[2]) && $v[2] !== '' ? wp_parse_args(trim((string) $v[2])) : []; if (!empty($vip_pass_data['level']) && !empty($vip_pass_data['time'])) { $pass_meta = $this->vip_meta(is_array($vip_pass_data) ? $vip_pass_data : []); } if (!$pass_meta) { $error_i++; continue 2; } break; case 'custom': $pass_meta = []; $only_pass_s = !empty($_REQUEST['only_pass_s']); if ($only_pass_s) { // 单密码模式:密码 标识 自定义参数 $_card = ''; $_pass = isset($v[0]) ? trim((string) $v[0]) : ''; $_other = isset($v[1]) ? trim((string) $v[1]) : ''; $custom_name = isset($v[2]) && $v[2] !== '' ? wp_parse_args(trim((string) $v[2])) : []; } else { $_other = isset($v[2]) ? trim((string) $v[2]) : ''; $custom_name = isset($v[3]) && $v[3] !== '' ? wp_parse_args(trim((string) $v[3])) : []; } if (!empty($custom_name['card_name'])) { $pass_meta['custom_name']['card_name'] = trim(sanitize_text_field((string) $custom_name['card_name'])); } if (!empty($custom_name['password_name'])) { $pass_meta['custom_name']['password_name'] = trim(sanitize_text_field((string) $custom_name['password_name'])); } break; } if ($_pass !== '') { $id = $svc->create([ 'card' => $_card, 'password' => $_pass, 'type' => $pass_type, 'meta' => $pass_meta, 'note' => $_other, ]); if ($id > 0) { $success_i++; } else { $error_i++; } } else { $error_i++; } } if ($success_i) { $this->page_notice( __('导入完成', 'aksrt'), sprintf(__('成功导入%s个卡密%s', 'aksrt'), (string) $success_i, $error_i ? sprintf(__(',%s个导入失败', 'aksrt'), (string) $error_i) : '') ); } else { $this->page_notice(__('导入失败', 'aksrt'), sprintf(__('%s个导入失败,数据格式错误', 'aksrt'), (string) $error_i), 'error'); } } private function handle_delete(): void { check_admin_referer(self::NONCE); $delete_ids = isset($_REQUEST['action_id']) ? array_map('absint', (array) wp_unslash($_REQUEST['action_id'])) : []; $delete_ids = array_filter($delete_ids); if (!$delete_ids) { $this->page_notice(__('错误!', 'aksrt'), __('未选择需要删除的内容', 'aksrt'), 'error'); return; } $delete_i = \AKSRT\Payment\ChargeCard::get_instance()->delete_by_ids($delete_ids); $this->page_notice(__('删除完成', 'aksrt'), sprintf(__('已删除%s个卡密', 'aksrt'), (string) $delete_i)); } /* ------------------------------------------------------------------ * 渲染辅助 * ------------------------------------------------------------------ */ /** * @param array $deps */ private function field_row(string $title, string $html, array $deps = [], string $desc = ''): void { $dep_attr = $deps ? ' data-dep="' . esc_attr((string) wp_json_encode($deps)) . '"' : ''; $hidden = ($deps && !$this->deps_visible($deps)) ? ' style="display:none;"' : ''; echo '' . esc_html($title) . '' . $html; // phpcs:ignore WordPress.Security.EscapeOutput -- 内部构造,已转义。 if ($desc !== '') { echo '

    ' . wp_kses_post($desc) . '

    '; } echo ''; } /** * @param array $deps */ private function submessage_row(array $deps, string $content): void { $hidden = $this->deps_visible($deps) ? '' : ' style="display:none;"'; echo '
    ' . $content . '
    '; // phpcs:ignore WordPress.Security.EscapeOutput -- 文案已转义。 } /** * 服务端按表单默认值预判依赖可见性(求值规则与行内 JS 一致)。 * 参考主题 CSF 的 dependency 在 DOM ready 后才隐藏不满足项,首帧会闪现全部字段; * 此处在渲染期直接置 display:none,修正「导入提醒初始全部显示」的问题。 * * @param array $deps */ private function deps_visible(array $deps): bool { foreach ($deps as $dep) { $v = (string) $this->default_form_value((string) ($dep['key'] ?? '')); $val = (string) ($dep['val'] ?? ''); switch ((string) ($dep['op'] ?? '')) { case '==': if ($v !== $val) { return false; } break; case '!=': if ($v === $val) { return false; } break; case 'any': if (!in_array($v, explode(',', $val), true)) { return false; } break; case 'checked': if ($v === '') { return false; } break; case 'unchecked': if ($v !== '') { return false; } break; } } return true; } /** * 表单字段的初始默认值(须与 field_row/button_set/spinner 渲染的默认值保持同步)。 * 未列出的键按空字符串处理,与行内 JS formVal 找不到元素时返回 '' 的行为一致。 */ private function default_form_value(string $key): string { switch ($key) { case 'add_type': return 'auto'; case 'type': return 'balance'; case 'auto_top_s': case 'only_pass_s': return ''; case 'vip_exchange[time]': return ''; case 'vip_exchange[unit]': return 'month'; default: return ''; } } /** * button_set:radio 按钮组(参考主题 CSF button_set 的原生替代)。 * * @param array $options */ private function button_set(string $name, array $options, string $current): string { $html = '
    '; foreach ($options as $value => $label) { $html .= ''; } return $html . '
    '; } private function spinner(string $name, int $value, int $min, int $max, int $step, string $unit): string { return ' ' . esc_html($unit) . ''; } private function page_notice(string $title, string $msg, string $type = 'success'): void { $html = ''; $html .= $title ? '

    ' . esc_html($title) . '

    ' : ''; $html .= $msg ? '

    ' . esc_html($msg) . '

    ' : ''; if ($html) { echo '
    ' . $html . '
    '; // phpcs:ignore WordPress.Security.EscapeOutput } } /** * 分页(参考主题 zibpay_admin_pagenavi 同款结构与禁用逻辑)。 */ private function render_pagenavi(int $total_count, int $number_per_page = 15): void { if ($total_count <= 0 || $number_per_page <= 0) { return; } $current_page = max(1, (int) ($_GET['paged'] ?? 1)); $total_pages = (int) ceil($total_count / $number_per_page); $first_page_url = add_query_arg('paged', 1); $last_page_url = add_query_arg('paged', $total_pages); $prev_page_url = '#'; $next_page_url = '#'; if ($current_page > 1 && $current_page < $total_pages) { $prev_page_url = add_query_arg('paged', $current_page - 1); $next_page_url = add_query_arg('paged', $current_page + 1); } elseif (1 == $current_page) { $first_page_url = '#'; if ($total_pages > 1) { $next_page_url = add_query_arg('paged', $current_page + 1); } else { $next_page_url = '#'; $last_page_url = '#'; } } elseif ($current_page == $total_pages) { $prev_page_url = add_query_arg('paged', $current_page - 1); $next_page_url = '#'; $last_page_url = '#'; } ?>
    « ‹ ' . esc_html((string) $total_pages) . ''); ?> › »
    */ private function type_labels(): array { return [ 'balance' => __('余额充值', 'aksrt'), 'vip' => __('会员兑换', 'aksrt'), 'credits' => __('积分兑换', 'aksrt'), 'custom' => __('自定义卡密', 'aksrt'), ]; } private function vip_level_name(int $level): string { $s = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : []; $name = trim((string) ($s['pay_user_vip_' . max(1, min(2, $level)) . '_name'] ?? '')); if ($name !== '') { return $name; } return $level === 2 ? __('二级会员', 'aksrt') : __('一级会员', 'aksrt'); } /** * VIP 卡展示文本:Permanent 为永久;day 为天;month 为个月。 * * @param array $meta */ private function vip_time_text(array $meta): string { $time = array_key_exists('time', $meta) ? (string) $meta['time'] : null; if ($time !== null && $time !== '') { if ($time === 'Permanent') { return __('永久', 'aksrt'); } $unit = (isset($meta['unit']) && $meta['unit'] === 'month') ? __('个月', 'aksrt') : __('天', 'aksrt'); return (string) ((int) $time) . $unit; } $days = (int) ($meta['days'] ?? 0); return $days > 0 ? $days . __('天', 'aksrt') : __('永久', 'aksrt'); } /** * VIP 卡 meta(内部统一保存 level/days + 参考主题的 time/unit)。 * * @param array $vip * @return array */ private function vip_meta(array $vip): array { $level = max(1, min(2, (int) ($vip['level'] ?? 1))); $time_raw = trim((string) ($vip['time'] ?? '')); $unit = (isset($vip['unit']) && $vip['unit'] === 'month') ? 'month' : 'day'; if ($time_raw === 'Permanent') { $days = 0; } else { $time = (int) $time_raw; $days = $unit === 'month' ? $time * 30 : $time; } return [ 'level' => $level, 'days' => max(0, $days), 'time' => $time_raw, 'unit' => $unit, ]; } /** * VIP 卡导出参数串(与参考主题导出格式一致)。 * * @param array $meta */ private function vip_meta_query(array $meta): string { if (isset($meta['time']) && (string) $meta['time'] !== '') { return build_query([ 'level' => (int) ($meta['level'] ?? 1), 'time' => (string) $meta['time'], 'unit' => (isset($meta['unit']) && $meta['unit'] === 'month') ? 'month' : 'day', ]); } $days = (int) ($meta['days'] ?? 0); return build_query([ 'level' => (int) ($meta['level'] ?? 1), 'time' => $days > 0 ? (string) $days : 'Permanent', 'unit' => 'day', ]); } /** * 余额卡面额展示(参考主题为数值文本,不追加单位)。 * * @param array $meta */ private function balance_amount_text(array $meta): string { $amount = (string) ($meta['amount'] ?? ''); if ($amount === '') { return '0'; } return (string) (float) $amount; } private function currency_unit(): string { $s = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : []; return trim((string) ($s['pay_currency_unit'] ?? '')) !== '' ? trim((string) $s['pay_currency_unit']) : __('元', 'aksrt'); } private function ensure_cap(): void { if (!current_user_can(self::CAPABILITY)) { wp_die(esc_html__('您没有权限访问此页面。', 'aksrt')); } } }