mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
feature: 每个客户端独立处理后端发送的系统消息
This commit is contained in:
@@ -3,4 +3,7 @@ package model
|
||||
type LoginAccount struct {
|
||||
Id uint64
|
||||
Username string
|
||||
|
||||
// ClientUuid 客户端UUID
|
||||
ClientUuid string
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/cache"
|
||||
"mayfly-go/pkg/config"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/rediscli"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"time"
|
||||
@@ -49,18 +50,28 @@ func PermissionHandler(rc *Ctx) error {
|
||||
if tokenStr == "" {
|
||||
return biz.PermissionErr
|
||||
}
|
||||
loginAccount, err := ParseToken(tokenStr)
|
||||
if err != nil || loginAccount == nil {
|
||||
userId, userName, err := ParseToken(tokenStr)
|
||||
if err != nil || userId == 0 {
|
||||
return biz.PermissionErr
|
||||
}
|
||||
// 权限不为nil,并且permission code不为空,则校验是否有权限code
|
||||
if permission != nil && permission.Code != "" {
|
||||
if !permissionCodeRegistry.HasCode(loginAccount.Id, permission.Code) {
|
||||
if !permissionCodeRegistry.HasCode(userId, permission.Code) {
|
||||
return biz.PermissionErr
|
||||
}
|
||||
}
|
||||
|
||||
rc.LoginAccount = loginAccount
|
||||
clientUuid := rc.GinCtx.Request.Header.Get("Client-Uuid")
|
||||
// header不存在则从查询参数token中获取
|
||||
if clientUuid == "" {
|
||||
clientUuid = rc.GinCtx.Query("clientUuid")
|
||||
}
|
||||
if rc.LoginAccount == nil {
|
||||
rc.LoginAccount = &model.LoginAccount{
|
||||
Id: userId,
|
||||
Username: userName,
|
||||
ClientUuid: clientUuid,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,8 @@ package req
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/config"
|
||||
"mayfly-go/pkg/model"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
@@ -28,9 +26,9 @@ func CreateToken(userId uint64, username string) string {
|
||||
}
|
||||
|
||||
// 解析token,并返回登录者账号信息
|
||||
func ParseToken(tokenStr string) (*model.LoginAccount, error) {
|
||||
func ParseToken(tokenStr string) (uint64, string, error) {
|
||||
if tokenStr == "" {
|
||||
return nil, errors.New("token error")
|
||||
return 0, "", errors.New("token error")
|
||||
}
|
||||
|
||||
// Parse token
|
||||
@@ -38,8 +36,8 @@ func ParseToken(tokenStr string) (*model.LoginAccount, error) {
|
||||
return []byte(config.Conf.Jwt.Key), nil
|
||||
})
|
||||
if err != nil || token == nil {
|
||||
return nil, err
|
||||
return 0, "", err
|
||||
}
|
||||
i := token.Claims.(jwt.MapClaims)
|
||||
return &model.LoginAccount{Id: uint64(i["id"].(float64)), Username: i["username"].(string)}, nil
|
||||
return uint64(i["id"].(float64)), i["username"].(string), nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ws
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"time"
|
||||
|
||||
@@ -16,18 +17,20 @@ type UserId uint64
|
||||
type ReadMsgHandlerFunc func([]byte)
|
||||
|
||||
type Client struct {
|
||||
ClientId string // 标识ID
|
||||
UserId UserId // 用户ID
|
||||
WsConn *websocket.Conn // 用户连接
|
||||
ClientId string // 标识ID
|
||||
UserId UserId // 用户ID
|
||||
ClientUuid string // 客户端UUID
|
||||
WsConn *websocket.Conn // 用户连接
|
||||
|
||||
ReadMsgHander ReadMsgHandlerFunc // 读取消息处理函数
|
||||
}
|
||||
|
||||
func NewClient(userId UserId, socket *websocket.Conn) *Client {
|
||||
func NewClient(userId UserId, clientUuid string, socket *websocket.Conn) *Client {
|
||||
cli := &Client{
|
||||
ClientId: stringx.Rand(16),
|
||||
UserId: userId,
|
||||
WsConn: socket,
|
||||
ClientId: stringx.Rand(16),
|
||||
UserId: userId,
|
||||
ClientUuid: clientUuid,
|
||||
WsConn: socket,
|
||||
}
|
||||
|
||||
return cli
|
||||
@@ -64,6 +67,8 @@ func (c *Client) Read() {
|
||||
|
||||
// 向客户端写入消息
|
||||
func (c *Client) WriteMsg(msg *Msg) error {
|
||||
logx.Debugf("发送消息: toUid=%v, data=%v", c.UserId, msg.Data)
|
||||
|
||||
if msg.Type == JsonMsg {
|
||||
bytes, _ := json.Marshal(msg.Data)
|
||||
return c.WsConn.WriteMessage(websocket.TextMessage, bytes)
|
||||
|
||||
@@ -11,7 +11,7 @@ const heartbeatInterval = 25 * time.Second
|
||||
|
||||
// 连接管理
|
||||
type ClientManager struct {
|
||||
ClientMap map[UserId]*Client // 全部的连接, key->userid, value->&client
|
||||
ClientMap map[string]*Client // 全部的连接, key->token, value->&client
|
||||
RwLock sync.RWMutex // 读写锁
|
||||
|
||||
ConnectChan chan *Client // 连接处理
|
||||
@@ -21,7 +21,7 @@ type ClientManager struct {
|
||||
|
||||
func NewClientManager() (clientManager *ClientManager) {
|
||||
return &ClientManager{
|
||||
ClientMap: make(map[UserId]*Client),
|
||||
ClientMap: make(map[string]*Client),
|
||||
ConnectChan: make(chan *Client, 10),
|
||||
DisConnectChan: make(chan *Client, 10),
|
||||
MsgChan: make(chan *Msg, 100),
|
||||
@@ -58,12 +58,12 @@ func (manager *ClientManager) CloseClient(client *Client) {
|
||||
}
|
||||
|
||||
// 根据用户id关闭客户端连接
|
||||
func (manager *ClientManager) CloseByUid(uid UserId) {
|
||||
manager.CloseClient(manager.GetByUid(UserId(uid)))
|
||||
func (manager *ClientManager) CloseByClientUuid(clientUuid string) {
|
||||
manager.CloseClient(manager.GetByClientUuid(clientUuid))
|
||||
}
|
||||
|
||||
// 获取所有的客户端
|
||||
func (manager *ClientManager) AllClient() map[UserId]*Client {
|
||||
func (manager *ClientManager) AllClient() map[string]*Client {
|
||||
manager.RwLock.RLock()
|
||||
defer manager.RwLock.RUnlock()
|
||||
|
||||
@@ -74,7 +74,19 @@ func (manager *ClientManager) AllClient() map[UserId]*Client {
|
||||
func (manager *ClientManager) GetByUid(userId UserId) *Client {
|
||||
manager.RwLock.RLock()
|
||||
defer manager.RwLock.RUnlock()
|
||||
return manager.ClientMap[userId]
|
||||
for _, client := range manager.ClientMap {
|
||||
if userId == client.UserId {
|
||||
return client
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 通过userId获取
|
||||
func (manager *ClientManager) GetByClientUuid(uuid string) *Client {
|
||||
manager.RwLock.RLock()
|
||||
defer manager.RwLock.RUnlock()
|
||||
return manager.ClientMap[uuid]
|
||||
}
|
||||
|
||||
// 客户端数量
|
||||
@@ -85,9 +97,8 @@ func (manager *ClientManager) Count() int {
|
||||
}
|
||||
|
||||
// 发送json数据给指定用户
|
||||
func (manager *ClientManager) SendJsonMsg(userId UserId, data any) {
|
||||
logx.Debugf("发送消息: toUid=%v, data=%v", userId, data)
|
||||
manager.MsgChan <- &Msg{ToUserId: userId, Data: data, Type: JsonMsg}
|
||||
func (manager *ClientManager) SendJsonMsg(clientUuid string, data any) {
|
||||
manager.MsgChan <- &Msg{ToClientUuid: clientUuid, Data: data, Type: JsonMsg}
|
||||
}
|
||||
|
||||
// 监听并发送给客户端信息
|
||||
@@ -95,7 +106,7 @@ func (manager *ClientManager) WriteMessage() {
|
||||
go func() {
|
||||
for {
|
||||
msg := <-manager.MsgChan
|
||||
if cli := manager.GetByUid(msg.ToUserId); cli != nil {
|
||||
if cli := manager.GetByClientUuid(msg.ToClientUuid); cli != nil {
|
||||
if err := cli.WriteMsg(msg); err != nil {
|
||||
manager.CloseClient(cli)
|
||||
}
|
||||
@@ -130,7 +141,7 @@ func (manager *ClientManager) HeartbeatTimer() {
|
||||
|
||||
// 处理建立连接
|
||||
func (manager *ClientManager) doConnect(client *Client) {
|
||||
cli := manager.GetByUid(client.UserId)
|
||||
cli := manager.GetByClientUuid(client.ClientUuid)
|
||||
if cli != nil {
|
||||
manager.doDisconnect(cli)
|
||||
}
|
||||
@@ -152,11 +163,11 @@ func (manager *ClientManager) doDisconnect(client *Client) {
|
||||
func (manager *ClientManager) addClient2Map(client *Client) {
|
||||
manager.RwLock.Lock()
|
||||
defer manager.RwLock.Unlock()
|
||||
manager.ClientMap[client.UserId] = client
|
||||
manager.ClientMap[client.ClientUuid] = client
|
||||
}
|
||||
|
||||
func (manager *ClientManager) delClient4Map(client *Client) {
|
||||
manager.RwLock.Lock()
|
||||
defer manager.RwLock.Unlock()
|
||||
delete(manager.ClientMap, client.UserId)
|
||||
delete(manager.ClientMap, client.ClientUuid)
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@ type Msg struct {
|
||||
ToUserId UserId
|
||||
Data any
|
||||
|
||||
Type MsgType // 消息类型
|
||||
Type MsgType // 消息类型
|
||||
ToClientUuid string
|
||||
}
|
||||
|
||||
@@ -21,18 +21,21 @@ func init() {
|
||||
}
|
||||
|
||||
// 添加ws客户端
|
||||
func AddClient(userId uint64, conn *websocket.Conn) *Client {
|
||||
cli := NewClient(UserId(userId), conn)
|
||||
func AddClient(userId uint64, clientUuid string, conn *websocket.Conn) *Client {
|
||||
if len(clientUuid) == 0 {
|
||||
return nil
|
||||
}
|
||||
cli := NewClient(UserId(userId), clientUuid, conn)
|
||||
cli.Read()
|
||||
Manager.AddClient(cli)
|
||||
return cli
|
||||
}
|
||||
|
||||
func CloseClient(userid uint64) {
|
||||
Manager.CloseByUid(UserId(userid))
|
||||
func CloseClient(clientUuid string) {
|
||||
Manager.CloseByClientUuid(clientUuid)
|
||||
}
|
||||
|
||||
// 对指定用户发送json类型消息
|
||||
func SendJsonMsg(userId uint64, msg any) {
|
||||
Manager.SendJsonMsg(UserId(userId), msg)
|
||||
func SendJsonMsg(clientUuid string, msg any) {
|
||||
Manager.SendJsonMsg(clientUuid, msg)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user