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
相关产品推荐
相关产品推荐

