使用WebSocket、MySQL、Go语言的Gin框架和GORM库来实现一个用户实时统计大屏概览

为了使用 WebSocket、MySQL、Go 的 Gin 框架和 GORM 实现用户实时统计大屏概览,以下是分步指南:


1. 项目结构与依赖

1.1 初始化项目
mkdir realtime-dashboard && cd realtime-dashboard
go mod init realtime-dashboard
1.2 安装依赖
go get -u github.com/gin-gonic/gin
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
go get -u github.com/gorilla/websocket

2. 数据库设计与模型定义

2.1 MySQL 表结构
-- 用户表
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    created_at DATETIME
);

-- 用户活动表
CREATE TABLE user_activities (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT,
    activity VARCHAR(50),
    created_at DATETIME
);
2.2 Go 模型定义
// models.go
package main

import "time"

type User struct {
    ID        uint      `gorm:"primaryKey"`
    Name      string
    CreatedAt time.Time
}

type UserActivity struct {
    ID        uint      `gorm:"primaryKey"`
    UserID    uint
    Activity  string
    CreatedAt time.Time
}

3. WebSocket 服务端实现

3.1 WebSocket 连接管理
// websocket.go
package main

import (
    "net/http"
    "sync"
    "github.com/gin-gonic/gin"
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool { return true },
}

var clients = make(map[*websocket.Conn]struct{})
var clientsMutex sync.Mutex

func handleWebSocket(c *gin.Context) {
    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    if err != nil {
        return
    }
    defer conn.Close()

    // 注册客户端
    clientsMutex.Lock()
    clients[conn] = struct{}{}
    clientsMutex.Unlock()

    // 保持连接
    for {
        if _, _, err := conn.ReadMessage(); err != nil {
            clientsMutex.Lock()
            delete(clients, conn)
            clientsMutex.Unlock()
            break
        }
    }
}
3.2 实时数据广播
func broadcastStats(stats map[string]interface{}) {
    clientsMutex.Lock()
    defer clientsMutex.Unlock()

    for client := range clients {
        if err := client.WriteJSON(stats); err != nil {
            client.Close()
            delete(clients, client)
        }
    }
}

4. 统计逻辑与 API 接口

4.1 统计核心逻辑
// stats.go
package main

import (
    "time"
    "gorm.io/gorm"
)

func getRealTimeStats(db *gorm.DB) map[string]interface{} {
    var stats = make(map[string]interface{})

    // 在线用户数(最近5分钟活跃)
    var onlineUsers int64
    fiveMinutesAgo := time.Now().Add(-5 * time.Minute)
    db.Model(&UserActivity{}).
        Where("activity = 'login' AND created_at >= ?", fiveMinutesAgo).
        Count(&onlineUsers)

    // 当日新增用户
    var todayNewUsers int64
    today := time.Now().Truncate(24 * time.Hour)
    db.Model(&User{}).
        Where("created_at >= ?", today).
        Count(&todayNewUsers)

    stats["online_users"] = onlineUsers
    stats["today_new_users"] = todayNewUsers
    return stats
}
4.2 用户登录接口
// handlers.go
package main

import (
    "github.com/gin-gonic/gin"
)

type LoginRequest struct {
    UserID uint `json:"user_id"`
}

func loginHandler(c *gin.Context, db *gorm.DB) {
    var req LoginRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": "Invalid request"})
        return
    }

    // 记录登录活动
    activity := UserActivity{
        UserID:    req.UserID,
        Activity:  "login",
        CreatedAt: time.Now(),
    }
    db.Create(&activity)

    // 立即广播最新统计
    stats := getRealTimeStats(db)
    broadcastStats(stats)
    c.JSON(200, gin.H{"status": "success"})
}

5. 主程序与定时任务

// main.go
package main

import (
    "time"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "github.com/gin-gonic/gin"
)

func main() {
    // 初始化数据库
    dsn := "user:password@tcp(localhost:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        panic("Failed to connect database")
    }
    db.AutoMigrate(&User{}, &UserActivity{})

    // 初始化 Gin
    r := gin.Default()

    // 路由配置
    r.GET("/ws", handleWebSocket)
    r.POST("/api/login", func(c *gin.Context) { loginHandler(c, db) })

    // 静态页面服务
    r.StaticFile("/", "./public/index.html")

    // 定时推送任务
    go func() {
        ticker := time.NewTicker(5 * time.Second)
        for range ticker.C {
            stats := getRealTimeStats(db)
            broadcastStats(stats)
        }
    }()

    // 启动服务
    r.Run(":8080")
}

6. 前端大屏页面

6.1 HTML 模板 (public/index.html)
DOCTYPE html>
<html>
<head>
    <title>实时统计大屏title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.3.0/dist/echarts.min.js">script>
head>
<body>
    <div id="dashboard" style="width: 100%; height: 100vh;">div>
    <script>
        let chart = echarts.init(document.getElementById('dashboard'));
        let option = {
            title: { text: '实时用户统计', left: 'center' },
            tooltip: { trigger: 'axis' },
            legend: { data: ['在线用户', '今日新增'], top: 30 },
            xAxis: { type: 'category', data: ['统计指标'] },
            yAxis: { type: 'value' },
            series: [
                { name: '在线用户', type: 'bar', data: [0] },
                { name: '今日新增', type: 'line', data: [0] }
            ]
        };

        // WebSocket 连接
        const ws = new WebSocket('ws://' + window.location.host + '/ws');
        ws.onmessage = (event) => {
            const stats = JSON.parse(event.data);
            option.series[0].data = [stats.online_users];
            option.series[1].data = [stats.today_new_users];
            chart.setOption(option);
        };
    script>
body>
html>

7. 运行与测试

7.1 启动服务
go run main.go
7.2 模拟用户登录(API 调用)
curl -X POST http://localhost:8080/api/login \
  -H "Content-Type: application/json" \
  -d '{"user_id": 1}'
7.3 访问大屏页面

打开浏览器访问 http://localhost:8080,实时数据将每5秒更新。


8. 高级优化方向

  1. 缓存优化:使用 Redis 缓存统计结果,减少数据库查询。
  2. 集群扩展:通过分片和副本集提升 MySQL 性能。
  3. 安全加固
    • WebSocket 连接增加 JWT 认证
    • 防止 SQL 注入(GORM 已处理)
  4. 数据持久化:定期归档历史统计到独立表。
  5. 前端优化:使用 WebGL 渲染大规模数据图表。

通过上述步骤,即可构建一个基于 WebSocket 的实时用户统计大屏系统。

你可能感兴趣的:(Go,websocket,mysql,golang)