223 lines
6.5 KiB
Go
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)
|
|
}
|
|
}
|