Files
2026-08-19 22:05:49 +08:00

144 lines
3.8 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 web
import (
"context"
"log/slog"
"net/netip"
"server/internal/db"
"server/internal/db/sqlc"
"server/internal/model/common"
"server/internal/model/request"
"server/internal/model/response"
"server/internal/pkg/dberr"
"server/internal/pkg/errs"
"server/internal/pkg/safego"
)
type PostService struct {
store *db.Store
}
func NewPostService(store *db.Store) *PostService {
return &PostService{
store: store,
}
}
func (s *PostService) List(ctx context.Context, p *common.Pagination) (*common.PageResult[sqlc.ListPublishedPostsRow], error) {
params := sqlc.ListPublishedPostsParams{
Limit: p.PageSize,
Offset: (p.Page - 1) * p.PageSize,
}
total, err := s.store.CountPublishedPosts(ctx)
if err != nil {
return nil, err
}
list, err := s.store.ListPublishedPosts(ctx, params)
if err != nil {
return nil, err
}
return &common.PageResult[sqlc.ListPublishedPostsRow]{
List: list,
Total: total,
}, nil
}
func (s *PostService) ListSearch(ctx context.Context, req request.SearchPublishedPostsParams) ([]sqlc.ListPublishedPostsWithFiltersRow, error) {
params := sqlc.ListPublishedPostsWithFiltersParams{
CategoryCode: &req.CategoryCode,
TagCode: &req.TagCode,
}
return s.store.ListPublishedPostsWithFilters(ctx, params)
}
func (s *PostService) GetPost(ctx context.Context, slug string, ip netip.Addr) (*sqlc.GetPublicPostBySlugRow, error) {
post, err := s.store.GetPublicPostBySlug(ctx, slug)
if err != nil {
return nil, dberr.MapNoRows(err, errs.ErrPostNotFound)
}
// 异步统计浏览量,不阻塞响应
// 用 WithoutCancel 脱离请求 context否则 handler 一返回、请求 ctx 被取消,写库会被中断
safego.Go(func() {
statsCtx := context.WithoutCancel(ctx)
if err = s.store.IncrementPostStatsView(statsCtx, sqlc.IncrementPostStatsViewParams{
PostID: post.ID,
Ip: ip,
}); err != nil {
slog.Error("increment post stats view failed", "post_id", post.ID, "error", err)
}
})
return &post, nil
}
func (s *PostService) ListCategoryStats(ctx context.Context) ([]sqlc.ListCategoryStatsRow, error) {
return s.store.ListCategoryStats(ctx)
}
func (s *PostService) ListArchives(ctx context.Context) ([]response.ArchiveYear, error) {
list, err := s.store.ListArchives(ctx)
if err != nil {
return nil, err
}
archive := make([]response.ArchiveYear, 0)
for _, item := range list {
y := item.PublishedAt.Year()
m := int(item.PublishedAt.Month())
if len(archive) == 0 || archive[len(archive)-1].Year != y {
archive = append(archive, response.ArchiveYear{
Year: y,
Total: 0,
ArchiveMonth: make([]response.ArchiveMonth, 0),
})
}
// 获取索引
lastYearIndex := len(archive) - 1
months := archive[lastYearIndex].ArchiveMonth
if len(months) == 0 || months[len(months)-1].Month != m {
archive[lastYearIndex].ArchiveMonth = append(archive[lastYearIndex].ArchiveMonth, response.ArchiveMonth{
Month: m,
Archive: make([]response.ArchivePost, 0),
})
}
lastMonthIndex := len(archive[lastYearIndex].ArchiveMonth) - 1
categoryName := ""
if item.CategoryName != nil {
categoryName = *item.CategoryName
}
archive[lastYearIndex].ArchiveMonth[lastMonthIndex].Archive =
append(archive[lastYearIndex].ArchiveMonth[lastMonthIndex].Archive, response.ArchivePost{
ID: item.ID,
Slug: item.Slug,
Title: item.Title,
PublishedAt: item.PublishedAt,
PublishedAtDisplay: item.PublishedAt.Format("01-02"),
CategoryName: categoryName,
})
archive[lastYearIndex].Total++
}
return archive, nil
}
func (s *PostService) ListPostTags(ctx context.Context) ([]sqlc.Tag, error) {
return s.store.ListAllTags(ctx)
}
func (s *PostService) GetPostsForSitemap(ctx context.Context) ([]sqlc.GetPostsForSitemapRow, error) {
return s.store.GetPostsForSitemap(ctx)
}