package schedule import ( "time" "tm/internal/notice" "tm/pkg/logger" "tm/pkg/mongo" "github.com/robfig/cron/v3" ) // CronScheduler handles scheduled tasks for TED operations type CronScheduler struct { cron *cron.Cron mongoManager *mongo.ConnectionManager noticeRepo notice.Repository logger logger.Logger } // Logger interface for structured logging type Logger interface { Info(msg string, fields map[string]interface{}) Error(msg string, fields map[string]interface{}) Debug(msg string, fields map[string]interface{}) } type Job struct { Name string Func func() Expr string } // NewCronScheduler creates a new cron scheduler instance func NewCronScheduler(logger logger.Logger, mongoManager *mongo.ConnectionManager, noticeRepo notice.Repository) *CronScheduler { // Create cron with timezone support and seconds precision c := cron.New(cron.WithLocation(time.UTC), cron.WithSeconds()) return &CronScheduler{ cron: c, mongoManager: mongoManager, noticeRepo: noticeRepo, logger: logger, } } // Start starts the cron scheduler func (cs *CronScheduler) Start() { cs.logger.Info("Starting TED cron scheduler", map[string]interface{}{}) cs.cron.Start() } // Stop stops the cron scheduler gracefully func (cs *CronScheduler) Stop() { cs.logger.Info("Stopping TED cron scheduler", map[string]interface{}{}) ctx := cs.cron.Stop() <-ctx.Done() } // ScheduleDailyJob schedules a job to run daily at 10:00 AM UTC func (cs *CronScheduler) AddJob(job Job) error { // Cron expression: "0 0 10 * * *" means: // Second: 0 // Minute: 0 // Hour: 10 (10 AM) // Day of month: * (every day) // Month: * (every month) // Day of week: * (every day of week) _, err := cs.cron.AddFunc(job.Expr, func() { now := time.Now().Local() cs.logger.Info("Starting daily TED job", map[string]interface{}{ "job_name": job.Name, "cron_expression": job.Expr, "time": time.Now().Format(time.RFC3339), }) job.Func() cs.logger.Info("Completed job successfully", map[string]interface{}{ "time": now.Format(time.RFC3339), "job_name": job.Name, "cron_expression": job.Expr, }) }) if err != nil { cs.logger.Error("Failed to schedule daily job", map[string]interface{}{ "error": err.Error(), "job_name": job.Name, "cron_expression": job.Expr, "time": time.Now().Format(time.RFC3339), }) return err } cs.logger.Info("Daily job scheduled successfully", map[string]interface{}{ "job_name": job.Name, "cron_expression": job.Expr, "time": time.Now().Format(time.RFC3339), }) return nil } // ExampleTEDScrapingJob is an example job function that can be scheduled func ExampleTEDScrapingJob() { // now := time.Now().Local() // // Example: Get current date for TED scraping // currentDate := time.Now().Format("02/01/2006") // currentYear := time.Now().Year() // // This is just an example - replace with actual TED scraping logic // ojs, err := GetOJS(currentYear, currentDate) // if err != nil { // log.Printf("Error getting OJS: %v", err) // return // } // log.Printf("Successfully retrieved OJS: %s for date: %s", ojs, currentDate) // Add your actual TED scraping/processing logic here // ojs, err := GetOJS(now.Year(), now.Format("02/01/2006")) // if err != nil { // cs.logger.Error("Failed to get OJS", map[string]interface{}{ // "error": err.Error(), // }) // return // } // scraper := NewTEDScraper(cs.config, cs.logger, cs.mongoManager, cs.tenderRepo) // result, err := scraper.DownloadDailyPackage(context.Background(), ojs) // if err != nil { // cs.logger.Error("Failed to download daily package", map[string]interface{}{ // "error": err.Error(), // }) // return // } // cs.logger.Info("Downloaded daily package successfully", map[string]interface{}{ // "result": result, // "time": now.Format(time.RFC3339), // "ojs": ojs, // }) }