Files
website/internal/platform/services/notifications/service.go

85 lines
1.8 KiB
Go

// Package notifysvc
// Path: /internal/platform/services/notifications
// File: service.go
// ToDo: carve out sql
package notifysvc
import (
"context"
"database/sql"
"errors"
"time"
domain "synlotto-website/internal/domain/notifications"
)
type Service struct {
DB *sql.DB
Now func() time.Time
Timeout time.Duration
}
func New(db *sql.DB, opts ...func(*Service)) *Service {
s := &Service{
DB: db,
Now: time.Now,
Timeout: 5 * time.Second,
}
for _, opt := range opts {
opt(s)
}
return s
}
func WithTimeout(d time.Duration) func(*Service) { return func(s *Service) { s.Timeout = d } }
// List returns newest-first notifications for a user.
func (s *Service) List(userID int64) ([]domain.Notification, error) {
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
defer cancel()
const q = `
SELECT id, title, body, is_read, created_at
FROM notifications
WHERE userId = ?
ORDER BY created_at DESC`
rows, err := s.DB.QueryContext(ctx, q, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var out []domain.Notification
for rows.Next() {
var n domain.Notification
if err := rows.Scan(&n.ID, &n.Title, &n.Body, &n.IsRead, &n.CreatedAt); err != nil {
return nil, err
}
out = append(out, n)
}
return out, rows.Err()
}
func (s *Service) GetByID(userID, id int64) (*domain.Notification, error) {
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
defer cancel()
const q = `
SELECT id, title, body, is_read, created_at
FROM notifications
WHERE userId = ? AND id = ?`
var n domain.Notification
err := s.DB.QueryRowContext(ctx, q, userID, id).
Scan(&n.ID, &n.Title, &n.Body, &n.IsRead, &n.CreatedAt)
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, err
}
return &n, nil
}