本文目录导读:

我来为你详细讲解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);
}
}
注意事项
- 幂等性处理:确保重复点击不会产生重复关注记录
- 事务处理:关注操作涉及多表更新,使用事务保证一致性
- 缓存策略:热门用户的粉丝数可缓存,减少数据库压力
- 异步处理:通知发送可使用消息队列异步处理
- 防刷机制:限制单个用户每天关注数量
这个实现方案涵盖了关注功能的核心逻辑,你可以根据具体需求进行调整和扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播