From 9d8ce128167dd78ca6350b0bb988950e0c9464be Mon Sep 17 00:00:00 2001 From: Mazyar Date: Sat, 11 Jul 2026 15:49:04 +0330 Subject: [PATCH 1/2] Refactor notification service to support multiple delivery channels - Updated the `sendToRecipient` method to iterate over the channels specified in the notification request, allowing notifications to be persisted for each channel. - Modified the `persistNotification` method to accept a channel parameter, enhancing the logic for handling different delivery methods (email, push, in-app). - Improved error logging to include the channel information when persisting notifications fails, providing better context for debugging. This update enhances the flexibility of the notification service by enabling it to handle multiple delivery channels more effectively, improving overall notification management. --- internal/notification/service.go | 40 +++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/internal/notification/service.go b/internal/notification/service.go index e14e072..6ee77e6 100644 --- a/internal/notification/service.go +++ b/internal/notification/service.go @@ -99,11 +99,14 @@ func (s *notificationService) Send(ctx context.Context, req *NotificationRequest } func (s *notificationService) sendToRecipient(ctx context.Context, recipient notificationRecipient, req *NotificationRequest, link, image string) { - if err := s.persistNotification(ctx, recipient, req, link, image); err != nil { - s.logger.Error("Failed to persist in-app notification", map[string]interface{}{ - "error": err.Error(), - "user_id": recipient.UserID, - }) + for _, channel := range req.Channels { + if err := s.persistNotification(ctx, recipient, req, link, image, channel); err != nil { + s.logger.Error("Failed to persist notification", map[string]interface{}{ + "error": err.Error(), + "user_id": recipient.UserID, + "channel": channel, + }) + } } metadata := map[string]any{ @@ -149,15 +152,26 @@ func (s *notificationService) sendToRecipient(ctx context.Context, recipient not } } -func (s *notificationService) persistNotification(ctx context.Context, recipient notificationRecipient, req *NotificationRequest, link, image string) error { - methods := map[string]string{ - "in_app": "true", - } - if slices.Contains(req.Channels, DeliveryChannelEmail) && recipient.Email != "" { +func (s *notificationService) persistNotification(ctx context.Context, recipient notificationRecipient, req *NotificationRequest, link, image string, channel DeliveryChannel) error { + methods := map[string]string{} + eventType := string(channel) + + switch channel { + case DeliveryChannelEmail: + if recipient.Email == "" { + return nil + } methods["email"] = recipient.Email - } - if slices.Contains(req.Channels, DeliveryChannelPush) && len(recipient.DeviceTokens) > 0 { + case DeliveryChannelPush: + if len(recipient.DeviceTokens) == 0 { + return nil + } methods["push"] = recipient.DeviceTokens[0] + case DeliveryChannelInApp: + eventType = EventTypeInApp + methods["in_app"] = "true" + default: + return nil } status := string(DeliveryStatusSent) @@ -173,7 +187,7 @@ func (s *notificationService) persistNotification(ctx context.Context, recipient Image: image, Priority: string(req.Priority), Type: string(req.Type), - EventType: EventTypeInApp, + EventType: eventType, Status: status, Methods: methods, Metadata: map[string]any{ From aafae2a26fd65403ad6ae844bea200c6e1798a74 Mon Sep 17 00:00:00 2001 From: Mazyar Date: Sat, 11 Jul 2026 21:06:29 +0330 Subject: [PATCH 2/2] Enhance notification entity and service for improved delivery channel handling - Introduced new event types for notifications, including email and push, to support a broader range of delivery methods. - Added a `persistedEventType` function to map delivery channels to their corresponding event types, improving clarity and maintainability. - Updated the `sendToRecipient` method to ensure in-app notifications are always persisted, enhancing the reliability of notification records. - Improved error logging for in-app notification persistence failures, providing better context for debugging. This update enhances the notification system's flexibility and reliability by supporting multiple delivery channels and improving error handling. --- internal/notification/entity.go | 22 ++++++++++++++++++++-- internal/notification/service.go | 21 ++++++++++++++++----- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/internal/notification/entity.go b/internal/notification/entity.go index 95292b8..7782444 100644 --- a/internal/notification/entity.go +++ b/internal/notification/entity.go @@ -28,8 +28,26 @@ const ( DeliveryChannelInApp DeliveryChannel = "in_app" ) -// EventTypeInApp identifies notifications stored for the in-app notification center. -const EventTypeInApp = "in_app" +// Persisted event types for stored notifications (admin list / notification center). +const ( + EventTypeInApp = "in_app" + EventTypeEmail = "email" + EventTypePush = "push" +) + +// persistedEventType maps a delivery channel to the event_type stored in MongoDB. +func persistedEventType(channel DeliveryChannel) (string, bool) { + switch channel { + case DeliveryChannelEmail: + return EventTypeEmail, true + case DeliveryChannelPush: + return EventTypePush, true + case DeliveryChannelInApp: + return EventTypeInApp, true + default: + return "", false + } +} // DeliveryStatus represents the delivery status of notification type NotificationStatus string diff --git a/internal/notification/service.go b/internal/notification/service.go index 6ee77e6..f2f1fd8 100644 --- a/internal/notification/service.go +++ b/internal/notification/service.go @@ -99,7 +99,18 @@ func (s *notificationService) Send(ctx context.Context, req *NotificationRequest } func (s *notificationService) sendToRecipient(ctx context.Context, recipient notificationRecipient, req *NotificationRequest, link, image string) { + // Always persist an in-app record so every send appears in the notification center. + if err := s.persistNotification(ctx, recipient, req, link, image, DeliveryChannelInApp); err != nil { + s.logger.Error("Failed to persist in-app notification", map[string]interface{}{ + "error": err.Error(), + "user_id": recipient.UserID, + }) + } + for _, channel := range req.Channels { + if channel == DeliveryChannelInApp { + continue + } if err := s.persistNotification(ctx, recipient, req, link, image, channel); err != nil { s.logger.Error("Failed to persist notification", map[string]interface{}{ "error": err.Error(), @@ -153,9 +164,12 @@ func (s *notificationService) sendToRecipient(ctx context.Context, recipient not } func (s *notificationService) persistNotification(ctx context.Context, recipient notificationRecipient, req *NotificationRequest, link, image string, channel DeliveryChannel) error { - methods := map[string]string{} - eventType := string(channel) + eventType, ok := persistedEventType(channel) + if !ok { + return nil + } + methods := map[string]string{} switch channel { case DeliveryChannelEmail: if recipient.Email == "" { @@ -168,10 +182,7 @@ func (s *notificationService) persistNotification(ctx context.Context, recipient } methods["push"] = recipient.DeviceTokens[0] case DeliveryChannelInApp: - eventType = EventTypeInApp methods["in_app"] = "true" - default: - return nil } status := string(DeliveryStatusSent)