dockerfile修改

This commit is contained in:
2026-02-04 13:06:11 +08:00
parent 15cea49f2d
commit 7b94177b83
8 changed files with 475 additions and 71 deletions

View File

@@ -6,23 +6,25 @@ import (
// Message 核心变更:增加 Room 字段,移除歧义字段
type Message struct {
Type string `json:"type"` // 消息类型(严格按常量)
User string `json:"user"` // 发送者(客户端填,服务端可校验)
Content string `json:"content"` // 消息内容
Time string `json:"time"` // 服务端统一覆盖为 RFC3339防客户端篡改
To string `json:"to,omitempty"` // 私聊目标(仅 type=private 时有效)
Room string `json:"room,omitempty"` // 房间ID仅 type=room 时有效)
Type string `json:"type"` // 消息类型(严格按常量)
User string `json:"user"` // 发送者(客户端填,服务端可校验)
Content string `json:"content"` // 消息内容
Time string `json:"time"` // 服务端统一覆盖为 RFC3339防客户端篡改
To string `json:"to,omitempty"` // 私聊目标(仅 type=private 时有效)
Room string `json:"room,omitempty"` // 房间ID仅 type=room 时有效)
Count int64 `json:"count,omitempty"` // 在线人数(仅 type=user_count 时有效)
}
// 消息类型常量
const (
MsgTypeLogin = "login" // 服务端生成:用户上线事件
MsgTypeLogout = "logout" // 服务端生成:用户下线事件
MsgTypeBroadcast = "broadcast" // 全体用户广播(无视房间)
MsgTypeRoom = "room" // 房间消息(必须带 Room 字段)
MsgTypePrivate = "private" // 私聊(必须带 To 字段)
MsgTypeSystem = "system" // 服务端生成:系统通知
MsgTypeError = "error" // 服务端生成:定向错误提示
MsgTypeLogin = "login" // 服务端生成:用户上线事件
MsgTypeLogout = "logout" // 服务端生成:用户下线事件
MsgTypeBroadcast = "broadcast" // 全体用户广播(无视房间)
MsgTypeRoom = "room" // 房间消息(必须带 Room 字段)
MsgTypePrivate = "private" // 私聊(必须带 To 字段)
MsgTypeSystem = "system" // 服务端生成:系统通知
MsgTypeError = "error" // 服务端生成:定向错误提示
MsgTypeUserCount = "user_count" // 服务端生成:在线人数更新
)
// 发送广播消息

56
internal/redis/client.go Normal file
View File

@@ -0,0 +1,56 @@
package redis
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/v9"
)
const UserZSet = "chat:users"
type Client struct {
rdb *redis.Client
}
// NewClient 创建 Redis 客户端
func NewClient(addr string, password string, db int) (*Client, error) {
rdb := redis.NewClient(&redis.Options{
Addr: addr,
Password: password,
DB: db,
})
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := rdb.Ping(ctx).Err(); err != nil {
return nil, fmt.Errorf("redis ping error: %w", err)
}
return &Client{rdb: rdb}, nil
}
// Close 关闭连接
func (c *Client) Close() error {
return c.rdb.Close()
}
// AddUserToZSet 添加用户到 ZSet
func (c *Client) AddUserToZSet(key string, user string, score int64) error {
return c.rdb.ZAdd(context.Background(), key, redis.Z{
Score: float64(score),
Member: user,
}).Err()
}
// RemoveUserFromZSet 从 ZSet 移除用户
func (c *Client) RemoveUserFromZSet(key string, user string) error {
return c.rdb.ZRem(context.Background(), key, user).Err()
}
// CountUsers 获取在线用户数
func (c *Client) CountUsers(key string) (int64, error) {
return c.rdb.ZCard(context.Background(), key).Result()
}

View File

@@ -3,6 +3,7 @@ package ws
import (
"ChatRoom/internal/models"
"ChatRoom/internal/rabbitmq"
"ChatRoom/internal/redis"
"encoding/json"
"log"
"net/http"
@@ -30,14 +31,15 @@ var upgrader = websocket.Upgrader{
// 连接
type Connection struct {
wsConn *websocket.Conn //websocket连接
rmqClient *rabbitmq.Client //rabbitmq客户端
queueName string //队列名称
userID string //用户ID
send chan []byte //发送通道
wsConn *websocket.Conn //websocket连接
rmqClient *rabbitmq.Client //rabbitmq客户端
redisClient *redis.Client //redis客户端
queueName string //队列名称
userID string //用户ID
send chan []byte //发送通道
}
func NewConnection(w http.ResponseWriter, r *http.Request, rmq *rabbitmq.Client) {
func NewConnection(w http.ResponseWriter, r *http.Request, rmq *rabbitmq.Client, redisClient *redis.Client) {
// 1. 升级 HTTP 到 WebSocket
wsConn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
@@ -51,6 +53,13 @@ func NewConnection(w http.ResponseWriter, r *http.Request, rmq *rabbitmq.Client)
wsConn.Close()
return
}
//redis记录用户
if err := redisClient.AddUserToZSet(redis.UserZSet, userID, time.Now().Unix()); err != nil {
log.Printf("Redis 用户记录失败: %v", err)
wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(5000, "redis error"))
wsConn.Close()
return
}
// 3. 为用户创建 RabbitMQ 队列
queueName, err := rmq.DeclareQueue()
if err != nil {
@@ -75,11 +84,12 @@ func NewConnection(w http.ResponseWriter, r *http.Request, rmq *rabbitmq.Client)
// 5. 创建 Connection 对象
conn := &Connection{
wsConn: wsConn,
rmqClient: rmq,
queueName: queueName,
userID: userID,
send: make(chan []byte, 256),
wsConn: wsConn,
rmqClient: rmq,
redisClient: redisClient,
queueName: queueName,
userID: userID,
send: make(chan []byte, 256),
}
// 发送登录成功消息
@@ -93,13 +103,20 @@ func NewConnection(w http.ResponseWriter, r *http.Request, rmq *rabbitmq.Client)
conn.send <- data
}
// 广播在线人数
conn.broadcastUserCount()
go conn.writePump()
go conn.readPump()
go conn.consumeFromRabbitMQ()
}
func (c *Connection) readPump() {
defer c.wsConn.Close()
defer func() {
c.redisClient.RemoveUserFromZSet(redis.UserZSet, c.userID)
c.broadcastUserCount()
c.wsConn.Close()
}()
c.wsConn.SetReadLimit(maxMessageSize)
c.wsConn.SetReadDeadline(time.Now().Add(pongWait))
@@ -231,3 +248,24 @@ func (c *Connection) consumeFromRabbitMQ() {
}
}
}
// 广播在线人数
func (c *Connection) broadcastUserCount() {
count, err := c.redisClient.CountUsers(redis.UserZSet)
if err != nil {
log.Printf("获取在线人数失败: %v", err)
return
}
msg := &models.Message{
Type: models.MsgTypeUserCount,
User: "system",
Count: count,
Time: time.Now().UTC().Format(time.RFC3339),
}
body, _ := json.Marshal(msg)
if err := c.rmqClient.Publish(c.rmqClient.ExchangeName, "chat.global", body); err != nil {
log.Printf("广播在线人数失败: %v", err)
}
}