2026-05-23 18:38:09 +08:00

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)
}