package notification import ( "context" "errors" "tm/internal/customer" "tm/internal/user" "tm/pkg/logger" "tm/pkg/notification" "tm/pkg/response" ) // NotificationService defines the interface for notification business logic type Service interface { Send(ctx context.Context, req *NotificationRequest) error GetNotifications(ctx context.Context, req *SearchForm, pagination *response.Pagination) (*NotificationListResponse, error) getUsers(ctx context.Context, userIDs []string) ([]notificationRecipient, error) getCustomers(ctx context.Context, values []string, target string) ([]notificationRecipient, error) } // notificationService implements NotificationService interface type notificationService struct { sdk notification.SDK userService user.Service customerService customer.Service logger logger.Logger } // NewNotificationService creates a new notification service func NewNotificationService( sdk notification.SDK, userService user.Service, customerService customer.Service, logger logger.Logger, ) Service { return ¬ificationService{ sdk: sdk, userService: userService, customerService: customerService, logger: logger, } } // Send sends a multiple notification func (s *notificationService) Send(ctx context.Context, req *NotificationRequest) error { s.logger.Info("Sending notification", map[string]interface{}{ "recipient": req.Recipient, "type": req.Type, "priority": req.Priority, }) if req.Target == TargetAudienceAllUsers || req.Target == TargetAudienceSpecificUsers { recipients, err := s.getUsers(ctx, req.Recipient) if err != nil { return err } for _, recipient := range recipients { for _, v := range recipient.DeviceTokens { s.sdk.SendNotification(ctx, ¬ification.NotificationRequest{ UserID: recipient.UserID, Title: req.Title, Message: req.Description, Type: string(req.Type), Priority: string(req.Priority), EventType: notification.EventTypePush, Metadata: map[string]any{ "tender": req.Tender, }, Methods: notification.NotificationMethods{ Push: v, }, ScheduledAt: req.ScheduleAt, }) } if recipient.Email != "" { s.sdk.SendNotification(ctx, ¬ification.NotificationRequest{ UserID: recipient.UserID, Title: req.Title, Message: req.Description, Type: string(req.Type), Priority: string(req.Priority), EventType: notification.EventTypeEmail, Metadata: map[string]any{ "tender": req.Tender, }, Methods: notification.NotificationMethods{ Email: recipient.Email, }, ScheduledAt: req.ScheduleAt, }) } } } else { recipients, err := s.getCustomers(ctx, req.Recipient, string(req.Target)) if err != nil { return err } for _, recipient := range recipients { for _, v := range recipient.DeviceTokens { s.sdk.SendNotification(ctx, ¬ification.NotificationRequest{ UserID: recipient.UserID, Title: req.Title, Message: req.Description, Type: string(req.Type), Priority: string(req.Priority), EventType: notification.EventTypePush, Methods: notification.NotificationMethods{ Push: v, }, ScheduledAt: req.ScheduleAt, }) } if recipient.Email != "" { s.sdk.SendNotification(ctx, ¬ification.NotificationRequest{ UserID: recipient.UserID, Title: req.Title, Message: req.Description, Type: string(req.Type), Priority: string(req.Priority), EventType: notification.EventTypeEmail, Methods: notification.NotificationMethods{ Email: recipient.Email, }, ScheduledAt: req.ScheduleAt, }) } } s.logger.Info("Notification sent", map[string]interface{}{ "recipient": req.Recipient, "channels": req.Channels, }) return nil } return nil } func (s *notificationService) GetNotifications(ctx context.Context, req *SearchForm, pagination *response.Pagination) (*NotificationListResponse, error) { s.logger.Info("Getting notifications", map[string]interface{}{ "status": req.Status, "method": req.Method, "event_type": req.EventType, "type": req.Type, "recipient": req.Recipient, "limit": pagination.Limit, "offset": pagination.Offset, }) notifications, err := s.sdk.GetNotifications(ctx, ¬ification.GetNotificationsRequest{ Status: req.Status, Method: req.Method, EventType: req.EventType, Type: req.Type, UserID: req.Recipient, PerPage: pagination.Limit, Page: pagination.Offset / pagination.Limit, }) if err != nil { s.logger.Error("Failed to get notifications", map[string]interface{}{ "error": err.Error(), }) return nil, err } notificationsResponse := make([]*NotificationResponse, 0) for _, notification := range notifications.Data { notificationsResponse = append(notificationsResponse, &NotificationResponse{ ID: notification.ID, UserID: notification.UserID, Title: notification.Title, Description: notification.Message, Link: ¬ification.Link, Priority: notification.Priority, Type: notification.Type, EventType: notification.EventType, Status: notification.Status, Methods: notification.Methods, Metadata: notification.Metadata, ScheduleAt: notification.ScheduledAt, }) } return &NotificationListResponse{ Notifications: notificationsResponse, Meta: &response.Meta{ Total: notifications.Pagination.Total, Limit: notifications.Pagination.PerPage, Offset: pagination.Offset, Page: notifications.Pagination.CurrentPage, Pages: notifications.Pagination.TotalPages, }, }, nil } func (s *notificationService) getUsers(ctx context.Context, userIDs []string) ([]notificationRecipient, error) { recipients := make([]notificationRecipient, 0) offset := 0 for { var users *user.UserListResponse var err error if len(userIDs) > 0 { users, err = s.userService.GetUsersByIDs(ctx, userIDs) if err != nil { return nil, err } } else { users, err = s.userService.Search(ctx, &user.SearchUsersForm{}, &response.Pagination{Limit: 100, Offset: offset}) if err != nil { return nil, err } offset += 100 if len(users.Users) == 0 { break } } for _, user := range users.Users { recipients = append(recipients, notificationRecipient{ UserID: user.ID, Email: user.Email, DeviceTokens: user.DeviceToken, }) } if len(recipients) == 0 { return nil, errors.New("no users found") } } return recipients, nil } func (s *notificationService) getCustomers(ctx context.Context, values []string, target string) ([]notificationRecipient, error) { recipients := make([]notificationRecipient, 0) if target == string(TargetAudienceSpecificRole) { customers, err := s.customerService.GetCustomersByRole(ctx, values[0]) if err != nil { return nil, err } for _, customer := range customers.Customers { recipients = append(recipients, notificationRecipient{ UserID: customer.ID, Email: customer.Email, DeviceTokens: customer.DeviceToken, }) } } else if target == string(TargetAudienceSpecificCompany) { customers, err := s.customerService.GetCustomersByCompanies(ctx, values) if err != nil { return nil, err } for _, customer := range customers.Customers { recipients = append(recipients, notificationRecipient{ UserID: customer.ID, Email: customer.Email, DeviceTokens: customer.DeviceToken, }) } } else if target == string(TargetAudienceSpecificCustomer) { customers, err := s.customerService.GetCustomersByIDs(ctx, values) if err != nil { return nil, err } for _, customer := range customers.Customers { recipients = append(recipients, notificationRecipient{ UserID: customer.ID, Email: customer.Email, DeviceTokens: customer.DeviceToken, }) } } else { offset := 0 for { customers, err := s.customerService.Search(ctx, &customer.SearchCustomersForm{}, &response.Pagination{Limit: 100, Offset: offset}) if err != nil { return nil, err } for _, customer := range customers.Customers { recipients = append(recipients, notificationRecipient{ UserID: customer.ID, Email: customer.Email, DeviceTokens: customer.DeviceToken, }) } offset += 100 if len(customers.Customers) == 0 { break } } } if len(recipients) == 0 { return nil, errors.New("no customers found") } return recipients, nil }