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 }