// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.31.1 // source: post.sql package sqlc import ( "context" "encoding/json" "time" ) const countPosts = `-- name: CountPosts :one SELECT COUNT(*) FROM posts ` func (q *Queries) CountPosts(ctx context.Context) (int64, error) { row := q.db.QueryRow(ctx, countPosts) var count int64 err := row.Scan(&count) return count, err } const countPublishedPosts = `-- name: CountPublishedPosts :one SELECT COUNT(*) FROM posts WHERE status = 1 AND published_at < NOW() ` func (q *Queries) CountPublishedPosts(ctx context.Context) (int64, error) { row := q.db.QueryRow(ctx, countPublishedPosts) var count int64 err := row.Scan(&count) return count, err } const createPost = `-- name: CreatePost :one INSERT INTO posts(title, cover_id, slug, content, summary, status, sort, published_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id ` type CreatePostParams struct { Title string `json:"title"` CoverID *int32 `json:"cover_id"` Slug string `json:"slug"` Content string `json:"content"` Summary string `json:"summary"` Status int16 `json:"status"` Sort *int32 `json:"sort"` PublishedAt time.Time `json:"published_at"` } func (q *Queries) CreatePost(ctx context.Context, arg CreatePostParams) (int32, error) { row := q.db.QueryRow(ctx, createPost, arg.Title, arg.CoverID, arg.Slug, arg.Content, arg.Summary, arg.Status, arg.Sort, arg.PublishedAt, ) var id int32 err := row.Scan(&id) return id, err } const deletePost = `-- name: DeletePost :execrows DELETE FROM posts WHERE id = $1 ` func (q *Queries) DeletePost(ctx context.Context, id int32) (int64, error) { result, err := q.db.Exec(ctx, deletePost, id) if err != nil { return 0, err } return result.RowsAffected(), nil } const getPostByID = `-- name: GetPostByID :one SELECT p.id, p.title, p.cover_id, p.slug, p.content, p.summary, p.status, p.sort, p.published_at, p.created_at, p.updated_at, f.file_url AS cover, f.file_size AS cover_size, c.name AS category_name, c.id AS category_id, COALESCE( array_agg(pt.tag_id ORDER BY pt.tag_id) FILTER (WHERE pt.tag_id IS NOT NULL), ARRAY []::integer[] ) AS tags FROM posts p LEFT JOIN files f ON f.id = p.cover_id LEFT JOIN post_category pc ON pc.post_id = p.id LEFT JOIN categories c ON c.id = pc.category_id LEFT JOIN post_tag pt ON pt.post_id = p.id WHERE p.id = $1 GROUP BY p.id, f.file_url, f.file_size, c.name, c.id LIMIT 1 ` type GetPostByIDRow struct { ID int32 `json:"id"` Title string `json:"title"` CoverID *int32 `json:"cover_id"` Slug string `json:"slug"` Content string `json:"content"` Summary string `json:"summary"` Status int16 `json:"status"` Sort *int32 `json:"sort"` PublishedAt time.Time `json:"published_at"` CreatedAt time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` Cover *string `json:"cover"` CoverSize *int64 `json:"cover_size"` CategoryName *string `json:"category_name"` CategoryID *int32 `json:"category_id"` Tags interface{} `json:"tags"` } func (q *Queries) GetPostByID(ctx context.Context, id int32) (GetPostByIDRow, error) { row := q.db.QueryRow(ctx, getPostByID, id) var i GetPostByIDRow err := row.Scan( &i.ID, &i.Title, &i.CoverID, &i.Slug, &i.Content, &i.Summary, &i.Status, &i.Sort, &i.PublishedAt, &i.CreatedAt, &i.UpdatedAt, &i.Cover, &i.CoverSize, &i.CategoryName, &i.CategoryID, &i.Tags, ) return i, err } const getPostsForSitemap = `-- name: GetPostsForSitemap :many SELECT slug, created_at, updated_at FROM posts WHERE status = 1 AND published_at < NOW() ORDER BY sort DESC, published_at DESC, id DESC ` type GetPostsForSitemapRow struct { Slug string `json:"slug"` CreatedAt time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` } func (q *Queries) GetPostsForSitemap(ctx context.Context) ([]GetPostsForSitemapRow, error) { rows, err := q.db.Query(ctx, getPostsForSitemap) if err != nil { return nil, err } defer rows.Close() items := []GetPostsForSitemapRow{} for rows.Next() { var i GetPostsForSitemapRow if err := rows.Scan(&i.Slug, &i.CreatedAt, &i.UpdatedAt); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const getPublicPostBySlug = `-- name: GetPublicPostBySlug :one SELECT p.id, p.title, p.slug, p.content, p.summary, p.published_at, f.file_url AS cover, COALESCE(ps.view_count, 0) AS view_count, c.code AS category_code, c.name AS category_name, COALESCE(t.tags, '[]') AS tags FROM posts p LEFT JOIN files f ON f.id = p.cover_id LEFT JOIN post_stats ps ON ps.post_id = p.id LEFT JOIN post_category pc ON pc.post_id = p.id LEFT JOIN categories c ON pc.category_id = c.id LEFT JOIN LATERAL ( SELECT jsonb_agg( jsonb_build_object('name', tag.name, 'code', tag.code) ) AS tags FROM post_tag pt JOIN tags tag ON tag.id = pt.tag_id WHERE pt.post_id = p.id ) t ON true WHERE p.slug = $1 AND p.status = 1 AND p.published_at < NOW() LIMIT 1 ` type GetPublicPostBySlugRow struct { ID int32 `json:"id"` Title string `json:"title"` Slug string `json:"slug"` Content string `json:"content"` Summary string `json:"summary"` PublishedAt time.Time `json:"published_at"` Cover *string `json:"cover"` ViewCount int32 `json:"view_count"` CategoryCode *string `json:"category_code"` CategoryName *string `json:"category_name"` Tags json.RawMessage `json:"tags"` } // web ------------------------------------------------------- func (q *Queries) GetPublicPostBySlug(ctx context.Context, slug string) (GetPublicPostBySlugRow, error) { row := q.db.QueryRow(ctx, getPublicPostBySlug, slug) var i GetPublicPostBySlugRow err := row.Scan( &i.ID, &i.Title, &i.Slug, &i.Content, &i.Summary, &i.PublishedAt, &i.Cover, &i.ViewCount, &i.CategoryCode, &i.CategoryName, &i.Tags, ) return i, err } const listArchives = `-- name: ListArchives :many SELECT p.id, p.slug, p.title, p.published_at, c."name" AS category_name FROM posts p LEFT JOIN post_category pc ON p.id = pc.post_id LEFT JOIN categories c ON c.id = pc.category_id WHERE p.status = 1 AND p.published_at < NOW() ORDER BY p.published_at DESC, p.id DESC ` type ListArchivesRow struct { ID int32 `json:"id"` Slug string `json:"slug"` Title string `json:"title"` PublishedAt time.Time `json:"published_at"` CategoryName *string `json:"category_name"` } func (q *Queries) ListArchives(ctx context.Context) ([]ListArchivesRow, error) { rows, err := q.db.Query(ctx, listArchives) if err != nil { return nil, err } defer rows.Close() items := []ListArchivesRow{} for rows.Next() { var i ListArchivesRow if err := rows.Scan( &i.ID, &i.Slug, &i.Title, &i.PublishedAt, &i.CategoryName, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listCategoryStats = `-- name: ListCategoryStats :many SELECT c.id, c.name, c.code, COUNT(p.id) AS post_count FROM categories c LEFT JOIN post_category pc ON c.id = pc.category_id LEFT JOIN posts p ON p.id = pc.post_id AND p.status = 1 AND p.published_at < NOW() GROUP BY c.id, c.sort, c.name, c.code ORDER BY c.sort DESC, c.id ` type ListCategoryStatsRow struct { ID int32 `json:"id"` Name string `json:"name"` Code string `json:"code"` PostCount int64 `json:"post_count"` } func (q *Queries) ListCategoryStats(ctx context.Context) ([]ListCategoryStatsRow, error) { rows, err := q.db.Query(ctx, listCategoryStats) if err != nil { return nil, err } defer rows.Close() items := []ListCategoryStatsRow{} for rows.Next() { var i ListCategoryStatsRow if err := rows.Scan( &i.ID, &i.Name, &i.Code, &i.PostCount, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listPosts = `-- name: ListPosts :many WITH paginated_posts AS ( -- 第一步:先精准查出当前页需要的文章 ID SELECT id, title, cover_id, slug, summary, status, sort, published_at, created_at, updated_at FROM posts ORDER BY sort DESC, published_at DESC, id DESC LIMIT $1 OFFSET $2) SELECT p.id, p.title, p.cover_id, p.slug, p.summary, p.status, p.sort, p.published_at, p.created_at, p.updated_at, f.file_url AS cover, f.file_size AS cover_size, c.name AS category_name, c.id AS category_id, COALESCE(ps.view_count, 0) AS view_count, COALESCE( jsonb_agg( jsonb_build_object( 'id', t.id, 'name', t.name, 'code', t.code ) ORDER BY t.sort DESC, t.id ) FILTER (WHERE t.id IS NOT NULL), '[]'::jsonb ) AS tags FROM paginated_posts p LEFT JOIN files f ON f.id = p.cover_id LEFT JOIN post_category pc ON pc.post_id = p.id LEFT JOIN categories c ON c.id = pc.category_id LEFT JOIN post_stats ps ON ps.post_id = p.id LEFT JOIN post_tag pt ON pt.post_id = p.id LEFT JOIN tags t ON t.id = pt.tag_id GROUP BY p.id, p.title, p.cover_id, p.slug, p.summary, p.status, p.sort, p.published_at, p.created_at, p.updated_at, f.file_url, f.file_size, c.name, c.id, ps.view_count ORDER BY p.sort DESC, p.published_at DESC, p.id DESC ` type ListPostsParams struct { Limit int32 `json:"limit"` Offset int32 `json:"offset"` } type ListPostsRow struct { ID int32 `json:"id"` Title string `json:"title"` CoverID *int32 `json:"cover_id"` Slug string `json:"slug"` Summary string `json:"summary"` Status int16 `json:"status"` Sort *int32 `json:"sort"` PublishedAt time.Time `json:"published_at"` CreatedAt time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` Cover *string `json:"cover"` CoverSize *int64 `json:"cover_size"` CategoryName *string `json:"category_name"` CategoryID *int32 `json:"category_id"` ViewCount int32 `json:"view_count"` Tags interface{} `json:"tags"` } // 第二步:用这极少量的记录去进行 JOIN func (q *Queries) ListPosts(ctx context.Context, arg ListPostsParams) ([]ListPostsRow, error) { rows, err := q.db.Query(ctx, listPosts, arg.Limit, arg.Offset) if err != nil { return nil, err } defer rows.Close() items := []ListPostsRow{} for rows.Next() { var i ListPostsRow if err := rows.Scan( &i.ID, &i.Title, &i.CoverID, &i.Slug, &i.Summary, &i.Status, &i.Sort, &i.PublishedAt, &i.CreatedAt, &i.UpdatedAt, &i.Cover, &i.CoverSize, &i.CategoryName, &i.CategoryID, &i.ViewCount, &i.Tags, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listPublishedPosts = `-- name: ListPublishedPosts :many WITH paginated_posts AS ( -- 第一步:先精准查出当前页需要的文章 ID SELECT id, title, cover_id, slug, summary, sort, published_at FROM posts WHERE status = 1 AND published_at < NOW() ORDER BY sort DESC, published_at DESC, id DESC LIMIT $1 OFFSET $2) SELECT p.id, p.title, p.cover_id, p.slug, p.summary, p.sort, p.published_at, f.file_url AS cover, c.name AS category_name, c.id AS category_id, c.code AS category_code, COALESCE(ps.view_count, 0) AS view_count, COALESCE( jsonb_agg( jsonb_build_object( 'id', t.id, 'name', t.name, 'code', t.code ) ORDER BY t.sort DESC, t.id ) FILTER (WHERE t.id IS NOT NULL), '[]'::jsonb ) AS tags FROM paginated_posts p LEFT JOIN files f ON f.id = p.cover_id LEFT JOIN post_category pc ON pc.post_id = p.id LEFT JOIN categories c ON c.id = pc.category_id LEFT JOIN post_stats ps ON ps.post_id = p.id LEFT JOIN post_tag pt ON pt.post_id = p.id LEFT JOIN tags t ON t.id = pt.tag_id GROUP BY p.id, p.title, p.cover_id, p.slug, p.summary, p.sort, p.published_at, f.file_url, c.name, c.id, c.code, ps.view_count ORDER BY p.sort DESC, p.published_at DESC, p.id DESC ` type ListPublishedPostsParams struct { Limit int32 `json:"limit"` Offset int32 `json:"offset"` } type ListPublishedPostsRow struct { ID int32 `json:"id"` Title string `json:"title"` CoverID *int32 `json:"cover_id"` Slug string `json:"slug"` Summary string `json:"summary"` Sort *int32 `json:"sort"` PublishedAt time.Time `json:"published_at"` Cover *string `json:"cover"` CategoryName *string `json:"category_name"` CategoryID *int32 `json:"category_id"` CategoryCode *string `json:"category_code"` ViewCount int32 `json:"view_count"` Tags interface{} `json:"tags"` } func (q *Queries) ListPublishedPosts(ctx context.Context, arg ListPublishedPostsParams) ([]ListPublishedPostsRow, error) { rows, err := q.db.Query(ctx, listPublishedPosts, arg.Limit, arg.Offset) if err != nil { return nil, err } defer rows.Close() items := []ListPublishedPostsRow{} for rows.Next() { var i ListPublishedPostsRow if err := rows.Scan( &i.ID, &i.Title, &i.CoverID, &i.Slug, &i.Summary, &i.Sort, &i.PublishedAt, &i.Cover, &i.CategoryName, &i.CategoryID, &i.CategoryCode, &i.ViewCount, &i.Tags, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listPublishedPostsWithFilters = `-- name: ListPublishedPostsWithFilters :many SELECT p.id, p.title, p.slug, p.summary, p.sort, p.published_at, f.file_url AS cover, c."name" AS category_name, c.code AS category_code, COALESCE(t.tags, '[]') AS tags, COALESCE(ps."view_count", 0) AS view_count FROM posts p LEFT JOIN post_category pc ON pc.post_id = p.id LEFT JOIN categories c ON c.id = pc.category_id LEFT JOIN post_stats ps ON ps.post_id = p.id LEFT JOIN files f ON f.id = p.cover_id LEFT JOIN LATERAL ( SELECT jsonb_agg( jsonb_build_object('name', tag.name, 'code', tag.code) ) AS tags FROM post_tag pt JOIN tags tag ON tag.id = pt.tag_id WHERE pt.post_id = p.id ) t ON true WHERE -- 只显示已发布的文章 p.status = 1 AND p.published_at < NOW() AND -- category code 过滤(NULL 或空字符串时不过滤) ( COALESCE($1::text, '') = '' OR EXISTS (SELECT 1 FROM post_category pc2 JOIN categories c2 ON c2.id = pc2.category_id WHERE pc2.post_id = p.id AND c2.code = $1::text) ) AND -- tag code 过滤(NULL 或空字符串时不过滤) ( COALESCE($2::text, '') = '' OR EXISTS (SELECT 1 FROM post_tag pt2 JOIN tags t2 ON t2.id = pt2.tag_id WHERE pt2.post_id = p.id AND t2.code = $2::text) ) ORDER BY p.sort DESC, p.published_at DESC, p.id DESC ` type ListPublishedPostsWithFiltersParams struct { CategoryCode *string `json:"category_code"` TagCode *string `json:"tag_code"` } type ListPublishedPostsWithFiltersRow struct { ID int32 `json:"id"` Title string `json:"title"` Slug string `json:"slug"` Summary string `json:"summary"` Sort *int32 `json:"sort"` PublishedAt time.Time `json:"published_at"` Cover *string `json:"cover"` CategoryName *string `json:"category_name"` CategoryCode *string `json:"category_code"` Tags json.RawMessage `json:"tags"` ViewCount int32 `json:"view_count"` } func (q *Queries) ListPublishedPostsWithFilters(ctx context.Context, arg ListPublishedPostsWithFiltersParams) ([]ListPublishedPostsWithFiltersRow, error) { rows, err := q.db.Query(ctx, listPublishedPostsWithFilters, arg.CategoryCode, arg.TagCode) if err != nil { return nil, err } defer rows.Close() items := []ListPublishedPostsWithFiltersRow{} for rows.Next() { var i ListPublishedPostsWithFiltersRow if err := rows.Scan( &i.ID, &i.Title, &i.Slug, &i.Summary, &i.Sort, &i.PublishedAt, &i.Cover, &i.CategoryName, &i.CategoryCode, &i.Tags, &i.ViewCount, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const updatePost = `-- name: UpdatePost :execrows UPDATE posts SET title = coalesce($1, title), cover_id = CASE WHEN $2::boolean THEN $3 ELSE cover_id END, slug = coalesce($4, slug), content = coalesce($5, content), summary = coalesce($6, summary), status = coalesce($7, status), sort = coalesce($8, sort), published_at = coalesce($9, published_at) WHERE id = $10 ` type UpdatePostParams struct { Title *string `json:"title"` UpdateCoverID bool `json:"update_cover_id"` CoverID *int32 `json:"cover_id"` Slug *string `json:"slug"` Content *string `json:"content"` Summary *string `json:"summary"` Status *int16 `json:"status"` Sort *int32 `json:"sort"` PublishedAt *time.Time `json:"published_at"` ID int32 `json:"id"` } func (q *Queries) UpdatePost(ctx context.Context, arg UpdatePostParams) (int64, error) { result, err := q.db.Exec(ctx, updatePost, arg.Title, arg.UpdateCoverID, arg.CoverID, arg.Slug, arg.Content, arg.Summary, arg.Status, arg.Sort, arg.PublishedAt, arg.ID, ) if err != nil { return 0, err } return result.RowsAffected(), nil }