144 lines
3.8 KiB
Go
144 lines
3.8 KiB
Go
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)
|
||
}
|