You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用MongoDB(含Mongoose)和Express实现类Instagram好友请求系统?

如何用MongoDB(Mongoose)和Express实现类似Instagram的私有用户关注请求系统

我正在开发一款社交应用,需要实现类似Instagram的好友请求系统,需求如下:

  • 用户A可关注用户B,B无需回关A
  • 若用户B为私有用户(isPrivate: true),B会收到A的关注请求,可选择同意或拒绝;同意后A将成为B的粉丝

目前我已有基础的User、UserFollower和UserFollowing Schema,支持公开用户的关注/取消关注,但缺少私有用户的请求授权逻辑。现有代码如下:

const userSchema = new Schema({
  email: String,
  // isPrivate: Boolean,
});

const userFollowerSchema = new Schema({
  userId: Schema.Types.ObjectId,
  followerIds: [Schema.Types.ObjectId],
});

const userFollowingSchema = new Schema({
  userId: Schema.Types.ObjectId,
  followingIds: [Schema.Types.ObjectId],
});

我愿意完全重构Schema以支持拉黑等扩展功能,请问该如何实现?


一、Schema重构

为了支持私有用户请求、状态管理和扩展拉黑功能,调整现有Schema并新增FollowRequest集合处理请求状态:

1. User Schema(新增私有状态与拉黑列表)

const userSchema = new Schema({
  email: String,
  isPrivate: { type: Boolean, default: false }, // 标记是否为私有用户
  blockedUsers: [{ type: Schema.Types.ObjectId, ref: 'User' }] // 拉黑用户ID列表
});

const User = mongoose.model('User', userSchema);

2. FollowRequest Schema(管理关注请求状态)

const followRequestSchema = new Schema({
  requester: { type: Schema.Types.ObjectId, ref: 'User', required: true }, // 请求发起者ID
  recipient: { type: Schema.Types.ObjectId, ref: 'User', required: true }, // 请求接收者ID
  status: { 
    type: String, 
    enum: ['pending', 'accepted', 'rejected'], 
    default: 'pending' 
  }, // 请求状态
  createdAt: { type: Date, default: Date.now }
});

// 唯一索引,避免重复发送同一请求
followRequestSchema.index({ requester: 1, recipient: 1 }, { unique: true });

const FollowRequest = mongoose.model('FollowRequest', followRequestSchema);

3. 粉丝/关注列表Schema(保留独立集合以支持大规模数据)

// 用户的粉丝集合
const userFollowerSchema = new Schema({
  userId: { type: Schema.Types.ObjectId, ref: 'User', unique: true },
  followerIds: [{ type: Schema.Types.ObjectId, ref: 'User' }]
});

// 用户的关注列表集合
const userFollowingSchema = new Schema({
  userId: { type: Schema.Types.ObjectId, ref: 'User', unique: true },
  followingIds: [{ type: Schema.Types.ObjectId, ref: 'User' }]
});

const UserFollower = mongoose.model('UserFollower', userFollowerSchema);
const UserFollowing = mongoose.model('UserFollowing', userFollowingSchema);

二、核心业务逻辑实现

1. 用户发起关注请求/直接关注

async function followUser(requesterId, recipientId) {
  const recipient = await User.findById(recipientId);

  // 检查是否被对方拉黑
  if (recipient.blockedUsers.includes(requesterId)) {
    throw new Error('您已被该用户拉黑,无法关注');
  }

  // 检查是否已关注过该用户
  const existingFollowing = await UserFollowing.findOne({ 
    userId: requesterId, 
    followingIds: recipientId 
  });
  if (existingFollowing) {
    throw new Error('您已关注该用户');
  }

  if (!recipient.isPrivate) {
    // 公开用户:直接建立关注关系
    await Promise.all([
      UserFollowing.findOneAndUpdate(
        { userId: requesterId },
        { $push: { followingIds: recipientId } },
        { upsert: true, new: true }
      ),
      UserFollower.findOneAndUpdate(
        { userId: recipientId },
        { $push: { followerIds: requesterId } },
        { upsert: true, new: true }
      )
    ]);
    return { message: '关注成功' };
  } else {
    // 私有用户:创建待处理请求
    try {
      await FollowRequest.create({ requester: requesterId, recipient: recipientId });
      return { message: '关注请求已发送,请等待对方确认' };
    } catch (err) {
      if (err.code === 11000) { // 唯一索引冲突,说明已有请求
        return { message: '您已发送过关注请求,请等待对方确认' };
      }
      throw err;
    }
  }
}

2. 私有用户处理关注请求(同意/拒绝)

async function handleFollowRequest(requestId, action, userId) {
  const request = await FollowRequest.findById(requestId);
  
  // 验证当前用户是否为请求接收者
  if (request.recipient.toString() !== userId.toString()) {
    throw new Error('无权处理该请求');
  }

  if (action === 'accept') {
    // 同意请求:建立双向关注关系
    await Promise.all([
      UserFollowing.findOneAndUpdate(
        { userId: request.requester },
        { $push: { followingIds: request.recipient } },
        { upsert: true, new: true }
      ),
      UserFollower.findOneAndUpdate(
        { userId: request.recipient },
        { $push: { followerIds: request.requester } },
        { upsert: true, new: true }
      ),
      FollowRequest.findByIdAndUpdate(requestId, { status: 'accepted' })
    ]);
    return { message: '已同意关注请求' };
  } else if (action === 'reject') {
    // 拒绝请求:更新状态
    await FollowRequest.findByIdAndUpdate(requestId, { status: 'rejected' });
    return { message: '已拒绝关注请求' };
  } else {
    throw new Error('无效操作,仅支持accept或reject');
  }
}

3. 获取当前用户的待处理请求

async function getPendingFollowRequests(userId) {
  return FollowRequest.find({ recipient: userId, status: 'pending' })
    .populate('requester', 'email') // 仅返回请求发起者的邮箱信息
    .sort({ createdAt: -1 });
}

三、Express API接口示例

1. 发起关注请求/关注用户

router.post('/follow', async (req, res) => {
  try {
    const { recipientId } = req.body;
    const requesterId = req.user._id; // 假设已通过认证中间件获取当前用户ID
    
    const result = await followUser(requesterId, recipientId);
    res.status(200).json(result);
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

2. 处理关注请求

router.post('/follow-request/:requestId', async (req, res) => {
  try {
    const { requestId } = req.params;
    const { action } = req.body;
    const userId = req.user._id;
    
    const result = await handleFollowRequest(requestId, action, userId);
    res.status(200).json(result);
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

3. 获取待处理请求列表

router.get('/follow-requests/pending', async (req, res) => {
  try {
    const userId = req.user._id;
    const requests = await getPendingFollowRequests(userId);
    res.status(200).json(requests);
  } catch (err) {
    res.status(500).json({ error: '获取请求失败' });
  }
});

四、扩展拉黑功能

实现拉黑操作,并同步清理相关请求:

async function blockUser(userId, targetId) {
  await User.findByIdAndUpdate(
    userId,
    { $addToSet: { blockedUsers: targetId } }, // 避免重复拉黑
    { new: true }
  );
  // 清理双方的关注请求(如果存在)
  await FollowRequest.deleteMany({
    $or: [
      { requester: userId, recipient: targetId },
      { requester: targetId, recipient: userId }
    ]
  });
  return { message: '拉黑成功' };
}

内容的提问来源于stack exchange,提问作者xandert.93

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 15:25:23