功能简介

点赞功能,一个博客的灵魂功能之一,你的博客可以没有评论和阅读,但是一定不能没有点赞,因为这是一种最基础的互动方式!所以我要从零开始开发一个健全的点赞功能

功能分析

功能点其实不困难,但是还是有一点复杂的,需要实现点赞和取消点赞,无法重复点赞。还有就是设计Api的时候需要考虑到后台要统计点赞数据。

这几个功能点里,比较难搞的就是用户无法重复点赞了,因为博客这类网站,是不需要用户登录的。也就没法像微博,qq,wechat这样记录用户Uid来判断用户是否点赞。所以现在我了解到的方法有两种。

1.是通过本地浏览器缓存去判断用户是否修改,但是这个是可以被修改的,而且用户清空浏览器缓存就么得了。

2.是通过用户的Ip地址去判断,一般来说。把点过赞的ip地址存到数据库里,每次点赞的时候去数据库里比对。有的话就是点赞过了,没有就没有点赞。我个人倾向这个方法。虽然也是有一定缺陷。不过仔细想想谁会换着代理来给我疯狂点赞呢?

代码实现

后端工作:

数据库设计

比较简洁,但是方便数据查找和统计。

const mongoose = require("mongoose");
const Schema = new mongoose.Schema({
  articleId: {
    type: mongoose.SchemaTypes.ObjectId,
    required: true,
  },
  ip: {
    type: String,
    required: true,
  },
});

module.exports = mongoose.model("Like", Schema);
Api设计

需要设计三个Api 1. 点赞,是的话去数据库里删除点赞信息,并且把文章的点赞数量减一,反之亦然。 2. 用户是否点赞,这个前端需要用到,如果用户点赞了,那么就在用户刷新或者再次访问的时候改变css样为已点赞的样式 3. 点赞数量,展示当前点赞数量,

注意,如果你使用了nginx反向代理,那么前端你获取的ip可能永远都是127.0.0.1,你需要在设置nginx转发时加上http header以获取真实的用户ip 解决方案

    
//plugin/like.js
module.exports = (app) => {
  let likeDb = require("../model/Like"); 
  let articleDb = require("../model/Article");
  //点赞接口
  app.post("/api/like", async (req, res) => {
    const userIp = req.get("X-Real-IP") || req.get("X-Forwarded-For") || req.ip;//获取真实ip
    const articleId = req.body.articleId;
    const userLike = await likeDb.findOne({
      userIp: userIp,
      articleId: articleId,
    });
    console.log(`用户点赞Ip: ${userIp}`);
    if (userLike) {
      console.log(`用户取消点赞`);
      try {
        await likeDb.findOneAndRemove({
          articleId: articleId,
          userIp: userIp,
        });
        await articleDb.findByIdAndUpdate(
          { _id: articleId },
          { $inc: { like: -1 } }
        );
        console.log(articleId, "取消点赞");
        res.status(200).send({ msg: "取消点赞" });
      } catch (error) {
        console.log("删除数据失败!", error);
      }
    } else {
      try {
        await likeDb.create({ articleId, userIp });
        await articleDb.findByIdAndUpdate(
          { _id: articleId },
          { $inc: { like: 1 } }
        );
        console.log(articleId, " 点赞成功");
        res.status(200).send({ msg: "点赞成功" });
      } catch (error) {
        console.log("创建数据失败", error);
      }
    }
  });

  //判断用户是否点过赞 主要用于在页面加载的时候的时候,来改变界面的css
  app.get("/api/like/beenLiked/:id", async (req, res) => {
    const userIp = req.get("X-Real-IP") || req.get("X-Forwarded-For") || req.ip;
    const articleId = await likeDb.findOne({
      userIp: userIp,
      articleId: req.params.id,
    });
    if (articleId) {
      res.status(200).send({ code: "200", msg: "已经点赞" });
    } else {
      res.status(200).send({ code: "400", msg: "还未点赞" });
    }
  });

  //点赞数量
  app.get("api/like/likeSum/:id", async (req, res) => {
    const articleSum = await likeDb
      .findOne({ articleId: req.params.id })
      .count();
    res.send({ msg: "点赞数量", articleSum: articleSum });
  });
};

==获取到用户的ip地址,在express里非常的简单,但是会发现会有一个前缀,去掉它的方法有两种:==

//去掉获取前缀方法
//1使用正则
req.ip.match(/\d+\.\d+\.\d+\.\d+/)

//2指定监听地址为0.0.0.0 (推荐)
app.listen(PORT, "0.0.0.0", () => {
  console.log(`服务启动 端口号:${PORT}`);
});

前端工作:

Api管理

所有的Api都应该统一管理,like是我新增的Api

import http from "../plugins/http";

//restFullApi
let restGetAll = async (url, query) => {
  return await http.get(`rest/${url}`, { params: query });
};

let restGetOne = async (url, params) => {
  return await http.get(`rest/${url}/${params}`);
};

let restUpdata = async (url, parmas, data) => {
  return await http.put(`rest/${url}/${parmas}`, data);
};

let restPostData = async (url, data) => {
  return await http.post(`rest/${url}`, data);
};

let restDeleteOne = async (url, parmas) => {
  return await http.delete(`rest/${url}/${parmas}`);
};

//点赞
let like = async articleData => {
  return await http.post(`like`, articleData);
};

export { restGetAll, restGetOne, restUpdata, restPostData, restDeleteOne,like };
界面设计

点赞样式,这个不多讲,每个人都有不同。我是从jq上移植过来的,当然你也可以直接参考我的代码GitHub,components下的like组件,记得点个👍。

默认样式:

image.png

点赞后:

image.png image.png

取消点赞

image.png

事件绑定和APi请求

样式不多讲,直接在组件里绑定事件就行。然后在事件里请求Api

<template>
<div>
     <div @click="like>写你自己的样式</div>
</div>
</template>

这里这个articleID代表了当前博客的唯一标识,是mongodb自动生成的唯一ID。我需要用它传递到后端哥们,让后端哥们去判断用户是否点赞(后端也是我(#`O′))

<script>
import { like } from "../api/api";
export default {
methods: {
async like() {
      let articleData = { articleId: this.articleId }; //打包数据
      let res = await like(articleData);
    }
}
</script>

阶段性效果

可以看到点赞后,后端返回数据点赞成功

image.png

取消点赞后

image.png

进阶

整个点赞效果已经基本完善了,但是还有很多地方需要完善。

在刷新后要判断用户是否点赞过,然后去相应的改变界面样式,这需要在界面渲染的实话去请求api判断是否点过赞。

如何防御通过爬虫和机器人快速的点赞对服务器造成攻击。

我已经完成了,你有什么思路吗?欢迎在评论下方留言讨论

0 / 10
0 / 30