package voiceroomrocket import ( "context" "encoding/json" "errors" "strconv" "strings" "chatapp3-golang/internal/model" "github.com/redis/go-redis/v9" "gorm.io/gorm" ) func (s *Service) notifyStatusUpdate(ctx context.Context, sysOrigin string, roomID int64, senderUserIDs ...int64) error { data := map[string]any{ "roomId": strconv.FormatInt(roomID, 10), } if status, err := s.GetStatus(ctx, AuthUser{SysOrigin: sysOrigin}, roomID); err == nil && status != nil { payload, _ := json.Marshal(status) _ = json.Unmarshal(payload, &data) } senderUserID := int64(0) if len(senderUserIDs) > 0 { senderUserID = senderUserIDs[0] } if senderUserID > 0 { data["senderUserId"] = strconv.FormatInt(senderUserID, 10) } if s.repo.Redis != nil { body, _ := json.Marshal(map[string]any{ "type": imTypeStatusUpdate, "sysOrigin": sysOrigin, "roomId": strconv.FormatInt(roomID, 10), "data": data, }) streamKey := strings.TrimSpace(s.cfg.VoiceRoomRocket.BroadcastStreamKey) if streamKey == "" { streamKey = "voice_room:rocket:broadcast" } _ = s.repo.Redis.XAdd(ctx, &redis.XAddArgs{ Stream: streamKey, Values: map[string]any{ "type": imTypeStatusUpdate, "roomId": strconv.FormatInt(roomID, 10), "body": string(body), }, }).Err() } _ = s.notifyRegionStatusUpdate(ctx, sysOrigin, senderUserID, data) if s.im == nil { return nil } groupID := s.resolveRoomAccount(ctx, roomID) return s.im.SendCustomGroupMessage(ctx, groupID, map[string]any{ "type": imTypeStatusUpdate, "data": data, }) } func (s *Service) notifyRegionStatusUpdate(ctx context.Context, sysOrigin string, senderUserID int64, data map[string]any) error { if s.regionBroadcast == nil || s.regionResolver == nil || s.userProfiles == nil || senderUserID <= 0 { return nil } profile, err := s.userProfiles.GetUserProfile(ctx, senderUserID) if err != nil { return err } countryCode := strings.TrimSpace(profile.CountryCode) if countryCode == "" { return nil } regionCode, err := s.regionResolver.ResolveRegionCodeByCountryCode(ctx, sysOrigin, countryCode) if err != nil { return err } regionCode = strings.TrimSpace(regionCode) if regionCode == "" { return nil } body := map[string]any{ "type": imTypeStatusUpdate, "data": data, } _, err = s.regionBroadcast.SendRegionCustomMessage(ctx, sysOrigin, regionCode, body) return err } func (s *Service) notifyLaunch(ctx context.Context, launch model.VoiceRoomRocketLaunch) error { triggerUser := map[string]any{} if launch.IgniteUserID != nil && s.userProfiles != nil { if profile, err := s.userProfiles.GetUserProfile(ctx, *launch.IgniteUserID); err == nil { triggerUser = map[string]any{ "userId": strconv.FormatInt(*launch.IgniteUserID, 10), "nickname": profile.UserNickname, "avatar": profile.UserAvatar, "account": profile.Account, "countryCode": profile.CountryCode, "countryName": profile.CountryName, } } } roomName := "" if s.roomProfiles != nil { if profiles, err := s.roomProfiles.MapRoomProfiles(ctx, []int64{launch.RoomID}); err == nil { if profile, ok := profiles[launch.RoomID]; ok { roomName = profile.RoomName if strings.TrimSpace(launch.RoomAccount) == "" && strings.TrimSpace(profile.RoomAccount) != "" { launch.RoomAccount = strings.TrimSpace(profile.RoomAccount) } } } } durationSeconds := defaultBroadcastDurationSeconds if snapshot, err := s.loadConfig(ctx, launch.SysOrigin); err == nil && snapshot.BroadcastDurationSeconds > 0 { durationSeconds = snapshot.BroadcastDurationSeconds } triggerUserID := "" if launch.IgniteUserID != nil { triggerUserID = strconv.FormatInt(*launch.IgniteUserID, 10) } previewRocketURL := "" rocketIconURL := "" rocketAnimationURL := "" if levelConfig, err := s.loadLevelConfig(ctx, launch.SysOrigin, launch.Level); err == nil { previewRocketURL = levelConfig.PreviewRocketURL rocketIconURL = levelConfig.RocketIconURL rocketAnimationURL = levelConfig.RocketAnimationURL } body := map[string]any{ "type": imTypeLaunchBroadcast, "data": map[string]any{ "roomId": strconv.FormatInt(launch.RoomID, 10), "roomAccount": launch.RoomAccount, "roomName": roomName, "roundNo": launch.RoundNo, "level": launch.Level, "durationSeconds": durationSeconds, "previewRocketUrl": previewRocketURL, "rocketIconUrl": rocketIconURL, "rocketAnimationUrl": rocketAnimationURL, "triggerUser": triggerUser, "triggerUserId": triggerUserID, "launchNo": launch.LaunchNo, }, } if s.repo.Redis != nil { payload, _ := json.Marshal(body) streamKey := strings.TrimSpace(s.cfg.VoiceRoomRocket.BroadcastStreamKey) if streamKey == "" { streamKey = "voice_room:rocket:broadcast" } _ = s.repo.Redis.XAdd(ctx, &redis.XAddArgs{ Stream: streamKey, Values: map[string]any{ "type": imTypeLaunchBroadcast, "roomId": strconv.FormatInt(launch.RoomID, 10), "launchNo": launch.LaunchNo, "body": string(payload), }, }).Err() } if s.im == nil { return nil } groupID := launch.RoomAccount if strings.TrimSpace(groupID) == "" { groupID = s.resolveRoomAccount(ctx, launch.RoomID) } return s.im.SendCustomGroupMessage(ctx, groupID, body) } func (s *Service) notifyRewardRecords(ctx context.Context, launch model.VoiceRoomRocketLaunch, records []model.VoiceRoomRocketRewardRecord) error { if len(records) == 0 { return nil } users := make([]string, 0, len(records)) seen := map[int64]struct{}{} for _, record := range records { if record.UserID <= 0 { continue } if _, exists := seen[record.UserID]; exists { continue } seen[record.UserID] = struct{}{} users = append(users, strconv.FormatInt(record.UserID, 10)) } if len(users) == 0 { return nil } body := map[string]any{ "type": imTypeRewardPopup, "data": map[string]any{ "roomId": strconv.FormatInt(launch.RoomID, 10), "launchNo": launch.LaunchNo, "level": launch.Level, "userIds": users, }, } s.publishRewardMessage(ctx, launch.RoomID, launch.LaunchNo, body) if s.im == nil { return nil } groupID := launch.RoomAccount if strings.TrimSpace(groupID) == "" { groupID = s.resolveRoomAccount(ctx, launch.RoomID) } return s.im.SendCustomGroupMessage(ctx, groupID, body) } func (s *Service) notifyRewardRecord(ctx context.Context, record model.VoiceRoomRocketRewardRecord) error { profile := s.mapUserProfiles(ctx, []int64{record.UserID})[record.UserID] body := map[string]any{ "type": imTypeRewardUser, "data": map[string]any{ "roomId": strconv.FormatInt(record.RoomID, 10), "launchNo": record.LaunchNo, "level": record.Level, "userId": strconv.FormatInt(record.UserID, 10), "userAvatar": profile.UserAvatar, "userNickname": profile.UserNickname, "account": profile.Account, "rewardScene": record.RewardScene, "rewardType": record.RewardType, "rewardName": record.RewardName, "rewardAmount": record.RewardAmount, "rewardRecordId": strconv.FormatInt(record.ID, 10), }, } s.publishRewardMessage(ctx, record.RoomID, record.LaunchNo, body) if s.im == nil { return nil } return s.im.SendCustomGroupMessage(ctx, s.resolveRoomAccount(ctx, record.RoomID), body) } func (s *Service) publishRewardMessage(ctx context.Context, roomID int64, launchNo string, body map[string]any) { if s.repo.Redis == nil { return } payload, _ := json.Marshal(body) streamKey := strings.TrimSpace(s.cfg.VoiceRoomRocket.BroadcastStreamKey) if streamKey == "" { streamKey = "voice_room:rocket:broadcast" } _ = s.repo.Redis.XAdd(ctx, &redis.XAddArgs{ Stream: streamKey, Values: map[string]any{ "type": body["type"], "roomId": strconv.FormatInt(roomID, 10), "launchNo": launchNo, "body": string(payload), }, }).Err() } func (s *Service) resolveRoomAccount(ctx context.Context, roomID int64) string { if roomID <= 0 { return "" } if s.roomProfiles != nil { profiles, err := s.roomProfiles.MapRoomProfiles(ctx, []int64{roomID}) if err == nil { if profile, ok := profiles[roomID]; ok && strings.TrimSpace(profile.RoomAccount) != "" { return strings.TrimSpace(profile.RoomAccount) } for _, profile := range profiles { if int64(profile.ID) == roomID && strings.TrimSpace(profile.RoomAccount) != "" { return strings.TrimSpace(profile.RoomAccount) } } } } var room model.RoomLiveBroadcastVIP err := s.repo.DB.WithContext(ctx). Where("room_id = ? OR id = ?", roomID, roomID). Order("update_time DESC"). First(&room).Error if err == nil && room.RoomAccount > 0 { return strconv.FormatInt(room.RoomAccount, 10) } if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return strconv.FormatInt(roomID, 10) } return strconv.FormatInt(roomID, 10) }