- Cloud: CloudFileVersions, CloudShareModal, versions/share in CloudView - Gear: GearDocumentsTab, GearRepairsTab, warranty badge, initialTab - MSW: cloud versions/share, gear documents/repairs, tags suggest - Stories: CloudFileVersions, CloudShareModal, GearDetailModal variants - gearService: listDocuments, uploadDocument, deleteDocument, listRepairs, createRepair, deleteRepair - cloudService: listVersions, restoreVersion, shareFile, getSharedFile - gear_warranty_notifier: 24h ticker, notifications for expiring warranty - tag_handler_test: unit tests - docs: API_REFERENCE, CHANGELOG, PROJECT_STATE, FEATURE_STATUS v0.802 - SCOPE_CONTROL, .cursorrules: scope v0.803 - archive: V0_802_RELEASE_SCOPE, RETROSPECTIVE_V0802
86 lines
2.3 KiB
Go
86 lines
2.3 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
|
|
"veza-backend-api/internal/models"
|
|
)
|
|
|
|
// GearWarrantyNotifier sends notifications when gear warranty is expiring
|
|
type GearWarrantyNotifier struct {
|
|
db *gorm.DB
|
|
notificationService *NotificationService
|
|
logger *zap.Logger
|
|
interval time.Duration
|
|
}
|
|
|
|
// NewGearWarrantyNotifier creates a new warranty notifier
|
|
func NewGearWarrantyNotifier(db *gorm.DB, notificationService *NotificationService, logger *zap.Logger) *GearWarrantyNotifier {
|
|
return &GearWarrantyNotifier{
|
|
db: db,
|
|
notificationService: notificationService,
|
|
logger: logger,
|
|
interval: 24 * time.Hour,
|
|
}
|
|
}
|
|
|
|
// Start runs the notifier loop
|
|
func (n *GearWarrantyNotifier) Start(ctx context.Context) {
|
|
if n.notificationService == nil {
|
|
n.logger.Info("Gear warranty notifier: notification service not configured")
|
|
return
|
|
}
|
|
|
|
ticker := time.NewTicker(n.interval)
|
|
defer ticker.Stop()
|
|
|
|
n.logger.Info("Gear warranty notifier started", zap.Duration("interval", n.interval))
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
if err := n.checkAndNotify(ctx); err != nil {
|
|
n.logger.Error("Gear warranty notifier failed", zap.Error(err))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (n *GearWarrantyNotifier) checkAndNotify(ctx context.Context) error {
|
|
// Gear with warranty_expire between now and now+30 days
|
|
now := time.Now()
|
|
expiryLimit := now.Add(30 * 24 * time.Hour)
|
|
|
|
var items []models.GearItem
|
|
err := n.db.WithContext(ctx).Where(
|
|
"warranty_expire IS NOT NULL AND warranty_expire > ? AND warranty_expire <= ?",
|
|
now, expiryLimit,
|
|
).Find(&items).Error
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
for _, item := range items {
|
|
if item.WarrantyExpire == nil {
|
|
continue
|
|
}
|
|
daysLeft := int(item.WarrantyExpire.Sub(now).Hours() / 24)
|
|
msg := fmt.Sprintf("%s warranty expires in %d days", item.Name, daysLeft)
|
|
link := fmt.Sprintf("/inventory?gear=%s", item.ID)
|
|
if err := n.notificationService.CreateNotification(item.UserID, "gear_warranty", "Warranty expiring soon", msg, link); err != nil {
|
|
n.logger.Warn("failed to send warranty notification", zap.String("gear_id", item.ID.String()), zap.Error(err))
|
|
}
|
|
}
|
|
|
|
if len(items) > 0 {
|
|
n.logger.Info("Gear warranty notifications sent", zap.Int("count", len(items)))
|
|
}
|
|
return nil
|
|
}
|