dana-medical-site/internal/storage/banners.go
2026-10-14 13:33:13 +00:00

153 lines
5.4 KiB
Go

package storage
import (
"context"
"time"
"dana/internal/domain"
)
// Banners отдаёт действующие баннеры: опубликованные и попадающие в период показа.
func (s *Storage) Banners(ctx context.Context, position string) ([]domain.Banner, error) {
rows, err := s.pool.Query(ctx, `
SELECT b.id, b.position, b.layout, b.theme, b.title, b.subtitle, b.badge, b.chips,
COALESCE(mi.path, ''), COALESCE(mm.path, ''),
b.link_url, b.button_text, b.disclaimer
FROM banners b
LEFT JOIN media mi ON mi.id = b.image_id
LEFT JOIN media mm ON mm.id = b.mobile_image_id
WHERE b.published
AND (b.starts_at IS NULL OR b.starts_at <= now())
AND (b.ends_at IS NULL OR b.ends_at > now())
AND ($1 = '' OR b.position = $1)
ORDER BY b.sort, b.id`, position)
if err != nil {
return nil, err
}
defer rows.Close()
out := []domain.Banner{}
for rows.Next() {
var b domain.Banner
if err := rows.Scan(&b.ID, &b.Position, &b.Layout, &b.Theme, &b.Title, &b.Subtitle, &b.Badge, &b.Chips,
&b.Image, &b.MobileImage, &b.LinkURL, &b.ButtonText, &b.Disclaimer); err != nil {
return nil, err
}
out = append(out, b)
}
return out, rows.Err()
}
// ---------- Админка ----------
type AdminBanner struct {
ID int64 `json:"id"`
Position string `json:"position"`
Layout string `json:"layout"`
Theme string `json:"theme"`
Title string `json:"title"`
Subtitle string `json:"subtitle"`
Badge string `json:"badge"`
Chips []string `json:"chips"`
ImageID *int64 `json:"image_id"`
Image string `json:"image,omitempty"`
MobileImageID *int64 `json:"mobile_image_id"`
MobileImage string `json:"mobile_image,omitempty"`
LinkURL string `json:"link_url"`
ButtonText string `json:"button_text"`
Disclaimer string `json:"disclaimer"`
StartsAt *time.Time `json:"starts_at"`
EndsAt *time.Time `json:"ends_at"`
Sort int `json:"sort"`
Published bool `json:"published"`
}
func (s *Storage) AdminBanners(ctx context.Context, position string) ([]AdminBanner, error) {
rows, err := s.pool.Query(ctx, `
SELECT b.id, b.position, b.layout, b.theme, b.title, b.subtitle, b.badge, b.chips,
b.image_id, COALESCE(mi.path, ''), b.mobile_image_id, COALESCE(mm.path, ''),
b.link_url, b.button_text, b.disclaimer,
b.starts_at, b.ends_at, b.sort, b.published
FROM banners b
LEFT JOIN media mi ON mi.id = b.image_id
LEFT JOIN media mm ON mm.id = b.mobile_image_id
WHERE ($1 = '' OR b.position = $1)
ORDER BY b.position, b.sort, b.id`, position)
if err != nil {
return nil, err
}
defer rows.Close()
out := []AdminBanner{}
for rows.Next() {
var a AdminBanner
if err := rows.Scan(&a.ID, &a.Position, &a.Layout, &a.Theme, &a.Title, &a.Subtitle, &a.Badge, &a.Chips,
&a.ImageID, &a.Image, &a.MobileImageID, &a.MobileImage,
&a.LinkURL, &a.ButtonText, &a.Disclaimer,
&a.StartsAt, &a.EndsAt, &a.Sort, &a.Published); err != nil {
return nil, err
}
out = append(out, a)
}
return out, rows.Err()
}
type BannerInput struct {
Position string `json:"position"`
Layout string `json:"layout"`
Theme string `json:"theme"`
Title string `json:"title"`
Subtitle string `json:"subtitle"`
Badge string `json:"badge"`
Chips []string `json:"chips"`
ImageID *int64 `json:"image_id"`
MobileImageID *int64 `json:"mobile_image_id"`
LinkURL string `json:"link_url"`
ButtonText string `json:"button_text"`
Disclaimer string `json:"disclaimer"`
StartsAt *time.Time `json:"starts_at"`
EndsAt *time.Time `json:"ends_at"`
Sort int `json:"sort"`
Published bool `json:"published"`
}
func (s *Storage) AdminCreateBanner(ctx context.Context, in BannerInput) (int64, error) {
var id int64
err := s.pool.QueryRow(ctx, `
INSERT INTO banners (position, layout, theme, title, subtitle, badge, chips, image_id, mobile_image_id,
link_url, button_text, disclaimer, starts_at, ends_at, sort, published)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16) RETURNING id`,
in.Position, in.Layout, in.Theme, in.Title, in.Subtitle, in.Badge, in.Chips, in.ImageID, in.MobileImageID,
in.LinkURL, in.ButtonText, in.Disclaimer, in.StartsAt, in.EndsAt, in.Sort, in.Published).Scan(&id)
return id, err
}
func (s *Storage) AdminUpdateBanner(ctx context.Context, id int64, in BannerInput) error {
tag, err := s.pool.Exec(ctx, `
UPDATE banners SET position=$2, layout=$3, theme=$4, title=$5, subtitle=$6, badge=$7, chips=$8,
image_id=$9, mobile_image_id=$10, link_url=$11, button_text=$12, disclaimer=$13,
starts_at=$14, ends_at=$15, sort=$16, published=$17, updated_at=now()
WHERE id=$1`,
id, in.Position, in.Layout, in.Theme, in.Title, in.Subtitle, in.Badge, in.Chips,
in.ImageID, in.MobileImageID, in.LinkURL, in.ButtonText, in.Disclaimer,
in.StartsAt, in.EndsAt, in.Sort, in.Published)
if err != nil {
return err
}
if tag.RowsAffected() == 0 {
return ErrNotFound
}
return nil
}
func (s *Storage) AdminDeleteBanner(ctx context.Context, id int64) error {
tag, err := s.pool.Exec(ctx, `DELETE FROM banners WHERE id=$1`, id)
if err != nil {
return err
}
if tag.RowsAffected() == 0 {
return ErrNotFound
}
return nil
}