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

108 lines
3.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package storage
import (
"context"
"encoding/json"
"time"
)
// AdminPost — новость или статья для админки, включая скрытые.
type AdminPost struct {
ID int64 `json:"id"`
Kind string `json:"kind"`
Slug string `json:"slug"`
Title string `json:"title"`
Lead string `json:"lead"`
Blocks json.RawMessage `json:"blocks"`
ImageID *int64 `json:"image_id"`
Image string `json:"image,omitempty"`
Published bool `json:"published"`
PublishedAt time.Time `json:"published_at"`
SEOTitle string `json:"seo_title"`
SEODesc string `json:"seo_description"`
SEOH1 string `json:"seo_h1"`
}
// AdminPosts — публикации одного вида (news | article), новые сверху.
func (s *Storage) AdminPosts(ctx context.Context, kind string) ([]AdminPost, error) {
rows, err := s.pool.Query(ctx, `
SELECT p.id, p.kind, p.slug, p.title, p.lead, p.blocks, p.image_id, COALESCE(m.path, ''),
p.published, p.published_at, p.seo_title, p.seo_description, p.seo_h1
FROM posts p
LEFT JOIN media m ON m.id = p.image_id
WHERE p.kind = $1 AND p.deleted_at IS NULL
ORDER BY p.published_at DESC, p.id DESC`, kind)
if err != nil {
return nil, err
}
defer rows.Close()
var out []AdminPost
for rows.Next() {
var a AdminPost
if err := rows.Scan(&a.ID, &a.Kind, &a.Slug, &a.Title, &a.Lead, &a.Blocks, &a.ImageID, &a.Image,
&a.Published, &a.PublishedAt, &a.SEOTitle, &a.SEODesc, &a.SEOH1); err != nil {
return nil, err
}
out = append(out, a)
}
return out, rows.Err()
}
type PostInput struct {
Kind string
Slug string
Title string
Lead string
Blocks json.RawMessage
ImageID *int64
Published bool
PublishedAt time.Time
SEOTitle string
SEODesc string
SEOH1 string
}
func (s *Storage) AdminCreatePost(ctx context.Context, in PostInput) (int64, error) {
var id int64
err := s.pool.QueryRow(ctx, `
INSERT INTO posts (kind, slug, title, lead, blocks, image_id, published, published_at,
seo_title, seo_description, seo_h1)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)
RETURNING id`,
in.Kind, in.Slug, in.Title, in.Lead, in.Blocks, in.ImageID, in.Published, in.PublishedAt,
in.SEOTitle, in.SEODesc, in.SEOH1).Scan(&id)
return id, wrapConflict(err)
}
// AdminUpdatePost меняет публикацию; вид (новость или статья) после создания не меняется.
func (s *Storage) AdminUpdatePost(ctx context.Context, id int64, in PostInput) error {
tag, err := s.pool.Exec(ctx, `
UPDATE posts SET slug=$3, title=$4, lead=$5, blocks=$6, image_id=$7, published=$8,
published_at=$9, seo_title=$10, seo_description=$11, seo_h1=$12, updated_at=now()
WHERE id=$1 AND kind=$2 AND deleted_at IS NULL`,
id, in.Kind, in.Slug, in.Title, in.Lead, in.Blocks, in.ImageID, in.Published,
in.PublishedAt, in.SEOTitle, in.SEODesc, in.SEOH1)
if err != nil {
return wrapConflict(err)
}
if tag.RowsAffected() == 0 {
return ErrNotFound
}
return nil
}
// AdminDeletePost — мягкое удаление, как у акций.
func (s *Storage) AdminDeletePost(ctx context.Context, kind string, id int64) error {
tag, err := s.pool.Exec(ctx, `
UPDATE posts SET deleted_at=now(), published=false, updated_at=now()
WHERE id=$1 AND kind=$2 AND deleted_at IS NULL`, id, kind)
if err != nil {
return err
}
if tag.RowsAffected() == 0 {
return ErrNotFound
}
return nil
}