172 lines
4.5 KiB
Go
172 lines
4.5 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"sync"
|
|
"time"
|
|
|
|
"apskel-pos-be/internal/constants"
|
|
"apskel-pos-be/internal/entities"
|
|
"apskel-pos-be/internal/models"
|
|
"apskel-pos-be/internal/processor"
|
|
"apskel-pos-be/internal/repository"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const (
|
|
defaultCheckInterval = 1 * time.Hour
|
|
OmsetMillionRupiah = 1_000_000.0
|
|
)
|
|
|
|
// OmsetMilestoneScheduler periodically checks each organization's total omset
|
|
// and sends a notification to owner/admin users when a milestone is reached.
|
|
//
|
|
// NOTE: Milestone tracking is in-memory; notifications may re-trigger after a restart.
|
|
// For persistent tracking, persist the notified state in the database.
|
|
type OmsetMilestoneScheduler struct {
|
|
orgRepo *repository.OrganizationRepositoryImpl
|
|
userRepo *repository.UserRepositoryImpl
|
|
notificationProc processor.NotificationProcessor
|
|
|
|
mu sync.Mutex
|
|
notified map[string]bool // "orgID:milestone" -> already notified
|
|
stopCh chan struct{}
|
|
}
|
|
|
|
func NewOmsetMilestoneScheduler(
|
|
orgRepo *repository.OrganizationRepositoryImpl,
|
|
userRepo *repository.UserRepositoryImpl,
|
|
notificationProc processor.NotificationProcessor,
|
|
) *OmsetMilestoneScheduler {
|
|
return &OmsetMilestoneScheduler{
|
|
orgRepo: orgRepo,
|
|
userRepo: userRepo,
|
|
notificationProc: notificationProc,
|
|
notified: make(map[string]bool),
|
|
stopCh: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// Start begins the periodic milestone check in a background goroutine.
|
|
func (s *OmsetMilestoneScheduler) Start(interval time.Duration) {
|
|
if interval <= 0 {
|
|
interval = defaultCheckInterval
|
|
}
|
|
|
|
go func() {
|
|
// Perform an initial check immediately.
|
|
s.checkAllOrganizations()
|
|
|
|
ticker := time.NewTicker(interval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
s.checkAllOrganizations()
|
|
case <-s.stopCh:
|
|
log.Println("Omset milestone scheduler stopped")
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
log.Println("Omset milestone scheduler started")
|
|
}
|
|
|
|
// Stop signals the scheduler to stop.
|
|
func (s *OmsetMilestoneScheduler) Stop() {
|
|
close(s.stopCh)
|
|
}
|
|
|
|
func (s *OmsetMilestoneScheduler) checkAllOrganizations() {
|
|
ctx := context.Background()
|
|
|
|
orgs, _, err := s.orgRepo.List(ctx, nil, 1000, 0)
|
|
if err != nil {
|
|
log.Printf("OmsetMilestoneScheduler: failed to list organizations: %v", err)
|
|
return
|
|
}
|
|
|
|
for _, org := range orgs {
|
|
s.checkOrganization(ctx, org)
|
|
}
|
|
}
|
|
|
|
func (s *OmsetMilestoneScheduler) checkOrganization(ctx context.Context, org *entities.Organization) {
|
|
totalOmset, err := s.orgRepo.GetTotalOmset(ctx, org.ID)
|
|
if err != nil {
|
|
log.Printf("OmsetMilestoneScheduler: failed to get total omset for org %s: %v", org.ID, err)
|
|
return
|
|
}
|
|
|
|
milestones := []float64{OmsetMillionRupiah}
|
|
|
|
for _, milestone := range milestones {
|
|
if totalOmset < milestone {
|
|
continue
|
|
}
|
|
|
|
key := fmt.Sprintf("%s:%.0f", org.ID.String(), milestone)
|
|
|
|
s.mu.Lock()
|
|
if s.notified[key] {
|
|
s.mu.Unlock()
|
|
continue
|
|
}
|
|
s.notified[key] = true
|
|
s.mu.Unlock()
|
|
|
|
s.sendMilestoneNotification(ctx, org, totalOmset, milestone)
|
|
}
|
|
}
|
|
|
|
func (s *OmsetMilestoneScheduler) sendMilestoneNotification(ctx context.Context, org *entities.Organization, totalOmset float64, milestone float64) {
|
|
users, err := s.userRepo.GetByOrganizationID(ctx, org.ID)
|
|
if err != nil {
|
|
log.Printf("OmsetMilestoneScheduler: failed to get users for org %s: %v", org.ID, err)
|
|
return
|
|
}
|
|
|
|
// Notify owner and admin users.
|
|
var receiverIDs []uuid.UUID
|
|
for _, user := range users {
|
|
roleStr := string(user.Role)
|
|
if roleStr == string(constants.RoleOwner) || roleStr == string(constants.RoleAdmin) {
|
|
receiverIDs = append(receiverIDs, user.ID)
|
|
}
|
|
}
|
|
|
|
if len(receiverIDs) == 0 {
|
|
return
|
|
}
|
|
|
|
orgID := org.ID
|
|
title := "🎉 Selamat! Omset Telah Mencapai 1 Juta Rupiah"
|
|
body := fmt.Sprintf("Organisasi %s telah mencapai omset Rp %.0f. Terus tingkatkan prestasinya!", org.Name, totalOmset)
|
|
|
|
notifReq := &models.SendNotificationRequest{
|
|
Title: title,
|
|
Body: body,
|
|
Type: "milestone",
|
|
Category: "omset_milestone",
|
|
NotifiableType: "organization",
|
|
NotifiableID: &orgID,
|
|
ReceiverIDs: receiverIDs,
|
|
Data: map[string]interface{}{
|
|
"organization_id": org.ID.String(),
|
|
"total_omset": totalOmset,
|
|
"milestone": milestone,
|
|
},
|
|
}
|
|
|
|
if _, err := s.notificationProc.Send(ctx, notifReq); err != nil {
|
|
log.Printf("OmsetMilestoneScheduler: failed to send notification for org %s: %v", org.ID, err)
|
|
} else {
|
|
log.Printf("OmsetMilestoneScheduler: sent milestone notification to org %s (omset: %.0f)", org.ID, totalOmset)
|
|
}
|
|
}
|