package channels import ( "context" "fmt" "time" "github.com/jackc/pgx/v5/pgxpool" ) type InAppNotification struct { ID string TenantSlug string UserID string Title string Body string ReadAt *time.Time CreatedAt time.Time } // InAppStore persistiert In-App-Benachrichtigungen (Akzeptanzkriterium 2). type InAppStore struct { pool *pgxpool.Pool } func NewInAppStore(pool *pgxpool.Pool) *InAppStore { return &InAppStore{pool: pool} } func (s *InAppStore) Create(ctx context.Context, tenantSlug, userID, title, body string) (string, error) { var id string err := s.pool.QueryRow(ctx, ` INSERT INTO in_app_notifications (tenant_slug, user_id, title, body) VALUES ($1, $2, $3, $4) RETURNING id `, tenantSlug, userID, title, body).Scan(&id) if err != nil { return "", fmt.Errorf("in-app-benachrichtigung speichern: %w", err) } return id, nil } // ListForUser liefert alle Benachrichtigungen eines Benutzers (ueber API // abrufbar, Akzeptanzkriterium 2). func (s *InAppStore) ListForUser(ctx context.Context, tenantSlug, userID string) ([]InAppNotification, error) { rows, err := s.pool.Query(ctx, ` SELECT id, title, body, read_at, created_at FROM in_app_notifications WHERE tenant_slug = $1 AND user_id = $2 ORDER BY created_at DESC `, tenantSlug, userID) if err != nil { return nil, fmt.Errorf("benachrichtigungen auflisten: %w", err) } defer rows.Close() var out []InAppNotification for rows.Next() { n := InAppNotification{TenantSlug: tenantSlug, UserID: userID} if err := rows.Scan(&n.ID, &n.Title, &n.Body, &n.ReadAt, &n.CreatedAt); err != nil { return nil, fmt.Errorf("benachrichtigung lesen: %w", err) } out = append(out, n) } return out, rows.Err() } // MarkRead markiert eine Benachrichtigung als gelesen (Akzeptanzkriterium 2 // / Pruefung 2). func (s *InAppStore) MarkRead(ctx context.Context, id string) error { _, err := s.pool.Exec(ctx, `UPDATE in_app_notifications SET read_at = now() WHERE id = $1`, id) if err != nil { return fmt.Errorf("als gelesen markieren: %w", err) } return nil }