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

Node.js电影应用如何实现每个用户对单条电影帖子仅点赞一次

实现单用户单电影帖子仅可点赞一次的方案

1. 调整Movies模型结构

原有模型仅记录点赞总数,没有存储点赞用户标识,需要新增likedBy字段存储所有点赞过该帖子的用户ID:

const mongoose = require('mongoose');
const moviesSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true
    },
    description: {
        type: String,
        required: true
    },
    link: {
        type: String,
        required: true
    },
    moviesData: {
        type: Date,
        required: true,
        default: Date.now // 原代码写Date.now()会导致初始化时固定时间,这里修正为传函数引用
    },
    user: {
        type: String,
        required: true,
    },
    userName: {
        type: String,
        required: false
    },
    likes: {
        type: Number,
        default: 0
    },
    // 新增点赞用户列表字段
    likedBy: {
        // 如果你的用户ID是MongoDB默认ObjectId类型,用下面两行配置
        type: [mongoose.Schema.Types.ObjectId],
        ref: 'User', // 这里对应你的用户模型名称,按需修改
        // 如果你的用户ID是自定义字符串类型,替换上面两行为:type: [String],
        default: []
    }
});

module.exports = mongoose.model('Movie', moviesSchema);

2. 改造点赞接口逻辑

首先确保你的接口已经接入用户鉴权(如JWT鉴权),可以从req对象中拿到当前登录用户的ID。推荐使用MongoDB原子操作实现,避免并发请求导致的重复点赞问题:

// 先引入你的鉴权中间件,保证只有登录用户可以调用点赞接口,且能从req.user拿到用户信息
const auth = require('../你的鉴权中间件文件路径');

// Add like to movie
router.patch('/admin/like/:id', auth, async (req, res) => {
    console.log("POST LIKE to movie")
    const movieId = req.params.id;
    // 这里的用户ID字段根据你的鉴权逻辑调整,部分场景下是req.user._id
    const currentUserId = req.user.id;

    try {
        const updateResult = await Movie.updateOne(
            // 查询条件:电影存在 + 当前用户未点赞
            { _id: movieId, likedBy: { $ne: currentUserId } },
            {
                // 将当前用户加入点赞列表
                $push: { likedBy: currentUserId },
                // 点赞数+1
                $inc: { likes: 1 }
            }
        );

        if (updateResult.matchedCount === 0) {
            // 区分两种匹配失败的场景
            const movieExists = await Movie.exists({ _id: movieId });
            if (!movieExists) {
                return res.status(404).json({ message: "对应的电影帖子不存在" });
            }
            return res.status(400).json({ message: "你已经对该帖子点过赞了" });
        }

        // 返回最新点赞数
        const updatedMovie = await Movie.findById(movieId, { likes: 1 });
        res.status(200).json({ message: "点赞成功", number_of_likes: updatedMovie.likes });

    } catch (error) {
        res.status(400).json({ message: "An error has occurred", error: error });
    }
})

补充说明

  • 若不需要单独维护likes字段,可以直接删除该字段,后续需要展示点赞数时直接读取likedBy数组的长度即可,避免出现数据不一致问题
  • 原子操作的实现方式比先查后改的性能更高,也能避免同一用户短时间内多次请求导致的重复点赞漏洞

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 09:36:04