123 lines
3.5 KiB
Go
123 lines
3.5 KiB
Go
package voiceroomrocket
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type roomAudienceUser struct {
|
|
UserID int64
|
|
LastSeenTime time.Time
|
|
}
|
|
|
|
func (s *Service) listOnlineRoomUsers(ctx context.Context, sysOrigin string, roomID int64, now time.Time) ([]roomAudienceUser, error) {
|
|
if s.repo.Redis == nil {
|
|
return nil, nil
|
|
}
|
|
members, err := s.repo.Redis.SMembers(ctx, onlineRoomKey(sysOrigin, roomID)).Result()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
users := make([]roomAudienceUser, 0, len(members))
|
|
seen := make(map[int64]struct{}, len(members))
|
|
for _, raw := range members {
|
|
userID, err := strconv.ParseInt(strings.TrimSpace(raw), 10, 64)
|
|
if err != nil || userID <= 0 {
|
|
continue
|
|
}
|
|
if _, exists := seen[userID]; exists {
|
|
continue
|
|
}
|
|
seen[userID] = struct{}{}
|
|
seenAt, active := s.loadActiveRoomSeenTime(ctx, sysOrigin, roomID, userID, now)
|
|
if !active {
|
|
_ = s.repo.Redis.SRem(ctx, onlineRoomKey(sysOrigin, roomID), userID).Err()
|
|
continue
|
|
}
|
|
users = append(users, roomAudienceUser{
|
|
UserID: userID,
|
|
LastSeenTime: seenAt,
|
|
})
|
|
}
|
|
sort.Slice(users, func(i, j int) bool {
|
|
return users[i].UserID < users[j].UserID
|
|
})
|
|
return users, nil
|
|
}
|
|
|
|
func (s *Service) loadActiveRoomSeenTime(ctx context.Context, sysOrigin string, roomID, userID int64, now time.Time) (time.Time, bool) {
|
|
if s.repo.Redis == nil {
|
|
return time.Time{}, false
|
|
}
|
|
rawRoomID, err := s.repo.Redis.Get(ctx, userRoomKey(sysOrigin, userID)).Result()
|
|
if err != nil {
|
|
return time.Time{}, false
|
|
}
|
|
currentRoomID, err := strconv.ParseInt(strings.TrimSpace(rawRoomID), 10, 64)
|
|
if err != nil || currentRoomID != roomID {
|
|
return time.Time{}, false
|
|
}
|
|
seenAt, ok := s.loadUserSeenTimeStrict(ctx, sysOrigin, userID, now)
|
|
if !ok {
|
|
return time.Time{}, false
|
|
}
|
|
if ttl := s.roomPresenceTTL(); ttl > 0 && now.Sub(seenAt) > ttl {
|
|
return time.Time{}, false
|
|
}
|
|
return seenAt, true
|
|
}
|
|
|
|
func (s *Service) loadUserSeenTimeStrict(ctx context.Context, sysOrigin string, userID int64, fallback time.Time) (time.Time, bool) {
|
|
if s.repo.Redis == nil {
|
|
return time.Time{}, false
|
|
}
|
|
raw, err := s.repo.Redis.Get(ctx, userSeenKey(sysOrigin, userID)).Result()
|
|
if err != nil {
|
|
return time.Time{}, false
|
|
}
|
|
millis, err := strconv.ParseInt(strings.TrimSpace(raw), 10, 64)
|
|
if err != nil || millis <= 0 {
|
|
return time.Time{}, false
|
|
}
|
|
return resolveMillisTime(millis, fallback), true
|
|
}
|
|
|
|
func (s *Service) loadUserSeenTime(ctx context.Context, sysOrigin string, userID int64, fallback time.Time) time.Time {
|
|
if s.repo.Redis == nil {
|
|
return fallback
|
|
}
|
|
raw, err := s.repo.Redis.Get(ctx, userSeenKey(sysOrigin, userID)).Result()
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
millis, err := strconv.ParseInt(strings.TrimSpace(raw), 10, 64)
|
|
if err != nil || millis <= 0 {
|
|
return fallback
|
|
}
|
|
return resolveMillisTime(millis, fallback)
|
|
}
|
|
|
|
func (s *Service) roomPresenceTTL() time.Duration {
|
|
seconds := s.cfg.VoiceRoomRedPacket.PresenceTTLSeconds
|
|
if seconds <= 0 {
|
|
seconds = defaultPresenceTTLSeconds
|
|
}
|
|
return time.Duration(seconds) * time.Second
|
|
}
|
|
|
|
func onlineRoomKey(sysOrigin string, roomID int64) string {
|
|
return fmt.Sprintf("voice_room:online:%s:%d", strings.ToUpper(strings.TrimSpace(sysOrigin)), roomID)
|
|
}
|
|
|
|
func userRoomKey(sysOrigin string, userID int64) string {
|
|
return fmt.Sprintf("voice_room:user_room:%s:%d", strings.ToUpper(strings.TrimSpace(sysOrigin)), userID)
|
|
}
|
|
|
|
func userSeenKey(sysOrigin string, userID int64) string {
|
|
return fmt.Sprintf("voice_room:user_seen:%s:%d", strings.ToUpper(strings.TrimSpace(sysOrigin)), userID)
|
|
}
|