|
|
|
package msggateway
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"github.com/openimsdk/protocol/constant"
|
|
|
|
"github.com/openimsdk/tools/log"
|
|
|
|
"github.com/openimsdk/tools/utils/datautil"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type UserMap interface {
|
|
|
|
GetAll(userID string) ([]*Client, bool)
|
|
|
|
Get(userID string, platformID int) ([]*Client, bool, bool)
|
|
|
|
Set(userID string, v *Client)
|
|
|
|
DeleteClients(userID string, clients []*Client) (isDeleteUser bool)
|
|
|
|
UserState() <-chan UserState
|
|
|
|
GetAllUserStatus(deadline time.Time, nowtime time.Time) []UserState
|
|
|
|
RecvSubChange(userID string, platformIDs []int32) bool
|
|
|
|
}
|
|
|
|
|
|
|
|
type UserState struct {
|
|
|
|
UserID string
|
|
|
|
Online []int32
|
|
|
|
Offline []int32
|
|
|
|
}
|
|
|
|
|
|
|
|
type UserPlatform struct {
|
|
|
|
Time time.Time
|
|
|
|
Clients []*Client
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserPlatform) PlatformIDs() []int32 {
|
|
|
|
if len(u.Clients) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
platformIDs := make([]int32, 0, len(u.Clients))
|
|
|
|
for _, client := range u.Clients {
|
|
|
|
platformIDs = append(platformIDs, int32(client.PlatformID))
|
|
|
|
}
|
|
|
|
return platformIDs
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserPlatform) PlatformIDSet() map[int32]struct{} {
|
|
|
|
if len(u.Clients) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
platformIDs := make(map[int32]struct{})
|
|
|
|
for _, client := range u.Clients {
|
|
|
|
platformIDs[int32(client.PlatformID)] = struct{}{}
|
|
|
|
}
|
|
|
|
return platformIDs
|
|
|
|
}
|
|
|
|
|
|
|
|
func newUserMap() UserMap {
|
|
|
|
return &userMap{
|
|
|
|
data: make(map[string]*UserPlatform),
|
|
|
|
ch: make(chan UserState, 10000),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type userMap struct {
|
|
|
|
lock sync.RWMutex
|
|
|
|
data map[string]*UserPlatform
|
|
|
|
ch chan UserState
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *userMap) RecvSubChange(userID string, platformIDs []int32) bool {
|
|
|
|
u.lock.RLock()
|
|
|
|
defer u.lock.RUnlock()
|
|
|
|
result, ok := u.data[userID]
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
localPlatformIDs := result.PlatformIDSet()
|
|
|
|
for _, platformID := range platformIDs {
|
|
|
|
delete(localPlatformIDs, platformID)
|
|
|
|
}
|
|
|
|
if len(localPlatformIDs) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
u.push(userID, result, nil)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *userMap) push(userID string, userPlatform *UserPlatform, offline []int32) bool {
|
|
|
|
select {
|
|
|
|
case u.ch <- UserState{UserID: userID, Online: userPlatform.PlatformIDs(), Offline: offline}:
|
|
|
|
userPlatform.Time = time.Now()
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *userMap) GetAll(userID string) ([]*Client, bool) {
|
|
|
|
u.lock.RLock()
|
|
|
|
defer u.lock.RUnlock()
|
|
|
|
result, ok := u.data[userID]
|
|
|
|
if !ok {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
return result.Clients, true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *userMap) Get(userID string, platformID int) ([]*Client, bool, bool) {
|
|
|
|
u.lock.RLock()
|
|
|
|
defer u.lock.RUnlock()
|
|
|
|
result, ok := u.data[userID]
|
|
|
|
if !ok {
|
|
|
|
return nil, false, false
|
|
|
|
}
|
|
|
|
var clients []*Client
|
|
|
|
for _, client := range result.Clients {
|
|
|
|
if client.PlatformID == platformID {
|
|
|
|
clients = append(clients, client)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return clients, true, len(clients) > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *userMap) Set(userID string, client *Client) {
|
|
|
|
log.ZDebug(context.Background(), "userMap Set", "userID", userID, "platformID", client.PlatformID, "platform", constant.PlatformIDToName(client.PlatformID), "pointer", fmt.Sprintf("%p", client))
|
|
|
|
u.lock.Lock()
|
|
|
|
defer u.lock.Unlock()
|
|
|
|
result, ok := u.data[userID]
|
|
|
|
if ok {
|
|
|
|
result.Clients = append(result.Clients, client)
|
|
|
|
} else {
|
|
|
|
result = &UserPlatform{
|
|
|
|
Clients: []*Client{client},
|
|
|
|
}
|
|
|
|
u.data[userID] = result
|
|
|
|
}
|
|
|
|
u.push(client.UserID, result, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *userMap) DeleteClients(userID string, clients []*Client) (isDeleteUser bool) {
|
|
|
|
if len(clients) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
u.lock.Lock()
|
|
|
|
defer u.lock.Unlock()
|
|
|
|
result, ok := u.data[userID]
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
offline := make([]int32, 0, len(clients))
|
|
|
|
deleteAddr := datautil.SliceSetAny(clients, func(client *Client) string {
|
|
|
|
return client.ctx.GetRemoteAddr()
|
|
|
|
})
|
|
|
|
tmp := result.Clients
|
|
|
|
result.Clients = result.Clients[:0]
|
|
|
|
for _, client := range tmp {
|
|
|
|
if _, delCli := deleteAddr[client.ctx.GetRemoteAddr()]; delCli {
|
|
|
|
offline = append(offline, int32(client.PlatformID))
|
|
|
|
} else {
|
|
|
|
result.Clients = append(result.Clients, client)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
defer u.push(userID, result, offline)
|
|
|
|
if len(result.Clients) > 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
delete(u.data, userID)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *userMap) GetAllUserStatus(deadline time.Time, nowtime time.Time) (result []UserState) {
|
|
|
|
u.lock.RLock()
|
|
|
|
defer u.lock.RUnlock()
|
|
|
|
result = make([]UserState, 0, len(u.data))
|
|
|
|
for userID, userPlatform := range u.data {
|
|
|
|
if deadline.Before(userPlatform.Time) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
userPlatform.Time = nowtime
|
|
|
|
online := make([]int32, 0, len(userPlatform.Clients))
|
|
|
|
for _, client := range userPlatform.Clients {
|
|
|
|
online = append(online, int32(client.PlatformID))
|
|
|
|
}
|
|
|
|
result = append(result, UserState{UserID: userID, Online: online})
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *userMap) UserState() <-chan UserState {
|
|
|
|
return u.ch
|
|
|
|
}
|