如何用Node Express+Sequelize PostgreSQL实现双向用户拉黑功能
实现用户拉黑功能(类似Instagram/Facebook)
1. 新增拉黑关系表
首先在PostgreSQL中创建存储拉黑关系的blocked_users表,用来记录拉黑与被拉黑的关联:
CREATE TABLE blocked_users ( blocker_id INTEGER REFERENCES user(user_id) ON DELETE CASCADE, blocked_id INTEGER REFERENCES user(user_id) ON DELETE CASCADE, PRIMARY KEY (blocker_id, blocked_id), CHECK (blocker_id != blocked_id) -- 禁止用户拉黑自己 );
这个表用blocker_id(拉黑者ID)和blocked_id(被拉黑者ID)做联合主键,避免重复存储同一组拉黑关系;外键设置ON DELETE CASCADE确保用户删除后,关联的拉黑记录会自动清除;CHECK约束直接防止用户拉黑自己。
2. 定义Sequelize模型
在Sequelize项目中创建BlockedUser模型,并与User模型建立自关联:
BlockedUser模型(blocked-user.model.js)
const { Model, DataTypes } = require('sequelize'); const sequelize = require('../config/database'); class BlockedUser extends Model {} BlockedUser.init({ blocker_id: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'user', key: 'user_id' } }, blocked_id: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'user', key: 'user_id' } } }, { sequelize, modelName: 'BlockedUser', tableName: 'blocked_users', timestamps: false, // 无需 createdAt/updatedAt 字段 indexes: [ { unique: true, fields: ['blocker_id', 'blocked_id'] } ] }); module.exports = BlockedUser;
关联User模型
在User模型中添加双向关联,方便后续查询操作:
const User = require('./user.model'); const BlockedUser = require('./blocked-user.model'); // 关联当前用户拉黑的人 User.hasMany(BlockedUser, { foreignKey: 'blocker_id', as: 'blockedUsers' }); // 关联拉黑当前用户的人 User.hasMany(BlockedUser, { foreignKey: 'blocked_id', as: 'blockedByUsers' });
3. 实现拉黑/取消拉黑API接口
假设你已经通过认证中间件(比如JWT)能获取当前用户ID,以下是核心接口实现:
拉黑用户接口
// POST /api/users/block app.post('/api/users/block', async (req, res) => { const { blockedId } = req.body; const currentUserId = req.user.id; // 从认证中间件拿当前用户ID // 基础校验 if (currentUserId === blockedId) { return res.status(400).json({ message: '不能拉黑自己' }); } // 检查目标用户是否存在 const targetUser = await User.findByPk(blockedId); if (!targetUser) { return res.status(404).json({ message: '目标用户不存在' }); } // 检查是否已拉黑该用户 const existingBlock = await BlockedUser.findOne({ where: { blocker_id: currentUserId, blocked_id: blockedId } }); if (existingBlock) { return res.status(400).json({ message: '已拉黑该用户' }); } // 创建拉黑记录 await BlockedUser.create({ blocker_id: currentUserId, blocked_id: blockedId }); res.status(200).json({ message: '拉黑成功' }); });
取消拉黑接口
// DELETE /api/users/unblock app.delete('/api/users/unblock', async (req, res) => { const { blockedId } = req.body; const currentUserId = req.user.id; const deletedCount = await BlockedUser.destroy({ where: { blocker_id: currentUserId, blocked_id: blockedId } }); if (deletedCount === 0) { return res.status(400).json({ message: '未拉黑该用户,无需取消' }); } res.status(200).json({ message: '取消拉黑成功' }); });
4. 修改用户查询API(排除拉黑关系)
修改原有的“查询所有用户”接口,排除三类用户:当前用户自己、当前用户拉黑的人、拉黑当前用户的人:
const { Op } = require('sequelize'); // GET /api/users app.get('/api/users', async (req, res) => { const currentUserId = req.user.id; // 获取当前用户拉黑的所有用户ID const blockedIds = await BlockedUser.findAll({ attributes: ['blocked_id'], where: { blocker_id: currentUserId } }).then(rows => rows.map(row => row.blocked_id)); // 获取所有拉黑当前用户的用户ID const blockingIds = await BlockedUser.findAll({ attributes: ['blocker_id'], where: { blocked_id: currentUserId } }).then(rows => rows.map(row => row.blocker_id)); // 合并需要排除的ID列表(去重+排除自己) const excludedIds = [...new Set([...blockedIds, ...blockingIds, currentUserId])]; // 查询符合条件的用户 const users = await User.findAll({ where: { user_id: { [Op.notIn]: excludedIds } }, attributes: ['user_id', 'name', 'email', 'is_active'] // 返回指定字段 }); res.status(200).json(users); });
5. 验证测试场景
- 用户A(user_id=1)拉黑用户B(user_id=2)后:
- A查询用户列表:仅能看到C(user_id=3)
- B查询用户列表:仅能看到C(user_id=3)
- C查询用户列表:能看到A和B
- A取消拉黑B后:三方互相可见
内容的提问来源于stack exchange,提问作者sumanth.js
相关产品推荐
相关产品推荐

