Files
backend_v2/internal/handler/ssg_handler.go
toom1996 30c9f21da4 update
2026-08-26 10:45:21 +08:00

91 lines
3.0 KiB
Go
Raw 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 handler
import (
"net/http"
"strings"
"fashionapi/internal/dto"
"fashionapi/internal/pkg/response"
"fashionapi/internal/service"
"github.com/gin-gonic/gin"
)
// SSGHandler 承载「仅供构建期(astro build)使用的内部接口」。
//
// 这些接口返回全量/全 ID 数据,理论上不应暴露给公网,因此:
// - 它们只挂在独立的内部端口(默认 8091,见 router.NewSSG)上;
// - 该端口由 docker-compose 绑定 127.0.0.1(回环),nginx 也绝不反代;
// - 复用现有 service 层逻辑,与对外公开接口保持同一份数据源与映射规则,
// 不会出现「构建接口与线上接口返回结构不一致」的坑。
type SSGHandler struct {
brands service.BrandService
articles service.ArticleService
}
// NewSSGHandler 创建 SSG handler。
func NewSSGHandler(brands service.BrandService, articles service.ArticleService) *SSGHandler {
return &SSGHandler{brands: brands, articles: articles}
}
// Popular 首页「热门品牌」区块数据(SSG 内部版)。
//
// GET /api/ssg/brands/popular?limit=20
//
// 与公开版逻辑完全一致(复用 service.BrandService.Popular),只是挪到了内部端口,
// 不再挂在对外公开的 /api/public 下。limit 默认 20、上限 100。
func (h *SSGHandler) Popular(c *gin.Context) {
limit := queryInt(c, "limit", 20)
if limit < 1 || limit > 100 {
limit = 20
}
list, err := h.brands.Popular(c.Request.Context(), limit, parseLocale(c))
if err != nil {
fail(c, err)
return
}
response.Data(c, http.StatusOK, list)
}
// Brands 品牌索引页(HBX 风格 A-Z)的 SSG 全量数据。
//
// GET /api/ssg/brands?size=300&only_with_articles=1&featured=1
//
// 直接复用 BrandQuery + service.BrandService.List,支持字母/搜索/精选/分页等全部参数,
// 返回 {data:[...]} 与公开列表同构,前端 brands.astro 无需任何适配。
func (h *SSGHandler) Brands(c *gin.Context) {
q := dto.BrandQuery{
Letter: strings.ToUpper(strings.TrimSpace(c.Query("letter"))),
Keyword: strings.TrimSpace(c.Query("keyword")),
Page: queryInt(c, "page", 1),
Size: queryInt(c, "size", 200),
OnlyWithArticles: queryBool(c, "only_with_articles", true),
Featured: queryBool(c, "featured", false),
Metric: strings.ToLower(strings.TrimSpace(c.Query("metric"))),
Limit: queryInt(c, "limit", 200),
Locale: parseLocale(c),
}
q.Normalize()
list, total, err := h.brands.List(c.Request.Context(), q)
if err != nil {
fail(c, err)
return
}
response.Page(c, http.StatusOK, list, total, q.Page, q.Size)
}
// ArticleIDs 供 SSG 的 getStaticPaths 枚举详情页路径用。
//
// GET /api/ssg/articles/ids
//
// 只回 [id,...](升序),不带回封面/描述等大字段,比拉 size=500 再 .map(id) 省带宽。
func (h *SSGHandler) ArticleIDs(c *gin.Context) {
ids, err := h.articles.IDs(c.Request.Context())
if err != nil {
fail(c, err)
return
}
response.Data(c, http.StatusOK, ids)
}