PHP代码社区动态怎么关注

wen PHP项目 23

本文目录导读:

PHP代码社区动态怎么关注

  1. 数据库设计
  2. 核心功能实现
  3. 动态流实现
  4. 前端交互实现
  5. API接口示例
  6. 优化建议

我来为你详细讲解PHP社区动态功能中的关注/粉丝系统实现方案。

数据库设计

关注关系表

CREATE TABLE `user_follows` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `follower_id` int(11) NOT NULL COMMENT '关注者ID',
    `following_id` int(11) NOT NULL COMMENT '被关注者ID',
    `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `status` tinyint(1) DEFAULT 1 COMMENT '1:关注 0:取消关注',
    PRIMARY KEY (`id`),
    UNIQUE KEY `unique_follow` (`follower_id`, `following_id`),
    KEY `idx_follower` (`follower_id`),
    KEY `idx_following` (`following_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

用户表增加统计字段

ALTER TABLE `users` 
ADD COLUMN `followers_count` int(11) DEFAULT 0 COMMENT '粉丝数',
ADD COLUMN `following_count` int(11) DEFAULT 0 COMMENT '关注数';

核心功能实现

关注/取关操作类

<?php
class FollowService {
    private $db;
    public function __construct($db) {
        $this->db = $db;
    }
    /**
     * 关注用户
     * @param int $followerId 当前用户ID
     * @param int $followingId 被关注用户ID
     * @return array
     */
    public function follow($followerId, $followingId) {
        // 不能关注自己
        if ($followerId == $followingId) {
            return ['code' => 0, 'msg' => '不能关注自己'];
        }
        try {
            $this->db->beginTransaction();
            // 检查是否已关注
            $follow = $this->checkFollow($followerId, $followingId);
            if ($follow) {
                if ($follow['status'] == 1) {
                    return ['code' => 0, 'msg' => '已关注该用户'];
                }
                // 重新关注(恢复)
                $sql = "UPDATE user_follows SET status = 1, created_at = NOW() 
                        WHERE follower_id = ? AND following_id = ?";
                $stmt = $this->db->prepare($sql);
                $stmt->execute([$followerId, $followingId]);
            } else {
                // 首次关注
                $sql = "INSERT INTO user_follows (follower_id, following_id) VALUES (?, ?)";
                $stmt = $this->db->prepare($sql);
                $stmt->execute([$followerId, $followingId]);
            }
            // 更新计数
            $this->updateFollowCount($followerId, $followingId, 'increment');
            $this->db->commit();
            // 发送通知
            $this->sendFollowNotification($followerId, $followingId);
            return ['code' => 1, 'msg' => '关注成功'];
        } catch (Exception $e) {
            $this->db->rollBack();
            return ['code' => 0, 'msg' => '操作失败: ' . $e->getMessage()];
        }
    }
    /**
     * 取消关注
     */
    public function unfollow($followerId, $followingId) {
        try {
            $this->db->beginTransaction();
            $sql = "UPDATE user_follows SET status = 0 WHERE follower_id = ? AND following_id = ?";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([$followerId, $followingId]);
            // 更新计数
            $this->updateFollowCount($followerId, $followingId, 'decrement');
            $this->db->commit();
            return ['code' => 1, 'msg' => '已取消关注'];
        } catch (Exception $e) {
            $this->db->rollBack();
            return ['code' => 0, 'msg' => '操作失败'];
        }
    }
    /**
     * 检查关注关系
     */
    public function checkFollow($followerId, $followingId) {
        $sql = "SELECT * FROM user_follows WHERE follower_id = ? AND following_id = ?";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$followerId, $followingId]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    /**
     * 获取关注状态
     */
    public function getFollowStatus($followerId, $followingId) {
        $follow = $this->checkFollow($followerId, $followingId);
        return $follow && $follow['status'] == 1;
    }
    /**
     * 更新关注计数
     */
    private function updateFollowCount($followerId, $followingId, $action) {
        $operator = ($action == 'increment') ? '+' : '-';
        // 更新关注者的关注数
        $sql = "UPDATE users SET following_count = following_count {$operator} 1 WHERE id = ?";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$followerId]);
        // 更新被关注者的粉丝数
        $sql = "UPDATE users SET followers_count = followers_count {$operator} 1 WHERE id = ?";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$followingId]);
    }
    /**
     * 发送关注通知
     */
    private function sendFollowNotification($followerId, $followingId) {
        // 获取关注者信息
        $follower = $this->getUserInfo($followerId);
        // 创建通知
        $notificationData = [
            'user_id' => $followingId,
            'type' => 'follow',
            'content' => "用户 {$follower['username']} 关注了你",
            'from_user_id' => $followerId,
            'created_at' => date('Y-m-d H:i:s')
        ];
        // 插入通知表
        $this->createNotification($notificationData);
    }
}

动态流实现

获取关注者动态

<?php
class FeedService {
    private $db;
    /**
     * 获取关注用户的动态(时间线)
     */
    public function getFollowFeed($userId, $page = 1, $limit = 20) {
        $offset = ($page - 1) * $limit;
        // 获取关注的活跃用户ID列表
        $followingIds = $this->getFollowingIds($userId);
        if (empty($followingIds)) {
            return ['list' => [], 'total' => 0];
        }
        $placeholders = implode(',', array_fill(0, count($followingIds), '?'));
        // 查询动态(帖子、评论、点赞等)
        $sql = "SELECT 
                    p.*,
                    u.username,
                    u.avatar,
                    u.id as user_id
                FROM posts p
                INNER JOIN users u ON p.user_id = u.id
                WHERE p.user_id IN ({$placeholders})
                    AND p.status = 1
                ORDER BY p.created_at DESC
                LIMIT ? OFFSET ?";
        $stmt = $this->db->prepare($sql);
        $params = array_merge($followingIds, [$limit, $offset]);
        $stmt->execute($params);
        $posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
        // 统计总数
        $countSql = "SELECT COUNT(*) FROM posts WHERE user_id IN ({$placeholders}) AND status = 1";
        $countStmt = $this->db->prepare($countSql);
        $countStmt->execute($followingIds);
        $total = $countStmt->fetchColumn();
        return [
            'list' => $posts,
            'total' => $total,
            'page' => $page,
            'limit' => $limit
        ];
    }
    /**
     * 获取关注的用户列表
     */
    private function getFollowingIds($userId) {
        $sql = "SELECT following_id FROM user_follows 
                WHERE follower_id = ? AND status = 1 
                ORDER BY created_at DESC";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$userId]);
        return $stmt->fetchAll(PDO::FETCH_COLUMN);
    }
}

前端交互实现

HTML模板

<!-- 关注按钮 -->
<div class="follow-btn-container">
    <button class="btn btn-follow" 
            data-user-id="<?= $targetUserId ?>"
            data-is-following="<?= $isFollowing ?>"
            onclick="toggleFollow(this)">
        <?php if ($isFollowing): ?>
            已关注
        <?php else: ?>
            + 关注
        <?php endif; ?>
    </button>
</div>
<!-- 动态流 -->
<div id="feed-container">
    <!-- 动态列表 -->
</div>

JavaScript实现

class FollowManager {
    constructor() {
        this.ajaxUrl = '/api/follow.php';
    }
    /**
     * 切换关注状态
     */
    toggleFollow(btn) {
        const userId = btn.dataset.userId;
        const isFollowing = btn.dataset.isFollowing === 'true';
        // 禁用按钮防止重复点击
        btn.disabled = true;
        const action = isFollowing ? 'unfollow' : 'follow';
        fetch(this.ajaxUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-Token': this.getCsrfToken()
            },
            body: JSON.stringify({
                action: action,
                target_user_id: userId
            })
        })
        .then(response => response.json())
        .then(data => {
            if (data.code === 1) {
                // 更新UI
                if (action === 'follow') {
                    btn.dataset.isFollowing = 'true';
                    btn.textContent = '已关注';
                    btn.classList.add('following');
                } else {
                    btn.dataset.isFollowing = 'false';
                    btn.textContent = '+ 关注';
                    btn.classList.remove('following');
                }
                // 更新粉丝数
                this.updateFollowerCount(userId, data.followers_count);
                // 刷新动态流
                if (data.feed_update) {
                    this.refreshFeed();
                }
            } else {
                alert(data.msg);
            }
        })
        .catch(error => {
            console.error('Error:', error);
            alert('操作失败,请重试');
        })
        .finally(() => {
            btn.disabled = false;
        });
    }
    /**
     * 加载更多动态
     */
    loadMoreFeed(page) {
        const feedContainer = document.getElementById('feed-container');
        fetch(`/api/feed.php?page=${page}&limit=20`, {
            headers: {
                'X-Requested-With': 'XMLHttpRequest'
            }
        })
        .then(response => response.json())
        .then(data => {
            if (data.list.length > 0) {
                // 追加动态到页面
                data.list.forEach(item => {
                    feedContainer.appendChild(this.createFeedItem(item));
                });
            } else {
                // 没有更多动态
                document.querySelector('.load-more').textContent = '没有更多了';
            }
        });
    }
    /**
     * 创建动态元素
     */
    createFeedItem(item) {
        const div = document.createElement('div');
        div.className = 'feed-item';
        div.innerHTML = `
            <div class="feed-header">
                <img src="${item.avatar}" alt="avatar" class="avatar">
                <span class="username">${item.username}</span>
                <span class="time">${this.formatTime(item.created_at)}</span>
            </div>
            <div class="feed-content">
                ${item.content}
            </div>
            <div class="feed-actions">
                <button onclick="likePost(${item.id})">赞 (${item.likes})</button>
                <button onclick="commentPost(${item.id})">评论 (${item.comments})</button>
            </div>
        `;
        return div;
    }
    /**
     * 时间格式化
     */
    formatTime(time) {
        const diff = Date.now() - new Date(time).getTime();
        const minutes = Math.floor(diff / 60000);
        if (minutes < 1) return '刚刚';
        if (minutes < 60) return `${minutes}分钟前`;
        const hours = Math.floor(minutes / 60);
        if (hours < 24) return `${hours}小时前`;
        const days = Math.floor(hours / 24);
        if (days < 7) return `${days}天前`;
        return time;
    }
}
// 初始化
const followManager = new FollowManager();

API接口示例

follow.php

<?php
header('Content-Type: application/json');
// 验证登录
session_start();
if (!isset($_SESSION['user_id'])) {
    die(json_encode(['code' => 0, 'msg' => '请先登录']));
}
$input = json_decode(file_get_contents('php://input'), true);
$action = $input['action'] ?? '';
$targetUserId = $input['target_user_id'] ?? 0;
$followService = new FollowService($db);
$currentUserId = $_SESSION['user_id'];
switch ($action) {
    case 'follow':
        $result = $followService->follow($currentUserId, $targetUserId);
        break;
    case 'unfollow':
        $result = $followService->unfollow($currentUserId, $targetUserId);
        break;
    default:
        $result = ['code' => 0, 'msg' => '无效操作'];
}
echo json_encode($result);

优化建议

性能优化

// 使用Redis缓存关注关系
class FollowCache {
    private $redis;
    public function isFollowing($followerId, $followingId) {
        $key = "user:{$followerId}:following";
        return $this->redis->sIsMember($key, $followingId);
    }
    public function getFollowingIds($userId) {
        $key = "user:{$userId}:following";
        return $this->redis->sMembers($key);
    }
}

注意事项

  1. 幂等性处理:确保重复点击不会产生重复关注记录
  2. 事务处理:关注操作涉及多表更新,使用事务保证一致性
  3. 缓存策略:热门用户的粉丝数可缓存,减少数据库压力
  4. 异步处理:通知发送可使用消息队列异步处理
  5. 防刷机制:限制单个用户每天关注数量

这个实现方案涵盖了关注功能的核心逻辑,你可以根据具体需求进行调整和扩展。

抱歉,评论功能暂时关闭!