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

NodeJS+MongoDB点赞/点踩系统:无法处理取消操作的问题

解决方案:修复点赞/点踩取消逻辑的问题

核心问题分析

你当前代码的关键错误是从req.body中获取usersLiked和usersDisliked——前端不会传递这两个数据库存储的数组,所以它们自然是undefined。必须先从MongoDB中查询目标Sauce文档,拿到当前的用户点赞/点踩列表,才能判断用户之前的操作状态。

另外,你的Schema里usersLiked和usersDisliked的类型定义有误,应该写成[String]而非["String <userId>"],后者是无效的Mongoose数组类型定义。

修正后的完整代码

第一步:修正Sauce模型的Schema

const mongoose = require('mongoose');
const sauceSchema = mongoose.Schema({
  userId: {type:String},
  name: {type:String, required:true},
  manufacturer:{type:String, required:true},
  description:{type:String, required:true},
  mainPepper: {type:String, required:true},
  imageUrl:{type:String, required:true},
  heat: {type:Number, required:true},
  likes: {type:Number, default: 0}, // 给默认值避免初始为undefined
  dislikes : {type:Number, default: 0},
  usersLiked : {type:[String], default: []}, // 修正为[String],加默认空数组
  usersDisliked: {type:[String], default: []},
});

module.exports = mongoose.model('sauce', sauceSchema);

第二步:重写点赞路由逻辑

exports.likeSauce = async (req, res, next) => {  
  try {
    const sauceId = req.params.id;
    const userId = req.body.userId;
    const likeStatus = req.body.like;

    // 先查询数据库中的目标sauce文档
    const sauce = await Sauce.findById(sauceId);
    if (!sauce) {
      return res.status(404).json({ error: 'Sauce not found' });
    }

    switch(likeStatus) {
      // 点赞操作
      case 1:
        // 如果用户之前点过踩,先取消踩
        if (sauce.usersDisliked.includes(userId)) {
          await Sauce.updateOne(
            { _id: sauceId },
            { 
              $inc: { dislikes: -1 },
              $pull: { usersDisliked: userId }
            }
          );
        }
        // 如果用户还没点过赞,执行点赞
        if (!sauce.usersLiked.includes(userId)) {
          await Sauce.updateOne(
            { _id: sauceId },
            { 
              $inc: { likes: 1 },
              $push: { usersLiked: userId }
            }
          );
          return res.status(201).json({ message: 'You liked this sauce' });
        }
        return res.status(200).json({ message: 'You already liked this sauce' });

      // 点踩操作
      case -1:
        // 如果用户之前点过赞,先取消赞
        if (sauce.usersLiked.includes(userId)) {
          await Sauce.updateOne(
            { _id: sauceId },
            { 
              $inc: { likes: -1 },
              $pull: { usersLiked: userId }
            }
          );
        }
        // 如果用户还没点过踩,执行点踩
        if (!sauce.usersDisliked.includes(userId)) {
          await Sauce.updateOne(
            { _id: sauceId },
            { 
              $inc: { dislikes: 1 },
              $push: { usersDisliked: userId }
            }
          );
          return res.status(201).json({ message: 'You disliked this sauce' });
        }
        return res.status(200).json({ message: 'You already disliked this sauce' });

      // 取消操作
      case 0:
        if (sauce.usersLiked.includes(userId)) {
          await Sauce.updateOne(
            { _id: sauceId },
            { 
              $inc: { likes: -1 },
              $pull: { usersLiked: userId }
            }
          );
          return res.status(200).json({ message: 'Like removed' });
        } else if (sauce.usersDisliked.includes(userId)) {
          await Sauce.updateOne(
            { _id: sauceId },
            { 
              $inc: { dislikes: -1 },
              $pull: { usersDisliked: userId }
            }
          );
          return res.status(200).json({ message: 'Dislike removed' });
        }
        return res.status(200).json({ message: 'No previous like/dislike to remove' });

      default:
        return res.status(400).json({ error: 'Invalid like status' });
    }
  } catch (error) {
    res.status(500).json({ error });
  }
};

关键改进点

  • 使用async/await替代链式then/catch,让逻辑更清晰易读
  • 先查询目标Sauce文档,获取真实的usersLiked和usersDisliked数组
  • 增加边界判断:比如用户重复点赞/点踩、Sauce不存在的情况
  • 修正Schema类型,给数值和数组字段加默认值,避免初始状态的undefined问题
  • 处理点赞/点踩时自动取消之前的相反操作(比如用户先点踩再点赞,会自动取消踩)

内容的提问来源于stack exchange,提问作者Elradsoldier

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 14:25:14