get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->usermeta} WHERE meta_key = 'aksrt_user_verify_status' AND meta_value = %s", $status )); } public function render_verify_page(): void { $this->ensure_cap(); $filters = $this->get_verify_filters(); $page_num = max(1, (int) ($_GET['paged'] ?? 1)); $per_page = 20; $result = $this->query_verify_requests($filters, $page_num, $per_page); $counts = $this->get_verify_status_counts(); echo '

' . esc_html__('身份认证', 'aksrt') . '

'; $this->flash(); echo '

' . esc_html(sprintf( /* translators: 1 pending, 2 verified, 3 rejected, 4 cancelled */ __('待审核 %1$d · 已通过 %2$d · 已驳回 %3$d · 已取消 %4$d', 'aksrt'), $counts['pending'], $counts['verified'], $counts['rejected'], $counts['cancelled'] )) . '

'; $this->render_verify_filters($filters); $this->render_verify_table($result['items']); $this->pagination($result['pages'], $page_num); echo '
'; } /** @return array{status:string,keyword:string,date_from:string,date_to:string} */ private function get_verify_filters(): array { $status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : 'pending'; if (!in_array($status, ['pending', 'verified', 'rejected', 'cancelled', ''], true)) { $status = 'pending'; } return [ 'status' => $status, 'keyword' => isset($_GET['keyword']) ? sanitize_text_field((string) wp_unslash($_GET['keyword'])) : '', 'date_from' => $this->sanitize_verify_date($_GET['date_from'] ?? ''), 'date_to' => $this->sanitize_verify_date($_GET['date_to'] ?? ''), ]; } private function sanitize_verify_date($value): string { $value = sanitize_text_field((string) wp_unslash($value)); return preg_match('/^\d{4}-\d{2}-\d{2}$/', $value) ? $value : ''; } /** @return array{pending:int,verified:int,rejected:int,cancelled:int,total:int} */ private function get_verify_status_counts(): array { global $wpdb; $counts = ['pending' => 0, 'verified' => 0, 'rejected' => 0, 'cancelled' => 0, 'total' => 0]; $rows = $wpdb->get_results( "SELECT meta_value AS status, COUNT(*) AS total FROM {$wpdb->usermeta} WHERE meta_key = 'aksrt_user_verify_status' GROUP BY meta_value", ARRAY_A ); foreach ((array) $rows as $row) { $status = sanitize_key((string) ($row['status'] ?? '')); $total = (int) ($row['total'] ?? 0); if (isset($counts[$status])) { $counts[$status] = $total; } $counts['total'] += $total; } return $counts; } /** * @param array{status:string,keyword:string,date_from:string,date_to:string} $filters * @return array{items:array>,pages:int,total:int} */ private function query_verify_requests(array $filters, int $page_num, int $per_page): array { global $wpdb; $where = ["status.meta_value IN ('pending','verified','rejected','cancelled')"]; $params = []; if ($filters['status'] !== '') { $where[] = 'status.meta_value = %s'; $params[] = $filters['status']; } if ($filters['keyword'] !== '') { $like = '%' . $wpdb->esc_like($filters['keyword']) . '%'; $where[] = '(u.display_name LIKE %s OR u.user_login LIKE %s OR u.user_email LIKE %s OR apply.meta_value LIKE %s)'; array_push($params, $like, $like, $like, $like); } if ($filters['date_from'] !== '') { $where[] = 'COALESCE(requested_at.meta_value, u.user_registered) >= %s'; $params[] = $filters['date_from'] . ' 00:00'; } if ($filters['date_to'] !== '') { $where[] = 'COALESCE(requested_at.meta_value, u.user_registered) <= %s'; $params[] = $filters['date_to'] . ' 23:59'; } $joins = "INNER JOIN {$wpdb->usermeta} status ON status.user_id = u.ID AND status.meta_key = 'aksrt_user_verify_status' INNER JOIN {$wpdb->usermeta} apply ON apply.user_id = u.ID AND apply.meta_key = 'aksrt_user_verify_apply' LEFT JOIN {$wpdb->usermeta} requested_at ON requested_at.user_id = u.ID AND requested_at.meta_key = 'aksrt_user_verify_requested_at' LEFT JOIN {$wpdb->usermeta} reason ON reason.user_id = u.ID AND reason.meta_key = 'aksrt_user_verify_reason' LEFT JOIN {$wpdb->usermeta} reviewed_at ON reviewed_at.user_id = u.ID AND reviewed_at.meta_key = 'aksrt_user_verify_reviewed_at' LEFT JOIN {$wpdb->usermeta} reviewer ON reviewer.user_id = u.ID AND reviewer.meta_key = 'aksrt_user_verify_reviewer'"; $where_sql = ' WHERE ' . implode(' AND ', $where); $count_sql = "SELECT COUNT(DISTINCT u.ID) FROM {$wpdb->users} u {$joins}{$where_sql}"; $total = $params ? (int) $wpdb->get_var($wpdb->prepare($count_sql, $params)) : (int) $wpdb->get_var($count_sql); $query_params = $params; $query_params[] = $per_page; $query_params[] = ($page_num - 1) * $per_page; $sql = "SELECT DISTINCT u.ID, u.display_name, u.user_login, u.user_email, status.meta_value AS verify_status, apply.meta_value AS verify_apply, requested_at.meta_value AS requested_at, reason.meta_value AS verify_reason, reviewed_at.meta_value AS reviewed_at, reviewer.meta_value AS reviewer_id FROM {$wpdb->users} u {$joins}{$where_sql} ORDER BY COALESCE(requested_at.meta_value, u.user_registered) DESC, u.ID DESC LIMIT %d OFFSET %d"; $items = $wpdb->get_results($wpdb->prepare($sql, $query_params), ARRAY_A); return [ 'items' => is_array($items) ? $items : [], 'pages' => max(1, (int) ceil(max(1, $total) / $per_page)), 'total' => $total, ]; } /** @param array{status:string,keyword:string,date_from:string,date_to:string} $filters */ private function render_verify_filters(array $filters): void { echo '
'; echo ''; echo ''; echo ''; echo ''; echo ''; submit_button(__('筛选', 'aksrt'), 'secondary', '', false); echo '
'; } /** @param array> $items */ private function render_verify_table(array $items): void { $type_labels = $this->get_verify_type_labels(); echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; if ($items === []) { echo ''; } foreach ($items as $row) { $this->render_verify_row($row, $type_labels); } echo '
' . esc_html__('用户', 'aksrt') . '' . esc_html__('状态', 'aksrt') . '' . esc_html__('申请资料', 'aksrt') . '' . esc_html__('审核记录', 'aksrt') . '' . esc_html__('操作', 'aksrt') . '
' . esc_html__('暂无认证申请', 'aksrt') . '
'; } /** @return array */ private function get_verify_type_labels(): array { $labels = []; if (class_exists('\AKSRT\User\AuthApply')) { foreach ((array) \AKSRT\User\AuthApply::settings()['types'] as $type) { $labels[(string) ($type['slug'] ?? '')] = (string) ($type['label'] ?? ''); } } return $labels; } /** @param array $row @param array $type_labels */ private function render_verify_row(array $row, array $type_labels): void { $user_id = (int) ($row['ID'] ?? 0); $status = sanitize_key((string) ($row['verify_status'] ?? '')); $apply = maybe_unserialize($row['verify_apply'] ?? []); $apply = is_array($apply) ? $apply : []; $history = $this->get_verify_history($user_id); $reviewer_id = (int) ($row['reviewer_id'] ?? 0); $reviewer = $reviewer_id > 0 ? get_userdata($reviewer_id) : null; echo ''; echo '' . esc_html((string) ($row['display_name'] ?? '')) . ''; echo '
@' . esc_html((string) ($row['user_login'] ?? '')) . ' · UID ' . esc_html((string) $user_id) . '
'; echo ''; echo '' . $this->verify_status_badge($status) . ''; echo ''; $type_key = sanitize_key((string) ($apply['type'] ?? '')); echo '' . esc_html((string) ($apply['name'] ?? __('未填写认证身份', 'aksrt'))) . ''; echo '
' . esc_html__('类型:', 'aksrt') . esc_html($type_labels[$type_key] ?? $type_key ?: '—') . '
'; if (!empty($apply['desc'])) { echo '
' . esc_html((string) $apply['desc']) . '
'; } if (!empty($apply['msg'])) { echo '
' . esc_html__('申请说明:', 'aksrt') . esc_html((string) $apply['msg']) . '
'; } echo '
' . esc_html__('申请时间:', 'aksrt') . esc_html((string) ($row['requested_at'] ?: ($apply['time'] ?? '—'))) . '
'; $evidence = class_exists('\AKSRT\User\AuthApply') ? \AKSRT\User\AuthApply::get_evidence_items($apply, $user_id, true) : []; foreach ($evidence as $item) { echo '' . esc_attr__('认证证明材料', 'aksrt') . ''; } echo ''; echo ''; if (!empty($row['reviewed_at'])) { echo '
' . esc_html((string) $row['reviewed_at']) . '
'; } if ($reviewer) { echo '
' . esc_html__('审核人:', 'aksrt') . esc_html($reviewer->display_name) . '
'; } if (!empty($row['verify_reason'])) { echo '
' . esc_html((string) $row['verify_reason']) . '
'; } if ($history !== []) { echo '
' . esc_html(sprintf(__('历史记录(%d)', 'aksrt'), count($history))) . '
    '; foreach ($history as $entry) { echo '
  1. ' . wp_kses_post($this->format_verify_history_entry($entry)) . '
  2. '; } echo '
'; } echo ''; $this->render_verify_actions($user_id, $status); echo ''; } private function verify_status_badge(string $status): string { $labels = [ 'pending' => __('待审核', 'aksrt'), 'verified' => __('已通过', 'aksrt'), 'rejected' => __('已驳回', 'aksrt'), 'cancelled' => __('已取消', 'aksrt'), ]; $colors = ['pending' => '#996800', 'verified' => '#008a20', 'rejected' => '#b32d2e', 'cancelled' => '#646970']; return '' . esc_html($labels[$status] ?? $status) . ''; } /** @param array $entry */ private function format_verify_history_entry(array $entry): string { $reviewer_id = (int) ($entry['reviewer'] ?? 0); $reviewer = $reviewer_id > 0 ? get_userdata($reviewer_id) : null; $parts = [ esc_html((string) ($entry['time'] ?? '')), $this->verify_status_badge(sanitize_key((string) ($entry['status'] ?? ''))), ]; if ($reviewer) { $parts[] = esc_html($reviewer->display_name); } if (!empty($entry['reason'])) { $parts[] = esc_html((string) $entry['reason']); } return implode(' · ', $parts); } private function render_verify_actions(int $user_id, string $status): void { if ($status === 'pending') { echo '
'; echo ''; echo ''; wp_nonce_field('aksrt_users_review_verify_' . $user_id); echo ''; echo ''; echo '
'; echo '
'; echo ''; echo ''; echo ''; wp_nonce_field('aksrt_users_review_verify_' . $user_id); echo ''; echo ''; echo '
'; return; } if ($status === 'verified') { echo '
'; echo ''; echo ''; echo ''; wp_nonce_field('aksrt_users_review_verify_' . $user_id); echo ''; echo ''; echo '
'; return; } echo '' . esc_html__('用户可在前台重新提交申请', 'aksrt') . ''; } public function handle_review_verify(): void { if (!current_user_can(self::CAPABILITY)) { wp_die(esc_html__('权限不足', 'aksrt')); } $user_id = isset($_POST['user_id']) ? (int) $_POST['user_id'] : 0; $status = isset($_POST['status']) ? sanitize_key((string) wp_unslash($_POST['status'])) : ''; $note = isset($_POST['note']) ? sanitize_textarea_field((string) wp_unslash($_POST['note'])) : ''; check_admin_referer('aksrt_users_review_verify_' . $user_id); $result = class_exists('\AKSRT\User\AuthApply') ? \AKSRT\User\AuthApply::review($user_id, $status, $note, get_current_user_id()) : new \WP_Error('aksrt_verify_unavailable', __('认证服务不可用', 'aksrt')); $redirect_status = $status === 'cancelled' ? 'cancelled' : 'pending'; $extra = ['status' => $redirect_status]; if (is_wp_error($result)) { $extra['aksrt_verify_error'] = $result->get_error_message(); } $this->redirect( 'aksrt_user_auth', is_wp_error($result) ? 'verify_failed' : 'verify_updated', $extra ); } /** @return array> */ private function get_verify_history(int $user_id): array { if (!class_exists('\AKSRT\User\AuthApply')) { return []; } return \AKSRT\User\AuthApply::get_review_history($user_id); } }