Yimaru-BackEnd/internal/services/wallet/notification.go
Samuel Tariku 3dfa1255b0 Refactor result notification service and remove redundant code
- Removed the CheckAndSendResultNotifications method from the result service.
- Consolidated notification logic into a new notification.go file.
- Updated email and in-app notification formatting to include event processing periods.
- Added error handling for wallet operations to check if wallets are active before processing transfers.
- Introduced new error for disabled wallets.
- Updated cron jobs to comment out unnecessary tasks.
- Added bulk update functionality for bet outcomes by odd IDs in the odd handler.
- Renamed ticket handler methods for clarity and consistency.
- Updated API version in routes.
2025-10-10 14:59:19 +03:00

290 lines
9.1 KiB
Go

package wallet
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"go.uber.org/zap"
)
func (s *Service) GetAdminNotificationRecipients(ctx context.Context, walletID int64, walletType domain.WalletType) ([]int64, error) {
var recipients []int64
switch walletType {
case domain.BranchWalletType:
branch, err := s.GetBranchByWalletID(ctx, walletID)
if err != nil {
s.mongoLogger.Error("[GetAdminNotificationRecipients] failed to GetBranchWalletByID", zap.Int64("walletID", walletID))
return nil, err
}
// Branch managers will be notified when branch wallet is empty
recipients = append(recipients, branch.BranchManagerID)
// Cashier will be notified
cashiers, err := s.userSvc.GetCashiersByBranch(ctx, branch.ID)
if err != nil {
return nil, err
}
for _, cashier := range cashiers {
recipients = append(recipients, cashier.ID)
}
// Admin will also be notified
admin, err := s.userSvc.GetAdminByCompanyID(ctx, branch.CompanyID)
if err != nil {
return nil, err
}
recipients = append(recipients, admin.ID)
case domain.CompanyWalletType:
company, err := s.GetCompanyByWalletID(ctx, walletID)
if err != nil {
return nil, err
}
recipients = append(recipients, company.AdminID)
default:
return nil, fmt.Errorf("Invalid wallet type")
}
users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{
Role: string(domain.RoleSuperAdmin),
})
if err != nil {
return nil, err
}
for _, user := range users {
recipients = append(recipients, user.ID)
}
return recipients, nil
}
func (s *Service) SendWalletUpdateNotification(ctx context.Context, wallet domain.Wallet) error {
raw, _ := json.Marshal(map[string]any{
"balance": wallet.Balance.Float32(),
"type": wallet.Type,
"timestamp": time.Now(),
})
headline := ""
message := ""
var receiver domain.NotificationRecieverSide
switch wallet.Type {
case domain.StaticWalletType:
headline = "Referral and Bonus Wallet Updated"
message = fmt.Sprintf("Your referral and bonus wallet balance is now %.2f", wallet.Balance.Float32())
receiver = domain.NotificationRecieverSideCustomer
case domain.RegularWalletType:
headline = "Wallet Updated"
message = fmt.Sprintf("Your wallet balance is now %.2f", wallet.Balance.Float32())
receiver = domain.NotificationRecieverSideCustomer
case domain.BranchWalletType:
headline = "Branch Wallet Updated"
message = fmt.Sprintf("branch wallet balance is now %.2f", wallet.Balance.Float32())
receiver = domain.NotificationRecieverSideBranchManager
case domain.CompanyWalletType:
headline = "Company Wallet Updated"
message = fmt.Sprintf("company wallet balance is now %.2f", wallet.Balance.Float32())
receiver = domain.NotificationRecieverSideAdmin
}
// Handle the wallet event: send notification
notification := &domain.Notification{
RecipientID: wallet.UserID,
DeliveryChannel: domain.DeliveryChannelInApp,
Reciever: receiver,
Type: domain.NotificationTypeWalletUpdated,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Level: domain.NotificationLevelInfo,
Priority: 2,
Metadata: raw,
Payload: domain.NotificationPayload{
Headline: headline,
Message: message,
},
}
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
s.mongoLogger.Error("[WalletSvc.SendWalletUpdateNotification] Failed to send notification",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
return nil
}
func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWallet domain.Wallet) error {
// Send different messages
// Send notification to admin team
adminNotification := &domain.Notification{
ErrorSeverity: "low",
IsRead: false,
DeliveryStatus: domain.DeliveryStatusPending,
RecipientID: adminWallet.UserID,
Type: domain.NOTIFICATION_TYPE_ADMIN_ALERT,
Level: domain.NotificationLevelWarning,
Reciever: domain.NotificationRecieverSideAdmin,
DeliveryChannel: domain.DeliveryChannelInApp, // Or any preferred admin channel
Payload: domain.NotificationPayload{
Headline: "CREDIT WARNING: System Running Out of Funds",
Message: fmt.Sprintf(
"Wallet ID %d is running low. Current balance: %.2f",
adminWallet.ID,
adminWallet.Balance.Float32(),
),
},
Priority: 1, // High priority for admin alerts
Metadata: fmt.Appendf(nil, `{
"wallet_id": %d,
"balance": %d,
"notification_type": "admin_alert"
}`, adminWallet.ID, adminWallet.Balance),
}
// Get admin recipients and send to all
adminRecipients, err := s.GetAdminNotificationRecipients(ctx, adminWallet.ID, adminWallet.Type)
if err != nil {
s.mongoLogger.Error("failed to get admin recipients",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
for _, adminID := range adminRecipients {
adminNotification.RecipientID = adminID
if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil {
s.mongoLogger.Error("failed to send admin notification",
zap.Int64("admin_id", adminID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
}
// adminNotification.DeliveryChannel = domain.DeliveryChannelEmail
// if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil {
// s.mongoLogger.Error("failed to send email admin notification",
// zap.Int64("admin_id", adminID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return err
// }
}
return nil
}
func (s *Service) SendAdminWalletInsufficientNotification(ctx context.Context, adminWallet domain.Wallet, amount domain.Currency) error {
// Send notification to admin team
adminNotification := &domain.Notification{
ErrorSeverity: domain.NotificationErrorSeverityLow,
IsRead: false,
DeliveryStatus: domain.DeliveryStatusPending,
RecipientID: adminWallet.UserID,
Type: domain.NOTIFICATION_TYPE_ADMIN_ALERT,
Level: domain.NotificationLevelError,
Reciever: domain.NotificationRecieverSideAdmin,
DeliveryChannel: domain.DeliveryChannelInApp, // Or any preferred admin channel
Payload: domain.NotificationPayload{
Headline: "CREDIT Error: Admin Wallet insufficient to process customer request",
Message: fmt.Sprintf(
"Wallet ID %d. Transaction Amount %.2f. Current balance: %.2f",
adminWallet.ID,
amount.Float32(),
adminWallet.Balance.Float32(),
),
},
Priority: 1, // High priority for admin alerts
Metadata: fmt.Appendf(nil, `{
"wallet_id": %d,
"balance": %d,
"transaction amount": %.2f,
"notification_type": "admin_alert"
}`, adminWallet.ID, adminWallet.Balance, amount.Float32()),
}
// Get admin recipients and send to all
recipients, err := s.GetAdminNotificationRecipients(ctx, adminWallet.ID, adminWallet.Type)
if err != nil {
s.mongoLogger.Error("failed to get admin recipients",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
for _, adminID := range recipients {
adminNotification.RecipientID = adminID
if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil {
s.mongoLogger.Error("failed to send admin notification",
zap.Int64("admin_id", adminID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
}
adminNotification.DeliveryChannel = domain.DeliveryChannelEmail
if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil {
s.mongoLogger.Error("failed to send email admin notification",
zap.Int64("admin_id", adminID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
}
return nil
}
func (s *Service) SendCustomerWalletInsufficientNotification(ctx context.Context, customerWallet domain.Wallet, amount domain.Currency) error {
// Send notification to admin team
customerNotification := &domain.Notification{
ErrorSeverity: domain.NotificationErrorSeverityLow,
IsRead: false,
DeliveryStatus: domain.DeliveryStatusPending,
RecipientID: customerWallet.UserID,
Type: domain.NOTIFICATION_TYPE_WALLET,
Level: domain.NotificationLevelError,
Reciever: domain.NotificationRecieverSideCustomer,
DeliveryChannel: domain.DeliveryChannelInApp, // Or any preferred admin channel
Payload: domain.NotificationPayload{
Headline: "CREDIT Error: Wallet insufficient",
Message: fmt.Sprintf(
"Wallet ID %d. Transaction Amount %.2f. Current balance: %.2f",
customerWallet.ID,
amount.Float32(),
customerWallet.Balance.Float32(),
),
},
Priority: 1, // High priority for admin alerts
Metadata: fmt.Appendf(nil, `{
"wallet_id": %d,
"balance": %d,
"transaction amount": %.2f,
"notification_type": "admin_alert"
}`, customerWallet.ID, customerWallet.Balance, amount.Float32()),
}
if err := s.notificationSvc.SendNotification(ctx, customerNotification); err != nil {
s.mongoLogger.Error("failed to create customer notification",
zap.Int64("customer_id", customerWallet.UserID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
return nil
}