package services import ( "context" "fmt" "time" "github.com/google/uuid" "go.uber.org/zap" "gorm.io/gorm" "veza-backend-api/internal/models" ) // AnnouncementService handles global announcements (v0.803 ADM1-04) type AnnouncementService struct { db *gorm.DB logger *zap.Logger } // NewAnnouncementService creates a new AnnouncementService func NewAnnouncementService(db *gorm.DB, logger *zap.Logger) *AnnouncementService { return &AnnouncementService{db: db, logger: logger} } // CreateAnnouncementRequest is the request for creating an announcement type CreateAnnouncementRequest struct { Title string `json:"title" binding:"required"` Content string `json:"content" binding:"required"` Type string `json:"type"` // info, warning, error IsActive bool `json:"is_active"` StartsAt *time.Time `json:"starts_at"` EndsAt *time.Time `json:"ends_at"` } // List returns all announcements (admin) func (s *AnnouncementService) List(ctx context.Context) ([]models.Announcement, error) { var list []models.Announcement if err := s.db.WithContext(ctx).Order("created_at DESC").Find(&list).Error; err != nil { return nil, fmt.Errorf("failed to list announcements: %w", err) } return list, nil } // Create creates an announcement func (s *AnnouncementService) Create(ctx context.Context, createdBy uuid.UUID, req CreateAnnouncementRequest) (*models.Announcement, error) { a := &models.Announcement{ Title: req.Title, Content: req.Content, Type: req.Type, IsActive: req.IsActive, CreatedBy: &createdBy, } if req.Type == "" { a.Type = "info" } if req.StartsAt != nil { a.StartsAt = *req.StartsAt } else { a.StartsAt = time.Now() } a.EndsAt = req.EndsAt if err := s.db.WithContext(ctx).Create(a).Error; err != nil { return nil, fmt.Errorf("failed to create announcement: %w", err) } return a, nil } // Delete deletes an announcement func (s *AnnouncementService) Delete(ctx context.Context, id uuid.UUID) error { result := s.db.WithContext(ctx).Delete(&models.Announcement{}, "id = ?", id) if result.Error != nil { return fmt.Errorf("failed to delete announcement: %w", result.Error) } if result.RowsAffected == 0 { return fmt.Errorf("announcement not found") } return nil } // GetActive returns currently active announcements (public) func (s *AnnouncementService) GetActive(ctx context.Context) ([]models.Announcement, error) { now := time.Now() var list []models.Announcement if err := s.db.WithContext(ctx). Where("is_active = ? AND starts_at <= ?", true, now). Where("ends_at IS NULL OR ends_at > ?", now). Order("starts_at DESC"). Find(&list).Error; err != nil { return nil, fmt.Errorf("failed to get active announcements: %w", err) } return list, nil }