如何实现单用户仅能点赞帖子一次?Node.js+MongoDB开发场景
解决单用户重复点赞问题的方案
我来帮你搞定这个重复点赞的问题,核心思路就是在后端先校验用户是否已经点赞过目标帖子,同时可以优化前端交互提升用户体验,具体实现如下:
1. 后端接口修改(核心校验逻辑)
目前你的接口直接将用户ID加入点赞数组,没有做重复校验。我们需要在添加点赞前,先检查当前用户是否已经出现在该帖子的点赞列表中:
修改后的server.js点赞接口代码:
app.post('/likes/:id', async (req, res) => { try { const post = await Post.findById(req.params.id); // 关键:检查当前用户是否已经点赞过该帖子 // 注意要把ObjectId转成字符串再对比,避免类型不匹配 const hasAlreadyLiked = post.likes.some(like => like.user.toString() === req.user.id); if (hasAlreadyLiked) { // 已经点赞过,直接重定向,不执行后续操作 return res.redirect('/cogfeed'); } // 未点赞过,才添加到点赞数组 post.likes.unshift({ user: req.user.id }); await post.save(); res.redirect('/cogfeed'); } catch (err) { console.log(err.message); res.status(500).send('Server Error') } })
额外优化:数据库层面防重复
可以给帖子模型的点赞数组添加唯一索引,从数据库层面避免重复点赞数据:
// 帖子模型中的点赞数组定义 likes: [{ user: { type: mongoose.Schema.Types.ObjectId, ref: 'users', unique: true // 添加唯一约束,同一个用户只能出现在该数组一次 } }]
⚠️ 注意:这个索引需要配合后端的校验一起用,不然重复添加时数据库会抛出错误,后端提前校验可以避免这种情况。
2. 前端交互优化(提升用户体验)
现在前端点击按钮后直接刷新页面,也没有反馈用户是否已经点赞。我们可以优化两点:
- 渲染时根据用户点赞状态,改变按钮的文本和禁用状态
- 点击后无需刷新页面,直接更新UI
模板渲染优化(以Handlebars为例)
先注册一个Handlebars辅助函数,用来判断当前用户是否点赞:
// 注册辅助函数,判断用户是否已点赞 handlebars.registerHelper('hasLiked', function(likesList, currentUserId) { return likesList.some(like => like.user.toString() === currentUserId); });
然后修改模板代码,动态渲染按钮状态:
<div> <button class="likesButton btn btn-dark {{#if (hasLiked this.likes currentUser._id)}}liked{{/if}}" id="{{this._id}}" {{#if (hasLiked this.likes currentUser._id)}}disabled{{/if}} > {{#if (hasLiked this.likes currentUser._id)}}已点赞{{else}}点赞{{/if}} </button> <span> {{ this.likes.length }} </span> </div>
(这里假设你能在模板中访问当前登录用户的currentUser对象)
前端点击事件优化
去掉页面刷新,直接更新按钮状态和点赞数:
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.js" integrity="sha256-bd8XIKzrtyJ1O5Sh3Xp3GiuMIzWC42ZekvrMMD4GxRg=" crossorigin="anonymous"></script> <script> const likesButtons = document.querySelectorAll('.likesButton'); likesButtons.forEach(likeButton => { likeButton.addEventListener('click', async () => { try { await axios.post(`/likes/${likeButton.id}`); // 直接更新UI,无需刷新页面 likeButton.textContent = '已点赞'; likeButton.disabled = true; const likeCountSpan = likeButton.nextElementSibling; likeCountSpan.textContent = parseInt(likeCountSpan.textContent) + 1; } catch (err) { console.error(err); alert('你已经点赞过这篇帖子了'); } }); }); </script>
内容的提问来源于stack exchange,提问作者Morgan
相关产品推荐
相关产品推荐

