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

182 lines
5.3 KiB
Go

package storage
import (
"context"
"encoding/json"
"time"
)
// ---------- Страницы ----------
type AdminPage struct {
ID int64 `json:"id"`
Slug string `json:"slug"`
Title string `json:"title"`
Blocks json.RawMessage `json:"blocks"`
Published bool `json:"published"`
SEOTitle string `json:"seo_title"`
SEODesc string `json:"seo_description"`
SEOH1 string `json:"seo_h1"`
UpdatedAt time.Time `json:"updated_at"`
}
func (s *Storage) AdminPages(ctx context.Context) ([]AdminPage, error) {
rows, err := s.pool.Query(ctx, `
SELECT id, slug, title, blocks, published, seo_title, seo_description, seo_h1, updated_at
FROM pages ORDER BY slug`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []AdminPage
for rows.Next() {
var p AdminPage
if err := rows.Scan(&p.ID, &p.Slug, &p.Title, &p.Blocks, &p.Published,
&p.SEOTitle, &p.SEODesc, &p.SEOH1, &p.UpdatedAt); err != nil {
return nil, err
}
out = append(out, p)
}
return out, rows.Err()
}
func (s *Storage) AdminPage(ctx context.Context, id int64) (*AdminPage, error) {
var p AdminPage
err := s.pool.QueryRow(ctx, `
SELECT id, slug, title, blocks, published, seo_title, seo_description, seo_h1, updated_at
FROM pages WHERE id=$1`, id).Scan(&p.ID, &p.Slug, &p.Title, &p.Blocks, &p.Published,
&p.SEOTitle, &p.SEODesc, &p.SEOH1, &p.UpdatedAt)
if err != nil {
return nil, wrapNotFound(err)
}
return &p, nil
}
type PageInput struct {
Title string
Blocks json.RawMessage
Published bool
SEOTitle string
SEODesc string
SEOH1 string
}
// AdminUpdatePage меняет содержимое страницы. Адрес (slug) не меняется: страницы
// привязаны к маршрутам сайта. Новых страниц админка не создаёт по той же причине.
func (s *Storage) AdminUpdatePage(ctx context.Context, id int64, in PageInput) error {
tag, err := s.pool.Exec(ctx, `
UPDATE pages SET title=$2, blocks=$3, published=$4, seo_title=$5, seo_description=$6,
seo_h1=$7, updated_at=now()
WHERE id=$1`,
id, in.Title, in.Blocks, in.Published, in.SEOTitle, in.SEODesc, in.SEOH1)
if err != nil {
return err
}
if tag.RowsAffected() == 0 {
return ErrNotFound
}
return nil
}
// ---------- Настройки ----------
// AdminSetSettings записывает несколько ключей за раз в одной транзакции.
func (s *Storage) AdminSetSettings(ctx context.Context, values map[string]any) error {
tx, err := s.pool.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx) //nolint:errcheck // после Commit откат ничего не делает
for key, value := range values {
raw, err := json.Marshal(value)
if err != nil {
return err
}
if _, err := tx.Exec(ctx, `
INSERT INTO settings (key, value, updated_at) VALUES ($1, $2, now())
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now()`,
key, raw); err != nil {
return err
}
}
return tx.Commit(ctx)
}
// ---------- Филиалы ----------
type AdminBranch struct {
ID int64 `json:"id"`
Slug string `json:"slug"`
Name string `json:"name"`
Address string `json:"address"`
Phones []string `json:"phones"`
WorkHours []string `json:"work_hours"`
Lat *float64 `json:"lat"`
Lon *float64 `json:"lon"`
Sort int `json:"sort"`
Published bool `json:"published"`
}
func (s *Storage) AdminBranches(ctx context.Context) ([]AdminBranch, error) {
rows, err := s.pool.Query(ctx, `
SELECT id, slug, name, address, phones, work_hours, lat, lon, sort, published
FROM branches ORDER BY sort, name`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []AdminBranch
for rows.Next() {
var b AdminBranch
if err := rows.Scan(&b.ID, &b.Slug, &b.Name, &b.Address, &b.Phones, &b.WorkHours,
&b.Lat, &b.Lon, &b.Sort, &b.Published); err != nil {
return nil, err
}
out = append(out, b)
}
return out, rows.Err()
}
type BranchInput struct {
Slug string
Name string
Address string
Phones []string
WorkHours []string
Lat *float64
Lon *float64
Sort int
Published bool
}
func (s *Storage) AdminCreateBranch(ctx context.Context, in BranchInput) (int64, error) {
phones, _ := json.Marshal(in.Phones)
hours, _ := json.Marshal(in.WorkHours)
var id int64
err := s.pool.QueryRow(ctx, `
INSERT INTO branches (slug, name, address, phones, work_hours, lat, lon, sort, published)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
RETURNING id`,
in.Slug, in.Name, in.Address, phones, hours, in.Lat, in.Lon, in.Sort, in.Published).Scan(&id)
return id, wrapConflict(err)
}
func (s *Storage) AdminUpdateBranch(ctx context.Context, id int64, in BranchInput) error {
phones, _ := json.Marshal(in.Phones)
hours, _ := json.Marshal(in.WorkHours)
tag, err := s.pool.Exec(ctx, `
UPDATE branches SET slug=$2, name=$3, address=$4, phones=$5, work_hours=$6, lat=$7, lon=$8,
sort=$9, published=$10, updated_at=now()
WHERE id=$1`,
id, in.Slug, in.Name, in.Address, phones, hours, in.Lat, in.Lon, in.Sort, in.Published)
if err != nil {
return wrapConflict(err)
}
if tag.RowsAffected() == 0 {
return ErrNotFound
}
return nil
}