table(); if ($table !== '') { global $wpdb; $count = (int) $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE user_id = %d AND is_read = 0", $user_id )); // 同步到 user_meta 以兼容旧读取方 update_user_meta($user_id, 'aksrt_unread_message_count', $count); return $count; } // 降级:从 user_meta 读取 $count = (int) get_user_meta($user_id, 'aksrt_unread_message_count', true); return max(0, $count); } /** * 标记单条通知为已读 */ public function mark_read(int $user_id, string $message_id): bool { if ($user_id <= 0 || empty($message_id)) return false; $table = $this->table(); if ($table !== '') { global $wpdb; $result = $wpdb->update( $table, ['is_read' => 1, 'read_at' => current_time('mysql')], ['id' => (int) $message_id, 'user_id' => $user_id, 'is_read' => 0], ['%d', '%s'], ['%d', '%d', '%d'] ); return $result !== false; } // 降级:仅更新计数 $current = $this->get_unread_count($user_id); if ($current > 0) { update_user_meta($user_id, 'aksrt_unread_message_count', max(0, $current - 1)); } return true; } /** * 标记用户所有通知为已读 */ public function mark_all_read(int $user_id): bool { if ($user_id <= 0) return false; $table = $this->table(); if ($table !== '') { global $wpdb; $wpdb->update( $table, ['is_read' => 1, 'read_at' => current_time('mysql')], ['user_id' => $user_id, 'is_read' => 0], ['%d', '%s'], ['%d', '%d'] ); } update_user_meta($user_id, 'aksrt_unread_message_count', 0); return true; } /** * 发送站内通知(持久化到 aksrt_notification 表) * * @param int $to_user_id 接收人 ID * @param string $type 通知类型 (comment_reply, download_ready, conversation, notice, ...) * @param string $content 通知内容摘要 * @param array $data 附加数据 (title, target_url, ...) * @return bool */ public function send(int $to_user_id, string $type, string $content, array $data = []): bool { if ($to_user_id <= 0 || empty($content)) return false; $table = $this->table(); if ($table !== '') { global $wpdb; $wpdb->insert($table, [ 'user_id' => $to_user_id, 'type' => sanitize_key($type), 'title' => sanitize_text_field((string) ($data['title'] ?? $content)), 'description' => sanitize_textarea_field($content), 'target_url' => esc_url_raw((string) ($data['target_url'] ?? '')), 'is_read' => 0, 'created_at' => current_time('mysql'), ], ['%d', '%s', '%s', '%s', '%s', '%d', '%s']); } // 同步更新 user_meta 未读计数 $current = (int) get_user_meta($to_user_id, 'aksrt_unread_message_count', true); update_user_meta($to_user_id, 'aksrt_unread_message_count', max(0, $current) + 1); // 触发通知钩子 do_action('aksrt_notification_sent', $to_user_id, $type, $data); return true; } /** * 获取用户通知列表 */ public function get_notifications(int $user_id, array $args = []): array { if ($user_id <= 0) return []; $table = $this->table(); if ($table !== '') { global $wpdb; $where = ['user_id = %d']; $params = [$user_id]; $type = sanitize_key((string) ($args['type'] ?? '')); if ($type !== '' && $type !== 'all') { if ($type === 'unread') { $where[] = 'is_read = 0'; } else { $where[] = 'type = %s'; $params[] = $type; } } $limit = max(1, min(100, (int) ($args['limit'] ?? 20))); $offset = max(0, (int) ($args['offset'] ?? 0)); $where_sql = implode(' AND ', $where); $sql = "SELECT * FROM {$table} WHERE {$where_sql} ORDER BY created_at DESC LIMIT %d OFFSET %d"; $params[] = $limit; $params[] = $offset; return (array) $wpdb->get_results($wpdb->prepare($sql, $params), ARRAY_A); } return []; } /** * 获取通知收件箱完整视图(供 page-messages.php 使用) */ public function get_all_notifications(int $user_id): array { return $this->get_notifications($user_id, ['limit' => 200]); } private function table(): string { if (class_exists('AKSRT_DB_Migrator')) { return AKSRT_DB_Migrator::table('notification'); } return ''; } /** * AJAX 标记已读 */ public function ajax_mark_read(): void { check_ajax_referer('aksrt_message', '_nonce'); $user_id = get_current_user_id(); if ($user_id <= 0) { wp_send_json_error(['message' => __('请先登录', 'aksrt')]); } $message_id = sanitize_text_field((string) ($_POST['message_id'] ?? '')); if ($this->mark_read($user_id, $message_id)) { wp_send_json_success(['unread' => $this->get_unread_count($user_id)]); } wp_send_json_error(['message' => __('操作失败', 'aksrt')]); } /** * AJAX 获取未读数 */ public function ajax_get_unread_count(): void { check_ajax_referer('aksrt_message', '_nonce'); $user_id = get_current_user_id(); if ($user_id <= 0) { wp_send_json_error(['message' => __('请先登录', 'aksrt')]); } wp_send_json_success(['unread' => $this->get_unread_count($user_id)]); } }