子比美化-状态功能

2026-05-02 0 224

功能简介

这是一个为WordPress网站开发的用户在线状态管理系统,提供了类似社交平台的用户状态功能。

主要功能

  • 多状态支持 :在线、离开、忙碌、离线四种状态
  • 智能监测 :自动检测用户活动状态并更新
  • 实时交互 :AJAX异步更新状态,无需刷新页面
  • 视觉反馈 :头像上显示状态指示器,直观明了
  • 后台管理 :完整的后台管理界面,支持批量操作
  • 响应式设计 :适配各种屏幕尺寸
  • 性能优化 :缓存机制减少数据库查询

技术实现

  • 用户在线状态表 :存储用户ID、状态、最后更新时间和手动设置标志
  • 用户元数据 :使用WordPress用户元数据存储活动时间和登录状态
  • 状态管理函数 :处理状态的获取、更新和缓存
  • 活动检测函数 :判断用户是否处于活动状态
  • 定时任务函数 :定期检查和更新用户状态
  • 缓存管理函数 :处理状态缓存的读写和清除
  • AJAX交互 :使用jQuery AJAX实现无刷新状态更新
  • 响应式CSS :适配不同屏幕尺寸的样式设计
  • 头像集成 :通过过滤器在头像上添加状态指示器
  • 用户体验优化 :添加加载状态、错误处理和成功反馈

效果图

子比美化-状态功能

使用教程

进入目录zibll\functions.php

//星空知-用户在线状态
// 创建用户在线状态表
function xk_zhplugs_create_user_online_status_table()
{
    global $wpdb;
    $table_name = $wpdb->prefix . 'user_online_status';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE IF NOT EXISTS $table_name (
        user_id bigint(20) NOT NULL,
        status varchar(20) DEFAULT 'online',
        last_update datetime DEFAULT CURRENT_TIMESTAMP,
        manual_set tinyint(1) DEFAULT 0,
        PRIMARY KEY (user_id)
    ) $charset_collate;";

    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);
    set_transient('xk_zhplugs_manual_set_field_checked', 1, DAY_IN_SECONDS);
}
add_action('init', 'xk_zhplugs_create_user_online_status_table');

function xk_zhplugs_get_status_monitor_time()
{
    static $time = null;
    if ($time === null) {
        $time = 300;// 默认5分钟
    }
    return $time;
}

function xk_zhplugs_is_user_active($user_id)
{
    $last_activity = get_user_meta($user_id, 'last_activity', true);
    if (!$last_activity) {
        return false;
    }
    $monitor_time = xk_zhplugs_get_status_monitor_time();
    return (time() - (int) $last_activity) <= $monitor_time;
}

function xk_zhplugs_update_user_activity()
{
    if (is_user_logged_in()) {
        $user_id = get_current_user_id();
        $current_time = time();
        update_user_meta($user_id, 'last_activity', $current_time);
        update_user_meta($user_id, 'is_logged_in', '1');
        error_log('更新了用户ID ' . $user_id . ' 的活动时间和登录状态');
        $current_status = xk_zhplugs_get_user_online_status($user_id);
        error_log('用户ID ' . $user_id . ' 当前状态: ' . $current_status);
        if ($current_status == 'offline') {
            global $wpdb;
            $table_name = $wpdb->prefix . 'user_online_status';

            $result = $wpdb->query(
                $wpdb->prepare(
                    "REPLACE INTO $table_name (user_id, status, last_update, manual_set) VALUES (%d, %s, %s, %d)",
                    $user_id,
                    'online',
                    current_time('mysql'),
                    0
                )
            );
            error_log('用户ID ' . $user_id . ' 状态从离线更新为在线,结果: ' . ($result !== false ? '成功' : '失败'));
            if ($result === false) {
                error_log('数据库更新失败: ' . $wpdb->last_error);
            }
            xk_zhplugs_clear_user_status_cache($user_id);
            error_log('用户ID ' . $user_id . ' 状态已更新为在线并清除了缓存');
        }
    }
}
add_action('init', 'xk_zhplugs_update_user_activity');

function xk_zhplugs_check_user_activity_status()
{
    error_log('开始检查用户活动状态');
    global $wpdb;
    $table_name = $wpdb->prefix . 'user_online_status';
    $users = $wpdb->get_results("SELECT user_id, status FROM $table_name");
    error_log('获取到 ' . count($users) . ' 个用户的状态信息');
    foreach ($users as $user) {
        $is_logged_in = get_user_meta($user->user_id, 'is_logged_in', true);
        $is_active = xk_zhplugs_is_user_active($user->user_id);
        error_log("用户ID {$user->user_id} 当前状态: {$user->status}, 登录状态: {$is_logged_in}, 活动状态: " . ($is_active ? '是' : '否'));
        if ($user->status != 'offline' && (!$is_active || $is_logged_in != '1')) {
            $result = $wpdb->query(
                $wpdb->prepare(
                    "REPLACE INTO $table_name (user_id, status, last_update, manual_set) VALUES (%d, %s, %s, %d)",
                    $user->user_id,
                    'offline',
                    current_time('mysql'),
                    0
                )
            );
            error_log("更新用户ID {$user->user_id} 状态为离线,结果: " . ($result !== false ? '成功' : '失败'));
            if ($result === false) {
                error_log('数据库更新失败: ' . $wpdb->last_error);
            }
            update_user_meta($user->user_id, 'is_logged_in', '0');
            xk_zhplugs_clear_user_status_cache($user->user_id);
            error_log("用户状态切换:用户ID {$user->user_id} 因" . (!$is_active ? '不活动' : '未登录') . "从 {$user->status} 状态切换为离线状态");
        }
    }
    error_log('用户活动状态检查完成');
}

function xk_zhplugs_schedule_activity_check()
{
    if (!wp_next_scheduled('xk_zhplugs_check_user_activity')) {
        wp_schedule_event(time(), 'every_minute', 'xk_zhplugs_check_user_activity');
    }
}
add_action('wp', 'xk_zhplugs_schedule_activity_check');
add_action('admin_init', 'xk_zhplugs_schedule_activity_check');
add_action('init', 'xk_zhplugs_schedule_activity_check');
add_action('xk_zhplugs_check_user_activity', 'xk_zhplugs_check_user_activity_status');

// 测试
function xk_zhplugs_manual_check_activity_status()
{
    xk_zhplugs_check_user_activity_status();
    error_log('手动触发了用户活动状态检查');
}
add_action('wp_ajax_check_activity_status', 'xk_zhplugs_manual_check_activity_status');
add_action('wp_ajax_nopriv_check_activity_status', 'xk_zhplugs_manual_check_activity_status');

function xk_zhplugs_add_cron_intervals($schedules)
{
    $schedules['every_minute'] = array(
        'interval' => 60, // 60秒
        'display' => __('每分钟')
    );
    return $schedules;
}
add_filter('cron_schedules', 'xk_zhplugs_add_cron_intervals');

function xk_zhplugs_set_online_on_login($user_login, $user)
{
    update_user_meta($user->ID, 'last_activity', time());
    update_user_meta($user->ID, 'is_logged_in', '1');
    global $wpdb;
    $table_name = $wpdb->prefix . 'user_online_status';
    $wpdb->query(
        $wpdb->prepare(
            "REPLACE INTO $table_name (user_id, status, last_update, manual_set) VALUES (%d, %s, %s, %d)",
            $user->ID,
            'online',
            current_time('mysql'),
            0
        )
    );
    xk_zhplugs_clear_user_status_cache($user->ID);
}
add_action('wp_login', 'xk_zhplugs_set_online_on_login', 10, 2);

function xk_zhplugs_set_status_on_logout()
{
    error_log('开始处理用户登出状态更新');
    $user_id = get_current_user_id();
    error_log('登出用户ID: ' . $user_id);
    if (!$user_id) {
        error_log('无法获取用户ID,跳过登出状态更新');
        return;
    }
    update_user_meta($user_id, 'is_logged_in', '0');
    error_log('更新了用户ID ' . $user_id . ' 的登录状态为离线');
    global $wpdb;
    $table_name = $wpdb->prefix . 'user_online_status';
    $result = $wpdb->query(
        $wpdb->prepare(
            "REPLACE INTO $table_name (user_id, status, last_update, manual_set) VALUES (%d, %s, %s, %d)",
            $user_id,
            'offline',
            current_time('mysql'),
            0
        )
    );
    error_log('更新用户ID ' . $user_id . ' 状态为离线,结果: ' . ($result !== false ? '成功' : '失败'));
    if ($result === false) {
        error_log('数据库更新失败: ' . $wpdb->last_error);
    }
    xk_zhplugs_clear_user_status_cache($user_id);
    error_log('清除了用户ID ' . $user_id . ' 的状态缓存');
}
add_action('wp_logout', 'xk_zhplugs_set_status_on_logout');
add_action('clear_auth_cookie', 'xk_zhplugs_set_status_on_logout', 100);
add_action('wp_login_failed', 'xk_zhplugs_set_status_on_logout');
add_action('wp_ajax_user_online_status_switch', 'xk_zhplugs_user_online_status_switch');

function xk_zhplugs_user_online_status_switch()
{
    header('Content-Type: application/json');
    if (!is_user_logged_in()) {
        echo json_encode(array('success' => false, 'data' => array('msg' => '请先登录')));
        exit;
    }
    $user_id = get_current_user_id();
    $status = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : '';
    $nonce = isset($_POST['nonce']) ? sanitize_text_field($_POST['nonce']) : '';
    if (empty($nonce) || !wp_verify_nonce($nonce, 'xk_zhplugs_user_online_status')) {
        echo json_encode(array('success' => false, 'data' => array('msg' => '安全验证失败')));
        exit;
    }
    $allowed_statuses = array('online', 'away', 'busy', 'offline');
    if (!in_array($status, $allowed_statuses)) {
        echo json_encode(array('success' => false, 'data' => array('msg' => '无效的状态')));
        exit;
    }
    if ($status != 'offline' && !xk_zhplugs_is_user_active($user_id)) {
        echo json_encode(array('success' => false, 'data' => array('msg' => '您的会话已超时,请刷新页面后再试')));
        exit;
    }
    global $wpdb;
    $table_name = $wpdb->prefix . 'user_online_status';
    $result = $wpdb->query(
        $wpdb->prepare(
            "REPLACE INTO $table_name (user_id, status, last_update, manual_set) VALUES (%d, %s, %s, %d)",
            $user_id,
            $status,
            current_time('mysql'),
            1
        )
    );
    if ($result !== false) {
        if ($status === 'offline') {
            update_user_meta($user_id, 'is_logged_in', '0');
        } else {
            update_user_meta($user_id, 'is_logged_in', '1');
            update_user_meta($user_id, 'last_activity', time());
        }
        if (function_exists('wp_cache_delete')) {
            wp_cache_delete($user_id, 'user_avatar');
        }
        if (function_exists('xk_zhplugs_clear_user_status_cache')) {
            xk_zhplugs_clear_user_status_cache($user_id);
        }
        $status_map = array('online' => '在线', 'away' => '离开', 'busy' => '忙碌', 'offline' => '离线');
        echo json_encode(array('success' => true, 'data' => array('msg' => '当前在线状态为:' . ($status_map[$status] ?? '在线'))));
    } else {
        error_log('在线状态更新失败: ' . $wpdb->last_error);
        echo json_encode(array('success' => false, 'data' => array('msg' => '在线状态更新失败')));
    }
    exit;
}

$xk_zhplugs_user_status_cache = array();
$xk_zhplugs_status_cache_time = array();

function xk_zhplugs_clear_user_status_cache($user_id = null)
{
    global $xk_zhplugs_user_status_cache, $xk_zhplugs_status_cache_time;
    error_log('开始清除用户状态缓存,用户ID: ' . ($user_id ? $user_id : '所有'));
    if ($user_id) {
        if (isset($xk_zhplugs_user_status_cache[$user_id])) {
            unset($xk_zhplugs_user_status_cache[$user_id]);
            error_log('清除了用户ID ' . $user_id . ' 的基础状态缓存');
        }
        if (isset($xk_zhplugs_status_cache_time[$user_id])) {
            unset($xk_zhplugs_status_cache_time[$user_id]);
        }
        $full_cache_key = 'user_status_full_' . $user_id;
        if (isset($xk_zhplugs_user_status_cache[$full_cache_key])) {
            unset($xk_zhplugs_user_status_cache[$full_cache_key]);
            error_log('清除了用户ID ' . $user_id . ' 的完整状态数据缓存');
        }
        if (isset($xk_zhplugs_status_cache_time[$full_cache_key])) {
            unset($xk_zhplugs_status_cache_time[$full_cache_key]);
        }
        if (function_exists('wp_cache_delete')) {
            wp_cache_delete($user_id, 'user_avatar');
            wp_cache_delete('user_status_' . $user_id);
            error_log('清除了用户ID ' . $user_id . ' 的WordPress对象缓存');
        }
        clean_user_cache(get_userdata($user_id));
        error_log('清除了用户ID ' . $user_id . ' 的用户元数据缓存');
    } else {
        $xk_zhplugs_user_status_cache = array();
        $xk_zhplugs_status_cache_time = array();
        error_log('清除了所有内存缓存');
        if (function_exists('wp_cache_flush_group')) {
            wp_cache_flush_group('user_avatar');
            error_log('清除了所有用户的头像缓存');
        }
        wp_cache_flush();
        error_log('清除了所有WordPress缓存');
    }
    error_log('用户状态缓存清除完成');
}


function xk_zhplugs_get_user_online_status($user_id = null)
{
    global $xk_zhplugs_user_status_cache, $xk_zhplugs_status_cache_time;
    if (!$user_id) {
        $user_id = get_current_user_id();
    }
    if (!$user_id) {
        return 'offline';
    }
    $cache_valid = isset($xk_zhplugs_user_status_cache[$user_id]) &&
        isset($xk_zhplugs_status_cache_time[$user_id]) &&
        (time() - $xk_zhplugs_status_cache_time[$user_id] < 60);
    if ($cache_valid) {
        $cached_status = $xk_zhplugs_user_status_cache[$user_id];
        if ($cached_status != 'offline' && !xk_zhplugs_is_user_active($user_id)) {
            return 'offline';
        }
        return $cached_status;
    }
    global $wpdb;
    $table_name = $wpdb->prefix . 'user_online_status';
    $status_data = $wpdb->get_row($wpdb->prepare(
        "SELECT status FROM $table_name WHERE user_id = %d",
        $user_id
    ));
    $status = $status_data ? $status_data->status : 'offline';
    if ($status != 'offline' && !xk_zhplugs_is_user_active($user_id)) {
        $status = 'offline';
    }
    $xk_zhplugs_user_status_cache[$user_id] = $status;
    $xk_zhplugs_status_cache_time[$user_id] = time();
    return $status;
}

// 用户在线状态小工具
class XK_Zhplugs_Online_Status_Widget extends WP_Widget
{
    public function __construct()
    {
        parent::__construct(
            'xk_zhplugs_online_status_widget',
            '星空知-在线状态',
            array('description' => '显示和切换用户在线状态')
        );
    }

    public function widget($args, $instance)
    {
        if (!is_user_logged_in()) {
            return;
        }

        echo $args['before_widget'];

        $title = !empty($instance['title']) ? $instance['title'] : '星空知-在线状态';
        echo $args['before_title'] . $title . $args['after_title'];

        $current_status = xk_zhplugs_get_user_online_status();

        $nonce = wp_create_nonce('xk_zhplugs_user_online_status');
        ?>
        <div class="fetl-user-online-status mt5">
            <div class="but <?php echo $current_status == 'online' ? 'fetl-active' : ''; ?>" data-sta="online">
                <span></span><span class="fetl-wd-k-all">在线</span>
            </div>
            <div class="but <?php echo $current_status == 'away' ? 'fetl-active' : ''; ?>" data-sta="away">
                <span></span><span class="fetl-wd-k-all">离开</span>
            </div>
            <div class="but <?php echo $current_status == 'busy' ? 'fetl-active' : ''; ?>" data-sta="busy">
                <span></span><span class="fetl-wd-k-all">忙碌</span>
            </div>
            <div class="but <?php echo $current_status == 'offline' ? 'fetl-active' : ''; ?>" data-sta="offline">
                <span></span><span class="fetl-wd-k-all">离线</span>
            </div>
            <input type="hidden" class="xk_zhplugs_user_online_status_nonce" value="<?php echo esc_attr($nonce); ?>">
        </div>
        <?php
        echo $args['after_widget'];
    }

    public function form($instance)
    {
        $title = !empty($instance['title']) ? $instance['title'] : '星空知-在线状态';
        ?>
        <p>
            <label for="<?php echo $this->get_field_id('title'); ?>">标题:</label>
            <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>"
                name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>">
        </p>
        <?php
    }

    public function update($new_instance, $old_instance)
    {
        $instance = array();
        $instance['title'] = !empty($new_instance['title']) ? strip_tags($new_instance['title']) : '';
        return $instance;
    }
}

// 注册小工具
add_action('widgets_init', function () {
    register_widget('XK_Zhplugs_Online_Status_Widget');
});

// 添加顶级后台管理菜单
add_action('admin_menu', 'xk_zhplugs_add_user_status_admin_menu', 20);
function xk_zhplugs_add_user_status_admin_menu()
{
    // 检查功能是否启用
    add_menu_page(
        '用户在线状态管理',
        '用户在线状态管理',
        'manage_options',
        'xk-user-status',
        'xk_zhplugs_user_status_admin_page',
        'dashicons-heart',
        25
    );
}


add_action('admin_init', 'xk_zhplugs_handle_status_form_submissions');
function xk_zhplugs_handle_status_form_submissions()
{

    global $pagenow;
    if ($pagenow === 'options-general.php' && isset($_GET['page']) && $_GET['page'] === 'xk-user-status') {

        wp_safe_redirect(admin_url('admin.php?page=xk-user-status'));
        exit;
    }

    if (isset($_POST['update_user_status'])) {
        xk_zhplugs_handle_update_user_status();
    }

    if (isset($_POST['bulk_update_status'])) {
        xk_zhplugs_handle_bulk_update_status();
    }
}


function xk_zhplugs_user_status_admin_page()
{
    global $title;
    $title = '用户在线状态管理';

    $search = isset($_GET['s']) ? sanitize_text_field($_GET['s']) : '';

    $status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : '';

    $valid_statuses = array('', 'online', 'away', 'busy', 'offline');
    if (!in_array($status_filter, $valid_statuses)) {
        $status_filter = '';
    }


    $users = xk_zhplugs_get_users_with_status($search, $status_filter);

    // 状态选项
    $status_options = array(
        'online' => '在线',
        'away' => '离开',
        'busy' => '忙碌',
        'offline' => '离线'
    );

    ?>
    <div class="wrap">
        <h1>用户在线状态管理</h1>

        <!-- 组合搜索和批量操作在同一行 -->
        <div class="tablenav top" style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
            <!-- 搜索和状态筛选表单 -->
            <form method="get" action="" style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
                <input type="hidden" name="page" value="xk-user-status">
                <label class="screen-reader-text" for="user-search-input">搜索用户:</label>
                <input type="search" id="user-search-input" name="s" value="<?php echo esc_attr($search); ?>"
                    placeholder="搜索用户名或昵称" style="min-width: 200px;">

                <!-- 状态筛选下拉框 -->
                <select name="status" id="status-filter" style="min-width: 120px;">
                    <option value="">所有状态</option>
                    <?php foreach ($status_options as $value => $label): ?>
                        <option value="<?php echo esc_attr($value); ?>" <?php selected($status_filter, $value); ?>>
                            <?php echo esc_html($label); ?>
                        </option>
                    <?php endforeach; ?>
                </select>

                <input type="submit" id="search-submit" class="button" value="筛选">
                <?php if (!empty($search) || !empty($status_filter)): ?>
                    <a href="<?php echo admin_url('admin.php?page=xk-user-status'); ?>" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"
                        class="button">清除筛选</a>
                <?php endif; ?>
            </form>
        </div>

        <!-- 批量操作表单 -->
        <form method="post" action="">
            <?php wp_nonce_field('xk_user_status_bulk', 'xk_user_status_nonce'); ?>
            <div class="tablenav top" style="margin-top: 10px;">
                <!-- 批量操作 -->
                <div class="alignleft actions bulkactions">
                    <label for="bulk-action-selector-top" class="screen-reader-text">选择批量操作</label>
                    <select name="bulk_status" id="bulk-action-selector-top">
                        <option value="">更改状态为...</option>
                        <?php foreach ($status_options as $value => $label): ?>
                            <option value="<?php echo esc_attr($value); ?>"><?php echo esc_html($label); ?></option>
                        <?php endforeach; ?>
                    </select>
                    <input type="submit" name="bulk_update_status" id="doaction" class="button action" value="应用">
                </div>
                <br class="clear">
            </div>

            <!-- 用户状态列表 -->
            <table class="wp-list-table widefat fixed striped users">
                <thead>
                    <tr>
                        <th scope="col" class="manage-column column-cb check-column">
                            <input type="checkbox" id="cb-select-all-1">
                        </th>
                        <th scope="col" class="manage-column column-username">用户名</th>
                        <th scope="col" class="manage-column column-name">昵称</th>
                        <th scope="col" class="manage-column column-status">当前状态</th>
                        <th scope="col" class="manage-column column-last_activity">最后活动时间</th>
                        <th scope="col" class="manage-column column-actions">操作</th>
                    </tr>
                </thead>
                <tbody id="the-list">
                    <?php if (!empty($users)): ?>
                        <?php foreach ($users as $user): ?>
                            <tr id="user-<?php echo $user->ID; ?>">
                                <th scope="row" class="check-column">
                                    <input type="checkbox" name="user_ids[]" value="<?php echo $user->ID; ?>">
                                </th>
                                <td class="username column-username">
                                    <strong><?php echo esc_html($user->user_login); ?></strong>
                                </td>
                                <td class="name column-name">
                                    <?php echo esc_html($user->display_name); ?>
                                </td>
                                <td class="status column-status">
                                    <span class="status-badge status-<?php echo $user->status; ?>">
                                        <?php echo $status_options[$user->status]; ?>
                                    </span>
                                </td>
                                <td class="last_activity column-last_activity">
                                    <?php echo $user->last_activity ? date('Y-m-d H:i:s', $user->last_activity) : '从未活动'; ?>
                                </td>
                                <td class="actions column-actions">
                                    <!-- 使用独立的表单,不嵌套在批量操作表单中 -->
                                    <form method="post" action="" style="display: inline;"
                                        id="update-form-<?php echo $user->ID; ?>">
                                        <?php wp_nonce_field('xk_user_status_update', 'xk_user_status_update_nonce'); ?>
                                        <input type="hidden" name="user_id" value="<?php echo $user->ID; ?>">
                                        <select name="new_status" style="margin-right: 5px;">
                                            <?php foreach ($status_options as $value => $label): ?>
                                                <option value="<?php echo esc_attr($value); ?>" <?php selected($user->status, $value); ?>><?php echo esc_html($label); ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                        <input type="submit" name="update_user_status" class="button button-small" value="更新">
                                    </form>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                    <?php else: ?>
                        <tr>
                            <td colspan="6" class="colspanchange">
                                <?php if (!empty($search) || !empty($status_filter)): ?>
                                    没有找到匹配条件的用户。
                                <?php else: ?>
                                    没有找到用户。
                                <?php endif; ?>
                            </td>
                        </tr>
                    <?php endif; ?>
                </tbody>
            </table>
        </form>

        <!-- 样式 -->
        <style>
            .status-badge {
                display: inline-block;
                padding: 2px 8px;
                border-radius: 12px;
                font-size: 12px;
                font-weight: bold;
                text-transform: uppercase;
            }

            .status-online {
                background-color: #c8e6c9;
                color: #2e7d32;
            }

            .status-away {
                background-color: #fff3cd;
                color: #856404;
            }

            .status-busy {
                background-color: #f8d7da;
                color: #721c24;
            }

            .status-offline {
                background-color: #e2e3e5;
                color: #383d41;
            }
        </style>
    </div>
    <?php
}

function xk_zhplugs_get_users_with_status($search = '', $status_filter = '')
{
    global $wpdb;
    $table_name = $wpdb->prefix . 'user_online_status';

    $sql = "SELECT DISTINCT u.ID, u.user_login, u.display_name, 
            uos.status, uos.manual_set, 
            um.meta_value as last_activity 
            FROM $wpdb->users u 
            LEFT JOIN $table_name uos ON u.ID = uos.user_id 
            LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id AND um.meta_key = 'last_activity'";

    $where_conditions = array();
    $prepare_args = array();

    if (!empty($search)) {
        $search = '%' . $wpdb->esc_like($search) . '%';
        $where_conditions[] = "(u.user_login LIKE %s OR u.display_name LIKE %s)";
        $prepare_args[] = $search;
        $prepare_args[] = $search;
    }

    if (!empty($status_filter)) {
        $where_conditions[] = "(uos.status = %s OR (uos.status IS NULL AND %s = 'offline'))";
        $prepare_args[] = $status_filter;
        $prepare_args[] = $status_filter;
    }

    if (!empty($where_conditions)) {
        $sql .= " WHERE " . implode(" AND ", $where_conditions);
    }

    $sql .= " ORDER BY uos.status ASC, u.user_login ASC";

    if (!empty($prepare_args)) {
        $users = $wpdb->get_results($wpdb->prepare($sql, $prepare_args));
    } else {
        $users = $wpdb->get_results($sql);
    }

    foreach ($users as $user) {
        if (empty($user->status)) {
            $user->status = 'offline';
        }
        if (!empty($user->last_activity)) {
            $user->last_activity = (int) $user->last_activity;
        }
    }

    return $users;
}


function xk_zhplugs_handle_update_user_status()
{



    if (!isset($_POST['xk_user_status_update_nonce']) || !wp_verify_nonce($_POST['xk_user_status_update_nonce'], 'xk_user_status_update')) {
        wp_die('安全验证失败');
    }


    if (!current_user_can('manage_options')) {
        wp_die('您没有权限执行此操作');
    }

    $user_id = isset($_POST['user_id']) ? (int) $_POST['user_id'] : 0;
    $new_status = isset($_POST['new_status']) ? sanitize_text_field($_POST['new_status']) : '';


    $allowed_statuses = array('online', 'away', 'busy', 'offline');
    if (!in_array($new_status, $allowed_statuses)) {
        wp_die('无效的状态值');
    }

    // 更新用户状态
    global $wpdb;
    $table_name = $wpdb->prefix . 'user_online_status';

    // 输出日志
    error_log("用户状态切换:管理员更新用户ID {$user_id} 为 {$new_status} 状态");

    $wpdb->query(
        $wpdb->prepare(
            "REPLACE INTO $table_name (user_id, status, last_update, manual_set) VALUES (%d, %s, %s, %d)",
            $user_id,
            $new_status,
            current_time('mysql'),
            1
        )
    );

    // 更新登录状态
    if ($new_status === 'offline') {
        update_user_meta($user_id, 'is_logged_in', '0');
    } else {
        update_user_meta($user_id, 'is_logged_in', '1');
        update_user_meta($user_id, 'last_activity', time());
    }

    // 清除缓存
    xk_zhplugs_clear_user_status_cache($user_id);

    // 重定向到原页面
    wp_redirect(admin_url('options-general.php?page=xk-user-status'));
    exit;
}

// 处理批量更新状态
function xk_zhplugs_handle_bulk_update_status()
{

    // 验证nonce
    if (!isset($_POST['xk_user_status_nonce']) || !wp_verify_nonce($_POST['xk_user_status_nonce'], 'xk_user_status_bulk')) {
        wp_die('安全验证失败');
    }

    // 检查权限
    if (!current_user_can('manage_options')) {
        wp_die('您没有权限执行此操作');
    }

    $user_ids = isset($_POST['user_ids']) ? array_map('intval', $_POST['user_ids']) : array();
    $bulk_status = isset($_POST['bulk_status']) ? sanitize_text_field($_POST['bulk_status']) : '';

    // 验证状态值
    $allowed_statuses = array('online', 'away', 'busy', 'offline');
    if (!in_array($bulk_status, $allowed_statuses) || empty($user_ids)) {
        wp_die('无效的操作参数');
    }

    // 批量更新用户状态
    global $wpdb;
    $table_name = $wpdb->prefix . 'user_online_status';
    $now = current_time('mysql');

    foreach ($user_ids as $user_id) {
        // 输出日志
        error_log("用户状态切换:管理员批量更新用户ID {$user_id} 为 {$bulk_status} 状态");

        $wpdb->query(
            $wpdb->prepare(
                "REPLACE INTO $table_name (user_id, status, last_update, manual_set) VALUES (%d, %s, %s, %d)",
                $user_id,
                $bulk_status,
                $now,
                1
            )
        );

        // 更新登录状态
        if ($bulk_status === 'offline') {
            update_user_meta($user_id, 'is_logged_in', '0');
        } else {
            update_user_meta($user_id, 'is_logged_in', '1');
            update_user_meta($user_id, 'last_activity', time());
        }

        // 清除缓存
        xk_zhplugs_clear_user_status_cache($user_id);
    }

    // 重定向到原页面
    wp_redirect(admin_url('options-general.php?page=xk-user-status'));
    exit;
}

// 在头像上添加在线状态指示器 - 只在前端生效
add_filter('get_avatar', 'xk_zhplugs_add_online_status_to_avatar', 99, 5);
function xk_zhplugs_add_online_status_to_avatar($avatar, $id_or_email, $size, $default, $alt)
{
    // 不在后台显示在线状态指示器,避免影响后台布局
    if (is_admin()) {
        return $avatar;
    }

    $user_id = 0;

    if (is_numeric($id_or_email)) {
        $user_id = $id_or_email;
    } elseif (is_object($id_or_email) && !empty($id_or_email->user_id)) {
        $user_id = $id_or_email->user_id;
    } elseif (is_email($id_or_email)) {
        $user = get_user_by('email', $id_or_email);
        $user_id = $user ? $user->ID : 0;
    }

    if ($user_id) {
        $status = xk_zhplugs_get_user_online_status($user_id);
        $avatar = '<div class="fetl-avatar-status-box" style="display:inline-block;position:relative;">' . $avatar .
            '<span class="fetl-online-status-dot fetl-status-' . esc_attr($status) . ' user-uid-' . esc_attr($user_id) . '"></span>' .
            '</div>';
    }

    return $avatar;
}

// 子比主题头像适配 - 优化版本
add_filter('zib_get_data_avatar', 'xk_zhplugs_zib_avatar_with_status', 10, 3);
function xk_zhplugs_zib_avatar_with_status($avatar, $user_id, $size)
{
    // 不在后台显示在线状态指示器,避免影响后台布局
    if (is_admin() || !$user_id) {
        return $avatar;
    }

    $status = xk_zhplugs_get_user_online_status($user_id);
    $status_config = array(
        'online' => array('color' => '#72e128', 'text' => '在线'),
        'away' => array('color' => '#faad14', 'text' => '离开'),
        'busy' => array('color' => '#f5222d', 'text' => '忙碌'),
        'offline' => array('color' => '#d9d9d9', 'text' => '离线')
    );
    $config = $status_config[$status] ?? $status_config['offline'];
    $dot_size = $size <= 40 ? 7 : ($size <= 60 ? 8 : ($size <= 100 ? 9 : 10));

    // 创建状态指示器HTML,包含状态类
    $dot_html = sprintf(
        '<span class="fetl-online-status-dot user-uid-%d fetl-status-%s" 
              style="position:absolute;bottom:1px;left:1px;width:%dpx;height:%dpx;border-radius:100%%;box-shadow:0 0 0 2px #fff;background-color:%s;z-index:2;" 
              title="%s">
        </span>',
        $user_id,
        $status,
        $dot_size,
        $dot_size,
        $config['color'],
        $config['text']
    );

    // 检查头像是否已经有容器,避免重复添加
    if (strpos($avatar, 'fetl-avatar-status-box') !== false) {
        // 如果已经有容器,只更新状态指示器
        $avatar = preg_replace(
            '/<span class="fetl-online-status-dot user-uid-' . $user_id . '.*?<\/span>/',
            $dot_html,
            $avatar
        );
    } else {
        // 否则,添加容器和状态指示器
        $avatar = sprintf(
            '<div class="fetl-avatar-status-box" style="position:relative;display:inline-block;vertical-align:middle;">
                %s
                %s
            </div>',
            $avatar,
            $dot_html
        );
    }

    return $avatar;
}

// 注入 JavaScript 代码
add_action('wp_footer', 'xk_zhplugs_inject_javascript');
function xk_zhplugs_inject_javascript()
{
    if (!is_user_logged_in())
        return;

    $nonce = wp_create_nonce('xk_zhplugs_user_online_status');
    $user_id = get_current_user_id();
    ?>
    <script type="text/javascript">
        jQuery(document).on('click', '.fetl-user-online-status .but', function () {
            var $this = jQuery(this);
            var $container = $this.closest('.fetl-user-online-status');
            var status = $this.data('sta');
            var nonce = $container.find('.xk_zhplugs_user_online_status_nonce').val() || window
                .xk_zhplugs_user_online_status_nonce || '<?php echo $nonce; ?>';
            var $allButtons = $container.find('.but');

            // 防止重复点击
            if ($this.data('processing')) return;
            $this.data('processing', 1);

            // 立即更新UI,给用户即时反馈
            $allButtons.removeClass('fetl-active');
            $this.addClass('fetl-active');

            // 添加加载状态类
            $this.addClass('status-updating');

            // 优化的AJAX请求,添加超时设置
            jQuery.ajax({
                url: '<?php echo admin_url('admin-ajax.php'); ?>',
                type: 'POST',
                data: {
                    action: 'user_online_status_switch',
                    status: status,
                    nonce: nonce
                },
                timeout: 5000, // 设置5秒超时
                cache: false,
                success: function (response) {
                    if (response.success) {
                        var user_id = '<?php echo $user_id; ?>';
                        if (user_id) {
                            // 使用更高效的选择器
                            jQuery('.fetl-online-status-dot.user-uid-' + user_id)
                                .removeClass(
                                    'fetl-status-online fetl-status-away fetl-status-busy fetl-status-offline'
                                )
                                .addClass('fetl-status-' + status);
                        }
                        if (typeof notyf === 'function') {
                            notyf(response.data.msg, 'success', 1000);
                        }
                    } else {
                        // 失败时恢复原状态
                        $allButtons.removeClass('fetl-active');
                        // 显示错误信息
                        if (typeof notyf === 'function') {
                            notyf(response.data.msg || '状态更新失败', "danger");
                        }
                    }
                },
                error: function (xhr, status, error) {
                    // 错误处理,恢复原状态
                    $allButtons.removeClass('fetl-active');

                    // 显示错误信息
                    var errorMsg = '网络请求异常';
                    if (status === 'timeout') {
                        errorMsg = '请求超时,请重试';
                    } else if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.msg) {
                        errorMsg = xhr.responseJSON.data.msg;
                    }

                    if (typeof notyf === 'function') {
                        notyf(errorMsg, "danger");
                    }
                },
                complete: function () {
                    // 清除加载状态
                    $this.removeClass('status-updating');
                    $this.data('processing', 0);
                }
            });
        });

        jQuery(document).on('click', "a[href*='action=logout']", function (e) {
            var nonce = window.xk_zhplugs_user_online_status_nonce || '<?php echo $nonce; ?>';
            var user_id = '<?php echo $user_id; ?>';
            if (user_id && nonce) {
                e.preventDefault();
                var logoutUrl = jQuery(this).attr('href');
                var $this = jQuery(this);

                // 添加加载状态
                $this.addClass('logging-out');

                // 优化的AJAX请求,添加超时设置
                jQuery.ajax({
                    url: '<?php echo admin_url('admin-ajax.php'); ?>',
                    type: 'POST',
                    data: {
                        action: 'user_online_status_switch',
                        status: 'offline',
                        nonce: nonce
                    },
                    timeout: 3000, // 3秒超时
                    cache: false,
                    error: function (xhr, status, error) {
                        // 即使请求失败,也执行登出
                        console.warn('登出状态更新失败,但仍将继续登出:', error);
                    },
                    complete: function () {
                        // 清除加载状态并执行登出
                        $this.removeClass('logging-out');
                        window.location.href = logoutUrl;
                    }
                });
            }
        });

        window.xk_zhplugs_user_online_status_nonce = '<?php echo $nonce; ?>';
        window.current_user_id = '<?php echo $user_id; ?>';
    </script>
    <?php
}

add_action('wp_head', 'xk_zhplugs_user_online_status_styles');
function xk_zhplugs_user_online_status_styles()
{

    ?>
    <style>
        .fetl-user-online-status {
            display: grid !important;
            grid-template-columns: repeat(4, 1fr);
            gap: calc(1.938rem - 5px);
            margin-bottom: calc(1.938rem - 5px);
        }

        .fetl-user-online-status>div {
            position: relative;
            background: var(--muted-border-color);
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 6px;
            padding: 6px;
            min-width: 50px;
            min-height: 50px;
            border-radius: var(--main-radius);
            border: 2px dashed transparent;
            cursor: pointer !important;
            transition: .15s;
            text-align: center;
            font-weight: 400;
            text-shadow: 0 0 0;
            line-height: 1.44;
        }

        .fetl-user-online-status>div:hover {
            border-color: var(--theme-color);
        }

        .fetl-user-online-status>div.fetl-active {
            border: 2px solid var(--theme-color) !important;
        }

        .fetl-user-online-status>div.fetl-active:before {
            position: absolute;
            top: -1px;
            text-align: center;
            font-size: 0.725rem;
            content: "";
            font-family: 'remixicon' !important;
            width: 16px;
            height: 16px;
            line-height: 14px;
            opacity: 0.9;
            color: #fff;
            right: -1px;
            left: auto;
            font-weight: 600;
            background-color: var(--theme-color);
            border-radius: 0 var(--main-radius) 0 5px;
            transition: all 0.2s;
            z-index: 1;
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='12' height='12'%3E%3Cpath fill='white' d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: center;
            background-size: 10px;
        }

        .fetl-wd-k-all {
            word-break: keep-all;
        }

        .fetl-user-online-status>div span:first-child {
            width: 13px;
            height: 13px;
            border-radius: 100%;
            box-shadow: 0 0 0 2px #fff;
        }

        .fetl-user-online-status>div span:last-child {
            font-size: .95rem;
            color: var(--muted-color);
        }

        [data-sta="online"] span:first-child {
            background-color: #36c76c !important;
        }

        [data-sta="away"] span:first-child {
            background-color: #ffd648 !important;
        }

        [data-sta="busy"] span:first-child {
            background-color: #ff6692 !important;
        }

        [data-sta="offline"] span:first-child {
            background-color: #526b7a !important;
        }

        .fetl-avatar-status-box {
            position: relative;
            display: inline-block;
        }

        .fetl-online-status-dot {
            position: absolute;
            bottom: 1px;
            left: 1px;
            width: 7px;
            height: 7px;
            border-radius: 100%;
            box-shadow: 0 0 0 2px #fff;
            z-index: 2;
        }

        .fetl-status-online {
            background-color: #36c76c !important;
        }

        .fetl-status-away {
            background-color: #ffd648 !important;
        }

        .fetl-status-busy {
            background-color: #ff6692 !important;
        }

        .fetl-status-offline {
            background-color: #526b7a !important;
        }

        @media (max-width:991px) {
            .fetl-user-online-status>div {
                min-width: auto;
                min-height: auto;
            }
        }

        @media (max-width:768px) {
            .fetl-user-online-status {
                gap: 10px;
            }

            .fetl-user-online-status>div {
                min-width: 40px;
                min-height: 40px;
                padding: 4px;
            }
        }

        .zib-widget .fetl-user-online-status {
            width: 100%;
        }
    </style>
    <?php
}

将以下代码添加到 zibll/inc/functions/zib-header.php 文件中:

查找位置: 先搜索以下代码:

$con .= $payvip ? '<div class="mt10 em09" style="padding:2px;">' . $payvip . '</div>' : '';

添加位置: 将以下代码插入到上述代码的 上面

$current_status = xk_zhplugs_get_user_online_status();
$nonce = wp_create_nonce('xk_zhplugs_user_online_status');
$con .= '<div class="mt10 fetl-user-online-status" style="padding:2px;">';
$con .= '<div class="but ' . ($current_status == 'online' ? 'fetl-active' : '') . '" data-sta="online">';
$con .= '<span></span><span class="fetl-wd-k-all">在线</span>';
$con .= '</div>';
$con .= '<div class="but ' . ($current_status == 'away' ? 'fetl-active' : '') . '" data-sta="away">';
$con .= '<span></span><span class="fetl-wd-k-all">离开</span>';
$con .= '</div>';
$con .= '<div class="but ' . ($current_status == 'busy' ? 'fetl-active' : '') . '" data-sta="busy">';
$con .= '<span></span><span class="fetl-wd-k-all">忙碌</span>';
$con .= '</div>';
$con .= '<div class="but ' . ($current_status == 'offline' ? 'fetl-active' : '') . '" data-sta="offline">';
$con .= '<span></span><span class="fetl-wd-k-all">离线</span>';
$con .= '</div>';
$con .= '<input type="hidden" class="xk_zhplugs_user_online_status_nonce" value="' . esc_attr($nonce) . '">';
$con .= '</div>';

添加头像状态过滤器

将以下代码添加到 zibll/inc/functions/zib-theme.php 文件中:

查找位置: 先搜索以下代码:

add_action('user_save_custom_avatar', function ($user_id) {

添加位置: 在 return $avatar; 代码的 上面 添加以下代码:

$avatar = apply_filters('zib_get_data_avatar', $avatar, $user_id, $size, $alt);

 

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

免费领取 子比美化 子比美化-状态功能 https://www.mflq.com/255.html

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务