153 lines
5.4 KiB
Go
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
|
|
}
|