2026-05-08 18:55:47 +08:00

223 lines
6.5 KiB
Go

package voiceroomredpacket
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"strconv"
"strings"
"chatapp3-golang/internal/config"
"chatapp3-golang/internal/model"
"chatapp3-golang/internal/service/tencentim"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
)
type redisNotifier struct {
redis redis.Cmdable
streamKey string
}
func newRedisNotifier(cache redis.Cmdable, streamKey string) *redisNotifier {
return &redisNotifier{redis: cache, streamKey: streamKey}
}
func (n *redisNotifier) publish(ctx context.Context, scope string, regionCode string, excludeRoomID int64, message notificationMessage) error {
if n == nil || n.redis == nil {
return nil
}
body, err := json.Marshal(message)
if err != nil {
return err
}
return n.redis.XAdd(ctx, &redis.XAddArgs{
Stream: n.streamKey,
Values: map[string]any{
"scope": scope,
"regionCode": regionCode,
"excludeRoomId": strconv.FormatInt(excludeRoomID, 10),
"roomId": strconv.FormatInt(message.RoomID, 10),
"packetNo": message.PacketNo,
"body": string(body),
},
}).Err()
}
func (s *Service) notifyCreated(ctx context.Context, packet model.VoiceRoomRedPacket) error {
message := notificationMessage{
Type: "VOICE_ROOM_RED_PACKET",
Event: "CREATED",
PacketNo: packet.PacketNo,
RoomID: packet.RoomID,
PacketMode: packet.PacketMode,
SenderUserID: packet.SenderUserID,
TotalAmount: packet.TotalAmount,
TotalCount: packet.TotalCount,
ClaimStartTime: packet.ClaimStartTime.UnixMilli(),
ExpireTime: packet.ExpireTime.UnixMilli(),
}
if packet.PacketMode == packetModeDelayed {
var notifyErr error
if err := s.notifier.publish(ctx, "REGION", packet.SenderRegionCode, packet.RoomID, message); err != nil {
notifyErr = err
}
if err := s.sendRegionIMRedPacketNotification(ctx, packet); err != nil {
return mergeNotifyErrors(notifyErr, err)
}
return notifyErr
}
var notifyErr error
if err := s.notifier.publish(ctx, "ROOM", packet.SenderRegionCode, 0, message); err != nil {
notifyErr = err
}
if err := s.sendRoomIMRedPacketNotification(ctx, packet); err != nil {
return mergeNotifyErrors(notifyErr, err)
}
return notifyErr
}
func (s *Service) sendRegionIMRedPacketNotification(ctx context.Context, packet model.VoiceRoomRedPacket) error {
if s == nil || s.regionIM == nil {
return tencentim.ErrNotConfigured
}
body, err := s.buildRoomRedPacketIMBody(ctx, packet)
if err != nil {
return err
}
_, err = s.regionIM.SendRegionCustomMessage(ctx, packet.SysOrigin, packet.SenderRegionCode, body)
return err
}
func (s *Service) sendRoomIMRedPacketNotification(ctx context.Context, packet model.VoiceRoomRedPacket) error {
if s == nil || s.im == nil {
return tencentim.ErrNotConfigured
}
groupID, err := s.resolveRoomIMGroupID(ctx, packet.RoomID)
if err != nil {
return err
}
body, err := s.buildRoomRedPacketIMBodyWithRoomAccount(ctx, packet, groupID)
if err != nil {
return err
}
return s.im.SendCustomGroupMessage(ctx, groupID, body)
}
func (s *Service) buildRoomRedPacketIMBody(ctx context.Context, packet model.VoiceRoomRedPacket) (map[string]any, error) {
roomAccount, _ := s.resolveRoomIMGroupID(ctx, packet.RoomID)
return s.buildRoomRedPacketIMBodyWithRoomAccount(ctx, packet, roomAccount)
}
func (s *Service) buildRoomRedPacketIMBodyWithRoomAccount(ctx context.Context, packet model.VoiceRoomRedPacket, roomAccount string) (map[string]any, error) {
profile, err := s.loadBroadcastUserProfile(ctx, packet.SenderUserID)
if err != nil {
return nil, err
}
return map[string]any{
"type": "ROOM_RED_PACKET",
"data": map[string]any{
"userNickname": profile.UserNickname,
"account": profile.Account,
"actualAccount": profile.Account,
"userAvatar": profile.UserAvatar,
"countryCode": profile.CountryCode,
"countryName": profile.CountryName,
"roomAccount": roomAccount,
"roomId": strconv.FormatInt(packet.RoomID, 10),
"packetId": packet.PacketNo,
"packetMode": packet.PacketMode,
"totalAmount": packet.TotalAmount,
"totalCount": packet.TotalCount,
"regionCode": packet.SenderRegionCode,
"claimStartTime": packet.ClaimStartTime.UnixMilli(),
"expireTime": packet.ExpireTime.UnixMilli(),
},
}, nil
}
func (s *Service) resolveRoomIMGroupID(ctx context.Context, roomID int64) (string, error) {
if roomID <= 0 {
return "", fmt.Errorf("room id is required")
}
if s != nil && s.roomProfiles != nil {
profiles, err := s.roomProfiles.MapRoomProfiles(ctx, []int64{roomID})
if err == nil {
if profile, ok := profiles[roomID]; ok {
if roomAccount := strings.TrimSpace(profile.RoomAccount); roomAccount != "" {
return roomAccount, nil
}
}
for _, profile := range profiles {
if int64(profile.ID) == roomID {
if roomAccount := strings.TrimSpace(profile.RoomAccount); roomAccount != "" {
return roomAccount, nil
}
}
}
}
}
var room model.RoomLiveBroadcastVIP
err := s.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), nil
}
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return "", err
}
return strconv.FormatInt(roomID, 10), nil
}
func mergeNotifyErrors(first error, second error) error {
if first == nil {
return second
}
if second == nil {
return first
}
return fmt.Errorf("%v; %w", first, second)
}
func (s *Service) loadBroadcastUserProfile(ctx context.Context, userID int64) (model.UserBaseInfo, error) {
var user model.UserBaseInfo
err := s.db.WithContext(ctx).Where("id = ?", userID).First(&user).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
account := strconv.FormatInt(userID, 10)
return model.UserBaseInfo{
ID: userID,
Account: account,
UserNickname: account,
}, nil
}
if err != nil {
return model.UserBaseInfo{}, err
}
if strings.TrimSpace(user.Account) == "" {
user.Account = strconv.FormatInt(userID, 10)
}
if strings.TrimSpace(user.UserNickname) == "" {
user.UserNickname = user.Account
}
return user, nil
}
func notifyStreamKey(cfg config.Config) string {
if value := cfg.VoiceRoomRedPacket.NotifyStreamKey; value != "" {
return value
}
return defaultNotifyStreamKey
}
func logNotifyFailure(packetNo string, err error) {
if err != nil {
log.Printf("voice room red packet notify failed packetNo=%s err=%v", packetNo, err)
}
}