2025-11-05 15:06:07 +00:00
|
|
|
package models
|
|
|
|
|
|
|
|
|
|
import (
|
2025-11-05 15:07:28 +00:00
|
|
|
"errors"
|
2025-11-05 15:06:07 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
)
|
|
|
|
|
|
2025-11-05 15:07:28 +00:00
|
|
|
// NotificationType represents the different types of notifications
|
|
|
|
|
type NotificationType string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
NotificationTypeWelcome NotificationType = "welcome"
|
|
|
|
|
NotificationTypePaymentSuccess NotificationType = "payment_success"
|
|
|
|
|
NotificationTypePaymentFailed NotificationType = "payment_failed"
|
|
|
|
|
NotificationTypeMeetingInfo NotificationType = "meeting_info"
|
|
|
|
|
NotificationTypeReminder NotificationType = "reminder"
|
|
|
|
|
NotificationTypeCancellation NotificationType = "cancellation"
|
|
|
|
|
NotificationTypeReschedule NotificationType = "reschedule"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// NotificationStatus represents the status of a notification
|
|
|
|
|
type NotificationStatus string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
NotificationStatusPending NotificationStatus = "pending"
|
|
|
|
|
NotificationStatusSent NotificationStatus = "sent"
|
|
|
|
|
NotificationStatusFailed NotificationStatus = "failed"
|
|
|
|
|
NotificationStatusSkipped NotificationStatus = "skipped"
|
|
|
|
|
)
|
|
|
|
|
|
2025-11-05 15:06:07 +00:00
|
|
|
// Notification represents email notifications
|
|
|
|
|
type Notification struct {
|
|
|
|
|
gorm.Model
|
2025-11-05 15:07:28 +00:00
|
|
|
UserID uint `json:"user_id" gorm:"index" validate:"required"`
|
|
|
|
|
User User `json:"user" gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
|
|
|
|
BookingID *uint `json:"booking_id" gorm:"index"`
|
|
|
|
|
Booking *Booking `json:"booking" gorm:"foreignKey:BookingID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"`
|
|
|
|
|
Type NotificationType `json:"type" gorm:"not null;size:50;index" validate:"required,oneof=welcome payment_success payment_failed meeting_info reminder cancellation reschedule"`
|
|
|
|
|
Subject string `json:"subject" gorm:"not null;size:255" validate:"required,max=255"`
|
|
|
|
|
Body string `json:"body" gorm:"type:text" validate:"required"`
|
|
|
|
|
SentAt *time.Time `json:"sent_at" gorm:"index"`
|
|
|
|
|
Status NotificationStatus `json:"status" gorm:"default:'pending';size:20;index" validate:"required,oneof=pending sent failed skipped"`
|
|
|
|
|
ScheduledAt *time.Time `json:"scheduled_at" gorm:"index"`
|
|
|
|
|
RetryCount int `json:"retry_count" gorm:"default:0;check:retry_count >= 0"`
|
|
|
|
|
ErrorMsg string `json:"error_msg" gorm:"size:500"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BeforeCreate is a GORM hook that runs before creating a notification record
|
|
|
|
|
func (n *Notification) BeforeCreate(tx *gorm.DB) error {
|
|
|
|
|
// Set default status if not provided
|
|
|
|
|
if n.Status == "" {
|
|
|
|
|
n.Status = NotificationStatusPending
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate required fields
|
|
|
|
|
if n.Subject == "" {
|
|
|
|
|
return errors.New("notification subject is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if n.Body == "" {
|
|
|
|
|
return errors.New("notification body is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If scheduled at is not set, schedule for immediate sending
|
|
|
|
|
if n.ScheduledAt == nil {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
n.ScheduledAt = &now
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BeforeUpdate is a GORM hook that runs before updating a notification record
|
|
|
|
|
func (n *Notification) BeforeUpdate(tx *gorm.DB) error {
|
|
|
|
|
// If status is being set to sent, set SentAt timestamp
|
|
|
|
|
if n.Status == NotificationStatusSent && n.SentAt == nil {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
n.SentAt = &now
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsReadyToSend checks if the notification is ready to be sent
|
|
|
|
|
func (n *Notification) IsReadyToSend() bool {
|
|
|
|
|
if n.Status != NotificationStatusPending {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if n.ScheduledAt == nil {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n.ScheduledAt.Before(time.Now()) || n.ScheduledAt.Equal(time.Now())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CanRetry checks if the notification can be retried (max 3 retries)
|
|
|
|
|
func (n *Notification) CanRetry() bool {
|
|
|
|
|
return n.Status == NotificationStatusFailed && n.RetryCount < 3
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MarkAsSent marks the notification as successfully sent
|
|
|
|
|
func (n *Notification) MarkAsSent() {
|
|
|
|
|
n.Status = NotificationStatusSent
|
|
|
|
|
now := time.Now()
|
|
|
|
|
n.SentAt = &now
|
|
|
|
|
n.ErrorMsg = ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MarkAsFailed marks the notification as failed with an error message
|
|
|
|
|
func (n *Notification) MarkAsFailed(errorMsg string) {
|
|
|
|
|
n.Status = NotificationStatusFailed
|
|
|
|
|
n.RetryCount++
|
|
|
|
|
n.ErrorMsg = errorMsg
|
2025-11-05 15:06:07 +00:00
|
|
|
}
|