【项目总结】关于访客记录功能模块技术点

392 阅读4分钟

1. 前言

不管在开发个人网站还是公司项目中,我们有可能会需要增加访客记录模块,添加访客记录模块是一个很有用的功能,通过这个功能,可以跟踪和记录访问者的活动,从而获得有关访问者行为的有价值的信息。访客记录不仅可以帮助我们了解访问者的偏好,还可以用于分析网站或应用的流量,改进用户体验,以及为市场营销和分析提供有力的数据支持。

当然,有很多已经做好的访客分析的第三方服务,百度分析、谷歌分析等等

但是,我要自己动手做一个 就想折腾折腾

2. 我的访客记录模块

没有做复杂的功能 只是记录访客的来源信息基于用户设备和IP等信息 足以够用了

1692157721166.png

3. 功能关键点

  • 获取用户真实IP:通过解析HTTP请求头或代理服务器配置,获取用户的真实IP地址以标识不同的访客。
  • 获取用户设备信息:使用工具如ua-parser-js解析用户代理字符串,提取设备、浏览器和操作系统等信息。
  • 解析用户IP地址:使用第三方IP地理位置解析服务,将IP地址转换为大致的地理位置信息。

访客记录主要逻辑在后端实现

获取用户真实IP

这里上线后还需要在服务器的nginx配置 才会生效

获取用户的真实IP地址是访客记录模块的关键部分,用于标识不同访客并了解其来源。然而,由于代理、VPN等网络结构,直接获取真实IP可能会受到一些限制。以下是获取用户真实IP的一些方法:

  1. HTTP请求头中的X-Forwarded-For(XFF):在HTTP请求头中,X-Forwarded-For字段可能包含用户的真实IP地址,尤其是当请求经过代理服务器时。您的服务器可以检查该字段并提取用户真实IP。
  2. Remote Address:服务器会记录与客户端建立连接的远程IP地址。然而,这可能是用户连接的代理服务器的IP地址,而不是用户的真实IP。
  3. WebSockets和Socket连接:如果您的应用使用了WebSocket或Socket连接,您可以从连接中获取用户的真实IP。
  4. 反向代理配置:如果您的应用在反向代理(如Nginx或Apache)后面运行,您可以配置代理服务器将用户的真实IP传递给应用服务器。
// 获取用户的真实公网IP
exports.getPublicIP = function (req) {
    const headers = req.headers;
    if (headers['x-real-ip']) {
        return headers['x-real-ip'];
    }
    if (headers['x-forwarded-for']) {
        const ipList = headers['x-forwarded-for'].split(',');
        return ipList[0];
    }
    return '0.0.0.0';
}

解析用户IP地址

根据用户IP解析地址 使用的是第三方免费接口

// IP地址解析
exports.parseIP = function (clientIp) {
    return new Promise((resolve, reject) => {
        request(
            `https://opendata.baidu.com/api.php?query=[${clientIp}]&co=&resource_id=6006&oe=utf8`,
            {method: 'GET'},
            function (error, response, body) {
                if (error !== null) {
                    reject(error);
                    return;
                }
                if (body && !body.status) {
                    resolve(body.length && JSON.parse(body).data[0].location || '-');
                }
            }
        );
    });
}

获取用户设备信息

使用 ua-parser-js 分析出用户的设备信息

ua-parser-js是一个JavaScript库,它用于解析用户代理字符串(User Agent String),以便从中提取有关用户设备和浏览器的信息。用户代理字符串是由浏览器发送给服务器的一段文本,其中包含了关于浏览器、操作系统、设备类型等信息。ua-parser-js库使开发人员能够从用户代理字符串中提取有关用户设备的详细信息,从而可以更好地进行设备适配和用户体验优化。

//识别常见的浏览器、操作系统和设备等信息
const u = new UAParser(req.headers['user-agent']);
// u 解析结果
{
  browser: {
    name: "Chrome",
    version: "80.0.3987.163",
    major: "80"
  },
  device: {
    model: undefined,
    type: undefined,
    vendor: undefined
  },
  engine: {
    name: "Blink"
  },
  os: {
    name: "Windows",
    version: "10.0"
  }
}

4. 数据库表设计

const mongoose = require('mongoose')
let schema = new mongoose.Schema({
    name: {
        type: String,
        comment: '用户名称'
    },
    type: {
        type: String,
        enum: ['client', 'admin'],
        default: 'client',
        comment: '站点类型'
    },
    page: {
        type: String,
        comment: '访问页面'
    },
    ip: {
        type: String,
        comment: '访问IP'
    },
    address: {
        type: String,
        comment: '访问来源'
    },
    browser: {
        type: String,
        comment: '设备'
    },
    viewNum: {
        type: Number,
        comment: '访问次数'
    },
    remark: {
        type: String,
        comment: '备注'
    },

}, {
    timestamps: true,
    versionKey: false, // 设置不需要version  _V:0
});

module.exports = mongoose.model('visitors', schema);

4.创建访客记录完整逻辑

要点:在今天之内的用户记录 对 用户名称、访问类型、IP、设备、地址、页面不同的数据进行单独录入新记录,否则只更新访问次数

/**
 * 创建访客记录
 * @returns {object} 200 - 成功响应
 * @returns {object} 400 - 参数验证错误
 * @returns {Error} default - 未知错误
 */
exports.visitorsCreate = [
    async (req, res, next) => {
        try {
            const clientIP = getPublicIP(req);
            //识别常见的浏览器、操作系统和设备等信息
            const u = new UAParser(req.headers['user-agent']);
            const address = await parseIP(clientIP);
            const equipment = u.getBrowser().name ? `${u.getBrowser().name}.v${u.getBrowser().major}` : '未知'
            const today = new Date().toISOString().split('T')[0]; // 获取今天的日期

            const existingVisitor = await VisitorsModel.findOne({
                name: req.body.name || '-',
                ip: clientIP,
                type: req.body.type || 'client',
                browser: equipment,
                address,
                page: req.body.page || '-',
                createdAt: {$gte: new Date(today)}, // 在今天之内的记录
            });

            if (existingVisitor) {
                // 如果今天已经记录过这个访客信息,则只更新浏览次数
                await VisitorsModel.findByIdAndUpdate(existingVisitor._id, {$inc: {viewNum: 1}});
                return apiResponse.successResponse(res, "ok.",);
            } else {
                // 否则,创建新的访客记录
                const newVisitors = {
                    page: req.body.page || '-',
                    type: req.body.type || 'client',
                    name: req.body.name || '-',
                    address,
                    ip: clientIP,
                    browser: equipment,
                    viewNum: 1, // 初始化浏览次数为1
                };
                const createdVisitor = await VisitorsModel.create(newVisitors);
                return apiResponse.successResponse(res, "ok.");
            }
        } catch (err) {
            next(err);
        }
    }
];

5. 总结及扩展点

  1. 在后端,需要创建API接口,以便前端或其他系统可以将访客数据发送到数据库。这涉及到以下步骤:
  • 接收数据:编写代码来接收来自前端的数据,包括访问时间、IP地址、用户代理等信息。
  • 验证和处理:对接收到的数据进行验证,确保数据的完整性和有效性。然后,将数据插入访客记录表中。
  • IP地址解析:使用IP地址解析服务将IP地址转换为地理位置信息,以便您可以获得访客的大致位置。
  1. 在前端,对分析的数据进行更加细粒度的呈现分析访客记录
  • 访客计数器:在页面上显示访客计数器,显示访客总数或唯一访客数量。
  • 访客地图:使用地图API,将访客的地理位置可视化,访问者可以看到来自不同地区的访问者分布情况。
  • 访问趋势图:绘制访问趋势图,显示不同时间段内的访问次数,帮助了解网站流量的高峰和低谷。
  • ...

总之 访客记录功能将上面三个关键点解决掉 其他数据就可以任意发挥了

不明白之处或者觉得处理的不好的地方可以评论区留言,期待和各位大佬的交流😊

基于vue3、nodejs、mongodb 个人主页前后端分离项目 gitee.com/Z568_568/ZH…