Yimaru-BackEnd/internal/web_server/cron.go

563 lines
18 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package httpserver
import (
"context"
"os"
"time"
// "time"
"log"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
betSvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse"
eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification"
oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/report"
resultsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/result"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/stats"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/orchestration"
"github.com/robfig/cron/v3"
"go.uber.org/zap"
)
func StartBetAPIDataFetchingCrons(
eventService *eventsvc.Service,
oddsService oddssvc.ServiceImpl,
resultService *resultsvc.Service,
mongoLogger *zap.Logger,
) {
c := cron.New(cron.WithSeconds())
schedule := []struct {
spec string
task func()
}{
{
spec: "0 0 * * * *", // Every 1 hour
task: func() {
start := time.Now()
mongoLogger.Info("[BetAPI Event Fetching Crons] Began fetching upcoming events cron task", zap.Time("timestamp", time.Now()))
if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
mongoLogger.Error("[BetAPI Event Fetching Crons] Failed to fetch upcoming events",
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
zap.Error(err),
)
} else {
mongoLogger.Info("[BetAPI Event Fetching Crons] Completed fetching upcoming events without errors",
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
}
},
},
{
spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events)
task: func() {
start := time.Now()
mongoLogger.Info("[BetAPI Pre-Match Odds Fetching Crons] Began fetching pre-match odds cron task",
zap.Time("timestamp", time.Now()),
)
if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
mongoLogger.Error("[BetAPI Pre-Match Odds Fetching Crons] Failed to fetch pre-match odds",
zap.Error(err),
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
} else {
mongoLogger.Info("[BetAPI Pre-Match Odds Fetching Crons] Completed fetching pre-match odds without errors",
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
}
},
},
{
spec: "0 */5 * * * *", // Every 5 Minutes
task: func() {
start := time.Now()
mongoLogger.Info("[BetAPI Check And Update Expired Events Crons] Began update all expired events status cron task",
zap.Time("timestamp", time.Now()),
)
if _, err := resultService.CheckAndUpdateExpiredB365Events(context.Background()); err != nil {
mongoLogger.Error("[BetAPI Check And Update Expired Events Crons] Failed to update expired events status",
zap.Error(err),
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
} else {
mongoLogger.Info("[BetAPI Check And Update Expired Events Crons] Completed expired events without errors",
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
}
},
},
{
spec: "0 */15 * * * *", // Every 15 Minutes
task: func() {
start := time.Now()
mongoLogger.Info("[BetAPI Fetch Result and Update Bets Crons] Began updating bets based on event results cron task")
if err := resultService.FetchB365ResultAndUpdateBets(context.Background()); err != nil {
mongoLogger.Error("[BetAPI Fetch Result and Update Bets Crons] Failed to process result",
zap.Error(err),
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
} else {
mongoLogger.Info("[BetAPI Fetch Result and Update Bets Crons] Completed processing all event result outcomes without errors",
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
}
},
},
{
spec: "0 0 0 * * 1", // Every Monday
task: func() {
mongoLogger.Info("[BetAPI Send Result Notification Crons] Began Send weekly result notification cron task",
zap.Time("timestamp", time.Now()),
)
if err := resultService.CheckAndSendResultNotifications(context.Background(), time.Now().Add(-7*24*time.Hour)); err != nil {
mongoLogger.Error("[BetAPI Send Result Notification Crons] Failed to process result",
zap.Time("timestamp", time.Now()),
zap.Error(err),
)
} else {
mongoLogger.Info("[BetAPI Send Result Notification Crons] Completed sending weekly result notification without errors",
zap.Time("timestamp", time.Now()),
)
}
},
},
}
for _, job := range schedule {
// job.task()
if _, err := c.AddFunc(job.spec, job.task); err != nil {
mongoLogger.Error("[BetAPI Data Fetching Crons] Failed to schedule data fetching cron job",
zap.Time("timestamp", time.Now()),
zap.Error(err),
)
}
}
c.Start()
mongoLogger.Info("[BetAPI Data Fetching Crons] Started Data Fetching Cron jobs",
zap.Time("timestamp", time.Now()),
)
}
func StartCleanupCrons(ticketService ticket.Service, notificationSvc *notificationservice.Service, mongoLogger *zap.Logger) {
c := cron.New(cron.WithSeconds())
schedule := []struct {
spec string
task func()
}{
{
spec: "0 0 * * * *", // Every hour
task: func() {
mongoLogger.Info("[Delete Old Ticket Crons] Deleting old tickets", zap.Time("timestamp", time.Now()))
if err := ticketService.DeleteOldTickets(context.Background()); err != nil {
mongoLogger.Error("[Delete Old Ticket Crons] Failed to remove old ticket",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
} else {
mongoLogger.Info("[Delete Old Ticket Crons] Successfully deleted old tickets",
zap.Time("timestamp", time.Now()),
)
}
},
},
{
spec: "0 0 0 * * 1", // Every Monday (Weekly)
task: func() {
mongoLogger.Info("[Delete Old Notification Crons] Deleting old notifications")
if err := notificationSvc.DeleteOldNotifications(context.Background()); err != nil {
mongoLogger.Error("Failed to remove old notifications",
zap.Time("timestamp", time.Now()),
zap.Error(err),
)
} else {
mongoLogger.Info("[Delete Old Notification Crons] Successfully deleted old notifications",
zap.Time("timestamp", time.Now()),
)
}
},
},
}
for _, job := range schedule {
if _, err := c.AddFunc(job.spec, job.task); err != nil {
mongoLogger.Error("[Start Cleanup Crons] Failed to schedule cleanup cron job",
zap.Time("timestamp", time.Now()),
zap.Error(err),
)
}
}
c.Start()
mongoLogger.Info("[Start Cleanup Crons] Started cleanup cron jobs",
zap.Time("timestamp", time.Now()),
)
}
func StartStatCrons(statService *stats.Service, mongoLogger *zap.Logger) {
c := cron.New(cron.WithSeconds())
schedule := []struct {
spec string
task func()
}{
{
spec: "0 0 * * * *", // Every hour
task: func() {
start := time.Now()
mongoLogger.Info("[Company Stats Crons] Updating company stats", zap.Time("timestamp", time.Now()))
if err := statService.UpdateCompanyStats(context.Background()); err != nil {
mongoLogger.Error("[Company Stats Crons] Failed to update company stats",
zap.Error(err),
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
} else {
mongoLogger.Info("[Company Stats Crons] Successfully updated company stats",
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
}
},
},
{
spec: "0 0 * * * *", // Every hour
task: func() {
start := time.Now()
mongoLogger.Info("[Branch Stats Crons] Updating branch stats", zap.Time("timestamp", time.Now()))
if err := statService.UpdateBranchStats(context.Background()); err != nil {
mongoLogger.Error("[Branch Stats Crons] Failed to update branch stats",
zap.Error(err),
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
} else {
mongoLogger.Info("[Branch Stats Crons] Successfully updated branch stats",
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
}
},
},
{
spec: "0 0 * * * *", // Every hour
task: func() {
start := time.Now()
mongoLogger.Info("[Wallet Stats Crons] Updating wallet stats", zap.Time("timestamp", time.Now()))
if err := statService.UpdateWalletStats(context.Background()); err != nil {
mongoLogger.Error("[Wallet Stats Crons] Failed to update wallet stats",
zap.Error(err),
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
} else {
mongoLogger.Info("[Wallet Stats Crons] Successfully updated wallet stats",
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
}
},
},
{
spec: "0 0 * * * *", // Hourly
task: func() {
start := time.Now()
mongoLogger.Info("[Event Stats Crons] Updating event stats")
if err := statService.UpdateEventBetStats(context.Background()); err != nil {
mongoLogger.Error("[Event Stats Crons] Failed to update event bet stats",
zap.Error(err),
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)),
)
} else {
mongoLogger.Info("[Event Stats Crons] Successfully updated event stats",
zap.Time("timestamp", time.Now()),
zap.Duration("duration", time.Since(start)))
}
},
},
}
for _, job := range schedule {
job.task()
if _, err := c.AddFunc(job.spec, job.task); err != nil {
mongoLogger.Error("[Starting Stats Crons] Failed to schedule stats cron job",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
}
}
c.Start()
mongoLogger.Info("Starting Stats Crons] Started Stat Cron Jobs", zap.Time("timestamp", time.Now()))
}
func StartReportCrons(reportService report.ReportService, mongoLogger *zap.Logger) {
c := cron.New(cron.WithSeconds())
schedule := []struct {
spec string
task func()
}{
{
spec: "0 * * * * *", // Every 5 Minutes
task: func() {
mongoLogger.Info("[Process Report Crons] Started Checking and Processing Reports")
if err := reportService.ProcessReportRequests(context.Background()); err != nil {
mongoLogger.Error("[Process Report Crons] Failed to process reports",
zap.Error(err),
)
} else {
mongoLogger.Info("[Process Report Crons] Successfully processed all reports")
}
},
},
}
for _, job := range schedule {
job.task()
if _, err := c.AddFunc(job.spec, job.task); err != nil {
mongoLogger.Error("[Report Crons] Failed to schedule report cron job",
zap.Time("timestamp", time.Now()),
zap.Error(err),
)
}
}
c.Start()
mongoLogger.Info("[Report Crons] Cron jobs started for reports", zap.Time("timestamp", time.Now()))
}
// SetupReportCronJobs schedules periodic report generation
func SetupReportandVirtualGameCronJobs(
ctx context.Context,
reportService report.ReportService,
virtualGameOrchestrationService *orchestration.Service,
outputDir string,
) {
c := cron.New(cron.WithSeconds()) // WithSeconds for testing, remove in prod
schedule := []struct {
spec string
period string
}{
// { spec: "*/60 * * * * *", period: "test" }, // every 60 seconds for testing
{
spec: "0 0 0 * * *", // daily at midnight
period: "daily",
},
}
for _, job := range schedule {
period := job.period
if _, err := c.AddFunc(job.spec, func() {
log.Printf("[%s] Running virtual game & provider report job...", period)
brandID := os.Getenv("VELI_BRAND_ID")
if brandID == "" {
log.Println("VELI_BRAND_ID not set, skipping virtual game sync")
return
}
// Step 1. Fetch and store all virtual games
req := domain.ProviderRequest{
BrandID: brandID,
ExtraData: true,
Size: 1000,
Page: 1,
}
allGames, err := virtualGameOrchestrationService.FetchAndStoreAllVirtualGames(ctx, req, "ETB")
if err != nil {
log.Printf("[%s] Error fetching/storing virtual games: %v", period, err)
return
}
log.Printf("[%s] Successfully fetched & stored %d virtual games", period, len(allGames))
// Step 2. Fetch all providers
providers, total, err := virtualGameOrchestrationService.ListProviders(ctx, 1000, 0)
if err != nil {
log.Printf("[%s] Failed to list providers: %v", period, err)
return
} else if total == 0 {
log.Printf("[%s] No providers found, skipping report generation", period)
return
}
log.Printf("[%s] Found %d total providers", period, total)
// Step 3. Create provider-level daily report entries
reportDate := time.Now().UTC().Truncate(24 * time.Hour)
for _, p := range providers {
createReq := domain.CreateVirtualGameProviderReport{
ProviderID: p.ProviderID,
ReportDate: reportDate,
TotalGamesPlayed: 0,
TotalBets: 0,
TotalPayouts: 0,
TotalPlayers: 0,
ReportType: period, // "daily"
}
_, err := virtualGameOrchestrationService.CreateVirtualGameProviderReport(ctx, createReq)
if err != nil {
log.Printf("[%s] Failed to create report for provider %s: %v", period, p.ProviderID, err)
continue
}
log.Printf("[%s] Created daily report row for provider: %s", period, p.ProviderID)
}
log.Printf("[%s] Daily provider reports created successfully", period)
}); err != nil {
log.Fatalf("Failed to schedule %s cron job: %v", period, err)
}
}
c.Start()
log.Printf("Cron jobs started. Reports will be saved to: %s", outputDir)
}
func ProcessBetCashback(ctx context.Context, betService *betSvc.Service) {
c := cron.New(cron.WithSeconds())
schedule := []struct {
spec string
task func()
}{
{
spec: "0 0 0 * * *", // Daily at midnight
task: func() {
log.Println("process bet cashbacks...")
if err := betService.ProcessBetCashback(ctx); err != nil {
log.Printf("Failed to process bet cashbacks: %v", err)
} else {
log.Printf("Successfully processed bet cashbacks")
}
},
},
}
for _, job := range schedule {
if _, err := c.AddFunc(job.spec, job.task); err != nil {
log.Fatalf("Failed to schedule cron job: %v", err)
}
}
c.Start()
log.Println("Cron jobs started for bet cashbacks")
}
func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger) {
c := cron.New(cron.WithSeconds())
schedule := []struct {
spec string
task func()
}{
{
spec: "0 0 */2 * * *", // Every 2 hours
task: func() {
ctx := context.Background()
// 1⃣ Sports
mongoLogger.Info("Began fetching and storing sports cron task")
if err := enetPulseSvc.FetchAndStoreSports(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store sports", zap.Error(err))
} else {
mongoLogger.Info("\n\n✅ Completed fetching and storing sports\n\n")
}
// 2⃣ Tournament Templates
mongoLogger.Info("Began fetching and storing tournament templates cron task")
if err := enetPulseSvc.FetchAndStoreTournamentTemplates(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store tournament templates", zap.Error(err))
} else {
mongoLogger.Info("\n\n✅ Completed fetching and storing tournament templates\n\n")
}
// 3⃣ Tournaments
mongoLogger.Info("Began fetching and storing tournaments cron task")
if err := enetPulseSvc.FetchAndStoreTournaments(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store tournaments", zap.Error(err))
} else {
mongoLogger.Info("\n\n✅ Completed fetching and storing tournaments\n\n")
}
// 4⃣ Tournament Stages
// mongoLogger.Info("Began fetching and storing tournament stages cron task")
// if err := enetPulseSvc.FetchAndStoreTournamentStages(ctx); err != nil {
// mongoLogger.Error("Failed to fetch and store tournament stages", zap.Error(err))
// } else {
// mongoLogger.Info("✅ \n\nCompleted fetching and storing tournament stages\n\n")
// }
// // 5⃣ Fixtures
mongoLogger.Info("Began fetching and storing fixtures cron task")
today := time.Now().Format("2006-01-02")
if err := enetPulseSvc.FetchAndStoreFixtures(ctx, today); err != nil {
mongoLogger.Error("Failed to fetch and store fixtures", zap.Error(err))
} else {
mongoLogger.Info("\n\n✅ Completed fetching and storing fixtures\n\n")
}
// 6⃣ Results
// mongoLogger.Info("Began fetching and storing results cron task")
// if err := enetPulseSvc.FetchAndStoreResults(ctx); err != nil {
// mongoLogger.Error("Failed to fetch and store results", zap.Error(err))
// } else {
// mongoLogger.Info("\n\n✅ Completed fetching and storing results\n\n")
// }
// 7 Outcome Types
mongoLogger.Info("Began fetching and storing outcome_types cron task")
if err := enetPulseSvc.FetchAndStoreOutcomeTypes(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store outcome_types", zap.Error(err))
} else {
mongoLogger.Info("\n\n✅ Completed fetching and storing outcome_types\n\n")
}
// 8 Preodds
mongoLogger.Info("Began fetching and storing preodds cron task")
if err := enetPulseSvc.FetchAndStorePreodds(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store preodds", zap.Error(err))
} else {
mongoLogger.Info("\n\n✅ Completed fetching and storing preodds\n\n")
}
},
},
}
for _, job := range schedule {
// Run immediately at startup
job.task()
// Schedule the task
if _, err := c.AddFunc(job.spec, job.task); err != nil {
mongoLogger.Error("Failed to schedule EnetPulse cron job", zap.Error(err))
}
}
c.Start()
log.Println("EnetPulse cron jobs started for sports, tournament templates, tournaments, tournament stages, fixtures, and results")
mongoLogger.Info("EnetPulse cron jobs started for sports, tournament templates, tournaments, tournament stages, fixtures, and results")
}