fix: sending daily result report instead of hourly
This commit is contained in:
parent
c77355ad4c
commit
144cb0a42c
|
|
@ -3,10 +3,9 @@ package messenger
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/resend/resend-go/v2"
|
"github.com/resend/resend-go/v2"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Service) SendEmail(ctx context.Context, receiverEmail, message string, subject string) error {
|
func (s *Service) SendEmail(ctx context.Context, receiverEmail, message string, messageHTML string, subject string) error {
|
||||||
apiKey := s.config.ResendApiKey
|
apiKey := s.config.ResendApiKey
|
||||||
client := resend.NewClient(apiKey)
|
client := resend.NewClient(apiKey)
|
||||||
formattedSenderEmail := "FortuneBets <" + s.config.ResendSenderEmail + ">"
|
formattedSenderEmail := "FortuneBets <" + s.config.ResendSenderEmail + ">"
|
||||||
|
|
@ -15,6 +14,7 @@ func (s *Service) SendEmail(ctx context.Context, receiverEmail, message string,
|
||||||
To: []string{receiverEmail},
|
To: []string{receiverEmail},
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
Text: message,
|
Text: message,
|
||||||
|
Html: messageHTML,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := client.Emails.Send(params)
|
_, err := client.Emails.Send(params)
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,7 @@ func (s *Service) handleNotification(notification *domain.Notification) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.DeliveryChannelEmail:
|
case domain.DeliveryChannelEmail:
|
||||||
err := s.SendNotificationEmail(ctx, notification.RecipientID, notification.Payload.Headline, notification.Payload.Message)
|
err := s.SendNotificationEmail(ctx, notification.RecipientID, notification.Payload.Message, notification.Payload.Headline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
notification.DeliveryStatus = domain.DeliveryStatusFailed
|
notification.DeliveryStatus = domain.DeliveryStatusFailed
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -371,7 +371,7 @@ func (s *Service) SendNotificationEmail(ctx context.Context, recipientID int64,
|
||||||
if user.Email == "" {
|
if user.Email == "" {
|
||||||
return fmt.Errorf("email is invalid")
|
return fmt.Errorf("email is invalid")
|
||||||
}
|
}
|
||||||
err = s.messengerSvc.SendEmail(ctx, user.Email, message, subject)
|
err = s.messengerSvc.SendEmail(ctx, user.Email, message, message, subject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.mongoLogger.Error("[NotificationSvc.HandleNotification] Failed to send notification SMS",
|
s.mongoLogger.Error("[NotificationSvc.HandleNotification] Failed to send notification SMS",
|
||||||
zap.Int64("recipient_id", recipientID),
|
zap.Int64("recipient_id", recipientID),
|
||||||
|
|
@ -440,7 +440,7 @@ func (s *Service) retryFailedNotifications() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case domain.DeliveryChannelEmail:
|
case domain.DeliveryChannelEmail:
|
||||||
if err := s.SendNotificationEmail(ctx, notification.RecipientID, notification.Payload.Headline, notification.Payload.Message); err == nil {
|
if err := s.SendNotificationEmail(ctx, notification.RecipientID, notification.Payload.Message, notification.Payload.Headline); err == nil {
|
||||||
notification.DeliveryStatus = domain.DeliveryStatusSent
|
notification.DeliveryStatus = domain.DeliveryStatusSent
|
||||||
if _, err := s.repo.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
|
if _, err := s.repo.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
|
||||||
s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Failed to update after retry",
|
s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Failed to update after retry",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/league"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/league"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger"
|
||||||
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification"
|
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
||||||
|
|
@ -33,6 +34,7 @@ type Service struct {
|
||||||
eventSvc event.Service
|
eventSvc event.Service
|
||||||
leagueSvc league.Service
|
leagueSvc league.Service
|
||||||
notificationSvc *notificationservice.Service
|
notificationSvc *notificationservice.Service
|
||||||
|
messengerSvc *messenger.Service
|
||||||
userSvc user.Service
|
userSvc user.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,6 +48,7 @@ func NewService(
|
||||||
eventSvc event.Service,
|
eventSvc event.Service,
|
||||||
leagueSvc league.Service,
|
leagueSvc league.Service,
|
||||||
notificationSvc *notificationservice.Service,
|
notificationSvc *notificationservice.Service,
|
||||||
|
messengerSvc *messenger.Service,
|
||||||
userSvc user.Service,
|
userSvc user.Service,
|
||||||
) *Service {
|
) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
|
|
@ -59,6 +62,7 @@ func NewService(
|
||||||
eventSvc: eventSvc,
|
eventSvc: eventSvc,
|
||||||
leagueSvc: leagueSvc,
|
leagueSvc: leagueSvc,
|
||||||
notificationSvc: notificationSvc,
|
notificationSvc: notificationSvc,
|
||||||
|
messengerSvc: messengerSvc,
|
||||||
userSvc: userSvc,
|
userSvc: userSvc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -491,6 +495,7 @@ func (s *Service) CheckAndSendResultNotifications(ctx context.Context, createdAf
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildHeadlineAndMessage(counts domain.ResultLog) (string, string) {
|
func buildHeadlineAndMessage(counts domain.ResultLog) (string, string) {
|
||||||
|
|
||||||
totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount
|
totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount
|
||||||
totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets + counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets
|
totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets + counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets
|
||||||
if totalIssues == 0 {
|
if totalIssues == 0 {
|
||||||
|
|
@ -521,6 +526,120 @@ func buildHeadlineAndMessage(counts domain.ResultLog) (string, string) {
|
||||||
return headline, message
|
return headline, message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildHeadlineAndMessageEmail(counts domain.ResultLog, user domain.User) (string, string, string) {
|
||||||
|
totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount +
|
||||||
|
counts.StatusPostponedCount + counts.StatusRemovedCount
|
||||||
|
totalEvents := counts.StatusEndedCount + counts.StatusNotFinishedCount +
|
||||||
|
counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount
|
||||||
|
totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets +
|
||||||
|
counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets
|
||||||
|
|
||||||
|
greeting := fmt.Sprintf("Hi %s %s,", user.FirstName, user.LastName)
|
||||||
|
|
||||||
|
if totalIssues == 0 {
|
||||||
|
headline := "✅ Daily Results Report — All Events Processed Successfully"
|
||||||
|
plain := fmt.Sprintf(`%s
|
||||||
|
|
||||||
|
Daily Results Summary:
|
||||||
|
- %d Ended Events
|
||||||
|
- %d Total Bets
|
||||||
|
|
||||||
|
All events were processed successfully, and no issues were detected.
|
||||||
|
|
||||||
|
Best regards,
|
||||||
|
The System`, greeting, counts.StatusEndedCount, totalBets)
|
||||||
|
|
||||||
|
html := fmt.Sprintf(`<p>%s</p>
|
||||||
|
<h2>Daily Results Summary</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>%d Ended Events</strong></li>
|
||||||
|
<li><strong>%d Total Bets</strong></li>
|
||||||
|
</ul>
|
||||||
|
<p>All events were processed successfully, and no issues were detected.</p>
|
||||||
|
<p>Best regards,<br>The System</p>`,
|
||||||
|
greeting, counts.StatusEndedCount, totalBets)
|
||||||
|
|
||||||
|
return headline, plain, html
|
||||||
|
}
|
||||||
|
|
||||||
|
partsPlain := []string{}
|
||||||
|
partsHTML := []string{}
|
||||||
|
|
||||||
|
if counts.StatusNotFinishedCount > 0 {
|
||||||
|
partsPlain = append(partsPlain,
|
||||||
|
fmt.Sprintf("- %d Unresolved Events (%d Bets)", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets))
|
||||||
|
partsHTML = append(partsHTML,
|
||||||
|
fmt.Sprintf("<li><strong>%d Unresolved Events</strong> (%d Bets)</li>", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets))
|
||||||
|
}
|
||||||
|
if counts.StatusToBeFixedCount > 0 {
|
||||||
|
partsPlain = append(partsPlain,
|
||||||
|
fmt.Sprintf("- %d Requires Review (%d Bets)", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets))
|
||||||
|
partsHTML = append(partsHTML,
|
||||||
|
fmt.Sprintf("<li><strong>%d Requires Review</strong> (%d Bets)</li>", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets))
|
||||||
|
}
|
||||||
|
if counts.StatusPostponedCount > 0 {
|
||||||
|
partsPlain = append(partsPlain,
|
||||||
|
fmt.Sprintf("- %d Postponed Events (%d Bets)", counts.StatusPostponedCount, counts.StatusPostponedBets))
|
||||||
|
partsHTML = append(partsHTML,
|
||||||
|
fmt.Sprintf("<li><strong>%d Postponed Events</strong> (%d Bets)</li>", counts.StatusPostponedCount, counts.StatusPostponedBets))
|
||||||
|
}
|
||||||
|
if counts.StatusRemovedCount > 0 {
|
||||||
|
partsPlain = append(partsPlain,
|
||||||
|
fmt.Sprintf("- %d Discarded Events (%d Bets)", counts.StatusRemovedCount, counts.StatusRemovedBets))
|
||||||
|
partsHTML = append(partsHTML,
|
||||||
|
fmt.Sprintf("<li><strong>%d Discarded Events</strong> (%d Bets)</li>", counts.StatusRemovedCount, counts.StatusRemovedBets))
|
||||||
|
}
|
||||||
|
if counts.StatusEndedCount > 0 {
|
||||||
|
partsPlain = append(partsPlain,
|
||||||
|
fmt.Sprintf("- %d Successfully Ended Events (%d Bets)", counts.StatusEndedCount, counts.StatusEndedBets))
|
||||||
|
partsHTML = append(partsHTML,
|
||||||
|
fmt.Sprintf("<li><strong>%d Successfully Ended Events</strong> (%d Bets)</li>", counts.StatusEndedCount, counts.StatusEndedBets))
|
||||||
|
}
|
||||||
|
|
||||||
|
headline := "⚠️ Daily Results Report — Review Required"
|
||||||
|
|
||||||
|
plain := fmt.Sprintf(`%s
|
||||||
|
|
||||||
|
Daily Results Summary:
|
||||||
|
%s
|
||||||
|
|
||||||
|
Totals:
|
||||||
|
- %d Events Processed
|
||||||
|
- %d Total Bets
|
||||||
|
|
||||||
|
Next Steps:
|
||||||
|
Some events require your attention. Please log into the admin dashboard to review pending issues.
|
||||||
|
|
||||||
|
Best regards,
|
||||||
|
The System`,
|
||||||
|
greeting,
|
||||||
|
strings.Join(partsPlain, "\n"),
|
||||||
|
totalEvents,
|
||||||
|
totalBets,
|
||||||
|
)
|
||||||
|
|
||||||
|
html := fmt.Sprintf(`<p>%s</p>
|
||||||
|
<h2>Daily Results Summary</h2>
|
||||||
|
<ul>
|
||||||
|
%s
|
||||||
|
</ul>
|
||||||
|
<h3>Totals</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>%d Events Processed</strong></li>
|
||||||
|
<li><strong>%d Total Bets</strong></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>Next Steps:</strong><br>Some events require your attention. Please <a href="https://your-dashboard-url.example">log into the admin dashboard</a> to review pending issues.</p>
|
||||||
|
<p>Best regards,<br>The System</p>`,
|
||||||
|
greeting,
|
||||||
|
strings.Join(partsHTML, "\n"),
|
||||||
|
totalEvents,
|
||||||
|
totalBets,
|
||||||
|
)
|
||||||
|
|
||||||
|
return headline, plain, html
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func (s *Service) SendAdminResultStatusErrorNotification(
|
func (s *Service) SendAdminResultStatusErrorNotification(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
counts domain.ResultLog,
|
counts domain.ResultLog,
|
||||||
|
|
@ -541,6 +660,8 @@ func (s *Service) SendAdminResultStatusErrorNotification(
|
||||||
}
|
}
|
||||||
|
|
||||||
headline, message := buildHeadlineAndMessage(counts)
|
headline, message := buildHeadlineAndMessage(counts)
|
||||||
|
|
||||||
|
|
||||||
notification := &domain.Notification{
|
notification := &domain.Notification{
|
||||||
ErrorSeverity: domain.NotificationErrorSeverityHigh,
|
ErrorSeverity: domain.NotificationErrorSeverityHigh,
|
||||||
DeliveryStatus: domain.DeliveryStatusPending,
|
DeliveryStatus: domain.DeliveryStatusPending,
|
||||||
|
|
@ -567,9 +688,14 @@ func (s *Service) SendAdminResultStatusErrorNotification(
|
||||||
)
|
)
|
||||||
sendErrors = append(sendErrors, err)
|
sendErrors = append(sendErrors, err)
|
||||||
}
|
}
|
||||||
notification.DeliveryChannel = domain.DeliveryChannelEmail
|
// notification.DeliveryChannel = domain.DeliveryChannelEmail
|
||||||
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
|
if user.Email == "" {
|
||||||
s.mongoLogger.Error("failed to send admin email notification",
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
subject, plain, html := buildHeadlineAndMessageEmail(counts, user)
|
||||||
|
if err := s.messengerSvc.SendEmail(ctx, user.Email, plain, html, subject); err != nil {
|
||||||
|
s.mongoLogger.Error("failed to send admin result report email",
|
||||||
zap.Int64("admin_id", user.ID),
|
zap.Int64("admin_id", user.ID),
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ func (s *Service) SendOtp(ctx context.Context, sentTo string, otpFor domain.OtpF
|
||||||
return fmt.Errorf("invalid sms provider: %s", provider)
|
return fmt.Errorf("invalid sms provider: %s", provider)
|
||||||
}
|
}
|
||||||
case domain.OtpMediumEmail:
|
case domain.OtpMediumEmail:
|
||||||
if err := s.messengerSvc.SendEmail(ctx, sentTo, message, "FortuneBets - One Time Password"); err != nil {
|
if err := s.messengerSvc.SendEmail(ctx, sentTo, message, message, "FortuneBets - One Time Password"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,15 +131,15 @@ func (s *Service) StartGame(ctx context.Context, req domain.GameStartRequest) (*
|
||||||
|
|
||||||
func (s *Service) StartDemoGame(ctx context.Context, req domain.DemoGameRequest) (*domain.GameStartResponse, error) {
|
func (s *Service) StartDemoGame(ctx context.Context, req domain.DemoGameRequest) (*domain.GameStartResponse, error) {
|
||||||
// 1. Check if provider is enabled in DB
|
// 1. Check if provider is enabled in DB
|
||||||
// provider, err := s.repo.GetVirtualGameProviderByID(ctx, req.ProviderID)
|
provider, err := s.repo.GetVirtualGameProviderByID(ctx, req.ProviderID)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, fmt.Errorf("failed to check provider %s: %w", req.ProviderID, err)
|
return nil, fmt.Errorf("failed to check provider %s: %w", req.ProviderID, err)
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if !provider.Enabled {
|
if !provider.Enabled {
|
||||||
// // Provider exists but is disabled → return error
|
// Provider exists but is disabled → return error
|
||||||
// return nil, fmt.Errorf("provider %s is disabled", req.ProviderID)
|
return nil, fmt.Errorf("provider %s is disabled", req.ProviderID)
|
||||||
// }
|
}
|
||||||
|
|
||||||
// 2. Prepare signature params
|
// 2. Prepare signature params
|
||||||
sigParams := map[string]any{
|
sigParams := map[string]any{
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
spec: "0 0 * * * *", // Every Day
|
spec: "0 0 0 * * *", // Every Day
|
||||||
task: func() {
|
task: func() {
|
||||||
mongoLogger.Info("Began Send daily result notification cron task")
|
mongoLogger.Info("Began Send daily result notification cron task")
|
||||||
if err := resultService.CheckAndSendResultNotifications(context.Background(), time.Now().Add(-24*time.Hour)); err != nil {
|
if err := resultService.CheckAndSendResultNotifications(context.Background(), time.Now().Add(-24*time.Hour)); err != nil {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user