back to top

小记 给文章添加点赞统计功能(小工具版)

展示效果

代码(补充说明,添加代码到当前的主题的function底部,后台新建小工具调用就行了):

<?php
// WordPress文章点赞功能 - 修复今日点赞统计问题
class Post_Likes_Widget extends WP_Widget {
    
    public function __construct() {
        parent::__construct(
            'post_likes_widget',
            '文章点赞',
            array(
                'description' => '在侧边栏显示文章点赞功能',
                'classname'   => 'post-likes-widget',
            )
        );
        
        add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
        add_action('wp_ajax_post_like', array($this, 'handle_post_like'));
        add_action('wp_ajax_nopriv_post_like', array($this, 'handle_post_like'));
        
        // 初始化数据库表
        add_action('init', array($this, 'init_db_table'));
    }
    
    // 初始化数据库表
    public function init_db_table() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'post_likes';
        $charset_collate = $wpdb->get_charset_collate();
        
        // 检查表是否存在,不存在则创建
        if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
            $sql = "CREATE TABLE $table_name (
                id mediumint(9) NOT NULL AUTO_INCREMENT,
                post_id mediumint(9) NOT NULL,
                user_ip varchar(45) NOT NULL,
                user_id bigint(20) DEFAULT 0,
                like_date datetime DEFAULT CURRENT_TIMESTAMP,
                PRIMARY KEY (id),
                KEY post_id (post_id),
                KEY user_ip (user_ip),
                KEY user_id (user_id)
            ) $charset_collate;";
            
            require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
            dbDelta($sql);
        }
    }
    
    // 修复的AJAX处理函数 - 解决用户识别问题
    public function handle_post_like() {
        // 设置正确的header
        header('Content-Type: application/json');
        
        // 验证nonce
        if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'like_nonce')) {
            wp_send_json_error('安全验证失败');
            return;
        }
        
        // 验证post_id
        if (!isset($_POST['post_id']) || empty($_POST['post_id'])) {
            wp_send_json_error('文章ID不能为空');
            return;
        }
        
        $post_id = intval($_POST['post_id']);
        $user_ip = $this->get_user_ip();
        $user_id = is_user_logged_in() ? get_current_user_id() : 0;
        
        // 验证文章是否存在
        if (!get_post($post_id)) {
            wp_send_json_error('文章不存在');
            return;
        }
        
        global $wpdb;
        $table_name = $wpdb->prefix . 'post_likes';
        
        // ========== 修复核心逻辑 ==========
        // 检查是否已经点赞 - 区分登录用户和游客
        $already_liked = false;
        
        if ($user_id > 0) {
            // 登录用户:使用user_id检查
            $already_liked = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM $table_name WHERE post_id = %d AND user_id = %d",
                $post_id, $user_id
            ));
            
            // 清理可能存在的相同IP的游客记录(避免重复)
            $wpdb->delete(
                $table_name,
                array(
                    'post_id' => $post_id,
                    'user_ip' => $user_ip,
                    'user_id' => 0
                ),
                array('%d', '%s', '%d')
            );
        } else {
            // 游客:使用user_ip检查
            $already_liked = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM $table_name WHERE post_id = %d AND user_ip = %s AND user_id = 0",
                $post_id, $user_ip
            ));
            
            // 清理可能存在的相同IP的登录用户记录(避免重复)
            $wpdb->delete(
                $table_name,
                array(
                    'post_id' => $post_id,
                    'user_ip' => $user_ip,
                    'user_id' => array(0, '>')
                ),
                array('%d', '%s', '%d')
            );
        }
        
        $already_liked = intval($already_liked) > 0;
        
        if (!$already_liked) {
            // 添加点赞
            $result = $wpdb->insert(
                $table_name,
                array(
                    'post_id' => $post_id,
                    'user_ip' => $user_ip,
                    'user_id' => $user_id
                ),
                array('%d', '%s', '%d')
            );
            
            if ($result === false) {
                wp_send_json_error('点赞失败');
                return;
            }
            
            $liked = true;
            $message = '感谢您的点赞!';
        } else {
            // 取消点赞
            $where_params = array('post_id' => $post_id);
            $where_format = array('%d');
            
            if ($user_id > 0) {
                $where_params['user_id'] = $user_id;
                $where_format[] = '%d';
            } else {
                $where_params['user_ip'] = $user_ip;
                $where_params['user_id'] = 0;
                $where_format[] = '%s';
                $where_format[] = '%d';
            }
            
            $result = $wpdb->delete($table_name, $where_params, $where_format);
            
            if ($result === false) {
                wp_send_json_error('取消点赞失败');
                return;
            }
            
            $liked = false;
            $message = '您已取消点赞';
        }
        
        // 获取更新后的点赞数
        $like_count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $table_name WHERE post_id = %d",
            $post_id
        ));
        
        // 获取全站今日点赞数(修复:统计所有文章,不只是当前文章)
        $today_likes = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $table_name WHERE DATE(like_date) = %s",
            date('Y-m-d')
        ));
        
        // 获取全站总点赞数
        $total_likes = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
        
        // 返回成功响应
        wp_send_json_success(array(
            'liked' => $liked,
            'count' => $like_count,
            'message' => $message,
            'today_likes' => $today_likes,
            'total_likes' => $total_likes
        ));
    }
    
    // 获取用户IP(修复版)
    private function get_user_ip() {
        if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        } else {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        
        // 处理多个IP的情况(如经过代理)
        if (strpos($ip, ',') !== false) {
            $ips = explode(',', $ip);
            $ip = trim($ips[0]);
        }
        
        return sanitize_text_field($ip);
    }
    
    public function widget($args, $instance) {
        if (!is_single() && !is_page()) {
            return;
        }
        
        $title = apply_filters('widget_title', empty($instance['title']) ? '' : $instance['title'], $instance, $this->id_base);
        
        echo $args['before_widget'];
        
        if ($title) {
            echo $args['before_title'] . $title . $args['after_title'];
        }
        
        echo $this->get_likes_html();
        echo $args['after_widget'];
    }
    
    public function form($instance) {
        $instance = wp_parse_args((array) $instance, array(
            'title' => '喜欢这篇文章?',
        ));
        
        $title = sanitize_text_field($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 = $old_instance;
        $instance['title'] = sanitize_text_field($new_instance['title']);
        return $instance;
    }
    
    private function get_likes_html() {
        $post_id = get_the_ID();
        $like_count = $this->get_post_likes_count($post_id);
        $already_liked = $this->has_user_liked_post($post_id);
        $today_likes = $this->get_today_likes_count(); // 修复:获取全站今日点赞
        $total_likes = $this->get_total_likes_count();
        
        $liked_class = $already_liked ? 'liked' : '';
        $current_button_text = $already_liked ? '已点赞' : '点赞';
        $heart_icon = $already_liked ? 'fas' : 'far';
        
        // 创建nonce
        $nonce = wp_create_nonce('like_nonce');
        
        ob_start();
        ?>
        <div class="post-likes-widget-content">
            <button class="like-widget-button <?php echo $liked_class; ?>" 
                    id="likeWidgetButton-<?php echo $post_id; ?>" 
                    data-post-id="<?php echo $post_id; ?>"
                    data-nonce="<?php echo $nonce; ?>">
                <i class="like-widget-icon <?php echo $heart_icon; ?> fa-heart"></i>
                <span class="like-widget-text"><?php echo $current_button_text; ?></span>
                <span class="like-widget-count"><?php echo $like_count; ?></span>
            </button>
            
            <div class="like-widget-message" id="likeWidgetMessage-<?php echo $post_id; ?>">
                已有<?php echo $like_count; ?>位读者点赞
            </div>
            
            <div class="like-widget-stats">
                <div class="like-widget-stat">
                    <span class="stat-value" id="todayLikes-<?php echo $post_id; ?>"><?php echo $today_likes; ?></span>
                    <span class="stat-label">今日点赞</span>
                </div>
                <div class="like-widget-stat">
                    <span class="stat-value" id="totalLikes-<?php echo $post_id; ?>"><?php echo $total_likes; ?></span>
                    <span class="stat-label">总点赞数</span>
                </div>
                <div class="like-widget-stat">
                    <span class="stat-value" id="articleLikes-<?php echo $post_id; ?>"><?php echo $like_count; ?></span>
                    <span class="stat-label">本文点赞</span>
                </div>
            </div>
        </div>
        
        <script>
        jQuery(document).ready(function($) {
            $('#likeWidgetButton-<?php echo $post_id; ?>').click(function() {
                var button = $(this);
                var post_id = button.data('post-id');
                var nonce = button.data('nonce');
                var messageEl = $('#likeWidgetMessage-<?php echo $post_id; ?>');
                
                // 禁用按钮防止重复点击
                button.prop('disabled', true).addClass('like-widget-loading');
                
                $.ajax({
                    url: '<?php echo admin_url('admin-ajax.php'); ?>',
                    type: 'POST',
                    data: {
                        action: 'post_like',
                        post_id: post_id,
                        security: nonce
                    },
                    success: function(response) {
                        if (response.success) {
                            var likeCountEl = button.find('.like-widget-count');
                            var likeTextEl = button.find('.like-widget-text');
                            var iconEl = button.find('.like-widget-icon');
                            
                            // 更新点赞数
                            likeCountEl.text(response.data.count);
                            
                            // 更新统计数据
                            $('#articleLikes-<?php echo $post_id; ?>').text(response.data.count);
                            $('#todayLikes-<?php echo $post_id; ?>').text(response.data.today_likes);
                            $('#totalLikes-<?php echo $post_id; ?>').text(response.data.total_likes);
                            
                            if (response.data.liked) {
                                button.addClass('liked');
                                likeTextEl.text('已点赞');
                                iconEl.removeClass('far').addClass('fas');
                            } else {
                                button.removeClass('liked');
                                likeTextEl.text('点赞');
                                iconEl.removeClass('fas').addClass('far');
                            }
                            
                            // 显示成功消息
                            messageEl.removeClass('error').addClass('success').text(response.data.message);
                            setTimeout(function() {
                                messageEl.removeClass('success').text('已有' + response.data.count + '位读者点赞');
                            }, 2000);
                            
                        } else {
                            messageEl.removeClass('success').addClass('error').text(response.data);
                        }
                    },
                    error: function(xhr, status, error) {
                        console.error('AJAX Error:', status, error);
                        messageEl.removeClass('success').addClass('error').text('网络错误,请重试');
                    },
                    complete: function() {
                        button.prop('disabled', false).removeClass('like-widget-loading');
                    }
                });
            });
        });
        </script>
        <?php
        return ob_get_clean();
    }
    
    private function get_post_likes_count($post_id) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'post_likes';
        $like_count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $table_name WHERE post_id = %d",
            $post_id
        ));
        
        return $like_count ? $like_count : 0;
    }
    
    // 修复:获取全站今日点赞数,不是当前文章的今日点赞
    private function get_today_likes_count() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'post_likes';
        $today = date('Y-m-d');
        
        $like_count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $table_name WHERE DATE(like_date) = %s",
            $today
        ));
        
        return $like_count ? $like_count : 0;
    }
    
    private function get_total_likes_count() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'post_likes';
        $total_likes = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
        
        return $total_likes ? $total_likes : 0;
    }
    
    private function has_user_liked_post($post_id) {
        $user_ip = $this->get_user_ip();
        $user_id = is_user_logged_in() ? get_current_user_id() : 0;
        
        global $wpdb;
        $table_name = $wpdb->prefix . 'post_likes';
        
        if ($user_id > 0) {
            // 登录用户:使用user_id检查
            $already_liked = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM $table_name WHERE post_id = %d AND user_id = %d",
                $post_id, $user_id
            ));
        } else {
            // 游客:使用user_ip检查
            $already_liked = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM $table_name WHERE post_id = %d AND user_ip = %s AND user_id = 0",
                $post_id, $user_ip
            ));
        }
        
        return $already_liked > 0;
    }
    
    public function enqueue_scripts() {
        if (is_active_widget(false, false, $this->id_base, true) && (is_single() || is_page())) {
            wp_enqueue_script('jquery');
            wp_enqueue_style('font-awesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css', array(), '6.4.0');
            
            // 添加内联样式
            wp_add_inline_style('font-awesome', $this->get_widget_css());
        }
    }
    
    private function get_widget_css() {
        return "
        .post-likes-widget .post-likes-widget-content {
            text-align: center;
            padding: 15px 0;
        }
        
        .like-widget-button {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            background: #fff;
            border: 2px solid #e74c3c;
            border-radius: 50px;
            padding: 10px 20px;
            font-size: 1rem;
            font-weight: 600;
            color: #e74c3c;
            cursor: pointer;
            transition: all 0.3s ease;
            box-shadow: 0 2px 5px rgba(231, 76, 60, 0.1);
            width: 100%;
            max-width: 200px;
            margin: 0 auto 10px;
        }
        
        .like-widget-button:hover {
            background: #e74c3c;
            color: white;
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(231, 76, 60, 0.2);
        }
        
        .like-widget-button.liked {
            background: #e74c3c;
            color: white;
            border-color: #c0392b;
        }
        
        .like-widget-button:disabled {
            opacity: 0.7;
            cursor: not-allowed;
            transform: none;
        }
        
        .like-widget-button:active {
            transform: translateY(0);
        }
        
        .like-widget-icon {
            margin-right: 6px;
            font-size: 1.1rem;
            width: 16px;
            text-align: center;
        }
        
        .like-widget-count {
            font-size: 1.2rem;
            font-weight: bold;
            margin-left: 5px;
            min-width: 25px;
            text-align: center;
        }
        
        .like-widget-message {
            font-size: 0.8rem;
            color: #7f8c8d;
            margin-bottom: 10px;
            min-height: 18px;
        }
        
        .like-widget-stats {
            display: flex;
            justify-content: space-around;
            border-top: 1px solid #eee;
            padding-top: 10px;
        }
        
        .like-widget-stat {
            text-align: center;
            flex: 1;
        }
        
        .like-widget-stat .stat-value {
            display: block;
            font-size: 1.1rem;
            font-weight: bold;
            color: #2c3e50;
        }
        
        .like-widget-stat .stat-label {
            display: block;
            font-size: 0.7rem;
            color: #7f8c8d;
            margin-top: 2px;
        }
        
        .like-widget-loading {
            opacity: 0.7;
        }
        
        .like-widget-message.success {
            color: #27ae60;
        }
        
        .like-widget-message.error {
            color: #e74c3c;
        }
        
        @media (max-width: 782px) {
            .like-widget-button {
                padding: 8px 16px;
                font-size: 0.9rem;
            }
            
            .like-widget-stats {
                flex-direction: column;
                gap: 8px;
            }
        }
        ";
    }
}

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

// 清理重复点赞数据(可选,用于修复现有数据)
function cleanup_duplicate_likes() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'post_likes';
    
    // 清理同一IP的重复记录(保留user_id不为0的记录)
    $wpdb->query("
        DELETE t1 FROM $table_name t1
        INNER JOIN $table_name t2 
        WHERE 
            t1.id < t2.id AND 
            t1.post_id = t2.post_id AND 
            t1.user_ip = t2.user_ip AND
            t1.user_id = 0 AND
            t2.user_id > 0
    ");
    
    // 清理同一用户的重复记录
    $wpdb->query("
        DELETE t1 FROM $table_name t1
        INNER JOIN $table_name t2 
        WHERE 
            t1.id < t2.id AND 
            t1.post_id = t2.post_id AND 
            t1.user_id = t2.user_id AND
            t1.user_id > 0
    ");
}

// 可选:在插件激活时运行一次数据清理
// add_action('init', 'cleanup_duplicate_likes');
?>

4 Comments

回复 qingzhao Cancel reply

Please enter your comment!
Please enter your name here