Files
wordpress-theme-aksrt/inc/classes/class-aksrt-message.php
T

220 lines
6.9 KiB
PHP

<?php
/**
* AKSRT Message — 站内通知系统
*
* 提供通知持久化(写入 aksrt_notification 表)、未读计数、标记已读。
* 与 AKSRT_Private_Message 联动:私信发送时自动创建通知记录。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
class AKSRT_Message {
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) self::$instance = new self();
return self::$instance;
}
public function init(): void {
add_action('wp_ajax_aksrt_mark_message_read', [$this, 'ajax_mark_read']);
add_action('wp_ajax_aksrt_get_unread_count', [$this, 'ajax_get_unread_count']);
}
/**
* 获取用户未读消息数
*/
public function get_unread_count(int $user_id): int {
if ($user_id <= 0) return 0;
// 优先从表中实时统计
$table = $this->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)]);
}
}