本文目录导读:

我来为你设计一个完整的PHP拉黑功能数据库方案,包含多种实现场景。
基础表结构设计
用户表(已有)
CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `nickname` varchar(50) DEFAULT NULL, `status` tinyint(1) DEFAULT '1' COMMENT '1正常 0禁用', `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
拉黑记录表(核心表)
CREATE TABLE `user_blocks` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '拉黑操作发起人ID', `blocked_user_id` int(11) NOT NULL COMMENT '被拉黑用户ID', `block_type` tinyint(1) DEFAULT '1' COMMENT '拉黑类型:1临时 2永久', `reason` varchar(255) DEFAULT NULL COMMENT '拉黑原因', `block_status` tinyint(1) DEFAULT '1' COMMENT '状态:1生效 0解除', `blocked_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '拉黑时间', `expired_at` timestamp NULL DEFAULT NULL COMMENT '过期时间(临时拉黑时使用)', `unblocked_at` timestamp NULL DEFAULT NULL COMMENT '解除拉黑时间', `unblock_reason` varchar(255) DEFAULT NULL COMMENT '解除原因', `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_user_blocked` (`user_id`, `blocked_user_id`, `block_status`), KEY `idx_blocked_user` (`blocked_user_id`), KEY `idx_user_id` (`user_id`), KEY `idx_expired_at` (`expired_at`), CONSTRAINT `fk_user_blocker` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`), CONSTRAINT `fk_user_blocked` FOREIGN KEY (`blocked_user_id`) REFERENCES `users`(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户拉黑记录表';
功能扩展设计
拉黑日志表(可选,用于审计)
CREATE TABLE `block_logs` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `block_id` bigint(20) NOT NULL COMMENT '关联拉黑记录ID', `user_id` int(11) NOT NULL COMMENT '操作人ID', `action` varchar(20) NOT NULL COMMENT '操作:block/unblock', `old_status` tinyint(1) DEFAULT NULL, `new_status` tinyint(1) DEFAULT NULL, `remark` varchar(255) DEFAULT NULL, `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_block_id` (`block_id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
批量拉黑表(管理后台用)
CREATE TABLE `batch_blocks` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `admin_id` int(11) NOT NULL COMMENT '管理员ID', `block_reason` varchar(255) DEFAULT NULL, `block_type` tinyint(1) DEFAULT '2' COMMENT '默认永久', `block_count` int(11) DEFAULT '0' COMMENT '拉黑数量', `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
PHP核心代码示例
拉黑类核心方法
<?php
class UserBlock {
private $pdo;
public function __construct($pdo) {
$this->pdo = $pdo;
}
/**
* 执行拉黑操作
*/
public function blockUser($userId, $blockedUserId, $type = 1, $reason = '', $expiredAt = null) {
// 参数校验
if ($userId == $blockedUserId) {
throw new Exception('不能拉黑自己');
}
// 检查是否已拉黑
if ($this->isBlocked($userId, $blockedUserId)) {
throw new Exception('该用户已被拉黑');
}
$sql = "INSERT INTO user_blocks (user_id, blocked_user_id, block_type, reason, expired_at)
VALUES (?, ?, ?, ?, ?)";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$userId, $blockedUserId, $type, $reason, $expiredAt]);
// 记录日志
$this->logAction(
$this->pdo->lastInsertId(),
$userId,
'block',
0,
1,
$reason
);
return true;
} catch (PDOException $e) {
throw new Exception('拉黑失败:' . $e->getMessage());
}
}
/**
* 解除拉黑
*/
public function unblockUser($userId, $blockedUserId, $unblockReason = '') {
$sql = "UPDATE user_blocks
SET block_status = 0, unblocked_at = NOW(), unblock_reason = ?
WHERE user_id = ? AND blocked_user_id = ? AND block_status = 1";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$unblockReason, $userId, $blockedUserId]);
if ($stmt->rowCount() > 0) {
// 记录日志
$blockId = $this->getBlockId($userId, $blockedUserId);
$this->logAction($blockId, $userId, 'unblock', 1, 0, $unblockReason);
return true;
}
return false;
} catch (PDOException $e) {
throw new Exception('解除拉黑失败:' . $e->getMessage());
}
}
/**
* 检查是否已拉黑(含过期判断)
*/
public function isBlocked($userId, $blockedUserId) {
$sql = "SELECT id FROM user_blocks
WHERE user_id = ? AND blocked_user_id = ?
AND block_status = 1
AND (expired_at IS NULL OR expired_at > NOW())
LIMIT 1";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$userId, $blockedUserId]);
return $stmt->fetchColumn() !== false;
}
/**
* 获取用户的拉黑列表
*/
public function getBlockedUsers($userId, $limit = 20, $offset = 0) {
$sql = "SELECT b.id, u.id as user_id, u.nickname, u.username,
b.reason, b.block_type, b.blocked_at, b.expired_at
FROM user_blocks b
INNER JOIN users u ON b.blocked_user_id = u.id
WHERE b.user_id = ? AND b.block_status = 1
AND (b.expired_at IS NULL OR b.expired_at > NOW())
ORDER BY b.blocked_at DESC
LIMIT {$limit} OFFSET {$offset}";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$userId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 检查是否需要清理过期拉黑
*/
public function cleanExpiredBlocks() {
$sql = "UPDATE user_blocks
SET block_status = 0, unblocked_at = NOW(),
unblock_reason = '系统自动解除过期拉黑'
WHERE block_status = 1
AND expired_at IS NOT NULL
AND expired_at <= NOW()";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute();
}
/**
* 记录操作日志
*/
private function logAction($blockId, $userId, $action, $oldStatus, $newStatus, $remark = '') {
$sql = "INSERT INTO block_logs (block_id, user_id, action, old_status, new_status, remark)
VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([$blockId, $userId, $action, $oldStatus, $newStatus, $remark]);
}
/**
* 获取拉黑记录ID
*/
private function getBlockId($userId, $blockedUserId) {
$sql = "SELECT id FROM user_blocks
WHERE user_id = ? AND blocked_user_id = ?
ORDER BY id DESC LIMIT 1";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$userId, $blockedUserId]);
return $stmt->fetchColumn();
}
}
使用示例
// 数据库连接
$pdo = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$block = new UserBlock($pdo);
// 拉黑用户(永久)
try {
$block->blockUser(1, 2, 2, '骚扰用户');
echo "拉黑成功";
} catch (Exception $e) {
echo "错误:" . $e->getMessage();
}
// 临时拉黑7天
try {
$expiredAt = date('Y-m-d H:i:s', strtotime('+7 days'));
$block->blockUser(1, 3, 1, '临时警告', $expiredAt);
echo "临时拉黑成功";
} catch (Exception $e) {
echo "错误:" . $e->getMessage();
}
// 检查是否被拉黑
if ($block->isBlocked(1, 2)) {
echo "该用户已被拉黑";
} else {
echo "可以正常交互";
}
// 拉黑列表
$list = $block->getBlockedUsers(1, 10, 0);
print_r($list);
性能优化建议
添加redis缓存(可选)
// Redis缓存检查是否被拉黑
class BlockCheckWithCache {
private $redis;
private $pdo;
public function checkIsBlocked($userId, $targetUserId) {
// 先从Redis缓存检查
$cacheKey = "block:{$userId}:{$targetUserId}";
$cached = $this->redis->get($cacheKey);
if ($cached !== false) {
return $cached == '1';
}
// 缓存未命中,查询数据库
$result = $this->isBlockedInDb($userId, $targetUserId);
// 写入缓存(10分钟过期)
$this->redis->setex($cacheKey, 600, $result ? '1' : '0');
return $result;
}
}
注意事项
- 索引优化:确保user_id和blocked_user_id上有合适的联合索引
- 定期清理:建议设置定时任务清理过期的临时拉黑记录
- 数据一致性:拉黑/解除操作要确保事务性
- 敏感操作:拉黑操作建议记录日志,方便追溯
- 性能考虑:高频的拉黑查询可以考虑使用缓存
这个设计可以满足大多数场景的拉黑功能需求,你可以根据实际业务进行调整,需要扩展功能(如多级拉黑、分组拉黑等)可以在此基础上进行修改。