veza/veza-backend-api/internal/services/announcement_service.go

93 lines
2.7 KiB
Go

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
}