package broadcast import ( "context" "encoding/json" "strconv" "strings" "hyapp/pkg/appcode" broadcastdomain "hyapp/services/activity-service/internal/domain/broadcast" ) // ConsumeRedPacketWalletEvent converts one MQ-delivered wallet red-packet fact into IM outbox records. func (s *Service) ConsumeRedPacketWalletEvent(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) error { if s == nil { return nil } ctx = appcode.WithContext(ctx, event.AppCode) switch event.EventType { case "WalletRedPacketCreated": return s.consumeRedPacketCreated(ctx, event) case "WalletRedPacketClaimed": if strings.EqualFold(event.Status, "finished") { return s.publishRedPacketRoomEvent(ctx, event, "STATUS_CHANGED", event.CreatedAtMS) } case "WalletRedPacketRefunded": return s.publishRedPacketRoomEvent(ctx, event, "STATUS_CHANGED", event.CreatedAtMS) } return nil } func (s *Service) ConsumeRedPacketCreatedEvent(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) error { if event.EventType == "" { event.EventType = "WalletRedPacketCreated" } return s.ConsumeRedPacketWalletEvent(ctx, event) } func (s *Service) consumeRedPacketCreated(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) error { if event.RoomID == "" { return nil } enriched, err := s.withRedPacketSenderProfile(ctx, event) if err != nil { return err } event = enriched if err := s.publishRedPacketRoomEvent(ctx, event, "CREATED", event.CreatedAtMS); err != nil { return err } if strings.EqualFold(event.PacketType, "delayed") && event.RegionID > 0 { if _, err := s.PublishRegionBroadcast(ctx, PublishInput{ EventID: event.EventID + ":region_created", BroadcastType: broadcastdomain.TypeRedPacket, RegionID: event.RegionID, PayloadJSON: redPacketIMPayload(event, "CREATED", "REGION"), }); err != nil { return err } } if strings.EqualFold(event.PacketType, "delayed") && event.OpenAtMS > 0 { _, err := s.PublishRoomBroadcast(ctx, event.RoomID, PublishInput{ EventID: event.EventID + ":open_due", BroadcastType: broadcastdomain.TypeRedPacket, RegionID: event.RegionID, PayloadJSON: redPacketIMPayload(event, "OPEN_DUE", "ROOM"), DeliverAtMS: event.OpenAtMS, }) return err } return nil } func (s *Service) withRedPacketSenderProfile(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) (broadcastdomain.RedPacketWalletEvent, error) { if event.SenderUserID <= 0 || s == nil || s.senderProfileSource == nil { return event, nil } profile, err := s.senderProfileSource.GetSenderProfile(ctx, event.SenderUserID) if err != nil { return event, err } if event.SenderAccount == "" { event.SenderAccount = profile.Account } if event.SenderNickname == "" { event.SenderNickname = profile.Nickname } if event.SenderAvatar == "" { event.SenderAvatar = profile.Avatar } if event.RegionCode == "" { event.RegionCode = profile.RegionCode } return event, nil } func (s *Service) publishRedPacketRoomEvent(ctx context.Context, event broadcastdomain.RedPacketWalletEvent, eventName string, fallbackDeliverAtMS int64) error { if event.RoomID == "" { return nil } _, err := s.PublishRoomBroadcast(ctx, event.RoomID, PublishInput{ EventID: event.EventID, BroadcastType: broadcastdomain.TypeRedPacket, RegionID: event.RegionID, PayloadJSON: redPacketIMPayload(event, eventName, "ROOM"), DeliverAtMS: fallbackDeliverAtMS, }) return err } func redPacketIMPayload(event broadcastdomain.RedPacketWalletEvent, eventName string, scope string) string { packetMode := "IMMEDIATE" if strings.EqualFold(event.PacketType, "delayed") { packetMode = "DELAYED" } status := strings.ToUpper(strings.TrimSpace(event.Status)) if status == "" { status = "ACTIVE" } payload := map[string]any{ "type": "ROOM_RED_PACKET", "data": map[string]any{ "event": eventName, "scope": scope, "packetNo": event.PacketID, "packetId": event.PacketID, "roomId": event.RoomID, "regionId": event.RegionID, "regionCode": event.RegionCode, "packetMode": packetMode, "senderUserId": redPacketUserIDString(event.SenderUserID), "actualAccount": redPacketSenderAccount(event), "userNickname": event.SenderNickname, "userAvatar": event.SenderAvatar, "totalAmount": event.TotalAmount, "totalCount": event.TotalCount, "remainAmount": event.RemainingAmount, "remainCount": event.RemainingCount, "status": status, "delaySeconds": redPacketDelaySeconds(event), "refundedAmount": event.RefundedAmount, "claimStartTime": event.OpenAtMS, "expireTime": event.ExpiresAtMS, "serverTime": event.CreatedAtMS, }, } encoded, err := json.Marshal(payload) if err != nil { return `{"type":"ROOM_RED_PACKET","data":{}}` } return string(encoded) } func redPacketUserIDString(userID int64) string { if userID <= 0 { return "" } return strconv.FormatInt(userID, 10) } func redPacketSenderAccount(event broadcastdomain.RedPacketWalletEvent) string { if strings.TrimSpace(event.SenderAccount) != "" { return event.SenderAccount } return redPacketUserIDString(event.SenderUserID) } func redPacketDelaySeconds(event broadcastdomain.RedPacketWalletEvent) int64 { if !strings.EqualFold(event.PacketType, "delayed") || event.OpenAtMS <= event.CreatedAtMS { return 0 } return (event.OpenAtMS - event.CreatedAtMS) / 1000 }