Files
mengyastore/mengyastore-backend-go/internal/handlers/public.go
2026-05-13 12:11:46 +08:00

79 lines
2.0 KiB
Go

package handlers
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
"mengyastore-backend/internal/models"
"mengyastore-backend/internal/storage"
)
type PublicHandler struct {
store *storage.ProductStore
}
func NewPublicHandler(store *storage.ProductStore) *PublicHandler {
return &PublicHandler{store: store}
}
// ListProducts 上架商品列表(公开字段,不含卡密与管理信息)。
// @Summary 上架商品列表
// @Tags 公开
// @Produce json
// @Success 200 {object} SwaggerProductListBody
// @Failure 500 {object} SwaggerErrorBody
// @Router /api/products [get]
func (h *PublicHandler) ListProducts(c *gin.Context) {
items, err := h.store.ListActive()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"data": sanitizeForPublic(items)})
}
// RecordProductView 记录商品浏览(去重策略由服务端指纹决定)。
// @Summary 记录商品浏览
// @Tags 公开
// @Produce json
// @Param id path string true "商品 ID"
// @Success 200 {object} SwaggerProductViewWrap
// @Failure 404 {object} SwaggerErrorBody
// @Router /api/products/{id}/view [post]
func (h *PublicHandler) RecordProductView(c *gin.Context) {
id := c.Param("id")
fingerprint := buildViewerFingerprint(c)
product, counted, err := h.store.IncrementView(id, fingerprint)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"data": gin.H{
"id": product.ID,
"viewCount": product.ViewCount,
"counted": counted,
},
})
}
func buildViewerFingerprint(c *gin.Context) string {
clientIP := strings.TrimSpace(c.ClientIP())
userAgent := strings.TrimSpace(c.GetHeader("User-Agent"))
language := strings.TrimSpace(c.GetHeader("Accept-Language"))
return clientIP + "|" + userAgent + "|" + language
}
func sanitizeForPublic(items []models.Product) []models.Product {
out := make([]models.Product, len(items))
for i, item := range items {
item.Codes = nil
item.FixedContent = ""
out[i] = item
}
return out
}